1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-25 01:20:30 +00:00

Removed the old pre-rabbit hole code

And the async module it was using
This commit is contained in:
Jeremy Ruston 2012-07-13 17:21:46 +01:00
parent 47435594e7
commit 2e0e74a353
747 changed files with 0 additions and 191675 deletions

View File

@ -1,29 +0,0 @@
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
Copyright (c) Jeremy Ruston 2004-2007
Copyright (c) UnaMesa Association 2007-2012
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of the UnaMesa Association nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

View File

@ -1,154 +0,0 @@
/*\
title: js/App.js
This is the main() function in the browser
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var WikiStore = require("./WikiStore.js").WikiStore,
HttpSync = require("./HttpSync.js").HttpSync,
Tiddler = require("./Tiddler.js").Tiddler,
tiddlerInput = require("./TiddlerInput.js"),
tiddlerOutput = require("./TiddlerOutput.js"),
Renderer = require("./Renderer.js").Renderer,
WikiTextParser = require("./WikiTextParser.js").WikiTextParser,
TiddlyTextParser = require("./TiddlyTextParser.js").TiddlyTextParser,
PlainTextParser = require("./PlainTextParser.js").PlainTextParser,
JavaScriptParser = require("./JavaScriptParser.js").JavaScriptParser,
JSONParser = require("./JSONParser.js").JSONParser,
ImageParser = require("./ImageParser.js").ImageParser,
utils = require("./Utils.js");
var App = function() {
var t;
// Check if we're running on the server or the client
this.isBrowser = typeof window !== "undefined";
// Create the main store
this.store = new WikiStore();
// Register the parsers
this.store.registerParser("text/x-tiddlywiki",new WikiTextParser({store: this.store}));
this.store.registerParser("text/x-tiddlywiki-css",new TiddlyTextParser({store: this.store}));
this.store.registerParser("text/plain",new PlainTextParser({store: this.store}));
this.store.registerParser(["image/svg+xml",".svg","image/jpg",".jpg","image/jpeg",".jpeg","image/png",".png","image/gif",".gif"],new ImageParser({store: this.store}));
this.store.registerParser(["application/javascript",".js"],new JavaScriptParser({store: this.store}));
this.store.registerParser(["application/json",".json"],new JSONParser({store: this.store}));
// Register the standard tiddler serializers and deserializers
tiddlerInput.register(this.store);
tiddlerOutput.register(this.store);
// Add the shadow tiddlers that are built into TiddlyWiki
var shadowShadowStore = new WikiStore({
shadowStore: null
}),
shadowShadows = [
{title: "StyleSheet", text: ""},
{title: "MarkupPreHead", text: ""},
{title: "MarkupPostHead", text: ""},
{title: "MarkupPreBody", text: ""},
{title: "MarkupPostBody", text: ""},
{title: "TabTimeline", text: "<<timeline>>"},
{title: "TabAll", text: "<<list all>>"},
{title: "TabTags", text: "<<allTags excludeLists>>"},
{title: "TabMoreMissing", text: "<<list missing>>"},
{title: "TabMoreOrphans", text: "<<list orphans>>"},
{title: "TabMoreShadowed", text: "<<list shadowed>>"},
{title: "AdvancedOptions", text: "<<options>>"},
{title: "PluginManager", text: "<<plugins>>"},
{title: "SystemSettings", text: ""},
{title: "ToolbarCommands", text: "|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|"},
{title: "WindowTitle", text: "<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>"},
{title: "DefaultTiddlers", text: "[[GettingStarted]]"},
{title: "MainMenu", text: "[[GettingStarted]]"},
{title: "SiteTitle", text: "My TiddlyWiki"},
{title: "SiteSubtitle", text: "a reusable non-linear personal web notebook"},
{title: "SiteUrl", text: ""},
{title: "SideBarOptions", text: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb" "Change TiddlyWiki advanced options">>'},
{title: "SideBarTabs", text: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>'},
{title: "TabMore", text: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'}
];
this.store.shadows.shadows = shadowShadowStore;
for(t=0; t<shadowShadows.length; t++) {
shadowShadowStore.addTiddler(new Tiddler(shadowShadows[t]));
}
// If in the browser, load the tiddlers built into the TiddlyWiki document
if(this.isBrowser) {
// First, the JavaScript system tiddlers
var moduleArea = document.getElementById("jsModules");
this.store.shadows.addTiddlers(this.store.deserializeTiddlers("(DOM)",moduleArea));
// Then, the ordinary tiddlers baked into the storeArea
var storeArea = document.getElementById("storeArea");
this.store.addTiddlers(this.store.deserializeTiddlers("(DOM)",storeArea));
// Finally, the shadow tiddlers
var shadowArea = document.getElementById("shadowArea");
this.store.shadows.addTiddlers(this.store.deserializeTiddlers("(DOM)",shadowArea));
}
// Reset pending events on the store so that we don't get events for the initial load
this.store.clearEvents();
// Bit of a hack to set up the macros
this.store.installMacro(require("./macros/chooser.js").macro);
this.store.installMacro(require("./macros/command.js").macro);
this.store.installMacro(require("./macros/echo.js").macro);
this.store.installMacro(require("./macros/edit.js").macro);
this.store.installMacro(require("./macros/image.js").macro);
this.store.installMacro(require("./macros/link.js").macro);
this.store.installMacro(require("./macros/list.js").macro);
this.store.installMacro(require("./macros/slider.js").macro);
this.store.installMacro(require("./macros/story.js").macro);
this.store.installMacro(require("./macros/tiddler.js").macro);
this.store.installMacro(require("./macros/version.js").macro);
this.store.installMacro(require("./macros/video.js").macro);
this.store.installMacro(require("./macros/view.js").macro);
this.store.installMacro(require("./macros/zoomer.js").macro);
// Install the default link massager
this.store.linkMassager = function(linkInfo) {
if(!linkInfo.isExternal) {
linkInfo.attributes.href = encodeURIComponent(linkInfo.to);
}
};
// Set up for the browser
if(this.isBrowser) {
// Set up HttpSync
this.httpSync = new HttpSync(this.store);
// Apply the dynamic stylesheet
var styleRenderer = this.store.renderMacro("tiddler",{target: "StyleSheet"});
utils.applyStyleSheet("StyleSheet",styleRenderer.render("text/plain"));
this.store.addEventListener("",function(changes) {
styleRenderer.refresh(changes);
utils.applyStyleSheet("StyleSheet",styleRenderer.render("text/plain"));
});
// Open the PageTemplate
var renderer = this.store.renderMacro("tiddler",{target: "PageTemplate"});
renderer.renderInDom(document.body);
// Register an event handler to handle refreshing the DOM
this.store.addEventListener("",function(changes) {
renderer.refreshInDom(changes);
});
// Set the page title and refresh it when needed
var titleRenderer = this.store.renderMacro("tiddler",{target: "WindowTitle"});
document.title = titleRenderer.render("text/plain");
this.store.addEventListener("",function(changes) {
titleRenderer.refresh(changes);
document.title = titleRenderer.render("text/plain");
});
// Listen for navigate events that weren't caught
document.addEventListener("tw-navigate",function (event) {
renderer.broadcastEvent(event);
},false);
// Set up a timer to change the value of a tiddler
var me = this,
s = setInterval || window.setInterval;
s(function() {
me.store.addTiddler(new Tiddler({
title: "ClockTiddler",
text: "The time was recently " + (new Date()).toString()
}));
},3000);
}
};
exports.App = App;
})();

View File

@ -1,114 +0,0 @@
/*\
title: js/ArgParser.js
Parse a space-separated string of name:value parameters. Values can be quoted with single quotes, double quotes, double square brackets, or double curly braces.
The parameters are returned in a structure that can be referenced like this:
(return).byName["name"][0] - First occurance of parameter with a given name
(return).byPos[0].n - Name of parameter in first position
(return).byPos[0].v.string - Value of parameter in first position
(return).byPos[0].v.evaluated - True if the parameter is to be evaluated
Options and their defaults are:
defaultName: null,
defaultValue: null,
noNames: false,
cascadeDefaults: false,
allowEval: true
\*/
(function(){
/*jslint node: true */
"use strict";
var ArgParser = function(argString,options) {
options = options || {};
var defaultName = options.defaultName,
defaultValue = options.defaultValue;
var parseToken = function(match,p) {
var n;
if(match[p]) { // Double quoted
n = {string: match[p]};
} else if(match[p+1]) { // Single quoted
n = {string: match[p+1]};
} else if(match[p+2]) { // Double-square-bracket quoted
n = {string: match[p+2]};
} else if(match[p+3]) { // Double-brace quoted
n = {string: match[p+3], evaluated: true};
} else if(match[p+4]) { // Unquoted
n = {string: match[p+4]};
} else if(match[p+5]) { // empty quote
n = {string: ""};
}
return n;
};
this.byPos = [];
var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")",
sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')",
dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])",
dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})",
unQuoted = options.noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)",
emptyQuote = "((?:\"\")|(?:''))",
skipSpace = "(?:\\s*)",
token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")",
re = options.noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg"),
match,n,v;
do {
match = re.exec(argString);
if(match) {
n = parseToken(match,1);
if(options.noNames) {
this.byPos.push({n:"", v:n});
} else {
v = parseToken(match,8);
if(v === undefined && defaultName) {
v = n;
n = defaultName;
} else if(v === undefined && defaultValue) {
v = defaultValue;
}
if(n.evaluated === true) {
n = "{{" + n.string + "}}";
} else if (typeof n === "object" && n.hasOwnProperty("string")) {
n = n.string;
}
this.byPos.push({n:n, v:v});
if(options.cascadeDefaults) {
defaultName = n;
defaultValue = v;
}
}
}
} while(match);
this.byName = {};
for(var t=0; t<this.byPos.length; t++) {
n = this.byPos[t].n;
v = this.byPos[t].v;
if(this.byName.hasOwnProperty(n))
this.byName[n].push(v);
else
this.byName[n] = [v];
}
};
// Retrieve the first occurance of a named parameter, or the default if missing
ArgParser.prototype.getValueByName = function(n) {
var v = this.byName[n];
return v && v.length > 0 ? v[0] : null;
};
// Retrieve all the string values as an array
ArgParser.prototype.getStringValues = function() {
var result = [];
for(var t=0; t<this.byPos.length; t++) {
result.push(this.byPos[t].v.string);
}
return result;
};
exports.ArgParser = ArgParser;
})();

View File

@ -1,79 +0,0 @@
/*\
title: js/Dependencies.js
Represents the dependencies of a tiddler or a parser node as these fields:
tiddlers: A hashmap of explicitly tiddler titles, with the value `false` if the dependency is skinny, and `true` if it is fat
dependentAll: True if there is an implicit skinny dependency on all available tiddlers
dependentOnContextTiddler: True if the node has a fat dependency on the current context tiddler
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("./Utils.js");
var Dependencies = function(skinnyTiddlers,fatTiddlers,dependentAll) {
var t,tiddlerTitle;
this.tiddlers = {};
this.dependentAll = dependentAll;
if(skinnyTiddlers) {
for(t=0; t<skinnyTiddlers.length; t++) {
tiddlerTitle = skinnyTiddlers[t];
if(typeof tiddlerTitle === "string" && tiddlerTitle !== "") {
this.tiddlers[tiddlerTitle] = false;
}
}
}
if(fatTiddlers) {
for(t=0; t<fatTiddlers.length; t++) {
tiddlerTitle = fatTiddlers[t];
if(typeof tiddlerTitle === "string" && tiddlerTitle !== "") {
this.tiddlers[tiddlerTitle] = true;
}
}
}
};
/*
Adds a dependency to a given tiddler. Note how setting a dependency of fat=false on a tiddler that already has
a dependency of fat=true will leave the fat setting as true
*/
Dependencies.prototype.addDependency = function(tiddlerTitle,fat) {
if(!this.tiddlers[tiddlerTitle]) {
this.tiddlers[tiddlerTitle] = fat;
}
};
Dependencies.prototype.mergeDependencies = function(dep) {
this.dependentAll = dep.dependentAll || this.dependentAll;
for(var t in dep.tiddlers) {
this.addDependency(t,dep.tiddlers[t]);
}
};
/*
Determine if these dependencies are impacted by the specified array of changes
changes: Hashmap of {title: "created|modified|deleted"}
contextTiddlerTitle: The title of the current context tiddler
*/
Dependencies.prototype.hasChanged = function(changes,contextTiddlerTitle) {
if(this.dependentAll) {
return true;
}
if(!!this.dependentOnContextTiddler && contextTiddlerTitle !== undefined && changes.hasOwnProperty(contextTiddlerTitle)) {
return true;
}
for(var c in changes) {
if(this.tiddlers.hasOwnProperty(c)) {
return true;
}
}
return false;
};
exports.Dependencies = Dependencies;
})();

View File

@ -1,107 +0,0 @@
/*\
title: js/FileRetriever.js
FileRetriever can asynchronously retrieve files from HTTP URLs or the local file system. Files are treated as utf-8 text or, if the filepath ends in one of the recognised binary extensions, as a base64 encoded binary string
\*/
(function(){
/*jslint node: true */
"use strict";
var fs = require("fs"),
path = require("path"),
url = require("url"),
util = require("util"),
http = require("http"),
https = require("https");
var FileRetriever = exports;
// These are the file extensions that we'll recognise as binary.
FileRetriever.binaryFileExtensions = [".jpg",".jpeg",".png",".gif"];
// Retrieve a local file and invoke callback(err,data) in the usual way
var fileRequest = function fileRequest(filepath,callback) {
fs.readFile(filepath, function (err,data) {
if(err) {
callback(err);
} else {
// Check if we need to base64 encode the file
if(FileRetriever.binaryFileExtensions.indexOf(path.extname(filepath)) !== -1) {
callback(err,data.toString("base64"));
} else {
callback(err,data.toString("utf8"));
}
}
});
};
// Retrieve a file over HTTP and invoke callback(err,data) in the usual way
var httpRequest = function(fileurl,callback) {
var opts = url.parse(fileurl),
httpLib = opts.protocol === "http:" ? http : https,
encoding = (FileRetriever.binaryFileExtensions.indexOf(path.extname(fileurl)) !== -1) ? "binary" : "utf8";
var request = httpLib.get(opts,function(res) {
if(res.statusCode != 200) {
var err = new Error("HTTP error");
err.code = res.statusCode.toString();
callback(err);
} else {
var data = [];
res.setEncoding(encoding);
res.on("data", function(chunk) {
data.push(chunk);
});
res.on("end", function() {
if(encoding === "binary") {
callback(null,(new Buffer(data.join(""),"binary")).toString("base64"));
} else {
callback(null,data.join(""));
}
});
}
});
request.addListener("error", function(err) {
callback(err);
});
request.end();
};
// Retrieve a file given a filepath specifier and a base directory. If the filepath isn't an absolute
// filepath or an absolute URL, then it is interpreted relative to the base directory, which can also be
// a local directory or a URL. On completion, the callback function is called as callback(err,data). The
// data hashmap is as follows:
// text: full text of file
// path: full path used to reach the file
// basename: the basename of the file
// extname: the extension of the file
FileRetriever.retrieveFile = function(filepath,baseDir,callback) {
var httpRegExp = /^(https?:\/\/)/gi,
result = {},
filepathIsHttp = httpRegExp.test(filepath),
baseDirIsHttp = httpRegExp.test(baseDir),
requester;
if(baseDirIsHttp || filepathIsHttp) {
// If we've got a full HTTP URI then we're good to go
result.path = url.resolve(baseDir,filepath);
var parsedPath = url.parse(result.path);
result.extname = path.extname(parsedPath.pathname);
result.basename = path.basename(parsedPath.extname);
requester = httpRequest;
} else {
// It's a file requested in a file context
result.path = path.resolve(baseDir,filepath);
result.extname = path.extname(result.path);
result.basename = path.basename(result.path,result.extname);
requester = fileRequest;
}
requester(result.path,function(err,data) {
if(!err) {
result.text = data;
}
callback(err,result);
});
};
})();

View File

@ -1,40 +0,0 @@
/*\
title: js/HttpSync.js
A very simple synchroniser. It PUTs updated or created tiddlers, and DELETEs deleted tiddlers.
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
function HttpSync(store) {
this.store = store;
this.changeCounts = {};
store.addEventListener("",function(changes) {
for(var title in changes) {
var x = new XMLHttpRequest(),
tiddler = store.getTiddler(title);
if(tiddler) {
var fieldStrings = tiddler.getFieldStrings(),
fields = {},
t;
for(t=0; t<fieldStrings.length; t++) {
fields[fieldStrings[t].name] = fieldStrings[t].value;
}
fields.text = tiddler.text;
x.open("PUT",window.location.toString() + encodeURIComponent(title),true);
x.setRequestHeader("Content-type", "application/json");
x.send(JSON.stringify(fields));
} else {
x.open("DELETE",window.location.toString() + encodeURIComponent(title),true);
x.send();
}
}
});
}
exports.HttpSync = HttpSync;
})();

View File

@ -1,32 +0,0 @@
/*\
title: js/ImageParser.js
Compiles images into JavaScript functions that render them in HTML
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
utils = require("./Utils.js");
var ImageParser = function(options) {
this.store = options.store;
};
ImageParser.prototype.parse = function(type,text) {
var src;
if(type === "image/svg+xml" || type === ".svg") {
src = "data:image/svg+xml," + encodeURIComponent(text);
} else {
src = "data:" + type + ";base64," + text;
}
return new Renderer([Renderer.ElementNode("img",{src: src})],new Dependencies(),this.store);
};
exports.ImageParser = ImageParser;
})();

View File

@ -1,51 +0,0 @@
/*\
title: js/JSONParser.js
Compiles JSON objects into JavaScript functions that render them in HTML and plain text
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
utils = require("./Utils.js");
var renderObject = function(obj) {
var children = [],t;
if(obj instanceof Array) {
for(t=0; t<obj.length; t++) {
children.push(Renderer.ElementNode("li",{
"class": ["jsonArrayMember"]
},[renderObject(obj[t])]));
}
return Renderer.ElementNode("ul",{
"class": ["jsonArray"]
},children);
} else if(typeof obj === "object") {
for(t in obj) {
children.push(Renderer.ElementNode("li",{
"class": ["jsonObjectMember"]
},[Renderer.SplitLabelNode("JSON",[Renderer.TextNode(t)],[renderObject(obj[t])])]));
}
return Renderer.ElementNode("ul",{
"class": ["jsonObject"]
},children);
} else {
return Renderer.LabelNode("JSON" + (typeof obj),[Renderer.TextNode(JSON.stringify(obj))],["jsonValue"]);
}
};
var JSONParser = function(options) {
this.store = options.store;
};
JSONParser.prototype.parse = function(type,text) {
return new Renderer([renderObject(JSON.parse(text))],new Dependencies(),this.store);
};
exports.JSONParser = JSONParser;
})();

View File

@ -1,106 +0,0 @@
/*\
title: js/JavaScriptParser.js
Parses JavaScript source code into a parse tree using Esprima
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
esprima = require("esprima");
// Initialise the parser
var JavaScriptParser = function(options) {
this.store = options.store;
};
// Parse a string of JavaScript code and return the parse tree as a wikitext parse tree
JavaScriptParser.prototype.parse = function(type,code) {
// Simplisticly replace tabs with spaces. Browsers will happily render tabs but most default to 8 character tab stops
code = code.replace(/\t/mg," ");
// Try to parse the code
var parseTree;
try {
parseTree = esprima.parse(code,{comment: true,tokens: true,range: true});
} catch(ex) {
// Return a helpful error if the parse failed
return new Renderer([
Renderer.ElementNode("pre",{"class": "javascript-source"},[
Renderer.TextNode(code.substring(0,ex.index)),
Renderer.ErrorNode(ex),
Renderer.TextNode(code.substring(ex.index))
])
],new Dependencies(),this.store);
}
// Helpers to render the comments and tokens with the appropriate classes
var self = this,
result = [],
nextComment = 0,
nextToken = 0,
currPos = 0;
var renderWhitespace = function(nextPos) {
if(currPos < nextPos) {
result.push(Renderer.TextNode(code.substring(currPos,nextPos)));
}
},
renderComment = function(comment) {
var text = comment.value,
element,
classes = ["javascript-comment"],
content = [];
renderWhitespace(comment.range[0]);
if(comment.type === "Block") {
element = "div";
classes.push("javascript-block-comment");
content.push(Renderer.TextNode("/*"));
} else {
element = "span";
classes.push("javascript-line-comment");
content.push(Renderer.TextNode("//"));
}
content.push.apply(content,self.store.parseText("text/x-tiddlywiki",text).nodes);
if(comment.type === "Block") {
content.push(Renderer.TextNode("*/"));
} else {
content.push(Renderer.TextNode("\n"));
}
result.push(Renderer.ElementNode(element,{"class": classes},content));
currPos = comment.range[1] + 1;
},
renderToken = function(token) {
renderWhitespace(token.range[0]);
result.push(Renderer.ElementNode("span",{
"class": "javascript-" + token.type.toLowerCase()
},[
Renderer.TextNode(token.value)
]));
currPos = token.range[1] + 1;
};
// Process the tokens interleaved with the comments
while(nextComment < parseTree.comments.length || nextToken < parseTree.tokens.length) {
if(nextComment < parseTree.comments.length && nextToken < parseTree.tokens.length) {
if(parseTree.comments[nextComment].range[0] < parseTree.tokens[nextToken].range[0]) {
renderComment(parseTree.comments[nextComment++]);
} else {
renderToken(parseTree.tokens[nextToken++]);
}
} else if(nextComment < parseTree.comments.length) {
renderComment(parseTree.comments[nextComment++]);
} else {
renderToken(parseTree.tokens[nextToken++]);
}
}
renderWhitespace(code.length);
// Wrap the whole lot in a `<PRE>`
return new Renderer([
Renderer.ElementNode("pre",{"class": "javascript-source"},result)
],new Dependencies(),this.store);
};
exports.JavaScriptParser = JavaScriptParser;
})();

View File

