1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-24 17:10:29 +00:00

Switched over to using new Renderer class

This commit is contained in:
Jeremy Ruston 2012-02-16 20:38:10 +00:00
parent fef0b9e4da
commit 0439f259b4
21 changed files with 300 additions and 1217 deletions

View File

@ -13,6 +13,7 @@ var WikiStore = require("./WikiStore.js").WikiStore,
Tiddler = require("./Tiddler.js").Tiddler,
tiddlerInput = require("./TiddlerInput.js"),
tiddlerOutput = require("./TiddlerOutput.js"),
Renderer = require("./Renderer.js").Renderer,
WikiTextParser = require("./WikiTextParser.js").WikiTextParser,
JSONParser = require("./JSONParser.js").JSONParser,
JavaScriptParser = require("./JavaScriptParser.js").JavaScriptParser,
@ -99,9 +100,8 @@ var App = function() {
// Set up navigation if we're in the browser
if(this.isBrowser) {
// Open the PageTemplate
var div = document.createElement("div");
this.store.renderTiddlerInNode(div,"PageTemplate");
document.body.appendChild(div);
var renderer = new Renderer("PageTemplate",null,this.store);
renderer.renderInDom(document.body);
// Set up a timer to change the value of a tiddler
var me = this;
window.setInterval(function() {
@ -112,7 +112,7 @@ var App = function() {
},3000);
// Register an event handler to handle refreshing the DOM
this.store.addEventListener("",function(changes) {
me.store.refreshDomNode(div,changes);
renderer.refreshInDom(changes);
});
}
};

View File

@ -1,278 +0,0 @@
/*\
title: js/HTML.js
Represents a fragment of HTML as a JavaScript object tree structure. Helper methods are provided to simplify
constructing HTML trees and to render the tree as an HTML string.
The nodes in the tree have a `type` field that is the name of the node for HTML elements:
{type: "br", attributes: {name: "value"}}
Attributes values can be strings, arrays of strings or hashmaps. String arrays are
rendered by joining them together with a space. Hashmaps are rendered as `attr="name1:value1;name2:value2;"`.
Elements with child nodes are expressed as:
{type: "div", children: [<childnodes>]}
Text nodes are represented as:
{type: "text", value: "A string"}
HTML entities are represented as:
{type: "entity", value: "quot"}
It is sometimes useful to be able to mix raw strings of HTML too:
{type: "raw", value: "<div>Something</div>"}
Other types of node can also be placed in the tree, but they will be ignored by the built-in render function.
For example, nodes of type `"macro"` are used by the WikiTextParser.
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("./Utils.js");
/*
Constructs an HTMLParseTree from a tree of nodes. A single node or an array of nodes can be passed.
As a shortcut, the constructor can be called as an ordinary function without the new keyword, in which case
it by default returns the `text/html` rendering of the tree.
*/
var HTML = function(tree,type) {
if(this instanceof HTML) {
// Called as a constructor
this.tree = tree;
} else {
// Called as a function
type = type || "text/html";
return (new HTML(tree)).render(type);
}
};
/*
Static method to simplify constructing an HTML element node
type: element name
attributes: hashmap of element attributes to add
options: hashmap of options
The attributes hashmap can contain strings or hashmaps of strings, (they are processed to attr="name1:value1;name2:value2;")
The options include:
content: a string to include as content in the element (also generates closing tag)
classes: an array of classnames to apply to the element
selfClosing: causes the element to be rendered with a trailing /, as in <br />
insertAfterAttributes: a string to insert after the attribute section of the element
*/
HTML.elem = function(type,attributes,children) {
var e = {type: type};
if(attributes) {
e.attributes = attributes;
}
if(children) {
e.children = children;
}
return e;
};
/*
Static method to construct a text node
*/
HTML.text = function(value) {
return {type: "text", value: value};
};
/*
Static method to construct an entity
*/
HTML.entity = function(value) {
return {type: "entity", value: value};
};
/*
Static method to construct a raw HTML node
*/
HTML.raw = function(value) {
return {type: "raw", value: value};
};
/*
Static method to construct a macro call
*/
HTML.macro = function(name,params,children,dependencies) {
var m = {type: "macro", name: name, params: params, dependencies: dependencies};
if(children) {
m.children = children;
}
return m;
};
/*
Static method to construct a label
*/
HTML.label = function(type,value,classes) {
classes = (classes || []).slice(0);
classes.push("label");
return HTML.elem("span",{
"class": classes,
"data-tw-label-type": type
},value);
};
/*
Static method to construct a split label
*/
HTML.splitLabel = function(type,left,right,classes) {
classes = (classes || []).slice(0);
classes.push("splitLabel");
return HTML.elem("span",{
"class": classes
},[
HTML.elem("span",{
"class": ["splitLabelLeft"],
"data-tw-label-type": type
},left),
HTML.elem("span",{
"class": ["splitLabelRight"]
},right)
]);
};
/*
Static method to construct a slider
*/
HTML.slider = function(type,label,tooltip,body) {
var attributes = {
"class": "tw-slider",
"data-tw-slider-type": type
};
if(tooltip) {
attributes.alt = tooltip;
attributes.title = tooltip;
}
return HTML.elem("div",
attributes,
[
HTML.elem("a",
{
"class": ["tw-slider-label"]
},[
HTML.text(label)
]
),
HTML.elem("div",
{
"class": ["tw-slider-body"],
"style": {"display": "none"}
},
body
)
]
);
};
/*
Render the HTML tree to a string, either of "text/html" or "text/plain"
*/
HTML.prototype.render = function(targetType) {
if(targetType == "text/plain") {
return this.renderPlain().join("");
} else if(targetType == "text/html") {
return this.renderHtml().join("");
} else {
return null;
}
};
/*
Render the HTML tree to a "text/html" string, returned as a string array
*/
HTML.prototype.renderHtml = function(output,node) {
output = output || [];
node = node || this.tree;
if(node instanceof Array) {
for(var t=0; t<node.length; t++) {
this.renderHtml(output,node[t]);
}
} else {
switch(node.type) {
case "text":
output.push(utils.htmlEncode(node.value));
break;
case "entity":
output.push(node.value);
break;
case "raw":
output.push(node.value);
break;
default:
output.push("<",node.type);
if(node.attributes) {
for(var a in node.attributes) {
var v = node.attributes[a];
if(v !== undefined) {
if(v instanceof Array) {
v = v.join(" ");
} else if(typeof v === "object") {
var s = [];
for(var p in v) {
s.push(p + ":" + v[p] + ";");
}
v = s.join("");
}
output.push(" ");
output.push(a);
output.push("='");
output.push(utils.htmlEncode(v));
output.push("'");
}
}
}
output.push(">");
if(node.children) {
this.renderHtml(output,node.children);
output.push("</",node.type,">");
}
break;
}
}
return output;
};
/*
Render the HTML tree to a "text/plain" string, returned as a string array
*/
HTML.prototype.renderPlain = function(output,node) {
output = output || [];
node = node || this.tree;
if(node instanceof Array) {
for(var t=0; t<node.length; t++) {
this.renderPlain(output,node[t]);
}
} else {
switch(node.type) {
case "text":
output.push(node.value);
break;
case "entity":
output.push(utils.entityDecode(node.value));
break;
case "raw":
output.push(node.value);
break;
default:
if(node.children) {
this.renderPlain(output,node.children);
}
break;
}
}
return output;
};
exports.HTML = HTML;
})();

View File

