/*\ title: $:/core/modules/treenodes/macro.js type: application/javascript module-type: treenode Macro node, used as the base class for macros \*/ (function(){ /*jshint node: true, browser: true */ /*global $tw: false */ "use strict"; var Node = require("./node.js").Node; /* Construct a renderer node representing a macro invocation macroName: name of the macro options: see below The options available are: srcParams: a string or a hashmap of parameters (each can be a string, or a fn(tiddler,wiki) for evaluated parameters) content: optional array of child nodes wiki: reference to the WikiStore associated with this macro dependencies: optional Dependencies object representing the dependencies of this macro isBlock: true if this macro is being used as an HTML block classes: array of classes to be assigned to the macro Note that the dependencies will be evaluated if not provided. */ var Macro = function(macroName,options) { options = options || {}; var MacroClass = options.wiki ? options.wiki.macros[macroName] : null; // Get the macro class if(this instanceof Macro) { // Save the details this.macroName = macroName; this.srcParams = options.srcParams || {}; this.content = options.content || []; this.wiki = options.wiki; this.dependencies = options.dependencies; this.isBlock = options.isBlock; this.classes = options.classes; // Parse the macro parameters if required if(typeof this.srcParams === "string") { this.srcParams = this.parseMacroParamString(this.srcParams); } // Evaluate the dependencies if required if(macroName && !this.dependencies) { this.dependencies = this.evaluateDependencies(); } // Get a reference to the static information about this macro if(MacroClass) { this.MacroClass = MacroClass; this.info = MacroClass.prototype.info; } } else { // If Macro() has been called without 'new' then instantiate the right macro class if(!MacroClass) { throw "Unknown macro '" + macroName + "'"; } return new MacroClass(macroName,options); } }; Macro.prototype = new Node(); Macro.prototype.constructor = Macro; /* Evaluate the dependencies of this macro invocation. If the macro provides an `evaluateDependencies` method then it is invoked to evaluate the dependencies. Otherwise it generates the dependencies based on the macro parameters provided */ Macro.prototype.evaluateDependencies = function() { // Figure out the dependencies from the metadata and parameters var dependencies = new $tw.Dependencies(); if(this.info.dependentAll) { dependencies.dependentAll = true; } if(this.info.dependentOnContextTiddler) { dependencies.dependentOnContextTiddler = true; } for(var m in this.info.params) { var paramInfo = this.info.params[m]; if(m in this.srcParams && paramInfo.type === "tiddler") { if(typeof this.srcParams[m] === "function") { dependencies.dependentAll = true; } else { dependencies.addDependency(this.srcParams[m],!paramInfo.skinny); } } } return dependencies; }; Macro.prototype.parseMacroParamString = function(paramString) { /*jslint evil: true */ var params = {}, args = new $tw.utils.ArgParser(paramString,{defaultName: "anon", cascadeDefaults: this.info.cascadeDefaults}), self = this, insertParam = function(name,arg) { if(arg.evaluated) { params[name] = eval("(function(tiddler,wiki) {return " + arg.string + ";})"); } else { params[name] = arg.string; } }; for(var m in this.info.params) { var param = this.info.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; }; Macro.prototype.hasParameter = function(name) { return $tw.utils.hop(this.params,name); }; Macro.prototype.cloneContent = function() { var contentClones = []; for(var t=0; t