@ -1,149 +0,0 @@
/*\
title: js/LocalFileSync.js
Loads tiddlers from a given directory, and then keeps the files up to date as tiddlers are modified, created or deleted.
\*/
(function(){
/*jslint node: true */
"use strict";
var retrieveFile = require("./FileRetriever.js").retrieveFile,
utils = require("./Utils.js"),
fs = require("fs"),
path = require("path"),
url = require("url"),
util = require("util"),
async = require("async");
function LocalFileSync(dirpath,store,callback) {
this.dirpath = dirpath;
this.store = store;
this.callback = callback;
this.tiddlers = {}; // A hashmap of <tiddlername>: {changeCount: <changeCount>, files: [name]}
var self = this,
sanitizeFilepath = function(filepath) {
return filepath.replace(/\//mg,"-");
};
// Set up a queue for loading tiddler files, tasks are {filepath:,callback:}
this.loadQueue = async.queue(function(task,callback) {
task.files = [];
retrieveFile(task.filepath,self.dirpath,function(err,data) {
if(err) {
callback(err);
} else {
task.files.push(task.filepath);
// Use the filepath as the default title for the tiddler
var fields = {
title: data.path
};
var tiddlers = self.store.deserializeTiddlers(data.extname,data.text,fields);
// Check for the .meta file
if(data.extname !== ".json" && tiddlers.length === 1) {
var metafile = task.filepath + ".meta";
retrieveFile(metafile,self.dirpath,function(err,data) {
if(err && err.code !== "ENOENT" && err.code !== "404") {
callback(err);
} else {
task.files.push(metafile);
var fields = tiddlers[0];
if(!err) {
var text = data.text.split("\n\n")[0];
if(text) {
fields = self.store.deserializeTiddlers("application/x-tiddler",text,fields)[0];
}
}
task.callback(task,[fields]);
callback(null);
}
});
} else {
task.callback(task,tiddlers);
callback(null);
}
}
});
},10);
// Call the callback when all the files are loaded
this.loadQueue.drain = function() {
callback(null);
};
// Query the folder content to get all the tiddlers
fs.readdir(this.dirpath,function(err,files) {
if(err) {
callback(err);
} else {
var loadCallback = function(task,tiddlers) {
for(var t=0; t<tiddlers.length; t++) {
var tiddler = tiddlers[t];
self.store.addTiddler(tiddler);
self.tiddlers[tiddler.title] = {
changeCount: self.store.getChangeCount(tiddler.title),
files: task.files
};
}
};
for(var t=0; t<files.length; t++) {
var f = files[t];
if(["..",".",".DS_Store"].indexOf(f) === -1 && f.indexOf(".meta") !== f.length-5) {
self.loadQueue.push({
filepath: f,
callback: loadCallback
});
}
}
}
});
// Set up a queue for saving tiddler files
this.saveQueue = async.queue(function(task,callback) {
var data = task.data,
encoding = "utf8";
if(task.binary) {
data = new Buffer(task.data,"base64").toString("binary");
encoding = "binary";
}
fs.writeFile(self.dirpath + "/" + task.name,data,encoding,function(err) {
callback(err);
});
},10);
// Install our event listener to listen out for tiddler changes
this.store.addEventListener("",function(changes) {
var t;
for(var title in changes) {
// Get the information about the tiddler
var tiddler = self.store.getTiddler(title),
changeCount = self.store.getChangeCount(title),
tiddlerInfo = self.tiddlers[title],
files = [];
if(tiddler) {
// Construct a changecount record if we don't have one
if(!tiddlerInfo) {
tiddlerInfo = {changeCount: 0, files: []};
self.tiddlers[title] = tiddlerInfo;
}
// Save the tiddler if the changecount has increased
if(changeCount > tiddlerInfo.changeCount) {
files = self.store.serializeTiddlers([tiddler],"application/x-tiddler");
for(t=0; t<files.length; t++) {
files[t].name = sanitizeFilepath(files[t].name);
tiddlerInfo.files.push(files[t].name);
self.saveQueue.push(files[t]);
}
}
} else {
// Delete the tiddler file if the tiddler has gone
if(tiddlerInfo) {
for(t=0; t<tiddlerInfo.files.length; t++) {
fs.unlink(self.dirpath + "/" + tiddlerInfo.files[t]);
}
delete self.tiddlers[title];
}
}
}
});
}
exports.LocalFileSync = LocalFileSync;
})();

View File

@ -1,26 +0,0 @@
/*\
title: js/PlainTextParser.js
Renders plain text tiddlers
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
utils = require("./Utils.js");
var PlainTextParser = function(options) {
this.store = options.store;
};
PlainTextParser.prototype.parse = function(type,text) {
return new Renderer([Renderer.ElementNode("pre",{},[Renderer.TextNode(text)])],new Dependencies(),this.store);
};
exports.PlainTextParser = PlainTextParser;
})();

View File

@ -1,524 +0,0 @@
/*\
title: js/Recipe.js
Recipe processing is in four parts:
1) The recipe file is parsed and any subrecipe files loaded recursively into this structure:
this.recipe = [
{marker: <marker>, filepath: <filepath>, baseDir: <baseDir>},
...
{marker: <marker>, filepath: <filepath>, baseDir: <baseDir>},
[
{marker: <marker>, filepath: <filepath>, baseDir: <baseDir>},
...
{marker: <marker>, filepath: <filepath>, baseDir: <baseDir>},
]
];
2) The tiddler files referenced by the recipe structure are loaded into it as an additional 'tiddlers'
member that contains an array of hashmaps of tiddler field values.
3) The recipe is scanned to create a hashmap of markers and their associated tiddlers. In cases where more
than one tiddler with the same title is assigned to a marker, the one that is later in the recipe file wins.
At this point tiddlers are placed in the store so that they can be referenced by title
this.markers = {
<marker>: [<tiddler title>,<tiddler title>,...],
<marker>: [<tiddler title>,<tiddler title>,...],
...
}
4) Finally, to actually cook the recipe, the template is processed by replacing the markers with the text of the associated tiddlers
\*/
(function(){
/*jslint node: true */
"use strict";
var Tiddler = require("./Tiddler.js").Tiddler,
utils = require("./Utils.js"),
retrieveFile = require("./FileRetriever.js").retrieveFile,
fs = require("fs"),
path = require("path"),
util = require("util"),
async = require("async");
/*
Load a recipe file. Arguments are:
options: See below
callback: Function to be called when the recipe has been loaded as callback(err), null === success
Options include:
filepath: The filepath of the recipe file to load. Can be a local path or an HTTP URL
store: Indicates the WikiStore to use to store the tiddlers (mandatory)
*/
var Recipe = function(options,callback) {
var me = this;
this.filepath = options.filepath;
this.store = options.store;
this.callback = callback;
this.recipe = [];
this.markers = {};
// A task queue for loading recipe files
this.recipeQueue = async.queue(function(task,callback) {
retrieveFile(task.filepath,task.baseDir,function(err,data) {
if(err) {
me.callback(err);
} else {
callback(me.processRecipeFile(task.recipe,data.text,data.path));
}
});
},1);
// A task queue for loading tiddler files
this.tiddlerQueue = async.queue(function(task,callback) {
me.readTiddlerFile(task.filepath,task.baseDir,function(err,data) {
if(err) {
me.callback(err);
} else {
if(data.length === 0) {
callback("Tiddler file '" + task.filepath + "' does not contain any tiddlers");
} else {
if(task.recipeLine.fields) {
for(var t=0; t<data.length; t++) {
for(var f in task.recipeLine.fields) {
data[t][f] = task.recipeLine.fields[f];
}
}
}
if(!task.recipeLine.tiddlers) {
task.recipeLine.tiddlers = [];
}
Array.prototype.push.apply(task.recipeLine.tiddlers,data);
callback(null);
}
}
});
},1);
// Called when all the recipes have been loaded
this.recipeQueue.drain = function() {
// Initiate the loading of the tiddlers referenced by the recipe
for(var r=0; r<me.recipe.length; r++) {
me.loadTiddlerFiles(me.recipe[r]);
}
};
// Called when all the tiddlers have been loaded
this.tiddlerQueue.drain = function() {
// Select the tiddlers that are associated with each marker
me.chooseTiddlers(me.recipe);
// Sort the main content tiddlers (makes it easier to diff TiddlyWiki files)
me.sortTiddlersForMarker("tiddler");
me.callback(null);
};
// Start the process off by queueing up the loading of the initial recipe
this.recipeQueue.push({filepath: this.filepath,
baseDir: process.cwd(),
recipe: this.recipe});
};
/*
Recursively queue loading the tiddler files referenced by a recipe line
*/
Recipe.prototype.loadTiddlerFiles = function(recipeLine) {
var me = this;
if(recipeLine instanceof Array) {
for(var r=0; r<recipeLine.length; r++) {
me.loadTiddlerFiles(recipeLine[r]);
}
} else {
var filepath = recipeLine.filepath, // eg ../js/*.js
filedir = path.dirname(filepath), // eg ../js
filename = path.basename(filepath), // eg *.js
posStar = filename.indexOf("*");
if(posStar !== -1) {
var fileRegExp = new RegExp("^" + filename.replace(/[\-\[\]{}()+?.,\\\^$|#\s]/g, "\\$&").replace("*",".*") + "$");
var files = fs.readdirSync(path.resolve(recipeLine.baseDir,filedir));
for(var f=0; f<files.length; f++) {
if(fileRegExp.test(files[f])) {
me.tiddlerQueue.push({
filepath: filedir + "/" + files[f],
baseDir: recipeLine.baseDir,
recipeLine: recipeLine
});
}
}
} else {
me.tiddlerQueue.push({filepath: filepath, baseDir: recipeLine.baseDir, recipeLine: recipeLine});
}
}
};
/*
Choose the tiddlers to be included on each marker
*/
Recipe.prototype.chooseTiddlers = function(recipe) {
// Loop through the lines of the recipe
for(var r=0; r<recipe.length; r++) {
var recipeLine = recipe[r];
if(recipeLine instanceof Array) {
// Process subrecipes recursively
this.chooseTiddlers(recipeLine);
} else {
// Choose the store and marker array to be used for this marker
var store = recipeLine.marker === "tiddler" ? this.store : this.store.shadows,
markerArray = this.markers[recipeLine.marker];
// Create the marker array if necessary
if(markerArray === undefined) {
this.markers[recipeLine.marker] = [];
markerArray = this.markers[recipeLine.marker];
}
if(recipeLine.tiddlers) {
// Process each of the tiddlers referenced by the recipe line
for(var t=0; t<recipeLine.tiddlers.length; t++) {
// Only add the tiddler to the marker if it isn't already there
var found = false;
for(var m=0; m<markerArray.length; m++) {
if(markerArray[m] === recipeLine.tiddlers[t].title) {
found = true;
}
}
if(!found) {
markerArray.push(recipeLine.tiddlers[t].title);
}
// Add the tiddler to the store
store.addTiddler(new Tiddler(recipeLine.tiddlers[t]));
}
}
}
}
};
/*
Sort the tiddlers associated with a particular marker
*/
Recipe.prototype.sortTiddlersForMarker = function(marker) {
if(this.markers[marker]) {
this.markers[marker].sort();
}
};
/*
Process the contents of a recipe file
recipe: a reference to the array in which to store the recipe contents
text: the text of the recipe file
recipePath: the full pathname used to reach the recipe file
The return value is `null` if the operation succeeded, or an error string if not
*/
Recipe.prototype.processRecipeFile = function(recipe,text,recipePath) {
var matchLine = function(linetext) {
var lineRegExp = /^(#?)(\s*)(#?)([^\s\:]+)\s*:\s*(.+)*\s*$/,
match = lineRegExp.exec(linetext);
return match ? {
comment: match[1] || match[3],
indent: match[2],
marker: match[4],
value: match[5]
} : null;
},
lines = text.split("\n"),
line = 0;
while(line < lines.length) {
var linetext = lines[line++],
match = matchLine(linetext);
if(match && !match.comment) {
if(match.indent.length > 0) {
return "Unexpected indentation in recipe file '" + recipePath + "'";
}
if(match.marker === "recipe") {
var insertionPoint = recipe.push([]) - 1;
this.recipeQueue.push({
filepath: match.value,
baseDir: path.dirname(recipePath),
recipe: recipe[insertionPoint]
});
} else {
var fieldLines = [],
fieldMatch = matchLine(lines[line]);
while(fieldMatch && fieldMatch.indent.length > 0) {
fieldLines.push(lines[line++]);
fieldMatch = matchLine(lines[line]);
}
var fields = {};
if(fieldLines.length > 0) {
fields = this.store.deserializeTiddlers("application/x-tiddler",fieldLines.join("\n"),{})[0];
}
recipe.push({
marker: match.marker,
filepath: match.value,
baseDir: path.dirname(recipePath),
fields: fields});
}
}
}
return null;
};
/*
Read a tiddler file and callback with an array of hashmaps of tiddler fields. For single
tiddler files it also looks for an accompanying .meta file
filepath: the filepath to the tiddler file (possibly relative)
baseDir: the base directory from which the filepath is taken
callback: called on completion as callback(err,data) where data is an array of tiddler fields
*/
Recipe.prototype.readTiddlerFile = function(filepath,baseDir,callback) {
var me = this;
// Read the tiddler file
retrieveFile(filepath,baseDir,function(err,data) {
if (err) {
callback(err);
return;
}
// Use the filepath as the default title for the tiddler
var fields = {
title: data.path
};
var tiddlers = me.store.deserializeTiddlers(data.extname,data.text,fields);
// Check for the .meta file
if(data.extname !== ".json" && tiddlers.length === 1) {
var metafile = filepath + ".meta";
retrieveFile(metafile,baseDir,function(err,data) {
if(err && err.code !== "ENOENT" && err.code !== "404") {
callback(err);
} else {
var fields = tiddlers[0];
if(!err) {
var text = data.text.split("\n\n")[0];
if(text) {
fields = me.store.deserializeTiddlers("application/x-tiddler",text,fields)[0];
}
}
callback(null,[fields]);
}
});
} else {
callback(null,tiddlers);
}
});
};
// Return a string of the cooked recipe
Recipe.prototype.cook = function() {
var template = this.markers.template ? this.store.getTiddlerText(this.markers.template[0]) : "",
out = [],
templateLines = template.split("\n");
for(var line=0; line<templateLines.length; line++) {
var templateRegExp = /^(?:<!--@@(.*)@@-->)|(?:&lt;!--@@(.*)@@--&gt;)$/gi;
var match = templateRegExp.exec(templateLines[line]);
if(match) {
var marker = match[1] === undefined ? match[2] : match[1];
this.outputTiddlersForMarker(out,marker);
} else {
if(line !== templateLines.length-1) {
out.push(templateLines[line],"\n");
}
}
}
return out.join("");
};
// Output all the tiddlers in the recipe with a particular marker
Recipe.prototype.outputTiddlersForMarker = function(out,marker) {
var tiddlers = [],
outputType = Recipe.tiddlerOutputMapper[marker] || "raw",
outputter = Recipe.tiddlerOutputter[outputType];
if(this.markers[marker]) {
tiddlers = this.markers[marker];
}
if(marker === "tiddler") {
this.store.forEachTiddler(function(title,tiddler) {
if(tiddlers.indexOf(title) === -1) {
tiddlers.push(title);
}
});
}
if(outputter) {
if((out.length > 1) && (Recipe.compatibilityCheats[marker] === "suppressLeadingNewline")) {
var lastLine = out[out.length-1];
if(lastLine.substr(-1) === "\n") {
out[out.length-1] = lastLine.substr(0,lastLine.length-1);
}
}
outputter.call(this,out,tiddlers);
if(Recipe.compatibilityCheats[marker] === "addTrailingNewline") {
out.push("\n");
}
}
};
// Allows for specialised processing for certain markers
Recipe.tiddlerOutputMapper = {
tiddler: "div",
js: "javascript",
jslib: "javascript",
jsdeprecated: "javascript",
jquery: "javascript",
shadow: "shadow",
title: "title",
jsmodule: "jsmodule",
pluginmodule: "pluginmodule",
base64ie: "base64ie"
};
Recipe.compatibilityCheats = {
"prehead": "addTrailingNewline",
"posthead": "addTrailingNewline",
"prebody": "addTrailingNewline",
"postscript": "addTrailingNewline",
"title": "suppressLeadingNewline"
};
Recipe.tiddlerOutputter = {
raw: function(out,tiddlers) {
// The default is just to output the raw text of the tiddler, ignoring any metadata
for(var t=0; t<tiddlers.length; t++) {
out.push(this.store.getTiddlerText(tiddlers[t]));
}
},
div: function(out,tiddlers) {
// Ordinary tiddlers are output as a <DIV>
for(var t=0; t<tiddlers.length; t++) {
var tid = this.store.getTiddler(tiddlers[t]);
out.push(this.store.serializeTiddlers([tid],"application/x-tiddler-html-div")[0].data,"\n");
}
},
javascript: function(out,tiddlers) {
// Lines starting with //# are removed from javascript tiddlers
for(var t=0; t<tiddlers.length; t++) {
var tid = this.store.getTiddler(tiddlers[t]),
text = tid.text;
var lines = text.split("\n");
for(var line=0; line<lines.length; line++) {
var commentRegExp = /^\s*\/\/#/gi;
if(!commentRegExp.test(lines[line])) {
out.push(lines[line]);
if(line !== lines.length-1) {
out.push("\n");
}
}
}
}
},
shadow: function(out,tiddlers) {
for(var t=0; t<tiddlers.length; t++) {
var title = tiddlers[t],
tid = this.store.shadows.getTiddler(title);
out.push(this.store.serializeTiddlers([tid],"application/x-tiddler-html-div")[0].data,"\n");
}
},
title: function(out,tiddlers) {
out.push(" ",this.store.renderTiddler("text/plain","WindowTitle")," ");
},
jsmodule: function(out,tiddlers) {
// JavaScript modules are output as a special script tag
for(var t=0; t<tiddlers.length; t++) {
var title = tiddlers[t],
tid = this.store.getTiddler(title);
out.push("<" + "script type=\"text/javascript\" data-tiddler-title=\"" + title + "\">");
out.push("define(\"" + title + "\",function(require,exports,module) {");
out.push(tid.text);
out.push("});");
out.push("</" + "script>");
}
},
pluginmodule: function(out,tiddlers) {
// plugin modules are output as a special script tag
for(var t=0; t<tiddlers.length; t++) {
var title = tiddlers[t],
tid = this.store.getTiddler(title);
out.push("<" + "script type=\"text/javascript\" data-tiddler-title=\"" + title + "\">");
out.push("$tw.modules.define(\"" + title + "\",\"" + tid.module + "\",function(module,exports,require) {");
out.push(tid.text);
out.push("});");
out.push("</" + "script>");
}
},
base64ie: function(out,tiddlers) {
// For IE, we output binary tiddlers in MHTML format (http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/)
if(tiddlers.length) {
out.push("<!--\n");
out.push("Content-Type: multipart/related; boundary=\"_tw_mhtml" + "_tiddler\"\n");
out.push("\n");
for(var t=0; t<tiddlers.length; t++) {
var tiddler = this.store.getTiddler(tiddlers[t]);
out.push("--_tw_mhtml" + "_tiddler\n");
out.push("Content-Location:" + tiddler.title + "\n");
out.push("Content-Type:" + tiddler.type + "\n");
out.push("Content-Transfer-Encoding:base64\n");
out.push("\n");
out.push(tiddler.text);
out.push("\n\n");
}
out.push("--_tw_mhtml" + "_tiddler--\n");
out.push("\n");
out.push("-->\n");
}
}
};
// Cook an RSS file of the most recent 20 tiddlers
Recipe.prototype.cookRss = function() {
var me = this,
numRssItems = 20,
s = [],
d = new Date(),
u = this.store.renderTiddler("text/plain","SiteUrl"),
encodeTiddlyLink = function(title) {
return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
},
tiddlerToRssItem = function(tiddler,uri) {
var s = "<title" + ">" + utils.htmlEncode(tiddler.title) + "</title" + ">\n";
s += "<description>" + utils.htmlEncode(me.store.renderTiddler("text/html",tiddler.title)) + "</description>\n";
var i;
if(tiddler.tags) {
for(i=0; i<tiddler.tags.length; i++) {
s += "<category>" + tiddler.tags[i] + "</category>\n";
}
}
s += "<link>" + uri + "#" + encodeURIComponent(encodeTiddlyLink(tiddler.title)) + "</link>\n";
if(tiddler.modified) {
s +="<pubDate>" + tiddler.modified.toUTCString() + "</pubDate>\n";
}
return s;
},
getRssTiddlers = function(sortField,excludeTag) {
var r = [];
me.store.forEachTiddler(sortField,excludeTag,function(title,tiddler) {
if(!tiddler.hasTag(excludeTag) && tiddler.modified !== undefined) {
r.push(tiddler);
}
});
return r;
};
// Assemble the header
s.push("<" + "?xml version=\"1.0\"?" + ">");
s.push("<rss version=\"2.0\">");
s.push("<channel>");
s.push("<title" + ">" + utils.htmlEncode(this.store.renderTiddler("text/plain","SiteTitle")) + "</title" + ">");
if(u)
s.push("<link>" + utils.htmlEncode(u) + "</link>");
s.push("<description>" + utils.htmlEncode(this.store.renderTiddler("text/plain","SiteSubtitle")) + "</description>");
//s.push("<language>" + config.locale + "</language>");
s.push("<pubDate>" + d.toUTCString() + "</pubDate>");
s.push("<lastBuildDate>" + d.toUTCString() + "</lastBuildDate>");
s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
s.push("<generator>https://github.com/Jermolene/cook.js</generator>");
// The body
var tiddlers = getRssTiddlers("modified","excludeLists");
var i,n = numRssItems > tiddlers.length ? 0 : tiddlers.length-numRssItems;
for(i=tiddlers.length-1; i>=n; i--) {
s.push("<item>\n" + tiddlerToRssItem(tiddlers[i],u) + "\n</item>");
}
// And footer
s.push("</channel>");
s.push("</rss>");
// Save it all
return s.join("\n");
};
exports.Recipe = Recipe;
})();

View File

@ -1,581 +0,0 @@
/*\
title: js/Renderer.js
Renderer objects encapsulate a tree of nodes that are capable of rendering and selectively updating an
HTML representation of themselves. The following node types are defined:
* ''MacroNode'' - represents an invocation of a macro
* ''ElementNode'' - represents a single HTML element
* ''TextNode'' - represents an HTML text node
* ''EntityNode'' - represents an HTML entity node
* ''RawNode'' - represents a chunk of unparsed HTML text
These node types are implemented with prototypal inheritance from a base Node class. One
unusual convenience in the implementation is that the node constructors can be called without an explicit
`new` keyword: the constructors check for `this` not being an instance of themselves, and recursively invoke
themselves with `new` when required.
Convenience functions are provided to wrap up the construction of some common interface elements:
* ''ErrorNode''
* ''LabelNode''
* ''SplitLabelNode''
\*/
(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");
/*
Intialise the renderer object
nodes: an array of Node objects
dependencies: an optional Dependencies object
store: a reference to the WikiStore object to use for rendering these nodes
*/
var Renderer = function(nodes,dependencies,store) {
this.nodes = nodes;
this.dependencies = dependencies;
this.store = store;
};
/*
The base class for all types of renderer nodes
*/
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.broadcastEvent = function(event) {
return true;
};
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
dependencies: optional Dependencies object representing the dependencies of this macro
Note that the dependencies will be evaluated if not provided.
*/
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 required
this.dependencies = dependencies ? dependencies : this.evaluateDependencies();
} else {
return new MacroNode(macroName,srcParams,children,store,dependencies);
}
};
MacroNode.prototype = new Node();
MacroNode.prototype.constructor = MacroNode;
/*
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
*/
MacroNode.prototype.evaluateDependencies = function() {
if(this.srcParams && this.macro) {
if(this.macro.evaluateDependencies) {
// Call the evaluateDependencies method if the macro provides it
return this.macro.evaluateDependencies.call(this);
} else {
// Figure out the dependencies from the metadata and parameters
var dependencies = new Dependencies();
if(this.macro.dependentAll) {
dependencies.dependentAll = true;
}
if(this.macro.dependentOnContextTiddler) {
dependencies.dependentOnContextTiddler = 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") {
dependencies.dependentAll = true;
} else {
dependencies.addDependency(this.srcParams[m],!paramInfo.skinny);
}
}
}
return dependencies;
}
}
};
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.hasParameter = function(name) {
return this.params.hasOwnProperty(name);
};
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<this.children.length; t++) {
childClones.push(this.children[t].clone());
}
}
return childClones;
};
MacroNode.prototype.execute = function(parents,tiddlerTitle) {
// Evaluate macro parameters to get their values
this.params = {};
var tiddler = this.store.getTiddler(tiddlerTitle);
if(!tiddler) {
tiddler = {title: tiddlerTitle};
}
for(var p in this.srcParams) {
if(typeof this.srcParams[p] === "function") {
this.params[p] = this.srcParams[p](tiddler,this.store,utils);
} else {
this.params[p] = this.srcParams[p];
}
}
// Save the context tiddler
this.tiddlerTitle = tiddlerTitle;
// Save a reference to the array of parents
this.parents = parents || [];
// Render the macro to get its content
this.content = this.macro.execute.call(this);
};
MacroNode.prototype.render = function(type) {
var output = [];
for(var t=0; t<this.content.length; t++) {
output.push(this.content[t].render(type));
}
return output.join("");
};
MacroNode.prototype.renderInDom = function(parentDomNode,insertBefore) {
// Create the wrapper node for the macro
var macroContainer = document.createElement(this.macro.wrapperTag || "span");
this.domNode = macroContainer;
if(insertBefore) {
parentDomNode.insertBefore(macroContainer,insertBefore);
} else {
parentDomNode.appendChild(macroContainer);
}
// Add some debugging information to it
macroContainer.setAttribute("data-tw-macro",this.macroName);
// Ask the macro to add event handlers to the node
if(this.macro.addEventHandlers) {
this.macro.addEventHandlers.call(this);
} else {
for(var e in this.macro.events) {
// Register this macro node to handle the event via the handleEvent() method
macroContainer.addEventListener(e,this,false);
}
}
// Render the content of the macro
for(var t=0; t<this.content.length; t++) {
this.content[t].renderInDom(macroContainer);
}
// Call the macro renderInDom method if it has one
if(this.macro.renderInDom) {
this.macro.renderInDom.call(this);
}
};
MacroNode.prototype.refresh = function(changes) {
var t,
self = this;
// Check if any of the dependencies of this macro node have changed
if(this.dependencies.hasChanged(changes,this.tiddlerTitle)) {
// Re-execute the macro if so
this.execute(this.parents,this.tiddlerTitle);
} else {
// Refresh any children
for(t=0; t<this.content.length; t++) {
this.content[t].refresh(changes);
}
}
};
MacroNode.prototype.refreshInDom = function(changes) {
var t,
self = this;
// Ask the macro to rerender itself if it can
if(this.macro.refreshInDom) {
this.macro.refreshInDom.call(this,changes);
} else {
// Check if any of the dependencies of this macro node have changed
if(this.dependencies.hasChanged(changes,this.tiddlerTitle)) {
// Manually reexecute and rerender this macro
while(this.domNode.hasChildNodes()) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.execute(this.parents,this.tiddlerTitle);
for(t=0; t<this.content.length; t++) {
this.content[t].renderInDom(this.domNode);
}
} else {
// Refresh any children
for(t=0; t<this.content.length; t++) {
this.content[t].refreshInDom(changes);
}
}
}
};
MacroNode.prototype.handleEvent = function(event) {
return this.macro.events[event.type].call(this,event);
};
MacroNode.prototype.broadcastEvent = function(event) {
if(this.macro.events && this.macro.events.hasOwnProperty(event.type)) {
if(!this.handleEvent(event)) {
return false;
}
}
if(this.content) {
for(var t=0; t<this.content.length; t++) {
if(!this.content[t].broadcastEvent(event)) {
return false;
}
}
}
return true;
};
var ElementNode = function(type,attributes,children) {
if(this instanceof ElementNode) {
this.type = type;
this.attributes = attributes;
this.children = children;
} else {
return new ElementNode(type,attributes,children);
}
};
ElementNode.prototype = new Node();
ElementNode.prototype.constructor = ElementNode;
ElementNode.prototype.clone = function() {
var childClones;
if(this.children) {
childClones = [];
for(var t=0; t<this.children.length; t++) {
childClones.push(this.children[t].clone());
}
}
return new ElementNode(this.type,this.attributes,childClones);
};
ElementNode.prototype.execute = function(parents,tiddlerTitle) {
if(this.children) {
for(var t=0; t<this.children.length; t++) {
this.children[t].execute(parents,tiddlerTitle);
}
}
};
ElementNode.prototype.render = function(type) {
var isHtml = type === "text/html",
output = [];
if(isHtml) {
output.push("<",this.type);
if(this.attributes) {
for(var a in this.attributes) {
var v = this.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(" ",a,"='",utils.htmlEncode(v),"'");
}
}
}
output.push(">");
}
if(this.children) {
for(var t=0; t<this.children.length; t++) {
output.push(this.children[t].render(type));
}
if(isHtml) {
output.push("</",this.type,">");
}
}
return output.join("");
};
ElementNode.prototype.renderInDom = function(parentDomNode,insertBefore) {
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);
}
}
}
}
if(insertBefore) {
parentDomNode.insertBefore(element,insertBefore);
} else {
parentDomNode.appendChild(element);
}
this.domNode = element;
if(this.children) {
for(var t=0; t<this.children.length; t++) {
this.children[t].renderInDom(element);
}
}
};
ElementNode.prototype.refresh = function(changes) {
if(this.children) {
for(var t=0; t<this.children.length; t++) {
this.children[t].refresh(changes);
}
}
};
ElementNode.prototype.refreshInDom = function(changes) {
if(this.children) {
for(var t=0; t<this.children.length; t++) {
this.children[t].refreshInDom(changes);
}
}
};
ElementNode.prototype.broadcastEvent = function(event) {
if(this.children) {
for(var t=0; t<this.children.length; t++) {
if(!this.children[t].broadcastEvent(event)) {
return false;
}
}
}
return true;
};
var TextNode = function(text) {
if(this instanceof TextNode) {
this.text = text;
} else {
return new TextNode(text);
}
};
TextNode.prototype = new Node();
TextNode.prototype.constructor = TextNode;
TextNode.prototype.render = function(type) {
return type === "text/html" ? utils.htmlEncode(this.text) : this.text;
};
TextNode.prototype.renderInDom = function(domNode) {
this.domNode = document.createTextNode(this.text);
domNode.appendChild(this.domNode);
};
var EntityNode = function(entity) {
if(this instanceof EntityNode) {
this.entity = entity;
} else {
return new EntityNode(entity);
}
};
EntityNode.prototype = new Node();
EntityNode.prototype.constructor = EntityNode;
EntityNode.prototype.render = function(type) {
return type === "text/html" ? this.entity : utils.entityDecode(this.entity);
};
EntityNode.prototype.renderInDom = function(domNode) {
this.domNode = document.createTextNode(utils.entityDecode(this.entity));
domNode.appendChild(this.domNode);
};
var RawNode = function(html) {
if(this instanceof RawNode) {
this.html = html;
} else {
return new RawNode(html);
}
};
RawNode.prototype = new Node();
RawNode.prototype.constructor = RawNode;
RawNode.prototype.render = function(type) {
return this.html;
};
RawNode.prototype.renderInDom = function(domNode) {
this.domNode = document.createElement("div");
this.domNode.innerHTML = this.html;
domNode.appendChild(this.domNode);
};
/*
Static method to construct an error message
*/
var ErrorNode = function(text) {
return new ElementNode("span",{
"class": ["label","label-important"]
},[
new TextNode(text)
]);
};
/*
Static method to construct a label
*/
var LabelNode = function(type,value,classes) {
classes = (classes || []).slice(0);
classes.push("label");
return new ElementNode("span",{
"class": classes,
"data-tw-label-type": type
},value);
};
/*
Static method to construct a split label
*/
var SplitLabelNode = function(type,left,right,classes) {
classes = (classes || []).slice(0);
classes.push("splitLabel");
return new ElementNode("span",{
"class": classes
},[
new ElementNode("span",{
"class": ["splitLabelLeft"],
"data-tw-label-type": type
},left),
new ElementNode("span",{
"class": ["splitLabelRight"]
},right)
]);
};
Renderer.MacroNode = MacroNode;
Renderer.ElementNode = ElementNode;
Renderer.TextNode = TextNode;
Renderer.EntityNode = EntityNode;
Renderer.RawNode = RawNode;
Renderer.ErrorNode = ErrorNode;
Renderer.LabelNode = LabelNode;
Renderer.SplitLabelNode = SplitLabelNode;
exports.Renderer = Renderer;
})();

View File

