1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-27 21:31:25 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Saq Imtiaz
bd050521ff Create eslint.yml 2025-10-02 22:30:06 +02:00
250 changed files with 4560 additions and 2733 deletions

View File

@@ -10,7 +10,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE_VERSION }}"
@@ -30,7 +30,7 @@ jobs:
TW5_BUILD_MAIN_EDITION: "./editions/prerelease"
TW5_BUILD_OUTPUT: "./output/prerelease"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE_VERSION }}"
@@ -62,7 +62,7 @@ jobs:
TW5_BUILD_OUTPUT: "./output"
TW5_BUILD_ARCHIVE: "./output"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "${{ env.NODE_VERSION }}"

View File

@@ -3,6 +3,9 @@ name: ESLint
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
workflow_dispatch:
concurrency:
@@ -20,7 +23,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -73,8 +73,10 @@ rm $TW5_BUILD_OUTPUT/dev/static/*
echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html
echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html
echo "<a href='./plugins/tiddlywiki/d3/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/d3/index.html</a>" > $TW5_BUILD_OUTPUT/d3demo.html
echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html
echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html
echo "<a href='./plugins/tiddlywiki/tahoelafs/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tahoelafs/index.html</a>" > $TW5_BUILD_OUTPUT/tahoelafs.html
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
@@ -299,6 +301,26 @@ node $TW5_BUILD_TIDDLYWIKI \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/tahoelafs/index.html Demo wiki with Tahoe-LAFS plugin
# /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/tahoelafs \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tahoelafs/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/d3/index.html Demo wiki with D3 plugin
# /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/d3demo \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/d3/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin
# /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin
node $TW5_BUILD_TIDDLYWIKI \

View File

@@ -641,7 +641,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
// Call the function and return the exports
return fn.apply(null,contextValues);
};
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
/*
Run code in a sandbox with only the specified context variables in scope
*/
@@ -1532,8 +1532,7 @@ Define all modules stored in ordinary tiddlers
*/
$tw.Wiki.prototype.defineTiddlerModules = function() {
this.each(function(tiddler,title) {
// Modules in draft tiddlers are disabled
if(tiddler.hasField("module-type") && (!tiddler.hasField("draft.of"))) {
if(tiddler.hasField("module-type")) {
switch(tiddler.fields.type) {
case "application/javascript":
// We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script
@@ -1560,11 +1559,6 @@ $tw.Wiki.prototype.defineShadowModules = function() {
this.eachShadow(function(tiddler,title) {
// Don't define the module if it is overidden by an ordinary tiddler
if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) {
if(tiddler.hasField("draft.of")) {
// Report a fundamental problem
console.warn(`TiddlyWiki: Plugins should not contain tiddlers with a 'draft.of' field: ${tiddler.fields.title}`);
return;
}
// Define the module
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
}
@@ -1913,7 +1907,7 @@ $tw.loadTiddlersFromFile = function(filepath,fields) {
fileSize = fs.statSync(filepath).size,
data;
if(fileSize > $tw.config.maxEditFileSize) {
data = "File " + filepath + " not loaded because it is too large";
data = "File " + filepath + "not loaded because it is too large";
console.log("Warning: " + data);
ext = ".txt";
} else {
@@ -1984,41 +1978,22 @@ filepath: pathname of the directory containing the specification file
$tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var tiddlers = [];
// Read the specification
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"), function(e) {
console.log("Warning: tiddlywiki.files in " + filepath + " invalid: " + e.message);
return {};
});
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"));
// Helper to process a file
var processFile = function(filename,isTiddlerFile,fields,isEditableFile,rootPath) {
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
type = (extInfo || {}).type || fields.type || "text/plain",
typeInfo = $tw.config.contentTypeInfo[type] || {},
pathname = path.resolve(filepath,filename),
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8"),
metadata = $tw.loadMetadataForFile(pathname) || {},
fileTooLarge = false,
text, fileTiddlers;
if("_canonical_uri" in fields) {
text = "";
} else if(fs.statSync(pathname).size > $tw.config.maxEditFileSize) {
var msg = "File " + pathname + " not loaded because it is too large";
console.log("Warning: " + msg);
fileTooLarge = true;
text = isTiddlerFile ? msg : "";
} else {
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8");
}
fileTiddlers;
if(isTiddlerFile) {
fileTiddlers = $tw.wiki.deserializeTiddlers(fileTooLarge ? ".txt" : path.extname(pathname),text,metadata) || [];
fileTiddlers = $tw.wiki.deserializeTiddlers(path.extname(pathname),text,metadata) || [];
} else {
fileTiddlers = [$tw.utils.extend({text: text},metadata)];
}
var combinedFields = $tw.utils.extend({},fields,metadata);
if(fileTooLarge && isTiddlerFile) {
delete combinedFields.type; // type altered
}
$tw.utils.each(fileTiddlers,function(tiddler) {
$tw.utils.each(combinedFields,function(fieldInfo,name) {
if(typeof fieldInfo === "string" || $tw.utils.isArray(fieldInfo)) {
@@ -2093,7 +2068,6 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
} else if(tidInfo.suffix) {
tidInfo.fields.text = {suffix: tidInfo.suffix};
}
tidInfo.fields = tidInfo.fields || {};
processFile(tidInfo.file,tidInfo.isTiddlerFile,tidInfo.fields);
});
// Process any listed directories
@@ -2115,7 +2089,6 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var thisPath = path.relative(filepath, files[t]),
filename = path.basename(thisPath);
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
dirSpec.fields = dirSpec.fields || {};
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path);
}
}
@@ -2578,10 +2551,10 @@ $tw.boot.execStartup = function(options){
if($tw.safeMode) {
$tw.wiki.processSafeMode();
}
// Register typed modules from the tiddlers we've just loaded and any modules within plugins
// Tiddlers should appear last so that they may overwrite shadows during module registration
$tw.wiki.defineShadowModules();
// Register typed modules from the tiddlers we've just loaded
$tw.wiki.defineTiddlerModules();
// And any modules within plugins
$tw.wiki.defineShadowModules();
// Make sure the crypto state tiddler is up to date
if($tw.crypto) {
$tw.crypto.updateCryptoStateTiddler();
@@ -2650,13 +2623,11 @@ $tw.boot.executeNextStartupTask = function(callback) {
$tw.boot.log(s.join(" "));
// Execute task
if(!$tw.utils.hop(task,"synchronous") || task.synchronous) {
const thenable = task.startup();
if(thenable && typeof thenable.then === "function"){
thenable.then(asyncTaskCallback);
return true;
} else {
return asyncTaskCallback();
task.startup();
if(task.name) {
$tw.boot.executedStartupModules[task.name] = true;
}
return $tw.boot.executeNextStartupTask(callback);
} else {
task.startup(asyncTaskCallback);
return true;

View File

@@ -99,18 +99,16 @@ Commander.prototype.executeNextCommand = function() {
}
}
if(command.info.synchronous) {
// Synchronous command (await thenables)
// Synchronous command
c = new command.Command(params,this);
err = c.execute();
if(err && typeof err.then === "function") {
err.then(e => { e ? this.callback(e) : this.executeNextCommand(); });
} else if(err) {
if(err) {
this.callback(err);
} else {
this.executeNextCommand();
}
} else {
// Asynchronous command (await thenables)
// Asynchronous command
c = new command.Command(params,this,function(err) {
if(err) {
self.callback(err);
@@ -119,9 +117,7 @@ Commander.prototype.executeNextCommand = function() {
}
});
err = c.execute();
if(err && typeof err.then === "function") {
err.then(e => { if(e) this.callback(e); });
} else if(err) {
if(err) {
this.callback(err);
}
}

View File

@@ -8,14 +8,10 @@ DELETE /recipes/default/tiddlers/:title
\*/
"use strict";
exports.methods = ["DELETE"];
exports.method = "DELETE";
exports.path = /^\/bags\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]);
state.wiki.deleteTiddler(title);

View File

@@ -8,14 +8,10 @@ GET /favicon.ico
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/favicon.ico$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64");

View File

@@ -8,66 +8,35 @@ GET /files/:filepath
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/files\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var path = require("path"),
fs = require("fs"),
util = require("util"),
suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]),
baseFilename = path.resolve(state.boot.wikiPath,"files"),
filename = path.resolve(baseFilename,suppliedFilename),
extension = path.extname(filename);
// Check that the filename is inside the wiki files folder
if(path.relative(baseFilename,filename).indexOf("..") === 0) {
return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
}
fs.stat(filename, function(err, stats) {
if(err) {
return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
} else {
var type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"),
responseHeaders = {
"Content-Type": type,
"Accept-Ranges": "bytes"
};
var rangeHeader = request.headers.range,
stream;
if(rangeHeader) {
// Handle range requests
var parts = rangeHeader.replace(/bytes=/, "").split("-"),
start = parseInt(parts[0], 10),
end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;
// Validate start and end
if(isNaN(start) || isNaN(end) || start < 0 || end < start || end >= stats.size) {
responseHeaders["Content-Range"] = "bytes */" + stats.size;
return response.writeHead(416, responseHeaders).end();
}
var chunksize = (end - start) + 1;
responseHeaders["Content-Range"] = "bytes " + start + "-" + end + "/" + stats.size;
responseHeaders["Content-Length"] = chunksize;
response.writeHead(206, responseHeaders);
stream = fs.createReadStream(filename, {start: start, end: end});
if(path.relative(baseFilename,filename).indexOf("..") !== 0) {
// Send the file
fs.readFile(filename,function(err,content) {
var status,content,type = "text/plain";
if(err) {
console.log("Error accessing file " + filename + ": " + err.toString());
status = 404;
content = "File '" + suppliedFilename + "' not found";
} else {
responseHeaders["Content-Length"] = stats.size;
response.writeHead(200, responseHeaders);
stream = fs.createReadStream(filename);
status = 200;
content = content;
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
}
// Common stream error handling
stream.on("error", function(err) {
if(!response.headersSent) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.end("Read error");
} else {
response.destroy();
}
});
stream.pipe(response);
}
});
state.sendResponse(status,{"Content-Type": type},content);
});
} else {
state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
}
};

View File

@@ -8,14 +8,10 @@ GET /
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
responseHeaders = {

View File

@@ -8,14 +8,10 @@ GET /login-basic -- force a Basic Authentication challenge
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/login-basic$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
if(!state.authenticatedUsername) {
// Challenge if there's no username

View File

@@ -8,14 +8,10 @@ GET /status
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/status$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var text = JSON.stringify({
username: state.authenticatedUsername || state.server.get("anon-username") || "",

View File

@@ -8,14 +8,10 @@ GET /:title
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/([^\/]+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
tiddler = state.wiki.getTiddler(title);

View File

@@ -8,14 +8,10 @@ GET /recipes/default/tiddlers/:title
\*/
"use strict";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
tiddler = state.wiki.getTiddler(title),

View File

@@ -10,14 +10,10 @@ GET /recipes/default/tiddlers.json?filter=<filter>
var DEFAULT_FILTER = "[all[tiddlers]!is[system]sort[title]]";
exports.methods = ["GET"];
exports.method = "GET";
exports.path = /^\/recipes\/default\/tiddlers.json$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var filter = state.queryParameters.filter || DEFAULT_FILTER;
if(state.wiki.getTiddlerText("$:/config/Server/AllowAllExternalFilters") !== "yes") {

View File

@@ -8,14 +8,10 @@ PUT /recipes/default/tiddlers/:title
\*/
"use strict";
exports.methods = ["PUT"];
exports.method = "PUT";
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
fields = $tw.utils.parseJSONSafe(state.data);

View File

@@ -74,11 +74,6 @@ function Server(options) {
// console.log("Loading server route " + title);
self.addRoute(routeDefinition);
});
this.routes.sort((a, b) => {
const priorityA = a.info?.priority ?? 100,
priorityB = b.info?.priority ?? 100;
return priorityB - priorityA;
});
// Initialise the http vs https
this.listenOptions = null;
this.protocol = "http";
@@ -222,7 +217,7 @@ Server.prototype.findMatchingRoute = function(request,state) {
} else {
match = potentialRoute.path.exec(pathname);
}
if(match && (potentialRoute.methods?.includes(request.method) || potentialRoute.method === request.method)) {
if(match && request.method === potentialRoute.method) {
state.params = [];
for(var p=1; p<match.length; p++) {
state.params.push(match[p]);

View File

@@ -147,7 +147,7 @@ Settings/AutoSave/Disabled/Description: Do not save changes automatically
Settings/AutoSave/Enabled/Description: Save changes automatically
Settings/AutoSave/Hint: Attempt to automatically save changes during editing when using a supporting saver
Settings/CamelCase/Caption: Camel Case Wiki Links
Settings/CamelCase/Hint: Requires reload to take effect
Settings/CamelCase/Hint: You can globally disable automatic linking of ~CamelCase phrases. Requires reload to take effect
Settings/CamelCase/Description: Enable automatic ~CamelCase linking
Settings/Caption: Settings
Settings/EditorToolbar/Caption: Editor Toolbar

View File

@@ -34,7 +34,7 @@ function FramedEngine(options) {
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette");
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
this.iframeDoc.open();
this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
this.iframeDoc.write("<meta name='color-scheme' content='" + colorScheme + "'>");
this.iframeDoc.close();
// Style the iframe
this.iframeNode.className = this.dummyTextArea.className;

View File

@@ -68,7 +68,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
// Fix height
this.engine.fixHeight();
// Focus if required
if($tw.browser && (this.editFocus === "true" || this.editFocus === "yes") && !$tw.utils.hasClass(this.parentDomNode.ownerDocument.activeElement,"tc-keep-focus")) {
if(this.editFocus === "true" || this.editFocus === "yes") {
this.engine.focus();
}
// Add widget message listeners

View File

@@ -217,10 +217,6 @@ function makeNumericReducingOperator(fnCalc,initialValue,fnFinal) {
source(function(tiddler,title) {
result.push($tw.utils.parseNumber(title));
});
// We return an empty array if there are no input titles
if(result.length === 0) {
return [];
}
var value = result.reduce(function(accumulator,currentValue) {
return fnCalc(accumulator,currentValue);
},initialValue);

View File

@@ -1,86 +0,0 @@
/*\
title: $:/core/modules/info/windowdimensions.js
type: application/javascript
module-type: info
\*/
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
if(!$tw.browser) {
return [];
}
class WindowDimensionsTracker {
constructor(updateCallback) {
this.updateCallback = updateCallback;
this.resizeHandlers = new Map();
this.dimensionsInfo = [
["outer/width", win => win.outerWidth],
["outer/height", win => win.outerHeight],
["inner/width", win => win.innerWidth],
["inner/height", win => win.innerHeight],
["client/width", win => win.document.documentElement.clientWidth],
["client/height", win => win.document.documentElement.clientHeight]
];
}
buildTiddlers(win,windowId) {
const prefix = `$:/info/browser/window/${windowId}/`;
return this.dimensionsInfo.map(([suffix, getter]) => ({
title: prefix + suffix,
text: String(getter(win))
}));
}
clearTiddlers(windowId) {
const prefix = `$:/info/browser/window/${windowId}/`,
deletions = this.dimensionsInfo.map(([suffix]) => prefix + suffix);
this.updateCallback([], deletions);
}
getUpdateHandler(win,windowId) {
let scheduled = false;
return () => {
if(!scheduled) {
scheduled = true;
requestAnimationFrame(() => {
this.updateCallback(this.buildTiddlers(win,windowId), []);
scheduled = false;
});
}
};
}
trackWindow(win,windowId) {
const handler = this.getUpdateHandler(win, windowId);
handler(); // initial update
win.addEventListener("resize",handler,{passive:true});
this.resizeHandlers.set(windowId,{win, handler});
}
untrackWindow(windowId) {
const entry = this.resizeHandlers.get(windowId);
if(entry) {
entry.win.removeEventListener("resize", entry.handler);
this.resizeHandlers.delete(windowId);
}
this.clearTiddlers(windowId);
}
}
const tracker = new WindowDimensionsTracker(updateInfoTiddlersCallback);
// Track main window
tracker.trackWindow(window,"system/main");
// Hook into event bus for user windows
if($tw.eventBus) {
$tw.eventBus.on("window:opened", ({window: win, windowID}) => {
tracker.trackWindow(win, "user/" + windowID);
});
$tw.eventBus.on("window:closed", ({windowID}) => {
tracker.untrackWindow("user/" + windowID);
});
}
return [];
};

View File

@@ -7,34 +7,23 @@ The audio parser parses an audio tiddler into an embeddable HTML element
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var AudioParser = function(type,text,options) {
var element = {
type: "element",
tag: "$audio", // Using $audio to enable widget interception
tag: "audio",
attributes: {
controls: {type: "string", value: "controls"},
style: {type: "string", value: "width: 100%; object-fit: contain"}
}
};
// Pass through source information
},
src;
if(options._canonical_uri) {
element.attributes.src = {type: "string", value: options._canonical_uri};
element.attributes.type = {type: "string", value: type};
} else if(text) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
element.attributes.type = {type: "string", value: type};
}
// Pass through tiddler title if available
if(options.title) {
element.attributes.tiddler = {type: "string", value: options.title};
}
this.tree = [element];
this.source = text;
this.type = type;
@@ -44,4 +33,3 @@ exports["audio/ogg"] = AudioParser;
exports["audio/mpeg"] = AudioParser;
exports["audio/mp3"] = AudioParser;
exports["audio/mp4"] = AudioParser;

View File

@@ -39,7 +39,7 @@ exports.parse = function() {
// Return the classed span
return [{
type: "element",
tag: "s",
tag: "strike",
children: tree
}];
};

View File

@@ -23,27 +23,6 @@ exports.init = function(parser) {
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
};
/*
Reject the match if we don't have a template or text reference
*/
exports.findNextMatch = function(startPos) {
this.matchRegExp.lastIndex = startPos;
this.match = this.matchRegExp.exec(this.parser.source);
if(this.match) {
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
// Bail if we don't have a template or text reference
if(!template && !textRef) {
return undefined;
} else {
return this.match.index;
}
} else {
return undefined;
}
return this.match ? this.match.index : undefined;
};
exports.parse = function() {
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;

View File

@@ -23,27 +23,6 @@ exports.init = function(parser) {
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
};
/*
Reject the match if we don't have a template or text reference
*/
exports.findNextMatch = function(startPos) {
this.matchRegExp.lastIndex = startPos;
this.match = this.matchRegExp.exec(this.parser.source);
if(this.match) {
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
// Bail if we don't have a template or text reference
if(!template && !textRef) {
return undefined;
} else {
return this.match.index;
}
} else {
return undefined;
}
return this.match ? this.match.index : undefined;
};
exports.parse = function() {
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;

View File

@@ -35,9 +35,7 @@ DownloadSaver.prototype.save = function(text,method,callback,options) {
}
// Set up the link
var link = document.createElement("a");
// We prefer Blobs if they're available, unless we're dealing with a tiddler type declaring itself full of base64 encoded content.
// Then we use data urls, because browsers will know to decode the stream and download the actual binary file as intended.
if(Blob !== undefined && !type.includes(";base64")) {
if(Blob !== undefined) {
var blob = new Blob([text], {type: type});
link.setAttribute("href", URL.createObjectURL(blob));
} else {

View File

@@ -1,66 +0,0 @@
/*\
title: $:/core/modules/savers/postmessage.js
type: application/javascript
module-type: saver
Handles saving changes via window.postMessage() to the window.parent
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Select the appropriate saver module and set it up
*/
var PostMessageSaver = function(wiki) {
this.publisher = new $tw.utils.BrowserMessagingPublisher({type: "SAVE"});
};
PostMessageSaver.prototype.save = function(text,method,callback,options) {
// Fail if the publisher hasn't been fully initialised
if(!this.publisher.canSend()) {
return false;
}
// Send the save request
this.publisher.send({
verb: "SAVE",
body: text
},function(err) {
if(err) {
callback("PostMessageSaver Error: " + err);
} else {
callback(null);
}
});
// Indicate that we handled the save
return true;
};
/*
Information about this saver
*/
PostMessageSaver.prototype.info = {
name: "postmessage",
capabilities: ["save", "autosave"],
priority: 100
};
/*
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
// Provisionally say that we can save
return true;
};
/*
Create an instance of this saver
*/
exports.create = function(wiki) {
return new PostMessageSaver(wiki);
};
})();

View File

@@ -1,46 +0,0 @@
/*\
title: $:/core/modules/startup/eventbus.js
type: application/javascript
module-type: startup
Event bus for cross module communication
\*/
exports.name = "eventbus";
exports.platforms = ["browser"];
exports.before = ["windows"];
exports.synchronous = true;
$tw.eventBus = {
listenersMap: new Map(),
on(event,handler) {
if(!this.listenersMap.has(event)) {
this.listenersMap.set(event,new Set());
}
const listeners = this.listenersMap.get(event);
listeners.add(handler);
},
off(event,handler) {
const listeners = this.listenersMap.get(event);
if(listeners) {
listeners.delete(handler);
}
},
once(event,handler) {
const wrapper = (...args) => {
handler(...args);
this.off(event, wrapper);
};
this.on(event, wrapper);
},
emit(event,data) {
const listeners = this.listenersMap.get(event);
if(listeners) {
listeners.forEach(fn => fn(data));
}
}
};

View File

@@ -19,16 +19,6 @@ exports.synchronous = true;
var FAVICON_TITLE = "$:/favicon.ico";
exports.startup = function() {
var setFavicon = function() {
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
if(tiddler) {
var faviconLink = document.getElementById("faviconLink"),
dataURI = $tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri);
faviconLink.setAttribute("href",dataURI);
$tw.faviconPublisher.send({verb: "FAVICON",body: dataURI});
}
}
$tw.faviconPublisher = new $tw.utils.BrowserMessagingPublisher({type: "FAVICON", onsubscribe: setFavicon});
// Set up the favicon
setFavicon();
// Reset the favicon when the tiddler changes
@@ -38,3 +28,11 @@ exports.startup = function() {
}
});
};
function setFavicon() {
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
if(tiddler) {
var faviconLink = document.getElementById("faviconLink");
faviconLink.setAttribute("href",$tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri));
}
}

View File

@@ -19,17 +19,11 @@ var TITLE_INFO_PLUGIN = "$:/temp/info-plugin";
exports.startup = function() {
// Function to bake the info plugin with new tiddlers
// additions: array of tiddler field objects
// removals: array of titles to remove
var updateInfoPlugin = function(additions = [], removals = []) {
var updateInfoPlugin = function(tiddlerFieldsArray) {
// Get the existing tiddlers
var json = $tw.wiki.getTiddlerData(TITLE_INFO_PLUGIN,{tiddlers: {}});
$tw.utils.each(removals,function(title) {
if(json.tiddlers[title]) {
delete json.tiddlers[title];
}
});
$tw.utils.each(additions,function(fields) {
// Add the new ones
$tw.utils.each(tiddlerFieldsArray,function(fields) {
if(fields && fields.title) {
json.tiddlers[fields.title] = fields;
}
@@ -53,7 +47,7 @@ exports.startup = function() {
}
});
updateInfoPlugin(tiddlerFieldsArray);
$tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
var changes = $tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
$tw.wiki.registerPluginTiddlers("info",[TITLE_INFO_PLUGIN]);
$tw.wiki.unpackPluginTiddlers();
};

View File

@@ -33,15 +33,10 @@ exports.startup = function() {
});
$tw.titleContainer = $tw.fakeDocument.createElement("div");
$tw.titleWidgetNode.render($tw.titleContainer,null);
var publishTitle = function() {
$tw.titlePublisher.send({verb: "PAGETITLE",body: document.title});
document.title = $tw.titleContainer.textContent;
};
$tw.titlePublisher = new $tw.utils.BrowserMessagingPublisher({type: "PAGETITLE", onsubscribe: publishTitle});
publishTitle();
document.title = $tw.titleContainer.textContent;
$tw.wiki.addEventListener("change",function(changes) {
if($tw.titleWidgetNode.refresh(changes,$tw.titleContainer,null)) {
publishTitle();
document.title = $tw.titleContainer.textContent;
}
});
// Set up the styles

View File

@@ -56,11 +56,9 @@ exports.startup = function() {
srcDocument.write("<!DOCTYPE html><head></head><body class='tc-body tc-single-tiddler-window'></body></html>");
srcDocument.close();
srcDocument.title = windowTitle;
$tw.eventBus.emit("window:opened",{windowID, window: srcWindow});
srcWindow.addEventListener("beforeunload",function(event) {
delete $tw.windows[windowID];
$tw.wiki.removeEventListener("change",refreshHandler);
$tw.eventBus.emit("window:closed",{windowID});
},false);
// Set up the styles
var styleWidgetNode = $tw.wiki.makeTranscludeWidget("$:/core/ui/PageStylesheet",{

View File

@@ -47,16 +47,16 @@ ClassicStoryView.prototype.insert = function(widget) {
// Reset the margin once the transition is over
setTimeout(function() {
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: ""}
]);
$tw.utils.removeStyle(targetElement, "transition");
},duration);
// Set up the initial position of the element
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: (-currHeight) + "px"},
{opacity: "0.0"}
]);
$tw.utils.removeStyle(targetElement, "transition");
$tw.utils.forceLayout(targetElement);
// Transition to the final position
$tw.utils.setStyle(targetElement,[
@@ -64,7 +64,7 @@ ClassicStoryView.prototype.insert = function(widget) {
"margin-bottom " + duration + "ms " + easing},
{marginBottom: currMarginBottom + "px"},
{opacity: "1.0"}
]);
]);
}
};
@@ -94,9 +94,11 @@ ClassicStoryView.prototype.remove = function(widget) {
setTimeout(removeElement,duration);
// Animate the closure
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "translateX(0px)"},
{marginBottom: currMarginBottom + "px"},
{opacity: "1.0"}
]);
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
$tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms " + easing + ", " +
@@ -111,4 +113,4 @@ ClassicStoryView.prototype.remove = function(widget) {
}
};
exports.classic = ClassicStoryView;
exports.classic = ClassicStoryView;

View File

@@ -37,7 +37,10 @@ PopStoryView.prototype.insert = function(widget) {
}
// Reset once the transition is over
setTimeout(function() {
$tw.utils.removeStyles(targetElement, ["transition", "transform"]);
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "none"}
]);
$tw.utils.setStyle(widget.document.body,[
{"overflow-x": ""}
]);
@@ -48,10 +51,10 @@ PopStoryView.prototype.insert = function(widget) {
]);
// Set up the initial position of the element
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "scale(2)"},
{opacity: "0.0"}
]);
$tw.utils.removeStyle(targetElement, "transition");
$tw.utils.forceLayout(targetElement);
// Transition to the final position
$tw.utils.setStyle(targetElement,[
@@ -60,9 +63,6 @@ PopStoryView.prototype.insert = function(widget) {
{transform: "scale(1)"},
{opacity: "1.0"}
]);
setTimeout(function() {
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opactity"]);
}, duration)
};
PopStoryView.prototype.remove = function(widget) {
@@ -81,7 +81,11 @@ PopStoryView.prototype.remove = function(widget) {
// Remove the element at the end of the transition
setTimeout(removeElement,duration);
// Animate the closure
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
$tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "scale(1)"},
{opacity: "1.0"}
]);
$tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
@@ -91,4 +95,4 @@ PopStoryView.prototype.remove = function(widget) {
]);
};
exports.pop = PopStoryView;
exports.pop = PopStoryView;

View File

@@ -96,9 +96,6 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
{transform: "translateX(0px) translateY(0px) scale(1)"},
{zIndex: "500"},
]);
setTimeout(function() {
$tw.utils.removeStyles(targetElement, ["transition", "opacity", "transform", "zIndex"]);
}, duration);
// Transform the previous tiddler out of the way and then hide it
if(prevCurrentTiddler && prevCurrentTiddler !== targetElement) {
scale = zoomBounds.width / sourceBounds.width;
@@ -210,9 +207,6 @@ ZoominListView.prototype.remove = function(widget) {
{opacity: "0"},
{zIndex: "0"}
]);
setTimeout(function() {
$tw.utils.removeStyles(toWidgetDomNode, ["transformOrigin", "transform", "transition", "opacity", "zIndex"]);
}, duration);
setTimeout(removeElement,duration);
// Now the tiddler we're going back to
if(toWidgetDomNode) {
@@ -228,4 +222,4 @@ ZoominListView.prototype.logTextNodeRoot = function(node) {
this.textNodeLogger.log($tw.language.getString("Error/ZoominTextNode") + " " + node.textContent);
};
exports.zoomin = ZoominListView;
exports.zoomin = ZoominListView;

View File

@@ -0,0 +1,142 @@
// From https://gist.github.com/Nijikokun/5192472
//
// UTF8 Module
//
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
(function (name, definition, context, dependencies) {
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
else { context[name] = definition.apply(context); }
})('utf8', function () {
return {
encode: function (string) {
if (typeof string !== 'string') return string;
else string = string.replace(/\r\n/g, "\n");
var output = "", i = 0, charCode;
for (i; i < string.length; i++) {
charCode = string.charCodeAt(i);
if (charCode < 128) {
output += String.fromCharCode(charCode);
} else if ((charCode > 127) && (charCode < 2048)) {
output += String.fromCharCode((charCode >> 6) | 192);
output += String.fromCharCode((charCode & 63) | 128);
} else if ((charCode > 55295) && (charCode < 57344) && string.length > i+1) {
// Surrogate pair
var hiSurrogate = charCode;
var loSurrogate = string.charCodeAt(i+1);
i++; // Skip the low surrogate on the next loop pass
var codePoint = (((hiSurrogate - 55296) << 10) | (loSurrogate - 56320)) + 65536;
output += String.fromCharCode((codePoint >> 18) | 240);
output += String.fromCharCode(((codePoint >> 12) & 63) | 128);
output += String.fromCharCode(((codePoint >> 6) & 63) | 128);
output += String.fromCharCode((codePoint & 63) | 128);
} else {
// Not a surrogate pair, or a dangling surrogate without its partner that we'll just encode as-is
output += String.fromCharCode((charCode >> 12) | 224);
output += String.fromCharCode(((charCode >> 6) & 63) | 128);
output += String.fromCharCode((charCode & 63) | 128);
}
}
return output;
},
decode: function (string) {
if (typeof string !== 'string') return string;
var output = "", i = 0, charCode = 0;
while (i < string.length) {
charCode = string.charCodeAt(i);
if (charCode < 128) {
output += String.fromCharCode(charCode),
i++;
} else if ((charCode > 191) && (charCode < 224)) {
output += String.fromCharCode(((charCode & 31) << 6) | (string.charCodeAt(i + 1) & 63));
i += 2;
} else if ((charCode > 223) && (charCode < 240)) {
output += String.fromCharCode(((charCode & 15) << 12) | ((string.charCodeAt(i + 1) & 63) << 6) | (string.charCodeAt(i + 2) & 63));
i += 3;
} else {
var codePoint = ((charCode & 7) << 18) | ((string.charCodeAt(i + 1) & 63) << 12) | ((string.charCodeAt(i + 2) & 63) << 6) | (string.charCodeAt(i + 3) & 63);
// output += String.fromCodePoint(codePoint); // Can't do this because Internet Explorer doesn't have String.fromCodePoint
output += String.fromCharCode(((codePoint - 65536) >> 10) + 55296) + String.fromCharCode(((codePoint - 65536) & 1023) + 56320); // So we do this instead
i += 4;
}
}
return output;
}
};
}, this);
// Base64 Module
//
// Cleaner, modularized and properly scoped base64 encoding and decoding module for strings.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
(function (name, definition, context, dependencies) {
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
else { context[name] = definition.apply(context); }
})('base64', function (utf8) {
var $this = this;
var $utf8 = utf8 || this.utf8;
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
return {
encode: function (input) {
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
if (typeof input !== 'string') return input;
else input = $utf8.encode(input);
var output = "", a, b, c, d, e, f, g, i = 0;
while (i < input.length) {
a = input.charCodeAt(i++);
b = input.charCodeAt(i++);
c = input.charCodeAt(i++);
d = a >> 2;
e = ((a & 3) << 4) | (b >> 4);
f = ((b & 15) << 2) | (c >> 6);
g = c & 63;
if (isNaN(b)) f = g = 64;
else if (isNaN(c)) g = 64;
output += map.charAt(d) + map.charAt(e) + map.charAt(f) + map.charAt(g);
}
return output;
},
decode: function (input) {
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
if (typeof input !== 'string') return input;
else input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
var output = "", a, b, c, d, e, f, g, i = 0;
while (i < input.length) {
d = map.indexOf(input.charAt(i++));
e = map.indexOf(input.charAt(i++));
f = map.indexOf(input.charAt(i++));
g = map.indexOf(input.charAt(i++));
a = (d << 2) | (e >> 4);
b = ((e & 15) << 4) | (f >> 2);
c = ((f & 3) << 6) | g;
output += String.fromCharCode(a);
if (f != 64) output += String.fromCharCode(b);
if (g != 64) output += String.fromCharCode(c);
}
return $utf8.decode(output);
}
}
}, this, [ "utf8" ]);

View File

@@ -0,0 +1,9 @@
// From https://gist.github.com/Nijikokun/5192472
//
// UTF8 Module
//
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
!function(r,e,o,t){void 0!==o.module&&o.module.exports?o.module.exports=e.apply(o):void 0!==o.define&&"function"===o.define&&o.define.amd?define("utf8",[],e):o.utf8=e.apply(o)}(0,function(){return{encode:function(r){if("string"!=typeof r)return r;r=r.replace(/\r\n/g,"\n");for(var e,o="",t=0;t<r.length;t++)if((e=r.charCodeAt(t))<128)o+=String.fromCharCode(e);else if(e>127&&e<2048)o+=String.fromCharCode(e>>6|192),o+=String.fromCharCode(63&e|128);else if(e>55295&&e<57344&&r.length>t+1){var i=e,n=r.charCodeAt(t+1);t++;var d=65536+(i-55296<<10|n-56320);o+=String.fromCharCode(d>>18|240),o+=String.fromCharCode(d>>12&63|128),o+=String.fromCharCode(d>>6&63|128),o+=String.fromCharCode(63&d|128)}else o+=String.fromCharCode(e>>12|224),o+=String.fromCharCode(e>>6&63|128),o+=String.fromCharCode(63&e|128);return o},decode:function(r){if("string"!=typeof r)return r;for(var e="",o=0,t=0;o<r.length;)if((t=r.charCodeAt(o))<128)e+=String.fromCharCode(t),o++;else if(t>191&&t<224)e+=String.fromCharCode((31&t)<<6|63&r.charCodeAt(o+1)),o+=2;else if(t>223&&t<240)e+=String.fromCharCode((15&t)<<12|(63&r.charCodeAt(o+1))<<6|63&r.charCodeAt(o+2)),o+=3;else{var i=(7&t)<<18|(63&r.charCodeAt(o+1))<<12|(63&r.charCodeAt(o+2))<<6|63&r.charCodeAt(o+3);e+=String.fromCharCode(55296+(i-65536>>10))+String.fromCharCode(56320+(i-65536&1023)),o+=4}return e}}},this),function(r,e,o,t){if(void 0!==o.module&&o.module.exports){if(t&&o.require)for(var i=0;i<t.length;i++)o[t[i]]=o.require(t[i]);o.module.exports=e.apply(o)}else void 0!==o.define&&"function"===o.define&&o.define.amd?define("base64",t||[],e):o.base64=e.apply(o)}(0,function(r){var e=r||this.utf8,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=e.encode(r);for(var t,i,n,d,f,a,h,C="",c=0;c<r.length;)d=(t=r.charCodeAt(c++))>>2,f=(3&t)<<4|(i=r.charCodeAt(c++))>>4,a=(15&i)<<2|(n=r.charCodeAt(c++))>>6,h=63&n,isNaN(i)?a=h=64:isNaN(n)&&(h=64),C+=o.charAt(d)+o.charAt(f)+o.charAt(a)+o.charAt(h);return C},decode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,i,n,d,f,a,h="",C=0;C<r.length;)t=o.indexOf(r.charAt(C++))<<2|(d=o.indexOf(r.charAt(C++)))>>4,i=(15&d)<<4|(f=o.indexOf(r.charAt(C++)))>>2,n=(3&f)<<6|(a=o.indexOf(r.charAt(C++))),h+=String.fromCharCode(t),64!=f&&(h+=String.fromCharCode(i)),64!=a&&(h+=String.fromCharCode(n));return e.decode(h)}}},this,["utf8"]);

