1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-24 00:50:28 +00:00

The big purge of the old widget mechanism

Getting rid of the old widget mechanism files finally gives us a payoff
for all the refactoring. Still a bit of tidying up to do, and we need
to re-introduce the animation mechanisms.
This commit is contained in:
Jeremy Ruston 2013-10-27 22:55:36 +00:00
parent 5610efff01
commit 20f03de712
57 changed files with 39 additions and 5068 deletions

View File

@ -11,7 +11,7 @@ mkdir -p tmp/tw2
node ./tiddlywiki.js \
editions/tw5.com \
--verbose \
--rendertiddler TiddlyWiki2ReadMe editions/tw2/readme.md text/html \
--new_rendertiddler TiddlyWiki2ReadMe editions/tw2/readme.md text/html \
|| exit 1
# cook the TiddlyWiki 2.x.x index file
@ -20,7 +20,7 @@ node ./tiddlywiki.js \
editions/tw2 \
--verbose \
--load editions/tw2/source/tiddlywiki.com/index.html.recipe \
--rendertiddler $:/core/templates/tiddlywiki2.template.html ./tmp/tw2/index.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki2.template.html ./tmp/tw2/index.html text/plain \
|| exit 1
opendiff tmp/tw2/index.html editions/tw2/target/index.2.6.5.html
diff -q tmp/tw2/index.html editions/tw2/target/prebuilt.html

20
bld.sh
View File

@ -35,12 +35,12 @@ rm $TW5_BUILD_OUTPUT/static/*
node ./tiddlywiki.js \
./editions/tw5.com \
--verbose \
--rendertiddler ReadMe ./readme.md text/html \
--rendertiddler ContributingTemplate ./contributing.md text/html \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
--rendertiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
--rendertiddler $:/core/templates/static.template.css $TW5_BUILD_OUTPUT/static/static.css text/plain \
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html $TW5_BUILD_OUTPUT/static text/plain \
--new_rendertiddler ReadMe ./readme.md text/html \
--new_rendertiddler ContributingTemplate ./contributing.md text/html \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
--new_rendertiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
--new_rendertiddler $:/core/templates/static.template.css $TW5_BUILD_OUTPUT/static/static.css text/plain \
--new_rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html $TW5_BUILD_OUTPUT/static text/plain \
|| exit 1
# Second, encrypted.html: a version of the main file encrypted with the password "password"
@ -49,7 +49,7 @@ node ./tiddlywiki.js \
./editions/tw5.com \
--verbose \
--password password \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
|| exit 1
# Third, empty.html: empty wiki for reuse
@ -57,7 +57,7 @@ node ./tiddlywiki.js \
node ./tiddlywiki.js \
./editions/empty \
--verbose \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \
|| exit 1
# Fourth, tahoelafs.html: empty wiki with plugin for Tahoe-LAFS
@ -65,7 +65,7 @@ node ./tiddlywiki.js \
node ./tiddlywiki.js \
./editions/tahoelafs \
--verbose \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/tahoelafs.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/tahoelafs.html text/plain \
|| exit 1
# Fifth, d3demo.html: wiki to demo d3 plugin
@ -73,7 +73,7 @@ node ./tiddlywiki.js \
node ./tiddlywiki.js \
./editions/d3demo \
--verbose \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/d3demo.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/d3demo.html text/plain \
|| exit 1
# Sixth, run the test edition to run the node.js tests and to generate test.html for tests in the browser

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/commands/rendertiddler.js
type: application/javascript
module-type: command
Command to render a tiddler and save it to a file
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "rendertiddler",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
if(this.params.length < 2) {
return "Missing filename";
}
var self = this,
fs = require("fs"),
path = require("path"),
title = this.params[0],
filename = this.params[1],
type = this.params[2] || "text/html";
fs.writeFile(filename,this.commander.wiki.renderTiddler(type,title),"utf8",function(err) {
self.callback(err);
});
return null;
};
exports.Command = Command;
})();

View File

@ -1,54 +0,0 @@
/*\
title: $:/core/modules/commands/rendertiddlers.js
type: application/javascript
module-type: command
Command to render several tiddlers to a folder of files
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "rendertiddlers",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
if(this.params.length < 2) {
return "Missing filename";
}
var self = this,
fs = require("fs"),
path = require("path"),
wiki = this.commander.wiki,
filter = this.params[0],
template = this.params[1],
pathname = this.params[2],
type = this.params[3] || "text/html",
extension = this.params[4] || ".html",
parser = wiki.parseTiddler(template),
tiddlers = wiki.filterTiddlers(filter);
$tw.utils.each(tiddlers,function(title) {
var renderTree = new $tw.WikiRenderTree(parser,{wiki: wiki, context: {tiddlerTitle: title}, document: $tw.document});
renderTree.execute();
var container = $tw.document.createElement("div");
renderTree.renderInDom(container);
var text = type === "text/html" ? container.innerHTML : container.textContent;
fs.writeFileSync(path.resolve(pathname,encodeURIComponent(title) + extension),text,"utf8");
});
return null;
};
exports.Command = Command;
})();

View File

@ -1,212 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/element.js
type: application/javascript
module-type: wikirenderer
Element renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Element widget. A degenerate widget that renders ordinary HTML elements
*/
var ElementWidget = function(renderer) {
this.renderer = renderer;
this.tag = this.renderer.parseTreeNode.tag;
this.attributes = this.renderer.attributes;
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
this.events = this.renderer.parseTreeNode.events;
};
ElementWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attribute dependencies have changed
if($tw.utils.count(changedAttributes) > 0) {
// Update our attributes
this.renderer.assignAttributes();
}
// Refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
};
/*
Element renderer
*/
var ElementRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
// Initialise widget classes
if(!this.widgetClasses) {
ElementRenderer.prototype.widgetClasses = $tw.modules.applyMethods("widget");
}
// Select the namespace for the tag
var tagNameSpaces = {
svg: "http://www.w3.org/2000/svg",
math: "http://www.w3.org/1998/Math/MathML"
};
this.namespace = tagNameSpaces[this.parseTreeNode.tag];
if(this.namespace) {
this.context = this.context || {};
this.context.namespace = this.namespace;
} else {
this.namespace = this.renderTree.getContextVariable(this.parentRenderer,"namespace","http://www.w3.org/1999/xhtml");
}
// Get the context tiddler title
this.tiddlerTitle = this.renderTree.getContextVariable(this.parentRenderer,"tiddlerTitle");
// Compute our dependencies
this.dependencies = {};
var self = this;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(attribute.type === "indirect") {
var tr = $tw.utils.parseTextReference(attribute.textReference);
self.dependencies[tr.title ? tr.title : self.tiddlerTitle] = true;
}
});
// Compute our attributes
this.attributes = {};
this.computeAttributes();
// Create the parasite widget object if required
if(this.parseTreeNode.tag.charAt(0) === "$") {
// Choose the class
var WidgetClass = this.widgetClasses[this.parseTreeNode.tag.substr(1)];
// Instantiate the widget
if(WidgetClass) {
this.widget = new WidgetClass(this);
} else {
WidgetClass = this.widgetClasses.error;
if(WidgetClass) {
this.widget = new WidgetClass(this,"Unknown widget '<" + this.parseTreeNode.tag + ">'");
}
}
}
// If we haven't got a widget, use the generic HTML element widget
if(!this.widget) {
this.widget = new ElementWidget(this);
}
};
ElementRenderer.prototype.computeAttributes = function() {
var changedAttributes = {},
self = this,
value;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(attribute.type === "indirect") {
value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
} else if(attribute.type === "macro") {
// Get the macro definition
var macro = self.renderTree.findMacroDefinition(self.parentRenderer,attribute.value.name);
if(!macro) {
value = "";
} else {
// Substitute the macro parameters
value = self.renderTree.substituteParameters(macro,attribute.value);
// Parse the text and render it as text
value = self.renderTree.wiki.renderText("text/plain","text/vnd.tiddlywiki",value,self.context);
}
} else { // String attribute
value = attribute.value;
}
// Check whether the attribute has changed
if(self.attributes[name] !== value) {
self.attributes[name] = value;
changedAttributes[name] = true;
}
});
return changedAttributes;
};
ElementRenderer.prototype.hasAttribute = function(name) {
return $tw.utils.hop(this.attributes,name);
};
ElementRenderer.prototype.getAttribute = function(name,defaultValue) {
if($tw.utils.hop(this.attributes,name)) {
return this.attributes[name];
} else {
return defaultValue;
}
};
ElementRenderer.prototype.renderInDom = function() {
// Check if our widget is providing an element
if(this.widget.tag) {
// Create the element
this.domNode = this.renderTree.document.createElementNS(this.namespace,this.widget.tag);
// Assign any specified event handlers
$tw.utils.addEventListeners(this.domNode,this.widget.events);
// Assign the attributes
this.assignAttributes();
// Render any child nodes
var self = this;
$tw.utils.each(this.widget.children,function(node) {
if(node.renderInDom) {
self.domNode.appendChild(node.renderInDom());
}
});
// Call postRenderInDom if the widget provides it and we're in the browser
if($tw.browser && this.widget.postRenderInDom) {
this.widget.postRenderInDom();
}
// Return the dom node
return this.domNode;
} else {
// If we're not generating an element, just render our first child
return this.widget.children[0].renderInDom();
}
};
ElementRenderer.prototype.assignAttributes = function() {
var self = this;
$tw.utils.each(this.widget.attributes,function(v,a) {
if(v !== undefined) {
if($tw.utils.isArray(v)) { // Ahem, could there be arrays other than className?
self.domNode.className = v.join(" ");
} else if (typeof v === "object") { // ...or objects other than style?
for(var p in v) {
self.domNode.style[$tw.utils.unHyphenateCss(p)] = v[p];
}
} else {
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
try {
self.domNode.setAttributeNS(null,a,v);
} catch(e) {
}
}
}
});
};
ElementRenderer.prototype.refreshInDom = function(changedTiddlers) {
// Update our attributes if required
var changedAttributes = {};
if($tw.utils.checkDependencies(this.dependencies,changedTiddlers)) {
changedAttributes = this.computeAttributes();
}
// Check if the widget has a refreshInDom method
if(this.widget.refreshInDom) {
// Let the widget refresh itself
this.widget.refreshInDom(changedAttributes,changedTiddlers);
} else {
// If not, assign the attributes and refresh any child nodes
this.assignAttributes();
$tw.utils.each(this.widget.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.element = ElementRenderer
})();

View File

@ -1,31 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/entity.js
type: application/javascript
module-type: wikirenderer
Entity renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Entity renderer
*/
var EntityRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
};
EntityRenderer.prototype.renderInDom = function() {
return this.renderTree.document.createTextNode($tw.utils.entityDecode(this.parseTreeNode.entity));
};
exports.entity = EntityRenderer
})();

View File

@ -1,65 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/macrocall.js
type: application/javascript
module-type: wikirenderer
Macro call renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Macro call renderer
*/
var MacroCallRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
// Find the macro definition
var macro = this.renderTree.findMacroDefinition(this.parentRenderer,this.parseTreeNode.name);
// Insert an error message if we couldn't find the macro
var childTree;
if(!macro) {
childTree = [{type: "text", text: "<<Undefined macro: " + this.parseTreeNode.name + ">>"}];
} else {
// Substitute the macro parameters
var text = this.renderTree.substituteParameters(macro,this.parseTreeNode);
// Parse the text
childTree = this.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.parseTreeNode.isBlock}).tree;
}
// Create the renderers for the child nodes
this.children = this.renderTree.createRenderers(this,childTree);
};
MacroCallRenderer.prototype.renderInDom = function() {
// Create the element
this.domNode = this.renderTree.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
this.domNode.setAttribute("data-macro-name",this.parseTreeNode.name);
// Render any child nodes
var self = this;
$tw.utils.each(this.children,function(node,index) {
if(node.renderInDom) {
self.domNode.appendChild(node.renderInDom());
}
});
// Return the dom node
return this.domNode;
};
MacroCallRenderer.prototype.refreshInDom = function(changedTiddlers) {
// Refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
};
exports.macrocall = MacroCallRenderer
})();

View File

@ -1,30 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/macrodef.js
type: application/javascript
module-type: wikirenderer
Macro definition renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Macro definition renderer
*/
var MacroDefRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
// Save the macro definition into the context of the rendertree
this.renderTree.context.macroDefinitions = this.renderTree.context.macroDefinitions || {};
this.renderTree.context.macroDefinitions[this.parseTreeNode.name] = this.parseTreeNode;
};
exports.macrodef = MacroDefRenderer
})();

View File

@ -1,33 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/raw.js
type: application/javascript
module-type: wikirenderer
Raw HTML renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Raw HTML renderer
*/
var RawRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
};
RawRenderer.prototype.renderInDom = function() {
var domNode = this.renderTree.document.createElement("div");
domNode.innerHTML = this.parseTreeNode.html;
return domNode;
};
exports.raw = RawRenderer
})();

View File

@ -1,31 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/text.js
type: application/javascript
module-type: wikirenderer
Text renderer
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Text renderer
*/
var TextRenderer = function(renderTree,parentRenderer,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.parentRenderer = parentRenderer;
this.parseTreeNode = parseTreeNode;
};
TextRenderer.prototype.renderInDom = function() {
return this.renderTree.document.createTextNode(this.parseTreeNode.text);
};
exports.text = TextRenderer
})();

View File