@ -10,7 +10,7 @@ Compiles images into JavaScript functions that render them in HTML
"use strict";
var WikiTextParseTree = require("./WikiTextParseTree.js").WikiTextParseTree,
HTML = require("./HTML.js").HTML,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js");
var ImageParser = function(options) {
@ -19,12 +19,12 @@ var ImageParser = function(options) {
ImageParser.prototype.parse = function(type,text) {
var src;
if(this.type === "image/svg+xml") {
if(type === "image/svg+xml") {
src = "data:" + type + "," + encodeURIComponent(text);
} else {
src = "data:" + type + ";base64," + text;
}
return new WikiTextParseTree([HTML.elem("img",{src: src})],{},this.store);
return new WikiTextParseTree([Renderer.ElementNode("img",{src: src})],{},this.store);
};
exports.ImageParser = ImageParser;

View File

@ -10,31 +10,31 @@ Compiles JSON objects into JavaScript functions that render them in HTML and pla
"use strict";
var WikiTextParseTree = require("./WikiTextParseTree.js").WikiTextParseTree,
HTML = require("./HTML.js").HTML,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js");
var renderObject = function(obj) {
var children = [],t;
if(obj instanceof Array) {
for(t=0; t<obj.length; t++) {
children.push(HTML.elem("li",{
children.push(Renderer.ElementNode("li",{
"class": ["jsonArrayMember"]
},[renderObject(obj[t])]));
}
return HTML.elem("ul",{
return Renderer.ElementNode("ul",{
"class": ["jsonArray"]
},children);
} else if(typeof obj === "object") {
for(t in obj) {
children.push(HTML.elem("li",{
children.push(Renderer.ElementNode("li",{
"class": ["jsonObjectMember"]
},[HTML.splitLabel("JSON",[HTML.text(t)],[renderObject(obj[t])])]));
},[Renderer.SplitLabelNode("JSON",[Renderer.TextNode(t)],[renderObject(obj[t])])]));
}
return HTML.elem("ul",{
return Renderer.ElementNode("ul",{
"class": ["jsonObject"]
},children);
} else {
return HTML.label("JSON" + (typeof obj),[HTML.text(JSON.stringify(obj))],["jsonValue"]);
return Renderer.LabelNode("JSON" + (typeof obj),[Renderer.TextNode(JSON.stringify(obj))],["jsonValue"]);
}
};

View File

@ -185,7 +185,7 @@ utils.entityDecode = function(s) {
if(c) {
return String.fromCharCode(c);
} else {
return null;
return s; // Couldn't convert it as an entity, just return it raw
}
}
};

View File

@ -13,18 +13,16 @@ WikiStore uses the .cache member of tiddlers to store the following information:
"use strict";
var Tiddler = require("./Tiddler.js").Tiddler,
HTML = require("./HTML.js").HTML,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js");
/* Creates a new WikiStore object
Available options are:
shadowStore: An existing WikiStore to use for shadow tiddler storage. Pass null to prevent a default shadow store from being created
disableHtmlWrapperNodes: If true will suppress the generation of the wrapper nodes used by the refresh and diagnostic machinery
*/
var WikiStore = function WikiStore(options) {
options = options || {};
this.disableHtmlWrapperNodes = !!options.disableHtmlWrapperNodes;
this.tiddlers = {}; // Hashmap of tiddlers by title
this.parsers = {}; // Hashmap of parsers by accepted MIME type
this.macros = {}; // Hashmap of macros by macro name
@ -265,229 +263,21 @@ WikiStore.prototype.parseTiddler = function(title) {
return parseTree;
};
/*
Compiles a block of text of a specified type into a renderer that renders the text in a particular MIME type
*/
WikiStore.prototype.compileText = function(type,text,targetType) {
var tree = this.parseText(type,text);
return tree.compile(targetType);
};
/*
Compiles a JavaScript function that renders a tiddler in a particular MIME type
*/
WikiStore.prototype.compileTiddler = function(title,type) {
var tiddler = this.getTiddler(title),
renderers = this.getCacheForTiddler(title,"renderers",function() {
return {};
});
if(tiddler) {
if(!renderers[type]) {
var tree = this.parseTiddler(title);
renderers[type] = tree.compile(type);
}
return renderers[type];
} else {
return null;
}
};
/*
Render a block of text of a specified type into a particular MIME type
*/
WikiStore.prototype.renderText = function(type,text,targetType,asTitle,options) {
options = options || {};
var noWrap = options.noWrap || this.disableHtmlWrapperNodes,
tiddler = this.getTiddler(asTitle),
renderer = this.compileText(type,text,targetType),
content = renderer.render(tiddler,this);
return noWrap ? content : HTML(HTML.elem("div",{
"data-tw-render-text": true,
"data-tw-render-as": asTitle
},[HTML.raw(content)]),targetType);
};
/*
Render a tiddler to a particular MIME type
targetType: target MIME type
title: title of the tiddler to render
template: optional title of the tiddler to use as a template
options: see below
Options include:
noWrap: Suppress the outer refresh wrapper nodes
*/
WikiStore.prototype.renderTiddler = function(targetType,title,templateTitle,options) {
options = options || {};
if(typeof templateTitle !== "string") {
templateTitle = title;
}
var noWrap = options.noWrap || this.disableHtmlWrapperNodes,
tiddler = this.getTiddler(title),
renderer = this.compileTiddler(templateTitle,targetType),
template,
content;
if(tiddler) {
content = renderer.render(tiddler,this);
if(title !== templateTitle) {
template = this.getTiddler(templateTitle);
return noWrap ? content : HTML(HTML.elem("div",{
"data-tw-render-tiddler": title,
"data-tw-render-template": templateTitle
},[HTML.raw(content)]),targetType);
} else {
return noWrap ? content : HTML(HTML.elem("div",{
"data-tw-render-tiddler": title
},[HTML.raw(content)]),targetType);
}
}
return null;
};
/*
Renders a tiddler and inserts the HTML into a DOM node, and then attaches the event handlers needed by macros
*/
WikiStore.prototype.renderTiddlerInNode = function(node,title,templateTitle,options) {
node.innerHTML = this.renderTiddler("text/html",title,templateTitle,options);
this.attachEventHandlers(node,title,templateTitle);
};
/*
Recursively attach macro event handlers for a node and its children
*/
WikiStore.prototype.attachEventHandlers = function(node,renderTiddler,renderTemplate) {
var me = this,
dispatchMacroEvent = function(event) {
var renderer = me.compileTiddler(renderTemplate ? renderTemplate : renderTiddler,"text/html"),
macroName = node.getAttribute("data-tw-macro"),
macro = me.macros[macroName],
step = node.getAttribute("data-tw-render-step");
return macro.events[event.type](event,node,renderTiddler,me,renderer.renderSteps[step].params(renderTiddler,renderer,me,utils));
};
if(node.getAttribute) {
var macroName = node.getAttribute("data-tw-macro");
if(typeof macroName === "string") {
var macro = this.macros[macroName];
if(macro.events) {
for(var e in macro.events) {
node.addEventListener(e,dispatchMacroEvent,false);
}
}
}
if(node.hasAttribute("data-tw-render-tiddler")) {
renderTiddler = node.getAttribute("data-tw-render-tiddler");
renderTemplate = node.getAttribute("data-tw-render-template");
}
}
if(node.hasChildNodes) {
for(var t=0; t<node.childNodes.length; t++) {
this.attachEventHandlers(node.childNodes[t],renderTiddler,renderTemplate);
}
}
WikiStore.prototype.renderTiddler = function(targetType,title,templateTitle) {
var r = new Renderer(title,templateTitle,this);
return r.render(targetType);
};
WikiStore.prototype.installMacro = function(macro) {
this.macros[macro.name] = macro;
};
/*
Executes a macro and returns the result
*/
WikiStore.prototype.renderMacro = function(macroName,targetType,tiddler,params,content) {
var macro = this.macros[macroName];
if(macro) {
return macro.render(targetType,tiddler,this,params,content);
} else {
return null;
}
};
/*
Re-renders a macro into a node
*/
WikiStore.prototype.rerenderMacro = function(node,changes,macroName,targetType,tiddler,params,content) {
var macro = this.macros[macroName];
if(macro) {
if(macro.rerender) {
macro.rerender(node,changes,targetType,tiddler,this,params,content);
} else {
node.innerHTML = macro.render(targetType,tiddler,this,params,content);
}
}
};
/*
Refresh a DOM node and it's children so that it reflects the current state of the store
node: reference to the DOM node to be refreshed
changes: hashmap of {title: "created|modified|deleted"}
renderer: the renderer to use to refresh the node (usually pass null)
tiddler: the tiddler to use as the context for executing the renderer
*/
WikiStore.prototype.refreshDomNode = function(node,changes,renderer,tiddler) {
var me = this,
refreshChildNodes = function(node,renderer,tiddler) {
if(node.hasChildNodes()) {
for(var c=0; c<node.childNodes.length; c++) {
me.refreshDomNode(node.childNodes[c],changes,renderer,tiddler);
}
}
};
// Get all the various attributes we need
var renderTiddler = node.getAttribute ? node.getAttribute("data-tw-render-tiddler") : null,
renderTemplate = node.getAttribute ? node.getAttribute("data-tw-render-template") : null,
macro = node.getAttribute ? node.getAttribute("data-tw-macro") : null,
renderStep = node.getAttribute ? node.getAttribute("data-tw-render-step") : null;
// Is this node the rendering of a tiddler?
if(renderTiddler !== null) {
// Rerender the content of the node if the tiddler being rendered or its template has changed
if(changes.hasOwnProperty(renderTiddler) || (renderTemplate && changes.hasOwnProperty(renderTemplate))) {
this.renderTiddlerInNode(node,renderTiddler,renderTemplate,{noWrap: true});
} else {
// If it hasn't changed, just refresh the child nodes
if(typeof renderTemplate !== "string") {
renderTemplate = renderTiddler;
}
refreshChildNodes(node,this.compileTiddler(renderTemplate,"text/html"),this.getTiddler(renderTiddler));
}
// Is this node a macro
} else if(macro !== null) {
// Get the render step
var r = renderer.renderSteps[renderStep];
// Refresh if a dependency has changed
if(this.hasDependencyChanged(r.dependencies,changes)) {
renderer.rerender(node,changes,tiddler,this,renderStep);
} else {
// If no change, just refresh the children
refreshChildNodes(node,renderer,tiddler);
}
// If it's not a macro or a tiddler rendering, just process any child nodes
} else {
refreshChildNodes(node,renderer,tiddler);
}
};
/*
Check if the specified dependencies are impacted by the specified changes
dependencies: a dependencies tree
changes: an array of titles of changed tiddlers
*/
WikiStore.prototype.hasDependencyChanged = function(dependencies,changes) {
if(dependencies.dependentAll) {
return true;
}
for(var rel in dependencies) {
if(rel !== "dependentAll") {
for(var t in dependencies[rel]) {
if(changes.hasOwnProperty(t)) {
return true;
}
}
}
}
return false;
};
exports.WikiStore = WikiStore;
})();

View File

@ -9,8 +9,7 @@ A container for the parse tree generated by parsing wikitext
/*jslint node: true */
"use strict";
var WikiTextRenderer = require("./WikiTextRenderer.js").WikiTextRenderer,
HTML = require("./HTML.js").HTML,
var Renderer = require("./Renderer.js").Renderer,
ArgParser = require("./ArgParser.js").ArgParser,
utils = require("./Utils.js");
@ -21,373 +20,6 @@ var WikiTextParseTree = function(tree,dependencies,store) {
this.store = store;
};
// Compile the parse tree into a JavaScript function that returns the required
// representation of the tree
WikiTextParseTree.prototype.compile = function(type,treenode) {
/*jslint evil: true */
treenode = treenode || this.tree;
var renderer = new WikiTextRenderer(),
renderStep = {},
renderStepIndex = renderer.addRenderStep(renderStep),
output = [];
if(type === "text/html") {
this.compileSubTreeHtml(output,renderer,treenode);
} else if(type === "text/plain") {
this.compileSubTreePlain(output,renderer,treenode);
} else {
return null;
}
// Create the parse tree for the rendering step function definition
var parseTree = this.store.jsParser.createTree(
[
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [
{
type: "ReturnStatement",
value: {
type: "FunctionCall",
name: {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: output
},
name: "join"
},
"arguments": [ {
type: "StringLiteral",
value: ""
}
]
}
}
]
}
]);
renderStep.type = "main";
renderStep.step = renderStepIndex;
renderStep.dependencies = {};
renderStep.handler = parseTree.compile("application/javascript").render;
return renderer;
};
var pushString = function(output,s) {
var last = output[output.length-1];
if(output.length > 0 && last.type === "StringLiterals") {
last.value.push(s);
} else if (output.length > 0 && last.type === "StringLiteral") {
last.type = "StringLiterals";
last.value = [last.value,s];
} else {
output.push({type: "StringLiteral", value: s});
}
};
WikiTextParseTree.prototype.compileMacroCall = function(output,renderer,type,node) {
/*jslint evil: true */
var name = node.name,
params = node.params,
macro = this.store.macros[name],
p,
n,
renderStep = {},
renderStepIndex = renderer.addRenderStep(renderStep);
// Check for errors
if(!macro) {
pushString(output,"{{** Unknown macro '" + name + "' **}}");
return;
}
if(macro.types.indexOf(type) === -1) {
pushString(output,"{{** Macro '" + name + "' cannot render to MIME type '" + type + "'**}}");
return;
}
renderStep.type = "macro";
renderStep.macro = name;
renderStep.renderType = type;
renderStep.step = renderStepIndex;
renderStep.dependencies = node.dependencies;
// Slot the parameters into the macro call
var properties = [];
for(p in params) {
if(params[p].type === "string") {
n = {type: "StringLiteral", value: params[p].value};
} else {
n = this.store.jsParser.parse(params[p].value).tree.elements[0];
}
properties.push({
type: "PropertyAssignment",
name: p,
value: n
});
}
renderStep.params = this.store.jsParser.createTree([
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [ {
type: "ReturnStatement",
value: {
type: "ObjectLiteral",
properties: properties
}
} ]
}
]).compile("application/javascript").render;
// Compile any child nodes
var subOutput = [];
if(node.children) {
this.compileSubTreeHtml(subOutput,renderer,node.children);
}
renderStep.content = this.store.jsParser.createTree([
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [
{
type: "ReturnStatement",
value: {
type: "FunctionCall",
name: {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: subOutput
},
name: "join"
},
"arguments": [ {
type: "StringLiteral",
value: ""
}
]
}
}
]
}
]).compile("application/javascript").render;
// Add the wrapper node
var wrapperTag = macro.wrapperTag || "div";
if(type === "text/html" && !this.store.disableHtmlWrapperNodes) {
pushString(output,HTML(HTML.elem(wrapperTag,{
"data-tw-macro": name,
"data-tw-render-step": renderStepIndex
})));
}
// Output the macro call
output.push({
type: "FunctionCall",
name: {
base: {
name: "renderer",
type: "Variable"},
name: "render",
type: "PropertyAccess"},
"arguments": [ {
type: "Variable",
name: "tiddler"
},{
type: "Variable",
name: "store"
},{
type: "NumericLiteral",
value: renderStepIndex
}]
});
if(type === "text/html" && !this.store.disableHtmlWrapperNodes) {
pushString(output,"</" + wrapperTag + ">");
}
};
WikiTextParseTree.prototype.compileElementHtml = function(output,renderer,element,options) {
options = options || {};
pushString(output,HTML(HTML.elem(element.type,element.attributes)));
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreeHtml(output,renderer,element.children);
}
pushString(output,"</" + element.type + ">");
}
};
WikiTextParseTree.prototype.compileSubTreeHtml = function(output,renderer,tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(output,utils.htmlEncode(tree[t].value));
break;
case "entity":
pushString(output,tree[t].value);
break;
case "br":
case "img":
this.compileElementHtml(output,renderer,tree[t],{selfClosing: true}); // Self closing elements
break;
case "macro":
this.compileMacroCall(output,renderer,"text/html",tree[t]);
break;
default:
this.compileElementHtml(output,renderer,tree[t]);
break;
}
}
};
WikiTextParseTree.prototype.compileElementPlain = function(output,renderer,element,options) {
options = options || {};
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreePlain(output,renderer,element.children);
}
}
};
WikiTextParseTree.prototype.compileSubTreePlain = function(output,renderer,tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(output,tree[t].value);
break;
case "entity":
var c = utils.entityDecode(tree[t].value);
if(c) {
pushString(output,c);
} else {
pushString(output,tree[t].value);
}
break;
case "br":
case "img":
this.compileElementPlain(output,renderer,tree[t],{selfClosing: true}); // Self closing elements
break;
case "macro":
this.compileMacroCall(output,renderer,"text/plain",tree[t]);
break;
default:
this.compileElementPlain(output,renderer,tree[t]);
break;
}
}
};
// Render the parse tree to a debugging string of the specified MIME type
WikiTextParseTree.prototype.toString = function(type) {
var renderNode,
renderArray = function(tree) {
var children = [];
for(var t=0; t<tree.length; t++) {
children.push(HTML.elem("li",{
"class": ["nodeWikiText"]
},renderNode(tree[t])));
}
return HTML.elem("ul",{
"class": ["treeWikiText"]
},children);
},
renderTextNode = function(node) {
return [HTML.splitLabel(
node.type,
[HTML.text(node.type)],
[HTML.raw(utils.htmlEncode(node.value).replace(/\n/g,"<br>"))],
["treeNode"]
)];
},
renderDependencies = function(dependencies) {
var output = [];
for(var d in dependencies) {
if(d === "dependentAll") {
output.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(dependencies[d])]));
} else {
var dependents = [];
for(var t in dependencies[d]) {
dependents.push(t);
}
output.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(dependents.join(", "))]));
}
}
return HTML.splitLabel(
"dependencies",
[HTML.text("Dependencies")],
output
);
},
renderMacroNode = function(node) {
var params = [],
ret = [];
for(var p in node.params) {
var v = node.params[p].value;
if(node.params[p].type === "eval") {
v = "{{" + v + "}}";
}
params.push(HTML.splitLabel(
"param",
[HTML.text(p)],
[HTML.text(v)]
));
}
ret.push(HTML.splitLabel(
"macro",
[HTML.text(node.name)],
params,
["treeNode"]
));
if(node.dependencies) {
ret.push(renderDependencies(node.dependencies));
}
if(node.children) {
ret.push(renderArray(node.children));
}
return ret;
},
renderHtmlNode = function(node) {
var attributes = [],
ret = [];
for(var a in node.attributes) {
var v = node.attributes[a];
if(typeof v === "string") {
v = v;
} else if(v instanceof Array) {
v = v.join("; ");
} else if(typeof v === "object") {
var o = [];
for(var n in v) {
o.push(n,":",v[n],";");
}
v = o.join("");
}
attributes.push(HTML.splitLabel(
"attribute",
[HTML.text(a)],
[HTML.text(v)]
));
}
ret.push(HTML.splitLabel(
"html",
[HTML.text(node.type)],
attributes,
["treeNode"]
));
if(node.children) {
ret.push(renderArray(node.children));
}
return ret;
};
renderNode = function(node) {
if(node.type === "text") {
return renderTextNode(node);
} else if(node.type === "macro") {
return renderMacroNode(node);
} else {
return renderHtmlNode(node);
}
};
return HTML(renderDependencies(this.dependencies),type) + HTML(renderArray(this.tree),type);
};
exports.WikiTextParseTree = WikiTextParseTree;
})();

