/*\ title: js/Renderer.js Renderer objects \*/ (function(){ /*jshint node: true, browser: true */ "use strict"; var utils = require("./Utils.js"), ArgParser = require("./ArgParser.js").ArgParser, Dependencies = require("./Dependencies.js").Dependencies, esprima = require("esprima"); var Node = function(children) { if(this instanceof Node) { this.children = children; } else { return new Node(children); } }; Node.prototype.clone = function() { // By default we don't actually clone nodes, we just re-use them (we do clone macros and elements) return this; }; Node.prototype.execute = Node.prototype.render = Node.prototype.renderInDom = Node.prototype.refresh = Node.prototype.refreshInDom = function() { // All these methods are no-ops by default }; /* Construct a renderer node representing a macro invocation macroName: name of the macro srcParams: a hashmap of parameters (each can be a string, or a fn(tiddler,store,utils) for evaluated parameters) children: optional array of child nodes store: reference to the WikiStore associated with this macro */ var MacroNode = function(macroName,srcParams,children,store,dependencies) { if(this instanceof MacroNode) { // Save the details this.macroName = macroName; this.macro = store.macros[macroName]; this.children = children; this.store = store; this.srcParams = typeof srcParams === "string" ? this.parseMacroParamString(srcParams) : srcParams; // Evaluate the dependencies if(dependencies) { this.dependencies = dependencies; } else { this.dependencies = new Dependencies(); if(this.srcParams && this.macro) { if(this.macro.dependentAll) { this.dependencies.dependentAll = true; } for(var m in this.macro.params) { var paramInfo = this.macro.params[m]; if(m in this.srcParams && paramInfo.type === "tiddler") { if(typeof this.srcParams[m] === "function") { this.dependencies.dependentAll = true; } else { this.dependencies.addDependency(this.srcParams[m],!paramInfo.skinny); } } } } } } else { return new MacroNode(macroName,srcParams,children,store,dependencies); } }; MacroNode.prototype = new Node(); MacroNode.prototype.constructor = MacroNode; MacroNode.prototype.parseMacroParamString = function(paramString) { /*jslint evil: true */ var params = {}, args = new ArgParser(paramString,{defaultName: "anon", cascadeDefaults: this.macro.cascadeDefaults}), self = this, insertParam = function(name,arg) { if(arg.evaluated) { params[name] = eval(esprima.generate( // (function(tiddler,store,utils) {return {paramOne: 1};}) { "type": "Program", "body": [ { "type": "ExpressionStatement", "expression": { "type": "FunctionExpression", "id": null, "params": [ { "type": "Identifier", "name": "tiddler" }, { "type": "Identifier", "name": "store" }, { "type": "Identifier", "name": "utils" } ], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": esprima.parse("(" + arg.string + ")").body[0].expression } ] } } } ] } )); } else { params[name] = arg.string; } }; for(var m in this.macro.params) { var param = this.macro.params[m], arg; if("byPos" in param && args.byPos[param.byPos] && (args.byPos[param.byPos].n === "anon" || args.byPos[param.byPos].n === m)) { arg = args.byPos[param.byPos].v; insertParam(m,arg); } else { arg = args.getValueByName(m); if(!arg && param.byName === "default") { arg = args.getValueByName("anon"); } if(arg) { insertParam(m,arg); } } } return params; }; MacroNode.prototype.clone = function() { return new MacroNode(this.macroName,this.srcParams,this.cloneChildren(),this.store,this.dependencies); }; MacroNode.prototype.cloneChildren = function() { var childClones; if(this.children) { childClones = []; for(var t=0; t"); } if(this.children) { for(var t=0; t"); } } return output.join(""); }; ElementNode.prototype.renderInDom = function(domNode) { var element = document.createElement(this.type); if(this.attributes) { for(var a in this.attributes) { var v = this.attributes[a]; if(v !== undefined) { if(v instanceof Array) { // Ahem, could there be arrays other than className? element.className = v.join(" "); } else if (typeof v === "object") { // ...or objects other than style? for(var p in v) { element.style[p] = v[p]; } } else { element.setAttribute(a,v); } } } } domNode.appendChild(element); if(this.children) { for(var t=0; t