@ -1,198 +0,0 @@
/*\
title: $:/core/modules/rendertree/wikirendertree.js
type: application/javascript
module-type: global
Wiki text render tree
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Create a render tree object for a parse tree
parser: reference to the parse tree to be rendered
options: see below
Options include:
wiki: mandatory reference to wiki associated with this render tree
context: optional hashmap of context variables (see below)
parentRenderer: optional reference to a parent renderer node for the context chain
document: optional document object to use instead of global document
Context variables include:
tiddlerTitle: title of the tiddler providing the context
templateTitle: title of the tiddler providing the current template
macroDefinitions: hashmap of macro definitions
*/
var WikiRenderTree = function(parser,options) {
this.parser = parser;
this.wiki = options.wiki;
this.context = options.context || {};
this.parentRenderer = options.parentRenderer;
this.document = options.document;
// Hashmap of the renderer classes
if(!this.rendererClasses) {
WikiRenderTree.prototype.rendererClasses = $tw.modules.applyMethods("wikirenderer");
}
};
/*
Generate the full render tree for this parse tree
*/
WikiRenderTree.prototype.execute = function() {
this.rendererTree = this.createRenderers(this,this.parser.tree);
};
/*
Create an array of renderers for an array of parse tree nodes
*/
WikiRenderTree.prototype.createRenderers = function(parentRenderer,parseTreeNodes) {
var rendererNodes = [];
if(parseTreeNodes) {
for(var t=0; t<parseTreeNodes.length; t++) {
rendererNodes.push(this.createRenderer(parentRenderer,parseTreeNodes[t]));
}
}
return rendererNodes;
};
/*
Create a renderer node for a parse tree node
*/
WikiRenderTree.prototype.createRenderer = function(parentRenderer,parseTreeNode) {
var RenderNodeClass = this.rendererClasses[parseTreeNode.type];
return new RenderNodeClass(this,parentRenderer,parseTreeNode);
};
/*
Render to the DOM
*/
WikiRenderTree.prototype.renderInDom = function(container) {
this.container = container;
$tw.utils.each(this.rendererTree,function(node) {
if(node.renderInDom) {
container.appendChild(node.renderInDom());
}
});
};
/*
Update the DOM rendering in the light of a set of changes
*/
WikiRenderTree.prototype.refreshInDom = function(changes) {
$tw.utils.each(this.rendererTree,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changes);
}
});
};
/*
Find the value of a given context variable for a particular renderer node
*/
WikiRenderTree.prototype.getContextVariable = function(renderer,name,defaultValue) {
while(renderer) {
if($tw.utils.hop(renderer.context,name)) {
return renderer.context[name];
}
renderer = renderer.parentRenderer;
};
return defaultValue;
};
/*
Check for render context recursion from a particular renderer node by returning true if the members of a proposed new render context are already present in the render context chain
*/
WikiRenderTree.prototype.checkContextRecursion = function(renderer,newContext) {
while(renderer) {
var context = renderer.context;
if(context) {
var match = true;
for(var member in newContext) {
if($tw.utils.hop(context,member)) {
if(newContext[member] !== context[member]) {
match = false;
}
} else {
match = false;
}
}
if(match) {
return true;
}
}
renderer = renderer.parentRenderer;
}
return false;
};
WikiRenderTree.prototype.getContextScopeId = function(renderer) {
var guidBits = [],
scopeComponents = ["tiddlerTitle","templateTitle"],
processContext = function(field,name) {
if(scopeComponents.indexOf(name) !== -1) {
guidBits.push(name + ":" + field + ";");
}
};
while(renderer) {
if(renderer.context) {
$tw.utils.each(renderer.context,processContext);
guidBits.push("-");
}
renderer = renderer.parentRenderer;
}
return guidBits.join("");
};
/*
Find a named macro definition
*/
WikiRenderTree.prototype.findMacroDefinition = function(renderer,name) {
while(renderer) {
if(renderer.context && renderer.context.macroDefinitions && renderer.context.macroDefinitions[name]) {
return renderer.context.macroDefinitions[name];
}
renderer = renderer.parentRenderer;
}
return undefined;
};
/*
Expand the parameters of a macro
*/
WikiRenderTree.prototype.substituteParameters = function(macroDefinition,macroCallParseTreeNode) {
var text = macroDefinition.text,
nextAnonParameter = 0; // Next candidate anonymous parameter in macro call
// Step through each of the parameters in the macro definition
for(var p=0; p<macroDefinition.params.length; p++) {
// Check if we've got a macro call parameter with the same name
var paramInfo = macroDefinition.params[p],
paramValue = undefined;
for(var m=0; m<macroCallParseTreeNode.params.length; m++) {
if(macroCallParseTreeNode.params[m].name === paramInfo.name) {
paramValue = macroCallParseTreeNode.params[m].value;
}
}
// If not, use the next available anonymous macro call parameter
if(!paramValue && nextAnonParameter < macroCallParseTreeNode.params.length) {
while(macroCallParseTreeNode.params[nextAnonParameter].name && nextAnonParameter < macroCallParseTreeNode.params.length-1) {
nextAnonParameter++;
}
if(!macroCallParseTreeNode.params[nextAnonParameter].name) {
paramValue = macroCallParseTreeNode.params[nextAnonParameter].value;
nextAnonParameter++;
}
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
// Replace any instances of this parameter
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
}
return text;
};
exports.WikiRenderTree = WikiRenderTree;
})();

View File

@ -1,150 +0,0 @@
/*\
title: $:/core/modules/widgets/button.js
type: application/javascript
module-type: widget
Implements the button widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ButtonWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
ButtonWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.message = this.renderer.getAttribute("message");
this.param = this.renderer.getAttribute("param");
this.set = this.renderer.getAttribute("set");
this.setTo = this.renderer.getAttribute("setTo");
this.popup = this.renderer.getAttribute("popup");
this.hover = this.renderer.getAttribute("hover");
this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
this["class"] = this.renderer.getAttribute("class");
this.selectedClass = this.renderer.getAttribute("selectedClass");
// Compose the button
var classes = ["tw-button"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
var events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
if(this.hover === "yes") {
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
}
if(this.set && this.setTo && this.selectedClass) {
if(this.isSelected()) {
classes.push(this.selectedClass);
}
}
// Set the return element
this.tag = "button";
this.attributes ={"class": classes.join(" ")};
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
this.events = events;
};
ButtonWidget.prototype.dispatchMessage = function(event) {
$tw.utils.dispatchCustomEvent(event.target,this.message,{
param: this.param,
tiddlerTitle: this.renderer.tiddlerTitle
});
};
ButtonWidget.prototype.triggerPopup = function(event) {
var title = this.popup;
if(this.qualifyTiddlerTitles) {
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
}
$tw.popup.triggerPopup({
domNode: this.renderer.domNode,
title: title,
wiki: this.renderer.renderTree.wiki
});
};
ButtonWidget.prototype.isSelected = function() {
var title = this.set;
if(this.qualifyTiddlerTitles) {
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
}
var tiddler = this.renderer.renderTree.wiki.getTiddler(title);
return tiddler ? tiddler.fields.text === this.setTo : false;
};
ButtonWidget.prototype.setTiddler = function() {
var title = this.set;
if(this.qualifyTiddlerTitles) {
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
}
var tiddler = this.renderer.renderTree.wiki.getTiddler(title);
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: title, text: this.setTo}));
};
ButtonWidget.prototype.handleClickEvent = function(event) {
var handled = false;
if(this.message) {
this.dispatchMessage(event);
handled = true;
}
if(this.popup) {
this.triggerPopup(event);
handled = true;
}
if(this.set) {
this.setTiddler();
handled = true;
}
event.stopPropagation();
event.preventDefault();
return handled;
};
ButtonWidget.prototype.handleMouseOverOrOutEvent = function(event) {
if(this.popup) {
this.triggerPopup(event);
}
event.preventDefault();
return false;
};
ButtonWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
var setTitle = this.set,
popupTitle = this.popup;
if(this.qualifyTiddlerTitles) {
var scopeId = this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
if(setTitle) {
setTitle = setTitle + "-" + scopeId;
}
if(popupTitle) {
popupTitle = popupTitle + "-" + scopeId;
}
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"] || (setTitle && changedTiddlers[setTitle]) || (popupTitle && changedTiddlers[popupTitle])) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.button = ButtonWidget;
})();

View File

@ -1,111 +0,0 @@
/*\
title: $:/core/modules/widgets/checkbox.js
type: application/javascript
module-type: widget
Implements the checkbox widget.
```
<$checkbox tag="done"/>
<$checkbox tiddler="HelloThere" tag="red"/>
<$checkbox tag="done">
<$view field="title" format="link"/>
</$checkbox>
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var CheckboxWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
CheckboxWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
this.tagName = this.renderer.getAttribute("tag");
this["class"] = this.renderer.getAttribute("class");
// Compute classes
var classes = ["tw-checkbox"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
// Create the checkbox and span elements
var nodeCheckbox = {
type: "element",
tag: "input",
attributes: {
type: {type: "string", value: "checkbox"}
}
},
nodeSpan = {
type: "element",
tag: "span",
children: this.renderer.parseTreeNode.children
};
// Set the state of the checkbox
if(this.getValue()) {
$tw.utils.addAttributeToParseTreeNode(nodeCheckbox,"checked","true");
}
// Set the return element
this.tag = "label";
this.attributes ={"class": classes.join(" ")};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[nodeCheckbox,nodeSpan]);
this.events = [{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}];
};
CheckboxWidget.prototype.getValue = function() {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
return tiddler ? tiddler.hasTag(this.tagName) : false;
};
CheckboxWidget.prototype.handleChangeEvent = function(event) {
var checked = this.children[0].domNode.checked,
tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(tiddler && tiddler.hasTag(this.tagName) !== checked) {
var newTags = tiddler.fields.tags.slice(0),
pos = newTags.indexOf(this.tagName);
if(pos !== -1) {
newTags.splice(pos,1);
}
if(checked) {
newTags.push(this.tagName);
}
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{tags: newTags}));
}
};
CheckboxWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.tiddler || changedAttributes.tag || changedAttributes["class"]) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// Update the checkbox if necessary
if(changedTiddlers[this.tiddlerTitle]) {
this.children[0].domNode.checked = this.getValue();
}
// Refresh children
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.checkbox = CheckboxWidget;
})();

View File

@ -1,59 +0,0 @@
/*\
title: $:/core/modules/widgets/count.js
type: application/javascript
module-type: widget
Implements the count widget that displays the number of tiddlers that match a filter
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var CountWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Execute the filter to get the initial count
this.executeFilter();
// Generate child nodes
this.generate();
};
CountWidget.prototype.executeFilter = function() {
// Get attributes
this.filter = this.renderer.getAttribute("filter");
// Execute the filter
if(this.filter) {
this.currentCount = this.renderer.renderTree.wiki.filterTiddlers(this.filter,this.renderer.tiddlerTitle).length;
} else {
this.currentCount = undefined;
}
};
CountWidget.prototype.generate = function() {
// Set the element
this.tag = "span";
this.attributes = {};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[
{type: "text", text: this.currentCount !== undefined ? this.currentCount.toString() : ""}
]);
};
CountWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Re-execute the filter to get the count
var oldCount = this.currentCount;
this.executeFilter();
if(this.currentCount !== oldCount) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
}
};
exports.count = CountWidget;
})();

View File

@ -1,53 +0,0 @@
/*\
title: $:/core/modules/widgets/datauri.js
type: application/javascript
module-type: widget
The datauri widget displays the contents of a tiddler as a data URI.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var DataUriWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
DataUriWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
// Compose the data URI
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
uri = "";
if(tiddler) {
var type = tiddler.fields.type || "text/vnd.tiddlywiki",
typeInfo = $tw.config.contentTypeInfo[type],
isBase64 = typeInfo && typeInfo.encoding === "base64",
parts = ["data:"];
parts.push(type);
parts.push(isBase64 ? ";base64" : "");
parts.push(",");
parts.push(isBase64 ? tiddler.fields.text : encodeURIComponent(tiddler.fields.text));
uri = parts.join("");
}
// Set the element
this.tag = "pre";
this.attributes = {
"class": "tw-data-uri"
};
// Create the renderers for the wrapper and the children
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: uri
}]);
};
exports.datauri = DataUriWidget;
})();

View File

@ -1,87 +0,0 @@
/*\
title: $:/core/modules/widgets/edit/edit.js
type: application/javascript
module-type: widget
The edit widget uses editor plugins to edit tiddlers of different types.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var EditWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Initialise the editors if they've not been done already
if(!this.editors) {
EditWidget.prototype.editors = {};
$tw.modules.applyMethods("editor",this.editors);
}
// Generate child nodes
this.generate();
};
EditWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
this.fieldName = this.renderer.getAttribute("field");
this.indexName = this.renderer.getAttribute("index");
if(!this.fieldName && !this.indexName) {
this.fieldName = "text";
}
// Choose the editor to use
// TODO: Tiddler field modules should be able to specify a field type from which the editor is derived
this.editorName = this.chooseEditor();
var Editor = this.editors[this.editorName];
// Instantiate the editor
this.editor = new Editor(this,this.tiddlerTitle,this.fieldName,this.indexName);
// Ask the editor to create the widget element
this.editor.render();
};
/*
Return the name of the editor that should handle this tiddler field
*/
EditWidget.prototype.chooseEditor = function() {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(this.fieldName === "text" && tiddler && tiddler.fields.type && this.editors[tiddler.fields.type]) {
return tiddler.fields.type;
}
return "text/vnd.tiddlywiki";
};
EditWidget.prototype.postRenderInDom = function() {
if(this.editor && this.editor.postRenderInDom) {
this.editor.postRenderInDom();
}
};
EditWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// We'll completely regenerate ourselves if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.format || this.chooseEditor() !== this.editorName) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) {
// Refresh the editor if our tiddler has changed
if(this.editor && this.editor.refreshInDom) {
this.editor.refreshInDom(changedTiddlers);
}
} else {
// Otherwise, just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.edit = EditWidget;
})();

View File

@ -1,318 +0,0 @@
/*\
title: $:/core/modules/widgets/edit/editors/bitmapeditor.js
type: application/javascript
module-type: editor
A bitmap editor
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Default images sizes
var DEFAULT_IMAGE_WIDTH = 300,
DEFAULT_IMAGE_HEIGHT = 185;
// The elements of the editor UI
var DOM_CANVAS = 0,
DOM_WIDTH = 1,
DOM_HEIGHT = 2;
var BitmapEditor = function(editWidget,tiddlerTitle,fieldName,indexName) {
this.editWidget = editWidget;
this.tiddlerTitle = tiddlerTitle;
this.fieldName = fieldName;
};
BitmapEditor.prototype.render = function() {
// Set the element details
this.editWidget.tag = "div";
this.editWidget.attributes = {
"class": "tw-edit-bitmapeditor-wrapper"
};
var children = [{
type: "element",
tag: "canvas",
attributes: {
"class": {type: "string", value: "tw-edit-bitmapeditor"}
},
events: [{
name: "touchstart",
handlerObject: this,
handlerMethod: "handleTouchStartEvent"
},{
name: "touchmove",
handlerObject: this,
handlerMethod: "handleTouchMoveEvent"
},{
name: "touchend",
handlerObject: this,
handlerMethod: "handleTouchEndEvent"
},{
name: "mousedown",
handlerObject: this,
handlerMethod: "handleMouseDownEvent"
},{
name: "mousemove",
handlerObject: this,
handlerMethod: "handleMouseMoveEvent"
},{
name: "mouseup",
handlerObject: this,
handlerMethod: "handleMouseUpEvent"
}]
},{
type: "element",
tag: "input",
attributes: {
"class": {type: "string", value: "tw-edit-bitmapeditor-width"},
"type": {type: "string", value: "number"},
"value": {type: "string", value: ""}
},
events: [{
name: "change",
handlerObject: this,
handlerMethod: "handleWidthChangeEvent"
}]
},{
type: "element",
tag: "input",
attributes: {
"class": {type: "string", value: "tw-edit-bitmapeditor-height"},
"type": {type: "string", value: "number"},
"value": {type: "string", value: ""}
},
events: [{
name: "change",
handlerObject: this,
handlerMethod: "handleHeightChangeEvent"
}]
}];
this.editWidget.children = this.editWidget.renderer.renderTree.createRenderers(this.editWidget.renderer,children);
};
BitmapEditor.prototype.postRenderInDom = function() {
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
canvas = this.getDomNode(DOM_CANVAS),
currImage = new Image();
// Set up event handlers for loading the image
var self = this;
currImage.onload = function() {
// Copy the image to the on-screen canvas
self.initCanvas(canvas,currImage.width,currImage.height,currImage);
// And also copy the current bitmap to the off-screen canvas
self.currCanvas = self.editWidget.renderer.renderTree.document.createElement("canvas");
self.initCanvas(self.currCanvas,currImage.width,currImage.height,currImage);
// Set the width and height input boxes
self.updateSize();
};
currImage.onerror = function() {
// Set the on-screen canvas size and clear it
self.initCanvas(canvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
// Set the off-screen canvas size and clear it
self.currCanvas = self.editWidget.renderer.renderTree.document.createElement("canvas");
self.initCanvas(self.currCanvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
// Set the width and height input boxes
self.updateSize();
}
// Get the current bitmap into an image object
currImage.src = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
};
BitmapEditor.prototype.initCanvas = function(canvas,width,height,image) {
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
if(image) {
ctx.drawImage(image,0,0);
} else {
ctx.fillStyle = "#fff";
ctx.fillRect(0,0,canvas.width,canvas.height);
}
}
BitmapEditor.prototype.getDomNode = function(index) {
return this.editWidget.renderer.domNode.childNodes[index];
};
/*
** Update the input boxes with the actual size of the canvas
*/
BitmapEditor.prototype.updateSize = function() {
this.getDomNode(DOM_WIDTH).value = this.currCanvas.width;
this.getDomNode(DOM_HEIGHT).value = this.currCanvas.height;
};
/*
** Change the size of the canvas, preserving the current image
*/
BitmapEditor.prototype.changeCanvasSize = function(newWidth,newHeight) {
// Create and size a new canvas
var newCanvas = this.editWidget.renderer.renderTree.document.createElement("canvas");
this.initCanvas(newCanvas,newWidth,newHeight);
// Copy the old image
var ctx = newCanvas.getContext("2d");
ctx.drawImage(this.currCanvas,0,0);
// Set the new canvas as the current one
this.currCanvas = newCanvas;
// Set the size of the onscreen canvas
var canvas = this.getDomNode(DOM_CANVAS);
canvas.width = newWidth;
canvas.height = newHeight;
// Paint the onscreen canvas with the offscreen canvas
ctx = canvas.getContext("2d");
ctx.drawImage(this.currCanvas,0,0);
};
BitmapEditor.prototype.handleWidthChangeEvent = function(event) {
// Get the new width
var newWidth = parseInt(this.getDomNode(DOM_WIDTH).value,10);
// Update if necessary
if(newWidth > 0 && newWidth !== this.currCanvas.width) {
this.changeCanvasSize(newWidth,this.currCanvas.height);
}
// Update the input controls
this.updateSize();
};
BitmapEditor.prototype.handleHeightChangeEvent = function(event) {
// Get the new width
var newHeight = parseInt(this.getDomNode(DOM_HEIGHT).value,10);
// Update if necessary
if(newHeight > 0 && newHeight !== this.currCanvas.height) {
this.changeCanvasSize(this.currCanvas.width,newHeight);
}
// Update the input controls
this.updateSize();
};
BitmapEditor.prototype.handleTouchStartEvent = function(event) {
this.brushDown = true;
this.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
event.stopPropagation();
return false;
};
BitmapEditor.prototype.handleTouchMoveEvent = function(event) {
if(this.brushDown) {
this.strokeMove(event.touches[0].clientX,event.touches[0].clientY);
}
event.preventDefault();
event.stopPropagation();
return false;
};
BitmapEditor.prototype.handleTouchEndEvent = function(event) {
if(this.brushDown) {
this.brushDown = false;
this.strokeEnd();
}
event.preventDefault();
event.stopPropagation();
return false;
};
BitmapEditor.prototype.handleMouseDownEvent = function(event) {
this.strokeStart(event.clientX,event.clientY);
this.brushDown = true;
event.preventDefault();
event.stopPropagation();
return false;
};
BitmapEditor.prototype.handleMouseMoveEvent = function(event) {
if(this.brushDown) {
this.strokeMove(event.clientX,event.clientY);
event.preventDefault();
event.stopPropagation();
return false;
}
return true;
};
BitmapEditor.prototype.handleMouseUpEvent = function(event) {
if(this.brushDown) {
this.brushDown = false;
this.strokeEnd();
event.preventDefault();
event.stopPropagation();
return false;
}
return true;
};
BitmapEditor.prototype.adjustCoordinates = function(x,y) {
var canvas = this.getDomNode(DOM_CANVAS),
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.getDomNode(DOM_CANVAS),
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.strokeStyle = "#ff0";
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.getDomNode(DOM_CANVAS),
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.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(tiddler) {
// data URIs look like "data:<type>;base64,<text>"
var dataURL = this.getDomNode(DOM_CANVAS).toDataURL(tiddler.fields.type,1.0),
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.editWidget.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
}
};
/*
Note that the bitmap editor intentionally doesn't have a refreshInDom method to avoid the situation where a bitmap being editted is modified externally
*/
exports["image/jpg"] = BitmapEditor;
exports["image/jpeg"] = BitmapEditor;
exports["image/png"] = BitmapEditor;
exports["image/gif"] = BitmapEditor;
})();

View File

@ -1,219 +0,0 @@
/*\
title: $:/core/modules/widgets/edit/editors/texteditor.js
type: application/javascript
module-type: editor
A plain text editor
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var MIN_TEXT_AREA_HEIGHT = 100;
var TextEditor = function(editWidget,tiddlerTitle,fieldName,indexName) {
this.editWidget = editWidget;
this.tiddlerTitle = tiddlerTitle;
this.fieldName = fieldName;
this.indexName = indexName;
};
/*
Get the tiddler being edited and current value
*/
TextEditor.prototype.getEditInfo = function() {
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
value;
if(this.fieldName) {
// Get the current tiddler and the field name
if(tiddler) {
// If we've got a tiddler, the value to display is the field string value
value = tiddler.getFieldString(this.fieldName);
} else {
// Otherwise, we need to construct a default value for the editor
switch(this.fieldName) {
case "text":
value = "Type the text for the tiddler '" + this.tiddlerTitle + "'";
break;
case "title":
value = this.tiddlerTitle;
break;
default:
value = "";
break;
}
value = this.editWidget.renderer.getAttribute("default",value);
}
} else {
value = this.editWidget.renderer.renderTree.wiki.extractTiddlerDataItem(this.tiddlerTitle,this.indexName,this.editWidget.renderer.getAttribute("default"));
}
return {tiddler: tiddler, value: value};
};
TextEditor.prototype.render = function() {
// Get the initial value of the editor
var editInfo = this.getEditInfo();
// Create the editor nodes
var node = {
type: "element",
attributes: {}
};
// Get the edit type associated with this field
var type = "input";
if(this.fieldName === "text") {
type = "textarea";
} else {
var fieldModule = $tw.Tiddler.fieldModules[this.fieldName];
if(fieldModule && fieldModule.editType) {
type = fieldModule.editType;
}
}
var type = this.editWidget.renderer.getAttribute("type",type);
switch(type) {
case "textarea":
node.tag = "textarea";
node.children = [{
type: "text",
text: editInfo.value
}];
break;
case "color":
node.tag = "input";
node.attributes.type = {type: "string", value: "color"};
node.attributes.value = {type: "string", value: editInfo.value};
break;
case "search":
node.tag = "input";
node.attributes.type = {type: "string", value: "search"};
node.attributes.value = {type: "string", value: editInfo.value};
break;
default: // "input"
node.tag = "input";
node.attributes.type = {type: "string", value: "text"};
node.attributes.value = {type: "string", value: editInfo.value};
break;
}
node.events = [
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
{name: "blur", handlerObject: this, handlerMethod: "handleBlurEvent"},
{name: "input", handlerObject: this, handlerMethod: "handleInputEvent"}
];
// Add a placeholder if specified
if(this.editWidget.renderer.hasAttribute("placeholder")) {
node.attributes.placeholder = {type: "string", value: this.editWidget.renderer.getAttribute("placeholder")};
}
// Set the element details
this.editWidget.tag = this.editWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
this.editWidget.attributes = {
"class": "tw-edit-texteditor"
};
if(this.editWidget.renderer.hasAttribute("class")) {
this.editWidget.attributes["class"] += " " + this.editWidget.renderer.getAttribute("class");
}
if(this.editWidget.renderer.hasAttribute("style")) {
this.editWidget.attributes.style = this.editWidget.attributes.style || "";
this.editWidget.attributes.style += this.editWidget.renderer.getAttribute("style");
}
this.editWidget.children = this.editWidget.renderer.renderTree.createRenderers(this.editWidget.renderer,[node]);
};
TextEditor.prototype.setFocus = function() {
if(this.editWidget.renderer.hasAttribute("focusSet")) {
var title = this.editWidget.renderer.getAttribute("focusSet");
if(this.editWidget.renderer.getAttribute("qualifyTiddlerTitles") === "yes") {
title = title + "-" + this.editWidget.renderer.renderTree.getContextScopeId(this.editWidget.renderer.parentRenderer);
}
$tw.popup.triggerPopup({
domNode: this.editWidget.renderer.domNode,
title: title,
wiki: this.editWidget.renderer.renderTree.wiki,
force: true
});
}
};
TextEditor.prototype.handleFocusEvent = function(event) {
// this.saveChanges();
// this.fixHeight();
this.setFocus();
return true;
};
TextEditor.prototype.handleBlurEvent = function(event) {
// this.saveChanges();
return true;
};
TextEditor.prototype.handleInputEvent = function(event) {
this.saveChanges();
this.fixHeight();
return true;
};
TextEditor.prototype.saveChanges = function() {
var text = this.editWidget.children[0].domNode.value
if(this.fieldName) {
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(!tiddler) {
tiddler = new $tw.Tiddler({title: this.tiddlerTitle});
}
var oldValue = tiddler.getFieldString(this.fieldName);
if(text !== oldValue) {
var update = {};
update[this.fieldName] = text;
this.editWidget.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
}
} else {
var data = this.editWidget.renderer.renderTree.wiki.getTiddlerData(this.tiddlerTitle,{});
if(data[this.indexName] !== text) {
data[this.indexName] = text;
this.editWidget.renderer.renderTree.wiki.setTiddlerData(this.tiddlerTitle,data);
}
}
};
TextEditor.prototype.fixHeight = function() {
var self = this;
if(this.editWidget.children[0].domNode && this.editWidget.children[0].domNode.type === "textarea") {
$tw.utils.nextTick(function() {
// Resize the textarea to fit its content
var textarea = self.editWidget.children[0].domNode,
scrollPosition = $tw.utils.getScrollPosition(),
scrollTop = scrollPosition.y;
// Set its height to auto so that it snaps to the correct height
textarea.style.height = "auto";
// Calculate the revised height
var newHeight = Math.max(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight,MIN_TEXT_AREA_HEIGHT);
// Only try to change the height if it has changed
if(newHeight !== textarea.offsetHeight) {
textarea.style.height = newHeight + "px";
// Make sure that the dimensions of the textarea are recalculated
$tw.utils.forceLayout(textarea);
// Check that the scroll position is still visible before trying to scroll back to it
scrollTop = Math.min(scrollTop,self.editWidget.renderer.renderTree.document.body.scrollHeight - window.innerHeight);
window.scrollTo(scrollPosition.x,scrollTop);
}
});
}
};
TextEditor.prototype.postRenderInDom = function() {
this.fixHeight();
};
TextEditor.prototype.refreshInDom = function() {
if(this.editWidget.renderer.renderTree.document.activeElement !== this.editWidget.children[0].domNode) {
var editInfo = this.getEditInfo();
this.editWidget.children[0].domNode.value = editInfo.value;
}
// Fix the height if needed
this.fixHeight();
};
exports["text/vnd.tiddlywiki"] = TextEditor;
exports["text/plain"] = TextEditor;
})();

View File

@ -1,51 +0,0 @@
/*\
title: $:/core/modules/widgets/encrypt.js
type: application/javascript
module-type: widget
Implements the encrypt widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var EncryptWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
EncryptWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.filter = this.renderer.getAttribute("filter");
// Check whether we've got an encryption password
var isEncrypted = $tw.crypto.hasPassword();
// Encrypt the filtered tiddlers
var tiddlers = this.renderer.renderTree.wiki.filterTiddlers(this.filter),
json = {},
self = this;
$tw.utils.each(tiddlers,function(title) {
var tiddler = self.renderer.renderTree.wiki.getTiddler(title),
jsonTiddler = {};
for(var f in tiddler.fields) {
jsonTiddler[f] = tiddler.getFieldString(f);
}
json[title] = jsonTiddler;
});
var encryptedText = $tw.utils.htmlEncode($tw.crypto.encrypt(JSON.stringify(json)));
// Set the return element
this.tag = "pre";
this.attributes ={"class": "tw-encrypt"};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: encryptedText
}]);
};
exports.encrypt = EncryptWidget;
})();

View File

@ -1,37 +0,0 @@
/*\
title: $:/core/modules/widgets/error.js
type: application/javascript
module-type: widget
The error widget displays an error message.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ErrorWidget = function(renderer,errorMessage) {
// Save state
this.renderer = renderer;
this.errorMessage = errorMessage;
// Generate child nodes
this.generate();
};
ErrorWidget.prototype.generate = function() {
// Set the element details
this.tag = "span";
this.attributes = {
"class": "tw-error-widget"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: this.errorMessage
}]);
};
exports.error = ErrorWidget;
})();

View File

@ -1,87 +0,0 @@
/*\
title: $:/core/modules/widgets/fieldmangler.js
type: application/javascript
module-type: widget
The fieldmangler widget modifies the fields of the current tiddler in response to messages.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var FieldManglerWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
FieldManglerWidget.prototype.generate = function() {
var self = this;
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
// Set the element
this.tag = "div";
this.attributes = {
"class": "tw-fieldmangler"
};
// Set event handlers
this.events = [
{name: "tw-remove-field", handlerObject: this, handlerMethod: "handleRemoveFieldEvent"},
{name: "tw-add-field", handlerObject: this, handlerMethod: "handleAddFieldEvent"},
{name: "tw-remove-tag", handlerObject: this, handlerMethod: "handleRemoveTagEvent"},
{name: "tw-add-tag", handlerObject: this, handlerMethod: "handleAddTagEvent"}
];
// Render the children
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
};
FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
deletion = {};
deletion[event.param] = undefined;
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,deletion));
return true;
};
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(tiddler && typeof event.param === "string" && event.param !== "" && !$tw.utils.hop(tiddler.fields,event.param)) {
var addition = {};
addition[event.param] = "";
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,addition));
}
return true;
};
FieldManglerWidget.prototype.handleRemoveTagEvent = function(event) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(tiddler && tiddler.fields.tags) {
var p = tiddler.fields.tags.indexOf(event.param);
if(p !== -1) {
var modification = {};
modification.tags = (tiddler.fields.tags || []).slice(0);
modification.tags.splice(p,1);
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
}
}
return true;
};
FieldManglerWidget.prototype.handleAddTagEvent = function(event) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
if(tiddler && typeof event.param === "string" && event.param !== "") {
var modification = {};
modification.tags = (tiddler.fields.tags || []).slice(0);
$tw.utils.pushTop(modification.tags,event.param);
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
}
return true;
};
exports.fieldmangler = FieldManglerWidget;
})();

View File

@ -1,94 +0,0 @@
/*\
title: $:/core/modules/widgets/fields.js
type: application/javascript
module-type: widget
The fields widget displays the fields of a tiddler through a text substitution template.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var FieldsWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
FieldsWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
this.template = this.renderer.getAttribute("template");
this.exclude = this.renderer.getAttribute("exclude");
this.stripTitlePrefix = this.renderer.getAttribute("stripTitlePrefix","no") === "yes";
// Get the tiddler we're displaying
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
// Get the exclusion list
var exclude;
if(this.exclude) {
exclude = this.exclude.split(" ");
} else {
exclude = ["text"];
}
// Compose the template
var text = [];
if(this.template && tiddler) {
var fields = [];
for(var fieldName in tiddler.fields) {
if(exclude.indexOf(fieldName) === -1) {
fields.push(fieldName);
}
}
fields.sort();
for(var f=0; f<fields.length; f++) {
fieldName = fields[f];
if(exclude.indexOf(fieldName) === -1) {
var row = this.template,
value = tiddler.getFieldString(fieldName);
if(this.stripTitlePrefix && fieldName === "title") {
var reStrip = /^\{[^\}]+\}(.+)/mg,
reMatch = reStrip.exec(value);
if(reMatch) {
value = reMatch[1];
}
}
row = row.replace("$name$",fieldName);
row = row.replace("$value$",value);
row = row.replace("$encoded_value$",$tw.utils.htmlEncode(value));
text.push(row)
}
}
}
// Set the element
this.tag = "pre";
this.attributes = {
"class": "tw-fields"
};
// Set up the attributes for the wrapper element
var classes = [];
if(this.renderer.hasAttribute("class")) {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
if(classes.length > 0) {
this.attributes["class"] = classes.join(" ");
}
if(this.renderer.hasAttribute("style")) {
this.attributes.style = this.renderer.getAttribute("style");
}
if(this.renderer.hasAttribute("tooltip")) {
this.attributes.title = this.renderer.getAttribute("tooltip");
}
// Create the renderers for the wrapper and the children
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: text.join("")
}]);
};
exports.fields = FieldsWidget;
})();

View File

@ -1,109 +0,0 @@
/*\
title: $:/core/modules/widgets/grid.js
type: application/javascript
module-type: widget
The grid widget.
This example renders a table made up of tiddlers titled `MySheet_A_1`, `MySheet_A_2`, `MySheet_A_3`, ... , `MySheet_B_1`, `MySheet_B_2`, `MySheet_B_3` etc.
```
<$grid prefix="MySheet" rows=20 cols=20/>
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var GridWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate widget elements
this.generate();
};
GridWidget.prototype.generate = function() {
// Get our attributes
this.prefix = this.renderer.getAttribute("prefix","Grid");
this.rows = parseInt(this.renderer.getAttribute("rows","10"),10);
this.cols = parseInt(this.renderer.getAttribute("cols","10"),10);
this["class"] = this.renderer.getAttribute("class");
// Set up the classes
var classes = ["tw-grid-frame"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
// Create the grid table element
this.tag = "div";
this.attributes = {
"class": classes.join(" ")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.generateTable());
};
GridWidget.prototype.generateTable = function() {
var rows = [];
for(var row=0; row<this.rows; row++) {
var tr = {
type: "element",
tag: "tr",
children: []
};
rows.push(tr);
for(var col=0; col<this.cols; col++) {
var td = {
type: "element",
tag: "td",
children: [{
type: "element",
tag: "$transclude",
attributes: {
title: {type: "string", value: this.getTableCellTitle(col,row)}
}
}]
};
tr.children.push(td);
}
}
return [{
type: "element",
tag: "table",
children: [{
type: "element",
tag: "tbody",
children: rows
}]
}];
};
GridWidget.prototype.getTableCellTitle = function(col,row) {
var c = String.fromCharCode(col % 26 + "A".charCodeAt(0));
col = Math.floor(col/26);
while(col>0) {
c = String.fromCharCode(col % 26 + "A".charCodeAt(0) - 1) + c;
col = Math.floor(col/26);
}
return this.prefix + "_" + c + "_" + (row + 1);
};
GridWidget.prototype.postRenderInDom = function() {
};
GridWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Reexecute the widget if any of our attributes have changed
if(true) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
}
};
exports.grid = GridWidget;
})();

View File

@ -1,256 +0,0 @@
/*\
title: $:/core/modules/widgets/import.js
type: application/javascript
module-type: widget
Implements the import widget.
```
<$import>
Import using the "browse..." button or drag files onto this text
</$import>
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ImportWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
ImportWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.browse = this.renderer.getAttribute("browse","yes");
this["class"] = this.renderer.getAttribute("class");
// Compute classes
var classes = ["tw-import"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
// Create the file input and container elements
var fileInput = {
type: "element",
tag: "input",
attributes: {
type: {type: "string", value: "file"},
style: {type: "string", value: this.browse === "no" ? "display: none;" : "display: block;"}
},
events: [{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}]
},
container = {
type: "element",
tag: "div",
children: this.renderer.parseTreeNode.children,
events: [
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"},
{name: "paste", handlerObject: this, handlerMethod: "handlePasteEvent"}]
};
// Set the return element
this.tag = "div";
this.attributes = {
"class": classes.join(" ")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[fileInput,container]);
};
ImportWidget.prototype.handleChangeEvent = function(event) {
event.stopPropagation();
this.importFiles(event.target.files);
};
ImportWidget.prototype.handleDragEnterEvent = function(event) {
// We count enter/leave events
this.dragEnterCount = (this.dragEnterCount || 0) + 1;
// If we're entering for the first time we need to apply highlighting
if(this.dragEnterCount === 1) {
$tw.utils.addClass(this.renderer.domNode,"tw-dragover");
}
// Tell the browser that we're ready to handle the drop
event.preventDefault();
// Tell the browser not to ripple the drag up to any parent drop handlers
event.stopPropagation();
};
ImportWidget.prototype.handleDragOverEvent = function(event) {
// Tell the browser that we're still interested in the drop
event.preventDefault();
event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy
};
ImportWidget.prototype.handleDragLeaveEvent = function(event) {
// Reduce the enter count
this.dragEnterCount = (this.dragEnterCount || 0) - 1;
// Remove highlighting if we're leaving externally
if(this.dragEnterCount <= 0) {
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
}
};
ImportWidget.prototype.handleDropEvent = function(event) {
var dataTransfer = event.dataTransfer;
// Reset the enter count
this.dragEnterCount = 0;
// Remove highlighting
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
// Try to import the various data types we understand
this.importData(dataTransfer);
// Import any files in the drop
this.importFiles(dataTransfer.files);
// Tell the browser that we handled the drop
event.preventDefault();
// Stop the drop ripple up to any parent handlers
event.stopPropagation();
};
ImportWidget.prototype.handlePasteEvent = function(event) {
// Let the browser handle it if we're in a textarea or input box
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1) {
var self = this,
items = event.clipboardData.items;
// Enumerate the clipboard items
for(var t = 0; t<items.length; t++) {
var item = items[t];
if(item.kind === "file") {
// Import any files
var file = item.getAsFile();
this.importFiles([file]);
} else if(item.kind === "string") {
// Create tiddlers from string items
item.getAsString(function(str) {
var fields = {
title: self.generateTitle("Untitled"),
text: str
};
self.storeTiddler(fields);
self.openTiddler(fields.title);
});
}
}
// Tell the browser that we've handled the paste
event.stopPropagation();
event.preventDefault();
}
};
ImportWidget.prototype.openTiddler = function(title) {
$tw.utils.dispatchCustomEvent(this.renderer.domNode,"tw-navigate",{
navigateTo: title,
navigateFromNode: this.renderer.domNode,
navigateFromClientRect: this.renderer.domNode.getBoundingClientRect()
});
};
ImportWidget.prototype.importData = function(dataTransfer) {
for(var t=0; t<this.importDataTypes.length; t++) {
var dataType = this.importDataTypes[t];
var data = dataTransfer.getData(dataType.type);
if(data !== "") {
var fields = dataType.handler(data);
if(!fields.title) {
fields.title = this.generateTitle("Untitled");
}
this.storeTiddler(fields);
this.openTiddler(fields.title);
return;
}
};
};
ImportWidget.prototype.importDataTypes = [
{type: "text/vnd.tiddler", handler: function(data) {
return JSON.parse(data);
}},
{type: "text/plain", handler: function(data) {
return {
text: data
};
}},
{type: "text/uri-list", handler: function(data) {
return {
text: data
};
}}
];
ImportWidget.prototype.importFiles = function(files) {
var self = this,
importFile = function(file) {
// Get the type, falling back to the filename extension
var type = file.type;
if(type === "" || !type) {
var dotPos = file.name.lastIndexOf(".");
if(dotPos !== -1) {
var fileExtensionInfo = $tw.config.fileExtensionInfo[file.name.substr(dotPos)];
if(fileExtensionInfo) {
type = fileExtensionInfo.type;
}
}
}
// Figure out if we're reading a binary file
var contentTypeInfo = $tw.config.contentTypeInfo[type],
isBinary = contentTypeInfo ? contentTypeInfo.encoding === "base64" : false;
// Create the FileReader
var reader = new FileReader();
reader.onload = function(event) {
// Deserialise the file contents
var fields = {
title: file.name || "Untitled",
type: type};
// Are we binary?
if(isBinary) {
var commaPos = event.target.result.indexOf(",");
if(commaPos !== -1) {
fields.text = event.target.result.substr(commaPos+1);
self.storeTiddler(fields);
self.openTiddler(fields.title);
}
} else {
var tiddlers = self.renderer.renderTree.wiki.deserializeTiddlers(type,event.target.result,fields);
if(!tiddlers) {
console.log("No tiddlers found in file ",file.name);
} else {
$tw.utils.each(tiddlers,function(tiddlerFields) {
tiddlerFields.title = self.generateTitle(tiddlerFields.title);
self.storeTiddler(tiddlerFields);
self.openTiddler(tiddlerFields.title);
});
}
}
};
if(isBinary) {
reader.readAsDataURL(file);
} else {
reader.readAsText(file);
}
};
for(var f=0; f<files.length; f++) {
importFile(files[f]);
};
};
ImportWidget.prototype.storeTiddler = function(fields) {
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields,this.renderer.renderTree.wiki.getModificationFields()));
};
ImportWidget.prototype.generateTitle = function(baseTitle) {
var c = 0;
do {
var title = baseTitle + (c ? " " + (c + 1) : "");
c++;
} while(this.renderer.renderTree.wiki.tiddlerExists(title));
return title;
};
exports.import = ImportWidget;
})();

View File

@ -1,115 +0,0 @@
/*\
title: $:/core/modules/widgets/info.js
type: application/javascript
module-type: widget
Implements the info widget that displays various information about a specified tiddler.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var InfoWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
InfoWidget.types = {};
InfoWidget.types.changecount = function(options) {
var text = options.wiki.getChangeCount(options.widget.renderer.tiddlerTitle);
return [{type: "text", text: text}];
};
InfoWidget.types.currentfield = function(options) {
var fieldName = options.widget.renderer.renderTree.getContextVariable(options.widget.renderer,"field","text");
return [{type: "text", text: fieldName}];
};
var FIELD_DESCRIPTION_PREFIX = "$:/docs/fields/";
InfoWidget.types.currentfielddescription = function(options) {
var fieldName = options.widget.renderer.renderTree.getContextVariable(options.widget.renderer,"field","text"),
descriptionTitle = FIELD_DESCRIPTION_PREFIX + fieldName;
return [{
type: "element",
tag: "$transclude",
isBlock: false,
attributes: {
title: {type: "string", value: descriptionTitle}
}
}];
};
var MODULE_TYPE_DESCRIPTION_PREFIX = "$:/docs/moduletypes/";
/*
Return a list of all the currently loaded modules grouped by type
*/
InfoWidget.types.modules = function(options) {
var output = [],
types = [];
// Collect and sort the module types
$tw.utils.each($tw.modules.types,function(moduleInfo,type) {
types.push(type);
});
types.sort();
// Output the module types
$tw.utils.each(types,function(moduleType) {
// Heading
output.push({type: "element", tag: "h2", children: [
{type: "text", text: moduleType}
]})
// Description
output.push({
type: "element",
tag: "$transclude",
isBlock: false,
attributes: {
title: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType}
}
});
// List each module
var list = {type: "element", tag: "ul", children: []},
modules = [];
$tw.utils.each($tw.modules.types[moduleType],function(moduleInfo,moduleName) {
var listItem = {type: "element", tag: "li", children: [
{type: "element", tag: "$link", attributes: {
to: {type: "string", value: moduleName}
}, children: [
{type: "text", text: moduleName}
]}
]}
list.children.push(listItem);
});
output.push(list);
});
return output;
};
InfoWidget.prototype.generate = function() {
// Get attributes
this.type = this.renderer.getAttribute("type","changecount").toLowerCase();
// Get the appropriate value for the current tiddler
var value = [],
fn = InfoWidget.types[this.type];
if(fn) {
value = fn({
wiki: this.renderer.renderTree.wiki,
widget: this
});
}
// Set the element
this.tag = "span";
this.attributes = {};
this.children = this.renderer.renderTree.createRenderers(this.renderer,value);
};
exports.info = InfoWidget;
})();

View File

@ -1,203 +0,0 @@
/*\
title: $:/core/modules/widgets/link.js
type: application/javascript
module-type: widget
Implements the link widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var isLinkExternal = function(to) {
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data):[^\s'"]+(?:\/|\b)/i;
return externalRegExp.test(to);
};
var LinkWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
LinkWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.to = this.renderer.getAttribute("to",this.renderer.tiddlerTitle);
this.hover = this.renderer.getAttribute("hover");
this.qualifyHoverTitles = this.renderer.getAttribute("qualifyHoverTitles");
// Qualify the hover tiddler title if needed
if(this.qualifyHoverTitles) {
this.hover = this.hover + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
}
// Determine the default link characteristics
this.isExternal = isLinkExternal(this.to);
if(!this.isExternal) {
this.isMissing = !this.renderer.renderTree.wiki.tiddlerExists(this.to);
this.isShadow = this.renderer.renderTree.wiki.isShadowTiddler(this.to);
}
// Compose the link
var classes = ["tw-tiddlylink"]
if(this.isExternal) {
$tw.utils.pushTop(classes,"tw-tiddlylink-external");
} else {
$tw.utils.pushTop(classes,"tw-tiddlylink-internal");
if(this.isShadow) {
$tw.utils.pushTop(classes,"tw-tiddlylink-shadow");
}
if(this.isMissing && !this.isShadow) {
$tw.utils.pushTop(classes,"tw-tiddlylink-missing");
} else {
if(!this.isMissing) {
$tw.utils.pushTop(classes,"tw-tiddlylink-resolves");
}
}
}
var events = [
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
{name: "dragstart", handlerObject: this, handlerMethod: "handleDragStartEvent"},
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
];
if(this.hover) {
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
}
// Get the value of the tw-wikilinks configuration macro
var wikiLinksMacro = this.renderer.renderTree.findMacroDefinition(this.renderer.parentRenderer,"tw-wikilinks"),
useWikiLinks = wikiLinksMacro ? !(wikiLinksMacro.text.trim() === "no") : true;
// Set up the element
if(useWikiLinks) {
this.tag = "a";
this.attributes = {
"class": classes.join(" ")
};
if(this.isExternal) {
this.attributes.href = this.to;
} else {
var wikiLinkTemplateMacro = this.renderer.renderTree.findMacroDefinition(this.renderer.parentRenderer,"tw-wikilink-template"),
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.text.trim() : "#$uri_encoded$";
this.wikiLinkText = wikiLinkTemplate.replace("$uri_encoded$",encodeURIComponent(this.to));
this.wikiLinkText = this.wikiLinkText.replace("$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
this.attributes.href = this.wikiLinkText;
}
this.events = events;
} else {
this.tag = "span";
}
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
};
LinkWidget.prototype.handleClickEvent = function(event) {
if(isLinkExternal(this.to)) {
event.target.setAttribute("target","_blank");
return true;
} else {
var bounds = this.renderer.domNode.getBoundingClientRect();
$tw.utils.dispatchCustomEvent(event.target,"tw-navigate",{
navigateTo: this.to,
navigateFromNode: this,
navigateFromClientRect: {
top: bounds.top,
left: bounds.left,
width: bounds.width,
right: bounds.right,
bottom: bounds.bottom,
height: bounds.height
}
});
event.preventDefault();
event.stopPropagation();
return false;
}
};
LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
if(this.hover) {
$tw.popup.triggerPopup({
title: this.hover,
domNode: this.renderer.domNode,
wiki: this.renderer.renderTree.wiki
});
}
event.preventDefault();
return false;
};
LinkWidget.prototype.handleDragStartEvent = function(event) {
if(this.to) {
// Set the dragging class on the element being dragged
$tw.utils.addClass(event.target,"tw-tiddlylink-dragging");
// Create the drag image elements
this.dragImage = this.renderer.renderTree.document.createElement("div");
this.dragImage.className = "tw-tiddler-dragger";
var inner = this.renderer.renderTree.document.createElement("div");
inner.className = "tw-tiddler-dragger-inner";
inner.appendChild(this.renderer.renderTree.document.createTextNode(this.to));
this.dragImage.appendChild(inner);
this.renderer.renderTree.document.body.appendChild(this.dragImage);
// Astoundingly, we need to cover the dragger up: http://www.kryogenix.org/code/browser/custom-drag-image.html
var bounds = this.dragImage.firstChild.getBoundingClientRect(),
cover = this.renderer.renderTree.document.createElement("div");
cover.className = "tw-tiddler-dragger-cover";
cover.style.left = (bounds.left - 8) + "px";
cover.style.top = (bounds.top - 8) + "px";
cover.style.width = (bounds.width + 16) + "px";
cover.style.height = (bounds.height + 16) + "px";
this.dragImage.appendChild(cover);
// Set the data transfer properties
var dataTransfer = event.dataTransfer;
dataTransfer.effectAllowed = "copy";
dataTransfer.setDragImage(this.dragImage.firstChild,-16,-16);
dataTransfer.clearData();
dataTransfer.setData("text/vnd.tiddler",this.renderer.renderTree.wiki.getTiddlerAsJson(this.to));
dataTransfer.setData("text/plain",this.renderer.renderTree.wiki.getTiddlerText(this.to,""));
event.stopPropagation();
} else {
event.preventDefault();
}
};
LinkWidget.prototype.handleDragEndEvent = function(event) {
// Remove the dragging class on the element being dragged
$tw.utils.removeClass(event.target,"tw-tiddlylink-dragging");
// Delete the drag image element
if(this.dragImage) {
this.dragImage.parentNode.removeChild(this.dragImage);
}
};
LinkWidget.prototype.postRenderInDom = function() {
// Add the draggable attribute to links (we don't include it in the static HTML representation)
if(this.renderer.domNode.tagName === "A") {
this.renderer.domNode.setAttribute("draggable",true);
}
};
LinkWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.targetTitle && changedTiddlers[this.targetTitle]) {
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle));
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.to || changedAttributes.hover || (this.to && changedTiddlers[this.to]) || (this.hover && changedTiddlers[this.hover])) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.link = LinkWidget;
})();

View File

@ -1,71 +0,0 @@
/*\
title: $:/core/modules/widgets/linkcatcher.js
type: application/javascript
module-type: widget
Implements the linkcatcher widget. It intercepts navigation events from its children, preventing normal navigation, and instead stores the name of the target tiddler in the text reference specified in the `to` attribute.
Using the linkcatcher widget allows the linking mechanism to be used for tasks like selecting the current theme tiddler from a list.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var LinkCatcherWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
LinkCatcherWidget.prototype.generate = function() {
// Get our attributes
this.to = this.renderer.getAttribute("to");
this.message = this.renderer.getAttribute("message");
this.set = this.renderer.getAttribute("set");
this.setTo = this.renderer.getAttribute("setTo");
// Set the element
this.tag = "div";
this.attributes = {
"class": "tw-linkcatcher"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
this.events = [
{name: "tw-navigate", handlerObject: this, handlerMethod: "handleNavigateEvent"}
];
};
LinkCatcherWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
};
// Navigate to a specified tiddler
LinkCatcherWidget.prototype.handleNavigateEvent = function(event) {
if(this.to) {
this.renderer.renderTree.wiki.setTextReference(this.to,event.navigateTo,this.renderer.tiddlerTitle);
}
if(this.message) {
$tw.utils.dispatchCustomEvent(this.renderer.domNode,this.message,{
param: event.navigateTo,
tiddlerTitle: this.renderer.tiddlerTitle
});
}
if(this.set) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.set);
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: this.set, text: this.setTo}));
}
event.stopPropagation();
return false;
};
exports.linkcatcher = LinkCatcherWidget;
})();

View File

@ -1,408 +0,0 @@
/*\
title: $:/core/modules/widgets/list/list.js
type: application/javascript
module-type: widget
The list widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ListWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Initialise the listviews if they've not been done already
if(!this.listViews) {
ListWidget.prototype.listViews = {};
$tw.modules.applyMethods("listview",this.listViews);
}
// Generate widget elements
this.generate();
};
var typeInfoByType = {
plain: {
frame: {
block: "div", inline: "span"
},
member: {
block: "div", inline: "span"
}
},
ul: {
frame: {
block: "ul", inline: "ul"
},
member: {
block: "li", inline: "li"
}
},
ol: {
frame: {
block: "ol", inline: "ol"
},
member: {
block: "li", inline: "li"
}
}
};
ListWidget.prototype.generate = function() {
// Get our attributes
this.macro = this.renderer.getAttribute("macro");
this.type = this.renderer.getAttribute("type","plain");
this.itemClass = this.renderer.getAttribute("itemClass");
this.template = this.renderer.getAttribute("template");
this.editTemplate = this.renderer.getAttribute("editTemplate");
this.emptyMessage = this.renderer.getAttribute("emptyMessage");
this["class"] = this.renderer.getAttribute("class");
// Get our type information
this.typeInfo = typeInfoByType[this.type] || typeInfoByType.plain;
// Set up the classes
var classes = ["tw-list-frame"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
// Get the list of tiddlers object
this.getTiddlerList();
// Create the list
var listMembers = [];
if(this.list.length === 0) {
// Check for an empty list
listMembers = [this.getEmptyMessage()];
} else {
// Create the list
for(var t=0; t<this.list.length; t++) {
listMembers.push(this.createListElement(this.list[t]));
}
}
// Create the list frame element
this.tag = this.renderer.parseTreeNode.isBlock ? this.typeInfo.frame.block : this.typeInfo.frame.inline;
this.attributes = {
"class": classes.join(" ")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,listMembers);
};
ListWidget.prototype.getTiddlerList = function() {
var filter;
if(this.renderer.hasAttribute("filter")) {
filter = this.renderer.getAttribute("filter");
}
if(!filter) {
filter = "[!is[system]]";
}
this.list = this.renderer.renderTree.wiki.filterTiddlers(filter,this.renderer.tiddlerTitle);
};
/*
Create and execute the nodes representing the empty message
*/
ListWidget.prototype.getEmptyMessage = function() {
return {
type: "element",
tag: "span",
children: this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",this.emptyMessage,{parseAsInline: true}).tree
};
};
/*
Create a list element representing a given tiddler
*/
ListWidget.prototype.createListElement = function(title) {
// Define an event handler that adds navigation information to the event
var handleEvent = function(event) {
event.navigateFromTitle = title;
return true;
},
classes = ["tw-list-element"];
// Add any specified classes
if(this.itemClass) {
$tw.utils.pushTop(classes,this.itemClass);
}
// Return the list element
return {
type: "element",
tag: this.renderer.parseTreeNode.isBlock ? this.typeInfo.member.block : this.typeInfo.member.inline,
attributes: {
"class": {type: "string", value: classes.join(" ")}
},
children: [this.createListElementParseTree(title)],
events: [
{name: "tw-navigate", handlerFunction: handleEvent},
{name: "tw-edit-tiddler", handlerFunction: handleEvent},
{name: "tw-save-tiddler", handlerFunction: handleEvent},
{name: "tw-close-tiddler", handlerFunction: handleEvent},
{name: "tw-new-tiddler", handlerFunction: handleEvent}
]
};
};
/*
Create the parse tree nodes needed to represent a given list element
*/
ListWidget.prototype.createListElementParseTree = function(title) {
if(this.macro) {
return this.createListElementMacro(title);
} else {
return this.createListElementTransclusion(title);
}
};
/*
Create a macro call to represent a list element
*/
ListWidget.prototype.createListElementMacro = function(title) {
// Create the macrocall rendertree node
return {
type: "macrocall",
name: this.macro,
params: [
{name: "title", value: title}
]
};
};
/*
Create a transclusion to represent a list element
*/
ListWidget.prototype.createListElementTransclusion = function(title) {
// Check if the tiddler is a draft
var tiddler = this.renderer.renderTree.wiki.getTiddler(title),
isDraft = tiddler ? tiddler.hasField("draft.of") : false;
// Figure out the template to use
var template = this.template,
templateTree = undefined;
if(isDraft && this.editTemplate) {
template = this.editTemplate;
}
// Check for not having a template
if(!template) {
if(this.renderer.parseTreeNode.children && this.renderer.parseTreeNode.children.length > 0) {
// Use our content as the template
templateTree = this.renderer.parseTreeNode.children;
} else {
// Use default content
templateTree = [{
type: "element",
tag: "$view",
attributes: {
field: {type: "string", value: "title"},
format: {type: "string", value: "link"}
}
}];
}
}
// Create the element widgets
if(this.renderer.hasAttribute("hackTemplate")) {
return {
type: "element",
tag: "$transclude",
isBlock: this.renderer.parseTreeNode.isBlock,
attributes: {
title: {type: "string", value: title}
}
};
} else {
if(!templateTree) {
templateTree = [{
type: "element",
tag: "$transclude",
attributes: {
title: {type: "string", value: template}
},
children: templateTree
}];
}
return {
type: "element",
tag: "$tiddler",
isBlock: this.renderer.parseTreeNode.isBlock,
attributes: {
title: {type: "string", value: title}
},
children: templateTree
};
}
};
/*
Remove a list element from the list, along with the attendant DOM nodes
*/
ListWidget.prototype.removeListElement = function(index) {
// Get the list element
var listElement = this.children[index];
// Invoke the listview to animate the removal
if(this.listview && this.listview.remove) {
if(!this.listview.remove(index)) {
// Only delete the DOM element if the listview.remove() returned false
listElement.domNode.parentNode.removeChild(listElement.domNode);
}
} else {
// Always remove the DOM node if we didn't invoke the listview
listElement.domNode.parentNode.removeChild(listElement.domNode);
}
// Then delete the actual renderer node
this.children.splice(index,1);
};
/*
Return the index of the list element that corresponds to a particular title
startIndex: index to start search (use zero to search from the top)
title: tiddler title to seach for
*/
ListWidget.prototype.findListElementByTitle = function(startIndex,title) {
var testNode = this.macro ? function(node) {
// We're looking for a macro list element
return node.widget.children[0].parseTreeNode.params[0].value === title;
} : (this.renderer.hasAttribute("hackTemplate") ? function(node) {
// We're looking for a transclusion list element
return node.widget.children[0].attributes.title === title;
} : function(node) {
// We're looking for a transclusion list element
return node.widget.children[0].attributes.title === title;
});
// Search for the list element
while(startIndex < this.children.length) {
if(testNode(this.children[startIndex])) {
return startIndex;
}
startIndex++;
}
return undefined;
};
ListWidget.prototype.postRenderInDom = function() {
this.listview = this.chooseListView();
this.history = [];
};
/*
Select the appropriate list viewer
*/
ListWidget.prototype.chooseListView = function() {
// Instantiate the list view
var listviewName = this.renderer.getAttribute("listview");
var ListView = this.listViews[listviewName];
return ListView ? new ListView(this) : null;
};
ListWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Reexecute the widget if any of our attributes have changed
if(changedAttributes.itemClass || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.type || changedAttributes.filter || changedAttributes.template || changedAttributes.history || changedAttributes.listview) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// Handle any changes to the list, and refresh any nodes we're reusing
this.handleListChanges(changedTiddlers);
// Update the history list
var history = this.renderer.getAttribute("history");
if(history && changedTiddlers[history]) {
this.handleHistoryChanges();
}
}
};
ListWidget.prototype.handleListChanges = function(changedTiddlers) {
var t,
prevListLength = this.list.length,
self = this;
// Get the list of tiddlers, having saved the previous length
this.getTiddlerList();
// Check if the list is empty
if(this.list.length === 0) {
// Check if it was empty before
if(prevListLength === 0) {
// If so, just refresh the empty message
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
return;
} else {
// If the list wasn't empty before, empty it
for(t=prevListLength-1; t>=0; t--) {
this.removeListElement(t);
}
// Insert the empty message
this.children = this.renderer.renderTree.createRenderers(this.renderer,[this.getEmptyMessage()]);
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.renderer.domNode.appendChild(node.renderInDom());
}
});
return;
}
} else {
// If it is not empty now, but was empty previously, then remove the empty message
if(prevListLength === 0) {
this.removeListElement(0);
}
}
// Step through the list and adjust our child list elements appropriately
for(t=0; t<this.list.length; t++) {
// Check to see if the list element is already there
var index = this.findListElementByTitle(t,this.list[t]);
if(index === undefined) {
// The list element isn't there, so we need to insert it
this.children.splice(t,0,this.renderer.renderTree.createRenderer(this.renderer,this.createListElement(this.list[t])));
var before = this.renderer.domNode.childNodes[t],
newNode = this.children[t].renderInDom();
if(before) {
this.renderer.domNode.insertBefore(newNode,before);
} else {
this.renderer.domNode.appendChild(newNode);
}
// Ask the listview to animate the insertion
if(this.listview && this.listview.insert) {
this.listview.insert(t);
}
} else {
// Delete any list elements preceding the one we want
for(var n=index-1; n>=t; n--) {
this.removeListElement(n);
}
// Refresh the node we're reusing
this.children[t].refreshInDom(changedTiddlers);
}
}
// Remove any left over elements
for(t=this.children.length-1; t>=this.list.length; t--) {
this.removeListElement(t);
}
};
/*
Handle any changes to the history list
*/
ListWidget.prototype.handleHistoryChanges = function() {
// Get the history data
var historyAtt = this.renderer.getAttribute("history"),
newHistory = this.renderer.renderTree.wiki.getTiddlerData(historyAtt,[]);
// Ignore any entries of the history that match the previous history
var entry = 0;
while(entry < newHistory.length && entry < this.history.length && newHistory[entry].title === this.history[entry].title) {
entry++;
}
// Navigate forwards to each of the new tiddlers
while(entry < newHistory.length) {
if(this.listview && this.listview.navigateTo) {
this.listview.navigateTo(newHistory[entry]);
}
entry++;
}
// Update the history
this.history = newHistory;
};
exports.list = ListWidget;
})();

View File

@ -1,100 +0,0 @@
/*\
title: $:/core/modules/widgets/list/listviews/classic.js
type: application/javascript
module-type: listview
Views the list as a linear sequence
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ClassicListView = function(listWidget) {
this.listWidget = listWidget;
}
ClassicListView.prototype.navigateTo = function(historyInfo) {
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title);
if(listElementIndex === undefined) {
return;
}
var listElementNode = this.listWidget.children[listElementIndex],
targetElement = listElementNode.domNode;
// Scroll the node into view
var scrollEvent = this.listWidget.renderer.renderTree.document.createEvent("Event");
scrollEvent.initEvent("tw-scroll",true,true);
targetElement.dispatchEvent(scrollEvent);
};
ClassicListView.prototype.insert = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode,
duration = $tw.utils.getAnimationDuration();
// Get the current height of the tiddler
var currMarginBottom = parseInt(window.getComputedStyle(targetElement).marginBottom,10),
currMarginTop = parseInt(window.getComputedStyle(targetElement).marginTop,10),
currHeight = targetElement.offsetHeight + currMarginTop;
// Reset the margin once the transition is over
setTimeout(function() {
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: ""}
]);
},duration);
// Set up the initial position of the element
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: (-currHeight) + "px"},
{opacity: "0.0"}
]);
$tw.utils.forceLayout(targetElement);
// Transition to the final position
$tw.utils.setStyle(targetElement,[
{transition: "opacity " + duration + "ms ease-in-out, " +
"margin-bottom " + duration + "ms ease-in-out"},
{marginBottom: currMarginBottom + "px"},
{opacity: "1.0"}
]);
};
ClassicListView.prototype.remove = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode,
duration = $tw.utils.getAnimationDuration();
// Get the current height of the tiddler
var currWidth = targetElement.offsetWidth,
currMarginBottom = parseInt(window.getComputedStyle(targetElement).marginBottom,10),
currMarginTop = parseInt(window.getComputedStyle(targetElement).marginTop,10),
currHeight = targetElement.offsetHeight + currMarginTop;
// Remove the element at the end of the transition
setTimeout(function() {
if(targetElement.parentNode) {
targetElement.parentNode.removeChild(targetElement);
}
},duration);
// Animate the closure
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "translateX(0px)"},
{marginBottom: currMarginBottom + "px"},
{opacity: "1.0"}
]);
$tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
"opacity " + duration + "ms ease-in-out, " +
"margin-bottom " + duration + "ms ease-in-out"},
{transform: "translateX(-" + currWidth + "px)"},
{marginBottom: (-currHeight) + "px"},
{opacity: "0.0"}
]);
// Returning true causes the DOM node not to be deleted
return true;
};
exports.classic = ClassicListView;
})();

View File

@ -1,75 +0,0 @@
/*\
title: $:/core/modules/widgets/list/listviews/pop.js
type: application/javascript
module-type: listview
Animates list insertions and removals
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var PopListView = function(listWidget) {
this.listWidget = listWidget;
}
PopListView.prototype.insert = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode,
duration = $tw.utils.getAnimationDuration();
// Reset once the transition is over
setTimeout(function() {
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "none"}
]);
},duration);
// Set up the initial position of the element
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "scale(2)"},
{opacity: "0.0"}
]);
$tw.utils.forceLayout(targetElement);
// Transition to the final position
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
"opacity " + duration + "ms ease-in-out"},
{transform: "scale(1)"},
{opacity: "1.0"}
]);
};
PopListView.prototype.remove = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode,
duration = $tw.utils.getAnimationDuration();
// Remove the element at the end of the transition
setTimeout(function() {
if(targetElement.parentNode) {
targetElement.parentNode.removeChild(targetElement);
}
},duration);
// Animate the closure
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "scale(1)"},
{opacity: "1.0"}
]);
$tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
"opacity " + duration + "ms ease-in-out"},
{transform: "scale(0.1)"},
{opacity: "0.0"}
]);
// Returning true causes the DOM node not to be deleted
return true;
};
exports.pop = PopListView;
})();

View File

@ -1,31 +0,0 @@
/*\
title: $:/core/modules/widgets/list/listviews/scroller.js
type: application/javascript
module-type: listview
A list view that scrolls to newly inserted elements
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ScrollerListView = function(listWidget) {
this.listWidget = listWidget;
}
ScrollerListView.prototype.navigateTo = function(historyInfo) {
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title),
listElementNode = this.listWidget.children[listElementIndex],
targetElement = listElementNode.domNode;
// Scroll the node into view
var scrollEvent = this.listWidget.renderer.renderTree.document.createEvent("Event");
scrollEvent.initEvent("tw-scroll",true,true);
targetElement.dispatchEvent(scrollEvent);
};
exports.scroller = ScrollerListView;
})();

View File

@ -1,207 +0,0 @@
/*\
title: $:/core/modules/widgets/list/listviews/zoomin.js
type: application/javascript
module-type: listview
Zooms between individual tiddlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var ZoominListView = function(listWidget) {
this.listWidget = listWidget;
this.storyNode = this.listWidget.renderer.domNode;
// Set the current tiddler
this.currentTiddler = this.listWidget.children[0].domNode;
// Make all the tiddlers position absolute, and hide all but the first one
this.storyNode.style.position = "relative";
for(var t=0; t<this.storyNode.children.length; t++) {
if(t) {
this.storyNode.children[t].style.display = "none";
}
this.storyNode.children[t].style.position = "absolute";
}
};
/*
Find the first descendent node that is a <$view field="title"> widget
*/
function findTitleNode(node) {
var t,r;
// Return true if this node is a view widget with the field attribute set to "title"
if(node instanceof $tw.WikiRenderTree.prototype.rendererClasses.element) {
if(node.widget instanceof $tw.WikiRenderTree.prototype.rendererClasses.element.prototype.widgetClasses.view && node.attributes.field === "title") {
return node;
}
if(node.widget.children) {
for(t=0; t<node.widget.children.length; t++) {
var r = findTitleNode(node.widget.children[t]);
if(r) {
return r;
}
}
}
} else {
if(node.children) {
for(t=0; t<node.children.length; t++) {
var r = findTitleNode(node.children[t]);
if(r) {
return r;
}
}
}
}
return null;
}
ZoominListView.prototype.insert = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode;
// Make the newly inserted node position absolute and hidden
$tw.utils.setStyle(targetElement,[
{display: "none"},
{position: "absolute"}
]);
};
/*
Visualise navigating back to the previous tiddler
storyElement: story element being closed
*/
ZoominListView.prototype.remove = function(index) {
var listElementNode = this.listWidget.children[index],
targetElement = listElementNode.domNode,
duration = $tw.utils.getAnimationDuration();
// Set up the tiddler that is being closed
$tw.utils.setStyle(targetElement,[
{position: "absolute"},
{display: "block"},
{transformOrigin: "50% 50%"},
{transform: "translateX(0px) translateY(0px) scale(1)"},
{transition: "none"},
{zIndex: "0"}
]);
// We'll move back to the previous or next element in the story
var toElement = this.storyNode.children[index - 1];
if(!toElement) {
toElement = this.storyNode.children[index + 1];
}
// Set up the tiddler we're moving back in
if(toElement) {
$tw.utils.setStyle(toElement,[
{position: "absolute"},
{display: "block"},
{transformOrigin: "50% 50%"},
{transform: "translateX(0px) translateY(0px) scale(10)"},
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
{opacity: "0"},
{zIndex: "500"}
]);
this.currentTiddler = toElement;
}
// Animate them both
// Force layout
$tw.utils.forceLayout(this.storyNode);
// First, the tiddler we're closing
$tw.utils.setStyle(targetElement,[
{transformOrigin: "50% 50%"},
{transform: "translateX(0px) translateY(0px) scale(0.1)"},
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
{opacity: "0"},
{zIndex: "0"}
]);
setTimeout(function() {
// Delete the DOM node when the transition is over
if(targetElement.parentNode) {
targetElement.parentNode.removeChild(targetElement);
}
},duration);
// Now the tiddler we're going back to
if(toElement) {
$tw.utils.setStyle(toElement,[
{transform: "translateX(0px) translateY(0px) scale(1)"},
{opacity: "1"}
]);
}
return true; // Indicate that we'll delete the DOM node
};
ZoominListView.prototype.navigateTo = function(historyInfo) {
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title),
duration = $tw.utils.getAnimationDuration();
if(listElementIndex === undefined) {
return;
}
var listElementNode = this.listWidget.children[listElementIndex],
targetElement = listElementNode.domNode;
// Make the new tiddler be position absolute and visible so that we can measure it
$tw.utils.setStyle(targetElement,[
{position: "absolute"},
{display: "block"},
{transformOrigin: "0 0"},
{transform: "translateX(0px) translateY(0px) scale(1)"},
{transition: "none"},
{opacity: "0.0"}
]);
// Get the position of the source node, or use the centre of the window as the source position
var sourceBounds = historyInfo.fromPageRect || {
left: window.innerWidth/2 - 2,
top: window.innerHeight/2 - 2,
width: window.innerWidth/8,
height: window.innerHeight/8
};
// Try to find the title node in the target tiddler
var titleNode = findTitleNode(listElementNode) || listElementNode,
zoomBounds = titleNode.domNode.getBoundingClientRect();
// Compute the transform for the target tiddler to make the title lie over the source rectange
var targetBounds = targetElement.getBoundingClientRect(),
scale = sourceBounds.width / zoomBounds.width,
x = sourceBounds.left - targetBounds.left - (zoomBounds.left - targetBounds.left) * scale,
y = sourceBounds.top - targetBounds.top - (zoomBounds.top - targetBounds.top) * scale;
// Transform the target tiddler to its starting position
$tw.utils.setStyle(targetElement,[
{transform: "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")"}
]);
// Force layout
$tw.utils.forceLayout(targetElement);
// Apply the ending transitions with a timeout to ensure that the previously applied transformations are applied first
var self = this,
prevCurrentTiddler = this.currentTiddler;
this.currentTiddler = targetElement;
// Transform the target tiddler to its natural size
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
{opacity: "1.0"},
{transform: "translateX(0px) translateY(0px) scale(1)"},
{zIndex: "500"},
]);
// Transform the previous tiddler out of the way and then hide it
if(prevCurrentTiddler && prevCurrentTiddler !== targetElement) {
var scale = zoomBounds.width / sourceBounds.width;
x = zoomBounds.left - targetBounds.left - (sourceBounds.left - targetBounds.left) * scale;
y = zoomBounds.top - targetBounds.top - (sourceBounds.top - targetBounds.top) * scale;
$tw.utils.setStyle(prevCurrentTiddler,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
{opacity: "0.0"},
{transformOrigin: "0 0"},
{transform: "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")"},
{zIndex: "0"}
]);
// Hide the tiddler when the transition has finished
setTimeout(function() {
if(self.currentTiddler !== prevCurrentTiddler) {
prevCurrentTiddler.style.display = "none";
}
},duration);
}
// Scroll the target into view
// $tw.pageScroller.scrollIntoView(targetElement);
};
exports.zoomin = ZoominListView;
})();

View File

@ -1,301 +0,0 @@
/*\
title: $:/core/modules/widgets/navigator.js
type: application/javascript
module-type: widget
Implements the navigator widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var NavigatorWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
NavigatorWidget.prototype.generate = function() {
// Get our parameters
this.storyTitle = this.renderer.getAttribute("story");
this.historyTitle = this.renderer.getAttribute("history");
// Set the element
this.tag = "div";
this.attributes = {
"class": "tw-navigator"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
this.events = [
{name: "tw-navigate", handlerObject: this, handlerMethod: "handleNavigateEvent"},
{name: "tw-edit-tiddler", handlerObject: this, handlerMethod: "handleEditTiddlerEvent"},
{name: "tw-delete-tiddler", handlerObject: this, handlerMethod: "handleDeleteTiddlerEvent"},
{name: "tw-save-tiddler", handlerObject: this, handlerMethod: "handleSaveTiddlerEvent"},
{name: "tw-cancel-tiddler", handlerObject: this, handlerMethod: "handleCancelTiddlerEvent"},
{name: "tw-close-tiddler", handlerObject: this, handlerMethod: "handleCloseTiddlerEvent"},
{name: "tw-close-all-tiddlers", handlerObject: this, handlerMethod: "handleCloseAllTiddlersEvent"},
{name: "tw-new-tiddler", handlerObject: this, handlerMethod: "handleNewTiddlerEvent"}
];
};
NavigatorWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
};
NavigatorWidget.prototype.getStoryList = function() {
this.storyList = this.renderer.renderTree.wiki.getTiddlerList(this.storyTitle);
};
NavigatorWidget.prototype.saveStoryList = function() {
var storyTiddler = this.renderer.renderTree.wiki.getTiddler(this.storyTitle);
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler({
title: this.storyTitle
},storyTiddler,{list: this.storyList}));
};
NavigatorWidget.prototype.findTitleInStory = function(title,defaultIndex) {
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === title) {
return t;
}
}
return defaultIndex;
};
// Navigate to a specified tiddler
NavigatorWidget.prototype.handleNavigateEvent = function(event) {
if(this.storyTitle) {
// Update the story tiddler if specified
this.getStoryList();
// See if the tiddler is already there
var slot = this.findTitleInStory(event.navigateTo,-1);
// If not we need to add it
if(slot === -1) {
// First we try to find the position of the story element we navigated from
slot = this.findTitleInStory(event.navigateFromTitle,-1) + 1;
// Add the tiddler
this.storyList.splice(slot,0,event.navigateTo);
// Save the story
this.saveStoryList();
}
}
// Add a new record to the top of the history stack
if(this.historyTitle) {
var historyList = this.renderer.renderTree.wiki.getTiddlerData(this.historyTitle,[]);
historyList.push({title: event.navigateTo, fromPageRect: event.navigateFromClientRect});
this.renderer.renderTree.wiki.setTiddlerData(this.historyTitle,historyList);
}
event.stopPropagation();
return false;
};
// Close a specified tiddler
NavigatorWidget.prototype.handleCloseTiddlerEvent = function(event) {
this.getStoryList();
// Look for tiddlers with this title to close
var slot = this.findTitleInStory(event.tiddlerTitle,-1);
if(slot !== -1) {
this.storyList.splice(slot,1);
this.saveStoryList();
}
event.stopPropagation();
return false;
};
// Close all tiddlers
NavigatorWidget.prototype.handleCloseAllTiddlersEvent = function(event) {
this.storyList = [];
this.saveStoryList();
event.stopPropagation();
return false;
};
// Place a tiddler in edit mode
NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
this.getStoryList();
// Replace the specified tiddler with a draft in edit mode
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === event.tiddlerTitle) {
// Compute the title for the draft
var draftTitle = this.generateDraftTitle(event.tiddlerTitle);
this.storyList[t] = draftTitle;
// Get the current value of the tiddler we're editing
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
// Save the initial value of the draft tiddler
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(
{
text: "Type the text for the tiddler '" + event.tiddlerTitle + "'"
},
tiddler,
{
title: draftTitle,
"draft.title": event.tiddlerTitle,
"draft.of": event.tiddlerTitle
},
this.renderer.renderTree.wiki.getModificationFields()
));
}
}
this.saveStoryList();
event.stopPropagation();
return false;
};
// Delete a tiddler
NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
// Get the tiddler title we're deleting
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
// Check if the tiddler we're deleting is in draft mode
if(tiddler.hasField("draft.title")) {
// Delete the original tiddler
this.renderer.renderTree.wiki.deleteTiddler(tiddler.fields["draft.of"]);
}
// Delete this tiddler
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
// Remove the closed tiddler from the story
this.getStoryList();
// Look for tiddler with this title to close
var slot = this.findTitleInStory(event.tiddlerTitle,-1);
if(slot !== -1) {
this.storyList.splice(slot,1);
this.saveStoryList();
}
event.stopPropagation();
return false;
};
/*
Generate a title for the draft of a given tiddler
*/
NavigatorWidget.prototype.generateDraftTitle = function(title) {
var c = 0;
do {
var draftTitle = "Draft " + (c ? (c + 1) + " " : "") + "of '" + title + "'";
c++;
} while(this.renderer.renderTree.wiki.tiddlerExists(draftTitle));
return draftTitle;
};
// Take a tiddler out of edit mode, saving the changes
NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
this.getStoryList();
var storyTiddlerModified = false; // We have to special case saving the story tiddler itself
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === event.tiddlerTitle) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
if(tiddler) {
var draftTitle = tiddler.fields["draft.title"],
draftOf = tiddler.fields["draft.of"];
if(draftTitle) {
var isRename = draftOf !== draftTitle,
isConfirmed = true;
if(isRename && this.renderer.renderTree.wiki.tiddlerExists(draftTitle)) {
isConfirmed = confirm("Do you wish to overwrite the tiddler '" + draftTitle + "'?");
}
if(isConfirmed) {
// Save the draft tiddler as the real tiddler
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(this.renderer.renderTree.wiki.getCreationFields(),tiddler,{
title: draftTitle,
"draft.title": undefined,
"draft.of": undefined
},this.renderer.renderTree.wiki.getModificationFields()));
// Remove the draft tiddler
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
// Remove the original tiddler if we're renaming it
if(isRename) {
this.renderer.renderTree.wiki.deleteTiddler(draftOf);
}
// Make the story record point to the newly saved tiddler
this.storyList[t] = draftTitle;
// Check if we're modifying the story tiddler itself
if(draftTitle === this.storyTitle) {
storyTiddlerModified = true;
}
}
}
}
}
}
if(!storyTiddlerModified) {
this.saveStoryList();
}
event.stopPropagation();
return false;
};
// Take a tiddler out of edit mode without saving the changes
NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
this.getStoryList();
var storyTiddlerModified = false;
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === event.tiddlerTitle) {
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
if(tiddler.hasField("draft.title")) {
// Remove the draft tiddler
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
// Make the story record point to the original tiddler
this.storyList[t] = tiddler.fields["draft.title"];
// Check if we're modifying the story tiddler itself
if(tiddler.fields["draft.title"] === this.storyTitle) {
storyTiddlerModified = true;
}
}
}
}
if(!storyTiddlerModified) {
this.saveStoryList();
}
event.stopPropagation();
return false;
};
// Create a new draft tiddler
NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
// Get the story details
this.getStoryList();
// Create the new tiddler
var title;
for(var t=0; true; t++) {
title = "New Tiddler" + (t ? " " + t : "");
if(!this.renderer.renderTree.wiki.tiddlerExists(title)) {
break;
}
}
var tiddler = new $tw.Tiddler(this.renderer.renderTree.wiki.getCreationFields(),{
title: title,
text: "Newly created tiddler"
},this.renderer.renderTree.wiki.getModificationFields());
this.renderer.renderTree.wiki.addTiddler(tiddler);
// Create the draft tiddler
var draftTitle = this.generateDraftTitle(title),
draftTiddler = new $tw.Tiddler({
text: "Type the text for the new tiddler",
title: draftTitle,
"draft.title": title,
"draft.of": title
},this.renderer.renderTree.wiki.getModificationFields());
this.renderer.renderTree.wiki.addTiddler(draftTiddler);
// Update the story to insert the new draft at the top
var slot = this.findTitleInStory(event.navigateFromTitle,-1) + 1;
this.storyList.splice(slot,0,draftTitle);
// Save the updated story
this.saveStoryList();
// Add a new record to the top of the history stack
var history = this.renderer.renderTree.wiki.getTiddlerData(this.historyTitle,[]);
history.push({title: draftTitle});
this.renderer.renderTree.wiki.setTiddlerData(this.historyTitle,history);
event.stopPropagation();
return false;
};
exports.navigator = NavigatorWidget;
})();

View File

@ -1,45 +0,0 @@
/*\
title: $:/core/modules/widgets/password.js
type: application/javascript
module-type: widget
Implements the password widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var PasswordWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
PasswordWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.name = this.renderer.getAttribute("name");
// Get the current password
var password = $tw.browser ? $tw.utils.getPassword(this.name) : "";
// Generate our element
this.tag = "input";
this.attributes = {
type: "password",
value: password
};
this.events = [
{name: "keyup", handlerObject: this},
{name: "input", handlerObject: this}];
};
PasswordWidget.prototype.handleEvent = function(event) {
var password = this.renderer.domNode.value;
return $tw.utils.savePassword(this.name,password);
};
exports.password = PasswordWidget;
})();

View File

@ -1,216 +0,0 @@
/*\
title: $:/core/modules/widgets/reveal.js
type: application/javascript
module-type: widget
Implements the reveal widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var RevealWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
RevealWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.state = this.renderer.getAttribute("state");
this.type = this.renderer.getAttribute("type");
this.text = this.renderer.getAttribute("text");
this.position = this.renderer.getAttribute("position");
this["default"] = this.renderer.getAttribute("default","");
this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
this["class"] = this.renderer.getAttribute("class");
this.animate = this.renderer.getAttribute("animate","no");
// Compute the title of the state tiddler and read it
this.stateTitle = this.state;
if(this.qualifyTiddlerTitles) {
this.stateTitle = this.stateTitle + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
}
this.readState();
// Set up the element attributes
var classes = ["tw-reveal"],
styles = [];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
if(this.isOpen) {
$tw.utils.pushTop(classes,"tw-reveal-open");
}
switch(this.type) {
case "popup":
styles.push("position:absolute;");
classes.push("tw-popup");
break;
}
styles.push("display:" + (this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none") + ";");
// Set the element
this.tag = "div";
this.attributes = {
"class": classes.join(" "),
style: styles.join("")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.isOpen ? this.renderer.parseTreeNode.children : []);
this.events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
};
/*
Read the state tiddler
*/
RevealWidget.prototype.readState = function() {
// Read the information from the state tiddler
if(this.stateTitle) {
var state = this.renderer.renderTree.wiki.getTextReference(this.stateTitle,this["default"],this.renderer.tiddlerTitle);
switch(this.type) {
case "popup":
this.readPopupState(state);
break;
case "match":
this.readMatchState(state);
break;
case "nomatch":
this.readMatchState(state);
this.isOpen = !this.isOpen;
break;
}
}
};
RevealWidget.prototype.readMatchState = function(state) {
this.isOpen = state === this.text;
};
RevealWidget.prototype.readPopupState = function(state) {
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
match = popupLocationRegExp.exec(state);
// Check if the state matches the location regexp
if(match) {
// If so, we're open
this.isOpen = true;
// Get the location
this.popup = {
left: parseFloat(match[1]),
top: parseFloat(match[2]),
width: parseFloat(match[3]),
height: parseFloat(match[4])
};
} else {
// If not, we're closed
this.isOpen = false;
}
};
RevealWidget.prototype.handleClickEvent = function(event) {
if(event.type === "click" && this.type === "popup") {
// Cancel the popup if we get a click on it
if(this.stateTitle) {
this.renderer.renderTree.wiki.deleteTextReference(this.stateTitle);
}
event.preventDefault();
return false;
}
return true;
};
RevealWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
var self = this;
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"]) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
var needChildrenRefresh = true; // Avoid refreshing the children nodes if we don't need to
// Get the open state
var previousState = this.isOpen
this.readState();
// Construct the child nodes if required
if(this.isOpen && this.children.length === 0) {
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
var parentNode = this.renderer.domNode;
$tw.utils.each(this.children,function(child) {
parentNode.appendChild(child.renderInDom());
});
needChildrenRefresh = false;
}
// Refresh any child nodes
if(needChildrenRefresh) {
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
// Animate the opening or closing
if(this.isOpen !== previousState) {
if(this.animate !== "no") {
if(this.isOpen) {
this.renderer.domNode.style.display = this.renderer.parseTreeNode.isBlock ? "block" : "inline";
$tw.anim.perform("open",this.renderer.domNode);
} else {
$tw.anim.perform("close",this.renderer.domNode,{callback: function() {
self.renderer.domNode.style.display = "none";
}});
}
} else {
this.renderer.domNode.style.display = this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none";
}
}
// Add or remove the tw-reveal-open class
$tw.utils.toggleClass(this.renderer.domNode,"tw-reveal-open",this.isOpen);
}
// Position the content if required
if(this.isOpen) {
this.postRenderInDom();
}
};
RevealWidget.prototype.postRenderInDom = function() {
switch(this.type) {
case "popup":
if(this.isOpen) {
this.renderer.domNode.style.position = "absolute";
this.renderer.domNode.style.zIndex = "1000";
switch(this.position) {
case "left":
this.renderer.domNode.style.left = (this.popup.left - this.renderer.domNode.offsetWidth) + "px";
this.renderer.domNode.style.top = this.popup.top + "px";
break;
case "above":
this.renderer.domNode.style.left = this.popup.left + "px";
this.renderer.domNode.style.top = (this.popup.top - this.renderer.domNode.offsetHeight) + "px";
break;
case "aboveright":
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height - this.renderer.domNode.offsetHeight) + "px";
break;
case "right":
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.renderer.domNode.style.top = this.popup.top + "px";
break;
case "belowleft":
this.renderer.domNode.style.left = (this.popup.left + this.popup.width - this.renderer.domNode.offsetWidth) + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
break;
default: // Below
this.renderer.domNode.style.left = this.popup.left + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
break;
}
}
break;
}
};
exports.reveal = RevealWidget;
})();

View File

@ -1,58 +0,0 @@
/*\
title: $:/core/modules/widgets/setstyle.js
type: application/javascript
module-type: widget
Implements the setstyle widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var SetStyleWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
SetStyleWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.name = this.renderer.getAttribute("name");
this.value = this.renderer.getAttribute("value");
this["class"] = this.renderer.getAttribute("class");
// Set up the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {
style: this.name + ":" + this.value
};
if(this["class"]) {
this.attributes["class"] = this["class"];
}
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
};
SetStyleWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.name || changedAttributes.value || changedAttributes["class"]) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.setstyle = SetStyleWidget;
})();

View File

@ -1,82 +0,0 @@
/*\
title: $:/core/modules/widgets/tiddler.js
type: application/javascript
module-type: widget
The tiddler widget sets the current tiddler to a specified title.
Attributes:
title: the title of the current tiddler
class: CSS classes
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var TiddlerWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
TiddlerWidget.prototype.generate = function() {
var self = this;
this.tiddlerTitle = this.renderer.getAttribute("title","");
// Set up the attributes for the wrapper element
var classes = ["tw-tiddler"];
if(this.renderer.hasAttribute("class")) {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
if(!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle) && !this.renderer.renderTree.wiki.isShadowTiddler(this.tiddlerTitle)) {
$tw.utils.pushTop(classes,"tw-tiddler-missing");
}
// Save the context for this renderer node
this.renderer.context = {
tiddlerTitle: this.tiddlerTitle
};
// Initialise events
this.events = [];
// Trap and update tag modification events
this.events.push({name: "tw-remove-tag", handlerFunction: function(event) {
event.currentTag = self.tiddlerTitle;
return true;
}});
// Set the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {};
if(classes.length > 0) {
this.attributes["class"] = classes.join(" ");
}
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
};
TiddlerWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) {
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle));
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.title) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.tiddler = TiddlerWidget;
})();

View File

@ -1,105 +0,0 @@
/*\
title: $:/core/modules/widgets/transclude.js
type: application/javascript
module-type: widget
The transclude widget includes another tiddler into the tiddler being rendered.
Attributes:
title: the title of the tiddler to transclude
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var TranscludeWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
TranscludeWidget.prototype.generate = function() {
var self = this,
templateParseTree;
// Get the render target details
this.transcludeTitle = this.renderer.getAttribute("title",this.renderer.tiddlerTitle);
this.transcludeField = this.renderer.getAttribute("field");
this.transcludeIndex = this.renderer.getAttribute("index");
// Check for recursion
if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{
transcludeTitle: this.transcludeTitle,
transcludeField: this.transcludeField,
transcludeIndex: this.transcludeIndex
})) {
templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}];
} else {
var parser;
if(this.transcludeField === "text" || (!this.transcludeField && !this.transcludeIndex)) {
parser = this.renderer.renderTree.wiki.parseTiddler(this.transcludeTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
} else {
var tiddler,text;
if(this.transcludeField) {
tiddler = this.renderer.renderTree.wiki.getTiddler(this.transcludeTitle);
text = tiddler ? tiddler.fields[this.transcludeField] : "";
if(text === undefined) {
text = "";
}
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
} else if(this.transcludeIndex) {
text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.transcludeTitle,this.transcludeIndex,"");
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
}
}
templateParseTree = parser ? parser.tree : [];
}
// Set up the attributes for the wrapper element
var classes = ["tw-transclude"];
if(this.renderer.hasAttribute("class")) {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
// Save the context for this renderer node
this.renderer.context = {
transcludeTitle: this.transcludeTitle,
transcludeField: this.transcludeField,
transcludeIndex: this.transcludeIndex
};
// Set the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {};
if(classes.length > 0) {
this.attributes["class"] = classes.join(" ");
}
if(this.renderer.hasAttribute("style")) {
this.attributes.style = this.renderer.getAttribute("style");
}
if(this.renderer.hasAttribute("tooltip")) {
this.attributes.title = this.renderer.getAttribute("tooltip");
}
this.children = this.renderer.renderTree.createRenderers(this.renderer,templateParseTree);
};
TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.transcludeField || changedAttributes.transcludeIndex || (this.transcludeTitle && changedTiddlers[this.transcludeTitle])) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.transclude = TranscludeWidget;
})();

View File

@ -1,34 +0,0 @@
/*\
title: $:/core/modules/widgets/version.js
type: application/javascript
module-type: widget
Implements the version widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var VersionWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
VersionWidget.prototype.generate = function() {
// Set the element
this.tag = "span";
this.attributes = {};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: $tw.version
}]);
};
exports.version = VersionWidget;
})();

View File

@ -1,70 +0,0 @@
/*\
title: $:/core/modules/widgets/video.js
type: application/javascript
module-type: widget
Implements the video widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var VideoWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
VideoWidget.prototype.generate = function() {
// Get attributes
this.src = this.renderer.getAttribute("src");
this.type = this.renderer.getAttribute("type","vimeo");
this.width = parseInt(this.renderer.getAttribute("width","640"),10);
this.height = parseInt(this.renderer.getAttribute("height","360"),10);
// Return the appropriate element
switch(this.type) {
case "vimeo":
this.tag = "iframe";
this.attributes = {
src: "http://player.vimeo.com/video/" + this.src + "?autoplay=0",
width: this.width,
height: this.height,
frameborder: 0
};
break;
case "youtube":
this.tag = "iframe";
this.attributes = {
src: "http://www.youtube.com/embed/" + this.src,
width: this.width,
height: this.height,
frameborder: 0
};
break;
case "archiveorg":
this.tag = "iframe";
this.attributes = {
src: "http://www.archive.org/embed/" + this.src,
width: this.width,
height: this.height,
frameborder: 0
};
break;
default:
this.tag = "div";
this.attributes = {};
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
type: "text",
text: "Unknown video type"
}]);
break;
}
};
exports.video = VideoWidget;
})();

View File

@ -1,120 +0,0 @@
/*\
title: $:/core/modules/widgets/view.js
type: application/javascript
module-type: widget
The view widget displays a tiddler field.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Define the "text" viewer here so that it is always available
*/
var TextViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
TextViewer.prototype.render = function() {
// Get the value as a string
if(this.field !== "text" && this.tiddler) {
this.value = this.tiddler.getFieldString(this.field);
}
var value = "";
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: value
}]);
};
var ViewWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Initialise the field viewers if they've not been done already
if(!this.fieldViewers) {
ViewWidget.prototype.fieldViewers = {text: TextViewer}; // Start with the built-in text viewer
$tw.modules.applyMethods("fieldviewer",this.fieldViewers);
}
// Generate child nodes
this.generate();
};
ViewWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
this.fieldName = this.renderer.getAttribute("field","text");
this.format = this.renderer.getAttribute("format","text");
// Get the value to display
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
value;
if(tiddler) {
if(this.fieldName === "text") {
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
value = this.renderer.renderTree.wiki.getTiddlerText(this.tiddlerTitle);
} else {
value = tiddler.fields[this.fieldName];
}
} else { // Use a special value if the tiddler is missing
switch(this.fieldName) {
case "title":
value = this.tiddlerTitle;
break;
case "modified":
case "created":
value = new Date();
break;
default:
value = "";
break;
}
}
// Choose the viewer to use
var Viewer = this.fieldViewers.text;
if($tw.utils.hop(this.fieldViewers,this.format)) {
Viewer = this.fieldViewers[this.format];
}
this.viewer = new Viewer(this,tiddler,this.fieldName,value);
// Ask the viewer to create the widget element
this.viewer.render();
};
ViewWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.format || (this.tiddlerTitle && changedTiddlers[this.tiddlerTitle])) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
ViewWidget.prototype.postRenderInDom = function() {
if(this.viewer && this.viewer.postRenderInDom) {
this.viewer.postRenderInDom();
}
};
exports.view = ViewWidget;
})();

View File

@ -1,41 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/date.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as a date
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var DateViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
DateViewer.prototype.render = function() {
var template = this.viewWidget.renderer.getAttribute("template","DD MMM YYYY"),
value = "";
if(this.value !== undefined) {
value = $tw.utils.formatDateString(this.value,template);
}
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-date"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: value
}]);
};
exports.date = DateViewer;
})();

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/htmlencoded.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as HTML encoded text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var HtmlEncodedViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
HtmlEncodedViewer.prototype.render = function() {
// Get the value as a string
if(this.field !== "text" && this.tiddler) {
this.value = this.tiddler.getFieldString(this.field);
}
var value = "";
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-htmlencoded"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: $tw.utils.htmlEncode(value)
}]);
};
exports.htmlencoded = HtmlEncodedViewer;
})();

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/htmlwikified.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as a textual HTML representation of the wikified text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var HtmlWikifiedViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
HtmlWikifiedViewer.prototype.render = function() {
// Parse the field text
var wiki = this.viewWidget.renderer.renderTree.wiki,
parser = wiki.parseText("text/vnd.tiddlywiki",this.value),
renderTree = new $tw.WikiRenderTree(parser,{wiki: wiki, parentRenderer: this.viewWidget.renderer, document: this.viewWidget.renderer.renderTree.document});
renderTree.execute();
var container = this.viewWidget.renderer.renderTree.document.createElement("div");
renderTree.renderInDom(container)
var text = container.innerHTML;
// Set the element details
this.viewWidget.tag = "pre";
this.viewWidget.attributes = {
"class": "tw-view-htmlwikified"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: text
}]);
};
exports.htmlwikified = HtmlWikifiedViewer;
})();

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/jsencoded.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as JavaScript stringified text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var JsEncodedViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
JsEncodedViewer.prototype.render = function() {
// Get the value as a string
if(this.field !== "text" && this.tiddler) {
this.value = this.tiddler.getFieldString(this.field);
}
var value = "";
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
// Set the element details
this.viewWidget.tag = "pre";
this.viewWidget.attributes = {
"class": "tw-view-jsencoded"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: $tw.utils.stringify(value)
}]);
};
exports.jsencoded = JsEncodedViewer;
})();

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/link.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as a link
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var LinkViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
LinkViewer.prototype.render = function() {
var text = this.value === undefined ? "" : this.value;
// Indicate that we're not generating an element
this.viewWidget.tag = this.viewWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
this.viewWidget.attributes = {
"class": "tw-view-link"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "element",
tag: "$link",
attributes: {
to: {type: "string", value: text}
},
children: [{
type: "text",
text: text
}]
}]);
};
exports.link = LinkViewer;
})();

