mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-22 10:54:46 +00:00
Compare commits
69 Commits
revised-js
...
twitter-ar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f922149b32 | ||
|
|
344110e289 | ||
|
|
ae12e8fb69 | ||
|
|
e8148ff978 | ||
|
|
965bd090a9 | ||
|
|
3be9b13814 | ||
|
|
62f26d6630 | ||
|
|
8fe2f6086d | ||
|
|
30af537b91 | ||
|
|
f54ecc23f3 | ||
|
|
67dd3f06bf | ||
|
|
6af3eb539b | ||
|
|
3f55f827a6 | ||
|
|
b9d27e9fd5 | ||
|
|
5b85786f73 | ||
|
|
24dbf69180 | ||
|
|
7b408c7adf | ||
|
|
c19d6d6328 | ||
|
|
941c09fae2 | ||
|
|
b531984f50 | ||
|
|
8f079e2d45 | ||
|
|
cfd894e6fb | ||
|
|
91327c1af0 | ||
|
|
6f98edd6bd | ||
|
|
23e0eeb556 | ||
|
|
f33c7e2aef | ||
|
|
fc586481a9 | ||
|
|
09179d2f62 | ||
|
|
d6ff38095b | ||
|
|
3bbbe77471 | ||
|
|
5657dfec0e | ||
|
|
31ab6dce77 | ||
|
|
4007610d52 | ||
|
|
fb34df84ed | ||
|
|
79e2e317cf | ||
|
|
36896c3db8 | ||
|
|
82de7c9b1a | ||
|
|
1841b0fa4f | ||
|
|
47f80339b2 | ||
|
|
db6abb9703 | ||
|
|
8d1e6b5d23 | ||
|
|
1df4c29d73 | ||
|
|
81e4745c56 | ||
|
|
87597ea273 | ||
|
|
4e9267ea58 | ||
|
|
dd66fcc759 | ||
|
|
8ebb9ef442 | ||
|
|
81ac987484 | ||
|
|
0a00da6db9 | ||
|
|
53d229592d | ||
|
|
166a156584 | ||
|
|
c5d3d4c26e | ||
|
|
51bdf60ee8 | ||
|
|
50f54ba6ca | ||
|
|
1a0ab68dec | ||
|
|
bb67f96562 | ||
|
|
a981f8ccfe | ||
|
|
859d15a446 | ||
|
|
debfd42d51 | ||
|
|
fe365354d0 | ||
|
|
d825f1c875 | ||
|
|
fe74a776e9 | ||
|
|
93e1a632b8 | ||
|
|
9b7edfc1a7 | ||
|
|
3b2c64a85b | ||
|
|
35b9faaa89 | ||
|
|
11b258a14b | ||
|
|
c8ad385947 | ||
|
|
c1f6e02d14 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
.c9/
|
||||
.vscode/
|
||||
tmp/
|
||||
output/
|
||||
node_modules/
|
||||
|
||||
@@ -233,6 +233,15 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/twitter-archivist/index.html Twitter Archivist edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/twitter-archivist \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/twitter-archivist/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Plugin demos
|
||||
|
||||
15
boot/boot.js
15
boot/boot.js
@@ -1230,13 +1230,16 @@ $tw.Wiki = function(options) {
|
||||
this.getTiddler = function(title) {
|
||||
if(title) {
|
||||
var t = tiddlers[title];
|
||||
if(t instanceof $tw.Tiddler) {
|
||||
if(t !== undefined) {
|
||||
return t;
|
||||
} else if(title !== undefined && shadowTiddlers[title]) {
|
||||
return shadowTiddlers[title].tiddler;
|
||||
} else {
|
||||
var s = shadowTiddlers[title];
|
||||
if(s !== undefined) {
|
||||
return s.tiddler;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Get an array of all tiddler titles
|
||||
@@ -2400,11 +2403,11 @@ $tw.boot.initStartup = function(options) {
|
||||
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
|
||||
$tw.utils.registerFileType("application/font-woff2","base64",".woff2");
|
||||
$tw.utils.registerFileType("audio/ogg","base64",".ogg");
|
||||
$tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]);
|
||||
$tw.utils.registerFileType("video/ogg","base64",[".ogm",".ogv",".ogg"]);
|
||||
$tw.utils.registerFileType("video/webm","base64",".webm");
|
||||
$tw.utils.registerFileType("video/mp4","base64",".mp4");
|
||||
$tw.utils.registerFileType("audio/mp3","base64",".mp3");
|
||||
$tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]);
|
||||
$tw.utils.registerFileType("text/markdown","utf8",[".md",".markdown"],{deserializerType:"text/x-markdown"});
|
||||
$tw.utils.registerFileType("text/x-markdown","utf8",[".md",".markdown"]);
|
||||
$tw.utils.registerFileType("application/enex+xml","utf8",".enex");
|
||||
@@ -2416,7 +2419,7 @@ $tw.boot.initStartup = function(options) {
|
||||
$tw.utils.registerFileType("application/epub+zip","base64",".epub");
|
||||
$tw.utils.registerFileType("application/octet-stream","base64",".octet-stream");
|
||||
// Create the wiki store for the app
|
||||
$tw.wiki = new $tw.Wiki();
|
||||
$tw.wiki = new $tw.Wiki($tw.safeMode && {enableIndexers: []});
|
||||
// Install built in tiddler fields modules
|
||||
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
|
||||
// Install the tiddler deserializer modules
|
||||
|
||||
@@ -13,7 +13,7 @@ dependents: For a plugin, lists the dependent plugin titles
|
||||
description: The descriptive text for a plugin, or a modal dialogue
|
||||
draft.of: For draft tiddlers, contains the title of the tiddler of which this is a draft
|
||||
draft.title: For draft tiddlers, contains the proposed new title of the tiddler
|
||||
footer: The footer text for a wizard
|
||||
footer: The footer text for a modal
|
||||
hide-body: The view template will hide bodies of tiddlers if set to ''yes''
|
||||
icon: The title of the tiddler containing the icon associated with a tiddler
|
||||
library: Indicates that a tiddler should be saved as a JavaScript library if set to ''yes''
|
||||
@@ -28,7 +28,7 @@ plugin-type: The type of plugin in a plugin tiddler
|
||||
revision: The revision of the tiddler held at the server
|
||||
released: Date of a TiddlyWiki release
|
||||
source: The source URL associated with a tiddler
|
||||
subtitle: The subtitle text for a wizard
|
||||
subtitle: The subtitle text for a modal
|
||||
tags: A list of tags associated with a tiddler
|
||||
text: The body text of a tiddler
|
||||
throttle.refresh: If present, throttles refreshes of this tiddler
|
||||
|
||||
@@ -87,7 +87,7 @@ function FramedEngine(options) {
|
||||
$tw.utils.addEventListeners(this.domNode,[
|
||||
{name: "click",handlerObject: this,handlerMethod: "handleClickEvent"},
|
||||
{name: "input",handlerObject: this,handlerMethod: "handleInputEvent"},
|
||||
{name: "keydown",handlerObject: this.widget,handlerMethod: "handleKeydownEvent"},
|
||||
{name: "keydown",handlerObject: this,handlerMethod: "handleKeydownEvent"},
|
||||
{name: "focus",handlerObject: this,handlerMethod: "handleFocusEvent"}
|
||||
]);
|
||||
// Add drag and drop event listeners if fileDrop is enabled
|
||||
@@ -192,6 +192,17 @@ FramedEngine.prototype.handleFocusEvent = function(event) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a keydown event
|
||||
*/
|
||||
FramedEngine.prototype.handleKeydownEvent = function(event) {
|
||||
if ($tw.keyboardManager.handleKeydownEvent(event, {onlyPriority: true})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.widget.handleKeydownEvent(event);
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a click
|
||||
*/
|
||||
|
||||
@@ -12,6 +12,9 @@ Adds tiddler filtering methods to the $tw.Wiki object.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/* Maximum permitted filter recursion depth */
|
||||
var MAX_FILTER_DEPTH = 300;
|
||||
|
||||
/*
|
||||
Parses an operation (i.e. a run) within a filter string
|
||||
operators: Array of array of operator nodes into which results should be inserted
|
||||
@@ -328,7 +331,7 @@ exports.compileFilter = function(filterString) {
|
||||
})());
|
||||
});
|
||||
// Return a function that applies the operations to a source iterator of tiddler titles
|
||||
var compiled = $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
|
||||
var fnMeasured = $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
|
||||
if(!source) {
|
||||
source = self.each;
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
@@ -338,9 +341,15 @@ exports.compileFilter = function(filterString) {
|
||||
widget = $tw.rootWidget;
|
||||
}
|
||||
var results = new $tw.utils.LinkedList();
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
} else {
|
||||
results.push("/**-- Excessive filter recursion --**/");
|
||||
}
|
||||
self.filterRecursionCount = self.filterRecursionCount - 1;
|
||||
return results.toArray();
|
||||
});
|
||||
if(this.filterCacheCount >= 2000) {
|
||||
@@ -350,9 +359,9 @@ exports.compileFilter = function(filterString) {
|
||||
this.filterCache = Object.create(null);
|
||||
this.filterCacheCount = 0;
|
||||
}
|
||||
this.filterCache[filterString] = compiled;
|
||||
this.filterCache[filterString] = fnMeasured;
|
||||
this.filterCacheCount++;
|
||||
return compiled;
|
||||
return fnMeasured;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
35
core/modules/filters/format/json.js
Normal file
35
core/modules/filters/format/json.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/format/json.js
|
||||
type: application/javascript
|
||||
module-type: formatfilteroperator
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.json = function(source,operand,options) {
|
||||
var results = [],
|
||||
spaces = null;
|
||||
if(operand) {
|
||||
spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand;
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title);
|
||||
try {
|
||||
data = JSON.parse(title);
|
||||
} catch(e) {
|
||||
data = undefined;
|
||||
}
|
||||
if(data !== undefined) {
|
||||
results.push(JSON.stringify(data,null,spaces));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -19,13 +19,13 @@ exports.variable = function(source,prefix,options) {
|
||||
var results = [];
|
||||
if(prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(!(title in options.widget.variables)) {
|
||||
if(options.widget.getVariable(title) === undefined) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(title in options.widget.variables) {
|
||||
if(options.widget.getVariable(title) !== undefined) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
|
||||
153
core/modules/filters/json-ops.js
Normal file
153
core/modules/filters/json-ops.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/json-ops.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operators for JSON operations
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["jsonget"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
var item = getDataItemValueAsString(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsonindexes"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
var item = getDataItemKeysAsStrings(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push.apply(results,item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsontype"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
var item = getDataItemType(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItemValueAsString(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item as a string
|
||||
return convertDataItemValueToString(item);
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the keys of the item at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItemKeysAsStrings(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item keys as a string
|
||||
return convertDataItemKeysToStrings(item);
|
||||
}
|
||||
|
||||
/*
|
||||
Return an array of the string representation of the values of a data item, or "undefined" if the item is undefined
|
||||
*/
|
||||
function convertDataItemValueToString(item) {
|
||||
// Return the item as a string
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
}
|
||||
if(typeof item === "object") {
|
||||
return JSON.stringify(item);
|
||||
}
|
||||
return item.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
Return an array of the string representation of the keys of a data item, or "undefined" if the item is undefined
|
||||
*/
|
||||
function convertDataItemKeysToStrings(item) {
|
||||
// Return the item as a string
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
} else if(typeof item === "object") {
|
||||
if(item === null) {
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
if($tw.utils.isArray(item)) {
|
||||
for(var i=0; i<item.length; i++) {
|
||||
results.push(i.toString());
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
$tw.utils.each(Object.keys(item).sort(),function(key) {
|
||||
results.push(key);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getDataItemType(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item type
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
} else if(item === null) {
|
||||
return "null";
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
return "array";
|
||||
} else if(typeof item === "object") {
|
||||
return "object";
|
||||
} else {
|
||||
return typeof item;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Get the item
|
||||
var item = data;
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
if(item !== undefined) {
|
||||
item = item[indexes[i]];
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -16,9 +16,15 @@ Filter operator for returning the names of the active variables
|
||||
Export our filter function
|
||||
*/
|
||||
exports.variables = function(source,operator,options) {
|
||||
var names = [];
|
||||
for(var variable in options.widget.variables) {
|
||||
names.push(variable);
|
||||
var names = [],
|
||||
widget = options.widget;
|
||||
while(widget && !widget.hasOwnProperty("variables")) {
|
||||
widget = widget.parentWidget;
|
||||
}
|
||||
if(widget && widget.variables) {
|
||||
for(var variable in widget.variables) {
|
||||
names.push(variable);
|
||||
}
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
@@ -141,6 +141,7 @@ function KeyboardManager(options) {
|
||||
this.shortcutKeysList = [], // Stores the shortcut-key descriptors
|
||||
this.shortcutActionList = [], // Stores the corresponding action strings
|
||||
this.shortcutParsedList = []; // Stores the parsed key descriptors
|
||||
this.shortcutPriorityList = []; // Stores the parsed shortcut priority
|
||||
this.lookupNames = ["shortcuts"];
|
||||
this.lookupNames.push($tw.platform.isMac ? "shortcuts-mac" : "shortcuts-not-mac")
|
||||
this.lookupNames.push($tw.platform.isWindows ? "shortcuts-windows" : "shortcuts-not-windows");
|
||||
@@ -318,12 +319,23 @@ KeyboardManager.prototype.updateShortcutLists = function(tiddlerList) {
|
||||
this.shortcutKeysList[i] = tiddlerFields.key !== undefined ? tiddlerFields.key : undefined;
|
||||
this.shortcutActionList[i] = tiddlerFields.text;
|
||||
this.shortcutParsedList[i] = this.shortcutKeysList[i] !== undefined ? this.parseKeyDescriptors(this.shortcutKeysList[i]) : undefined;
|
||||
this.shortcutPriorityList[i] = tiddlerFields.priority === "yes" ? true : false;
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.handleKeydownEvent = function(event) {
|
||||
/*
|
||||
event: the keyboard event object
|
||||
options:
|
||||
onlyPriority: true if only priority global shortcuts should be invoked
|
||||
*/
|
||||
KeyboardManager.prototype.handleKeydownEvent = function(event, options) {
|
||||
options = options || {};
|
||||
var key, action;
|
||||
for(var i=0; i<this.shortcutTiddlers.length; i++) {
|
||||
if(options.onlyPriority && this.shortcutPriorityList[i] !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.shortcutParsedList[i] !== undefined && this.checkKeyDescriptors(event,this.shortcutParsedList[i])) {
|
||||
key = this.shortcutParsedList[i];
|
||||
action = this.shortcutActionList[i];
|
||||
|
||||
@@ -41,9 +41,6 @@ exports.parse = function() {
|
||||
var node = {
|
||||
type: "element",
|
||||
tag: "span",
|
||||
attributes: {
|
||||
"class": {type: "string", value: "tc-inline-style"}
|
||||
},
|
||||
children: tree
|
||||
};
|
||||
if(classString) {
|
||||
@@ -52,6 +49,9 @@ exports.parse = function() {
|
||||
if(stylesString) {
|
||||
$tw.utils.addAttributeToParseTreeNode(node,"style",stylesString);
|
||||
}
|
||||
if(!classString && !stylesString) {
|
||||
$tw.utils.addClassToParseTreeNode(node,"tc-inline-style");
|
||||
}
|
||||
return [node];
|
||||
};
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ WikiParser.prototype.loadRemoteTiddler = function(url) {
|
||||
*/
|
||||
WikiParser.prototype.setupRules = function(proto,configPrefix) {
|
||||
var self = this;
|
||||
if(!$tw.safemode) {
|
||||
if(!$tw.safeMode) {
|
||||
$tw.utils.each(proto,function(object,name) {
|
||||
if(self.wiki.getTiddlerText(configPrefix + name,"enable") !== "enable") {
|
||||
delete proto[name];
|
||||
|
||||
@@ -30,6 +30,16 @@ exports.handler = function(request,response,state) {
|
||||
if(fields.revision) {
|
||||
delete fields.revision;
|
||||
}
|
||||
// If this is a skinny tiddler, it means the client never got the full
|
||||
// version of the tiddler to edit. So we must preserve whatever text
|
||||
// already exists on the server, or else we'll inadvertently delete it.
|
||||
if(fields._is_skinny !== undefined) {
|
||||
var tiddler = state.wiki.getTiddler(title);
|
||||
if(tiddler) {
|
||||
fields.text = tiddler.fields.text;
|
||||
}
|
||||
delete fields._is_skinny;
|
||||
}
|
||||
state.wiki.addTiddler(new $tw.Tiddler(fields,{title: title}));
|
||||
var changeCount = state.wiki.getChangeCount(title).toString();
|
||||
response.writeHead(204, "OK",{
|
||||
|
||||
@@ -42,7 +42,7 @@ var TW_TextNode = function(text) {
|
||||
this.textContent = text + "";
|
||||
};
|
||||
|
||||
TW_TextNode.prototype = Object.create(TW_Node.prototype);
|
||||
Object.setPrototypeOf(TW_TextNode,TW_Node.prototype);
|
||||
|
||||
Object.defineProperty(TW_TextNode.prototype, "nodeType", {
|
||||
get: function() {
|
||||
@@ -67,7 +67,7 @@ var TW_Element = function(tag,namespace) {
|
||||
this.namespaceURI = namespace || "http://www.w3.org/1999/xhtml";
|
||||
};
|
||||
|
||||
TW_Element.prototype = Object.create(TW_Node.prototype);
|
||||
Object.setPrototypeOf(TW_Element,TW_Node.prototype);
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "style", {
|
||||
get: function() {
|
||||
|
||||
@@ -48,7 +48,9 @@ Logger.prototype.log = function(/* args */) {
|
||||
this.saveBufferLogger.buffer = this.saveBufferLogger.buffer.slice(-this.saveBufferLogger.saveLimit);
|
||||
}
|
||||
if(console !== undefined && console.log !== undefined) {
|
||||
return Function.apply.call(console.log, console, [$tw.utils.terminalColour(this.colour),this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0)).concat($tw.utils.terminalColour()));
|
||||
var logMessage = [$tw.utils.terminalColour(this.colour) + this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0));
|
||||
logMessage[logMessage.length-1] += $tw.utils.terminalColour();
|
||||
return Function.apply.call(console.log, console, logMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,12 +12,26 @@ Parse tree utility functions.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Add attribute to parse tree node
|
||||
Can be invoked as (node,name,value) or (node,attr)
|
||||
*/
|
||||
exports.addAttributeToParseTreeNode = function(node,name,value) {
|
||||
var attribute = {name: name, type: "string", value: value};
|
||||
var attribute = typeof name === "object" ? name : {name: name, type: "string", value: value};
|
||||
name = attribute.name;
|
||||
node.attributes = node.attributes || {};
|
||||
node.orderedAttributes = node.orderedAttributes || [];
|
||||
node.attributes[name] = attribute;
|
||||
if(node.orderedAttributes) {
|
||||
var foundIndex = -1;
|
||||
$tw.utils.each(node.orderedAttributes,function(attr,index) {
|
||||
if(attr.name === name) {
|
||||
foundIndex = index;
|
||||
}
|
||||
});
|
||||
if(foundIndex === -1) {
|
||||
node.orderedAttributes.push(attribute);
|
||||
} else {
|
||||
node.orderedAttributes[foundIndex] = attribute;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,10 +65,8 @@ exports.addClassToParseTreeNode = function(node,classString) {
|
||||
// If the class attribute does not exist, we must create it first.
|
||||
attribute = {name: "class", type: "string", value: ""};
|
||||
node.attributes["class"] = attribute;
|
||||
if(node.orderedAttributes) {
|
||||
// If there are orderedAttributes, we've got to add them there too.
|
||||
node.orderedAttributes.push(attribute);
|
||||
}
|
||||
node.orderedAttributes = node.orderedAttributes || [];
|
||||
node.orderedAttributes.push(attribute);
|
||||
}
|
||||
if(attribute.type === "string") {
|
||||
if(attribute.value !== "") {
|
||||
@@ -74,10 +86,8 @@ exports.addStyleToParseTreeNode = function(node,name,value) {
|
||||
if(!attribute) {
|
||||
attribute = {name: "style", type: "string", value: ""};
|
||||
node.attributes.style = attribute;
|
||||
if(node.orderedAttributes) {
|
||||
// If there are orderedAttributes, we've got to add them there too.
|
||||
node.orderedAttributes.push(attribute);
|
||||
}
|
||||
node.orderedAttributes = node.orderedAttributes || [];
|
||||
node.orderedAttributes.push(attribute);
|
||||
}
|
||||
if(attribute.type === "string") {
|
||||
attribute.value += name + ":" + value + ";";
|
||||
|
||||
@@ -66,14 +66,14 @@ CheckboxWidget.prototype.render = function(parent,nextSibling) {
|
||||
CheckboxWidget.prototype.getValue = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.checkboxTitle);
|
||||
if(tiddler || this.checkboxFilter) {
|
||||
if(this.checkboxTag) {
|
||||
if(tiddler && this.checkboxTag) {
|
||||
if(this.checkboxInvertTag === "yes") {
|
||||
return !tiddler.hasTag(this.checkboxTag);
|
||||
} else {
|
||||
return tiddler.hasTag(this.checkboxTag);
|
||||
}
|
||||
}
|
||||
if(this.checkboxField || this.checkboxIndex) {
|
||||
if(tiddler && (this.checkboxField || this.checkboxIndex)) {
|
||||
// Same logic applies to fields and indexes
|
||||
var value;
|
||||
if(this.checkboxField) {
|
||||
@@ -206,11 +206,18 @@ CheckboxWidget.prototype.handleChangeEvent = function(event) {
|
||||
}
|
||||
// Set the list field (or index) if specified
|
||||
if(this.checkboxListField || this.checkboxListIndex) {
|
||||
var listContents, oldPos, newPos;
|
||||
var fieldContents, listContents, oldPos, newPos;
|
||||
if(this.checkboxListField) {
|
||||
listContents = tiddler.getFieldList(this.checkboxListField);
|
||||
fieldContents = tiddler ? tiddler.fields[this.checkboxListField] : undefined;
|
||||
} else {
|
||||
listContents = $tw.utils.parseStringArray(this.wiki.extractTiddlerDataItem(this.checkboxTitle,this.checkboxListIndex) || "") || [];
|
||||
fieldContents = this.wiki.extractTiddlerDataItem(this.checkboxTitle,this.checkboxListIndex);
|
||||
}
|
||||
if($tw.utils.isArray(fieldContents)) {
|
||||
// Make a copy so we can modify it without changing original that's refrenced elsewhere
|
||||
listContents = fieldContents.slice(0);
|
||||
} else {
|
||||
listContents = $tw.utils.parseStringArray(fieldContents) || [];
|
||||
// No need to copy since parseStringArray returns a fresh array, not refrenced elsewhere
|
||||
}
|
||||
oldPos = notValue ? listContents.indexOf(notValue) : -1;
|
||||
newPos = value ? listContents.indexOf(value) : -1;
|
||||
|
||||
63
core/modules/widgets/error.js
Normal file
63
core/modules/widgets/error.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/error.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Error widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ErrorWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ErrorWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ErrorWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var message = this.getAttribute("$message","Unknown error"),
|
||||
domNode = this.document.createElement("span");
|
||||
domNode.appendChild(this.document.createTextNode(message));
|
||||
domNode.className = "tc-error";
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ErrorWidget.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
|
||||
*/
|
||||
ErrorWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes["$message"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.error = ErrorWidget;
|
||||
|
||||
})();
|
||||
108
core/modules/widgets/genesis.js
Normal file
108
core/modules/widgets/genesis.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/genesis.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Genesis widget for dynamically creating widgets
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var GenesisWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
GenesisWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
GenesisWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes({filterFn: function(name) {
|
||||
// Only compute our own attributes which start with a single dollar
|
||||
return name.charAt(0) === "$" && name.charAt(1) !== "$";
|
||||
}});
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
GenesisWidget.prototype.execute = function() {
|
||||
var self = this;
|
||||
// Collect attributes
|
||||
this.genesisType = this.getAttribute("$type","element");
|
||||
this.genesisRemappable = this.getAttribute("$remappable","yes") === "yes";
|
||||
this.genesisNames = this.getAttribute("$names","");
|
||||
this.genesisValues = this.getAttribute("$values","");
|
||||
// Construct parse tree
|
||||
var isElementWidget = this.genesisType.charAt(0) !== "$",
|
||||
nodeType = isElementWidget ? "element" : this.genesisType.substr(1),
|
||||
nodeTag = isElementWidget ? this.genesisType : undefined;
|
||||
var parseTreeNodes = [{
|
||||
type: nodeType,
|
||||
tag: nodeTag,
|
||||
attributes: {},
|
||||
orderedAttributes: [],
|
||||
children: this.parseTreeNode.children || [],
|
||||
isNotRemappable: !this.genesisRemappable
|
||||
}];
|
||||
// Apply explicit attributes
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
|
||||
var name = attribute.name;
|
||||
if(name.charAt(0) === "$") {
|
||||
if(name.charAt(1) === "$") {
|
||||
// Double $$ is changed to a single $
|
||||
name = name.substr(1);
|
||||
} else {
|
||||
// Single dollar is ignored
|
||||
return;
|
||||
}
|
||||
}
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],$tw.utils.extend({},attribute,{name: name}));
|
||||
});
|
||||
// Apply attributes in $names/$values
|
||||
this.attributeNames = [];
|
||||
this.attributeValues = [];
|
||||
if(this.genesisNames && this.genesisValues) {
|
||||
this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this);
|
||||
this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this);
|
||||
$tw.utils.each(this.attributeNames,function(varname,index) {
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],varname,self.attributeValues[index] || "");
|
||||
});
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(parseTreeNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
GenesisWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
filterNames = this.getAttribute("$names",""),
|
||||
filterValues = this.getAttribute("$values",""),
|
||||
attributeNames = this.wiki.filterTiddlers(filterNames,this),
|
||||
attributeValues = this.wiki.filterTiddlers(filterValues,this);
|
||||
if($tw.utils.count(changedAttributes) > 0 || !$tw.utils.isArrayEqual(this.attributeNames,attributeNames) || !$tw.utils.isArrayEqual(this.attributeValues,attributeValues)) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.genesis = GenesisWidget;
|
||||
|
||||
})();
|
||||
@@ -39,7 +39,10 @@ Compute the internal state of the widget
|
||||
ImportVariablesWidget.prototype.execute = function(tiddlerList) {
|
||||
var widgetPointer = this;
|
||||
// Got to flush all the accumulated variables
|
||||
this.variables = new this.variablesConstructor();
|
||||
this.variables = Object.create(null);
|
||||
if(this.parentWidget) {
|
||||
Object.setPrototypeOf(this.variables,this.parentWidget.variables);
|
||||
}
|
||||
// Get our parameters
|
||||
this.filter = this.getAttribute("filter");
|
||||
// Compute the filter
|
||||
|
||||
@@ -53,6 +53,10 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
|
||||
};
|
||||
|
||||
KeyboardWidget.prototype.handleChangeEvent = function(event) {
|
||||
if ($tw.keyboardManager.handleKeydownEvent(event, {onlyPriority: true})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var keyInfo = $tw.keyboardManager.getMatchingKeyDescriptor(event,this.keyInfoArray);
|
||||
if(keyInfo) {
|
||||
var handled = this.invokeActions(this,event);
|
||||
|
||||
@@ -51,11 +51,9 @@ LetWidget.prototype.computeAttributes = function() {
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
|
||||
var value = self.computeAttribute(attribute),
|
||||
name = attribute.name;
|
||||
if(name.charAt(0) !== "$") {
|
||||
// Now that it's prepped, we're allowed to look this variable up
|
||||
// when defining later variables
|
||||
self.currentValueFor[name] = value;
|
||||
}
|
||||
// Now that it's prepped, we're allowed to look this variable up
|
||||
// when defining later variables
|
||||
self.currentValueFor[name] = value;
|
||||
});
|
||||
// Run through again, setting variables and looking for differences
|
||||
$tw.utils.each(this.currentValueFor,function(value,name) {
|
||||
|
||||
@@ -175,6 +175,11 @@ SelectWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return true;
|
||||
// If the target tiddler value has changed, just update setting and refresh the children
|
||||
} else {
|
||||
if(changedAttributes.class) {
|
||||
this.selectClass = this.getAttribute("class");
|
||||
this.getSelectDomNode().setAttribute("class",this.selectClass);
|
||||
}
|
||||
|
||||
var childrenRefreshed = this.refreshChildren(changedTiddlers);
|
||||
if(changedTiddlers[this.selectTitle] || childrenRefreshed) {
|
||||
this.setSelectValue();
|
||||
|
||||
@@ -70,11 +70,9 @@ TranscludeWidget.prototype.execute = function() {
|
||||
// Check for recursion
|
||||
if(parser) {
|
||||
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) {
|
||||
parseTreeNodes = [{type: "element", tag: "span", attributes: {
|
||||
"class": {type: "string", value: "tc-error"}
|
||||
}, children: [
|
||||
{type: "text", text: $tw.language.getString("Error/RecursiveTransclusion")}
|
||||
]}];
|
||||
parseTreeNodes = [{type: "error", attributes: {
|
||||
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
||||
}}];
|
||||
}
|
||||
}
|
||||
// Construct the child widgets
|
||||
|
||||
@@ -12,6 +12,9 @@ Widget base class
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/* Maximum permitted depth of the widget tree for recursion detection */
|
||||
var MAX_WIDGET_TREE_DEPTH = 1000;
|
||||
|
||||
/*
|
||||
Create a widget object for a parse tree node
|
||||
parseTreeNode: reference to the parse tree node to be rendered
|
||||
@@ -38,9 +41,10 @@ Widget.prototype.initialise = function(parseTreeNode,options) {
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
this.wiki = options.wiki;
|
||||
this.parentWidget = options.parentWidget;
|
||||
this.variablesConstructor = function() {};
|
||||
this.variablesConstructor.prototype = this.parentWidget ? this.parentWidget.variables : {};
|
||||
this.variables = new this.variablesConstructor();
|
||||
this.variables = Object.create(null);
|
||||
if(this.parentWidget) {
|
||||
Object.setPrototypeOf(this.variables,this.parentWidget.variables);
|
||||
}
|
||||
this.document = options.document;
|
||||
this.attributes = {};
|
||||
this.children = [];
|
||||
@@ -357,6 +361,20 @@ Widget.prototype.assignAttributes = function(domNode,options) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get the number of ancestor widgets for this widget
|
||||
*/
|
||||
Widget.prototype.getAncestorCount = function() {
|
||||
if(this.ancestorCount === undefined) {
|
||||
if(this.parentWidget) {
|
||||
this.ancestorCount = this.parentWidget.getAncestorCount() + 1;
|
||||
} else {
|
||||
this.ancestorCount = 0;
|
||||
}
|
||||
}
|
||||
return this.ancestorCount;
|
||||
};
|
||||
|
||||
/*
|
||||
Make child widgets correspondng to specified parseTreeNodes
|
||||
*/
|
||||
@@ -364,21 +382,29 @@ Widget.prototype.makeChildWidgets = function(parseTreeNodes,options) {
|
||||
options = options || {};
|
||||
this.children = [];
|
||||
var self = this;
|
||||
// Create set variable widgets for each variable
|
||||
$tw.utils.each(options.variables,function(value,name) {
|
||||
var setVariableWidget = {
|
||||
type: "set",
|
||||
attributes: {
|
||||
name: {type: "string", value: name},
|
||||
value: {type: "string", value: value}
|
||||
},
|
||||
children: parseTreeNodes
|
||||
};
|
||||
parseTreeNodes = [setVariableWidget];
|
||||
});
|
||||
$tw.utils.each(parseTreeNodes || (this.parseTreeNode && this.parseTreeNode.children),function(childNode) {
|
||||
self.children.push(self.makeChildWidget(childNode));
|
||||
});
|
||||
// Check for too much recursion
|
||||
if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
|
||||
this.children.push(this.makeChildWidget({type: "error", attributes: {
|
||||
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
||||
}}));
|
||||
} else {
|
||||
// Create set variable widgets for each variable
|
||||
$tw.utils.each(options.variables,function(value,name) {
|
||||
var setVariableWidget = {
|
||||
type: "set",
|
||||
attributes: {
|
||||
name: {type: "string", value: name},
|
||||
value: {type: "string", value: value}
|
||||
},
|
||||
children: parseTreeNodes
|
||||
};
|
||||
parseTreeNodes = [setVariableWidget];
|
||||
});
|
||||
// Create the child widgets
|
||||
$tw.utils.each(parseTreeNodes || (this.parseTreeNode && this.parseTreeNode.children),function(childNode) {
|
||||
self.children.push(self.makeChildWidget(childNode));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333333
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333353
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333333
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #00a
|
||||
external-link-foreground: #00e
|
||||
foreground: #000
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: <<colour foreground>>
|
||||
message-border: <<colour background>>
|
||||
message-foreground: <<colour background>>
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #00a
|
||||
external-link-foreground: #00e
|
||||
foreground: #fff
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: <<colour foreground>>
|
||||
message-border: <<colour background>>
|
||||
message-foreground: <<colour background>>
|
||||
|
||||
@@ -32,6 +32,8 @@ external-link-foreground-hover:
|
||||
external-link-foreground-visited: #BF5AF2
|
||||
external-link-foreground: #32D74B
|
||||
foreground: #FFFFFF
|
||||
highlight-background: #ffff78
|
||||
highlight-foreground: #000000
|
||||
menubar-background: #464646
|
||||
menubar-foreground: #ffffff
|
||||
message-background: <<colour background>>
|
||||
|
||||
@@ -36,6 +36,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333333
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -40,6 +40,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #313163
|
||||
external-link-foreground: #555592
|
||||
foreground: #2D2A23
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
menubar-background: #CDC2A6
|
||||
menubar-foreground: #5A5446
|
||||
message-background: #ECE5CF
|
||||
|
||||
@@ -41,6 +41,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #d3869b
|
||||
external-link-foreground: #8ec07c
|
||||
foreground: #fbf1c7
|
||||
highlight-background: #ffff79
|
||||
highlight-foreground: #000000
|
||||
menubar-background: #504945
|
||||
menubar-foreground: <<colour foreground>>
|
||||
message-background: #83a598
|
||||
|
||||
@@ -41,6 +41,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #5E81AC
|
||||
external-link-foreground: #8FBCBB
|
||||
foreground: #d8dee9
|
||||
highlight-background: #ffff78
|
||||
highlight-foreground: #000000
|
||||
menubar-background: #2E3440
|
||||
menubar-foreground: #d8dee9
|
||||
message-background: #2E3440
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333333
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -131,6 +131,8 @@ external-link-background-hover: inherit
|
||||
external-link-background-visited: inherit
|
||||
external-link-background: inherit
|
||||
external-link-foreground-hover: inherit
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-border: #cfd6e6
|
||||
modal-border: #999999
|
||||
select-tag-background:
|
||||
|
||||
@@ -35,6 +35,8 @@ external-link-foreground: #268bd2
|
||||
external-link-foreground-hover:
|
||||
external-link-foreground-visited: #268bd2
|
||||
foreground: #839496
|
||||
highlight-background: #ffff78
|
||||
highlight-foreground: #000000
|
||||
message-background: #002b36
|
||||
message-border: #586e75
|
||||
message-foreground: #839496
|
||||
|
||||
@@ -35,6 +35,8 @@ external-link-foreground: #268bd2
|
||||
external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #268bd2
|
||||
foreground: #657b83
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #fdf6e3
|
||||
message-border: #93a1a1
|
||||
message-foreground: #657b83
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover:
|
||||
external-link-foreground-visited:
|
||||
external-link-foreground:
|
||||
foreground: rgba(0, 0, 0, 0.87)
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: <<colour background>>
|
||||
message-border: <<colour very-muted-foreground>>
|
||||
message-foreground: rgba(0, 0, 0, 0.54)
|
||||
|
||||
@@ -34,6 +34,8 @@ external-link-foreground-hover:
|
||||
external-link-foreground-visited: #7c318c
|
||||
external-link-foreground: #9e3eb3
|
||||
foreground: rgba(255, 255, 255, 0.7)
|
||||
highlight-background: #ffff78
|
||||
highlight-foreground: #000000
|
||||
message-background: <<colour background>>
|
||||
message-border: <<colour very-muted-foreground>>
|
||||
message-foreground: rgba(255, 255, 255, 0.54)
|
||||
|
||||
@@ -43,6 +43,8 @@ external-link-foreground: rgb(179, 179, 255)
|
||||
external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: rgb(153, 153, 255)
|
||||
foreground: rgb(179, 179, 179)
|
||||
highlight-background: #ffff78
|
||||
highlight-foreground: #000000
|
||||
message-background: <<colour tag-foreground>>
|
||||
message-border: #96ccff
|
||||
message-foreground: <<colour tag-background>>
|
||||
|
||||
@@ -42,6 +42,8 @@ external-link-foreground-hover: inherit
|
||||
external-link-foreground-visited: #0000aa
|
||||
external-link-foreground: #0000ee
|
||||
foreground: #333333
|
||||
highlight-background: #ffff00
|
||||
highlight-foreground: #000000
|
||||
message-background: #ecf2ff
|
||||
message-border: #cfd6e6
|
||||
message-foreground: #547599
|
||||
|
||||
@@ -18,7 +18,7 @@ tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
|
||||
|
||||
<$navigator story="$:/StoryList" history="$:/HistoryList">
|
||||
|
||||
<$transclude mode="block"/>
|
||||
<$transclude tiddler="$:/core/ui/ViewTemplate/body" mode="block"/>
|
||||
|
||||
</$navigator>
|
||||
|
||||
|
||||
@@ -2,11 +2,5 @@ title: $:/core/ui/Actions/new-image
|
||||
tags: $:/tags/Actions
|
||||
description: create a new image tiddler
|
||||
|
||||
\define get-type()
|
||||
image/$(imageType)$
|
||||
\end
|
||||
\define get-tags() $(textFieldTags)$ $(tagsFieldTags)$
|
||||
\whitespace trim
|
||||
<$vars imageType={{$:/config/NewImageType}} textFieldTags={{$:/config/NewJournal/Tags}} tagsFieldTags={{$:/config/NewJournal/Tags!!tags}}>
|
||||
<$action-sendmessage $message="tm-new-tiddler" type=<<get-type>> tags=<<get-tags>>/>
|
||||
</$vars>
|
||||
<$action-sendmessage $message="tm-new-tiddler" type={{{ [{$:/config/NewImageType}addprefix[image/]] }}}/>
|
||||
|
||||
@@ -15,7 +15,7 @@ tags: $:/tags/AdvancedSearch/FilterButton
|
||||
<div class="tc-block-dropdown-wrapper">
|
||||
<div class="tc-block-dropdown tc-edit-type-dropdown">
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Filter]]">
|
||||
<$link to={{!!filter}}><$transclude field="description"/></$link>
|
||||
<$link to={{!!filter}}><$let tv-wikilinks="no"><$transclude field="description"/></$let></$link>
|
||||
</$list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ title: $:/core/ui/EditTemplate
|
||||
<div
|
||||
data-tiddler-title=<<currentTiddler>>
|
||||
data-tags={{!!tags}}
|
||||
class={{{ tc-tiddler-frame tc-tiddler-edit-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}
|
||||
class={{{ [all[shadows+tiddlers]tag[$:/tags/ClassFilters/TiddlerTemplate]!is[draft]] :map:flat[subfilter{!!text}] tc-tiddler-frame tc-tiddler-edit-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}
|
||||
role="region"
|
||||
aria-label={{$:/language/EditTemplate/Caption}}>
|
||||
<$fieldmangler>
|
||||
|
||||
@@ -5,6 +5,6 @@ caption: {{$:/language/EditTemplate/Body/Preview/Type/Output}}
|
||||
\import [all[shadows+tiddlers]tag[$:/tags/Macro/View]!has[draft.of]] [all[shadows+tiddlers]tag[$:/tags/Macro/View/Body]!has[draft.of]]
|
||||
<$set name="tv-tiddler-preview" value="yes">
|
||||
|
||||
<$transclude />
|
||||
<$transclude tiddler={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/ViewTemplateBodyFilter]!is[draft]get[text]] :and[!is[blank]else[$:/core/ui/ViewTemplate/body/default]] }}} />
|
||||
|
||||
</$set>
|
||||
|
||||
@@ -3,9 +3,6 @@ name: {{$:/language/PageTemplate/Name}}
|
||||
description: {{$:/language/PageTemplate/Description}}
|
||||
|
||||
\whitespace trim
|
||||
\define containerClasses()
|
||||
tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
|
||||
\end
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
|
||||
<$vars
|
||||
@@ -17,7 +14,7 @@ tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
|
||||
storyviewTitle={{$:/view}}
|
||||
languageTitle={{{ [{$:/language}get[name]] }}}>
|
||||
|
||||
<div class=<<containerClasses>>>
|
||||
<div class={{{ [all[shadows+tiddlers]tag[$:/tags/ClassFilters/PageTemplate]!is[draft]] :map:flat[subfilter{!!text}] tc-page-container [[tc-page-view-]addsuffix<storyviewTitle>] [[tc-language-]addsuffix<languageTitle>] :and[unique[]join[ ]] }}} >
|
||||
|
||||
<$navigator story="$:/StoryList" history="$:/HistoryList" openLinkFromInsideRiver={{$:/config/Navigation/openLinkFromInsideRiver}} openLinkFromOutsideRiver={{$:/config/Navigation/openLinkFromOutsideRiver}} relinkOnRename={{$:/config/RelinkOnRename}}>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ $:/state/folded/$(currentTiddler)$
|
||||
\define cancel-delete-tiddler-actions(message) <$action-sendmessage $message="tm-$message$-tiddler"/>
|
||||
\import [all[shadows+tiddlers]tag[$:/tags/Macro/View]!has[draft.of]]
|
||||
<$vars storyTiddler=<<currentTiddler>> tiddlerInfoState=<<qualify "$:/state/popup/tiddler-info">>>
|
||||
<div data-tiddler-title=<<currentTiddler>> data-tags={{!!tags}} class={{{ tc-tiddler-frame tc-tiddler-view-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[shadow]is[tiddler]then[tc-tiddler-overridden-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}} role="article">
|
||||
<div data-tiddler-title=<<currentTiddler>> data-tags={{!!tags}} class={{{ [all[shadows+tiddlers]tag[$:/tags/ClassFilters/TiddlerTemplate]!is[draft]] :map:flat[subfilter{!!text}] tc-tiddler-frame tc-tiddler-view-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[shadow]is[tiddler]then[tc-tiddler-overridden-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}} role="article">
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!has[draft.of]]" variable="listItem">
|
||||
<$transclude tiddler=<<listItem>>/>
|
||||
</$list>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
title: $:/core/macros/list
|
||||
tags: $:/tags/Macro
|
||||
|
||||
\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage)
|
||||
\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage,field:"caption")
|
||||
\whitespace trim
|
||||
<$type$ class="$class$">
|
||||
<$list filter="$filter$" emptyMessage=<<__emptyMessage__>>>
|
||||
<$subtype$>
|
||||
<$genesis $type=<<__type__>> class=<<__class__>>>
|
||||
<$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>>
|
||||
<$genesis $type=<<__subtype__>>>
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field="caption">
|
||||
<$transclude field=<<__field__>>>
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$subtype$>
|
||||
</$genesis>
|
||||
</$list>
|
||||
</$type$>
|
||||
</$genesis>
|
||||
\end
|
||||
|
||||
\define list-links-draggable-drop-actions()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20190115173333457
|
||||
modified: 20190115173723915
|
||||
modified: 20221029175754753
|
||||
tags: TableOfContents
|
||||
title: HelloThere
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -15,6 +15,7 @@ Welcome to the developer documentation for TiddlyWiki (https://tiddlywiki.com/).
|
||||
** [[Using ES2016 for Writing Plugins]]
|
||||
** [[Adding Babel Polyfill to TiddlyWiki]]
|
||||
** [[TiddlyWiki Drag and Drop Interoperability]]
|
||||
** [[Javascript Widget Tutorial]]
|
||||
* The original developer documentation from https://tiddlywiki.com:
|
||||
** [[TiddlyWiki for Developers]]
|
||||
** [[TiddlyWiki Coding Style Guidelines]]
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
created: 20190202160512541
|
||||
modified: 20190222231130254
|
||||
title: Child widgets tutorial
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
\define showTrees(wikitext)
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>wiki text</th><th>html</th><th>renders as</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>`$wikitext$`</td>
|
||||
<td>
|
||||
<$wikify name=html text="""$wikitext$""" output=html mode=inline>
|
||||
<$text text=<<html>>/>
|
||||
</$wikify>
|
||||
</td>
|
||||
<td>$wikitext$</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>parse tree</th><th>widget tree</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: text-top">
|
||||
<pre><code>
|
||||
<$wikify name=parsetree text="""$wikitext$""" output=parsetree mode=inline>
|
||||
<<parsetree>>
|
||||
</$wikify>
|
||||
</code></pre>
|
||||
</td>
|
||||
<td style="vertical-align: text-top">
|
||||
<pre><code>
|
||||
<$wikify name=widgettree text="""$wikitext$""" output=widgettree mode=inline>
|
||||
<<widgettree>>
|
||||
</$wikify>
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
\end
|
||||
|
||||
! Introduction
|
||||
|
||||
Until now the examples have covered only simple, leaf widgets, but widgets can be arranged into a hierarchy. Compared to a leaf-only widget, widget classes which support having children must contain code to instantiate the children.
|
||||
|
||||
Not all widgets need to support having children. Widgets whose only purpose is to integrate third party javascript libraries, for example may not need it.
|
||||
|
||||
! wiki text ⇨ parse tree ⇨ widget tree ⇨ DOM tree
|
||||
|
||||
# [[wiki text|https://tiddlywiki.com/#WikiText]] - Users write content in tiddlers using wiki text.
|
||||
# [[parse tree|https://tiddlywiki.com/dev/#ParsingMechanism]] - A parse tree is a JSON data structure describing the wiki text. Wiki text can be converted into a parse tree using `this.wiki.parseText`.
|
||||
# ''widget tree'' - Most items in the parse tree correspond to one or more items in the widget tree. The `this.makeChildWidgets` method is used to convert the parse tree into a widget tree.
|
||||
# [[DOM tree|https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction]] - A DOM tree is a standard javascript datastructure used by browsers to display a web page. The `this.renderChildren` method is used to instantiate the widget class and then render each widget in the widget tree. If a given widget will be visible in the browser, then one or more DOM nodes will be created.
|
||||
|
||||
!Examples
|
||||
|
||||
!! Simple trees without nesting
|
||||
|
||||
[[Widgets in wiki text|https://tiddlywiki.com/#Widgets%20in%20WikiText]] have a syntax similar to html (i.e. `<$list/>`). But all [[wiki markup|https://tiddlywiki.com/#WikiText]] is seen by the tiddlywiki code as widgets.
|
||||
|
||||
Even a simple string with no special syntax will be treated as a widget. In this case a widget of type `text`:
|
||||
|
||||
<<showTrees """hello""">>
|
||||
|
||||
The above bare string of text is mostly just a synonym for this widget syntax:
|
||||
|
||||
<<showTrees """<$text text=hello/>""">>
|
||||
|
||||
Some of the details of the parseTree and widgetTree are different in the two examples, but the general structure is the same and the rendered output is the same.
|
||||
|
||||
In these two simple examples, there is no nesting and so no child widgets are created.
|
||||
|
||||
!! Trees with one level of nesting
|
||||
|
||||
This next example shows the structure for bold wiki syntax. It is still simple, but does introduce one level of nesting: `element`→`text`
|
||||
|
||||
The wiki syntax for bold converts to the standard html element `strong`. Any standard html element is represented in tiddlywiki as a widget of type `element`:
|
||||
|
||||
<<showTrees """''bold''""">>
|
||||
|
||||
Another example showing one level of nesting (`link`→`text`):
|
||||
|
||||
<<showTrees """<$link to=atiddler>link</$link>""">>
|
||||
|
||||
!!Widgets with no DOM contribution
|
||||
Not all widgets contribute items to the DOM. The purpose of some widgets is to affect the display of descendant widgets by changing some state. The `$set` widget for example sets a variable which will be accessible to the descendant widgets:
|
||||
|
||||
<<showTrees """<$set name=myvar value=hi/>""">>
|
||||
|
||||
Nothing is rendered and there is no html, but the parse tree and widget tree contain information about the variable.
|
||||
|
||||
!! Dynamic widget children using this.wiki.parseText
|
||||
|
||||
In all the examples so far, there has been a close mapping between the nesting structure of the parse tree and the widget tree. If the item is in the parse tree, then it is in the widget tree. If the parse tree item has children, then the widget tree has the same number of children.
|
||||
|
||||
However, there are several examples of widgets in which more levels of nesting are created dynamically during the widget rendering process
|
||||
|
||||
The `$macrocall` widget is one example:
|
||||
|
||||
<<showTrees """<$macrocall $name=now/>""" >>
|
||||
|
||||
The parse tree has just a single type=$macrocall element with no children. The widget tree has that same type=$macrocall element, but it also has a child widget which wasn't in the parse tree.
|
||||
|
||||
In all cases, the `$macrocall` widget calls the given macro and then calls `this.wiki.parseText` on the output. This results in a new parse tree which is used to make the child widgets.
|
||||
|
||||
In this particular case, the `now` macro is called. It returns a simple string of text, so when it is parsed the result causes the creation of a single child text widget containing the current date and time.
|
||||
|
||||
!! Widgets which do not support nesting
|
||||
|
||||
Just as some widgets can cause widget trees to have more nesting than the parse tree, some widgets can cause widget trees to have less nesting.
|
||||
|
||||
This happens when there is no use for the widget to have any children. The `$text` widget, for example, has no use for displaying any descendants.
|
||||
|
||||
This behavior is accomplished by not calling the `makeChildWidgets` method. Without that method call, the child widgets are not created from the child parse tree items. For example:
|
||||
|
||||
<$macrocall $name=showTrees wikitext="""<$text text="hi">ignored child text</$text>"""/>
|
||||
|
||||
Since the `$text` widget does not have a call to `makeChildWidgets`, 'ignored child text' above is present in the parse tree, but not in the widget tree.
|
||||
|
||||
!Reference
|
||||
|
||||
|!method|!when to call|!what it does|
|
||||
|`makeChildWidgets`|directly or indirectly from `render` method|converts child parse tree items into widget tree items|
|
||||
|`renderChildren`|directly or indirectly from `render` method, after the `makeChildWidgets` call|calls the `render` method of the child widget|
|
||||
|`refreshChildren`|directly or indirectly from `refresh` method, only needed if `refreshSelf` is not called|calls the `refresh` method of of the child widgets so they can check if refresh is needed|
|
||||
|`this.wiki.parseText`|pass the output parse tree to `makeChildWidgets`|converts the given text to a parse tree...only needed for dynamically constructed parsetree|
|
||||
|
||||
|!example call|!purpose|!widgets using this approach|
|
||||
|`this.makeChildWidgets()`|construct child widgets from the static parse tree|[[$link|$:/core/modules/widgets/link.js]], [[$button|$:/core/modules/widgets/button.js]], etc.|
|
||||
|`this.makeChildWidgets(parseTreeNodes)`|construct child widgets from a dynamic parse tree|[[$list|$:/core/modules/widgets/list.js]], [[$transclude|$:/core/modules/widgets/transclude.js]], [[$macrocall|$:/core/modules/widgets/macrocall.js]], etc|
|
||||
|`this.renderChildren(parent, nextSibling)`|when the widget adds nothing to the dom, just pass the `render` arguments through to the children|[[$set|$:/core/modules/widgets/set.js]], [[$importvariables|$:/core/modules/widgets/importvariables.js]], [[$tiddler|$:/core/modules/widgets/tiddler.js]], [[$wikify|$:/core/modules/widgets/wikify.js]], etc.|
|
||||
|`this.renderChildren(domNode, null)`|passes the dom node generated by this widget into the children|[[$link|$:/core/modules/widgets/link.js]], [[$button|$:/core/modules/widgets/button.js]], etc|
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
created: 20190201120100249
|
||||
modified: 20190201222600576
|
||||
tags:
|
||||
title: Do nothing widget demo
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[donothing.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[do nothing widget]]"/>
|
||||
<$data $tiddler=donothing.js/>
|
||||
<$data title="do nothing widget" text="""
|
||||
```
|
||||
<$donothing/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$donothing/>
|
||||
"""/>
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,25 @@
|
||||
created: 20190201232102417
|
||||
modified: 20190202145547621
|
||||
tags:
|
||||
title: Do nothing widget tutorial
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
In order to define a widget in a tiddler, the tiddler must satisfy these requirements:
|
||||
|
||||
* type field is application/javascript
|
||||
* module-type field is widget
|
||||
* the text field contains the javascript code
|
||||
|
||||
The [[donothing.js]] tiddler fulfills the requirements and its code looks like this:
|
||||
{{donothing.js}}
|
||||
That code does 2 key things:
|
||||
|
||||
* Imports the core Widget class ([[$:/core/modules/widgets/widget.js]])
|
||||
* exports the class in an attribute with the name we want our widget to have (`donothing`)
|
||||
|
||||
Here's what it looks like:
|
||||
{{Do nothing widget demo}}
|
||||
And it worked. No error message this time.
|
||||
|
||||
''Exercise'': Modify [[donothing.js]] and [[Do nothing widget demo]] so the widget is named `noop` instead of `donothing`
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
created: 20190201114718313
|
||||
modified: 20190201231556294
|
||||
tags:
|
||||
title: Hello World demo
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[hello.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
|
||||
<$data $tiddler=hello.js/>
|
||||
<$data title="hello world widget" text="""
|
||||
```
|
||||
<$hello/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello/>
|
||||
"""/>
|
||||
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,18 @@
|
||||
created: 20190201232200698
|
||||
modified: 20190216175629825
|
||||
tags:
|
||||
title: Hello World widget tutorial
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Now let's create a widget which actually has output.
|
||||
|
||||
When tiddlywiki encounters a widget definition like `<$hello>` it will create an object which is an instance of the class which is exported by the widget code.
|
||||
|
||||
In addition to creating an instance of the class, tiddlywiki will call the render method of the resulting object. The render method is expected to create a dom node which will be display in place of the widget.
|
||||
|
||||
In the `donothing` example the core widget was exported. The core widget class doesn't have a render method which creates a dom node and that's why there is no output. Getting output requires writing a widget class which inherits from the core `Widget` class. Code in the render method of this class will display the hello world message.
|
||||
|
||||
The [[hello.js]] tiddler has code which accomplishes that:
|
||||
{{hello.js}}
|
||||
The code for importing the core widget class remains, but now we also have code to create our own class which inherits from it. In addition an implementation of the `render` method is included. Here is the result:
|
||||
{{Hello World demo}}
|
||||
@@ -0,0 +1,37 @@
|
||||
created: 20190202035524804
|
||||
modified: 20221029161501848
|
||||
tags:
|
||||
title: Javascript Widget Tutorial
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
! Introduction
|
||||
This tutorial provides step-by-step, interactive examples of how to write code for tiddlywiki widgets. The demo javascript code can be modified without having to reload the entire wiki.
|
||||
|
||||
Intended audience:
|
||||
|
||||
# Those who know tiddlywiki well and know programming and javascript and want to write their own widget. I don't make any effort to explain javascript here. For that you will need other resources.
|
||||
# Those who know tiddlywiki well and don't know javascript, but want to understand more about how tiddlywiki works. You should be able to skim through and interact with the demos and learn something.
|
||||
|
||||
!The tutorial
|
||||
*[[Undefined widget tutorial]]
|
||||
*[[Do nothing widget tutorial]]
|
||||
*[[Hello World widget tutorial]]
|
||||
*[[Widget refresh tutorial part I]]
|
||||
*[[Widget refresh tutorial part II]]
|
||||
*[[Widget refresh tutorial part III]]
|
||||
*[[Widget attributes tutorial part I]]
|
||||
*[[Widget attributes tutorial part II]]
|
||||
*[[Child widgets tutorial]]
|
||||
|
||||
! Notes
|
||||
|
||||
tiddlywiki doesn't support dynamically reloading javascript. If you change a javascript tiddler, then you need to save and reload the wiki before the changes will take affect.
|
||||
|
||||
To avoid the need for such reloads, the excellent [[innerwiki plugin|https://tiddlywiki.com/prerelease/plugins/tiddlywiki/innerwiki/]] is used. This allows an inner wiki to be created from a subset of tiddlers in the outer wiki. Each time the inner wiki is refreshed, its entire wiki is reloaded and the javascript changes in the inner wiki will take affect.
|
||||
|
||||
Without the need for reloads, a tiddlywiki instance with the [[innerwiki plugin|https://tiddlywiki.com/prerelease/plugins/tiddlywiki/innerwiki/]] installed works great as a playground for interacting with tiddlywiki javascript.
|
||||
|
||||
! Other documentation on writing TW widgets
|
||||
|
||||
*WidgetModules
|
||||
*[[Widgets]]
|
||||
@@ -0,0 +1,18 @@
|
||||
created: 20190201212238781
|
||||
modified: 20190201213112748
|
||||
tags:
|
||||
title: Undefined widget demo
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[Undefined widget]]"/>
|
||||
<$data title="Undefined widget" text="""
|
||||
```
|
||||
<$donothing/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$donothing/>
|
||||
"""/>
|
||||
</$innerwiki>
|
||||
@@ -0,0 +1,11 @@
|
||||
created: 20190201232001970
|
||||
modified: 20190202145655413
|
||||
tags:
|
||||
title: Undefined widget tutorial
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Let's start be defining a minimal widget which does nothing. It doesn't support any attributes, no child elements, and it doesn't output anything. The name of the widget will be `donothing`. If it does nothing, then how can we verify after writing the code that we accomplished anything? Well, let's see what happens when an undefined widget is referenced.
|
||||
|
||||
{{Undefined widget demo}}
|
||||
|
||||
Since we haven't written the code, the attempt to render the widget gives an undefined widget error. So we will know the donothing code accomplishes something if we don't get the undefined widget error when rendering.
|
||||
@@ -0,0 +1,48 @@
|
||||
created: 20190204020507195
|
||||
modified: 20190204031520013
|
||||
tags:
|
||||
title: Widget attributes demo I
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[hello-attribute.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
|
||||
<$data $tiddler=hello-attribute.js/>
|
||||
<$data title="hello world widget" text="""
|
||||
```
|
||||
<$hello/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello/>
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
<$hello message="pale blue dot"/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello message="pale blue dot"/>
|
||||
|
||||
---
|
||||
|
||||
<$edit-text focus=yes tiddler=test tag=input/>
|
||||
|
||||
```
|
||||
<$hello message={{test!!text}}/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello message={{test!!text}}/>
|
||||
|
||||
"""/>
|
||||
|
||||
<$data title="test" text="Alice"/>
|
||||
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,48 @@
|
||||
created: 20190205024953535
|
||||
modified: 20190205025028737
|
||||
tags:
|
||||
title: Widget attributes demo II
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[hello-attribute-optimized.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
|
||||
<$data $tiddler=hello-attribute-optimized.js/>
|
||||
<$data title="hello world widget" text="""
|
||||
```
|
||||
<$hello/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello/>
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
<$hello message="pale blue dot"/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello message="pale blue dot"/>
|
||||
|
||||
---
|
||||
|
||||
<$edit-text focus=yes tiddler=test tag=input/>
|
||||
|
||||
```
|
||||
<$hello message={{test!!text}}/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$hello message={{test!!text}}/>
|
||||
|
||||
"""/>
|
||||
|
||||
<$data title="test" text="Alice"/>
|
||||
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,50 @@
|
||||
created: 20190202035425715
|
||||
modified: 20190205023518575
|
||||
tags:
|
||||
title: Widget attributes tutorial part I
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
So far none of the widgets we've implemented have had any code for handling widget attributes A vast majority of useful widgets will need to support attributes in order to make them useful.
|
||||
|
||||
As an example, let's change the Hello World widget so it can greet not just the world, but whoever/whatever the user wants. To do that we can add support for an attribute named `what` and call it like `<$hello what="pale blue dot"/>`.
|
||||
|
||||
The tiddlywiki widget class provides methods `computeAttributes` and `getAttribute` which can together be used by the widget code to discover what values are passed into the widget.
|
||||
|
||||
The `computeAttributes` and `getAttribute` methods can be called like this (the second parameter to `getAttribute` will be used as the default value if the user doesn't provide that attribute in the widget call):
|
||||
|
||||
```javascript
|
||||
this.computeAttributes();
|
||||
var message = this.getAttribute("message", "World");
|
||||
```
|
||||
|
||||
Then the `message` variable can be used to construct the Hello XXX string:
|
||||
|
||||
```javascript
|
||||
var textNode = this.document.createTextNode("Hello, " + message + "!");
|
||||
```
|
||||
|
||||
The original [[hello.js]] code only implements a `render` method. The `refresh` method is not needed because the output from the widget can never be different...it will always be "Hello, World!".
|
||||
|
||||
Even with a `message` attribute, you might think the `render` by itself is enough. After all, the value of the input parameter `message` can only be changed by modifying the wiki text which means the tiddler will be redisplayed from scratch.
|
||||
|
||||
However, tiddlywiki has a syntax which allows parameter values to vary without modifying the wiki text. See https://tiddlywiki.com/#Widgets%20in%20WikiText for details. As one example, if the widget were called like `<$hello message={{MyTiddler!!field}}/>`, then every time the `field` field of `MyTiddler` were modified, only the `refresh` method would be called. Therefore, in order to get the widget display to update, the refresh method needs to be implemented.
|
||||
|
||||
If the `computeAttributes` and `getAttribute` calls are placed in the `render` method then we can implement a performance unoptimized version of refresh as was done in [[Widget refresh tutorial part II]]:
|
||||
|
||||
```javascript
|
||||
/*
|
||||
A widget with optimized performance will selectively refresh, but here we refresh always
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and
|
||||
// replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
The full code can be seen at [[hello-attribute.js]] and here is the result ([[Widget attributes demo I]]):
|
||||
|
||||
{{Widget attributes demo I}}
|
||||
|
||||
The third example above is the only one which requires the refresh method in order to behave properly.
|
||||
@@ -0,0 +1,33 @@
|
||||
created: 20190205023543910
|
||||
modified: 20190217012121130
|
||||
tags:
|
||||
title: Widget attributes tutorial part II
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
This example will build on the previous one. The only modification will be to add a check to the `refresh` method. The `refreshSelf` will only be called if a change to the attributes is detected.
|
||||
|
||||
The `computeAttributes` method returns a Javascript object containing properties for each attribute which has changed. So a check like `if (changedAttributes.attr1 || changedAttributes.attr2 || changedAttributes.attr3)` etc. can be used to detect the change. See the refresh method in [[$:/core/modules/widgets/view.js]] for an example showing the check for multiple attributes.
|
||||
|
||||
For this example, `message` is the only attribute implemented.
|
||||
|
||||
```javascript
|
||||
/*
|
||||
Refresh if the attribute value changed since render
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Find which attributes have changed
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if (changedAttributes.message) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The full code can be seen at [[hello-attribute-optimized.js]] and here is the result ([[Widget attributes demo II]]):
|
||||
|
||||
{{Widget attributes demo II}}
|
||||
|
||||
The visible behavior here should be identical to the unoptimized behavior of the previous tutorial.
|
||||
@@ -0,0 +1,44 @@
|
||||
created: 20190201233806976
|
||||
modified: 20221029194854112
|
||||
tags:
|
||||
title: Widget refresh demo I
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[tiddlerfield-norefresh.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[tiddler field widget]]"/>
|
||||
<$data title="test" text="type new text here"/>
|
||||
<$data $tiddler=tiddlerfield-norefresh.js/>
|
||||
<$data title="tiddler field widget" text="""
|
||||
<$edit-text focus=yes tiddler=test tag=input/>
|
||||
<$button set="!!refresh" setTo={{test}}>Force refresh</$button>
|
||||
<$list filter="[{!!refresh}]">
|
||||
|
||||
<div>
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$tiddlerfield/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$tiddlerfield/>
|
||||
</div>
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$view tiddler="test"/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$view tiddler="test"/>
|
||||
</div>
|
||||
</div>
|
||||
</$list>
|
||||
"""/>
|
||||
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,40 @@
|
||||
created: 20190202032354223
|
||||
modified: 20190217005540498
|
||||
tags:
|
||||
title: Widget refresh demo II
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[tiddlerfield.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[tiddler field widget]]"/>
|
||||
<$data title="test" text="type new text here"/>
|
||||
<$data $tiddler=tiddlerfield.js/>
|
||||
<$data title="tiddler field widget" text="""
|
||||
<$edit-text focus=yes tiddler=test tag=input/>
|
||||
|
||||
<div>
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$tiddlerfield/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$tiddlerfield/>
|
||||
</div>
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$view tiddler="test"/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$view tiddler="test"/>
|
||||
</div>
|
||||
"""/>
|
||||
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,50 @@
|
||||
created: 20190202122928187
|
||||
modified: 20190216191939585
|
||||
tags:
|
||||
title: Widget refresh demo III
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<!-- The innerwiki doesn't refresh on its own when tiddlers on the outside change, so use the list widget to force a dependency -->
|
||||
<$list name=refresh filter=[[refreshcount.js]get[text]]>
|
||||
<$innerwiki width="600" height="400" style="width:100%;">
|
||||
<$data title="$:/DefaultTiddlers" text="[[refresh count widget]]"/>
|
||||
<$data title="test" text="Text field of tiddler='test'"/>
|
||||
<$data $tiddler=refreshcount.js/>
|
||||
<$data title="refresh count widget" text="""
|
||||
|
||||
*<$button set="test!!test" setTo="hello">Modify a different tiddler</$button>
|
||||
*<$button set="!!test" setTo="hello">Modify this tiddler</$button>
|
||||
*<$edit-text focus=yes tiddler=test tag=input/>
|
||||
|
||||
<div>
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$refreshcount/>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$refreshcount/>
|
||||
</div>
|
||||
|
||||
<div style="display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}">
|
||||
|
||||
```
|
||||
<$list filter="[[test]get[text]]">
|
||||
<<currentTiddler>><br>
|
||||
<$refreshcount/>
|
||||
</$list>
|
||||
```
|
||||
|
||||
Renders as:
|
||||
|
||||
<$list filter="[[test]get[text]]">
|
||||
<<currentTiddler>><br>
|
||||
<$refreshcount/>
|
||||
</$list>
|
||||
"""/>
|
||||
</div>
|
||||
</div>
|
||||
</$innerwiki>
|
||||
</$list>
|
||||
@@ -0,0 +1,47 @@
|
||||
created: 20190201232847949
|
||||
modified: 20221029203553291
|
||||
tags:
|
||||
title: Widget refresh tutorial part I
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
But what if we want to display dynamic content? How can we display information and keep it up to date as it changes? Let's display the content of a tiddler field.
|
||||
|
||||
The [[tiddlerfield-norefresh.js]] which defines the `tiddlerfield` widget is almost the same as [[hello.js]] except for this part:
|
||||
|
||||
```javascript
|
||||
MyWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var text = this.wiki.getTiddlerText("test", "<empty>")
|
||||
var textNode = this.document.createTextNode(text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
```
|
||||
|
||||
Instead of creating the text dom node from a static string, the text field of the `test` tiddler is used. This is similar to using the view widget like this: `<$view tiddler="test"/>`
|
||||
|
||||
Here's how it looks (see [[Widget refresh demo I]] to look at the code):
|
||||
|
||||
{{Widget refresh demo I}}
|
||||
|
||||
Notice if you change the text in the input box, the output from the `tiddlerfield` widget doesn't change, but the output of the `view` widget does. Only after the ''Force refresh'' button is clicked does the output of `tiddlerfield` update to match the input box contents.
|
||||
|
||||
What's going on here? The render method of the widget code is only called by tiddlywiki core when the widget is first created. After that, it isn't called again unless the widget is completely destroyed and then created again.
|
||||
|
||||
The tiddlywiki ~ViewWidget has a properly written `refresh` method so typing in the input box will cause its content to update. However, the `tiddlerfield` widget does not have a `refresh` method at all. It has no way of being notified that the `test` tiddler content has changed. Its output will not change until the ''Force refresh'' button is clicked.
|
||||
|
||||
See the next example for an implementation of the `refresh` method for the `tiddlerfield` widget.
|
||||
|
||||
The code for the refresh button looks like this:
|
||||
|
||||
```
|
||||
<$button set="!!refresh" setTo={{test}}>Force refresh</$button>
|
||||
```
|
||||
|
||||
and the widgets are enclosed in a list widget like this:
|
||||
|
||||
```
|
||||
<$list filter="[{!!refresh}]">...</$list>
|
||||
```
|
||||
|
||||
When the button is clicked the field `refresh` in the containing tiddler is modified and it causes the children of the list widget to be created from scratch. The render method is called and the output of the `tiddlerfield` widget updates.
|
||||
@@ -0,0 +1,37 @@
|
||||
created: 20190201232910076
|
||||
modified: 20190217014335419
|
||||
tags:
|
||||
title: Widget refresh tutorial part II
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
This example is like [[Widget refresh tutorial part I]] except the widget output will be automatically refreshed when the tiddler field changes.
|
||||
|
||||
[[tiddlerfield.js]] is the same as [[tiddlerfield-norefresh.js]], except this `refresh` method is added:
|
||||
|
||||
```javascript
|
||||
/*
|
||||
A widget with optimized performance will selectively refresh, but here we refresh always
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and
|
||||
// replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
The `refreshSelf` method called above is implemented by the core widget class and it takes care of cleaning the old dom node and calling the render function.
|
||||
|
||||
Here is the result ([[Widget refresh demo II]]):
|
||||
|
||||
{{Widget refresh demo II}}
|
||||
|
||||
And now any typing into the input box will cause both the `tiddlerfield` and the `view` widget output to refresh immediately.
|
||||
|
||||
Note this is a naive version of `refresh`. It unconditionally refreshes itself. This is far from optimal since the `refresh` method for all visible widgets is called every time the tiddler store changes. But the way we've defined our widget, the output ONLY depends on the tiddler titled `text`.
|
||||
|
||||
In tiddlywiki the tiddler store is used for everything and almost any interaction will result in an update to the store. This means almost any interaction will cause the refresh method to be called. If you type into the search box, for example, the `tiddlerfield` widget will be refreshed with every keystroke.
|
||||
|
||||
Adding and removing dom elements is a relatively expensive operation, so if someone has used the list widget to create a few hundred instances of this widget, then such tiddlywiki interactions might gain a noticable lag. Therefore, it usually makes sense to avoid modifying the dom when possible by writing a smarter `refresh` method.
|
||||
|
||||
''Exercise'' - change the refresh method above to only call `refreshSelf` when the `changedTiddlers` input array contains `test` as one of its entries (Hint: see the refresh method in $:/core/modules/widgets/view.js for an example).
|
||||
@@ -0,0 +1,26 @@
|
||||
created: 20190202034841184
|
||||
modified: 20221029201023638
|
||||
tags:
|
||||
title: Widget refresh tutorial part III
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
This tutorial is intended to demonstrate a few examples of when calls to the `refresh` method happen vs. when a widget is recreated from scratch.
|
||||
|
||||
This is accomplished with a [[refreshcount.js]] widget which sets a counter to zero when the widget is created and increments the counter every time the `refresh` method is called. Here is the full code:
|
||||
|
||||
{{refreshcount.js}}
|
||||
|
||||
These are the key parts of the code from above:
|
||||
|
||||
```javascript
|
||||
this.refreshCount = 0;
|
||||
this.document.createTextNode(this.refreshCount + " refreshes");
|
||||
this.refreshCount++;
|
||||
```
|
||||
|
||||
In the following example (see [[Widget refresh demo III]] for the code), two instances of the `refreshcount` widget are created. One at the top level and the other inside a list widget whose filter results depend on the value in the `text` field of the `test` widget. The tiddler store can be modified in a few ways via two buttons and an input box:
|
||||
{{Widget refresh demo III}}
|
||||
|
||||
* ''Modify a different tiddler'' - every time this button is clicked, both counters increment, indicating the `refresh` method is being called
|
||||
* ''Modify this tiddler'' - clicking this button modifies the tiddler itself. In earlier ~TiddlyWiki versions that caused both widgets to be recreated from scratch and the counters are thereby reset to zero. Since [[version 5.2.0|https://tiddlywiki.com/#Release%205.2.0]] modifying another field in this tiddler does not cause the widgets to be recreated and the counters are not reset.
|
||||
* ''Text field of tiddler='test''' - typing text into the input box causes the counter in the standalone `refreshcount` widget to be incremented. But the other instance of the widget is embedded inside a list widget whose filter output depends on the value of the tiddler field which is being modified. This causes it to recreate its children from scratch and the counter is reset every time. So with every keystroke, the counter on the left is incremented, but the counter on the right goes to/stays at zero.
|
||||
@@ -0,0 +1,47 @@
|
||||
/*\
|
||||
|
||||
Library function for creating widget using a dom creating function
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
function createDomWidget(domCreatorFunction) {
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var domNode = domCreatorFunction(this.document);
|
||||
parent.insertBefore(domNode, nextSibling);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
A widget with optimized performance will selectively refresh, but here we refresh always
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
|
||||
return MyWidget;
|
||||
}
|
||||
module.exports = createDomWidget;
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190201025244440
|
||||
modified: 20190201030708723
|
||||
module-type: library
|
||||
tags:
|
||||
title: domwidget.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,16 @@
|
||||
/*\
|
||||
|
||||
Do nothing widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
exports.donothing = Widget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190201115945945
|
||||
modified: 20190201222441271
|
||||
module-type: widget
|
||||
tags:
|
||||
title: donothing.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,51 @@
|
||||
/*\
|
||||
|
||||
Hello, World widget
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
var message = this.getAttribute("message", "World");
|
||||
var textNode = this.document.createTextNode("Hello, " + message + "!");
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Refresh if the attribute value changed since render
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Find which attributes have changed
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if (changedAttributes.message) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.hello = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190205024846183
|
||||
modified: 20190205025110882
|
||||
module-type: widget
|
||||
tags:
|
||||
title: hello-attribute-optimized.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,47 @@
|
||||
/*\
|
||||
|
||||
Hello, World widget
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
var message = this.getAttribute("message", "World");
|
||||
var textNode = this.document.createTextNode("Hello, " + message + "!");
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
A widget with optimized performance will selectively refresh, but here we refresh always
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and
|
||||
// replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.hello = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190204020011193
|
||||
modified: 20190204030332147
|
||||
module-type: widget
|
||||
tags:
|
||||
title: hello-attribute.js
|
||||
type: application/javascript
|
||||
35
editions/dev/tiddlers/javascript-widget-tutorial/hello.js
Normal file
35
editions/dev/tiddlers/javascript-widget-tutorial/hello.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*\
|
||||
|
||||
Hello, World widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var textNode = this.document.createTextNode("Hello, World!");
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
exports.hello = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190201114558816
|
||||
modified: 20190201224846870
|
||||
module-type: widget
|
||||
tags:
|
||||
title: hello.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,43 @@
|
||||
/*\
|
||||
|
||||
widget to count the number of times this widget refreshes
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.refreshCount = 0;
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var textNode = this.document.createTextNode(this.refreshCount + " refreshes");
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.refreshCount++;
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.refreshcount = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190201005026324
|
||||
modified: 20190202143451303
|
||||
module-type: widget
|
||||
tags:
|
||||
title: refreshcount.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,36 @@
|
||||
/*\
|
||||
|
||||
Hello, World widget
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var text = this.wiki.getTiddlerText("test", "<empty>")
|
||||
var textNode = this.document.createTextNode(text);
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
exports.tiddlerfield = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190201233714872
|
||||
modified: 20190202030615781
|
||||
module-type: widget
|
||||
tags:
|
||||
title: tiddlerfield-norefresh.js
|
||||
type: application/javascript
|
||||
@@ -0,0 +1,46 @@
|
||||
/*\
|
||||
|
||||
Hello, World widget
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MyWidget = function(parseTreeNode, options) {
|
||||
this.initialise(parseTreeNode, options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MyWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MyWidget.prototype.render = function(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
var text = this.wiki.getTiddlerText("test", "<empty>")
|
||||
var textNode = this.document.createTextNode(text);
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
A widget with optimized performance will selectively refresh, but here we refresh always
|
||||
*/
|
||||
MyWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Regenerate and rerender the widget and
|
||||
// replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.tiddlerfield = MyWidget;
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
created: 20190202032530728
|
||||
modified: 20190202032700995
|
||||
module-type: widget
|
||||
tags:
|
||||
title: tiddlerfield.js
|
||||
type: application/javascript
|
||||
@@ -5,7 +5,8 @@
|
||||
"tiddlywiki/nodewebkitsaver",
|
||||
"tiddlywiki/github-fork-ribbon",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/internals"
|
||||
"tiddlywiki/internals",
|
||||
"tiddlywiki/innerwiki"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
created: 20160504080001125
|
||||
modified: 20160504080318928
|
||||
title: $:/core/ui/TagTemplate
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
\define tag-styles()
|
||||
background-color:$(backgroundColor)$;
|
||||
fill:$(foregroundColor)$;
|
||||
color:$(foregroundColor)$;
|
||||
\end
|
||||
|
||||
\define tag-body-inner(colour,fallbackTarget,colourA,colourB)
|
||||
<$set name="foregroundColor" value=<<contrastcolour target:"""$colour$""" fallbackTarget:"""$fallbackTarget$""" colourA:"""$colourA$""" colourB:"""$colourB$""">>>
|
||||
<$set name="backgroundColor" value="""$colour$""">
|
||||
<$button popup=<<qualify "$:/state/popup/tag">> class="tc-btn-invisible tc-tag-label" style=<<tag-styles>>>
|
||||
<$transclude tiddler={{!!icon}}/> <$view field="fr-title"><$view field="title" format="text" /></$view>
|
||||
</$button>
|
||||
<$reveal state=<<qualify "$:/state/popup/tag">> type="popup" position="below" animate="yes"><div class="tc-drop-down"><$transclude tiddler="$:/core/ui/ListItemTemplate"/>
|
||||
<hr>
|
||||
<$list filter="[all[current]tagging[]]" template="$:/core/ui/ListItemTemplate"/>
|
||||
</div>
|
||||
</$reveal>
|
||||
</$set>
|
||||
</$set>
|
||||
\end
|
||||
|
||||
\define tag-body(colour,palette)
|
||||
<span class="tc-tag-list-item">
|
||||
<$macrocall $name="tag-body-inner" colour="""$colour$""" fallbackTarget={{$palette$##tag-background}} colourA={{$palette$##foreground}} colourB={{$palette$##background}}/>
|
||||
</span>
|
||||
\end
|
||||
|
||||
<$macrocall $name="tag-body" colour={{!!color}} palette={{$:/palette}}/>
|
||||
@@ -117,7 +117,7 @@ C'est un exemple de tiddler. Voir [[Macros Table des matières (Exemples)|Table-
|
||||
<table class="doc-bad-example">
|
||||
<tbody>
|
||||
<tr class="evenRow">
|
||||
<td><span class="tc-inline-style" style="font-size:1.5em;">⚠</span> Attention :<br> Ne faites pas comme ça !</td>
|
||||
<td><span style="font-size:1.5em;">⚠</span> Attention :<br> Ne faites pas comme ça !</td>
|
||||
<td>
|
||||
|
||||
$eg$
|
||||
|
||||
113
editions/fr-FR/tiddlers/system/$__core_macros_list.tid
Normal file
113
editions/fr-FR/tiddlers/system/$__core_macros_list.tid
Normal file
@@ -0,0 +1,113 @@
|
||||
created: 20220830224607117
|
||||
modified: 20220830224638865
|
||||
tags: $:/tags/Macro
|
||||
title: $:/core/macros/list
|
||||
|
||||
\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage)
|
||||
\whitespace trim
|
||||
<$type$ class="$class$">
|
||||
<$list filter="$filter$" emptyMessage=<<__emptyMessage__>>>
|
||||
<$subtype$>
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field="caption">
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$subtype$>
|
||||
</$list>
|
||||
</$type$>
|
||||
\end
|
||||
|
||||
\define list-links-draggable-drop-actions()
|
||||
<$action-listops $tiddler=<<targetTiddler>> $field=<<targetField>> $subfilter="+[insertbefore<actionTiddler>,<currentTiddler>]"/>
|
||||
\end
|
||||
|
||||
\define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate)
|
||||
\whitespace trim
|
||||
<span class="tc-links-draggable-list">
|
||||
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
|
||||
<$type$ class="$class$">
|
||||
<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>>
|
||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="""$subtype$""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<div class="tc-droppable-placeholder"/>
|
||||
<div>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field="caption">
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</div>
|
||||
</$droppable>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="div" enable=<<tv-enable-drag-and-drop>>>
|
||||
<div class="tc-droppable-placeholder">
|
||||
{{$:/core/images/blank}}
|
||||
</div>
|
||||
<div style="height:0.5em;"/>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$type$>
|
||||
</$vars>
|
||||
</span>
|
||||
\end
|
||||
|
||||
\define list-tagged-draggable-drop-actions(tag)
|
||||
\whitespace trim
|
||||
<!-- Save the current ordering of the tiddlers with this tag -->
|
||||
<$set name="order" filter="[<__tag__>tagging[]]">
|
||||
<!-- Remove any list-after or list-before fields from the tiddlers with this tag -->
|
||||
<$list filter="[<__tag__>tagging[]]">
|
||||
<$action-deletefield $field="list-before"/>
|
||||
<$action-deletefield $field="list-after"/>
|
||||
</$list>
|
||||
<!-- Save the new order to the Tag Tiddler -->
|
||||
<$action-listops $tiddler=<<__tag__>> $field="list" $filter="+[enlist<order>] +[insertbefore<actionTiddler>,<currentTiddler>]"/>
|
||||
<!-- Make sure the newly added item has the right tag -->
|
||||
<!-- Removing this line makes dragging tags within the dropdown work as intended -->
|
||||
<!--<$action-listops $tiddler=<<actionTiddler>> $tags=<<__tag__>>/>-->
|
||||
<!-- Using the following 5 lines as replacement makes dragging titles from outside into the dropdown apply the tag -->
|
||||
<$list filter="[<actionTiddler>!contains:tags<__tag__>]">
|
||||
<$fieldmangler tiddler=<<actionTiddler>>>
|
||||
<$action-sendmessage $message="tm-add-tag" $param=<<__tag__>>/>
|
||||
</$fieldmangler>
|
||||
</$list>
|
||||
</$set>
|
||||
\end
|
||||
|
||||
\define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div",storyview:"")
|
||||
\whitespace trim
|
||||
<span class="tc-tagged-draggable-list">
|
||||
<$set name="tag" value=<<__tag__>>>
|
||||
<$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>> storyview=<<__storyview__>>>
|
||||
<$elementTag$ class="tc-menu-list-item">
|
||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<$elementTag$ class="tc-droppable-placeholder"/>
|
||||
<$elementTag$>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$view field="fr-title">
|
||||
<$view field="title"/>
|
||||
</$view>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</$elementTag$>
|
||||
</$droppable>
|
||||
</$elementTag$>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<$elementTag$ class="tc-droppable-placeholder"/>
|
||||
<$elementTag$ style="height:0.5em;">
|
||||
</$elementTag$>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$set>
|
||||
</span>
|
||||
\end
|
||||
39
editions/fr-FR/tiddlers/system/$__core_macros_tag.tid
Normal file
39
editions/fr-FR/tiddlers/system/$__core_macros_tag.tid
Normal file
@@ -0,0 +1,39 @@
|
||||
created: 20220830190922373
|
||||
modified: 20220830191056761
|
||||
tags: $:/tags/Macro
|
||||
title: $:/core/macros/tag
|
||||
|
||||
\define tag-pill-styles()
|
||||
background-color:$(backgroundColor)$;
|
||||
fill:$(foregroundColor)$;
|
||||
color:$(foregroundColor)$;
|
||||
\end
|
||||
|
||||
<!-- This has no whitespace trim to avoid modifying $actions$. Closing tags omitted for brevity. -->
|
||||
\define tag-pill-inner(tag,icon,colour,fallbackTarget,colourA,colourB,element-tag,element-attributes,actions)
|
||||
<$vars
|
||||
foregroundColor=<<contrastcolour target:"""$colour$""" fallbackTarget:"""$fallbackTarget$""" colourA:"""$colourA$""" colourB:"""$colourB$""">>
|
||||
backgroundColor="""$colour$"""
|
||||
><$element-tag$
|
||||
$element-attributes$
|
||||
class="tc-tag-label tc-btn-invisible"
|
||||
style=<<tag-pill-styles>>
|
||||
>$actions$<$transclude tiddler="""$icon$"""/><$view tiddler=<<__tag__>> field="fr-title" format="text"><$view tiddler=<<__tag__>> field="title" format="text" /></$view></$element-tag$>
|
||||
\end
|
||||
|
||||
\define tag-pill-body(tag,icon,colour,palette,element-tag,element-attributes,actions)
|
||||
<$macrocall $name="tag-pill-inner" tag=<<__tag__>> icon="""$icon$""" colour="""$colour$""" fallbackTarget={{$palette$##tag-background}} colourA={{$palette$##foreground}} colourB={{$palette$##background}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/>
|
||||
\end
|
||||
|
||||
\define tag-pill(tag,element-tag:"span",element-attributes:"",actions:"")
|
||||
\whitespace trim
|
||||
<span class="tc-tag-list-item" data-tag-title=<<__tag__>>>
|
||||
<$let currentTiddler=<<__tag__>>>
|
||||
<$macrocall $name="tag-pill-body" tag=<<__tag__>> icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/>
|
||||
</$let>
|
||||
</span>
|
||||
\end
|
||||
|
||||
\define tag(tag)
|
||||
{{$tag$||$:/core/ui/TagTemplate}}
|
||||
\end
|
||||
@@ -0,0 +1,25 @@
|
||||
created: 20220830194301860
|
||||
modified: 20220830194658750
|
||||
title: $:/core/ui/TagPickerTagTemplate
|
||||
|
||||
\whitespace trim
|
||||
<$button class=<<button-classes>> tag="a" tooltip={{$:/language/EditTemplate/Tags/Add/Button/Hint}}>
|
||||
<$list filter="[<saveTiddler>minlength[1]]">
|
||||
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="[<tag>]"/>
|
||||
</$list>
|
||||
<$set name="currentTiddlerCSSEscaped" value={{{ [<saveTiddler>escapecss[]] }}}>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param=<<get-tagpicker-focus-selector>> preventScroll="true"/>
|
||||
</$set>
|
||||
<<delete-tag-state-tiddlers>>
|
||||
<$list filter="[<refreshTitle>minlength[1]]">
|
||||
<$action-setfield $tiddler=<<refreshTitle>> text="yes"/>
|
||||
</$list>
|
||||
<<actions>>
|
||||
<$set name="backgroundColor" value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}>
|
||||
<$wikify name="foregroundColor" text="""<$macrocall $name="contrastcolour" target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>>/>""">
|
||||
<span class="tc-tag-label tc-btn-invisible" style=<<tag-pill-styles>>>
|
||||
{{||$:/core/ui/TiddlerIcon}}<$view field="fr-title" format="text"><$view field="title" format="text"/></$view>
|
||||
</span>
|
||||
</$wikify>
|
||||
</$set>
|
||||
</$button>
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: 5.2.4
|
||||
created: 20220802100223020
|
||||
modified: 20220802100223020
|
||||
created: 20221101094408196
|
||||
modified: 20221101094408196
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.2.4
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -11,9 +11,19 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
*
|
||||
|
||||
! Translation improvements
|
||||
! Translation improvement
|
||||
|
||||
*
|
||||
Improvements to the following translations:
|
||||
|
||||
* Chinese
|
||||
* Polish
|
||||
* Spanish
|
||||
* Japanese
|
||||
|
||||
Improvements to the translation features of TiddlyWiki:
|
||||
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6882">> the [[Translators Edition|Translate TiddlyWiki into your language]] to add an option to display the original English text underneath the text area
|
||||
* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/6933">> "delete" button text in $:/AdvancedSearch so that it is translatable
|
||||
|
||||
! Accessibility Improvements
|
||||
|
||||
@@ -21,11 +31,17 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
! Usability Improvements
|
||||
|
||||
*
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d62a16ee464fb9984b766b48504829a1a3eb143b">> problem with long presses on tiddler links triggering a preview on iOS/iPadOS
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6910">> consistency of button and input elements across browsers
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d825f1c875f5e46158c9c41c8c66471138c162d1">> edit preview to use the [[View Template Body Cascade]]
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/36896c3db8c9678c0385a561996248a6f00a45ff">> opening a tiddler in a new window to use the [[View Template Body Cascade]]
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6970">> detection of infinite recursion errors in widgets and filters
|
||||
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6877">> default styles for [[styled runs|Styles and Classes in WikiText]]
|
||||
|
||||
! Widget Improvements
|
||||
|
||||
*
|
||||
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6961">> new GenesisWidget that allows the dynamic construction of another widget, where the name and attributes of the new widget can be dynamically determined, without needing to be known in advance
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/127f660c91020dcbb43897d954066b31af729e74">> EditTextWidget to remove the default text "Type the text for the tiddler 'foo'"
|
||||
|
||||
! Filter improvements
|
||||
|
||||
@@ -33,11 +49,20 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
! Hackability Improvements
|
||||
|
||||
*
|
||||
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6976">> support for [[SystemTag: $:/tags/ClassFilters/TiddlerTemplate]] and [[SystemTag: $:/tags/ClassFilters/PageTemplate]] to assign dynamic CSS classes to both tiddler frames and the page template
|
||||
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6936">> new operators for reading and formatting JSON data: [[jsonget Operator]], [[jsonindexes Operator]], [[jsontype Operator]] and [[format Operator]]
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/c5d3d4c26e8fe27f272dda004aec27d6b66c4f60">> safe mode to disable wiki store indexers
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/166a1565843878083fb1eba47c73b8e67b78400d">> safe mode to prevent globally disabling parser rules
|
||||
* <<.link-badge-removed "https://github.com/Jermolene/TiddlyWiki5/commit/1df4c29d73073788ba3859668112e8bb46171a6c">> restriction of the LetWidget being unable to create variables whose names begin with a dollar sign
|
||||
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6735">> keyboard shortcut handling to allow to global shortcuts to override all other shortcuts
|
||||
|
||||
|
||||
! Bug Fixes
|
||||
|
||||
*
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/fb34df84ed41882c1c2a6ff54f0e908b43ef95a3">> "new image" keyboard shortcut not to assign journal tags
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6987">> SelectWidget class to update if it uses a filter
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/6303">> issue with availability of variables within filter runs
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7017">> issue with wikification within the advanced search filter dropdown
|
||||
|
||||
! Developer Improvements
|
||||
|
||||
@@ -45,15 +70,35 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
! Node.js Improvements
|
||||
|
||||
*
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6947">> console logging to avoid spaces and `<empty string>` message
|
||||
|
||||
! Performance Improvements
|
||||
|
||||
*
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/53d229592df76c6dd607e40be5bea4d5e063c48e">> performance of `wiki.getTiddler()`
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/81ac9874846b3ead275f67010fcfdb49f3d2f43c">> performance of variable prototype chain handling
|
||||
|
||||
|
||||
! Acknowledgements
|
||||
|
||||
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
|
||||
|
||||
<<.contributors """
|
||||
bestony
|
||||
btheado
|
||||
BramChen
|
||||
EvidentlyCube
|
||||
FlashSystems
|
||||
flibbles
|
||||
fu-sen
|
||||
joebordes
|
||||
hoelzro
|
||||
Marxsal
|
||||
oflg
|
||||
pmario
|
||||
rmunn
|
||||
roma0104
|
||||
saqimtiaz
|
||||
tw-FRed
|
||||
twMat
|
||||
xcazin
|
||||
""">>
|
||||
|
||||
15
editions/test/tiddlers/tests/data/filters/Recursion.tid
Normal file
15
editions/test/tiddlers/tests/data/filters/Recursion.tid
Normal file
@@ -0,0 +1,15 @@
|
||||
title: Filters/Recursion
|
||||
description: Filter recursion detection
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
\define myfilter() [subfilter<myfilter>]
|
||||
|
||||
<$text text={{{ [subfilter<myfilter>] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>/**-- Excessive filter recursion --**/</p>
|
||||
27
editions/test/tiddlers/tests/data/filters/fake-variables.tid
Normal file
27
editions/test/tiddlers/tests/data/filters/fake-variables.tid
Normal file
@@ -0,0 +1,27 @@
|
||||
title: Filters/FakeVariables
|
||||
description: Test for https://github.com/Jermolene/TiddlyWiki5/issues/6303
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$list variable="var" filter="[[existing variable should have output]] :filter[[..currentTiddler]is[variable]]">
|
||||
<p><<var>></p>
|
||||
</$list>
|
||||
|
||||
<$list variable="var" filter="[[non-existing variable should not have output]] :filter[[nonExistingVariable]is[variable]]">
|
||||
<p><<var>></p>
|
||||
</$list>
|
||||
|
||||
<$list variable="var" filter="[[existing variable negated should not have output]] :filter[[..currentTiddler]!is[variable]]">
|
||||
<p><<var>></p>
|
||||
</$list>
|
||||
|
||||
<$list variable="var" filter="[[non-existing variable negated should have output]] :filter[[nonExistingVariable]!is[variable]]">
|
||||
<p><<var>></p>
|
||||
</$list>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><p>existing variable should have output</p></p><p></p><p></p><p><p>non-existing variable negated should have output</p></p>
|
||||
@@ -0,0 +1,14 @@
|
||||
title: Genesis/DollarSigns
|
||||
description: Usage of genesis widget with attributes starting with dollar signs
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$genesis $type="$let" myvar="Kitten">(<$text text=<<myvar>>/>)</$genesis>
|
||||
<$genesis $type="$let" $$myvar="Kitten">(<$text text=<<$myvar>>/>)</$genesis>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>(Kitten)(Kitten)</p>
|
||||
@@ -0,0 +1,14 @@
|
||||
title: Genesis/MultipleAttributes
|
||||
description: Usage of genesis widget with multiple attributes
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$genesis $type="$let" $names="myvar other" $values="Kitten Donkey" myvar={{{ Shark }}}>(<$text text=<<myvar>>/>|<$text text=<<other>>/>)</$genesis>
|
||||
<$genesis $type="$let" $names="$myvar $other" $values="Kitten Donkey" $$myvar="Shark">(<$text text=<<$myvar>>/>|<$text text=<<$other>>/>)</$genesis>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>(Kitten|Donkey)(Kitten|Donkey)</p>
|
||||
14
editions/test/tiddlers/tests/data/genesis-widget/Simple.tid
Normal file
14
editions/test/tiddlers/tests/data/genesis-widget/Simple.tid
Normal file
@@ -0,0 +1,14 @@
|
||||
title: Genesis/Simple
|
||||
description: Simple usage of genesis widget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$genesis $type="div">Mouse</$genesis>
|
||||
<$genesis $type="div" class="tc-thing" label="Squeak">Mouse</$genesis>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div>Mouse</div><div class="tc-thing" label="Squeak">Mouse</div></p>
|
||||
13
editions/test/tiddlers/tests/data/transclude/Recursion.tid
Normal file
13
editions/test/tiddlers/tests/data/transclude/Recursion.tid
Normal file
@@ -0,0 +1,13 @@
|
||||
title: Transclude/Recursion
|
||||
description: Transclusion recursion detection
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$transclude $tiddler="Output"/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><span class="tc-error">Recursive transclusion error in transclude widget</span></p>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user