mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-26 01:50:28 +00:00
Removed the old pre-rabbit hole code
And the async module it was using
This commit is contained in:
parent
47435594e7
commit
2e0e74a353
@ -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.
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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 = /^(?:<!--@@(.*)@@-->)|(?:<!--@@(.*)@@-->)$/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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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 "&", < to "<", > to ">" and " to """
|
|
||||||
utils.htmlEncode = function(s)
|
|
||||||
{
|
|
||||||
if(s) {
|
|
||||||
return s.toString().replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,""");
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert "&" to &, "<" to <, ">" to > and """ to "
|
|
||||||
utils.htmlDecode = function(s)
|
|
||||||
{
|
|
||||||
return s.toString().replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/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",
|
|
||||||
' <style id="' + id + '" type="text/css">' + css + '</style>'); // fails without
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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("—"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
})();
|
|
@ -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 [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -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)])];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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)];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -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
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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")];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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 [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -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 [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -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 [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -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 <options>
|
|
||||||
</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 <filepath></code></td><td align='left'>Loads a specfied <code>.recipe</code> file</td></tr><tr class='oddRow'><td align='left'><code>--load <filepath></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 <dirpath></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 <dirpath></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 <title> <filename> [<type>]</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 <outdir></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 <outdir></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 <port></code></td><td align='left'>Serve the cooked TiddlyWiki over HTTP at <code>/</code></td></tr><tr class='oddRow'><td align='left'><code>--servetiddlers <port></code></td><td align='left'>Serve individual tiddlers over HTTP at <code>/tiddlertitle</code></td></tr><tr class='evenRow'><td align='left'><code>--wikitest <dir> [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 "text/plain" and "text/html" 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><<tiddler Disclaimer>></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><<story story:MyStoryTiddler>>
|
|
||||||
</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><<story>></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("MyStoryTiddler");
|
|
||||||
store.addTiddler(new Tiddler(storyTiddler,{text: navigateTo + "\n" + 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: {"tiddlertitle1": true, "tiddlertitle2": 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><<list>></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'>"text/html"</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 — for example, it provides the fields displayed by the <code><<view>></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("myNode");
|
|
||||||
var renderer = parseTree.compile("text/html");
|
|
||||||
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: "created|modified|deleted"}</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><<story>></code> macro; all other macros are rerendered by calling the ordinary <code>render()</code> method again. The reason that the <code><<story>></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 -> link]]</code></li><li> Addition of multiline macros, e.g.</li></ol><pre><<myMacro
|
|
||||||
param1: Parameter value
|
|
||||||
param2: value
|
|
||||||
"unnamed parameter"
|
|
||||||
param4: ((
|
|
||||||
A multiline parameter that can go on for as long as it likes
|
|
||||||
and contain linebreaks.
|
|
||||||
))
|
|
||||||
>>
|
|
||||||
</pre><ol><li> Addition of typed text blocks, e.g.</li></ol><pre> $$$.js
|
|
||||||
return "This will have syntax highlighting applied"
|
|
||||||
$$$
|
|
||||||
</pre></div><br><br><em>This <code>readme</code> file was automatically generated by TiddlyWiki5</em><br></div>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
recipe: ../tiddlywiki/tiddlywiki.html.recipe
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||||||
recipe: ../tiddlywiki/tiddlywikinonoscript.html.recipe
|
|
||||||
recipe: tiddlywiki-com-ref/split.recipe
|
|
||||||
recipe: tiddlywiki-com/split.recipe
|
|
@ -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
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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%/}}}|
|
|
@ -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
|
|
||||||
}}}
|
|
@ -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.
|
|
@ -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:
|
|
||||||
{{{
|
|
||||||
Some plain text including WikiLinks
|
|
||||||
}}}
|
|
||||||
Displays as:
|
|
||||||
{{{
|
|
||||||
Some plain text including WikiLinks
|
|
||||||
}}}
|
|
||||||
!See Also
|
|
||||||
[[Suppressing Formatting]]
|
|
@ -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])
|
|
||||||
}}}
|
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||||||
title: DefaultTiddlers
|
|
||||||
modifier: colmbritton
|
|
||||||
created: 20110211154017
|
|
||||||
modified: 20110211163338
|
|
||||||
creator: colmbritton
|
|
||||||
|
|
||||||
[[Reference]]
|
|
@ -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]]
|
|
||||||
}}}
|
|
@ -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>
|
|
||||||
}}}
|
|
@ -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.
|
|
@ -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: "
|
|
||||||
And a pound sign: £
|
|
||||||
}}}
|
|
||||||
Displays as:
|
|
||||||
Here is a quote symbol: "
|
|
||||||
And a pound sign: £
|
|
||||||
!Notes
|
|
||||||
For a full list of available HTML references see:
|
|
||||||
http://www.w3schools.com/tags/ref_entities.asp
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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]]
|
|
@ -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
|
|
@ -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
|
|
@ -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]]
|
|
@ -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]]
|
|
@ -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.
|
|
@ -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" />
|
|
||||||
<!--}}}-->
|
|
@ -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>
|
|
||||||
<!--}}}-->
|
|
||||||
}}}
|
|
@ -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>>
|
|
||||||
}}}
|
|
@ -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
|
|
@ -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>>|
|
|
@ -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
|
|
@ -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]]
|
|
@ -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.
|
|
@ -1,7 +0,0 @@
|
|||||||
title: SiteSubtitle
|
|
||||||
modifier: colmbritton
|
|
||||||
created: 20110211132837
|
|
||||||
tags: excludeLists excludeSearch
|
|
||||||
creator: colmbritton
|
|
||||||
|
|
||||||
a TiddlySpace
|
|
@ -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
|
|
||||||
}}}
|
|
@ -1,7 +0,0 @@
|
|||||||
title: SiteTitle
|
|
||||||
modifier: colmbritton
|
|
||||||
created: 20110211132837
|
|
||||||
tags: excludeLists excludeSearch
|
|
||||||
creator: colmbritton
|
|
||||||
|
|
||||||
tiddlywiki-com-ref
|
|
@ -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.
|
|
@ -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]]
|
|
||||||
/*}}}*/
|
|
||||||
}}}
|
|
@ -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]]
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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; }
|
|
||||||
}}}
|
|
@ -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>
|
|
||||||
}}}
|
|
@ -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]]
|
|
@ -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.
|
|
@ -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
|
|
||||||
}}}
|
|
@ -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>>
|
|
@ -1,6 +0,0 @@
|
|||||||
title: anotherTiddlerToTransclude
|
|
||||||
modifier: jermolene
|
|
||||||
created: 20110228143927
|
|
||||||
creator: jermolene
|
|
||||||
|
|
||||||
Hello $1, welcome to $2
|
|
@ -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>>
|
|
@ -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>>
|
|
||||||
}}}
|
|
@ -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>>
|
|
@ -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>>
|
|
@ -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>>
|
|
@ -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>>
|
|
@ -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]>>
|
|
@ -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
Loading…
Reference in New Issue
Block a user