mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-23 11:24:40 +00:00
Compare commits
18 Commits
browser-me
...
offload-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c333d8d2e | ||
|
|
948c040554 | ||
|
|
5f104f129f | ||
|
|
98f850609d | ||
|
|
3f5c2bfba3 | ||
|
|
ee16e48a43 | ||
|
|
a989079201 | ||
|
|
12934caaa8 | ||
|
|
f70e1b8a46 | ||
|
|
52ee685572 | ||
|
|
c684f885ce | ||
|
|
c6c83bc18b | ||
|
|
b80213128f | ||
|
|
3faf503073 | ||
|
|
2ea6153a74 | ||
|
|
1752eba1e8 | ||
|
|
98dc094593 | ||
|
|
fc4190bb25 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -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 }}"
|
||||
|
||||
40
.github/workflows/eslint.yml
vendored
40
.github/workflows/eslint.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: ESLint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: lint-${{ github.event.pull_request.number || github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed for GitHub Checks API
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --include=dev
|
||||
|
||||
- name: Run ESLint with reviewdog (GitHub Checks)
|
||||
uses: reviewdog/action-eslint@v1
|
||||
with:
|
||||
eslint_flags: '.'
|
||||
reporter: github-pr-check
|
||||
fail_level: error
|
||||
level: error
|
||||
tool_name: ESLint PR code
|
||||
@@ -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 \
|
||||
|
||||
65
boot/boot.js
65
boot/boot.js
@@ -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
|
||||
*/
|
||||
@@ -799,13 +799,12 @@ the password, and to encrypt/decrypt a block of text
|
||||
$tw.utils.Crypto = function() {
|
||||
var sjcl = $tw.node ? (global.sjcl || require("./sjcl.js")) : window.sjcl,
|
||||
currentPassword = null,
|
||||
callSjcl = function(method,inputText,password,options) {
|
||||
options = options || {};
|
||||
callSjcl = function(method,inputText,password) {
|
||||
password = password || currentPassword;
|
||||
var outputText;
|
||||
try {
|
||||
if(password) {
|
||||
outputText = sjcl[method](password,inputText,options);
|
||||
outputText = sjcl[method](password,inputText);
|
||||
}
|
||||
} catch(ex) {
|
||||
console.log("Crypto error:" + ex);
|
||||
@@ -831,8 +830,7 @@ $tw.utils.Crypto = function() {
|
||||
return !!currentPassword;
|
||||
}
|
||||
this.encrypt = function(text,password) {
|
||||
// set default ks:256 -- see: http://bitwiseshiftleft.github.io/sjcl/doc/convenience.js.html
|
||||
return callSjcl("encrypt",text,password,{v:1,iter:10000,ks:256,ts:64,mode:"ccm",adata:"",cipher:"aes"});
|
||||
return callSjcl("encrypt",text,password);
|
||||
};
|
||||
this.decrypt = function(text,password) {
|
||||
return callSjcl("decrypt",text,password);
|
||||
@@ -1532,8 +1530,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 +1557,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 +1905,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 +1976,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 +2066,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 +2087,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 +2549,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 +2621,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;
|
||||
|
||||
@@ -6,7 +6,6 @@ talk.tiddlywiki.org: jeremyruston
|
||||
github: Jermolene
|
||||
linkedin: www.linkedin.com/in/jermy
|
||||
flickr: www.flickr.com/photos/jermy/
|
||||
bluesky: https://bsky.app/profile/jermolene.bsky.social
|
||||
homepage: jermolene.com
|
||||
email: jeremy@jermolene.com
|
||||
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAtAAEBAAMAAAAAAAAAAAAAAAAHBgIEBQEBAQEBAAAAAAAAAAAAAAAAAgQBBf/aAAwDAQACEAMQAAAANF4uTuPRhD2nBLnUiJvKM0DtMKy//8QAKxAAAgIBAwMDAQkAAAAAAAAAAQIDBBEABRITITEiMkFxFEJRUmFicoGR/9oACAEBAAE/AInTA6gUGP4ZOQbW1bPsmyUq1q+gmvFPUzZPDkPamtwqU75ks04JakroVcg5RwRjg66NUx25KbzqJYyMngfqSuq0M3NZYIebJIvZozIvI/iNPcp/aalSdJXsS4VcKeIzlvU3jVTcYLNiaGISrjkhWQYDfQ63pYAzCDBsOiu7Dsx4EHH6r2w2ttimjd2IsNErhhJHKI04/uzqxuCxpBYVVWKSHqwMyMSQ33SB7dUJFmlkMYRgnqZgCMf7rf8AeEt3A9YOhjXAb2k8u7dtT1RZeOtXmYxiOPj4ZWY/lb51skqUNnNW/wBNzC7IpB6gQeeB/jq/fqGOaLbowuYn5MAQOw8LjW5Vmeo0qIsqYLLKjHIZmwv9fB1//8QAHxEAAQMEAwEAAAAAAAAAAAAAEQABAgMSIWExMkFR/9oACAECAQE/AD9iTy2lJmHUB8BVKM4SNSOj46a29saX/8QAHREAAgICAwEAAAAAAAAAAAAAAQIAAwQRITGBkf/aAAgBAwEBPwDHpFpJZtamVSiBWT2Yt7hmCDsb+TKtsKqpGg3M/9k=
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,3 @@ This plugin contains TiddlyWiki's core components that are only needed on the se
|
||||
|
||||
* Commands
|
||||
* HTTP server code
|
||||
* Utility functions for server
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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") || "",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 [];
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -82,7 +82,6 @@ exports.parseTokenString = function(source,pos,token) {
|
||||
|
||||
/*
|
||||
Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,}
|
||||
Use the "Y" (sticky) flag to avoid searching the entire rest of the string
|
||||
*/
|
||||
exports.parseTokenRegExp = function(source,pos,reToken) {
|
||||
var node = {
|
||||
@@ -173,7 +172,7 @@ exports.parseMacroParameter = function(source,pos) {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexp
|
||||
const reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/y;
|
||||
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the parameter
|
||||
@@ -241,7 +240,7 @@ exports.parseMacroInvocation = function(source,pos) {
|
||||
params: []
|
||||
};
|
||||
// Define our regexps
|
||||
const reMacroName = /([^\s>"'=]+)/y;
|
||||
var reMacroName = /([^\s>"'=]+)/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a double less than sign
|
||||
@@ -278,7 +277,7 @@ exports.parseFilterVariable = function(source) {
|
||||
params: [],
|
||||
},
|
||||
pos = 0,
|
||||
reName = /([^\s"']+)/y;
|
||||
reName = /([^\s"']+)/g;
|
||||
// If there is no whitespace or it is an empty string then there are no macro parameters
|
||||
if(/^\S*$/.test(source)) {
|
||||
node.name = source;
|
||||
@@ -303,11 +302,11 @@ exports.parseAttribute = function(source,pos) {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexps
|
||||
const reAttributeName = /([^\/\s>"'`=]+)/y,
|
||||
reUnquotedAttribute = /([^\/\s<>"'`=]+)/y,
|
||||
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/y,
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/y,
|
||||
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/y;
|
||||
var reAttributeName = /([^\/\s>"'`=]+)/g,
|
||||
reUnquotedAttribute = /([^\/\s<>"'`=]+)/g,
|
||||
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g,
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/g,
|
||||
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the attribute name
|
||||
|
||||
@@ -39,7 +39,7 @@ exports.parse = function() {
|
||||
// Return the classed span
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "s",
|
||||
tag: "strike",
|
||||
children: tree
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ exports.parse = function() {
|
||||
// Advance the parser position to past the tag
|
||||
this.parser.pos = tag.end;
|
||||
// Check for an immediately following double linebreak
|
||||
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
|
||||
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
|
||||
// Set whether we're in block mode
|
||||
tag.isBlock = this.is.block || hasLineBreak;
|
||||
// Parse the body if we need to
|
||||
@@ -100,7 +100,7 @@ exports.parseTag = function(source,pos,options) {
|
||||
orderedAttributes: []
|
||||
};
|
||||
// Define our regexps
|
||||
const reTagName = /([a-zA-Z0-9\-\$\.]+)/y;
|
||||
var reTagName = /([a-zA-Z0-9\-\$\.]+)/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a less than sign
|
||||
@@ -148,7 +148,7 @@ exports.parseTag = function(source,pos,options) {
|
||||
pos = token.end;
|
||||
// Check for a required line break
|
||||
if(options.requireLineBreak) {
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ exports.parseImage = function(source,pos) {
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the source up to the terminating `]]`
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/y);
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
142
core/modules/utils/base64-utf8/base64-utf8.module.js
Normal file
142
core/modules/utils/base64-utf8/base64-utf8.module.js
Normal 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" ]);
|
||||
9
core/modules/utils/base64-utf8/base64-utf8.module.min.js
vendored
Normal file
9
core/modules/utils/base64-utf8/base64-utf8.module.min.js
vendored
Normal 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"]);
|
||||
14
core/modules/utils/base64-utf8/tiddlywiki.files
Normal file
14
core/modules/utils/base64-utf8/tiddlywiki.files
Normal 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);"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
})();
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -181,7 +181,7 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
|
||||
};
|
||||
|
||||
ScrollableWidget.prototype.listenerFunction = function(event) {
|
||||
var self = this;
|
||||
self = this;
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(function() {
|
||||
var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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/>
|
||||
|
||||
@@ -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>
|
||||
 
|
||||
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
|
||||
<$button class="tc-btn-invisible">
|
||||
|
||||
@@ -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>
|
||||
 
|
||||
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
|
||||
<$button class="tc-btn-invisible">
|
||||
|
||||
@@ -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>
|
||||
 
|
||||
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
|
||||
<$button class="tc-btn-invisible">
|
||||
|
||||
@@ -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/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: $:/config/TiddlerInfo/Mode
|
||||
text: sticky
|
||||
text: popup
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,6 +3,7 @@ tags: $:/tags/Macro
|
||||
|
||||
\define tag-pill-styles()
|
||||
background-color:$(backgroundColor)$;
|
||||
fill:$(foregroundColor)$;
|
||||
color:$(foregroundColor)$;
|
||||
\end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
44
editions/d3demo/tiddlers/CloudData.tid
Normal file
44
editions/d3demo/tiddlers/CloudData.tid
Normal 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}
|
||||
]
|
||||
3
editions/d3demo/tiddlers/DefaultTiddlers.tid
Normal file
3
editions/d3demo/tiddlers/DefaultTiddlers.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/DefaultTiddlers
|
||||
|
||||
[[HelloThere]]
|
||||
10
editions/d3demo/tiddlers/GraphData.tid
Normal file
10
editions/d3demo/tiddlers/GraphData.tid
Normal file
File diff suppressed because one or more lines are too long
17
editions/d3demo/tiddlers/HelloThere.tid
Normal file
17
editions/d3demo/tiddlers/HelloThere.tid
Normal 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]]//
|
||||
3
editions/d3demo/tiddlers/SiteSubtitle.tid
Normal file
3
editions/d3demo/tiddlers/SiteSubtitle.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/SiteSubtitle
|
||||
|
||||
a demo of the D3.js plugin for TiddlyWiki5
|
||||
3
editions/d3demo/tiddlers/SiteTitle.tid
Normal file
3
editions/d3demo/tiddlers/SiteTitle.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/SiteTitle
|
||||
|
||||
d3demo
|
||||
3
editions/d3demo/tiddlers/grouped.tid
Normal file
3
editions/d3demo/tiddlers/grouped.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/grouped
|
||||
|
||||
yes
|
||||
3
editions/d3demo/tiddlers/spiral.tid
Normal file
3
editions/d3demo/tiddlers/spiral.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/spiral
|
||||
|
||||
archimedean
|
||||
16
editions/d3demo/tiddlywiki.info
Normal file
16
editions/d3demo/tiddlywiki.info
Normal 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"]
|
||||
}
|
||||
}
|
||||
@@ -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 you’ll add, only for learning purpose. You don’t 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 you’ll 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 won’t 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 it’s 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 you’ll 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 you’ll 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
|
||||
|
||||
It’s 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 you’re adding to the correct file or creating a new one if it doesn’t 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 plugin’s 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%>
|
||||
```
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
3
editions/tahoelafs/tiddlers/DefaultTiddlers.tid
Normal file
3
editions/tahoelafs/tiddlers/DefaultTiddlers.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/DefaultTiddlers
|
||||
|
||||
HelloThere
|
||||
6
editions/tahoelafs/tiddlers/HelloThere.tid
Normal file
6
editions/tahoelafs/tiddlers/HelloThere.tid
Normal 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>
|
||||
3
editions/tahoelafs/tiddlers/SiteSubtitle.tid
Normal file
3
editions/tahoelafs/tiddlers/SiteSubtitle.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/SiteSubtitle
|
||||
|
||||
Tahoe-LAFS edition
|
||||
3
editions/tahoelafs/tiddlers/SiteTitle.tid
Normal file
3
editions/tahoelafs/tiddlers/SiteTitle.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/SiteTitle
|
||||
|
||||
TiddlyWiki5
|
||||
14
editions/tahoelafs/tiddlywiki.info
Normal file
14
editions/tahoelafs/tiddlywiki.info
Normal 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"]
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ Tests the checkbox widget thoroughly.
|
||||
];
|
||||
|
||||
var indexModeTests = fieldModeTests.map(data => {
|
||||
var newData = Object.assign({}, data);
|
||||
var newData = {...data};
|
||||
var newName = data.testName.replace('field mode', 'index mode');
|
||||
var tiddlerOneAlreadyExists = false;
|
||||
var newTiddlers = data.tiddlers.map(tiddler => {
|
||||
@@ -274,13 +274,14 @@ Tests the checkbox widget thoroughly.
|
||||
listModeTests
|
||||
.filter(data => data.widgetText.includes("listField='colors'"))
|
||||
.map(data => {
|
||||
var newData = Object.assign({}, data, {
|
||||
tiddlers: data.tiddlers.map(tiddler => Object.assign({}, tiddler, {list: tiddler.colors, colors: undefined})),
|
||||
var newData = {
|
||||
...data,
|
||||
tiddlers: data.tiddlers.map(tiddler => ({...tiddler, list: tiddler.colors, colors: undefined})),
|
||||
widgetText: data.widgetText.replace("listField='colors'", "listField='list'"),
|
||||
expectedChange: {
|
||||
"Colors": { list: data.expectedChange.Colors.colors.split(' ') }
|
||||
},
|
||||
})
|
||||
}
|
||||
return newData;
|
||||
})
|
||||
);
|
||||
@@ -288,19 +289,20 @@ Tests the checkbox widget thoroughly.
|
||||
listModeTests
|
||||
.filter(data => data.widgetText.includes("listField='colors'"))
|
||||
.map(data => {
|
||||
var newData = Object.assign({}, data, {
|
||||
tiddlers: data.tiddlers.map(tiddler => Object.assign({}, tiddler, {tags: tiddler.colors, colors: undefined})),
|
||||
var newData = {
|
||||
...data,
|
||||
tiddlers: data.tiddlers.map(tiddler => ({...tiddler, tags: tiddler.colors, colors: undefined})),
|
||||
widgetText: data.widgetText.replace("listField='colors'", "listField='tags'"),
|
||||
expectedChange: {
|
||||
"Colors": { tags: data.expectedChange.Colors.colors.split(' ') }
|
||||
},
|
||||
})
|
||||
}
|
||||
return newData;
|
||||
})
|
||||
);
|
||||
|
||||
var indexListModeTests = listModeTests.map(data => {
|
||||
var newData = Object.assign({}, data);
|
||||
var newData = {...data};
|
||||
var newName = data.testName.replace('list mode', 'index list mode');
|
||||
var newTiddlers = data.tiddlers.map(tiddler => {
|
||||
if (tiddler.hasOwnProperty('colors')) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user