View File

@ -33,7 +33,7 @@ HTML nodes look like this:
var WikiTextRules = require("./WikiTextRules.js"),
WikiTextParseTree = require("./WikiTextParseTree.js").WikiTextParseTree,
HTML = require("./HTML.js").HTML,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js"),
util = require("util");
@ -97,7 +97,7 @@ WikiTextParser.prototype.mergeDependencies = function(newDependencies) {
WikiTextParser.prototype.outputText = function(place,startPos,endPos) {
if(startPos < endPos) {
place.push(HTML.text(this.source.substring(startPos,endPos)));
place.push(Renderer.TextNode(this.source.substring(startPos,endPos)));
}
};

View File

@ -1,136 +0,0 @@
/*\
title: js/WikiTextRenderer.js
An array of JavaScript functions that generate a specified representation of a parse tree
\*/
(function(){
/*jslint node: true */
"use strict";
var HTML = require("./HTML.js").HTML,
utils = require("./Utils.js");
var WikiTextRenderer = function() {
this.renderSteps = []; // Array of {step: n, renderType: "main"|"macro", dependencies: {},handler: function(tiddler,renderer,store,utils) {}}
};
WikiTextRenderer.prototype.addRenderStep = function(renderStep) {
this.renderSteps.push(renderStep);
return this.renderSteps.length - 1;
};
WikiTextRenderer.prototype.render = function(tiddler,store,renderStep) {
renderStep = renderStep || 0;
var step = this.renderSteps[renderStep];
if(renderStep < this.renderSteps.length) {
switch(step.type) {
case "main":
return step.handler(tiddler,this,store,utils);
case "macro":
return store.renderMacro(step.macro,
step.renderType,
tiddler,
step.params(tiddler,this,store,utils),
step.content(tiddler,this,store,utils));
}
} else {
return null;
}
};
WikiTextRenderer.prototype.rerender = function(node,changes,tiddler,store,renderStep) {
renderStep = renderStep || 0;
var step = this.renderSteps[renderStep];
if(renderStep < this.renderSteps.length) {
switch(step.type) {
case "main":
node.innerHTML = step.handler(tiddler,this,store,utils);
break;
case "macro":
store.rerenderMacro(node,changes,step.macro,
step.renderType,
tiddler,
step.params(tiddler,this,store,utils),
step.content(tiddler,this,store,utils));
break;
}
}
};
WikiTextRenderer.prototype.toString = function(type) {
var renderNode,
renderArray = function(tree) {
var children = [];
for(var t=0; t<tree.length; t++) {
children.push(HTML.elem("li",{
"class": ["nodeWikiTextRenderer"]
},renderNode(tree[t])));
}
return HTML.elem("ul",{
"class": ["treeWikiTextRenderer"]
},children);
};
renderNode = function(node) {
var ret = [];
ret.push(HTML.splitLabel(
"rendererStep",
[HTML.text(node.step.toString())],
[HTML.text(node.type.toString())]
));
if(node.macro) {
ret.push(HTML.splitLabel(
"macro",
[HTML.text("macro")],
[HTML.text(node.macro)]
));
}
if(node.params) {
ret.push(HTML.splitLabel(
"params",
[HTML.text("params")],
[HTML.raw(utils.htmlEncode(node.params.toString()).replace(/\n/g,"<br>"))]
));
}
if(node.dependencies) {
var dependencies = [];
for(var d in node.dependencies) {
if(d === "dependentAll") {
dependencies.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(node.dependencies[d])]));
} else {
var dependents = [];
for(var t in node.dependencies[d]) {
dependents.push(t);
}
dependencies.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(dependents.join(","))]));
}
}
ret.push(HTML.splitLabel(
"dependencies",
[HTML.text("Dependencies")],
dependencies
));
}
if(node.content) {
ret.push(HTML.splitLabel(
"content",
[HTML.text("content")],
[HTML.raw(utils.htmlEncode(node.content.toString()).replace(/\n/g,"<br>"))]
));
}
if(node.handler) {
ret.push(HTML.splitLabel(
"handler",
[HTML.text("handler")],
[HTML.raw(utils.htmlEncode(node.handler.toString()).replace(/\n/g,"<br>"))]
));
}
return ret;
};
return HTML(renderArray(this.renderSteps),type);
};
exports.WikiTextRenderer = WikiTextRenderer;
})();

