// Phonagen-web // Browser-based gerenator for phonagen outputs // Main object var phonagen = {} // Discrete distribution // The distribution object defines a discrete distribution phonagen.Distribution = function () { this.total = 0 // Total number of elements in the distribution this.items = new Map() // Map of item -> number of elements } // Add 'occur' number of 'elem' elements into the distribution phonagen.Distribution.prototype.addTo = function (elem, occur) { this.total += occur if (this.items.has(elem)) { this.items.set(elem, occur + this.items.get(elem)) } else { this.items.set(elem, occur) } } // Pick a random element from the distribution phonagen.Distribution.prototype.pickFrom = function () { var idx = Math.floor(Math.random() * this.total) var acc = 0 var pick = undefined for (var [elem, occur] of this.items) { acc += occur pick = elem if (acc > idx) { break } } return pick } // Chain generator // This generator makes an array of elements from a table of current chain -> next element phonagen.ChainGenerator = function (order) { this.order = order this.nextItems = new Map() } // Populate the table of next elements from a description phonagen.ChainGenerator.prototype.initializeFromDescription = function (description) { for (var elem of description) { var dist = new phonagen.Distribution() for (var next of elem['possible-outputs']) { dist.addTo(next.value, next.occurences) } this.nextItems.set(elem.input.toString(), dist) // note: toString() when setting and getting to be able to compare the arrays } } // Generate an array of elements phonagen.ChainGenerator.prototype.generate = function () { var next = function (cg, current, output) { var nextElem = cg.nextItems.get(current.toString()).pickFrom() if (nextElem === "") { return output } else { current.shift() current.push(nextElem) output.push(nextElem) return next(cg, current, output) } } var current = new Array() for (var i = 0; i < this.order; ++i) { current.push("") } return next(this, current, new Array()) } // Rule generator // This generator makes an array of elements from rules describing how to generate the array // Each rule is associated to a distribution of patterns indicating how to replace a rule identifier phonagen.RuleGenerator = function () { this.rules = new Map() } // Populate the rules table from a description phonagen.RuleGenerator.prototype.initializeFromDescription = function (description) { for (var rule of description) { var dist = new phonagen.Distribution() for (var pat of rule.distribution) { dist.addTo(pat.pattern, pat.occurences) } this.rules.set(rule.id, dist) } } // Generate an array of elements phonagen.RuleGenerator.prototype.generate = function () { var replacePattern = function (rules, input) { var output = new Array() for (var i of input) { if (rules.has(i)) { output = output.concat(replacePattern(rules, rules.get(i).pickFrom())) } else { output.push(i) } } return output } return replacePattern(this.rules, this.rules.get('word').pickFrom()) } // Model of the phonagen generator phonagen.model = {} // Generate a word from the generator id phonagen.generateWord = function () { var selector = document.getElementById('generator-selector') var generatorId = selector.options[selector.selectedIndex].value var generator = phonagen.model.generators.get(generatorId) document.getElementById('phonagen-output').innerHTML = generator.generate() } // Transform a list of phoneme ids to a formatted strings phonagen.formatWord = function (phonemes, phonology) { // Construct the different transcriptions var wordTranscriptions = new Map() for (var writing of phonology.transcriptions) { var word = "" for (var id of phonemes) { if (phonology.entries.has(id)) { word += phonology.entries.get(id)[writing] } } wordTranscriptions.set(writing, word) } // Construct what should be displayed var text = "

" text += "" + wordTranscriptions.get(phonology.mainTranscription) + "
" text += "Pronounciation: /" + wordTranscriptions.get("phoneme") +"/
" for (var [writing, value] of wordTranscriptions) { if ( (writing !== "phoneme") && (writing !== phonology.mainTranscription) ) { text += "Translitteration (" + writing + "): " + value + "
" } } text += "

" return text } // Make a rule generator phonagen.makeRuleBasedGenerator = function (rules, phonology) { var ruleGenerator = new phonagen.RuleGenerator() ruleGenerator.initializeFromDescription(rules) return function () { var phonemes = ruleGenerator.generate() return phonagen.formatWord(phonemes, phonology) } } // Make a chain generator phonagen.makeChainBasedGenerator = function (order, chains, phonology) { var chainGenerator = new phonagen.ChainGenerator(order) chainGenerator.initializeFromDescription(chains) return function () { var phonemes = chainGenerator.generate() return phonagen.formatWord(phonemes, phonology) } } // Parse a raw phonology phonagen.parsePhonology = function (phon) { var phonology = {} phonology.description = phon.description phonology.transcriptions = phon.transcriptions phonology.mainTranscription = phon['main-transcription'] phonology.entries = new Map() for (var entry of phon.entries) { phonology.entries.set(entry.id, entry) } return phonology } // Parse a raw generator description phonagen.parseGenerator = function (gen, phonologies) { var generator = {} generator.description = gen.description generator.phonology = phonologies.get(gen.phonology) // Type if (gen.type === "rules") { generator.generate = phonagen.makeRuleBasedGenerator(gen.rules, generator.phonology) } else if (gen.type === "chains") { generator.generate = phonagen.makeChainBasedGenerator(gen.order, gen.chains, generator.phonology) } else { generator.generate = function () { return "Error: unsupported generator type: " + gen.type } } return generator } // Draw the phonagen div phonagen.drawDiv = function (model) { var contents = '' contents += '' contents += '' contents += '
' contents += '

' document.getElementById('phonagen').innerHTML = contents } // Load the model phonagen.loadModel = function (model) { // Parse the raw model // Phonologies phonagen.model.phonologies = new Map() for (let phon of model.phonologies) { phonagen.model.phonologies.set(phon.id, phonagen.parsePhonology(phon)) } // Generators phonagen.model.generators = new Map() for (let gen of model.generators) { phonagen.model.generators.set(gen.id, phonagen.parseGenerator(gen, phonagen.model.phonologies)) } // Draw the phonagen
phonagen.drawDiv(phonagen.model) } // Loading function for Phonagen-htmljs phonagen.load = function (jsonFile) { fetch(jsonFile) .then(function(response) { return response.json() }) .then(phonagen.loadModel) } // Export info //module.exports = phonagen