Class: #structure#
v.2015-05-30
Story.prototype.appendJSON = function() {\n\tvar JSONtoAppend = tale.lookup("tags", "JSON");\n\tif(!JSONtoAppend.length) return;\n\n\tfor(i in JSONtoAppend){\n\t\tvar newJSON = JSON.parse(JSONtoAppend[i].text);\n\t\t$.extend(this.data, newJSON);\n\t}\n\tconsole.log("appended");\n}\n\nfunction Story(){\n\tvar grammars = tale.lookup("tags", "grammar", "title");\n\tthis.data = {};\n\n\tfor(var i in grammars){\n\t\tif(grammars[i] == undefined) continue;\n\t\t// Passage names become grammar names, Passage text becomes grammar text. \n\t\tthis.data[grammars[i].title] = grammars[i].text.split('\sn');\n\t}\n\n\tthis.appendJSON();\n\n\tconsole.log("Story: ", this);\n}\nStory.prototype.constructor = Story;\n\n// Append this to the tale object because I don't know where else to put it.\nTale.prototype.story = new Story();\n\nStory.prototype.toHTML = function() {\n\tvar output = [];\n\tvar tab = " ";\n\tvar beg = '\sn' + tab + "\s"<span class=\s"grammarContents\s">{{{"\n\tvar end = "}}}</span>\s""\n\n\tfor(var i in this.data){\n\t\tvar gram = "<span class=\s"grammarTitle\s">\s"" + i + "\s"</span>: [";\n\t\tgram += beg + this.data[i].join(end + ',' + beg) + end;\n\t\tgram += "]";\n\t\toutput.push(gram);\n\t}\n\treturn "{\sn" + output.join(",\sn") + "\sn}";\n}\n\nTale.prototype.JSONtoTwee = function() {\n\tvar JSONtoConvert = tale.lookup("tags", "JSON");\n\tvar combinedJSON = ""\n\n\tfor (var i in JSONtoConvert){\n\t\tcombinedJSON += JSONtoConvert[i].text;\n\t}\n\n\t// Note the {{{}}} delimiters in textPost. This is intended for display in Twine, so\n\t// if you're just running these raw they aren't necessary.\n\tvar regex = {titlesPre: /\st"(.+)": \s[/g, titlesPost: "<br>:: $1 [grammar]",\n\t\t\t\t textPre: /\st*"(.+)",*(?:\sn\st)?(?:\s],)*\sn/g, textPost: "{{{$1}}}<br>"}\n\n\tvar tweeOutput = combinedJSON.replace(regex.titlesPre, regex.titlesPost);\n\ttweeOutput = tweeOutput.replace(regex.textPre, regex.textPost);\n\ttweeOutput = tweeOutput.replace(/({\sn)|(]\sn})/g, "")\n\n\treturn tweeOutput;\n}
window.tracery = {\n utilities : {}\n};\n\n(function () {/**\n * @author Kate Compton\n */\n\nfunction inQuotes(s) {\n return '"' + s + '"';\n};\n\nfunction parseAction(action) {\n return action;\n};\n\n// tag format\n// a thing to expand, plus actions\n\nfunction parseTag(tag) {\n var errors = [];\n var prefxns = [];\n var postfxns = [];\n\n var lvl = 0;\n var start = 0;\n\n var inPre = true;\n\n var symbol,\n mods;\n\n function nonAction(end) {\n if (start !== end) {\n var section = tag.substring(start, end);\n if (!inPre) {\n errors.push("multiple possible expansion symbols in tag!" + tag);\n } else {\n inPre = false;\n var split = section.split(".");\n symbol = split[0];\n mods = split.slice(1, split.length);\n }\n\n }\n start = end;\n };\n\n for (var i = 0; i < tag.length; i++) {\n var c = tag.charAt(i);\n\n switch(c) {\n case '[':\n if (lvl === 0) {\n nonAction(i);\n }\n\n lvl++;\n break;\n case ']':\n lvl--;\n if (lvl === 0) {\n var section = tag.substring(start + 1, i);\n if (inPre)\n prefxns.push(parseAction(section));\n else\n postfxns.push(parseAction(section));\n start = i + 1;\n }\n break;\n\n default:\n if (lvl === 0) {\n\n }\n break;\n\n }\n }\n nonAction(i);\n\n if (lvl > 0) {\n var error = "Too many '[' in rule " + inQuotes(tag);\n errors.push(error);\n\n }\n\n if (lvl < 0) {\n var error = "Too many ']' in rule " + inQuotes(tag);\n errors.push(error);\n\n }\n\n return {\n preActions : prefxns,\n postActions : postfxns,\n symbol : symbol,\n mods : mods,\n raw : tag,\n errors : errors,\n };\n};\n\n// Split a rule into sections\nfunction parseRule(rule) {\n var sections = [];\n var errors = [];\n if (!( typeof rule == 'string' || rule instanceof String)) {\n errors.push("Cannot parse non-string rule " + rule);\n sections.errors = errors;\n return sections;\n }\n\n if (rule.length === 0) {\n return [];\n }\n\n var lvl = 0;\n var start = 0;\n var inTag = false;\n\n function createSection(end) {\n var section = rule.substring(start, end);\n if (section.length > 0) {\n if (inTag)\n sections.push(parseTag(section));\n else\n sections.push(section);\n }\n inTag = !inTag;\n start = end + 1;\n\n }\n\n for (var i = 0; i < rule.length; i++) {\n var c = rule.charAt(i);\n\n switch(c) {\n case '[':\n lvl++;\n break;\n case ']':\n lvl--;\n break;\n case '#':\n if (lvl === 0) {\n createSection(i);\n }\n break;\n default:\n break;\n\n }\n\n }\n\n if (lvl > 0) {\n var error = "Too many '[' in rule " + inQuotes(rule);\n errors.push(error);\n\n }\n\n if (lvl < 0) {\n var error = "Too many ']' in rule " + inQuotes(rule);\n errors.push(error);\n\n }\n\n if (inTag) {\n var error = "Odd number of '#' in rule " + inQuotes(rule);\n errors.push(error);\n }\n\n createSection(rule.length);\n sections.errors = errors;\n return sections;\n};\n\nfunction testParse(rule, shouldFail) {\n console.log("-------");\n console.log("Test parse rule: " + inQuotes(rule) + " " + shouldFail);\n var parsed = parseRule(rule);\n if (parsed.errors && parsed.errors.length > 0) {\n for (var i = 0; i < parsed.errors.length; i++) {\n console.log(parsed.errors[i]);\n }\n }\n \n\n}\n\nfunction testParseTag(tag, shouldFail) {\n console.log("-------");\n console.log("Test parse tag: " + inQuotes(tag) + " " + shouldFail);\n var parsed = parseTag(tag);\n if (parsed.errors && parsed.errors.length > 0) {\n for (var i = 0; i < parsed.errors.length; i++) {\n console.log(parsed.errors[i]);\n }\n }\n}\n\ntracery.testParse = testParse;\ntracery.testParseTag = testParseTag;\ntracery.parseRule = parseRule;\ntracery.parseTag = parseTag;\n\n\nfunction spacer(size) {\n var s = "";\n for (var i = 0; i < size * 3; i++) {\n s += " ";\n }\n return s;\n}\n\n/* Simple JavaScript Inheritance\n * By John Resig http://ejohn.org/\n * MIT Licensed.\n */\n\nfunction extend(destination, source) {\n for (var k in source) {\n if (source.hasOwnProperty(k)) {\n destination[k] = source[k];\n }\n }\n return destination;\n}\n\n// Inspired by base2 and Prototype\n(function() {\n var initializing = false,\n fnTest = /xyz/.test(function() { xyz;\n }) ? /\sb_super\sb/ : /.*/;\n\n // The base Class implementation (does nothing)\n this.Class = function() {\n };\n\n // Create a new Class that inherits from this class\n Class.extend = function(prop) {\n var _super = this.prototype;\n\n // Instantiate a base class (but only create the instance,\n // don't run the init constructor)\n initializing = true;\n var prototype = new this();\n initializing = false;\n\n // Copy the properties over onto the new prototype\n for (var name in prop) {\n // Check if we're overwriting an existing function\n prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) {\n return function() {\n var tmp = this._super;\n\n // Add a new ._super() method that is the same method\n // but on the super-class\n this._super = _super[name];\n\n // The method only need to be bound temporarily, so we\n // remove it when we're done executing\n var ret = fn.apply(this, arguments);\n this._super = tmp;\n\n return ret;\n };\n })(name, prop[name]) : prop[name];\n }\n\n // The dummy class constructor\n function Class() {\n // All construction is actually done in the init method\n if (!initializing && this.init)\n this.init.apply(this, arguments);\n }\n\n // Populate our constructed prototype object\n Class.prototype = prototype;\n\n // Enforce the constructor to be what we expect\n Class.prototype.constructor = Class;\n\n // And make this class extendable\n Class.extend = arguments.callee;\n\n return Class;\n };\n})();\n\n/**\n * @author Kate\n */\n\nvar Rule = function(raw) {\n this.raw = raw;\n this.sections = parseRule(raw);\n\n};\n\nRule.prototype.getParsed = function() {\n if (!this.sections)\n this.sections = parseRule(raw);\n\n return this.sections;\n};\n\nRule.prototype.toString = function() {\n return this.raw;\n};\n\nRule.prototype.toJSONString = function() {\n return this.raw;\n};\n\n/**\n * @author Kate\n */\n\nvar RuleWeighting = Object.freeze({\n RED : 0,\n GREEN : 1,\n BLUE : 2\n});\n\nvar RuleSet = function(rules) {\n // is the rules obj an array? A RuleSet, or a string?\n if (rules.constructor === Array) {\n // make a copy\n rules = rules.slice(0, rules.length);\n } else if (rules.prototype === RuleSet) {\n // clone\n } else if ( typeof rules == 'string' || rules instanceof String) {\n var args = Array.prototype.slice.call(arguments);\n rules = args;\n } else {\n console.log(rules);\n throw ("creating ruleset with unknown object type!");\n }\n\n // create rules and their use counts\n\n this.rules = rules;\n this.parseAll();\n\n this.uses = [];\n this.startUses = [];\n this.totalUses = 0;\n for (var i = 0; i < this.rules.length; i++) {\n this.uses[i] = 0;\n this.startUses[i] = this.uses[i];\n this.totalUses += this.uses[i];\n }\n\n};\n\n//========================================================\n// Iterating over rules\n\nRuleSet.prototype.parseAll = function(fxn) {\n for (var i = 0; i < this.rules.length; i++) {\n if (this.rules[i].prototype !== Rule)\n this.rules[i] = new Rule(this.rules[i]);\n }\n\n};\n\n//========================================================\n// Iterating over rules\n\nRuleSet.prototype.mapRules = function(fxn) {\n return this.rules.map(function(rule, index) {\n return fxn(rule, index);\n });\n};\n\nRuleSet.prototype.applyToRules = function(fxn) {\n for (var i = 0; i < this.rules.length; i++) {\n fxn(this.rules[i], i);\n }\n};\n//========================================================\nRuleSet.prototype.get = function() {\n var index = this.getIndex();\n\n return this.rules[index];\n};\n\nRuleSet.prototype.getRandomIndex = function() {\n return Math.floor(this.uses.length * Math.random());\n};\n\nRuleSet.prototype.getIndex = function() {\n // Weighted distribution\n // Imagine a bar of length 1, how to divide the length\n // s.t. a random dist will result in the dist we want?\n\n var index = this.getRandomIndex();\n // What if the uses determine the chance of rerolling?\n\n var median = this.totalUses / this.uses.length;\n\n var count = 0;\n while (this.uses[index] > median && count < 20) {\n index = this.getRandomIndex();\n count++;\n }\n\n // reroll more likely if index is too much higher\n\n return index;\n};\n\nRuleSet.prototype.decayUses = function(pct) {\n this.totalUses = 0;\n for (var i = 0; i < this.uses; i++) {\n\n this.uses[index] *= 1 - pct;\n this.totalUses += this.uses[index];\n }\n};\n\nRuleSet.prototype.testRandom = function() {\n console.log("Test random");\n var counts = [];\n for (var i = 0; i < this.uses.length; i++) {\n counts[i] = 0;\n }\n\n var testCount = 10 * this.uses.length;\n for (var i = 0; i < testCount; i++) {\n\n var index = this.getIndex();\n this.uses[index] += 1;\n\n counts[index]++;\n this.decayUses(.1);\n }\n\n for (var i = 0; i < this.uses.length; i++) {\n console.log(i + ":\st" + counts[i] + " \st" + this.uses[i]);\n }\n};\n\nRuleSet.prototype.getSaveRules = function() {\n var jsonRules = this.rules.map(function(rule) {\n return rule.toJSONString();\n });\n\n return jsonRules;\n};\n\n/**\n * @author Kate Compton\n */\n\nvar Action = function(node, raw) {\n\n this.node = node;\n this.grammar = node.grammar;\n this.raw = raw;\n\n};\n\nAction.prototype.activate = function() {\n\n var node = this.node;\n node.actions.push(this);\n\n // replace any hashtags\n this.amended = this.grammar.flatten(this.raw);\n\n var parsed = parseTag(this.amended);\n var subActionRaw = parsed.preActions;\n if (subActionRaw && subActionRaw.length > 0) {\n this.subactions = subActionRaw.map(function(action) {\n return new Action(node, action);\n });\n\n }\n\n if (parsed.symbol) {\n var split = parsed.symbol.split(":");\n\n if (split.length === 2) {\n this.push = {\n symbol : split[0],\n\n // split into multiple rules\n rules : split[1].split(","),\n };\n // push\n node.grammar.pushRules(this.push.symbol, this.push.rules);\n\n } else\n throw ("Unknown action: " + parsed.symbol);\n }\n\n if (this.subactions) {\n for (var i = 0; i < this.subactions.length; i++) {\n this.subactions[i].activate();\n }\n }\n\n};\n\nAction.prototype.deactivate = function() {\n if (this.subactions) {\n for (var i = 0; i < this.subactions.length; i++) {\n this.subactions[i].deactivate();\n }\n }\n\n if (this.push) {\n this.node.grammar.popRules(this.push.symbol, this.push.rules);\n }\n};\n\n/**\n * @author Kate Compton\n */\n\nvar isConsonant = function(c) {\n c = c.toLowerCase();\n switch(c) {\n case 'a':\n return false;\n case 'e':\n return false;\n case 'i':\n return false;\n case 'o':\n return false;\n case 'u':\n return false;\n\n }\n return true;\n};\n\nfunction endsWithConY(s) {\n if (s.charAt(s.length - 1) === 'y') {\n return isConsonant(s.charAt(s.length - 2));\n }\n return false;\n};\n\nvar universalModifiers = {\n capitalizeAll : function(s) {\n return s.replace(/(?:^|\ss)\sS/g, function(a) {\n return a.toUpperCase();\n });\n\n },\n\n capitalize : function(s) {\n return s.charAt(0).toUpperCase() + s.slice(1);\n\n },\n\n inQuotes : function(s) {\n return '"' + s + '"';\n },\n\n comma : function(s) {\n var last = s.charAt(s.length - 1);\n if (last === ",")\n return s;\n if (last === ".")\n return s;\n if (last === "?")\n return s;\n if (last === "!")\n return s;\n return s + ",";\n },\n\n beeSpeak : function(s) {\n // s = s.replace("s", "zzz");\n\n s = s.replace(/s/, 'zzz');\n return s;\n },\n\n a : function(s) {\n if (!isConsonant(s.charAt()))\n return "an " + s;\n return "a " + s;\n\n },\n\n s : function(s) {\n\n var last = s.charAt(s.length - 1);\n\n switch(last) {\n case 'y':\n\n // rays, convoys\n if (!isConsonant(s.charAt(s.length - 2))) {\n return s + "s";\n }\n // harpies, cries\n else {\n return s.slice(0, s.length - 1) + "ies";\n }\n break;\n\n // oxen, boxen, foxen\n case 'x':\n return s.slice(0, s.length - 1) + "en";\n case 'z':\n return s.slice(0, s.length - 1) + "es";\n case 'h':\n return s.slice(0, s.length - 1) + "es";\n\n default:\n return s + "s";\n };\n\n },\n\n ed : function(s) {\n\n var index = s.indexOf(" ");\n var s = s;\n var rest = "";\n if (index > 0) {\n rest = s.substring(index, s.length);\n s = s.substring(0, index);\n\n }\n\n var last = s.charAt(s.length - 1);\n\n switch(last) {\n case 'y':\n\n // rays, convoys\n if (isConsonant(s.charAt(s.length - 2))) {\n return s.slice(0, s.length - 1) + "ied" + rest;\n\n }\n // harpies, cries\n else {\n return s + "ed" + rest;\n }\n break;\n case 'e':\n return s + "d" + rest;\n\n break;\n\n default:\n return s + "ed" + rest;\n };\n }\n};\n/**\n * @author Kate Compton\n */\n\n// A tracery expansion node\nvar nodeCount = 0;\n\nvar ExpansionNode = Class.extend({\n init : function() {\n this.depth = 0;\n this.id = nodeCount;\n nodeCount++;\n this.childText = "[[UNEXPANDED]]";\n },\n\n setParent : function(parent) {\n if (parent) {\n this.depth = parent.depth + 1;\n this.parent = parent;\n this.grammar = parent.grammar;\n }\n },\n\n expand : function() {\n // do nothing\n return "???";\n },\n\n expandChildren : function() {\n\n if (this.children) {\n this.childText = "";\n for (var i = 0; i < this.children.length; i++) {\n this.children[i].expand();\n this.childText += this.children[i].finalText;\n }\n this.finalText = this.childText;\n }\n\n },\n\n createChildrenFromSections : function(sections) {\n var root = this;\n this.children = sections.map(function(section) {\n\n if ( typeof section == 'string' || section instanceof String) {\n // Plaintext\n return new TextNode(root, section);\n } else {\n return new TagNode(root, section);\n }\n });\n }\n});\n\nvar RootNode = ExpansionNode.extend({\n init : function(grammar, rawRule) {\n this._super();\n this.grammar = grammar;\n this.parsedRule = parseRule(rawRule);\n },\n\n expand : function() {\n var root = this;\n this.createChildrenFromSections(this.parsedRule);\n\n // expand the children\n this.expandChildren();\n },\n});\n\nvar TagNode = ExpansionNode.extend({\n init : function(parent, parsedTag) {\n this._super();\n\n if (!(parsedTag !== null && typeof parsedTag === 'object')) {\n if ( typeof parsedTag == 'string' || parsedTag instanceof String) {\n console.warn("Can't make tagNode from unparsed string!");\n parsedTag = parseTag(parsedTag);\n\n } else {\n console.log("Unknown tagNode input: ", parsedTag);\n throw ("Can't make tagNode from strange tag!");\n\n }\n }\n\n this.setParent(parent);\n $.extend(this, parsedTag);\n },\n\n expand : function() {\n if (tracery.outputExpansionTrace)\n console.log(r.sections);\n\n this.rule = this.grammar.getRule(this.symbol);\n\n this.actions = [];\n\n // Parse the rule if it hasn't been already\n this.createChildrenFromSections(this.rule.getParsed());\n\n // Do any pre-expansion actions!\n for (var i = 0; i < this.preActions.length; i++) {\n var action = new Action(this, this.preActions[i]);\n action.activate();\n }\n\n // Map each child section to a node\n if (!this.rule.sections)\n console.log(this.rule);\n\n this.expandChildren();\n\n for (var i = 0; i < this.actions.length; i++) {\n\n this.actions[i].deactivate();\n }\n\n this.finalText = this.childText;\n for (var i = 0; i < this.mods.length; i++) {\n this.finalText = this.grammar.applyMod(this.mods[i], this.finalText);\n }\n\n },\n\n toLabel : function() {\n return this.symbol;\n },\n toString : function() {\n return "TagNode '" + this.symbol + "' mods:" + this.mods + ", preactions:" + this.preActions + ", postactions" + this.postActions;\n }\n});\n\nvar TextNode = ExpansionNode.extend({\n isLeaf : true,\n init : function(parent, text) {\n this._super();\n\n this.setParent(parent);\n\n this.text = text;\n\n this.finalText = text;\n },\n expand : function() {\n // do nothing\n },\n\n toLabel : function() {\n return this.text;\n }\n});\n\n/**\n * @author Kate Compton\n */\n\nfunction Symbol(grammar, key) {\n this.grammar = grammar;\n this.key = key;\n this.currentRules = undefined;\n this.ruleSets = [];\n\n};\n\nSymbol.prototype.loadFrom = function(rules) {\n\n rules = this.wrapRules(rules);\n this.baseRules = rules;\n\n this.ruleSets.push(rules);\n this.currentRules = this.ruleSets[this.ruleSets.length - 1];\n\n};\n\n//========================================================\n// Iterating over rules\n\nSymbol.prototype.mapRules = function(fxn) {\n\n return this.currentRules.mapRules(fxn);\n};\n\nSymbol.prototype.applyToRules = function(fxn) {\n this.currentRules.applyToRules(fxn);\n};\n\n//==================================================\n// Rule pushpops\nSymbol.prototype.wrapRules = function(rules) {\n if (rules.prototype !== RuleSet) {\n if (Array.isArray(rules)) {\n return new RuleSet(rules);\n } else if ( typeof rules == 'string' || rules instanceof String) {\n return new RuleSet(rules);\n } else {\n throw ("Unknown rules type: " + rules);\n }\n }\n // already a ruleset\n return rules;\n};\n\nSymbol.prototype.pushRules = function(rules) {\n rules = this.wrapRules(rules);\n this.ruleSets.push(rules);\n this.currentRules = this.ruleSets[this.ruleSets.length - 1];\n};\n\nSymbol.prototype.popRules = function() {\n var exRules = this.ruleSets.pop();\n\n if (this.ruleSets.length === 0) {\n //console.warn("No more rules for " + this + "!");\n }\n this.currentRules = this.ruleSets[this.ruleSets.length - 1];\n};\n\n// Clear everything and set the rules\nSymbol.prototype.setRules = function(rules) {\n\n rules = this.wrapRules(rules);\n this.ruleSets = [rules];\n this.currentRules = rules;\n\n};\n\nSymbol.prototype.addRule = function(rule) {\n this.currentRules.addRule(seed);\n};\n\n//========================================================\n// selection\n\nSymbol.prototype.select = function() {\n this.isSelected = true;\n\n};\n\nSymbol.prototype.deselect = function() {\n this.isSelected = false;\n};\n\n//==================================================\n// Getters\n\nSymbol.prototype.getRule = function(seed) {\n return this.currentRules.get(seed);\n};\n\n//==================================================\n\nSymbol.prototype.toString = function() {\n return this.key + ": " + this.currentRules + "(overlaying " + (this.ruleSets.length - 1) + ")";\n};\nSymbol.prototype.toJSON = function() {\n\n var rules = this.baseRules.rules.map(function(rule) {\n return '"' + rule.raw + '"';\n });\n return '"' + this.key + '"' + ": [" + rules.join(", ") + "]";\n};\n\nSymbol.prototype.toHTML = function(useSpans) {\n var keySpan = '"' + this.key + '"';\n if (useSpans)\n keySpan = "<span class='symbol symbol_" + this.key + "'>" + keySpan + "</span>";\n\n var rules = this.baseRules.rules.map(function(rule) {\n var s = '"' + rule.raw + '"';\n if (useSpans)\n s = "<span class='rule'>" + s + "</span>";\n return s;\n });\n return keySpan + ": [" + rules.join(", ") + "]";\n};\n\n/**\n * @author Kate Compton\n */\n\nfunction Grammar() {\n this.clear();\n};\n\nGrammar.prototype.clear = function() {\n // Symbol library\n this.symbols = {};\n \n this.errors = [];\n \n // Modifier library\n this.modifiers = {};\n\n // add the universal mods\n for (var mod in universalModifiers) {\n if (universalModifiers.hasOwnProperty(mod))\n this.modifiers[mod] = universalModifiers[mod];\n }\n};\n//========================================================\n// Loading\n\nGrammar.prototype.loadFrom = function(obj) {\n var symbolSrc;\n\n this.clear();\n\n if (obj.symbols !== undefined) {\n symbolSrc = obj.symbols;\n } else {\n symbolSrc = obj;\n }\n\n // get all json keys\n var keys = Object.keys(symbolSrc);\n\n this.symbolNames = [];\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n this.symbolNames.push(key);\n\n this.symbols[key] = new Symbol(this, key);\n this.symbols[key].loadFrom(symbolSrc[key]);\n }\n\n};\n\nGrammar.prototype.toHTML = function(useSpans) {\n // get all json keys\n var keys = Object.keys(this.symbols);\n\n this.symbolNames = [];\n\n var lines = [];\n\n var count = 0;\n for (var i = 0; i < keys.length; i++) {\n\n var key = keys[i];\n var symbol = this.symbols[key];\n\n if (symbol && symbol.baseRules) {\n\n lines.push(" " + this.symbols[key].toHTML(useSpans));\n\n }\n };\n\n var s;\n s = lines.join(",</p><p>");\n s = "{<p>" + s + "</p>}";\n return s;\n};\n\nGrammar.prototype.toJSON = function() {\n // get all json keys\n var keys = Object.keys(this.symbols);\n\n this.symbolNames = [];\n\n var lines = [];\n\n var count = 0;\n for (var i = 0; i < keys.length; i++) {\n\n var key = keys[i];\n var symbol = this.symbols[key];\n\n if (symbol && symbol.baseRules) {\n\n lines.push(" " + this.symbols[key].toJSON());\n\n }\n };\n\n var s;\n s = lines.join(",\sn");\n s = "{\sn" + s + "\sn}";\n return s;\n};\n\n//========================================================\n// selection\n\nGrammar.prototype.select = function() {\n this.isSelected = true;\n};\n\nGrammar.prototype.deselect = function() {\n this.isSelected = false;\n};\n\n//========================================================\n// Iterating over symbols\n\nGrammar.prototype.mapSymbols = function(fxn) {\n var symbols = this.symbols;\n return this.symbolNames.map(function(name) {\n return fxn(symbols[name], name);\n });\n};\n\nGrammar.prototype.applyToSymbols = function(fxn) {\n for (var i = 0; i < this.symbolNames.length; i++) {\n var key = this.symbolNames[i];\n fxn(this.symbols[key], key);\n }\n};\n\n//========================================================\nGrammar.prototype.addOrGetSymbol = function(key) {\n if (this.symbols[key] === undefined)\n this.symbols[key] = new Symbol(key);\n\n return this.symbols[key];\n};\n\nGrammar.prototype.pushRules = function(key, rules) {\n var symbol = this.addOrGetSymbol(key);\n symbol.pushRules(rules);\n};\n\nGrammar.prototype.popRules = function(key, rules) {\n var symbol = this.addOrGetSymbol(key);\n var popped = symbol.popRules();\n\n if (symbol.ruleSets.length === 0) {\n // remove symbol\n this.symbols[key] = undefined;\n }\n};\n\nGrammar.prototype.applyMod = function(modName, text) {\n if (!this.modifiers[modName]) {\n console.log(this.modifiers);\n throw ("Unknown mod: " + modName);\n }\n return this.modifiers[modName](text);\n};\n\n//============================================================\nGrammar.prototype.getRule = function(key, seed) {\n var symbol = this.symbols[key];\n if (symbol === undefined) {\n var r = new Rule("{{" + key + "}}");\n\n r.error = "Missing symbol " + key;\n return r;\n }\n\n var rule = symbol.getRule();\n if (rule === undefined) {\n var r = new Rule("[" + key + "]");\n console.log(r.sections);\n r.error = "Symbol " + key + " has no rule";\n return r;\n }\n\n return rule;\n};\n\n//============================================================\n// Expansions\nGrammar.prototype.expand = function(raw) {\n\n // Start a new tree\n var root = new RootNode(this, raw);\n\n root.expand();\n\n return root;\n};\n\nGrammar.prototype.flatten = function(raw) {\n\n // Start a new tree\n var root = new RootNode(this, raw);\n\n root.expand();\n\n return root.childText;\n};\n\n//===============\n\nGrammar.prototype.analyze = function() {\n this.symbolNames = [];\n for (var name in this.symbols) {\n if (this.symbols.hasOwnProperty(name)) {\n this.symbolNames.push(name);\n }\n }\n\n // parse every rule\n\n for (var i = 0; i < this.symbolNames.length; i++) {\n var key = this.symbolNames[i];\n var symbol = this.symbols[key];\n // parse all\n for (var j = 0; j < symbol.baseRules.length; j++) {\n var rule = symbol.baseRules[j];\n rule.parsed = tracery.parse(rule.raw);\n // console.log(rule);\n\n }\n }\n\n};\n\nGrammar.prototype.selectSymbol = function(key) {\n console.log(this);\n var symbol = this.get(key);\n};\n/**\n * @author Kate Compton\n\n */\n\ntracery.createGrammar = function(obj) {\n var grammar = new Grammar();\n grammar.loadFrom(obj);\n return grammar;\n};\n\ntracery.test = function() {\n\n console.log("==========================================");\n console.log("test tracery");\n\n // good\n tracery.testParse("", false);\n tracery.testParse("fooo", false);\n tracery.testParse("####", false);\n tracery.testParse("#[]#[]##", false);\n tracery.testParse("#someSymbol# and #someOtherSymbol#", false);\n tracery.testParse("#someOtherSymbol.cap.pluralize#", false);\n tracery.testParse("#[#do some things#]symbol.mod[someotherthings[and a function]]#", false);\n tracery.testParse("#[fxn][fxn][fxn[subfxn]]symbol[[fxn]]#", false);\n tracery.testParse("#[fxn][#fxn#][fxn[#subfxn#]]symbol[[fxn]]#", false);\n tracery.testParse("#hero# ate some #color# #animal.s#", false);\n tracery.testParseTag("[action]symbol.mod1.mod2[postAction]", false);\n\n // bad\n tracery.testParse("#someSymbol# and #someOtherSymbol", true);\n tracery.testParse("#[fxn][fxn][fxn[subfxn]]symbol[fxn]]#", true);\n\n // bad\n tracery.testParseTag("stuff[action]symbol.mod1.mod2[postAction]", true);\n tracery.testParseTag("[action]symbol.mod1.mod2[postAction]stuff", true);\n\n tracery.testParse("#hero# ate some #color# #animal.s#", true);\n tracery.testParse("#[#setPronouns#][#setOccupation#][hero:#name#]story#", true);\n\n};\n \n})();
<<if !tale.grammar>>\n\t<<if tracery>>\n\t\t<<set tale.grammar = tracery.createGrammar(tale.story.data)>>\n\t\t<<print console.log("grammar: ", tale.grammar)>>\n\t<<else>>\n\t\t<<print console.log("grammar instantiation failed")>>\n\t<<endif>>\n<<endif>>
/%\n\nUses the parameter if one was passed. Then, checks for the $symbol variable. If neither is present, uses "origin". Clears $symbol at the end.\n\n%/<<if parameter(0)>><<set $symbol to parameter(0)>><<else>><<if $symbol>><<else>><<set $symbol to "origin">><<endif>><<endif>><<print console.log("trace " + $symbol)>><<print tale.grammar.flatten("#" + $symbol + "#")>><<forget $symbol>>
# [[animals|https://en.wikipedia.org/wiki/List_of_animal_names]]\n# [[dnd classes|http://diceofdoom.com/blog/2009/08/table-of-all-dd-4e-character-classes/]], [[2|http://www.dandwiki.com/wiki/DnD_NPC_Classes]], [[oriental|http://alcyius.com/dndtools/classes/rulebook/oriental-adventures--96/index.html]]\n# [[professions|http://lingolex.com/jobfr.htm]]\n# [[adjectives|https://www.powerthesaurus.org/scrofulous]]\n# [[rooms|http://www.enchantedlearning.com/wordlist/rooms.shtml]]\n# [[titles|https://en.wikipedia.org/wiki/Forms_of_address_in_the_United_Kingdom]]\n\n[[Back|Start]]
<<traceryInit>>
body {\n\tbackground-color: #000;\n\tcolor: #fff;\n\tfont-family: Verdana,sans-serif;\n\tfont-size: 62.5%;\n\tmargin: 4em 15% 5% 5em;\n}\n#sidebar {\n\tleft: 7.5em;\n\tmargin: 0;\n\tpadding: 0 1em 0 0;\n\tfont: bold 13px monospace;\n}\n#sidebar ul {\n\tpadding: 0;\n}\n#sidebar li {\n\tcolor: #333;\n\ttext-align: right;\n\tbackground-repeat: no-repeat;\n\tmargin-bottom: 1em;\n\tline-height: 1.4em;\n\tlist-style: none;\n}\n#sidebar li a {\n\tcolor: #333;\n\ttext-decoration: none;\n}\n#sidebar li a:hover, #sidebar #title a:hover, #snapback:hover, #restart:hover {\n\tcolor: #fff;\n\tcursor: pointer;\n\ttext-decoration: none;\n}\n#sidebar #title {\n\tfont-size: 150%;\n}\n#sidebar #title, #sidebar #title:hover, #sidebar #title a {\n\tcolor: #7FA062;\n}\n#sidebar #storySubtitle {\n\tfont-size: 75%;\n}\n#storyAuthor {\n\tfont-size: 70%;\n}\n#sidebar #storyMenu {\n\tline-height: 2.5em;\n\tmargin-bottom: .5em;\n\tcolor: #7FA062;\n\tcursor: auto;\n}\n#sidebar #credits {\n\tpadding-top: 2em;\n\tfont-weight: normal;\n\tfont-size: 80%;\n}\n#sidebar #credits:hover {\n\tcolor: #333;\n}\n#sidebar #credits a {\n\ttext-decoration: none;\n}\n#passages {\n\tborder-left: 1px solid #333;\n\tpadding-left: 1.5em;\n}\n.menu {\n\tbackground-color: #343434;\n\tcolor: #fff;\n\topacity: .9;\n\tborder: 1px solid #fff;\n\ttext-align: left;\n\tfont: 1.1em monospace;\n\tline-height: 2em;\n}\n.menu div {\n\tpadding: 0 .4em;\n}\n.menu div:hover {\n\tcursor: pointer;\n\tbackground-color: #fff;\n\tcolor: #343434;\n}\n.passage {\n\tfont-size: 16px;\n\tline-height: 150%;\n\tfont-family: monospace;\n\tmargin-bottom: 2em;\n\ttext-align: center;\n\n}\n.passage a {\n\tfont-weight: bold;\n\tcolor: #599096;\n\ttext-decoration: none;\n}\n.passage a:hover {\n\tcolor: #C4FBF9;\n}\n.content > ul {\n\tpadding-top: 1.3em;\n}\n.passage ul, .passage ol {\n\tmargin-left: .5em;\n\tpadding-left: 1.5em;\n}\n.passage li {\n\tmargin-right: 6em;\n}\n.passage table {\n\tborder-collapse: collapse;\n\tfont-size: 100%;\n\tmargin: .8em 1.0em;\n}\n.passage th,.passage td,.passage tr,.passage caption {\n\tpadding: 3px;\n}\n.passage hr {\n\theight: 1px;\n}\n.passage center {\n\tmax-width:50%;\n\tmargin:auto;\n}\n.marked {\n\tmargin-right: 12px;\n\tpadding: 3px;\n}\n.disabled {\n\tfont-weight: bold;\n\tcolor: #333;\n}\n@media screen and (max-width: 640px) {\n\tbody {\n\t\tmargin: 5%;\n\t}\n\t#sidebar {\n\t\twidth:100%;\n\t\tmargin: 0;\n\t\tborder-bottom: 1px solid #333;\n\t}\n\t#passages {\n\t\tpadding-top: 2em;\n\t\tborder-left: 0;\n\t}\n}
twinecery checks for Twine passages tagged "grammar", then splits them based on newlines and turns them into a Tracery object. Run traces with <<trace>> or <<trace "someSymbol">>. You can also link to a trace as a passage by itself with [[trace]] or [[trace][$symbol = "someSymbol"]]. If no symbol is specified, it defaults to "origin".
\n<<trace>>\n\n[[Want another?|Start]]\n[[Sources]]
Manticores\nMedusa\nAdventure parks\nAmusement arcades\nBirdwatching\nBowling\nCaption contests\nCrosswords\nGames\nGame design\nGolf\nGoofing off\nKart racing\nNine-pin bowling\nOrigami\nSand art and play\nSnowball fights\nAcephali\nAfrits\nAngels\nBacchae\nBanshees\nBaphomet\nBasilisks\nBerserkers\nBigfoot\nBlue Men\nBogies\nHags\nHarpies\nHesperides\nHippocampus\nHippogriffs\nHobgoblins\nThe Barchan\nThe Blowout\nThe Desert pavement\nThe Desert varnish\nThe Dune\nThe Dreikanter\nThe Bight\nThe Blowhole\nThe Channel\nThe Cape\nThe Calanque\nThe Cliff\nThe Coast\nThe Continental shelf\nThe Coral reef\nThe Alluvial fan\nThe Anabranch\nThe Arroyo and (wash)\nThe Backswamp\nThe Bar\nThe Bayou\nThe Bench\nThe Braided channel\nThe Cave\nThe Cliff\nThe Cut bank\nThe Crevasse splay\nThe Confluence\nThe Drainage basin\nThe Drainage divide\nThe Endorheic basin\nThe Esker\nThe Exhumed river channel\nThe Floodplain\nThe Fluvial island\nThe Fluvial terrace\nThe Gorge and canyon\nThe Gully\nThe Natural levee\nThe Marsh\nThe Meander\nThe Oxbow lake\nThe Point bar\nThe Plunge pool\nThe Rapid\nThe Riffle\nThe River\nThe River delta\nThe River island\nThe Rock-cut basin\nThe Shut-in\nThe Thalweg\nThe Towhead\nThe Shoal\nThe Spring\nThe Stream\nThe Stream pool\nThe Swamp\nThe Valley and Vale\nThe Wadi\nThe Waterfall\nThe Watershed\nThe Yazoo stream\nThe Glen\nThe Gorge\nThe Graben\nThe Gulf\nThe Gully\nThe Guyot\nThe Hanging valley\nThe Automobile repair shop\nThe Car wash\nThe Convention center\nThe Forum\nThe Gas station\nThe Hotel\nThe Internet cafe\nThe Market\nThe Market house\nThe Skyscraper\nThe Shop\nThe Shopping mall\nThe Supermarket\nThe Warehouse\nThe Restaurant\nThe Apartment block\nThe Asylum\nThe Condominium\nThe Dormitory\nThe Duplex\nThe Capitol\nThe City hall\nThe Consulate\nThe Courthouse\nThe Embassy\nThe Fire station\nThe Meeting house\nThe Moot hall\nThe Palace\nThe Parliament\nThe Police station\nThe Post office\nThe Prison\nThe Brewery\nThe Factory\nThe Foundry\nThe Power plant\nThe Winery\nThe Mill\nThe Monastery\nThe Mithraeum\nThe Fire temple\nThe Shrine\nThe Synagogue\nThe Temple\nThe Pagoda\nThe Gurdwara\nThe Apartment\nThe Bakery\nThe Beauty salon\nThe Bookstore or bookshop\nThe Clinic\nThe Community hall\nThe Department store\nThe Eatery\nThe Fast-food restaurant\nThe Film studio\nThe Floristry\nThe Folly\nThe Food court\nThe Gym\nThe Hospice\nThe Hospital\nThe Hut\nThe Igloo
Offbeat Classes, powered by [[Tracery|http://tracery.io/]]
hopscat
Guinea fowl\nAccordina\nAir horn\nAlphorn\nBagpipe\nBazooka\nBeatboxing\nMellophone\nMelodica\nMezzo-soprano\nPan flute\nPiccolo\nGet-Together\nCelebration\nReunion\nFestivity\nJamboree\nReception\nSoirée\nSocial\nGoat\nGoldfinch\nGoosander\nGoose\nGorilla\nGoshawk\nGrasshopper\nGrouse\nGuanaco\nPitch pipe\nRecorder\nSackbut\nSerpent\nTenor\nTin whistle\nWhip\nYodel\nAllergic\nAnaemic\nBilious\nAnemic\nArthritic\nCarcinogenic\nColicky\nConsumptive\nCorrupt\nDegraded\nDepraved\nDiabetic\nDyspeptic\nEpileptic\nEvil\nFallen\nHomely\nHypertensive\nIll\nImmoral\nLeprous\nMalarial\nMalignant\nMeasly\nNasty\nPalsied\nParalytic\nPerverted\nPocky\nQueasy\nReprobate\nRheumatic\nRickety\nRotten\nSick\nSickly\nSyphilitic\nTubercular\nTumorous\nUgly\nVacillant\nVicious\nVile\nBrutal\nCapricious\nContrary\nCruel\nAnteroom\nArmory\nAssembly Room\nAttic\nBackroom\nBallroom\nBasement\nBathroom\nBedroom\nBoardroom\nBoiler Room\nBoudoir\nCell\nCellar\nChanging Room\nChapel\nClassroom\nClean Room\nCloakroom\nCold Room\nCommon Room\nConference Room\nConservatory\nControl Room\nCourtroom\nCubby\nDarkroom\nDen\nDining Room\nDormitory\nDrawing Room\nDressing Room\nDungeon\nEmergency Room\nEngine Room\nEntrance\nFamily Room\nFitting Room\nFormal Dining Room\nFoyer\nFront Room\nGate\nGame Room\nGarage\nGarret\nGreen Room\nGrotto\nGuest Room\nGym\nHallway\nHomeroom\nHospital\nJail Cell\nKitchen\nKitchenette\nLadies' Room\nLarder\nLaundry Room\nLibrary\nLiving Room\nLobby\nLocker Room\nLoft\nLounge\nLunchroom\nMaid's Room\nMailroom\nMen's Room\nMorning Room\nMotel Room\nNewsroom\nNursery\nOffice\nOperating Room\nPanic Room\nPantry\nParlour\nPlayroom\nPool Room\nPowder Room\nPrison Cell\nFreezer\nRecovery Room\nRestroom Room\nRumpus Room\nSalesroom\nSalon\nSchoolroom\nScreen Porch\nScullery\nSick Room\nSitting Room\nSolarium\nStaff Room\nStateroom\nStockroom\nStoreroom\nStudio\nStudy Room\nSuite\nSunroom\nTack Room\nUtility Room\nVestibule\nVisitor's Room\nWaiting Room\nWardroom\nWashroom\nWater Closet\nWine Cellar\nWomen's Room\nWorkroom\nDay\nNight\nNoon\nMidnight\nAfternoon\nTwilight\nBedtime\nDark\nDarkness\nDuskiness\nEvening\nEventide\nGloom\nNightfall\nNighttime\nObscurity\nMammoth\nManatee\nMandrill\nMink\nMole\nMongoose\nMonkey\nMoose\nMouse\nSalamander\nPuppet\nToy Car\nSalmon\nSand dollar\nSandpiper\nSardine\nSea lion\nSea urchin\nSeahorse\nSeal\nShark
Ardent\nAvenger\nBarbarian\nBard\nBattlemind\nCleric\nDruid\nFighter\nInvoker\nMonk\nPaladin\nPsion\nRanger\nRogue\nRunepriest\nShaman\nSeeker\nSorcerer\nWarden\nWarlock\nWarlord\nWizard\nAdept\nArcane Adept\nAristocrat-Socialite\nConstruct of Justice\nCultist\nDruidic Adept\nElven Native\nEvil Clown\nFool\nHouse mage\nHybrid 3rd Class Warrior\nNimble City Guard\nNomad\nPeasant Warrior\nPhilosopher\nPrimitive\nRed Shirt Warrior\nRevolutionary\nSensitive\nSlave\nVariant Commoner for Militaristic Societies \nVillainous Fighter\nSneak\nVillainous Spellcaster\nWarrior\nWitch Doctor \nMage\nNun\nMystic\nThief/Rogue\nAkodo Champion\nBattle Maiden\nBayushi Deceiver\nBear Warrior\nBlade Dancer\nDaidoji Bodyguard\nEunuch Warlock\nHenshin Mystic\nHida Defender\nIaijutsu Master\nKishi Charger\nMaho-bujin\nMaho-tsukai\nMantis Mercenary\nMirumoto Niten Master\nMoto Avenger\nNinja Spy\nSamurai\nShadow Scout\nShaman\nShapeshifter\nShiba Protector\nShintao Monk\nShugenja\nSingh Rager\nSohei\nTattooed Monk\nVoid Disciple\nWeapon Master\nFortune Teller\nTrapper\nWoodsman\nWitch Hunter\nYakuza\nAdministrative Worker\nAdvertising Manager\nAerial Rigger\nAgricultural Adviser\nAgricultural Machinery Mechanic\nAgronomist\nAir Traffic Controller\nAircraft Instrument Technician\nAircraft Mechanic\nAirline Clerk\nAmmunition And Explosives Operative\nAnimal Technician\nAnimator\nAnthropologist\nApplications Manager\nApprentice Training Officer\nArchaeologist\nArchitect\nArchitectural Conservation Officer\nArt Critic And Historian\nSurgeon\nHairdresser\nAccountant\nCaretaker\nDriver\nConsultant\nForeman\nShoemaker\nCook\nPriest\nPriest\nDentist\nServant\nCustoms Officer \nDustman\nWriter\nElectrician\nEmployee\nBank Clerk\nOffice Worker\nNurse\nStudent\nPostman\nFarmer\nWatchmaker\nAir Hostess\nEngineer\nGardener\nJournalist\nJudge\nBookseller \nBuilder\nModel\nSailor\nMechanic\nDoctor\nMiner\nMonk\nInstructor\nNanny\nLabourer\nFisherman\nPainter\nLord\nChemist\nPilot\nPlumber \nPoliceman\nPolitician\nFireman\nTeacher\nPsychiatrist\nPsychologist\nReceptionist\nNun\nReporter\nScientist\nSecretary\nWaiter\nSoldier\nShorthand Typist\nTailor\nTechnician\nBullfighter\nTranslator\nSalesman\nShop Assistant\nVet \nSeraphim\nCherubim\nNephilim\nLord Of Shouting\nWatcher\nEmperor\nKing\nArchduke\nGrand Prince\nGrand Duke\nPrince\nDuke\nSovereign Prince\nMarquess\nMarquis\nMargrave\nLandgrave\nCount\nEarl\nViscount\nKing\nQueen\nPrincess\nPrince\nMonarch\nRuler \nCrown \nCrowned Head \nPotentate\nVidame\nBaron\nBaronet\nHereditary Knight\nBeast\nBehemoth\nDemon\nDevil\nDragon\nFreak\nGiant\nHorror\nVillain\nWhale\nAbnormality\nBarbarian\nBrute\nCentaur\nColossus\nFiend\nFrankenstein\nHellion\nLeviathan\nMammoth\nMiscreation\nMonstrosity\nMutant\nOgre\nPhoenix\nSavage\nTitan\nKnight\nEsquire\nGentleman\nArchbishop\nDean\nArchdeacon\nPrebendary\nCanon\nPriest\nDeacon
#type# #class#\n#class# of #domain#
jquery:on\nhash:off\nbookmark:on\nmodernizr:off\nundo:off\nobfuscate:off\nexitprompt:off\nblankcss:off\n