@ -1,201 +0,0 @@
/*\
title: js/Tiddler.js
Tiddlers are an immutable dictionary of name:value pairs called fields. Values can be a string,
an array of strings, or a JavaScript date object.
The only field that is required is the `title` field, but useful tiddlers also have a `text`
field, and some or all of the standard fields `modified`, `modifier`, `created`, `creator`,
`tags` and `type`.
Hardcoded in the system is the knowledge that the 'tags' field is a string array, and that
the 'modified' and 'created' fields are dates. All other fields are strings.
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("./Utils.js"),
ArgParser = require("./ArgParser.js").ArgParser;
var Tiddler = function(/* tiddler,fields */) {
var fields = {}, // Keep the fields private, later we'll expose getters for them
tags, // Keep the tags separately because they're the only Array field
f,t,c,arg,src;
// Accumulate the supplied fields
for(c=0; c<arguments.length; c++) {
arg = arguments[c];
src = null;
if(arg instanceof Tiddler) {
src = arg.getFields();
} else {
src = arg;
}
for(t in src) {
if(src[t] === undefined) {
// If we get a field that's undefined, delete any previous field value
delete fields[t];
} else {
f = Tiddler.parseTiddlerField(t,src[t]);
if(f !== null) {
fields[t] = f;
}
}
}
}
// Pull out the tags
if(fields.tags) {
tags = fields.tags;
delete fields.tags;
}
// Expose the fields as read only properties
for(f in fields) {
Object.defineProperty(this,f,{value: fields[f], writeable: false});
}
// Expose the tags as a getter
Object.defineProperty(this,"tags",{get: function() {return tags ? tags.slice(0) : [];}});
// Get a copy of the fields hashmap
this.getFields = function() {
var r = {};
for(var f in fields) {
r[f] = fields[f];
}
if(tags) {
r.tags = tags.slice(0);
}
return r;
};
// Return a field as a string
this.getFieldString = function(field) {
var result, t;
if(field === "tags") {
if(tags) {
result = [];
for(t=0; t<tags.length; t++) {
if(tags[t].indexOf(" ") !== -1) {
result.push("[[" + tags[t] + "]]");
} else {
result.push(tags[t]);
}
}
return result.join(" ");
} else {
return null;
}
} else {
if(fields.hasOwnProperty(field)) {
if(field === "created" || field === "modified") {
return utils.convertToYYYYMMDDHHMM(fields[field]);
} else {
return fields[field];
}
} else {
return null;
}
}
};
// Get all the tiddler fields as an array of {name:string,value:string} objects
this.getFieldStrings = function() {
var result = [],
fieldOrder = "title creator modifier created modified tags type".split(" "),
t,v;
for(t=0; t<fieldOrder.length; t++) {
v = this.getFieldString(fieldOrder[t]);
if(v !== null) {
result.push({name: fieldOrder[t], value: v});
}
}
for(t in fields) {
if(fieldOrder.indexOf(t) === -1 && t !== "text") {
result.push({name: t, value: fields[t]});
}
}
return result;
};
};
Tiddler.prototype.hasTag = function(tag) {
return this.tags.indexOf(tag) !== -1;
};
Tiddler.standardFields = {
title: { type: "string"},
modifier: { type: "string"},
modified: { type: "date"},
creator: { type: "string"},
created: { type: "date"},
tags: { type: "tags"},
type: { type: "string"},
text: { type: "string"}
};
// These are the non-string fields
Tiddler.specialTiddlerFields = {
"created": "date",
"modified": "date",
"tags": "array"
};
Tiddler.isStandardField = function(name) {
return name in Tiddler.standardFields;
};
Tiddler.specialTiddlerFieldParsers = {
date: function(value) {
if(typeof value === "string") {
return utils.convertFromYYYYMMDDHHMMSSMMM(value);
} else if (value instanceof Date) {
return value;
} else {
return null;
}
},
array: function(value) {
if(typeof value === "string") {
var parser = new ArgParser(value,{noNames: true, allowEval: false});
return parser.getStringValues();
} else if (value instanceof Array) {
var result = [];
for(var t=0; t<value.length; t++) {
if(typeof value[t] === "string") {
result.push(value[t]);
}
}
return result;
} else {
return null;
}
}
};
Tiddler.compareTiddlerFields = function(a,b,sortField) {
var aa = a[sortField] || 0,
bb = b[sortField] || 0;
if(aa < bb) {
return -1;
} else {
if(aa > bb) {
return 1;
} else {
return 0;
}
}
};
Tiddler.parseTiddlerField = function(name,value) {
var type = Tiddler.specialTiddlerFields[name];
if(type) {
return Tiddler.specialTiddlerFieldParsers[type](value);
} else if (typeof value === "string") {
return value;
} else {
return null;
}
};
exports.Tiddler = Tiddler;
})();

View File

@ -1,242 +0,0 @@
/*\
title: js/TiddlerInput.js
Functions concerned with parsing representations of tiddlers
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("./Utils.js"),
Tiddler = require("./Tiddler.js").Tiddler,
util = require("util");
var tiddlerInput = exports;
/*
Utility function to parse a block of metadata and merge the results into a hashmap of tiddler fields.
The block consists of newline delimited lines consisting of the field name, a colon, and then the value. For example:
title: Safari
modifier: blaine
created: 20110211110700
modified: 20110211131020
tags: browsers issues
creator: psd
*/
var parseMetaDataBlock = function(metaData,fields) {
var result = {};
if(fields) {
for(var t in fields) {
result[t] = fields[t];
}
}
metaData.split("\n").forEach(function(line) {
var p = line.indexOf(":");
if(p !== -1) {
var field = line.substr(0, p).trim();
var value = line.substr(p+1).trim();
result[field] = value;
}
});
return result;
};
/*
Utility function to parse an old-style tiddler DIV. It looks like this:
<div title="Title" creator="JoeBloggs" modifier="JoeBloggs" created="201102111106" modified="201102111310" tags="myTag [[my long tag]]">
<pre>The text of the tiddler (without the expected HTML encoding).
</pre>
</div>
Note that the field attributes are HTML encoded, but that the body of the <PRE> tag is not.
*/
var parseTiddlerDiv = function(text,fields) {
var result = {};
if(fields) {
for(var t in fields) {
result[t] = fields[t];
}
}
var divRegExp = /^\s*<div\s+([^>]*)>((?:.|\n)*)<\/div>\s*$/gi,
subDivRegExp = /^\s*<pre>((?:.|\n)*)<\/pre>\s*$/gi,
attrRegExp = /\s*([^=\s]+)\s*=\s*"([^"]*)"/gi,
match = divRegExp.exec(text);
if(match) {
var subMatch = subDivRegExp.exec(match[2]); // Body of the <DIV> tag
if(subMatch) {
result.text = subMatch[1];
} else {
result.text = match[2];
}
var attrMatch;
do {
attrMatch = attrRegExp.exec(match[1]);
if(attrMatch) {
var name = attrMatch[1];
var value = attrMatch[2];
result[name] = value;
}
} while(attrMatch);
}
return result;
};
var inputTiddlerPlain = function(text,fields) {
fields.text = text;
return [fields];
};
var inputTiddlerJavaScript = function(text,fields) {
var headerCommentRegExp = /^\/\*\\\n((?:^[^\n]*\n)+?)(^\\\*\/$\n?)/mg,
match = headerCommentRegExp.exec(text);
fields.text = text;
if(match) {
fields = parseMetaDataBlock(match[1],fields);
}
return [fields];
};
var inputTiddlerDiv = function(text,fields) {
return [parseTiddlerDiv(text,fields)];
};
var inputTiddler = function(text,fields) {
var split = text.indexOf("\n\n");
if(split !== -1) {
fields.text = text.substr(split + 2);
}
if(split === -1) {
split = text.length;
}
fields = parseMetaDataBlock(text.substr(0,split),fields);
return [fields];
};
var inputTiddlerJSON = function(text,fields) {
var tiddlers = JSON.parse(text),
result = [],
getKnownFields = function(tid) {
var fields = {};
"title text created creator modified modifier type tags".split(" ").forEach(function(value) {
fields[value] = tid[value];
});
return fields;
};
for(var t=0; t<tiddlers.length; t++) {
result.push(getKnownFields(tiddlers[t]));
}
return result;
};
var inputTiddlyWiki = function(text,fields) {
var locateStoreArea = function(tiddlywikidoc) {
var startSaveArea = '<div id="' + 'storeArea">',
startSaveAreaRegExp = /<div id=["']?storeArea['"]?>/gi,
endSaveArea = '</d' + 'iv>',
endSaveAreaCaps = '</D' + 'IV>',
posOpeningDiv = tiddlywikidoc.search(startSaveAreaRegExp),
limitClosingDiv = tiddlywikidoc.indexOf("<"+"!--POST-STOREAREA--"+">");
if(limitClosingDiv == -1) {
limitClosingDiv = tiddlywikidoc.indexOf("<"+"!--POST-BODY-START--"+">");
}
var start = limitClosingDiv == -1 ? tiddlywikidoc.length : limitClosingDiv,
posClosingDiv = tiddlywikidoc.lastIndexOf(endSaveArea,start);
if(posClosingDiv == -1) {
posClosingDiv = tiddlywikidoc.lastIndexOf(endSaveAreaCaps,start);
}
return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv + startSaveArea.length,posClosingDiv] : null;
},
results = [],
storeAreaPos = locateStoreArea(text);
if(storeAreaPos) {
var endOfDivRegExp = /(<\/div>\s*)/gi,
startPos = storeAreaPos[0];
endOfDivRegExp.lastIndex = startPos;
var match = endOfDivRegExp.exec(text);
while(match && startPos < storeAreaPos[1]) {
var endPos = endOfDivRegExp.lastIndex,
tiddlerFields = parseTiddlerDiv(text.substring(startPos,endPos),fields);
if(tiddlerFields.text !== null) {
tiddlerFields.text = utils.htmlDecode(tiddlerFields.text);
results.push(tiddlerFields);
}
startPos = endPos;
match = endOfDivRegExp.exec(text);
}
}
return results;
};
// Given a reference to a DOM node, return the tiddlers stored in the immediate child nodes
var inputTiddlerDOM = function(node) {
var extractTextTiddler = function(node) {
var e = node.firstChild;
while(e && e.nodeName.toLowerCase() !== "pre") {
e = e.nextSibling;
}
var title = node.getAttribute ? node.getAttribute("title") : null;
if(e && title) {
var i,
attrs = node.attributes,
tiddler = {
text: utils.htmlDecode(e.innerHTML)
};
for(i=attrs.length-1; i >= 0; i--) {
if(attrs[i].specified) {
var value = attrs[i].value,
name = attrs[i].name;
if(!Tiddler.isStandardField(name)) {
value = utils.unescapeLineBreaks(value);
}
tiddler[name] = value;
}
}
return tiddler;
} else {
return null;
}
},
extractModuleTiddler = function(node) {
if(node.hasAttribute && node.hasAttribute("data-tiddler-title")) {
var text = node.innerHTML,
s = text.indexOf("{"),
e = text.lastIndexOf("}");
if(s !== -1 && e !== -1) {
text = text.substring(s+1,e-1);
}
return {
title: node.getAttribute("data-tiddler-title"),
text: text,
type: "application/javascript"
};
} else {
return null;
}
},
t,tiddlers = [];
for(t = 0; t < node.childNodes.length; t++) {
var tiddler = extractTextTiddler(node.childNodes[t]);
tiddler = tiddler || extractModuleTiddler(node.childNodes[t]);
if(tiddler) {
tiddlers.push(tiddler);
}
}
return tiddlers;
};
tiddlerInput.register = function(store) {
store.registerTiddlerDeserializer(".txt","text/plain",inputTiddlerPlain);
store.registerTiddlerDeserializer(".js","application/javascript",inputTiddlerJavaScript);
store.registerTiddlerDeserializer(".tiddler","application/x-tiddler-html-div",inputTiddlerDiv);
store.registerTiddlerDeserializer(".tid","application/x-tiddler",inputTiddler);
store.registerTiddlerDeserializer(".json","application/json",inputTiddlerJSON);
store.registerTiddlerDeserializer(".tiddlywiki","application/x-tiddlywiki",inputTiddlyWiki);
store.registerTiddlerDeserializer("(DOM)","(DOM)",inputTiddlerDOM);
};
})();

View File

@ -1,135 +0,0 @@
/*\
title: js/TiddlerOutput.js
Serializers that output tiddlers in a variety of formats.
store.serializeTiddlers(tiddlers,type)
tiddlers: An array of tiddler objects
type: The target output type as a file extension like `.tid` or a MIME type like `application/x-tiddler`. If `null` or `undefined` then the best type is chosen automatically
The serializer returns an array of information defining one or more files containing the tiddlers:
[
{name: "title.tid", type: "application/x-tiddler", ext: ".tid", data: "xxxxx"},
{name: "title.jpg", type: "image/jpeg", ext: ".jpg", binary: true, data: "xxxxx"},
{name: "title.jpg.meta", type: "application/x-tiddler-metadata", ext: ".meta", data: "xxxxx"}
]
Notes:
* The `type` field is the type of the file, which is not necessrily the same as the type of the tiddler.
* The `binary` field may be omitted if it is not `true`
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("./Utils.js"),
util = require("util");
var tiddlerOutput = exports;
var outputMetaDataBlock = function(tiddler) {
var result = [],
fields = tiddler.getFieldStrings(),
t;
for(t=0; t<fields.length; t++) {
result.push(fields[t].name + ": " + fields[t].value);
}
return result.join("\n");
};
/*
Output tiddlers as separate files in their native formats (ie. `.tid` or `.jpg`/`.jpg.meta`)
*/
var outputTiddlers = function(tiddlers) {
var result = [];
for(var t=0; t<tiddlers.length; t++) {
var tiddler = tiddlers[t],
extension,
binary = false;
switch(tiddler.type) {
case "image/jpeg":
extension = ".jpg";
binary = true;
break;
case "image/gif":
extension = ".gif";
binary = true;
break;
case "image/png":
extension = ".png";
binary = true;
break;
case "image/svg+xml":
extension = ".svg";
break;
default:
extension = ".tid";
break;
}
if(extension === ".tid") {
result.push({
name: tiddler.title + ".tid",
type: "application/x-tiddler",
extension: ".tid",
data: outputMetaDataBlock(tiddler) + "\n\n" + tiddler.text,
binary: false
});
} else {
result.push({
name: tiddler.title,
type: tiddler.type,
extension: extension,
data: tiddler.text,
binary: binary
});
result.push({
name: tiddler.title + ".meta",
type: "application/x-tiddler-metadata",
extension: ".meta",
data: outputMetaDataBlock(tiddler),
binary: false
});
}
}
return result;
};
/*
Output an array of tiddlers as HTML <DIV>s
out - array to push the output strings
tid - the tiddler to be output
The fields are in the order title, creator, modifier, created, modified, tags, followed by any others
*/
var outputTiddlerDivs = function(tiddlers) {
var result = [];
for(var t=0; t<tiddlers.length; t++) {
var tiddler = tiddlers[t],
output = [],
fieldStrings = tiddler.getFieldStrings();
output.push("<div");
for(var f=0; f<fieldStrings.length; f++) {
output.push(" " + fieldStrings[f].name + "=\"" + fieldStrings[f].value + "\"");
}
output.push(">\n<pre>");
output.push(utils.htmlEncode(tiddler.text));
output.push("</pre>\n</div>");
result.push({
name: tiddler.title,
type: "application/x-tiddler-html-div",
extension: ".tiddler",
data: output.join("")
});
}
return result;
};
tiddlerOutput.register = function(store) {
store.registerTiddlerSerializer(".tid","application/x-tiddler",outputTiddlers);
store.registerTiddlerSerializer(".tiddler","application/x-tiddler-html-div",outputTiddlerDivs);
};
})();

View File

@ -1,58 +0,0 @@
/*\
title: js/TiddlyTextParser.js
Parses a plain text block that can also contain macros and transclusions.
The syntax for transclusions is:
[[tiddlerTitle]]
The syntax for macros is:
<<macroName params>>
\*/
(function(){
/*jslint node: true */
"use strict";
var Dependencies = require("./Dependencies.js").Dependencies,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js");
var TiddlyTextParser = function(options) {
this.store = options.store;
};
TiddlyTextParser.prototype.parse = function(type,text) {
var output = [],
dependencies = new Dependencies(),
macroRegExp = /(?:\[\[([^\]]+)\]\])|(?:<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>)/mg,
lastMatchPos = 0,
match,
macroNode;
do {
match = macroRegExp.exec(text);
if(match) {
output.push(Renderer.TextNode(text.substring(lastMatchPos,match.index)));
if(match[1]) { // Transclusion
macroNode = Renderer.MacroNode("tiddler",{
target: match[1]
},[],this.store);
} else if(match[2]) { // Macro call
macroNode = Renderer.MacroNode(match[2],match[3],[],this.store);
}
output.push(macroNode);
dependencies.mergeDependencies(macroNode.dependencies);
lastMatchPos = match.index + match[0].length;
} else {
output.push(Renderer.TextNode(text.substr(lastMatchPos)));
}
} while(match);
return new Renderer(output,dependencies,this.store);
};
exports.TiddlyTextParser = TiddlyTextParser;
})();

View File

@ -1,294 +0,0 @@
/*\
title: js/Utils.js
Various static utility functions.
This file is a bit of a dumping ground; the expectation is that most of these functions will be refactored.
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var utils = exports;
// Pad a string to a certain length with zeros
utils.zeroPad = function(n,d)
{
var s = n.toString();
if(s.length < d)
s = "000000000000000000000000000".substr(0,d-s.length) + s;
return s;
};
// Convert a date to UTC YYYYMMDDHHMM string format
utils.convertToYYYYMMDDHHMM = function(date)
{
return date.getUTCFullYear() + utils.zeroPad(date.getUTCMonth()+1,2) + utils.zeroPad(date.getUTCDate(),2) + utils.zeroPad(date.getUTCHours(),2) + utils.zeroPad(date.getUTCMinutes(),2);
};
// Convert a date to UTC YYYYMMDDHHMMSSMMM string format
utils.convertToYYYYMMDDHHMMSSMMM = function(date)
{
return date.getUTCFullYear() + utils.zeroPad(date.getUTCMonth()+1,2) + utils.zeroPad(date.getUTCDate(),2) + utils.zeroPad(date.getUTCHours(),2) + utils.zeroPad(date.getUTCMinutes(),2) + utils.zeroPad(date.getUTCSeconds(),2) + utils.zeroPad(date.getUTCMilliseconds(),3) +"0";
};
// Create a UTC date from a YYYYMMDDHHMM format string
utils.convertFromYYYYMMDDHHMM = function(d)
{
return utils.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,12));
};
// Create a UTC date from a YYYYMMDDHHMMSS format string
utils.convertFromYYYYMMDDHHMMSS = function(d)
{
return utils.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,14));
};
// Create a UTC date from a YYYYMMDDHHMMSSMMM format string
utils.convertFromYYYYMMDDHHMMSSMMM = function(d)
{
return new Date(Date.UTC(parseInt(d.substr(0,4),10),
parseInt(d.substr(4,2),10)-1,
parseInt(d.substr(6,2),10),
parseInt(d.substr(8,2)||"00",10),
parseInt(d.substr(10,2)||"00",10),
parseInt(d.substr(12,2)||"00",10),
parseInt(d.substr(14,3)||"000",10)));
};
utils.formatDateString = function (date,template) {
var t = template.replace(/0hh12/g,utils.zeroPad(utils.getHours12(date),2));
t = t.replace(/hh12/g,utils.getHours12(date));
t = t.replace(/0hh/g,utils.zeroPad(date.getHours(),2));
t = t.replace(/hh/g,date.getHours());
t = t.replace(/mmm/g,utils.dateFormats.shortMonths[date.getMonth()]);
t = t.replace(/0mm/g,utils.zeroPad(date.getMinutes(),2));
t = t.replace(/mm/g,date.getMinutes());
t = t.replace(/0ss/g,utils.zeroPad(date.getSeconds(),2));
t = t.replace(/ss/g,date.getSeconds());
t = t.replace(/[ap]m/g,utils.getAmPm(date).toLowerCase());
t = t.replace(/[AP]M/g,utils.getAmPm(date).toUpperCase());
t = t.replace(/wYYYY/g,utils.getYearForWeekNo(date));
t = t.replace(/wYY/g,utils.zeroPad(utils.getYearForWeekNo(date)-2000,2));
t = t.replace(/YYYY/g,date.getFullYear());
t = t.replace(/YY/g,utils.zeroPad(date.getFullYear()-2000,2));
t = t.replace(/MMM/g,utils.dateFormats.months[date.getMonth()]);
t = t.replace(/0MM/g,utils.zeroPad(date.getMonth()+1,2));
t = t.replace(/MM/g,date.getMonth()+1);
t = t.replace(/0WW/g,utils.zeroPad(utils.getWeek(date),2));
t = t.replace(/WW/g,utils.getWeek(date));
t = t.replace(/DDD/g,utils.dateFormats.days[date.getDay()]);
t = t.replace(/ddd/g,utils.dateFormats.shortDays[date.getDay()]);
t = t.replace(/0DD/g,utils.zeroPad(date.getDate(),2));
t = t.replace(/DDth/g,date.getDate()+utils.getDaySuffix(date));
t = t.replace(/DD/g,date.getDate());
var tz = date.getTimezoneOffset();
var atz = Math.abs(tz);
t = t.replace(/TZD/g,(tz < 0 ? '+' : '-') + utils.zeroPad(Math.floor(atz / 60),2) + ':' + utils.zeroPad(atz % 60,2));
t = t.replace(/\\/g,"");
return t;
};
utils.dateFormats = {
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"],
days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
daySuffixes: ["st","nd","rd","th","th","th","th","th","th","th",
"th","th","th","th","th","th","th","th","th","th",
"st","nd","rd","th","th","th","th","th","th","th",
"st"],
am: "am",
pm: "pm"
};
utils.getAmPm = function(date) {
return date.getHours() >= 12 ? utils.dateFormats.pm : utils.dateFormats.am;
};
utils.getDaySuffix = function(date) {
return utils.dateFormats.daySuffixes[date.getDate()-1];
};
utils.getWeek = function(date) {
var dt = new Date(date.getTime());
var d = dt.getDay();
if(d === 0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
return Math.floor(n/7)+1;
};
utils.getYearForWeekNo = function(date) {
var dt = new Date(date.getTime());
var d = dt.getDay();
if(d === 0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
return dt.getFullYear();
};
utils.getHours12 = function(date) {
var h = date.getHours();
return h > 12 ? h-12 : ( h > 0 ? h : 12 );
};
// Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
utils.htmlEncode = function(s)
{
if(s) {
return s.toString().replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;");
} else {
return "";
}
};
// Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
utils.htmlDecode = function(s)
{
return s.toString().replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
};
// Converts all HTML entities to their character equivalents
utils.entityDecode = function(s) {
var e = s.substr(1,s.length-2); // Strip the & and the ;
if(e.charAt(0) === "#") {
if(e.charAt(1) === "x" || e.charAt(1) === "X") {
return String.fromCharCode(parseInt(e.substr(2),16));
} else {
return String.fromCharCode(parseInt(e.substr(1),10));
}
} else {
var c = utils.htmlEntities[e];
if(c) {
return String.fromCharCode(c);
} else {
return s; // Couldn't convert it as an entity, just return it raw
}
}
};
utils.htmlEntities = {quot:34, amp:38, apos:39, lt:60, gt:62, nbsp:160, iexcl:161, cent:162, pound:163, curren:164, yen:165, brvbar:166, sect:167, uml:168, copy:169, ordf:170, laquo:171, not:172, shy:173, reg:174, macr:175, deg:176, plusmn:177, sup2:178, sup3:179, acute:180, micro:181, para:182, middot:183, cedil:184, sup1:185, ordm:186, raquo:187, frac14:188, frac12:189, frac34:190, iquest:191, Agrave:192, Aacute:193, Acirc:194, Atilde:195, Auml:196, Aring:197, AElig:198, Ccedil:199, Egrave:200, Eacute:201, Ecirc:202, Euml:203, Igrave:204, Iacute:205, Icirc:206, Iuml:207, ETH:208, Ntilde:209, Ograve:210, Oacute:211, Ocirc:212, Otilde:213, Ouml:214, times:215, Oslash:216, Ugrave:217, Uacute:218, Ucirc:219, Uuml:220, Yacute:221, THORN:222, szlig:223, agrave:224, aacute:225, acirc:226, atilde:227, auml:228, aring:229, aelig:230, ccedil:231, egrave:232, eacute:233, ecirc:234, euml:235, igrave:236, iacute:237, icirc:238, iuml:239, eth:240, ntilde:241, ograve:242, oacute:243, ocirc:244, otilde:245, ouml:246, divide:247, oslash:248, ugrave:249, uacute:250, ucirc:251, uuml:252, yacute:253, thorn:254, yuml:255, OElig:338, oelig:339, Scaron:352, scaron:353, Yuml:376, fnof:402, circ:710, tilde:732, Alpha:913, Beta:914, Gamma:915, Delta:916, Epsilon:917, Zeta:918, Eta:919, Theta:920, Iota:921, Kappa:922, Lambda:923, Mu:924, Nu:925, Xi:926, Omicron:927, Pi:928, Rho:929, Sigma:931, Tau:932, Upsilon:933, Phi:934, Chi:935, Psi:936, Omega:937, alpha:945, beta:946, gamma:947, delta:948, epsilon:949, zeta:950, eta:951, theta:952, iota:953, kappa:954, lambda:955, mu:956, nu:957, xi:958, omicron:959, pi:960, rho:961, sigmaf:962, sigma:963, tau:964, upsilon:965, phi:966, chi:967, psi:968, omega:969, thetasym:977, upsih:978, piv:982, ensp:8194, emsp:8195, thinsp:8201, zwnj:8204, zwj:8205, lrm:8206, rlm:8207, ndash:8211, mdash:8212, lsquo:8216, rsquo:8217, sbquo:8218, ldquo:8220, rdquo:8221, bdquo:8222, dagger:8224, Dagger:8225, bull:8226, hellip:8230, permil:8240, prime:8242, Prime:8243, lsaquo:8249, rsaquo:8250, oline:8254, frasl:8260, euro:8364, image:8465, weierp:8472, real:8476, trade:8482, alefsym:8501, larr:8592, uarr:8593, rarr:8594, darr:8595, harr:8596, crarr:8629, lArr:8656, uArr:8657, rArr:8658, dArr:8659, hArr:8660, forall:8704, part:8706, exist:8707, empty:8709, nabla:8711, isin:8712, notin:8713, ni:8715, prod:8719, sum:8721, minus:8722, lowast:8727, radic:8730, prop:8733, infin:8734, ang:8736, and:8743, or:8744, cap:8745, cup:8746, int:8747, there4:8756, sim:8764, cong:8773, asymp:8776, ne:8800, equiv:8801, le:8804, ge:8805, sub:8834, sup:8835, nsub:8836, sube:8838, supe:8839, oplus:8853, otimes:8855, perp:8869, sdot:8901, lceil:8968, rceil:8969, lfloor:8970, rfloor:8971, lang:9001, rang:9002, loz:9674, spades:9824, clubs:9827, hearts:9829, diams:9830 };
utils.unescapeLineBreaks = function(s) {
return s.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
};
/*
* Returns an escape sequence for given character. Uses \x for characters <=
* 0xFF to save space, \u for the rest.
*
* The code needs to be in sync with th code template in the compilation
* function for "action" nodes.
*/
// Copied from peg.js, thanks to David Majda
utils.escape = function(ch) {
var escapeChar,
length,
charCode = ch.charCodeAt(0);
if (charCode <= 0xFF) {
escapeChar = 'x';
length = 2;
} else {
escapeChar = 'u';
length = 4;
}
return '\\' + escapeChar + utils.zeroPad(charCode.toString(16).toUpperCase(),length);
};
// Turns a string into a legal JavaScript string
// Copied from peg.js, thanks to David Majda
utils.stringify = function(s) {
/*
* ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
* literal except for the closing quote character, backslash, carriage return,
* line separator, paragraph separator, and line feed. Any character may
* appear in the form of an escape sequence.
*
* For portability, we also escape escape all non-ASCII characters.
*/
return s
.replace(/\\/g, '\\\\') // backslash
.replace(/"/g, '\\"') // double quote character
.replace(/'/g, "\\'") // single quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\n/g, '\\n') // line feed
.replace(/[\x80-\uFFFF]/g, utils.escape); // non-ASCII characters
};
utils.nextTick = function(fn) {
/*global window: false */
if(typeof window !== "undefined") {
// Apparently it would be faster to use postMessage - http://dbaron.org/log/20100309-faster-timeouts
window.setTimeout(fn,4);
} else {
process.nextTick(fn);
}
};
/*
Determines whether element 'a' contains element 'b'
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
*/
utils.domContains = function(a,b) {
return a.contains ?
a != b && a.contains(b) :
!!(a.compareDocumentPosition(b) & 16);
};
utils.hasClass = function(el,className) {
return el.className.split(" ").indexOf(className) !== -1;
};
utils.addClass = function(el,className) {
var c = el.className.split(" ");
if(c.indexOf(className) === -1) {
c.push(className);
}
el.className = c.join(" ");
};
utils.removeClass = function(el,className) {
var c = el.className.split(" "),
p = c.indexOf(className);
if(p !== -1) {
c.splice(p,1);
el.className = c.join(" ");
}
};
utils.toggleClass = function(el,className,status) {
if(status === undefined) {
status = !utils.hasClass(el,className);
}
if(status) {
utils.addClass(el,className);
} else {
utils.removeClass(el,className);
}
};
utils.applyStyleSheet = function(id,css) {
var el = document.getElementById(id);
if(document.createStyleSheet) { // Older versions of IE
if(el) {
el.parentNode.removeChild(el);
}
document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd",
'&nbsp;<style id="' + id + '" type="text/css">' + css + '</style>'); // fails without &nbsp;
} else { // Modern browsers
if(el) {
el.replaceChild(document.createTextNode(css), el.firstChild);
} else {
el = document.createElement("style");
el.type = "text/css";
el.id = id;
el.appendChild(document.createTextNode(css));
document.getElementsByTagName("head")[0].appendChild(el);
}
}
};
})();

