Monday, August 11, 2008

Javascript Lorem Ipsum Generator

About 2 years ago I wanted a Lorem Ipsum Javascript to make adding Lorem Ipsum to webpages in progress a little less work & bandwidth consuming.
It's worked well for quite awhile now, but today seemed like a good day to revamp the script.

This new version of the script works the same way as the last version.
HTML comments are placed where Lorem Ipsum should go & the script replaces those comment nodes with text nodes at the DOM level.
Version 2 is also about 3 times faster, the comment scheme is easier to use, & there's some convience methods added.

Starting with the easier comment scheme.
Comments are placed like so depending on whether words, sentences, or paragraphs should replace the comment.
It starts with the number of units to use, followed by what type of units to use. The script is pretty forgiving about the usage of whitespace when defining these. The script also doesn't care if the unit name is pluralized or not.
<-- 2 words -->
<-- 4 sentences -->
<-- 1 paragraph -->


Ranges can also be used for the amount of units.
For instance, if somewhere between 2-5 words should be generated you could use the following comment.
The script is just as forgiving with whitespace between the range numbers as it is with the rest of the format.
<-- 2- 5 word -->


There's a main method included that will start at the <body> element & process all comment nodes in the document. This method can be called at the end of the document, or added as an event listener.
LoremIpsum.LoremIpsum();
window.addEventListener('load', LoremIpsum.LoremIpsum, false);
window.attachEvent('onload', LoremIpsum.LoremIpsum);


The processing can also be rooted at any other node by using the scanElm method.
var trunk = document.getElementById('example');
LoremIpsum.scanElm(trunk);


The script doesn't have to be used to replace comments in the document, or in a certain node.
The word, sentence, & paragraph generation methods can be called directly & will return however many units they're asked for.
The only restriction is that ranges can't be used when calling the methods directly.
If you want random numbers of units, you'll need to generate those numbers yourself.
var elm = document.getElementById('elm');
var word_count = Math.ceil(Math.random() * 10);
var ipsum = LoremIpsum.words(word_count);
elm.appendChild(document.createTextNode(ipsum));


Quite a few aliases are included for each method.
Camel notation, Underscore notation, Lowercase notation.
main & init are included for starting with the root element.
get_* schemes are included for the generation methods.
/* Convenience / aliases / etc */
LoremIpsum.main = LoremIpsum.init = LoremIpsum.LoremIpsum;
LoremIpsum.getWords = LoremIpsum.get_words = LoremIpsum.words;
LoremIpsum.getSentences = LoremIpsum.get_sentences = LoremIpsum.sentences;
LoremIpsum.getParagraphs = LoremIpsum.get_paragraphs = LoremIpsum.paragraphs;
LoremIpsum.scan_elm = LoremIpsum.scanelm = LoremIpsum.scanElm;
lorem_ipsum = loremipsum = loremIpsum = LoremIpsum;


Here's the source, it saves to about 4.5KB fully commented & uncompressed.
The methods include further documentation in the form of comments.

