The One with the Thoughts of Frans

Archive for Scripting

Javascript Associative Array of SVG Color Keywords

I thought I needed an associative array of SVG color keywords. I couldn’t find one, so I created this. Enjoy.

// see
var SVGColors = new Object();
SVGColors['aliceblue'] = [240,248,255];
SVGColors['antiquewhite'] = [250,235,215];
SVGColors['aqua'] = [0,255,255];
SVGColors['aquamarine'] = [127,255,212];
SVGColors['azure'] = [240,255,255];
SVGColors['beige'] = [245,245,220];
SVGColors['bisque'] = [255,228,196];
SVGColors['black'] = [0,0,0];
SVGColors['blanchedalmond'] = [255,235,205];
SVGColors['blue'] = [0,0,255];
SVGColors['blueviolet'] = [138,43,226];
SVGColors['brown'] = [165,42,42];
SVGColors['burlywood'] = [222,184,135];
SVGColors['cadetblue'] = [95,158,160];
SVGColors['chartreuse'] = [127,255,0];
SVGColors['chocolate'] = [210,105,30];
SVGColors['coral'] = [255,127,80];
SVGColors['cornflowerblue'] = [100,149,237];
SVGColors['cornsilk'] = [255,248,220];
SVGColors['crimson'] = [220,20,60];
SVGColors['cyan'] = [0,255,255];
SVGColors['darkblue'] = [0,0,139];
SVGColors['darkcyan'] = [0,139,139];
SVGColors['darkgoldenrod'] = [184,134,11];
SVGColors['darkgray'] = [169,169,169];
SVGColors['darkgreen'] = [0,100,0];
SVGColors['darkgrey'] = [169,169,169];
SVGColors['darkkhaki'] = [189,183,107];
SVGColors['darkmagenta'] = [139,0,139];
SVGColors['darkolivegreen'] = [85,107,47];
SVGColors['darkorange'] = [255,140,0];
SVGColors['darkorchid'] = [153,50,204];
SVGColors['darkred'] = [139,0,0];
SVGColors['darksalmon'] = [233,150,122];
SVGColors['darkseagreen'] = [143,188,143];
SVGColors['darkslateblue'] = [72,61,139];
SVGColors['darkslategray'] = [47,79,79];
SVGColors['darkslategrey'] = [47,79,79];
SVGColors['darkturquoise'] = [0,206,209];
SVGColors['darkviolet'] = [148,0,211];
SVGColors['deeppink'] = [255,20,147];
SVGColors['deepskyblue'] = [0,191,255];
SVGColors['dimgray'] = [105,105,105];
SVGColors['dimgrey'] = [105,105,105];
SVGColors['dodgerblue'] = [30,144,255];
SVGColors['firebrick'] = [178,34,34];
SVGColors['floralwhite'] = [255,250,240];
SVGColors['forestgreen'] = [34,139,34];
SVGColors['fuchsia'] = [255,0,255];
SVGColors['gainsboro'] = [220,220,220];
SVGColors['ghostwhite'] = [248,248,255];
SVGColors['gold'] = [255,215,0];
SVGColors['goldenrod'] = [218,165,32];
SVGColors['gray'] = [128,128,128];
SVGColors['green'] = [0,128,0];
SVGColors['greenyellow'] = [173,255,47];
SVGColors['grey'] = [128,128,128];
SVGColors['honeydew'] = [240,255,240];
SVGColors['hotpink'] = [255,105,180];
SVGColors['indianred'] = [205,92,92];
SVGColors['indigo'] = [75,0,130];
SVGColors['ivory'] = [255,255,240];
SVGColors['khaki'] = [240,230,140];
SVGColors['lavender'] = [230,230,250];
SVGColors['lavenderblush'] = [255,240,245];
SVGColors['lawngreen'] = [124,252,0];
SVGColors['lemonchiffon'] = [255,250,205];
SVGColors['lightblue'] = [173,216,230];
SVGColors['lightcoral'] = [240,128,128];
SVGColors['lightcyan'] = [224,255,255];
SVGColors['lightgoldenrodyellow'] = [250,250,210];
SVGColors['lightgray'] = [211,211,211];
SVGColors['lightgreen'] = [144,238,144];
SVGColors['lightgrey'] = [211,211,211];
SVGColors['lightpink'] = [255,182,193];
SVGColors['lightsalmon'] = [255,160,122];
SVGColors['lightseagreen'] = [32,178,170];
SVGColors['lightskyblue'] = [135,206,250];
SVGColors['lightslategray'] = [119,136,153];
SVGColors['lightslategrey'] = [119,136,153];
SVGColors['lightsteelblue'] = [176,196,222];
SVGColors['lightyellow'] = [255,255,224];
SVGColors['lime'] = [0,255,0];
SVGColors['limegreen'] = [50,205,50];
SVGColors['linen'] = [250,240,230];
SVGColors['magenta'] = [255,0,255];
SVGColors['maroon'] = [128,0,0];
SVGColors['mediumaquamarine'] = [102,205,170];
SVGColors['mediumblue'] = [0,0,205];
SVGColors['mediumorchid'] = [186,85,211];
SVGColors['mediumpurple'] = [147,112,219];
SVGColors['mediumseagreen'] = [60,179,113];
SVGColors['mediumslateblue'] = [123,104,238];
SVGColors['mediumspringgreen'] = [0,250,154];
SVGColors['mediumturquoise'] = [72,209,204];
SVGColors['mediumvioletred'] = [199,21,133];
SVGColors['midnightblue'] = [25,25,112];
SVGColors['mintcream'] = [245,255,250];
SVGColors['mistyrose'] = [255,228,225];
SVGColors['moccasin'] = [255,228,181];
SVGColors['navajowhite'] = [255,222,173];
SVGColors['navy'] = [0,0,128];
SVGColors['oldlace'] = [253,245,230];
SVGColors['olive'] = [128,128,0];
SVGColors['olivedrab'] = [107,142,35];
SVGColors['orange'] = [255,165,0];
SVGColors['orangered'] = [255,69,0];
SVGColors['orchid'] = [218,112,214];
SVGColors['palegoldenrod'] = [238,232,170];
SVGColors['palegreen'] = [152,251,152];
SVGColors['paleturquoise'] = [175,238,238];
SVGColors['palevioletred'] = [219,112,147];
SVGColors['papayawhip'] = [255,239,213];
SVGColors['peachpuff'] = [255,218,185];
SVGColors['peru'] = [205,133,63];
SVGColors['pink'] = [255,192,203];
SVGColors['plum'] = [221,160,221];
SVGColors['powderblue'] = [176,224,230];
SVGColors['purple'] = [128,0,128];
SVGColors['red'] = [255,0,0];
SVGColors['rosybrown'] = [188,143,143];
SVGColors['royalblue'] = [65,105,225];
SVGColors['saddlebrown'] = [139,69,19];
SVGColors['salmon'] = [250,128,114];
SVGColors['sandybrown'] = [244,164,96];
SVGColors['seagreen'] = [46,139,87];
SVGColors['seashell'] = [255,245,238];
SVGColors['sienna'] = [160,82,45];
SVGColors['silver'] = [192,192,192];
SVGColors['skyblue'] = [135,206,235];
SVGColors['slateblue'] = [106,90,205];
SVGColors['slategray'] = [112,128,144];
SVGColors['slategrey'] = [112,128,144];
SVGColors['snow'] = [255,250,250];
SVGColors['springgreen'] = [0,255,127];
SVGColors['steelblue'] = [70,130,180];
SVGColors['tan'] = [210,180,140];
SVGColors['teal'] = [0,128,128];
SVGColors['thistle'] = [216,191,216];
SVGColors['tomato'] = [255,99,71];
SVGColors['turquoise'] = [64,224,208];
SVGColors['violet'] = [238,130,238];
SVGColors['wheat'] = [245,222,179];
SVGColors['white'] = [255,255,255];
SVGColors['whitesmoke'] = [245,245,245];
SVGColors['yellow'] = [255,255,0];
SVGColors['yellowgreen'] = [154,205,50];