View File

@@ -0,0 +1,14 @@
{
"tiddlers": [
{
"file": "base64-utf8.module.min.js",
"fields": {
"type": "application/javascript",
"title": "$:/core/modules/utils/base64-utf8/base64-utf8.module.js",
"module-type": "library"
},
"prefix": "(function(){",
"suffix": "}).call(exports);"
}
]
}

View File

@@ -24,26 +24,6 @@ exports.setStyle = function(element,styles) {
}
};
/*
Remove style properties of an element
element: dom node
styleProperties: ordered array of string property names
*/
exports.removeStyles = function(element, styleProperties) {
for (var i=0; i<styleProperties.length; i++) {
element.style.removeProperty($tw.utils.convertStyleNameToPropertyName(styleProperties[i]));
}
}
/*
Remove single style property of an element
element: dom node
styleProperty: string property name
*/
exports.removeStyle = function(element, styleProperty) {
$tw.utils.removeStyles(element, [styleProperty])
}
/*
Converts a standard CSS property name into the local browser-specific equivalent. For example:
"background-color" --> "backgroundColor"

View File

@@ -210,7 +210,7 @@ Modal.prototype.display = function(title,options) {
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" || tiddler.fields["mask-closable"] === "" || "mask-closable" in tiddler.fields === false)) {
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

View File

@@ -1,126 +0,0 @@
/*\
title: $:/core/modules/utils/messaging.js
type: application/javascript
module-type: utils-browser
Messaging utilities for use with window.postMessage() etc.
This module intentionally has no dependencies so that it can be included in non-TiddlyWiki projects
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var RESPONSE_TIMEOUT = 2 * 1000;
/*
Class to handle subscribing to publishers
target: Target window (eg iframe.contentWindow)
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
onsubscribe: Function to be invoked with err parameter when the subscription is established, or there is a timeout
onmessage: Function to be invoked when a new message arrives, invoked with (data,callback). The callback is invoked with the argument (response)
*/
function BrowserMessagingSubscriber(options) {
var self = this;
this.target = options.target;
this.type = options.type;
this.onsubscribe = options.onsubscribe || function() {};
this.onmessage = options.onmessage;
this.hasConfirmed = false;
this.channel = new MessageChannel();
this.channel.port1.addEventListener("message",function(event) {
if(this.timerID) {
clearTimeout(this.timerID);
this.timerID = null;
}
if(event.data) {
if(event.data.verb === "SUBSCRIBED") {
self.hasConfirmed = true;
self.onsubscribe(null);
} else if(event.data.verb === self.type) {
self.onmessage(event.data,function(response) {
// Send the response back on the supplied port, and then close it
event.ports[0].postMessage(response);
event.ports[0].close();
});
}
}
});
// Set a timer so that if we don't hear from the iframe before a timeout we alert the user
this.timerID = setTimeout(function() {
if(!self.hasConfirmed) {
self.onsubscribe("NO_RESPONSE");
}
},RESPONSE_TIMEOUT);
this.channel.port1.start();
this.target.postMessage({verb: "SUBSCRIBE",to: self.type},"*",[this.channel.port2]);
}
exports.BrowserMessagingSubscriber = BrowserMessagingSubscriber;
/*
Class to handle publishing subscriptions
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
onsubscribe: Function to be invoked when a subscription occurs
*/
function BrowserMessagingPublisher(options) {
var self = this;
this.type = options.type;
this.hostIsListening = false;
this.port = null;
// Listen to connection requests from the host
window.addEventListener("message",function(event) {
if(event.data && event.data.verb === "SUBSCRIBE" && event.data.to === self.type) {
self.hostIsListening = true;
// Acknowledge
self.port = event.ports[0];
self.port.postMessage({verb: "SUBSCRIBED", to: self.type});
if(options.onsubscribe) {
options.onsubscribe(event.data);
}
}
});
}
BrowserMessagingPublisher.prototype.canSend = function() {
return !!this.hostIsListening && !!this.port;
};
BrowserMessagingPublisher.prototype.send = function(data,callback) {
var self = this;
callback = callback || function() {};
// Check that we've been initialised by the host
if(!this.hostIsListening || !this.port) {
return false;
}
// Create a channel for the confirmation
var channel = new MessageChannel();
channel.port1.addEventListener("message",function(event) {
if(event.data && event.data.verb === "OK") {
callback(null);
} else {
callback("BrowserMessagingPublisher for " + self.type + " error: " + (event.data || {}).verb);
}
channel.port1.close();
});
channel.port1.start();
// Send the save request with the port for the response
this.port.postMessage(data,[channel.port2]);
};
BrowserMessagingPublisher.prototype.close = function() {
if(this.port) {
this.port.close();
this.hostIsListening = false;
this.port = null;
}
};
exports.BrowserMessagingPublisher = BrowserMessagingPublisher;
})();

