1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-02-13 21:49:50 +00:00

Compare commits

..

29 Commits

Author SHA1 Message Date
jeremy@jermolene.com
75c1e4b4d8 Fix typo 2021-10-27 12:46:25 +01:00
Jeremy Ruston
979f079c7a Merge branch 'master' into external-tasks 2021-10-27 12:27:35 +01:00
jeremy@jermolene.com
894fb1ad35 Fix merging error 2021-03-30 21:28:48 +01:00
jeremy@jermolene.com
9a3c60173a Merge branch 'master' into external-tasks 2021-03-30 21:26:01 +01:00
Jeremy Ruston
2b4619dcbe Merge branch 'master' into external-tasks 2019-10-22 21:52:41 +01:00
Jeremy Ruston
bb20d43cbf Merge branch 'master' into external-tasks 2019-09-24 13:23:42 +01:00
Jeremy Ruston
9c29b15fcd Merge branch 'master' into external-tasks 2019-08-06 21:16:52 +01:00
Jermolene
994a91001d Merge branch 'master' into external-tasks 2019-01-06 17:33:20 +00:00
Jermolene
c85c172fe4 Merge branch 'master' into external-tasks 2018-10-24 10:33:34 +01:00
Jermolene
eeb06a75a6 Add new tagger demo 2018-10-24 10:31:58 +01:00
Jermolene
ff9539059a Refactor pipe command
To make it easier to handle state, and enable us to add support for the "incomingTitle" parameter
2018-10-24 10:31:42 +01:00
Jermolene
5831df9ece Docs updates 2018-10-24 10:30:41 +01:00
Jermolene
b7f758bbbe Docs improvements 2018-10-19 17:56:37 +01:00
Jermolene
36509509bc Decrease server polling interval
5 * 1000 millseconds, instead of the default 60 * 1000
2018-10-19 17:25:41 +01:00
Jermolene
e83d0fb6f5 Merge branch 'master' into external-tasks 2018-10-19 16:32:53 +01:00
Jermolene
a65e9a7b42 @joearm's fixes 2018-10-19 16:29:45 +01:00
Jermolene
9b912f6d65 Update socket-erlang example 2018-10-19 10:18:08 +01:00
Jermolene
4b45afc11f Improvements to sample text 2018-10-19 10:17:38 +01:00
Jermolene
e714693cfe Preliminary support for length+type+message 2018-10-18 17:15:59 +01:00
Jermolene
dffd4e56b5 Bring "Alice in Wonderland" properly into the wiki
We were referencing it in the tw5.com edition, but that makes it harder to use the externalpipesdemo edition independently.
2018-10-18 14:21:59 +01:00
Jermolene
a0b3e1a564 Docs improvements 2018-10-17 21:27:46 +01:00
Jermolene
4efd5288d0 Merge branch 'master' into external-tasks 2018-10-14 18:35:11 +01:00
Jermolene
a2d3778465 Improve demo 2018-10-14 18:35:07 +01:00
Jermolene
f2917c3355 Add support for remote commands
Makes it possible to invoke any of TW5's Node.js commands from the browser. Useful for the new pipe command, but also handy for e.g. rendering tiddlers on the server, fetching remote files, or importing local files etc

Docs to come.
2018-10-14 18:34:55 +01:00
Jermolene
4fcbaa2b12 Mimic - don't crash if there's no source text 2018-10-14 18:32:31 +01:00
Jermolene
d29b9c2726 Merge branch 'master' into external-tasks 2018-10-13 17:25:05 +01:00
Jermolene
97bb1fa8c9 Rename external tasks to external pipes
And add support for connecting via sockets to existing processers/tasks
2018-10-13 14:50:21 +01:00
Jermolene
2e5035ec2a Merge branch 'master' into external-tasks 2018-10-09 21:31:22 +01:00
Jermolene
b292985df3 Initial commit 2018-10-01 19:44:48 +01:00
413 changed files with 6350 additions and 3834 deletions

View File