View File

@ -1,347 +0,0 @@
/*\
title: js/WikiStore.js
WikiStore uses the .cache member of tiddlers to store the following information:
parseTree: Caches the parse tree for the tiddler
renderers: Caches rendering functions for this tiddler (indexed by MIME type)
\*/
(function(){
/*jslint node: true */
"use strict";
var Tiddler = require("./Tiddler.js").Tiddler,
Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
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
*/
var WikiStore = function WikiStore(options) {
options = options || {};
this.tiddlers = {}; // Hashmap of tiddlers by title
this.parsers = {}; // Hashmap of parsers by accepted MIME type
this.macros = {}; // Hashmap of macros by macro name
this.caches = {}; // Hashmap of cache objects by tiddler title, each is a hashmap of named caches
this.changeCount = {}; // Hashmap of integer changecount (>1) for each tiddler; persistent across deletions
this.tiddlerSerializers = {}; // Hashmap of serializers by target MIME type
this.tiddlerDeserializers = {}; // Hashmap of deserializers by accepted MIME type
this.eventListeners = []; // Array of {filter:,listener:}
this.eventsTriggered = false;
this.changedTiddlers = {}; // Hashmap of {title: "created|modified|deleted"}
this.shadows = options.shadowStore !== undefined ? options.shadowStore : new WikiStore({
shadowStore: null
});
};
WikiStore.prototype.incChangeCount = function(title) {
if(this.changeCount.hasOwnProperty(title)) {
this.changeCount[title]++;
} else {
this.changeCount[title] = 1;
}
};
WikiStore.prototype.getChangeCount = function(title) {
if(this.changeCount.hasOwnProperty(title)) {
return this.changeCount[title];
} else {
return 0;
}
};
WikiStore.prototype.registerParser = function(type,parser) {
if(type instanceof Array) {
for(var t=0; t<type.length; t++) {
this.parsers[type[t]] = parser;
}
} else {
this.parsers[type] = parser;
}
};
WikiStore.prototype.registerTiddlerSerializer = function(extension,mimeType,serializer) {
this.tiddlerSerializers[extension] = serializer;
this.tiddlerSerializers[mimeType] = serializer;
};
WikiStore.prototype.registerTiddlerDeserializer = function(extension,mimeType,deserializer) {
this.tiddlerDeserializers[extension] = deserializer;
this.tiddlerDeserializers[mimeType] = deserializer;
};
WikiStore.prototype.addEventListener = function(filter,listener) {
this.eventListeners.push({
filter: filter,
listener: listener
});
};
WikiStore.prototype.removeEventListener = function(listener) {
for(var c=this.eventListeners.length-1; c>=0; c--) {
var l = this.eventListeners[c];
if(l.listener === listener) {
this.eventListeners.splice(c,1);
}
}
};
/*
Causes a tiddler to be marked as changed, so that event listeners are triggered for it
type: Type of change to be registered for the tiddler "created", "modified" or "deleted"
If the tiddler is already touched, the resultant touch type is as follows:
If the tiddler is already marked "created",
... attempts to mark it "modified" leave it "created"
... attempts to mark it "deleted" succeed
If the tiddler is already marked "modified",
... attempts to mark it "deleted" succeed
If the tiddler is already marked "deleted",
... attempts to mark it "created" succeed
... attempts to mark it "modified" fail
*/
WikiStore.prototype.touchTiddler = function(type,title) {
this.changedTiddlers[title] = type;
this.triggerEvents();
};
WikiStore.prototype.clearEvents = function() {
this.changedTiddlers = {};
};
/*
Trigger the execution of the event dispatcher at the next tick, if it is not already triggered
*/
WikiStore.prototype.triggerEvents = function() {
if(!this.eventsTriggered) {
var me = this;
utils.nextTick(function() {
var changes = me.changedTiddlers;
me.changedTiddlers = {};
me.eventsTriggered = false;
for(var e=0; e<me.eventListeners.length; e++) {
var listener = me.eventListeners[e];
listener.listener(changes);
}
});
this.eventsTriggered = true;
}
};
WikiStore.prototype.getTiddler = function(title) {
var t = this.tiddlers[title];
if(t instanceof Tiddler) {
return t;
} else if(this.shadows) {
return this.shadows.getTiddler(title);
} else {
return null;
}
};
WikiStore.prototype.getTiddlerText = function(title,defaultText) {
defaultText = typeof defaultText === "string" ? defaultText : null;
var t = this.getTiddler(title);
return t instanceof Tiddler ? t.text : defaultText;
};
WikiStore.prototype.deleteTiddler = function(title) {
this.incChangeCount(title);
delete this.tiddlers[title];
this.clearCache(title);
this.touchTiddler("deleted",title);
};
WikiStore.prototype.tiddlerExists = function(title) {
var exists = this.tiddlers[title] instanceof Tiddler;
if(exists) {
return true;
} else if (this.shadows) {
return this.shadows.tiddlerExists(title);
}
return ;
};
WikiStore.prototype.addTiddler = function(tiddler) {
// Check if we're passed a fields hashmap instead of a tiddler
if(!(tiddler instanceof Tiddler)) {
tiddler = new Tiddler(tiddler);
}
this.incChangeCount(tiddler.title);
var status = tiddler.title in this.tiddlers ? "modified" : "created";
this.clearCache(tiddler.title);
this.tiddlers[tiddler.title] = tiddler;
this.touchTiddler(status,tiddler.title);
};
WikiStore.prototype.addTiddlers = function(tiddlers) {
for(var t=0; t<tiddlers.length; t++) {
this.addTiddler(tiddlers[t]);
}
};
WikiStore.prototype.forEachTiddler = function(/* [sortField,[excludeTag,]]callback */) {
var a = 0,
sortField = arguments.length > 1 ? arguments[a++] : null,
excludeTag = arguments.length > 2 ? arguments[a++] : null,
callback = arguments[a++],
t,
tiddlers = [],
tiddler;
if(sortField) {
for(t in this.tiddlers) {
tiddlers.push(this.tiddlers[t]);
}
tiddlers.sort(function (a,b) {return Tiddler.compareTiddlerFields(a,b,sortField);});
for(t=0; t<tiddlers.length; t++) {
if(!tiddlers[t].hasTag(excludeTag)) {
callback.call(this,tiddlers[t].title,tiddlers[t]);
}
}
} else {
for(t in this.tiddlers) {
tiddler = this.tiddlers[t];
if(tiddler instanceof Tiddler && !tiddler.hasTag(excludeTag))
callback.call(this,t,tiddler);
}
}
};
WikiStore.prototype.getTitles = function(sortField,excludeTag) {
sortField = sortField || "title";
var tiddlers = [];
this.forEachTiddler(sortField,excludeTag,function(title,tiddler) {
tiddlers.push(title);
});
return tiddlers;
};
WikiStore.prototype.getMissingTitles = function() {
return [];
};
WikiStore.prototype.getOrphanTitles = function() {
return [];
};
WikiStore.prototype.getShadowTitles = function() {
return this.shadows ? this.shadows.getTitles() : [];
};
WikiStore.prototype.serializeTiddlers = function(tiddlers,type) {
type = type || "application/x-tiddler";
var serializer = this.tiddlerSerializers[type];
if(serializer) {
return serializer(tiddlers);
} else {
return null;
}
};
WikiStore.prototype.deserializeTiddlers = function(type,text,srcFields) {
var fields = {},
deserializer = this.tiddlerDeserializers[type],
t;
if(srcFields) {
for(t in srcFields) {
fields[t] = srcFields[t];
}
}
if(deserializer) {
return deserializer(text,fields);
} else {
// Return a raw tiddler for unknown types
fields.text = text;
return [fields];
}
};
// Return the named cache object for a tiddler. If the cache doesn't exist then the initializer function is invoked to create it
WikiStore.prototype.getCacheForTiddler = function(title,cacheName,initializer) {
var caches = this.caches[title];
if(caches && caches[cacheName]) {
return caches[cacheName];
} else {
if(!caches) {
caches = {};
this.caches[title] = caches;
}
caches[cacheName] = initializer();
return caches[cacheName];
}
};
// Clear all caches associated with a particular tiddler
WikiStore.prototype.clearCache = function(title) {
if(this.caches.hasOwnProperty(title)) {
delete this.caches[title];
}
};
/*
Parse a block of text of a specified MIME type
Options are:
defaultType: Default MIME type to use if the specified one is unknown
*/
WikiStore.prototype.parseText = function(type,text,options) {
options = options || {};
var parser = this.parsers[type];
if(!parser) {
parser = this.parsers[options.defaultType || "text/x-tiddlywiki"];
}
if(parser) {
return parser.parse(type,text);
} else {
return null;
}
};
WikiStore.prototype.parseTiddler = function(title) {
var me = this,
tiddler = this.getTiddler(title);
return tiddler ? this.getCacheForTiddler(title,"parseTree",function() {
return me.parseText(tiddler.type,tiddler.text);
}) : null;
};
/*
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
*/
WikiStore.prototype.renderTiddler = function(targetType,tiddlerTitle,templateTitle) {
// Construct the tiddler macro
var macro = Renderer.MacroNode(
"tiddler",
{target: tiddlerTitle, template: templateTitle},
null,
this);
// Execute the macro
macro.execute([],tiddlerTitle);
// Render it
return macro.render(targetType);
};
WikiStore.prototype.installMacro = function(macro) {
this.macros[macro.name] = macro;
};
WikiStore.prototype.renderMacro = function(macroName,params,children,tiddlerTitle) {
var macro = Renderer.MacroNode(macroName,params,children,this);
macro.execute([],tiddlerTitle);
return macro;
};
exports.WikiStore = WikiStore;
})();

View File

@ -1,187 +0,0 @@
/*\
title: js/WikiTextParser.js
Parses a block of tiddlywiki-format wiki text into a parse tree object. This is a transliterated version of the old TiddlyWiki code. The plan is to replace it with a new, mostly backwards compatible parser built in PEGJS.
A wikitext parse tree is an array of objects with a `type` field that can be `text`,`macro` or the name of an HTML element.
Text nodes are represented as `{type: "text", value: "A string of text"}`.
Macro nodes look like this:
`
{type: "macro", name: "view", params: {
one: {type: "eval", value: "2+2"},
two: {type: "string", value: "twenty two"}
}}
`
HTML nodes look like this:
`
{type: "div", attributes: {
src: "one"
styles: {
"background-color": "#fff",
"color": "#000"
}
}}
`
\*/
(function(){
/*jslint node: true */
"use strict";
var WikiTextRules = require("./WikiTextRules.js"),
Dependencies = require("./Dependencies.js").Dependencies,
Renderer = require("./Renderer.js").Renderer,
utils = require("./Utils.js"),
util = require("util");
/*
Creates a new instance of the wiki text parser with the specified options. The
options are a hashmap of mandatory members as follows:
store: The store object to use to parse any cascaded content (eg transclusion)
Planned:
enableRules: An array of names of wiki text rules to enable. If not specified, all rules are available
extraRules: An array of additional rule handlers to add
enableMacros: An array of names of macros to enable. If not specified, all macros are available
extraMacros: An array of additional macro handlers to add
*/
var WikiTextParser = function(options) {
this.store = options.store;
this.autoLinkWikiWords = true;
this.rules = WikiTextRules.rules;
var pattern = [];
for(var n=0; n<this.rules.length; n++) {
pattern.push("(" + this.rules[n].match + ")");
}
this.rulesRegExp = new RegExp(pattern.join("|"),"mg");
};
WikiTextParser.prototype.parse = function(type,text) {
this.source = text;
this.nextMatch = 0;
this.children = [];
this.dependencies = new Dependencies();
this.output = null;
this.subWikify(this.children);
var tree = new Renderer(this.children,this.dependencies,this.store);
this.source = null;
this.children = null;
return tree;
};
WikiTextParser.prototype.outputText = function(place,startPos,endPos) {
if(startPos < endPos) {
place.push(Renderer.TextNode(this.source.substring(startPos,endPos)));
}
};
WikiTextParser.prototype.subWikify = function(output,terminator) {
// Handle the terminated and unterminated cases separately, this speeds up wikifikation by about 30%
if(terminator)
this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
else
this.subWikifyUnterm(output);
};
WikiTextParser.prototype.subWikifyUnterm = function(output) {
// subWikify can be indirectly recursive, so we need to save the old output pointer
var oldOutput = this.output;
this.output = output;
// Get the first match
this.rulesRegExp.lastIndex = this.nextMatch;
var ruleMatch = this.rulesRegExp.exec(this.source);
while(ruleMatch) {
// Output any text before the match
if(ruleMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,ruleMatch.index);
// Set the match parameters for the handler
this.matchStart = ruleMatch.index;
this.matchLength = ruleMatch[0].length;
this.matchText = ruleMatch[0];
this.nextMatch = this.rulesRegExp.lastIndex;
// Figure out which rule matched and call its handler
var t;
for(t=1; t<ruleMatch.length; t++) {
if(ruleMatch[t]) {
this.rules[t-1].handler(this);
this.rulesRegExp.lastIndex = this.nextMatch;
break;
}
}
// Get the next match
ruleMatch = this.rulesRegExp.exec(this.source);
}
// Output any text after the last match
if(this.nextMatch < this.source.length) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
// Restore the output pointer
this.output = oldOutput;
};
WikiTextParser.prototype.subWikifyTerm = function(output,terminatorRegExp) {
// subWikify can be indirectly recursive, so we need to save the old output pointer
var oldOutput = this.output;
this.output = output;
// Get the first matches for the rule and terminator RegExps
terminatorRegExp.lastIndex = this.nextMatch;
var terminatorMatch = terminatorRegExp.exec(this.source);
this.rulesRegExp.lastIndex = this.nextMatch;
var ruleMatch = this.rulesRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
while(terminatorMatch || ruleMatch) {
// Check for a terminator match before the next rule match
if(terminatorMatch && (!ruleMatch || terminatorMatch.index <= ruleMatch.index)) {
// Output any text before the match
if(terminatorMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,terminatorMatch.index);
// Set the match parameters
this.matchText = terminatorMatch[1];
this.matchLength = terminatorMatch[1].length;
this.matchStart = terminatorMatch.index;
this.nextMatch = this.matchStart + this.matchLength;
// Restore the output pointer
this.output = oldOutput;
return;
}
// It must be a rule match; output any text before the match
if(ruleMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,ruleMatch.index);
// Set the match parameters
this.matchStart = ruleMatch.index;
this.matchLength = ruleMatch[0].length;
this.matchText = ruleMatch[0];
this.nextMatch = this.rulesRegExp.lastIndex;
// Figure out which rule matched and call its handler
var t;
for(t=1; t<ruleMatch.length; t++) {
if(ruleMatch[t]) {
this.rules[t-1].handler(this);
this.rulesRegExp.lastIndex = this.nextMatch;
break;
}
}
// Get the next match
terminatorRegExp.lastIndex = this.nextMatch;
terminatorMatch = terminatorRegExp.exec(this.source);
ruleMatch = this.rulesRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
}
// Output any text after the last match
if(this.nextMatch < this.source.length) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
// Restore the output pointer
this.output = oldOutput;
};
exports.WikiTextParser = WikiTextParser;
})();

View File

@ -1,711 +0,0 @@
/*\
title: js/WikiTextRules.js
\*/
(function(){
/*jslint node: true */
"use strict";
var ArgParser = require("./ArgParser.js").ArgParser,
Renderer = require("./Renderer.js").Renderer,
Dependencies = require("./Dependencies.js").Dependencies,
util = require("util");
var textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
sliceSeparator: "::",
sectionSeparator: "##",
urlPattern: "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)",
unWikiLink: "~",
brackettedLink: "\\[\\[([^\\]]+)\\]\\]",
titledBrackettedLink: "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]"
};
textPrimitives.wikiLink = "(?:(?:" + textPrimitives.upperLetter + "+" +
textPrimitives.lowerLetter + "+" +
textPrimitives.upperLetter +
textPrimitives.anyLetter + "*)|(?:" +
textPrimitives.upperLetter + "{2,}" +
textPrimitives.lowerLetter + "+))";
textPrimitives.cssLookahead = "(?:(" + textPrimitives.anyLetter +
"+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
textPrimitives.cssLookaheadRegExp = new RegExp(textPrimitives.cssLookahead,"mg");
textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + textPrimitives.titledBrackettedLink + ")|(?:" +
textPrimitives.brackettedLink + ")|(?:" +
textPrimitives.urlPattern + ")","mg");
textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ textPrimitives.wikiLink + ")|(?:" +
textPrimitives.titledBrackettedLink + ")|(?:" +
textPrimitives.brackettedLink + ")|(?:" +
textPrimitives.urlPattern + ")","mg");
// Helper to add an attribute to an HTML node
var setAttr = function(node,attr,value) {
if(!node.attributes) {
node.attributes = {};
}
node.attributes[attr] = value;
};
var inlineCssHelper = function(w) {
var styles = [];
textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = textPrimitives.cssLookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
var s,v;
if(lookaheadMatch[1]) {
s = lookaheadMatch[1];
v = lookaheadMatch[2];
} else {
s = lookaheadMatch[3];
v = lookaheadMatch[4];
}
if(s=="bgcolor")
s = "backgroundColor";
if(s=="float")
s = "cssFloat";
styles.push({style: s, value: v});
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = textPrimitives.cssLookaheadRegExp.exec(w.source);
}
return styles;
};
var applyCssHelper = function(e,styles) {
if(styles.length > 0) {
if(!e.attributes) {
e.attributes = {};
}
if(!e.attributes.style) {
e.attributes.style = {};
}
for(var t=0; t< styles.length; t++) {
e.attributes.style[styles[t].style] = styles[t].value;
}
}
};
var enclosedTextHelper = function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
w.output.push(Renderer.ElementNode(this.element,null,[Renderer.TextNode(text)]));
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
};
var insertMacroCall = function(w,output,name,params,children) {
if(name in w.store.macros) {
var macroNode = Renderer.MacroNode(name,params,children,w.store);
w.dependencies.mergeDependencies(macroNode.dependencies);
output.push(macroNode);
}
};
var rules = [
{
name: "table",
match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
cellTermRegExp: /((?:\x20*)\|)/mg,
rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
handler: function(w)
{
var table = Renderer.ElementNode("table",{"class": "table"},[]);
w.output.push(table);
var prevColumns = [];
var currRowType = null;
var rowContainer;
var rowCount = 0;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
var nextRowType = lookaheadMatch[2];
if(nextRowType == "k") {
table.attributes["class"] = lookaheadMatch[1];
w.nextMatch += lookaheadMatch[0].length+1;
} else {
if(nextRowType != currRowType) {
rowContainer = Renderer.ElementNode(this.rowTypes[nextRowType],{},[]);
table.children.push(rowContainer);
currRowType = nextRowType;
}
if(currRowType == "c") {
// Caption
w.nextMatch++;
// Move the caption to the first row if it isn't already
if(table.children.length !== 1) {
table.children.pop(); // Take rowContainer out of the children array
table.children.splice(0,0,rowContainer); // Insert it at the bottom
}
rowContainer.attributes.align = rowCount === 0 ? "top" : "bottom";
w.subWikifyTerm(rowContainer.children,this.rowTermRegExp);
} else {
var theRow = Renderer.ElementNode("tr",{},[]);
theRow.attributes["class"] = rowCount%2 ? "oddRow" : "evenRow";
rowContainer.children.push(theRow);
this.rowHandler(w,theRow.children,prevColumns);
rowCount++;
}
}
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
},
rowHandler: function(w,e,prevColumns)
{
var col = 0;
var colSpanCount = 1;
var prevCell = null;
this.cellRegExp.lastIndex = w.nextMatch;
var cellMatch = this.cellRegExp.exec(w.source);
while(cellMatch && cellMatch.index == w.nextMatch) {
if(cellMatch[1] == "~") {
// Rowspan
var last = prevColumns[col];
if(last) {
last.rowSpanCount++;
last.element.attributes.rowspan = last.rowSpanCount;
last.element.attributes.valign = "center";
if(colSpanCount > 1) {
last.element.attributes.colspan = colSpanCount;
colSpanCount = 1;
}
}
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[1] == ">") {
// Colspan
colSpanCount++;
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[2]) {
// End of row
if(prevCell && colSpanCount > 1) {
prevCell.attributes.colspan = colSpanCount;
}
w.nextMatch = this.cellRegExp.lastIndex;
break;
} else {
// Cell
w.nextMatch++;
var styles = inlineCssHelper(w);
var spaceLeft = false;
var chr = w.source.substr(w.nextMatch,1);
while(chr == " ") {
spaceLeft = true;
w.nextMatch++;
chr = w.source.substr(w.nextMatch,1);
}
var cell;
if(chr == "!") {
cell = Renderer.ElementNode("th",{},[]);
e.push(cell);
w.nextMatch++;
} else {
cell = Renderer.ElementNode("td",{},[]);
e.push(cell);
}
prevCell = cell;
prevColumns[col] = {rowSpanCount:1,element:cell};
if(colSpanCount > 1) {
cell.attributes.colspan = colSpanCount;
colSpanCount = 1;
}
applyCssHelper(cell,styles);
w.subWikifyTerm(cell.children,this.cellTermRegExp);
if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
cell.attributes.align = spaceLeft ? "center" : "left";
else if(spaceLeft)
cell.attributes.align = "right";
w.nextMatch--;
}
col++;
this.cellRegExp.lastIndex = w.nextMatch;
cellMatch = this.cellRegExp.exec(w.source);
}
}
},
{
name: "heading",
match: "^!{1,6}",
termRegExp: /(\n)/mg,
handler: function(w)
{
var e = Renderer.ElementNode("h" + w.matchLength,{},[]);
w.output.push(e);
w.subWikifyTerm(e.children,this.termRegExp);
}
},
{
name: "list",
match: "^(?:[\\*#;:]+)",
lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var stack = [w.output];
var currLevel = 0, currType = null;
var listLevel, listType, itemType, baseType;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
if(lookaheadMatch[1]) {
listType = "ul";
itemType = "li";
} else if(lookaheadMatch[2]) {
listType = "ol";
itemType = "li";
} else if(lookaheadMatch[3]) {
listType = "dl";
itemType = "dt";
} else if(lookaheadMatch[4]) {
listType = "dl";
itemType = "dd";
}
if(!baseType)
baseType = listType;
listLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
var t,e;
if(listLevel > currLevel) {
for(t=currLevel; t<listLevel; t++) {
var target = stack[stack.length-1];
if(currLevel !== 0 && target.children) {
target = target.children[target.children.length-1];
}
e = Renderer.ElementNode(listType,{},[]);
target.push(e);
stack.push(e.children);
}
} else if(listType!=baseType && listLevel==1) {
w.nextMatch -= lookaheadMatch[0].length;
return;
} else if(listLevel < currLevel) {
for(t=currLevel; t>listLevel; t--)
stack.pop();
} else if(listLevel == currLevel && listType != currType) {
stack.pop();
e = Renderer.ElementNode(listType,{},[]);
stack[stack.length-1].push(e);
stack.push(e.children);
}
currLevel = listLevel;
currType = listType;
e = Renderer.ElementNode(itemType,{},[]);
stack[stack.length-1].push(e);
w.subWikifyTerm(e.children,this.termRegExp);
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
}
},
{
name: "quoteByBlock",
match: "^<<<\\n",
termRegExp: /(^<<<(\n|$))/mg,
element: "blockquote",
handler: function(w) {
var e = Renderer.ElementNode(this.element,{},[]);
w.output.push(e);
w.subWikifyTerm(e.children,this.termRegExp);
}
},
{
name: "quoteByLine",
match: "^>+",
lookaheadRegExp: /^>+/mg,
termRegExp: /(\n)/mg,
element: "blockquote",
handler: function(w)
{
var stack = [w.output];
var currLevel = 0;
var newLevel = w.matchLength;
var t,matched,e;
do {
if(newLevel > currLevel) {
for(t=currLevel; t<newLevel; t++) {
e = Renderer.ElementNode(this.element,{},[]);
stack[stack.length-1].push(e);
}
} else if(newLevel < currLevel) {
for(t=currLevel; t>newLevel; t--)
stack.pop();
}
currLevel = newLevel;
w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
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;
if(matched) {
newLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
}
} while(matched);
}
},
{
name: "rule",
match: "^----+$\\n?|<hr ?/?>\\n?",
handler: function(w)
{
w.output.push(Renderer.ElementNode("hr"));
}
},
{
name: "monospacedByLine",
match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|<!--\\{\\{\\{-->)\\n",
element: "pre",
handler: function(w)
{
switch(w.matchText) {
case "/*{{{*/\n": // CSS
this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\*\}\}\}\*\/$\n?)/mg;
break;
case "{{{\n": // monospaced block
this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\f*\}\}\}$\n?)/mg;
break;
case "//{{{\n": // plugin
this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\/\}\}\}$\n?)/mg;
break;
case "<!--{{{-->\n": //template
this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^\f*<!--\}\}\}-->$\n?)/mg;
break;
default:
break;
}
enclosedTextHelper.call(this,w);
}
},
{
name: "typedBlock",
match: "^\\$\\$\\$(?:.*)\\n",
lookaheadRegExp: /^\$\$\$(.*)\n((?:^[^\n]*\n)+?)(^\f*\$\$\$$\n?)/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
// The wikitext parsing infrastructure is horribly unre-entrant
var mimeType = lookaheadMatch[1],
content = lookaheadMatch[2],
oldOutput = w.output,
oldSource = w.source,
oldNextMatch = w.nextMatch,
oldChildren = w.children,
oldDependencies = w.dependencies,
parseTree = w.store.parseText(mimeType,content,{defaultType: "text/plain"});
w.output = oldOutput;
w.source = oldSource;
w.nextMatch = oldNextMatch;
w.children = oldChildren;
w.dependencies = oldDependencies;
w.output.push.apply(w.output,parseTree.nodes);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "wikifyComment",
match: "^(?:/\\*\\*\\*|<!---)\\n",
handler: function(w)
{
var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
w.subWikifyTerm(w.output,termRegExp);
}
},
{
name: "macro",
match: "<<",
lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
var name = lookaheadMatch[1];
insertMacroCall(w,w.output,name,lookaheadMatch[2]);
}
}
},
{
name: "prettyLink",
match: "\\[\\[",
lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1],
link = text;
if(lookaheadMatch[3]) {
// Pretty bracketted link
link = lookaheadMatch[3];
}
insertMacroCall(w,w.output,"link",{to: link},[Renderer.TextNode(text)]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "wikiLink",
match: textPrimitives.unWikiLink+"?"+textPrimitives.wikiLink,
handler: function(w)
{
if(w.matchText.substr(0,1) == textPrimitives.unWikiLink) {
w.outputText(w.output,w.matchStart+1,w.nextMatch);
return;
}
if(w.matchStart > 0) {
var preRegExp = new RegExp(textPrimitives.anyLetterStrict,"mg");
preRegExp.lastIndex = w.matchStart-1;
var preMatch = preRegExp.exec(w.source);
if(preMatch.index == w.matchStart-1) {
w.outputText(w.output,w.matchStart,w.nextMatch);
return;
}
}
if(w.autoLinkWikiWords) {
insertMacroCall(w,w.output,"link",{to: w.matchText},[Renderer.TextNode(w.source.substring(w.matchStart,w.nextMatch))]);
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
},
{
name: "urlLink",
match: textPrimitives.urlPattern,
handler: function(w)
{
insertMacroCall(w,w.output,"link",{to: w.matchText},[Renderer.TextNode(w.source.substring(w.matchStart,w.nextMatch))]);
}
},
{
name: "image",
match: "\\[[<>]?[Ii][Mm][Gg]\\[",
// [<] sequence below is to avoid lessThan-questionMark sequence so TiddlyWikis can be included in PHP files
lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source),
imageParams = {},
linkParams = {};
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
if(lookaheadMatch[1]) {
imageParams.alignment = "left";
} else if(lookaheadMatch[2]) {
imageParams.alignment = "right";
}
if(lookaheadMatch[3]) {
imageParams.text = lookaheadMatch[3];
}
imageParams.src = lookaheadMatch[4];
if(lookaheadMatch[5]) {
linkParams.target = lookaheadMatch[5];
var linkChildren = [];
insertMacroCall(w,w.output,"link",linkParams,linkChildren);
insertMacroCall(w,linkChildren,"image",imageParams);
} else {
insertMacroCall(w,w.output,"image",imageParams);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "html",
match: "<[Hh][Tt][Mm][Ll]>",
lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(Renderer.ElementNode("html",{},[Renderer.RawNode(lookaheadMatch[1])]));
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "commentByBlock",
match: "/%",
lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
},
{
name: "characterFormat",
match: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{|`",
handler: function(w)
{
var e,lookaheadRegExp,lookaheadMatch;
switch(w.matchText) {
case "''":
e = Renderer.ElementNode("strong",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/('')/mg);
break;
case "//":
e = Renderer.ElementNode("em",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(\/\/)/mg);
break;
case "__":
e = Renderer.ElementNode("u",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(__)/mg);
break;
case "^^":
e = Renderer.ElementNode("sup",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(\^\^)/mg);
break;
case "~~":
e = Renderer.ElementNode("sub",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(~~)/mg);
break;
case "--":
e = Renderer.ElementNode("strike",null,[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(--)/mg);
break;
case "`":
lookaheadRegExp = /`((?:.|\n)*?)`/mg;
lookaheadRegExp.lastIndex = w.matchStart;
lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(Renderer.ElementNode("code",null,[Renderer.TextNode(lookaheadMatch[1])]));
w.nextMatch = lookaheadRegExp.lastIndex;
}
break;
case "{{{":
lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
lookaheadRegExp.lastIndex = w.matchStart;
lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(Renderer.ElementNode("code",null,[Renderer.TextNode(lookaheadMatch[1])]));
w.nextMatch = lookaheadRegExp.lastIndex;
}
break;
}
}
},
{
name: "customFormat",
match: "@@|\\{\\{",
handler: function(w)
{
switch(w.matchText) {
case "@@":
var e = Renderer.ElementNode("span",null,[]);
w.output.push(e);
var styles = inlineCssHelper(w);
if(styles.length === 0)
setAttr(e,"class","marked");
else
applyCssHelper(e,styles);
w.subWikifyTerm(e.children,/(@@)/mg);
break;
case "{{":
var lookaheadRegExp = /\{\{[\s]*([\-\w]+[\-\s\w]*)[\s]*\{(\n?)/mg;
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch) {
w.nextMatch = lookaheadRegExp.lastIndex;
e = Renderer.ElementNode(lookaheadMatch[2] == "\n" ? "div" : "span",{
"class": lookaheadMatch[1]
},[]);
w.output.push(e);
w.subWikifyTerm(e.children,/(\}\}\})/mg);
}
break;
}
}
},
{
name: "mdash",
match: "--",
handler: function(w)
{
w.output.push(Renderer.EntityNode("&mdash;"));
}
},
{
name: "lineBreak",
match: "\\n|<br ?/?>",
handler: function(w)
{
w.output.push(Renderer.ElementNode("br"));
}
},
{
name: "rawText",
match: "\"{3}|<nowiki>",
lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.output.push(Renderer.TextNode(lookaheadMatch[1]));
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "htmlEntitiesEncoding",
match: "&#?[a-zA-Z0-9]{2,8};",
handler: function(w)
{
w.output.push(Renderer.EntityNode(w.matchText));
}
}
];
exports.rules = rules;
})();