SimplePie-based Feed Mashup

This tool is now named Tubes and is hosted on Bitbucket.

As I wrote a few months ago, Yahoo Pipes is a nice tool. Nonetheless, it has a few shortcomings which annoyed me because I could neither fix nor work around them. Therefore, I decided to write my own mashup tool. For the impatient, you can download the file right now before reading anything else.

Since SimplePie seems to be the feed aggregation library of choice for many projects, I decided to go with it. I ran into a few minor issues, but nothing I couldn’t handle easily. The code I wrote is based on the multifeeds.php demo file and SimplePie 1.1.3, because in 1.2 it didn’t work (the multifeeds demo, that is — by extension I suppose this file won’t either). It’s a little rough around the edges, and SimplePie is clearly meant for HTML output rather than XML (although its HTML isn’t quite decent either, even if the input feed is), so I decided to fix the whole thing up with Tidy, which takes care of low quality input material as well. Hopefully that makes this whole thing more robust than it would otherwise be. The code is based around bringing various Opera feeds I read together in one big feed, but this can very easily be changed.

So now that I’ve got the basics of output into a feed taken care of, I can easily duplicate other functionality of Yahoo Pipes if I want. Much better.


Unobtrusive Input Value Modifier

Inputs that say things like “search here” are generally messed up. In this post I will first explain why and then I will show you how to do it properly.