View File

@ -8,7 +8,7 @@ title: js/WikiTextRules.js
"use strict";
var ArgParser = require("./ArgParser.js").ArgParser,
HTML = require("./HTML.js").HTML,
Renderer = require("./Renderer.js").Renderer,
util = require("util");
var textPrimitives = {
@ -97,11 +97,38 @@ var enclosedTextHelper = function(w) {
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
w.output.push(HTML.elem(this.element,null,[HTML.text(text)]));
w.output.push(Renderer.ElementNode(this.element,null,[Renderer.TextNode(text)]));
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
};
var compileMacroParams = function(w,params) {
// Slot the parameters into the macro call
var properties = [],n;
for(var p in params) {
if(params[p].type === "string") {
n = {type: "StringLiteral", value: params[p].value};
} else {
n = w.store.jsParser.parse(params[p].value).tree.elements[0];
}
properties.push({type: "PropertyAssignment",name: p,value: n});
}
return w.store.jsParser.createTree([
{
type: "Function",
name: null,
params: ["tiddler","store","utils"], // These are the parameters passed to the parameter expressions
elements: [ {
type: "ReturnStatement",
value: {
type: "ObjectLiteral",
properties: properties
}
} ]
}
]).compile("application/javascript").render;
};
var insertMacroCall = function(w,output,name,params,children) {
var macro = w.store.macros[name],
dependencies = {};
@ -131,7 +158,7 @@ var insertMacroCall = function(w,output,name,params,children) {
}
}
w.mergeDependencies(dependencies);
output.push(HTML.macro(name,params,children,dependencies));
output.push(Renderer.MacroNode(name,compileMacroParams(w,params),children,dependencies,w.store));
}
};
@ -174,7 +201,7 @@ var rules = [
rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
handler: function(w)
{
var table = HTML.elem("table",{"class": "twtable"},[]);
var table = Renderer.ElementNode("table",{"class": "twtable"},[]);
w.output.push(table);
var prevColumns = [];
var currRowType = null;
@ -190,7 +217,7 @@ var rules = [
w.nextMatch += lookaheadMatch[0].length+1;
} else {
if(nextRowType != currRowType) {
rowContainer = HTML.elem(this.rowTypes[nextRowType],{},[]);
rowContainer = Renderer.ElementNode(this.rowTypes[nextRowType],{},[]);
table.children.push(rowContainer);
currRowType = nextRowType;
}
@ -205,7 +232,7 @@ var rules = [
rowContainer.attributes.align = rowCount === 0 ? "top" : "bottom";
w.subWikifyTerm(rowContainer.children,this.rowTermRegExp);
} else {
var theRow = HTML.elem("tr",{},[]);
var theRow = Renderer.ElementNode("tr",{},[]);
theRow.attributes["class"] = rowCount%2 ? "oddRow" : "evenRow";
rowContainer.children.push(theRow);
this.rowHandler(w,theRow.children,prevColumns);
@ -261,11 +288,11 @@ var rules = [
}
var cell;
if(chr == "!") {
cell = HTML.elem("th",{},[]);
cell = Renderer.ElementNode("th",{},[]);
e.push(cell);
w.nextMatch++;
} else {
cell = HTML.elem("td",{},[]);
cell = Renderer.ElementNode("td",{},[]);
e.push(cell);
}
prevCell = cell;
@ -295,7 +322,7 @@ var rules = [
termRegExp: /(\n)/mg,
handler: function(w)
{
var e = HTML.elem("h" + w.matchLength,{},[]);
var e = Renderer.ElementNode("h" + w.matchLength,{},[]);
w.output.push(e);
w.subWikifyTerm(e.children,this.termRegExp);
}
@ -339,7 +366,7 @@ var rules = [
if(currLevel !== 0 && target.children) {
target = target.children[target.children.length-1];
}
e = HTML.elem(listType,{},[]);
e = Renderer.ElementNode(listType,{},[]);
target.push(e);
stack.push(e.children);
}
@ -351,13 +378,13 @@ var rules = [
stack.pop();
} else if(listLevel == currLevel && listType != currType) {
stack.pop();
e = HTML.elem(listType,{},[]);
e = Renderer.ElementNode(listType,{},[]);
stack[stack.length-1].push(e);
stack.push(e.children);
}
currLevel = listLevel;
currType = listType;
e = HTML.elem(itemType,{},[]);
e = Renderer.ElementNode(itemType,{},[]);
stack[stack.length-1].push(e);
w.subWikifyTerm(e.children,this.termRegExp);
this.lookaheadRegExp.lastIndex = w.nextMatch;
@ -372,7 +399,7 @@ var rules = [
termRegExp: /(^<<<(\n|$))/mg,
element: "blockquote",
handler: function(w) {
var e = HTML.elem(this.element,{},[]);
var e = Renderer.ElementNode(this.element,{},[]);
w.output.push(e);
w.subWikifyTerm(e.children,this.termRegExp);
}
@ -393,7 +420,7 @@ var rules = [
do {
if(newLevel > currLevel) {
for(t=currLevel; t<newLevel; t++) {
e = HTML.elem(this.element,{},[]);
e = Renderer.ElementNode(this.element,{},[]);
stack[stack.length-1].push(e);
}
} else if(newLevel < currLevel) {
@ -402,7 +429,7 @@ var rules = [
}
currLevel = newLevel;
w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
stack[stack.length-1].push(HTML.elem("br"));
stack[stack.length-1].push(Renderer.ElementNode("br"));
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
@ -419,7 +446,7 @@ var rules = [
match: "^----+$\\n?|<hr ?/?>\\n?",
handler: function(w)
{
w.output.push(HTML.elem("hr"));
w.output.push(Renderer.ElementNode("hr"));
}
},
@ -492,7 +519,7 @@ var rules = [
}
insertMacroCall(w,w.output,"link",{
target: {type: "string", value: link}
},[HTML.text(text)]);
},[Renderer.TextNode(text)]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
@ -519,7 +546,7 @@ var rules = [
if(w.autoLinkWikiWords) {
insertMacroCall(w,w.output,"link",{
target: {type: "string", value: w.matchText}
},[HTML.text(w.source.substring(w.matchStart,w.nextMatch))]);
},[Renderer.TextNode(w.source.substring(w.matchStart,w.nextMatch))]);
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
@ -533,7 +560,7 @@ var rules = [
{
insertMacroCall(w,w.output,"link",{
target: {type: "string", value: w.matchText}
},[HTML.text(w.source.substring(w.matchStart,w.nextMatch))]);
},[Renderer.TextNode(w.source.substring(w.matchStart,w.nextMatch))]);
}
},
@ -584,7 +611,7 @@ var rules = [
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(HTML.elem("html",{},[HTML.raw(lookaheadMatch[1])]));
w.output.push(Renderer.ElementNode("html",{},[Renderer.RawNode(lookaheadMatch[1])]));
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
@ -611,32 +638,32 @@ var rules = [
var e,lookaheadRegExp,lookaheadMatch;
switch(w.matchText) {
case "''":
e = HTML.elem("strong",null,[]);
e = Renderer.ElementNode("strong",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/('')/mg);
break;
case "//":
e = HTML.elem("em",null,[]);
e = Renderer.ElementNode("em",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(\/\/)/mg);
break;
case "__":
e = HTML.elem("u",null,[]);
e = Renderer.ElementNode("u",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(__)/mg);
break;
case "^^":
e = HTML.elem("sup",null,[]);
e = Renderer.ElementNode("sup",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(\^\^)/mg);
break;
case "~~":
e = HTML.elem("sub",null,[]);
e = Renderer.ElementNode("sub",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(~~)/mg);
break;
case "--":
e = HTML.elem("strike",null,[]);
e = Renderer.ElementNode("strike",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(--)/mg);
break;
@ -645,7 +672,7 @@ var rules = [
lookaheadRegExp.lastIndex = w.matchStart;
lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(HTML.elem("code",null,[HTML.text(lookaheadMatch[1])]));
w.output.push(Renderer.ElementNode("code",null,[Renderer.TextNode(lookaheadMatch[1])]));
w.nextMatch = lookaheadRegExp.lastIndex;
}
break;
@ -654,7 +681,7 @@ var rules = [
lookaheadRegExp.lastIndex = w.matchStart;
lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(HTML.elem("code",null,[HTML.text(lookaheadMatch[1])]));
w.output.push(Renderer.ElementNode("code",null,[Renderer.TextNode(lookaheadMatch[1])]));
w.nextMatch = lookaheadRegExp.lastIndex;
}
break;
@ -669,7 +696,7 @@ var rules = [
{
switch(w.matchText) {
case "@@":
var e = HTML.elem("span",null,[]);
var e = Renderer.ElementNode("span",null,[]);
w.output.push(e);
var styles = inlineCssHelper(w);
if(styles.length === 0)
@ -684,7 +711,7 @@ var rules = [
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch) {
w.nextMatch = lookaheadRegExp.lastIndex;
e = HTML.elem(lookaheadMatch[2] == "\n" ? "div" : "span",{
e = Renderer.ElementNode(lookaheadMatch[2] == "\n" ? "div" : "span",{
"class": lookaheadMatch[1]
},[]);
w.output.push(e);
@ -700,7 +727,7 @@ var rules = [
match: "--",
handler: function(w)
{
w.output.push(HTML.entity("&mdash;"));
w.output.push(Renderer.EntityNode("&mdash;"));
}
},
@ -709,7 +736,7 @@ var rules = [
match: "\\n|<br ?/?>",
handler: function(w)
{
w.output.push(HTML.elem("br"));
w.output.push(Renderer.ElementNode("br"));
}
},
@ -722,7 +749,7 @@ var rules = [
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(HTML.text(lookaheadMatch[1]));
w.output.push(Renderer.TextNode(lookaheadMatch[1]));
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
@ -733,7 +760,7 @@ var rules = [
match: "&#?[a-zA-Z0-9]{2,8};",
handler: function(w)
{
w.output.push(HTML.entity(w.matchText));
w.output.push(Renderer.EntityNode(w.matchText));
}
}

View File

@ -7,7 +7,7 @@ title: js/macros/echo.js
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "echo",
@ -15,12 +15,8 @@ exports.macro = {
params: {
text: {byPos: 0, type: "text", optional: false}
},
render: function(type,tiddler,store,params) {
if(type === "text/html") {
return utils.htmlEncode(params.text);
} else {
return params.text;
}
execute: function(macroNode,tiddler,store) {
return [Renderer.TextNode(macroNode.params.text)];
}
};

View File

@ -7,8 +7,7 @@ title: js/macros/image.js
/*jslint node: true */
"use strict";
var HTML = require("../HTML.js").HTML,
utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "image",
@ -18,28 +17,27 @@ exports.macro = {
text: {byName: true, type: "text", optional: true},
alignment: {byName: true, type: "text", optional: true}
},
render: function(type,tiddler,store,params) {
if(type === "text/html") {
if(store.tiddlerExists(params.src)) {
if(params.text) {
return HTML(HTML.elem("div",{
alt: params.text,
title: params.text
},[
HTML.raw(store.renderTiddler(type,params.src))
]));
execute: function(macroNode,tiddler,store) {
if(store.tiddlerExists(macroNode.params.src)) {
var imageTree = store.parseTiddler(macroNode.params.src).tree,
cloneImage = [];
for(var t=0; t<imageTree.length; t++) {
cloneImage.push(imageTree[t].clone());
}
if(macroNode.params.text) {
return [Renderer.ElementNode("div",{
alt: macroNode.params.text,
title: macroNode.params.text
},cloneImage)];
} else {
return store.renderTiddler(type,params.src);
return cloneImage;
}
} else {
return HTML(HTML.elem("img",{
href: params.src,
alt: params.text,
title: params.text
}));
}
} else if (type === "text/plain") {
return params.text ? params.text : "";
return [Renderer.ElementNode("img",{
href: macroNode.params.src,
alt: macroNode.params.text,
title: macroNode.params.text
})];
}
}
};

View File

@ -7,7 +7,7 @@ title: js/macros/info.js
/*jslint node: true */
"use strict";
var HTML = require("../HTML.js").HTML,
var Renderer = require("../Renderer.js").Renderer,
utils = require("../Utils.js");
exports.macro = {
@ -31,16 +31,16 @@ exports.macro = {
var parseTree = store.parseTiddler(tiddler.title);
switch(info) {
case "parsetree":
return HTML(HTML.slider("parsetree",
return [Renderer.SliderNode("parsetree",
"Parse tree",
"The parse tree for this tiddler",
HTML.raw(parseTree.toString(type))),type);
(parseTree.toString(type)))];
//break;
case "compiled":
return HTML(HTML.slider("compiled",
return [Renderer.SliderNode("compiled",
"Render functions",
"The render functions for this tiddler",
HTML.raw(parseTree.compile(type).toString(type))),type);
(parseTree.compile(type).toString(type)))];
//break;
}
}

View File

@ -7,8 +7,7 @@ title: js/macros/link.js
/*jslint node: true, browser: true */
"use strict";
var HTML = require("../HTML.js").HTML,
utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
var isLinkExternal = function(target) {
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data):[^\s'"]+(?:\/|\b)/i;
@ -23,42 +22,43 @@ exports.macro = {
target: {byName: "default", type: "tiddler", rel: "link", optional: false}
},
events: {
click: function(event,node,tiddler,store,params) {
if(isLinkExternal(params.target)) {
click: function(event,macroNode) {
if(isLinkExternal(macroNode.params.target)) {
event.target.setAttribute("target","_blank");
return true;
} else {
var navEvent = document.createEvent("Event");
navEvent.initEvent("tw-navigate",true,true);
navEvent.navigateTo = params.target;
node.dispatchEvent(navEvent);
navEvent.navigateTo = macroNode.params.target;
event.target.dispatchEvent(navEvent);
event.preventDefault();
return false;
}
}
},
render: function(type,tiddler,store,params,content) {
execute: function(macroNode,tiddler,store) {
var classes = ["tw-tiddlylink"],
target = params.target;
if(isLinkExternal(params.target)) {
target = macroNode.params.target;
if(isLinkExternal(target)) {
classes.push("tw-tiddlylink-external");
} else {
classes.push("tw-tiddlylink-internal");
if(store.tiddlerExists(params.target)) {
if(store.tiddlerExists(target)) {
classes.push("tw-tiddlylink-resolves");
} else {
classes.push("tw-tiddlylink-missing");
}
target = encodeURIComponent(target);
}
return HTML(HTML.elem(
var content = [Renderer.ElementNode(
"a",{
href: target,
"class": classes
},[
HTML.raw(content)
]
),type);
},macroNode.cloneChildren())];
for(var t=0; t<content.length; t++) {
content[t].execute(tiddler);
}
return content;
}
};

View File

@ -7,7 +7,7 @@ title: js/macros/list.js
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
var handlers = {
all: function(store) {
@ -41,35 +41,33 @@ exports.macro = {
template: {byName: true, type: "tiddler", optional: true},
emptyMessage: {byName: true, type: "text", optional: true}
},
render: function(type,tiddler,store,params) {
execute: function(macroNode,tiddler,store) {
var templateType = "text/x-tiddlywiki",
templateText = "<<view title link>>",
template = params.template ? store.getTiddler(params.template) : null,
output = [],
isHtml = type === "text/html",
encoder = isHtml ? utils.htmlEncode : function(x) {return x;},
pushTag = isHtml ? function(x) {output.push(x);} : function(x) {},
handler,
template = macroNode.params.template ? store.getTiddler(macroNode.params.template) : null,
content = [],
t;
if(template) {
templateType = template.type;
templateText = template.text;
}
handler = handlers[params.type];
var handler = handlers[macroNode.params.type];
handler = handler || handlers.all;
var tiddlers = handler(store);
if(tiddlers.length === 0) {
return params.emptyMessage ? encoder(params.emptyMessage) : "";
return [Renderer.TextNode(macroNode.params.emptyMessage || "")];
} else {
var renderer = store.compileText(templateType,templateText,type);
pushTag("<ul>");
var templateTree = store.parseText(templateType,templateText).tree;
for(t=0; t<tiddlers.length; t++) {
pushTag("<li>");
output.push(renderer.render(store.getTiddler(tiddlers[t]),store));
pushTag("</li>");
var cloneTemplate = [];
for(var c=0; c<templateTree.length; c++) {
cloneTemplate.push(templateTree[c].clone());
}
pushTag("</ul>");
return output.join("");
var listNode = Renderer.ElementNode("li",null,cloneTemplate);
listNode.execute(store.getTiddler(tiddlers[t]));
content.push(listNode);
}
return [Renderer.ElementNode("ul",null,content)];
}
}
};

View File

@ -7,7 +7,7 @@ title: js/macros/slider.js
/*jslint node: true */
"use strict";
var HTML = require("../HTML.js").HTML,
var Renderer = require("../Renderer.js").Renderer,
utils = require("../Utils.js");
exports.macro = {
@ -20,23 +20,25 @@ exports.macro = {
tooltip: {byPos: 3, type: "text", optional: true}
},
events: {
click: function(event,node,tiddler,store,params) {
var el = node.firstChild.firstChild.nextSibling;
click: function(event,macroNode) {
var el = event.currentTarget.firstChild.firstChild.nextSibling;
el.style.display = el.style.display === "block" ? "none" : "block";
event.preventDefault();
return false;
}
},
render: function(type,tiddler,store,params) {
if(type === "text/html") {
return HTML(HTML.slider(params.name,
params.label,
params.tooltip,
HTML.raw(store.renderTiddler(type,params.targetTiddler))),type);
} else if(type === "text/plain") {
return store.renderTiddler(type,params.target);
}
return null;
execute: function(macroNode,tiddler,store) {
var target = macroNode.params.targetTiddler,
dependencies = {include: {}};
dependencies.include[target] = 1;
var content = Renderer.SliderNode(macroNode.params.name,
macroNode.params.label,
macroNode.params.tooltip,
[
Renderer.MacroNode("tiddler",{target: target},null,dependencies,store)
]);
content.execute(tiddler);
return [content];
}
};

View File

@ -8,6 +8,7 @@ title: js/macros/story.js
"use strict";
var Tiddler = require("../Tiddler.js").Tiddler,
Renderer = require("../Renderer.js").Renderer,
utils = require("../Utils.js");
// Parse the text of a story tiddler into an array of tiddler titles
@ -23,19 +24,6 @@ var parseStory = function(storyText) {
return tiddlers;
};
// Search the children of a node looking for the required tiddler rendering
var searchTiddlerNode = function(node,renderTiddler,renderTemplate) {
while(node !== null) {
if(node.getAttribute && node.getAttribute("data-tw-render-tiddler") === renderTiddler) {
if(!renderTemplate || (renderTemplate && node.getAttribute("data-tw-render-template") == renderTemplate)) {
return node;
}
}
node = node.nextSibling;
}
return null;
};
exports.macro = {
name: "story",
types: ["text/html","text/plain"],
@ -44,9 +32,9 @@ exports.macro = {
template: {byName: true, type: "tiddler", optional: true}
},
events: {
"tw-navigate": function(event,node,tiddler,store,params) {
var storyTiddler = store.getTiddler(params.story);
store.addTiddler(new Tiddler(storyTiddler,{text: event.navigateTo + "\n" + storyTiddler.text}));
"tw-navigate": function(event,macroNode) {
var storyTiddler = macroNode.store.getTiddler(macroNode.params.story);
macroNode.store.addTiddler(new Tiddler(storyTiddler,{text: event.navigateTo + "\n" + storyTiddler.text}));
$("html,body").animate({
scrollTop: 0
}, 400);
@ -54,53 +42,77 @@ exports.macro = {
return false;
}
},
render: function(type,tiddler,store,params) {
var tiddlers = parseStory(store.getTiddlerText(params.story)),
output = [];
execute: function(macroNode,tiddler,store) {
var tiddlers = parseStory(store.getTiddlerText(macroNode.params.story)),
content = [];
for(var t=0; t<tiddlers.length; t++) {
if(params.template) {
output.push(store.renderTiddler(type,tiddlers[t],params.template));
} else {
output.push(store.renderTiddler(type,tiddlers[t]));
var paramFn = {target: tiddlers[t],template: macroNode.params.template},
dependencies = {include: {}};
dependencies.include[tiddlers[t]] = 1;
if(macroNode.params.template) {
dependencies.include[macroNode.params.template] = 1;
}
var m = Renderer.MacroNode("tiddler",paramFn,null,dependencies,store);
m.execute(tiddler);
content.push(m);
}
return output.join("\n");
return content;
},
rerender: function(node,changes,type,tiddler,store,params) {
refresh: function(macroNode,tiddler,store) {
/*jslint browser: true */
// Get the tiddlers we're supposed to be displaying
var targetTiddlers = parseStory(store.getTiddlerText(params.story)),
currNode = node.firstChild,
nextNode;
for(var t=0; t<targetTiddlers.length; t++) {
var targetTiddlers = parseStory(store.getTiddlerText(macroNode.params.story)),
template = macroNode.params.template,
t,n,domNode,
findTiddler = function (childIndex,tiddlerTitle,templateTitle) {
while(childIndex < macroNode.content.length) {
var params = macroNode.content[childIndex].params;
if(params.target === tiddlerTitle) {
if(!templateTitle || params.template === templateTitle) {
return childIndex;
}
}
childIndex++;
}
return null;
};
for(t=0; t<targetTiddlers.length; t++) {
// See if the node we want is already there
var tiddlerNode = searchTiddlerNode(currNode,targetTiddlers[t],params.template);
var tiddlerNode = findTiddler(t,targetTiddlers[t],template);
if(tiddlerNode === null) {
// If not, render the tiddler
var tmpNode = document.createElement("div");
store.renderTiddlerInNode(tmpNode,targetTiddlers[t],params.template);
tiddlerNode = tmpNode.firstChild;
node.insertBefore(tiddlerNode,currNode);
var paramFn = {target: targetTiddlers[t],template: template},
dependencies = {include: {}};
dependencies.include[targetTiddlers[t]] = 1;
if(template) {
dependencies.include[template] = 1;
}
var m = Renderer.MacroNode("tiddler",paramFn,null,dependencies,store);
m.execute(store.getTiddler(targetTiddlers[t]));
m.renderInDom(macroNode.domNode,macroNode.domNode.childNodes[t]);
macroNode.content.splice(t,0,m);
} else {
// Delete any nodes preceding the one we want
while(currNode !== tiddlerNode) {
nextNode = currNode.nextSibling;
node.removeChild(currNode);
currNode = nextNode;
if(tiddlerNode > t) {
// First delete the DOM nodes
for(n=t; n<tiddlerNode; n++) {
domNode = macroNode.content[n].domNode;
domNode.parentNode.removeChild(domNode);
}
// Refresh it
store.refreshDomNode(tiddlerNode,changes);
currNode = currNode.nextSibling;
// Then delete the actual renderer nodes
macroNode.content.splice(t,tiddlerNode-t);
}
}
// Remove any unused nodes
while(currNode !== null) {
nextNode = currNode.nextSibling;
node.removeChild(currNode);
currNode = nextNode;
}
// Remove any left over nodes
if(macroNode.content.length > targetTiddlers.length) {
for(t=targetTiddlers.length; t<macroNode.content.length; t++) {
domNode = macroNode.content[t].domNode;
domNode.parentNode.removeChild(domNode);
}
macroNode.content.splice(targetTiddlers.length,macroNode.content.length-targetTiddlers.length);
}
}
};
})();

View File

@ -42,7 +42,7 @@ the `template` parameter or, if that parameter is missing, the tiddler named in
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "tiddler",
@ -53,29 +53,39 @@ exports.macro = {
template: {byName: true, type: "tiddler", optional: true},
"with": {byName: true, type: "text", optional: true, dependentAll: true}
},
render: function(type,tiddler,store,params) {
var renderTitle = params.target,
renderTemplate = params.template;
execute: function(macroNode,tiddler,store) {
var renderTitle = macroNode.params.target,
renderTemplate = macroNode.params.template,
content,
contentClone = [],
t;
if(typeof renderTitle !== "string") {
renderTitle = tiddler.title;
}
if(typeof renderTemplate !== "string") {
renderTemplate = renderTitle;
}
if(params["with"]) {
if("with" in macroNode.params) {
// Parameterised transclusion
var targetTiddler = store.getTiddler(renderTemplate),
text = targetTiddler.text;
var withTokens = [params["with"]];
for(var t=0; t<withTokens.length; t++) {
var withTokens = [macroNode.params["with"]];
for(t=0; t<withTokens.length; t++) {
var placeholderRegExp = new RegExp("\\$"+(t+1),"mg");
text = text.replace(placeholderRegExp,withTokens[t]);
}
return store.renderText(targetTiddler.type,text,type,renderTitle);
content = store.parseText(targetTiddler.type,text).tree;
} else {
// There's no parameterisation, so we can just render the target tiddler directly
return store.renderTiddler(type,renderTitle,renderTemplate);
content = store.parseTiddler(renderTemplate).tree;
}
for(t=0; t<content.length; t++) {
contentClone.push(content[t].clone());
}
for(t=0; t<contentClone.length; t++) {
contentClone[t].execute(store.getTiddler(renderTitle));
}
return contentClone;
}
};

View File

@ -7,15 +7,15 @@ title: js/macros/version.js
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "version",
types: ["text/html","text/plain"],
params: {
},
render: function(type,tiddler,store,params) {
return "5.0.0";
execute: function(macroNode,tiddler,store) {
return [Renderer.TextNode("5.0.0")];
}
};

View File

@ -7,7 +7,7 @@ title: js/macros/video.js
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "video",
@ -18,21 +18,36 @@ exports.macro = {
width: {byName: true, type: "text", optional: true},
height: {byName: true, type: "text", optional: true}
},
render: function(type,tiddler,store,params) {
var videoType = params.type || "vimeo",
videoWidth = params.width || 640,
videoHeight = params.height || 360;
if(type === "text/html") {
execute: function(macroNode,tiddler,store) {
var src = macroNode.params.src,
videoType = macroNode.params.type || "vimeo",
videoWidth = macroNode.params.width || 640,
videoHeight = macroNode.params.height || 360;
switch(videoType) {
case "vimeo":
return "<iframe src='http://player.vimeo.com/video/" + params.src + "?autoplay=0' width='" + videoWidth + "' height='" + videoHeight + "' frameborder='0'></iframe>";
return [Renderer.ElementNode("iframe",{
src: "http://player.vimeo.com/video/" + src + "?autoplay=0",
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
case "youtube":
return "<iframe type='text/html' width='" + videoWidth + "' height='" + videoHeight + "' src='http://www.youtube.com/embed/" + params.src + "' frameborder='0'></iframe>";
return [Renderer.ElementNode("iframe",{
type: "text/html",
src: "http://www.youtube.com/embed/" + src,
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
case "archiveorg":
return "<iframe src='http://www.archive.org/embed/" + params.src + "' width='" + videoWidth + "' height='" + videoHeight + "' frameborder='0'></iframe>";
}
} else if (type === "text/plain") {
return ""; // Not really sure how to render a video into plain text...
return [Renderer.ElementNode("iframe",{
src: "http://www.archive.org/embed/" + src,
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
default:
return [];
}
}
};

View File

@ -7,7 +7,8 @@ title: js/macros/view.js
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
var Renderer = require("../Renderer.js").Renderer,
utils = require("../Utils.js");
exports.macro = {
name: "view",
@ -17,28 +18,44 @@ exports.macro = {
format: {byPos: 1, type: "text", optional: true},
template: {byPos: 2, type: "text", optional: true}
},
render: function(type,tiddler,store,params) {
var encoder = type === "text/html" ? utils.htmlEncode : function(x) {return x;};
execute: function(macroNode,tiddler,store) {
if(!tiddler) {
return "{{** Missing tiddler **}}";
return Renderer.TextNode("{{** Missing tiddler **}}");
} else {
var v = tiddler[params.field];
var v = tiddler[macroNode.params.field],
content,
t,
contentClone = [];
if(v !== undefined) {
switch(params.format) {
switch(macroNode.params.format) {
case "link":
return store.renderMacro("link",type,tiddler,{target: v},encoder(v));
var dependencies = {link: {}};
dependencies.link[v] = 1;
var link = Renderer.MacroNode("link",
{target: v},
[Renderer.TextNode(v)],
dependencies,
store);
link.execute(tiddler);
return [link];
case "wikified":
if(params.field === "text") {
return store.renderTiddler(type,tiddler.title);
if(macroNode.params.field === "text") {
content = store.parseTiddler(tiddler.title).tree;
} else {
return store.renderText("text/x-tiddlywiki",v,type,tiddler.title);
content = store.parseText("text/x-tiddlywiki",v).tree;
}
break;
for(t=0; t<content.length; t++) {
contentClone.push(content[t].clone());
}
for(t=0; t<contentClone.length; t++) {
contentClone[t].execute(tiddler);
}
return contentClone;
case "date":
var template = params.template || "DD MMM YYYY";
return encoder(utils.formatDateString(v,template));
var template = macroNode.params.template || "DD MMM YYYY";
return [Renderer.TextNode(utils.formatDateString(v,template))];
default: // "text"
return encoder(v);
return [Renderer.TextNode(v)];
}
}
}