@@ -1,8 +1,15 @@
# Ignore "third party" code whose style we will not change.
# Known minified files
/boot/sjcl.js
/core/modules/utils/base64-utf8/base64-utf8.module.js
/core/modules/utils/base64-utf8/base64-utf8.module.min.js
/core/modules/utils/diff-match-patch/diff_match_patch.js
/core/modules/utils/diff-match-patch/diff_match_patch_uncompressed.js
/core/modules/utils/dom/csscolorparser.js
/plugins/tiddlywiki/*/files/
/plugins/tiddlywiki/async/files/async.min.v1.5.0.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/anyword-hint.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/css-hint.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/html-hint.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/javascript-hint.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/show-hint.js
/plugins/tiddlywiki/codemirror-autocomplete/files/addon/hint/xml-hint.js
/plugins/tiddlywiki/codemirror-closebrackets/files/addon/edit/closebrackets.js
/plugins/tiddlywiki/codemirror-closebrackets/files/addon/edit/matchbrackets.js
/plugins/tiddlywiki/codemirror-closetag/files/addon/edit/closetag.js
/plugins/tiddlywiki/codemirror-closetag/files/addon/fold/xml-fold.js

View File

@@ -64,23 +64,7 @@ rules:
init-declarations: 'off'
jsx-quotes: error
key-spacing: 'off'
keyword-spacing:
- error
- before: true
after: false
overrides:
'case':
after: true
'do':
'after': true
'else':
after: true
'return':
after: true
'throw':
after: true
'try':
after: true
keyword-spacing: 'off'
line-comment-position: 'off'
linebreak-style: 'off'
lines-around-comment: 'off'

View File

@@ -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.2
TW5_BUILD_VERSION=v5.2.0
fi
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"

View File

@@ -2649,4 +2649,3 @@ if(typeof(exports) !== "undefined") {
} else {
_boot(window.$tw);
}
//# sourceURL=$:/boot/boot.js

View File

@@ -117,4 +117,3 @@ if(typeof(exports) === "undefined") {
// Export functionality as a module
exports.bootprefix = _bootprefix;
}
//# sourceURL=$:/boot/bootprefix.js

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@ type: text/plain
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
Copyright (c) 2004-2007, Jeremy Ruston
Copyright (c) 2007-2022, UnaMesa Association
Copyright (c) 2007-2021, UnaMesa Association
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -27,15 +27,10 @@ Basics/Tiddlers/Prompt: Number of tiddlers
Basics/Title/Prompt: Title of this ~TiddlyWiki
Basics/Username/Prompt: Username for signing edits
Basics/Version/Prompt: ~TiddlyWiki version
Cascades/Caption: Cascades
Cascades/Hint: These global rules are used to dynamically choose certain templates. The result of the cascade is the result of the first filter in the sequence that returns a result
Cascades/TagPrompt: Filters tagged <$macrocall $name="tag" tag=<<currentTiddler>>/>
EditorTypes/Caption: Editor Types
EditorTypes/Editor/Caption: Editor
EditorTypes/Hint: These tiddlers determine which editor is used to edit specific tiddler types.
EditorTypes/Type/Caption: Type
EditTemplateBody/Caption: Edit Template Body
EditTemplateBody/Hint: This rule cascade is used by the default edit template to dynamically choose the template for editing the body of a tiddler.
Info/Caption: Info
Info/Hint: Information about this TiddlyWiki
KeyboardShortcuts/Add/Prompt: Type shortcut here
@@ -196,8 +191,6 @@ Settings/TitleLinks/Yes/Description: Display tiddler titles as links
Settings/MissingLinks/Caption: Wiki Links
Settings/MissingLinks/Hint: Choose whether to link to tiddlers that do not exist yet
Settings/MissingLinks/Description: Enable links to missing tiddlers
StoryTiddler/Caption: Story Tiddler
StoryTiddler/Hint: This rule cascade is used to dynamically choose the template for displaying a tiddler in the story river.
StoryView/Caption: Story View
StoryView/Prompt: Current view:
Stylesheets/Caption: Stylesheets
@@ -208,10 +201,6 @@ Theme/Caption: Theme
Theme/Prompt: Current theme:
TiddlerFields/Caption: Tiddler Fields
TiddlerFields/Hint: This is the full set of TiddlerFields in use in this wiki (including system tiddlers but excluding shadow tiddlers).
TiddlerColour/Caption: Tiddler Colour
TiddlerColour/Hint: This rules cascade is used to dynamically choose the colour for a tiddler (used for the icon and the associated tag pill).
TiddlerIcon/Caption: Tiddler Icon
TiddlerIcon/Hint: This rules cascade is used to dynamically choose the icon for a tiddler.
Toolbars/Caption: Toolbars
Toolbars/EditToolbar/Caption: Edit Toolbar
Toolbars/EditToolbar/Hint: Choose which buttons are displayed for tiddlers in edit mode. Drag and drop to change the ordering
@@ -223,7 +212,3 @@ Toolbars/EditorToolbar/Hint: Choose which buttons are displayed in the editor to
Toolbars/ViewToolbar/Caption: View Toolbar
Toolbars/ViewToolbar/Hint: Choose which buttons are displayed for tiddlers in view mode. Drag and drop to change the ordering
Tools/Download/Full/Caption: Download full wiki
ViewTemplateBody/Caption: View Template Body
ViewTemplateBody/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the body of a tiddler.
ViewTemplateTitle/Caption: View Template Title
ViewTemplateTitle/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the title of a tiddler.

View File

@@ -3,7 +3,6 @@ title: $:/language/Docs/Fields/
_canonical_uri: The full URI of an external image tiddler
bag: The name of the bag from which a tiddler came
caption: The text to be displayed on a tab or button
code-body: The view template will display the tiddler as code if set to ''yes''
color: The CSS color value associated with a tiddler
component: The name of the component responsible for an [[alert tiddler|AlertMechanism]]
current-tiddler: Used to cache the top tiddler in a [[history list|HistoryMechanism]]
@@ -14,9 +13,9 @@ 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
hide-body: The view template will hide bodies of tiddlers if set to ''yes''
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''
library: Indicates that a tiddler should be saved as a JavaScript library if set to: ''yes''
list: An ordered list of tiddler titles associated with a tiddler
list-before: If set, the title of a tiddler before which this tiddler should be added to the ordered list of tiddler titles, or at the start of the list if this field is present but empty
list-after: If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles, or at the end of the list if this field is present but empty
@@ -33,7 +32,7 @@ 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
title: The unique name of a tiddler
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to ''no''
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
type: The content type of a tiddler
version: Version information for a plugin
_is_skinny: If present, indicates that the tiddler text field must be loaded from the server

View File

@@ -0,0 +1,13 @@
title: $:/language/Help/pipe
description: Pipe data to/from external processes
Pipe data to/from external processes.
```
--pipe <pipename> <filter> <incomingTitle> <arguments>...
```
* ''pipename'': identifies the pipe, matching the name defined in the `tiddlywiki.info` file
* ''filter'': identifies the tiddlers to be passed to the pipe
* ''incomingTitle'': provides a title to be applied to incoming tiddlers for the `raw-text` output format
* ''arguments'': passed through to external task pipes, and ignored for other types of pipe

View File

@@ -14,7 +14,7 @@ ConfirmAction: Do you wish to proceed?
Count: count
DefaultNewTiddlerTitle: New Tiddler
Diffs/CountMessage: <<diff-count>> differences
DropMessage: Drop now (or use the 'Escape' key to cancel)
DropMessage: Drop here (or use the 'Escape' key to cancel)
Encryption/Cancel: Cancel
Encryption/ConfirmClearPassword: Do you wish to clear the password? This will remove the encryption applied when saving this wiki
Encryption/PromptSetPassword: Set a new password for this TiddlyWiki

View File

@@ -0,0 +1,278 @@
/*\
title: $:/core/modules/commands/pipe.js
type: application/javascript
module-type: command
Command to execute an external task
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "pipe",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Missing parameters";
}
var name = self.params[0], // External pipe name
outgoingFilter = self.params[1], // Filter of tiddlers to write to the pipe
incomingTitle = self.params[2],
args = self.params.slice(3); // Remaining arguments are passed on as tasks arguments
// Find the pipe information
var pipeInfo = ($tw.boot.wikiInfo["external-pipes"] || {})[name];
if(!pipeInfo) {
return this.callback("External pipe \"" + name + "\" not found");
}
// Create the pipe instance and process a message
var pipe = new Pipe({
name: name,
pipeInfo: pipeInfo,
outgoingFilter: outgoingFilter,
incomingTitle: incomingTitle,
args: args,
command: this
});
pipe.processMessage(this.callback);
};
function Pipe(options) {
this.name = options.name;
this.pipeInfo = options.pipeInfo;
this.outgoingFilter = options.outgoingFilter;
this.incomingTitle = options.incomingTitle;
this.args = options.args;
this.command = options.command;
}
Pipe.prototype.processMessage = function(callback) {
// Get the outgoing data
var data = this.composeOutgoingData(this.outgoingFilter);
// Connect to the pipe
switch(this.pipeInfo.type) {
case "task":
this.pipeExternalTask(data,callback);
break;
case "socket":
this.pipeSocket(data,callback);
break;
case "socket-erlang":
this.pipeSocketErlang(data,callback);
break;
default:
callback("Invalid pipe specifier '" + this.name + "': " + this.pipeInfo.type);
break;
}
};
Pipe.prototype.log = function(args) {
this.command.commander.log("Pipe: " + Array.prototype.slice.call(arguments,0).join(" "));
};
Pipe.prototype.pipeExternalTask = function(data,callback) {
var self = this,
spawn = require("child_process").spawn,
path = require("path"),
childProcess = spawn(path.resolve($tw.boot.wikiPath,this.pipeInfo.path),this.args,{
stdio: ["pipe","pipe",process.stderr],
shell: true,
env: $tw.utils.extend({},process.env,this.pipeInfo.environment)
});
// Pass the tiddlers over the outgoing stream
childProcess.stdin.on("error",function(err) {
self.log("Task stdin error",err)
});
childProcess.stdin.write(data);
childProcess.stdin.end();
// Catch the output
var chunks = [];
childProcess.stdout.on("data",function(chunk) {
chunks.push(chunk.toString());
});
childProcess.stdout.on("close",function() {
self.log("Task stdout close");
self.processIncomingData(chunks.join(""));
});
childProcess.stdout.on("error",function(err) {
self.log("Task stdout error",err)
});
// Pick up the output when the process ends
childProcess.on("error",function(err) {
self.log("Task error",err)
});
childProcess.on("exit",function(code,signal) {
self.log("Task exit",code,signal)
if(code !== 0) {
return callback("Error executing external task: " + code);
}
// Exit successfully
callback(null);
});
};
Pipe.prototype.pipeSocket = function(data,callback) {
var self = this,
net = require("net"),
socket = new net.Socket({
allowHalfOpen: true
}),
chunks = [];
socket.connect(this.pipeInfo.port,this.pipeInfo.host || 8081,function() {
self.log("Socket connection",this.pipeInfo.port,this.pipeInfo.host);
socket.write(data);
socket.end();
});
socket.on("error",function(e) {
self.log("Socket error",e)
});
socket.on("data",function(data) {
chunks.push(data.toString());
});
socket.on("end",function() {
self.processIncomingData(chunks.join(""));
self.log("Socket end");
socket.destroy();
});
// Add a "close" event handler for the client socket
socket.on("close",function() {
self.log("Socket closed");
return callback(null);
});
return null;
};
Pipe.prototype.pipeSocketErlang = function(data,callback) {
var self = this,
encoding = this.pipeInfo.encoding || "utf8",
net = require("net"),
socket = new net.Socket(),
accumulator = Buffer.alloc(0);
socket.connect(this.pipeInfo.port,this.pipeInfo.host || 8081,function() {
self.log("Socket connection",self.pipeInfo.port,self.pipeInfo.host);
var dataBytes = Buffer.from(data,encoding);
// Write 32-bit big endian message length
var lengthBytes = Buffer.alloc(4);
lengthBytes.writeUInt32BE(dataBytes.length + 1,0)
console.log("Writing bytes",dataBytes.length + 1);
socket.write(lengthBytes);
// Write 8-bit type
var typeByte = Buffer.alloc(1);
typeByte.writeUInt8(1,0);
socket.write(typeByte);
// Write data
socket.write(dataBytes);
});
socket.on("error",function(e) {
self.log("Socket error",e)
});
socket.on("data",function(data) {
console.log("Received data",data.length)
accumulator = Buffer.concat([accumulator,data]);
while(accumulator.length > 4) {
var length = accumulator.readInt32BE(0);
if(accumulator.length >= (length + 4)) {
if(length < 1) {
throw "ERROR: Incoming message length field is less than 1";
}
var type = accumulator.readUInt8(4),
dataLength = length - 1,
data = accumulator.toString(encoding,5,dataLength + 5);
console.log("Got message",length,type)
self.processIncomingData(data);
accumulator = accumulator.slice(length + 4);
socket.end();
return callback(null);
} else {
break;
}
}
});
socket.on("end",function() {
self.log("Socket end");
socket.destroy();
});
// Add a "close" event handler for the client socket
socket.on("close",function() {
self.log("Socket closed");
return callback(null);
});
return null;
};
Pipe.prototype.composeOutgoingData = function(outgoingFilter) {
var self = this,
pipeInfoInput = this.pipeInfo.input || {},
data;
switch(pipeInfoInput.format || "json-raw-tiddlers") {
case "rendered-text":
var titles = self.command.commander.wiki.filterTiddlers(outgoingFilter),
output = [];
$tw.utils.each(titles,function(title) {
output.push(self.command.commander.wiki.renderTiddler("text/plain",title));
});
data = output.join("");
break;
case "json-rendered-text-tiddlers":
var titles = self.command.commander.wiki.filterTiddlers(outgoingFilter),
tiddlers = [];
$tw.utils.each(titles,function(title) {
tiddlers.push({
title: title,
text: self.command.commander.wiki.renderTiddler("text/plain",title)
})
});
data = JSON.stringify(tiddlers);
break;
case "json-raw-tiddlers":
// Intentional fall-through
default:
data = this.command.commander.wiki.getTiddlersAsJson(outgoingFilter);
break;
}
return data;
};
Pipe.prototype.processIncomingData = function(data) {
var pipeInfoOutput = this.pipeInfo.output || {},
jsonData;
switch(pipeInfoOutput.format || "text") {
case "json-raw-tiddlers":
try {
jsonData = JSON.parse(data);
} catch(e) {
this.log("Error parsing returned JSON: " + e + "\n\n\n->\n" + data);
}
// Add the tiddlers
if(jsonData) {
this.command.commander.wiki.addTiddlers(jsonData);
}
break;
case "text":
// Intentional fall-through
default:
console.log("Writing tiddler",pipeInfoOutput.tiddler,{
text: data, title: this.incomingTitle
})
this.command.commander.wiki.addTiddler(new $tw.Tiddler(pipeInfoOutput.tiddler,{
text: data, title: this.incomingTitle || pipeInfoOutput.tiddler.title
}));
break;
}
};
exports.Command = Command;
})();

View File

@@ -205,7 +205,7 @@ FramedEngine.prototype.handleInputEvent = function(event) {
this.widget.saveChanges(this.getText());
this.fixHeight();
if(this.widget.editInputActions) {
this.widget.invokeActionString(this.widget.editInputActions,this,event,{actionValue: this.getText()});
this.widget.invokeActionString(this.widget.editInputActions);
}
return true;
};

View File

@@ -133,7 +133,7 @@ SimpleEngine.prototype.handleInputEvent = function(event) {
this.widget.saveChanges(this.getText());
this.fixHeight();
if(this.widget.editInputActions) {
this.widget.invokeActionString(this.widget.editInputActions,this,event,{actionValue: this.getText()});
this.widget.invokeActionString(this.widget.editInputActions);
}
return true;
};

View File

@@ -324,7 +324,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
If there are no Files, let the browser handle it.
*/
EditTextWidget.prototype.handleDropEvent = function(event) {
if($tw.utils.dragEventContainsFiles(event)) {
if(event.dataTransfer.files.length) {
event.preventDefault();
event.stopPropagation();
this.dispatchDOMEvent(this.cloneEvent(event,["dataTransfer"]));

View File

@@ -13,17 +13,15 @@ Text editor operation to wrap the selected lines with a prefix and suffix
"use strict";
exports["wrap-lines"] = function(event,operation) {
var prefix = event.paramObject.prefix || "",
suffix = event.paramObject.suffix || "";
// Cut just past the preceding line break, or the start of the text
operation.cutStart = $tw.utils.findPrecedingLineBreak(operation.text,operation.selStart);
// Cut to just past the following line break, or to the end of the text
operation.cutEnd = $tw.utils.findFollowingLineBreak(operation.text,operation.selEnd);
// Add the prefix and suffix
operation.replacement = prefix + "\n" +
operation.replacement = event.paramObject.prefix + "\n" +
operation.text.substring(operation.cutStart,operation.cutEnd) + "\n" +
suffix + "\n";
operation.newSelStart = operation.cutStart + prefix.length + 1;
event.paramObject.suffix + "\n";
operation.newSelStart = operation.cutStart + event.paramObject.prefix.length + 1;
operation.newSelEnd = operation.newSelStart + (operation.cutEnd - operation.cutStart);
};

View File

@@ -1,53 +0,0 @@
/*\
title: $:/core/modules/filterrunprefixes/cascade.js
type: application/javascript
module-type: filterrunprefix
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter prefix function
*/
exports.cascade = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length !== 0) {
var filterList = operationSubFunction(source,widget),
filterFnList = [];
var inputResults = results.toArray();
results.clear();
$tw.utils.each(inputResults,function(title) {
var result = ""; // If no filter matches, we return an empty string
$tw.utils.each(filterList,function(filter,index) {
if(!filterFnList[index]) {
filterFnList[index] = options.wiki.compileFilter(filter);
}
var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
if(output.length !== 0) {
result = output[0];
return false;
}
});
results.push(result);
});
}
}
};
})();

