mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-23 11:24:40 +00:00
Compare commits
3 Commits
coalesce-p
...
core-icons
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
374a0fe56d | ||
|
|
06758183d3 | ||
|
|
46d323c902 |
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
- master
|
||||
- tiddlywiki-com
|
||||
env:
|
||||
NODE_VERSION: "18"
|
||||
NODE_VERSION: "12"
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -14,13 +14,7 @@ jobs:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: "${{ env.NODE_VERSION }}"
|
||||
- run: "./bin/ci-test.sh"
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
- run: "./bin/test.sh"
|
||||
build-prerelease:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@@ -60,7 +54,6 @@ jobs:
|
||||
TW5_BUILD_TIDDLYWIKI: "./node_modules/tiddlywiki/tiddlywiki.js"
|
||||
TW5_BUILD_MAIN_EDITION: "./editions/tw5.com"
|
||||
TW5_BUILD_OUTPUT: "./output"
|
||||
TW5_BUILD_ARCHIVE: "./output"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,10 +1,7 @@
|
||||
.DS_Store
|
||||
.c9/
|
||||
.vs/
|
||||
.vscode/
|
||||
tmp/
|
||||
output/
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.3.3
|
||||
TW5_BUILD_VERSION=v5.2.8
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
@@ -84,27 +84,10 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n"
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Core distributions
|
||||
# Core distribution
|
||||
#
|
||||
######################################################
|
||||
|
||||
# Conditionally build archive if $TW5_BUILD_ARCHIVE variable is set, otherwise do nothing
|
||||
#
|
||||
# /archive/Empty-TiddlyWiki-<version>.html Empty archived version
|
||||
# /archive/TiddlyWiki-<version>.html Full archived version
|
||||
|
||||
if [ -n "$TW5_BUILD_ARCHIVE" ]; then
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--version \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_ARCHIVE \
|
||||
--build archive \
|
||||
|| exit 1
|
||||
fi
|
||||
|
||||
# /index.html Main site
|
||||
# /favicon.ico Favicon for main site
|
||||
# /static.html Static rendering of default tiddlers
|
||||
@@ -112,7 +95,6 @@ fi
|
||||
# /static/* Static single tiddlers
|
||||
# /static/static.css Static stylesheet
|
||||
# /static/favicon.ico Favicon for static pages
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
@@ -122,15 +104,13 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||
--build favicon static index \
|
||||
|| exit 1
|
||||
|
||||
# /empty.html Empty
|
||||
# /empty.hta For Internet Explorer
|
||||
# /empty-external-core.html External core empty
|
||||
# /tiddlywikicore-<version>.js Core plugin javascript
|
||||
# /empty.html Empty
|
||||
# /empty.hta For Internet Explorer
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/empty \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build empty emptyexternalcore \
|
||||
--build empty \
|
||||
|| exit 1
|
||||
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# test TiddlyWiki5 for tiddlywiki.com
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/test \
|
||||
--verbose \
|
||||
--version \
|
||||
--rendertiddler $:/core/save/all test.html text/plain \
|
||||
--test \
|
||||
|| exit 1
|
||||
|
||||
npm install playwright @playwright/test
|
||||
npx playwright install chromium firefox --with-deps
|
||||
|
||||
npx playwright test
|
||||
81
boot/boot.js
81
boot/boot.js
@@ -569,22 +569,10 @@ $tw.utils.getTypeEncoding = function(ext) {
|
||||
return typeInfo ? typeInfo.encoding : "utf8";
|
||||
};
|
||||
|
||||
var globalCheck =[
|
||||
" Object.defineProperty(Object.prototype, '__temp__', {",
|
||||
" get: function () { return this; },",
|
||||
" configurable: true",
|
||||
" });",
|
||||
" if(Object.keys(__temp__).length){",
|
||||
" console.log(\"Warning: Global assignment detected\",Object.keys(__temp__));",
|
||||
" delete Object.prototype.__temp__;",
|
||||
" }",
|
||||
" delete Object.prototype.__temp__;",
|
||||
].join('\n');
|
||||
|
||||
/*
|
||||
Run code globally with specified context variables in scope
|
||||
*/
|
||||
$tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
|
||||
$tw.utils.evalGlobal = function(code,context,filename) {
|
||||
var contextCopy = $tw.utils.extend(Object.create(null),context);
|
||||
// Get the context variables as a pair of arrays of names and values
|
||||
var contextNames = [], contextValues = [];
|
||||
@@ -593,38 +581,25 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
|
||||
contextValues.push(value);
|
||||
});
|
||||
// Add the code prologue and epilogue
|
||||
code = [
|
||||
"(function(" + contextNames.join(",") + ") {",
|
||||
" (function(){" + code + "\n;})();\n",
|
||||
(!$tw.browser && sandbox && !allowGlobals) ? globalCheck : "",
|
||||
"\nreturn exports;\n",
|
||||
"})"
|
||||
].join("");
|
||||
|
||||
code = "(function(" + contextNames.join(",") + ") {(function(){\n" + code + "\n;})();\nreturn exports;\n})\n";
|
||||
// Compile the code into a function
|
||||
var fn;
|
||||
if($tw.browser) {
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
|
||||
} else {
|
||||
if(sandbox){
|
||||
fn = vm.runInContext(code,sandbox,filename)
|
||||
} else {
|
||||
fn = vm.runInThisContext(code,filename);
|
||||
}
|
||||
fn = vm.runInThisContext(code,filename);
|
||||
}
|
||||
// Call the function and return the exports
|
||||
return fn.apply(null,contextValues);
|
||||
};
|
||||
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
|
||||
|
||||
/*
|
||||
Run code in a sandbox with only the specified context variables in scope
|
||||
*/
|
||||
$tw.utils.evalSandboxed = $tw.browser ? $tw.utils.evalGlobal : function(code,context,filename,allowGlobals) {
|
||||
return $tw.utils.evalGlobal(
|
||||
code,context,filename,
|
||||
allowGlobals ? vm.createContext({}) : $tw.utils.sandbox,
|
||||
allowGlobals
|
||||
);
|
||||
$tw.utils.evalSandboxed = $tw.browser ? $tw.utils.evalGlobal : function(code,context,filename) {
|
||||
var sandbox = $tw.utils.extend(Object.create(null),context);
|
||||
vm.runInNewContext(code,sandbox,filename);
|
||||
return sandbox.exports;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -925,7 +900,7 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
|
||||
}
|
||||
} else {
|
||||
// line number should be included in e.stack for runtime errors
|
||||
$tw.utils.error("Error executing boot module " + name + ": " + String(e) + "\n\n" + e.stack);
|
||||
$tw.utils.error("Error executing boot module " + name + ": " + JSON.stringify(e) + "\n\n" + e.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1149,7 +1124,7 @@ $tw.Wiki = function(options) {
|
||||
shadowTiddlerTitles = null,
|
||||
getShadowTiddlerTitles = function() {
|
||||
if(!shadowTiddlerTitles) {
|
||||
shadowTiddlerTitles = Object.keys(shadowTiddlers).sort(function(a,b) {return a.localeCompare(b);});
|
||||
shadowTiddlerTitles = Object.keys(shadowTiddlers);
|
||||
}
|
||||
return shadowTiddlerTitles;
|
||||
},
|
||||
@@ -1945,7 +1920,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
// Read the specification
|
||||
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 processFile = function(filename,isTiddlerFile,fields,isEditableFile) {
|
||||
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
|
||||
type = (extInfo || {}).type || fields.type || "text/plain",
|
||||
typeInfo = $tw.config.contentTypeInfo[type] || {},
|
||||
@@ -1966,12 +1941,6 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
} else {
|
||||
var value = tiddler[name];
|
||||
switch(fieldInfo.source) {
|
||||
case "subdirectories":
|
||||
value = path.relative(rootPath, filename).split('/').slice(0, -1);
|
||||
break;
|
||||
case "filepath":
|
||||
value = path.relative(rootPath, filename);
|
||||
break;
|
||||
case "filename":
|
||||
value = path.basename(filename);
|
||||
break;
|
||||
@@ -2054,7 +2023,7 @@ $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)) {
|
||||
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path);
|
||||
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2079,11 +2048,7 @@ $tw.loadPluginFolder = function(filepath,excludeRegExp) {
|
||||
console.log("Warning: missing plugin.info file in " + filepath);
|
||||
return null;
|
||||
}
|
||||
var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"),function() {return null;});
|
||||
if(!pluginInfo) {
|
||||
console.log("warning: invalid JSON in plugin.info file at " + infoPath);
|
||||
pluginInfo = {};
|
||||
}
|
||||
var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"));
|
||||
// Read the plugin files
|
||||
var pluginFiles = $tw.loadTiddlersFromPath(filepath,excludeRegExp);
|
||||
// Save the plugin tiddlers into the plugin info
|
||||
@@ -2200,11 +2165,7 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||
pluginFields;
|
||||
// Bail if we don't have a wiki info file
|
||||
if(fs.existsSync(wikiInfoPath)) {
|
||||
wikiInfo = $tw.utils.parseJSONSafe(fs.readFileSync(wikiInfoPath,"utf8"),function() {return null;});
|
||||
if(!wikiInfo) {
|
||||
console.log("warning: invalid JSON in tiddlywiki.info file at " + wikiInfoPath);
|
||||
wikiInfo = {};
|
||||
}
|
||||
wikiInfo = $tw.utils.parseJSONSafe(fs.readFileSync(wikiInfoPath,"utf8"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -2447,7 +2408,7 @@ $tw.boot.initStartup = function(options) {
|
||||
$tw.utils.registerFileType("video/webm","base64",".webm");
|
||||
$tw.utils.registerFileType("video/mp4","base64",".mp4");
|
||||
$tw.utils.registerFileType("audio/mp3","base64",".mp3");
|
||||
$tw.utils.registerFileType("audio/mpeg","base64",[".mp3",".m2a",".mp2",".mpa",".mpg",".mpga"]);
|
||||
$tw.utils.registerFileType("audio/mpeg","base64");
|
||||
$tw.utils.registerFileType("text/markdown","utf8",[".md",".markdown"],{deserializerType:"text/x-markdown"});
|
||||
$tw.utils.registerFileType("text/x-markdown","utf8",[".md",".markdown"]);
|
||||
$tw.utils.registerFileType("application/enex+xml","utf8",".enex");
|
||||
@@ -2674,18 +2635,6 @@ $tw.hooks.addHook = function(hookName,definition) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Delete hooks from the hashmap
|
||||
*/
|
||||
$tw.hooks.removeHook = function(hookName,definition) {
|
||||
if($tw.utils.hop($tw.hooks.names,hookName)) {
|
||||
var p = $tw.hooks.names[hookName].indexOf(definition);
|
||||
if(p !== -1) {
|
||||
$tw.hooks.names[hookName].splice(p, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Invoke the hook by key
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@ title: $:/Acknowledgements
|
||||
TiddlyWiki incorporates code from these fine OpenSource projects:
|
||||
|
||||
* [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]]
|
||||
* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]]
|
||||
* [[The Jasmine JavaScript Test Framework|http://pivotal.github.io/jasmine/]]
|
||||
* [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]]
|
||||
|
||||
And media from these projects:
|
||||
|
||||
@@ -4,7 +4,7 @@ type: text/plain
|
||||
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
|
||||
|
||||
Copyright (c) 2004-2007, Jeremy Ruston
|
||||
Copyright (c) 2007-2024, UnaMesa Association
|
||||
Copyright (c) 2007-2023, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
title: $:/core/images/network-activity
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" class="tc-image-network-activity tc-image-button" viewBox="0 0 128 128"><g class={{{ [{$:/state/http-requests}match[0]then[]else[tc-network-activity-background]] }}}>
|
||||
<$list filter="[{$:/state/http-requests}match[0]]" variable="ignore">
|
||||
<path d="M64.043 45.153a4.002 4.002 0 0 1 4.367 2.21l.084.188 30.403 73.4a4 4 0 0 1-7.307 3.25l-.084-.188-3.103-7.49-8.898 8.899a3.985 3.985 0 0 1-2.624 1.166l-.205.005a3.987 3.987 0 0 1-2.828-1.171l-9.849-9.848-9.847 9.848a3.985 3.985 0 0 1-2.624 1.166l-.204.005a3.987 3.987 0 0 1-2.829-1.171l-8.899-8.9-3.102 7.491a4 4 0 1 1-7.391-3.062l30.403-73.4a4.001 4.001 0 0 1 4.495-2.39l.042-.008Zm13.636 56.74-8.023 8.024 7.02 7.019 8.023-8.022-7.02-7.02Zm-27.353.008-7.019 7.019 8.016 8.016 7.019-7.02-8.016-8.015Zm13.68-13.68-8.023 8.023 8.016 8.016 8.023-8.023-8.016-8.016Zm-8.971-8.971-4.687 11.315 8.001-8.001-3.314-3.314Zm17.933.009-3.305 3.305 7.979 7.979-4.674-11.284ZM64 57.607l-5.666 13.68c.096.072.188.15.278.232l.133.126 5.261 5.262 5.262-5.262c.128-.127.261-.244.4-.35L64 57.607Zm0-34.69a8 8 0 1 1 0 16 8 8 0 0 1 0-16Z"/>
|
||||
</$list>
|
||||
<$list filter="[{$:/state/http-requests}!match[0]]" variable="ignore">
|
||||
<path d="M109.395.952a4.002 4.002 0 0 1 3.787 2.708C117.529 11.62 120 20.753 120 30.462c0 15.186-6.044 28.96-15.858 39.047a4 4 0 1 1-6.47-4.626l-.12-.094C106.466 56.074 112 43.914 112 30.462c0-8.492-2.205-16.469-6.074-23.39l.054-.036a4 4 0 0 1 3.415-6.084Zm-90.762 0a4 4 0 0 1 3.072 6.562l.093.06A47.786 47.786 0 0 0 16 30.463c0 13.315 5.42 25.363 14.176 34.058l-.01.007a4 4 0 1 1-6.312 4.863l-.063.05C14.017 59.359 8 45.613 8 30.462c0-9.77 2.502-18.956 6.9-26.952A4.002 4.002 0 0 1 18.634.952Z"/><path d="M64.043 44.698a4.002 4.002 0 0 1 4.367 2.21l.084.188 30.403 73.4a4 4 0 0 1-7.307 3.25l-.084-.188-3.103-7.49-8.898 8.9a3.985 3.985 0 0 1-2.624 1.166l-.205.005a3.987 3.987 0 0 1-2.828-1.172l-9.849-9.848-9.847 9.848a3.985 3.985 0 0 1-2.624 1.167l-.204.005a3.987 3.987 0 0 1-2.829-1.172l-8.899-8.899-3.102 7.49a4 4 0 0 1-7.391-3.061l30.403-73.4a4.001 4.001 0 0 1 4.495-2.39l.042-.009ZM77.68 101.44l-8.023 8.023 7.02 7.019 8.023-8.022-7.02-7.02Zm-27.353.007-7.019 7.019 8.016 8.016 7.019-7.019-8.016-8.016Zm13.68-13.68-8.023 8.023 8.016 8.016 8.023-8.023-8.016-8.016Zm-8.971-8.971L50.348 90.11l8.001-8.001-3.314-3.314Zm17.933.009-3.305 3.305 7.979 7.979-4.674-11.284ZM64 57.152l-5.666 13.68c.096.073.188.15.278.232l.133.127 5.261 5.261 5.262-5.261c.128-.128.261-.244.4-.351L64 57.152ZM38.503 1.058a4 4 0 0 1 2.7 6.952l.17-.175C35.582 13.625 32 21.625 32 30.462c0 8.838 3.582 16.838 9.374 22.629a4 4 0 0 1-5.659 5.658l-.01.01C28.473 51.52 24 41.526 24 30.485 24 19.567 28.374 9.67 35.466 2.453a3.995 3.995 0 0 1 3.037-1.395ZM89.369.952c1.14 0 2.17.478 2.899 1.244l.005-.006C99.518 9.43 104 19.434 104 30.485c0 10.826-4.3 20.648-11.287 27.85a4 4 0 1 1-6.054-5.213l-.032-.032C92.418 47.299 96 39.299 96 30.462c0-8.73-3.496-16.643-9.164-22.416A4 4 0 0 1 89.368.952Zm-39.282 11.14a4 4 0 0 1 2.59 7.048l.01.009A15.95 15.95 0 0 0 48 30.462a15.95 15.95 0 0 0 4.687 11.315l-.01.01a4 4 0 1 1-5.82 5.47l.173.177A23.925 23.925 0 0 1 40 30.462a23.925 23.925 0 0 1 7.03-16.97l.01.01a3.991 3.991 0 0 1 3.047-1.41Zm27.895.07a3.99 3.99 0 0 1 2.984 1.336l.006-.005A23.925 23.925 0 0 1 88 30.463a23.92 23.92 0 0 1-6.707 16.642l-.3.305a4 4 0 1 1-5.679-5.632v-.002A15.95 15.95 0 0 0 80 30.462a15.95 15.95 0 0 0-4.685-11.312 4.012 4.012 0 0 1-1.333-2.987 4 4 0 0 1 4-4ZM64 22.463a8 8 0 1 1 0 16 8 8 0 0 1 0-16Z"/>
|
||||
</$list>
|
||||
</g></svg>
|
||||
@@ -1,4 +1,6 @@
|
||||
title: $:/core/images/new-journal-button
|
||||
tags: $:/tags/Image
|
||||
|
||||
<$parameters size="22pt" day=<<now "DD">>><svg width=<<size>> height=<<size>> class="tc-image-new-journal-button tc-image-button" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M102.545 112.818v11.818c0 1.306 1.086 2.364 2.425 2.364h6.06c1.34 0 2.425-1.058 2.425-2.364v-11.818h12.12c1.34 0 2.425-1.058 2.425-2.363v-5.91c0-1.305-1.085-2.363-2.424-2.363h-12.121V90.364c0-1.306-1.086-2.364-2.425-2.364h-6.06c-1.34 0-2.425 1.058-2.425 2.364v11.818h-12.12c-1.34 0-2.425 1.058-2.425 2.363v5.91c0 1.305 1.085 2.363 2.424 2.363h12.121zM60.016 4.965c-4.781-2.76-10.897-1.118-13.656 3.66L5.553 79.305A9.993 9.993 0 009.21 92.963l51.04 29.468c4.78 2.76 10.897 1.118 13.655-3.66l40.808-70.681a9.993 9.993 0 00-3.658-13.656L60.016 4.965zm-3.567 27.963a6 6 0 106-10.393 6 6 0 00-6 10.393zm31.697 17.928a6 6 0 106-10.392 6 6 0 00-6 10.392z"/><text class="tc-fill-background" font-family="Helvetica" font-size="47.172" font-weight="bold" transform="rotate(30 25.742 95.82)"><tspan x="42" y="77.485" text-anchor="middle"><$text text=<<day>>/></tspan></text></g></svg></$parameters>
|
||||
<$parameters size="22pt" day=<<now "DD">>>
|
||||
<svg width=<<size>> height=<<size>> class="tc-image-new-journal-button tc-image-button" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M102.545 112.818v11.818c0 1.306 1.086 2.364 2.425 2.364h6.06c1.34 0 2.425-1.058 2.425-2.364v-11.818h12.12c1.34 0 2.425-1.058 2.425-2.363v-5.91c0-1.305-1.085-2.363-2.424-2.363h-12.121V90.364c0-1.306-1.086-2.364-2.425-2.364h-6.06c-1.34 0-2.425 1.058-2.425 2.364v11.818h-12.12c-1.34 0-2.425 1.058-2.425 2.363v5.91c0 1.305 1.085 2.363 2.424 2.363h12.121zM60.016 4.965c-4.781-2.76-10.897-1.118-13.656 3.66L5.553 79.305A9.993 9.993 0 009.21 92.963l51.04 29.468c4.78 2.76 10.897 1.118 13.655-3.66l40.808-70.681a9.993 9.993 0 00-3.658-13.656L60.016 4.965zm-3.567 27.963a6 6 0 106-10.393 6 6 0 00-6 10.393zm31.697 17.928a6 6 0 106-10.392 6 6 0 00-6 10.392z"/><text class="tc-fill-background" font-family="Helvetica" font-size="47.172" font-weight="bold" transform="rotate(30 25.742 95.82)"><tspan x="42" y="77.485" text-anchor="middle"><$text text=<<day>>/></tspan></text></g></svg>
|
||||
</$parameters>
|
||||
@@ -67,8 +67,6 @@ More/Caption: more
|
||||
More/Hint: More actions
|
||||
NewHere/Caption: new here
|
||||
NewHere/Hint: Create a new tiddler tagged with this one
|
||||
NetworkActivity/Caption: network activity
|
||||
NetworkActivity/Hint: Cancel all network activity
|
||||
NewJournal/Caption: new journal
|
||||
NewJournal/Hint: Create a new journal tiddler
|
||||
NewJournalHere/Caption: new journal here
|
||||
|
||||
@@ -3,4 +3,4 @@ title: $:/language/Exporters/
|
||||
StaticRiver: Static HTML
|
||||
JsonFile: JSON file
|
||||
CsvFile: CSV file
|
||||
TidFile: TID text file
|
||||
TidFile: ".tid" file
|
||||
|
||||
@@ -4,7 +4,6 @@ _canonical_uri: The full URI of an external image tiddler
|
||||
author: Name of the author of a plugin
|
||||
bag: The name of the bag from which a tiddler came
|
||||
caption: The text to be displayed on a tab or button
|
||||
class: The CSS class applied to a tiddler when rendering it - see [[Custom styles by user-class]]. Also used for [[Modals]]
|
||||
code-body: The view template will display the tiddler as code if set to ''yes''
|
||||
color: The CSS color value associated with a tiddler
|
||||
component: The name of the component responsible for an [[alert tiddler|AlertMechanism]]
|
||||
|
||||
@@ -10,7 +10,7 @@ Sequentially run the command tokens returned from a filter
|
||||
Examples
|
||||
|
||||
```
|
||||
--commands "[enlist:raw{$:/build-commands-as-text}]"
|
||||
--commands "[enlist{$:/build-commands-as-text}]"
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -18,7 +18,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
|
||||
* ''anon-username'' - the username for signing edits for anonymous users
|
||||
* ''username'' - optional username for basic authentication
|
||||
* ''password'' - optional password for basic authentication
|
||||
* ''authenticated-user-header'' - optional name of request header to be used for trusted authentication.
|
||||
* ''authenticated-user-header'' - optional name of header to be used for trusted authentication
|
||||
* ''readers'' - comma-separated list of principals allowed to read from this wiki
|
||||
* ''writers'' - comma-separated list of principals allowed to write to this wiki
|
||||
* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no")
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Saves a wiki to a new wiki folder
|
||||
<<.from-version "5.1.20">> Saves the current wiki as a wiki folder, including tiddlers, plugins and configuration:
|
||||
|
||||
```
|
||||
--savewikifolder <wikifolderpath> [<filter>] [ [<name>=<value>] ]*
|
||||
--savewikifolder <wikifolderpath> [<filter>]
|
||||
```
|
||||
|
||||
* The target wiki folder must be empty or non-existent
|
||||
@@ -12,23 +12,8 @@ description: Saves a wiki to a new wiki folder
|
||||
* Plugins from the official plugin library are replaced with references to those plugins in the `tiddlywiki.info` file
|
||||
* Custom plugins are unpacked into their own folder
|
||||
|
||||
The following options are supported:
|
||||
|
||||
* ''filter'': a filter expression that defines the tiddlers to include in the output.
|
||||
* ''explodePlugins'': defaults to "yes"
|
||||
** ''yes'' will "explode" plugins into separate tiddler files and save them to the plugin directory within the wiki folder
|
||||
** ''no'' will suppress exploding plugins into their constituent tiddler files. It will save the plugin as a single JSON tiddler in the tiddlers folder
|
||||
|
||||
Note that both ''explodePlugins'' options will produce wiki folders that build the exact same original wiki. The difference lies in how plugins are represented in the wiki folder.
|
||||
|
||||
A common usage is to convert a TiddlyWiki HTML file into a wiki folder:
|
||||
|
||||
```
|
||||
tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder
|
||||
```
|
||||
|
||||
Save the plugin to the tiddlers directory of the target wiki folder:
|
||||
|
||||
```
|
||||
tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder explodePlugins=no
|
||||
```
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
title: $:/language/Help/server
|
||||
description: (deprecated: see 'listen' command) Provides an HTTP server interface to TiddlyWiki
|
||||
description: Provides an HTTP server interface to TiddlyWiki (deprecated in favour of the new listen command)
|
||||
|
||||
Legacy command to serve a wiki over HTTP.
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ Encryption/RepeatPassword: Repeat password
|
||||
Encryption/PasswordNoMatch: Passwords do not match
|
||||
Encryption/SetPassword: Set password
|
||||
Error/Caption: Error
|
||||
Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'deserialize' operator
|
||||
Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator
|
||||
Error/Filter: Filter error
|
||||
Error/FilterSyntax: Syntax error in filter expression
|
||||
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
title: $:/SiteTitle
|
||||
|
||||
My TiddlyWiki
|
||||
My ~TiddlyWiki
|
||||
@@ -1,5 +1,5 @@
|
||||
title: $:/language/Docs/Types/image/svg+xml
|
||||
description: SVG image
|
||||
description: Structured Vector Graphics image
|
||||
name: image/svg+xml
|
||||
group: Image
|
||||
group-sort: 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
title: $:/language/Docs/Types/image/x-icon
|
||||
description: ICO icon
|
||||
description: ICO format icon file
|
||||
name: image/x-icon
|
||||
group: Image
|
||||
group-sort: 1
|
||||
|
||||
@@ -46,7 +46,7 @@ Command.prototype.execute = function() {
|
||||
type = tiddler.fields.type || "text/vnd.tiddlywiki",
|
||||
contentTypeInfo = $tw.config.contentTypeInfo[type] || {encoding: "utf8"},
|
||||
filename = path.resolve(pathname,$tw.utils.encodeURIComponentExtended(title));
|
||||
fs.writeFileSync(filename,tiddler.fields.text || "",contentTypeInfo.encoding);
|
||||
fs.writeFileSync(filename,tiddler.fields.text,contentTypeInfo.encoding);
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -5,14 +5,7 @@ module-type: command
|
||||
|
||||
Command to save the current wiki as a wiki folder
|
||||
|
||||
--savewikifolder <wikifolderpath> [ [<name>=<value>] ]*
|
||||
|
||||
The following options are supported:
|
||||
|
||||
* ''filter'': a filter expression defining the tiddlers to be included in the output
|
||||
* ''explodePlugins'': set to "no" to suppress exploding plugins into their constituent shadow tiddlers (defaults to "yes")
|
||||
|
||||
Supports backward compatibility with --savewikifolder <wikifolderpath> [<filter>] [ [<name>=<value>] ]*
|
||||
--savewikifolder <wikifolderpath> [<filter>]
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@@ -42,28 +35,14 @@ Command.prototype.execute = function() {
|
||||
if(this.params.length < 1) {
|
||||
return "Missing wiki folder path";
|
||||
}
|
||||
var regFilter = /^[a-zA-Z0-9\.\-_]+=/g, // dynamic parameters
|
||||
namedParames,
|
||||
tiddlerFilter,
|
||||
options = {};
|
||||
if (regFilter.test(this.params[1])) {
|
||||
namedParames = this.commander.extractNamedParameters(this.params.slice(1));
|
||||
tiddlerFilter = namedParames.filter || "[all[tiddlers]]";
|
||||
} else {
|
||||
namedParames = this.commander.extractNamedParameters(this.params.slice(2));
|
||||
tiddlerFilter = this.params[1];
|
||||
}
|
||||
tiddlerFilter = tiddlerFilter || "[all[tiddlers]]";
|
||||
options.explodePlugins = namedParames.explodePlugins || "yes";
|
||||
var wikifoldermaker = new WikiFolderMaker(this.params[0],tiddlerFilter,this.commander,options);
|
||||
var wikifoldermaker = new WikiFolderMaker(this.params[0],this.params[1],this.commander);
|
||||
return wikifoldermaker.save();
|
||||
};
|
||||
|
||||
function WikiFolderMaker(wikiFolderPath,wikiFilter,commander,options) {
|
||||
function WikiFolderMaker(wikiFolderPath,wikiFilter,commander) {
|
||||
this.wikiFolderPath = wikiFolderPath;
|
||||
this.wikiFilter = wikiFilter;
|
||||
this.wikiFilter = wikiFilter || "[all[tiddlers]]";
|
||||
this.commander = commander;
|
||||
this.explodePlugins = options.explodePlugins;
|
||||
this.wiki = commander.wiki;
|
||||
this.savedPaths = []; // So that we can detect filename clashes
|
||||
}
|
||||
@@ -114,13 +93,10 @@ WikiFolderMaker.prototype.save = function() {
|
||||
self.log("Adding built-in plugin: " + libraryDetails.name);
|
||||
newWikiInfo[libraryDetails.type] = newWikiInfo[libraryDetails.type] || [];
|
||||
$tw.utils.pushTop(newWikiInfo[libraryDetails.type],libraryDetails.name);
|
||||
} else if(self.explodePlugins !== "no") {
|
||||
} else {
|
||||
// A custom plugin
|
||||
self.log("Processing custom plugin: " + title);
|
||||
self.saveCustomPlugin(tiddler);
|
||||
} else if(self.explodePlugins === "no") {
|
||||
self.log("Processing custom plugin to tiddlders folder: " + title);
|
||||
self.saveTiddler("tiddlers", tiddler);
|
||||
}
|
||||
} else {
|
||||
// Ordinary tiddler
|
||||
|
||||
@@ -60,7 +60,7 @@ function FramedEngine(options) {
|
||||
this.domNode.value = this.value;
|
||||
}
|
||||
// Set the attributes
|
||||
if(this.widget.editType && this.widget.editTag !== "textarea") {
|
||||
if(this.widget.editType) {
|
||||
this.domNode.setAttribute("type",this.widget.editType);
|
||||
}
|
||||
if(this.widget.editPlaceholder) {
|
||||
@@ -118,8 +118,6 @@ FramedEngine.prototype.copyStyles = function() {
|
||||
this.domNode.style.margin = "0";
|
||||
// In Chrome setting -webkit-text-fill-color overrides the placeholder text colour
|
||||
this.domNode.style["-webkit-text-fill-color"] = "currentcolor";
|
||||
// Ensure we don't force text direction to LTR
|
||||
this.domNode.style.removeProperty("direction");
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -34,7 +34,7 @@ function SimpleEngine(options) {
|
||||
this.domNode.value = this.value;
|
||||
}
|
||||
// Set the attributes
|
||||
if(this.widget.editType && this.widget.editTag !== "textarea") {
|
||||
if(this.widget.editType) {
|
||||
this.domNode.setAttribute("type",this.widget.editType);
|
||||
}
|
||||
if(this.widget.editPlaceholder) {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/then.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Replace results of previous runs unless empty
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.then = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length !== 0) {
|
||||
// Only run if previous run(s) produced results
|
||||
var thisRunResult = operationSubFunction(source,widget);
|
||||
if(thisRunResult.length !== 0) {
|
||||
// Replace results only if this run actually produces a result
|
||||
results.clear();
|
||||
results.pushTop(thisRunResult);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -12,8 +12,6 @@ Adds tiddler filtering methods to the $tw.Wiki object.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widgetClass = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
/* Maximum permitted filter recursion depth */
|
||||
var MAX_FILTER_DEPTH = 300;
|
||||
|
||||
@@ -271,7 +269,7 @@ exports.compileFilter = function(filterString) {
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
} else if(operand.variable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,12 @@ function getAllFilterOperators() {
|
||||
Export our filter function
|
||||
*/
|
||||
exports.all = function(source,operator,options) {
|
||||
// Get our suboperators
|
||||
var allFilterOperators = getAllFilterOperators();
|
||||
// Cycle through the suboperators accumulating their results
|
||||
var results = new $tw.utils.LinkedList(),
|
||||
subops = operator.operand.split("+");
|
||||
// Check for common optimisations
|
||||
var subops = operator.operand.split("+");
|
||||
if(subops.length === 1 && subops[0] === "") {
|
||||
return source;
|
||||
} else if(subops.length === 1 && subops[0] === "tiddlers") {
|
||||
@@ -42,10 +46,6 @@ exports.all = function(source,operator,options) {
|
||||
return options.wiki.eachShadowPlusTiddlers;
|
||||
}
|
||||
// Do it the hard way
|
||||
// Get our suboperators
|
||||
var allFilterOperators = getAllFilterOperators();
|
||||
// Cycle through the suboperators accumulating their results
|
||||
var results = new $tw.utils.LinkedList();
|
||||
for(var t=0; t<subops.length; t++) {
|
||||
var subop = allFilterOperators[subops[t]];
|
||||
if(subop) {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/deserialize.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
Filter operator for deserializing string data into JSON representing tiddlers
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["deserialize"] = function(source,operator,options) {
|
||||
var results = [],
|
||||
deserializer;
|
||||
if(operator.operand) {
|
||||
// Get the deserializer identified by the operand
|
||||
deserializer = $tw.Wiki.tiddlerDeserializerModules[operator.operand];
|
||||
if(deserializer) {
|
||||
source(function(tiddler,title) {
|
||||
var tiddlers;
|
||||
try {
|
||||
tiddlers = deserializer(title);
|
||||
} catch(e) {
|
||||
// Return an empty array if we could not extract any tiddlers
|
||||
tiddlers = [];
|
||||
}
|
||||
results.push(JSON.stringify(tiddlers));
|
||||
});
|
||||
} else {
|
||||
return [$tw.language.getString("Error/DeserializeOperator/UnknownDeserializer")];
|
||||
}
|
||||
} else {
|
||||
return [$tw.language.getString("Error/DeserializeOperator/MissingOperand")];
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
})();
|
||||
@@ -18,20 +18,16 @@ Export our filter functions
|
||||
|
||||
exports.decodebase64 = function(source,operator,options) {
|
||||
var results = [];
|
||||
var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1;
|
||||
var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1;
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.base64Decode(title,binary,urlsafe));
|
||||
results.push($tw.utils.base64Decode(title));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.encodebase64 = function(source,operator,options) {
|
||||
var results = [];
|
||||
var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1;
|
||||
var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1;
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.base64Encode(title,binary,urlsafe));
|
||||
results.push($tw.utils.base64Encode(title));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/format/timestamp.js
|
||||
type: application/javascript
|
||||
module-type: formatfilteroperator
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.timestamp = function(source,operand,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
if (title.match(/^-?\d+$/)) {
|
||||
var value = new Date(Number(title));
|
||||
results.push($tw.utils.formatDateString(value,operand || "[UTC]YYYY0MM0DD0hh0mm0ss0XXX"));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
})();
|
||||
@@ -17,13 +17,9 @@ Export our filter function
|
||||
*/
|
||||
exports.function = function(source,operator,options) {
|
||||
var functionName = operator.operands[0],
|
||||
params = [];
|
||||
$tw.utils.each(operator.operands.slice(1),function(param) {
|
||||
params.push({value: param});
|
||||
});
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
|
||||
variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName);
|
||||
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
|
||||
return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source});
|
||||
}
|
||||
// Return the input list if the function wasn't found
|
||||
var results = [];
|
||||
|
||||
@@ -68,54 +68,6 @@ exports["jsontype"] = function(source,operator,options) {
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsonset"] = function(source,operator,options) {
|
||||
var suffixes = operator.suffixes || [],
|
||||
type = suffixes[0] && suffixes[0][0],
|
||||
indexes = operator.operands.slice(0,-1),
|
||||
value = operator.operands[operator.operands.length - 1],
|
||||
results = [];
|
||||
if(operator.operands.length === 1 && operator.operands[0] === "") {
|
||||
value = undefined; // Prevents the value from being assigned
|
||||
}
|
||||
switch(type) {
|
||||
case "string":
|
||||
// Use value unchanged
|
||||
break;
|
||||
case "boolean":
|
||||
value = (value === "true" ? true : (value === "false" ? false : undefined));
|
||||
break;
|
||||
case "number":
|
||||
value = $tw.utils.parseNumber(value);
|
||||
break;
|
||||
case "array":
|
||||
indexes = operator.operands;
|
||||
value = [];
|
||||
break;
|
||||
case "object":
|
||||
indexes = operator.operands;
|
||||
value = {};
|
||||
break;
|
||||
case "null":
|
||||
indexes = operator.operands;
|
||||
value = null;
|
||||
break;
|
||||
case "json":
|
||||
value = $tw.utils.parseJSONSafe(value,function() {return undefined;});
|
||||
break;
|
||||
default:
|
||||
// Use value unchanged
|
||||
break;
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
data = setDataItem(data,indexes,value);
|
||||
results.push(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -213,18 +165,6 @@ function getDataItemType(data,indexes) {
|
||||
}
|
||||
}
|
||||
|
||||
function getItemAtIndex(item,index) {
|
||||
if($tw.utils.hop(item,index)) {
|
||||
return item[index];
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
index = $tw.utils.parseInt(index);
|
||||
if(index < 0) { index = index + item.length };
|
||||
return item[index]; // Will be undefined if index was out-of-bounds
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -237,7 +177,7 @@ function getDataItem(data,indexes) {
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
if(item !== undefined) {
|
||||
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
|
||||
item = getItemAtIndex(item,indexes[i]);
|
||||
item = item[indexes[i]];
|
||||
} else {
|
||||
item = undefined;
|
||||
}
|
||||
@@ -246,39 +186,5 @@ function getDataItem(data,indexes) {
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
|
||||
*/
|
||||
function setDataItem(data,indexes,value) {
|
||||
// Ignore attempts to assign undefined
|
||||
if(value === undefined) {
|
||||
return data;
|
||||
}
|
||||
// Check for the root item
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return value;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain
|
||||
var current = data;
|
||||
for(var i = 0; i < indexes.length - 1; i++) {
|
||||
current = getItemAtIndex(current,indexes[i]);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
}
|
||||
// Add the value to the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if($tw.utils.isArray(current)) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
}
|
||||
// Only set indexes on objects and arrays
|
||||
if(typeof current === "object") {
|
||||
current[lastIndex] = value;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -58,7 +58,6 @@ Last entry/entries in list
|
||||
exports.last = function(source,operator,options) {
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
if(count === 0) return results;
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/substitute.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for substituting variables and embedded filter expressions with their corresponding values
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.substitute = function(source,operator,options) {
|
||||
var results = [],
|
||||
operands = [];
|
||||
$tw.utils.each(operator.operands,function(operand,index){
|
||||
operands.push({
|
||||
name: (index + 1).toString(),
|
||||
value: operand
|
||||
});
|
||||
});
|
||||
source(function(tiddler,title) {
|
||||
if(title) {
|
||||
results.push(options.wiki.getSubstitutedText(title,options.widget,{substitutions:operands}));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -21,14 +21,10 @@ Export our filter function
|
||||
*/
|
||||
exports["[unknown]"] = function(source,operator,options) {
|
||||
// Check for a user defined filter operator
|
||||
if(operator.operator.indexOf(".") !== -1) {
|
||||
var params = [];
|
||||
$tw.utils.each(operator.operands,function(param) {
|
||||
params.push({value: param});
|
||||
});
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
|
||||
if(variableInfo && variableInfo.srcVariable) {
|
||||
var list = variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
|
||||
if(operator.operator.charAt(0) === ".") {
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator);
|
||||
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source});
|
||||
if(operator.prefix === "!") {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
|
||||
@@ -305,11 +305,10 @@ exports.parseAttribute = function(source,pos) {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexps
|
||||
var reAttributeName = /([^\/\s>"'`=]+)/g,
|
||||
reUnquotedAttribute = /([^\/\s<>"'`=]+)/g,
|
||||
var reAttributeName = /([^\/\s>"'=]+)/g,
|
||||
reUnquotedAttribute = /([^\/\s<>"'=]+)/g,
|
||||
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g,
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/g,
|
||||
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g;
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the attribute name
|
||||
@@ -362,15 +361,8 @@ exports.parseAttribute = function(source,pos) {
|
||||
node.type = "macro";
|
||||
node.value = macroInvocation;
|
||||
} else {
|
||||
var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
|
||||
if(substitutedValue) {
|
||||
pos = substitutedValue.end;
|
||||
node.type = "substituted";
|
||||
node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@ The plain text parser processes blocks of source text into a degenerate parse tr
|
||||
|
||||
var TextParser = function(type,text,options) {
|
||||
this.tree = [{
|
||||
type: "genesis",
|
||||
type: "codeblock",
|
||||
attributes: {
|
||||
$type: {name: "$type", type: "string", value: "$codeblock"},
|
||||
code: {name: "code", type: "string", value: text},
|
||||
language: {name: "language", type: "string", value: type},
|
||||
$remappable: {name: "$remappable", type:"string", value: "no"}
|
||||
code: {type: "string", value: text},
|
||||
language: {type: "string", value: type}
|
||||
}
|
||||
}];
|
||||
this.source = text;
|
||||
@@ -34,3 +32,4 @@ exports["text/css"] = TextParser;
|
||||
exports["application/x-tiddler-dictionary"] = TextParser;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/conditional.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Conditional shortcut syntax
|
||||
|
||||
```
|
||||
This is a <% if [{something}] %>Elephant<% elseif [{else}] %>Pelican<% else %>Crocodile<% endif %>
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "conditional";
|
||||
exports.types = {inline: true, block: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /\<\%\s*if\s+/mg;
|
||||
this.terminateIfRegExp = /\%\>/mg;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
// Look for the next <% if shortcut
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
// If not found then return no match
|
||||
if(!this.match) {
|
||||
return undefined;
|
||||
}
|
||||
// Check for the next %>
|
||||
this.terminateIfRegExp.lastIndex = this.match.index;
|
||||
this.terminateIfMatch = this.terminateIfRegExp.exec(this.parser.source);
|
||||
// If not found then return no match
|
||||
if(!this.terminateIfMatch) {
|
||||
return undefined;
|
||||
}
|
||||
// Return the position at which the construction was found
|
||||
return this.match.index;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get the filter condition
|
||||
var filterCondition = this.parser.source.substring(this.match.index + this.match[0].length,this.terminateIfMatch.index);
|
||||
// Advance the parser position to past the %>
|
||||
this.parser.pos = this.terminateIfMatch.index + this.terminateIfMatch[0].length;
|
||||
// Parse the if clause
|
||||
return this.parseIfClause(filterCondition);
|
||||
};
|
||||
|
||||
exports.parseIfClause = function(filterCondition) {
|
||||
// Create the list widget
|
||||
var listWidget = {
|
||||
type: "list",
|
||||
tag: "$list",
|
||||
isBlock: this.is.block,
|
||||
children: [
|
||||
{
|
||||
type: "list-template",
|
||||
tag: "$list-template"
|
||||
},
|
||||
{
|
||||
type: "list-empty",
|
||||
tag: "$list-empty"
|
||||
}
|
||||
]
|
||||
};
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"filter",filterCondition);
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"variable","condition");
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"limit","1");
|
||||
// Check for an immediately following double linebreak
|
||||
var hasLineBreak = !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
|
||||
// Parse the body looking for else or endif
|
||||
var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>|\\<\\%\\s*(else)\\s*\\%\\>|\\<\\%\\s*(elseif)\\s+([\\s\\S]+?)\\%\\>",
|
||||
ex;
|
||||
if(hasLineBreak) {
|
||||
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
|
||||
} else {
|
||||
var reEnd = new RegExp(reEndString,"mg");
|
||||
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
|
||||
}
|
||||
// Put the body into the list template
|
||||
listWidget.children[0].children = ex.tree;
|
||||
// Check for an else or elseif
|
||||
if(ex.match) {
|
||||
if(ex.match[1] === "endif") {
|
||||
// Nothing to do if we just found an endif
|
||||
} else if(ex.match[2] === "else") {
|
||||
// Check for an immediately following double linebreak
|
||||
hasLineBreak = !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
|
||||
// If we found an else then we need to parse the body looking for the endif
|
||||
var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>",
|
||||
ex;
|
||||
if(hasLineBreak) {
|
||||
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
|
||||
} else {
|
||||
var reEnd = new RegExp(reEndString,"mg");
|
||||
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
|
||||
}
|
||||
// Put the parsed content inside the list empty template
|
||||
listWidget.children[1].children = ex.tree;
|
||||
} else if(ex.match[3] === "elseif") {
|
||||
// Parse the elseif clause by reusing this parser, passing the new filter condition
|
||||
listWidget.children[1].children = this.parseIfClause(ex.match[4]);
|
||||
}
|
||||
}
|
||||
// Return the parse tree node
|
||||
return [listWidget];
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -35,7 +35,7 @@ Instantiate parse rule
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg;
|
||||
this.matchRegExp = /^\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -49,11 +49,11 @@ exports.parse = function() {
|
||||
if(this.match[3]) {
|
||||
params = $tw.utils.parseParameterDefinition(this.match[4]);
|
||||
}
|
||||
// Is the remainder of the line blank after the parameter close paren?
|
||||
// Is this a multiline definition?
|
||||
var reEnd;
|
||||
if(this.match[5]) {
|
||||
// If so, it is a multiline definition and the end of the body is marked with \end
|
||||
reEnd = new RegExp("((:?^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
|
||||
// If so, the end of the body is marked with \end
|
||||
reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
|
||||
} else {
|
||||
// Otherwise, the end of the definition is marked by the end of the line
|
||||
reEnd = /($|\r?\n)/mg;
|
||||
|
||||
@@ -78,7 +78,7 @@ exports.parseTag = function(source,pos,options) {
|
||||
orderedAttributes: []
|
||||
};
|
||||
// Define our regexps
|
||||
var reTagName = /([a-zA-Z0-9\-\$\.]+)/g;
|
||||
var reTagName = /([a-zA-Z0-9\-\$]+)/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a less than sign
|
||||
@@ -138,7 +138,7 @@ exports.parseTag = function(source,pos,options) {
|
||||
|
||||
exports.findNextTag = function(source,pos,options) {
|
||||
// A regexp for finding candidate HTML tags
|
||||
var reLookahead = /<([a-zA-Z\-\$\.]+)/g;
|
||||
var reLookahead = /<([a-zA-Z\-\$]+)/g;
|
||||
// Find the next candidate
|
||||
reLookahead.lastIndex = pos;
|
||||
var match = reLookahead.exec(source);
|
||||
|
||||
@@ -54,11 +54,11 @@ exports.parse = function() {
|
||||
paramMatch = reParam.exec(paramString);
|
||||
}
|
||||
}
|
||||
// Is the remainder of the \define line blank after the parameter close paren?
|
||||
// Is this a multiline definition?
|
||||
var reEnd;
|
||||
if(this.match[3]) {
|
||||
// If so, it is a multiline definition and the end of the body is marked with \end
|
||||
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
|
||||
// If so, the end of the body is marked with \end
|
||||
reEnd = new RegExp("(\\r?\\n[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
|
||||
} else {
|
||||
// Otherwise, the end of the definition is marked by the end of the line
|
||||
reEnd = /($|\r?\n)/mg;
|
||||
|
||||
@@ -26,7 +26,7 @@ Instantiate parse rule
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /\\parameters\s*\(([^)]*)\)(\s*\r?\n)?/mg;
|
||||
this.matchRegExp = /^\\parameters\s*\(([^)]*)\)\s*\r?\n/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/wikilinkprefix.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki text inline rule for suppressed wiki links. For example:
|
||||
|
||||
```
|
||||
~SuppressedLink
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "wikilinkprefix";
|
||||
exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = new RegExp($tw.config.textPrimitives.unWikiLink + $tw.config.textPrimitives.wikiLink,"mg");
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get the details of the match
|
||||
var linkText = this.match[0];
|
||||
// Move past the wikilink
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Return the link without unwikilink character as plain text
|
||||
return [{type: "text", text: linkText.substr(1)}];
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -194,7 +194,6 @@ Parse any pragmas at the beginning of a block of parse text
|
||||
WikiParser.prototype.parsePragmas = function() {
|
||||
var currentTreeBranch = this.tree;
|
||||
while(true) {
|
||||
var savedPos = this.pos;
|
||||
// Skip whitespace
|
||||
this.skipWhitespace();
|
||||
// Check for the end of the text
|
||||
@@ -205,7 +204,6 @@ WikiParser.prototype.parsePragmas = function() {
|
||||
var nextMatch = this.findNextMatch(this.pragmaRules,this.pos);
|
||||
// If not, just exit
|
||||
if(!nextMatch || nextMatch.matchIndex !== this.pos) {
|
||||
this.pos = savedPos;
|
||||
break;
|
||||
}
|
||||
// Process the pragma rule
|
||||
@@ -216,8 +214,6 @@ WikiParser.prototype.parsePragmas = function() {
|
||||
subTree[0].children = [];
|
||||
currentTreeBranch = subTree[0].children;
|
||||
}
|
||||
// Skip whitespace after the pragma
|
||||
this.skipWhitespace();
|
||||
}
|
||||
return currentTreeBranch;
|
||||
};
|
||||
@@ -227,7 +223,7 @@ Parse a block from the current position
|
||||
terminatorRegExpString: optional regular expression string that identifies the end of plain paragraphs. Must not include capturing parenthesis
|
||||
*/
|
||||
WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
|
||||
var terminatorRegExp = terminatorRegExpString ? new RegExp(terminatorRegExpString + "|\\r?\\n\\r?\\n","mg") : /(\r?\n\r?\n)/mg;
|
||||
var terminatorRegExp = terminatorRegExpString ? new RegExp("(" + terminatorRegExpString + "|\\r?\\n\\r?\\n)","mg") : /(\r?\n\r?\n)/mg;
|
||||
this.skipWhitespace();
|
||||
if(this.pos >= this.sourceLength) {
|
||||
return [];
|
||||
@@ -267,22 +263,12 @@ WikiParser.prototype.parseBlocksUnterminated = function() {
|
||||
return tree;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse blocks of text until a terminating regexp is encountered. Wrapper for parseBlocksTerminatedExtended that just returns the parse tree
|
||||
*/
|
||||
WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
|
||||
var ex = this.parseBlocksTerminatedExtended(terminatorRegExpString);
|
||||
return ex.tree;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse blocks of text until a terminating regexp is encountered
|
||||
*/
|
||||
WikiParser.prototype.parseBlocksTerminatedExtended = function(terminatorRegExpString) {
|
||||
var terminatorRegExp = new RegExp(terminatorRegExpString,"mg"),
|
||||
result = {
|
||||
tree: []
|
||||
};
|
||||
WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
|
||||
var terminatorRegExp = new RegExp("(" + terminatorRegExpString + ")","mg"),
|
||||
tree = [];
|
||||
// Skip any whitespace
|
||||
this.skipWhitespace();
|
||||
// Check if we've got the end marker
|
||||
@@ -291,7 +277,7 @@ WikiParser.prototype.parseBlocksTerminatedExtended = function(terminatorRegExpSt
|
||||
// Parse the text into blocks
|
||||
while(this.pos < this.sourceLength && !(match && match.index === this.pos)) {
|
||||
var blocks = this.parseBlock(terminatorRegExpString);
|
||||
result.tree.push.apply(result.tree,blocks);
|
||||
tree.push.apply(tree,blocks);
|
||||
// Skip any whitespace
|
||||
this.skipWhitespace();
|
||||
// Check if we've got the end marker
|
||||
@@ -300,9 +286,8 @@ WikiParser.prototype.parseBlocksTerminatedExtended = function(terminatorRegExpSt
|
||||
}
|
||||
if(match && match.index === this.pos) {
|
||||
this.pos = match.index + match[0].length;
|
||||
result.match = match;
|
||||
}
|
||||
return result;
|
||||
return tree;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -345,11 +330,6 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
|
||||
};
|
||||
|
||||
WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,options) {
|
||||
var ex = this.parseInlineRunTerminatedExtended(terminatorRegExp,options);
|
||||
return ex.tree;
|
||||
};
|
||||
|
||||
WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegExp,options) {
|
||||
options = options || {};
|
||||
var tree = [];
|
||||
// Find the next occurrence of the terminator
|
||||
@@ -369,10 +349,7 @@ WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegEx
|
||||
if(options.eatTerminator) {
|
||||
this.pos += terminatorMatch[0].length;
|
||||
}
|
||||
return {
|
||||
match: terminatorMatch,
|
||||
tree: tree
|
||||
};
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
// Process any inline rule, along with the text preceding it
|
||||
@@ -396,9 +373,7 @@ WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegEx
|
||||
this.pushTextWidget(tree,this.source.substr(this.pos),this.pos,this.sourceLength);
|
||||
}
|
||||
this.pos = this.sourceLength;
|
||||
return {
|
||||
tree: tree
|
||||
};
|
||||
return tree;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -31,7 +31,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Authorization": "Basic " + $tw.utils.base64Encode(username + ":" + password),
|
||||
"Authorization": "Basic " + window.btoa(username + ":" + password),
|
||||
"If-None-Match": ""
|
||||
};
|
||||
// Bail if we don't have everything we need
|
||||
|
||||
@@ -37,7 +37,7 @@ HeaderAuthenticator.prototype.authenticateRequest = function(request,response,st
|
||||
return false;
|
||||
} else {
|
||||
// authenticatedUsername will be undefined for anonymous users
|
||||
state.authenticatedUsername = $tw.utils.decodeURIComponentSafe(username);
|
||||
state.authenticatedUsername = username;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ Startup logic concerned with managing plugins
|
||||
// Export name and synchronous status
|
||||
exports.name = "plugins";
|
||||
exports.after = ["load-modules"];
|
||||
exports.before = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
var TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE = "$:/status/RequireReloadDueToPluginChange";
|
||||
|
||||
@@ -20,39 +20,6 @@ exports.before = ["story"];
|
||||
exports.synchronous = true;
|
||||
|
||||
exports.startup = function() {
|
||||
// Install the HTTP client event handler
|
||||
$tw.httpClient = new $tw.utils.HttpClient();
|
||||
var getPropertiesWithPrefix = function(properties,prefix) {
|
||||
var result = Object.create(null);
|
||||
$tw.utils.each(properties,function(value,name) {
|
||||
if(name.indexOf(prefix) === 0) {
|
||||
result[name.substring(prefix.length)] = properties[name];
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
$tw.rootWidget.addEventListener("tm-http-request",function(event) {
|
||||
var params = event.paramObject || {};
|
||||
$tw.httpClient.initiateHttpRequest({
|
||||
wiki: event.widget.wiki,
|
||||
url: params.url,
|
||||
method: params.method,
|
||||
body: params.body,
|
||||
binary: params.binary,
|
||||
oncompletion: params.oncompletion,
|
||||
onprogress: params.onprogress,
|
||||
bindStatus: params["bind-status"],
|
||||
bindProgress: params["bind-progress"],
|
||||
variables: getPropertiesWithPrefix(params,"var-"),
|
||||
headers: getPropertiesWithPrefix(params,"header-"),
|
||||
passwordHeaders: getPropertiesWithPrefix(params,"password-header-"),
|
||||
queryStrings: getPropertiesWithPrefix(params,"query-"),
|
||||
passwordQueryStrings: getPropertiesWithPrefix(params,"password-query-")
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tm-http-cancel-all-requests",function(event) {
|
||||
$tw.httpClient.cancelAllHttpRequests();
|
||||
});
|
||||
// Install the modal message mechanism
|
||||
$tw.modal = new $tw.utils.Modal($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tm-modal",function(event) {
|
||||
@@ -74,8 +41,12 @@ exports.startup = function() {
|
||||
$tw.rootWidget.addEventListener("tm-focus-selector",function(event) {
|
||||
var selector = event.param || "",
|
||||
element,
|
||||
baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document;
|
||||
element = $tw.utils.querySelectorSafe(selector,baseElement);
|
||||
doc = event.event && event.event.target ? event.event.target.ownerDocument : document;
|
||||
try {
|
||||
element = doc.querySelector(selector);
|
||||
} catch(e) {
|
||||
console.log("Error in selector: ",selector)
|
||||
}
|
||||
if(element && element.focus) {
|
||||
element.focus(event.paramObject);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ exports.startup = function() {
|
||||
if($tw.browser) {
|
||||
$tw.browser.isIE = (/msie|trident/i.test(navigator.userAgent));
|
||||
$tw.browser.isFirefox = !!document.mozFullScreenEnabled;
|
||||
// 2023-07-21 Edge returns UA below. So we use "isChromeLike"
|
||||
//'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/114.0.0.0 safari/537.36 edg/114.0.1823.82'
|
||||
$tw.browser.isChromeLike = navigator.userAgent.toLowerCase().indexOf("chrome") > -1;
|
||||
$tw.browser.hasTouch = !!window.matchMedia && window.matchMedia("(pointer: coarse)").matches;
|
||||
$tw.browser.isMobileChrome = $tw.browser.isChromeLike && $tw.browser.hasTouch;
|
||||
}
|
||||
// Platform detection
|
||||
$tw.platform = {};
|
||||
|
||||
@@ -40,7 +40,7 @@ exports.startup = function() {
|
||||
variables = $tw.utils.extend({},paramObject,{currentTiddler: title, "tv-window-id": windowID});
|
||||
// Open the window
|
||||
var srcWindow,
|
||||
srcDocument;
|
||||
srcDocument;
|
||||
// In case that popup blockers deny opening a new window
|
||||
try {
|
||||
srcWindow = window.open("","external-" + windowID,"scrollbars,width=" + width + ",height=" + height + (top ? ",top=" + top : "" ) + (left ? ",left=" + left : "" )),
|
||||
@@ -52,7 +52,6 @@ exports.startup = function() {
|
||||
$tw.windows[windowID] = srcWindow;
|
||||
// Check for reopening the same window
|
||||
if(srcWindow.haveInitialisedWindow) {
|
||||
srcWindow.focus();
|
||||
return;
|
||||
}
|
||||
// Initialise the document
|
||||
|
||||
@@ -129,7 +129,7 @@ function findTitleDomNode(widget,targetClass) {
|
||||
targetClass = targetClass || "tc-title";
|
||||
var domNode = widget.findFirstDomNode();
|
||||
if(domNode && domNode.querySelector) {
|
||||
return $tw.utils.querySelectorSafe("." + targetClass,domNode);
|
||||
return domNode.querySelector("." + targetClass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
|
||||
Syncer.prototype.titleSyncDisableLazyLoading = "$:/config/SyncDisableLazyLoading";
|
||||
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
|
||||
Syncer.prototype.titleSyncThrottleInterval = "$:/config/SyncThrottleInterval";
|
||||
Syncer.prototype.taskTimerInterval = 0.25 * 1000; // Interval for sync timer
|
||||
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
|
||||
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
|
||||
Syncer.prototype.errorRetryInterval = 5 * 1000; // Interval to retry after an error
|
||||
Syncer.prototype.fallbackInterval = 10 * 1000; // Unless the task is older than 10s
|
||||
@@ -74,11 +74,9 @@ function Syncer(options) {
|
||||
this.titlesHaveBeenLazyLoaded = {}; // Hashmap of titles of tiddlers that have already been lazily loaded from the server
|
||||
// Timers
|
||||
this.taskTimerId = null; // Timer for task dispatch
|
||||
this.pollTimerId = null; // Timer for polling server
|
||||
// Number of outstanding requests
|
||||
this.numTasksInProgress = 0;
|
||||
// True when we want to force an immediate sync from the server
|
||||
this.forceSyncFromServer = false;
|
||||
this.timestampLastSyncFromServer = new Date();
|
||||
// Listen out for changes to tiddlers
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
// Filter the changes to just include ones that are being synced
|
||||
@@ -205,37 +203,33 @@ Syncer.prototype.readTiddlerInfo = function() {
|
||||
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
||||
*/
|
||||
Syncer.prototype.isDirty = function() {
|
||||
var self = this;
|
||||
function checkIsDirty() {
|
||||
// Check tiddlers that are in the store and included in the filter function
|
||||
var titles = self.getSyncedTiddlers();
|
||||
for(var index=0; index<titles.length; index++) {
|
||||
var title = titles[index],
|
||||
tiddlerInfo = self.tiddlerInfo[title];
|
||||
if(self.wiki.tiddlerExists(title)) {
|
||||
if(tiddlerInfo) {
|
||||
// If the tiddler is known on the server and has been modified locally then it needs to be saved to the server
|
||||
if(self.wiki.getChangeCount(title) > tiddlerInfo.changeCount) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// If the tiddler isn't known on the server then it needs to be saved to the server
|
||||
this.logger.log("Checking dirty status");
|
||||
// Check tiddlers that are in the store and included in the filter function
|
||||
var titles = this.getSyncedTiddlers();
|
||||
for(var index=0; index<titles.length; index++) {
|
||||
var title = titles[index],
|
||||
tiddlerInfo = this.tiddlerInfo[title];
|
||||
if(this.wiki.tiddlerExists(title)) {
|
||||
if(tiddlerInfo) {
|
||||
// If the tiddler is known on the server and has been modified locally then it needs to be saved to the server
|
||||
if(this.wiki.getChangeCount(title) > tiddlerInfo.changeCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check tiddlers that are known from the server but not currently in the store
|
||||
titles = Object.keys(self.tiddlerInfo);
|
||||
for(index=0; index<titles.length; index++) {
|
||||
if(!self.wiki.tiddlerExists(titles[index])) {
|
||||
// There must be a pending delete
|
||||
} else {
|
||||
// If the tiddler isn't known on the server then it needs to be saved to the server
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var dirtyStatus = checkIsDirty();
|
||||
return dirtyStatus;
|
||||
// Check tiddlers that are known from the server but not currently in the store
|
||||
titles = Object.keys(this.tiddlerInfo);
|
||||
for(index=0; index<titles.length; index++) {
|
||||
if(!this.wiki.tiddlerExists(titles[index])) {
|
||||
// There must be a pending delete
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -299,16 +293,92 @@ Syncer.prototype.getStatus = function(callback) {
|
||||
Synchronise from the server by reading the skinny tiddler list and queuing up loads for any tiddlers that we don't already have up to date
|
||||
*/
|
||||
Syncer.prototype.syncFromServer = function() {
|
||||
if(this.canSyncFromServer()) {
|
||||
this.forceSyncFromServer = true;
|
||||
this.processTaskQueue();
|
||||
var self = this,
|
||||
cancelNextSync = function() {
|
||||
if(self.pollTimerId) {
|
||||
clearTimeout(self.pollTimerId);
|
||||
self.pollTimerId = null;
|
||||
}
|
||||
},
|
||||
triggerNextSync = function() {
|
||||
self.pollTimerId = setTimeout(function() {
|
||||
self.pollTimerId = null;
|
||||
self.syncFromServer.call(self);
|
||||
},self.pollTimerInterval);
|
||||
},
|
||||
syncSystemFromServer = (self.wiki.getTiddlerText("$:/config/SyncSystemTiddlersFromServer") === "yes" ? true : false);
|
||||
if(this.syncadaptor && this.syncadaptor.getUpdatedTiddlers) {
|
||||
this.logger.log("Retrieving updated tiddler list");
|
||||
cancelNextSync();
|
||||
this.syncadaptor.getUpdatedTiddlers(self,function(err,updates) {
|
||||
triggerNextSync();
|
||||
if(err) {
|
||||
self.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||
return;
|
||||
}
|
||||
if(updates) {
|
||||
$tw.utils.each(updates.modifications,function(title) {
|
||||
self.titlesToBeLoaded[title] = true;
|
||||
});
|
||||
$tw.utils.each(updates.deletions,function(title) {
|
||||
if(syncSystemFromServer || !self.wiki.isSystemTiddler(title)) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
if(updates.modifications.length > 0 || updates.deletions.length > 0) {
|
||||
self.processTaskQueue();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
|
||||
this.logger.log("Retrieving skinny tiddler list");
|
||||
cancelNextSync();
|
||||
this.syncadaptor.getSkinnyTiddlers(function(err,tiddlers) {
|
||||
triggerNextSync();
|
||||
// Check for errors
|
||||
if(err) {
|
||||
self.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||
return;
|
||||
}
|
||||
// Keep track of which tiddlers we already know about have been reported this time
|
||||
var previousTitles = Object.keys(self.tiddlerInfo);
|
||||
// Process each incoming tiddler
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
// Get the incoming tiddler fields, and the existing tiddler
|
||||
var tiddlerFields = tiddlers[t],
|
||||
incomingRevision = tiddlerFields.revision + "",
|
||||
tiddler = self.wiki.tiddlerExists(tiddlerFields.title) && self.wiki.getTiddler(tiddlerFields.title),
|
||||
tiddlerInfo = self.tiddlerInfo[tiddlerFields.title],
|
||||
currRevision = tiddlerInfo ? tiddlerInfo.revision : null,
|
||||
indexInPreviousTitles = previousTitles.indexOf(tiddlerFields.title);
|
||||
if(indexInPreviousTitles !== -1) {
|
||||
previousTitles.splice(indexInPreviousTitles,1);
|
||||
}
|
||||
// Ignore the incoming tiddler if it's the same as the revision we've already got
|
||||
if(currRevision !== incomingRevision) {
|
||||
// Only load the skinny version if we don't already have a fat version of the tiddler
|
||||
if(!tiddler || tiddler.fields.text === undefined) {
|
||||
self.storeTiddler(tiddlerFields);
|
||||
}
|
||||
// Do a full load of this tiddler
|
||||
self.titlesToBeLoaded[tiddlerFields.title] = true;
|
||||
}
|
||||
}
|
||||
// Delete any tiddlers that were previously reported but missing this time
|
||||
$tw.utils.each(previousTitles,function(title) {
|
||||
if(syncSystemFromServer || !self.wiki.isSystemTiddler(title)) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
self.processTaskQueue();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Syncer.prototype.canSyncFromServer = function() {
|
||||
return !!this.syncadaptor.getUpdatedTiddlers || !!this.syncadaptor.getSkinnyTiddlers;
|
||||
}
|
||||
|
||||
/*
|
||||
Force load a tiddler from the server
|
||||
*/
|
||||
@@ -440,7 +510,7 @@ Syncer.prototype.processTaskQueue = function() {
|
||||
} else {
|
||||
self.updateDirtyStatus();
|
||||
// Process the next task
|
||||
self.processTaskQueue.call(self);
|
||||
self.processTaskQueue.call(self);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -448,39 +518,31 @@ Syncer.prototype.processTaskQueue = function() {
|
||||
this.updateDirtyStatus();
|
||||
// And trigger a timeout if there is a pending task
|
||||
if(task === true) {
|
||||
this.triggerTimeout(this.taskTimerInterval);
|
||||
} else if(this.canSyncFromServer()) {
|
||||
this.triggerTimeout(this.pollTimerInterval);
|
||||
this.triggerTimeout();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.updateDirtyStatus();
|
||||
this.triggerTimeout(this.taskTimerInterval);
|
||||
this.updateDirtyStatus();
|
||||
}
|
||||
};
|
||||
|
||||
Syncer.prototype.triggerTimeout = function(interval) {
|
||||
var self = this;
|
||||
if(this.taskTimerId) {
|
||||
clearTimeout(this.taskTimerId);
|
||||
if(!this.taskTimerId) {
|
||||
this.taskTimerId = setTimeout(function() {
|
||||
self.taskTimerId = null;
|
||||
self.processTaskQueue.call(self);
|
||||
},interval || self.taskTimerInterval);
|
||||
}
|
||||
this.taskTimerId = setTimeout(function() {
|
||||
self.taskTimerId = null;
|
||||
self.processTaskQueue.call(self);
|
||||
},interval || self.taskTimerInterval);
|
||||
};
|
||||
|
||||
/*
|
||||
Choose the next sync task. We prioritise saves to the server, then getting updates from the server, then deletes to the server, then loads from the server
|
||||
Choose the next sync task. We prioritise saves, then deletes, then loads from the server
|
||||
|
||||
Returns either:
|
||||
* a task object
|
||||
* the boolean true if there are pending sync tasks that aren't yet due
|
||||
* null if there's no pending sync tasks (just the next poll)
|
||||
Returns either a task object, null if there's no upcoming tasks, or the boolean true if there are pending tasks that aren't yet due
|
||||
*/
|
||||
Syncer.prototype.chooseNextTask = function() {
|
||||
var now = new Date(),
|
||||
thresholdLastSaved = now - this.throttleInterval,
|
||||
var thresholdLastSaved = (new Date()) - this.throttleInterval,
|
||||
havePending = null;
|
||||
// First we look for tiddlers that have been modified locally and need saving back to the server
|
||||
var titles = this.getSyncedTiddlers();
|
||||
@@ -494,18 +556,14 @@ Syncer.prototype.chooseNextTask = function() {
|
||||
isReadyToSave = !tiddlerInfo || !tiddlerInfo.timestampLastSaved || tiddlerInfo.timestampLastSaved < thresholdLastSaved;
|
||||
if(hasChanged) {
|
||||
if(isReadyToSave) {
|
||||
return new SaveTiddlerTask(this,title);
|
||||
return new SaveTiddlerTask(this,title);
|
||||
} else {
|
||||
havePending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second we check for an outstanding sync from server
|
||||
if(this.forceSyncFromServer || (this.timestampLastSyncFromServer && (now.valueOf() >= (this.timestampLastSyncFromServer.valueOf() + this.pollTimerInterval)))) {
|
||||
return new SyncFromServerTask(this);
|
||||
}
|
||||
// Third, we check tiddlers that are known from the server but not currently in the store, and so need deleting on the server
|
||||
// Second, we check tiddlers that are known from the server but not currently in the store, and so need deleting on the server
|
||||
titles = Object.keys(this.tiddlerInfo);
|
||||
for(index=0; index<titles.length; index++) {
|
||||
title = titles[index];
|
||||
@@ -515,13 +573,13 @@ Syncer.prototype.chooseNextTask = function() {
|
||||
return new DeleteTiddlerTask(this,title);
|
||||
}
|
||||
}
|
||||
// Finally, check for tiddlers that need loading
|
||||
// Check for tiddlers that need loading
|
||||
title = Object.keys(this.titlesToBeLoaded)[0];
|
||||
if(title) {
|
||||
delete this.titlesToBeLoaded[title];
|
||||
return new LoadTiddlerTask(this,title);
|
||||
}
|
||||
// No tasks are ready now, but might be in the future
|
||||
// No tasks are ready
|
||||
return havePending;
|
||||
};
|
||||
|
||||
@@ -531,10 +589,6 @@ function SaveTiddlerTask(syncer,title) {
|
||||
this.type = "save";
|
||||
}
|
||||
|
||||
SaveTiddlerTask.prototype.toString = function() {
|
||||
return "SAVE " + this.title;
|
||||
}
|
||||
|
||||
SaveTiddlerTask.prototype.run = function(callback) {
|
||||
var self = this,
|
||||
changeCount = this.syncer.wiki.getChangeCount(this.title),
|
||||
@@ -559,6 +613,7 @@ SaveTiddlerTask.prototype.run = function(callback) {
|
||||
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
|
||||
});
|
||||
} else {
|
||||
this.syncer.logger.log(" Not Dispatching 'save' task:",this.title,"tiddler does not exist");
|
||||
$tw.utils.nextTick(callback(null));
|
||||
}
|
||||
};
|
||||
@@ -569,10 +624,6 @@ function DeleteTiddlerTask(syncer,title) {
|
||||
this.type = "delete";
|
||||
}
|
||||
|
||||
DeleteTiddlerTask.prototype.toString = function() {
|
||||
return "DELETE " + this.title;
|
||||
}
|
||||
|
||||
DeleteTiddlerTask.prototype.run = function(callback) {
|
||||
var self = this;
|
||||
this.syncer.logger.log("Dispatching 'delete' task:",this.title);
|
||||
@@ -596,10 +647,6 @@ function LoadTiddlerTask(syncer,title) {
|
||||
this.type = "load";
|
||||
}
|
||||
|
||||
LoadTiddlerTask.prototype.toString = function() {
|
||||
return "LOAD " + this.title;
|
||||
}
|
||||
|
||||
LoadTiddlerTask.prototype.run = function(callback) {
|
||||
var self = this;
|
||||
this.syncer.logger.log("Dispatching 'load' task:",this.title);
|
||||
@@ -617,91 +664,6 @@ LoadTiddlerTask.prototype.run = function(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
function SyncFromServerTask(syncer) {
|
||||
this.syncer = syncer;
|
||||
this.type = "syncfromserver";
|
||||
}
|
||||
|
||||
SyncFromServerTask.prototype.toString = function() {
|
||||
return "SYNCFROMSERVER";
|
||||
}
|
||||
|
||||
SyncFromServerTask.prototype.run = function(callback) {
|
||||
var self = this;
|
||||
var syncSystemFromServer = (self.syncer.wiki.getTiddlerText("$:/config/SyncSystemTiddlersFromServer") === "yes" ? true : false);
|
||||
var successCallback = function() {
|
||||
self.syncer.forceSyncFromServer = false;
|
||||
self.syncer.timestampLastSyncFromServer = new Date();
|
||||
callback(null);
|
||||
};
|
||||
if(this.syncer.syncadaptor.getUpdatedTiddlers) {
|
||||
this.syncer.syncadaptor.getUpdatedTiddlers(self.syncer,function(err,updates) {
|
||||
if(err) {
|
||||
self.syncer.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||
return callback(err);
|
||||
}
|
||||
if(updates) {
|
||||
$tw.utils.each(updates.modifications,function(title) {
|
||||
self.syncer.titlesToBeLoaded[title] = true;
|
||||
});
|
||||
$tw.utils.each(updates.deletions,function(title) {
|
||||
if(syncSystemFromServer || !self.syncer.wiki.isSystemTiddler(title)) {
|
||||
delete self.syncer.tiddlerInfo[title];
|
||||
self.syncer.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.syncer.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
return successCallback();
|
||||
});
|
||||
} else if(this.syncer.syncadaptor.getSkinnyTiddlers) {
|
||||
this.syncer.syncadaptor.getSkinnyTiddlers(function(err,tiddlers) {
|
||||
// Check for errors
|
||||
if(err) {
|
||||
self.syncer.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||
return callback(err);
|
||||
}
|
||||
// Keep track of which tiddlers we already know about have been reported this time
|
||||
var previousTitles = Object.keys(self.syncer.tiddlerInfo);
|
||||
// Process each incoming tiddler
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
// Get the incoming tiddler fields, and the existing tiddler
|
||||
var tiddlerFields = tiddlers[t],
|
||||
incomingRevision = tiddlerFields.revision + "",
|
||||
tiddler = self.syncer.wiki.tiddlerExists(tiddlerFields.title) && self.syncer.wiki.getTiddler(tiddlerFields.title),
|
||||
tiddlerInfo = self.syncer.tiddlerInfo[tiddlerFields.title],
|
||||
currRevision = tiddlerInfo ? tiddlerInfo.revision : null,
|
||||
indexInPreviousTitles = previousTitles.indexOf(tiddlerFields.title);
|
||||
if(indexInPreviousTitles !== -1) {
|
||||
previousTitles.splice(indexInPreviousTitles,1);
|
||||
}
|
||||
// Ignore the incoming tiddler if it's the same as the revision we've already got
|
||||
if(currRevision !== incomingRevision) {
|
||||
// Only load the skinny version if we don't already have a fat version of the tiddler
|
||||
if(!tiddler || tiddler.fields.text === undefined) {
|
||||
self.syncer.storeTiddler(tiddlerFields);
|
||||
}
|
||||
// Do a full load of this tiddler
|
||||
self.syncer.titlesToBeLoaded[tiddlerFields.title] = true;
|
||||
}
|
||||
}
|
||||
// Delete any tiddlers that were previously reported but missing this time
|
||||
$tw.utils.each(previousTitles,function(title) {
|
||||
if(syncSystemFromServer || !self.syncer.wiki.isSystemTiddler(title)) {
|
||||
delete self.syncer.tiddlerInfo[title];
|
||||
self.syncer.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.syncer.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
self.syncer.forceSyncFromServer = false;
|
||||
self.syncer.timestampLastSyncFromServer = new Date();
|
||||
return successCallback();
|
||||
});
|
||||
} else {
|
||||
return successCallback();
|
||||
}
|
||||
};
|
||||
|
||||
exports.Syncer = Syncer;
|
||||
|
||||
})();
|
||||
|
||||
@@ -313,7 +313,7 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
variables["dom-" + attribute.name] = attribute.value.toString();
|
||||
});
|
||||
|
||||
if("offsetLeft" in selectedNode) {
|
||||
if(selectedNode.offsetLeft) {
|
||||
// Add variables with a (relative and absolute) popup coordinate string for the selected node
|
||||
var nodeRect = {
|
||||
left: selectedNode.offsetLeft,
|
||||
@@ -338,12 +338,12 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
}
|
||||
}
|
||||
|
||||
if(domNode && ("offsetWidth" in domNode)) {
|
||||
if(domNode && domNode.offsetWidth) {
|
||||
variables["tv-widgetnode-width"] = domNode.offsetWidth.toString();
|
||||
variables["tv-widgetnode-height"] = domNode.offsetHeight.toString();
|
||||
}
|
||||
|
||||
if(event && ("clientX" in event) && ("clientY" in event)) {
|
||||
if(event && event.clientX && event.clientY) {
|
||||
if(selectedNode) {
|
||||
// Add variables for event X and Y position relative to selected node
|
||||
selectedNodeRect = selectedNode.getBoundingClientRect();
|
||||
@@ -365,25 +365,5 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
return variables;
|
||||
};
|
||||
|
||||
/*
|
||||
Make sure the CSS selector is not invalid
|
||||
*/
|
||||
exports.querySelectorSafe = function(selector,baseElement) {
|
||||
baseElement = baseElement || document;
|
||||
try {
|
||||
return baseElement.querySelector(selector);
|
||||
} catch(e) {
|
||||
console.log("Invalid selector: ",selector);
|
||||
}
|
||||
};
|
||||
|
||||
exports.querySelectorAllSafe = function(selector,baseElement) {
|
||||
baseElement = baseElement || document;
|
||||
try {
|
||||
return baseElement.querySelectorAll(selector);
|
||||
} catch(e) {
|
||||
console.log("Invalid selector: ",selector);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -80,7 +80,7 @@ exports.makeDraggable = function(options) {
|
||||
if(dataTransfer.setDragImage) {
|
||||
if(dragImageType === "pill") {
|
||||
dataTransfer.setDragImage(dragImage.firstChild,-16,-16);
|
||||
} else if(dragImageType === "blank") {
|
||||
} else if (dragImageType === "blank") {
|
||||
dragImage.removeChild(dragImage.firstChild);
|
||||
dataTransfer.setDragImage(dragImage,0,0);
|
||||
} else {
|
||||
@@ -106,9 +106,7 @@ exports.makeDraggable = function(options) {
|
||||
dataTransfer.setData("text/vnd.tiddler",jsonData);
|
||||
dataTransfer.setData("text/plain",titleString);
|
||||
dataTransfer.setData("text/x-moz-url","data:text/vnd.tiddler," + encodeURIComponent(jsonData));
|
||||
}
|
||||
// If browser is Chrome-like and has a touch-input device do NOT .setData
|
||||
if(!($tw.browser.isMobileChrome)) {
|
||||
} else {
|
||||
dataTransfer.setData("URL","data:text/vnd.tiddler," + encodeURIComponent(jsonData));
|
||||
}
|
||||
dataTransfer.setData("Text",titleString);
|
||||
@@ -161,7 +159,7 @@ exports.importDataTransfer = function(dataTransfer,fallbackTitle,callback) {
|
||||
if(!$tw.browser.isIE || importDataTypes[t].IECompatible) {
|
||||
// Get the data
|
||||
var dataType = importDataTypes[t];
|
||||
var data = dataTransfer.getData(dataType.type);
|
||||
var data = dataTransfer.getData(dataType.type);
|
||||
// Import the tiddlers in the data
|
||||
if(data !== "" && data !== null) {
|
||||
if($tw.log.IMPORT) {
|
||||
@@ -175,36 +173,6 @@ exports.importDataTransfer = function(dataTransfer,fallbackTitle,callback) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.importPaste = function(item,fallbackTitle,callback) {
|
||||
// Try each provided data type in turn
|
||||
for(var t=0; t<importDataTypes.length; t++) {
|
||||
if(item.type === importDataTypes[t].type) {
|
||||
// Get the data
|
||||
var dataType = importDataTypes[t];
|
||||
|
||||
item.getAsString(function(data){
|
||||
if($tw.log.IMPORT) {
|
||||
console.log("Importing data type '" + dataType.type + "', data: '" + data + "'")
|
||||
}
|
||||
var tiddlerFields = dataType.toTiddlerFieldsArray(data,fallbackTitle);
|
||||
callback(tiddlerFields);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.itemHasValidDataType = function(item) {
|
||||
for(var t=0; t<importDataTypes.length; t++) {
|
||||
if(!$tw.browser.isIE || importDataTypes[t].IECompatible) {
|
||||
if(item.type === importDataTypes[t].type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var importDataTypes = [
|
||||
{type: "text/vnd.tiddler", IECompatible: false, toTiddlerFieldsArray: function(data,fallbackTitle) {
|
||||
return parseJSONTiddlers(data,fallbackTitle);
|
||||
@@ -237,13 +205,7 @@ var importDataTypes = [
|
||||
return [{title: fallbackTitle, text: data}];
|
||||
}},
|
||||
{type: "text/uri-list", IECompatible: false, toTiddlerFieldsArray: function(data,fallbackTitle) {
|
||||
// Check for tiddler data URI
|
||||
var match = $tw.utils.decodeURIComponentSafe(data).match(/^data\:text\/vnd\.tiddler,(.*)/i);
|
||||
if(match) {
|
||||
return parseJSONTiddlers(match[1],fallbackTitle);
|
||||
} else {
|
||||
return [{title: fallbackTitle, text: data}]; // As URL string
|
||||
}
|
||||
return [{title: fallbackTitle, text: data}];
|
||||
}}
|
||||
];
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: $:/core/modules/utils/dom/http.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
|
||||
HTTP support
|
||||
Browser HTTP support
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@@ -13,220 +13,12 @@ HTTP support
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Manage tm-http-request events. Options include:
|
||||
wiki: Reference to the wiki to be used for state tiddler tracking
|
||||
stateTrackerTitle: Title of tiddler to be used for state tiddler tracking
|
||||
*/
|
||||
function HttpClient(options) {
|
||||
options = options || {};
|
||||
this.nextId = 1;
|
||||
this.wiki = options.wiki || $tw.wiki;
|
||||
this.stateTrackerTitle = options.stateTrackerTitle || "$:/state/http-requests";
|
||||
this.requests = []; // Array of {id: string,request: HttpClientRequest}
|
||||
this.updateRequestTracker();
|
||||
}
|
||||
|
||||
/*
|
||||
Return the index into this.requests[] corresponding to a given ID. Returns null if not found
|
||||
*/
|
||||
HttpClient.prototype.getRequestIndex = function(targetId) {
|
||||
var targetIndex = null;
|
||||
$tw.utils.each(this.requests,function(requestInfo,index) {
|
||||
if(requestInfo.id === targetId) {
|
||||
targetIndex = index;
|
||||
}
|
||||
});
|
||||
return targetIndex;
|
||||
};
|
||||
|
||||
/*
|
||||
Update the state tiddler that is tracking the outstanding requests
|
||||
*/
|
||||
HttpClient.prototype.updateRequestTracker = function() {
|
||||
this.wiki.addTiddler({title: this.stateTrackerTitle, text: "" + this.requests.length});
|
||||
};
|
||||
|
||||
HttpClient.prototype.initiateHttpRequest = function(options) {
|
||||
var self = this,
|
||||
id = this.nextId,
|
||||
request = new HttpClientRequest(options);
|
||||
this.nextId += 1;
|
||||
this.requests.push({id: id, request: request});
|
||||
this.updateRequestTracker();
|
||||
request.send(function(err) {
|
||||
var targetIndex = self.getRequestIndex(id);
|
||||
if(targetIndex !== null) {
|
||||
self.requests.splice(targetIndex,1);
|
||||
self.updateRequestTracker();
|
||||
}
|
||||
});
|
||||
return id;
|
||||
};
|
||||
|
||||
HttpClient.prototype.cancelAllHttpRequests = function() {
|
||||
var self = this;
|
||||
if(this.requests.length > 0) {
|
||||
for(var t=this.requests.length - 1; t--; t>=0) {
|
||||
var requestInfo = this.requests[t];
|
||||
requestInfo.request.cancel();
|
||||
}
|
||||
}
|
||||
this.requests = [];
|
||||
this.updateRequestTracker();
|
||||
};
|
||||
|
||||
HttpClient.prototype.cancelHttpRequest = function(targetId) {
|
||||
var targetIndex = this.getRequestIndex(targetId);
|
||||
if(targetIndex !== null) {
|
||||
this.requests[targetIndex].request.cancel();
|
||||
this.requests.splice(targetIndex,1);
|
||||
this.updateRequestTracker();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Initiate an HTTP request. Options:
|
||||
wiki: wiki to be used for executing action strings
|
||||
url: URL for request
|
||||
method: method eg GET, POST
|
||||
body: text of request body
|
||||
binary: set to "yes" to force binary processing of response payload
|
||||
oncompletion: action string to be invoked on completion
|
||||
onprogress: action string to be invoked on progress updates
|
||||
bindStatus: optional title of tiddler to which status ("pending", "complete", "error") should be written
|
||||
bindProgress: optional title of tiddler to which the progress of the request (0 to 100) should be bound
|
||||
variables: hashmap of variable name to string value passed to action strings
|
||||
headers: hashmap of header name to header value to be sent with the request
|
||||
passwordHeaders: hashmap of header name to password store name to be sent with the request
|
||||
queryStrings: hashmap of query string parameter name to parameter value to be sent with the request
|
||||
passwordQueryStrings: hashmap of query string parameter name to password store name to be sent with the request
|
||||
*/
|
||||
function HttpClientRequest(options) {
|
||||
var self = this;
|
||||
console.log("Initiating an HTTP request",options)
|
||||
this.wiki = options.wiki;
|
||||
this.completionActions = options.oncompletion;
|
||||
this.progressActions = options.onprogress;
|
||||
this.bindStatus = options["bindStatus"];
|
||||
this.bindProgress = options["bindProgress"];
|
||||
this.method = options.method || "GET";
|
||||
this.body = options.body || "";
|
||||
this.binary = options.binary || "";
|
||||
this.variables = options.variables;
|
||||
var url = options.url;
|
||||
$tw.utils.each(options.queryStrings,function(value,name) {
|
||||
url = $tw.utils.setQueryStringParameter(url,name,value);
|
||||
});
|
||||
$tw.utils.each(options.passwordQueryStrings,function(value,name) {
|
||||
url = $tw.utils.setQueryStringParameter(url,name,$tw.utils.getPassword(value) || "");
|
||||
});
|
||||
this.url = url;
|
||||
this.requestHeaders = {};
|
||||
$tw.utils.each(options.headers,function(value,name) {
|
||||
self.requestHeaders[name] = value;
|
||||
});
|
||||
$tw.utils.each(options.passwordHeaders,function(value,name) {
|
||||
self.requestHeaders[name] = $tw.utils.getPassword(value) || "";
|
||||
});
|
||||
}
|
||||
|
||||
HttpClientRequest.prototype.send = function(callback) {
|
||||
var self = this,
|
||||
setBinding = function(title,text) {
|
||||
if(title) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: title, text: text}));
|
||||
}
|
||||
};
|
||||
if(this.url) {
|
||||
setBinding(this.bindStatus,"pending");
|
||||
setBinding(this.bindProgress,"0");
|
||||
// Set the request tracker tiddler
|
||||
var requestTrackerTitle = this.wiki.generateNewTitle("$:/temp/HttpRequest");
|
||||
this.wiki.addTiddler({
|
||||
title: requestTrackerTitle,
|
||||
tags: "$:/tags/HttpRequest",
|
||||
text: JSON.stringify({
|
||||
url: this.url,
|
||||
type: this.method,
|
||||
status: "inprogress",
|
||||
headers: this.requestHeaders,
|
||||
data: this.body
|
||||
})
|
||||
});
|
||||
this.xhr = $tw.utils.httpRequest({
|
||||
url: this.url,
|
||||
type: this.method,
|
||||
headers: this.requestHeaders,
|
||||
data: this.body,
|
||||
returnProp: this.binary === "" ? "responseText" : "response",
|
||||
responseType: this.binary === "" ? "text" : "arraybuffer",
|
||||
callback: function(err,data,xhr) {
|
||||
var hasSucceeded = xhr.status >= 200 && xhr.status < 300,
|
||||
completionCode = hasSucceeded ? "complete" : "error",
|
||||
headers = {};
|
||||
$tw.utils.each(xhr.getAllResponseHeaders().split("\r\n"),function(line) {
|
||||
var pos = line.indexOf(":");
|
||||
if(pos !== -1) {
|
||||
headers[line.substr(0,pos)] = line.substr(pos + 1).trim();
|
||||
}
|
||||
});
|
||||
setBinding(self.bindStatus,completionCode);
|
||||
setBinding(self.bindProgress,"100");
|
||||
var resultVariables = {
|
||||
status: xhr.status.toString(),
|
||||
statusText: xhr.statusText,
|
||||
error: (err || "").toString(),
|
||||
data: (data || "").toString(),
|
||||
headers: JSON.stringify(headers)
|
||||
};
|
||||
/* Convert data from binary to base64 */
|
||||
if (xhr.responseType === "arraybuffer") {
|
||||
var binary = "",
|
||||
bytes = new Uint8Array(data),
|
||||
len = bytes.byteLength;
|
||||
for (var i=0; i<len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
resultVariables.data = $tw.utils.base64Encode(binary,true);
|
||||
}
|
||||
self.wiki.addTiddler(new $tw.Tiddler(self.wiki.getTiddler(requestTrackerTitle),{
|
||||
status: completionCode,
|
||||
}));
|
||||
self.wiki.invokeActionString(self.completionActions,undefined,$tw.utils.extend({},self.variables,resultVariables),{parentWidget: $tw.rootWidget});
|
||||
callback(hasSucceeded ? null : xhr.statusText);
|
||||
// console.log("Back!",err,data,xhr);
|
||||
},
|
||||
progress: function(lengthComputable,loaded,total) {
|
||||
if(lengthComputable) {
|
||||
setBinding(self.bindProgress,"" + Math.floor((loaded/total) * 100))
|
||||
}
|
||||
self.wiki.invokeActionString(self.progressActions,undefined,{
|
||||
lengthComputable: lengthComputable ? "yes" : "no",
|
||||
loaded: loaded,
|
||||
total: total
|
||||
},{parentWidget: $tw.rootWidget});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
HttpClientRequest.prototype.cancel = function() {
|
||||
if(this.xhr) {
|
||||
this.xhr.abort();
|
||||
}
|
||||
};
|
||||
|
||||
exports.HttpClient = HttpClient;
|
||||
|
||||
/*
|
||||
Make an HTTP request. Options are:
|
||||
A quick and dirty HTTP function; to be refactored later. Options are:
|
||||
url: URL to retrieve
|
||||
headers: hashmap of headers to send
|
||||
type: GET, PUT, POST etc
|
||||
callback: function invoked with (err,data,xhr)
|
||||
progress: optional function invoked with (lengthComputable,loaded,total)
|
||||
returnProp: string name of the property to return as first argument of callback
|
||||
responseType: "text" or "arraybuffer"
|
||||
*/
|
||||
exports.httpRequest = function(options) {
|
||||
var type = options.type || "GET",
|
||||
@@ -279,7 +71,6 @@ exports.httpRequest = function(options) {
|
||||
}
|
||||
}
|
||||
}
|
||||
request.responseType = options.responseType || "text";
|
||||
// Set up the state change handler
|
||||
request.onreadystatechange = function() {
|
||||
if(this.readyState === 4) {
|
||||
@@ -292,16 +83,8 @@ exports.httpRequest = function(options) {
|
||||
options.callback($tw.language.getString("Error/XMLHttpRequest") + ": " + this.status,null,this);
|
||||
}
|
||||
};
|
||||
// Handle progress
|
||||
if(options.progress) {
|
||||
request.onprogress = function(event) {
|
||||
console.log("Progress event",event)
|
||||
options.progress(event.lengthComputable,event.loaded,event.total);
|
||||
};
|
||||
}
|
||||
// Make the request
|
||||
request.open(type,url,true);
|
||||
// Headers
|
||||
if(headers) {
|
||||
$tw.utils.each(headers,function(header,headerTitle,object) {
|
||||
request.setRequestHeader(headerTitle,header);
|
||||
@@ -313,7 +96,6 @@ exports.httpRequest = function(options) {
|
||||
if(!hasHeader("X-Requested-With") && !isSimpleRequest(type,headers)) {
|
||||
request.setRequestHeader("X-Requested-With","TiddlyWiki");
|
||||
}
|
||||
// Send data
|
||||
try {
|
||||
request.send(data);
|
||||
} catch(e) {
|
||||
@@ -322,19 +104,4 @@ exports.httpRequest = function(options) {
|
||||
return request;
|
||||
};
|
||||
|
||||
exports.setQueryStringParameter = function(url,paramName,paramValue) {
|
||||
var URL = $tw.browser ? window.URL : require("url").URL,
|
||||
newUrl;
|
||||
try {
|
||||
newUrl = new URL(url);
|
||||
} catch(e) {
|
||||
}
|
||||
if(newUrl && paramName) {
|
||||
newUrl.searchParams.set(paramName,paramValue || "");
|
||||
return newUrl.toString();
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -127,8 +127,8 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) {
|
||||
};
|
||||
|
||||
PageScroller.prototype.scrollSelectorIntoView = function(baseElement,selector,callback,options) {
|
||||
baseElement = baseElement || document;
|
||||
var element = $tw.utils.querySelectorSafe(selector,baseElement);
|
||||
baseElement = baseElement || document.body;
|
||||
var element = baseElement.querySelector(selector);
|
||||
if(element) {
|
||||
this.scrollIntoView(element,callback,options);
|
||||
}
|
||||
|
||||
@@ -104,11 +104,7 @@ TW_Element.prototype.setAttribute = function(name,value) {
|
||||
if(this.isRaw) {
|
||||
throw "Cannot setAttribute on a raw TW_Element";
|
||||
}
|
||||
if(name === "style") {
|
||||
this.style = value;
|
||||
} else {
|
||||
this.attributes[name] = value + "";
|
||||
}
|
||||
this.attributes[name] = value + "";
|
||||
};
|
||||
|
||||
TW_Element.prototype.setAttributeNS = function(namespace,name,value) {
|
||||
|
||||
@@ -819,41 +819,18 @@ exports.hashString = function(str) {
|
||||
},0);
|
||||
};
|
||||
|
||||
/*
|
||||
Base64 utility functions that work in either browser or Node.js
|
||||
*/
|
||||
if(typeof window !== 'undefined') {
|
||||
exports.btoa = function(binstr) { return window.btoa(binstr); }
|
||||
exports.atob = function(b64) { return window.atob(b64); }
|
||||
} else {
|
||||
exports.btoa = function(binstr) {
|
||||
return Buffer.from(binstr, 'binary').toString('base64');
|
||||
}
|
||||
exports.atob = function(b64) {
|
||||
return Buffer.from(b64, 'base64').toString('binary');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Decode a base64 string
|
||||
*/
|
||||
exports.base64Decode = function(string64,binary,urlsafe) {
|
||||
var encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
|
||||
if(binary) return exports.atob(encoded)
|
||||
else return base64utf8.base64.decode.call(base64utf8,encoded);
|
||||
exports.base64Decode = function(string64) {
|
||||
return base64utf8.base64.decode.call(base64utf8,string64);
|
||||
};
|
||||
|
||||
/*
|
||||
Encode a string to base64
|
||||
*/
|
||||
exports.base64Encode = function(string64,binary,urlsafe) {
|
||||
var encoded;
|
||||
if(binary) encoded = exports.btoa(string64);
|
||||
else encoded = base64utf8.base64.encode.call(base64utf8,string64);
|
||||
if(urlsafe) {
|
||||
encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_');
|
||||
}
|
||||
return encoded;
|
||||
exports.base64Encode = function(string64) {
|
||||
return base64utf8.base64.encode.call(base64utf8,string64);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -70,11 +70,6 @@ BrowseWidget.prototype.render = function(parent,nextSibling) {
|
||||
}
|
||||
return false;
|
||||
},false);
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -100,11 +95,6 @@ BrowseWidget.prototype.execute = function() {
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
BrowseWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,11 +59,6 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||
$tw.utils.pushTop(classes,"tc-popup-handle");
|
||||
}
|
||||
domNode.className = classes.join(" ");
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Assign other attributes
|
||||
if(this.style) {
|
||||
domNode.setAttribute("style",this.style);
|
||||
@@ -255,7 +250,7 @@ ButtonWidget.prototype.updateDomNodeClasses = function() {
|
||||
//Add new classes from updated class attribute.
|
||||
$tw.utils.pushTop(domNodeClasses,newClasses);
|
||||
this.domNode.className = domNodeClasses.join(" ");
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
@@ -265,15 +260,8 @@ ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -53,11 +53,6 @@ CheckboxWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.labelDomNode.appendChild(this.inputDomNode);
|
||||
this.spanDomNode = this.document.createElement("span");
|
||||
this.labelDomNode.appendChild(this.spanDomNode);
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
||||
@@ -117,11 +112,11 @@ CheckboxWidget.prototype.getValue = function() {
|
||||
var list;
|
||||
if(this.checkboxListField) {
|
||||
if($tw.utils.hop(tiddler.fields,this.checkboxListField)) {
|
||||
list = tiddler.getFieldList(this.checkboxListField) || [];
|
||||
list = tiddler.getFieldList(this.checkboxListField);
|
||||
} else {
|
||||
list = $tw.utils.parseStringArray(this.checkboxDefault || "") || [];
|
||||
}
|
||||
} else if(this.checkboxListIndex) {
|
||||
} else if (this.checkboxListIndex) {
|
||||
list = $tw.utils.parseStringArray(this.wiki.extractTiddlerDataItem(tiddler,this.checkboxListIndex,this.checkboxDefault || "")) || [];
|
||||
} else {
|
||||
list = this.wiki.filterTiddlers(this.checkboxFilter,this) || [];
|
||||
@@ -213,22 +208,16 @@ CheckboxWidget.prototype.handleChangeEvent = function(event) {
|
||||
if(this.checkboxListField || this.checkboxListIndex) {
|
||||
var fieldContents, listContents, oldPos, newPos;
|
||||
if(this.checkboxListField) {
|
||||
fieldContents = (tiddler ? tiddler.fields[this.checkboxListField] : undefined) || [];
|
||||
fieldContents = tiddler ? tiddler.fields[this.checkboxListField] : undefined;
|
||||
} else {
|
||||
fieldContents = this.wiki.extractTiddlerDataItem(this.checkboxTitle,this.checkboxListIndex);
|
||||
}
|
||||
if($tw.utils.isArray(fieldContents)) {
|
||||
// Make a copy so we can modify it without changing original that's refrenced elsewhere
|
||||
listContents = fieldContents.slice(0);
|
||||
} else if(fieldContents === undefined) {
|
||||
listContents = [];
|
||||
} else if(typeof fieldContents === "string") {
|
||||
listContents = $tw.utils.parseStringArray(fieldContents);
|
||||
// No need to copy since parseStringArray returns a fresh array, not refrenced elsewhere
|
||||
} else {
|
||||
// Field was neither an array nor a string; it's probably something that shouldn't become
|
||||
// an array (such as a date field), so bail out *without* triggering actions
|
||||
return;
|
||||
listContents = $tw.utils.parseStringArray(fieldContents) || [];
|
||||
// No need to copy since parseStringArray returns a fresh array, not refrenced elsewhere
|
||||
}
|
||||
oldPos = notValue ? listContents.indexOf(notValue) : -1;
|
||||
newPos = value ? listContents.indexOf(value) : -1;
|
||||
@@ -330,11 +319,6 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
$tw.utils.removeClass(this.labelDomNode,"tc-checkbox-checked");
|
||||
}
|
||||
}
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
return this.refreshChildren(changedTiddlers) || refreshed;
|
||||
}
|
||||
};
|
||||
@@ -342,4 +326,3 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
exports.checkbox = CheckboxWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -39,7 +39,7 @@ DiffTextWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.execute();
|
||||
// Create the diff
|
||||
var dmpObject = new dmp.diff_match_patch(),
|
||||
diffs = dmpObject.diff_main(this.getAttribute("source",""),this.getAttribute("dest",""));
|
||||
diffs = dmpObject.diff_main(this.getAttribute("source"),this.getAttribute("dest"));
|
||||
// Apply required cleanup
|
||||
switch(this.getAttribute("cleanup","semantic")) {
|
||||
case "none":
|
||||
|
||||
@@ -52,11 +52,6 @@ DraggableWidget.prototype.render = function(parent,nextSibling) {
|
||||
classes.push("tc-draggable");
|
||||
}
|
||||
domNode.setAttribute("class",classes.join(" "));
|
||||
// Assign data- attributes and style. attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert the node into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -113,19 +108,13 @@ DraggableWidget.prototype.updateDomNodeClasses = function() {
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DraggableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tag || changedAttributes.selector || changedAttributes.dragimagetype || changedAttributes.enable || changedAttributes.startactions || changedAttributes.endactions) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
changedAttributesCount = $tw.utils.count(changedAttributes);
|
||||
if(changedAttributesCount === 1 && changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
} else if(changedAttributesCount > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -42,11 +42,6 @@ DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||
domNode = this.document.createElement(tag);
|
||||
this.domNode = domNode;
|
||||
this.assignDomNodeClasses();
|
||||
// Assign data- attributes and style. attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add event handlers
|
||||
if(this.droppableEnable) {
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
@@ -171,15 +166,8 @@ DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
if(changedAttributes.tag || changedAttributes.enable || changedAttributes.disabledClass || changedAttributes.actions || changedAttributes.effect) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -271,20 +271,6 @@ DropZoneWidget.prototype.handlePasteEvent = function(event) {
|
||||
callback: readFileCallback,
|
||||
deserializer: this.dropzoneDeserializer
|
||||
});
|
||||
} else if(item.kind === "string" && !["text/html", "text/plain", "Text"].includes(item.type) && $tw.utils.itemHasValidDataType(item)) {
|
||||
// Try to import the various data types we understand
|
||||
var fallbackTitle = self.wiki.generateNewTitle("Untitled");
|
||||
//Use the deserializer specified if any
|
||||
if(this.dropzoneDeserializer) {
|
||||
item.getAsString(function(str){
|
||||
var tiddlerFields = self.wiki.deserializeTiddlers(null,str,{title: fallbackTitle},{deserializer:self.dropzoneDeserializer});
|
||||
if(tiddlerFields && tiddlerFields.length) {
|
||||
readFileCallback(tiddlerFields);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$tw.utils.importPaste(item,fallbackTitle,readFileCallback);
|
||||
}
|
||||
} else if(item.kind === "string") {
|
||||
// Create tiddlers from string items
|
||||
var tiddlerFields;
|
||||
|
||||
@@ -90,7 +90,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
EditWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Refresh if an attribute has changed, or the type associated with the target tiddler has changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (this.getEditorType() !== this.editorType)) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (changedTiddlers[this.editTitle] && this.getEditorType() !== this.editorType)) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -58,25 +58,24 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.wiki.isImageTiddler(this.imageSource)) {
|
||||
var type = tiddler.fields.type,
|
||||
text = tiddler.fields.text,
|
||||
_canonical_uri = tiddler.fields._canonical_uri,
|
||||
typeInfo = $tw.config.contentTypeInfo[type] || {},
|
||||
deserializerType = typeInfo.deserializerType || type;
|
||||
_canonical_uri = tiddler.fields._canonical_uri;
|
||||
// If the tiddler has body text then it doesn't need to be lazily loaded
|
||||
if(text) {
|
||||
// Render the appropriate element for the image type by looking up the encoding in the content type info
|
||||
var encoding = typeInfo.encoding || "utf8";
|
||||
if (encoding === "base64") {
|
||||
// .pdf .png .jpg etc.
|
||||
src = "data:" + deserializerType + ";base64," + text;
|
||||
if (deserializerType === "application/pdf") {
|
||||
// Render the appropriate element for the image type
|
||||
switch(type) {
|
||||
case "application/pdf":
|
||||
tag = "embed";
|
||||
}
|
||||
} else {
|
||||
// .svg .tid .xml etc.
|
||||
src = "data:" + deserializerType + "," + encodeURIComponent(text);
|
||||
src = "data:application/pdf;base64," + text;
|
||||
break;
|
||||
case "image/svg+xml":
|
||||
src = "data:image/svg+xml," + encodeURIComponent(text);
|
||||
break;
|
||||
default:
|
||||
src = "data:" + type + ";base64," + text;
|
||||
break;
|
||||
}
|
||||
} else if(_canonical_uri) {
|
||||
switch(deserializerType) {
|
||||
switch(type) {
|
||||
case "application/pdf":
|
||||
tag = "embed";
|
||||
src = _canonical_uri;
|
||||
@@ -100,9 +99,6 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.imageClass) {
|
||||
domNode.setAttribute("class",this.imageClass);
|
||||
}
|
||||
if(this.imageUsemap) {
|
||||
domNode.setAttribute("usemap",this.imageUsemap);
|
||||
}
|
||||
if(this.imageWidth) {
|
||||
domNode.setAttribute("width",this.imageWidth);
|
||||
}
|
||||
@@ -142,7 +138,6 @@ ImageWidget.prototype.execute = function() {
|
||||
this.imageWidth = this.getAttribute("width");
|
||||
this.imageHeight = this.getAttribute("height");
|
||||
this.imageClass = this.getAttribute("class");
|
||||
this.imageUsemap = this.getAttribute("usemap");
|
||||
this.imageTooltip = this.getAttribute("tooltip");
|
||||
this.imageAlt = this.getAttribute("alt");
|
||||
this.lazyLoading = this.getAttribute("loading");
|
||||
@@ -153,7 +148,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
ImageWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.usemap || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
|
||||
if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -49,7 +49,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
|
||||
this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this);
|
||||
// Accumulate the <$set> widgets from each tiddler
|
||||
$tw.utils.each(this.tiddlerList,function(title) {
|
||||
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true, configTrimWhiteSpace:true});
|
||||
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true});
|
||||
if(parser) {
|
||||
var parseTreeNode = parser.tree[0];
|
||||
while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) {
|
||||
|
||||
@@ -43,11 +43,6 @@ LinkWidget.prototype.render = function(parent,nextSibling) {
|
||||
} else {
|
||||
// Just insert the link text
|
||||
var domNode = this.document.createElement("span");
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
@@ -143,11 +138,6 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
||||
widget: this
|
||||
});
|
||||
}
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert the link into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -217,7 +207,8 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if($tw.utils.count(changedAttributes) > 0 || changedTiddlers[this.to]) {
|
||||
if(changedAttributes.to || changedTiddlers[this.to] || changedAttributes["aria-label"] || changedAttributes.tooltip ||
|
||||
changedAttributes["class"] || changedAttributes.tabindex || changedAttributes.draggable || changedAttributes.tag) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
@@ -227,4 +218,3 @@ LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
exports.link = LinkWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -92,9 +92,12 @@ LinkCatcherWidget.prototype.handleNavigateEvent = function(event) {
|
||||
this.executingActions = false;
|
||||
}
|
||||
} else {
|
||||
// This is a navigate event generated by the actions of this linkcatcher,
|
||||
// so we don't trap it again, but just pass it to the parent
|
||||
this.parentWidget.dispatchEvent(event);
|
||||
// This is a navigate event generated by the actions of this linkcatcher, so we don't trap it again, but just pass it to the parent
|
||||
this.parentWidget.dispatchEvent({
|
||||
type: "tm-navigate",
|
||||
param: event.navigateTo,
|
||||
navigateTo: event.navigateTo
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -28,18 +28,6 @@ Inherit from the base widget class
|
||||
*/
|
||||
ListWidget.prototype = new Widget();
|
||||
|
||||
ListWidget.prototype.initialise = function(parseTreeNode,options) {
|
||||
// Bail if parseTreeNode is undefined, meaning that the ListWidget constructor was called without any arguments so that it can be subclassed
|
||||
if(parseTreeNode === undefined) {
|
||||
return;
|
||||
}
|
||||
// First call parent constructor to set everything else up
|
||||
Widget.prototype.initialise.call(this,parseTreeNode,options);
|
||||
// Now look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
||||
// This is safe to do during initialization because parse trees never change after creation
|
||||
this.findExplicitTemplates();
|
||||
}
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
@@ -50,8 +38,8 @@ ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
$tw.modules.applyMethods("storyview",this.storyViews);
|
||||
}
|
||||
this.parentDomNode = parent;
|
||||
var changedAttributes = this.computeAttributes();
|
||||
this.execute(changedAttributes);
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
// Construct the storyview
|
||||
var StoryView = this.storyViews[this.storyViewName];
|
||||
@@ -71,8 +59,7 @@ ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListWidget.prototype.execute = function(changedAttributes) {
|
||||
var self = this;
|
||||
ListWidget.prototype.execute = function() {
|
||||
// Get our attributes
|
||||
this.template = this.getAttribute("template");
|
||||
this.editTemplate = this.getAttribute("editTemplate");
|
||||
@@ -80,10 +67,6 @@ ListWidget.prototype.execute = function(changedAttributes) {
|
||||
this.counterName = this.getAttribute("counter");
|
||||
this.storyViewName = this.getAttribute("storyview");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Create join template only if needed
|
||||
if(this.join === undefined || (changedAttributes && changedAttributes.join)) {
|
||||
this.join = this.makeJoinTemplate();
|
||||
}
|
||||
// Compose the list elements
|
||||
this.list = this.getTiddlerList();
|
||||
var members = [],
|
||||
@@ -102,57 +85,18 @@ ListWidget.prototype.execute = function(changedAttributes) {
|
||||
this.history = [];
|
||||
};
|
||||
|
||||
ListWidget.prototype.findExplicitTemplates = function() {
|
||||
var self = this;
|
||||
this.explicitListTemplate = null;
|
||||
this.explicitEmptyTemplate = null;
|
||||
this.explicitJoinTemplate = null;
|
||||
this.hasTemplateInBody = false;
|
||||
var searchChildren = function(childNodes) {
|
||||
var foundInlineTemplate = false;
|
||||
$tw.utils.each(childNodes,function(node) {
|
||||
if(node.type === "list-template") {
|
||||
self.explicitListTemplate = node.children;
|
||||
} else if(node.type === "list-empty") {
|
||||
self.explicitEmptyTemplate = node.children;
|
||||
} else if(node.type === "list-join") {
|
||||
self.explicitJoinTemplate = node.children;
|
||||
} else if(node.type === "element" && node.tag === "p") {
|
||||
searchChildren(node.children);
|
||||
foundInlineTemplate = true;
|
||||
} else {
|
||||
foundInlineTemplate = true;
|
||||
}
|
||||
});
|
||||
return foundInlineTemplate;
|
||||
};
|
||||
this.hasTemplateInBody = searchChildren(this.parseTreeNode.children);
|
||||
}
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var limit = $tw.utils.getInt(this.getAttribute("limit",""),undefined);
|
||||
var defaultFilter = "[!is[system]sort[title]]";
|
||||
var results = this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
|
||||
if(limit !== undefined) {
|
||||
if(limit >= 0) {
|
||||
results = results.slice(0,limit);
|
||||
} else {
|
||||
results = results.slice(limit);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
var parser,
|
||||
emptyMessage = this.getAttribute("emptyMessage");
|
||||
// If emptyMessage attribute is not present or empty then look for an explicit empty template
|
||||
if(!emptyMessage) {
|
||||
if(this.explicitEmptyTemplate) {
|
||||
return this.explicitEmptyTemplate;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
emptyMessage = this.getAttribute("emptyMessage","");
|
||||
// this.wiki.parseText() calls
|
||||
// new Parser(..), which should only be done, if needed, because it's heavy!
|
||||
if (emptyMessage === "") {
|
||||
return [];
|
||||
}
|
||||
parser = this.wiki.parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
if(parser) {
|
||||
@@ -162,24 +106,6 @@ ListWidget.prototype.getEmptyMessage = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compose the template for a join between list items
|
||||
*/
|
||||
ListWidget.prototype.makeJoinTemplate = function() {
|
||||
var parser,
|
||||
join = this.getAttribute("join","");
|
||||
if(join) {
|
||||
parser = this.wiki.parseText("text/vnd.tiddlywiki",join,{parseAsInline:true})
|
||||
if(parser) {
|
||||
return parser.tree;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
return this.explicitJoinTemplate; // May be null, and that's fine
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compose the template for a list item
|
||||
*/
|
||||
@@ -188,7 +114,6 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
|
||||
var tiddler = this.wiki.getTiddler(title),
|
||||
isDraft = tiddler && tiddler.hasField("draft.of"),
|
||||
template = this.template,
|
||||
join = this.join,
|
||||
templateTree;
|
||||
if(isDraft && this.editTemplate) {
|
||||
template = this.editTemplate;
|
||||
@@ -197,29 +122,22 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
|
||||
if(template) {
|
||||
templateTree = [{type: "transclude", attributes: {tiddler: {type: "string", value: template}}}];
|
||||
} else {
|
||||
// Check for child nodes of the list widget
|
||||
if(this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
|
||||
// Check for a <$list-item> widget
|
||||
if(this.explicitListTemplate) {
|
||||
templateTree = this.explicitListTemplate;
|
||||
} else if(this.hasTemplateInBody) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
}
|
||||
}
|
||||
if(!templateTree || templateTree.length === 0) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
} else {
|
||||
// Default template is a link to the title
|
||||
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||
{type: "text", text: title}
|
||||
{type: "text", text: title}
|
||||
]}]}];
|
||||
}
|
||||
}
|
||||
// Return the list item
|
||||
var parseTreeNode = {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree, join: join};
|
||||
parseTreeNode.isLast = index === this.list.length - 1;
|
||||
var parseTreeNode = {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree};
|
||||
if(this.counterName) {
|
||||
parseTreeNode.counter = (index + 1).toString();
|
||||
parseTreeNode.counterName = this.counterName;
|
||||
parseTreeNode.isFirst = index === 0;
|
||||
parseTreeNode.isLast = index === this.list.length - 1;
|
||||
}
|
||||
return parseTreeNode;
|
||||
};
|
||||
@@ -235,7 +153,7 @@ ListWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.storyview.refreshStart(changedTiddlers,changedAttributes);
|
||||
}
|
||||
// Completely refresh if any of our attributes have changed
|
||||
if(changedAttributes.filter || changedAttributes.variable || changedAttributes.counter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.join || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) {
|
||||
if(changedAttributes.filter || changedAttributes.variable || changedAttributes.counter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) {
|
||||
this.refreshSelf();
|
||||
result = true;
|
||||
} else {
|
||||
@@ -307,8 +225,6 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
// If we are providing an counter variable then we must refresh the items, otherwise we can rearrange them
|
||||
var hasRefreshed = false,t;
|
||||
if(this.counterName) {
|
||||
var mustRefreshOldLast = false;
|
||||
var oldLength = this.children.length;
|
||||
// Cycle through the list and remove and re-insert the first item that has changed, and all the remaining items
|
||||
for(t=0; t<this.list.length; t++) {
|
||||
if(hasRefreshed || !this.children[t] || this.children[t].parseTreeNode.itemTitle !== this.list[t]) {
|
||||
@@ -316,9 +232,6 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
this.removeListItem(t);
|
||||
}
|
||||
this.insertListItem(t,this.list[t]);
|
||||
if(!hasRefreshed && t === oldLength) {
|
||||
mustRefreshOldLast = true;
|
||||
}
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// Refresh the item we're reusing
|
||||
@@ -326,12 +239,6 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
// If items were inserted then we must recreate the item that used to be at the last position as it is no longer last
|
||||
if(mustRefreshOldLast && oldLength > 0) {
|
||||
var oldLastIdx = oldLength-1;
|
||||
this.removeListItem(oldLastIdx);
|
||||
this.insertListItem(oldLastIdx,this.list[oldLastIdx]);
|
||||
}
|
||||
// If there are items to remove and we have not refreshed then recreate the item that will now be at the last position
|
||||
if(!hasRefreshed && this.children.length > this.list.length) {
|
||||
this.removeListItem(this.list.length-1);
|
||||
@@ -339,29 +246,10 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
}
|
||||
} else {
|
||||
// Cycle through the list, inserting and removing list items as needed
|
||||
var mustRecreateLastItem = false;
|
||||
if(this.join && this.join.length) {
|
||||
if(this.children.length !== this.list.length) {
|
||||
mustRecreateLastItem = true;
|
||||
} else if(prevList[prevList.length-1] !== this.list[this.list.length-1]) {
|
||||
mustRecreateLastItem = true;
|
||||
}
|
||||
}
|
||||
var isLast = false, wasLast = false;
|
||||
for(t=0; t<this.list.length; t++) {
|
||||
isLast = t === this.list.length-1;
|
||||
var index = this.findListItem(t,this.list[t]);
|
||||
wasLast = index === this.children.length-1;
|
||||
if(wasLast && (index !== t || this.children.length !== this.list.length)) {
|
||||
mustRecreateLastItem = !!(this.join && this.join.length);
|
||||
}
|
||||
if(index === undefined) {
|
||||
// The list item must be inserted
|
||||
if(isLast && mustRecreateLastItem && t>0) {
|
||||
// First re-create previosly-last item that will no longer be last
|
||||
this.removeListItem(t-1);
|
||||
this.insertListItem(t-1,this.list[t-1]);
|
||||
}
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
@@ -370,15 +258,9 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
this.removeListItem(n);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
// Refresh the item we're reusing, or recreate if necessary
|
||||
if(mustRecreateLastItem && (isLast || wasLast)) {
|
||||
this.removeListItem(t);
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,17 +350,8 @@ ListItemWidget.prototype.execute = function() {
|
||||
this.setVariable(this.parseTreeNode.counterName + "-first",this.parseTreeNode.isFirst ? "yes" : "no");
|
||||
this.setVariable(this.parseTreeNode.counterName + "-last",this.parseTreeNode.isLast ? "yes" : "no");
|
||||
}
|
||||
// Add join if needed
|
||||
var children = this.parseTreeNode.children,
|
||||
join = this.parseTreeNode.join;
|
||||
if(join && join.length && !this.parseTreeNode.isLast) {
|
||||
children = children.slice(0);
|
||||
$tw.utils.each(join,function(joinNode) {
|
||||
children.push(joinNode);
|
||||
})
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(children);
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -490,37 +363,4 @@ ListItemWidget.prototype.refresh = function(changedTiddlers) {
|
||||
|
||||
exports.listitem = ListItemWidget;
|
||||
|
||||
/*
|
||||
Make <$list-template> and <$list-empty> widgets that do nothing
|
||||
*/
|
||||
var ListTemplateWidget = function(parseTreeNode,options) {
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
ListTemplateWidget.prototype = new Widget();
|
||||
ListTemplateWidget.prototype.render = function() {}
|
||||
ListTemplateWidget.prototype.refresh = function() { return false; }
|
||||
|
||||
exports["list-template"] = ListTemplateWidget;
|
||||
|
||||
var ListEmptyWidget = function(parseTreeNode,options) {
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
ListEmptyWidget.prototype = new Widget();
|
||||
ListEmptyWidget.prototype.render = function() {}
|
||||
ListEmptyWidget.prototype.refresh = function() { return false; }
|
||||
|
||||
exports["list-empty"] = ListEmptyWidget;
|
||||
|
||||
var ListJoinWidget = function(parseTreeNode,options) {
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
ListJoinWidget.prototype = new Widget();
|
||||
ListJoinWidget.prototype.render = function() {}
|
||||
ListJoinWidget.prototype.refresh = function() { return false; }
|
||||
|
||||
exports["list-join"] = ListJoinWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -51,8 +51,7 @@ MacroCallWidget.prototype.execute = function() {
|
||||
var positionalName = 0,
|
||||
parseTreeNodes = [{
|
||||
type: "transclude",
|
||||
isBlock: this.parseTreeNode.isBlock,
|
||||
children: this.parseTreeNode.children
|
||||
isBlock: this.parseTreeNode.isBlock
|
||||
}];
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$variable",this.macroName);
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$type",this.parseType);
|
||||
|
||||
@@ -42,24 +42,31 @@ Compute the internal state of the widget
|
||||
*/
|
||||
ParametersWidget.prototype.execute = function() {
|
||||
var self = this;
|
||||
this.parametersDepth = Math.max(parseInt(this.getAttribute("$depth","1"),10) || 1,1);
|
||||
// Find the parent transclusions
|
||||
var pointer = this.getContainingTransclude();
|
||||
var pointer = this.parentWidget,
|
||||
depth = this.parametersDepth;
|
||||
while(pointer) {
|
||||
if(pointer instanceof TranscludeWidget) {
|
||||
depth--;
|
||||
if(depth <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pointer = pointer.parentWidget;
|
||||
}
|
||||
// Process each parameter
|
||||
if(pointer) {
|
||||
// It's important to remember this, because when we refresh, we'll need to make sure this widget is starting at the same index.
|
||||
this.initialParameterIndex = pointer.parameterIndex;
|
||||
if(pointer instanceof TranscludeWidget) {
|
||||
// Get the value for each defined parameter
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(self.parseTreeNode),function(attr) {
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(self.parseTreeNode),function(attr,index) {
|
||||
var name = attr.name;
|
||||
// If the attribute name starts with $$ then reduce to a single dollar
|
||||
if(name.substr(0,2) === "$$") {
|
||||
name = name.substr(1);
|
||||
}
|
||||
var value = pointer.getTransclusionParameter(name,self.getAttribute(attr.name,""));
|
||||
var value = pointer.getTransclusionParameter(name,index,self.getAttribute(attr.name,""));
|
||||
self.setVariable(name,value);
|
||||
});
|
||||
// We remember where we left the unnamed parameter index.
|
||||
this.finalParameterIndex = pointer.parameterIndex;
|
||||
// Assign any metaparameters
|
||||
$tw.utils.each(pointer.getTransclusionMetaParameters(),function(getValue,name) {
|
||||
var variableName = self.getAttribute("$" + name);
|
||||
@@ -77,29 +84,13 @@ Refresh the widget by ensuring our attributes are up to date
|
||||
*/
|
||||
ParametersWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
var pointer = this.getContainingTransclude();
|
||||
var currentParameterIndex;
|
||||
if(pointer) {
|
||||
currentParameterIndex = pointer.parameterIndex;
|
||||
}
|
||||
if(Object.keys(changedAttributes).length || currentParameterIndex !== this.initialParameterIndex) {
|
||||
if(Object.keys(changedAttributes).length) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(pointer) {
|
||||
// We set the index for unnamed parameters for our $transclude widget in case any later $parameters show up. They need to be able to confirm their indices are starting in the right place, because if not, they need to refresh.
|
||||
pointer.parameterIndex = this.finalParameterIndex;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
ParametersWidget.prototype.getContainingTransclude = function() {
|
||||
var pointer = this.parentWidget;
|
||||
while(pointer && !(pointer instanceof TranscludeWidget)) {
|
||||
pointer = pointer.parentWidget;
|
||||
}
|
||||
return pointer;
|
||||
};
|
||||
|
||||
exports.parameters = ParametersWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -40,10 +40,6 @@ RadioWidget.prototype.render = function(parent,nextSibling) {
|
||||
);
|
||||
this.inputDomNode = this.document.createElement("input");
|
||||
this.inputDomNode.setAttribute("type","radio");
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
if(isChecked) {
|
||||
this.inputDomNode.checked = true;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,6 @@ RangeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.inputDomNode.setAttribute("disabled",true);
|
||||
}
|
||||
this.inputDomNode.value = this.getValue();
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name:"mousedown", handlerObject:this, handlerMethod:"handleMouseDownEvent"},
|
||||
|
||||
@@ -12,8 +12,6 @@ Scrollable widget
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var DEBOUNCE_INTERVAL = 100; // Delay after last scroll event before updating the bound tiddler
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ScrollableWidget = function(parseTreeNode,options) {
|
||||
@@ -121,8 +119,8 @@ ScrollableWidget.prototype.scrollIntoView = function(element,callback,options) {
|
||||
};
|
||||
|
||||
ScrollableWidget.prototype.scrollSelectorIntoView = function(baseElement,selector,callback,options) {
|
||||
baseElement = baseElement || document;
|
||||
var element = $tw.utils.querySelectorSafe(selector,baseElement);
|
||||
baseElement = baseElement || document.body;
|
||||
var element = baseElement.querySelector(selector);
|
||||
if(element) {
|
||||
this.scrollIntoView(element,callback,options);
|
||||
}
|
||||
@@ -173,53 +171,6 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
|
||||
parent.insertBefore(this.outerDomNode,nextSibling);
|
||||
this.renderChildren(this.innerDomNode,null);
|
||||
this.domNodes.push(this.outerDomNode);
|
||||
// If the scroll position is bound to a tiddler
|
||||
if(this.scrollableBind) {
|
||||
// After a delay for rendering, scroll to the bound position
|
||||
this.updateScrollPositionFromBoundTiddler();
|
||||
// Set up event listener
|
||||
this.currentListener = this.listenerFunction.bind(this);
|
||||
this.outerDomNode.addEventListener("scroll", this.currentListener);
|
||||
}
|
||||
};
|
||||
|
||||
ScrollableWidget.prototype.listenerFunction = function(event) {
|
||||
self = this;
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(function() {
|
||||
var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
|
||||
newTiddlerFields = {
|
||||
title: self.scrollableBind,
|
||||
"scroll-left": self.outerDomNode.scrollLeft.toString(),
|
||||
"scroll-top": self.outerDomNode.scrollTop.toString()
|
||||
};
|
||||
if(!existingTiddler || (existingTiddler.fields["title"] !== newTiddlerFields["title"]) || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields));
|
||||
}
|
||||
}, DEBOUNCE_INTERVAL);
|
||||
}
|
||||
|
||||
ScrollableWidget.prototype.updateScrollPositionFromBoundTiddler = function() {
|
||||
// Bail if we're running on the fakedom
|
||||
if(!this.outerDomNode.scrollTo) {
|
||||
return;
|
||||
}
|
||||
var tiddler = this.wiki.getTiddler(this.scrollableBind);
|
||||
if(tiddler) {
|
||||
var scrollLeftTo = this.outerDomNode.scrollLeft;
|
||||
if(parseFloat(tiddler.fields["scroll-left"]).toString() === tiddler.fields["scroll-left"]) {
|
||||
scrollLeftTo = parseFloat(tiddler.fields["scroll-left"]);
|
||||
}
|
||||
var scrollTopTo = this.outerDomNode.scrollTop;
|
||||
if(parseFloat(tiddler.fields["scroll-top"]).toString() === tiddler.fields["scroll-top"]) {
|
||||
scrollTopTo = parseFloat(tiddler.fields["scroll-top"]);
|
||||
}
|
||||
this.outerDomNode.scrollTo({
|
||||
top: scrollTopTo,
|
||||
left: scrollLeftTo,
|
||||
behavior: "instant"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -227,7 +178,6 @@ Compute the internal state of the widget
|
||||
*/
|
||||
ScrollableWidget.prototype.execute = function() {
|
||||
// Get attributes
|
||||
this.scrollableBind = this.getAttribute("bind");
|
||||
this.fallthrough = this.getAttribute("fallthrough","yes");
|
||||
this["class"] = this.getAttribute("class");
|
||||
// Make child widgets
|
||||
@@ -243,22 +193,7 @@ ScrollableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
// If the bound tiddler has changed, update the eventListener and update scroll position
|
||||
if(changedAttributes["bind"]) {
|
||||
if(this.currentListener) {
|
||||
this.outerDomNode.removeEventListener("scroll", this.currentListener, false);
|
||||
}
|
||||
this.scrollableBind = this.getAttribute("bind");
|
||||
this.currentListener = this.listenerFunction.bind(this);
|
||||
this.outerDomNode.addEventListener("scroll", this.currentListener);
|
||||
}
|
||||
// Refresh children
|
||||
var result = this.refreshChildren(changedTiddlers);
|
||||
// If the bound tiddler has changed, update scroll position
|
||||
if(changedAttributes["bind"] || changedTiddlers[this.getAttribute("bind")]) {
|
||||
this.updateScrollPositionFromBoundTiddler();
|
||||
}
|
||||
return result;
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.scrollable = ScrollableWidget;
|
||||
|
||||
@@ -40,31 +40,7 @@ SelectWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
//Create element
|
||||
var domNode = this.document.createElement("select");
|
||||
if(this.selectClass) {
|
||||
domNode.className = this.selectClass;
|
||||
}
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
if(this.selectMultiple) {
|
||||
domNode.setAttribute("multiple","multiple");
|
||||
}
|
||||
if(this.selectSize) {
|
||||
domNode.setAttribute("size",this.selectSize);
|
||||
}
|
||||
if(this.selectTabindex) {
|
||||
domNode.setAttribute("tabindex",this.selectTabindex);
|
||||
}
|
||||
if(this.selectTooltip) {
|
||||
domNode.setAttribute("title",this.selectTooltip);
|
||||
}
|
||||
this.parentDomNode.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
this.renderChildren(parent,nextSibling);
|
||||
this.setSelectValue();
|
||||
if(this.selectFocus == "yes") {
|
||||
this.getSelectDomNode().focus();
|
||||
@@ -137,7 +113,7 @@ SelectWidget.prototype.setSelectValue = function() {
|
||||
Get the DOM node of the select element
|
||||
*/
|
||||
SelectWidget.prototype.getSelectDomNode = function() {
|
||||
return this.domNodes[0];
|
||||
return this.children[0].domNodes[0];
|
||||
};
|
||||
|
||||
// Return an array of the selected opion values
|
||||
@@ -169,11 +145,27 @@ SelectWidget.prototype.execute = function() {
|
||||
this.selectDefault = this.getAttribute("default");
|
||||
this.selectMultiple = this.getAttribute("multiple", false);
|
||||
this.selectSize = this.getAttribute("size");
|
||||
this.selectTabindex = this.getAttribute("tabindex");
|
||||
this.selectTooltip = this.getAttribute("tooltip");
|
||||
this.selectFocus = this.getAttribute("focus");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
var selectNode = {
|
||||
type: "element",
|
||||
tag: "select",
|
||||
children: this.parseTreeNode.children
|
||||
};
|
||||
if(this.selectClass) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"class",this.selectClass);
|
||||
}
|
||||
if(this.selectMultiple) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"multiple","multiple");
|
||||
}
|
||||
if(this.selectSize) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"size",this.selectSize);
|
||||
}
|
||||
if(this.selectTooltip) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"title",this.selectTooltip);
|
||||
}
|
||||
this.makeChildWidgets([selectNode]);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -182,21 +174,17 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
SelectWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// If we're using a different tiddler/field/index then completely refresh ourselves
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip || changedAttributes.tabindex) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
// If the target tiddler value has changed, just update setting and refresh the children
|
||||
} else {
|
||||
if(changedAttributes.class) {
|
||||
this.selectClass = this.getAttribute("class");
|
||||
this.getSelectDomNode().setAttribute("class",this.selectClass);
|
||||
}
|
||||
this.assignAttributes(this.getSelectDomNode(),{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
|
||||
var childrenRefreshed = this.refreshChildren(changedTiddlers);
|
||||
// If the target tiddler value has changed, just update setting and refresh the children
|
||||
if(changedTiddlers[this.selectTitle] || childrenRefreshed) {
|
||||
this.setSelectValue();
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ SlotWidget.prototype.execute = function() {
|
||||
var pointer = this.parentWidget,
|
||||
depth = this.slotDepth;
|
||||
while(pointer) {
|
||||
if(pointer instanceof TranscludeWidget && pointer.hasVisibleSlots()) {
|
||||
if(pointer instanceof TranscludeWidget) {
|
||||
depth--;
|
||||
if(depth <= 0) {
|
||||
break;
|
||||
|
||||
@@ -41,43 +41,27 @@ TranscludeWidget.prototype.execute = function() {
|
||||
this.collectAttributes();
|
||||
this.collectStringParameters();
|
||||
this.collectSlotFillParameters();
|
||||
// Determine whether we're being used in inline or block mode
|
||||
var parseAsInline = !this.parseTreeNode.isBlock;
|
||||
if(this.transcludeMode === "inline") {
|
||||
parseAsInline = true;
|
||||
} else if(this.transcludeMode === "block") {
|
||||
parseAsInline = false;
|
||||
}
|
||||
// Set 'thisTiddler'
|
||||
this.setVariable("thisTiddler",this.transcludeTitle);
|
||||
var parseTreeNodes, target;
|
||||
// Get the parse tree nodes that we are transcluding
|
||||
var target = this.getTransclusionTarget(),
|
||||
parseTreeNodes = target.parseTreeNodes;
|
||||
this.sourceText = target.text;
|
||||
this.sourceType = target.type;
|
||||
this.parseAsInline = target.parseAsInline;
|
||||
// Process the transclusion according to the output type
|
||||
switch(this.transcludeOutput || "text/html") {
|
||||
case "text/html":
|
||||
// Return the parse tree nodes of the target
|
||||
target = this.parseTransclusionTarget(parseAsInline);
|
||||
this.parseAsInline = target.parseAsInline;
|
||||
parseTreeNodes = target.parseTreeNodes;
|
||||
// No further processing required
|
||||
break;
|
||||
case "text/raw":
|
||||
// Just return the raw text
|
||||
target = this.getTransclusionTarget();
|
||||
parseTreeNodes = [{type: "text", text: target.text}];
|
||||
parseTreeNodes = [{type: "text", text: this.sourceText}];
|
||||
break;
|
||||
default:
|
||||
// "text/plain" is the plain text result of wikifying the text
|
||||
target = this.parseTransclusionTarget(parseAsInline);
|
||||
var widgetNode = this.wiki.makeWidget(target.parser,{
|
||||
parentWidget: this,
|
||||
document: $tw.fakeDocument
|
||||
});
|
||||
var container = $tw.fakeDocument.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
parseTreeNodes = [{type: "text", text: container.textContent}];
|
||||
// text/plain
|
||||
var plainText = this.wiki.renderText("text/plain",this.sourceType,this.sourceText,{parentWidget: this});
|
||||
parseTreeNodes = [{type: "text", text: plainText}];
|
||||
break;
|
||||
}
|
||||
this.sourceText = target.text;
|
||||
this.parserType = target.type;
|
||||
// Set the legacy transclusion context variables only if we're not transcluding a variable
|
||||
if(!this.transcludeVariable) {
|
||||
var recursionMarker = this.makeRecursionMarker();
|
||||
@@ -109,7 +93,6 @@ TranscludeWidget.prototype.collectAttributes = function() {
|
||||
this.recursionMarker = this.getAttribute("recursionMarker","yes");
|
||||
} else {
|
||||
this.transcludeVariable = this.getAttribute("$variable");
|
||||
this.transcludeVariableIsFunction = false;
|
||||
this.transcludeType = this.getAttribute("$type");
|
||||
this.transcludeOutput = this.getAttribute("$output","text/html");
|
||||
this.transcludeTitle = this.getAttribute("$tiddler",this.getVariable("currentTiddler"));
|
||||
@@ -175,132 +158,114 @@ TranscludeWidget.prototype.collectSlotFillParameters = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Get transcluded details as an object {text:,type:}
|
||||
Get transcluded parse tree nodes as an object {parser:,text:,type:}
|
||||
*/
|
||||
TranscludeWidget.prototype.getTransclusionTarget = function() {
|
||||
var self = this;
|
||||
var text;
|
||||
// Return the text and type of the target
|
||||
if(this.hasAttribute("$variable")) {
|
||||
if(this.transcludeVariable) {
|
||||
// Transcluding a variable
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()});
|
||||
this.transcludeVariableIsFunction = variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition;
|
||||
text = variableInfo.text;
|
||||
this.transcludeFunctionResult = text;
|
||||
return {
|
||||
text: variableInfo.text,
|
||||
type: this.transcludeType
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Transcluding a text reference
|
||||
var parserInfo = this.wiki.getTextReferenceParserInfo(
|
||||
this.transcludeTitle,
|
||||
this.transcludeField,
|
||||
this.transcludeIndex,
|
||||
{
|
||||
subTiddler: this.transcludeSubTiddler,
|
||||
defaultType: this.transcludeType
|
||||
});
|
||||
return {
|
||||
text: parserInfo.text,
|
||||
type: parserInfo.type
|
||||
};
|
||||
// Determine whether we're being used in inline or block mode
|
||||
var parseAsInline = !this.parseTreeNode.isBlock;
|
||||
if(this.transcludeMode === "inline") {
|
||||
parseAsInline = true;
|
||||
} else if(this.transcludeMode === "block") {
|
||||
parseAsInline = false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get transcluded parse tree nodes as an object {text:,type:,parseTreeNodes:,parseAsInline:}
|
||||
*/
|
||||
TranscludeWidget.prototype.parseTransclusionTarget = function(parseAsInline) {
|
||||
var self = this;
|
||||
var parser;
|
||||
// Get the parse tree
|
||||
if(this.hasAttribute("$variable")) {
|
||||
if(this.transcludeVariable) {
|
||||
// Transcluding a variable
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
|
||||
srcVariable = variableInfo && variableInfo.srcVariable;
|
||||
if(srcVariable && srcVariable.isFunctionDefinition) {
|
||||
this.transcludeVariableIsFunction = true;
|
||||
this.transcludeFunctionResult = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
|
||||
}
|
||||
if(variableInfo.text) {
|
||||
if(srcVariable && srcVariable.isFunctionDefinition) {
|
||||
parser = {
|
||||
tree: [{
|
||||
type: "text",
|
||||
text: this.transcludeFunctionResult
|
||||
}],
|
||||
source: this.transcludeFunctionResult,
|
||||
type: "text/vnd.tiddlywiki"
|
||||
if(this.transcludeVariable) {
|
||||
// Transcluding a variable
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
|
||||
srcVariable = variableInfo && variableInfo.srcVariable;
|
||||
if(srcVariable) {
|
||||
if(srcVariable.isFunctionDefinition) {
|
||||
// Function to return parameters by name or position
|
||||
var fnGetParam = function(name,index) {
|
||||
// Parameter names starting with dollar must be escaped to double dollars
|
||||
if(name.charAt(0) === "$") {
|
||||
name = "$" + name;
|
||||
}
|
||||
// Look for the parameter by name
|
||||
if(self.hasAttribute(name)) {
|
||||
return self.getAttribute(name);
|
||||
// Look for the parameter by index
|
||||
} else if(self.hasAttribute(index + "")) {
|
||||
return self.getAttribute(index + "");
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || "";
|
||||
parser = {
|
||||
tree: [{
|
||||
type: "text",
|
||||
text: result
|
||||
}],
|
||||
source: result,
|
||||
type: "text/vnd.tiddlywiki"
|
||||
};
|
||||
if(parseAsInline) {
|
||||
parser.tree[0] = {
|
||||
type: "text",
|
||||
text: result
|
||||
};
|
||||
if(parseAsInline) {
|
||||
parser.tree[0] = {
|
||||
type: "text",
|
||||
text: this.transcludeFunctionResult
|
||||
};
|
||||
} else {
|
||||
parser.tree[0] = {
|
||||
type: "element",
|
||||
tag: "p",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: this.transcludeFunctionResult
|
||||
}]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
|
||||
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
|
||||
parser = srcVariable[cacheKey];
|
||||
} else {
|
||||
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable && srcVariable.configTrimWhiteSpace});
|
||||
if(variableInfo.isCacheable) {
|
||||
srcVariable[cacheKey] = parser;
|
||||
}
|
||||
parser.tree[0] = {
|
||||
type: "element",
|
||||
tag: "p",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: result
|
||||
}]
|
||||
}
|
||||
}
|
||||
if(parser) {
|
||||
// Add parameters widget for procedures and custom widgets
|
||||
if(srcVariable && (srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition)) {
|
||||
parser = {
|
||||
tree: [
|
||||
{
|
||||
type: "parameters",
|
||||
children: parser.tree
|
||||
}
|
||||
],
|
||||
source: parser.source,
|
||||
type: parser.type
|
||||
}
|
||||
$tw.utils.each(srcVariable.params,function(param) {
|
||||
var name = param.name;
|
||||
// Parameter names starting with dollar must be escaped to double dollars
|
||||
if(name.charAt(0) === "$") {
|
||||
name = "$" + name;
|
||||
}
|
||||
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
|
||||
});
|
||||
} else if(srcVariable && !srcVariable.isFunctionDefinition) {
|
||||
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
|
||||
parser = {
|
||||
tree: [
|
||||
{
|
||||
type: "vars",
|
||||
children: parser.tree
|
||||
}
|
||||
],
|
||||
source: parser.source,
|
||||
type: parser.type
|
||||
}
|
||||
$tw.utils.each(variableInfo.params,function(param) {
|
||||
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],"__" + param.name + "__",param.value)
|
||||
});
|
||||
} else {
|
||||
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
|
||||
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
|
||||
parser = srcVariable[cacheKey];
|
||||
} else {
|
||||
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
|
||||
if(variableInfo.isCacheable) {
|
||||
srcVariable[cacheKey] = parser;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(parser) {
|
||||
// Add parameters widget for procedures and custom widgets
|
||||
if(srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition) {
|
||||
parser = {
|
||||
tree: [
|
||||
{
|
||||
type: "parameters",
|
||||
children: parser.tree
|
||||
}
|
||||
],
|
||||
source: parser.source,
|
||||
type: parser.type
|
||||
}
|
||||
$tw.utils.each(srcVariable.params,function(param) {
|
||||
var name = param.name;
|
||||
// Parameter names starting with dollar must be escaped to double dollars
|
||||
if(name.charAt(0) === "$") {
|
||||
name = "$" + name;
|
||||
}
|
||||
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
|
||||
});
|
||||
} else {
|
||||
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
|
||||
parser = {
|
||||
tree: [
|
||||
{
|
||||
type: "vars",
|
||||
children: parser.tree
|
||||
}
|
||||
],
|
||||
source: parser.source,
|
||||
type: parser.type
|
||||
}
|
||||
$tw.utils.each(variableInfo.params,function(param) {
|
||||
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],"__" + param.name + "__",param.value)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Transcluding a text reference
|
||||
@@ -315,13 +280,24 @@ TranscludeWidget.prototype.parseTransclusionTarget = function(parseAsInline) {
|
||||
});
|
||||
}
|
||||
// Return the parse tree
|
||||
return {
|
||||
parser: parser,
|
||||
parseTreeNodes: parser ? parser.tree : (this.slotFillParseTrees["ts-missing"] || []),
|
||||
parseAsInline: parseAsInline,
|
||||
text: parser && parser.source,
|
||||
type: parser && parser.type
|
||||
};
|
||||
if(parser) {
|
||||
return {
|
||||
parser: parser,
|
||||
parseTreeNodes: parser.tree,
|
||||
parseAsInline: parseAsInline,
|
||||
text: parser.source,
|
||||
type: parser.type
|
||||
};
|
||||
} else {
|
||||
// If there's no parse tree then return the missing slot value
|
||||
return {
|
||||
parser: null,
|
||||
parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []),
|
||||
parseAsInline: parseAsInline,
|
||||
text: null,
|
||||
type: null
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -358,26 +334,14 @@ TranscludeWidget.prototype.getOrderedTransclusionParameters = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
The parameter index indicates is used by internal $parameter widgets to sequentially assign unnamed parameters.
|
||||
Fetch the value of a parameter
|
||||
*/
|
||||
Object.defineProperty(TranscludeWidget.prototype, 'parameterIndex', {
|
||||
get: function() { return this.claimedIndices || 0; },
|
||||
set: function(value) { this.claimedIndices = value; }
|
||||
});
|
||||
|
||||
/*
|
||||
Fetch the value of a parameter given either its name or index
|
||||
*/
|
||||
TranscludeWidget.prototype.getTransclusionParameter = function(name,defaultValue) {
|
||||
TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaultValue) {
|
||||
if(name in this.stringParametersByName) {
|
||||
return this.stringParametersByName[name];
|
||||
} else {
|
||||
// Let's see if this name was already assigned an index
|
||||
var index = this.parameterIndex;
|
||||
var name = "" + index;
|
||||
if(name in this.stringParametersByName) {
|
||||
// This parameter now corresponds to this index. No other parameters may correspond to it.
|
||||
this.claimedIndices = index + 1;
|
||||
return this.stringParametersByName[name];
|
||||
}
|
||||
}
|
||||
@@ -416,13 +380,6 @@ TranscludeWidget.prototype.getTransclusionSlotFill = function(name,defaultParseT
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Return whether this transclusion should be visible to the slot widget
|
||||
*/
|
||||
TranscludeWidget.prototype.hasVisibleSlots = function() {
|
||||
return this.getAttribute("$fillignore","no") === "no";
|
||||
}
|
||||
|
||||
/*
|
||||
Compose a string comprising the title, field and/or index to identify this transclusion for recursion detection
|
||||
*/
|
||||
@@ -448,21 +405,12 @@ TranscludeWidget.prototype.parserNeedsRefresh = function() {
|
||||
return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType)
|
||||
};
|
||||
|
||||
TranscludeWidget.prototype.functionNeedsRefresh = function() {
|
||||
var oldResult = this.transcludeFunctionResult;
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()});
|
||||
var newResult = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
|
||||
return oldResult !== newResult;
|
||||
}
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TranscludeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Reset the parameter index so that internal $parameter widgets can double-check their unnamed parameter indices.
|
||||
this.parameterIndex = 0;
|
||||
if(($tw.utils.count(changedAttributes) > 0) || (this.transcludeVariableIsFunction && this.functionNeedsRefresh()) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
|
||||
if(($tw.utils.count(changedAttributes) > 0) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -112,18 +112,14 @@ Get the prevailing value of a context variable
|
||||
name: name of variable
|
||||
options: see below
|
||||
Options include
|
||||
|
||||
params: array of {name:, value:} for each parameter
|
||||
defaultValue: default value if the variable is not defined
|
||||
source: optional source iterator for evaluating function invocations
|
||||
allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent
|
||||
|
||||
Returns an object with the following fields:
|
||||
|
||||
params: array of {name:,value:} or {value:} of parameters to be applied
|
||||
params: array of {name:,value:} of parameters passed to wikitext variables
|
||||
text: text of variable, with parameters properly substituted
|
||||
resultList: result of variable evaluation as an array
|
||||
srcVariable: reference to the object defining the variable
|
||||
*/
|
||||
Widget.prototype.getVariableInfo = function(name,options) {
|
||||
options = options || {};
|
||||
@@ -139,8 +135,7 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
||||
if(variable) {
|
||||
var originalValue = variable.value,
|
||||
value = originalValue,
|
||||
params = [],
|
||||
resultList = [value];
|
||||
params = [];
|
||||
// Only substitute parameter and variable references if this variable was defined with the \define pragma
|
||||
if(variable.isMacroDefinition) {
|
||||
params = self.resolveVariableParameters(variable.params,actualParams);
|
||||
@@ -149,28 +144,10 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
||||
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
|
||||
});
|
||||
value = self.substituteVariableReferences(value,options);
|
||||
resultList = [value];
|
||||
} else if(variable.isFunctionDefinition) {
|
||||
// Function evaluations
|
||||
params = self.resolveVariableParameters(variable.params,actualParams);
|
||||
var variables = options.variables || Object.create(null);
|
||||
// Apply default parameter values
|
||||
$tw.utils.each(variable.params,function(param,index) {
|
||||
if(param["default"]) {
|
||||
variables[param.name] = param["default"];
|
||||
}
|
||||
});
|
||||
// Parameters are an array of {value:} or {name:, value:} pairs
|
||||
$tw.utils.each(params,function(param) {
|
||||
variables[param.name] = param.value;
|
||||
});
|
||||
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
|
||||
value = resultList[0] || "";
|
||||
}
|
||||
return {
|
||||
text: value,
|
||||
params: params,
|
||||
resultList: resultList,
|
||||
srcVariable: variable,
|
||||
isCacheable: originalValue === value
|
||||
};
|
||||
@@ -182,7 +159,7 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
||||
}
|
||||
return {
|
||||
text: text,
|
||||
resultList: [text]
|
||||
srcVariable: {}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -340,11 +317,62 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
};
|
||||
},
|
||||
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables,
|
||||
evaluateVariable: self.evaluateVariable,
|
||||
resolveVariableParameters: self.resolveVariableParameters,
|
||||
wiki: self.wiki
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Evaluate a variable and associated actual parameters and result the resulting array.
|
||||
The way that the variable is evaluated depends upon its type:
|
||||
* Functions are evaluated as parameterised filter strings
|
||||
* Macros are returned as plain text with substitution of parameters
|
||||
* Procedures and widgets are returned as plain text
|
||||
|
||||
Options are:
|
||||
params - the actual parameters – may be one of:
|
||||
* an array of values that may be an anonymous string value, or a {name:, value:} pair
|
||||
* a hashmap of {name: value} pairs
|
||||
* a function invoked with parameters (name,index) that returns a parameter value by name or position
|
||||
source - iterator for source tiddlers
|
||||
*/
|
||||
Widget.prototype.evaluateVariable = function(name,options) {
|
||||
options = options || {};
|
||||
var params = options.params || [];
|
||||
// Get the details of the variable (includes processing text substitution for macros
|
||||
var variableInfo = this.getVariableInfo(name,{params: params,defaultValue: ""});
|
||||
// Process function parameters
|
||||
var variables = Object.create(null);
|
||||
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
// Apply default parameter values
|
||||
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
|
||||
if(param["default"]) {
|
||||
variables[param.name] = param["default"];
|
||||
}
|
||||
});
|
||||
if($tw.utils.isArray(params)) {
|
||||
// Parameters are an array of values or {name:, value:} pairs
|
||||
$tw.utils.each(this.resolveVariableParameters(variableInfo.srcVariable.params,params),function(param) {
|
||||
variables[param.name] = param.value;
|
||||
});
|
||||
} else if(typeof params === "function") {
|
||||
// Parameters are passed via a function
|
||||
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
|
||||
variables[param.name] = params(param.name,index) || param["default"] || "";
|
||||
});
|
||||
} else {
|
||||
// Parameters are a hashmap
|
||||
$tw.utils.each(params,function(value,name) {
|
||||
variables[name] = value;
|
||||
});
|
||||
}
|
||||
return this.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables),options.source);
|
||||
} else {
|
||||
return [variableInfo.text];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed.
|
||||
Options include:
|
||||
@@ -375,12 +403,16 @@ Widget.prototype.computeAttribute = function(attribute) {
|
||||
if(attribute.type === "filtered") {
|
||||
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
|
||||
} else if(attribute.type === "indirect") {
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
|
||||
} else if(attribute.type === "macro") {
|
||||
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
|
||||
value = variableInfo.text;
|
||||
} else if(attribute.type === "substituted") {
|
||||
value = this.wiki.getSubstitutedText(attribute.rawValue,this) || "";
|
||||
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
// It is a function definition. Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter
|
||||
var paramArray = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params);
|
||||
value = this.evaluateVariable(attribute.value.name,{params: paramArray})[0] || "";
|
||||
} else {
|
||||
value = variableInfo.text;
|
||||
}
|
||||
} else { // String attribute
|
||||
value = attribute.value;
|
||||
}
|
||||
@@ -413,34 +445,16 @@ Widget.prototype.getAttribute = function(name,defaultText) {
|
||||
};
|
||||
|
||||
/*
|
||||
Assign the common attributes of the widget to a domNode
|
||||
Assign the computed attributes of the widget to a domNode
|
||||
options include:
|
||||
sourcePrefix: prefix of attributes that are to be directly assigned (defaults to the empty string meaning all attributes)
|
||||
destPrefix: prefix to be applied to attribute names that are to be directly assigned (defaults to the emtpy string which means no prefix is added)
|
||||
changedAttributes: hashmap by attribute name of attributes to process (if missing, process all attributes)
|
||||
excludeEventAttributes: ignores attributes whose name would begin with "on"
|
||||
excludeEventAttributes: ignores attributes whose name begins with "on"
|
||||
*/
|
||||
Widget.prototype.assignAttributes = function(domNode,options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
changedAttributes = options.changedAttributes || this.attributes,
|
||||
sourcePrefix = options.sourcePrefix || "",
|
||||
destPrefix = options.destPrefix || "",
|
||||
EVENT_ATTRIBUTE_PREFIX = "on";
|
||||
var self = this;
|
||||
var assignAttribute = function(name,value) {
|
||||
// Process any style attributes before considering sourcePrefix and destPrefix
|
||||
if(name.substr(0,6) === "style." && name.length > 6) {
|
||||
domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value;
|
||||
return;
|
||||
}
|
||||
// Check if the sourcePrefix is a match
|
||||
if(name.substr(0,sourcePrefix.length) === sourcePrefix) {
|
||||
name = destPrefix + name.substr(sourcePrefix.length);
|
||||
} else {
|
||||
value = undefined;
|
||||
}
|
||||
// Check for excluded attribute names
|
||||
if(options.excludeEventAttributes && name.substr(0,2).toLowerCase() === EVENT_ATTRIBUTE_PREFIX) {
|
||||
if(options.excludeEventAttributes && name.substr(0,2) === "on") {
|
||||
value = undefined;
|
||||
}
|
||||
if(value !== undefined) {
|
||||
@@ -450,24 +464,26 @@ Widget.prototype.assignAttributes = function(domNode,options) {
|
||||
namespace = "http://www.w3.org/1999/xlink";
|
||||
name = name.substr(6);
|
||||
}
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(namespace,name,value);
|
||||
} catch(e) {
|
||||
// Handle styles
|
||||
if(name.substr(0,6) === "style." && name.length > 6) {
|
||||
domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value;
|
||||
} else {
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(namespace,name,value);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// If the parse tree node has the orderedAttributes property then use that order
|
||||
}
|
||||
// Not all parse tree nodes have the orderedAttributes property
|
||||
if(this.parseTreeNode.orderedAttributes) {
|
||||
$tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) {
|
||||
if(attribute.name in changedAttributes) {
|
||||
assignAttribute(attribute.name,self.getAttribute(attribute.name));
|
||||
}
|
||||
});
|
||||
// Otherwise update each changed attribute irrespective of order
|
||||
assignAttribute(attribute.name,self.attributes[attribute.name]);
|
||||
});
|
||||
} else {
|
||||
$tw.utils.each(changedAttributes,function(value,name) {
|
||||
assignAttribute(name,self.getAttribute(name));
|
||||
$tw.utils.each(Object.keys(self.attributes).sort(),function(name) {
|
||||
assignAttribute(name,self.attributes[name]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -530,8 +546,8 @@ Widget.prototype.makeChildWidget = function(parseTreeNode,options) {
|
||||
var variableDefinitionName = "$" + parseTreeNode.type;
|
||||
if(this.variables[variableDefinitionName]) {
|
||||
var isOverrideable = function() {
|
||||
// Widget is overrideable if its name contains a period, or if it is an existing JS widget and we're not in safe mode
|
||||
return parseTreeNode.type.indexOf(".") !== -1 || (!!self.widgetClasses[parseTreeNode.type] && !$tw.safeMode);
|
||||
// Widget is overrideable if it has a double dollar user defined name, or if it is an existing JS widget and we're not in safe mode
|
||||
return parseTreeNode.type.charAt(0) === "$" || (!!self.widgetClasses[parseTreeNode.type] && !$tw.safeMode);
|
||||
};
|
||||
if(!parseTreeNode.isNotRemappable && isOverrideable()) {
|
||||
var variableInfo = this.getVariableInfo(variableDefinitionName,{allowSelfAssigned: true});
|
||||
@@ -813,20 +829,6 @@ Widget.prototype.allowActionPropagation = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Evaluate a variable with parameters. This is a static convenience method that attempts to evaluate a variable as a function, returning an array of strings
|
||||
*/
|
||||
Widget.evaluateVariable = function(widget,name,options) {
|
||||
var result;
|
||||
if(widget.getVariableInfo) {
|
||||
var variableInfo = widget.getVariableInfo(name,options);
|
||||
result = variableInfo.resultList || [variableInfo.text];
|
||||
} else {
|
||||
result = [widget.getVariable(name)];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.widget = Widget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -1063,34 +1063,6 @@ exports.getTextReferenceParserInfo = function(title,field,index,options) {
|
||||
return parserInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse a block of text of a specified MIME type
|
||||
text: text on which to perform substitutions
|
||||
widget
|
||||
options: see below
|
||||
Options include:
|
||||
substitutions: an optional array of substitutions
|
||||
*/
|
||||
exports.getSubstitutedText = function(text,widget,options) {
|
||||
options = options || {};
|
||||
text = text || "";
|
||||
var self = this,
|
||||
substitutions = options.substitutions || [],
|
||||
output;
|
||||
// Evaluate embedded filters and substitute with first result
|
||||
output = text.replace(/\$\{([\S\s]+?)\}\$/g, function(match,filter) {
|
||||
return self.filterTiddlers(filter,widget)[0] || "";
|
||||
});
|
||||
// Process any substitutions provided in options
|
||||
$tw.utils.each(substitutions,function(substitute) {
|
||||
output = $tw.utils.replaceString(output,new RegExp("\\$" + $tw.utils.escapeRegExp(substitute.name) + "\\$","mg"),substitute.value);
|
||||
});
|
||||
// Substitute any variable references with their values
|
||||
return output.replace(/\$\(([^\)\$]+)\)\$/g, function(match,varname) {
|
||||
return widget.getVariable(varname,{defaultValue: ""})
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Make a widget tree for a parse tree
|
||||
parser: parser object
|
||||
@@ -1177,7 +1149,7 @@ exports.makeTranscludeWidget = function(title,options) {
|
||||
if(options.importVariables) {
|
||||
parseTreeImportVariables.attributes.filter.value = options.importVariables;
|
||||
} else if(options.importPageMacros) {
|
||||
parseTreeImportVariables.attributes.filter.value = this.getTiddlerText("$:/core/config/GlobalImportFilter");
|
||||
parseTreeImportVariables.attributes.filter.value = "[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]";
|
||||
}
|
||||
parseTreeDiv.tree[0].children.push(parseTreeImportVariables);
|
||||
parseTreeImportVariables.children.push(parseTreeTransclude);
|
||||
@@ -1287,7 +1259,7 @@ exports.search = function(text,options) {
|
||||
console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e);
|
||||
}
|
||||
} else if(options.some) {
|
||||
terms = text.trim().split(/[^\S\xA0]+/);
|
||||
terms = text.trim().split(/ +/);
|
||||
if(terms.length === 1 && terms[0] === "") {
|
||||
searchTermsRegExps = null;
|
||||
} else {
|
||||
@@ -1298,7 +1270,7 @@ exports.search = function(text,options) {
|
||||
searchTermsRegExps.push(new RegExp("(" + regExpStr + ")",flags));
|
||||
}
|
||||
} else { // default: words
|
||||
terms = text.split(/[^\S\xA0]+/);
|
||||
terms = text.split(/ +/);
|
||||
if(terms.length === 1 && terms[0] === "") {
|
||||
searchTermsRegExps = null;
|
||||
} else {
|
||||
@@ -1443,14 +1415,6 @@ exports.checkTiddlerText = function(title,targetText,options) {
|
||||
return text === targetText;
|
||||
}
|
||||
|
||||
/*
|
||||
Execute an action string without an associated context widget
|
||||
*/
|
||||
exports.invokeActionString = function(actions,event,variables,options) {
|
||||
var widget = this.makeWidget(null,{parentWidget: options.parentWidget});
|
||||
widget.invokeActionString(actions,null,event,variables);
|
||||
};
|
||||
|
||||
/*
|
||||
Read an array of browser File objects, invoking callback(tiddlerFieldsArray) once they're all read
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,6 @@ modal-footer-background: #f5f5f5
|
||||
modal-footer-border: #dddddd
|
||||
modal-header-border: #eeeeee
|
||||
muted-foreground: #bbb
|
||||
network-activity-foreground: #448844
|
||||
notification-background: #ffffdd
|
||||
notification-border: #999999
|
||||
page-background: #f4f4f4
|
||||
|
||||
@@ -3,7 +3,6 @@ tags: $:/tags/Exporter
|
||||
description: {{$:/language/Exporters/StaticRiver}}
|
||||
extension: .html
|
||||
|
||||
\define tv-config-static() yes
|
||||
\define tv-wikilink-template() #$uri_encoded$
|
||||
\define tv-config-toolbar-icons() no
|
||||
\define tv-config-toolbar-text() no
|
||||
|
||||
@@ -3,5 +3,5 @@ title: $:/core/templates/exporters/StaticRiver/Content
|
||||
\define renderContent()
|
||||
{{{ $(exportFilter)$ ||$:/core/templates/static-tiddler}}}
|
||||
\end
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
<<renderContent>>
|
||||
|
||||
@@ -7,5 +7,5 @@ condition: [<count>compare:lte[1]]
|
||||
\define renderContent()
|
||||
{{{ $(exportFilter)$ +[limit[1]] ||$:/core/templates/tid-tiddler}}}
|
||||
\end
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
<<renderContent>>
|
||||
@@ -1,14 +1,11 @@
|
||||
title: $:/core/save/all-external-js
|
||||
|
||||
\whitespace trim
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
\define saveTiddlerFilter()
|
||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
|
||||
\end
|
||||
|
||||
<!-- Important: core library is provided by serving URI encoded $:/core/templates/tiddlywiki5.js -->
|
||||
\define defaultCoreURL() %24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js
|
||||
|
||||
<$let coreURL={{{ [[coreURL]is[variable]then<coreURL>else<defaultCoreURL>] }}}>
|
||||
{{$:/core/templates/tiddlywiki5-external-js.html}}
|
||||
</$let>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
title: $:/core/save/offline-external-js
|
||||
|
||||
\whitespace trim
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
\define saveTiddlerFilter()
|
||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/plugins/tiddlywiki/filesystem]] -[[$:/plugins/tiddlywiki/tiddlyweb]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
|
||||
\end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
title: $:/core/templates/html-json-skinny-tiddler
|
||||
|
||||
<$text text=<<join>>/><$jsontiddler tiddler=<<currentTiddler>> exclude="text" escapeUnsafeScriptChars="yes"/>
|
||||
<$list filter="[<numTiddlers>compare:number:gteq[1]] ~[<counter>!match[1]]">`,`<$text text=<<newline>>/></$list>
|
||||
<$jsontiddler tiddler=<<currentTiddler>> exclude="text" escapeUnsafeScriptChars="yes"/>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
title: $:/core/templates/html-json-tiddler
|
||||
|
||||
<$jsontiddler tiddler=<<currentTiddler>> escapeUnsafeScriptChars="yes"/>
|
||||
<$list filter="[<counter>!match[1]]">`,`<$text text=<<newline>>/></$list><$jsontiddler tiddler=<<currentTiddler>> escapeUnsafeScriptChars="yes"/>
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/core/save/all
|
||||
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
\define saveTiddlerFilter()
|
||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
|
||||
\end
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
title: $:/core/templates/server/static.tiddler.html
|
||||
|
||||
\whitespace trim
|
||||
\define tv-config-static() yes
|
||||
\define tv-wikilink-template() $uri_encoded$
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
|
||||
@@ -4,7 +4,7 @@ title: $:/core/templates/single.tiddler.window
|
||||
\define containerClasses()
|
||||
tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
|
||||
\end
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
|
||||
<$vars
|
||||
tv-config-toolbar-icons={{$:/config/Toolbar/Icons}}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
title: $:/core/templates/static.template.html
|
||||
type: text/vnd.tiddlywiki-html
|
||||
|
||||
\define tv-config-static() yes
|
||||
\define tv-wikilink-template() static/$uri_doubleencoded$.html
|
||||
\define tv-config-toolbar-icons() no
|
||||
\define tv-config-toolbar-text() no
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
title: $:/core/templates/static.tiddler.html
|
||||
|
||||
\define tv-wikilink-template() $uri_doubleencoded$.html
|
||||
\define tv-config-static() yes
|
||||
\define tv-config-toolbar-icons() no
|
||||
\define tv-config-toolbar-text() no
|
||||
\define tv-config-toolbar-class() tc-btn-invisible
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
`<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@@ -6,12 +6,14 @@ title: $:/core/templates/store.area.template.html
|
||||
<$list filter="[[storeAreaFormat]is[variable]getvariable[]else[json]match[json]]">
|
||||
<!-- New-style JSON store area, with an old-style store area for compatibility with v5.1.x tooling -->
|
||||
`<script class="tiddlywiki-tiddler-store" type="application/json">[`
|
||||
<$let newline={{{ [charcode[10]] }}} join=`,$(newline)$`>
|
||||
<$vars newline={{{ [charcode[10]] }}}>
|
||||
<$text text=<<newline>>/>
|
||||
<$list filter=<<saveTiddlerFilter>> join=<<join>> template="$:/core/templates/html-json-tiddler"/>
|
||||
<$list filter="[subfilter<skinnySaveTiddlerFilter>]" template="$:/core/templates/html-json-skinny-tiddler"/>
|
||||
<$list filter=<<saveTiddlerFilter>> counter="counter" template="$:/core/templates/html-json-tiddler"/>
|
||||
<$vars numTiddlers={{{ [subfilter<saveTiddlerFilter>count[]] }}}>
|
||||
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} counter="counter" template="$:/core/templates/html-json-skinny-tiddler"/>
|
||||
</$vars>
|
||||
<$text text=<<newline>>/>
|
||||
</$let>
|
||||
</$vars>
|
||||
`]</script>`
|
||||
`<div id="storeArea" style="display:none;">`
|
||||
`</div>`
|
||||
@@ -20,8 +22,8 @@ title: $:/core/templates/store.area.template.html
|
||||
<!-- Old-style DIV/PRE-based store area -->
|
||||
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
|
||||
`<div id="storeArea" style="display:none;">`
|
||||
<$list filter={{{ [<saveTiddlerFilter>] }}} template="$:/core/templates/html-div-tiddler"/>
|
||||
<$list filter="[subfilter<skinnySaveTiddlerFilter>]" template="$:/core/templates/html-div-skinny-tiddler"/>
|
||||
<$list filter=<<saveTiddlerFilter>> template="$:/core/templates/html-div-tiddler"/>
|
||||
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} template="$:/core/templates/html-div-skinny-tiddler"/>
|
||||
`</div>`
|
||||
</$reveal>
|
||||
</$list>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
code-body: yes
|
||||
title: $:/core/ui/AlertTemplate
|
||||
|
||||
\whitespace trim
|
||||
|
||||
@@ -30,15 +30,15 @@ Block transclusions are shown in red, and inline transclusions are shown in gree
|
||||
<!-- Look for a parameter starting with $ to determine if we are in legacy mode -->
|
||||
<$list filter="[<@params>jsonindexes[]] :filter[<currentTiddler>prefix[$]] +[limit[1]]" variable="ignore" emptyMessage="""
|
||||
<!-- Legacy mode: we render the transclusion without a dollar sign for recursionMarker and mode -->
|
||||
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" recursionMarker="no" mode=<<mode>> $$fillignore="yes">
|
||||
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" recursionMarker="no" mode=<<mode>>>
|
||||
<!-- Reach back up to the grandparent transclusion to get the correct slot value -->
|
||||
<$slot $name="ts-raw"/>
|
||||
<$slot $name="ts-raw" $depth="2"/>
|
||||
</$genesis>
|
||||
""">
|
||||
<!-- Non-legacy mode: we use dollar signs for the recursionMarker and mode -->
|
||||
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" $$recursionMarker="no" $$mode=<<mode>> $$fillignore="yes">
|
||||
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" $$recursionMarker="no" $$mode=<<mode>>>
|
||||
<!-- Reach back up to the grandparent transclusion to get the correct slot fill value -->
|
||||
<$slot $name="ts-raw"/>
|
||||
<$slot $name="ts-raw" $depth="2"/>
|
||||
</$genesis>
|
||||
</$list>
|
||||
</$genesis>
|
||||
|
||||
@@ -26,10 +26,10 @@ caption: {{$:/language/ControlPanel/Basics/Caption}}
|
||||
|<$link to="$:/SiteSubtitle"><<lingo Subtitle/Prompt>></$link> |<$edit-text tiddler="$:/SiteSubtitle" default="" tag="input"/> |
|
||||
|<$link to="$:/status/UserName"><<lingo Username/Prompt>></$link> |<$edit-text tiddler="$:/status/UserName" default="" tag="input"/> |
|
||||
|<$link to="$:/config/AnimationDuration"><<lingo AnimDuration/Prompt>></$link> |<$edit-text tiddler="$:/config/AnimationDuration" default="" tag="input"/> |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit class="tc-edit-texteditor" tiddler="$:/DefaultTiddlers" autoHeight="yes"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit class="tc-edit-texteditor" tiddler="$:/DefaultTiddlers"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/language/DefaultNewTiddlerTitle"><<lingo NewTiddler/Title/Prompt>></$link> |<$edit-text tiddler="$:/language/DefaultNewTiddlerTitle" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Title"><<lingo NewJournal/Title/Prompt>></$link> |<$edit-text tiddler="$:/config/NewJournal/Title" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" class="tc-edit-texteditor" default="" autoHeight="yes"/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" class="tc-edit-texteditor" default=""/> |
|
||||
|<$link to="$:/config/NewTiddler/Tags"><<lingo NewTiddler/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewTiddler/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/NewJournal/Tags"><<lingo NewJournal/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewJournal/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/AutoFocus"><<lingo AutoFocus/Prompt>></$link> |{{$:/snippets/minifocusswitcher}} |
|
||||
|
||||
@@ -211,7 +211,7 @@ $:/state/add-plugin-info/$(connectionTiddler)$/$(assetInfo)$
|
||||
</div>
|
||||
\end
|
||||
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
\whitespace trim
|
||||
|
||||
<div>
|
||||
|
||||
@@ -2,6 +2,18 @@ title: $:/core/ui/ControlPanel/Settings
|
||||
tags: $:/tags/ControlPanel
|
||||
caption: {{$:/language/ControlPanel/Settings/Caption}}
|
||||
|
||||
<div class="tc-control-panel">
|
||||
<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/SettingsTab]!has[draft.of]]" default="$:/core/ui/ControlPanel/Settings/TiddlyWiki" explicitState="$:/state/tab--697582678"/>
|
||||
</div>
|
||||
\define lingo-base() $:/language/ControlPanel/Settings/
|
||||
|
||||
<<lingo Hint>>
|
||||
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Settings]]">
|
||||
|
||||
<div style="border-top:1px solid #eee;">
|
||||
|
||||
!! <$link><$transclude field="caption"/></$link>
|
||||
|
||||
<$transclude/>
|
||||
|
||||
</div>
|
||||
|
||||
</$list>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user