LibroIpsum.coffee | |
---|---|
| |
LibroIpsum is a simple placeholder text generator similar to other lorem ipsum tools. LibroIpsum differs from traditional lorem ipsum in that it can be used to generate random phrases from any source text, using character distribution analysis to generate new phrases. | |
Use LibroIpsum from Node.js, RequireJS, or directly in the browser. | |
Node.js
RequireJS
Browser | |
Usage | |
| |
do -> | |
LibroIpsumGenerates phrases using character distribution of text from a given string | class LibroIpsum |
Ignore opening and closing punctuation because of difficulty to ensure matching pair | @ignoredCharacters: [
'"'
'`'
'‘', '’'
'“', '”'
'[', ']'
'(', ')'
'{', '}'
'«', '»'
'\r', '\n'
] |
Sentence-ending characters. Used to locate phrase-starting key | @sentenceEnders: [
'.'
'!'
'?'
] |
Clause-separating characters. Used to cleanly end generated phrase | @clauseSeparators: [
@sentenceEnders...
','
';'
]
constructor: (@sourceText) ->
@frequencyLib = new FrequencyLibrary |
Return randomly generated phrase with | generate: (numberOfWords, keyLength = 6) ->
return '' unless numberOfWords > 0
workingKey = @getKey(keyLength)
currentWords = (workingKey.match(/\s/g) or []).length + 1
phrase = workingKey
while currentWords <= numberOfWords
distributedChar = @getDistributedChar(workingKey)
if distributedChar?
workingKey += distributedChar
workingKey = workingKey.slice(1)
currentWords++ if /\s/.test(distributedChar)
else
phrase = phrase.replace /\s+$/, ''
workingKey = @getKey(keyLength)
distributedChar = " #{workingKey}"
currentWords += (workingKey.match(/\s/g) or []).length + 1
phrase += distributedChar
rCleanEnd = new RegExp("[\\\\#{LibroIpsum.clauseSeparators.join('\\\\')}\\s]*$")
phrase = phrase.split(' ')
.slice(0, numberOfWords)
.join(' ')
.replace(rCleanEnd, '') + '.'
phrase |
Generates a random key from the text. Preference is given to keys which start a sentence, but if no sentence structure is detected, a random substring is generated. | getKey: (length) ->
return '' if !length
concatSentenceEnders = "\\\\#{LibroIpsum.sentenceEnders.join('\\\\')}"
rKey = new RegExp("(^[A-Z].{#{length - 1}}|[#{concatSentenceEnders}]\\s*[A-Z].{#{length - 1}})", 'gm');
keyMatch = @sourceText.match(rKey)
if keyMatch
rClean = new RegExp("^[#{concatSentenceEnders}]?\\s*(.+)")
key = keyMatch[Math.floor(Math.random() * keyMatch.length)].replace(rClean, '$1')
else
startIndex = Math.floor(Math.random() * (@sourceText.length - length))
key = @sourceText.substring(startIndex, startIndex + length)
key |
Returns a character based on the character distribution of characters following the specified key | getDistributedChar: (key) ->
unless @frequencyLib.contains(key)
foundIndex = 0
while foundIndex >= 0
foundIndex = @sourceText.indexOf(key, foundIndex)
keyMatchEnd = foundIndex + key.length
if foundIndex >= 0
foundIndex++
lookAhead = @sourceText[keyMatchEnd]
if keyMatchEnd < @sourceText.length and lookAhead not in LibroIpsum.ignoredCharacters
@frequencyLib.add(key, lookAhead)
return null unless @frequencyLib.getFrequencies(key)
@frequencyLib.randomUniformChoose(key) |
MultiSetA set in which members can appear more than once | class MultiSet |
constructor: (initialItem) ->
@cardinality = 0
@multiSetRep = {}
@add(initialChar) if initialItem | |
Returns the number of elements in this multiset (ie its cardinality). Because multisets can include duplicates, the cardinality may be larger than the number of distinct elements. | getCardinality: ->
@cardinality |
Returns the number of occurrences of a given element in the multiset | getElementCount: (target) ->
@multiSetRep[target] or 0 |
Returns a set such that every element in the multiset is in the set (but no duplicates exist) | getElementSet: ->
Object.keys(@multiSetRep) |
Adds a single element to the multiset, increasing cardinality by one | add: (item) ->
@multiSetRep[item] = 0 unless item of @multiSetRep
@multiSetRep[item]++
@cardinality++; |
Removes the target, if it is present in the multiset. Returns true if and only if it changes the multiset. Note that this method removes only a single instance of the target. Thus, assuming the target is in the multiset, this method decreases the cardinality of the multiset by one. | remove: (target) ->
return false unless target of @multiSetRep
@multiSetRep[target]--
delete @multiSetRep[target] if @multiSetRep[target] is 0
@cardinality-- |
Returns an item chosen randomly based upon the distribution of items of the multiset | randomUniformChoose: ->
choice = Math.floor(Math.random() * @cardinality)
for key, keyCount of @multiSetRep
choice -= keyCount
if choice < 0
return key |
FrequencyLibraryHelper class for tracking character distribution following keys, and choosing random character based upon distribution. | class FrequencyLibrary |
constructor: ->
@freqLibRep = {} | |
Returns number of keys in the library | size: ->
Object.keys(@freqLibRep).length |
Returns true if the target book is contained in the library | contains: (target) ->
target of @freqLibRep |
Returns a MultiSet for the specified key in the library | getFrequencies: (target) ->
return @freqLibRep[target] |
Modifies the character occurrences associated with name to include one more occurrence of element | add: (name, element) ->
ms = @getFrequencies(name)
unless ms?
ms = @freqLibRep[name] = new MultiSet
ms.add(element) |
Modifies the character occurrences associated with name to include one less occurrence of element. If this removal results in no elements being associated with name, name is removed from the library. Returns true if and only if removal was successful. | remove: (name, element) ->
return false unless @contains(target)
ms = @getFrequencies(name)
cardBefore = ms.getCardinality()
ms.remove(element)
cardAfter = ms.getCardinality()
freqLibRep.remove(name) if ms.getCardinality() is 0
cardBefore - cardAfter is 1 |
Returns a random character, chosen from the same distribution as the characters appear in the text. For example, if 15% of the characters following "the" are 'i', then this method should return an 'i' 15% of the time. | randomUniformChoose: (name) ->
ms = @getFrequencies(name)
return 0 unless ms?
ms.randomUniformChoose() |
Object.keys polyfill (ES5) | Object.keys ?= (obj) ->
key for key of obj when obj.hasOwnProperty(key) |
Expose LibroIpsum | |
Add to global object | this.LibroIpsum = LibroIpsum |
Expose to Node.js | module.exports = LibroIpsum if module?.exports? |
Define with RequireJS | if typeof define is 'function' and define.amd?
define 'LibroIpsum', -> LibroIpsum
|