View File

@@ -16,30 +16,23 @@ Export our filter function
exports.filter = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var resultsToRemove = [],
index = 0;
var resultsToRemove = [];
results.each(function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
getVariable: function(name) {
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return widget.getVariable("currentTiddler");
default:
return widget.getVariable(name);
}
}
});
if(filtered.length === 0) {
resultsToRemove.push(title);
}
++index;
});
results.remove(resultsToRemove);
}

View File

@@ -15,29 +15,22 @@ Export our filter prefix function
exports.map = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var inputTitles = results.toArray(),
index = 0;
var inputTitles = results.toArray();
results.clear();
$tw.utils.each(inputTitles,function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (inputTitles.length - 1 - index),
"length": "" + inputTitles.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
getVariable: function(name) {
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return widget.getVariable("currentTiddler");
default:
return widget.getVariable(name);
}
}
});
results.push(filtered[0] || "");
++index;
});
}
}

View File

@@ -15,24 +15,26 @@ Export our filter prefix function
exports.reduce = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var accumulator = "",
index = 0;
var accumulator = "";
var index = 0;
results.each(function(title) {
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length,
"accumulator": "" + accumulator
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
getVariable: function(name) {
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return widget.getVariable("currentTiddler");
case "accumulator":
return "" + accumulator;
case "index":
return "" + index;
case "revIndex":
return "" + (results.length - 1 - index);
case "length":
return "" + results.length;
default:
return widget.getVariable(name);
}
}
});

View File

@@ -26,16 +26,14 @@ exports.sort = function(operationSubFunction,options) {
compareFn;
results.each(function(title) {
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
getVariable: function(name) {
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return widget.getVariable("currentTiddler");
default:
return widget.getVariable(name);
}
}
});

View File

@@ -95,12 +95,10 @@ function parseFilterOperation(operators,filterString,p) {
if(nextBracketPos === -1) {
throw "Missing closing bracket in filter expression";
}
if(operator.regexp) {
operand.text = "";
} else {
if(!operator.regexp) {
operand.text = filterString.substring(p,nextBracketPos);
operator.operands.push(operand);
}
operator.operands.push(operand);
p = nextBracketPos + 1;
}

View File

@@ -1,27 +0,0 @@
/*\
title: $:/core/modules/filters/crypto.js
type: application/javascript
module-type: filteroperator
Filter operators for cryptography, using the Stanford JavaScript library
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.sha256 = function(source,operator,options) {
var results = [],
length = parseInt(operator.operand,10) || 20,
sha256 = function(text) {
return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(text)).substr(0,length);
};
source(function(tiddler,title) {
results.push(sha256(title));
});
return results;
};
})();

View File

@@ -103,16 +103,4 @@ exports.nth = function(source,operator,options) {
return results.slice(count - 1,count);
};
/*
The zero based nth member of the list
*/
exports.zth = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,0),
results = [];
source(function(tiddler,title) {
results.push(title);
});
return results.slice(count,count + 1);
};
})();

View File