Ignore everything that follows, other than perhaps the text about what not to do. You should now use <input placeholder="some text">, as HTML 5 support is now sufficiently common. Also note that the placeholder attribute typically isn’t sufficient by itself.

The WordPress theme I’m modifying for my wife had the following HTML in it:

<input type="text" value="Search this site" onfocus="if (this.value == 'Search this site') {this.value = '';}" onblur="if (this.value == '') {this.value = 'Search this site...';}" name="s" id="searchbox" />

There are three main problems with this.

  1. It prefills the search box with “Search this site.” This is incredibly annoying and obnoxious behavior for people with Javascript disabled. If you must add such text, add it through Javascript.
  2. The values don’t even match up, so if you focus on the INPUT, deselect it and select it again the text stays there. Even for users with Javascript enabled. Errors of this type could easily be avoided if the text value were stored in a variable.
  3. Which brings me to the third problem. The script should be external so it can be cached. That would save bandwidth for both you and your visitors and if there are people with Javascript disabled, they won’t even have to load the junkscript once.

Because just about every such box I have ever encountered is complete and utter crap (this is actually one of the better ones), I decided to reproduce its functionality in an unobtrusive manner, eliminating all of the mistakes I outlined above.

The HTML is now reduced to this:

<input name="s" id="searchbox"/>

You could remove the trailing slash if you’re writing HTML, or put a space in between if that’s the way you write XHTML, but that’s of no consequence otherwise. The type="text" is not essential because it’s the default, but it shouldn’t hurt to leave it in. Also see Anne van Kesteren’s Optimizing Optimizing HTML for some tips on going somewhat over the top with minimalism.

The Javascript that changes the text to “Search this site…” is now in an external file:

