Skip to content

Instantly share code, notes, and snippets.

@dribnet
Last active May 7, 2019 07:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 31 You must be signed in to fork a gist
  • Save dribnet/a937cdee02e0ee311d500000cf9e7a6c to your computer and use it in GitHub Desktop.
Save dribnet/a937cdee02e0ee311d500000cf9e7a6c to your computer and use it in GitHub Desktop.
PS3 MDDN 342 2016
license: mit
// note: this file is poorly named - it can generally be ignored.
// helper functions below for supporting blocks/purview
function saveBlocksImages(doZoom) {
if(doZoom == null) {
doZoom = false;
}
// generate 960x500 preview.jpg of entire canvas
// TODO: should this be recycled?
var offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 960;
offscreenCanvas.height = 500;
var context = offscreenCanvas.getContext('2d');
// background is flat white
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 960, 500);
context.drawImage(this.canvas, 0, 0, 960, 500);
// save to browser
var downloadMime = 'image/octet-stream';
var imageData = offscreenCanvas.toDataURL('image/jpeg');
imageData = imageData.replace('image/jpeg', downloadMime);
p5.prototype.downloadFile(imageData, 'preview.jpg', 'jpg');
// generate 230x120 thumbnail.png centered on mouse
offscreenCanvas.width = 230;
offscreenCanvas.height = 120;
// background is flat white
context = offscreenCanvas.getContext('2d');
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 230, 120);
if(doZoom) {
// pixelDensity does the right thing on retina displays
var pd = this._pixelDensity;
var sx = pd * mouseX - pd * 230/2;
var sy = pd * mouseY - pd * 120/2;
var sw = pd * 230;
var sh = pd * 120;
// bounds checking - just displace if necessary
if (sx < 0) {
sx = 0;
}
if (sx > this.canvas.width - sw) {
sx = this.canvas.width - sw;
}
if (sy < 0) {
sy = 0;
}
if (sy > this.canvas.height - sh) {
sy = this.canvas.height - sh;
}
// save to browser
context.drawImage(this.canvas, sx, sy, sw, sh, 0, 0, 230, 120);
}
else {
// now scaledown
var full_width = this.canvas.width;
var full_height = this.canvas.height;
context.drawImage(this.canvas, 0, 0, full_width, full_height, 0, 0, 230, 120);
}
imageData = offscreenCanvas.toDataURL('image/png');
imageData = imageData.replace('image/png', downloadMime);
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png');
}

PS3 MDDN 342 2016

An example of how to get started with the Online Chatbot.

Do not modify the skech.js: all changes should be made to bot.js.

