UNPKG

lorem

Version:

Filler text generator compatible with Node.js, Require.js and plain-old <script/>.

349 lines (328 loc) 13.7 kB
/** * Lorem - 0.4.0 - JQuery-based Lorem Ipsum provider * * https://github.com/shyiko/lorem * * Copyright (c) 2012 Stanley Shyiko * Licensed under the MIT license. */ (function (fn) { if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { var $; try { $ = require('jquery'); } catch(ex) { } fn($, exports); // node.js } else if (typeof define === 'function' && define['amd']) { define(['jquery', 'exports'], fn); // amd } else { fn(jQuery, this.lorem = {}); // <script> tag } }(function ($, exports) { /** * @param {String} text text * @param {*} delimiter delimiter * @return {Array} array of tokens */ function tokenize(text, delimiter) { var words = text.split(delimiter), result = []; for (var i = 0; i < words.length; i++) { var word = words[i]; if (word.length > 0) { result.push(word.toLowerCase()); } } return result; } /** * @param {String} string string to capitalize * @return {String} original string with first letter capitalized */ function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } /** * @param {Number} min lower bound * @param {Number} max higher bound * @return {Number} number between min and max */ function between(min, max){ return Math.round(Math.random() * (max - min) + min); } /** * @param {Function} fn function * @param {Number} length length of target array * @return {Array} array where each element was obtained by calling fn */ function array(fn, length) { var result = []; for (var i = 0; i < length; i++) { result.push(fn()); } return result; } /** * @param {String} string string to parse * @param {*} defaultValue value to return if result is NaN or less than 1 * @return {Number} substring(1, index of '_' (if any)) */ function extractNumber(string, defaultValue) { var extensionIndex = string.indexOf('_'); if (extensionIndex < 0) { extensionIndex = string.length; } var number = parseInt(string.substr(1, extensionIndex), 10); return !isNaN(number) && number > 0 ? number : defaultValue; } /** * @param {String} string string to parse * @param {*} defaultValueOnTheLeft {this value}x{indifferent} * @param {*} defaultValueOnTheRight {indifferent}x{this value} * @return {Object} {left: valueOnTheLeft, right: valueOnTheRight} */ function extractExtension(string, defaultValueOnTheLeft, defaultValueOnTheRight) { var extensionIndex = string.indexOf('_'), result = { left: defaultValueOnTheLeft, right: defaultValueOnTheRight }; if (extensionIndex > -1) { var indexOfDelimiter = string.indexOf('x'); if (indexOfDelimiter > -1) { result.left = parseInt(string.substr(extensionIndex + 1, indexOfDelimiter), 10) || result.left; result.right = parseInt(string.substr(indexOfDelimiter + 1), 10) || result.right; } else { result.right = result.left = parseInt(string.substr(extensionIndex + 1), 10) || result.left; } } return result; } /** * @param string string to parse * @return {Number} maximum number of characters, -1 if not provided */ function extractMaximumNumberOfCharacters(string) { var extensionIndex = string.indexOf('$'); if (extensionIndex > -1) { var number = parseInt(string.substr(extensionIndex + 1), 10); if (!isNaN(number) && number > 0) { return number; } } return -1; } /** * @param {Array} array array of strings * @param {String} stringBefore string to prepped before each array element * @param {String} stringAfter string to append after each array element * @return {String} stringBefore + array.join(stringAfter + stringBefore) + stringAfter */ function join(array, stringBefore, stringAfter) { return stringBefore.concat( array.join(stringAfter.concat(stringBefore)) ).concat(stringAfter); } var defaults = { text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' + 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' + 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' + 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ' + 'At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, ' + 'quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, ' + 'qui officia deserunt mollitia animi, id est laborum et dolorum fuga. ' + 'Et harum quidem rerum facilis est et expedita distinctio. ' + 'Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, ' + 'facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.', wordDelimiter: /\s|[,.]/, numberOfSentencesPerParagraph: {min: 4, max: 7}, numberOfWordsPerSentence: {min: 4, max: 9}, imageURL: 'http://placehold.it/${w}x${h}', offlineImage: 'data:image/gif;base64,R0lGODdhAQABAIABAMzMzP///ywAAAAAAQABAAACAkQBADs=', useOfflineImage: false, /** * {String} class prefix to apply lorem to */ prefix: 'lorem_', /** * {String} class to add to all lorem-recognized elements. optional */ markerClass: 'lorem-marker', /** * {String} data- attribute to apply lorem to */ dataAttribute: 'lorem' }; defaults._tokens = tokenize(defaults.text, defaults.wordDelimiter); /** * @return {Object} copy of lorem defaults */ exports.defaults = function() { var o = {}; for (var dkey in defaults) { o[dkey] = defaults[dkey]; } return o; }; /** * @param {Object} options options. can be undefined * @return {Object} default options overridden with provided ones */ function mergeWithDefaultOnes(options) { var o = exports.defaults(); mergePropertiesIn(o, options); return o; } /** * @param target destination object * @param source object to copy properties from */ function mergePropertiesIn(target, source) { if (source) { for (var okey in source) { target[okey] = source[okey]; } if (source.hasOwnProperty('text')) { target._tokens = tokenize(target.text, target.wordDelimiter); if (target._tokens.length < 1) { throw new Error('Tokenization of text must provide at least one token'); } } } } /** * @param {Object} options options to replace default ones */ exports.overrideDefaults = function(options) { mergePropertiesIn(defaults, options); }; /** * @param {String} cls class name * @param {Object} options options * @return {*} undefined if cls doesn't start with lorem prefix, {html: value, attributes: {...}} otherwise */ function ipsum(cls, options) { var op = options.numberOfSentencesPerParagraph, os = options.numberOfWordsPerSentence, tokenFn = (function() { var tokens = options._tokens, numberOfTokens = tokens.length; return function() { return tokens[between(0, numberOfTokens - 1)]; }; }()), cutOffFn = function(string, limit) { return limit > -1 ? string.substr(0, limit) : string; }, suffix = cls.substr(0, options.prefix.length) === options.prefix ? // left in order to save backward-compatibility with releases <= 0.3.0 cls.substr(options.prefix.length) : cls, result = { attributes: {} }; switch(suffix[0]) { case 'p': // paragraph p[<number>[_<minimum number of sentences>[x<maximum number of sentences>]]] var numberOfParagraphs = extractNumber(suffix, 1), pd = extractExtension(suffix, op.min, op.max), sentenceFnP = function() { return capitalize(array(tokenFn, between(os.min, os.max)).join(' ') .concat('.')); }, paragraphFn = function() { return array(sentenceFnP, between(pd.left, pd.right)).join(' '); }; result.html = join(array(paragraphFn, numberOfParagraphs), '<p>', '</p>'); break; case 's': // sentence s[<number>[_<minimum number of words>[x<maximum number of words>]]][$<maximum number of characters>] var numberOfSentences = extractNumber(suffix, 1), sd = extractExtension(suffix, os.min, os.max), sl = extractMaximumNumberOfCharacters(suffix), sentenceFnS = function() { return capitalize(array(tokenFn, between(sd.left, sd.right)).join(' ') .concat('.')); }; result.html = cutOffFn(array(sentenceFnS, numberOfSentences).join(' '), sl); break; case 'w': // word w[<number>][$<maximum number of characters>] var numberOfWords = extractNumber(suffix, 1), wl = extractMaximumNumberOfCharacters(suffix); result.html = cutOffFn(array(tokenFn, numberOfWords).join(' '), wl); break; case 'i': // image<width>x<height> var indexOfDelimiter = suffix.indexOf('x'); if (indexOfDelimiter > -1) { var width = parseInt(suffix.substr(1, indexOfDelimiter), 10), height = parseInt(suffix.substr(indexOfDelimiter + 1), 10); if (!isNaN(width) && !isNaN(height)) { var attrs = result.attributes; if (options.useOfflineImage) { attrs.src = options.offlineImage; attrs.width = width; attrs.height = height; } else { attrs.src = options.imageURL. replace('${w}', width).replace('${h}', height); } } } } return result; } /** * @param {String} cls class name * @param {Object} options overrides for defaults. optional * @return {String} generated text/html */ exports.ipsum = function(cls, options) { var holder = ipsum(cls, mergeWithDefaultOnes(options)); if (holder) { if (holder.attributes.src) { return holder.attributes.src; } if (holder.html) { return holder.html; } } }; /** * @param $el jquery element * @param prefix class prefix * @return {String} undefined if $el has no class which starts with a prefix, class name otherwise */ function findFirstClassWithAPrefix($el, prefix) { var classes = $el.attr('class'); if (classes) { classes = classes.split(' '); for (var i = 0, prefixLength = prefix.length; i < classes.length; i++) { var cls = classes[i]; if (cls.substr(0, prefixLength) === prefix) { return cls; } } } } /** * @param {Object} $el jquery element * @param {Object} options */ function applyIpsumToElement($el, options) { var cls = $el.attr('data-' + options.dataAttribute) || findFirstClassWithAPrefix($el, options.prefix); if (cls) { var lorem = ipsum(cls, options); if (lorem.html) { $el[$el.is('input') ? 'val' : 'html'](lorem.html); } if (!$.isEmptyObject(lorem.attributes)) { $el.attr(lorem.attributes); } if (options.markerClass && !$el.hasClass(options.markerClass)) { $el.addClass(options.markerClass); } } } /** * jQuery Plugin * @param {Object} options overrides for defaults. optional * @return {Object} jquery objects for chaining */ if ($) { $.fn.ipsum = function(options) { var o = mergeWithDefaultOnes(options); return this.each(function() { var $this = $(this), $els = $('[class*="' + o.prefix + '"],[data-' + o.dataAttribute + ']', $this); applyIpsumToElement($this, o); $els.each(function(index, el) { applyIpsumToElement($(el), o); }); }); }; } }));