(function() {
	function searchbox_text_change() {
		var s = document.getElementById('searchbox');
		var s_text = 'Search this site...'
		if (s.value == '') s.value = s_text;
		else if (s.value == s_text) s.value = '';
		var s = document.getElementById('searchbox');
		s.addEventListener('focus', searchbox_text_change, false);
		s.addEventListener('blur', searchbox_text_change, false);

All of this took me about five minutes. There’s a tiny bit of redundancy going on, but I can’t be bothered to fix that. It’s superior to just about any stupid such script I ever encountered and likely to most of the same type that I will encounter in the future. Use this script as much as you please. You don’t even have to link back to me, since I just want those bloody things to work in a way that doesn’t make me cringe.

The same kind of principle applies to autofocus junk. Never ever do any such thing if I started typing something. I’d rather you never did it at all, but if you really feel that you must do it for some masochistic reason, at the very least check if the value is still empty with if (s.value=='').


Taking Sidenotes to 2010

Five years ago there were lots of posts dealing with people’s visions of the least-bad method to include sidenotes — or footnotes — to HTML, and like any self-respecting HTML-geek I created my own take on the matter. As might be expected from five year old writings it is now outdated, and I’m glad it is. It means the cruft can be retired, and media queries can be used to their full glory — except in IE8, that is.

The script I wrote to supply non-Opera browsers with faux-media-query functionality assumes that any browser not Opera should have the script applied to it, because at the time Opera 7+ was the only browser that supported media queries. I knew this wasn’t exactly the proper way to write scripts, but it was meant to be updated to use some more intelligent detection at some point. As such things go, however, it never was. In my defense, the worst the script did was duplicate some functionality that was already provided by media queries, so I rather doubt anybody noticed any adverse effects. Heck, they might have noticed positive effects, since as I wrote at the time, “For now, it might even be the best solution to apply the Javascript to Opera as well, because Opera does not reapply media queries on resize yet (and it does fire the onresize event as every browser does).” For good measure I’m also including the script as I used it on my website since 2006. It has this nifty little added feature that it doesn’t actually do anything if there are no sidenotes present, which is something media queries cannot do. I think I considered writing a more intelligent check based on style features that would be set by the media query back in early ’06, but I can’t recall why I never did. For those interested in hacking the old script, the way I set it up it should be possible to determine whether media queries are supported very easily by combining a test for at least medium width with the marginRight style property on the sidenotes. If set, media queries are working; if not, go ahead and do some scripting magic.

Now, on to the updated sidenotes. I abandoned absolute positioning in favor of going completely for float. I believe I wanted to do this originally, but there were too many float bugs in all kinds of browsers to make it viable (that means everything not Presto or KHTML). Since these appear to be fixed, there is no reason not to take full advantage of floats, which most important means using clear so that sidenotes will not overlap.

I still think my original reasoning is quite valid, however, which means I don’t think sidenotes should be inserted lightly or contain overly long texts.

Let’s start out. How do we markup a sidenote? Well, as HTML contains no way whatsoever to markup a foot- or sidenote, the logical choice is small. Why small? Well, it means that the content of small is less important. A footnote should not be a footnote at all if it’s as important, or more important than the text itself, right? Thus, the markup of the sidenote is as follows:

<small class="sidenote">A sidenote</small>

This is still what I use, but ASIDE would be more appropriate in HTML 5.

The sidenote as I created it is meant to be put at the end of a sentence, inside a paragraph. Therefore it would be displayed at its original position in the text if author CSS was disabled, or read at its intended location on screenreaders. If it wouldn’t be put as a separate sentence, it would look strange if not displayed the intended way. The sidenote is placed inside the paragraph with the other text, for if it would require multiple paragraphs, should it be a sidenote?

There is one issue I didn’t take into account five years ago. For example, including two paragraphs or so of background information on a country or city in a sidenote would be an appropriate use of sidenotes since it’s not really a part of the text. My original stance (although not explicitly written) was that this should be solved with hyperlinks, but I have somewhat revised this stance. The markup would then become something like:

<div class="sidenote">
	<h3>Were Sidenotes Always Compatible With Any Element?</h3>
	<p>You could always apply the <code>sidenote</code> class to any element, such as <code>P</code> or <code>DIV</code>.</p>

Or in HTML 5:

	<h3>Were Sidenotes Always Compatible With Any Element?</h3>
	<p>You could always apply the <code>sidenote</code> class to any element, such as <code>P</code> or <code>DIV</code>.</p>

The main sidenote CSS is still very similar to what it was in 2005.

.sidenote {
	background: #efd;
	display: block;
	float: right;
	clear: right;
	width: 200px;
	border: 1px solid #eee;
	border-right: 0;
	margin: 2px;
	margin-right: -20px;
	padding: 3px;
	text-indent: 0;
	cursor: help;
.sidenote:before { content: '\2190' ' '; }
.sidenote:hover {
	background: #ff0;
/* enable usage of code in sidenotes without the layout breaking  */
.sidenote code {
	white-space: normal;

There are a few minor differences, but other than the addition of the .sidenote code line nothing worth mentioning. Only a few weeks ago I noticed that adding a line of code to a sidenote somewhat broke my layout because it stretched beyond the viewport. A few more global ways to accomplish normal white space in sidenotes come to mind (such as !important in the main class or .sidenote *), but from what I understand using such methods increases parsing time, if only ever so slightly.

The media queries performing the sidenote magic were significantly slimmed down, and a low-resolution in-line display was added:

@media all and (max-width: 350px) {
	.sidenote {
		display: inline;
		float: none;
		border: 0;
		margin: 0;
	.sidenote:before {content:"";}
@media all and (min-width: 750px) {
	.sidenote {
		margin: 0;
@media all and (min-width: 980px) {

Switching completely to float makes it possible to keep the overrides to a minimum, but that’s not the important change here. I switched to simple media queries for two reasons.

  1. It’s much easier to maintain and change. No more duplication.
  2. I don’t think most media types are as relevant anymore as I did back then. Specifically, in regard to such things as handheld devices what I want to do is offer different layouts based on screen size, not on whether they consider themselves to be handheld, screen, or some other fancy media type. Safari on the iPhone considers itself a big browser, for instance, but should it really get the “big” layout? None of this is especially relevant for my sidenotes, but it does reflect my opinion on it. Additionally, specifically overriding for certain media types rather than being specifically inclusive makes sure that no one is left out. In other words, this is future-safe. If the media type magazine ever emerges (they already did a magazine with an eInk cover, didn’t they?), my media query is ready for it now. And for those who care about such things, it also avoids an IE bug or two.

That’s it. My sidenotes are ready for the nearby future. They’re so 2010. Feel free to use or expand on my ideas, but please add a link back to me somewhere if you do.


Add Show or Hide Deleted Text Button

For an upcoming post, I used the DEL element quite extensively. I figured that, while potentially interesting, it would help readability to be able to temporarily remove them from display, so I wrote this little script. Incidentally, it was the first time in about three years—meaning the first time since I finished the layout and scripts when I started this blog—that I added any new scripts, or even edited the file containing them. While I am probably far from the most suitable person for this job, I decided to write a little tutorial, explaining what I used and why I used it.

I’ll start by dropping a major bomb. Here is the script.

// Checks if there is deleted text in post entries. Adds a Show or hide deleted text button if there is.
// Copyright © Frans de jonge 2009. Licensed under a Creative Commons Atrribution 2.0 license.
function AddShowHideDeletedTextButton() {
	var postEntries = document.getElementsByClassName('postentry');
	var postEntry = postEntries[0];
	if ( postEntries.length == 1 && postEntry.getElementsByTagName('del').length > 0 ) {
		var hideText = 'Hide deleted text', showText = 'Show deleted text';
		var button = document.createElement('input');
		button.setAttribute('type', 'button');
		button.setAttribute('value', hideText);
		function () {
			var e = postEntry.getElementsByTagName('del');
			for ( var i = 0; i<e.length; i++ ) {
				if (e[i].style.display == 'none') {
					e[i].style.display = 'inline';
					this.setAttribute('value', hideText);
				else {
					e[i].style.display = 'none';
					this.setAttribute('value', showText);

I’m going to assume that you have at least a basic understanding of functions and variables, but may not be completely aware of DOM methods and how to use them. A very valuable resource is the Mozilla Developer Center, or MDC. It provides information on much more than just JavaScript and the DOM, but while I find the CSS and even HTML specifications fairly easy to get around in, the DOM specifications, with their language neutral, very extensive descriptions are quite hard in comparison. They are exactly the way they ought to be, but this makes it somewhat hard to find relevant information about actual usage and how to use it. That’s where the MDC comes in. It typically comes with a clear, concise summary at the top of the page, and if you don’t understand that, it usually comes with more extensive code samples as well.

	var postEntries = document.getElementsByClassName('postentry');
	var postEntry = postEntries[0];
	if ( postEntries.length == 1 && postEntry.getElementsByTagName('del').length > 0 ) {

DOM methods used:

In my blog, I have marked up all of the contents of my posts with a DIV element, with, as you might have guessed, a class postentry. This particular bit of code is meant to make sure that we are on the individual page of the post (i.e. not in some kind of overview) and that there are actually deleted elements around. Adding a button on an overview page would result in all kinds of problems—if you add only one it may not be anywhere near where it is needed, so you’d have to add multiple buttons, check if the post where it will be added has deleted elements, etc.—which are easy to avoid by keeping it restricted to one post.

		var button = document.createElement('input');
		button.setAttribute('type', 'button');
		button.setAttribute('value', hideText);

DOM methods used:

Here we see the proper way to create an element through the DOM. First we create an element, in this case input, and then we set a few attributes to the values we want. Note that I defined hideText earlier, with the value Hide deleted text.

		function () {
			var e = postEntry.getElementsByTagName('del');
			for ( var i = 0; i<e.length; i++ ) {
				if (e[i].style.display == 'none') {
					e[i].style.display = 'inline';
					this.setAttribute('value', hideText);
				else {
					e[i].style.display = 'none';
					this.setAttribute('value', showText);

DOM methods used:

Here we definitely have one of the more interesting parts of the code. Had I written this script about 8 years ago, I would have used something like var button = '<input type="button" value='+hideText+'>' and consequently added an eventListener like button.onclick = someFunction;, where someFunction would be separately defined. I am using the proper DOM method instead. This may cause problems in IE, for which there are fixes available, but since this blog is strictly personal I decided not to care about that. Notice how I did not define the function separately, but included it as an anonymous function. Since the function is not intended to be used anywhere else, it’s easier, and, ideally, it will be completely removed from memory if the button were to be removed.

The property allows you to access and change style properties on an element. As the MDC says, It is generally better to use the style property than to use elt.setAttribute('style', '...'), since use of the style property will not overwrite other CSS properties that may be specified in the style attribute. I may not be defining any other styles on DEL elements yet, but this way the script won’t break my site if I do add some more style to it in the future, or if someone else wishes to adapt it for use on their own page.


DOM methods used:

To finish it off, we’ve got the insertBefore construct. It inserts a node as a child of the node to which it is applied, before a specified other child of this parent node. When I first came across it a few years ago, I thought it was somewhat confusing at first. It should be noted that node means much more than element, but in these specific examples it shouldn’t matter, so I didn’t go into it. If you wish to learn more, I recommend you try the MDC or a search engine.

In conclusion, I hope this helped someone out there a bit. I thought it was interesting to reminisce about how I would have written this script if it were 2003.


Extend Opera was launched a few days ago. It’s a user initiative, aimed at bringing those aspects of Opera customization that Opera software has somewhat abandoned together.


Google in English

Google apparently thinks that it knows what I want better than I do. I have clearly specified “en-US” as my preferred language, “en” if that’s not available, and “nl” if that’s not available. I tried removing the “nl” in an attempt to get Google to speak English to me, but it won’t comply. I finally got annoyed enough to write this little script to fix it. It would be easy to adjust for other preferred languages.

Install Google in English user script.


Geeks, code can be sexy

I was reading entries on my old weblog from 2005 on the Wayback Machine, as I realised I hadn’t posted something on my current one in ages. So hereby. UDKM is Daniel.

(| !!UDKM |” U Don’t Know Me “) says (2:02):
Dude, words can’t express how much I love PHP designer 2007

Frenzie says (2:03):

(| !!UDKM |” U Don’t Know Me “) says (2:03):
And my panties get’s wet of highlighted PHP code.

Frenzie says (2:04):

(| !!UDKM |” U Don’t Know Me “) says (2:04):
dude, highlighted PHP code is TEH SEX!

Frenzie says (2:05):
I know man

(| !!UDKM |” U Don’t Know Me “) says (2:07):
Highlited HTML is semi-sex.

Frenzie says (2:07):

Frenzie says (2:07):
Highlighted CSS is a girl with a tight ass and nice titties.

(| !!UDKM |” U Don’t Know Me “) says (2:12):
Oh yeah, it’s an ugly font thought. Atleast in PHP designer 2007

Frenzie says (2:13):

(| !!UDKM |” U Don’t Know Me “) says (2:13):
So it’s a nice ass with nice titties but a fucked face.

Frenzie says (2:13):
Yeah, pretty much.

Comments (2)

Opera User JS and GreaseMonkey interoperability

I’ve written a User JS with enhancements for AvidGamers. However, GreaseMonkey automatically wraps the entire script in an anonymous function. To create functions in your User JS, which are accessible in the page itself (by elements you inserted), you need to attach it to the window object.

All of that would be great, but it didn’t work. Digging through the GreaseMonkey website I eventually discovered that this had to do with some kind of security bug (the link to Mark Pilgrims guide as the way to learn about GreaseMonkey didn’t help much).

To make a long story short, I used the following to create Opera User JS and Greasemonkey interoperability.

// for GreaseMonkey compatibility
if (typeof unsafeWindow != 'undefined') window = unsafeWindow;

// how to add variables
window['some_variable'] = 'Some text';

// how to add functions
window.some_function = function (var1, var2) {
	// do something

To keep your code clean it’s a good idea to add in some extra variables for reference inside your code.

var some_variable = window[some_variable];

Regardless of security issues which removed direct access to the window object, it seems a tad weird to me to always wrap the code in an anonymous function. Should that not be up to me, the script author, to decide? Opera’s implementation appears to be just perfect…

Comments (1)

« Newer Entries