// images for the background
var closed_images = [
"sunny.jpg",
"cloudy.jpg",
"windy.jpg",
"rainy.jpg"
]
// 10 day high temperature forecast
var closed_data = [
[20, 22, 24, 22, 24, 22, 20, 15, 10, 15],
[30, 29, 27, 25, 25, 23, 22, 22, 23, 20],
[10, 12, 10, 12, 10, 12, 10, 12, 10, 12]
]
function bot() {
// make this true once image has been drawn
this.have_drawn = false;
// helper function to draw an owl
this.owl = function(x, y, g, s) {
push();
translate(x, y);
scale(s); // Set the createCanvas
stroke(g); // Set the gray value
strokeWeight(70);
line(0, -35, 0, -65); // Body
noStroke();
fill(255-g);
ellipse(-17.5, -65, 35, 35); // Left eye dome
ellipse(17.5, -65, 35, 35); // Right eye dome
arc(0, -65, 70, 70, 0, PI); // Chin
fill(g);
ellipse(-14, -65, 8, 8); // Left eye
ellipse(14, -65, 8, 8); // Right eye
quad(0, -58, 4, -51, 0, -44, -4, -51); // Beak
pop();
}
// return true if image has been drawn
this.isDone = function() {
return this.have_drawn;
}
// load all external images or data
this.preload = function() {
// chose a random image from the list
var image_name = random(closed_images);
println(image_name);
// load the image the variable img
this.img = loadImage(image_name)
}
this.setup = function() {
// chose a random set of data from the list of data
this.temperatures = random(closed_data);
println(this.temperatures);
}
this.grammar = {
"animal": ["bird","animal","creature","owl","night-watcher"],
"reaction": ["vexing","perplexing","exciting","wistful","enigmatic"],
"prediction": ["prediction", "forecast", "prognostication"],
"timeunit": ["day", "night"]
}
this.respond = function() {
// draw the background image
image(this.img);
// chose how many owls to draw
var num_owls = Math.floor(focusedRandom(5, 11, 3, 7));
// draw the owls based on the data in temperatures
var spacing = 400 / num_owls;
for (i=0; i<num_owls; i++) {
var xpos = 35 + spacing * i;
var gray = int(focusedRandom(0, 102, 3))
var scalar = focusedRandom(0.15, 0.75, 2);
this.owl(xpos, 220-2*this.temperatures[i], gray, scalar);
}
// set have_drawn to true since we have completed
this.have_drawn = true;
// construct the message
var grammar = tracery.createGrammar(this.grammar);
var seed = "" + num_owls + " #timeunit# #reaction# #animal.s# #prediction#";
var message = grammar.flatten(seed);
return message;
}
}
function resetFocusedRandom() {
return Math.seedrandom(arguments);
}
function focusedRandom(min, max, focus, mean) {
// console.log("hello")
if(max === undefined) {
max = min;
min = 0;
}
if(focus === undefined) {
focus = 1.0;
}
if(mean === undefined) {
mean = (min + max) / 2.0;
}
if(focus == 0) {
return d3.randomUniform(min, max)();
}
else if(focus < 0) {
focus = -1 / focus;
}
sigma = (max - mean) / focus;
val = d3.randomNormal(mean, sigma)();
if (val > min && val < max) {
return val;
}
return d3.randomUniform(min, max)();
}
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/p5.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js"></script>
<script src="https://d3js.org/d3-random.v1.min.js"></script>
<script language="javascript" type="text/javascript" src="focusedRandom.js"></script>
<script language="javascript" type="text/javascript" src=".purview_helper.js"></script>
<script src="tracery.js"></script>
<script language="javascript" type="text/javascript" src="bot.js"></script>
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<style>
body {padding: 0; margin: 0;}
ul li {
list-style:none;
overflow:hidden;
border:1px solid #dedede;
margin:5px;
padding:5px;
}
.media img {
max-width:440px;
max-height:220px;
}
</style>
</head>
<body style="background-color:white">
<div id="canvasContainer"></div>
<pre>
<p id="tweet_text">
</p>
</pre>
<hr>
<div id="tweetExamples"></div>
</body>
var rndSeed;
var bot;
var renderReady = false;
function preload() {
bot = new bot();
bot.preload();
}
function setup () {
var main_canvas = createCanvas(440, 220);
main_canvas.parent('canvasContainer');
rndSeed = random(1024);
bot.setup();
}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
function reportRenderReady() {
finalDiv = createDiv('(render ready)');
finalDiv.id("render_ready")
}
function draw() {
background(204);
// randomSeed(0);
resetFocusedRandom(rndSeed);
message = bot.respond();
var text = select('#tweet_text');
text.html(message);
if(renderReady == false) {
if(bot.isDone()) {
reportRenderReady();
renderReady = true;
}
}
}
/**
* @author Kate Compton
*/
var tracery = {
utilities : {}
};
(function() {
function inQuotes(s) {
return '"' + s + '"';
};
function parseAction(action) {
return action;
};
// tag format
// a thing to expand, plus actions
function parseTag(tag) {
var errors = [];
var prefxns = [];
var postfxns = [];
var lvl = 0;
var start = 0;
var inPre = true;
var symbol,
mods;
function nonAction(end) {
if (start !== end) {
var section = tag.substring(start, end);
if (!inPre) {
errors.push("multiple possible expansion symbols in tag!" + tag);
} else {
inPre = false;
var split = section.split(".");
symbol = split[0];
mods = split.slice(1, split.length);
}
}
start = end;
};
for (var i = 0; i < tag.length; i++) {
var c = tag.charAt(i);
switch(c) {
case '[':
if (lvl === 0) {
nonAction(i);
}
lvl++;
break;
case ']':
lvl--;
if (lvl === 0) {
var section = tag.substring(start + 1, i);
if (inPre)
prefxns.push(parseAction(section));
else
postfxns.push(parseAction(section));
start = i + 1;
}
break;
default:
if (lvl === 0) {
}
break;
}
}
nonAction(i);
if (lvl > 0) {
var error = "Too many '[' in rule " + inQuotes(tag);
errors.push(error);
}
if (lvl < 0) {
var error = "Too many ']' in rule " + inQuotes(tag);
errors.push(error);
}
return {
preActions : prefxns,
postActions : postfxns,
symbol : symbol,
mods : mods,
raw : tag,
errors : errors,
};
};
// Split a rule into sections
function parseRule(rule) {
var sections = [];
var errors = [];
if (!( typeof rule == 'string' || rule instanceof String)) {
errors.push("Cannot parse non-string rule " + rule);
sections.errors = errors;
return sections;
}
if (rule.length === 0) {
return [];
}
var lvl = 0;
var start = 0;
var inTag = false;
function createSection(end) {
var section = rule.substring(start, end);
if (section.length > 0) {
if (inTag)
sections.push(parseTag(section));
else
sections.push(section);
}
inTag = !inTag;
start = end + 1;
}
for (var i = 0; i < rule.length; i++) {
var c = rule.charAt(i);
switch(c) {
case '[':
lvl++;
break;
case ']':
lvl--;
break;
case '#':
if (lvl === 0) {
createSection(i);
}
break;
default:
break;
}
}
if (lvl > 0) {
var error = "Too many '[' in rule " + inQuotes(rule);
errors.push(error);
}
if (lvl < 0) {
var error = "Too many ']' in rule " + inQuotes(rule);
errors.push(error);
}
if (inTag) {
var error = "Odd number of '#' in rule " + inQuotes(rule);
errors.push(error);
}
createSection(rule.length);
sections.errors = errors;
return sections;
};
function testParse(rule, shouldFail) {
console.log("-------");
console.log("Test parse rule: " + inQuotes(rule) + " " + shouldFail);
var parsed = parseRule(rule);
if (parsed.errors && parsed.errors.length > 0) {
for (var i = 0; i < parsed.errors.length; i++) {
console.log(parsed.errors[i]);
}
}
}
function testParseTag(tag, shouldFail) {
console.log("-------");
console.log("Test parse tag: " + inQuotes(tag) + " " + shouldFail);
var parsed = parseTag(tag);
if (parsed.errors && parsed.errors.length > 0) {
for (var i = 0; i < parsed.errors.length; i++) {
console.log(parsed.errors[i]);
}
}
}
tracery.testParse = testParse;
tracery.testParseTag = testParseTag;
tracery.parseRule = parseRule;
tracery.parseTag = parseTag;
function spacer(size) {
var s = "";
for (var i = 0; i < size * 3; i++) {
s += " ";
}
return s;
}
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}
// Inspired by base2 and Prototype
(function() {
var initializing = false,
fnTest = /xyz/.test(function() { xyz;
}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function() {
};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) {
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
/**
* @author Kate
*/
var Rule = function(raw) {
this.raw = raw;
this.sections = parseRule(raw);
};
Rule.prototype.getParsed = function() {
if (!this.sections)
this.sections = parseRule(raw);
return this.sections;
};
Rule.prototype.toString = function() {
return this.raw;
};
Rule.prototype.toJSONString = function() {
return this.raw;
};
/**
* @author Kate
*/
var RuleWeighting = Object.freeze({
RED : 0,
GREEN : 1,
BLUE : 2
});
var RuleSet = function(rules) {
// is the rules obj an array? A RuleSet, or a string?
if (rules.constructor === Array) {
// make a copy
rules = rules.slice(0, rules.length);
} else if (rules.prototype === RuleSet) {
// clone
} else if ( typeof rules == 'string' || rules instanceof String) {
var args = Array.prototype.slice.call(arguments);
rules = args;
} else {
console.log(rules);
throw ("creating ruleset with unknown object type!");
}
// create rules and their use counts
this.rules = rules;
this.parseAll();
this.uses = [];
this.startUses = [];
this.totalUses = 0;
for (var i = 0; i < this.rules.length; i++) {
this.uses[i] = 0;
this.startUses[i] = this.uses[i];
this.totalUses += this.uses[i];
}
};
//========================================================
// Iterating over rules
RuleSet.prototype.parseAll = function(fxn) {
for (var i = 0; i < this.rules.length; i++) {
if (this.rules[i].prototype !== Rule)
this.rules[i] = new Rule(this.rules[i]);
}
};
//========================================================
// Iterating over rules
RuleSet.prototype.mapRules = function(fxn) {
return this.rules.map(function(rule, index) {
return fxn(rule, index);
});
};
RuleSet.prototype.applyToRules = function(fxn) {
for (var i = 0; i < this.rules.length; i++) {
fxn(this.rules[i], i);
}
};
//========================================================
RuleSet.prototype.get = function() {
var index = this.getIndex();
return this.rules[index];
};
RuleSet.prototype.getRandomIndex = function() {
return Math.floor(this.uses.length * Math.random());
};
RuleSet.prototype.getIndex = function() {
// Weighted distribution
// Imagine a bar of length 1, how to divide the length
// s.t. a random dist will result in the dist we want?
var index = this.getRandomIndex();
// What if the uses determine the chance of rerolling?
var median = this.totalUses / this.uses.length;
var count = 0;
while (this.uses[index] > median && count < 20) {
index = this.getRandomIndex();
count++;
}
// reroll more likely if index is too much higher
return index;
};
RuleSet.prototype.decayUses = function(pct) {
this.totalUses = 0;
for (var i = 0; i < this.uses; i++) {
this.uses[index] *= 1 - pct;
this.totalUses += this.uses[index];
}
};
RuleSet.prototype.testRandom = function() {
console.log("Test random");
var counts = [];
for (var i = 0; i < this.uses.length; i++) {
counts[i] = 0;
}
var testCount = 10 * this.uses.length;
for (var i = 0; i < testCount; i++) {
var index = this.getIndex();
this.uses[index] += 1;
counts[index]++;
this.decayUses(.1);
}
for (var i = 0; i < this.uses.length; i++) {
console.log(i + ":\t" + counts[i] + " \t" + this.uses[i]);
}
};
RuleSet.prototype.getSaveRules = function() {
var jsonRules = this.rules.map(function(rule) {
return rule.toJSONString();
});
return jsonRules;
};
/**
* @author Kate Compton
*/
var Action = function(node, raw) {
this.node = node;
this.grammar = node.grammar;
this.raw = raw;
};
Action.prototype.activate = function() {
var node = this.node;
node.actions.push(this);
// replace any hashtags
this.amended = this.grammar.flatten(this.raw);
var parsed = parseTag(this.amended);
var subActionRaw = parsed.preActions;
if (subActionRaw && subActionRaw.length > 0) {
this.subactions = subActionRaw.map(function(action) {
return new Action(node, action);
});
}
if (parsed.symbol) {
var split = parsed.symbol.split(":");
if (split.length === 2) {
this.push = {
symbol : split[0],
// split into multiple rules
rules : split[1].split(","),
};
// push
node.grammar.pushRules(this.push.symbol, this.push.rules);
} else
throw ("Unknown action: " + parsed.symbol);
}
if (this.subactions) {
for (var i = 0; i < this.subactions.length; i++) {
this.subactions[i].activate();
}
}
};
Action.prototype.deactivate = function() {
if (this.subactions) {
for (var i = 0; i < this.subactions.length; i++) {
this.subactions[i].deactivate();
}
}
if (this.push) {
this.node.grammar.popRules(this.push.symbol, this.push.rules);
}
};
/**
* @author Kate Compton
*/
var isConsonant = function(c) {
c = c.toLowerCase();
switch(c) {
case 'a':
return false;
case 'e':
return false;
case 'i':
return false;
case 'o':
return false;
case 'u':
return false;
}
return true;
};
function endsWithConY(s) {
if (s.charAt(s.length - 1) === 'y') {
return isConsonant(s.charAt(s.length - 2));
}
return false;
};
var universalModifiers = {
capitalizeAll : function(s) {
return s.replace(/(?:^|\s)\S/g, function(a) {
return a.toUpperCase();
});
},
capitalize : function(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
},
inQuotes : function(s) {
return '"' + s + '"';
},
comma : function(s) {
var last = s.charAt(s.length - 1);
if (last === ",")
return s;
if (last === ".")
return s;
if (last === "?")
return s;
if (last === "!")
return s;
return s + ",";
},
beeSpeak : function(s) {
// s = s.replace("s", "zzz");
s = s.replace(/s/, 'zzz');
return s;
},
a : function(s) {
if (!isConsonant(s.charAt()))
return "an " + s;
return "a " + s;
},
s : function(s) {
var last = s.charAt(s.length - 1);
switch(last) {
case 'y':
// rays, convoys
if (!isConsonant(s.charAt(s.length - 2))) {
return s + "s";
}
// harpies, cries
else {
return s.slice(0, s.length - 1) + "ies";
}
break;
// oxen, boxen, foxen
case 'x':
return s.slice(0, s.length - 1) + "xen";
case 'z':
return s.slice(0, s.length - 1) + "zes";
case 'h':
return s.slice(0, s.length - 1) + "hes";
default:
return s + "s";
};
},
ed : function(s) {
var index = s.indexOf(" ");
var s = s;
var rest = "";
if (index > 0) {
rest = s.substring(index, s.length);
s = s.substring(0, index);
}
var last = s.charAt(s.length - 1);
switch(last) {
case 'y':
// rays, convoys
if (isConsonant(s.charAt(s.length - 2))) {
return s.slice(0, s.length - 1) + "ied" + rest;
}
// harpies, cries
else {
return s + "ed" + rest;
}
break;
case 'e':
return s + "d" + rest;
break;
default:
return s + "ed" + rest;
};
}
};
/**
* @author Kate Compton
*/
// A tracery expansion node
var nodeCount = 0;
var ExpansionNode = Class.extend({
init : function() {
this.depth = 0;
this.id = nodeCount;
nodeCount++;
this.childText = "[[UNEXPANDED]]";
},
setParent : function(parent) {
if (parent) {
this.depth = parent.depth + 1;
this.parent = parent;
this.grammar = parent.grammar;
}
},
expand : function() {
// do nothing
return "???";
},
expandChildren : function() {
if (this.children) {
this.childText = "";
for (var i = 0; i < this.children.length; i++) {
this.children[i].expand();
this.childText += this.children[i].finalText;
}
this.finalText = this.childText;
}
},
createChildrenFromSections : function(sections) {
var root = this;
this.children = sections.map(function(section) {
if ( typeof section == 'string' || section instanceof String) {
// Plaintext
return new TextNode(root, section);
} else {
return new TagNode(root, section);
}
});
}
});
var RootNode = ExpansionNode.extend({
init : function(grammar, rawRule) {
this._super();
this.grammar = grammar;
this.parsedRule = parseRule(rawRule);
},
expand : function() {
var root = this;
this.createChildrenFromSections(this.parsedRule);
// expand the children
this.expandChildren();
},
});
function simpleExtend(a, b){
for(var key in b)
if(b.hasOwnProperty(key))
a[key] = b[key];
return a;
}
var TagNode = ExpansionNode.extend({
init : function(parent, parsedTag) {
this._super();
if (!(parsedTag !== null && typeof parsedTag === 'object')) {
if ( typeof parsedTag == 'string' || parsedTag instanceof String) {
console.warn("Can't make tagNode from unparsed string!");
parsedTag = parseTag(parsedTag);
} else {
console.log("Unknown tagNode input: ", parsedTag);
throw ("Can't make tagNode from strange tag!");
}
}
this.setParent(parent);
// NOTE: removed jquery dependency here
// $.extend(this, parsedTag);
simpleExtend(this, parsedTag);
},
expand : function() {
if (tracery.outputExpansionTrace)
console.log(r.sections);
this.rule = this.grammar.getRule(this.symbol);
this.actions = [];
// Parse the rule if it hasn't been already
this.createChildrenFromSections(this.rule.getParsed());
// Do any pre-expansion actions!
for (var i = 0; i < this.preActions.length; i++) {
var action = new Action(this, this.preActions[i]);
action.activate();
}
// Map each child section to a node
if (!this.rule.sections)
console.log(this.rule);
this.expandChildren();
for (var i = 0; i < this.actions.length; i++) {
this.actions[i].deactivate();
}
this.finalText = this.childText;
for (var i = 0; i < this.mods.length; i++) {
this.finalText = this.grammar.applyMod(this.mods[i], this.finalText);
}
},
toLabel : function() {
return this.symbol;
},
toString : function() {
return "TagNode '" + this.symbol + "' mods:" + this.mods + ", preactions:" + this.preActions + ", postactions" + this.postActions;
}
});
var TextNode = ExpansionNode.extend({
isLeaf : true,
init : function(parent, text) {
this._super();
this.setParent(parent);
this.text = text;
this.finalText = text;
},
expand : function() {
// do nothing
},
toLabel : function() {
return this.text;
}
});
/**
* @author Kate Compton
*/
function Symbol(grammar, key) {
this.grammar = grammar;
this.key = key;
this.currentRules = undefined;
this.ruleSets = [];
};
Symbol.prototype.loadFrom = function(rules) {
rules = this.wrapRules(rules);
this.baseRules = rules;
this.ruleSets.push(rules);
this.currentRules = this.ruleSets[this.ruleSets.length - 1];
};
//========================================================
// Iterating over rules
Symbol.prototype.mapRules = function(fxn) {
return this.currentRules.mapRules(fxn);
};
Symbol.prototype.applyToRules = function(fxn) {
this.currentRules.applyToRules(fxn);
};
//==================================================
// Rule pushpops
Symbol.prototype.wrapRules = function(rules) {
if (rules.prototype !== RuleSet) {
if (Array.isArray(rules)) {
return new RuleSet(rules);
} else if ( typeof rules == 'string' || rules instanceof String) {
return new RuleSet(rules);
} else {
throw ("Unknown rules type: " + rules);
}
}
// already a ruleset
return rules;
};
Symbol.prototype.pushRules = function(rules) {
rules = this.wrapRules(rules);
this.ruleSets.push(rules);
this.currentRules = this.ruleSets[this.ruleSets.length - 1];
};
Symbol.prototype.popRules = function() {
var exRules = this.ruleSets.pop();
if (this.ruleSets.length === 0) {
//console.warn("No more rules for " + this + "!");
}
this.currentRules = this.ruleSets[this.ruleSets.length - 1];
};
// Clear everything and set the rules
Symbol.prototype.setRules = function(rules) {
rules = this.wrapRules(rules);
this.ruleSets = [rules];
this.currentRules = rules;
};
Symbol.prototype.addRule = function(rule) {
this.currentRules.addRule(seed);
};
//========================================================
// selection
Symbol.prototype.select = function() {
this.isSelected = true;
};
Symbol.prototype.deselect = function() {
this.isSelected = false;
};
//==================================================
// Getters
Symbol.prototype.getRule = function(seed) {
return this.currentRules.get(seed);
};
//==================================================
Symbol.prototype.toString = function() {
return this.key + ": " + this.currentRules + "(overlaying " + (this.ruleSets.length - 1) + ")";
};
Symbol.prototype.toJSON = function() {
var rules = this.baseRules.rules.map(function(rule) {
return '"' + rule.raw + '"';
});
return '"' + this.key + '"' + ": [" + rules.join(", ") + "]";
};
Symbol.prototype.toHTML = function(useSpans) {
var keySpan = '"' + this.key + '"';
if (useSpans)
keySpan = "<span class='symbol symbol_" + this.key + "'>" + keySpan + "</span>";
var rules = this.baseRules.rules.map(function(rule) {
// replace any anglebrackets for html
var cleaned = rule.raw.replace(/&/g, "&amp;");
cleaned = cleaned.replace(/>/g, "&gt;");
cleaned = cleaned.replace(/</g, "&lt;");
var s = '"' + cleaned + '"';
if (useSpans)
s = "<span class='rule'>" + s + "</span>";
return s;
});
return keySpan + ": [" + rules.join(", ") + "]";
};
/**
* @author Kate Compton
*/
function Grammar() {
this.clear();
};
Grammar.prototype.clear = function() {
// Symbol library
this.symbols = {};
this.errors = [];
// Modifier library
this.modifiers = {};
// add the universal mods
for (var mod in universalModifiers) {
if (universalModifiers.hasOwnProperty(mod))
this.modifiers[mod] = universalModifiers[mod];
}
};
//========================================================
// Loading
Grammar.prototype.loadFrom = function(obj) {
var symbolSrc;
this.clear();
if (obj.symbols !== undefined) {
symbolSrc = obj.symbols;
} else {
symbolSrc = obj;
}
// get all json keys
var keys = Object.keys(symbolSrc);
this.symbolNames = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
this.symbolNames.push(key);
this.symbols[key] = new Symbol(this, key);
this.symbols[key].loadFrom(symbolSrc[key]);
}
};
Grammar.prototype.toHTML = function(useSpans) {
// get all json keys
var keys = Object.keys(this.symbols);
this.symbolNames = [];
var lines = [];
var count = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var symbol = this.symbols[key];
if (symbol && symbol.baseRules) {
lines.push(" " + this.symbols[key].toHTML(useSpans));
}
};
var s;
s = lines.join(",</p><p>");
s = "{<p>" + s + "</p>}";
return s;
};
Grammar.prototype.toJSON = function() {
// get all json keys
var keys = Object.keys(this.symbols);
this.symbolNames = [];
var lines = [];
var count = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var symbol = this.symbols[key];
if (symbol && symbol.baseRules) {
lines.push(" " + this.symbols[key].toJSON());
}
};
var s;
s = lines.join(",\n");
s = "{\n" + s + "\n}";
return s;
};
//========================================================
// selection
Grammar.prototype.select = function() {
this.isSelected = true;
};
Grammar.prototype.deselect = function() {
this.isSelected = false;
};
//========================================================
// Iterating over symbols
Grammar.prototype.mapSymbols = function(fxn) {
var symbols = this.symbols;
return this.symbolNames.map(function(name) {
return fxn(symbols[name], name);
});
};
Grammar.prototype.applyToSymbols = function(fxn) {
for (var i = 0; i < this.symbolNames.length; i++) {
var key = this.symbolNames[i];
fxn(this.symbols[key], key);
}
};
//========================================================
Grammar.prototype.addOrGetSymbol = function(key) {
if (this.symbols[key] === undefined)
this.symbols[key] = new Symbol(key);
return this.symbols[key];
};
Grammar.prototype.pushRules = function(key, rules) {
var symbol = this.addOrGetSymbol(key);
symbol.pushRules(rules);
};
Grammar.prototype.popRules = function(key, rules) {
var symbol = this.addOrGetSymbol(key);
var popped = symbol.popRules();
if (symbol.ruleSets.length === 0) {
// remove symbol
this.symbols[key] = undefined;
}
};
Grammar.prototype.applyMod = function(modName, text) {
if (!this.modifiers[modName]) {
console.log(this.modifiers);
throw ("Unknown mod: " + modName);
}
return this.modifiers[modName](text);
};
//============================================================
Grammar.prototype.getRule = function(key, seed) {
var symbol = this.symbols[key];
if (symbol === undefined) {
var r = new Rule("{{" + key + "}}");
r.error = "Missing symbol " + key;
return r;
}
var rule = symbol.getRule();
if (rule === undefined) {
var r = new Rule("[" + key + "]");
console.log(r.sections);
r.error = "Symbol " + key + " has no rule";
return r;
}
return rule;
};
//============================================================
// Expansions
Grammar.prototype.expand = function(raw) {
// Start a new tree
var root = new RootNode(this, raw);
root.expand();
return root;
};
Grammar.prototype.flatten = function(raw) {
// Start a new tree
var root = new RootNode(this, raw);
root.expand();
return root.childText;
};
//===============
Grammar.prototype.analyze = function() {
this.symbolNames = [];
for (var name in this.symbols) {
if (this.symbols.hasOwnProperty(name)) {
this.symbolNames.push(name);
}
}
// parse every rule
for (var i = 0; i < this.symbolNames.length; i++) {
var key = this.symbolNames[i];
var symbol = this.symbols[key];
// parse all
for (var j = 0; j < symbol.baseRules.length; j++) {
var rule = symbol.baseRules[j];
rule.parsed = tracery.parse(rule.raw);
// console.log(rule);
}
}
};
Grammar.prototype.selectSymbol = function(key) {
console.log(this);
var symbol = this.get(key);
};
/**
* @author Kate Compton
*/
tracery.createGrammar = function(obj) {
var grammar = new Grammar();
grammar.loadFrom(obj);
return grammar;
};
tracery.test = function() {
console.log("==========================================");
console.log("test tracery");
// good
tracery.testParse("", false);
tracery.testParse("fooo", false);
tracery.testParse("####", false);
tracery.testParse("#[]#[]##", false);
tracery.testParse("#someSymbol# and #someOtherSymbol#", false);
tracery.testParse("#someOtherSymbol.cap.pluralize#", false);
tracery.testParse("#[#do some things#]symbol.mod[someotherthings[and a function]]#", false);
tracery.testParse("#[fxn][fxn][fxn[subfxn]]symbol[[fxn]]#", false);
tracery.testParse("#[fxn][#fxn#][fxn[#subfxn#]]symbol[[fxn]]#", false);
tracery.testParse("#hero# ate some #color# #animal.s#", false);
tracery.testParseTag("[action]symbol.mod1.mod2[postAction]", false);
// bad
tracery.testParse("#someSymbol# and #someOtherSymbol", true);
tracery.testParse("#[fxn][fxn][fxn[subfxn]]symbol[fxn]]#", true);
// bad
tracery.testParseTag("stuff[action]symbol.mod1.mod2[postAction]", true);
tracery.testParseTag("[action]symbol.mod1.mod2[postAction]stuff", true);
tracery.testParse("#hero# ate some #color# #animal.s#", true);
tracery.testParse("#[#setPronouns#][#setOccupation#][hero:#name#]story#", true);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment