mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 03:57:21 +00:00
parent
6ea264f3bc
commit
ed35d91be6
44
core/modules/commands/new_rendertiddler.js
Executable file
44
core/modules/commands/new_rendertiddler.js
Executable file
@ -0,0 +1,44 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/new_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: "new_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.new_renderTiddler(type,title),"utf8",function(err) {
|
||||
self.callback(err);
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
73
core/modules/new_widgets/element.js
Executable file
73
core/modules/new_widgets/element.js
Executable file
@ -0,0 +1,73 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/element.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Element widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var ElementWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ElementWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ElementWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var domNode = this.document.createElement(this.parseTreeNode.tag);
|
||||
this.assignAttributes(domNode);
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ElementWidget.prototype.execute = function() {
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ElementWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
hasChangedAttributes = $tw.utils.count(changedAttributes) > 0;
|
||||
if(hasChangedAttributes) {
|
||||
// Update our attributes
|
||||
this.assignAttributes(this.domNodes[0]);
|
||||
}
|
||||
var hasRefreshed = this.refreshChildren(changedTiddlers);
|
||||
return hasRefreshed || hasChangedAttributes;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
ElementWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.element = ElementWidget;
|
||||
|
||||
})();
|
62
core/modules/new_widgets/entity.js
Executable file
62
core/modules/new_widgets/entity.js
Executable file
@ -0,0 +1,62 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/entity.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
HTML entity widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EntityWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EntityWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EntityWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode($tw.utils.entityDecode(this.parseTreeNode.entity));
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EntityWidget.prototype.execute = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EntityWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
EntityWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.entity = EntityWidget;
|
||||
|
||||
})();
|
113
core/modules/new_widgets/fields.js
Executable file
113
core/modules/new_widgets/fields.js
Executable file
@ -0,0 +1,113 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/fields.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
View widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var FieldsWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
FieldsWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
FieldsWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
FieldsWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("tiddlerTitle"));
|
||||
this.template = this.getAttribute("template");
|
||||
this.exclude = this.getAttribute("exclude");
|
||||
this.stripTitlePrefix = this.getAttribute("stripTitlePrefix","no") === "yes";
|
||||
// Get the value to display
|
||||
var tiddler = this.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.text = text.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
FieldsWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.template || changedAttributes.exclude || changedAttributes.stripTitlePrefix || changedTiddlers[this.tiddlerTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
FieldsWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.fields = FieldsWidget;
|
||||
|
||||
})();
|
117
core/modules/new_widgets/link.js
Executable file
117
core/modules/new_widgets/link.js
Executable file
@ -0,0 +1,117 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/link.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Link widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var LinkWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
LinkWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LinkWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Create our element
|
||||
var domNode = this.document.createElement("a");
|
||||
// Assign classes
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink");
|
||||
if(this.isShadow) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-shadow");
|
||||
}
|
||||
if(this.isMissing && !this.isShadow) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-missing");
|
||||
} else {
|
||||
if(!this.isMissing) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-resolves");
|
||||
}
|
||||
}
|
||||
// Set an href
|
||||
domNode.setAttribute("href",this.to);
|
||||
// Add a click event handler
|
||||
domNode.addEventListener("click",function (event) {
|
||||
// Send the click on it's way as a navigate event
|
||||
var bounds = domNode.getBoundingClientRect();
|
||||
self.dispatchEvent({
|
||||
type: "tw-navigate",
|
||||
navigateTo: self.to,
|
||||
navigateFromNode: self,
|
||||
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;
|
||||
},false);
|
||||
// Insert the link into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
LinkWidget.prototype.execute = function() {
|
||||
// Get the target tiddler title
|
||||
this.to = this.getAttribute("to",this.getVariable("tiddlerTitle"));
|
||||
// Determine the link characteristics
|
||||
this.isMissing = !this.wiki.tiddlerExists(this.to);
|
||||
this.isShadow = this.wiki.isShadowTiddler(this.to);
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.to || changedTiddlers[this.to]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
LinkWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.link = LinkWidget;
|
||||
|
||||
})();
|
258
core/modules/new_widgets/list.js
Executable file
258
core/modules/new_widgets/list.js
Executable file
@ -0,0 +1,258 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/list.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
List and list item widgets
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
/*
|
||||
The list widget creates list element sub-widgets that reach back into the list widget for their configuration
|
||||
*/
|
||||
|
||||
var ListWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ListWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListWidget.prototype.execute = function() {
|
||||
// Get our attributes
|
||||
this.template = this.getAttribute("template");
|
||||
this.editTemplate = this.getAttribute("editTemplate");
|
||||
this.preserveCurrentTiddler = this.getAttribute("preserveCurrentTiddler","no") === "yes";
|
||||
// Compose the list elements
|
||||
this.list = this.getTiddlerList();
|
||||
var members = [],
|
||||
self = this;
|
||||
// Check for an empty list
|
||||
if(this.list.length === 0) {
|
||||
members = this.getEmptyMessage();
|
||||
} else {
|
||||
$tw.utils.each(this.list,function(title,index) {
|
||||
members.push(self.makeItemTemplate(title));
|
||||
});
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(members);
|
||||
};
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var defaultFilter = "[!is[system]sort[title]]";
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this.getVariable("tiddlerTitle"));
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
var emptyMessage = this.getAttribute("emptyMessage",""),
|
||||
parser = this.wiki.new_parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
if(parser) {
|
||||
return parser.tree;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compose the template for a list item
|
||||
*/
|
||||
ListWidget.prototype.makeItemTemplate = function(title) {
|
||||
// Check if the tiddler is a draft
|
||||
var tiddler = this.wiki.getTiddler(title),
|
||||
isDraft = tiddler && tiddler.hasField("draft.of"),
|
||||
template = this.template,
|
||||
templateTree;
|
||||
if(isDraft && this.editTemplate) {
|
||||
template = this.editTemplate;
|
||||
}
|
||||
// Compose the transclusion of the template
|
||||
if(this.hasAttribute("hackTemplate")) {
|
||||
templateTree = [{type: "transclude", attributes: {title: {type: "string", value: title}}}];
|
||||
} else {
|
||||
if(template) {
|
||||
templateTree = [{type: "transclude", attributes: {title: {type: "string", value: template}}}];
|
||||
} else {
|
||||
if(this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
} else {
|
||||
// Default template is a link to the title
|
||||
templateTree = [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||
{type: "text", text: title}
|
||||
]}];
|
||||
}
|
||||
}
|
||||
templateTree = [{type: "tiddler", attributes: {title: {type: "string", value: title}}, children: templateTree}]
|
||||
}
|
||||
// Return the list item
|
||||
return {type: "listitem", itemTitle: title, children: templateTree};
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ListWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Completely refresh if any of our attributes have changed
|
||||
if(changedAttributes.filter || changedAttributes.preserveCurrentTiddler) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
// Handle any changes to the list
|
||||
return this.handleListChanges(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Process any changes to the list
|
||||
*/
|
||||
ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
// Get the new list
|
||||
var prevList = this.list;
|
||||
this.list = this.getTiddlerList();
|
||||
// Check for an empty list
|
||||
if(this.list.length === 0) {
|
||||
// Check if it was empty before
|
||||
if(prevList.length === 0) {
|
||||
// If so, just refresh the empty message
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
} else {
|
||||
// Replace the previous content with the empty message
|
||||
var nextSibling = this.findNextSibling();
|
||||
this.removeChildDomNodes();
|
||||
this.makeChildWidgets(this.getEmptyMessage());
|
||||
this.renderChildren(this.parentDomNode,nextSibling);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// If the list was empty then we need to remove the empty message
|
||||
if(prevList.length === 0) {
|
||||
this.removeChildDomNodes();
|
||||
this.children = [];
|
||||
}
|
||||
// Cycle through the list, inserting and removing list items as needed
|
||||
var hasRefreshed = false;
|
||||
for(var t=0; t<this.list.length; t++) {
|
||||
var index = this.findListItem(t,this.list[t]);
|
||||
if(index === undefined) {
|
||||
// The list item must be inserted
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// There are intervening list items that must be removed
|
||||
for(var n=index-1; n>=t; n--) {
|
||||
this.removeListItem(n);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
// Remove any left over items
|
||||
for(t=this.children.length-1; t>=this.list.length; t--) {
|
||||
this.removeListItem(t);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
return hasRefreshed;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Find the list item with a given title, starting from a specified position
|
||||
*/
|
||||
ListWidget.prototype.findListItem = function(startIndex,title) {
|
||||
while(startIndex < this.children.length) {
|
||||
if(this.children[startIndex].parseTreeNode.itemTitle === title) {
|
||||
return startIndex;
|
||||
}
|
||||
startIndex++;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Insert a new list item at the specified index
|
||||
*/
|
||||
ListWidget.prototype.insertListItem = function(index,title) {
|
||||
var newItem = this.makeChildWidget(this.makeItemTemplate(title));
|
||||
newItem.parentDomNode = this.parentDomNode; // Hack to enable findNextSibling() to work
|
||||
this.children.splice(index,0,newItem);
|
||||
var nextSibling = newItem.findNextSibling();
|
||||
newItem.render(this.parentDomNode,nextSibling);
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Remvoe the specified list item
|
||||
*/
|
||||
ListWidget.prototype.removeListItem = function(index) {
|
||||
// Remove the DOM nodes owned by this item
|
||||
this.children[index].removeChildDomNodes();
|
||||
// Remove the child widget
|
||||
this.children.splice(index,1);
|
||||
};
|
||||
|
||||
exports.list = ListWidget;
|
||||
|
||||
var ListItemWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ListItemWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ListItemWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListItemWidget.prototype.execute = function() {
|
||||
// Set the current list item title
|
||||
this.setVariable("listItem",this.parseTreeNode.itemTitle);
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ListItemWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.listitem = ListItemWidget;
|
||||
|
||||
})();
|
120
core/modules/new_widgets/navigator.js
Executable file
120
core/modules/new_widgets/navigator.js
Executable file
@ -0,0 +1,120 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/navigator.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Navigator widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var NavigatorWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tw-navigate", handler: "handleNavigateEvent"},
|
||||
{type: "tw-edit-tiddler", handler: "handleEditTiddlerEvent"},
|
||||
{type: "tw-delete-tiddler", handler: "handleDeleteTiddlerEvent"},
|
||||
{type: "tw-save-tiddler", handler: "handleSaveTiddlerEvent"},
|
||||
{type: "tw-cancel-tiddler", handler: "handleCancelTiddlerEvent"},
|
||||
{type: "tw-close-tiddler", handler: "handleCloseTiddlerEvent"},
|
||||
{type: "tw-close-all-tiddlers", handler: "handleCloseAllTiddlersEvent"},
|
||||
{type: "tw-new-tiddler", handler: "handleNewTiddlerEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
NavigatorWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
NavigatorWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
NavigatorWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.storyTitle = this.getAttribute("story");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
NavigatorWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.story || changedAttributes.history) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.getStoryList = function() {
|
||||
this.storyList = this.wiki.getTiddlerList(this.storyTitle);
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.saveStoryList = function() {
|
||||
var storyTiddler = this.wiki.getTiddler(this.storyTitle);
|
||||
this.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;
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a tw-navigate event
|
||||
*/
|
||||
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.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
historyList.push({title: event.navigateTo, fromPageRect: event.navigateFromClientRect});
|
||||
this.wiki.setTiddlerData(this.historyTitle,historyList);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.navigator = NavigatorWidget;
|
||||
|
||||
})();
|
134
core/modules/new_widgets/reveal.js
Executable file
134
core/modules/new_widgets/reveal.js
Executable file
@ -0,0 +1,134 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/reveal.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Reveal widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var RevealWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
RevealWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
RevealWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var domNode = this.document.createElement("div");
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
RevealWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.state = this.getAttribute("state");
|
||||
this.type = this.getAttribute("type");
|
||||
this.text = this.getAttribute("text");
|
||||
this.position = this.getAttribute("position");
|
||||
this["default"] = this.getAttribute("default","");
|
||||
this.qualifyTiddlerTitles = this.getAttribute("qualifyTiddlerTitles");
|
||||
this["class"] = this.getAttribute("class");
|
||||
this.animate = this.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.getStateQualifier();
|
||||
}
|
||||
this.readState();
|
||||
// Construct the child widgets
|
||||
var childNodes = this.isOpen ? this.parseTreeNode.children : [];
|
||||
this.makeChildWidgets(childNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Read the state tiddler
|
||||
*/
|
||||
RevealWidget.prototype.readState = function() {
|
||||
// Read the information from the state tiddler
|
||||
if(this.stateTitle) {
|
||||
var state = this.wiki.getTextReference(this.stateTitle,this["default"],this.getVariable("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;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
RevealWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"] || changedAttributes.animate || changedTiddlers[this.stateTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
RevealWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.reveal = RevealWidget;
|
||||
|
||||
})();
|
64
core/modules/new_widgets/setvariable.js
Executable file
64
core/modules/new_widgets/setvariable.js
Executable file
@ -0,0 +1,64 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/setvariable.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Setvariable widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var SetVariableWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
SetVariableWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
SetVariableWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
SetVariableWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.setName = this.getAttribute("name","tiddlerTitle");
|
||||
this.setValue = this.getAttribute("value");
|
||||
// Set context variable
|
||||
this.setVariable(this.setName,this.setValue);
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
SetVariableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.name || changedAttributes.value) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.setvariable = SetVariableWidget;
|
||||
|
||||
})();
|
22
core/modules/new_widgets/tempwidgets.js
Executable file
22
core/modules/new_widgets/tempwidgets.js
Executable file
@ -0,0 +1,22 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/tempwidgets.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Temporary shim widgets
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
exports.button = Widget;
|
||||
exports.linkcatcher = Widget;
|
||||
exports.setstyle = Widget;
|
||||
exports["import"] = Widget;
|
||||
|
||||
})();
|
63
core/modules/new_widgets/text.js
Executable file
63
core/modules/new_widgets/text.js
Executable file
@ -0,0 +1,63 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/text.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Text node widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TextNodeWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TextNodeWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TextNodeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.parseTreeNode.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TextNodeWidget.prototype.execute = function() {
|
||||
// Nothing to do for a text node
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TextNodeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
TextNodeWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.text = TextNodeWidget;
|
||||
|
||||
})();
|
75
core/modules/new_widgets/tiddler.js
Executable file
75
core/modules/new_widgets/tiddler.js
Executable file
@ -0,0 +1,75 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/tiddler.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Tiddler widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TiddlerWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tw-navigate", handler: "handleNavigateEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TiddlerWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TiddlerWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TiddlerWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.tiddlerTitle = this.getAttribute("title","");
|
||||
// Set context variable
|
||||
this.setVariable("tiddlerTitle",this.tiddlerTitle);
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TiddlerWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.name || changedAttributes.value) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a tw-navigate event
|
||||
*/
|
||||
TiddlerWidget.prototype.handleNavigateEvent = function(event) {
|
||||
console.log("Setting navigateFromTitle to",this.tiddlerTitle)
|
||||
event.navigateFromTitle = this.tiddlerTitle;
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.tiddler = TiddlerWidget;
|
||||
|
||||
})();
|
93
core/modules/new_widgets/transclude.js
Executable file
93
core/modules/new_widgets/transclude.js
Executable file
@ -0,0 +1,93 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/transclude.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Transclude widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TranscludeWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TranscludeWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TranscludeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TranscludeWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.transcludeTitle = this.getAttribute("title",this.getVariable("tiddlerTitle"));
|
||||
this.transcludeField = this.getAttribute("field");
|
||||
this.transcludeIndex = this.getAttribute("index");
|
||||
// Check for recursion
|
||||
var recursionMarker = this.makeRecursionMarker();;
|
||||
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) {
|
||||
this.makeChildWidgets([{type: "text", text: "Tiddler recursion error in transclude widget"}]);
|
||||
return;
|
||||
}
|
||||
// Set context variables for recursion detection
|
||||
this.setVariable("transclusion",recursionMarker);
|
||||
// Parse the text reference
|
||||
var parser = this.wiki.new_parseTextReference(
|
||||
this.transcludeTitle,
|
||||
this.transcludeField,
|
||||
this.transcludeIndex,
|
||||
{parseAsInline: !this.parseTreeNode.isBlock}),
|
||||
parseTreeNodes = parser ? parser.tree : [];
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(parseTreeNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Compose a string comprising the title, field and/or index to identify this transclusion for recursion detection
|
||||
*/
|
||||
TranscludeWidget.prototype.makeRecursionMarker = function() {
|
||||
var output = [];
|
||||
output.push("{");
|
||||
output.push(this.transcludeTitle || "");
|
||||
output.push("|");
|
||||
output.push(this.transcludeField || "");
|
||||
output.push("|");
|
||||
output.push(this.transcludeIndex || "");
|
||||
output.push("}");
|
||||
return output.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TranscludeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.title || changedAttributes.field || changedAttributes.index || changedTiddlers[this.transcludeTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.transclude = TranscludeWidget;
|
||||
|
||||
})();
|
62
core/modules/new_widgets/version.js
Executable file
62
core/modules/new_widgets/version.js
Executable file
@ -0,0 +1,62 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/version.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Version widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var VersionWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
VersionWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
VersionWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode($tw.version);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
VersionWidget.prototype.execute = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
VersionWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
VersionWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.version = VersionWidget;
|
||||
|
||||
})();
|
143
core/modules/new_widgets/view.js
Executable file
143
core/modules/new_widgets/view.js
Executable file
@ -0,0 +1,143 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/view.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
View widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var ViewWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ViewWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ViewWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ViewWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.viewTitle = this.getAttribute("title",this.getVariable("tiddlerTitle"));
|
||||
this.viewField = this.getAttribute("field","text");
|
||||
this.viewIndex = this.getAttribute("index");
|
||||
this.viewFormat = this.getAttribute("format","text");
|
||||
switch(this.viewFormat) {
|
||||
case "wikified":
|
||||
this.text = this.getValueAsWikified();
|
||||
break;
|
||||
case "htmlwikified":
|
||||
this.text = this.getValueAsHtmlWikified();
|
||||
break;
|
||||
case "htmlencoded":
|
||||
this.text = this.getValueAsHtmlEncoded();
|
||||
break;
|
||||
case "date":
|
||||
this.text = this.getValueAsDate(this.viewFormat);
|
||||
break;
|
||||
case "relativedate":
|
||||
this.text = this.getValueAsRelativeDate();
|
||||
break;
|
||||
default: // "text"
|
||||
this.text = this.getValueAsText();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
The various formatter functions are baked into this widget for the moment. Eventually they will be replaced by macro functions
|
||||
*/
|
||||
|
||||
ViewWidget.prototype.getValueAsText = function() {
|
||||
// Get the value to display
|
||||
var text,
|
||||
tiddler = this.wiki.getTiddler(this.viewTitle);
|
||||
if(tiddler) {
|
||||
if(this.viewField === "text") {
|
||||
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
||||
text = this.wiki.getTiddlerText(this.viewTitle);
|
||||
} else {
|
||||
text = tiddler.fields[this.viewField];
|
||||
}
|
||||
} else { // Use a special value if the tiddler is missing
|
||||
switch(this.viewField) {
|
||||
case "title":
|
||||
text = this.getVariable("tiddlerTitle");
|
||||
break;
|
||||
case "modified":
|
||||
case "created":
|
||||
text = new Date();
|
||||
break;
|
||||
default:
|
||||
text = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlWikified = function() {
|
||||
return this.wiki.new_renderText("text/plain","text/vnd.tiddlywiki",this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlEncoded = function() {
|
||||
return $tw.utils.htmlEncode(this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsDate = function(format) {
|
||||
return $tw.utils.formatDateString(this.getValueAsText(),format);
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsRelativeDate = function(format) {
|
||||
var d = new Date(this.getValueAsText());
|
||||
return $tw.utils.getRelativeDate((new Date()) - d).description;
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ViewWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.title || changedAttributes.field || changedAttributes.index || changedTiddlers[this.viewTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
ViewWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.view = ViewWidget;
|
||||
|
||||
})();
|
378
core/modules/new_widgets/widget.js
Executable file
378
core/modules/new_widgets/widget.js
Executable file
@ -0,0 +1,378 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/widget.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Widget base class
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Create a widget object for a parse tree node
|
||||
parseTreeNode: reference to the parse tree node to be rendered
|
||||
options: see below
|
||||
Options include:
|
||||
wiki: mandatory reference to wiki associated with this render tree
|
||||
variables: optional hashmap of context variables (see below)
|
||||
parentWidget: 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 Widget = function(parseTreeNode,options) {
|
||||
if(arguments.length > 0) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Initialise widget properties. These steps are pulled out of the constructor so that we can reuse them in subclasses
|
||||
*/
|
||||
Widget.prototype.initialise = function(parseTreeNode,options) {
|
||||
options = options || {};
|
||||
// Save widget info
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
this.wiki = options.wiki;
|
||||
this.variables = options.variables || {};
|
||||
this.parentWidget = options.parentWidget;
|
||||
this.document = options.document;
|
||||
this.attributes = {};
|
||||
this.children = [];
|
||||
this.domNodes = [];
|
||||
this.eventListeners = {};
|
||||
// Hashmap of the widget classes
|
||||
if(!this.widgetClasses) {
|
||||
Widget.prototype.widgetClasses = $tw.modules.applyMethods("new_widget");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
Widget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
Widget.prototype.execute = function() {
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Get the prevailing value of a context variable
|
||||
name: name of variable
|
||||
params: array of {name:, value:} for each parameter
|
||||
*/
|
||||
Widget.prototype.getVariable = function(name,params) {
|
||||
// Search up the widget tree for the variable name
|
||||
var node = this;
|
||||
while(node && !$tw.utils.hop(node.variables,name)) {
|
||||
node = node.parentWidget;
|
||||
}
|
||||
if(!node) {
|
||||
return undefined;
|
||||
}
|
||||
// Get the value
|
||||
var value = node.variables[name].value;
|
||||
// Substitute any parameters specified in the definition
|
||||
var defParams = node.variables[name].params;
|
||||
if(defParams) {
|
||||
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
|
||||
paramInfo, paramValue;
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<defParams.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
paramInfo = defParams[p];
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<params.length; m++) {
|
||||
if(params[m].name === paramInfo.name) {
|
||||
paramValue = params[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
while(nextAnonParameter < params.length && params[nextAnonParameter].name) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(paramValue === undefined && nextAnonParameter < params.length) {
|
||||
paramValue = params[nextAnonParameter++].value;
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Replace any instances of this parameter
|
||||
value = value.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/*
|
||||
Set the value of a context variable
|
||||
name: name of the variable
|
||||
value: value of the variable
|
||||
params: array of {name:, default:} for each parameter
|
||||
*/
|
||||
Widget.prototype.setVariable = function(name,value,params) {
|
||||
this.variables[name] = {value: value, params: params};
|
||||
};
|
||||
|
||||
/*
|
||||
Check whether a given context variable value exists in the parent chain
|
||||
*/
|
||||
Widget.prototype.hasVariable = function(name,value) {
|
||||
var node = this;
|
||||
while(node) {
|
||||
if($tw.utils.hop(node.variables,name) && node.variables[name].value === value) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentWidget;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Construct a qualifying string based on concatenating the values of a given variable in the parent chain
|
||||
*/
|
||||
Widget.prototype.getStateQualifier = function(name) {
|
||||
name = name || "transclusion";
|
||||
var output = [],
|
||||
node = this;
|
||||
while(node) {
|
||||
if($tw.utils.hop(node.variables,name)) {
|
||||
output.push(node.getVariable(name));
|
||||
}
|
||||
node = node.parentWidget;
|
||||
}
|
||||
return output.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed
|
||||
*/
|
||||
Widget.prototype.computeAttributes = function() {
|
||||
var changedAttributes = {},
|
||||
self = this,
|
||||
value;
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(attribute.type === "indirect") {
|
||||
value = self.wiki.getTextReference(attribute.textReference,"",self.getVariable("tiddlerTitle"));
|
||||
} else if(attribute.type === "macro") {
|
||||
value = self.getVariable(attribute.value.name,attribute.value.params);
|
||||
} 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;
|
||||
};
|
||||
|
||||
/*
|
||||
Check for the presence of an attribute
|
||||
*/
|
||||
Widget.prototype.hasAttribute = function(name) {
|
||||
return $tw.utils.hop(this.attributes,name);
|
||||
};
|
||||
|
||||
/*
|
||||
Get the value of an attribute
|
||||
*/
|
||||
Widget.prototype.getAttribute = function(name,defaultText) {
|
||||
if($tw.utils.hop(this.attributes,name)) {
|
||||
return this.attributes[name];
|
||||
} else {
|
||||
return defaultText;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Assign the computed attributes of the widget to a domNode
|
||||
*/
|
||||
Widget.prototype.assignAttributes = function(domNode) {
|
||||
var self = this;
|
||||
$tw.utils.each(this.attributes,function(v,a) {
|
||||
if(v !== undefined) {
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(null,a,v);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Make child widgets correspondng to specified parseTreeNodes
|
||||
*/
|
||||
Widget.prototype.makeChildWidgets = function(parseTreeNodes) {
|
||||
this.children = [];
|
||||
var self = this;
|
||||
$tw.utils.each(parseTreeNodes || this.parseTreeNode.children,function(childNode) {
|
||||
self.children.push(self.makeChildWidget(childNode));
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Construct the widget object for a parse tree node
|
||||
*/
|
||||
Widget.prototype.makeChildWidget = function(parseTreeNode) {
|
||||
var WidgetClass = this.widgetClasses[parseTreeNode.type];
|
||||
if(!WidgetClass) {
|
||||
WidgetClass = this.widgetClasses["text"];
|
||||
parseTreeNode = {type: "text", text: "Undefined widget '" + parseTreeNode.type + "'"};
|
||||
}
|
||||
return new WidgetClass(parseTreeNode,{
|
||||
wiki: this.wiki,
|
||||
variables: {},
|
||||
parentWidget: this,
|
||||
document: this.document
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Render the children of this widget into the DOM
|
||||
*/
|
||||
Widget.prototype.renderChildren = function(parent,nextSibling) {
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
childWidget.render(parent,nextSibling);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Add a list of event listeners from an array [{type:,handler:},...]
|
||||
*/
|
||||
Widget.prototype.addEventListeners = function(listeners) {
|
||||
var self = this;
|
||||
$tw.utils.each(listeners,function(listenerInfo) {
|
||||
self.addEventListener(listenerInfo.type,listenerInfo.handler);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Add an event listener
|
||||
*/
|
||||
Widget.prototype.addEventListener = function(type,handler) {
|
||||
var self = this;
|
||||
if(typeof handler === "string") { // The handler is a method name on this widget
|
||||
this.eventListeners[type] = function(event) {
|
||||
return self[handler].call(self,event);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Dispatch an event to a widget. If the widget doesn't handle the event then it is also dispatched to the parent widget
|
||||
*/
|
||||
Widget.prototype.dispatchEvent = function(event) {
|
||||
// Dispatch the event if this widget handles it
|
||||
var listener = this.eventListeners[event.type];
|
||||
if(listener) {
|
||||
// Don't propagate the event if the listener returned false
|
||||
if(!listener(event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Dispatch the event to the parent widget
|
||||
if(this.parentWidget) {
|
||||
return this.parentWidget.dispatchEvent(event);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
Widget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Rebuild a previously rendered widget
|
||||
*/
|
||||
Widget.prototype.refreshSelf = function() {
|
||||
var nextSibling = this.findNextSibling();
|
||||
this.removeChildDomNodes();
|
||||
this.render(this.parentDomNode,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Refresh all the children of a widget
|
||||
*/
|
||||
Widget.prototype.refreshChildren = function(changedTiddlers) {
|
||||
var self = this,
|
||||
refreshed = false;
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
refreshed = childWidget.refresh(changedTiddlers) || refreshed;
|
||||
});
|
||||
return refreshed;
|
||||
};
|
||||
|
||||
/*
|
||||
Find the next sibling in the DOM to this widget. This is done by scanning the widget tree through all next siblings and their descendents that share the same parent DOM node
|
||||
*/
|
||||
Widget.prototype.findNextSibling = function(startIndex) {
|
||||
// Refer to this widget by its index within its parents children
|
||||
var parent = this.parentWidget,
|
||||
index = startIndex !== undefined ? startIndex : parent.children.indexOf(this);
|
||||
if(index === -1) {
|
||||
throw "node not found in parents children";
|
||||
}
|
||||
// Look for a DOM node in the later siblings
|
||||
while(++index < parent.children.length) {
|
||||
var domNode = parent.children[index].findFirstDomNode();
|
||||
if(domNode) {
|
||||
return domNode;
|
||||
}
|
||||
}
|
||||
// Go back and look for later siblings of our parent if it has the same parent dom node
|
||||
parent = parent.parentWidget;
|
||||
if(parent && parent.parentWidget && parent.parentWidget.parentDomNode === this.parentDomNode) {
|
||||
index = parent.parentWidget.children.indexOf(parent);
|
||||
return parent.findNextSibling(index);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Find the first DOM node generated by a widget or its children
|
||||
*/
|
||||
Widget.prototype.findFirstDomNode = function() {
|
||||
// Return the first dom node of this widget, if we've got one
|
||||
if(this.domNodes.length > 0) {
|
||||
return this.domNodes[0];
|
||||
}
|
||||
// Otherwise, recursively call our children
|
||||
for(var t=0; t<this.children.length; t++) {
|
||||
var domNode = this.children[t].findFirstDomNode();
|
||||
if(domNode) {
|
||||
return domNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
Widget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
childWidget.removeChildDomNodes();
|
||||
});
|
||||
};
|
||||
|
||||
exports.widget = Widget;
|
||||
|
||||
})();
|
33
core/modules/startup.js
Normal file → Executable file
33
core/modules/startup.js
Normal file → Executable file
@ -12,6 +12,8 @@ This is the main application logic for both the client and server
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
var modules,n,m,f,commander;
|
||||
// Load modules
|
||||
@ -102,16 +104,31 @@ exports.startup = function() {
|
||||
$tw.stylesheetManager = new $tw.utils.StylesheetManager($tw.wiki);
|
||||
// Display the PageTemplate
|
||||
var templateTitle = "$:/core/ui/PageTemplate",
|
||||
parser = $tw.wiki.parseTiddler(templateTitle),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: $tw.wiki, context: {tiddlerTitle: templateTitle}, document: document});
|
||||
renderTree.execute();
|
||||
$tw.pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.pageContainer,"tw-page-container");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
renderTree.renderInDom($tw.pageContainer);
|
||||
parser = $tw.wiki.new_parseTiddler(templateTitle),
|
||||
parseTreeNode = parser ? {type: "widget", children: parser.tree} : undefined,
|
||||
widgetNode = new widget.widget(parseTreeNode,{
|
||||
wiki: $tw.wiki,
|
||||
document: document
|
||||
});
|
||||
$tw.new_pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.new_pageContainer,"tw-page-container");
|
||||
document.body.insertBefore($tw.new_pageContainer,document.body.firstChild);
|
||||
widgetNode.render($tw.new_pageContainer,null);
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
renderTree.refreshInDom(changes);
|
||||
widgetNode.refresh(changes,$tw.new_pageContainer,null);
|
||||
});
|
||||
// // Display the old PageTemplate
|
||||
// var old_templateTitle = "$:/core/ui/PageTemplate",
|
||||
// old_parser = $tw.wiki.parseTiddler(old_templateTitle),
|
||||
// renderTree = new $tw.WikiRenderTree(old_parser,{wiki: $tw.wiki, context: {tiddlerTitle: old_templateTitle}, document: document});
|
||||
// renderTree.execute();
|
||||
// $tw.pageContainer = document.createElement("div");
|
||||
// $tw.utils.addClass($tw.pageContainer,"tw-page-container");
|
||||
// document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
// renderTree.renderInDom($tw.pageContainer);
|
||||
// $tw.wiki.addEventListener("change",function(changes) {
|
||||
// renderTree.refreshInDom(changes);
|
||||
// });
|
||||
// If we're being viewed on a data: URI then give instructions for how to save
|
||||
if(document.location.protocol === "data:") {
|
||||
$tw.utils.dispatchCustomEvent(document,"tw-modal",{
|
||||
|
42
core/modules/utils/fakedom.js
Normal file → Executable file
42
core/modules/utils/fakedom.js
Normal file → Executable file
@ -12,16 +12,27 @@ A barebones implementation of DOM interfaces needed by the rendering mechanism.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var TW_TextNode = function(text) {
|
||||
this.textContent = text;
|
||||
// Sequence number used to enable us to track objects for testing
|
||||
var sequenceNumber = null;
|
||||
|
||||
var bumpSequenceNumber = function(object) {
|
||||
if(sequenceNumber !== null) {
|
||||
object.sequenceNumber = sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
var TW_TextNode = function(text) {
|
||||
bumpSequenceNumber(this);
|
||||
this.textContent = text;
|
||||
};
|
||||
|
||||
var TW_Element = function(tag) {
|
||||
bumpSequenceNumber(this);
|
||||
this.tag = tag;
|
||||
this.attributes = {};
|
||||
this.isRaw = false;
|
||||
this.children = [];
|
||||
}
|
||||
};
|
||||
|
||||
TW_Element.prototype.setAttribute = function(name,value) {
|
||||
if(this.isRaw) {
|
||||
@ -39,6 +50,19 @@ TW_Element.prototype.appendChild = function(node) {
|
||||
node.parentNode = this;
|
||||
};
|
||||
|
||||
TW_Element.prototype.insertBefore = function(node,nextSibling) {
|
||||
if(nextSibling) {
|
||||
var p = this.children.indexOf(nextSibling);
|
||||
if(p !== -1) {
|
||||
this.children.splice(p,0,node);
|
||||
} else {
|
||||
this.appendChild(node);
|
||||
}
|
||||
} else {
|
||||
this.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
TW_Element.prototype.removeChild = function(node) {
|
||||
var p = this.children.indexOf(node);
|
||||
if(p !== -1) {
|
||||
@ -60,6 +84,15 @@ TW_Element.prototype.addEventListener = function(type,listener,useCapture) {
|
||||
// Do nothing
|
||||
};
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "className", {
|
||||
get: function() {
|
||||
return this.attributes["class"] || "";
|
||||
},
|
||||
set: function(value) {
|
||||
this.attributes["class"] = value;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "outerHTML", {
|
||||
get: function() {
|
||||
var output = [],attr,a,v;
|
||||
@ -123,6 +156,9 @@ Object.defineProperty(TW_Element.prototype, "textContent", {
|
||||
});
|
||||
|
||||
var document = {
|
||||
setSequenceNumber: function(value) {
|
||||
sequenceNumber = value;
|
||||
},
|
||||
createElementNS: function(namespace,tag) {
|
||||
return new TW_Element(tag);
|
||||
},
|
||||
|
87
core/modules/wiki.js
Normal file → Executable file
87
core/modules/wiki.js
Normal file → Executable file
@ -24,6 +24,8 @@ last dispatched. Each entry is a hashmap containing two fields:
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
var USER_NAME_TITLE = "$:/status/UserName";
|
||||
|
||||
/*
|
||||
@ -545,6 +547,10 @@ exports.getTiddlerList = function(title) {
|
||||
|
||||
// Return the named cache object for a tiddler. If the cache doesn't exist then the initializer function is invoked to create it
|
||||
exports.getCacheForTiddler = function(title,cacheName,initializer) {
|
||||
|
||||
// Temporarily disable caching so that tweakParseTreeNode() works
|
||||
return initializer();
|
||||
|
||||
this.caches = this.caches || {};
|
||||
var caches = this.caches[title];
|
||||
if(caches && caches[cacheName]) {
|
||||
@ -621,6 +627,52 @@ exports.parseTiddler = function(title,options) {
|
||||
}) : null;
|
||||
};
|
||||
|
||||
// We need to tweak parse trees generated by the existing parser because of the change from {type:"element",tag:"$tiddler",...} to {type:"tiddler",...}
|
||||
var tweakParseTreeNode = function(node) {
|
||||
if(node.type === "element" && node.tag.charAt(0) === "$") {
|
||||
node.type = node.tag.substr(1);
|
||||
}
|
||||
tweakParseTreeNodes(node.children);
|
||||
},
|
||||
tweakParseTreeNodes = function(nodeList) {
|
||||
$tw.utils.each(nodeList,tweakParseTreeNode);
|
||||
};
|
||||
|
||||
exports.new_parseText = function(type,text,options) {
|
||||
var parser = this.parseText(type,text,options);
|
||||
if(parser) {
|
||||
tweakParseTreeNodes(parser.tree)
|
||||
};
|
||||
return parser;
|
||||
};
|
||||
|
||||
exports.new_parseTiddler = function(title,options) {
|
||||
var parser = this.parseTiddler(title,options);
|
||||
if(parser) {
|
||||
tweakParseTreeNodes(parser.tree)
|
||||
};
|
||||
return parser;
|
||||
};
|
||||
|
||||
exports.new_parseTextReference = function(title,field,index,options) {
|
||||
if(field === "text" || (!field && !index)) {
|
||||
return this.new_parseTiddler(title,options);
|
||||
} else {
|
||||
var tiddler,text;
|
||||
if(field) {
|
||||
tiddler = this.getTiddler(title);
|
||||
text = tiddler ? tiddler.fields[field] : "";
|
||||
if(text === undefined) {
|
||||
text = "";
|
||||
}
|
||||
return this.new_parseText("text/vnd.tiddlywiki",text,options);
|
||||
} else if(index) {
|
||||
text = this.extractTiddlerDataItem(title,index,"");
|
||||
return this.new_parseText("text/vnd.tiddlywiki",text,options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Parse text in a specified format and render it into another format
|
||||
outputType: content type for the output
|
||||
@ -636,6 +688,24 @@ exports.renderText = function(outputType,textType,text,context) {
|
||||
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
|
||||
textType: content type of the input text
|
||||
text: input text
|
||||
*/
|
||||
exports.new_renderText = function(outputType,textType,text,context) {
|
||||
var parser = $tw.wiki.new_parseText(textType,text),
|
||||
parseTreeNode = parser ? {type: "widget", children: parser.tree} : undefined,
|
||||
widgetNode = new widget.widget(parseTreeNode,{
|
||||
wiki: this,
|
||||
document: $tw.document
|
||||
});
|
||||
var container = $tw.document.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
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
|
||||
@ -650,6 +720,23 @@ exports.renderTiddler = function(outputType,title,context) {
|
||||
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,context) {
|
||||
var parser = $tw.wiki.new_parseTiddler(title),
|
||||
parseTreeNode = parser ? {type: "widget", children: parser.tree} : undefined,
|
||||
widgetNode = new widget.widget(parseTreeNode,{
|
||||
wiki: this,
|
||||
document: $tw.document
|
||||
});
|
||||
var container = $tw.document.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
return outputType === "text/html" ? container.innerHTML : container.textContent;
|
||||
};
|
||||
|
||||
/*
|
||||
Select the appropriate saver modules and set them up
|
||||
*/
|
||||
|
@ -1,3 +1,3 @@
|
||||
title: $:/core/templates/wikified-tiddler
|
||||
|
||||
<$view field="text" format="wikified" />
|
||||
<$transclude />
|
@ -15,7 +15,7 @@ modifier: JeremyRuston
|
||||
<$transclude title="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="no">hide preview</$button>
|
||||
<div class="tw-tiddler-preview">
|
||||
<div class="tw-tiddler-preview-preview">
|
||||
<$view field="text" format="wikified"/>
|
||||
<$transclude />
|
||||
</div>
|
||||
<div class="tw-tiddler-preview-edit">
|
||||
<$edit field="text"/>
|
||||
|
@ -2,5 +2,5 @@ title: $:/core/ui/ViewTemplate/body
|
||||
tags: $:/tags/ViewTemplate
|
||||
|
||||
<div class="body">
|
||||
<$view field="text" format="wikified"/>
|
||||
<$transclude />
|
||||
</div>
|
399
editions/test/tiddlers/tests/test-widget.js
Executable file
399
editions/test/tiddlers/tests/test-widget.js
Executable file
@ -0,0 +1,399 @@
|
||||
/*\
|
||||
title: test-widget.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("Widget module", function() {
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
function createWidgetNode(parseTreeNode,wiki,variables) {
|
||||
return new widget.widget(parseTreeNode,{
|
||||
wiki: wiki,
|
||||
variables: variables || {},
|
||||
document: $tw.document
|
||||
});
|
||||
}
|
||||
|
||||
function parseText(text,wiki) {
|
||||
var parser = wiki.new_parseText("text/vnd.tiddlywiki",text);
|
||||
return parser ? {type: "widget", children: parser.tree} : undefined;
|
||||
}
|
||||
|
||||
function renderWidgetNode(widgetNode) {
|
||||
$tw.document.setSequenceNumber(0);
|
||||
var wrapper = $tw.document.createElement("div");
|
||||
widgetNode.render(wrapper,null);
|
||||
// console.log(require("util").inspect(wrapper,{depth: 8}));
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function refreshWidgetNode(widgetNode,wrapper,changes) {
|
||||
var changedTiddlers = {};
|
||||
if(changes) {
|
||||
$tw.utils.each(changes,function(title) {
|
||||
changedTiddlers[title] = true;
|
||||
});
|
||||
}
|
||||
widgetNode.refresh(changedTiddlers,wrapper,null);
|
||||
// console.log(require("util").inspect(wrapper,{depth: 8}));
|
||||
}
|
||||
|
||||
it("should deal with text nodes and HTML elements", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Test parse tree
|
||||
var parseTreeNode = {type: "widget", children: [
|
||||
{type: "text", text: "A text node"},
|
||||
{type: "element", tag: "div", attributes: {
|
||||
"class": {type: "string", value: "myClass"},
|
||||
"title": {type: "string", value: "myTitle"}
|
||||
}, children: [
|
||||
{type: "text", text: " and the content of a DIV"},
|
||||
{type: "element", tag: "div", children: [
|
||||
{type: "text", text: " and an inner DIV"},
|
||||
]},
|
||||
{type: "text", text: " and back in the outer DIV"}
|
||||
]}
|
||||
]};
|
||||
// Construct the widget node
|
||||
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
describe("should render", function() {
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("A text node<div class='myClass' title='myTitle'>\n and the content of a DIV<div>\n and an inner DIV</div> and back in the outer DIV</div>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[1].children[0].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[1].children[1].sequenceNumber).toBe(4);
|
||||
expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5);
|
||||
expect(wrapper.children[1].children[2].sequenceNumber).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
it("should deal with transclude widgets and indirect attributes", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add a tiddler
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "the quick brown fox"}
|
||||
]);
|
||||
// Test parse tree
|
||||
var parseTreeNode = {type: "widget", children: [
|
||||
{type: "text", text: "A text node"},
|
||||
{type: "element", tag: "div", attributes: {
|
||||
"class": {type: "string", value: "myClass"},
|
||||
"title": {type: "indirect", textReference: "TiddlerOne"}
|
||||
}, children: [
|
||||
{type: "text", text: " and the content of a DIV"},
|
||||
{type: "element", tag: "div", children: [
|
||||
{type: "text", text: " and an inner DIV"},
|
||||
]},
|
||||
{type: "text", text: " and back in the outer DIV"},
|
||||
{type: "transclude", attributes: {
|
||||
"title": {type: "string", value: "TiddlerOne"}
|
||||
}}
|
||||
]},
|
||||
{type: "transclude", attributes: {
|
||||
"title": {type: "string", value: "TiddlerOne"}
|
||||
}}
|
||||
]};
|
||||
// Construct the widget node
|
||||
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
describe("should render", function() {
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("A text node<div class='myClass' title='the quick brown fox'>\n and the content of a DIV<div>\n and an inner DIV</div> and back in the outer DIVthe quick brown fox</div>the quick brown fox");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[1].children[0].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[1].children[1].sequenceNumber).toBe(4);
|
||||
expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5);
|
||||
expect(wrapper.children[1].children[2].sequenceNumber).toBe(6);
|
||||
expect(wrapper.children[1].children[3].sequenceNumber).toBe(7);
|
||||
expect(wrapper.children[2].sequenceNumber).toBe(8);
|
||||
});
|
||||
// Change the transcluded tiddler
|
||||
wiki.addTiddler({title: "TiddlerOne", text: "jumps over the lazy dog"});
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerOne"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("A text node<div class='myClass' title='jumps over the lazy dog'>\n and the content of a DIV<div>\n and an inner DIV</div> and back in the outer DIVjumps over the lazy dog</div>jumps over the lazy dog");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[1].children[0].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[1].children[1].sequenceNumber).toBe(4);
|
||||
expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5);
|
||||
expect(wrapper.children[1].children[2].sequenceNumber).toBe(6);
|
||||
expect(wrapper.children[1].children[3].sequenceNumber).toBe(9);
|
||||
expect(wrapper.children[2].sequenceNumber).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
it("should detect recursion of the transclude macro", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add a tiddler
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "<$transclude title='TiddlerOne'/>\n"}
|
||||
]);
|
||||
// Test parse tree
|
||||
var parseTreeNode = {type: "widget", children: [
|
||||
{type: "transclude", attributes: {
|
||||
"title": {type: "string", value: "TiddlerOne"}
|
||||
}}
|
||||
]};
|
||||
// Construct the widget node
|
||||
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
describe("should detect the recursion", function() {
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("Tiddler recursion error in transclude widget\n");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should parse and render transclusions", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add a tiddler
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "Jolly Old World"},
|
||||
{title: "TiddlerTwo", text: "<$transclude title={{TiddlerThree}}/>"},
|
||||
{title: "TiddlerThree", text: "TiddlerOne"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "My <$transclude title='TiddlerTwo'/> is Jolly"
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nMy Jolly Old World is Jolly</p>");
|
||||
});
|
||||
|
||||
it("should render the view widget", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add a tiddler
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "Jolly Old World"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "<$view title='TiddlerOne'/>";
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nJolly Old World</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(2);
|
||||
// Change the transcluded tiddler
|
||||
wiki.addTiddler({title: "TiddlerOne", text: "World-wide Jelly"});
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerOne"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nWorld-wide Jelly</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
it("should deal with the setvariable widget", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add some tiddlers
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "Jolly Old World"},
|
||||
{title: "TiddlerTwo", text: "<$transclude title={{TiddlerThree}}/>"},
|
||||
{title: "TiddlerThree", text: "TiddlerOne"},
|
||||
{title: "TiddlerFour", text: "TiddlerTwo"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "My <$setvariable name='tiddlerTitle' value={{TiddlerFour}}><$transclude title={{!!title}}/></$setvariable> is Jolly"
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nMy Jolly Old World is Jolly</p>");
|
||||
// Change the transcluded tiddler
|
||||
wiki.addTiddler({title: "TiddlerFour", text: "TiddlerOne"});
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerFour"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nMy Jolly Old World is Jolly</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[0].children[1].sequenceNumber).toBe(5);
|
||||
expect(wrapper.children[0].children[2].sequenceNumber).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
it("should deal with attributes specified as macro invocations", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Construct the widget node
|
||||
var text = "<div class=<<myMacro 'something' three:'thing'>>>Content</div>";
|
||||
var variables = {
|
||||
myMacro: {
|
||||
value: "My something $one$, $two$ or other $three$",
|
||||
params: [
|
||||
{name: "one", "default": "paramOne"},
|
||||
{name: "two"},
|
||||
{name: "three", "default": "paramTwo"}
|
||||
]
|
||||
}
|
||||
};
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki,variables);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\n<div class='My something something, or other thing'>\nContent</div></p>");
|
||||
});
|
||||
|
||||
it("should deal with the list widget", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add some tiddlers
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "Jolly Old World"},
|
||||
{title: "TiddlerTwo", text: "Worldly Old Jelly"},
|
||||
{title: "TiddlerThree", text: "Golly Gosh"},
|
||||
{title: "TiddlerFour", text: "Lemon Squash"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "<$list><$view field='title'/></$list>";
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo</p>");
|
||||
// Add another tiddler
|
||||
wiki.addTiddler({title: "TiddlerFive", text: "Jalapeno Peppers"});
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerFive"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(6);
|
||||
expect(wrapper.children[0].children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[0].children[2].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[0].children[3].sequenceNumber).toBe(4);
|
||||
expect(wrapper.children[0].children[4].sequenceNumber).toBe(5);
|
||||
});
|
||||
// Remove a tiddler
|
||||
wiki.deleteTiddler("TiddlerThree");
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFiveTiddlerFourTiddlerOneTiddlerTwo</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(6);
|
||||
expect(wrapper.children[0].children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[0].children[2].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[0].children[3].sequenceNumber).toBe(5);
|
||||
});
|
||||
// Add it back a tiddler
|
||||
wiki.addTiddler({title: "TiddlerThree", text: "Something"});
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo</p>");
|
||||
// Test the sequence numbers in the DOM
|
||||
expect(wrapper.sequenceNumber).toBe(0);
|
||||
expect(wrapper.children[0].sequenceNumber).toBe(1);
|
||||
expect(wrapper.children[0].children[0].sequenceNumber).toBe(6);
|
||||
expect(wrapper.children[0].children[1].sequenceNumber).toBe(2);
|
||||
expect(wrapper.children[0].children[2].sequenceNumber).toBe(3);
|
||||
expect(wrapper.children[0].children[3].sequenceNumber).toBe(7);
|
||||
expect(wrapper.children[0].children[4].sequenceNumber).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
it("should deal with the list widget and external templates", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add some tiddlers
|
||||
wiki.addTiddlers([
|
||||
{title: "$:/myTemplate", text: "<$tiddler title=<<listItem>>><$view field='title'/></$tiddler>"},
|
||||
{title: "TiddlerOne", text: "Jolly Old World"},
|
||||
{title: "TiddlerTwo", text: "Worldly Old Jelly"},
|
||||
{title: "TiddlerThree", text: "Golly Gosh"},
|
||||
{title: "TiddlerFour", text: "Lemon Squash"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "<$list template='$:/myTemplate'></$list>";
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo</p>");
|
||||
});
|
||||
|
||||
it("should deal with the list widget and empty lists", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Construct the widget node
|
||||
var text = "<$list emptyMessage='nothing'><$view field='title'/></$list>";
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nnothing</p>");
|
||||
});
|
||||
|
||||
it("should refresh lists that become empty", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
// Add some tiddlers
|
||||
wiki.addTiddlers([
|
||||
{title: "TiddlerOne", text: "Jolly Old World"},
|
||||
{title: "TiddlerTwo", text: "Worldly Old Jelly"},
|
||||
{title: "TiddlerThree", text: "Golly Gosh"},
|
||||
{title: "TiddlerFour", text: "Lemon Squash"}
|
||||
]);
|
||||
// Construct the widget node
|
||||
var text = "<$list emptyMessage='nothing'><$view field='title'/></$list>";
|
||||
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||
// Render the widget node to the DOM
|
||||
var wrapper = renderWidgetNode(widgetNode);
|
||||
// Test the rendering
|
||||
expect(wrapper.innerHTML).toBe("<p>\nTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo</p>");
|
||||
// Get rid of the tiddlers
|
||||
wiki.deleteTiddler("TiddlerOne");
|
||||
wiki.deleteTiddler("TiddlerTwo");
|
||||
wiki.deleteTiddler("TiddlerThree");
|
||||
wiki.deleteTiddler("TiddlerFour");
|
||||
// Refresh
|
||||
refreshWidgetNode(widgetNode,wrapper,["TiddlerOne","TiddlerTwo","TiddlerThree","TiddlerFour"]);
|
||||
describe("should refresh", function() {
|
||||
// Test the refreshing
|
||||
expect(wrapper.innerHTML).toBe("<p>\nnothing</p>");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -83,5 +83,5 @@ The following commands are available:
|
||||
|
||||
<$list filter="[tag[command]sort[title]]">
|
||||
!!! <$view field="title" format="link"/>
|
||||
<$view field="text" format="wikified"/>
|
||||
<$transclude />
|
||||
</$list>
|
||||
|
28
nbld.sh
Executable file
28
nbld.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Testing the new widget mechanism
|
||||
|
||||
# Set up the build output directory
|
||||
|
||||
if [ -z "$TW5_BUILD_OUTPUT" ]; then
|
||||
TW5_BUILD_OUTPUT=../jermolene.github.com
|
||||
fi
|
||||
|
||||
if [ ! -d "$TW5_BUILD_OUTPUT" ]; then
|
||||
echo 'A valid TW5_BUILD_OUTPUT environment variable must be set'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_OUTPUT as [$TW5_BUILD_OUTPUT]"
|
||||
|
||||
# Build it
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tw5.com \
|
||||
--verbose \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Run tests
|
||||
|
||||
./test.sh
|
Loading…
Reference in New Issue
Block a user