View File

@ -1,158 +0,0 @@
/*\
title: js/macros/chooser.js
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
Tiddler = require("../Tiddler.js").Tiddler,
utils = require("../Utils.js");
function showChooser(macroNode) {
if(!macroNode.chooserDisplayed) {
macroNode.chooserDisplayed = true;
var nodes = [];
macroNode.store.forEachTiddler("title",function(title,tiddler) {
nodes.push(Renderer.ElementNode("li",{
"data-link": title
},[
Renderer.TextNode(title)
]));
});
var wrapper = Renderer.ElementNode("ul",{},nodes);
wrapper.execute(macroNode.parents,macroNode.tiddlerTitle);
macroNode.content = [wrapper];
macroNode.content[0].renderInDom(macroNode.domNode);
}
}
/*
Select the appropriate chooser item given a touch/mouse position in screen coordinates
*/
function select(macroNode,y) {
if(macroNode.content.length > 0) {
var targetIndex = Math.floor(macroNode.content[0].domNode.childNodes.length * (y/window.innerHeight)),
target = macroNode.content[0].domNode.childNodes[targetIndex];
if(target) {
deselect(macroNode);
macroNode.selectedNode = target;
utils.addClass(target,"selected");
}
}
}
function deselect(macroNode) {
if(macroNode.selectedNode) {
utils.removeClass(macroNode.selectedNode,"selected");
macroNode.selectedNode = null;
}
}
function action(macroNode) {
if(macroNode.selectedNode) {
var navEvent = document.createEvent("Event");
navEvent.initEvent("tw-navigate",true,true);
navEvent.navigateTo = macroNode.selectedNode.getAttribute("data-link");
macroNode.domNode.dispatchEvent(navEvent);
}
}
/*
Set the position of the chooser panel within its wrapper given a touch/mouse position in screen coordinates
*/
function hoverChooser(macroNode,x,y) {
if(macroNode.chooserDisplayed) {
// Get the target element that the touch/mouse is over
select(macroNode,y);
// Things we need for sizing and positioning the chooser
var domPanel = macroNode.content[0].domNode,
heightPanel = domPanel.offsetHeight,
widthPanel = domPanel.offsetWidth;
// Position the chooser div to account for scrolling
macroNode.content[0].domNode.style.top = window.pageYOffset + "px";
// Scale the panel to fit
var scaleFactor = window.innerHeight/heightPanel;
// Scale up as we move right
var expandFactor = x > 50 ? ((x+150)/200) : 1;
// Set up the transform
var scale = scaleFactor * expandFactor,
translateX = x > 16 ? 0 : -(((16-x)/16) * widthPanel) / scale,
translateY = (y / scale) - ((y/window.innerHeight) * heightPanel);
domPanel.style.webkitTransformOrigin =
domPanel.style.MozTransformOrigin = "0 0";
domPanel.style.webkitTransform =
domPanel.style.MozTransform = "scale(" + scale + ") translateX(" + translateX + "px) translateY(" + translateY + "px)";
}
}
function hideChooser(macroNode) {
if(macroNode.chooserDisplayed) {
deselect(macroNode);
macroNode.chooserDisplayed = false;
macroNode.domNode.removeChild(macroNode.content[0].domNode);
macroNode.content = [];
}
}
exports.macro = {
name: "chooser",
wrapperTag: "div",
params: {
},
events: {
touchstart: function(event) {
showChooser(this);
hoverChooser(this,event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
},
touchmove: function(event) {
hoverChooser(this,event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
},
touchend: function(event) {
action(this);
hideChooser(this);
event.preventDefault();
return false;
},
mouseover: function(event) {
if(event.target === this.domNode) {
showChooser(this);
hoverChooser(this,event.clientX,event.clientY);
event.preventDefault();
return false;
}
},
mousemove: function(event) {
hoverChooser(this,event.clientX,event.clientY);
event.preventDefault();
return false;
},
mouseup: function(event) {
action(this);
hideChooser(this);
event.preventDefault();
return false;
},
mouseout: function(event) {
if(!utils.domContains(this.domNode,event.relatedTarget)) {
hideChooser(this);
event.preventDefault();
return false;
}
}
},
execute: function() {
this.chooserDisplayed = false;
return [];
}
};
})();

View File

@ -1,39 +0,0 @@
/*\
title: js/macros/command.js
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "command",
params: {
name: {byName: "default", type: "text"},
label: {byName: true, type: "text"},
"class": {byName: true, type: "text"}
},
events: {
"click": function(event) {
var commandEvent = document.createEvent("Event");
commandEvent.initEvent("tw-" + this.params.name,true,true);
commandEvent.tiddlerTitle = this.tiddlerTitle;
commandEvent.commandOrigin = this;
event.target.dispatchEvent(commandEvent);
event.preventDefault();
return false;
}
},
execute: function() {
var attributes = {};
if(this.hasParameter("class")) {
attributes["class"] = this.params["class"].split(" ");
}
return [Renderer.ElementNode("button",attributes,[Renderer.TextNode(this.params.label)])];
}
};
})();

View File

@ -1,22 +0,0 @@
/*\
title: js/macros/echo.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "echo",
params: {
text: {byPos: 0, type: "text"}
},
execute: function() {
return [Renderer.TextNode(this.params.text)];
}
};
})();

View File

@ -1,318 +0,0 @@
/*\
title: js/macros/edit.js
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var Tiddler = require("../Tiddler.js").Tiddler,
Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
utils = require("../Utils.js");
function BitmapEditor(macroNode) {
this.macroNode = macroNode;
}
BitmapEditor.prototype.getContent = function() {
return [Renderer.ElementNode("canvas",{
"class": ["tw-edit-field"]
},[])];
};
BitmapEditor.prototype.renderInDom = function() {
var tiddler = this.macroNode.store.getTiddler(this.macroNode.tiddlerTitle),
canvas = this.macroNode.content[0].domNode,
currImage = new Image();
// Set the macro node itself to be position: relative
this.macroNode.domNode.style.position = "relative";
// Get the current bitmap into an image object
currImage.src = "data:" + tiddler.type + ";base64," + tiddler.text;
// Copy it to the on-screen canvas
canvas.width = currImage.width;
canvas.height = currImage.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(currImage,0,0);
// And also copy the current bitmap to the off-screen canvas
this.currCanvas = document.createElement("canvas");
this.currCanvas.width = currImage.width;
this.currCanvas.height = currImage.height;
ctx = this.currCanvas.getContext("2d");
ctx.drawImage(currImage,0,0);
};
BitmapEditor.prototype.addEventHandlers = function() {
var self = this;
this.macroNode.domNode.addEventListener("touchstart",function(event) {
self.brushDown = true;
self.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
event.stopPropagation();
return false;
},false);
this.macroNode.domNode.addEventListener("touchmove",function(event) {
if(self.brushDown) {
self.strokeMove(event.touches[0].clientX,event.touches[0].clientY);
}
event.preventDefault();
event.stopPropagation();
return false;
},false);
this.macroNode.domNode.addEventListener("touchend",function(event) {
if(self.brushDown) {
self.brushDown = false;
self.strokeEnd();
}
event.preventDefault();
event.stopPropagation();
return false;
},false);
this.macroNode.domNode.addEventListener("mousedown",function(event) {
self.strokeStart(event.clientX,event.clientY);
self.brushDown = true;
event.preventDefault();
event.stopPropagation();
return false;
},false);
this.macroNode.domNode.addEventListener("mousemove",function(event) {
if(self.brushDown) {
self.strokeMove(event.clientX,event.clientY);
event.preventDefault();
event.stopPropagation();
return false;
}
},false);
this.macroNode.domNode.addEventListener("mouseup",function(event) {
if(self.brushDown) {
self.brushDown = false;
self.strokeEnd();
event.preventDefault();
event.stopPropagation();
return false;
}
return true;
},false);
};
BitmapEditor.prototype.adjustCoordinates = function(x,y) {
var canvas = this.macroNode.content[0].domNode,
canvasRect = canvas.getBoundingClientRect(),
scale = canvas.width/canvasRect.width;
return {x: (x - canvasRect.left) * scale, y: (y - canvasRect.top) * scale};
};
BitmapEditor.prototype.strokeStart = function(x,y) {
// Start off a new stroke
this.stroke = [this.adjustCoordinates(x,y)];
};
BitmapEditor.prototype.strokeMove = function(x,y) {
var canvas = this.macroNode.content[0].domNode,
ctx = canvas.getContext("2d"),
t;
// Add the new position to the end of the stroke
this.stroke.push(this.adjustCoordinates(x,y));
// Redraw the previous image
ctx.drawImage(this.currCanvas,0,0);
// Render the stroke
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(this.stroke[0].x,this.stroke[0].y);
for(t=1; t<this.stroke.length-1; t++) {
var s1 = this.stroke[t],
s2 = this.stroke[t-1],
tx = (s1.x + s2.x)/2,
ty = (s1.y + s2.y)/2;
ctx.quadraticCurveTo(s2.x,s2.y,tx,ty);
}
ctx.stroke();
};
BitmapEditor.prototype.strokeEnd = function() {
// Copy the bitmap to the off-screen canvas
var canvas = this.macroNode.content[0].domNode,
ctx = this.currCanvas.getContext("2d");
ctx.drawImage(canvas,0,0);
// Save the image into the tiddler
this.saveChanges();
};
BitmapEditor.prototype.saveChanges = function() {
var tiddler = this.macroNode.store.getTiddler(this.macroNode.tiddlerTitle);
if(tiddler) {
// data URIs look like "data:<type>;base64,<text>"
var dataURL = this.macroNode.content[0].domNode.toDataURL(tiddler.type,0.95),
posColon = dataURL.indexOf(":"),
posSemiColon = dataURL.indexOf(";"),
posComma = dataURL.indexOf(","),
type = dataURL.substring(posColon+1,posSemiColon),
text = dataURL.substring(posComma+1);
var update = {type: type, text: text};
this.macroNode.store.addTiddler(new Tiddler(tiddler,update));
}
};
BitmapEditor.prototype.isRefreshable = function() {
// Don't ever refresh the bitmap editor
return false;
};
function TextEditor(macroNode) {
this.macroNode = macroNode;
}
TextEditor.prototype.getContent = function() {
var tiddler = this.macroNode.store.getTiddler(this.macroNode.tiddlerTitle),
field = this.macroNode.hasParameter("field") ? this.macroNode.params.field : "title",
value;
if(tiddler) {
value = tiddler.getFieldString(field);
} else {
switch(field) {
case "text":
value = "Type the text for the tiddler '" + this.macroNode.tiddlerTitle + "'";
break;
case "title":
value = this.macroNode.tiddlerTitle;
break;
default:
value = "";
break;
}
}
var attributes = {
"class": ["tw-edit-field"]
},
tagName,
content = [];
if(field === "text") {
tagName = "textarea";
content.push(Renderer.TextNode(value));
} else {
tagName = "input";
attributes.type = "text";
attributes.value = value;
}
return [Renderer.ElementNode(tagName,attributes,content)];
};
TextEditor.prototype.addEventHandlers = function() {
this.macroNode.domNode.addEventListener("focus",this,false);
this.macroNode.domNode.addEventListener("keyup",this,false);
};
TextEditor.prototype.handleEvent = function(event) {
// Get the value of the field if it might have changed
if("keyup".split(" ").indexOf(event.type) !== -1) {
this.saveChanges();
}
// Whatever the event, fix the height of the textarea if required
var self = this;
window.setTimeout(function() {
self.fixHeight();
},5);
return true;
};
TextEditor.prototype.saveChanges = function() {
var text = this.macroNode.content[0].domNode.value,
tiddler = this.macroNode.store.getTiddler(this.macroNode.tiddlerTitle);
if(tiddler && text !== tiddler[this.macroNode.params.field]) {
var update = {};
update[this.macroNode.params.field] = text;
this.macroNode.store.addTiddler(new Tiddler(tiddler,update));
}
};
TextEditor.prototype.fixHeight = function() {
if(this.macroNode.content[0] && this.macroNode.content[0].domNode) {
var wrapper = this.macroNode.domNode,
textarea = this.macroNode.content[0].domNode;
// Set the text area height to 1px temporarily, which allows us to read the true scrollHeight
var prevWrapperHeight = wrapper.style.height;
wrapper.style.height = textarea.style.height + "px";
textarea.style.overflow = "hidden";
textarea.style.height = "1px";
textarea.style.height = textarea.scrollHeight + "px";
wrapper.style.height = prevWrapperHeight;
}
};
TextEditor.prototype.renderInDom = function() {
this.fixHeight();
};
TextEditor.prototype.isRefreshable = function() {
// Don't refresh the editor if it contains the caret or selection
return !window.getSelection().containsNode(this.macroNode.domNode, true);
};
exports.macro = {
name: "edit",
dependentOnContextTiddler: true,
params: {
field: {byPos: 0, type: "text"}
},
execute: function() {
// Get the tiddler being editted
var tiddler = this.store.getTiddler(this.tiddlerTitle);
// Figure out which editor to use
var Editor = TextEditor;
if(this.params.field === "text") {
if(["image/jpg","image/jpeg","image/png","image/gif"].indexOf(tiddler.type) !== -1) {
Editor = BitmapEditor;
}
}
this.editor = new Editor(this);
var content = this.editor.getContent();
for(var t=0; t<content.length; t++) {
content[t].execute(this.parents,this.tiddlerTitle);
}
return content;
},
addEventHandlers: function() {
if(this.editor.addEventHandlers) {
this.editor.addEventHandlers();
}
},
renderInDom: function() {
if(this.editor.renderInDom) {
this.editor.renderInDom();
}
},
refreshInDom: function(changes) {
var t;
// Only refresh if a dependency is triggered
if(this.dependencies.hasChanged(changes,this.tiddlerTitle)) {
// Only refresh if the editor lets us
if(this.editor.isRefreshable()) {
// Remove the event handlers so they don't get triggered by the following DOM manipulations
for(var e in exports.macro.events) {
this.domNode.removeEventListener(e,this,false);
}
// Remove the previous content
while(this.domNode.hasChildNodes()) {
this.domNode.removeChild(this.domNode.firstChild);
}
// Execute the new content
this.execute(this.parents,this.tiddlerTitle);
// Render to the DOM
for(t=0; t<this.content.length; t++) {
this.content[t].renderInDom(this.domNode);
}
}
} else {
// Refresh any children
for(t=0; t<this.content.length; t++) {
this.content[t].refreshInDom(changes);
}
}
}
};
})();

View File

@ -1,44 +0,0 @@
/*\
title: js/macros/image.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "image",
params: {
src: {byName: "default", type: "tiddler"},
text: {byName: true, type: "text"},
alignment: {byName: true, type: "text"}
},
execute: function() {
if(this.store.tiddlerExists(this.params.src)) {
var imageTree = this.store.parseTiddler(this.params.src).nodes,
cloneImage = [];
for(var t=0; t<imageTree.length; t++) {
cloneImage.push(imageTree[t].clone());
}
if(this.params.text) {
return [Renderer.ElementNode("div",{
alt: this.params.text,
title: this.params.text
},cloneImage)];
} else {
return cloneImage;
}
} else {
return [Renderer.ElementNode("img",{
src: this.params.src,
alt: this.params.text,
title: this.params.text
})];
}
}
};
})();

View File

@ -1,112 +0,0 @@
/*\
title: js/macros/link.js
Implements the link macro.
A special callback function is used to massage links according to the needs of the host platform.
The linkMassager is stored in the `linkMassager` property of the store object. It is a function
that takes a `linkInfo` structure as the only parameter. It contains a hashmap of information
as follows:
{
to: the target of the link
space: an optional space associated with the link
isExternal: true if the link has been determined to be an external link by the default heuristics
isMissing: true if a non-external link references a missing tiddler
attributes: a hashmap of HTML attributes to add to the `<a>` tag
suppressLink: see below
}
The link massager is called with the `attributes` hashmap initialised as follows:
{
class: an array of strings representing the CSS classes to be applied to the link. The default classes are already applieda according to whether the heuristics decide the tiddler is external or missing
href: the href to be used in the link (defaults to the unencoded value of the `to` parameter)
}
Note that the member `class` cannot be referred to with JavaScript dot syntax: use `linkInfo.attributes["class"]` rather than `linkInfo.attributes.class`.
The linkMassager can modify the `classes` and `href` fields as required, and add additional HTML attributes, such as the `target` attribute.
The linkMassager can cause the link to be suppressed by setting the `linkInfo.suppressLink` to `true`. The content of the link will still be displayed.
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
var isLinkExternal = function(to) {
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data):[^\s'"]+(?:\/|\b)/i;
return externalRegExp.test(to);
};
exports.macro = {
name: "link",
params: {
to: {byName: "default", type: "tiddler", skinny: true},
space: {byName: true, type: "text"}
},
events: {
click: function(event) {
if(isLinkExternal(this.params.to)) {
event.target.setAttribute("target","_blank");
return true;
} else {
var navEvent = document.createEvent("Event");
navEvent.initEvent("tw-navigate",true,true);
navEvent.navigateTo = this.params.to;
event.target.dispatchEvent(navEvent);
event.preventDefault();
return false;
}
}
},
execute: function() {
// Assemble the information about the link
var linkInfo = {
to: this.params.to,
space: this.params.space
};
// Generate the default link characteristics
linkInfo.isExternal = isLinkExternal(linkInfo.to);
if(!linkInfo.isExternal) {
linkInfo.isMissing = !this.store.tiddlerExists(linkInfo.to);
}
linkInfo.attributes = {
href: linkInfo.to
};
// Generate the default classes for the link
linkInfo.attributes["class"] = ["tw-tiddlylink"];
if(linkInfo.isExternal) {
linkInfo.attributes["class"].push("tw-tiddlylink-external");
} else {
linkInfo.attributes["class"].push("tw-tiddlylink-internal");
if(linkInfo.isMissing) {
linkInfo.attributes["class"].push("tw-tiddlylink-missing");
} else {
linkInfo.attributes["class"].push("tw-tiddlylink-resolves");
}
}
// Invoke the link massager if defined
if(this.store.linkMassager) {
this.store.linkMassager(linkInfo);
}
// Create the link
var content;
if(linkInfo.suppressLink) {
content = this.cloneChildren();
} else {
content = [Renderer.ElementNode("a",linkInfo.attributes,this.cloneChildren())];
}
for(var t=0; t<content.length; t++) {
content[t].execute(this.parents,this.tiddlerTitle);
}
return content;
}
};
})();

View File

@ -1,77 +0,0 @@
/*\
title: js/macros/list.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
var handlers = {
all: function(store) {
return store.getTitles("title","excludeLists");
},
missing: function(store) {
return store.getMissingTitles();
},
orphans: function(store) {
return store.getOrphanTitles();
},
shadowed: function(store) {
return store.getShadowTitles();
},
touched: function(store) {
// Server syncing isn't implemented yet
return [];
},
filter: function(store) {
// Filters aren't implemented yet
return [];
}
};
exports.macro = {
name: "list",
dependentAll: true, // Tiddlers containing <<list>> macro are dependent on every tiddler
params: {
type: {byName: "default", type: "text"},
template: {byName: true, type: "tiddler"},
emptyMessage: {byName: true, type: "text"}
},
execute: function() {
var templateType = "text/x-tiddlywiki",
templateText = "<<view title link>>",
template = this.params.template ? this.store.getTiddler(this.params.template) : null,
content = [],
t,
parents = this.parents;
if(template) {
parents = parents.slice(0);
parents.push(template.title);
templateType = template.type;
templateText = template.text;
}
var handler = handlers[this.params.type];
handler = handler || handlers.all;
var tiddlers = handler(this.store);
if(tiddlers.length === 0) {
return [Renderer.TextNode(this.params.emptyMessage || "")];
} else {
var templateTree = this.store.parseText(templateType,templateText).nodes;
for(t=0; t<tiddlers.length; t++) {
var cloneTemplate = [];
for(var c=0; c<templateTree.length; c++) {
cloneTemplate.push(templateTree[c].clone());
}
var listNode = Renderer.ElementNode("li",null,cloneTemplate);
listNode.execute(parents,tiddlers[t]);
content.push(listNode);
}
return [Renderer.ElementNode("ul",null,content)];
}
}
};
})();

View File

@ -1,175 +0,0 @@
/*\
title: js/macros/slider.js
!Introduction
The slider macro is used to selectively reveal a chunk of text. By default, it renders as a button that may be clicked or touched to reveal the enclosed text.
The enclosed text can be a string of WikiText or be taken from a target tiddler.
The current state of the slider can be stored as the string "open" or "closed" in a specified tiddler. If the value of that tiddler changes then the slider is automatically updated. If no state tiddler is specified then the state of the slider isn't retained, but the slider still works as expected.
!!Parameters
|`state` //(defaults to 1st parameter)// |The title of the tiddler to contain the current state of the slider |
|`default` |The initial state of the slider, either `open` or `closed` |
|`class` |A CSS class to be applied to the slider root element |
|`content` |The WikiText to be enclosed in the slider. Overrides the `target` parameter, if present |
|`target` //(defaults to 2nd parameter)// |The title of the tiddler that contains the enclosed text. Ignored if the `content` parameter is specified |
|`label` //(defaults to 3rd parameter)// |The plain text to be displayed as the label for the slider button |
|`tooltip` //(defaults to 4th parameter)// |The plain text tooltip to be displayed when the mouse hovers over the slider button |
!!Markup
The markup generated by the slider macro is:
{{{
<span class="tw-slider {user defined class}">
<a class="btn-info">{slider label}</a>
<div class="tw-slider-body" style="display:{state}">{slider content}</div>
</span>
}}}
!!Examples
A minimal slider:
{{{
<<slider target:MyTiddler>>
}}}
!!Notes
The slider is a good study example of a simple interactive macro.
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
Tiddler = require("../Tiddler.js").Tiddler,
utils = require("../Utils.js");
function getOpenState(macroNode) {
if(macroNode.params.hasOwnProperty("state")) {
var stateTiddler = macroNode.store.getTiddler(macroNode.params.state);
if(stateTiddler) {
return stateTiddler.text.trim() === "open";
}
}
if(macroNode.params.hasOwnProperty("default")) {
return macroNode.params["default"] === "open";
}
return false;
}
function saveOpenState(macroNode) {
if(macroNode.params.hasOwnProperty("state")) {
var stateTiddler = macroNode.store.getTiddler(macroNode.params.state) ||
new Tiddler({title: macroNode.params.state, text: ""});
macroNode.store.addTiddler(new Tiddler(stateTiddler,{text: macroNode.isOpen ? "open" : "closed"}));
return true;
}
return false;
}
function getSliderContent(macroNode) {
if(macroNode.params.hasOwnProperty("content")) {
return macroNode.store.parseText("text/x-tiddlywiki",macroNode.params.content).nodes;
} else if(macroNode.params.hasOwnProperty("target")) {
return [Renderer.MacroNode(
"tiddler",
{target: macroNode.params.target},
null,
macroNode.store)];
} else {
return [Renderer.ErrorNode("No content specified for slider")];
}
}
exports.macro = {
name: "slider",
params: {
state: {byPos: 0, type: "tiddler"},
"default": {byName: true, type: "text"},
"class": {byName: true, type: "text"},
target: {byPos: 1, type: "tiddler"},
label: {byPos: 2, type: "text"},
tooltip: {byPos: 3, type: "text"},
content: {byName: true, type: "text"}
},
events: {
click: function(event) {
if(event.target === this.domNode.firstChild.firstChild) {
this.isOpen = !this.isOpen;
if(!saveOpenState(this)) {
exports.macro.refreshInDom.call(this,{});
}
event.preventDefault();
return false;
} else {
return true;
}
}
},
execute: function() {
this.isOpen = getOpenState(this);
var sliderContent = [];
if(this.isOpen) {
sliderContent = getSliderContent(this);
}
var attributes = {
"class": ["tw-slider"]
};
if(this.hasParameter("class")) {
attributes["class"].push(this.params["class"]);
}
if(this.hasParameter("state")) {
attributes["data-tw-slider-type"] = this.params.state;
}
if(this.hasParameter("tooltip")) {
attributes.alt = this.params.tooltip;
attributes.title = this.params.tooltip;
}
var content = Renderer.ElementNode("span",
attributes,
[
Renderer.ElementNode("a",
{
"class": ["btn","btn-info"]
},[
Renderer.TextNode(this.params.label ? this.params.label : this.params.target)
]
),
Renderer.ElementNode("div",
{
"class": ["tw-slider-body"],
"style": {"display": this.isOpen ? "block" : "none"}
},
sliderContent
)
]
);
content.execute(this.parents,this.tiddlerTitle);
return [content];
},
refreshInDom: function(changes) {
var needContentRefresh = true; // Avoid refreshing the content nodes if we don't need to
// If the state tiddler has changed then reset the open state
if(this.hasParameter("state") && changes.hasOwnProperty(this.params.state)) {
this.isOpen = getOpenState(this);
}
// Render the content if the slider is open and we don't have any content yet
if(this.isOpen && this.content[0].children[1].children.length === 0) {
// Remove the existing dom node for the body
this.content[0].domNode.removeChild(this.content[0].children[1].domNode);
// Get the slider content and execute it
this.content[0].children[1].children = getSliderContent(this);
this.content[0].children[1].execute(this.parents,this.tiddlerTitle);
this.content[0].children[1].renderInDom(this.content[0].domNode,null);
needContentRefresh = false; // Don't refresh the children if we've just created them
}
// Set the visibility of the slider content
this.content[0].children[1].domNode.style.display = this.isOpen ? "block" : "none";
// Refresh any children
if(needContentRefresh) {
for(var t=0; t<this.content.length; t++) {
this.content[t].refreshInDom(changes);
}
}
}
};
})();

View File

@ -1,191 +0,0 @@
/*\
title: js/macros/story.js
\*/
(function(){
/*jslint node: true, jquery: true, browser: true */
"use strict";
var Tiddler = require("../Tiddler.js").Tiddler,
Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
utils = require("../Utils.js");
function scrollToTop(duration) {
if (duration < 0) {
return;
}
var delta = (-document.body.scrollTop/duration) * 10;
window.setTimeout(function() {
document.body.scrollTop = document.body.scrollTop + delta;
scrollToTop(duration-10);
}, 10);
}
exports.macro = {
name: "story",
params: {
story: {byName: "default", type: "tiddler"},
defaultViewTemplate: {byName: true, type: "tiddler"},
defaultEditTemplate: {byName: true, type: "tiddler"}
},
events: {
"tw-navigate": function(event) {
var template = this.hasParameter("defaultViewTemplate") ? this.params.defaultViewTemplate : "ViewTemplate",
storyTiddler = this.store.getTiddler(this.params.story),
story = {tiddlers: []};
if(storyTiddler && storyTiddler.hasOwnProperty("text")) {
story = JSON.parse(storyTiddler.text);
}
story.tiddlers.unshift({title: event.navigateTo, template: template});
this.store.addTiddler(new Tiddler(storyTiddler,{text: JSON.stringify(story)}));
scrollToTop(400);
event.stopPropagation();
return false;
},
"tw-EditTiddler": function(event) {
var template = this.hasParameter("defaultEditTemplate") ? this.params.defaultEditTemplate : "EditTemplate",
storyTiddler = this.store.getTiddler(this.params.story),
story = {tiddlers: []};
if(storyTiddler && storyTiddler.hasOwnProperty("text")) {
story = JSON.parse(storyTiddler.text);
}
for(var t=0; t<story.tiddlers.length; t++) {
var storyRecord = story.tiddlers[t];
if(storyRecord.title === event.tiddlerTitle && storyRecord.template !== template) {
storyRecord.title = "Draft " + (new Date()) + " of " + event.tiddlerTitle;
storyRecord.template = template;
var tiddler = this.store.getTiddler(event.tiddlerTitle);
this.store.addTiddler(new Tiddler(
{
text: "Type the text for the tiddler '" + event.tiddlerTitle + "'"
},
tiddler,
{
title: storyRecord.title,
"draft.title": event.tiddlerTitle,
"draft.of": event.tiddlerTitle
}));
}
}
this.store.addTiddler(new Tiddler(storyTiddler,{text: JSON.stringify(story)}));
event.stopPropagation();
return false;
},
"tw-SaveTiddler": function(event) {
var template = this.hasParameter("defaultViewTemplate") ? this.params.defaultEditTemplate : "ViewTemplate",
storyTiddler = this.store.getTiddler(this.params.story),
story = {tiddlers: []},
storyTiddlerModified = false;
if(storyTiddler && storyTiddler.hasOwnProperty("text")) {
story = JSON.parse(storyTiddler.text);
}
for(var t=0; t<story.tiddlers.length; t++) {
var storyRecord = story.tiddlers[t];
if(storyRecord.title === event.tiddlerTitle && storyRecord.template !== template) {
var tiddler = this.store.getTiddler(storyRecord.title);
if(tiddler && tiddler.hasOwnProperty("draft.title")) {
// Save the draft tiddler as the real tiddler
this.store.addTiddler(new Tiddler(tiddler,{title: tiddler["draft.title"],"draft.title": undefined, "draft.of": undefined}));
// Remove the draft tiddler
this.store.deleteTiddler(storyRecord.title);
// Remove the original tiddler if we're renaming it
if(tiddler["draft.of"] !== tiddler["draft.title"]) {
this.store.deleteTiddler(tiddler["draft.of"]);
}
// Make the story record point to the newly saved tiddler
storyRecord.title = tiddler["draft.title"];
storyRecord.template = template;
// Check if we're modifying the story tiddler itself
if(tiddler["draft.title"] === this.params.story) {
storyTiddlerModified = true;
}
}
}
}
if(!storyTiddlerModified) {
this.store.addTiddler(new Tiddler(storyTiddler,{text: JSON.stringify(story)}));
}
event.stopPropagation();
return false;
}
},
execute: function() {
var story = JSON.parse(this.store.getTiddlerText(this.params.story)),
content = [];
for(var t=0; t<story.tiddlers.length; t++) {
var m = Renderer.MacroNode("tiddler",
{target: story.tiddlers[t].title,template: story.tiddlers[t].template},
null,
this.store);
m.execute(this.parents,this.tiddlerTitle);
content.push(m);
}
return content;
},
refreshInDom: function(changes) {
var t;
/*jslint browser: true */
if(this.dependencies.hasChanged(changes,this.tiddlerTitle)) {
// Get the tiddlers we're supposed to be displaying
var self = this,
story = JSON.parse(this.store.getTiddlerText(this.params.story)),
template = this.params.template,
n,domNode,
findTiddler = function (childIndex,tiddlerTitle,templateTitle) {
while(childIndex < self.content.length) {
var params = self.content[childIndex].params;
if(params.target === tiddlerTitle) {
if(!templateTitle || params.template === templateTitle) {
return childIndex;
}
}
childIndex++;
}
return null;
};
for(t=0; t<story.tiddlers.length; t++) {
// See if the node we want is already there
var tiddlerNode = findTiddler(t,story.tiddlers[t].title,story.tiddlers[t].template);
if(tiddlerNode === null) {
// If not, render the tiddler
var m = Renderer.MacroNode("tiddler",
{target: story.tiddlers[t].title,template: story.tiddlers[t].template},
null,
this.store);
m.execute(this.parents,this.tiddlerTitle);
m.renderInDom(this.domNode,this.domNode.childNodes[t]);
this.content.splice(t,0,m);
} else {
// Delete any nodes preceding the one we want
if(tiddlerNode > t) {
// First delete the DOM nodes
for(n=t; n<tiddlerNode; n++) {
domNode = this.content[n].domNode;
domNode.parentNode.removeChild(domNode);
}
// Then delete the actual renderer nodes
this.content.splice(t,tiddlerNode-t);
}
// Refresh the DOM node we're reusing
this.content[t].refreshInDom(changes);
}
}
// Remove any left over nodes
if(this.content.length > story.tiddlers.length) {
for(t=story.tiddlers.length; t<this.content.length; t++) {
domNode = this.content[t].domNode;
domNode.parentNode.removeChild(domNode);
}
this.content.splice(story.tiddlers.length,this.content.length-story.tiddlers.length);
}
} else {
for(t=0; t<this.content.length; t++) {
this.content[t].refreshInDom(changes);
}
}
}
};
})();