LoremIpsum = {
fullstop: '.', /* Character(s) to add to the end of sentences */
min_wps: 10, /* Minimum words per sentence */
max_wps: 25, /* Maximum words per sentence */
min_spp: 2, /* Minimum sentences per paragraph */
max_spp: 8, /* Maximum sentences per paragraph */

/* Word bank
Words are selected from this array randomly
to generate complete text nodes.

NOTE: This array __can__ be altered provided it remains a single-dimension array.
*/
src: new Array('a','adipiscing','amet','arcu','at','auctor','bibendum','commodo','congue','curabitur','cursus','diam','donec','duis','eget','elementum','enim','eros','et','eu','fusce','gravida','in','integer','ipsum','justo','lectus','leo','ligula','lorem','maecenas','magna','malesuada','massa','mattis','mauris','metus','molestie','morbi','nam','nec','nibh','non','nulla','odio','orci','ornare','pellentesque','pharetra','porta','porttitor','proin','quam','quisque','risus','rutrum','sagittis','sapien','sed','sem','sit','sodales','tellus','tempus','ultricies','urna','ut','vitae','vivamus','vulputate'),

/* words, getWords, get_words
@param words: Number of words to return
@param fullstop: String to append to the words, such as sentence ending punctuation.
@returns String: String of whitespace delimited words.
*/
words: function(words, fullstop){
var lastword, nextword, str, wc = (LoremIpsum.src.length - 1);
lastword = nextword = Math.round(Math.random() * wc);
str = LoremIpsum.src[lastword].charAt(0).toUpperCase() + LoremIpsum.src[lastword].substr(1);
words--;
while(words > 0){
while(lastword == nextword){
nextword = Math.round(Math.random() * wc);
}
str += (' ' + LoremIpsum.src[nextword]);
lastword = nextword;
words--;
}
return str + (fullstop ? fullstop : '');
},

/* sentences, getSentences, get_sentences
@param sentences: Number of sentences to return.
@returns String: String of sentences delimited by current value of LoremIpsum.fullstop
*/
sentences: function(sentences){
var wpsos = LoremIpsum.max_wps - LoremIpsum.min_wps;
var str = LoremIpsum.words((LoremIpsum.min_wps + Math.round(Math.random() * wpsos)), LoremIpsum.fullstop);
sentences--;
while(sentences > 0){
str += (' ' + LoremIpsum.words((LoremIpsum.min_wps + Math.round(Math.random() * wpsos)), LoremIpsum.fullstop));
sentences--;
}
return str;
},

/* paragraphs, getParagraphs, get_paragraphs
@param paragraphs: Number of paragraphs to return.
@param format: How to format each paragraph, where %s is the text.
For instance: <p class="myclass">%s</p>
@returns String: String of paragraphs formated using @format.
*/
paragraphs: function(paragraphs, format){
if(!format){
format = "%s\n\n";
}
var sppos = LoremIpsum.max_spp - LoremIpsum.min_spp;
var str = format.replace(/%s/i, LoremIpsum.sentences(LoremIpsum.min_spp + Math.round(Math.random() * sppos)));
paragraphs--;
while(paragraphs > 0){
str += format.replace(/%s/i, LoremIpsum.sentences(LoremIpsum.min_spp + Math.round(Math.random() * sppos)));
paragraphs--;
}
return str;
},

/* scanElm, scanelm, scan_elm
@param elm: Entry point, DOM Node to begin searching for comment nodes.
@returns Number: The number of comments replaced with text nodes.
*/
scanElm: function(elm){
var rc = 0;
for(var c = 0; c < elm.childNodes.length; c++) {
var child = elm.childNodes[c];
if(child.nodeType == 1){
LoremIpsum.scanElm(child);
}else if (child.nodeType == 8){
var matches = child.data.match(/^\s*(\d+(?:\s*-\s*\d+)?)\s+(words?|sentences?|paragraphs?)\s*$/i);
if(matches){
if(matches[1].indexOf('-') > -1){
var ms = matches[1].split(/\s*-\s*/);
matches[1] = Math.max(ms[0], Math.ceil(Math.random() * ms[1]));
}
child.parentNode.replaceChild(document.createTextNode(LoremIpsum[matches[2]](matches[1])), child);
rc++;
}
}
};
return rc;
},

/* LoremIpsum, main, init
Starts with the root node & recursively processes all comment nodes.
*/
LoremIpsum: function(){
var body = document.documentElement ? document.documentElement : document.body;
LoremIpsum.scanElm(body);
}
};
/* Convenience - aliases - etc */
LoremIpsum.main = LoremIpsum.init = LoremIpsum.LoremIpsum;
LoremIpsum.getWords = LoremIpsum.get_words = LoremIpsum.words;
LoremIpsum.getSentences = LoremIpsum.get_sentences = LoremIpsum.sentences;
LoremIpsum.getParagraphs = LoremIpsum.get_paragraphs = LoremIpsum.paragraphs;
LoremIpsum.scan_elm = LoremIpsum.scanelm = LoremIpsum.scanElm;
lorem_ipsum = loremipsum = loremIpsum = LoremIpsum;

Monday, August 4, 2008

Native Ubuntu/Gnome Alternative to Colorpix

One of the applications I've missed most when transitioning from Windows 2000/XP to Ubuntu Linux is Colorpix, a useful little color picker that grabs the pixel under your mouse and transforms it into a number of different color formats.

I tried gcolor2, but wasn't very fond of it. gcolor2 included a bunch of functionality I didn't need and not enough of the functionality I wanted. Perhaps the functionality with the largest weight on my decision making was the magnified section provided by Colorpix for making sure I get the color for the pixel I want. It might seem obvious in some instances when you're over the color you want without magnification, but alot of times I'm looking for the color of a single pixel wide line & it's nice to know I need to move left one pixel instead of moving right a few pixels before realizing I'm going the wrong direction.

My other alternative, was using colorpix through Wine like some bloggers have mentioned here and here.
The only problem I had with that is I don't always have Wine up and running, so it can take a few seconds to load.


So, since I'm trying to pickup C/C++ I decided to write my own implementation of Colorpix to run natively on my Ubuntu system.
So far it starts instantly VS waiting for a translator to load, displays the color in RGB and hexidecimal format, and magnifies an 11x11 pixel area around the pointer 2000%

I've made the source available via a project named Picksel at Google Code.