View File

@ -1,77 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/relativedate.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as a relative date
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var RelativeDateViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
RelativeDateViewer.prototype.render = function() {
var template = this.viewWidget.renderer.getAttribute("template","DD MMM YYYY"),
value = "";
if(this.value !== undefined) {
value = $tw.utils.formatDateString(this.value,template);
}
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-date"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: value
}]);
};
/*
Trigger the timer when the relative date is put into the DOM
*/
RelativeDateViewer.prototype.postRenderInDom = function() {
this.update();
};
/*
Trigger the timer for the next update of the relative date
*/
RelativeDateViewer.prototype.setTimer = function() {
var self = this;
if(this.relativeDate.updatePeriod < 24 * 60 * 60 * 1000) {
window.setTimeout(function() {
// Only call the update function if the dom node is still in the document
if($tw.utils.domContains(self.viewWidget.renderer.renderTree.document,self.viewWidget.renderer.domNode)) {
self.update.call(self);
}
},this.relativeDate.updatePeriod);
}
};
/*
Update the relative date display, and trigger the timer for the next update
*/
RelativeDateViewer.prototype.update = function() {
this.relativeDate = $tw.utils.getRelativeDate((new Date()) - this.value);
if(this.relativeDate.delta > 0) {
while(this.viewWidget.renderer.domNode.hasChildNodes()) {
this.viewWidget.renderer.domNode.removeChild(this.viewWidget.renderer.domNode.firstChild);
}
this.viewWidget.renderer.domNode.appendChild(this.viewWidget.renderer.renderTree.document.createTextNode(this.relativeDate.description));
this.setTimer();
}
};
exports.relativedate = RelativeDateViewer;
})();

View File

@ -1,44 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/urlencoded.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as url encoded text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var UrlEncodedViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
UrlEncodedViewer.prototype.render = function() {
// Get the value as a string
if(this.field !== "text" && this.tiddler) {
this.value = this.tiddler.getFieldString(this.field);
}
var value = "";
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-urlencoded"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
type: "text",
text: encodeURIComponent(value)
}]);
};
exports.urlencoded = UrlEncodedViewer;
})();

View File

@ -1,43 +0,0 @@
/*\
title: $:/core/modules/widgets/view/viewers/wikified.js
type: application/javascript
module-type: fieldviewer
A viewer for viewing tiddler fields as wikified text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var WikifiedViewer = function(viewWidget,tiddler,field,value) {
this.viewWidget = viewWidget;
this.tiddler = tiddler;
this.field = field;
this.value = value;
};
WikifiedViewer.prototype.render = function() {
// Set the element details
this.viewWidget.tag = this.viewWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
this.viewWidget.attributes = {};
var node = {
type: "element",
tag: "$transclude",
attributes: {
"class": "tw-view-wikified",
field: {type: "string", value: this.field}
},
isBlock: this.viewWidget.renderer.parseTreeNode.isBlock
};
if(this.tiddler && this.tiddler.fields.title) {
node.attributes.target = {type: "string", value: this.tiddler.fields.title}
}
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[node]);
};
exports.wikified = WikifiedViewer;
})();

View File

@ -329,13 +329,13 @@ exports.getTiddlerLinks = function(title) {
// We'll cache the links so they only get computed if the tiddler changes
return this.getCacheForTiddler(title,"links",function() {
// Parse the tiddler
var parser = self.parseTiddler(title);
var parser = self.new_parseTiddler(title);
// Count up the links
var links = [],
checkParseTree = function(parseTree) {
for(var t=0; t<parseTree.length; t++) {
var parseTreeNode = parseTree[t];
if(parseTreeNode.type === "element" && parseTreeNode.tag === "$link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
if(parseTreeNode.type === "link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
var value = parseTreeNode.attributes.to.value;
if(links.indexOf(value) === -1) {
links.push(value);
@ -606,7 +606,7 @@ Parse a block of text of a specified MIME type
Options include:
parseAsInline: if true, the text of the tiddler will be parsed as an inline run
*/
exports.parseText = function(type,text,options) {
exports.old_parseText = function(type,text,options) {
options = options || {};
// Select a parser
var Parser = $tw.Wiki.parsers[type];
@ -629,13 +629,13 @@ exports.parseText = function(type,text,options) {
/*
Parse a tiddler according to its MIME type
*/
exports.parseTiddler = function(title,options) {
exports.old_parseTiddler = function(title,options) {
options = options || {};
var cacheType = options.parseAsInline ? "newInlineParseTree" : "newBlockParseTree",
tiddler = this.getTiddler(title),
self = this;
return tiddler ? this.getCacheForTiddler(title,cacheType,function() {
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
return self.old_parseText(tiddler.fields.type,tiddler.fields.text,options);
}) : null;
};
@ -643,6 +643,7 @@ exports.parseTiddler = function(title,options) {
var tweakParseTreeNode = function(node) {
if(node.type === "element" && node.tag.charAt(0) === "$") {
node.type = node.tag.substr(1);
delete node.tag;
}
tweakParseTreeNodes(node.children);
};
@ -672,7 +673,7 @@ var tweakParser = function(parser) {
};
exports.new_parseText = function(type,text,options) {
var parser = this.parseText(type,text,options);
var parser = this.old_parseText(type,text,options);
if(parser) {
tweakParser(parser)
};
@ -680,7 +681,7 @@ exports.new_parseText = function(type,text,options) {
};
exports.new_parseTiddler = function(title,options) {
var parser = this.parseTiddler(title,options);
var parser = this.old_parseTiddler(title,options);
if(parser) {
tweakParser(parser)
};
@ -706,21 +707,6 @@ exports.new_parseTextReference = function(title,field,index,options) {
}
};
/*
Parse text in a specified format and render it into another format
outputType: content type for the output
textType: content type of the input text
text: input text
*/
exports.renderText = function(outputType,textType,text,context) {
var parser = this.parseText(textType,text),
renderTree = new $tw.WikiRenderTree(parser,{wiki: this, context: context, document: $tw.document});
renderTree.execute();
var container = $tw.document.createElement("div");
renderTree.renderInDom(container)
return outputType === "text/html" ? container.innerHTML : container.textContent;
};
/*
Parse text in a specified format and render it into another format
outputType: content type for the output
@ -740,27 +726,13 @@ exports.new_renderText = function(outputType,textType,text,parentWidget) {
return outputType === "text/html" ? container.innerHTML : container.textContent;
};
/*
Parse text from a tiddler and render it into another format
outputType: content type for the output
title: title of the tiddler to be rendered
*/
exports.renderTiddler = function(outputType,title,context) {
var parser = this.parseTiddler(title),
renderTree = new $tw.WikiRenderTree(parser,{wiki: this, context: context, document: $tw.document});
renderTree.execute();
var container = $tw.document.createElement("div");
renderTree.renderInDom(container)
return outputType === "text/html" ? container.innerHTML : container.textContent;
};
/*
Parse text from a tiddler and render it into another format
outputType: content type for the output
title: title of the tiddler to be rendered
*/
exports.new_renderTiddler = function(outputType,title,parentWidget) {
var parser = $tw.wiki.new_parseTiddler(title),
var parser = this.new_parseTiddler(title),
parseTreeNode = parser ? {type: "widget", children: parser.tree} : undefined,
widgetNode = new widget.widget(parseTreeNode,{
wiki: this,
@ -821,7 +793,7 @@ exports.saveWiki = function(options) {
options = options || {};
var template = options.template || "$:/core/templates/tiddlywiki5.template.html",
downloadType = options.downloadType || "text/plain";
var text = this.renderTiddler(downloadType,template);
var text = this.new_renderTiddler(downloadType,template);
this.callSaver("save",text,function(err) {
$tw.notifier.display("$:/messages/Saved");
});

View File

@ -1,47 +0,0 @@
/*\
title: test-wikitext.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests the wikitext rendering pipeline end-to-end. We also need tests that individually test parsers, rendertreenodes etc., but this gets us started.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
describe("WikiText tests", function() {
// Create a wiki
var wiki = new $tw.Wiki();
// Add a couple of tiddlers
wiki.addTiddler({title: "TiddlerOne", text: "The quick brown fox"});
wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"});
wiki.addTiddler({title: "TiddlerThree", text: "The speed of sound\n\nThe light of speed"});
it("should render tiddlers with no special markup render as-is", function() {
expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
});
it("should preserve single new lines", function() {
expect(wiki.renderTiddler("text/plain","TiddlerTwo")).toBe("The rain in Spain\nfalls mainly on the plain");
});
it("should use double new lines to create paragraphs", function() {
// The paragraphs are lost in the conversion to plain text
expect(wiki.renderTiddler("text/plain","TiddlerThree")).toBe("The speed of soundThe light of speed");
});
it("should render plain text tiddlers as a paragraph", function() {
expect(wiki.renderTiddler("text/html","TiddlerOne")).toBe("<p>\nThe quick brown fox</p>");
});
it("should preserve single new lines", function() {
expect(wiki.renderTiddler("text/html","TiddlerTwo")).toBe("<p>\nThe rain in Spain\nfalls mainly on the plain</p>");
});
it("should use double new lines to create paragraphs", function() {
expect(wiki.renderTiddler("text/html","TiddlerThree")).toBe("<p>\nThe speed of sound</p><p>\nThe light of speed</p>");
});
});
})();

View File

@ -19,7 +19,7 @@ describe("WikiText parser tests", function() {
// Define a parsing shortcut
var parse = function(text) {
return wiki.parseText("text/vnd.tiddlywiki",text).tree;
return wiki.new_parseText("text/vnd.tiddlywiki",text).tree;
};
it("should parse tags", function() {
@ -70,7 +70,7 @@ describe("WikiText parser tests", function() {
);
expect(parse("<$reveal state='$:/temp/search' type='nomatch' text=''>")).toEqual(
[ { type : 'element', tag : 'p', children : [ { type : 'element', start : 0, attributes : { state : { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, type : { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, text : { start : 46, name : 'text', type : 'string', value : '', end : 54 } }, tag : '$reveal', end : 55, children : [ ], isBlock : false } ] } ]
[ { type : 'element', tag : 'p', children : [ { type : 'reveal', start : 0, attributes : { state : { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, type : { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, text : { start : 46, name : 'text', type : 'string', value : '', end : 54 } }, end : 55, isBlock : false, children : [ ] } ] } ]
);
expect(parse("<div attribute={{TiddlerTitle!!field}}>some text</div>")).toEqual(
@ -93,7 +93,7 @@ describe("WikiText parser tests", function() {
it("should parse macro definitions", function() {
expect(parse("\\define myMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'macrodef', name : 'myMacro', params : [ ], text : 'nothing' } ]
[ { type : 'setvariable', name : 'myMacro', params : [ ], text : 'nothing', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ] } ]
);

View File

@ -23,30 +23,29 @@ describe("WikiText tests", function() {
wiki.addTiddler({title: "TiddlerFour", text: "\\define my-macro(adjective:'groovy')\nThis is my ''amazingly'' $adjective$ macro!\n\\end\n\n<$link to=<<my-macro>>>This is a link</$link>"});
it("should render tiddlers with no special markup as-is", function() {
expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
expect(wiki.new_renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
});
it("should preserve single new lines", function() {
expect(wiki.renderTiddler("text/plain","TiddlerTwo")).toBe("The rain in Spain\nfalls mainly on the plain");
expect(wiki.new_renderTiddler("text/plain","TiddlerTwo")).toBe("The rain in Spain\nfalls mainly on the plain");
});
it("should use double new lines to create paragraphs", function() {
// The paragraphs are lost in the conversion to plain text
expect(wiki.renderTiddler("text/plain","TiddlerThree")).toBe("The speed of soundThe light of speed");
expect(wiki.new_renderTiddler("text/plain","TiddlerThree")).toBe("The speed of soundThe light of speed");
});
it("should render plain text tiddlers as a paragraph", function() {
expect(wiki.renderTiddler("text/html","TiddlerOne")).toBe("<p>\nThe quick brown fox</p>");
expect(wiki.new_renderTiddler("text/html","TiddlerOne")).toBe("<p>\nThe quick brown fox</p>");
});
it("should preserve single new lines", function() {
expect(wiki.renderTiddler("text/html","TiddlerTwo")).toBe("<p>\nThe rain in Spain\nfalls mainly on the plain</p>");
expect(wiki.new_renderTiddler("text/html","TiddlerTwo")).toBe("<p>\nThe rain in Spain\nfalls mainly on the plain</p>");
});
it("should use double new lines to create paragraphs", function() {
expect(wiki.renderTiddler("text/html","TiddlerThree")).toBe("<p>\nThe speed of sound</p><p>\nThe light of speed</p>");
expect(wiki.new_renderTiddler("text/html","TiddlerThree")).toBe("<p>\nThe speed of sound</p><p>\nThe light of speed</p>");
});
it("should support attributes specified as macro invocations", function() {
expect(wiki.renderTiddler("text/html","TiddlerFour")).toBe("<p>\n<a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-missing' href='#This%20is%20my%20amazingly%20groovy%20macro!'>\nThis is a link</a></p>");
expect(wiki.new_renderTiddler("text/html","TiddlerFour")).toBe("<p>\n<a class=' tw-tiddlylink tw-tiddlylink-missing' href='#This%20is%20my%20amazingly%20groovy%20macro!'>\nThis is a link</a></p>");
});
});
})();

View File

@ -1,9 +1,7 @@
<h1 class=''>
Building classic <span>
TiddlyWiki</span> with <span>
TiddlyWiki5</span></h1><div class='tw-tiddler'>
<div class='tw-transclude'>
<p>
TiddlyWiki5</span></h1><p>
<span>
TiddlyWiki5</span> can be used to build older 2.x.x versions of <span>
TiddlyWiki</span> from their constituent components. Doing so involves these additional features over and above those used for building <span>
@ -34,4 +32,4 @@ node ../../tiddlywiki.js \
--verbose \
--load &lt;path_to_recipe_file&gt; \
--rendertiddler $:/core/templates/tiddlywiki2.template.html &lt;path_to_write_index_file&gt; text/plain \
|| exit 1</pre></div></div>
|| exit 1</pre>

View File

@ -9,6 +9,6 @@ node ./tiddlywiki.js \
./editions/empty \
--verbose \
--load $1 \
--rendertiddler $:/core/templates/split-recipe tmp/ginsu/split.recipe text/plain \
--rendertiddlers [!is[system]] $:/core/templates/tid-tiddler tmp/ginsu text/plain .tid \
--new_rendertiddler $:/core/templates/split-recipe tmp/ginsu/split.recipe text/plain \
--new_rendertiddlers [!is[system]] $:/core/templates/tid-tiddler tmp/ginsu text/plain .tid \
|| exit 1

View File

@ -20,6 +20,6 @@ echo "Using TW5_BUILD_OUTPUT as [$TW5_BUILD_OUTPUT]"
node ./tiddlywiki.js \
./editions/test \
--verbose \
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/test.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/test.html text/plain \
|| exit 1

View File

@ -15,7 +15,7 @@ mkdir -p tmp
node ./tiddlywiki.js \
editions/tw5tiddlyweb \
--verbose \
--rendertiddler $:/core/templates/tiddlywiki5.template.html tmp/tiddlyweb.html text/plain \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html tmp/tiddlyweb.html text/plain \
|| exit 1
# Prepend the type information that TiddlyWeb needs to turn the .html file into a .tid file