View File

@ -1,156 +0,0 @@
/*\
title: js/macros/tiddler.js
The tiddler macros transcludes another macro into the tiddler being rendered.
Parameters:
target: the title of the tiddler to transclude
template: the title of the tiddler to use as a template for the transcluded tiddler
with: optional parameters to be substituted into the rendered tiddler
The simplest case is to just supply a target tiddler:
<<tiddler Foo>> or <<transclude target:Foo>>
This will render the tiddler Foo within the current tiddler. If the tiddler Foo includes
the view macro (or other macros that reference the fields of the current tiddler), then the
fields of the tiddler Foo will be accessed.
If you want to transclude the tiddler as a template, so that the fields referenced by the view
macro are those of the tiddler doing the transcluding, then you can instead specify the tiddler
as a template:
<<tiddler template:Foo>>
The effect is the same as the previous example: the text of the tiddler Foo is rendered. The
difference is that the view macro will access the fields of the tiddler doing the transcluding.
The `target` and `template` parameters may be combined:
<<tiddler target:Foo template:Bar>>
Here, the text of the tiddler `Bar` will be transcluded, with the macros within it accessing the fields
of the tiddler `Foo`.
Finally, the `with` parameter is used to substitute values for the special markers $1, $2, $3 etc. The
substitutions are performed on the tiddler whose text is being rendered: either the tiddler named in
the `template` parameter or, if that parameter is missing, the tiddler named in the `target` parameter.
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
utils = require("../Utils.js");
exports.macro = {
name: "tiddler",
cascadeParams: true, // Cascade names of named parameters to following anonymous parameters
params: {
target: {byName: "default", type: "tiddler"},
template: {byName: true, type: "tiddler"},
"with": {byName: true, type: "text", dependentAll: true}
},
evaluateDependencies: function() {
var dependencies = new Dependencies(),
template = this.srcParams.template;
if(template === undefined) {
template = this.srcParams.target;
}
if(typeof template === "function") {
dependencies.dependentAll = true;
} else {
dependencies.addDependency(template,true);
}
return dependencies;
},
execute: function() {
var renderTitle = this.params.target,
renderTemplate = this.params.template,
content,
contentClone = [],
t,
parents = this.parents.slice(0);
// If there's no render title specified then use the current tiddler title
if(typeof renderTitle !== "string") {
renderTitle = this.tiddlerTitle;
}
// If there's no template specified then use the target tiddler title
if(typeof renderTemplate !== "string") {
renderTemplate = renderTitle;
}
// Check for recursion
if(parents.indexOf(renderTemplate) !== -1) {
content = [Renderer.ErrorNode("Tiddler recursion error in <<tiddler>> macro")];
} else {
if("with" in this.params) {
// Parameterised transclusion
var targetTiddler = this.store.getTiddler(renderTemplate),
text = targetTiddler.text;
var withTokens = [this.params["with"]];
for(t=0; t<withTokens.length; t++) {
var placeholderRegExp = new RegExp("\\$"+(t+1),"mg");
text = text.replace(placeholderRegExp,withTokens[t]);
}
content = this.store.parseText(targetTiddler.type,text).nodes;
} else {
// There's no parameterisation, so we can just render the target tiddler directly
var parseTree = this.store.parseTiddler(renderTemplate);
content = parseTree ? parseTree.nodes : [];
}
}
// Update the stack of tiddler titles for recursion detection
parents.push(renderTemplate);
// Clone the content
for(t=0; t<content.length; t++) {
contentClone.push(content[t].clone());
}
// Execute macros within the content
for(t=0; t<contentClone.length; t++) {
contentClone[t].execute(parents,renderTitle);
}
// Set up the attributes for the wrapper element
var attributes = {
"data-tiddler-target": renderTitle,
"data-tiddler-template": renderTemplate,
"class": ["tw-tiddler-frame"]
};
if(!this.store.tiddlerExists(renderTitle)) {
attributes["class"].push("tw-tiddler-missing");
}
// Return the content
return [Renderer.ElementNode("div",attributes,contentClone)];
},
refreshInDom: function(changes) {
var t;
// Set the class for missing tiddlers
var renderTitle = this.params.target;
if(typeof renderTitle !== "string") {
renderTitle = this.params.template;
}
if(renderTitle) {
utils.toggleClass(this.content[0].domNode,"tw-tiddler-missing",!this.store.tiddlerExists(renderTitle));
}
// Rerender the tiddler if it is impacted by the changes
if(this.dependencies.hasChanged(changes,this.tiddlerTitle)) {
// Manually reexecute and rerender this macro
while(this.domNode.hasChildNodes()) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.execute(this.parents,this.tiddlerTitle);
for(t=0; t<this.content.length; t++) {
this.content[t].renderInDom(this.domNode);
}
} else {
// Refresh any children
for(t=0; t<this.content.length; t++) {
this.content[t].refreshInDom(changes);
}
}
}
};
})();

View File

@ -1,21 +0,0 @@
/*\
title: js/macros/version.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "version",
params: {
},
execute: function() {
return [Renderer.TextNode("5.0.0")];
}
};
})();

View File

@ -1,54 +0,0 @@
/*\
title: js/macros/video.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer;
exports.macro = {
name: "video",
params: {
src: {byName: "default", type: "text"},
type: {byName: true, type: "text"},
width: {byName: true, type: "text"},
height: {byName: true, type: "text"}
},
execute: function() {
var src = this.params.src,
videoType = this.params.type || "vimeo",
videoWidth = this.params.width || 640,
videoHeight = this.params.height || 360;
switch(videoType) {
case "vimeo":
return [Renderer.ElementNode("iframe",{
src: "http://player.vimeo.com/video/" + src + "?autoplay=0",
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
case "youtube":
return [Renderer.ElementNode("iframe",{
type: "text/html",
src: "http://www.youtube.com/embed/" + src,
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
case "archiveorg":
return [Renderer.ElementNode("iframe",{
src: "http://www.archive.org/embed/" + src,
width: videoWidth,
height: videoHeight,
frameborder: 0
})];
default:
return [];
}
}
};
})();

View File

@ -1,105 +0,0 @@
/*\
title: js/macros/view.js
\*/
(function(){
/*jslint node: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
utils = require("../Utils.js");
exports.macro = {
name: "view",
dependentOnContextTiddler: true,
params: {
field: {byPos: 0, type: "text"},
format: {byPos: 1, type: "text"},
template: {byPos: 2, type: "text"}
},
execute: function() {
var tiddler = this.store.getTiddler(this.tiddlerTitle),
field = this.hasParameter("field") ? this.params.field : "title",
value,
content,
t,
contentClone = [],
parents = this.parents;
if(tiddler) {
value = tiddler[field];
} else {
switch(field) {
case "text":
value = "The tiddler '" + this.tiddlerTitle + "' does not exist";
break;
case "title":
value = this.tiddlerTitle;
break;
case "modified":
case "created":
value = new Date();
break;
default:
value = "Missing tiddler '" + this.tiddlerTitle + "'";
break;
}
}
switch(this.params.format) {
case "link":
if(value === undefined) {
return [];
} else {
var link = Renderer.MacroNode("link",
{to: value},
[Renderer.TextNode(value)],
this.store);
link.execute(parents,this.tiddlerTitle);
return [link];
}
break;
case "wikified":
if(tiddler && this.params.field === "text") {
if(parents.indexOf(tiddler.title) !== -1) {
content = [Renderer.ErrorNode("Tiddler recursion error in <<view>> macro")];
} else {
content = this.store.parseTiddler(tiddler.title).nodes;
}
parents = parents.slice(0);
parents.push(tiddler.title);
} else {
content = this.store.parseText("text/x-tiddlywiki",value).nodes;
}
for(t=0; t<content.length; t++) {
contentClone.push(content[t].clone());
}
for(t=0; t<contentClone.length; t++) {
contentClone[t].execute(parents,this.tiddlerTitle);
}
return contentClone;
case "date":
var template = this.params.template || "DD MMM YYYY";
if(value === undefined) {
return [];
} else {
return [Renderer.TextNode(utils.formatDateString(value,template))];
}
break;
default: // "text"
// Get the stringified version of the field value
if(field !== "text" && tiddler) {
value = tiddler.getFieldString(field);
}
if(value === undefined || value === null) {
return [];
} else {
return [Renderer.TextNode(value)];
}
}
return [];
}
};
})();

View File

@ -1,106 +0,0 @@
/*\
title: js/macros/zoomer.js
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
var Renderer = require("../Renderer.js").Renderer,
Dependencies = require("../Dependencies.js").Dependencies,
Tiddler = require("../Tiddler.js").Tiddler,
utils = require("../Utils.js");
function startZoomer(macroNode,x,y) {
macroNode.inZoomer = true;
macroNode.startX = x;
macroNode.startY = y;
utils.addClass(document.body,"in-zoomer");
}
function stopZoomer(macroNode) {
var newScrollY = macroNode.yFactor * (macroNode.bodyHeight - macroNode.windowHeight);
macroNode.inZoomer = false;
window.scrollTo(0,newScrollY);
document.body.style.webkitTransform = "translateY(" + newScrollY * macroNode.xFactor + "px) " +
"scale(" + macroNode.scale + ") " +
"translateY(" + ((macroNode.windowHeight / macroNode.scale) - macroNode.bodyHeight) * macroNode.yFactor * macroNode.xFactor + "px)";
utils.removeClass(document.body,"in-zoomer");
document.body.style.webkitTransform = "translateY(0) scale(1) translateY(0)";
}
/*
Zoom the body element given a touch/mouse position in screen coordinates
*/
function hoverZoomer(macroNode,x,y) {
// Put the transform origin at the top in the middle
document.body.style.webkitTransformOrigin = "50% 0";
// Some shortcuts
macroNode.bodyWidth = document.body.offsetWidth;
macroNode.bodyHeight = document.body.offsetHeight;
macroNode.windowWidth = window.innerWidth;
macroNode.windowHeight = window.innerHeight;
// Compute the scale factor for fitting the entire page into the window. This is
// the scale factor we'll use when the touch is far to the left
macroNode.minScale = macroNode.windowHeight / macroNode.bodyHeight;
if(macroNode.minScale < 0.1) {
// Don't scale to less than 10% of original size
macroNode.minScale = 0.1;
} else if(macroNode.minScale > 1) {
// Nor should we scale up if the body is shorter than the window
macroNode.minScale = 1;
}
// We divide the screen into two horizontal zones divided by the right edge of the body at maximum zoom (ie minimum scale)
macroNode.splitPos = macroNode.windowWidth/2 + (macroNode.bodyWidth * macroNode.minScale)/2;
// Compute the 0->1 ratio (from right to left) of the position of the touch within the right zone
macroNode.xFactor = (macroNode.windowWidth - x) / (macroNode.windowWidth - macroNode.splitPos);
if(macroNode.xFactor > 1) {
macroNode.xFactor = 1;
}
// And the 0->1 ratio (from top to bottom) of the position of the touch down the screen
macroNode.yFactor = y/macroNode.windowHeight;
// Now interpolate the scale
macroNode.scale = (macroNode.minScale - 1) * macroNode.xFactor + 1;
// Apply the transform. The malarkey with .toFixed() is because otherwise we might get numbers in
// exponential notation (such as 5.1e-15) that are illegal in CSS
var preTranslateY = window.scrollY * macroNode.xFactor,
scale = macroNode.scale,
postTranslateY = ((macroNode.windowHeight / macroNode.scale) - macroNode.bodyHeight) * macroNode.yFactor * macroNode.xFactor;
var transform = "translateY(" + preTranslateY.toFixed(8) + "px) " +
"scale(" + scale.toFixed(8) + ") " +
"translateY(" + postTranslateY.toFixed(8) + "px)";
document.body.style.webkitTransform = transform;
}
exports.macro = {
name: "zoomer",
wrapperTag: "div",
params: {
},
events: {
touchstart: function(event) {
startZoomer(this,event.touches[0].clientX,event.touches[0].clientY);
hoverZoomer(this,event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
},
touchmove: function(event) {
hoverZoomer(this,event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
return false;
},
touchend: function(event) {
stopZoomer(this);
event.preventDefault();
return false;
}
},
execute: function() {
this.inZoomer = false;
return [];
}
};
})();

View File

@ -1,39 +0,0 @@
<div data-tiddler-target='ReadMe' data-tiddler-template='ReadMe' class='tw-tiddler-frame'><h1>Welcome to TiddlyWiki5</h1><div data-tiddler-target='HelloThere' data-tiddler-template='HelloThere' class='tw-tiddler-frame'>Welcome to TiddlyWiki5, a reboot of TiddlyWiki, the venerable, reusable non-linear personal web notebook first released in 2004. It is a complete interactive wiki that can run from a single HTML file in the browser or as a powerful node.js application.<br><br>TiddlyWiki5 is currently in early beta, which is to say that it is useful but incomplete. You can try out the prototype at <a href='http://tiddlywiki.com/tiddlywiki5' class='tw-tiddlylink tw-tiddlylink-external'>http://tiddlywiki.com/tiddlywiki5</a>, get involved in the <a href='https://github.com/Jermolene/TiddlyWiki5' class='tw-tiddlylink tw-tiddlylink-external'>development on GitHub</a> or the discussions on <a href='http://groups.google.com/group/TiddlyWikiDev' class='tw-tiddlylink tw-tiddlylink-external'>the TiddlyWikiDev Google Group</a>.<br></div><br><h1>Usage</h1><div data-tiddler-target='CommandLineInterface' data-tiddler-template='CommandLineInterface' class='tw-tiddler-frame'>TiddlyWiki5 can be used on the command line to perform an extensive set of operations based on RecipeFiles, TiddlerFiles and TiddlyWikiFiles.<br><br>The command line interface for TiddlyWiki5 has been designed to support two distinct usages:<br><ul><li> Cooking (or building) old 2.6.x versions of classic TiddlyWiki from the constituent JavaScript components</li><li> Cooking and serving TiddlyWiki5 itself</li></ul><br><h2>Usage</h2><code>
node tiddlywiki.js &lt;options&gt;
</code><br>The command line options are processed sequentially from left to right. Processing pauses during long operations, like loading a recipe file and all the subrecipes and tiddlers that it references, and then resumes with the next command line option in sequence.<br><br>The state that is carried between options is as follows:<br><ul><li> The set of tiddlers that are currently loaded into memory</li><li> The recipe file last used to load tiddlers (recipe files are used primarily to manage shadow tiddlers)</li><li> The TiddlerFileStore used to store the main content tiddlers. This is independent of the current recipe</li></ul><br>The following options are available:<br><table class='table'><tbody><tr class='evenRow'><td align='left'><code>--recipe &lt;filepath&gt;</code></td><td align='left'>Loads a specfied <code>.recipe</code> file</td></tr><tr class='oddRow'><td align='left'><code>--load &lt;filepath&gt;</code></td><td align='left'>Load additional tiddlers from 2.x.x TiddlyWiki files (<code>.html</code>), <code>.tiddler</code>, <code>.tid</code>, <code>.json</code> or other files</td></tr><tr class='evenRow'><td align='left'><code>--store &lt;dirpath&gt;</code></td><td align='left'>Load a specified TiddlerFileStore</td></tr><tr class='oddRow'><td align='left'><code>--links none</code></td><td align='left'>Determines how links will be generated by subsequent options. See below for details</td></tr><tr class='evenRow'><td align='left'><code>--savewiki &lt;dirpath&gt;</code></td><td align='left'>Saves all the loaded tiddlers as a single file TiddlyWiki called <code>index.html</code> and an RSS feed called <code>index.xml</code> in a new directory of the specified name</td></tr><tr class='oddRow'><td align='left'><code>--savetiddler &lt;title&gt; &lt;filename&gt; [&lt;type&gt;]</code></td><td align='left'>Save an individual tiddler as a specified MIME type, defaults to <code>text/html</code></td></tr><tr class='evenRow'><td align='left'><code>--savetiddlers &lt;outdir&gt;</code></td><td align='left'>Saves all the loaded tiddlers as <code>.tid</code> files in the specified directory</td></tr><tr class='oddRow'><td align='left'><code>--savehtml &lt;outdir&gt;</code></td><td align='left'>Saves all the loaded tiddlers as static, unstyled <code>.html</code> files in the specified directory</td></tr><tr class='evenRow'><td align='left'><code>--servewiki &lt;port&gt;</code></td><td align='left'>Serve the cooked TiddlyWiki over HTTP at <code>/</code></td></tr><tr class='oddRow'><td align='left'><code>--servetiddlers &lt;port&gt;</code></td><td align='left'>Serve individual tiddlers over HTTP at <code>/tiddlertitle</code></td></tr><tr class='evenRow'><td align='left'><code>--wikitest &lt;dir&gt; [save]</code></td><td align='left'>Run wikification tests against the tiddlers in the given directory. Include the <code>save</code> flag to save the test result files as the new targets</td></tr><tr class='oddRow'><td align='left'><code>--dumpstore</code></td><td align='left'>Dump the TiddlyWiki store in JSON format</td></tr><tr class='evenRow'><td align='left'><code>--dumprecipe</code></td><td align='left'>Dump the current recipe in JSON format</td></tr><tr class='oddRow'><td align='left'><code>--verbose</code></td><td align='left'>verbose output, useful for debugging</td></tr></tbody></table><h2> Examples</h2>This example loads the tiddlers from a TiddlyWiki HTML file and makes them available over HTTP:<br><code>
node tiddlywiki.js --load mywiki.html --servewiki 127.0.0.1:8000
</code><br>This example cooks a TiddlyWiki from a recipe:<br><code>
node tiddlywiki.js --recipe tiddlywiki.com/index.recipe --savewiki tmp/
</code><br>This example ginsus a TiddlyWiki into its constituent tiddlers:<br><code>
node tiddlywiki.js --load mywiki.html --savetiddlers tmp/tiddlers
</code><br><h2> Notes</h2>The HTTP serving functionality built into TiddlyWiki5 is designed to support personal use scenarios. If you want to flexibly and robustly share tiddlers on the web for multiple users, you should use TiddlyWeb or TiddlySpace.<br><br><code>--servewiki</code> and <code>--servetiddlers</code> are for different purposes and should not be used together. The former is for TiddlyWiki core developers who want to be able to edit the TiddlyWiki source files in a text editor and view the results in the browser by clicking refresh; it is slow because it reloads all the TiddlyWiki JavaScript files each time the page is loaded. The latter is for experimenting with the new wikification engine.<br><br><code>--wikitest</code> looks for <code>*.tid</code> files in the specified folder. It then wikifies the tiddlers to both &quot;text/plain&quot; and &quot;text/html&quot; format and checks the results against the content of the <code>*.html</code> and <code>*.txt</code> files in the same directory.<br><br><code>--links</code> controls the way that links are generated. Currently, the only option supported is:<br><br> <code>none</code>: Tiddler links are disabled<br><br><br></div><br><h1>Testing</h1><div data-tiddler-target='Testing' data-tiddler-template='Testing' class='tw-tiddler-frame'><h1>Test Scripts</h1><br>Three test scripts are provided, each as a Mac OS X <code>*.sh</code> bash script and a Windows <code>*.bat</code> batch file. In each case they should be run with the current directory set to the directory in which they reside.<br><br><ul><li> <code>test.sh</code>/<code>test.bat</code> cooks the main tiddlywiki.com recipe for TiddlyWiki 2.6.5 and compares it with the results of the old build process (ie, running cook.rb and then opening the file in a browser and performing a 'save changes' operation). It also runs a series of wikifications tests that work off the data in <code>test/wikitests/</code>.</li><li> <code>tw5.sh</code>/<code>tw5.bat</code> builds TiddlyWiki5 as a static HTML file</li><li> <code>tw5s.sh</code>/<code>tw5s.bat</code> serves TiddlyWiki5 over HTTP on port 8080</li></ul></div><br><h1>Architecture</h1><div data-tiddler-target='TiddlyWikiArchitecture' data-tiddler-template='TiddlyWikiArchitecture' class='tw-tiddler-frame'><h2> Overview</h2><br>The heart of TiddlyWiki can be seen as an extensible representation transformation engine. Given the text of a tiddler and its associated MIME type, the engine can produce a rendering of the tiddler in a new MIME type. Furthermore, it can efficiently selectively update the rendering to track any changes in the tiddler or its dependents.<br><br>The most important transformations are from <code>text/x-tiddlywiki</code> wikitext into <code>text/html</code> or <code>text/plain</code> but the engine is used throughout the system for other transformations, such as converting images for display in HTML, sanitising fragments of JavaScript, and processing CSS.<br><br>The key feature of wikitext is the ability to include one tiddler within another (usually referred to as <em>transclusion</em>). For example, one could have a tiddler called <em>Disclaimer</em> that contains the boilerplate of a legal disclaimer, and then include it within lots of different tiddlers with the macro call <code>&lt;&lt;tiddler Disclaimer&gt;&gt;</code>. This simple feature brings great power in terms of encapsulating and reusing content, and evolving a clean, usable implementation architecture to support it efficiently is a key objective of the TiddlyWiki5 design.<br><br>It turns out that the transclusion capability combined with the selective refreshing mechanism provides a good foundation for building TiddlyWiki's user interface itself. Consider, for example, the StoryMacro in its simplest form:<br><pre>&lt;&lt;story story:MyStoryTiddler&gt;&gt;
</pre>The story macro looks for a list of tiddler titles in the tiddler <code>MyStoryTiddler</code>, and displays them in sequence. The subtle part is that subsequently, if <code>MyStoryTiddler</code> changes, the <code>&lt;&lt;story&gt;&gt;</code> macro is selectively re-rendered. So, to navigate to a new tiddler, code merely needs to add the name of the tiddler and a line break to the top of <code>MyStoryTiddler</code>:<br><pre>var storyTiddler = store.getTiddler(&quot;MyStoryTiddler&quot;);
store.addTiddler(new Tiddler(storyTiddler,{text: navigateTo + &quot;\n&quot; + storyTiddler.text}));
</pre>The mechanisms that allow all of this to work are fairly intricate. The sections below progressively build the key architectural concepts of TiddlyWiki5 in a way that should provide a good basis for exploring the code directly.<br><h2> Tiddlers</h2>Tiddlers are an immutable dictionary of name:value pairs called fields.<br><br>The only field that is required is the <code>title</code> field, but useful tiddlers also have a <code>text</code> field, and some or all of the standard fields <code>modified</code>, <code>modifier</code>, <code>created</code>, <code>creator</code>, <code>tags</code> and <code>type</code>.<br><br>Hardcoded in the system is the knowledge that the <code>tags</code> field is a string array, and that the <code>modified</code> and <code>created</code> fields are JavaScript <code>Date</code> objects. All other fields are strings.<br><br>The <code>type</code> field identifies the representation of the tiddler text with a MIME type.<br><h2> WikiStore</h2>Groups of uniquely titled tiddlers are contained in WikiStore objects.<br><br>The WikiStore also manages the plugin modules used for macros, and operations like serializing, deserializing, parsing and rendering tiddlers.<br><br>Each WikiStore is connected to another shadow store that is used to provide default content. Under usual circumstances, when an attempt is made to retrieve a tiddler that doesn't exist in the store, the search continues into its shadow store (and so on, if the shadow store itself has a shadow store).<br><h2> WikiStore Events</h2>Clients can register event handlers with the WikiStore object. Event handlers can be registered to be triggered for modifications to any tiddler in the store, or with a filter to only be invoked when a particular tiddler or set of tiddlers changes.<br><br>Whenever a change is made to a tiddler, the wikistore registers a <code>nexttick</code> handler (if it hasn't already done so). The <code>nexttick</code> handler looks back at all the tiddler changes, and dispatches any matching event handlers. <br><h2> Parsing and Rendering</h2>TiddlyWiki parses the content of tiddlers to build an internal tree representation that is used for several purposes:<br><ul><li> Rendering a tiddler to other formats (e.g. converting wikitext to HTML)</li><li> Detecting outgoing links from a tiddler, and from them...</li><li> ...computing incoming links to a tiddler</li><li> Detecting tiddlers that are orphans with no incoming links</li><li> Detecting tiddlers that are referred to but missing</li></ul>The parse tree is built when needed, and then cached by the WikiStore until the tiddler changes.<br><br>TiddlyWiki5 uses multiple parsers:<br><ul><li> Wikitext (<code>text/x-tiddlywiki</code>) in <code>js/WikiTextParser.js</code></li><li> JavaScript (<code>text/javascript</code>) in <code>js/JavaScriptParser.js</code></li><li> Images (<code>image/png</code> and <code>image/jpg</code>) in <code>js/ImageParser.js</code></li><li> JSON (<code>application/json</code>) in <code>js/JSONParser.js</code></li></ul>Additional parsers are planned:<br><ul><li> CSS (<code>text/css</code>)</li><li> Recipe (<code>text/x-tiddlywiki-recipe</code>)</li></ul>One global instance of each parser is instantiated in <code>js/App.js</code> and registered with the main WikiStore object.<br><br>The parsers are all used the same way:<br><pre class='javascript-source'><span class='javascript-keyword'>var</span> <span class='javascript-identifier'>parseTree</span> <span class='javascript-punctuator'>=</span> <span class='javascript-identifier'>parser</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>parse</span><span class='javascript-punctuator'>(</span><span class='javascript-identifier'>type</span><span class='javascript-punctuator'>,</span><span class='javascript-identifier'>text</span><span class='javascript-punctuator'>)</span> <span class='javascript-comment javascript-line-comment'>// Parses the text and returns a parse tree object
</span></pre>The parse tree object exposes the following fields:<br><pre class='javascript-source'><span class='javascript-keyword'>var</span> <span class='javascript-identifier'>renderer</span> <span class='javascript-punctuator'>=</span> <span class='javascript-identifier'>parseTree</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>compile</span><span class='javascript-punctuator'>(</span><span class='javascript-identifier'>type</span><span class='javascript-punctuator'>)</span><span class='javascript-punctuator'>;</span> <span class='javascript-comment javascript-line-comment'>// Compiles the parse tree into a renderer for the specified MIME type
</span><span class='javascript-identifier'>console</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>log</span><span class='javascript-punctuator'>(</span><span class='javascript-identifier'>parseTree</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>toString</span><span class='javascript-punctuator'>(</span><span class='javascript-identifier'>type</span><span class='javascript-punctuator'>)</span><span class='javascript-punctuator'>)</span><span class='javascript-punctuator'>;</span> <span class='javascript-comment javascript-line-comment'>// Returns a readable string representation of the parse tree (either <code>text/html</code> or <code>text/plain</code>)
</span><span class='javascript-keyword'>var</span> <span class='javascript-identifier'>dependencies</span> <span class='javascript-punctuator'>=</span> <span class='javascript-identifier'>parseTree</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>dependencies</span><span class='javascript-punctuator'>;</span> <span class='javascript-comment javascript-line-comment'>// Gets the dependencies of the parse tree (see below)
</span></pre>The dependencies are returned as an object like this:<br><pre>{
tiddlers: {&quot;tiddlertitle1&quot;: true, &quot;tiddlertitle2&quot;: false},
dependentAll: false
}
</pre>The <code>tiddlers</code> field is a hashmap of the title of each tiddler that is linked or included in the current one. The value is <code>true</code> if the tiddler is a <em>'fat'</em> dependency (ie the text is included in some way) or <code>false</code> if the tiddler is a <em><code>skinny</code></em> dependency.<br><br>The <code>dependentAll</code> field is used to indicate that the tiddler contains a macro that scans the entire pool of tiddlers (for example the <code>&lt;&lt;list&gt;&gt;</code> macro), and is potentially dependent on any of them. The effect is that the tiddler should be rerendered whenever any other tiddler changes.<br><h2> Rendering</h2>The <code>parseTree.compile(type)</code> method returns a renderer object that contains a JavaScript function that generates the new representation of the original parsed text.<br><br>The renderer is invoked as follows:<br><pre class='javascript-source'><span class='javascript-keyword'>var</span> <span class='javascript-identifier'>renderer</span> <span class='javascript-punctuator'>=</span> <span class='javascript-identifier'>parseTree</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>compile</span><span class='javascript-punctuator'>(</span><span class='javascript-string'>&quot;text/html&quot;</span><span class='javascript-punctuator'>)</span><span class='javascript-punctuator'>;</span>
<span class='javascript-keyword'>var</span> <span class='javascript-identifier'>html</span> <span class='javascript-punctuator'>=</span> <span class='javascript-identifier'>renderer</span><span class='javascript-punctuator'>.</span><span class='javascript-identifier'>render</span><span class='javascript-punctuator'>(</span><span class='javascript-identifier'>tiddler</span><span class='javascript-punctuator'>,</span><span class='javascript-identifier'>store</span><span class='javascript-punctuator'>)</span><span class='javascript-punctuator'>;</span>
</pre>The <code>tiddler</code> parameter to the <code>render</code> method identifies the tiddler that is acting as the context for this rendering &mdash; for example, it provides the fields displayed by the <code>&lt;&lt;view&gt;&gt;</code> macro. The <code>store</code> parameter is used to resolve any references to other tiddlers.<br><h2> Rerendering</h2>When rendering to the HTML/SVG DOM in the browser, TiddlyWiki5 also allows a previous rendering to be selectively updated in response to changes in dependent tiddlers. At the moment, only the WikiTextRenderer supports rerendering.<br><br>The rerender method on the renderer is called as follows:<br><pre>var node = document.getElementById(&quot;myNode&quot;);
var renderer = parseTree.compile(&quot;text/html&quot;);
myNode.innerHTML = renderer.render(tiddler,store);
// And then, later:
renderer.rerender(node,changes,tiddler,store,renderStep);
</pre>The parameters to <code>rerender()</code> are:<br><table class='table'><tbody><tr class='evenRow'><th align='left'>Name</th><th align='left'>Description</th></tr><tr class='oddRow'><td align='left'>node</td><td align='left'>A reference to the DOM node containing the rendering to be rerendered</td></tr><tr class='evenRow'><td align='left'>changes</td><td align='left'>A hashmap of <code>{title: &quot;created|modified|deleted&quot;}</code> indicating which tiddlers have changed since the original rendering</td></tr><tr class='oddRow'><td align='left'>tiddler</td><td align='left'>The tiddler providing the rendering context</td></tr><tr class='evenRow'><td align='left'>store</td><td align='left'>The store to use for resolving references to other tiddlers</td></tr><tr class='oddRow'><td align='left'>renderStep</td><td align='left'>See below</td></tr></tbody></table>Currently, the only macro that supports rerendering is the <code>&lt;&lt;story&gt;&gt;</code> macro; all other macros are rerendered by calling the ordinary <code>render()</code> method again. The reason that the <code>&lt;&lt;story&gt;&gt;</code> macro goes to the trouble of having a <code>rerender()</code> method is so that it can be carefully selective about not disturbing tiddlers in the DOM that aren't affected by the change. If there were, for instance, a video playing in one of the open tiddlers it would be reset to the beginning if the tiddler were rerendered.<br><br><br></div><br><h1>Planned WikiText Features</h1><div data-tiddler-target='NewWikiTextFeatures' data-tiddler-template='NewWikiTextFeatures' class='tw-tiddler-frame'>It is proposed to extend the existing TiddlyWiki WikiText syntax with the following extensions<br><br><ol><li> Addition of <code>**bold**</code> character formatting</li><li> Addition of <code>`backtick for code`</code> character formatting</li><li> Addition of WikiCreole-style forced line break, e.g. <code>force\\linebreak</code></li><li> Addition of WikiCreole-style headings, e.g. <code>==Heading</code></li><li> Addition of WikiCreole-style headings in tables, e.g. <code>|=|=table|=header|</code></li><li> Addition of white-listed HTML and SVG tags intermixed with wikitext</li><li> Addition of WikiCreole-style pretty links, e.g. <code>[[description -&gt; link]]</code></li><li> Addition of multiline macros, e.g.</li></ol><pre>&lt;&lt;myMacro
param1: Parameter value
param2: value
&quot;unnamed parameter&quot;
param4: ((
A multiline parameter that can go on for as long as it likes
and contain linebreaks.
))
&gt;&gt;
</pre><ol><li> Addition of typed text blocks, e.g.</li></ol><pre> $$$.js
return &quot;This will have syntax highlighting applied&quot;
$$$
</pre></div><br><br><em>This <code>readme</code> file was automatically generated by TiddlyWiki5</em><br></div>

View File

@ -1,11 +0,0 @@
mkdir tmp\newcooked
node tiddlywiki.js --recipe test\tiddlywiki.2.6.5\source\tiddlywiki.com\index.html.recipe --savewiki tmp\newcooked
windiff tmp\newcooked\index.html test\tiddlywiki.2.6.5\target\index.2.6.5.html
node tiddlywiki.js --wikitest test\wikitests\
rem jshint *.js
rem jshint js
rem jshint tiddlywiki5\boot\*.js

View File

@ -1,21 +0,0 @@
#!/bin/sh
# create a temporary directory if it doesn't already exist
mkdir -p tmp
# cook tiddlywiki 2.6.5 with cook.js
mkdir -p tmp/newcooked
node tiddlywiki.js \
--recipe $PWD/test/tiddlywiki.2.6.5/source/tiddlywiki.com/index.html.recipe \
--savewiki tmp/newcooked \
|| exit 1
# compare the two
diff tmp/newcooked/index.html test/tiddlywiki.2.6.5/target/index.2.6.5.html
# Run the wikification tests
node tiddlywiki.js --wikitest test/wikitests/
jshint *.js
jshint js
jshint tiddlywiki5/boot/*.js

View File

@ -1,3 +0,0 @@
recipe: https://raw.github.com/TiddlyWiki/tiddlywiki/master/tiddlywikinonoscript.html.recipe
tiddler: http://tiddlywiki-com.tiddlyspace.com/bags/tiddlywiki-com-ref_public/tiddlers.json?fat=1
tiddler: http://tiddlywiki-com.tiddlyspace.com/bags/tiddlywiki-com_public/tiddlers.json?fat=1

View File

@ -1,63 +0,0 @@
TiddlyWiki
==========
https://github.com/TiddlyWiki/tiddlywiki.com
Description
-----------
This repository contains the tools required to create the site http://tiddlywiki.com/
The content for tiddlywiki.com is obtained from a [TiddlySpace](http://tiddlyspace.com/).
Prerequisites
-------------
Ensure that you have downloaded and installed TiddlyWiki as described at https://github.com/TiddlyWiki/tiddlywiki
You need perl to build tiddlywiki.com. If you do not have it installed, it can be downloaded [here](http://www.perl.org/get.html).
You need to set up `ginsu`. Copy the `ginsu` script file to somewhere that is on your path. Edit this file according to the instructions in the file.
You need to set up the `tiddler2tid`. Copy the `tiddler2tid` script file to somewhere that is on your path.
Building tiddlywiki.com
-----------------------
After downloading and installing TiddlyWiki checkout the version of TiddlyWiki that you wish to use for tiddlywiki.com. Ongoing development occurs in the tiddlywiki repository, so you need to checkout a tagged release version of TiddlyWiki. Change to the tiddlywiki directory and checkout the required version, eg:
git checkout tags/v2.6.5
Change back to the tiddlywiki.com directory.
Pull down the tiddlywiki.com content form TiddlySpace by invoking the `pull.sh` script:
./pull.sh
Edit the build script `bld` setting the correct version number for TiddlyWiki.
Invoke the build script:
./bld
You now need to generate the TiddlyWiki RSS file. To do this open the TiddlyWiki file index.html in Firefox, ensure the AdvancedOption "Generate an RSS feed when saving changes" is set, and then save the TiddlyWiki. Doing this also causes TiddlyWiki to generate some static HTML for display when Javascript is not enabled.
Edit the upload script `upload` setting the correct version number for TiddlyWiki.
Finally you need to upload the TiddlyWiki files to tiddlywiki.com. If this is the first time you are uploading, then you will need to create a `tmp` directory on tiddlywiki.com:
ssh user@tiddlywiki.com
[enter your password when prompted]
mkdir tmp
exit
You can now upload the TiddlyWiki files, run the upload script:
./upload
You will be prompted for your password on several occasions during the upload process. To do this you will of course need an account on tiddlywiki.com. The upload script assumes your remote user name is the same as your local user name, if it is not then you may specify your remote user name as the first parameter to the upload script.
Migrated from http://svn.tiddlywiki.org on 20110719.

View File

@ -1,17 +0,0 @@
#!/usr/bin/env bash
# Usage:
# bld [release]
DEFAULT_RELEASE="2.6.5"
RELEASE=${1:-$DEFAULT_RELEASE}
DEST=$PWD/cooked/tiddlywiki.com
mkdir -p cooked
mkdir -p cooked/tiddlywiki.com
cook $PWD/index.html.recipe -d $DEST -o index.$RELEASE.html
cook $PWD/empty.html.recipe -d $DEST -o empty.$RELEASE.html
cp ../tiddlywiki/java/TiddlySaver.jar $DEST/TiddlySaver.jar
rm $DEST/empty.$RELEASE.zip
cp $DEST/empty.$RELEASE.html tmp/empty.html
zip -j $DEST/empty.$RELEASE.zip tmp/empty.html $DEST/TiddlySaver.jar
rm tmp/empty.html

View File

@ -1 +0,0 @@
recipe: ../tiddlywiki/tiddlywiki.html.recipe

View File

@ -1,14 +0,0 @@
#!/bin/bash
#
# This is a sample ginsu script to be used with the tiddlywiki.com scripts
# Adjust this script and then install it somewhere on your $PATH, such as ~/bin.
#
# You will need to adjust COOKER_TRUNK below.
#
# Change this to where you have the cooker code installed
COOKER_TRUNK=$HOME/Documents/Code/GitHub/tiddlywiki/cooker
DEFAULT_FILENAME=index
FILENAME=${1:-$DEFAULT_FILENAME}
DEST=$PWD
RECIPE=$PWD/$FILENAME.html
ruby -C $COOKER_TRUNK ginsu.rb $RECIPE -d$DEST $2 $3 $4 $5

View File

@ -1,3 +0,0 @@
recipe: ../tiddlywiki/tiddlywikinonoscript.html.recipe
recipe: tiddlywiki-com-ref/split.recipe
recipe: tiddlywiki-com/split.recipe

View File

@ -1,58 +0,0 @@
#!/bin/sh
#
# this hack pulls down the wikis for each bag, splitting the wiki into tiddlers using ginsu
# long term plan is to use the "fat" JSON for a bag
#
set -e
export space
export dir
mkdir -p tmp
for space in tiddlywiki-com-ref tiddlywiki-com
do
mkdir -p $space
dir=tmp/${space}.html.0
curl -s http://${space}.tiddlyspace.com/bags/${space}_public/tiddlers.wiki > tmp/$space.html
# clear out the space directory so we can see deleted files when we commit
rm -f $space/*
# backup any existing exploded content
mkdir -p backups
[ -d $dir ] && mv $dir backups/$$
# split into tiddlers
(
cd tmp
ginsu $space > /dev/null
)
# convert .tiddler files into .tid files
(
cd "$dir"
tiddler2tid *.tiddler
find . -name \*.tid -o -name \*.js -o -name \*.meta |
while read file
do
sed -e '/^server.*: /d' -e '/^_hash:/d' < "$file" > "../../$space/$file"
done
)
# make recipe based on files in the space directory
(
cd $space
find . -name \*.tid -o -name \*.js |
grep -v '\.jpg\.' |
grep -v 'PageTemplate' |
grep -v 'SplashScreen' |
grep -v 'SiteSubtitle' |
sed 's/^/tiddler: /' > split.recipe
)
done
cook $PWD/index.html.recipe

View File

@ -1,53 +0,0 @@
#!/usr//bin/env perl
#
# convert .tiddler into .tid files
# useful for ginsu a TiddlyWiki, then HTTP PUT them to TiddlyWeb/TiddlySpaces
#
use strict;
sub read_file {
my ($filename) = @_;
undef $/;
local *FILE;
open FILE, "< $filename";
binmode(FILE, ":utf8");
my $c = <FILE>;
close FILE;
return $c;
}
foreach my $file (@ARGV) {
my $tid = $file;
my $text = "";
if ($file =~ /.tiddler$/) {
$tid =~ s/dler$//;
$text = read_file($file, encoding => 'utf8');
my $attrs = $text;
$attrs =~ s/\s*<div([^>]*)>.*$/$1/s;
$attrs =~ s/\s*(\w+)\s*=\s*["']([^"']*)["']\s*/$1: $2\n/gs;
$text =~ s/^\s*<div[^>]*>\s*<\s*pre>\s*(.*)\s*<\/pre\s*>\s*<\/div\s*>\s*$/$1/s;
$text = $attrs . "\n" . $text;
} elsif ($file =~ /.js$/) {
$tid =~ s/.js$/.tid/;
$text = read_file($file . ".meta") . "\n" . read_file($file);
}
if ($text) {
print "$tid\n";
open(FILE, "> $tid");
binmode(FILE, ":utf8");
print FILE $text;
close(FILE);
}
}

