mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-22 10:54:46 +00:00
Compare commits
145 Commits
improved-r
...
v5.2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cd2fc029d | ||
|
|
6235f29749 | ||
|
|
6385534638 | ||
|
|
ba3cba6170 | ||
|
|
34c9e83bec | ||
|
|
ed9cc84fb2 | ||
|
|
22bc826247 | ||
|
|
6e6efcafc9 | ||
|
|
3c81558d74 | ||
|
|
cc47bb0330 | ||
|
|
0ce5788747 | ||
|
|
1118de319e | ||
|
|
c2d82ccb32 | ||
|
|
a899aac92c | ||
|
|
bef11fe6a2 | ||
|
|
17a1ae23eb | ||
|
|
9f867ad51e | ||
|
|
28c1e6bfc3 | ||
|
|
2db886793e | ||
|
|
8d050e0e69 | ||
|
|
e77006de63 | ||
|
|
80442b7f7f | ||
|
|
10bb3ba09d | ||
|
|
58013ba435 | ||
|
|
ee7bde58a2 | ||
|
|
229159fea7 | ||
|
|
a311e5ebac | ||
|
|
9ff479ce87 | ||
|
|
77053cfe13 | ||
|
|
1bd58db944 | ||
|
|
4be0c17dd0 | ||
|
|
595da5f9f6 | ||
|
|
ff674b9117 | ||
|
|
38dce175d6 | ||
|
|
1ab9f457b4 | ||
|
|
1eddb52de5 | ||
|
|
52fd6ce9c2 | ||
|
|
62308792c8 | ||
|
|
272ba6a4b7 | ||
|
|
90449c9458 | ||
|
|
5b8f36a594 | ||
|
|
acc7224758 | ||
|
|
0db987da60 | ||
|
|
a93a499684 | ||
|
|
2c33502a4a | ||
|
|
451a3454b5 | ||
|
|
45a7eb1c03 | ||
|
|
d03da6085b | ||
|
|
fa4dc2a4e9 | ||
|
|
3918e59cc1 | ||
|
|
f7ccba4c25 | ||
|
|
319d7fbe9c | ||
|
|
67c8f29160 | ||
|
|
856aca2f92 | ||
|
|
34a20463c7 | ||
|
|
56f13133d8 | ||
|
|
684673cbff | ||
|
|
bea1a6b14f | ||
|
|
32a033bb50 | ||
|
|
97f7db169a | ||
|
|
850a4dd351 | ||
|
|
d707e6f825 | ||
|
|
5c378855ab | ||
|
|
ebc1f7e4ce | ||
|
|
2fcbf3b521 | ||
|
|
00927d2e13 | ||
|
|
882e040e62 | ||
|
|
026739e2e0 | ||
|
|
fba9efcf4a | ||
|
|
acfea3a212 | ||
|
|
0dc30086e9 | ||
|
|
cb0d0cfa6d | ||
|
|
d32d559f93 | ||
|
|
8ead7e0624 | ||
|
|
4f7b10e055 | ||
|
|
b8a30091ee | ||
|
|
6955f14c3c | ||
|
|
994c5a2970 | ||
|
|
ea150029f5 | ||
|
|
c663d2ba00 | ||
|
|
39e8c8b125 | ||
|
|
3713ee4fa9 | ||
|
|
a99f934371 | ||
|
|
63803fd99d | ||
|
|
79ec96cb59 | ||
|
|
419b3b3534 | ||
|
|
368963def0 | ||
|
|
830ffe64de | ||
|
|
1483195cd5 | ||
|
|
9b9ff1e843 | ||
|
|
ea3503e30c | ||
|
|
5e116d2a57 | ||
|
|
aa5183a08e | ||
|
|
10cb585dae | ||
|
|
029203dbc0 | ||
|
|
832868ecae | ||
|
|
7f48b6c6ce | ||
|
|
b263ee3c80 | ||
|
|
b097d2ec48 | ||
|
|
ef779c11e8 | ||
|
|
06520c8994 | ||
|
|
710e51fe04 | ||
|
|
af6c017086 | ||
|
|
34353f4065 | ||
|
|
fedc23d73c | ||
|
|
344110e289 | ||
|
|
72f06581b6 | ||
|
|
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 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
.DS_Store
|
||||
.c9/
|
||||
.vscode/
|
||||
tmp/
|
||||
output/
|
||||
node_modules/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.2.3
|
||||
TW5_BUILD_VERSION=v5.2.4
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
@@ -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
|
||||
@@ -450,7 +459,7 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/library/$TW5_BUILD_VERSION \
|
||||
--build \
|
||||
--build library\
|
||||
|| exit 1
|
||||
|
||||
# Delete the temporary build tiddler
|
||||
|
||||
@@ -9,6 +9,7 @@ node ./tiddlywiki.js \
|
||||
--verbose \
|
||||
--version \
|
||||
--rendertiddler $:/core/save/all test.html text/plain \
|
||||
--test \
|
||||
|| exit 1
|
||||
|
||||
echo To run the tests in a browser, open "editions/test/output/test.html"
|
||||
|
||||
@@ -2403,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");
|
||||
|
||||
4
core/images/layout-button.tid
Executable file
4
core/images/layout-button.tid
Executable file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/layout-button
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" class="tc-image-layout-button tc-image-button" viewBox="0 0 24 24" stroke-width="1" stroke="none"><path d="M0 0h24v24H0z" fill="none"/><rect x="2" y="2" width="7" height="7" rx="2"/><rect x="2" y="13" width="7" height="9" rx="2"/><rect x="12" y="2" width="10" height="20" rx="2"/></svg>
|
||||
6
core/images/mastodon.tid
Normal file
6
core/images/mastodon.tid
Normal file
@@ -0,0 +1,6 @@
|
||||
title: $:/core/images/mastodon
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" class="tc-image-mastodon tc-image-button" viewBox="0 0 128 128">
|
||||
<path d="M112.716,76.735C111.231,85.764 99.411,95.646 85.836,97.561C78.757,98.559 71.787,99.476 64.355,99.073C52.201,98.415 42.61,95.646 42.61,95.646C42.61,97.044 42.683,98.374 42.829,99.619C44.409,113.79 54.723,114.639 64.493,115.035C74.354,115.434 83.134,112.163 83.134,112.163L83.539,122.695C83.539,122.695 76.642,127.071 64.355,127.875C57.58,128.315 49.167,127.674 39.369,124.61C18.118,117.965 14.463,91.202 13.904,64.048C13.733,55.985 13.839,48.383 13.839,42.024C13.839,14.257 29.238,6.118 29.238,6.118C37.002,1.905 50.326,0.134 64.177,-0L64.517,-0C78.369,0.134 91.701,1.905 99.465,6.118C99.465,6.118 114.864,14.257 114.864,42.024C114.864,42.024 115.057,62.511 112.716,76.735ZM96.7,44.179C96.7,37.307 95.219,31.847 92.245,27.807C89.177,23.767 85.16,21.696 80.174,21.696C74.403,21.696 70.034,24.316 67.146,29.556L64.337,35.118L61.529,29.556C58.64,24.316 54.271,21.696 48.501,21.696C43.514,21.696 39.497,23.767 36.43,27.807C33.455,31.847 31.974,37.307 31.974,44.179L31.974,77.8L43.249,77.8L43.249,45.167C43.249,38.288 45.699,34.796 50.599,34.796C56.017,34.796 58.733,38.938 58.733,47.128L58.733,64.99L69.941,64.99L69.941,47.128C69.941,38.938 72.657,34.796 78.075,34.796C82.975,34.796 85.425,38.288 85.425,45.167L85.425,77.8L96.7,77.8L96.7,44.179Z"/>
|
||||
</svg>
|
||||
@@ -59,6 +59,8 @@ Home/Caption: home
|
||||
Home/Hint: Open the default tiddlers
|
||||
Language/Caption: language
|
||||
Language/Hint: Choose the user interface language
|
||||
LayoutSwitcher/Hint: Open layout switcher
|
||||
LayoutSwitcher/Caption: layout
|
||||
Manager/Caption: tiddler manager
|
||||
Manager/Hint: Open tiddler manager
|
||||
More/Caption: more
|
||||
|
||||
@@ -7,7 +7,7 @@ Appearance/Hint: Ways to customise the appearance of your TiddlyWiki.
|
||||
Basics/AnimDuration/Prompt: Animation duration
|
||||
Basics/AutoFocus/Prompt: Default focus field for new tiddlers
|
||||
Basics/Caption: Basics
|
||||
Basics/DefaultTiddlers/BottomHint: Use [[double square brackets]] for titles with spaces. Or you can choose to <$button set="$:/DefaultTiddlers" setTo="[list[$:/StoryList]]">retain story ordering</$button>
|
||||
Basics/DefaultTiddlers/BottomHint: Use [[double square brackets]] for titles with spaces. Or you can choose to {{retain story ordering||$:/snippets/retain-story-ordering-button}}
|
||||
Basics/DefaultTiddlers/Prompt: Default tiddlers
|
||||
Basics/DefaultTiddlers/TopHint: Choose which tiddlers are displayed at startup
|
||||
Basics/Language/Prompt: Hello! Current language:
|
||||
@@ -90,8 +90,8 @@ Plugins/Languages/Caption: Languages
|
||||
Plugins/Languages/Hint: Language pack plugins
|
||||
Plugins/NoInfoFound/Hint: No ''"<$text text=<<currentTab>>/>"'' found
|
||||
Plugins/NotInstalled/Hint: This plugin is not currently installed
|
||||
Plugins/OpenPluginLibrary: open plugin library
|
||||
Plugins/ClosePluginLibrary: close plugin library
|
||||
Plugins/OpenPluginLibrary: Open plugin library
|
||||
Plugins/ClosePluginLibrary: Close plugin library
|
||||
Plugins/PluginWillRequireReload: (requires reload)
|
||||
Plugins/Plugins/Caption: Plugins
|
||||
Plugins/Plugins/Hint: Plugins
|
||||
|
||||
@@ -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
|
||||
|
||||
18
core/language/en-GB/Help/commands.tid
Normal file
18
core/language/en-GB/Help/commands.tid
Normal file
@@ -0,0 +1,18 @@
|
||||
title: $:/language/Help/commands
|
||||
description: Run commands returned from a filter
|
||||
|
||||
Sequentially run the command tokens returned from a filter
|
||||
|
||||
```
|
||||
--commands <filter>
|
||||
```
|
||||
|
||||
Examples
|
||||
|
||||
```
|
||||
--commands "[enlist{$:/build-commands-as-text}]"
|
||||
```
|
||||
|
||||
```
|
||||
--commands "[{$:/build-commands-as-json}jsonindexes[]] :map[{$:/build-commands-as-json}jsonget<currentTiddler>]"
|
||||
```
|
||||
42
core/modules/commands/commands.js
Normal file
42
core/modules/commands/commands.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/commands.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Runs the commands returned from a filter
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "commands",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
var Command = function(params, commander) {
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
// Parse the filter
|
||||
var filter = this.params[0];
|
||||
if(!filter) {
|
||||
return "No filter specified";
|
||||
}
|
||||
var commands = this.commander.wiki.filterTiddlers(filter)
|
||||
if(commands.length === 0) {
|
||||
return "No tiddlers found for filter '" + filter + "'";
|
||||
}
|
||||
this.commander.addCommandTokens(commands);
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -298,7 +298,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
Propogate keydown events to our container for the keyboard widgets benefit
|
||||
*/
|
||||
EditTextWidget.prototype.propogateKeydownEvent = function(event) {
|
||||
var newEvent = this.cloneEvent(event,["keyCode","which","metaKey","ctrlKey","altKey","shiftKey"]);
|
||||
var newEvent = this.cloneEvent(event,["keyCode","code","which","key","metaKey","ctrlKey","altKey","shiftKey"]);
|
||||
return !this.parentDomNode.dispatchEvent(newEvent);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,9 +17,23 @@ exports["jsonget"] = function(source,operator,options) {
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
var item = getDataItemValueAsString(data,operator.operands);
|
||||
var items = getDataItemValueAsStrings(data,operator.operands);
|
||||
if(items !== undefined) {
|
||||
results.push.apply(results,items);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsonextract"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
var item = getDataItem(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push(item);
|
||||
results.push(JSON.stringify(item));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -31,9 +45,9 @@ exports["jsonindexes"] = function(source,operator,options) {
|
||||
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);
|
||||
var items = getDataItemKeysAsStrings(data,operator.operands);
|
||||
if(items !== undefined) {
|
||||
results.push.apply(results,items);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -57,11 +71,11 @@ exports["jsontype"] = function(source,operator,options) {
|
||||
/*
|
||||
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) {
|
||||
function getDataItemValueAsStrings(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item as a string
|
||||
return convertDataItemValueToString(item);
|
||||
// Return the item as a string list
|
||||
return convertDataItemValueToStrings(item);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -77,15 +91,34 @@ function getDataItemKeysAsStrings(data,indexes) {
|
||||
/*
|
||||
Return an array of the string representation of the values of a data item, or "undefined" if the item is undefined
|
||||
*/
|
||||
function convertDataItemValueToString(item) {
|
||||
function convertDataItemValueToStrings(item) {
|
||||
// Return the item as a string
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
return undefined;
|
||||
} else if(item === null) {
|
||||
return ["null"]
|
||||
} else if(typeof item === "object") {
|
||||
var results = [],i,t;
|
||||
if($tw.utils.isArray(item)) {
|
||||
// Return all the items in arrays recursively
|
||||
for(i=0; i<item.length; i++) {
|
||||
t = convertDataItemValueToStrings(item[i])
|
||||
if(t !== undefined) {
|
||||
results.push.apply(results,t);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Return all the values in objects recursively
|
||||
$tw.utils.each(Object.keys(item).sort(),function(key) {
|
||||
t = convertDataItemValueToStrings(item[key]);
|
||||
if(t !== undefined) {
|
||||
results.push.apply(results,t);
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
if(typeof item === "object") {
|
||||
return JSON.stringify(item);
|
||||
}
|
||||
return item.toString();
|
||||
return [item.toString()];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -13,6 +13,11 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
|
||||
"use strict";
|
||||
|
||||
var CsvParser = function(type,text,options) {
|
||||
// Special handler for tab-delimited files
|
||||
if (type === 'text/tab-delimited-values' && !options.separator) {
|
||||
options.separator = "\t";
|
||||
}
|
||||
|
||||
// Table framework
|
||||
this.tree = [{
|
||||
"type": "scrollable", "children": [{
|
||||
@@ -24,30 +29,33 @@ var CsvParser = function(type,text,options) {
|
||||
}]
|
||||
}];
|
||||
// Split the text into lines
|
||||
var lines = text.split(/\r?\n/mg),
|
||||
var lines = $tw.utils.parseCsvString(text, options),
|
||||
tag = "th";
|
||||
var maxColumns = 0;
|
||||
$tw.utils.each(lines, function(columns) {
|
||||
maxColumns = Math.max(columns.length, maxColumns);
|
||||
});
|
||||
|
||||
for(var line=0; line<lines.length; line++) {
|
||||
var lineText = lines[line];
|
||||
if(lineText) {
|
||||
var row = {
|
||||
"type": "element", "tag": "tr", "children": []
|
||||
};
|
||||
var columns = lineText.split(",");
|
||||
for(var column=0; column<columns.length; column++) {
|
||||
row.children.push({
|
||||
"type": "element", "tag": tag, "children": [{
|
||||
"type": "text",
|
||||
"text": columns[column]
|
||||
}]
|
||||
});
|
||||
}
|
||||
tag = "td";
|
||||
this.tree[0].children[0].children[0].children.push(row);
|
||||
var columns = lines[line];
|
||||
var row = {
|
||||
"type": "element", "tag": "tr", "children": []
|
||||
};
|
||||
for(var column=0; column<maxColumns; column++) {
|
||||
row.children.push({
|
||||
"type": "element", "tag": tag, "children": [{
|
||||
"type": "text",
|
||||
"text": columns[column] || ''
|
||||
}]
|
||||
});
|
||||
}
|
||||
tag = "td";
|
||||
this.tree[0].children[0].children[0].children.push(row);
|
||||
}
|
||||
};
|
||||
|
||||
exports["text/csv"] = CsvParser;
|
||||
exports["text/tab-delimited-values"] = CsvParser;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ exports.parse = function() {
|
||||
var reEnd;
|
||||
if(this.match[3]) {
|
||||
// If so, the end of the body is marked with \end
|
||||
reEnd = /(\r?\n\\end[^\S\n\r]*(?:$|\r?\n))/mg;
|
||||
reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
|
||||
} else {
|
||||
// Otherwise, the end of the definition is marked by the end of the line
|
||||
reEnd = /($|\r?\n)/mg;
|
||||
|
||||
68
core/modules/parsers/wikiparser/rules/parsermode.js
Normal file
68
core/modules/parsers/wikiparser/rules/parsermode.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/parsermode.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki pragma rule for parser mode specifications
|
||||
|
||||
```
|
||||
\parsermode block
|
||||
\parsermode inline
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "parsermode";
|
||||
exports.types = {pragma: true};
|
||||
|
||||
/*
|
||||
Instantiate parse rule
|
||||
*/
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /^\\parsermode[^\S\n]/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Move past the pragma invocation
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Parse whitespace delimited tokens terminated by a line break
|
||||
var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg,
|
||||
parserMode = undefined;
|
||||
reMatch.lastIndex = this.parser.pos;
|
||||
var match = reMatch.exec(this.parser.source);
|
||||
while(match && match.index === this.parser.pos) {
|
||||
this.parser.pos = reMatch.lastIndex;
|
||||
// Exit if we've got the line break
|
||||
if(match[2]) {
|
||||
break;
|
||||
}
|
||||
// Process the token
|
||||
if(match[1]) {
|
||||
parserMode = match[1];
|
||||
}
|
||||
// Match the next token
|
||||
match = reMatch.exec(this.parser.source);
|
||||
}
|
||||
// Process the tokens
|
||||
if(parserMode !== undefined) {
|
||||
if(parserMode === "block") {
|
||||
this.parser.parseAsInline = false;
|
||||
} else if(parserMode === "inline") {
|
||||
this.parser.parseAsInline = true;
|
||||
}
|
||||
}
|
||||
// No parse tree nodes to return
|
||||
return [];
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ var WikiParser = function(type,text,options) {
|
||||
this.sourceLength = this.source.length;
|
||||
// Flag for ignoring whitespace
|
||||
this.configTrimWhiteSpace = false;
|
||||
// Parser mode
|
||||
this.parseAsInline = options.parseAsInline;
|
||||
// Set current parse position
|
||||
this.pos = 0;
|
||||
// Start with empty output
|
||||
@@ -83,7 +85,7 @@ var WikiParser = function(type,text,options) {
|
||||
// Parse any pragmas
|
||||
var topBranch = this.parsePragmas();
|
||||
// Parse the text into inline runs or blocks
|
||||
if(options.parseAsInline) {
|
||||
if(this.parseAsInline) {
|
||||
topBranch.push.apply(topBranch,this.parseInlineRun());
|
||||
} else {
|
||||
topBranch.push.apply(topBranch,this.parseBlocks());
|
||||
|
||||
@@ -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",{
|
||||
|
||||
@@ -121,7 +121,11 @@ exports.startup = function() {
|
||||
});
|
||||
// Set up the syncer object if we've got a syncadaptor
|
||||
if($tw.syncadaptor) {
|
||||
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki, syncadaptor: $tw.syncadaptor});
|
||||
$tw.syncer = new $tw.Syncer({
|
||||
wiki: $tw.wiki,
|
||||
syncadaptor: $tw.syncadaptor,
|
||||
logging: $tw.wiki.getTiddlerText('$:/config/SyncLogging', "yes") === "yes"
|
||||
});
|
||||
}
|
||||
// Setup the saver handler
|
||||
$tw.saverHandler = new $tw.SaverHandler({
|
||||
|
||||
@@ -12,35 +12,113 @@ A barebones CSV parser
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var QUOTE = '"';
|
||||
|
||||
var getCellInfo = function(text, start, length, SEPARATOR) {
|
||||
var isCellQuoted = text.charAt(start) === QUOTE;
|
||||
var cellStart = isCellQuoted ? start + 1 : start;
|
||||
|
||||
if (text.charAt(i) === SEPARATOR) {
|
||||
return [cellStart, cellStart, false];
|
||||
}
|
||||
|
||||
for (var i = cellStart; i < length; i++) {
|
||||
var cellCharacter = text.charAt(i);
|
||||
var isEOL = cellCharacter === "\n" || cellCharacter === "\r";
|
||||
|
||||
if (isEOL && !isCellQuoted) {
|
||||
return [cellStart, i, false];
|
||||
|
||||
} else if (cellCharacter === SEPARATOR && !isCellQuoted) {
|
||||
return [cellStart, i, false];
|
||||
|
||||
} else if (cellCharacter === QUOTE && isCellQuoted) {
|
||||
var nextCharacter = i + 1 < length ? text.charAt(i + 1) : '';
|
||||
if (nextCharacter !== QUOTE) {
|
||||
return [cellStart, i, true];
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [cellStart, i, isCellQuoted];
|
||||
}
|
||||
|
||||
exports.parseCsvString = function(text, options) {
|
||||
if (!text) {
|
||||
return [];
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
var SEPARATOR = options.separator || ",",
|
||||
length = text.length,
|
||||
rows = [],
|
||||
nextRow = [];
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var cellInfo = getCellInfo(text, i, length, SEPARATOR);
|
||||
var cellText = text.substring(cellInfo[0], cellInfo[1]);
|
||||
if (cellInfo[2]) {
|
||||
cellText = cellText.replace(/""/g, '"');
|
||||
cellInfo[1]++;
|
||||
}
|
||||
nextRow.push(cellText);
|
||||
|
||||
i = cellInfo[1];
|
||||
|
||||
var character = text.charAt(i);
|
||||
var nextCharacter = i + 1 < length ? text.charAt(i + 1) : '';
|
||||
|
||||
if (character === "\r" || character === "\n") {
|
||||
// Edge case for empty rows
|
||||
if (nextRow.length === 1 && nextRow[0] === '') {
|
||||
nextRow.length = 0;
|
||||
}
|
||||
rows.push(nextRow);
|
||||
nextRow = [];
|
||||
|
||||
if (character === "\r") {
|
||||
var nextCharacter = i + 1 < length ? text.charAt(i + 1) : '';
|
||||
|
||||
if (nextCharacter === "\n") {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case if last cell in last row is an empty cell
|
||||
if (text.charAt(length - 1) === SEPARATOR) {
|
||||
nextRow.push("");
|
||||
}
|
||||
|
||||
rows.push(nextRow);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse a CSV string with a header row and return an array of hashmaps.
|
||||
*/
|
||||
exports.parseCsvStringWithHeader = function(text,options) {
|
||||
options = options || {};
|
||||
var separator = options.separator || ",",
|
||||
rows = text.split(/\r?\n/mg).map(function(row) {
|
||||
return $tw.utils.trim(row);
|
||||
}).filter(function(row) {
|
||||
return row !== "";
|
||||
});
|
||||
if(rows.length < 1) {
|
||||
return "Missing header row";
|
||||
}
|
||||
var headings = rows[0].split(separator),
|
||||
results = [];
|
||||
for(var row=1; row<rows.length; row++) {
|
||||
var columns = rows[row].split(separator),
|
||||
columnResult = Object.create(null);
|
||||
if(columns.length !== headings.length) {
|
||||
return "Malformed CSV row '" + rows[row] + "'";
|
||||
var csv = $tw.utils.parseCsvString(text, options);
|
||||
var headers = csv[0];
|
||||
|
||||
csv = csv.slice(1);
|
||||
for (var i = 0; i < csv.length; i++) {
|
||||
var row = csv[i];
|
||||
var rowObject = Object.create(null);
|
||||
|
||||
for(var columnIndex=0; columnIndex<headers.length; columnIndex++) {
|
||||
var columnName = headers[columnIndex];
|
||||
if (columnName) {
|
||||
rowObject[columnName] = $tw.utils.trim(row[columnIndex] || "");
|
||||
}
|
||||
}
|
||||
for(var column=0; column<columns.length; column++) {
|
||||
var columnName = headings[column];
|
||||
columnResult[columnName] = $tw.utils.trim(columns[column] || "");
|
||||
}
|
||||
results.push(columnResult);
|
||||
csv[i] = rowObject;
|
||||
}
|
||||
return results;
|
||||
return csv;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -12,6 +12,8 @@ Various static DOM-related utility functions.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
/*
|
||||
Determines whether element 'a' contains element 'b'
|
||||
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
|
||||
@@ -294,8 +296,21 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
});
|
||||
|
||||
if(selectedNode.offsetLeft) {
|
||||
// Add a variable with a popup coordinate string for the selected node
|
||||
variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")";
|
||||
// Add variables with a (relative and absolute) popup coordinate string for the selected node
|
||||
var nodeRect = {
|
||||
left: selectedNode.offsetLeft,
|
||||
top: selectedNode.offsetTop,
|
||||
width: selectedNode.offsetWidth,
|
||||
height: selectedNode.offsetHeight
|
||||
};
|
||||
variables["tv-popup-coords"] = Popup.buildCoordinates(Popup.coordinatePrefix.csOffsetParent,nodeRect);
|
||||
|
||||
var absRect = $tw.utils.extend({}, nodeRect);
|
||||
for (var currentNode = selectedNode.offsetParent; currentNode; currentNode = currentNode.offsetParent) {
|
||||
absRect.left += currentNode.offsetLeft;
|
||||
absRect.top += currentNode.offsetTop;
|
||||
}
|
||||
variables["tv-popup-abs-coords"] = Popup.buildCoordinates(Popup.coordinatePrefix.csAbsolute,absRect);
|
||||
|
||||
// Add variables for offset of selected node
|
||||
variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString();
|
||||
|
||||
@@ -26,6 +26,8 @@ Display a modal dialogue
|
||||
options: see below
|
||||
Options include:
|
||||
downloadLink: Text of a big download link to include
|
||||
event: widget event
|
||||
variables: from event.paramObject
|
||||
*/
|
||||
Modal.prototype.display = function(title,options) {
|
||||
options = options || {};
|
||||
@@ -209,6 +211,10 @@ Modal.prototype.display = function(title,options) {
|
||||
headerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
||||
bodyWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
||||
footerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
||||
// Whether to close the modal dialog when the mask (area outside the modal) is clicked
|
||||
if(tiddler.fields && (tiddler.fields["mask-closable"] === "yes" || tiddler.fields["mask-closable"] === "true")) {
|
||||
modalBackdrop.addEventListener("click",closeHandler,false);
|
||||
}
|
||||
// Set the initial styles for the message
|
||||
$tw.utils.setStyle(modalBackdrop,[
|
||||
{opacity: "0"}
|
||||
|
||||
@@ -22,6 +22,19 @@ var Popup = function(options) {
|
||||
this.popups = []; // Array of {title:,wiki:,domNode:} objects
|
||||
};
|
||||
|
||||
/*
|
||||
Global regular expression for parsing the location of a popup.
|
||||
This is also used by the Reveal widget.
|
||||
*/
|
||||
exports.popupLocationRegExp = /^(@?)\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/
|
||||
|
||||
/*
|
||||
Objekt containing the available prefixes for coordinates build with the `buildCoordinates` function:
|
||||
- csOffsetParent: Uses a coordinate system based on the offset parent (no prefix).
|
||||
- csAbsolute: Use an absolute coordinate system (prefix "@").
|
||||
*/
|
||||
exports.coordinatePrefix = { csOffsetParent: "", csAbsolute: "@" }
|
||||
|
||||
/*
|
||||
Trigger a popup open or closed. Parameters are in a hashmap:
|
||||
title: title of the tiddler where the popup details are stored
|
||||
@@ -136,8 +149,17 @@ Popup.prototype.show = function(options) {
|
||||
height: options.domNode.offsetHeight
|
||||
};
|
||||
}
|
||||
var popupRect = "(" + rect.left + "," + rect.top + "," +
|
||||
rect.width + "," + rect.height + ")";
|
||||
if(options.absolute && options.domNode) {
|
||||
// Walk the offsetParent chain and add the position of the offsetParents to make
|
||||
// the position absolute to the root node of the page.
|
||||
var currentNode = options.domNode.offsetParent;
|
||||
while(currentNode) {
|
||||
rect.left += currentNode.offsetLeft;
|
||||
rect.top += currentNode.offsetTop;
|
||||
currentNode = currentNode.offsetParent;
|
||||
}
|
||||
}
|
||||
var popupRect = exports.buildCoordinates(options.absolute?exports.coordinatePrefix.csAbsolute:exports.coordinatePrefix.csOffsetParent,rect);
|
||||
if(options.noStateReference) {
|
||||
options.wiki.setText(options.title,"text",undefined,popupRect);
|
||||
} else {
|
||||
@@ -172,13 +194,54 @@ Popup.prototype.cancel = function(level) {
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the specified title and text identifies an active popup
|
||||
Returns true if the specified title and text identifies an active popup.
|
||||
This function is safe to call, even if the popup class was not initialized.
|
||||
*/
|
||||
Popup.prototype.readPopupState = function(text) {
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/;
|
||||
return popupLocationRegExp.test(text);
|
||||
exports.readPopupState = function(text) {
|
||||
return exports.popupLocationRegExp.test(text);
|
||||
};
|
||||
|
||||
/*
|
||||
Parses a coordinate string in the format `(x,y,w,h)` or `@(x,y,z,h)` and returns
|
||||
an object containing the position, width and height. The absolute-Mark is boolean
|
||||
value that indicates the coordinate system of the coordinates. If they start with
|
||||
an `@`, `absolute` is set to true and the coordinates are relative to the root
|
||||
element. If the initial `@` is missing, they are relative to the offset parent
|
||||
element and `absoute` is false.
|
||||
This function is safe to call, even if the popup class was not initialized.
|
||||
*/
|
||||
exports.parseCoordinates = function(coordinates) {
|
||||
var match = exports.popupLocationRegExp.exec(coordinates);
|
||||
if(match) {
|
||||
return {
|
||||
absolute: (match[1] === "@"),
|
||||
left: parseFloat(match[2]),
|
||||
top: parseFloat(match[3]),
|
||||
width: parseFloat(match[4]),
|
||||
height: parseFloat(match[5])
|
||||
};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Builds a coordinate string from a coordinate system identifier and an object
|
||||
containing the left, top, width and height values.
|
||||
Use constants defined in coordinatePrefix to specify a coordinate system.
|
||||
If one of the parameters is invalid for building a coordinate string `(0,0,0,0)`
|
||||
will be returned.
|
||||
This function is safe to call, even if the popup class was not initialized.
|
||||
*/
|
||||
exports.buildCoordinates = function(prefix,position) {
|
||||
var coord = prefix + "(" + position.left + "," + position.top + "," + position.width + "," + position.height + ")";
|
||||
if (exports.popupLocationRegExp.test(coord)) {
|
||||
return coord;
|
||||
} else {
|
||||
return "(0,0,0,0)";
|
||||
}
|
||||
}
|
||||
|
||||
exports.Popup = Popup;
|
||||
|
||||
})();
|
||||
|
||||
@@ -15,10 +15,11 @@ function LinkedList() {
|
||||
|
||||
LinkedList.prototype.clear = function() {
|
||||
// LinkedList performs the duty of both the head and tail node
|
||||
this.next = Object.create(null);
|
||||
this.prev = Object.create(null);
|
||||
this.first = undefined;
|
||||
this.last = undefined;
|
||||
this.next = new LLMap();
|
||||
this.prev = new LLMap();
|
||||
// Linked list head initially points to itself
|
||||
this.next.set(null, null);
|
||||
this.prev.set(null, null);
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
@@ -41,28 +42,29 @@ Push behaves like array.push and accepts multiple string arguments. But it also
|
||||
accepts a single array argument too, to be consistent with its other methods.
|
||||
*/
|
||||
LinkedList.prototype.push = function(/* values */) {
|
||||
var values = arguments;
|
||||
var i, values = arguments;
|
||||
if($tw.utils.isArray(values[0])) {
|
||||
values = values[0];
|
||||
}
|
||||
for(var i = 0; i < values.length; i++) {
|
||||
for(i = 0; i < values.length; i++) {
|
||||
_assertString(values[i]);
|
||||
}
|
||||
for(var i = 0; i < values.length; i++) {
|
||||
for(i = 0; i < values.length; i++) {
|
||||
_linkToEnd(this,values[i]);
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
LinkedList.prototype.pushTop = function(value) {
|
||||
var t;
|
||||
if($tw.utils.isArray(value)) {
|
||||
for (var t=0; t<value.length; t++) {
|
||||
for (t=0; t<value.length; t++) {
|
||||
_assertString(value[t]);
|
||||
}
|
||||
for(var t=0; t<value.length; t++) {
|
||||
for(t=0; t<value.length; t++) {
|
||||
_removeOne(this,value[t]);
|
||||
}
|
||||
for(var t=0; t<value.length; t++) {
|
||||
for(t=0; t<value.length; t++) {
|
||||
_linkToEnd(this,value[t]);
|
||||
}
|
||||
} else {
|
||||
@@ -74,11 +76,11 @@ LinkedList.prototype.pushTop = function(value) {
|
||||
|
||||
LinkedList.prototype.each = function(callback) {
|
||||
var visits = Object.create(null),
|
||||
value = this.first;
|
||||
while(value !== undefined) {
|
||||
value = this.next.get(null);
|
||||
while(value !== null) {
|
||||
callback(value);
|
||||
var next = this.next[value];
|
||||
if(typeof next === "object") {
|
||||
var next = this.next.get(value);
|
||||
if(Array.isArray(next)) {
|
||||
var i = visits[value] || 0;
|
||||
visits[value] = i+1;
|
||||
value = next[i];
|
||||
@@ -105,91 +107,79 @@ LinkedList.prototype.makeTiddlerIterator = function(wiki) {
|
||||
};
|
||||
|
||||
function _removeOne(list,value) {
|
||||
var prevEntry = list.prev[value],
|
||||
nextEntry = list.next[value],
|
||||
var nextEntry = list.next.get(value);
|
||||
if(nextEntry === undefined) {
|
||||
return;
|
||||
}
|
||||
var prevEntry = list.prev.get(value),
|
||||
prev = prevEntry,
|
||||
next = nextEntry;
|
||||
if(typeof nextEntry === "object") {
|
||||
next = nextEntry,
|
||||
ref;
|
||||
if(Array.isArray(nextEntry)) {
|
||||
next = nextEntry[0];
|
||||
prev = prevEntry[0];
|
||||
}
|
||||
// Relink preceding element.
|
||||
if(list.first === value) {
|
||||
list.first = next
|
||||
} else if(prev !== undefined) {
|
||||
if(typeof list.next[prev] === "object") {
|
||||
if(next === undefined) {
|
||||
// Must have been last, and 'i' would be last element.
|
||||
list.next[prev].pop();
|
||||
} else {
|
||||
var i = list.next[prev].indexOf(value);
|
||||
list.next[prev][i] = next;
|
||||
}
|
||||
} else {
|
||||
list.next[prev] = next;
|
||||
}
|
||||
ref = list.next.get(prev);
|
||||
if(Array.isArray(ref)) {
|
||||
var i = ref.indexOf(value);
|
||||
ref[i] = next;
|
||||
} else {
|
||||
return;
|
||||
list.next.set(prev,next);
|
||||
}
|
||||
|
||||
// Now relink following element
|
||||
// Check "next !== undefined" rather than "list.last === value" because
|
||||
// we need to know if the FIRST value is the last in the list, not the last.
|
||||
if(next !== undefined) {
|
||||
if(typeof list.prev[next] === "object") {
|
||||
if(prev === undefined) {
|
||||
// Must have been first, and 'i' would be 0.
|
||||
list.prev[next].shift();
|
||||
} else {
|
||||
var i = list.prev[next].indexOf(value);
|
||||
list.prev[next][i] = prev;
|
||||
}
|
||||
} else {
|
||||
list.prev[next] = prev;
|
||||
}
|
||||
ref = list.prev.get(next);
|
||||
if(Array.isArray(ref)) {
|
||||
var i = ref.indexOf(value);
|
||||
ref[i] = prev;
|
||||
} else {
|
||||
list.last = prev;
|
||||
list.prev.set(next,prev);
|
||||
}
|
||||
|
||||
// Delink actual value. If it uses arrays, just remove first entries.
|
||||
if(typeof nextEntry === "object") {
|
||||
if(Array.isArray(nextEntry) && nextEntry.length > 1) {
|
||||
nextEntry.shift();
|
||||
prevEntry.shift();
|
||||
} else {
|
||||
list.next[value] = undefined;
|
||||
list.prev[value] = undefined;
|
||||
list.next.set(value,undefined);
|
||||
list.prev.set(value,undefined);
|
||||
}
|
||||
list.length -= 1;
|
||||
};
|
||||
|
||||
// Sticks the given node onto the end of the list.
|
||||
function _linkToEnd(list,value) {
|
||||
if(list.first === undefined) {
|
||||
list.first = value;
|
||||
var old = list.next.get(value);
|
||||
var last = list.prev.get(null);
|
||||
// Does it already exists?
|
||||
if(old !== undefined) {
|
||||
if(!Array.isArray(old)) {
|
||||
old = [old];
|
||||
list.next.set(value,old);
|
||||
list.prev.set(value,[list.prev.get(value)]);
|
||||
}
|
||||
old.push(null);
|
||||
list.prev.get(value).push(last);
|
||||
} else {
|
||||
// Does it already exists?
|
||||
if(list.first === value || list.prev[value] !== undefined) {
|
||||
if(typeof list.next[value] === "string") {
|
||||
list.next[value] = [list.next[value]];
|
||||
list.prev[value] = [list.prev[value]];
|
||||
} else if(typeof list.next[value] === "undefined") {
|
||||
// list.next[value] must be undefined.
|
||||
// Special case. List already has 1 value. It's at the end.
|
||||
list.next[value] = [];
|
||||
list.prev[value] = [list.prev[value]];
|
||||
}
|
||||
list.prev[value].push(list.last);
|
||||
// We do NOT append a new value onto "next" list. Iteration will
|
||||
// figure out it must point to End-of-List on its own.
|
||||
} else {
|
||||
list.prev[value] = list.last;
|
||||
}
|
||||
// Make the old last point to this new one.
|
||||
if(typeof list.next[list.last] === "object") {
|
||||
list.next[list.last].push(value);
|
||||
} else {
|
||||
list.next[list.last] = value;
|
||||
}
|
||||
list.next.set(value,null);
|
||||
list.prev.set(value,last);
|
||||
}
|
||||
// Make the old last point to this new one.
|
||||
if(value !== last) {
|
||||
var array = list.next.get(last);
|
||||
if(Array.isArray(array)) {
|
||||
array[array.length-1] = value;
|
||||
} else {
|
||||
list.next.set(last,value);
|
||||
}
|
||||
list.prev.set(null,value);
|
||||
} else {
|
||||
// Edge case, the pushed value was already the last value.
|
||||
// The second-to-last nextPtr for that value must point to itself now.
|
||||
var array = list.next.get(last);
|
||||
array[array.length-2] = value;
|
||||
}
|
||||
list.last = value;
|
||||
list.length += 1;
|
||||
};
|
||||
|
||||
@@ -199,6 +189,20 @@ function _assertString(value) {
|
||||
}
|
||||
};
|
||||
|
||||
var LLMap = function() {
|
||||
this.map = Object.create(null);
|
||||
};
|
||||
|
||||
// Just a wrapper so our object map can also accept null.
|
||||
LLMap.prototype = {
|
||||
set: function(key,val) {
|
||||
(key === null) ? (this.null = val) : (this.map[key] = val);
|
||||
},
|
||||
get: function(key) {
|
||||
return (key === null) ? this.null : this.map[key];
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkedList = LinkedList;
|
||||
|
||||
})();
|
||||
|
||||
@@ -65,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 !== "") {
|
||||
@@ -88,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 + ";";
|
||||
|
||||
@@ -354,6 +354,9 @@ exports.formatDateString = function(date,template) {
|
||||
var result = "",
|
||||
t = template,
|
||||
matches = [
|
||||
[/^TIMESTAMP/, function() {
|
||||
return date.getTime();
|
||||
}],
|
||||
[/^0hh12/, function() {
|
||||
return $tw.utils.pad($tw.utils.getHours12(date));
|
||||
}],
|
||||
|
||||
@@ -14,6 +14,8 @@ Action widget to trigger a popup.
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
var ActionPopupWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
@@ -57,20 +59,20 @@ Invoke the action associated with this widget
|
||||
*/
|
||||
ActionPopupWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
||||
// Trigger the popup
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
match = popupLocationRegExp.exec(this.actionCoords || "");
|
||||
if(match) {
|
||||
var coordinates = Popup.parseCoordinates(this.actionCoords || "");
|
||||
if(coordinates) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: null,
|
||||
domNodeRect: {
|
||||
left: parseFloat(match[1]),
|
||||
top: parseFloat(match[2]),
|
||||
width: parseFloat(match[3]),
|
||||
height: parseFloat(match[4])
|
||||
left: coordinates.left,
|
||||
top: coordinates.top,
|
||||
width: coordinates.width,
|
||||
height: coordinates.height
|
||||
},
|
||||
title: this.actionState,
|
||||
wiki: this.wiki,
|
||||
floating: this.floating
|
||||
floating: this.floating,
|
||||
absolute: coordinates.absolute
|
||||
});
|
||||
} else {
|
||||
$tw.popup.cancel(0);
|
||||
|
||||
@@ -14,6 +14,8 @@ Button widget
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
var ButtonWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
@@ -147,7 +149,7 @@ ButtonWidget.prototype.isSelected = function() {
|
||||
|
||||
ButtonWidget.prototype.isPoppedUp = function() {
|
||||
var tiddler = this.popupTitle ? this.wiki.getTiddler(this.popupTitle) : this.wiki.getTiddler(this.popup);
|
||||
var result = tiddler && tiddler.fields.text ? $tw.popup.readPopupState(tiddler.fields.text) : false;
|
||||
var result = tiddler && tiddler.fields.text ? Popup.readPopupState(tiddler.fields.text) : false;
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -173,6 +175,7 @@ ButtonWidget.prototype.triggerPopup = function(event) {
|
||||
if(this.popupTitle) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.domNodes[0],
|
||||
absolute: (this.popupAbsCoords === "yes"),
|
||||
title: this.popupTitle,
|
||||
wiki: this.wiki,
|
||||
noStateReference: true
|
||||
@@ -180,6 +183,7 @@ ButtonWidget.prototype.triggerPopup = function(event) {
|
||||
} else {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.domNodes[0],
|
||||
absolute: (this.popupAbsCoords === "yes"),
|
||||
title: this.popup,
|
||||
wiki: this.wiki
|
||||
});
|
||||
@@ -223,6 +227,7 @@ ButtonWidget.prototype.execute = function() {
|
||||
this.setField = this.getAttribute("setField");
|
||||
this.setIndex = this.getAttribute("setIndex");
|
||||
this.popupTitle = this.getAttribute("popupTitle");
|
||||
this.popupAbsCoords = this.getAttribute("popupAbsCoords", "no");
|
||||
this.tabIndex = this.getAttribute("tabindex");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
// Make child widgets
|
||||
@@ -252,7 +257,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
|
||||
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;
|
||||
|
||||
})();
|
||||
@@ -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);
|
||||
|
||||
@@ -14,6 +14,8 @@ Reveal widget
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
var RevealWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
@@ -94,6 +96,13 @@ RevealWidget.prototype.positionPopup = function(domNode) {
|
||||
left = Math.max(0,left);
|
||||
top = Math.max(0,top);
|
||||
}
|
||||
if (this.popup.absolute) {
|
||||
// Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
|
||||
for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
|
||||
left -= offsetParentDomNode.offsetLeft;
|
||||
top -= offsetParentDomNode.offsetTop;
|
||||
}
|
||||
}
|
||||
domNode.style.left = left + "px";
|
||||
domNode.style.top = top + "px";
|
||||
};
|
||||
@@ -183,19 +192,11 @@ RevealWidget.prototype.compareStateText = function(state) {
|
||||
};
|
||||
|
||||
RevealWidget.prototype.readPopupState = function(state) {
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
match = popupLocationRegExp.exec(state);
|
||||
this.popup = Popup.parseCoordinates(state);
|
||||
// Check if the state matches the location regexp
|
||||
if(match) {
|
||||
if(this.popup) {
|
||||
// If so, we're open
|
||||
this.isOpen = true;
|
||||
// Get the location
|
||||
this.popup = {
|
||||
left: parseFloat(match[1]),
|
||||
top: parseFloat(match[2]),
|
||||
width: parseFloat(match[3]),
|
||||
height: parseFloat(match[4])
|
||||
};
|
||||
} else {
|
||||
// If not, we're closed
|
||||
this.isOpen = false;
|
||||
|
||||
@@ -42,6 +42,9 @@ SelectWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
this.setSelectValue();
|
||||
if(this.selectFocus == "yes") {
|
||||
this.getSelectDomNode().focus();
|
||||
}
|
||||
$tw.utils.addEventListeners(this.getSelectDomNode(),[
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
||||
]);
|
||||
@@ -143,6 +146,7 @@ SelectWidget.prototype.execute = function() {
|
||||
this.selectMultiple = this.getAttribute("multiple", false);
|
||||
this.selectSize = this.getAttribute("size");
|
||||
this.selectTooltip = this.getAttribute("tooltip");
|
||||
this.selectFocus = this.getAttribute("focus");
|
||||
// Make the child widgets
|
||||
var selectNode = {
|
||||
type: "element",
|
||||
@@ -175,6 +179,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
|
||||
@@ -358,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
|
||||
*/
|
||||
@@ -365,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
|
||||
|
||||
@@ -4,5 +4,7 @@ title: $:/core/save/all-external-js
|
||||
\define saveTiddlerFilter()
|
||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
|
||||
\end
|
||||
\define coreURL() %24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js
|
||||
{{$:/core/templates/tiddlywiki5-external-js.html}}
|
||||
\define defaultCoreURL() %24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js
|
||||
<$let coreURL={{{ [[coreURL]is[variable]then<coreURL>else<defaultCoreURL>] }}}>
|
||||
{{$:/core/templates/tiddlywiki5-external-js.html}}
|
||||
</$let>
|
||||
|
||||
@@ -4,5 +4,7 @@ title: $:/core/save/offline-external-js
|
||||
\define saveTiddlerFilter()
|
||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/plugins/tiddlywiki/filesystem]] -[[$:/plugins/tiddlywiki/tiddlyweb]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
|
||||
\end
|
||||
\define coreURL() tiddlywikicore-$(version)$.js
|
||||
{{$:/core/templates/tiddlywiki5-external-js.html}}
|
||||
\define defaultCoreURL() tiddlywikicore-$(version)$.js
|
||||
<$let coreURL={{{ [[coreURL]is[variable]then<coreURL>else<defaultCoreURL>] }}}>
|
||||
{{$:/core/templates/tiddlywiki5-external-js.html}}
|
||||
</$let>
|
||||
|
||||
@@ -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/]] }}}/>
|
||||
|
||||
@@ -14,8 +14,8 @@ tags: $:/tags/AdvancedSearch/FilterButton
|
||||
<$linkcatcher actions="<$action-setfield $tiddler='$:/temp/advancedsearch' text=<<navigateTo>>/><$action-setfield $tiddler='$:/temp/advancedsearch/input' text=<<navigateTo>>/><$action-setfield $tiddler='$:/temp/advancedsearch/refresh' text='yes'/><$action-sendmessage $message='tm-focus-selector' $param='.tc-advanced-search input' />">
|
||||
<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>
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Filter]!is[draft]]">
|
||||
<$link to={{!!filter}}><$let tv-wikilinks="no"><$transclude field="description"/></$let></$link>
|
||||
</$list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,15 +20,16 @@ caption: {{$:/language/ControlPanel/Basics/Caption}}
|
||||
\end
|
||||
\whitespace trim
|
||||
|
||||
|tc-max-width tc-edit-max-width|k
|
||||
|<<lingo Version/Prompt>> |''<<version>>'' |
|
||||
|<$link to="$:/SiteTitle"><<lingo Title/Prompt>></$link> |<$edit-text tiddler="$:/SiteTitle" default="" tag="input"/> |
|
||||
|<$link to="$:/SiteSubtitle"><<lingo Subtitle/Prompt>></$link> |<$edit-text tiddler="$:/SiteSubtitle" default="" tag="input"/> |
|
||||
|<$link to="$:/status/UserName"><<lingo Username/Prompt>></$link> |<$edit-text tiddler="$:/status/UserName" default="" tag="input"/> |
|
||||
|<$link to="$:/config/AnimationDuration"><<lingo AnimDuration/Prompt>></$link> |<$edit-text tiddler="$:/config/AnimationDuration" default="" tag="input"/> |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit tag="textarea" tiddler="$:/DefaultTiddlers" class="tc-edit-texteditor"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit class="tc-edit-texteditor" tiddler="$:/DefaultTiddlers"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/language/DefaultNewTiddlerTitle"><<lingo NewTiddler/Title/Prompt>></$link> |<$edit-text tiddler="$:/language/DefaultNewTiddlerTitle" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Title"><<lingo NewJournal/Title/Prompt>></$link> |<$edit-text tiddler="$:/config/NewJournal/Title" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" tag="textarea" class="tc-edit-texteditor" default=""/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" class="tc-edit-texteditor" default=""/> |
|
||||
|<$link to="$:/config/NewTiddler/Tags"><<lingo NewTiddler/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewTiddler/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/NewJournal/Tags"><<lingo NewJournal/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewJournal/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/AutoFocus"><<lingo AutoFocus/Prompt>></$link> |{{$:/snippets/minifocusswitcher}} |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
title: $:/snippets/retain-story-ordering-button
|
||||
|
||||
<$button set="$:/DefaultTiddlers" setTo={{$:/config/ControlPanel/Basics/DefaultTiddlers/RetainStory}} ><<currentTiddler>></$button>
|
||||
@@ -1,7 +1,12 @@
|
||||
title: $:/core/ui/EditTemplate
|
||||
|
||||
\define delete-edittemplate-state-tiddlers() <$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix<newFieldValueTiddlerPrefix>] [<newFieldNameInputTiddler>] [<newFieldNameSelectionTiddler>] [<newTagNameTiddler>] [<newTagNameInputTiddler>] [<newTagNameSelectionTiddler>] [<typeInputTiddler>] [<typeSelectionTiddler>]"/>
|
||||
\define delete-edittemplate-state-tiddlers()
|
||||
<$set name="safeNewFieldValueTiddlerPrefix" value=<<newFieldValueTiddlerPrefix>> emptyValue=<<qualify "$:/temp/NewFieldValue">> >
|
||||
<$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix[$:/temp/NewFieldValue]prefix<safeNewFieldValueTiddlerPrefix>] [<newFieldNameInputTiddler>] [<newFieldNameSelectionTiddler>] [<newTagNameTiddler>] [<newTagNameInputTiddler>] [<newTagNameSelectionTiddler>] [<typeInputTiddler>] [<typeSelectionTiddler>]"/>
|
||||
</$set>
|
||||
\end
|
||||
|
||||
<!-- Beware this is duplicated from fields.tid. For details see bug #7054 -->
|
||||
\define get-field-value-tiddler-filter() [subfilter<get-field-editor-filter>sha256[16]addprefix[/]addprefix<newFieldValueTiddlerPrefix>]
|
||||
\define get-field-editor-filter() [<newFieldNameTiddler>get[text]else[]] :cascade[all[shadows+tiddlers]tag[$:/tags/FieldEditorFilter]!is[draft]get[text]] :and[!is[blank]else{$:/core/ui/EditTemplate/fieldEditor/default}]
|
||||
|
||||
@@ -23,7 +28,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>
|
||||
|
||||
@@ -19,7 +19,7 @@ $:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$
|
||||
|
||||
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
|
||||
|
||||
<div class="tc-tiddler-preview-preview">
|
||||
<div class="tc-tiddler-preview-preview" data-tiddler-title={{!!draft.title}} data-tags={{!!tags}}>
|
||||
|
||||
<$transclude tiddler={{$:/state/editpreviewtype}} mode="inline">
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ $:/config/EditTemplateFields/Visibility/$(currentField)$
|
||||
[[hide]] -[title{$(config-title)$}]
|
||||
\end
|
||||
|
||||
<!-- Beware this is duplicated from EditTemplate.tid. For details see bug #7054 -->
|
||||
\define get-field-value-tiddler-filter() [subfilter<get-field-editor-filter>sha256[16]addprefix[/]addprefix<newFieldValueTiddlerPrefix>]
|
||||
\define get-field-editor-filter() [<newFieldNameTiddler>get[text]else[]] :cascade[all[shadows+tiddlers]tag[$:/tags/FieldEditorFilter]!is[draft]get[text]] :and[!is[blank]else{$:/core/ui/EditTemplate/fieldEditor/default}]
|
||||
|
||||
\define current-tiddler-new-field-selector()
|
||||
[data-tiddler-title="$(currentTiddlerCSSescaped)$"] .tc-edit-field-add-name-wrapper input
|
||||
\end
|
||||
@@ -17,7 +21,9 @@ $:/config/EditTemplateFields/Visibility/$(currentField)$
|
||||
\define new-field-actions()
|
||||
\whitespace trim
|
||||
<$action-sendmessage $message="tm-add-field" $name={{{ [<newFieldNameTiddler>get[text]] }}} $value={{{ [<newFieldNameTiddler>get[text]] :map[subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
<$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix<newFieldValueTiddlerPrefix>] [<storeTitle>] [<searchListState>]"/>
|
||||
<$set name="safeNewFieldValueTiddlerPrefix" value=<<newFieldValueTiddlerPrefix>> emptyValue=<<qualify "$:/temp/NewFieldValue">> >
|
||||
<$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix[$:/temp/NewFieldValue]prefix<safeNewFieldValueTiddlerPrefix>] [<storeTitle>] [<searchListState>]"/>
|
||||
</$set>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param=<<current-tiddler-new-field-selector>>/>
|
||||
\end
|
||||
|
||||
@@ -52,7 +58,9 @@ $:/config/EditTemplateFields/Visibility/$(currentField)$
|
||||
<$action-sendmessage $message="tm-add-field"
|
||||
$name=<<name>>
|
||||
$value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
<$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix<newFieldValueTiddlerPrefix>] [<storeTitle>] [<searchListState>]"/>
|
||||
<$set name="safeNewFieldValueTiddlerPrefix" value=<<newFieldValueTiddlerPrefix>> emptyValue=<<qualify "$:/temp/NewFieldValue">> >
|
||||
<$action-deletetiddler $filter="[<newFieldNameTiddler>] [prefix[$:/temp/NewFieldValue]prefix<safeNewFieldValueTiddlerPrefix>] [<storeTitle>] [<searchListState>]"/>
|
||||
</$set>
|
||||
<<lingo Fields/Add/Button>>
|
||||
</$button>
|
||||
</$reveal>
|
||||
@@ -65,6 +73,7 @@ $value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
\end
|
||||
\whitespace trim
|
||||
|
||||
<$set name="newFieldValueTiddlerPrefix" value=<<newFieldValueTiddlerPrefix>> emptyValue=<<qualify "$:/temp/NewFieldValue">> >
|
||||
<div class="tc-edit-fields">
|
||||
<table class={{{ [all[current]fields[]] :filter[lookup[$:/config/EditTemplateFields/Visibility/]!match[hide]] +[count[]!match[0]] +[then[tc-edit-fields]] ~[[tc-edit-fields tc-edit-fields-small]] }}}>
|
||||
<tbody>
|
||||
@@ -148,3 +157,4 @@ $value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
</$vars>
|
||||
</div>
|
||||
</$fieldmangler>
|
||||
</$set>
|
||||
@@ -117,15 +117,15 @@ title: $:/core/ui/ImportListing
|
||||
</div>
|
||||
</td>
|
||||
</$reveal>
|
||||
<tr>
|
||||
<$reveal type="match" text="yes" state=<<previewPopupState>> tag="tr">
|
||||
<td colspan="3">
|
||||
<$reveal type="match" text="yes" state=<<previewPopupState>> tag="div">
|
||||
<$list filter="[{$:/state/importpreviewtype}has[text]]" variable="listItem" emptyMessage={{$:/core/ui/ImportPreviews/Text}}>
|
||||
<$transclude tiddler={{$:/state/importpreviewtype}}/>
|
||||
<div>
|
||||
<$transclude tiddler={{$:/state/importpreviewtype}}/>
|
||||
</div>
|
||||
</$list>
|
||||
</$reveal>
|
||||
</td>
|
||||
</tr>
|
||||
</$reveal>
|
||||
</$list>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -4,6 +4,6 @@ key: ((advanced-search))
|
||||
|
||||
\whitespace trim
|
||||
<$navigator story="$:/StoryList" history="$:/HistoryList">
|
||||
<$action-navigate $to="$:/AdvancedSearch"/>
|
||||
<$action-navigate $to="$:/AdvancedSearch" $scroll="yes"/>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param="""[data-tiddler-title="$:/AdvancedSearch"] .tc-search input""" preventScroll="true"/>
|
||||
</$navigator>
|
||||
|
||||
15
core/ui/PageControls/layout.tid
Normal file
15
core/ui/PageControls/layout.tid
Normal file
@@ -0,0 +1,15 @@
|
||||
title: $:/core/ui/Buttons/layout
|
||||
tags: $:/tags/PageControls
|
||||
caption: {{$:/core/images/layout-button}} {{$:/language/Buttons/LayoutSwitcher/Caption}}
|
||||
description: {{$:/language/LayoutSwitcher/Description}}
|
||||
|
||||
\whitespace trim
|
||||
<$button tooltip={{$:/language/Buttons/LayoutSwitcher/Hint}} aria-label={{$:/language/Buttons/LayoutSwitcher/Caption}} class=<<tv-config-toolbar-class>>>
|
||||
<$action-sendmessage $message="tm-show-switcher" switch="layout"/>
|
||||
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
|
||||
{{$:/core/images/layout-button}}
|
||||
</$list>
|
||||
<$list filter="[<tv-config-toolbar-text>match[yes]]">
|
||||
<span class="tc-btn-text"><$text text={{$:/language/Buttons/LayoutSwitcher/Caption}}/></span>
|
||||
</$list>
|
||||
</$button>
|
||||
@@ -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}}>
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ tags: $:/tags/SideBar
|
||||
caption: {{$:/language/SideBar/More/Caption}}
|
||||
|
||||
\whitespace trim
|
||||
<div class="tc-more-sidebar">
|
||||
<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/MoreSideBar]!has[draft.of]]" default={{$:/config/DefaultMoreSidebarTab}} state="$:/state/tab/moresidebar" class="tc-vertical tc-sidebar-tabs-more" explicitState="$:/state/tab/moresidebar-1850697562"/>
|
||||
<div class={{{ [{$:/config/ui/SideBar/More/horizontal}match[yes]then[tc-sidebar-tabs]else[tc-more-sidebar]] }}}>
|
||||
<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/MoreSideBar]!has[draft.of]]" default={{$:/config/DefaultMoreSidebarTab}} state="$:/state/tab/moresidebar" class={{{ [{$:/config/ui/SideBar/More/horizontal}match[yes]then[tc-sidebar-tabs-more]else[tc-vertical tc-sidebar-tabs-more]] }}} explicitState="$:/state/tab/moresidebar-1850697562"/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
title: $:/core/ui/SwitcherModal
|
||||
subtitle: <$text text={{{[<switch>lookup[$:/language/Switcher/Subtitle/]]}}}/>
|
||||
class: tc-modal-centered
|
||||
mask-closable: yes
|
||||
|
||||
<$tiddler tiddler={{{[<switch>lookup[$:/config/SwitcherTargets/]]}}}>
|
||||
|
||||
|
||||
@@ -3,89 +3,101 @@ icon: $:/core/images/tag-button
|
||||
color: #bbb
|
||||
|
||||
\define lingo-base() $:/language/TagManager/
|
||||
|
||||
\define iconEditorTab(type)
|
||||
\whitespace trim
|
||||
<$link to=""><<lingo Icons/None>></$link>
|
||||
<$list filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[sort[title]] +[$type$is[system]]">
|
||||
<$link to={{!!title}}>
|
||||
<$transclude/> <$view field="title"/>
|
||||
</$link>
|
||||
<$link to={{!!title}}>
|
||||
<$transclude/> <$view field="title"/>
|
||||
</$link>
|
||||
</$list>
|
||||
\end
|
||||
|
||||
\define iconEditor(title)
|
||||
\whitespace trim
|
||||
<div class="tc-drop-down-wrapper">
|
||||
<$button popupTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} class="tc-btn-invisible tc-btn-dropdown">{{$:/core/images/down-arrow}}</$button>
|
||||
<$reveal stateTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} type="popup" position="belowleft" text="" default="">
|
||||
<div class="tc-drop-down">
|
||||
<$linkcatcher actions="""<$action-setfield $tiddler=<<__title__>> icon=<<navigateTo>>/>""">
|
||||
<<iconEditorTab type:"!">>
|
||||
<hr/>
|
||||
<<iconEditorTab type:"">>
|
||||
</$linkcatcher>
|
||||
</div>
|
||||
</$reveal>
|
||||
<$button popupTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} class="tc-btn-invisible tc-btn-dropdown">
|
||||
{{$:/core/images/down-arrow}}
|
||||
</$button>
|
||||
<$reveal stateTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} type="popup" position="belowleft" text="" default="">
|
||||
<div class="tc-drop-down">
|
||||
<$linkcatcher actions="""<$action-setfield $tiddler=<<__title__>> icon=<<navigateTo>>/>""">
|
||||
<<iconEditorTab type:"!">>
|
||||
<hr/>
|
||||
<<iconEditorTab type:"">>
|
||||
</$linkcatcher>
|
||||
</div>
|
||||
</$reveal>
|
||||
</div>
|
||||
\end
|
||||
|
||||
\define toggleButton(state)
|
||||
\whitespace trim
|
||||
<$reveal stateTitle=<<__state__>> type="match" text="closed" default="closed">
|
||||
<$button setTitle=<<__state__>> setTo="open" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
||||
{{$:/core/images/info-button}}
|
||||
</$button>
|
||||
<$button setTitle=<<__state__>> setTo="open" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
||||
{{$:/core/images/info-button}}
|
||||
</$button>
|
||||
</$reveal>
|
||||
<$reveal stateTitle=<<__state__>> type="match" text="open" default="closed">
|
||||
<$button setTitle=<<__state__>> setTo="closed" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
||||
{{$:/core/images/info-button}}
|
||||
</$button>
|
||||
<$button setTitle=<<__state__>> setTo="closed" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
||||
{{$:/core/images/info-button}}
|
||||
</$button>
|
||||
</$reveal>
|
||||
\end
|
||||
|
||||
\whitespace trim
|
||||
<table class="tc-tag-manager-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><<lingo Colour/Heading>></th>
|
||||
<th class="tc-tag-manager-tag"><<lingo Tag/Heading>></th>
|
||||
<th><<lingo Count/Heading>></th>
|
||||
<th><<lingo Icon/Heading>></th>
|
||||
<th><<lingo Info/Heading>></th>
|
||||
</tr>
|
||||
<$list filter="[tags[]!is[system]sort[title]]">
|
||||
<tr>
|
||||
<td><$edit-text field="color" tag="input" type="color"/></td>
|
||||
<td>{{||$:/core/ui/TagTemplate}}</td>
|
||||
<td><$count filter="[all[current]tagging[]]"/></td>
|
||||
<td>
|
||||
<$macrocall $name="iconEditor" title={{!!title}}/>
|
||||
</td>
|
||||
<td>
|
||||
<$macrocall $name="toggleButton" state={{{ [[$:/state/tag-manager/]addsuffix<currentTiddler>] }}} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colspan="4">
|
||||
<$reveal stateTitle={{{ [[$:/state/tag-manager/]addsuffix<currentTiddler>] }}} type="match" text="open" default="">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td><<lingo Colour/Heading>></td><td><$edit-text field="color" tag="input" type="text" size="9"/></td></tr>
|
||||
<tr><td><<lingo Icon/Heading>></td><td><$edit-text field="icon" tag="input" size="45"/></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</$reveal>
|
||||
</td>
|
||||
</tr>
|
||||
</$list>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style="position:relative;">
|
||||
{{$:/core/ui/UntaggedTemplate}}
|
||||
</td>
|
||||
<td>
|
||||
<small class="tc-menu-list-count"><$count filter="[untagged[]!is[system]] -[tags[]]"/></small>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><<lingo Colour/Heading>></th>
|
||||
<th class="tc-tag-manager-tag"><<lingo Tag/Heading>></th>
|
||||
<th><<lingo Count/Heading>></th>
|
||||
<th><<lingo Icon/Heading>></th>
|
||||
<th><<lingo Info/Heading>></th>
|
||||
</tr>
|
||||
<$list filter="[tags[]!is[system]sort[title]]">
|
||||
<tr>
|
||||
<td><$edit-text field="color" tag="input" type="color"/></td>
|
||||
<td>{{||$:/core/ui/TagTemplate}}</td>
|
||||
<td><$count filter="[all[current]tagging[]]"/></td>
|
||||
<td>
|
||||
<$macrocall $name="iconEditor" title={{!!title}}/>
|
||||
</td>
|
||||
<td>
|
||||
<$macrocall $name="toggleButton" state={{{ [[$:/state/tag-manager/]addsuffix<currentTiddler>] }}} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colspan="4">
|
||||
<$reveal stateTitle={{{ [[$:/state/tag-manager/]addsuffix<currentTiddler>] }}} type="match" text="open" default="">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><<lingo Colour/Heading>></td>
|
||||
<td><$edit-text field="color" tag="input" type="text" size="9"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><<lingo Icon/Heading>></td>
|
||||
<td><$edit-text field="icon" tag="input" size="45"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</$reveal>
|
||||
</td>
|
||||
</tr>
|
||||
</$list>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style="position:relative;">
|
||||
{{$:/core/ui/UntaggedTemplate}}
|
||||
</td>
|
||||
<td>
|
||||
<small class="tc-menu-list-count"><$count filter="[untagged[]!is[system]] -[tags[]]"/></small>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,17 +2,21 @@ title: $:/core/ui/ViewTemplate/unfold
|
||||
tags: $:/tags/ViewTemplate
|
||||
|
||||
\whitespace trim
|
||||
<$reveal tag="div" type="nomatch" state="$:/config/ViewToolbarButtons/Visibility/$:/core/ui/Buttons/fold-bar" text="hide">
|
||||
<div class="tc-reveal">
|
||||
<$list filter="[{$:/config/ViewToolbarButtons/Visibility/$:/core/ui/Buttons/fold-bar}match[show]]" variable="ignore">
|
||||
<$reveal tag="div" type="nomatch" stateTitle=<<folded-state>> text="hide" default="show" retain="yes" animate="yes">
|
||||
<$button tooltip={{$:/language/Buttons/Fold/Hint}} aria-label={{$:/language/Buttons/Fold/Caption}} class="tc-fold-banner">
|
||||
<$action-sendmessage $message="tm-fold-tiddler" $param=<<currentTiddler>> foldedState=<<folded-state>>/>
|
||||
{{$:/core/images/chevron-up}}
|
||||
</$button>
|
||||
</$reveal>
|
||||
</$list>
|
||||
<$list filter="[{$:/config/ViewToolbarButtons/Visibility/$:/core/ui/Buttons/fold-bar}match[show]] :else[<folded-state>get[text]match[hide]]" variable="ignore">
|
||||
<$reveal tag="div" type="nomatch" stateTitle=<<folded-state>> text="show" default="show" retain="yes" animate="yes">
|
||||
<$button tooltip={{$:/language/Buttons/Unfold/Hint}} aria-label={{$:/language/Buttons/Unfold/Caption}} class="tc-unfold-banner">
|
||||
<$action-sendmessage $message="tm-fold-tiddler" $param=<<currentTiddler>> foldedState=<<folded-state>>/>
|
||||
{{$:/core/images/chevron-down}}
|
||||
</$button>
|
||||
</$reveal>
|
||||
</$reveal>
|
||||
</$list>
|
||||
</div>
|
||||
3
core/wiki/config/DefaultTiddlersRetainStory.tid
Normal file
3
core/wiki/config/DefaultTiddlersRetainStory.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/config/ControlPanel/Basics/DefaultTiddlers/RetainStory
|
||||
|
||||
[list[$:/StoryList]]
|
||||
@@ -21,4 +21,5 @@ core/ui/Buttons/print: hide
|
||||
core/ui/Buttons/storyview: hide
|
||||
core/ui/Buttons/timestamp: hide
|
||||
core/ui/Buttons/theme: hide
|
||||
core/ui/Buttons/layout: hide
|
||||
core/ui/Buttons/unfold-all: hide
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -3,14 +3,11 @@ modified: 20141212110330815
|
||||
tags: $:/tags/Macro
|
||||
title: $:/core/macros/timeline
|
||||
|
||||
\define timeline-title()
|
||||
\whitespace trim
|
||||
<!-- Override this macro with a global macro
|
||||
of the same name if you need to change
|
||||
how titles are displayed on the timeline
|
||||
-->
|
||||
<$view field="title"/>
|
||||
\end
|
||||
<!-- Override one or both of the following two macros with a global or local macro of the same name
|
||||
if you need to change how titles are displayed on a timeline -->
|
||||
|
||||
\define timeline-title() <$view field="title"/>
|
||||
\define timeline-link() <$link to={{!!title}}><<timeline-title>></$link>
|
||||
\define timeline(limit:"100",format:"DDth MMM YYYY",subfilter:"",dateField:"modified")
|
||||
\whitespace trim
|
||||
<div class="tc-timeline">
|
||||
@@ -19,7 +16,7 @@ title: $:/core/macros/timeline
|
||||
<$view field="$dateField$" format="date" template="$format$"/>
|
||||
<$list filter="[sameday:$dateField${!!$dateField$}!is[system]$subfilter$!sort[$dateField$]]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link to={{!!title}}><<timeline-title>></$link>
|
||||
<<timeline-link>>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
title: $:/core/macros/toc
|
||||
tags: $:/tags/Macro
|
||||
|
||||
|
||||
\define toc-open-icon() $:/core/images/down-arrow
|
||||
\define toc-closed-icon() $:/core/images/right-arrow
|
||||
|
||||
\define toc-caption()
|
||||
\whitespace trim
|
||||
<span class="tc-toc-caption tc-tiny-gap-left">
|
||||
@@ -45,12 +49,12 @@ tags: $:/tags/Macro
|
||||
<$link to={{{ [<currentTiddler>get[target]else<currentTiddler>] }}}>
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/right-arrow}}
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
</$button>
|
||||
</$reveal>
|
||||
<$reveal type="match" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="close" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/down-arrow}}
|
||||
<$transclude tiddler=<<toc-open-icon>> />
|
||||
</$button>
|
||||
</$reveal>
|
||||
<<toc-caption>>
|
||||
@@ -71,13 +75,13 @@ tags: $:/tags/Macro
|
||||
<li class=<<toc-item-class>>>
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/right-arrow}}
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
<<toc-caption>>
|
||||
</$button>
|
||||
</$reveal>
|
||||
<$reveal type="match" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="close" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/down-arrow}}
|
||||
<$transclude tiddler=<<toc-open-icon>> />
|
||||
<<toc-caption>>
|
||||
</$button>
|
||||
</$reveal>
|
||||
@@ -117,12 +121,12 @@ tags: $:/tags/Macro
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button>">
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/right-arrow}}
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
</$button>
|
||||
</$reveal>
|
||||
<$reveal type="match" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="close" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/down-arrow}}
|
||||
<$transclude tiddler=<<toc-open-icon>> />
|
||||
</$button>
|
||||
</$reveal>
|
||||
</$list>
|
||||
@@ -144,13 +148,13 @@ tags: $:/tags/Macro
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button> <$view field='caption'><$view field='title'/></$view>">
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/right-arrow}}
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
<<toc-caption>>
|
||||
</$button>
|
||||
</$reveal>
|
||||
<$reveal type="match" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="close" class="tc-btn-invisible tc-popup-keep">
|
||||
{{$:/core/images/down-arrow}}
|
||||
<$transclude tiddler=<<toc-open-icon>> />
|
||||
<<toc-caption>>
|
||||
</$button>
|
||||
</$reveal>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: $:/tags/PageControls
|
||||
list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/more-page-actions]]
|
||||
list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/layout]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/more-page-actions]]
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"tiddlywiki/snowwhite"
|
||||
],
|
||||
"languages": [
|
||||
"en-GB",
|
||||
"de-AT",
|
||||
"de-DE"
|
||||
],
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"tiddlywiki/internals"
|
||||
],
|
||||
"languages": [
|
||||
"en-GB",
|
||||
"de-AT",
|
||||
"de-DE"
|
||||
],
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"tiddlywiki/internals"
|
||||
],
|
||||
"languages": [
|
||||
"en-GB",
|
||||
"de-AT",
|
||||
"de-DE"
|
||||
],
|
||||
|
||||
@@ -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,42 @@
|
||||
created: 20221114225038703
|
||||
modified: 20221114230502925
|
||||
tags: howto
|
||||
title: How to run a local plugin library for testing
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
!! Start the Library Server
|
||||
|
||||
The "pluginlibrary" edition contains the components needed to set up a local server for ~TiddlyWiki plugin library testing or development.
|
||||
|
||||
The following commands will create the library files and start a test server at http://localhost:8888
|
||||
|
||||
```
|
||||
cd /your/path/to/TiddlyWiki5/editions/pluginlibrary
|
||||
tiddlywiki --build test-server
|
||||
```
|
||||
|
||||
!! Important
|
||||
|
||||
''This server is read-only. Nothing is saved back to the filesystem''
|
||||
|
||||
!! Test the Library with a Single File Wiki
|
||||
|
||||
* Open a single file wiki and import the configuration tiddler form http://localhost:8888/#%24%3A%2Fconfig%2FLocalPluginLibrary
|
||||
* Open the ''$:/ControlPanel : Plugins : "Get More Plugins"'' modal
|
||||
|
||||
!! Test the Library with a Node.js Wiki
|
||||
|
||||
* Create a new wiki with eg:
|
||||
|
||||
<<<
|
||||
```
|
||||
cd /temp/
|
||||
tiddlywiki my-wiki --init server
|
||||
tiddlywiki my-wiki --listen
|
||||
```
|
||||
<<<
|
||||
|
||||
* Open "my-wiki" from http://localhost:8080
|
||||
* Import the http://localhost:8888/#%24%3A%2Fconfig%2FLocalPluginLibrary tiddler into "my-wiki"
|
||||
* Open the ''$:/ControlPanel : Plugins : "Get More Plugins"'' modal
|
||||
|
||||
@@ -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.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user