@@ -121,23 +121,21 @@ exports["search-replace"] = function(source,operator,options) {
flagSuffix = (suffixes[0] ? (suffixes[0][0] || "") : ""),
flags = (flagSuffix.indexOf("g") !== -1 ? "g" : "") + (flagSuffix.indexOf("i") !== -1 ? "i" : "") + (flagSuffix.indexOf("m") !== -1 ? "m" : ""),
isRegExp = (suffixes[1] && suffixes[1][0] === "regexp") ? true : false,
//Escape regexp characters if the operand is not a regular expression
searchTerm = isRegExp ? operator.operand : $tw.utils.escapeRegExp(operator.operand),
//Escape $ character in replacement string if not in regular expression mode
replacement = isRegExp ? operator.operands[1] : (operator.operands[1]||"").replace(/\$/g,"$$$$"),
searchTerm,
regExp;
try {
regExp = new RegExp(searchTerm,flags);
} catch(ex) {
return ["RegExp error: " + ex];
}
source(function(tiddler,title) {
if(title && (operator.operands.length > 1)) {
//Escape regexp characters if the operand is not a regular expression
searchTerm = isRegExp ? operator.operand : $tw.utils.escapeRegExp(operator.operand);
try {
regExp = new RegExp(searchTerm,flags);
} catch(ex) {
return ["RegExp error: " + ex];
}
results.push(
title.replace(regExp,replacement)
title.replace(regExp,operator.operands[1])
);
regExp.lastIndex = 0;
} else {
results.push(title);
}

View File

@@ -0,0 +1,55 @@
/*\
title: $:/core/modules/server/routes/post-commands.js
type: application/javascript
module-type: route
POST /commands/
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "POST";
exports.path = /^\/commands\/$/;
exports.handler = function(request,response,state) {
// Check we're enabled
if(!($tw.boot.wikiInfo.config || {})["allow-remote-commands"]) {
response.writeHead(404);
response.end();
return;
}
// Get the job descriptor
var jobDescriptor = JSON.parse(state.data);
console.log("JOB START:",jobDescriptor)
// Respond OK
response.writeHead(204, "OK",{
"Content-Type": "application/json"
});
// Maintain status
var setStatus = function(status,message) {
if(jobDescriptor.statusTitle) {
state.wiki.addTiddler(new $tw.Tiddler({title: jobDescriptor.statusTitle,text: status,message: message}));
}
}
setStatus("started");
// Initiate the commands
var commander = new $tw.Commander(
jobDescriptor.commands || [],
function(err) {
setStatus(err ? "error" : "ok",err ? err : undefined);
console.log("JOB END:",err)
},
state.wiki,
{output: process.stdout, error: process.stderr}
);
commander.execute();
// Return results
response.end(JSON.stringify({}),"utf8"); // Nothing useful for us to return
};
}());

View File

@@ -51,20 +51,6 @@ exports.startup = function() {
element.focus(event.paramObject);
}
});
// Install the tm-rename-tiddler and tm-relink-tiddler messages
var makeRenameHandler = function(method) {
return function(event) {
var options = {},
paramObject = event.paramObject || {},
from = paramObject.from || event.tiddlerTitle,
to = paramObject.to;
options.dontRenameInTags = (paramObject.renameInTags === "false" || paramObject.renameInTags === "no") ? true : false;
options.dontRenameInLists = (paramObject.renameInLists === "false" || paramObject.renameInLists === "no") ? true : false;
$tw.wiki[method](from,to,options);
};
};
$tw.rootWidget.addEventListener("tm-rename-tiddler",makeRenameHandler("renameTiddler"));
$tw.rootWidget.addEventListener("tm-relink-tiddler",makeRenameHandler("relinkTiddler"));
// Install the scroller
$tw.pageScroller = new $tw.utils.PageScroller();
$tw.rootWidget.addEventListener("tm-scroll",function(event) {

View File

@@ -130,6 +130,11 @@ function Syncer(options) {
$tw.rootWidget.addEventListener("tm-server-refresh",function() {
self.handleRefreshEvent();
});
$tw.rootWidget.addEventListener("tm-execute-job",function(event) {
if(self.syncadaptor && self.syncadaptor.executeJob) {
self.syncadaptor.executeJob(event);
}
});
$tw.rootWidget.addEventListener("tm-copy-syncer-logs-to-clipboard",function() {
$tw.utils.copyToClipboard($tw.utils.getSystemInfo() + "\n\nLog:\n" + self.logger.getBuffer());
});

View File

@@ -24,13 +24,13 @@ exports.isDraft = function() {
return this.hasField("draft.of");
};
exports.getFieldString = function(field,defaultValue) {
exports.getFieldString = function(field) {
var value = this.fields[field];
// Check for a missing field
if(value === undefined || value === null) {
return defaultValue || "";
return "";
}
// Stringify the field with the associated tiddler field module (if any)
// Parse the field with the associated module (if any)
var fieldModule = $tw.Tiddler.fieldModules[field];
if(fieldModule && fieldModule.stringify) {
return fieldModule.stringify.call(this,value);

View File

@@ -21,7 +21,7 @@ exports.extractEncryptedStoreArea = function(text) {
if(encryptedStoreAreaStart !== -1) {
var encryptedStoreAreaEnd = text.indexOf("</pre>",encryptedStoreAreaStart);
if(encryptedStoreAreaEnd !== -1) {
return $tw.utils.htmlDecode(text.substring(encryptedStoreAreaStart + encryptedStoreAreaStartMarker.length,encryptedStoreAreaEnd));
return $tw.utils.htmlDecode(text.substring(encryptedStoreAreaStart + encryptedStoreAreaStartMarker.length,encryptedStoreAreaEnd-1));
}
}
return null;

View File

@@ -208,10 +208,10 @@ function parseJSONTiddlers(json,fallbackTitle) {
return data;
};
function dragEventContainsType(event,targetType) {
exports.dragEventContainsFiles = function(event) {
if(event.dataTransfer.types) {
for(var i=0; i<event.dataTransfer.types.length; i++) {
if(event.dataTransfer.types[i] === targetType) {
if(event.dataTransfer.types[i] === "Files") {
return true;
break;
}
@@ -220,10 +220,4 @@ function dragEventContainsType(event,targetType) {
return false;
};
exports.dragEventContainsFiles = function(event) {
return (dragEventContainsType(event,"Files") && !dragEventContainsType(event,"text/plain"));
};
exports.dragEventContainsType = dragEventContainsType;
})();

View File

@@ -49,14 +49,10 @@ Handle an event
*/
PageScroller.prototype.handleEvent = function(event) {
if(event.type === "tm-scroll") {
var options = {};
if($tw.utils.hop(event.paramObject,"animationDuration")) {
options.animationDuration = event.paramObject.animationDuration;
}
if(event.paramObject && event.paramObject.selector) {
this.scrollSelectorIntoView(null,event.paramObject.selector,null,options);
this.scrollSelectorIntoView(null,event.paramObject.selector);
} else {
this.scrollIntoView(event.target,null,options);
this.scrollIntoView(event.target);
}
return false; // Event was handled
}
@@ -66,10 +62,10 @@ PageScroller.prototype.handleEvent = function(event) {
/*
Handle a scroll event hitting the page document
*/
PageScroller.prototype.scrollIntoView = function(element,callback,options) {
PageScroller.prototype.scrollIntoView = function(element,callback) {
var self = this,
duration = $tw.utils.hop(options,"animationDuration") ? parseInt(options.animationDuration) : $tw.utils.getAnimationDuration(),
srcWindow = element ? element.ownerDocument.defaultView : window;
duration = $tw.utils.getAnimationDuration(),
srcWindow = element ? element.ownerDocument.defaultView : window;
// Now get ready to scroll the body
this.cancelScroll(srcWindow);
this.startTime = Date.now();
@@ -126,11 +122,11 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) {
drawFrame();
};
PageScroller.prototype.scrollSelectorIntoView = function(baseElement,selector,callback,options) {
PageScroller.prototype.scrollSelectorIntoView = function(baseElement,selector,callback) {
baseElement = baseElement || document.body;
var element = baseElement.querySelector(selector);
if(element) {
this.scrollIntoView(element,callback,options);
this.scrollIntoView(element,callback);
}
};

View File

@@ -87,7 +87,7 @@ SendMessageWidget.prototype.invokeAction = function(triggeringWidget,event) {
param: param,
paramObject: paramObject,
event: event,
tiddlerTitle: this.getVariable("currentTiddler"),
currentTiddler: this.getVariable("currentTiddler"),
navigateFromTitle: this.getVariable("storyTiddler")
};
this.dispatchEvent(params);

View File

@@ -52,13 +52,7 @@ CodeBlockWidget.prototype.execute = function() {
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
CodeBlockWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.code || changedAttributes.language) {
this.refreshSelf();
return true;
} else {
return false;
}
return false;
};
exports.codeblock = CodeBlockWidget;

View File

@@ -198,8 +198,7 @@ DropZoneWidget.prototype.handleDropEvent = function(event) {
this.resetState();
// Import any files in the drop
var numFiles = 0;
// If we have type text/vnd.tiddlywiki then skip trying to import files
if(dataTransfer.files && !$tw.utils.dragEventContainsType(event,"text/vnd.tiddler")) {
if(dataTransfer.files) {
numFiles = this.wiki.readFiles(dataTransfer.files,{
callback: readFileCallback,
deserializer: this.dropzoneDeserializer

View File

@@ -111,16 +111,6 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
if(this.imageAlt) {
domNode.setAttribute("alt",this.imageAlt);
}
// Add classes when the image loads or fails
$tw.utils.addClass(domNode,"tc-image-loading");
domNode.addEventListener("load",function() {
$tw.utils.removeClass(domNode,"tc-image-loading");
$tw.utils.addClass(domNode,"tc-image-loaded");
},false);
domNode.addEventListener("error",function() {
$tw.utils.removeClass(domNode,"tc-image-loading");
$tw.utils.addClass(domNode,"tc-image-error");
},false);
// Insert element
parent.insertBefore(domNode,nextSibling);
this.domNodes.push(domNode);

View File

@@ -46,7 +46,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this);
// Accumulate the <$set> widgets from each tiddler
$tw.utils.each(this.tiddlerList,function(title) {
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true});
var parser = widgetPointer.wiki.parseTiddler(title);
if(parser) {
var parseTreeNode = parser.tree[0];
while(parseTreeNode && parseTreeNode.type === "set") {

View File

@@ -1,96 +0,0 @@
/*\
title: $:/core/modules/widgets/let.js
type: application/javascript
module-type: widget
This widget allows defining multiple variables at once, while allowing
the later variables to depend upon the earlier ones.
```
\define helloworld() Hello world!
<$let currentTiddler="target" value={{!!value}} currentTiddler="different">
{{!!value}} will be different from <<value>>
</$let>
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var LetWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
LetWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
LetWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
LetWidget.prototype.computeAttributes = function() {
// Before computing attributes, we must make clear that none of the
// existing attributes are staged for lookup, even on a refresh
var changedAttributes = {},
self = this;
this.currentValueFor = Object.create(null);
$tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) {
var value = self.computeAttribute(attribute),
name = attribute.name;
if(name.charAt(0) !== "$") {
// Now that it's prepped, we're allowed to look this variable up
// when defining later variables
self.currentValueFor[name] = value;
}
});
// Run through again, setting variables and looking for differences
$tw.utils.each(this.currentValueFor,function(value,name) {
if (self.attributes[name] !== value) {
self.attributes[name] = value;
self.setVariable(name,value);
changedAttributes[name] = true;
}
});
return changedAttributes;
};
LetWidget.prototype.getVariableInfo = function(name,options) {
// Special handling: If this variable exists in this very $let, we can
// use it, but only if it's been staged.
if ($tw.utils.hop(this.currentValueFor,name)) {
return {
text: this.currentValueFor[name]
};
}
return Widget.prototype.getVariableInfo.call(this,name,options);
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
LetWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if($tw.utils.count(changedAttributes) > 0) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports["let"] = LetWidget;
})();

View File

@@ -51,9 +51,6 @@ ListWidget.prototype.render = function(parent,nextSibling) {
} else {
this.storyview = null;
}
if(this.storyview && this.storyview.renderEnd) {
this.storyview.renderEnd();
}
};
/*

View File

@@ -44,7 +44,8 @@ NavigatorWidget.prototype.render = function(parent,nextSibling) {
{type: "tm-fold-tiddler", handler: "handleFoldTiddlerEvent"},
{type: "tm-fold-other-tiddlers", handler: "handleFoldOtherTiddlersEvent"},
{type: "tm-fold-all-tiddlers", handler: "handleFoldAllTiddlersEvent"},
{type: "tm-unfold-all-tiddlers", handler: "handleUnfoldAllTiddlersEvent"}
{type: "tm-unfold-all-tiddlers", handler: "handleUnfoldAllTiddlersEvent"},
{type: "tm-rename-tiddler", handler: "handleRenameTiddlerEvent"}
]);
this.parentDomNode = parent;
this.computeAttributes();
@@ -635,6 +636,16 @@ NavigatorWidget.prototype.handleUnfoldAllTiddlersEvent = function(event) {
});
};
NavigatorWidget.prototype.handleRenameTiddlerEvent = function(event) {
var options = {},
paramObject = event.paramObject || {},
from = paramObject.from || event.tiddlerTitle,
to = paramObject.to;
options.dontRenameInTags = (paramObject.renameInTags === "false" || paramObject.renameInTags === "no") ? true : false;
options.dontRenameInLists = (paramObject.renameInLists === "false" || paramObject.renameInLists === "no") ? true : false;
this.wiki.renameTiddler(from,to,options);
};
exports.navigator = NavigatorWidget;
})();

View File

@@ -64,9 +64,9 @@ RadioWidget.prototype.getValue = function() {
tiddler = this.wiki.getTiddler(this.radioTitle);
if(tiddler) {
if(this.radioIndex) {
value = this.wiki.extractTiddlerDataItem(this.radioTitle,this.radioIndex,this.radioDefault);
value = this.wiki.extractTiddlerDataItem(this.radioTitle,this.radioIndex);
} else {
value = tiddler.getFieldString(this.radioField,this.radioDefault);
value = tiddler.getFieldString(this.radioField);
}
} else {
value = this.radioDefault;

View File

@@ -38,14 +38,10 @@ ScrollableWidget.prototype.handleScrollEvent = function(event) {
if(this.outerDomNode.scrollWidth <= this.outerDomNode.offsetWidth && this.outerDomNode.scrollHeight <= this.outerDomNode.offsetHeight && this.fallthrough === "yes") {
return true;
}
var options = {};
if($tw.utils.hop(event.paramObject,"animationDuration")) {
options.animationDuration = event.paramObject.animationDuration;
}
if(event.paramObject && event.paramObject.selector) {
this.scrollSelectorIntoView(null,event.paramObject.selector,null,options);
this.scrollSelectorIntoView(null,event.paramObject.selector);
} else {
this.scrollIntoView(event.target,null,options);
this.scrollIntoView(event.target);
}
return false; // Handled event
};
@@ -53,9 +49,9 @@ ScrollableWidget.prototype.handleScrollEvent = function(event) {
/*
Scroll an element into view
*/
ScrollableWidget.prototype.scrollIntoView = function(element,callback,options) {
var duration = $tw.utils.hop(options,"animationDuration") ? parseInt(options.animationDuration) : $tw.utils.getAnimationDuration(),
srcWindow = element ? element.ownerDocument.defaultView : window;
ScrollableWidget.prototype.scrollIntoView = function(element) {
var duration = $tw.utils.getAnimationDuration(),
srcWindow = element ? element.ownerDocument.defaultView : window;
this.cancelScroll();
this.startTime = Date.now();
var scrollPosition = {
@@ -118,11 +114,11 @@ ScrollableWidget.prototype.scrollIntoView = function(element,callback,options) {
}
};
ScrollableWidget.prototype.scrollSelectorIntoView = function(baseElement,selector,callback,options) {
ScrollableWidget.prototype.scrollSelectorIntoView = function(baseElement,selector,callback) {
baseElement = baseElement || document.body;
var element = baseElement.querySelector(selector);
if(element) {
this.scrollIntoView(element,callback,options);
this.scrollIntoView(element,callback);
}
};
@@ -159,6 +155,8 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
// Create elements
this.outerDomNode = this.document.createElement("div");
$tw.utils.setStyle(this.outerDomNode,[
{overflowY: "auto"},
{overflowX: "auto"},
{webkitOverflowScrolling: "touch"}
]);
this.innerDomNode = this.document.createElement("div");

View File

@@ -29,12 +29,14 @@ var VarsWidget = function(parseTreeNode,options) {
/*
Inherit from the base widget class
*/
VarsWidget.prototype = new Widget();
VarsWidget.prototype = Object.create(Widget.prototype);
/*
Render this widget into the DOM
*/
VarsWidget.prototype.render = function(parent,nextSibling) {
// Call the constructor
Widget.call(this);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
@@ -61,7 +63,7 @@ Refresh the widget by ensuring our attributes are up to date
*/
VarsWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if($tw.utils.count(changedAttributes) > 0) {
if(Object.keys(changedAttributes).length) {
this.refreshSelf();
return true;
}

View File

@@ -122,7 +122,7 @@ Widget.prototype.getVariableInfo = function(name,options) {
});
// Only substitute variable references if this variable was defined with the \define pragma
if(variable.isMacroDefinition) {
value = this.substituteVariableReferences(value,options);
value = this.substituteVariableReferences(value);
}
return {
text: value,
@@ -175,10 +175,10 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
return results;
};
Widget.prototype.substituteVariableReferences = function(text,options) {
Widget.prototype.substituteVariableReferences = function(text) {
var self = this;
return (text || "").replace(/\$\(([^\)\$]+)\)\$/g,function(match,p1,offset,string) {
return options.variables && options.variables[p1] || (self.getVariable(p1,{defaultValue: ""}));
return self.getVariable(p1,{defaultValue: ""});
});
};
@@ -263,9 +263,19 @@ Compute the current values of the attributes of the widget. Returns a hashmap of
*/
Widget.prototype.computeAttributes = function() {
var changedAttributes = {},
self = this;
self = this,
value;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
var value = self.computeAttribute(attribute);
if(attribute.type === "filtered") {
value = self.wiki.filterTiddlers(attribute.filter,self)[0] || "";
} else if(attribute.type === "indirect") {
value = self.wiki.getTextReference(attribute.textReference,"",self.getVariable("currentTiddler"));
} else if(attribute.type === "macro") {
value = self.getVariable(attribute.value.name,{params: attribute.value.params});
} else { // String attribute
value = attribute.value;
}
// Check whether the attribute has changed
if(self.attributes[name] !== value) {
self.attributes[name] = value;
changedAttributes[name] = true;
@@ -274,20 +284,6 @@ Widget.prototype.computeAttributes = function() {
return changedAttributes;
};
Widget.prototype.computeAttribute = function(attribute) {
var value;
if(attribute.type === "filtered") {
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
} else if(attribute.type === "indirect") {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
} else if(attribute.type === "macro") {
value = this.getVariable(attribute.value.name,{params: attribute.value.params});
} else { // String attribute
value = attribute.value;
}
return value;
};
/*
Check for the presence of an attribute
*/
@@ -314,40 +310,24 @@ excludeEventAttributes: ignores attributes whose name begins with "on"
Widget.prototype.assignAttributes = function(domNode,options) {
options = options || {};
var self = this;
var assignAttribute = function(name,value) {
// Check for excluded attribute names
if(options.excludeEventAttributes && name.substr(0,2) === "on") {
value = undefined;
$tw.utils.each(this.attributes,function(v,a) {
// Check exclusions
if(options.excludeEventAttributes && a.substr(0,2) === "on") {
v = undefined;
}
if(value !== undefined) {
// Handle the xlink: namespace
var namespace = null;
if(name.substr(0,6) === "xlink:" && name.length > 6) {
namespace = "http://www.w3.org/1999/xlink";
name = name.substr(6);
}
// Handle styles
if(name.substr(0,6) === "style." && name.length > 6) {
domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value;
} else {
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
try {
domNode.setAttributeNS(namespace,name,value);
} catch(e) {
if(v !== undefined) {
var b = a.split(":");
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
try {
if (b.length == 2 && b[0] == "xlink"){
domNode.setAttributeNS("http://www.w3.org/1999/xlink",b[1],v);
} else {
domNode.setAttributeNS(null,a,v);
}
} catch(e) {
}
}
}
// Not all parse tree nodes have the orderedAttributes property
if(this.parseTreeNode.orderedAttributes) {
$tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) {
assignAttribute(attribute.name,self.attributes[attribute.name]);
});
} else {
$tw.utils.each(Object.keys(self.attributes).sort(),function(name) {
assignAttribute(name,self.attributes[name]);
});
}
});
};
/*

View File

@@ -46,31 +46,26 @@ function relinkTiddler(fromTitle,toTitle,options) {
if(!tiddler.fields["plugin-type"] && type !== "application/javascript") {
var tags = tiddler.fields.tags ? tiddler.fields.tags.slice(0) : undefined,
list = tiddler.fields.list ? tiddler.fields.list.slice(0) : undefined,
isModified = false,
processList = function(listField) {
if(listField && listField.indexOf(fromTitle) !== -1) {
// Remove any existing instances of the toTitle
var p = listField.indexOf(toTitle);
while(p !== -1) {
listField.splice(p,1);
p = listField.indexOf(toTitle);
}
// Replace the fromTitle with toTitle
$tw.utils.each(listField,function (title,index) {
if(title === fromTitle) {
listField[index] = toTitle;
isModified = true;
}
});
}
};
isModified = false;
if(!options.dontRenameInTags) {
// Rename tags
processList(tags);
$tw.utils.each(tags,function (title,index) {
if(title === fromTitle) {
console.log("Renaming tag '" + tags[index] + "' to '" + toTitle + "' of tiddler '" + tiddler.fields.title + "'");
tags[index] = toTitle;
isModified = true;
}
});
}
if(!options.dontRenameInLists) {
// Rename lists
processList(list);
$tw.utils.each(list,function (title,index) {
if(title === fromTitle) {
console.log("Renaming list item '" + list[index] + "' to '" + toTitle + "' of tiddler '" + tiddler.fields.title + "'");
list[index] = toTitle;
isModified = true;
}
});
}
if(isModified) {
var newTiddler = new $tw.Tiddler(tiddler,{tags: tags, list: list},self.getModificationFields())

View File

@@ -6,7 +6,7 @@ title: $:/core/templates/tiddlywiki5.html
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--~~ Raw markup for the top of the head section ~~-->
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}`
`{{{ [<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}`
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta name="application-name" content="TiddlyWiki" />
<meta name="generator" content="TiddlyWiki" />

View File

@@ -1,7 +1,7 @@
title: $:/core/ui/Components/tag-link
<$link>
<$set name="backgroundColor" value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}>
<$set name="backgroundColor" value={{!!color}}>
<span style=<<tag-styles>> class="tc-tag-label">
<$view field="title" format="text"/>
</span>

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/Cascades
tags: $:/tags/ControlPanel/Advanced
caption: {{$:/language/ControlPanel/Cascades/Caption}}
{{$:/language/ControlPanel/Cascades/Hint}}
<div class="tc-control-panel">
<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Cascades]!has[draft.of]]" default="$:/core/ui/ControlPanel/StoryTiddler"/>
</div>

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/EditTemplateBody
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/EditTemplateBody/Caption}}
\define lingo-base() $:/language/ControlPanel/EditTemplateBody/
<<lingo Hint>>
{{$:/tags/EditTemplateBodyFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/StoryTiddler
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/StoryTiddler/Caption}}
\define lingo-base() $:/language/ControlPanel/StoryTiddler/
<<lingo Hint>>
{{$:/tags/StoryTiddlerTemplateFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/TiddlerColour
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/TiddlerColour/Caption}}
\define lingo-base() $:/language/ControlPanel/TiddlerColour/
<<lingo Hint>>
{{$:/tags/TiddlerColourFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/TiddlerIcon
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/TiddlerIcon/Caption}}
\define lingo-base() $:/language/ControlPanel/TiddlerIcon/
<<lingo Hint>>
{{$:/tags/TiddlerIconFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/ViewTemplateBody
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/ViewTemplateBody/Caption}}
\define lingo-base() $:/language/ControlPanel/ViewTemplateBody/
<<lingo Hint>>
{{$:/tags/ViewTemplateBodyFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,9 +0,0 @@
title: $:/core/ui/ControlPanel/ViewTemplateTitle
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/ViewTemplateTitle/Caption}}
\define lingo-base() $:/language/ControlPanel/ViewTemplateTitle/
<<lingo Hint>>
{{$:/tags/ViewTemplateTitleFilter||$:/snippets/ListTaggedCascade}}

View File

@@ -1,4 +1,54 @@
title: $:/core/ui/EditTemplate/body
tags: $:/tags/EditTemplate
<$transclude tiddler={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/EditTemplateBodyFilter]!is[draft]get[text]] :and[!is[blank]else[$:/core/ui/EditTemplate/body/default]] }}} />
\define lingo-base() $:/language/EditTemplate/Body/
\define config-visibility-title()
$:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$
\end
\define importFileActions()
<$action-popup $state=<<importState>> $coords="(0,0,0,0)" $floating="yes"/>
\end
<$list filter="[all[current]has[_canonical_uri]]">
<div class="tc-message-box">
<<lingo External/Hint>>
<a href={{!!_canonical_uri}}><$text text={{!!_canonical_uri}}/></a>
<$edit-text field="_canonical_uri" class="tc-edit-fields" tabindex={{$:/config/EditTabIndex}} cancelPopups="yes"></$edit-text>
</div>
</$list>
<$list filter="[all[current]!has[_canonical_uri]]">
<$vars importTitle=<<qualify $:/ImportImage>> importState=<<qualify $:/state/ImportImage>> >
<$dropzone importTitle=<<importTitle>> autoOpenOnImport="no" contentTypesFilter={{$:/config/Editor/ImportContentTypesFilter}} class="tc-dropzone-editor" enable={{{ [{$:/config/DragAndDrop/Enable}match[no]] :else[subfilter{$:/config/Editor/EnableImportFilter}then[yes]else[no]] }}} filesOnly="yes" actions=<<importFileActions>> ><$reveal state="$:/state/showeditpreview" type="match" text="yes">
<div class="tc-tiddler-preview">
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
<div class="tc-tiddler-preview-preview">
<$transclude tiddler={{$:/state/editpreviewtype}} mode="inline">
<$transclude tiddler="$:/core/ui/EditTemplate/body/preview/output" mode="inline"/>
</$transclude>
</div>
</div>
</$reveal>
<$reveal state="$:/state/showeditpreview" type="nomatch" text="yes">
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
</$reveal>
</$dropzone>
</$vars>
</$list>

View File

@@ -1,13 +0,0 @@
title: $:/core/ui/EditTemplate/body/canonical-uri
\define lingo-base() $:/language/EditTemplate/Body/
<div class="tc-message-box">
<<lingo External/Hint>>
<a href={{!!_canonical_uri}}><$text text={{!!_canonical_uri}}/></a>
<$edit-text field="_canonical_uri" class="tc-edit-fields" tabindex={{$:/config/EditTabIndex}} cancelPopups="yes"></$edit-text>
</div>

View File

@@ -1,38 +0,0 @@
title: $:/core/ui/EditTemplate/body/default
\define config-visibility-title()
$:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$
\end
\define importFileActions()
<$action-popup $state=<<importState>> $coords="(0,0,0,0)" $floating="yes"/>
\end
<$set name="edit-preview-state" value={{{ [{$:/config/ShowEditPreview/PerTiddler}!match[yes]then[$:/state/showeditpreview]] :else[<qualify "$:/state/showeditpreview">] }}}>
<$vars importTitle=<<qualify $:/ImportImage>> importState=<<qualify $:/state/ImportImage>> >
<$dropzone importTitle=<<importTitle>> autoOpenOnImport="no" contentTypesFilter={{$:/config/Editor/ImportContentTypesFilter}} class="tc-dropzone-editor" enable={{{ [{$:/config/DragAndDrop/Enable}match[no]] :else[subfilter{$:/config/Editor/EnableImportFilter}then[yes]else[no]] }}} filesOnly="yes" actions=<<importFileActions>> ><$reveal stateTitle=<<edit-preview-state>> type="match" text="yes">
<div class="tc-tiddler-preview">
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
<div class="tc-tiddler-preview-preview">
<$transclude tiddler={{$:/state/editpreviewtype}} mode="inline">
<$transclude tiddler="$:/core/ui/EditTemplate/body/preview/output" mode="inline"/>
</$transclude>
</div>
</div>
</$reveal>
<$reveal stateTitle=<<edit-preview-state>> type="nomatch" text="yes">
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
</$reveal>
</$dropzone>
</$vars>
</$set>

View File

@@ -29,7 +29,7 @@ color:$(foregroundColor)$;
\whitespace trim
<div class="tc-edit-tags">
<$list filter="[list[!!$tagField$]sort[title]]" storyview="pop">
<$macrocall $name="tag-body" colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} tagField=<<__tagField__>>/>
<$macrocall $name="tag-body" colour={{!!color}} palette={{$:/palette}} icon={{!!icon}} tagField=<<__tagField__>>/>
</$list>
<$vars tabIndex={{$:/config/EditTabIndex}} cancelPopups="yes">
<$macrocall $name="tag-picker" tagField=<<__tagField__>>/>

View File

@@ -23,7 +23,7 @@ first-search-filter: [all[shadows+tiddlers]prefix[$:/language/Docs/Types/]sort[d
<$text text={{!!group}}/>
</div>
<$set name="userInput" value={{{ [<typeInputTiddler>get[text]] }}}>
<$list filter="[all[shadows+tiddlers]prefix[$:/language/Docs/Types/]group{!!group}] +[sort[description]] +[removeprefix[$:/language/Docs/Types/]] +[search<userInput>]"><span class={{{ [<currentTiddler>addsuffix[-primaryList]] -[<typeSelectionTiddler>get[text]] +[then[]else[tc-list-item-selected]] }}}><$link to={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]get[name]] }}}><$view tiddler={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]] }}} field="description"/><$text text=" "/>(<$view tiddler={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]] }}} field="name"/>)</$link></span>
<$list filter="[all[shadows+tiddlers]prefix[$:/language/Docs/Types/]group{!!group}] +[sort[description]] +[removeprefix[$:/language/Docs/Types/]] +[search<userInput>]"><span class={{{ [<currentTiddler>addsuffix[-primaryList]] -[<typeSelectionTiddler>get[text]] +[then[]else[tc-list-item-selected]] }}}><$link to={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]get[name]] }}}><$view tiddler={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]] }}} field="description"/> (<$view tiddler={{{ [<currentTiddler>addprefix[$:/language/Docs/Types/]] }}} field="name"/>)</$link></span>
</$list>
</$set>
</$list>

View File

@@ -8,11 +8,11 @@ condition: [<targetTiddler>]
button-classes: tc-text-editor-toolbar-item-start-group
shortcuts: ((preview))
<$reveal state=<<edit-preview-state>> type="match" text="yes" tag="span">
<$reveal state="$:/state/showeditpreview" type="match" text="yes" tag="span">
{{$:/core/images/preview-open}}
<$action-setfield $tiddler=<<edit-preview-state>> $value="no"/>
<$action-setfield $tiddler="$:/state/showeditpreview" $value="no"/>
</$reveal>
<$reveal state=<<edit-preview-state>> type="nomatch" text="yes" tag="span">
<$reveal state="$:/state/showeditpreview" type="nomatch" text="yes" tag="span">
{{$:/core/images/preview-closed}}
<$action-setfield $tiddler=<<edit-preview-state>> $value="yes"/>
<$action-setfield $tiddler="$:/state/showeditpreview" $value="yes"/>
</$reveal>

View File

@@ -1,14 +0,0 @@
title: $:/snippets/ListTaggedCascade
{{||$:/language/ControlPanel/Cascades/TagPrompt}}
<ol>
<$list filter="[all[shadows+tiddlers]tag<currentTiddler>]">
<li>
<div>
<$link><$text text=<<currentTiddler>>/></$link>
</div>
<$codeblock code={{!!text}}/>
</li>
</$list>
</ol>

View File

@@ -14,7 +14,7 @@ tags: $:/tags/PageTemplate
</section>
<$list filter="[list[$:/StoryList]]" history="$:/HistoryList" template="$:/core/ui/StoryTiddlerTemplate" storyview={{$:/view}} emptyMessage={{$:/config/EmptyStoryMessage}}/>
<$list filter="[list[$:/StoryList]]" history="$:/HistoryList" template={{$:/config/ui/ViewTemplate}} editTemplate={{$:/config/ui/EditTemplate}} storyview={{$:/view}} emptyMessage={{$:/config/EmptyStoryMessage}}/>
<section class="story-frontdrop">

View File

@@ -1,15 +1,5 @@
title: $:/core/ui/PluginListItemTemplate
\whitespace trim
<$link to={{!!title}} class="tc-plugin-info">
<div class="tc-plugin-info-chunk tc-plugin-info-icon">
<$transclude tiddler=<<currentTiddler>> subtiddler={{{ [<currentTiddler>addsuffix[/icon]] }}}>
<$transclude tiddler={{{ [<currentTiddler>get[plugin-type]addprefix[$:/core/images/plugin-generic-]] }}}/>
</$transclude>
</div>
<div class="tc-plugin-info-chunk tc-plugin-info-description">
<h1>
''<$text text={{{ [<currentTiddler>get[name]] ~[<currentTiddler>split[/]last[1]] }}}/>'':&nbsp;<$view field="description"><$view field="title"/></$view>
</h1>
</div>
</$link>
<div class="tc-menu-list-item">
<$link to={{!!title}}><$view field="description"><$view field="title"/></$view></$link>
</div>

View File

@@ -1,3 +0,0 @@
title: $:/core/ui/StoryTiddlerTemplate
<$transclude tiddler={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/StoryTiddlerTemplateFilter]!is[draft]get[text]] :and[!is[blank]else{$:/config/ui/ViewTemplate}] }}} />

View File

@@ -13,10 +13,10 @@ title: $:/core/ui/TagPickerTagTemplate
<$action-setfield $tiddler=<<refreshTitle>> text="yes"/>
</$list>
<<actions>>
<$set name="backgroundColor" value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}>
<$wikify name="foregroundColor" text="""<$macrocall $name="contrastcolour" target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>>/>""">
<$set name="backgroundColor" value={{!!color}}>
<$wikify name="foregroundColor" text="""<$macrocall $name="contrastcolour" target={{!!color}} fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>>/>""">
<span class="tc-tag-label tc-btn-invisible" style=<<tag-pill-styles>>>
{{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/>
<$transclude tiddler={{!!icon}}/><$view field="title" format="text"/>
</span>
</$wikify>
</$set>

View File

@@ -3,7 +3,7 @@ title: $:/core/ui/TagTemplate
\whitespace trim
<span class="tc-tag-list-item" data-tag-title=<<currentTiddler>>>
<$set name="transclusion" value=<<currentTiddler>>>
<$macrocall $name="tag-pill-body" tag=<<currentTiddler>> icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$button""" element-attributes="""popup=<<qualify "$:/state/popup/tag">> dragFilter='[all[current]tagging[]]' tag='span'"""/>
<$macrocall $name="tag-pill-body" tag=<<currentTiddler>> icon={{!!icon}} colour={{!!color}} palette={{$:/palette}} element-tag="""$button""" element-attributes="""popup=<<qualify "$:/state/popup/tag">> dragFilter='[all[current]tagging[]]' tag='span'"""/>
<$reveal state=<<qualify "$:/state/popup/tag">> type="popup" position="below" animate="yes" class="tc-drop-down">
<$set name="tv-show-missing-links" value="yes">
<$transclude tiddler="$:/core/ui/ListItemTemplate"/>

View File

@@ -1,8 +0,0 @@
title: $:/core/ui/TiddlerIcon
\whitespace trim
<$let tiddlerIcon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}}>
<$list filter="[<tiddlerIcon>!is[blank]]" variable="ignore">
<$transclude tiddler=<<tiddlerIcon>>/>
</$list>
</$let>

View File

@@ -3,6 +3,14 @@ tags: $:/tags/ViewTemplate
<$reveal tag="div" class="tc-tiddler-body" type="nomatch" stateTitle=<<folded-state>> text="hide" retain="yes" animate="yes">
<$transclude tiddler={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/ViewTemplateBodyFilter]!is[draft]get[text]] :and[!is[blank]else[$:/core/ui/ViewTemplate/body/default]] }}} />
<$list filter="[all[current]!has[plugin-type]!field:hide-body[yes]]">
<$transclude>
<$transclude tiddler="$:/language/MissingTiddler/Hint"/>
</$transclude>
</$list>
</$reveal>

View File

@@ -1,3 +0,0 @@
title: $:/core/ui/ViewTemplate/body/blank
<!-- Intentionally blank -->

View File

@@ -1,3 +0,0 @@
title: $:/core/ui/ViewTemplate/body/code
<$codeblock code={{{ [<currentTiddler>get[text]] }}} language={{{ [<currentTiddler>get[type]else[text/vnd.tiddlywiki]] }}}/>

View File

@@ -1,7 +0,0 @@
title: $:/core/ui/ViewTemplate/body/default
<$transclude>
<$transclude tiddler="$:/language/MissingTiddler/Hint"/>
</$transclude>

View File

@@ -1,10 +0,0 @@
title: $:/core/ui/ViewTemplate/body/plugin
<div class="tc-tiddler-plugin-info">
<$let plugin-type={{!!plugin-type}}
default-popup-state="yes"
qualified-state=<<qualify "$:/state/plugin-info">>
>
{{||$:/core/ui/Components/plugin-info}}
</$let>
</div>

View File

@@ -1,4 +1,5 @@
title: $:/core/ui/ViewTemplate/body/import
title: $:/core/ui/ViewTemplate/import
tags: $:/tags/ViewTemplate
\define lingo-base() $:/language/Import/

View File

@@ -0,0 +1,15 @@
title: $:/core/ui/ViewTemplate/plugin
tags: $:/tags/ViewTemplate
<$reveal tag="div" class="tc-tiddler-plugin-info" type="nomatch" stateTitle=<<folded-state>> text="hide" retain="yes" animate="yes">
<$list filter="[all[current]has[plugin-type]] -[all[current]field:plugin-type[import]]">
<$set name="plugin-type" value={{!!plugin-type}}>
<$set name="default-popup-state" value="yes">
<$set name="qualified-state" value=<<qualify "$:/state/plugin-info">>>
{{||$:/core/ui/Components/plugin-info}}
</$set>
</$set>
</$set>
</$list>
</$reveal>

View File

@@ -12,12 +12,25 @@ fill:$(foregroundColor)$;
</span>
<$set name="tv-wikilinks" value={{$:/config/Tiddlers/TitleLinks}}>
<$link>
<$let foregroundColor={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}>
<$set name="foregroundColor" value={{!!color}}>
<$list filter="[all[current]has[icon]]~[[$:/config/DefaultTiddlerIcon]has[text]]">
<span class="tc-tiddler-title-icon" style=<<title-styles>>>
{{||$:/core/ui/TiddlerIcon}}
<$transclude tiddler={{!!icon}}>
<$transclude tiddler={{$:/config/DefaultTiddlerIcon}}/>
</$transclude>
</span>
</$let>
<$transclude tiddler={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/ViewTemplateTitleFilter]!is[draft]get[text]] :and[!is[blank]else[$:/core/ui/ViewTemplate/title/default]] }}} />
</$list>
</$set>
<$list filter="[all[current]removeprefix[$:/]]">
<h2 class="tc-title" title={{$:/language/SystemTiddler/Tooltip}}>
<span class="tc-system-title-prefix">$:/</span><$text text=<<currentTiddler>>/>
</h2>
</$list>
<$list filter="[all[current]!prefix[$:/]]">
<h2 class="tc-title">
<$view field="title"/>
</h2>
</$list>
</$link>
</$set>
</div>

View File

@@ -1,6 +0,0 @@
title: $:/core/ui/ViewTemplate/title/default
\whitespace trim
<h2 class="tc-title">
<$view field="title"/>
</h2>

View File

@@ -1,6 +0,0 @@
title: $:/core/ui/ViewTemplate/title/system
\whitespace trim
<h2 class="tc-title" title={{$:/language/SystemTiddler/Tooltip}}>
<span class="tc-system-title-prefix">$:/</span><$text text={{{ [<currentTiddler>removeprefix[$:/]] }}}/>
</h2>

View File

@@ -1,7 +1,7 @@
title: $:/snippets/allfields
\define renderfield(title)
<tr class="tc-view-field"><td class="tc-view-field-name">''<$text text=<<__title__>>/>'':</td><td class="tc-view-field-value">//{{$:/language/Docs/Fields/$title$}}//</td></tr>
<tr class="tc-view-field"><td class="tc-view-field-name">''$title$'':</td><td class="tc-view-field-value">//{{$:/language/Docs/Fields/$title$}}//</td></tr>
\end
<table class="tc-view-field-table"><tbody><$list filter="[fields[]sort[title]]" variable="listItem"><$macrocall $name="renderfield" title=<<listItem>>/></$list>
</tbody></table>

View File

@@ -1,5 +0,0 @@
title: $:/config/EditTemplateBodyFilters/
tags: $:/tags/EditTemplateBodyFilter
canonical-uri: [has[_canonical_uri]then[$:/core/ui/EditTemplate/body/canonical-uri]]
default: [[$:/core/ui/EditTemplate/body/default]]

View File

@@ -1,6 +1,6 @@
title: $:/config/OfficialPluginLibrary
tags: $:/tags/PluginLibrary
url: https://tiddlywiki.com/library/v5.2.2/index.html
url: https://tiddlywiki.com/library/v5.2.0/index.html
caption: {{$:/language/OfficialPluginLibrary}}
{{$:/language/OfficialPluginLibrary/Hint}}

View File

@@ -1,5 +0,0 @@
title: $:/config/StoryTiddlerTemplateFilters/
tags: $:/tags/StoryTiddlerTemplateFilter
draft: [is[draft]then{$:/config/ui/EditTemplate}]
default: [{$:/config/ui/ViewTemplate}]

View File

@@ -1,5 +0,0 @@
title: $:/config/TiddlerColourFilters/
tags: $:/tags/TiddlerColourFilter
color-field: [has[color]then{!!color}]
default: [[$:/config/DefaultTiddlerColour]has[text]get[text]trim[]]

View File

@@ -1,5 +0,0 @@
title: $:/config/TiddlerIconFilters/
tags: $:/tags/TiddlerIconFilter
icon-field: [has[icon]then{!!icon}]
default: [{$:/config/DefaultTiddlerIcon}has[text]]

View File

@@ -1,9 +0,0 @@
title: $:/config/ViewTemplateBodyFilters/
tags: $:/tags/ViewTemplateBodyFilter
system: [prefix[$:/boot/]] [prefix[$:/config/]] [prefix[$:/core/macros]] [prefix[$:/core/save/]] [prefix[$:/core/templates/]] [prefix[$:/core/ui/]split[/]count[]compare:number:eq[4]] [prefix[$:/info/]] [prefix[$:/language/]] [prefix[$:/languages/]] [prefix[$:/snippets/]] [prefix[$:/state/]] [prefix[$:/status/]] [prefix[$:/info/]] [prefix[$:/temp/]] +[limit[1]then[$:/core/ui/ViewTemplate/body/code]]
code-body: [field:code-body[yes]then[$:/core/ui/ViewTemplate/body/code]]
import: [field:plugin-type[import]then[$:/core/ui/ViewTemplate/body/import]]
plugin: [has[plugin-type]then[$:/core/ui/ViewTemplate/body/plugin]]
hide-body: [field:hide-body[yes]then[$:/core/ui/ViewTemplate/body/blank]]
default: [[$:/core/ui/ViewTemplate/body/default]]

View File

@@ -1,5 +0,0 @@
title: $:/config/ViewTemplateTitleFilters/
tags: $:/tags/ViewTemplateTitleFilter
system: [prefix[$:/]then[$:/core/ui/ViewTemplate/title/system]]
default: [[$:/core/ui/ViewTemplate/title/default]]

View File

@@ -1,9 +0,0 @@
title: $:/snippets/DebugStylesheets
<style>[test]{list-style:'❌'}</style>
<ul>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Stylesheet]has[modified]]" counter="n">
<style>{{!!text}}[test="<<n>>"]{list-style:disc;}</style>
<li test=<<n>>><$link/></li>
</$list>
</ul>

View File

@@ -21,9 +21,7 @@ $actions$<$transclude tiddler="""$icon$"""/><$view tiddler=<<__tag__>> field="ti
\define tag-pill(tag,element-tag:"span",element-attributes:"",actions:"")
<span class="tc-tag-list-item" data-tag-title=<<__tag__>>>
<$let currentTiddler=<<__tag__>>>
<$macrocall $name="tag-pill-body" tag=<<__tag__>> icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/>
</$let>
<$macrocall $name="tag-pill-body" tag=<<__tag__>> icon={{{ [<__tag__>get[icon]] }}} colour={{{ [<__tag__>get[color]] }}} palette={{$:/palette}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/>
</span>
\end

View File

@@ -1,7 +1,6 @@
title: $:/snippets/peek-stylesheets
\define expandable-stylesheets-list()
\whitespace trim
<ol>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Stylesheet]!has[draft.of]]">
<$vars state=<<qualify "$:/state/peek-stylesheets/open/">>>
@@ -39,7 +38,6 @@ title: $:/snippets/peek-stylesheets
\end
\define stylesheets-list()
\whitespace trim
<ol>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Stylesheet]!has[draft.of]]">
<li>
@@ -59,7 +57,6 @@ title: $:/snippets/peek-stylesheets
</$list>
</ol>
\end
\whitespace trim
<$vars modeState=<<qualify "$:/state/peek-stylesheets/mode/">>>

View File

@@ -1,2 +0,0 @@
title: $:/tags/EditTemplateBodyFilter
list: $:/config/EditTemplateBodyFilters/canonical-uri $:/config/EditTemplateBodyFilters/default

View File

@@ -1,2 +0,0 @@
title: $:/tags/StoryTiddlerTemplateFilter
list: $:/config/StoryTiddlerTemplateFilters/draft $:/config/StoryTiddlerTemplateFilters/default

View File

@@ -1,3 +0,0 @@
title: $:/tags/TiddlerColourFilter
list: $:/config/TiddlerColourFilters/color-field $:/config/TiddlerColourFilters/default

View File

@@ -1,3 +0,0 @@
title: $:/tags/TiddlerIconFilter
list: $:/config/TiddlerIconFilters/icon-field $:/config/TiddlerIconFilters/default

View File

@@ -1,3 +0,0 @@
title: $:/tags/ViewTemplateBodyFilter
list: $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/default

View File

@@ -1,3 +0,0 @@
title: $:/tags/ViewTemplateTitleFilter
list: $:/config/ViewTemplateTitleFilters/system $:/config/ViewTemplateTitleFilters/default

View File

@@ -0,0 +1,123 @@
created: 20181001171604072
modified: 20181001184306738
title: External Pipes
type: text/vnd.tiddlywiki
!! Introduction
The external pipes mechanism is intended to address the fact that it can be difficult for experienced developers to quickly get started working with TiddlyWiki's code because it requires a good understanding of JavaScript, and some relatively unusual concepts and techniques that are likely to be unfamiliar to most.
Many software developers are comfortable using and building text-based command line tools that follow the Unix philosophy of using stdin/stdout or TCP sockets for input and output, allowing tools to be chained together in powerful ways.
External pipes support data transfer with external programs via two techniques:
* By directly executing the program and communicating with it via stdin and stdout ([[mimic.js|External Pipes mimic.js]] and [[stats.js|External Pipes stats.js]])
* By connecting to an existing TCP socket via a host address and port ([[reverser.js|External Pipes reverser.js]])
Pipes are registered and executed under Node.js. Execution can be triggered via a server command or via a message propagated from the browser.
Pipes currently support a simple question/response model:
* TiddlyWiki opens a connection to the host/port, or starts the external task
* TiddlyWiki sends the outgoing data through the pipe
* TiddlyWiki receives the incoming data through the pipe
* The connection is close, or the external task exits
!! `externalpipesdemo` Edition
The edition at `editions/externalpipesdemo` in the TiddlyWiki 5 repo contains the example tasks referenced in this documentation.
!! Configuration
Pipes must be registered in the `tiddlywiki.info` file of a wiki folder. For example:
{{External Pipes tiddlywiki.info}}
!!! Task Configuration Properties
TBD
!!! Input Formats
The available input formats are:
* `raw-text` - concatenated raw text of the tiddlers
* `rendered-text` - concatenated rendered text of the tiddlers
* `rendered-html` - concatenated rendered html of the tiddlers
* `json-raw-tiddlers` - raw tiddlers in JSON
* `json-rendered-text-tiddlers` - rendered tiddlers in JSON
!!! Output Formats
The available output formats include:
* `raw-text` - raw text
* `json-raw-tiddlers` - raw tiddlers in JSON
!! Usage
Pipes can be invoked via the `--pipe` command, or via a widget message propagated from the browser to the server.
!!! `--pipe` Command
The `--pipe` command triggers the invocation of a pipe task. The parameters are:
```
tiddlywiki <wikifolder> --pipe <pipename> <filter> <incomingTitle> <arguments>...
```
The ''pipename'' identifies the pipe, and must match the name defined in the `tiddlywiki.info` file.
The ''filter'' identifies the tiddlers to be passed to the pipe
The ''incomingTitle'' provides a title to be applied to incoming tiddlers for the `raw-text` output format
The remaining ''arguments'' are passed through to external task pipes, and ignored for other types of pipe.
!!! `tm-exec-task` Message
TBD
!! Examples
The edition `editions/externalpipesdemo` in the TiddlyWiki5 repo contains a demo wiki that invokes sample tasks.
!!! Mimic
The `mimic.js` example is a simple command line tool that accepts source text via stdin and outputs a sort of statistical parody of the input text. It is written without any knowledge of TiddlyWiki, and so serves as an example of using an existing text-based tool with TiddlyWiki.
```
tiddlywiki editions/externalpipesdemo/ --verbose --pipe mimic '[[Alice in Wonderland]]' HelloThere 5 4000 --build index
```
View the resulting wiki at `editions/externaltaskdemo/output/index.html`: the tiddler "HelloThere" will contain a garbled version of the "Alice in Wonderland" text.
!!! Stats
The `stats.js` example is a simple command line tool that accepts source text via stdin and outputs a sort of statistical parody of the input text. It is written without any knowledge of TiddlyWiki, and so serves as an example of using an existing text-based tool with TiddlyWiki.
```
tiddlywiki editions/externalpipesdemo/ --verbose --pipe stats '[[Alice in Wonderland]]' HelloThere --build index
```
View the resulting wiki at `editions/externaltaskdemo/output/index.html`: the tiddler "HelloThere" will contain statistics about the "Alice in Wonderland" text.
!!! Reverser
The `reverser.js` example is an simple command line tool that listens for connections on a host address and port, accepts source text and then returns the same text with the characters reversed.
Two separate commands are needed to run the sample: one to get the server running, and the second to run TiddlyWiki and invoke the server.
To run the server, open a command prompt in the `demo-tasks` directory and run:
```
../reverser.js 8081
```
And then in another command prompt in the root of the repo run:
```
tiddlywiki editions/externalpipesdemo/ --verbose --pipe reverser '[[Alice in Wonderland]]' HelloThere --build index
```
View the resulting wiki at `editions/externaltaskdemo/output/index.html`: the tiddler "HelloThere" will contain the reversed text of "Alice in Wonderland".

View File

@@ -0,0 +1,32 @@
{
"tiddlers": [
{
"file": "../../../../../externalpipesdemo/tiddlywiki.info",
"fields": {
"type": "text/plain",
"title": "External Pipes tiddlywiki.info"
}
},
{
"file": "../../../../../externalpipesdemo/demo-tasks/mimic.js",
"fields": {
"type": "application/javascript",
"title": "External Pipes mimic.js"
}
},
{
"file": "../../../../../externalpipesdemo/demo-tasks/reverser.js",
"fields": {
"type": "application/javascript",
"title": "External Pipes reverser.js"
}
},
{
"file": "../../../../../externalpipesdemo/demo-tasks/stats.js",
"fields": {
"type": "application/javascript",
"title": "External Pipes stats.js"
}
}
]
}

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env node
/*
A socket server that listens on a host/port for connections and suggests tags for the incoming text
badtagger.js <port> <host>
This utility is provided as an example of using an external task that doesn't have any prior knowledge of
TiddlyWiki. Like many Unix utilities, it just reads and writes to a socket.
*/
var net = require("net"),
port = parseInt(process.argv[2] || "",10) || 8081, // Port
host = process.argv[3] || "127.0.0.1"; // Host
var server = net.createServer();
server.listen(port,host);
server.on("connection", function(sock) {
console.log("CONNECTED: " + sock.remoteAddress +":"+ sock.remotePort);
// Trap errors
sock.on("error",function(e) {
console.log("ERROR: " + e);
});
// Read data until the end
var accumulator = Buffer.alloc(0);
sock.on("data",function(data) {
console.log("DATA " + sock.remoteAddress + ": " + data.length);
accumulator = Buffer.concat([accumulator,Buffer.from(data)]);
while(accumulator.length > 4) {
var length = accumulator.readInt32BE(0);
if(accumulator.length >= (length + 4)) {
if(length < 1) {
throw "ERROR: Incoming message length field is less than 1";
}
var type = accumulator.readUInt8(4),
dataLength = length - 1,
data = accumulator.toString("latin1",5,dataLength + 5);
accumulator = accumulator.slice(length + 4);
// Recase it
console.log("MESSAGE",length,type);
var suggestedTags = Buffer.from(suggestTags(data),"latin1");
// Send it back
var lengthBytes = Buffer.alloc(4);
lengthBytes.writeUInt32BE(suggestedTags.length + 1,0)
console.log("RESPONSE",1,suggestedTags.length)
sock.write(lengthBytes);
var typeByte = Buffer.alloc(1);
typeByte.writeUInt8(1,0);
sock.write(typeByte);
sock.write(suggestedTags);
} else {
break;
}
}
});
sock.on("end",function() {
console.log("END")
sock.end();
});
sock.on("close", function(data) {
console.log("CLOSED: " + sock.remoteAddress +" "+ sock.remotePort);
});
});
function suggestTags(str) {
var tags = [];
if(/e/mi.test(str)) {
tags.push("elephant");
}
if(/s/mi.test(str)) {
tags.push("snake");
}
if(/c/mi.test(str)) {
tags.push("cow");
}
return tags.join("\n");
}

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env node
/*
Reads source text from stdin and mimics it to stdout to stdout using a simple statistical analysis of ngram frequency
mimic.js <ngram-length> <output-length>
This utility is provided as an example of using an external task that doesn't have any prior knowledge of
TiddlyWiki. Like many Unix utilities, it just reads input from stdin and writes its output to stdout.
*/
var paramNgramLength = parseInt(process.argv[2] || "",10) || 3, // Size of ngrams for mimicing
paramOutputLength = parseInt(process.argv[3] || "",10) || 1000;
process.stdin.resume();
process.stdin.setEncoding("utf8");
var inputChunks = [];
process.stdin.on("data",function(chunk) {
inputChunks.push(chunk);
});
process.stdin.on("end",function() {
// Do the mimicry
var output = mimic(inputChunks.join(""),paramNgramLength);
// Output the result
process.stdout.write(output);
});
function mimic(sourceText,paramNgramLength) {
if(!sourceText) {
return "";
}
var tree = {};
scanText(tree,sourceText,paramNgramLength);
return generateText(tree,sourceText,paramNgramLength,paramOutputLength);
}
/*
The source text is scanned to build a tree of the ngram prefixes as follows:
{
"abc": { // The ngram prefix described by this record
count: 42, // The number of times the prefix is found in the source text
next: [ // An array of information about each subsequent character encountered after the prefix
{char: "d", count: 41},
{char: " ", count: 1}
]
},
"def": ... etc
}
*/
// Process the source text into the specified tree with the chosen ngram size
function scanText(tree,sourceText,size) {
var currgram = [],ptr,c,ngram,branch,n;
if(sourceText.length <= size*2)
return tree;
sourceText += sourceText.substring(0,size*2-1); // Wrap the text around
for(ptr=0; ptr<size; ptr++) {
currgram.push(sourceText.substr(ptr,1));
}
while(ptr < sourceText.length) {
ngram = currgram.join("");
c = sourceText.substr(ptr++,1);
branch = tree[ngram];
if(branch === undefined) {
branch = tree[ngram] = {count: 0,next: []};
}
for(n = 0; n<branch.next.length; n++) {
if(branch.next[n].char === c)
break;
}
if(branch.next[n] === undefined) {
branch.next[n] = {char: c, count: 1};
} else {
branch.next[n].count++;
}
branch.count++;
currgram.push(c)
currgram.shift();
}
return tree;
}
// Use the tree to generate mimicry
function generateText(tree,sourceText,size,length) {
var currgram = [];
for(var t=0; t<size; t++) {
currgram.push(sourceText.substr(t,1));
}
var result = [];
var c,ngram,branch,r,n;
for(t=0; t<length; t++) {
ngram = currgram.join("");
branch = tree[ngram];
n = 0;
r = Math.floor(Math.random() * branch.count);
while(r >= branch.next[n].count) {
r = r - branch.next[n].count;
n++;
}
c = branch.next[n].char;
result.push(c);
currgram.push(c)
currgram.shift();
}
return result.join("");
}

View File

@@ -0,0 +1,3 @@
# Sample Processes for TiddlyWiki External Pipes
"External Pipes" are connections enabling a wiki to communicate with an external process to perform arbitrary processing on a group of tiddlers.

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env escript
%% run with sh ./recase_erl.sh
-mode(compile).
main(_) ->
{ok, Listen} = gen_tcp:listen(8081, [binary,{packet,4},
{active,true}]),
spawn(fun() -> par_connect(Listen) end),
receive after infinity -> void end.
par_connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
io:format("connected ~n"),
spawn(fun() -> par_connect(Listen) end),
loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Bin} ->
io:format("received ~p bytes ~s ~n",[size(Bin), Bin]),
Return = recase_binary(Bin),
io:format("sending: ~p bytes~n",[size(Return)]),
gen_tcp:send(Socket, Return),
loop(Socket);
Other ->
io:format("received ~p~n",[Other])
end.
recase_binary(<<1,B/binary>>) ->
L = binary_to_list(B),
L1 = [recase(I) || I <- L],
B1 = list_to_binary(L1),
<<1,B1/binary>>.
recase(I) when I >= $a, I =< $z -> I - $a + $A;
recase(I) when I >= $A, I =< $Z -> I - $A + $a;
recase(I) -> I.

Some files were not shown because too many files have changed in this diff Show More