View File

@ -1,18 +0,0 @@
title: Basic Formatting
modifier: psd
created: 20110211142531
modified: 201102151515
tags: formatting
creator: matt
|Style|Formatting|h
|''bold''|{{{''bold''}}} - two single-quotes, not a double-quote|
|//italics//|{{{//italics//}}}|
|''//bold italics//''|{{{''//bold italics//''}}}|
|__underline__|{{{__underline__}}}|
|--strikethrough--|{{{--Strikethrough--}}}|
|super^^script^^|{{{super^^script^^}}}|
|sub~~script~~|{{{sub~~script~~}}}|
|@@Highlight@@|{{{@@Highlight@@}}}|
|{{{plain text}}}|{{{ {{{PlainText No ''Formatting''}}} }}}|
|/%this text will be invisible%/hidden text|{{{/%this text will be invisible%/}}}|

View File

@ -1,31 +0,0 @@
title: CSS Formatting
modifier: jermolene
created: 20110211142635
modified: 20111103182214
tags: formatting
creator: matt
!!Inline Styles
Apply CSS properties inline:
{{{
@@color:#4bbbbb;Some random text@@
}}}
Displays as:
@@color:#4bbbbb;Some random text@@
!!CSS classes
CSS classes can be applied to text blocks or runs. This form creates an HTML {{{<span>}}}:
{{{
{{customClassName{Some random text}}}
}}}
Displays as:
{{customClassName{Some random text}}}
This form generates an HTML {{{<div>}}}:
{{{
{{customClassName{
Some random text
}}}
}}}
Displays as:
{{customClassName{
Some random text
}}}

View File

@ -1,6 +0,0 @@
title: CamelCase
modifier: matt
created: 20110221141430
creator: matt
CamelCase (camel case or camel-case)—also known as medial capitals—is the practice of writing compound words or phrases in which the elements are joined without spaces.

View File

@ -1,17 +0,0 @@
title: Code Formatting
modifier: colmbritton
created: 20110211142613
modified: 20110216114812
tags: formatting
creator: matt
Text such as computer code that should be displayed without wiki processing and preserving line breaks:
&#123;&#123;&#123;
Some plain text including WikiLinks
&#125;&#125;&#125;
Displays as:
{{{
Some plain text including WikiLinks
}}}
!See Also
[[Suppressing Formatting]]

View File

@ -1,27 +0,0 @@
title: ColorPalette shadows
modifier: matt
created: 20110211143633
modified: 201102151510
tags: shadow
creator: matt
This tiddler determines the colour scheme used within the TiddlyWiki. When a new space is created the random color palette macro determines the default, however these can be overwritten directly in the ColorPalette tiddler.
{{{
Background: #e0e3f5
Foreground: #090d1e
PrimaryPale: #b9c2e8
PrimaryLight: #7485d2
PrimaryMid: #384fb1
PrimaryDark: #0c1126
SecondaryPale: #cbe8b9
SecondaryLight: #98d274
SecondaryMid: #67b138
SecondaryDark: #16260c
TertiaryPale: #e8bab9
TertiaryLight: #d27574
TertiaryMid: #b13a38
TertiaryDark: #260c0c
Error: #f88
ColorPaletteParameters: HSL([229|48], [0.5146822107288709],[0.1|0.8208696653333263])
}}}

View File

@ -1,36 +0,0 @@
title: Date Formats
modifier: matt
created: 20110216113958
modified: 20110221141513
creator: colmbritton
Several [[Macros|MacrosContent]] including the [[Today Macro|today macro]] take a [[Date Format String|Date Formats]] as an optional argument. This string can be a combination of ordinary text, with some special characters that get substituted by parts of the date:
* {{{DDD}}} - day of week in full (eg, "Monday")
* {{{ddd}}} - short day of week (eg, "Mon")
* {{{DD}}} - day of month
* {{{0DD}}} - adds a leading zero
* {{{DDth}}} - adds a suffix
* {{{WW}}} - ~ISO-8601 week number of year
* {{{0WW}}} - adds a leading zero
* {{{MMM}}} - month in full (eg, "July")
* {{{mmm}}} - short month (eg, "Jul")
* {{{MM}}} - month number
* {{{0MM}}} - adds leading zero
* {{{YYYY}}} - full year
* {{{YY}}} - two digit year
* {{{wYYYY}}} - full year with respect to week number
* {{{wYY}}} two digit year with respect to week number
* {{{hh}}} - hours
* {{{0hh}}} - adds a leading zero
* {{{hh12}}} - hours in 12 hour clock
* {{{0hh12}}} - hours in 12 hour clock with leading zero
* {{{mm}}} - minutes
* {{{0mm}}} - minutes with leading zero
* {{{ss}}} - seconds
* {{{0ss}}} - seconds with leading zero
* {{{am}}} or {{{pm}}} - lower case AM/PM indicator
* {{{AM}}} or {{{PM}}} - upper case AM/PM indicator
!!!!Examples
{{{DDth MMM YYYY}}} - 16th February 2011
{{{DDth mmm hh:mm:ss}}} - 16th Feb 2011 11:38:42

View File

@ -1,7 +0,0 @@
title: DefaultTiddlers
modifier: colmbritton
created: 20110211154017
modified: 20110211163338
creator: colmbritton
[[Reference]]

