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;

3 comments:

Anonymous said...

Awesome !
I was wondering if you were ever going to update this script.

Zoltan Hawyluk said...

Have used it for years, but just wanted to say for the record: This script rocks! I have used it to mock up web page layouts and it is awesome to generate data for web applications as well. Saves a lot of time from cutting and pasting random text or coming up with my own. Many thanks!

Joe Kovar said...

I ended up packaging this into an Opera extension. (Opera 11+)

https://addons.labs.opera.com/addons/extensions/details/auto-ipsum/