View File

@@ -9,6 +9,8 @@ Various static utility functions.
"use strict";
var base64utf8 = require("$:/core/modules/utils/base64-utf8/base64-utf8.module.js");
/*
Display a message, in colour if we're on a terminal
*/
@@ -840,50 +842,22 @@ if(typeof window !== 'undefined') {
}
}
exports.base64ToBytes = function(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0));
};
exports.bytesToBase64 = function(bytes) {
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = function(str) {
if ($tw.browser) {
return exports.bytesToBase64(new TextEncoder().encode(str));
} else {
const buff = Buffer.from(str, "utf-8");
return buff.toString("base64");
}
};
exports.base64DecodeUtf8 = function(str) {
if ($tw.browser) {
return new TextDecoder().decode(exports.base64ToBytes(str));
} else {
const buff = Buffer.from(str, "base64");
return buff.toString("utf-8");
}
};
/*
Decode a base64 string
*/
exports.base64Decode = function(string64,binary,urlsafe) {
const encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
var encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
if(binary) return exports.atob(encoded)
else return exports.base64DecodeUtf8(encoded);
else return base64utf8.base64.decode.call(base64utf8,encoded);
};
/*
Encode a string to base64
*/
exports.base64Encode = function(string64,binary,urlsafe) {
let encoded;
var encoded;
if(binary) encoded = exports.btoa(string64);
else encoded = exports.base64EncodeUtf8(string64);
else encoded = base64utf8.base64.encode.call(base64utf8,string64);
if(urlsafe) {
encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_');
}
@@ -1049,7 +1023,7 @@ exports.makeCompareFunction = function(type,options) {
return compare(dateA,dateB);
},
"version": function(a,b) {
return compare($tw.utils.compareVersions(a,b),0);
return $tw.utils.compareVersions(a,b);
},
"alphanumeric": function(a,b) {
if(!isCaseSensitive) {

View File

@@ -1,103 +0,0 @@
/*\
title: $:/core/modules/widgets/audio.js
type: application/javascript
module-type: widget
Basic Audio widget for displaying audio files.
This is a simple implementation that can be overridden by plugins
for more advanced functionality.
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var AudioWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
AudioWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
AudioWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
// Create audio element
var audioElement = this.document.createElement("audio");
audioElement.setAttribute("controls", this.getAttribute("controls", "controls"));
audioElement.setAttribute("style", this.getAttribute("style", "width: 100%; object-fit: contain"));
audioElement.className = "tw-audio-element";
// Set source
if(this.audioSource) {
if (this.audioSource.indexOf("data:") === 0) {
audioElement.setAttribute("src", this.audioSource);
} else {
var sourceElement = this.document.createElement("source");
sourceElement.setAttribute("src", this.audioSource);
if(this.audioType) {
sourceElement.setAttribute("type", this.audioType);
}
audioElement.appendChild(sourceElement);
}
}
// Insert the audio into the DOM
parent.insertBefore(audioElement, nextSibling);
this.domNodes.push(audioElement);
};
/*
Compute the internal state of the widget
*/
AudioWidget.prototype.execute = function() {
// Get the audio source and type
this.audioSource = this.getAttribute("src");
this.audioType = this.getAttribute("type");
this.audioControls = this.getAttribute("controls", "controls");
// Try to get from tiddler attribute
if(!this.audioSource && this.getAttribute("tiddler")) {
var tiddlerTitle = this.getAttribute("tiddler");
var tiddler = this.wiki.getTiddler(tiddlerTitle);
if(tiddler) {
if(tiddler.fields._canonical_uri) {
this.audioSource = tiddler.fields._canonical_uri;
this.audioType = tiddler.fields.type;
} else if(tiddler.fields.text) {
this.audioSource = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
this.audioType = tiddler.fields.type;
}
}
}
// Make sure we have a tiddler for saving timestamps
this.tiddlerTitle = this.getAttribute("tiddler");
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
AudioWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.src || changedAttributes.type || changedAttributes.controls || changedAttributes.tiddler) {
this.refreshSelf();
return true;
} else {
return false;
}
};
exports.audio = AudioWidget;

View File

@@ -61,10 +61,6 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
sourcePrefix: "data-",
destPrefix: "data-"
});
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
// Assign other attributes
if(this.style) {
domNode.setAttribute("style",this.style);
@@ -72,6 +68,9 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
if(this.tooltip) {
domNode.setAttribute("title",this.tooltip);
}
if(this["aria-label"]) {
domNode.setAttribute("aria-label",this["aria-label"]);
}
if (this.role) {
domNode.setAttribute("role", this.role);
}
@@ -216,6 +215,7 @@ ButtonWidget.prototype.execute = function() {
this.setTo = this.getAttribute("setTo");
this.popup = this.getAttribute("popup");
this.hover = this.getAttribute("hover");
this["aria-label"] = this.getAttribute("aria-label");
this.role = this.getAttribute("role");
this.tooltip = this.getAttribute("tooltip");
this.style = this.getAttribute("style");
@@ -271,10 +271,6 @@ ButtonWidget.prototype.refresh = function(changedTiddlers) {
sourcePrefix: "data-",
destPrefix: "data-"
});
this.assignAttributes(this.domNodes[0],{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
}
return this.refreshChildren(changedTiddlers);
};

View File

@@ -74,8 +74,6 @@ ElementWidget.prototype.render = function(parent,nextSibling) {
// Create the DOM node and render children
var domNode = this.document.createElementNS(this.namespace,this.tag);
this.assignAttributes(domNode,{excludeEventAttributes: true});
// Allow hooks to manipulate the DOM node. Eg: Add debug info
$tw.hooks.invokeHook("th-dom-rendering-element", domNode, this);
parent.insertBefore(domNode,nextSibling);
this.renderChildren(domNode,null);
this.domNodes.push(domNode);

View File

@@ -44,7 +44,7 @@ EventWidget.prototype.render = function(parent,nextSibling) {
domNode.addEventListener(type,function(event) {
var selector = self.getAttribute("selector"),
matchSelector = self.getAttribute("matchSelector"),
actions = self.getAttribute("$"+type),
actions = self.getAttribute("$"+type) || self.getAttribute("actions-"+type),
stopPropagation = self.getAttribute("stopPropagation","onaction"),
selectedNode = event.target,
selectedNodeRect,
@@ -122,6 +122,9 @@ EventWidget.prototype.execute = function() {
self.types.push(key.slice(1));
}
});
if(!this.types.length) {
this.types = this.getAttribute("events","").split(" ");
}
this.elementTag = this.getAttribute("tag");
// Make child widgets
this.makeChildWidgets();

View File

@@ -45,10 +45,6 @@ LinkWidget.prototype.render = function(parent,nextSibling) {
sourcePrefix: "data-",
destPrefix: "data-"
});
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
parent.insertBefore(domNode,nextSibling);
this.renderChildren(domNode,null);
this.domNodes.push(domNode);
@@ -129,13 +125,9 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
});
domNode.setAttribute("title",tooltipText);
}
if(this.role) {
domNode.setAttribute("role",this.role);
if(this["aria-label"]) {
domNode.setAttribute("aria-label",this["aria-label"]);
}
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
})
// Add a click event handler
$tw.utils.addEventListeners(domNode,[
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
@@ -147,8 +139,6 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
dragTiddlerFn: function() {return self.to;},
widget: this
});
} else if(this.draggable === "no") {
domNode.setAttribute("draggable","false");
}
// Assign data- attributes
this.assignAttributes(domNode,{
@@ -198,7 +188,7 @@ LinkWidget.prototype.execute = function() {
// Pick up our attributes
this.to = this.getAttribute("to",this.getVariable("currentTiddler"));
this.tooltip = this.getAttribute("tooltip");
this.role = this.getAttribute("role");
this["aria-label"] = this.getAttribute("aria-label");
this.linkClasses = this.getAttribute("class");
this.overrideClasses = this.getAttribute("overrideClass");
this.tabIndex = this.getAttribute("tabindex");

View File

@@ -94,6 +94,8 @@ RangeWidget.prototype.getActionVariables = function(options) {
// actionsStart
RangeWidget.prototype.handleMouseDownEvent = function(event) {
this.mouseDown = true; // TODO remove once IE is gone.
this.startValue = this.inputDomNode.value; // TODO remove this line once IE is gone!
this.handleEvent(event);
// Trigger actions
if(this.actionsMouseDown) {
@@ -104,16 +106,26 @@ RangeWidget.prototype.handleMouseDownEvent = function(event) {
// actionsStop
RangeWidget.prototype.handleMouseUpEvent = function(event) {
this.mouseDown = false; // TODO remove once IE is gone.
this.handleEvent(event);
// Trigger actions
if(this.actionsMouseUp) {
var variables = this.getActionVariables()
this.invokeActionString(this.actionsMouseUp,this,event,variables);
}
// TODO remove the following if() once IE is gone!
if ($tw.browser.isIE) {
if (this.startValue !== this.inputDomNode.value) {
this.handleChangeEvent(event);
this.startValue = this.inputDomNode.value;
}
}
}
RangeWidget.prototype.handleChangeEvent = function(event) {
this.handleInputEvent(event);
if (this.mouseDown) { // TODO refactor this function once IE is gone.
this.handleInputEvent(event);
}
};
RangeWidget.prototype.handleInputEvent = function(event) {
@@ -140,6 +152,8 @@ RangeWidget.prototype.handleEvent = function(event) {
Compute the internal state of the widget
*/
RangeWidget.prototype.execute = function() {
// TODO remove the next 1 lines once IE is gone!
this.mouseUp = true; // Needed for IE10
// Get the parameters from the attributes
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.tiddlerField = this.getAttribute("field","text");

View File

@@ -1443,7 +1443,7 @@ exports.search = function(text,options) {
// Don't search the text field if the content type is binary
var fieldName = searchFields[fieldIndex];
if(fieldName === "text" && contentTypeInfo.encoding !== "utf8") {
continue;
break;
}
var str = tiddler.fields[fieldName],
t;

View File

@@ -47,7 +47,6 @@ modal-footer-background: #f5f5f5
modal-footer-border: #dddddd
modal-header-border: #eeeeee
muted-foreground: #bbb
network-activity-foreground: <<colour primary>>
notification-background: #ffffdd
notification-border: #999999
page-background: #6f6f70
@@ -57,26 +56,22 @@ primary: #29a6ee
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #222222
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #c2c1c2
sidebar-foreground-shadow: transparent
sidebar-foreground-shadow: rgba(255,255,255,0)
sidebar-foreground: #d3d2d4
sidebar-muted-foreground-hover: #333333
sidebar-muted-foreground-hover: #444444
sidebar-muted-foreground: #c0c0c0
sidebar-tab-background-selected: #6f6f70
sidebar-tab-background: #666667
sidebar-tab-border-selected: #999
sidebar-tab-border: #515151
sidebar-tab-divider: #999
sidebar-tab-foreground-selected: #bfbfbf
sidebar-tab-foreground: #b0b0b0
sidebar-tab-foreground-selected:
sidebar-tab-foreground: #999
sidebar-tiddler-link-foreground-hover: #444444
sidebar-tiddler-link-foreground: #aaaaaa
sidebar-tiddler-link-foreground: #d1d0d2
site-title-foreground: <<colour tiddler-title-foreground>>
stability-deprecated: #bf616a
stability-experimental: #d08770
stability-legacy: #88c0d0
stability-stable: #a3be8c
static-alert-foreground: #aaaaaa
tab-background-selected: #ffffff
tab-background: #d8d8d8

View File

@@ -1,13 +1,12 @@
title: $:/core/templates/tiddlywiki5-external-js.html
<$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] +[[$:/core]plugintiddlers[]]">
`<!doctype html>
`{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--~~ Raw markup for the top of the head section ~~-->
`{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}`
`{{{ [enlist<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" />
@@ -23,13 +22,13 @@ title: $:/core/templates/tiddlywiki5-external-js.html
<!--~~ This is a Tiddlywiki file. The points of interest in the file are marked with this pattern ~~-->
<!--~~ Raw markup ~~-->
`{{{ [enlist<rawMarkupFilter>tag[$:/core/wiki/rawmarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/core/wiki/rawmarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}`
</head>
<body class="tc-body">
<!--~~ Raw markup for the top of the body section ~~-->
`{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/TopBody]] ||$:/core/templates/raw-static-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/TopBody]] ||$:/core/templates/raw-static-tiddler}}}`
<!--~~ Static styles ~~-->
<div id="styleArea">
`{{$:/boot/boot.css||$:/core/templates/css-tiddler}}`
@@ -43,10 +42,9 @@ title: $:/core/templates/tiddlywiki5-external-js.html
<!--~~ Ordinary tiddlers ~~-->
`{{$:/core/templates/store.area.template.html}}`
<!--~~ Raw markup for the bottom of the body section ~~-->
`{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}`
<!--~~ Load external JavaScripts ~~-->
<script src="`{{{ [<coreURL>] }}}`" onerror="alert('Error: Cannot load `{{{ [<coreURL>] }}}`');"></script>
</body>
</html>`
</$set>
</$set>

View File

@@ -62,34 +62,28 @@ caption: {{$:/language/Search/Filter/Caption}}
</$list>
\end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim
<<lingo Filter/Hint>>
<div class="tc-search tc-advanced-search">
<$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>> class="tc-small-gap-right">
<$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch"
refreshTitle="$:/temp/advancedsearch/refresh"
selectionStateTitle="$:/temp/advancedsearch/selected-item"
type="search"
tag="input"
focus={{$:/config/Search/AutoFocus}}
configTiddlerFilter="[[$:/temp/advancedsearch]]"
firstSearchFilterField="text"
inputAcceptActions=<<input-accept-actions>>
inputAcceptVariantActions=<<input-accept-variant-actions>>
inputCancelActions=<<cancel-search-actions>>
/>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>> class="tc-small-gap-right">
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch"
refreshTitle="$:/temp/advancedsearch/refresh"
selectionStateTitle="$:/temp/advancedsearch/selected-item"
type="search"
tag="input"
focus={{$:/config/Search/AutoFocus}}
configTiddlerFilter="[[$:/temp/advancedsearch]]"
firstSearchFilterField="text"
inputAcceptActions=<<input-accept-actions>>
inputAcceptVariantActions=<<input-accept-variant-actions>>
inputCancelActions=<<cancel-search-actions>>
/>
</$keyboard>
</$keyboard>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterButton]!has[draft.of]]">
<$transclude/>

View File

@@ -53,20 +53,13 @@ first-search-filter: [all[shadows]search<userInput>sort[title]limit[250]] -[[$:/
</$list></$list>
\end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim
<<lingo Shadows/Hint>>
<div class="tc-search">
<$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>>>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch"
@@ -81,6 +74,7 @@ first-search-filter: [all[shadows]search<userInput>sort[title]limit[250]] -[[$:/
inputAcceptVariantActions=<<input-accept-variant-actions>>
filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard>
</$keyboard>
&#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible">

View File

@@ -17,24 +17,15 @@ caption: {{$:/language/Search/Standard/Caption}}
\procedure input-accept-variant-actions() <$list filter="[{$:/config/Search/NavigateOnEnter/enable}match[yes]]" emptyMessage="<$list filter='[<tiddler>get[text]!is[missing]] :else[<tiddler>get[text]is[shadow]]'><$list filter='[<tiddler>get[text]minlength[1]]'><$action-sendmessage $message='tm-edit-tiddler' $param={{{ [<tiddler>get[text]] }}}/></$list></$list>"><$list filter="[<tiddler>get[text]minlength[1]]"><$action-sendmessage $message="tm-edit-tiddler" $param={{{ [<tiddler>get[text]] }}}/></$list></$list>
\procedure input-actions()
<%if [<event-code>match[ArrowRight]] :and[<modifier>match[alt-shift]] %>
<<next-search-tab>>
<%elseif [<event-code>match[ArrowLeft]] :and[<modifier>match[alt-shift]] %>
<<previous-search-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim
<<lingo Standard/Hint>>
<div class="tc-search">
<$keyboard key="((input-tab-right)) ((input-tab-left)) shift-alt-Right shift-alt-Left" actions=<<input-actions>>>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$keyboard key="shift-alt-Right" actions=<<next-search-tab>>>
<$keyboard key="shift-alt-Left" actions=<<previous-search-tab>>>
<$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch"
@@ -49,6 +40,9 @@ caption: {{$:/language/Search/Standard/Caption}}
configTiddlerFilter="[[$:/state/advancedsearch/standard/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]"
filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard>
</$keyboard>
</$keyboard>
</$keyboard>
&#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible">

View File

@@ -52,20 +52,13 @@ first-search-filter: [is[system]search<userInput>sort[title]limit[250]] :except[
</$list></$list>
\end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim
<<lingo System/Hint>>
<div class="tc-search">
<$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>>>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch"
@@ -80,6 +73,7 @@ first-search-filter: [is[system]search<userInput>sort[title]limit[250]] :except[
inputAcceptVariantActions=<<input-accept-variant-actions>>
filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard>
</$keyboard>
&#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible">

View File

@@ -2,9 +2,9 @@ code-body: yes
title: $:/core/ui/AlertTemplate
\whitespace trim
<div class="tc-alert" role="alertdialog">
<div class="tc-alert">
<div class="tc-alert-toolbar">
<$button class="tc-btn-invisible" aria-label={{$:/language/Buttons/Close/Caption}}><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/cancel-button}}</$button>
<$button class="tc-btn-invisible"><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/cancel-button}}</$button>
</div>
<div class="tc-alert-subtitle">
<$wikify name="format" text=<<lingo Tiddler/DateFormat>>>
@@ -19,7 +19,7 @@ title: $:/core/ui/AlertTemplate
</$reveal>
</$wikify>
</div>
<div class="tc-alert-body" role="alert" aria-atomic="true">
<div class="tc-alert-body">
<$transclude/>

View File

@@ -17,11 +17,13 @@ tags: $:/tags/EditTemplate
<$let backgroundColor=<<colour>> >
<span class="tc-tag-label tc-tag-list-item tc-small-gap-right"
data-tag-title=<<currentTiddler>>
style=`color:$(foregroundColor)$; background-color:$(backgroundColor)$;`
style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;`
>
<$transclude tiddler=<<icon>>/>
<$view field="title" format="text"/>
<$button class="tc-btn-invisible tc-remove-tag-button">
<$button class="tc-btn-invisible tc-remove-tag-button"
style.fill=<<foregroundColor>>
>
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/>
{{$:/core/images/close-button}}
</$button>

View File

@@ -10,7 +10,7 @@ first-search-filter: [all[shadows+tiddlers]prefix[$:/language/Docs/Types/]sort[d
<em class="tc-edit tc-small-gap-right"><<lingo Type/Prompt>></em>
<div class="tc-type-selector-dropdown-wrapper">
<div class="tc-type-selector"><$fieldmangler>
<$transclude $variable="keyboard-driven-input" tiddler=<<currentTiddler>> storeTitle=<<typeInputTiddler>> refreshTitle=<<refreshTitle>> selectionStateTitle=<<typeSelectionTiddler>> field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-edit-texteditor tc-popup-handle tc-keep-focus" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] :else[[false]] }}} cancelPopups="yes" configTiddlerFilter="[[$:/core/ui/EditTemplate/type]]" inputCancelActions=<<input-cancel-actions>>/><$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown tc-small-gap" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button><$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}<$action-deletetiddler $filter="[<typeInputTiddler>] [<storeTitle>] [<refreshTitle>] [<selectionStateTitle>]"/></$button>
<$transclude $variable="keyboard-driven-input" tiddler=<<currentTiddler>> storeTitle=<<typeInputTiddler>> refreshTitle=<<refreshTitle>> selectionStateTitle=<<typeSelectionTiddler>> field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-edit-texteditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] :else[[false]] }}} cancelPopups="yes" configTiddlerFilter="[[$:/core/ui/EditTemplate/type]]" inputCancelActions=<<input-cancel-actions>>/><$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown tc-small-gap" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button><$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}<$action-deletetiddler $filter="[<typeInputTiddler>] [<storeTitle>] [<refreshTitle>] [<selectionStateTitle>]"/></$button>
</$fieldmangler></div>
<div class="tc-block-dropdown-wrapper">

View File

@@ -37,6 +37,8 @@ title: $:/core/ui/ImportListing
\define suppressedField() suppressed-$(payloadTiddler)$
\define newImportTitleTiddler() $:/temp/NewImportTitle-$(payloadTiddler)$
\define previewPopupState() $(currentTiddler)$!!popup-$(payloadTiddler)$
\define renameFieldState() $(currentTiddler)$!!state-rename-$(payloadTiddler)$
@@ -100,7 +102,19 @@ title: $:/core/ui/ImportListing
<$reveal type="match" state=<<renameFieldState>> text="yes" tag="tr">
<td colspan="3">
<div class="tc-flex">
<$edit-text field=<<renameField>> default={{{[<payloadTiddler>]}}} tag="input" class="tc-import-rename tc-flex-grow-1"/>
<$edit-text tiddler=<<newImportTitleTiddler>> default={{{[subfilter<payloadTitleFilter>]}}} tag="input" class="tc-import-rename tc-flex-grow-1"/>
<span class="tc-small-gap-left">
<$button class="tc-btn-invisible" set=<<renameFieldState>> setTo="no" tooltip={{{[<lingo-base>addsuffix[Listing/Rename/CancelRename]get[text]]}}}>
{{$:/core/images/close-button}}
<$action-deletetiddler $tiddler=<<newImportTitleTiddler>>/>
</$button>
<span class="tc-small-gap-right"/>
</span>
<$button class="tc-btn-invisible" set=<<renameFieldState>> setTo="no" tooltip={{{[<lingo-base>addsuffix[Listing/Rename/ConfirmRename]get[text]]}}}>
{{$:/core/images/done-button}}
<$action-setfield $field=<<renameField>> $value={{{[<newImportTitleTiddler>get[text]minlength[1]else<payloadTiddler>]}}} />
<$action-deletetiddler $tiddler=<<newImportTitleTiddler>>/>
</$button>
</div>
</td>
</$reveal>

View File

@@ -1,8 +1,8 @@
title: $:/core/ui/PageTemplate/alerts
tags: $:/tags/PageTemplate
<div class="tc-alerts" role="region" aria-label="Alerts">
<div class="tc-alerts">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Alert]!is[draft]]" template="$:/core/ui/AlertTemplate" storyview="pop"/>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Alert]!has[draft.of]]" template="$:/core/ui/AlertTemplate" storyview="pop"/>
</div>

View File

@@ -46,21 +46,13 @@ tags: $:/tags/SideBarSegment
\procedure advanced-search-actions() <$action-setfield $tiddler="$:/temp/advancedsearch" text={{$:/temp/search/input}}/><$action-setfield $tiddler="$:/temp/advancedsearch/input" text={{$:/temp/search/input}}/><<delete-state-tiddlers>><$action-navigate $to="$:/AdvancedSearch"/><$action-setfield $tiddler="$:/temp/advancedsearch/refresh" text="yes"/><$action-sendmessage $message="tm-focus-selector" $param="""[data-tiddler-title="$:/AdvancedSearch"] .tc-search input""" preventScroll="true"/><$action-deletetiddler $filter="$:/temp/search $:/temp/search/input $:/temp/search/refresh [<searchListState>]"/>
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%elseif [<event-key-descriptor>match[((advanced-search-sidebar))]] %>
<<advanced-search-actions>>
<%endif%>
\end
<div class="tc-sidebar-lists tc-sidebar-search">
<$vars editTiddler="$:/temp/search" searchTiddler="$:/temp/search/input" searchListState=<<qualify "$:/state/search-list/selected-item">>>
<div class="tc-search">
<$keyboard key="((input-tab-right)) ((input-tab-left)) ((advanced-search-sidebar))" actions=<<input-actions>>>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$keyboard key="((advanced-search-sidebar))" actions=<<advanced-search-actions>>>
<form class="tc-form-inline">
<$transclude $variable="keyboard-driven-input" tiddler=<<editTiddler>> storeTitle=<<searchTiddler>>
selectionStateTitle=<<searchListState>> refreshTitle="$:/temp/search/refresh" type="search"
@@ -70,6 +62,8 @@ tags: $:/tags/SideBarSegment
configTiddlerFilter="[[$:/state/search/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]"/>
</form>
</$keyboard>
</$keyboard>
</$keyboard>
<$reveal state=<<searchTiddler>> type="nomatch" text="">
<$button tooltip={{$:/language/Buttons/AdvancedSearch/Hint}} aria-label={{$:/language/Buttons/AdvancedSearch/Caption}} class="tc-btn-invisible">
<<advanced-search-actions>>

View File

@@ -2,7 +2,7 @@ title: $:/core/ui/ViewTemplate/title
tags: $:/tags/ViewTemplate
\whitespace trim
\define title-styles() color:$(foregroundColor)$;
\define title-styles() fill:$(foregroundColor)$;
<div class="tc-tiddler-title tc-clearfix">
<div class="tc-titlebar">

View File

@@ -1,2 +1,2 @@
title: $:/config/TiddlerInfo/Mode
text: sticky
text: popup

View File

@@ -120,21 +120,11 @@ tags: $:/tags/Macro
\procedure keyboard-driven-input(tiddler,storeTitle,field:"text",index:"",tag:"input",type,focus:"",inputAcceptActions,inputAcceptVariantActions,inputCancelActions,placeholder:"",default:"",class,focusPopup,rows,minHeight,tabindex,size,autoHeight,filterMinLength:"0",refreshTitle,selectionStateTitle,cancelPopups:"",configTiddlerFilter,firstSearchFilterField:"first-search-filter",secondSearchFilterField:"second-search-filter")
\whitespace trim
\procedure keyboard-driven-input-actions()
<%if [<event-key-descriptor>match[((input-accept))]] %>
<<inputAcceptActions>>
<%elseif [<event-key-descriptor>match[((input-accept-variant))]] %>
<<inputAcceptVariantActions>>
<%elseif [<event-key-descriptor>match[((input-up))]] %>
<<input-next-actions-before>>
<%elseif [<event-key-descriptor>match[((input-down))]] %>
<<input-next-actions-after>>
<%elseif [<event-key-descriptor>match[((input-cancel))]] %>
<<inputCancelActions>>
<%endif%>
\end keyboard-driven-input-actions
<$keyboard key="((input-accept)) ((input-accept-variant)) ((input-up)) ((input-down)) ((input-cancel))" actions=<<keyboard-driven-input-actions>>>
<$keyboard key="((input-accept))" actions=<<inputAcceptActions>>>
<$keyboard key="((input-accept-variant))" actions=<<inputAcceptVariantActions>>>
<$keyboard key="((input-up))" actions=<<input-next-actions-before>>>
<$keyboard key="((input-down))" actions=<<input-next-actions-after>>>
<$keyboard key="((input-cancel))" actions=<<inputCancelActions>>>
<$edit-text
tiddler=<<tiddler>> field=<<field>> index=<<index>>
inputActions=<<keyboard-input-actions>> tag=<<tag>> class=<<class>>
@@ -144,4 +134,8 @@ tags: $:/tags/Macro
refreshTitle=<<refreshTitle>> cancelPopups=<<cancelPopups>>
/>
</$keyboard>
\end keyboard-driven-input
</$keyboard>
</$keyboard>
</$keyboard>
</$keyboard>
\end

View File

@@ -24,49 +24,44 @@ tags: $:/tags/Macro
\define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate)
\whitespace trim
<$set name="_tiddler" value="""$tiddler$""" emptyValue=<<currentTiddler>> >
<$let field-reference={{{ [<_tiddler>] "!!" [[$field$]] +[join[]] }}}
targetTiddler=<<_tiddler>>
targetField="""$field$"""
>
<span class="tc-links-draggable-list">
<$genesis $type=<<__type__>> class="$class$">
<$list filter="[list<field-reference>]" emptyMessage=<<__emptyMessage__>>>
<$droppable
actions=<<list-links-draggable-drop-actions>>
tag="""$subtype$"""
enable=<<tv-enable-drag-and-drop>>
>
<div class="tc-droppable-placeholder"/>
<div>
<$transclude tiddler="""$itemTemplate$""">
<$link to={{!!title}}>
<$let tv-wikilinks="no">
<$transclude field="caption">
<$view field="title"/>
</$transclude>
</$let>
</$link>
</$transclude>
</div>
</$droppable>
</$list>
<$tiddler tiddler="">
<$droppable
actions=<<list-links-draggable-drop-actions>>
tag="div"
enable=<<tv-enable-drag-and-drop>>
>
<div class="tc-droppable-placeholder">
{{$:/core/images/blank}}
</div>
<div style="height:0.5em;"/>
</$droppable>
</$tiddler>
</$genesis>
</span>
</$let>
</$set>
<span class="tc-links-draggable-list">
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
<$genesis $type=<<__type__>> class="$class$">
<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>>
<$droppable
actions=<<list-links-draggable-drop-actions>>
tag="""$subtype$"""
enable=<<tv-enable-drag-and-drop>>
>
<div class="tc-droppable-placeholder"/>
<div>
<$transclude tiddler="""$itemTemplate$""">
<$link to={{!!title}}>
<$let tv-wikilinks="no">
<$transclude field="caption">
<$view field="title"/>
</$transclude>
</$let>
</$link>
</$transclude>
</div>
</$droppable>
</$list>
<$tiddler tiddler="">
<$droppable
actions=<<list-links-draggable-drop-actions>>
tag="div"
enable=<<tv-enable-drag-and-drop>>
>
<div class="tc-droppable-placeholder">
{{$:/core/images/blank}}
</div>
<div style="height:0.5em;"/>
</$droppable>
</$tiddler>
</$genesis>
</$vars>
</span>
\end
\define list-tagged-draggable-drop-actions(tag)

View File

@@ -3,6 +3,7 @@ tags: $:/tags/Macro
\define tag-pill-styles()
background-color:$(backgroundColor)$;
fill:$(foregroundColor)$;
color:$(foregroundColor)$;
\end

View File

@@ -21,7 +21,7 @@ tags: $:/tags/Macro
style="width:$width$px;height:$height$px;background-color:$background-color$;"
></$reveal></div><div
class="tc-thumbnail-icon"
style="color:$color$;"
style="fill:$color$;color:$color$;"
>$icon$</div><div class="tc-thumbnail-caption">$caption$</div></div></$link>
\end

View File

@@ -0,0 +1,44 @@
title: CloudData
type: application/json
[
{"text": "Tokyo/Yokohama", "size": 33.200},
{"text": "New York Metro", "size": 17.800},
{"text": "Sao Paulo", "size": 17.700},
{"text": "Seoul/Incheon", "size": 17.500},
{"text": "Mexico City", "size": 17.400},
{"text": "Osaka/Kobe/Kyoto", "size": 16.425},
{"text": "Manila", "size": 14.750},
{"text": "Mumbai", "size": 14.350},
{"text": "Delhi", "size": 14.300},
{"text": "Jakarta", "size": 14.250},
{"text": "Lagos", "size": 13.400},
{"text": "Kolkata", "size": 12.700},
{"text": "Cairo", "size": 12.200},
{"text": "Los Angeles", "size": 11.789},
{"text": "Buenos Aires", "size": 11.200},
{"text": "Rio de Janeiro", "size": 10.800},
{"text": "Moscow", "size": 10.500},
{"text": "Shanghai", "size": 10.000},
{"text": "Karachi", "size": 9.800},
{"text": "Paris", "size": 9.645},
{"text": "Istanbul", "size": 9.000},
{"text": "Nagoya", "size": 9.000},
{"text": "Beijing", "size": 8.614},
{"text": "Chicago", "size": 8.308},
{"text": "London", "size": 8.278},
{"text": "Shenzhen", "size": 8.000},
{"text": "Essen/Dusseldorf", "size": 7.350},
{"text": "Tehran", "size": 7.250},
{"text": "Bogota", "size": 7.000},
{"text": "Lima", "size": 7.000},
{"text": "Bangkok", "size": 6.500},
{"text": "Johannesburg/East Rand", "size": 6.000},
{"text": "Chennai", "size": 5.950},
{"text": "Taipei", "size": 5.700},
{"text": "Baghdad", "size": 5.500},
{"text": "Santiago", "size": 5.425},
{"text": "Bangalore", "size": 5.400},
{"text": "Hyderabad", "size": 5.300},
{"text": "St Petersburg", "size": 5.300}
]

View File

@@ -0,0 +1,3 @@
title: $:/DefaultTiddlers
[[HelloThere]]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
title: HelloThere
This is a demo of TiddlyWiki5 incorporating a plugin for the [[D3.js]] visualisation library.
! Word Cloud
<$d3cloud data="CloudData" spiral={{$:/spiral}}/>
//[[Raw data|CloudData]]//
! Bar Chart
<$d3bar grouped={{$:/grouped}} data="GraphData"/>
<$button set="$:/grouped" setTo="yes">grouped</$button> <$button set="$:/grouped" setTo="no">stacked</$button>
//[[Raw data|GraphData]]//

View File

@@ -0,0 +1,3 @@
title: $:/SiteSubtitle
a demo of the D3.js plugin for TiddlyWiki5

View File

@@ -0,0 +1,3 @@
title: $:/SiteTitle
d3demo

View File

@@ -0,0 +1,3 @@
title: $:/grouped
yes

View File

@@ -0,0 +1,3 @@
title: $:/spiral
archimedean

View File

@@ -0,0 +1,16 @@
{
"description": "Demo of the D3 plugin",
"plugins": [
"tiddlywiki/d3"
],
"themes": [
"tiddlywiki/vanilla",
"tiddlywiki/snowwhite"
],
"includeWikis": [
],
"build": {
"index": [
"--rendertiddler","$:/core/save/all","d3demo.html","text/plain"]
}
}

View File

@@ -1,127 +0,0 @@
created: 20240802065815656
modified: 20240802065836064
title: How to Create a Custom Cascade Entry
type: text/vnd.tiddlywiki
This guide explains how to add a new [[cascade|https://tiddlywiki.com/#Cascades]] to the ~TiddlyWiki core or your own plugins. This allows third-party plugins to extend the functionality of the core or your plugin.
!! How Cascade Works in the Core
This section explains how the existing WikiText in the core interacts with the new WikiText youll add, only for learning purpose. You dont need to modify the core WikiText when adding a new cascade.
!!! The Default Template as a Fallback
The default behavior in ~TiddlyWiki is defined by [[$:/core/ui/ViewTemplate/tags/default]].
<pre>
<$view tiddler="$:/core" subtiddler="$:/core/ui/ViewTemplate/tags/default" mode=block format=text/>
</pre>
!!! Transclusion of the Active Template
[[$:/core/ui/ViewTemplate/tags]] uses a filter expression to find the cascade filter and the view template youll add.
<pre>
<$view tiddler="$:/core" subtiddler="$:/core/ui/ViewTemplate/tags" mode=block format=text/>
</pre>
The `:cascade` clause collects all tiddlers it finds and uses their filter text sequentially. Most filters wont return any text and will be skipped. The first filter that returns a tiddler title becomes the result of the `:cascade` clause. If no filters return a result, the fallback default filter will be used.
The `:and[!is[blank]else` clause provides additional fallback protection, though its often redundant because a fallback is typically tagged with `$:/tags/ViewTemplateTagsFilter`. However, including fallbacks is a good practice for defensive programming.
!! Adding a New Cascade Entry
This section contains the WikiText youll need to add to the core. Modify it to suit your needs instead of copying it directly.
!!! Creating a Control Panel Tab
To create a new tab under [[ControlPanel|$:/ControlPanel]] → Advanced → [[Cascade|$:/core/ui/ControlPanel/Cascades]], use the following code:
[[$:/core/ui/ControlPanel/ViewTemplateTags]] uses a filter expression to find the cascade filter and the view template youll add.
<pre>
<$view tiddler="$:/core" subtiddler="$:/core/ui/ControlPanel/ViewTemplateTags" mode=block format=text/>
</pre>
Add the following metadata:
```tid
tags: $:/tags/ControlPanel/Cascades
caption: {{$:/language/ControlPanel/ViewTemplateTags/Caption}}
```
!!! Adding a New Language Entry
Its important to add related language files. Create a file starting with `title: $:/language/ControlPanel/`:
```multid
title: $:/language/ControlPanel/
ViewTemplateTags/Caption: View Template Tags
ViewTemplateTags/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the tags area of a tiddler.
```
!!! Adding Default Configuration
Similar to the language file, add a config file starting with `title: $:/config/ViewTemplateTagsFilters/`. For example:
```tid
title: $:/config/ViewTemplateTagsFilters/
tags: $:/tags/ViewTemplateTagsFilter
default: [[$:/core/ui/ViewTemplate/tags/default]]
```
Different templates may have their own config files. Ensure youre adding to the correct file or creating a new one if it doesnt exist.
!! Using the New Cascade
This section provides a simplified example based on a real-world use case. It demonstrates how to override the default template with a custom template.
!!! Your Template
Add the content you want to display conditionally. Update `publisher/plugin-name` to your plugins name.
```tid
code-body: yes
title: $:/plugins/publisher/plugin-name/EditMode
\whitespace trim
<$reveal type="nomatch" stateTitle=<<folded-state>> text="hide" tag="div" retain="yes" animate="yes">
<div class="tc-tags-wrapper" style="display:flex">
<$transclude tiddler="$:/core/ui/EditTemplate/tags"/>
<$button class="tc-btn-invisible" style="margin-left:1em;">
{{$:/core/images/done-button}}
<$action-deletetiddler $tiddler={{{ [[$:/state/edit-view-mode-tags/]addsuffix<storyTiddler>] }}}/>
</$button>
</div>
</$reveal>
```
!!! The Condition
Write a filter that ends with `then[$:/plugins/publisher/plugin-name/EditMode]`.
```tid
code-body: yes
tags: $:/tags/ViewTemplateTagsFilter
title: $:/plugins/publisher/plugin-name/CascadeEditMode
list-before: $:/config/ViewTemplateTagsFilters/default
[[$:/state/edit-view-mode-tags/]addsuffix<currentTiddler>get[text]compare:string:eq[yes]then[$:/plugins/publisher/plugin-name/EditMode]]
```
!!! A Button to Trigger the Condition
```tid
code-body: yes
tags: $:/tags/ViewTemplate/Tags
title: $:/plugins/publisher/plugin-name/TriggerEdit
\whitespace trim
<%if [<storyTiddler>get[tags]!is[blank]] %>
<$button class="tc-btn-invisible" set={{{ [[$:/state/edit-view-mode-tags/]addsuffix<storyTiddler>] }}} setTo="yes" tooltip="add tags">
{{$:/core/images/new-here-button}}
</$button>
<%endif%>
```

View File

@@ -1,10 +1,9 @@
chapter.of: UI and Rendering Pipeline
created: 20140717175203036
modified: 20240802065804331
modified: 20140717182314488
sub.num: 5
tags: doc
title: RootWidget and Rendering Startup
type: text/vnd.tiddlywiki
The previous parts of this chapter showed how WikiText is transformed to DOM nodes which dynamically react to tiddler changes and a way to compose tiddlers from other tiddlers.
This last part describes how the TiddlyWiki core plug-in starts up a UI build from tiddlers and WikiText.
@@ -30,9 +29,6 @@ and a listener is registered at the store which executes the refresh function of
[[Techniques for including other tiddlers and Templates|Transclusion and TextReference]] are finally used in [[$:/core/ui/PageTemplate]] to build the TiddlyWiki UI only from tiddlers written in WikiText (with widgets implemented in javascript):
For example to implement the list of open wiki pages the [[$:/core/ui/PageTemplate]] contains a [[navigator widget|$:/core/modules/widgets/navigator.js]] which maintains a list of open tiddlers in a field of [[$:/StoryList]] and handles events like ``tm-navigate`` by adding a tiddler specified as parameter to the top of the list in [[$:/StoryList]].
The [[story tiddler|$:/core/ui/PageTemplate/story]] transcluded in [[$:/core/ui/PageTemplate]] then uses a ~ListWidget to transclude all tiddlers in [[$:/StoryList]] through a special template [[$:/core/ui/ViewTemplate]].
The ViewTemplate here is a combination of different fragments, like title fragment and body fragment, each fragment can be override individually using [[Cascade Mechanism|How to Create a Custom Cascade Entry]].
A event of the type ``tm-close-tiddler`` would remove a specified tiddler from [[$:/StoryList]].
The [[Event Mechanism]] would trigger a changed event which triggers a call of the ~ListWidget's refresh function which would remove the tiddler from the list, closing the tiddler.

View File

@@ -1,6 +1,5 @@
created: 20141115211411211
title: ReleaseTemplate
code-body: yes
type: text/vnd.tiddlywiki
<h2><$link to=<<currentTab>>><$view tiddler=<<currentTab>> field="title"/></$link></h2>

View File

@@ -19,5 +19,4 @@ The pre-release is also available as an [[empty wiki|https://tiddlywiki.com/prer
</div>
<div class="tc-subtitle">Updated: <$view field="modified" format="date" template={{$:/language/Tiddler/DateFormat}}/></div>
<$transclude mode="block"/>
</$list>

View File

@@ -0,0 +1,3 @@
title: $:/DefaultTiddlers
HelloThere

View File

@@ -0,0 +1,6 @@
title: HelloThere
This is an experimental edition of TiddlyWiki5 for use with [[Tahoe-LAFS|https://tahoe-lafs.org/]]. At this point it is largely for experimentation by @zooko. Click the ''save changes'' button to PUT the updated TiddlyWiki HTML file back to the server.
<$button message="tm-new-tiddler">New Tiddler</$button>
<$button message="tm-save-wiki">Save Changes</$button>

View File

@@ -0,0 +1,3 @@
title: $:/SiteSubtitle
Tahoe-LAFS edition

View File

@@ -0,0 +1,3 @@
title: $:/SiteTitle
TiddlyWiki5

View File

@@ -0,0 +1,14 @@
{
"description": "Demo of TahoeLAFS plugin",
"plugins": [
"tiddlywiki/tahoelafs"
],
"themes": [
"tiddlywiki/vanilla",
"tiddlywiki/snowwhite"
],
"build": {
"index": [
"--rendertiddler","$:/core/save/all","tahoelafs.html","text/plain"]
}
}

View File

@@ -372,13 +372,13 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
it("should handle the variance operator", function() {
expect(parseFloat(wiki.filterTiddlers("[tag[shopping]get[price]variance[]]").join(","))).toBeCloseTo(2.92);
expect(parseFloat(wiki.filterTiddlers("[tag[food]get[price]variance[]]").join(","))).toBeCloseTo(3.367);
expect(wiki.filterTiddlers(" +[variance[]]").toString()).toBe("");
expect(wiki.filterTiddlers(" +[variance[]]").toString()).toBe("NaN");
});
it("should handle the standard-deviation operator", function() {
expect(parseFloat(wiki.filterTiddlers("[tag[shopping]get[price]standard-deviation[]]").join(","))).toBeCloseTo(1.71);
expect(parseFloat(wiki.filterTiddlers("[tag[food]get[price]standard-deviation[]]").join(","))).toBeCloseTo(1.835);
expect(wiki.filterTiddlers(" +[standard-deviation[]]").toString()).toBe("");
expect(wiki.filterTiddlers(" +[standard-deviation[]]").toString()).toBe("NaN");
});
it("should handle the :intersection prefix", function() {
@@ -420,8 +420,6 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
expect(wiki.filterTiddlers("[tag[cakes]] :sort:string[{!!title}]").join(",")).toBe("cheesecake,Cheesecake,chocolate cake,Chocolate Cake,Persian love cake,Pound cake");
expect(wiki.filterTiddlers("[tag[cakes]] :sort:string:casesensitive[{!!title}]").join(",")).toBe("Cheesecake,Chocolate Cake,Persian love cake,Pound cake,cheesecake,chocolate cake");
expect(wiki.filterTiddlers("[tag[cakes]] :sort:string:casesensitive,reverse[{!!title}]").join(",")).toBe("chocolate cake,cheesecake,Pound cake,Persian love cake,Chocolate Cake,Cheesecake");
expect(wiki.filterTiddlers("1.2.0 1.0.0 1.0.5 :sort:version[{!!title}]").join(",")).toBe("1.0.0,1.0.5,1.2.0");
expect(wiki.filterTiddlers("1.2.0 1.0.0 1.0.5 :sort:version:reverse[{!!title}]").join(",")).toBe("1.2.0,1.0.5,1.0.0");
});
it("should handle the :map prefix", function() {

View File

@@ -1,10 +1,11 @@
created: 20240313100515958
modified: 20251023154747366
modified: 20240313103959789
tags: Editions
title: TiddlyWiki Docs PR Maker
''~TiddlyWiki Docs PR Maker'' is a special edition of tiddlywiki.com designed to help you contribute to and improve the documentation.
https://edit.tiddlywiki.com
''~TiddlyWiki Docs PR Maker'' is a special edition of tiddlywiki.com designed to help you contribute to and improve the documentation made by [[@saqimtiaz|https://github.com/saqimtiaz/]].
https://saqimtiaz.github.io/tw5-docs-pr-maker/
All changes made to the documentation can be very easily submitted to GitHub -- the pull request will be automatically made, hence the "PR Maker" name of the edition.

View File

@@ -1,5 +1,5 @@
created: 20150412191004348
modified: 20251022153208584
modified: 20240925114810504
tags: Community Reference
title: Developers
type: text/vnd.tiddlywiki
@@ -8,4 +8,3 @@ type: text/vnd.tiddlywiki
* Get involved in the [[development on GitHub|https://github.com/TiddlyWiki/TiddlyWiki5]]
* [[GitHub Discussions|https://github.com/TiddlyWiki/TiddlyWiki5/discussions]] are for Q&A and open-ended discussion
* [[GitHub Issues|https://github.com/TiddlyWiki/TiddlyWiki5/issues]] are for raising bug reports and proposing specific, actionable new ideas
* See [[Contributing]] for guidelines on how to contribute to the project.

View File

@@ -1,22 +1,22 @@
created: 20250730154331065
modified: 20250731162557775
modifier: Jeremy Ruston
tags: [[History of TiddlyWiki]]
title: Filter Syntax History
tags: [[History of TiddlyWiki]]
modifier: Jeremy Ruston
created: 20250730154331065
modified: 20250730154331065
In response to [[a discussion|https://talk.tiddlywiki.org/t/filter-syntax-history/13058]] about the filter syntax in TiddlyWiki5 I posted this brief personal history.
For context, before TiddlyWiki, I only had practical experience of a very small number of languages: machine code, assembly language, BASIC, FORTH, C, C++ and of course JavaScript. I had a smattering of Java, Pascal and one or two other ancient languages but no experience of actor based languages like Erlang, or modern functional languages.
The story starts with the double square bracket syntax used in wikitext for links. In 2004 this was already an established usage in wikis. However, I switched the ordering of pretty links because I thought Wikipedia's `[[link address|link text]]` was the wrong way around. It broke up sentences: `The file is [[https://site.com/thing|here]]` seems less readable than `The file is [[here|https://site.com/thing]]`. For a long time I regretted this decision, and wished that I had just gone with Wikipedia's established usage. Others have since pointed out that ~TiddlyWiki's ordering is actually consistent with [[Markdown]], which might be regarded as the winner of the markup wars.
The story starts with the double square bracket syntax used in wikitext for links. In 2004 this was already an established usage in wikis. However, I switched the ordering of pretty links because I thought Wikipedia's `[[link address|link text]]` was the wrong way around. It broke up sentences: `The file is [[https://site.com/thing|here]]` seems less readable than `The file is [[here|https://site.com/thing]]`. For a long time I regretted this decision, and wished that I had just gone with Wikipedia's established usage. Others have since pointed out that TiddlyWiki's ordering is actually consistent with [[Markdown]], which might be regarded as the winner of the markup wars.
I looked at the double square bracket link syntax is that it establishes a way to quote page/tiddler titles so that they may contain spaces, and don't have to use CamelCase. Thus, right at the start of [[TiddlyWiki Classic|TiddlyWikiClassic]] when I was implementing the first iteration of the DefaultTiddlers feature it was natural to use double square brackets to quote titles containing spaces, making a list of titles to be opened at startup.
I looked at the double square bracket link syntax is that it establishes a way to quote page/tiddler titles so that they may contain spaces, and don't have to use CamelCase. Thus, right at the start of TiddlyWiki Classic when I was implementing the first iteration of the DefaultTiddlers feature it was natural to use double square brackets to quote titles containing spaces, making a list of titles to be opened at startup.
Soon, I wanted to extend the implementation of DefaultTiddlers so that it could be used to open all tiddlers with a particular tag while retaining backwards compatibility.
A trick that I am apt to use in such situations is to try to engineer things so that the current behaviour is re-interpreted as a shortcut syntax for a new, richer syntax that provides more flexibility. In this case, the idea was that in filters we would interpret `[[mytiddler]]` as a shortcut for `[title[mytiddler]]`. Then we could put any keywords we like in place of "title", giving us an infinitely extensible syntax. A similar example is the way that we implemented filter run prefixes by retrospectively defining the absence of a prefix as implying a default prefix.
The new syntax was first [[introduced in 2007|https://github.com/TiddlyWiki/TiddlyWikiClassic/commit/1928962ea6811b1ca67378ed3cd62059a9806ae9]], with a simplified syntax that only supported a single `tag` operator but was just about sufficient for intended purpose. The only documentation was a comment in the source code (complete with a typo):
The new syntax was first [introduced in 2007] (see [this commit](https://github.com/TiddlyWiki/TiddlyWikiClassic/commit/1928962ea6811b1ca67378ed3cd62059a9806ae9)), with a simplified syntax that only supported a single `tag` operator but was just about sufficient for intended purpose. The only documentation was a comment in the source code (complete with a typo):
```
// Filter a list of tiddlers
@@ -24,19 +24,19 @@ The new syntax was first [[introduced in 2007|https://github.com/TiddlyWiki/Tidd
//# Returns an array of Tiddler() objects that match the filter expression
```
Provision to combine the filter operators had been on my mind from the beginning. When ~TiddlyWiki 5 started in 2011 I reused the simple implementation from ~TiddlyWiki Classic. Smashing operators [[was finally implemented in May 2012|https://github.com/TiddlyWiki/TiddlyWiki5/commit/8b0703b694e982b2bc448bdb133742164723dd8a]]. By the time of the launch the filter language had grown into pretty much what it is today -- see the [[documentation for TiddlyWiki v5.1.0|https://tiddlywiki.com/archive/full/TiddlyWiki-5.1.0#Introduction%20to%20Filters]].
Provision to combine the filter operators had been on my mind from the beginning. When TiddlyWiki 5 started in 2011 I reused the simple implementation from TiddlyWiki Classic. Smashing operators [was finally implemented in May 2012](https://github.com/TiddlyWiki/TiddlyWiki5/commit/8b0703b694e982b2bc448bdb133742164723dd8a). By the time of the launch the filter language had grown into pretty much what it is today -- see the [documentation for TiddlyWiki v5.1.0](https://tiddlywiki.com/archive/full/TiddlyWiki-5.1.0#Introduction%20to%20Filters).
The filter syntax had undoubtedly evolved into something approaching a programming language. As others have probably expressed much more eloquently, a characteristic of the programming languages that I love is that they start with a small number of principles that are consistently applied and combined. In the case of ~TiddlyWiki, the list would be very roughly:
The filter syntax had undoubtedly evolved into something approaching a programming language. As others have probably expressed much more eloquently, a characteristic of the programming languages that I love is that they start with a small number of principles that are consistently applied and combined. In the case of TiddlyWiki, the list would be very roughly:
* Double square brackets for [[linking and quoting|Linking in WikiText]]
* Curly braces for [[transclusion|Transclusion in WikiText]]
* Angle brackets for [[macros|Procedures]] (which evolved into variables)
* Double square brackets for linking and quoting
* Curly braces for transclusion
* Angle brackets for macros (which evolved into variables)
* Double exclamation marks to indicate fields `{{!!myfield}}`
* Double hashes to indicate indexes `{{##myindex}}`
* Smashing together adjacent [[filter operations|Introduction to filter notation]] by removing the combining `][`
* The dollar sign as a rough signifier of data [[owned by the system|Transclusion in WikiText]] rather than the user
* Smashing together adjacent filter operations by removing the combining `][`
* The dollar sign as a rough signifier of data owned by the system rather than the user
As I have written about elsewhere I was privileged to know Joe Armstrong, the co-inventor of Erlang, in the last few years of his life we were working together on a book about ~TiddlyWiki when he passed away in 2019. Joe had contacted me out of the blue ten years before to express his admiration for ~TiddlyWiki, and we had developed a friendship. He was actually a big fan of TW5's filter syntax, and used to make me feel better about it by joking that I had (re-)invented the monad, which sounded impressive to me. That doesn't make the filter language any easier to learn, but it does mean that it is *worth* learning: it's a real language, based on the same principles as other languages.
As I have written about elsewhere I was privileged to know Joe Armstrong, the co-inventor of Erlang, in the last few years of his life we were working together on a book about TiddlyWiki when he passed away in 2019. Joe had contacted me out of the blue ten years before to express his admiration for TiddlyWiki, and we had developed a friendship. He was actually a big fan of TW5's filter syntax, and used to make me feel better about it by joking that I had (re-)invented the monad, which sounded impressive to me. That doesn't make the filter language any easier to learn, but it does mean that it is *worth* learning: it's a real language, based on the same principles as other languages.
I find it pleasing that the TW5 filter language has its roots in decisions that were taken in the TWC days. It's still hard to learn, but that's an ongoing paradox of programming: people want to do complicated things, and complicated things are complicated. It's hard to see how we could have made filters any simpler without depriving users of the possibility of doing complicated things.

View File

@@ -1,6 +1,6 @@
caption: render
created: 20170919131752774
modified: 20250811164528905
modified: 20170919131805871
tags: Commands
title: RenderCommand
type: text/vnd.tiddlywiki
@@ -10,7 +10,6 @@ type: text/vnd.tiddlywiki
<$button class="tc-btn-invisible" style="text-decoration:underline">
Show available rendering templates
<$action-setfield $tiddler="$:/temp/advancedsearch" text="[all[shadows]prefix[$:/core/templates/]]"/>
<$action-setfield $tiddler="$:/temp/advancedsearch/input" text="[all[shadows]prefix[$:/core/templates/]]"/>
<$action-setfield $tiddler="$:/state/tab--1498284803" text="$:/core/ui/AdvancedSearch/Filter"/>
<$action-navigate $to="$:/AdvancedSearch"/>
</$button>

View File

@@ -3,7 +3,7 @@ created: 20250708130030654
modified: 20250826162904085
<div style.float="right" style.padding-left="1em">
<$image source="Community Survey 2025" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
<$image source="Community Survey 2025.png" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
</div>
The core developers work hard year by year to continuously improve ~TiddlyWiki. Part of the satisfaction is that we are not just building software for ourselves, were serving the needs of a wider community of users.

View File

@@ -1,5 +1,5 @@
created: 20140820151051019
modified: 20251023154718268
modified: 20240313114828368
tags: Community
title: Improving TiddlyWiki Documentation
type: text/vnd.tiddlywiki
@@ -20,7 +20,7 @@ You can choose to edit the documentation using the [[TiddlyWiki Docs PR Maker]]
!! Using [[Docs PR Maker|TiddlyWiki Docs PR Maker]] edition
# Go to https://edit.tiddlywiki.com or click the link displayed in the ribbon underneath the title when editing a tiddler on tiddlywiki.com
# Go to https://saqimtiaz.github.io/tw5-docs-pr-maker/ or click the link displayed in the ribbon underneath the title when editing a tiddler on tiddlywiki.com
# Go through the quick introduction where you will need to provide your ~GitHub username and a ~GitHub access token (you will be guided in creating one)
# Edit or create tiddlers to update the documentation, the wiki will keep track of all changes
# Click the "Submit updates" button and check if all the tiddlers that you edited are included in the submission; if not, drag them into the box

View File

@@ -1,10 +1,9 @@
created: 20160204225047445
lost-url: http://www.networkworld.com/article/3028098/open-source-tools/tiddlywiki-a-free-open-source-wiki-revisited.html
modified: 20160204225307847
tags: Articles
title: "A free, open source wiki revisited" by Mark Gibbs, NetworkWorld
type: text/vnd.tiddlywiki
url: https://web.archive.org/web/20180911094717/https://www.networkworld.com/article/3028098/open-source-tools/tiddlywiki-a-free-open-source-wiki-revisited.html
url: http://www.networkworld.com/article/3028098/open-source-tools/tiddlywiki-a-free-open-source-wiki-revisited.html
Interesting article giving the perspective of someone who has been away from TiddlyWiki for a few years:

View File

@@ -1,10 +1,9 @@
created: 20141122093837330
lost-url: http://cardo.wiki
modified: 20210106151026996
tags: [[Community Editions]]
title: "Cardo - Task and Project Management Wiki" by David Szego
type: text/vnd.tiddlywiki
url: https://web.archive.org/web/20181114094516/http://cardo.wiki/#Joe%20Cardo:%5B%5BJoe%20Cardo%5D%5D
url: http://cardo.wiki
Task & Delegation Tracking, Meetings & Agenda Items, Project Roles, Progress & Status Updates, Reference Items, Tickler Calendar, Conversation Logging, Book Notes & Library, and More!
@@ -12,10 +11,4 @@ Task & Delegation Tracking, Meetings & Agenda Items, Project Roles, Progress & S
<<<
Cardo is a standalone, browser-based tool that can be used as a simple task manager, or as a complex Project Management system (and indeed, I do use it this way in my daily work) as well as a fully Wiki-ized personal knowledge store. It runs completely independently in the browser, even without an Internet connection, making it possible to carry around on a USB stick, or to use on the morning commute.
<<<
* https://cardo-revisited.tiddlyhost.com (re-activated TW v5.3.8)
* https://cardo-dyumnin.tiddlyhost.com (TW v5.1.19)
<<<

View File

@@ -3,7 +3,7 @@ modified: 20140716084548184
tags: Examples
title: "PETTIL - Forth for the Commodore PET" by Charlie Hitselberger
type: text/vnd.tiddlywiki
url: http://pettilmobile.com
url: http://pettilmobile.com/tw/
A fast Forth interpreter for the [[Commodore PET|https://en.wikipedia.org/wiki/Commodore_PET]], written in 6502 assembly language. The TiddlyWiki containing program documentation is automatically generated from the source code: see https://github.com/chitselb/pettil.

View File

@@ -1,10 +1,9 @@
created: 20141122093837330
lost-url: http://luckysushi.ru/habarovsk/heeg.html
modified: 20210106151027143
tags: Examples
title: "Lucky Sushi" online shop by sini-Kit
type: text/vnd.tiddlywiki
url: https://web.archive.org/web/20241127033249/http://luckysushi.ru/habarovsk/heeg.html#index
url: http://luckysushi.ru/habarovsk/heeg.html#index
A complete online shop made in ~TiddlyWiki!

View File

@@ -1,10 +1,9 @@
created: 20150403110356105
lost-url: http://tw5-dev.cibm.de
modified: 20210106151027470
tags: [[Community Plugins]]
title: IndexedDB Plugin by Andreas Abeck
type: text/vnd.tiddlywiki
url: https://web.archive.org/web/20180118083556/http://tw5-dev.cibm.de/
url: http://tw5-dev.cibm.de
A plugin that allows changes to be synchronised with the IndexedDB database that is built-in to most browsers.

View File

@@ -1,9 +1,9 @@
created: 20201117162655614
modified: 20251020041616967
modified: 20201117162926714
tags: [[Community Plugins]]
title: Slider by Mohammad
type: text/vnd.tiddlywiki
url: https://kookma.github.io/TW-Slider/
url: https://kookma.github.io/slider/
Slider is a plugin to create an ordered set of tiddlers also called Trail.

View File

@@ -1,20 +0,0 @@
created: 20251009192405351
modified: 20251009192405351
tags: [[Community Plugins]] Resources
title: TW5-Graph by Flibbles
type: text/vnd.tiddlywiki
url: https://flibbles.github.io/tw5-graph/
TW5-Graph is a framework to integrate TiddlyWiki with existing graphing and visualization engines (such as [[Vis-Network|https://visjs.github.io/vis-network/docs/network/]]). The plugin allows for easy and sophisticated graphs to represent your tiddlers, or whatever else you want.
It is the spiritual successor to [[TiddlyMap|TiddlyMap Plugin by Felix Küppers]].
With TW5-Graph you can:
* Quickly create and edit graphs through a simple point-and-click GUI
* Use wikitext widgets to create customized graphs and template to exactly match your needs
* Easily create dynamic graphs to represent changing state or individual tiddlers
There's loads TW5-Graph can do for you. [[Check out its demo website here.|https://flibbles.github.io/tw5-graph/]]
Or visit its source code [[here|https://github.com/flibbles/tw5-graph]].

View File

@@ -0,0 +1,16 @@
created: 20140410103123179
modified: 20210106151027370
tags: [[Other Resources]]
title: "TWeb.at" by Mario Pietsch
type: text/vnd.tiddlywiki
url: https://tweb.at
A collection of TiddlyWiki resources from Mario Pietsch, with a focus on cloud deployments. Mario also maintains the German translation of TiddlyWiki.
{{!!url}}
<<<
My name is Mario Pietsch from Austria. I'm living near Salzburg.
This page, will be the portal to my TiddlyWiki, TiddlyWeb related content.
<<<

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