View File

@ -1,19 +0,0 @@
title: DefaultTiddlers shadows
modifier: matt
created: 20110211143659
modified: 201102151510
tags: shadow
creator: matt
This tiddler contains a list of tiddlers that are opened automatically when you load the tiddlywiki.
The default is:
{{{
[[GettingStarted]]
}}}
An example could be:
{{{
[[HelloThere]]
[[Reference]]
[[Features]]
}}}

View File

@ -1,34 +0,0 @@
title: EditTemplate shadows
modifier: matt
created: 20110211143734
modified: 201102151511
tags: shadow
creator: matt
This tiddler contains the markup used to display tiddlers in edit mode. It is designed to make it easy to change the layout/structure of the tiddler in edit mode.
By default it contains the following markup:
{{{
<div class='toolbar'
macro='toolbar [[ToolbarCommands::EditToolbar]] icons:yes'>
</div>
<div class='heading editorHeading'>
<div class='editor title' macro='edit title'></div>
<div class='tagClear'></div>
</div>
<div class='annotationsBox' macro='annotations'>
<div class='editSpaceSiteIcon'
macro='tiddlerOrigin height:16 width:16 label:no interactive:no'>
</div>
<div class="privacyEdit" macro='setPrivacy label:no interactive:no'></div>
<div class='tagClear'></div>
</div>
<div class='editor' macro='edit text'></div>
<div class='editorFooter'>
<div class='tagTitle'>tags</div>
<div class='editor' macro='edit tags'></div>
<div class='tagAnnotation'>
<span macro='message views.editor.tagPrompt'></span>
<span macro='tagChooser excludeLists'></span>
</div>
</div>
}}}

View File

@ -1,13 +0,0 @@
title: GettingStarted shadows
modifier: matt
created: 20110211143758
modified: 201102151537
tags: shadow
creator: matt
The GettingStarted shadow tiddler contains information about how to start using your TiddlyWiki.
You can change it to include anything you desire.
For example a lot of tiddlywiki authors use it to explain what their wiki is about. This is a particularly useful approach because by default the GettingStarted tiddler is in the [[DefaultTiddlers|Shadow - DefaultTiddlers]] tiddler, thus opens automatically upon loading of the page.
By default it is also part of the [[MainMenu|Shadow - MainMenu]] tiddler so is included on the left side of the menu bar above.

View File

@ -1,18 +0,0 @@
title: HTML Entities Formatting
modifier: psd
created: 20110211142850
modified: 201102151515
tags: formatting
creator: matt
HTML entities can be used to easily type special characters:
{{{
Here is a quote symbol: &quot;
And a pound sign: &pound;
}}}
Displays as:
Here is a quote symbol: &quot;
And a pound sign: &pound;
!Notes
For a full list of available HTML references see:
http://www.w3schools.com/tags/ref_entities.asp

View File

@ -1,20 +0,0 @@
title: HTML Formatting
modifier: psd
created: 20110211142909
modified: 20110215151851
tags: formatting
creator: matt
Raw HTML text can be included in a tiddler:
{{{
<html>
This is some <strong>HTML</strong> formatting
</html>
}}}
<html>
This is some <strong>HTML</strong> formatting
</html>
!Notes
* only static HTML elements that are valid within a {{{<div>}}} work correctly
* {{{document.write}}} cannot be used to generate dynamic content
* External {{{<script>}}} elements cannot be used within {{{<html>}}} blocks

View File

@ -1,22 +0,0 @@
title: Headings Formatting
modifier: psd
created: 20110211142759
modified: 201102151516
tags: formatting
creator: matt
{{{
!Heading Level 1
!!Heading Level 2
!!!Heading Level 3
!!!!Heading Level 4
!!!!!Heading Level 5
!!!!!!Heading Level 6
}}}
Display as:
!Heading Level 1
!!Heading Level 2
!!!Heading Level 3
!!!!Heading Level 4
!!!!!Heading Level 5
!!!!!!Heading Level 6

View File

@ -1,27 +0,0 @@
title: Horizontal Rule Formatting
modifier: psd
created: 20110211142821
modified: 20110215151841
tags: formatting
creator: matt
Four dashes on a line by themselves are used to introduce a horizontal rule:
{{{
Before the rule
----
After the rule
}}}
Displays as:
Before the rule
----
After the rule
The HTML tag {{{<hr>}}} can be used as an alternative syntax:
{{{
Before the rule<hr>After the rule
}}}
Displays as:
Before the rule<hr>After the rule

View File

@ -1,41 +0,0 @@
title: Image Formatting
modifier: psd
created: 20110211143038
modified: 201102151519
tags: formatting
creator: matt
!Simple Images
{{{
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
Displays as:
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
!Tooltips for Images
{{{
[img[tooltip|http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
Displays as:
[img[tooltip|http://wikitext.tiddlyspace.com/fractalveg.jpg]]
!Image Links
{{{
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg][http://www.flickr.com/photos/jermy/10134618/]]
}}}
Displays as:
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg][http://www.flickr.com/photos/jermy/10134618/]]
!Floating Images with Text
{{{
[<img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
[<img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]Displays as.
{{{
@@clear:both;display:block; @@
}}}
Will then clear the float.
@@clear:both;display:block;Like this@@
{{{
[>img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
[>img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]Displays as.@@clear:both;display:block; @@
!See Also
[[Image Macro]]

View File

@ -1,13 +0,0 @@
title: Line Break Formatting
modifier: psd
created: 20110211143101
modified: 20110215151912
tags: formatting
creator: matt
Line breaks can be forced explicitly:
{{{
Some text with a<br>line break in the middle
}}}
Displays as:
Some text with a<br>line break in the middle

View File

@ -1,71 +0,0 @@
title: Link Formatting
modifier: psd
created: 20110211143122
modified: 20110215151920
tags: formatting
creator: matt
!Wiki Links
Any words or phrases that are CamelCase or compound words - in which the elements are joined without spaces - will result in them becoming links to tiddlers with that name.
For example,
{{{
WikiWord
}}}
Displays as:
WikiWord
To stop this happening the words must be escaped:
{{{
~WikiWord
}}}
Displays as:
~WikiWord
Alternatively, a tiddler can be linked to using square brackets to encompass the whole tiddler title:
{{{
[[tiddler name]]
}}}
!Pretty Links
Optionally, custom text can be added, separated by a pipe character (|)
{{{
[[alternative link text|tiddler name]]
}}}
Displays as:
[[link to our WikiWords tiddler|WikiWords]]
!External Links
Writing the URL in the text results in a link to that external site:
{{{
http://osmosoft.com
}}}
Displays as:
http://osmosoft.com
Similar to pretty links alternative text can be used to link to external sites:
{{{
[[Visit the Osmosoft site|http://osmosoft.com]]
}}}
Displays as:
[[Visit the Osmosoft site|http://osmosoft.com]]
!Links to Other Spaces
Link to a space by preceding it with {{{@}}}:
{{{
@about
}}}
Displays as:
@about
Suppress space linking with {{{~}}}:
{{{
~@about
}}}
~@about
Link to a tiddler within another space:
{{{
TiddlyWiki@glossary
[[TiddlySpace API]]@glossary
[[Information about the HTTP interface|TiddlySpace API]]@glossary
}}}
Displays as:
TiddlyWiki@glossary
[[TiddlySpace API]]@glossary
[[Information about the HTTP interface|TiddlySpace API]]@glossary

View File

@ -1,44 +0,0 @@
title: List Formatting
modifier: psd
created: 20110211143223
modified: 20110215151929
tags: formatting
creator: matt
!Ordered Lists
Lists can be ordered with numbers and letters:
{{{
#List item one
##List item two
###List item three
}}}
Displays as:
#List item one
##List item two
###List item three
!Unordered Lists
Lists can be unordered:
{{{
*Unordered List Level 1
**Unordered List Level 2
***Unordered List Level 3
}}}
Displays as:
*Unordered List Level 1
**Unordered List Level 2
***Unordered List Level 3
!Definition Lists
Definition lists can also be created:
{{{
;Title 1
:Definition of title 1
;Title 2
:Definition of title 2
}}}
Displays as:
;Title 1
:Definition of title 1
;Title 2
:Definition of title 2
See also [[Tagging Macro]]

View File

@ -1,23 +0,0 @@
title: MacrosContent
modifier: colmbritton
created: 20110211144608
modified: 20110211153206
creator: colmbritton
[[allTags|allTags macro]]
[[closeAll|closeAll macro]]
[[list|list macro]]
[[newJournal|newJournal macro]]
[[newTiddler|newTiddler macro]]
[[permaview|permaview macro]]
[[saveChanges|saveChanges macro]]
[[search|search macro]]
[[slider|slider macro]]
[[tabs|tabs macro]]
[[tag|tag macro]]
[[tagging|tagging macro]]
[[tags|tags macro]]
[[tiddler|tiddler macro]]
[[timeline|timeline macro]]
[[today|today macro]]
[[version|version macro]]

View File

@ -1,8 +0,0 @@
title: MainMenu shadows
modifier: matt
created: 20110211161157
modified: 201102151511
tags: shadow
creator: matt
This tiddler contains a list of the tiddlers that appear in the MainMenu. In the default TiddlySpace theme the MainMenu appears in the horizontal bar at the top of the page and in the default TiddlyWiki theme the MainMenu is on the left.

View File

@ -1,11 +0,0 @@
title: MarkupPreHead
modifier: colmbritton
created: 20110211132837
tags: excludeLists
creator: colmbritton
<!--{{{-->
<link rel="shortcut icon" href="/recipes/tiddlywiki-com-ref_public/tiddlers/favicon.ico" />
<link href="/bags/tiddlywiki-com-ref_public/tiddlers.atom" rel="alternate"
type="application/atom+xml" title="tiddlywiki-com-ref's public feed" />
<!--}}}-->

View File

@ -1,35 +0,0 @@
title: PageTemplate shadows
modifier: matt
created: 20110211161254
modified: 201102151511
tags: shadow
creator: matt
This tiddler contains the mark up to display the page. You can change the mark up to create a custom view of the page.
By default it contains the following markup:
{{{
<!--{{{-->
<div class='header'>
<div id='sidebarSearch'>
<span macro='search'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='clearFloat'></div>
</div>
<div id='menuBar'>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<!--}}}-->
}}}

View File

@ -1,11 +0,0 @@
title: PluginManager shadows
modifier: matt
created: 20110211161318
modified: 201102151511
tags: shadow
creator: matt
This tiddler contains the command to display the plugin manager:
{{{
<<plugins>>
}}}

View File

@ -1,29 +0,0 @@
title: Quotations Formatting
modifier: psd
created: 20110211143244
modified: 201102151517
tags: formatting
creator: matt
!Block Quotations
Blocks of text can be displayed as quotations:
{{{
<<<
Steve Jobs: "computers are like a bicycle for our minds"
<<<
}}}
Displays as:
<<<
Steve Jobs: "computers are like a bicycle for our minds"
<<<
!Nested Quotations
Quotes can be displayed on multi-levels:
{{{
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3
}}}
Displays as:
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3

View File

@ -1,8 +0,0 @@
title: Reference
modifier: jermolene
created: 20110211145806
modified: 20111020134406
creator: colmbritton
|!Formatting|!Macros|!Shadow tiddlers|!Miscellaneous|
|<<tiddler WikiTextContent>>|<<tiddler MacrosContent>>|<<tiddler ShadowTiddlersContent>>|<<tiddler ReferenceContent>>|

View File

@ -1,26 +0,0 @@
title: ReferenceContent
modifier: jermolene
created: 20111020134406
tags: gettingstarted instructions
creator: jermolene
InterfaceOptions
SaveChanges
ToolbarButtons
SafeMode
KeyboardShortcuts
StartupParameters
SpecialTags
SpecialTiddlers
PermaView
HtmlEntities
[[Tags]]
IncrementalSearch
RegExpSearch
SaveEmptyTemplate
CustomStyleSheet
NestedStyleSheets
NestedTemplates
TiddlerSlicing
CustomMarkup
MobileDevices

View File

@ -1,24 +0,0 @@
title: ShadowTiddlersContent
modifier: matt
created: 20110211144608
modified: 20110215153659
creator: colmbritton
[[ColorPalette|ColorPalette shadows]]
[[DefaultTiddlers|DefaultTiddlers shadows]]
[[EditTemplate|EditTemplate shadows]]
[[GettingStarted|GettingStarted shadows]]
[[MainMenu|MainMenu shadows]]
[[PageTemplate|PageTemplate shadows]]
[[PluginManager|PluginManager shadows]]
[[SideBarOptions|SideBarOptions shadows]]
[[SiteSubtitle|SiteSubtitle shadows]]
[[SiteTitle|SiteTitle shadows]]
[[StyleSheet|StyleSheet shadows]]
[[TabAll|TabAll shadows]]
[[TabMoreMissing|TabMoreMissing shadows]]
[[TabMoreOrphans|TabMoreOrphans shadows]]
[[TabMoreShadowed|TabMoreShadowed shadows]]
[[TabTimeline|TabTimeline shadows]]
[[ViewTemplate|ViewTemplate shadows]]
[[WindowTitle|WindowTitle shadows]]

View File

@ -1,15 +0,0 @@
title: SideBarOptions shadows
modifier: matt
created: 20110211143834
modified: 201102151511
tags: shadow
creator: matt
This tiddler contains the commands that produce the buttons contained in the slider above the timeline on the right.
In ~TiddlySpace they are displayed on the right hand side of the menu bar.
By default it is set to:
{{{
<<closeAll>><<permaview>><<newTiddler>>
}}}
You can edit it to include anything else you want.

View File

@ -1,7 +0,0 @@
title: SiteSubtitle
modifier: colmbritton
created: 20110211132837
tags: excludeLists excludeSearch
creator: colmbritton
a TiddlySpace

View File

@ -1,17 +0,0 @@
title: SiteSubtitle shadows
modifier: matt
created: 20110211143856
modified: 201102151512
tags: shadow
creator: matt
In the SiteSubtitle tiddler you can define the subtitle of your tiddlywiki (seen under the main title)
In ~TiddlySpace by default it is set as
{{{
a TiddlySpace
}}}
You can set it to anything you want, such as:
{{{
documentation on the shadow tiddlers
}}}

View File

@ -1,7 +0,0 @@
title: SiteTitle
modifier: colmbritton
created: 20110211132837
tags: excludeLists excludeSearch
creator: colmbritton
tiddlywiki-com-ref

View File

@ -1,12 +0,0 @@
title: SiteTitle shadows
modifier: matt
created: 20110211161338
modified: 201102151512
tags: shadow
creator: matt
In the SiteTitle tiddler you can define the title of your TiddlyWiki. When you first create a TiddlyWiki it defaults to
{{{
TiddlyWiki
}}}
However you can edit it to display anything that you choose.

View File

@ -1,19 +0,0 @@
title: StyleSheet shadows
modifier: matt
created: 20110211143924
modified: 201102151512
tags: shadow
creator: matt
The StyleSheet tiddler allows you to customise the look and feel of your TiddlyWiki using CSS
You can put simple one line tweaks in, like:
{{{
.mainMenu { color: red; }
}}}
Or you can put in full ~StyleSheets, like that seen in TiddlySpace in the StyleSheetTiddlySpace
{{{
/*{{{*/
[[StyleSheetTiddlySpace]]
/*}}}*/
}}}

View File

@ -1,22 +0,0 @@
title: Suppressing Formatting
modifier: psd
created: 20110211143305
modified: 201102151517
tags: formatting
creator: matt
Wiki markup rules can be suppressed for any given section of text by enclosing it in 3 ''double'' quotes:
{{{
"""//WikiWord//"""
}}}
Displays as:
"""//WikiWord//"""
WikiWords can also be suppressed by prefixing the WikiWord with ''~'':
{{{
~WikiWord
}}}
Displays as:
~WikiWord
!!Also See
[[Code formatting]]

View File

@ -1,12 +0,0 @@
title: TabAll shadows
modifier: matt
created: 20110211161404
modified: 201102151513
tags: shadow
creator: matt
The TabAll tiddler contains a command to list all of the tiddler in your TiddlyWiki.
{{{
<<list all>>
}}}
In TiddlyWiki this list appears as default in the {{{All}}} tab on the right-hand menu.

View File

@ -1,12 +0,0 @@
title: TabMoreMissing shadows
modifier: matt
created: 20110211161435
modified: 201102151514
tags: shadow
creator: matt
The TabMoreMissing tiddler contains a command to list all of the tiddlers that have links to them but are undefined.
{{{
<<list missing>>
}}}
In TiddlyWiki this list appears as default in the {{{Missing}}} tab on the right-hand menu.

View File

@ -1,12 +0,0 @@
title: TabMoreOrphans shadows
modifier: matt
created: 20110211161504
modified: 201102151514
tags: shadow
creator: matt
The TabMoreOrphans tiddler contains a command that lists all the tiddlers that are not linked to from any other tidder.
{{{
<<list orphans>>
}}}
In TiddlyWiki this list appears as default in the {{{Orphans}}} tab on the right-hand menu.

View File

@ -1,12 +0,0 @@
title: TabMoreShadowed shadows
modifier: matt
created: 20110211161542
modified: 201102151514
tags: shadow
creator: matt
The TabMoreShadowed tiddler contains a command that displays a list of all tiddlers shadowed with default content in them
{{{
<<list shadowed>>
}}}
In TiddlyWiki this list appears as default in the {{{Shadows}}} tab on the right-hand menu.

View File

@ -1,12 +0,0 @@
title: TabTimeline shadows
modifier: matt
created: 20110211161625
modified: 201102151514
tags: shadow
creator: matt
The TabTimeline tiddler contains a command to list in reverse chronological the tiddlers in your TiddlyWiki
{{{
<<timeline>>
}}}
In TiddlyWiki this list appears as default in the {{{Recent}}} tab on the right-hand menu.

View File

@ -1,112 +0,0 @@
title: Tables Formatting
modifier: psd
created: 20110211143329
modified: 201102151517
tags: formatting
creator: matt
!Simple Tables
{{{
|North West|North|North East|
|West|Here|East|
|South West|South|South East|
}}}
Displays as:
|North West|North|North East|
|West|Here|East|
|South West|South|South East|
!Cell Formatting
*Insert a space before cell content to right justify cell
*Insert a space after cell content to left justify cell
*Insert spaces before and after cell content to centre justify cell
*Insert an exclamation mark ({{{!}}}) as the first non-space character of a cell to turn it into a header cell
For example:
{{{
|!First column|!Second column|!Third column|
|left | centre | right|
}}}
Displays as:
|!First column|!Second column|!Third column|
|left | centre | right|
!Table Headers and Footers
* Mark a table row as a header by adding an 'h' to the end
* Mark a table row as a footer by adding an 'f' to the end
For example:
{{{
|North West|North|North East|h
|West|Here|East|
|South West|South|South East|f
}}}
Displays as:
|North West|North|North East|h
|West|Here|East|
|South West|South|South East|f
!Table Caption
A caption can be added above or below a table by adding a special row marked with a 'c':
{{{
|A caption above the table|c
|North West|North|North East|
|West|Here|East|
|South West|South|South East|
}}}
{{{
|North West|North|North East|
|West|Here|East|
|South West|South|South East|
|A caption below the table|c
}}}
Displays as:
|A caption above the table|c
|North West|North|North East|h
|West|Here|East|
|South West|South|South East|f
|North West|North|North East|h
|West|Here|East|
|South West|South|South East|f
|A caption below the table|c
!Mergine Table Cells
A cell can be merged horizontally with the cell to its right by giving it the text {{{>}}}:
{{{
|North West|North|North East|
|>|>|West and Here and East|
|South West|South|South East|
}}}
Displays as:
|North West|North|North East|
|>|>|West and Here and East|
|South West|South|South East|
A cell can be merged vertically with the cell in the row above by giving it the text {{{~}}}:
{{{
|Westerly|North|North East|
|~|Here|East|
|~|South|South East|
}}}
Displays as:
|Westerly|North|North East|
|~|Here|East|
|~|South|South East|
!Table CSS Formatting
A CSS class can be added to an entire table by adding a special row tagged with a 'k':
{{{
|myClass|k
|North West|North|North East|
|West|Here|East|
|South West|South|South East|
}}}
CSS properties can be added to a table cell by preceding the cell content with CSS name/value pairs. There are two alternative forms of syntax:
{{{
|color:red; North West|opacity:0.5;North|North East|
|color(green):West|Here|East|
|South West|South|South East|
}}}
Displays as:
|color:red; North West|opacity:0.5;North|North East|
|color(green):West|Here|East|
|South West|South|South East|
!Alternating Rows
TiddlyWiki automatically assigns the classes {{{oddRow}}} and {{{evenRow}}} to table rows {{{<TR>}}} elements. These can then be styled via the StyleSheet:
{{{
.viewer tr.oddRow { background-color: #fff; }
.viewer tr.evenRow { background-color: #ffc; }
}}}

View File

@ -1,40 +0,0 @@
title: ViewTemplate shadows
modifier: matt
created: 20110211143943
modified: 201102151515
tags: shadow
creator: matt
This tiddler contains the mark up to display tiddlers. You can change the mark up to create a custom view of a tiddler.
By default it contains the following markup:
{{{
<div class='toolbar'
macro='toolbar [[ToolbarCommands::ViewToolbar]] icons:yes height:16 width:16 more:popup'>
</div>
<div class='heading'>
<span class='spaceSiteIcon'
macro='tiddlerOrigin label:no spaceLink:yes height:48 width:48 preserveAspectRatio:yes'>
</span>
<span class="titleBar">
<div class='title' macro='view title text'></div>
<div class="subtitle" macro='viewRevisions page:5'>
last modified on
<span macro="view modified date"></span>
</div>
</span>
<span class='followPlaceHolder' macro='followTiddlers'></span>
<span class='modifierIcon'
macro='view modifier SiteIcon label:no spaceLink:yes height:48 width:48 preserveAspectRatio:yes'>
</span>
<div class='tagClear'></div>
</div>
<div class='content'>
<div class='viewer' macro='view text wikified'></div>
</div>
<div class='tagInfo'>
<div class='tidTags' macro='tags'></div>
<div class='tagging' macro='tagging'></div>
</div>
}}}

View File

@ -1,20 +0,0 @@
title: WikiTextContent
modifier: matt
created: 20110211144608
modified: 20110215154054
creator: colmbritton
[[Basic|Basic Formatting]]
[[CSS|CSS Formatting]]
[[Code|Code Formatting]]
[[HTML Entities|HTML Entities Formatting]]
[[HTML|HTML Formatting]]
[[Headings|Headings Formatting]]
[[Horizontal Rules|Horizontal Rule Formatting]]
[[Images|Image Formatting]]
[[Line Breaks|Line Break Formatting]]
[[Links|Link Formatting]]
[[Lists|List Formatting]]
[[Quotations|Quotations Formatting]]
[[Suppressing|Suppressing Formatting]]
[[Tables|Tables Formatting]]

View File

@ -1,6 +0,0 @@
title: WikiWord
modifier: blaine
created: 20110211151015
creator: blaine
A WikiWord is a word composed of a bunch of other words slammed together with each of their first letters capitalised. WikiWord notation in a conventional WikiWikiWeb is used to name individual pages while TiddlyWiki uses WikiWord titles for smaller chunks of MicroContent. Referring to a page with a WikiWord automatically creates a link to it. Clicking on a link jumps to that page or, if it doesn't exist, to an editor to create it. It's also easy to have NonWikiWordLinks, and there's a WikiWordEscape for situations where you don't want a WikiWord to be interpreted as a link.

View File

@ -1,21 +0,0 @@
title: WindowTitle shadows
modifier: matt
created: 20110211144008
modified: 201102151515
tags: shadow
creator: matt
In this tiddler you put the text you wish to be displayed as the title of the page in the browser.
By default it is set to:
{{{
<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>
}}}
which could display something like:
{{{
shadowtiddlers - a tiddlywiki
}}}
You can change this at any time. For example:
{{{
Shadow Tiddlers Documentation | TiddlyWiki.com
}}}

View File

@ -1,15 +0,0 @@
title: allTags macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144608
tags: macro
creator: colmbritton
At it's most basic this macro lists all the tags used in you tiddlywiki as dropdown lists of all the tiddlers which have that tag
For example
{{{
<<allTags>>
}}}
Displays as:
<<allTags>>

View File

@ -1,6 +0,0 @@
title: anotherTiddlerToTransclude
modifier: jermolene
created: 20110228143927
creator: jermolene
Hello $1, welcome to $2

View File

@ -1,14 +0,0 @@
title: closeAll macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144608
tags: macro
creator: colmbritton
To close all open tiddlers:
{{{
<<closeAll>>
}}}
Displays as:
<<closeAll>>

View File

@ -1,37 +0,0 @@
title: list macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144608
tags: macro
creator: colmbritton
!all
To list all tiddlers
{{{
<<list all>>
}}}
!filter
List tiddlers that match a certain [[filter|filters syntax]]. The following example lists all plugins.
{{{
<<list filter [tag[systemConfig]]>>
}}}
!Missing
To list tiddlers that have links to them but are not defined:
{{{
<<list missing>>
}}}
!Orphans
To list tiddlers that are not linked to from any other tiddlers:
{{{
<<list orphans>>
}}}
!Shadowed
To list tiddlers shadowed with default contents:
{{{
<<list shadowed>>
}}}
!Touched
Show tiddlers that have been modified locally:
{{{
<<list touched>>
}}}

View File

@ -1,14 +0,0 @@
title: newJournal macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144608
tags: macro
creator: colmbritton
To create a new tiddler from the current date and time:
{{{
<<newJournal>>
}}}
Displays as:
<<newJournal>>

View File

@ -1,22 +0,0 @@
title: newTiddler macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144609
tags: macro
creator: colmbritton
Using the following you get a new tiddler button:
{{{
<<newTiddler>>
}}}
Displays as:
<<newTiddler>>
Certain attributes can also be attached, such as a label, default text, tags and fields:
{{{
<<newTiddler label:WikiWord text:"text" tag:tag tag:tag accessKey:key focus:field>>
}}}
Displays as:
<<newTiddler label:WikiWord text:"text" tag:tag tag:tag accessKey:key focus:field>>

View File

@ -1,17 +0,0 @@
title: options macro
modifier: jermolene
created: 20110221142149
modified: 20111103182247
tags: macro
creator: colmbritton
The options macro, used predominately in the backstage, allows the user to set a number of cookie based options that affect the enabled features of the Tiddlywiki. These settings can be trivial, such as do you want animations enabled, or can be used for more complex things, such as enable regular expressions searches.
It is used as below:
{{{
<<options>>
}}}
Options can be used by plugin developers to add settings that can be used by their plugin.
Displays as:
<<options>>

View File

@ -1,14 +0,0 @@
title: permaview macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144610
tags: macro
creator: colmbritton
To create a permaview URL for all open tiddlers:
{{{
<<permaview>>
}}}
Displays as:
<<permaview>>

View File

@ -1,23 +0,0 @@
title: saveChanges macro
modifier: colmbritton
created: 20110211143953
modified: 20110211144610
tags: macro
creator: colmbritton
Using the following creates a save changes button
{{{
<<saveChanges>>
}}}
Displays as:
<<saveChanges>>
The button label can also be changed and a tooltip added:
{{{
<<saveChanges [label] [tip]>>
}}}
Displays as:
<<saveChanges [label] [tip]>>

View File

@ -1,22 +0,0 @@
title: search macro
modifier: colmbritton
created: 20110211143954
modified: 20110211144610
tags: macro
creator: colmbritton
Search fields can be created using:
{{{
<<search>>
}}}
Displays as:
<<search>>
A default search term can also be added:
{{{
<<search [term]>>
}}}
Displays as:
<<search [term]>>

Some files were not shown because too many files have changed in this diff Show More