1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-23 19:34:39 +00:00

Compare commits

..

24 Commits

Author SHA1 Message Date
Jeremy Ruston
9cbf5a3c27 Add an incompleteness caveat 2025-09-09 17:31:38 +01:00
Jeremy Ruston
74c1b47fa7 Update modified/created dates for docs tiddlers 2025-09-09 17:31:23 +01:00
Jeremy Ruston
24891aea4b Prerelease local plugin library URI is wrong, but shouldn't fix that here 2025-09-09 13:39:08 +01:00
Jeremy Ruston
6bb88edced Fix merge from master instead of tiddlywiki-com 2025-09-09 13:35:39 +01:00
Jeremy Ruston
dd1351fdf4 Merge branch 'master' into community-cards 2025-09-09 13:26:34 +01:00
Jeremy Ruston
479014a312 Remove erroneously committed file 2025-06-19 14:41:37 +01:00
Jeremy Ruston
3134222651 Merge branch 'master' into community-cards 2025-06-19 14:33:11 +01:00
Jeremy Ruston
bdc2067b07 Add card for Motovun Jack
cc @saqimtiaz
2025-06-11 17:38:01 +01:00
Jeremy Ruston
e1a1c591ee Add card for @Arlen22 2025-06-11 17:18:38 +01:00
Jeremy Ruston
9bcfc5ad9c Person cards should show team leaderships/memberships 2025-05-30 12:15:05 +01:00
Jeremy Ruston
13c6eae17b Update succession team blurb 2025-05-30 12:09:12 +01:00
Jeremy Ruston
6cd2f52360 New community tab for people 2025-05-30 12:08:57 +01:00
Jeremy Ruston
542df127c5 Project team icon 2025-05-28 13:58:37 +01:00
Jeremy Ruston
a46f076da5 Add newsletter team 2025-05-28 11:27:08 +01:00
Jeremy Ruston
c5bddcc781 Add default icon for people 2025-05-28 09:58:13 +01:00
Jeremy Ruston
245e895cb0 Fix copy-paste error 2025-05-28 09:57:53 +01:00
Jeremy Ruston
f51531a0f9 Fix generic team icon 2025-05-27 16:37:06 +01:00
Jeremy Ruston
e490b39a97 Reorganise table of contents 2025-05-24 15:08:08 +01:00
Jeremy Ruston
007ca20e11 Add a pill for vacant positions 2025-05-23 13:00:14 +01:00
Jeremy Ruston
30afbb5578 More styling tweaks 2025-05-23 12:25:25 +01:00
Jeremy Ruston
fb8f01ee89 Introduce procedure for displaying teams
And some styling tweaks
2025-05-23 12:20:03 +01:00
Jeremy Ruston
6afa41a52d Force CI build 2025-05-23 11:54:27 +01:00
Jeremy Ruston
498f23efe9 Oops 2025-05-23 11:43:17 +01:00
Jeremy Ruston
687071d33e Initial Commit 2025-05-23 11:41:34 +01:00
1008 changed files with 34697 additions and 17101 deletions

4
.gitattributes vendored
View File

@@ -1,4 +0,0 @@
boot/** -linguist-generated
**/tiddlywiki.files linguist-language=JSON
**/tiddlywiki.info linguist-language=JSON
**/plugin.info linguist-language=JSON

View File

@@ -1,62 +0,0 @@
---
name: Bug report
about: Create a report to help us improve TiddlyWiki 5
title: "[Report] "
type: report
---
<!-- Remove elements, that you do not need -->
<!-- Add screenshots where needed -->
**Problem Description**
<!-- Describe your problem: A clear and concise description of what your problem is -->
**To Reproduce**
Steps to reproduce the behavior:
1. At https://tiddlywiki.com
2. Click on ...
3. Scroll down to ...
4. See ...
**Expected behavior**
As a user,
<!-- As a developer, -->
I would expect ...
**TiddlyWiki Configuration**
<!-- Please complete the following information -->
- Report created with: [Wiki Information](https://tiddlywiki.com/#%24%3A%2Fcore%2Fui%2FControlPanel%2FWikiInformation)
<!-- Your report comes here -->
<!-- or -->
<!-- Add it manually -->
- Version: <!-- e.g. v5.3.8 -->
- Saving mechanism: <!-- e.g. Node.js, TiddlyDesktop, TiddlyHost etc -->
- Plugins installed: <!-- e.g. Freelinks, TiddlyMap ... other 3rd party plugins -->
**Desktop**
<!-- Please complete the following information -->
- OS: <!-- e.g. iOS -->
- Browser: <!-- e.g. chrome, safari, FireFox -- Version: -->
**Smartphone**
<!-- Please complete the following information -->
- Device: <!-- e.g. iPhone6 -->
- OS: <!-- e.g. iOS8.1 -->
- Browser: <!-- e.g. stock browser, safari, FireFox -- Version: -->
**Additional context**
<!-- Add any other context about the problem here. -->

67
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Bug report
description: Create a report to help us improve TiddlyWiki 5
title: "[BUG] "
body:
- type: textarea
id: Describe
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: Expected
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: false
- type: textarea
id: Reproduce
attributes:
label: To Reproduce
description: "Steps to reproduce the behavior:"
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: false
- type: textarea
id: Screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
placeholder: Drag image here to upload screenshot!
validations:
required: false
- type: textarea
id: Configuration
attributes:
label: TiddlyWiki Configuration
description: please complete the following information
placeholder: |
- Version [e.g. v5.1.24]
- Saving mechanism [e.g. Node.js, TiddlyDesktop, TiddlyHost etc]
- Plugins installed [e.g. Freelinks, TiddlyMap]
### Desktop (please complete the following information):
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
### Smartphone (please complete the following information):
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
validations:
required: true
- type: textarea
id: Context
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -4,23 +4,17 @@ about: Suggest an idea for TiddlyWiki 5
title: "[IDEA]"
labels: ''
assignees: ''
type: idea
---
**Is your idea related to a problem? Please describe.**
A clear and concise description of what the problem is. Eg:
As a user, I would like [...]
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

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

View File

@@ -1,40 +0,0 @@
name: ESLint
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
concurrency:
group: lint-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
permissions:
contents: read
# Needed for GitHub Checks API
checks: write
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install --include=dev
- name: Run ESLint with reviewdog (GitHub Checks)
uses: reviewdog/action-eslint@v1
with:
eslint_flags: '.'
reporter: github-pr-check
fail_level: error
level: error
tool_name: ESLint PR code

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: build-size-check
id: get_sizes
uses: TiddlyWiki/cerebrus@v6
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Build and check size
uses: TiddlyWiki/cerebrus@v6
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ inputs.pr_number }}
repo: ${{ github.repository }}

View File

@@ -0,0 +1,18 @@
name: Validate PR Paths
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Validate PR
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,37 +0,0 @@
name: PR Validation
on:
pull_request_target:
types: [opened, reopened, synchronize]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
# Step 1: Validate PR paths
- name: Validate PR Paths
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: rules
continue-on-error: true
# Step 2: Validate change notes
- name: Validate Change Notes
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: changenotes
continue-on-error: false

View File

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

View File

@@ -8,8 +8,6 @@ On the server this file is executed directly to boot TiddlyWiki. In the browser,
\*/
/* eslint-disable @stylistic/indent */
var _boot = (function($tw) {
/*jslint node: true, browser: true */
@@ -46,8 +44,12 @@ $tw.utils.hop = function(object,property) {
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
};
/** @deprecated Use Array.isArray instead */
$tw.utils.isArray = value => Array.isArray(value);
/*
Determine if a value is an array
*/
$tw.utils.isArray = function(value) {
return Object.prototype.toString.call(value) == "[object Array]";
};
/*
Check if an array is equal by value and by reference.
@@ -126,22 +128,35 @@ $tw.utils.pushTop = function(array,value) {
return array;
};
/** @deprecated Use instanceof Date instead */
$tw.utils.isDate = value => value instanceof Date;
/*
Determine if a value is a date
*/
$tw.utils.isDate = function(value) {
return Object.prototype.toString.call(value) === "[object Date]";
};
/** @deprecated Use array iterative methods instead */
/*
Iterate through all the own properties of an object or array. Callback is invoked with (element,title,object)
*/
$tw.utils.each = function(object,callback) {
var next,f,length;
if(object) {
if(Array.isArray(object)) {
object.every((element,index,array) => {
const next = callback(element,index,array);
return next !== false;
});
if(Object.prototype.toString.call(object) == "[object Array]") {
for(f=0, length=object.length; f<length; f++) {
next = callback(object[f],f,object);
if(next === false) {
break;
}
}
} else {
Object.entries(object).every(entry => {
const next = callback(entry[1], entry[0], object);
return next !== false;
});
var keys = Object.keys(object);
for(f=0, length=keys.length; f<length; f++) {
var key = keys[f];
next = callback(object[key],key,object);
if(next === false) {
break;
}
}
}
}
};
@@ -316,13 +331,32 @@ $tw.utils.htmlDecode = function(s) {
return s.toString().replace(/&lt;/mg,"<").replace(/&nbsp;/mg,"\xA0").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
};
/** @deprecated Use window.location.hash instead. */
$tw.utils.getLocationHash = () => window.location.hash;
/*
Get the browser location.hash. We don't use location.hash because of the way that Firefox auto-urldecodes it (see http://stackoverflow.com/questions/1703552/encoding-of-window-location-hash)
*/
$tw.utils.getLocationHash = function() {
var href = window.location.href;
var idx = href.indexOf('#');
if(idx === -1) {
return "#";
} else if(href.substr(idx + 1,1) === "#" || href.substr(idx + 1,3) === "%23") {
// Special case: ignore location hash if it itself starts with a #
return "#";
} else {
return href.substring(idx);
}
};
/** @deprecated Pad a string to a given length with "0"s. Length defaults to 2 */
$tw.utils.pad = function(value,length = 2) {
const s = value.toString();
return s.padStart(length, "0");
/*
Pad a string to a given length with "0"s. Length defaults to 2
*/
$tw.utils.pad = function(value,length) {
length = length || 2;
var s = value.toString();
if(s.length < length) {
s = "000000000000000000000000000".substr(0,length - s.length) + s;
}
return s;
};
// Convert a date into UTC YYYYMMDDHHMMSSmmm format
@@ -596,7 +630,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
// Compile the code into a function
var fn;
if($tw.browser) {
fn = window["eval"](code + "\n\n//# sourceURL=" + filename); // eslint-disable-line no-eval -- See https://github.com/TiddlyWiki/TiddlyWiki5/issues/6839
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
} else {
if(sandbox){
fn = vm.runInContext(code,sandbox,filename)
@@ -607,7 +641,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
// Call the function and return the exports
return fn.apply(null,contextValues);
};
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
/*
Run code in a sandbox with only the specified context variables in scope
*/
@@ -765,13 +799,12 @@ the password, and to encrypt/decrypt a block of text
$tw.utils.Crypto = function() {
var sjcl = $tw.node ? (global.sjcl || require("./sjcl.js")) : window.sjcl,
currentPassword = null,
callSjcl = function(method,inputText,password,options) {
options = options || {};
callSjcl = function(method,inputText,password) {
password = password || currentPassword;
var outputText;
try {
if(password) {
outputText = sjcl[method](password,inputText,options);
outputText = sjcl[method](password,inputText);
}
} catch(ex) {
console.log("Crypto error:" + ex);
@@ -797,8 +830,7 @@ $tw.utils.Crypto = function() {
return !!currentPassword;
}
this.encrypt = function(text,password) {
// set default ks:256 -- see: http://bitwiseshiftleft.github.io/sjcl/doc/convenience.js.html
return callSjcl("encrypt",text,password,{v:1,iter:10000,ks:256,ts:64,mode:"ccm",adata:"",cipher:"aes"});
return callSjcl("encrypt",text,password);
};
this.decrypt = function(text,password) {
return callSjcl("decrypt",text,password);
@@ -1401,7 +1433,7 @@ $tw.Wiki = function(options) {
checkTiddler = function(tiddler,title) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] && (!pluginType || tiddler.fields["plugin-type"] === pluginType)) {
var disablingTiddler = self.getTiddler("$:/config/Plugins/Disabled/" + title);
if(title === "$:/core" || title === "$:/core-server" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
if(title === "$:/core" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
self.unregisterPluginTiddlers(null,[title]); // Unregister the plugin if it's already registered
pluginTiddlers.push(tiddler);
registeredTitles.push(tiddler.fields.title);
@@ -1498,8 +1530,7 @@ Define all modules stored in ordinary tiddlers
*/
$tw.Wiki.prototype.defineTiddlerModules = function() {
this.each(function(tiddler,title) {
// Modules in draft tiddlers are disabled
if(tiddler.hasField("module-type") && (!tiddler.hasField("draft.of"))) {
if(tiddler.hasField("module-type")) {
switch(tiddler.fields.type) {
case "application/javascript":
// We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script
@@ -1526,11 +1557,6 @@ $tw.Wiki.prototype.defineShadowModules = function() {
this.eachShadow(function(tiddler,title) {
// Don't define the module if it is overidden by an ordinary tiddler
if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) {
if(tiddler.hasField("draft.of")) {
// Report a fundamental problem
console.warn(`TiddlyWiki: Plugins should not contain tiddlers with a 'draft.of' field: ${tiddler.fields.title}`);
return;
}
// Define the module
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
}
@@ -1879,7 +1905,7 @@ $tw.loadTiddlersFromFile = function(filepath,fields) {
fileSize = fs.statSync(filepath).size,
data;
if(fileSize > $tw.config.maxEditFileSize) {
data = "File " + filepath + " not loaded because it is too large";
data = "File " + filepath + "not loaded because it is too large";
console.log("Warning: " + data);
ext = ".txt";
} else {
@@ -1950,41 +1976,22 @@ filepath: pathname of the directory containing the specification file
$tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var tiddlers = [];
// Read the specification
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"), function(e) {
console.log("Warning: tiddlywiki.files in " + filepath + " invalid: " + e.message);
return {};
});
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"));
// Helper to process a file
var processFile = function(filename,isTiddlerFile,fields,isEditableFile,rootPath) {
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
type = (extInfo || {}).type || fields.type || "text/plain",
typeInfo = $tw.config.contentTypeInfo[type] || {},
pathname = path.resolve(filepath,filename),
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8"),
metadata = $tw.loadMetadataForFile(pathname) || {},
fileTooLarge = false,
text, fileTiddlers;
if("_canonical_uri" in fields) {
text = "";
} else if(fs.statSync(pathname).size > $tw.config.maxEditFileSize) {
var msg = "File " + pathname + " not loaded because it is too large";
console.log("Warning: " + msg);
fileTooLarge = true;
text = isTiddlerFile ? msg : "";
} else {
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8");
}
fileTiddlers;
if(isTiddlerFile) {
fileTiddlers = $tw.wiki.deserializeTiddlers(fileTooLarge ? ".txt" : path.extname(pathname),text,metadata) || [];
fileTiddlers = $tw.wiki.deserializeTiddlers(path.extname(pathname),text,metadata) || [];
} else {
fileTiddlers = [$tw.utils.extend({text: text},metadata)];
}
var combinedFields = $tw.utils.extend({},fields,metadata);
if(fileTooLarge && isTiddlerFile) {
delete combinedFields.type; // type altered
}
$tw.utils.each(fileTiddlers,function(tiddler) {
$tw.utils.each(combinedFields,function(fieldInfo,name) {
if(typeof fieldInfo === "string" || $tw.utils.isArray(fieldInfo)) {
@@ -2059,7 +2066,6 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
} else if(tidInfo.suffix) {
tidInfo.fields.text = {suffix: tidInfo.suffix};
}
tidInfo.fields = tidInfo.fields || {};
processFile(tidInfo.file,tidInfo.isTiddlerFile,tidInfo.fields);
});
// Process any listed directories
@@ -2081,7 +2087,6 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var thisPath = path.relative(filepath, files[t]),
filename = path.basename(thisPath);
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
dirSpec.fields = dirSpec.fields || {};
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path);
}
}
@@ -2345,7 +2350,6 @@ $tw.loadTiddlersNode = function() {
});
// Load the core tiddlers
$tw.wiki.addTiddler($tw.loadPluginFolder($tw.boot.corePath));
$tw.wiki.addTiddler($tw.loadPluginFolder($tw.boot.coreServerPath));
// Load any extra plugins
$tw.utils.each($tw.boot.extraPlugins,function(name) {
if(name.charAt(0) === "+") { // Relative path to plugin
@@ -2419,7 +2423,6 @@ $tw.boot.initStartup = function(options) {
// System paths and filenames
$tw.boot.bootPath = options.bootPath || path.dirname(module.filename);
$tw.boot.corePath = path.resolve($tw.boot.bootPath,"../core");
$tw.boot.coreServerPath = path.resolve($tw.boot.bootPath,"../core-server");
// If there's no arguments then default to `--help`
if($tw.boot.argv.length === 0) {
$tw.boot.argv = ["--help"];
@@ -2544,10 +2547,10 @@ $tw.boot.execStartup = function(options){
if($tw.safeMode) {
$tw.wiki.processSafeMode();
}
// Register typed modules from the tiddlers we've just loaded and any modules within plugins
// Tiddlers should appear last so that they may overwrite shadows during module registration
$tw.wiki.defineShadowModules();
// Register typed modules from the tiddlers we've just loaded
$tw.wiki.defineTiddlerModules();
// And any modules within plugins
$tw.wiki.defineShadowModules();
// Make sure the crypto state tiddler is up to date
if($tw.crypto) {
$tw.crypto.updateCryptoStateTiddler();
@@ -2616,13 +2619,11 @@ $tw.boot.executeNextStartupTask = function(callback) {
$tw.boot.log(s.join(" "));
// Execute task
if(!$tw.utils.hop(task,"synchronous") || task.synchronous) {
const thenable = task.startup();
if(thenable && typeof thenable.then === "function"){
thenable.then(asyncTaskCallback);
return true;
} else {
return asyncTaskCallback();
task.startup();
if(task.name) {
$tw.boot.executedStartupModules[task.name] = true;
}
return $tw.boot.executeNextStartupTask(callback);
} else {
task.startup(asyncTaskCallback);
return true;
@@ -2767,8 +2768,6 @@ return $tw;
});
/* eslint-enable @stylistic/indent */
if(typeof(exports) !== "undefined") {
exports.TiddlyWiki = _boot;
} else {

View File

@@ -12,8 +12,6 @@ See Boot.js for further details of the boot process.
\*/
/* eslint-disable @stylistic/indent */
var _bootprefix = (function($tw) {
"use strict";
@@ -116,8 +114,6 @@ return $tw;
});
/* eslint-enable @stylistic/indent */
if(typeof(exports) === "undefined") {
// Set up $tw global for the browser
window.$tw = _bootprefix(window.$tw);

View File

@@ -1,14 +0,0 @@
title: @Christian_Byron
tags: Community/Person
fullname: Christian Byron
talk.tiddlywiki.org: Christian_Byron
github: ceebeetree
linkedin: www.linkedin.com/in/christian-byron-b84a594/
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEBAAMBAAAAAAAAAAAAAAAHBgEDBQQBAAMBAAAAAAAAAAAAAAAAAAABAwX/2gAMAwEAAhADEAAAADv2xtJlY03sqePW3ARS1RSydIhcH//EACcQAAICAgIBAgYDAAAAAAAAAAECAwQFEQASMRMhBhBBk8HRIzJx/9oACAEBAAE/AMFQxs+NExqJLMCwYE+SOT4bF3qr+hAIpRsDQ6lWH0Yco4S/eVniRVQHXZzrZ5dwGQpQtNII2RfJVvHMRl5cbKxC94n/ALp+RxfiKpNcgMMUqPIwjcnWip/I5XtUowaL3Ujir/xt79Glb6/4OZ7MV5oEpUzuIa7MPB14A5jpoYLsEsydo1bbLre+CWEEEYab7Uf74ZYSSThpvtR/vmRmhnuzywp1jZtquta+VPM49qlcy24lf017At7g8uZnHrUsGK3Ez+m3UBvcnXy//8QAHhEAAgEFAAMAAAAAAAAAAAAAAQIDAAQRIkEyUaH/2gAIAQIBAT8AmiuVlZkLEeQOflJPcvMAF0z65V+h0YIW52rBDuxUrztf/8QAIxEBAAEDAwMFAAAAAAAAAAAAAgEAAxEEBSMSQcEiMVJxof/aAAgBAwEBPwC/Z1ZvNBOYz1Gc/lDUat3ySPRM/H2P3W4hcbIldpxnxW3BcjQk9oznzX//2Q==
Hello ~TiddlyWikiers - I have been a long time fan, recent contributor to the TW community.
Recently I have volunteered to run the [[TiddlyWiki Newsletter|https://tiddlywiki.substack.com/]] to spread the great news about TW.
I have been in the IT industry for about thirty years, mostly as a consultant and technical arcitect.
More recently I went back to study a masters in IT focussing on AI and data science.
Now my partner and I have started our own business ([[Sphere Innovations|https://sphere-innovations.com.au]]) - in consulting and building web applications for small to medium size businesses here in Australia.

View File

@@ -6,7 +6,6 @@ talk.tiddlywiki.org: jeremyruston
github: Jermolene
linkedin: www.linkedin.com/in/jermy
flickr: www.flickr.com/photos/jermy/
bluesky: https://bsky.app/profile/jermolene.bsky.social
homepage: jermolene.com
email: jeremy@jermolene.com
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAtAAEBAAMAAAAAAAAAAAAAAAAHBgIEBQEBAQEBAAAAAAAAAAAAAAAAAgQBBf/aAAwDAQACEAMQAAAANF4uTuPRhD2nBLnUiJvKM0DtMKy//8QAKxAAAgIBAwMDAQkAAAAAAAAAAQIDBBEABRITITEiMkFxFEJRUmFicoGR/9oACAEBAAE/AInTA6gUGP4ZOQbW1bPsmyUq1q+gmvFPUzZPDkPamtwqU75ks04JakroVcg5RwRjg66NUx25KbzqJYyMngfqSuq0M3NZYIebJIvZozIvI/iNPcp/aalSdJXsS4VcKeIzlvU3jVTcYLNiaGISrjkhWQYDfQ63pYAzCDBsOiu7Dsx4EHH6r2w2ttimjd2IsNErhhJHKI04/uzqxuCxpBYVVWKSHqwMyMSQ33SB7dUJFmlkMYRgnqZgCMf7rf8AeEt3A9YOhjXAb2k8u7dtT1RZeOtXmYxiOPj4ZWY/lb51skqUNnNW/wBNzC7IpB6gQeeB/jq/fqGOaLbowuYn5MAQOw8LjW5Vmeo0qIsqYLLKjHIZmwv9fB1//8QAHxEAAQMEAwEAAAAAAAAAAAAAEQABAgMSIWExMkFR/9oACAECAQE/AD9iTy2lJmHUB8BVKM4SNSOj46a29saX/8QAHREAAgICAwEAAAAAAAAAAAAAAQIAAwQRITGBkf/aAAgBAwEBPwDHpFpJZtamVSiBWT2Yt7hmCDsb+TKtsKqpGg3M/9k=

View File

@@ -1,19 +0,0 @@
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEAAwEBAAAAAAAAAAAAAAAGAwQHAgUBAAMBAAAAAAAAAAAAAAAAAAACAwT/2gAMAwEAAhADEAAAAOfCWAMdKKetM4wOvY5OcvZnrYf/xAApEAACAQQBBAECBwAAAAAAAAABAgMABAURQQYSIVETFCIxMkJicYKh/9oACAEBAAE/AEtysaStr7mPaPeuazWdMM4gEnfPryW8hBUuZvou2RXRxyreBWPmgyNqs8f8MOQalhdY7Vz+R4/s/qfP+1edNi/zl7HDcFbmS3E8CcMR4INP0PkBhklIm+sZNtFtQiV0nj57Owl+dSrSTFgD6/CtH4VV9lU3oAbPngAVY389lc5URuUZkMxhnR4pvW0VwDqsP1FNmLWYqCpikMbngmliJNY+aKzyTxXS6lRAyg/u5rq+5x2RsuyTa3MQMlvKniRGThTUd1JYXUdzAwDvqVxGdRXMbfrVOD7HBrG3mNEsU8z98TRhl9eRzX//xAAcEQACAgIDAAAAAAAAAAAAAAABAgARAzESIVH/2gAIAQIBAT8ARuXZPsul3Eoje5lBQWBP/8QAGREAAwEBAQAAAAAAAAAAAAAAAAECEiER/9oACAEDAQE/AM98Lk7LJe20z//Z
created: 20251110102157310
first-sighting: 2019-03-01
fullname: Lin Onetwo
github: linonetwo
homepage: https://wiki.onetwo.website/
modified: 20251111184556193
tags: Community/Person Community/Team/Contributors
talk.tiddlywiki.org: linonetwo
title: @linonetwo
type: text/vnd.tiddlywiki
Since 2014, when I started college, I've been on a quest for a lifelong PKM tool. I cherish my life and all my experiences, and I dont want to forget any of them. When Im deeply focused on a task, its easy to lose sight of other important parts of my life—so I needed a system to help me stay balanced.
Early on, I tried TiddlyWiki several times, but I was initially put off by its save mechanism and markup editing. That changed when I discovered an auto-backup script, which gave me the confidence to fully commit. Over time, I improved the script and eventually transitioned to using TidGi-Desktop and TidGi-Mobile.
Today, my TiddlyWiki holds all my game design ideas and progress logs—it has truly become my second brain. With the help of LLM-powered programming tools, Ive enhanced it with numerous plugins, allowing me to manage my mind in a more programmable and structured way. As a game developer, TiddlyWiki isn't the core of my professional work; But I've invested so much time because it's fundamentally about upgrading my mind.
Most of my notes are open by default and shared publicly on my homepage as a digital garden.

View File

@@ -1,25 +0,0 @@
avatar: UklGRiwIAABXRUJQVlA4WAoAAAAwAAAAPwAAPwAASUNDUCACAAAAAAIgbGNtcwRAAABtbnRyR1JBWVhZWiAH6QALAAoACwADAAZhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZXNjAAAAzAAAAG5jcHJ0AAABPAAAADZ3dHB0AAABdAAAABRrVFJDAAABiAAAACBkbW5kAAABqAAAACRkbWRkAAABzAAAAFJtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFIAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA81EAAQAAAAEWzHBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbbWx1YwAAAAAAAAABAAAADGVuVVMAAAAIAAAAHABHAEkATQBQbWx1YwAAAAAAAAABAAAADGVuVVMAAAA2AAAAHABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAVlA4TOYFAAAvP8APEDWGgbRtWv+yt/0WImICOBvWn1C4dFi1bStbvpY8Qg2ePANNNAMh3N2db/7A91/7CHBvBBRr25ZFH+4k98ihkqi2CP4tsANvX8a+8y8Ct04dn0nuUt39ZiBJkowqt911M+MJ1G3bNiZJr1iP0DZ+2bbdadsqprOjAqmoUIX9hf3Fl5/uPYV7I3OMeoFzIvrvwG0kRUr3zPLdYMMXaqrMMsp0K4fufKO6c2hFV5Zh7kRROZX0PSCmB/3KWQwpuiekWelSRZDW94d0q750NrxavpFn1eLNQ9EV8nWlmAET6Q8lrCRTcjFLlLImluK3iXJW/hT47KGklS8OlzWUtXLFYDRCSS74ojUjxggqKMoxd6A1lTCyvsvyzC5/d7BsCHb7yIcHyrX2yR/NPnsAdRT2i0Pwp/o0Il6ix8hsRAuJmQgcr4KREfAiMgUVm9KqmfSxL5pOJspVwwTiV6jiIAg1RMhHpERhbvwgGI34Hc49T7UeKZtXwEqJ+BAaoBneperJH0POs1u4dufwv8Gf+qcOfjyvX6ZIVgxE0Rw87YF3BSc9c7jsXfdjOBG7FwmSb39pfGRwu8IuvUjJNoTpFzkEvDg6W3Qt/9nf99ZXPy8HM43IweTKyNR+WVatXcWWyakBksj9cqW+QetplcjsKElvZH/zuOO/PrCx//tL3/6x/O/C1PZZvSKuulLcS4l8M1ewGPR6ef5sllXW2eGQZ7hVSEZiPmcqrSS8e2ElX8o7t1fvB9LFetmEx5hx1Xuye2PpfjZnSjj7QfKTB3bZZo05Zvh6YuivX24cpc8+ddvADWG9odrSwFalVurxUiidDHmTiaoNkkh2gjbcpxMiAbd39aVP119/N9k4+euNKfcNjwaPhZEuUupUsJrHchw1LkPrRC9bQKa3M8Mj/xx903drdnHMpbirj1ENsUre0oo3N+7gat+2ZctKdsIUYc21sRu+Ucdhn+P7DyarftW00iu3Tmbv+hTfdCTmyaIPT4PrYZDFtBN2W8S9m4oTB5Z2P3Oe7weKjVBq86kXX/r0+WuvTAzfjqm1hsYRPWlbxm4n3IaeGOJEizv8orH9w5ejjmSrfOuEq/HxT6eDemtsZ/HTvvG1/8iVspxZILrlkz/cdsIbIroOgJileFSty2xiHNW5t9fbHJ3ze87bp5T9vc8RuqMB0ReDSt464R/BJxspvgpEsrVAJMTsYg2QovPTOHrvQ9et/S2Xx+40z7dY4JBX0Pz/ElH/T73U2DkK8EiqC9hM/zV3frQfzjaAqO16s1l6xCUXnBFlYxyIer3eEdth7u5xsHKxWoGLqzY3wIULt9G3K6soei9jZ+UcF+Ka3M/II9EUWrJ/LLxy+Q9xIh0vOl3NZCrVnBsuFUTOSnJnSioRWZ9q4g+ZDk5XVORoW2qX2hbIkna3JOrdR3jmpHVLovUkLES6grRO010u0GkDlX7SpH1DQ64Wl2zaSUJv1Mtti2G7kx5IyftWMhfDlGClcxvIUhP5crhp9LIb1Vne187oSAWxelcR/kXjYQTZboW+Oj1pqF0gmfZhSDD6bSgzGWrw3s7QLNtCV+2uatYrd/aFtjDI8R52e/DdyKgRKXBhEak3Ev50+GCUA9EFUor39htVMxmWvW8AM6ptG416rZvdWn+MarIEyH5r6ruZSrx8XrWDP370vbfTjqpmZGIbiFPFoihc4jcrlYi9p3ndSuymZ+XLaKza/P/HUWHn5Axdkd9OjBskY0+pIlz4AlFPFs+aStK5PBIRR4MVVJDihsy4JdEA4pVcrVqMZDyL2/8aYocikEAR9Xjc1BNG9zEiJG7n/cGyrtnblkClBhEgMW4Kx21BEBGJjLa0hcOGmTK64KsKLfKr9QyQELclxY3hqowTIZKdZNTSS5BWiBPlKxDWBVSS41bOepkhTkhGDajLfLyUBOKlkMHPgOhx3JoRN/cEiRgSWdgF2yCyDQu4IcbNo8ftTzxveOJ5y+h509h52+h549h569h587/M20f/b1AB
created: 20251110102157310
first-sighting: 2009-11-14
fullname: Mario Pietsch
github: pmario
homepage: https://wikilabs.github.io/
modified: 20251110124935183
tags: Community/Person Community/Team/Contributors
talk.tiddlywiki.org: pmario
title: @pmario
type: text/vnd.tiddlywiki
youtube: https://www.youtube.com/@pmario
''Hi, My name is Mario Pietsch''. Back in 2009 I was ''searching'' for ''a simple presentation tool'' and discovered ~TiddlyWiki Classic, Monkey Pirate ~TiddlyWiki ([[MPTW|https://mptw.tiddlyspot.com/]]) with ~TagglyTagging, Eric Shulman's ~TiddlyTools, Saq Imtiaz's navigation macros, and more. --- ''I was captivated''.
After a deep dive, I combined these elements into my own "Presentation Manager", along [[3 step by step tutorials|https://groups.google.com/g/tiddlywiki/c/qG_tZ1x0MEU/m/-vLA0luMicYJ]] to help others build it.
Thanks to ''the positive spirit'' of the ~TiddlyWiki community, I am proud to be part of it since 2009.
When Jeremy started developing ~TiddlyWiki 5 on ~GitHub, I joined in—opening [[issue no. 1|https://github.com/TiddlyWiki/TiddlyWiki5/issues/1]] all the way up to 13. For what thats good ;) Since then, I have submitted nearly 600 pull requests and more than 500 issues, many of which have been merged or resolved.
My ~TiddlyWiki 5 "laboratory" is at https://wikilabs.github.io, and I also share content on my ''~YouTube'' channel: https://www.youtube.com/@pmario
Have fun!<br>
Mario

View File

@@ -1,19 +0,0 @@
title: Developer Experience Team
tags: Community/Team
modified: 20251109200632671
created: 20251109200632671
leader: @pmario
team: @saqimtiaz
The Developer Experience Team improves the experience of software contributors to the TiddlyWiki project. This includes enhancing documentation, streamlining contribution processes, and providing tools and resources to help developers effectively contribute to TiddlyWiki.
Tools and resources managed by the Developer Experience Team include:
* Advising and assisting contributors, particularly new developers
* Maintenance of developer-focused documentation on the https://tiddlywiki.com/dev/ site, including:
** Development environment setup guides
** Code review processes and best practices
** Contribution guidelines and documentation
* Continuous integration and deployment scripts providing feedback on pull requests
* Devising and implementing labelling systems for issues and pull requests
* Automation scripts to simplify common development tasks

View File

@@ -1,8 +1,8 @@
created: 20250909171928024
modified: 20251110133437795
tags: Community/Team
team: @MotovunJack
title: Infrastructure Team
tags: Community/Team
modified: 20250909171928024
created: 20250909171928024
team: @MotovunJack
The Infrastructure Team is responsible for maintaining and improving the infrastructure that supports the TiddlyWiki project. This includes the hosting, deployment, and management of the TiddlyWiki websites and services, as well as the tools and systems used by the TiddlyWiki community.
@@ -12,4 +12,3 @@ The infrastructure includes:
* github.com/TiddlyWiki
* tiddlywiki.com DNS
* Netlify account for PR previews
* edit.tiddlywiki.com

View File

@@ -0,0 +1,6 @@
title: Newsletter Team
tags: Community/Team
modified: 20250909171928024
created: 20250909171928024
The Newsletter Team is responsible for producing the TiddlyWiki Newsletter, a monthly email newsletter that highlights news, updates, and community contributions related to TiddlyWiki.

View File

@@ -1,11 +0,0 @@
title: Quality Assurance Team
created: 20251112125742296
modified: 20251112125742296
tags: Community/Team
team:
leader: @Leilei332
title: Quality Assurance Team
The Quality Assurance Team is responsible for ensuring the quality and reliability of TiddlyWiki releases. This includes reviewing code submissions, testing new features, identifying bugs, and verifying that fixes are effective.

View File

@@ -1,15 +0,0 @@
title: TiddlyWiki Newsletter Team
tags: Community/Team
modified: 20251219090709874
created: 20250909171928024
leader: @Christian_Byron
The Newsletter Team is responsible for producing the [[TiddlyWiki Newsletter]]. We would love to have your help if you would like to get involved.
! Audience
The newsletter is intended for TiddlyWiki end users who do not track all the discussions on https://talk.tiddlywiki.org/.
Coverage of developer topics such as JavaScript and intricate wikitext should be handled thoughtfully to avoid alienating the core audience of end users.
Subscribing to the newsletter is intended to give people confidence that they will not miss any important developments.

View File

@@ -1,5 +1,5 @@
title: Community/Team
modified: 20250909171928024
created: 20250909171928024
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[Quality Assurance Team]] [[Infrastructure Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Succession Team]]
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Infrastructure Team]] [[Succession Team]]

View File

@@ -1,11 +0,0 @@
{
"title": "$:/core-server",
"name": "Core Server Components",
"description": "TiddlyWiki5 core server components",
"author": "JeremyRuston",
"core-version": ">=5.0.0",
"platform": "server",
"plugin-priority": "0",
"list": "readme",
"stability": "STABILITY_2_STABLE"
}

View File

@@ -1,7 +0,0 @@
title: $:/core-server/readme
This plugin contains TiddlyWiki's core components that are only needed on the server, comprising:
* Commands
* HTTP server code
* Utility functions for server

View File

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

View File

@@ -1,30 +0,0 @@
/*\
title: $:/core-modules/modules/utils/base64.js
type: application/javascript
module-type: utils-node
Base64 UTF-8 utlity functions.
\*/
"use strict";
const{ TextEncoder, TextDecoder } = require("node:util");
exports.btoa = binstr => Buffer.from(binstr, "binary").toString("base64");
exports.atob = b64 => Buffer.from(b64, "base64").toString("binary");
function base64ToBytes(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, m => m.codePointAt(0));
};
function bytesToBase64(bytes) {
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = str => bytesToBase64(new TextEncoder().encode(str));
exports.base64DecodeUtf8 = str => new TextDecoder().decode(base64ToBytes(str));

View File

@@ -1,95 +0,0 @@
/*\
title: $:/core-server/modules/utils/escapecss.js
type: application/javascript
module-type: utils-node
Provides CSS.escape() functionality.
\*/
"use strict";
exports.escapeCSS = (function() {
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
/* eslint-disable */
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
return function(value) {
if (arguments.length == 0) {
throw new TypeError('`CSS.escape` requires an argument.');
}
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
result += '\uFFFD';
continue;
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index == 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit == 0x002D
)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
index == 0 &&
length == 1 &&
codeUnit == 0x002D
) {
result += '\\' + string.charAt(index);
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002D ||
codeUnit == 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// https://drafts.csswg.org/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
/* eslint-enable */
})();

View File

@@ -5,4 +5,3 @@ 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/]]
* [[modern-normalize by Sindre Sorhus|https://github.com/sindresorhus/modern-normalize]]
* [[diff-match-patch-es by antfu|https://github.com/antfu/diff-match-patch-es]]

View File

@@ -57,7 +57,6 @@ LayoutSwitcher/Caption: Layout
LoadedModules/Caption: Loaded Modules
LoadedModules/Hint: These are the currently loaded tiddler modules linked to their source tiddlers. Any italicised modules lack a source tiddler, typically because they were setup during the boot process.
Palette/Caption: Palette
Palette/CustomSettings/Prompt: Custom settings for current palette: <<palette-link>>
Palette/Editor/Clone/Caption: clone
Palette/Editor/Clone/Prompt: It is recommended that you clone this shadow palette before editing it
Palette/Editor/Delete/Hint: delete this entry from the current palette
@@ -148,7 +147,7 @@ Settings/AutoSave/Disabled/Description: Do not save changes automatically
Settings/AutoSave/Enabled/Description: Save changes automatically
Settings/AutoSave/Hint: Attempt to automatically save changes during editing when using a supporting saver
Settings/CamelCase/Caption: Camel Case Wiki Links
Settings/CamelCase/Hint: Requires reload to take effect
Settings/CamelCase/Hint: You can globally disable automatic linking of ~CamelCase phrases. Requires reload to take effect
Settings/CamelCase/Description: Enable automatic ~CamelCase linking
Settings/Caption: Settings
Settings/EditorToolbar/Caption: Editor Toolbar

View File

@@ -1,6 +1,5 @@
title: $:/language/
Alerts: Alerts
AboveStory/ClassicPlugin/Warning: It looks like you are trying to load a plugin designed for ~TiddlyWiki Classic. Please note that [[these plugins do not work with TiddlyWiki version 5.x.x|https://tiddlywiki.com/#TiddlyWikiClassic]]. ~TiddlyWiki Classic plugins detected:
BinaryWarning/Prompt: This tiddler contains binary data
ClassicWarning/Hint: This tiddler is written in TiddlyWiki Classic wiki text format, which is not fully compatible with TiddlyWiki version 5. See https://tiddlywiki.com/static/Upgrading.html for more details.
@@ -30,7 +29,6 @@ Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'des
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/FilterPragma: Filter Error: Unknown filter pragma
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
Error/IsFilterOperator: Filter Error: Unknown parameter for the 'is' filter operator
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator

View File

@@ -1,116 +0,0 @@
/*\
title: $:/core/modules/background-actions.js
type: application/javascript
module-type: global
Class to dispatch actions when filters change
\*/
"use strict";
class BackgroundActionDispatcher {
constructor(filterTracker, wiki) {
this.filterTracker = filterTracker;
this.wiki = wiki;
this.nextTrackedFilterId = 1;
this.trackedFilters = new Map(); // Use Map for better key management
// Track the filter for the background actions
this.filterTracker.track({
filterString: "[all[tiddlers+shadows]tag[$:/tags/BackgroundAction]!is[draft]]",
fnEnter: title => this.trackFilter(title),
fnLeave: (title, enterValue) => this.untrackFilter(enterValue),
fnChange: (title, enterValue) => {
this.untrackFilter(enterValue);
return this.trackFilter(title);
},
fnProcess: changes => this.process(changes)
});
}
trackFilter(title) {
const tiddler = this.wiki.getTiddler(title);
const id = this.nextTrackedFilterId++;
const tracker = new BackgroundActionTracker({
wiki: this.wiki,
title,
trackFilter: tiddler.fields["track-filter"],
actions: tiddler.fields.text
});
this.trackedFilters.set(id, tracker);
return id;
}
untrackFilter(enterValue) {
const tracker = this.trackedFilters.get(enterValue);
if(tracker) {
tracker.destroy();
}
this.trackedFilters.delete(enterValue);
}
process(changes) {
for(const tracker of this.trackedFilters.values()) {
tracker.process(changes);
}
}
}
/*
Represents an individual tracked filter. Options include:
wiki: wiki to use
title: title of the tiddler being tracked
trackFilter: filter string to track changes
actions: actions to be executed when the filter changes
*/
class BackgroundActionTracker {
constructor({wiki, title, trackFilter, actions}) {
this.wiki = wiki;
this.title = title;
this.trackFilter = trackFilter;
this.actions = actions;
this.filterTracker = new $tw.FilterTracker(this.wiki);
this.hasChanged = false;
this.trackerID = this.filterTracker.track({
filterString: this.trackFilter,
fnEnter: () => { this.hasChanged = true; },
fnLeave: () => { this.hasChanged = true; },
fnProcess: changes => {
if(this.hasChanged) {
this.hasChanged = false;
console.log("Processing background action", this.title);
const tiddler = this.wiki.getTiddler(this.title);
let doActions = true;
if(tiddler && tiddler.fields.platforms) {
doActions = false;
const platforms = $tw.utils.parseStringArray(tiddler.fields.platforms);
if(($tw.browser && platforms.includes("browser")) || ($tw.node && platforms.includes("node"))) {
doActions = true;
}
}
if(doActions) {
this.wiki.invokeActionString(
this.actions,
null,
{
currentTiddler: this.title
},{
parentWidget: $tw.rootWidget
}
);
}
}
}
});
}
process(changes) {
this.filterTracker.handleChangeEvent(changes);
}
destroy() {
this.filterTracker.untrack(this.trackerID);
}
}
exports.BackgroundActionDispatcher = BackgroundActionDispatcher;

View File

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

View File

@@ -76,7 +76,6 @@ WikiFolderMaker.prototype.tiddlersToIgnore = [
"$:/boot/boot.js",
"$:/boot/bootprefix.js",
"$:/core",
"$:/core-server",
"$:/library/sjcl.js",
"$:/temp/info-plugin"
];

View File

@@ -31,10 +31,10 @@ function FramedEngine(options) {
this.parentNode.insertBefore(this.iframeNode,this.nextSibling);
this.iframeDoc = this.iframeNode.contentWindow.document;
// (Firefox requires us to put some empty content in the iframe)
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette/palette-colours");
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette");
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
this.iframeDoc.open();
this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
this.iframeDoc.write("<meta name='color-scheme' content='" + colorScheme + "'>");
this.iframeDoc.close();
// Style the iframe
this.iframeNode.className = this.dummyTextArea.className;
@@ -156,8 +156,8 @@ Fix the height of textarea to fit content
FramedEngine.prototype.fixHeight = function() {
// Make sure styles are updated
this.copyStyles();
// If .editRows is initialised, it takes precedence
if(this.widget.editTag === "textarea" && !this.widget.editRows) {
// Adjust height
if(this.widget.editTag === "textarea") {
if(this.widget.editAutoHeight) {
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);

View File

@@ -28,12 +28,7 @@ function SimpleEngine(options) {
if(this.widget.editTag === "textarea") {
this.domNode.appendChild(this.widget.document.createTextNode(this.value));
} else {
if(this.widget.editType === "color") {
// The <input type="color"> element requires a six digit hex value
this.domNode.value = $tw.utils.convertCSSColorToRGBString(this.value);
} else {
this.domNode.value = this.value;
}
this.domNode.value = this.value;
}
// Set the attributes
if(this.widget.editType && this.widget.editTag !== "textarea") {
@@ -88,9 +83,6 @@ Update the DomNode with the new text
*/
SimpleEngine.prototype.updateDomNodeText = function(text) {
try {
if(this.widget.editType === "color") {
text = $tw.utils.convertCSSColorToRGBString(text);
}
this.domNode.value = text;
} catch(e) {
// Ignore
@@ -108,8 +100,7 @@ SimpleEngine.prototype.getText = function() {
Fix the height of textarea to fit content
*/
SimpleEngine.prototype.fixHeight = function() {
// If .editRows is initialised, it takes precedence
if((this.widget.editTag === "textarea") && !this.widget.editRows) {
if(this.widget.editTag === "textarea") {
if(this.widget.editAutoHeight) {
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);

View File

@@ -68,7 +68,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
// Fix height
this.engine.fixHeight();
// Focus if required
if($tw.browser && (this.editFocus === "true" || this.editFocus === "yes") && !$tw.utils.hasClass(this.parentDomNode.ownerDocument.activeElement,"tc-keep-focus")) {
if(this.editFocus === "true" || this.editFocus === "yes") {
this.engine.focus();
}
// Add widget message listeners
@@ -217,10 +217,12 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedTiddlers["$:/palette"] || changedAttributes.disabled || changedAttributes.fileDrop) {
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedTiddlers["$:/palette"] || changedAttributes.disabled || changedAttributes.fileDrop) {
this.refreshSelf();
return true;
} else if(changedAttributes["default"] || changedTiddlers[this.editRefreshTitle] || changedTiddlers[this.editTitle]) {
} else if (changedTiddlers[this.editRefreshTitle]) {
this.engine.updateDomNodeText(this.getEditInfo().value);
} else if(changedTiddlers[this.editTitle]) {
var editInfo = this.getEditInfo();
this.updateEditor(editInfo.value,editInfo.type);
}

View File

@@ -1,106 +0,0 @@
/*\
title: $:/core/modules/filter-tracker.js
type: application/javascript
module-type: global
Class to track the results of a filter string
\*/
"use strict";
class FilterTracker {
constructor(wiki) {
this.wiki = wiki;
this.trackers = new Map();
this.nextTrackerId = 1;
}
handleChangeEvent(changes) {
this.processTrackers();
this.processChanges(changes);
}
/*
Add a tracker to the filter tracker. Returns null if any of the parameters are invalid, or a tracker id if the tracker was added successfully. Options include:
filterString: the filter string to track
fnEnter: function to call when a title enters the filter results. Called even if the tiddler does not actually exist. Called as (title), and should return a truthy value that is stored in the tracker as the "enterValue"
fnLeave: function to call when a title leaves the filter results. Called as (title,enterValue)
fnChange: function to call when a tiddler changes in the filter results. Only called for filter results that identify a tiddler or shadow tiddler. Called as (title,enterValue), and may optionally return a replacement enterValue
fnProcess: function to call each time the tracker is processed, after any enter, leave or change functions are called. Called as (changes)
*/
track(options = {}) {
const {
filterString,
fnEnter,
fnLeave,
fnChange,
fnProcess
} = options;
const id = this.nextTrackerId++;
const tracker = {
id,
filterString,
fnEnter,
fnLeave,
fnChange,
fnProcess,
previousResults: [],
resultValues: {}
};
this.trackers.set(id, tracker);
// Process the tracker
this.processTracker(id);
return id;
}
untrack(id) {
this.trackers.delete(id);
}
processTrackers() {
for(const id of this.trackers.keys()) {
this.processTracker(id);
}
}
processTracker(id) {
const tracker = this.trackers.get(id);
if(!tracker) return;
const results = [];
// Evaluate the filter and remove duplicate results
$tw.utils.each(this.wiki.filterTiddlers(tracker.filterString), title => {
$tw.utils.pushTop(results, title);
});
// Process the newly entered results
results.forEach(title => {
if(!tracker.previousResults.includes(title) && !tracker.resultValues[title] && tracker.fnEnter) {
tracker.resultValues[title] = tracker.fnEnter(title) || true;
}
});
// Process the results that have just left
tracker.previousResults.forEach(title => {
if(!results.includes(title) && tracker.resultValues[title] && tracker.fnLeave) {
tracker.fnLeave(title, tracker.resultValues[title]);
delete tracker.resultValues[title];
}
});
// Update the previous results
tracker.previousResults = results;
}
processChanges(changes) {
for(const tracker of this.trackers.values()) {
Object.keys(changes).forEach(title => {
if(title && tracker.previousResults.includes(title) && tracker.fnChange) {
tracker.resultValues[title] = tracker.fnChange(title, tracker.resultValues[title]) || tracker.resultValues[title];
}
});
if(tracker.fnProcess) {
tracker.fnProcess(changes);
}
}
}
}
exports.FilterTracker = FilterTracker;

View File

@@ -1,24 +0,0 @@
/*\
title: $:/core/modules/filterrunprefixes/apply.js
type: application/javascript
module-type: filterrunprefix
Filter run prefix to make input titles available as variables when evaluating the filter run
\*/
"use strict";
exports.apply = function(operationSubFunction) {
return function(results,source,widget) {
source = widget.wiki.makeTiddlerIterator([]);
var variables = {},
counter = 1;
results.each(function(title) {
variables["$" + counter] = title;
counter++;
});
results.clear();
results.pushTop(operationSubFunction(source,widget.makeFakeWidgetWithVariables(variables)));
};
};

View File

@@ -1,41 +0,0 @@
/*\
title: $:/core/modules/filterrunprefixes/let.js
type: application/javascript
module-type: filterrunprefix
Assign a value to a variable
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter prefix function
*/
exports.let = function(operationSubFunction,options) {
// Return the filter run prefix function
return function(results,source,widget) {
// Save the result list
var resultList = results.toArray();
// Clear the results
results.clear();
// Evaluate the subfunction to get the variable name
var subFunctionResults = operationSubFunction(source,widget);
if(subFunctionResults.length === 0) {
return;
}
var name = subFunctionResults[0];
if(typeof name !== "string" || name.length === 0) {
return;
}
// Assign the result of the subfunction to the variable
var variables = {};
variables[name] = resultList;
// Return the variables
return {
variables: variables
};
};
};

View File

@@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
operator.prefix = filterString.charAt(p++);
}
// Get the operator name
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
if(nextBracketPos === -1) {
throw "Missing [ in filter expression";
}
@@ -79,17 +79,13 @@ function parseFilterOperation(operators,filterString,p) {
operand.variable = true;
nextBracketPos = filterString.indexOf(">",p);
break;
case "(": // Round brackets
operand.multiValuedVariable = true;
nextBracketPos = filterString.indexOf(")",p);
break;
case "/": // regexp brackets
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
rexMatch = rex.exec(filterString.substring(p));
if(rexMatch) {
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
// DEPRECATION WARNING
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
// DEPRECATION WARNING
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
nextBracketPos = p + rex.lastIndex - 1;
}
else {
@@ -116,7 +112,7 @@ function parseFilterOperation(operators,filterString,p) {
// Check for multiple operands
while(filterString.charAt(p) === ",") {
p++;
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
nextBracketPos = p;
p++;
parseOperand(filterString.charAt(nextBracketPos));
@@ -145,17 +141,7 @@ exports.parseFilter = function(filterString) {
p = 0, // Current position in the filter string
match;
var whitespaceRegExp = /(\s+)/mg,
// Groups:
// 1 - pragma
// 2 - pragma suffix
// 3 - entire filter run prefix
// 4 - filter run prefix name
// 5 - filter run prefix suffixes
// 6 - opening square bracket following filter run prefix
// 7 - double quoted string following filter run prefix
// 8 - single quoted string following filter run prefix
// 9 - anything except for whitespace and square brackets
operandRegExp = /(?:::(\w+)(?:\:(\w+))?(?=\s|$)|((?:\+|\-|~|(?:=>?)|:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+)))/mg;
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
while(p < filterString.length) {
// Skip any whitespace
whitespaceRegExp.lastIndex = p;
@@ -166,53 +152,41 @@ exports.parseFilter = function(filterString) {
// Match the start of the operation
if(p < filterString.length) {
operandRegExp.lastIndex = p;
match = operandRegExp.exec(filterString);
if(!match || match.index !== p) {
throw $tw.language.getString("Error/FilterSyntax");
}
var operation = {
prefix: "",
operators: []
};
match = operandRegExp.exec(filterString);
if(match && match.index === p) {
if(match[1]) {
// If there is a filter pragma
operation.pragma = match[1];
operation.suffix = match[2];
p = match.index + match[0].length;
} else if(match[3]) {
// If there is a filter run prefix
operation.prefix = match[3];
p = p + operation.prefix.length;
// Name for named prefixes
if(match[4]) {
operation.namedPrefix = match[4];
}
// Suffixes for filter run prefix
if(match[5]) {
operation.suffixes = [];
$tw.utils.each(match[5].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
});
if(match[1]) {
operation.prefix = match[1];
p = p + operation.prefix.length;
if(match[2]) {
operation.namedPrefix = match[2];
}
if(match[3]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
});
}
});
}
// Opening square bracket
if(match[6]) {
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
}
} else {
// No filter run prefix
p = parseFilterOperation(operation.operators,filterString,p);
}
// Quoted strings and unquoted title
if(match[7] || match[8] || match[9]) { // Double quoted string, single quoted string or unquoted title
if(match[4]) { // Opening square bracket
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
}
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
operation.operators.push(
{operator: "title", operands: [{text: match[7] || match[8] || match[9]}]}
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
);
}
results.push(operation);
@@ -237,30 +211,23 @@ exports.getFilterRunPrefixes = function() {
return this.filterRunPrefixes;
}
exports.filterTiddlers = function(filterString,widget,source,options) {
var fn = this.compileFilter(filterString,options);
try {
const fnResult = fn.call(this,source,widget);
return fnResult;
} catch(e) {
return [`${$tw.language.getString("Error/Filter")}: ${e}`];
}
exports.filterTiddlers = function(filterString,widget,source) {
var fn = this.compileFilter(filterString);
return fn.call(this,source,widget);
};
/*
Compile a filter into a function with the signature fn(source,widget,options) where:
Compile a filter into a function with the signature fn(source,widget) where:
source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title)
widget: an optional widget node for retrieving the current tiddler etc.
*/
exports.compileFilter = function(filterString,options) {
var defaultFilterRunPrefix = (options || {}).defaultFilterRunPrefix || "or";
var cacheKey = filterString + "|" + defaultFilterRunPrefix;
exports.compileFilter = function(filterString) {
if(!this.filterCache) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
if(this.filterCache[cacheKey] !== undefined) {
return this.filterCache[cacheKey];
if(this.filterCache[filterString] !== undefined) {
return this.filterCache[filterString];
}
var filterParseTree;
try {
@@ -284,8 +251,6 @@ exports.compileFilter = function(filterString,options) {
results = [];
$tw.utils.each(operation.operators,function(operator) {
var operands = [],
multiValueOperands = [],
isMultiValueOperand = [],
operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
@@ -301,47 +266,28 @@ exports.compileFilter = function(filterString,options) {
if(operand.indirect) {
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
operand.multiValue = [operand.value];
} 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.multiValue = [operand.value];
} else if(operand.multiValuedVariable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
operand.value = operand.multiValue[0] || "";
} else {
operand.value = "";
operand.multiValue = [];
}
operand.isMultiValueOperand = true;
} else {
operand.value = operand.text;
operand.multiValue = [operand.value];
}
operands.push(operand.value);
multiValueOperands.push(operand.multiValue);
isMultiValueOperand.push(!!operand.isMultiValueOperand);
});
// Invoke the appropriate filteroperator module
results = operatorFunction(accumulator,{
operator: operator.operator,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
multiValueOperands: multiValueOperands,
isMultiValueOperand: isMultiValueOperand,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
regexp: operator.regexp
},{
wiki: self,
widget: widget,
defaultFilterRunPrefix: defaultFilterRunPrefix
});
operator: operator.operator,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
regexp: operator.regexp
},{
wiki: self,
widget: widget
});
if($tw.utils.isArray(results)) {
accumulator = self.makeTiddlerIterator(results);
} else {
@@ -361,45 +307,27 @@ exports.compileFilter = function(filterString,options) {
var filterRunPrefixes = self.getFilterRunPrefixes();
// Wrap the operator functions in a wrapper function that depends on the prefix
operationFunctions.push((function() {
if(operation.pragma) {
switch(operation.pragma) {
case "defaultprefix":
defaultFilterRunPrefix = operation.suffix || "or";
break;
default:
var options = {wiki: self, suffixes: operation.suffixes || []};
switch(operation.prefix || "") {
case "": // No prefix means that the operation is unioned into the result
return filterRunPrefixes["or"](operationSubFunction, options);
case "=": // The results of the operation are pushed into the result without deduplication
return filterRunPrefixes["all"](operationSubFunction, options);
case "-": // The results of this operation are removed from the main result
return filterRunPrefixes["except"](operationSubFunction, options);
case "+": // This operation is applied to the main results so far
return filterRunPrefixes["and"](operationSubFunction, options);
case "~": // This operation is unioned into the result only if the main result so far is empty
return filterRunPrefixes["else"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterPragma"));
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
return function(results,source,widget) {
// Dummy response
};
} else {
var options = {wiki: self, suffixes: operation.suffixes || []};
switch(operation.prefix || "") {
case "": // Use the default filter run prefix if none is specified
return filterRunPrefixes[defaultFilterRunPrefix](operationSubFunction, options);
case "=": // The results of the operation are pushed into the result without deduplication
return filterRunPrefixes["all"](operationSubFunction, options);
case "-": // The results of this operation are removed from the main result
return filterRunPrefixes["except"](operationSubFunction, options);
case "+": // This operation is applied to the main results so far
return filterRunPrefixes["and"](operationSubFunction, options);
case "~": // This operation is unioned into the result only if the main result so far is empty
return filterRunPrefixes["else"](operationSubFunction, options);
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
return filterRunPrefixes["let"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
}
}
}
})());
});
@@ -417,13 +345,7 @@ exports.compileFilter = function(filterString,options) {
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
$tw.utils.each(operationFunctions,function(operationFunction) {
var operationResult = operationFunction(results,source,widget);
if(operationResult) {
if(operationResult.variables) {
// If the filter run prefix has returned variables, create a new fake widget with those variables
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
}
}
operationFunction(results,source,widget);
});
} else {
results.push("/**-- Excessive filter recursion --**/");
@@ -438,7 +360,7 @@ exports.compileFilter = function(filterString,options) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
this.filterCache[cacheKey] = fnMeasured;
this.filterCache[filterString] = fnMeasured;
this.filterCacheCount++;
return fnMeasured;
};

View File

@@ -1,21 +0,0 @@
/*\
title: $:/core/modules/filters/changecount.js
type: application/javascript
module-type: filteroperator
Filter operator for retrieving the changecount for each title in the list.
\*/
"use strict";
/*
Export our filter function
*/
exports.changecount = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(options.wiki.getChangeCount(title) + "");
});
return results;
};

View File

@@ -1,140 +0,0 @@
/*\
title: $:/core/modules/filters/colour-ops.js
type: application/javascript
module-type: filteroperator
Filter operators for colour operations
\*/
"use strict";
var Color = require("$:/core/modules/utils/dom/color.js").Color,
colourSpacesList = Object.keys(Color.spaces),
hueAdjustersList = ["raw","increasing","decreasing","longer","shorter"];
exports["colour-lighten"] = makeSerialColourOperator(function (colour, operator, options) {
return colour.lighten($tw.utils.parseNumber(operator.operand)).display().toString();
});
exports["colour-darken"] = makeSerialColourOperator(function (colour, operator, options) {
return colour.darken($tw.utils.parseNumber(operator.operand)).display().toString();
});
exports["colour-get-oklch"] = makeSerialColourOperator(function (colour, operator, options) {
var prop = ((operator.suffixes || [])[0] || ["l"])[0];
if(["l","c","h"].indexOf(prop) !== -1) {
colour = colour.oklch[prop];
}
return colour.toString();
});
exports["colour-set-oklch"] = makeSerialColourOperator(function (colour, operator, options) {
var prop = ((operator.suffixes || [])[0] || ["l"])[0];
if(["l","c","h"].indexOf(prop) !== -1) {
colour.oklch[prop] = $tw.utils.parseNumber(operator.operand);
}
return colour.display().toString();
});
exports["colour-set-alpha"] = makeSerialColourOperator(function (colour, operator, options) {
colour.alpha = $tw.utils.parseNumber(operator.operand);
return colour.display().toString();
});
exports["colour-contrast"] = makeParallelColourOperator(function (colours, operator, options) {
var colourContrasts = [];
$tw.utils.each(colours,function(colour,index) {
if(!colour) {
colour = $tw.utils.parseCSSColorObject("white");
colours[index] = colour;
}
if(index > 0) {
colourContrasts.push(colour.contrast(colours[index - 1],"DeltaPhi").toString());
}
});
return colourContrasts;
});
exports["colour-best-contrast"] = makeParallelColourOperator(function (colours, operator, options, originalColours) {
var bestContrast = 0,
bestColour = null;
if(colours.length < 2) {
return [];
}
var targetColour = colours[colours.length - 1];
for(var t=0; t<colours.length; t++) {
var colour = colours[t];
if(colour) {
var contrast = colour.contrast(targetColour,"DeltaPhi");
if(contrast > bestContrast) {
bestContrast = contrast;
bestColour = originalColours[t];
}
}
}
if(bestColour) {
return [bestColour];
} else {
return [];
}
});
exports["colour-interpolate"] = function(source,operator,options) {
// Get the colour space suffix
var space = ((((operator.suffixes || [])[0] || ["srgb"])[0]) || "").toLowerCase();
if(colourSpacesList.indexOf(space) === -1) {
space = "lch";
}
// Get the hue adjuster suffix
var hueAdjuster = ((((operator.suffixes || [])[1] || ["shorter"])[0]) || "").toLowerCase();
if(hueAdjustersList.indexOf(hueAdjuster) === -1) {
hueAdjuster = "shorter";
}
// Get the colours
if(operator.operands.length < 2) {
return [];
}
var colourA = $tw.utils.parseCSSColorObject(operator.operands[0]),
colourB = $tw.utils.parseCSSColorObject(operator.operands[1]);
if(!colourA || !colourB) {
return [];
}
var rangefn = colourA.range(colourB,{space: space, hue: hueAdjuster});
// Cycle through the weights
var results = [];
source(function(tiddler,title) {
var index = $tw.utils.parseNumber(title);
var colour = rangefn(index);
results.push(colour.display().toString());
});
return results;
};
function makeSerialColourOperator(fn) {
return function (source, operator, options) {
var results = [];
source(function (tiddler, title) {
var c = $tw.utils.parseCSSColorObject(title);
if (c) {
c = fn(c, operator, options);
results.push(c);
} else {
results.push("");
}
});
return results;
};
}
function makeParallelColourOperator(fn) {
return function (source, operator, options) {
var originalColours = [],
colours = [];
source(function (tiddler, title) {
originalColours.push(title);
colours.push($tw.utils.parseCSSColorObject(title));
});
return fn(colours, operator, options, originalColours);
};
}

View File

@@ -13,9 +13,7 @@ Filter operator returning those input titles that pass a subfilter
Export our filter function
*/
exports.filter = function(source,operator,options) {
var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] || [options.defaultFilterRunPrefix] || [])[0] || "or",
filterFn = options.wiki.compileFilter(operator.operand,{defaultFilterRunPrefix}),
var filterFn = options.wiki.compileFilter(operator.operand),
results = [],
target = operator.prefix !== "!";
source(function(tiddler,title) {

View File

@@ -16,8 +16,12 @@ exports.json = function(source,operand,options) {
spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand;
}
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,function(){return undefined;});
var data = $tw.utils.parseJSONSafe(title);
try {
data = JSON.parse(title);
} catch(e) {
data = undefined;
}
if(data !== undefined) {
results.push(JSON.stringify(data,null,spaces));
}

View File

@@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
params = [],
results;
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
$tw.utils.each(operator.operands.slice(1),function(param) {
params.push({value: param});
});
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});

View File

@@ -113,22 +113,6 @@ exports["jsonset"] = function(source,operator,options) {
return results;
};
exports["jsondelete"] = function(source,operator,options) {
var indexes = operator.operands,
results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
// If parsing failed (data equals original title and is a string), return unchanged
if(data === title && typeof data === "string") {
results.push(title);
} else if(data) {
data = deleteDataItem(data,indexes);
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
*/
@@ -160,7 +144,7 @@ function convertDataItemValueToStrings(item) {
return ["null"]
} else if(typeof item === "object") {
var results = [],i,t;
if(Array.isArray(item)) {
if($tw.utils.isArray(item)) {
// Return all the items in arrays recursively
for(i=0; i<item.length; i++) {
t = convertDataItemValueToStrings(item[i])
@@ -194,7 +178,7 @@ function convertDataItemKeysToStrings(item) {
return [];
}
var results = [];
if(Array.isArray(item)) {
if($tw.utils.isArray(item)) {
for(var i=0; i<item.length; i++) {
results.push(i.toString());
}
@@ -217,7 +201,7 @@ function getDataItemType(data,indexes) {
return item;
} else if(item === null) {
return "null";
} else if(Array.isArray(item)) {
} else if($tw.utils.isArray(item)) {
return "array";
} else if(typeof item === "object") {
return "object";
@@ -229,7 +213,7 @@ function getDataItemType(data,indexes) {
function getItemAtIndex(item,index) {
if($tw.utils.hop(item,index)) {
return item[index];
} else if(Array.isArray(item)) {
} 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
@@ -239,16 +223,15 @@ function getItemAtIndex(item,index) {
}
/*
Traverse the index chain and return the item at the specified depth.
Returns the item at the end of the traversal, or undefined if traversal fails.
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
*/
function traverseIndexChain(data,indexes,stopBeforeLast) {
function getDataItem(data,indexes) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Get the item
var item = data;
var stopIndex = stopBeforeLast ? indexes.length - 1 : indexes.length;
for(var i = 0; i < stopIndex; i++) {
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]);
@@ -260,13 +243,6 @@ function traverseIndexChain(data,indexes,stopBeforeLast) {
return item;
}
/*
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
*/
function getDataItem(data,indexes) {
return traverseIndexChain(data,indexes,false);
}
/*
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
*/
@@ -279,15 +255,18 @@ function setDataItem(data,indexes,value) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return value;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
// 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(Array.isArray(current)) {
if($tw.utils.isArray(current)) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
}
@@ -297,32 +276,3 @@ function setDataItem(data,indexes,value) {
}
return data;
}
/*
Given a JSON data structure and an array of index strings, return the data structure with the item at the end of the index chain deleted. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then the JSON data structure is returned unmodified.
*/
function deleteDataItem(data,indexes) {
// Check for the root item - don't delete the root
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined || current === null) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Delete the item at the end of the index chain
var lastIndex = indexes[indexes.length - 1];
if(Array.isArray(current) && current !== null) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
// Check if index is valid before splicing
if(lastIndex >= 0 && lastIndex < current.length) {
current.splice(lastIndex,1);
}
} else if(typeof current === "object" && current !== null) {
delete current[lastIndex];
}
return data;
}

View File

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

View File

@@ -14,31 +14,31 @@ Export our filter function
*/
exports.sort = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,false,undefined,operator.operands[1]);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,false);
return results;
};
exports.nsort = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,true,undefined,operator.operands[1]);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,true);
return results;
};
exports.sortan = function(source, operator, options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results, operator.operands[0] || "title", operator.prefix === "!",false,false,true,operator.operands[1]);
options.wiki.sortTiddlers(results, operator.operand || "title", operator.prefix === "!",false,false,true);
return results;
};
exports.sortcs = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,false,undefined,operator.operands[1]);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,false);
return results;
};
exports.nsortcs = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,true,undefined,operator.operands[1]);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,true);
return results;
};

View File

@@ -37,14 +37,14 @@ exports.trim = function(source,operator,options) {
operand = (operator.operand || ""),
fnCalc;
if(suffix === "prefix") {
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];};
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];}
} else if(suffix === "suffix") {
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];};
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];}
} else {
if(operand === "") {
fnCalc = function(a) {return [$tw.utils.trim(a)];};
fnCalc = function(a) {return [$tw.utils.trim(a)];}
} else {
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];};
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];}
}
}
source(function(tiddler,title) {
@@ -71,53 +71,107 @@ exports.join = makeStringReducingOperator(
},null
);
const dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
exports.levenshtein = makeStringBinaryOperator(
function(a,b) {
const diffs = dmp.diffMain(a,b);
return [dmp.diffLevenshtein(diffs).toString()];
var dmpObject = new dmp.diff_match_patch(),
diffs = dmpObject.diff_main(a,b);
return [dmpObject.diff_levenshtein(diffs) + ""];
}
);
// this function is adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
// these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diffLineWordMode(text1,text2,mode) {
var a = $tw.utils.diffPartsToChars(text1,text2,mode);
var dmpObject = new dmp.diff_match_patch();
var a = diffPartsToChars(text1,text2,mode);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diffMain(lineText1,lineText2,false);
dmp.diffCharsToLines(diffs,lineArray);
var diffs = dmpObject.diff_main(lineText1,lineText2,false);
dmpObject.diff_charsToLines_(diffs,lineArray);
return diffs;
}
function diffPartsToChars(text1,text2,mode) {
var lineArray = [];
var lineHash = {};
lineArray[0] = '';
function diff_linesToPartsMunge_(text,mode) {
var chars = '';
var lineStart = 0;
var lineEnd = -1;
var lineArrayLength = lineArray.length,
regexpResult;
var searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf('\n', lineStart);
if(lineEnd == -1) {
lineEnd = text.length - 1;
}
}
var line = text.substring(lineStart, lineEnd + 1);
if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
} else {
if(lineArrayLength == maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
var maxLines = 40000;
var chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
var chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
};
exports.makepatches = function(source,operator,options) {
var suffix = operator.suffix || "",
var dmpObject = new dmp.diff_match_patch(),
suffix = operator.suffix || "",
result = [];
source(function(tiddler,title) {
let diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmp.patchMake(title,diffs);
} else {
patches = dmp.patchMake(title,operator.operand);
}
Array.prototype.push.apply(result,[dmp.patchToText(patches)]);
});
source(function(tiddler,title) {
var diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmpObject.patch_make(title,diffs);
} else {
patches = dmpObject.patch_make(title,operator.operand);
}
Array.prototype.push.apply(result,[dmpObject.patch_toText(patches)]);
});
return result;
};
exports.applypatches = makeStringBinaryOperator(
function(a,b) {
let patches;
var dmpObject = new dmp.diff_match_patch(),
patches;
try {
patches = dmp.patchFromText(b);
patches = dmpObject.patch_fromText(b);
} catch(e) {
}
if(patches) {
return [dmp.patchApply(patches,a)[0]];
return [dmpObject.patch_apply(patches,a)[0]];
} else {
return [a];
}
@@ -225,7 +279,7 @@ exports.pad = function(source,operator,options) {
}
});
return results;
};
}
exports.charcode = function(source,operator,options) {
var chars = [];

View File

@@ -13,9 +13,7 @@ Filter operator returning its operand evaluated as a filter
Export our filter function
*/
exports.subfilter = function(source,operator,options) {
var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] || [options.defaultFilterRunPrefix] || [])[0] || "or";
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source,{defaultFilterRunPrefix});
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source);
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {

View File

@@ -16,13 +16,12 @@ exports.title = function(source,operator,options) {
var results = [];
if(operator.prefix === "!") {
source(function(tiddler,title) {
var titleList = operator.multiValueOperands[0] || [];
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
if(tiddler && tiddler.fields.title !== operator.operand) {
results.push(title);
}
});
} else {
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
results.push(operator.operand);
}
return results;
};

View File

@@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator
if(operator.operator.indexOf(".") !== -1) {
var params = [];
$tw.utils.each(operator.multiValueOperands,function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
$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) {

View File

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

View File

@@ -1,67 +0,0 @@
/*\
title: $:/core/modules/info/mediaquerytracker.js
type: application/javascript
module-type: info
Initialise $:/info/ tiddlers derived from media queries via
\*/
"use strict";
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
if($tw.browser) {
// Functions to start and stop tracking a particular media query tracker tiddler
function track(title) {
var result = {},
tiddler = $tw.wiki.getTiddler(title);
if(tiddler) {
var mediaQuery = tiddler.fields["media-query"],
infoTiddler = tiddler.fields["info-tiddler"],
infoTiddlerAlt = tiddler.fields["info-tiddler-alt"];
if(mediaQuery && infoTiddler) {
// Evaluate and track the media query
result.mqList = window.matchMedia(mediaQuery);
function getResultTiddlers() {
var value = result.mqList.matches ? "yes" : "no",
tiddlers = [];
tiddlers.push({title: infoTiddler, text: value});
if(infoTiddlerAlt) {
tiddlers.push({title: infoTiddlerAlt, text: value});
}
return tiddlers;
};
updateInfoTiddlersCallback(getResultTiddlers());
result.handler = function(event) {
updateInfoTiddlersCallback(getResultTiddlers());
};
result.mqList.addEventListener("change",result.handler);
}
}
return result;
}
function untrack(enterValue) {
if(enterValue.mqList && enterValue.handler) {
enterValue.mqList.removeEventListener("change",enterValue.handler);
}
}
// Track media query tracker tiddlers
function fnEnter(title) {
return track(title);
}
function fnLeave(title,enterValue) {
untrack(enterValue);
}
function fnChange(title,enterValue) {
untrack(enterValue);
return track(title);
}
$tw.filterTracker.track({
filterString: "[all[tiddlers+shadows]tag[$:/tags/MediaQueryTracker]!is[draft]]",
fnEnter: fnEnter,
fnLeave: fnLeave,
fnChange: fnChange
});
}
return [];
};

View File

@@ -33,6 +33,13 @@ exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
// Screen size
infoTiddlerFields.push({title: "$:/info/browser/screen/width", text: window.screen.width.toString()});
infoTiddlerFields.push({title: "$:/info/browser/screen/height", text: window.screen.height.toString()});
// Dark mode through event listener on MediaQueryList
var mqList = window.matchMedia("(prefers-color-scheme: dark)"),
getDarkModeTiddler = function() {return {title: "$:/info/darkmode", text: mqList.matches ? "yes" : "no"};};
infoTiddlerFields.push(getDarkModeTiddler());
mqList.addListener(function(event) {
updateInfoTiddlersCallback([getDarkModeTiddler()]);
});
// Language
infoTiddlerFields.push({title: "$:/info/browser/language", text: navigator.language || ""});
}

View File

@@ -26,26 +26,25 @@ exports.params = [
Run the macro
*/
exports.run = function(target,fallbackTarget,colourA,colourB) {
var rgbTarget = $tw.utils.parseCSSColorObject(target) || $tw.utils.parseCSSColorObject(fallbackTarget);
var rgbTarget = $tw.utils.parseCSSColor(target) || $tw.utils.parseCSSColor(fallbackTarget);
if(!rgbTarget) {
return colourA;
}
var rgbColourA = $tw.utils.parseCSSColorObject(colourA),
rgbColourB = $tw.utils.parseCSSColorObject(colourB);
var rgbColourA = $tw.utils.parseCSSColor(colourA),
rgbColourB = $tw.utils.parseCSSColor(colourB);
if(rgbColourA && !rgbColourB) {
return colourA;
return rgbColourA;
}
if(rgbColourB && !rgbColourA) {
return colourB;
return rgbColourB;
}
if(!rgbColourA && !rgbColourB) {
// If neither colour is readable, return a crude inverse of the target
rgbTarget.srgb.r = 1 - rgbTarget.srgb.r;
rgbTarget.srgb.g = 1 - rgbTarget.srgb.g;
rgbTarget.srgb.b = 1 - rgbTarget.srgb.b;
return rgbTarget.display();
return [255 - rgbTarget[0],255 - rgbTarget[1],255 - rgbTarget[2],rgbTarget[3]];
}
var aContrast = rgbColourA.contrast(rgbTarget,"DeltaPhi"),
bContrast = rgbColourB.contrast(rgbTarget,"DeltaPhi");
return aContrast > bContrast ? colourA : colourB;
// Colour brightness formula derived from http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
var brightnessTarget = rgbTarget[0] * 0.299 + rgbTarget[1] * 0.587 + rgbTarget[2] * 0.114,
brightnessA = rgbColourA[0] * 0.299 + rgbColourA[1] * 0.587 + rgbColourA[2] * 0.114,
brightnessB = rgbColourB[0] * 0.299 + rgbColourB[1] * 0.587 + rgbColourB[2] * 0.114;
return Math.abs(brightnessTarget - brightnessA) > Math.abs(brightnessTarget - brightnessB) ? colourA : colourB;
};

View File

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

View File

@@ -11,13 +11,10 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
var CsvParser = function(type,text,options) {
// Special handler for tab-delimited files
if(
!options.separator &&
(type === "text/tab-delimited-values" || type === "text/tab-separated-values")
) {
if (type === 'text/tab-delimited-values' && !options.separator) {
options.separator = "\t";
}
// Table framework
this.tree = [{
"type": "scrollable", "children": [{
@@ -35,7 +32,7 @@ var CsvParser = function(type,text,options) {
$tw.utils.each(lines, function(columns) {
maxColumns = Math.max(columns.length, maxColumns);
});
for(var line=0; line<lines.length; line++) {
var columns = lines[line];
var row = {
@@ -58,4 +55,3 @@ var CsvParser = function(type,text,options) {
exports["text/csv"] = CsvParser;
exports["text/tab-delimited-values"] = CsvParser;
exports["text/tab-separated-values"] = CsvParser;

View File

@@ -11,16 +11,17 @@ The image parser parses an image into an embeddable HTML element
var ImageParser = function(type,text,options) {
var element = {
type: "image",
attributes: {}
};
type: "element",
tag: "img",
attributes: {}
};
if(options._canonical_uri) {
element.attributes.source = {type: "string", value: options._canonical_uri};
element.attributes.src = {type: "string", value: options._canonical_uri};
} else if(text) {
if(type === "image/svg+xml" || type === ".svg") {
element.attributes.source = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
element.attributes.src = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
} else {
element.attributes.source = {type: "string", value: "data:" + type + ";base64," + text};
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
}
}
this.tree = [element];

View File

@@ -82,7 +82,6 @@ exports.parseTokenString = function(source,pos,token) {
/*
Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,}
Use the "Y" (sticky) flag to avoid searching the entire rest of the string
*/
exports.parseTokenRegExp = function(source,pos,reToken) {
var node = {
@@ -173,7 +172,7 @@ exports.parseMacroParameter = function(source,pos) {
start: pos
};
// Define our regexp
const reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/y;
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for the parameter
@@ -241,7 +240,7 @@ exports.parseMacroInvocation = function(source,pos) {
params: []
};
// Define our regexps
const reMacroName = /([^\s>"'=]+)/y;
var reMacroName = /([^\s>"'=]+)/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a double less than sign
@@ -278,7 +277,7 @@ exports.parseFilterVariable = function(source) {
params: [],
},
pos = 0,
reName = /([^\s"']+)/y;
reName = /([^\s"']+)/g;
// If there is no whitespace or it is an empty string then there are no macro parameters
if(/^\S*$/.test(source)) {
node.name = source;
@@ -303,11 +302,11 @@ exports.parseAttribute = function(source,pos) {
start: pos
};
// Define our regexps
const reAttributeName = /([^\/\s>"'`=]+)/y,
reUnquotedAttribute = /([^\/\s<>"'`=]+)/y,
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/y,
reIndirectValue = /\{\{([^\}]+)\}\}/y,
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/y;
var reAttributeName = /([^\/\s>"'`=]+)/g,
reUnquotedAttribute = /([^\/\s<>"'`=]+)/g,
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g,
reIndirectValue = /\{\{([^\}]+)\}\}/g,
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Get the attribute name

View File

@@ -22,7 +22,7 @@ Note that the syntax for comments is simplified to an opening "<!--" sequence an
"use strict";
exports.name = "commentblock";
exports.types = {block: true, pragma: true};
exports.types = {block:true, pragma:true};
exports.init = function(parser) {
this.parser = parser;
@@ -43,18 +43,9 @@ exports.findNextMatch = function(startPos) {
return undefined;
};
exports.parse = function() {
// Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex;
// Return a node representing the comment that is not rendered
var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
children: [],
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
// Don't return any elements
return [];
};

View File

@@ -40,13 +40,6 @@ exports.findNextMatch = function(startPos) {
exports.parse = function() {
// Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex;
// Return a node representing the inline comment
var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
// Don't return any elements
return [];
};

View File

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

View File

@@ -28,11 +28,11 @@ exports.init = function(parser) {
exports.parse = function() {
// Move past the match
var start = this.parser.pos;
var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// Create the link unless it is suppressed
if(this.match[0].substr(0,1) === "~") {
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}];
return [{type: "text", text: this.match[0].substr(1)}];
} else {
return [{
type: "element",

View File

@@ -6,15 +6,15 @@ module-type: wikirule
Wiki pragma rule for function, procedure and widget definitions
```
\function name(param:"defaultvalue", param2:"defaultvalue")
\function name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\procedure name(param:"defaultvalue", param2:"defaultvalue")
\procedure name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\widget $mywidget(param:"defaultvalue", param2:"defaultvalue")
\widget $mywidget(param:defaultvalue,param2:defaultvalue)
definition text
\end
```

View File

@@ -50,8 +50,6 @@ exports.parse = function() {
}
}
} while(match && !match[1]);
// Mark first and last node, and return the nodes
if(tree[0]) tree[0].isRuleStart = true;
if(tree[tree.length-1]) tree[tree.length-1].isRuleEnd = true;
// Return the nodes
return tree;
};

View File

@@ -41,7 +41,7 @@ Parse the most recent match
exports.parse = function() {
// Retrieve the most recent match so that recursive calls don't overwrite it
var tag = this.nextTag;
if(!tag.isSelfClosing) {
if (!tag.isSelfClosing) {
tag.openTagStart = tag.start;
tag.openTagEnd = tag.end;
}
@@ -49,7 +49,7 @@ exports.parse = function() {
// Advance the parser position to past the tag
this.parser.pos = tag.end;
// Check for an immediately following double linebreak
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
// Set whether we're in block mode
tag.isBlock = this.is.block || hasLineBreak;
// Parse the body if we need to
@@ -63,22 +63,22 @@ exports.parse = function() {
}
tag.end = this.parser.pos;
tag.closeTagEnd = tag.end;
if(tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== ">") {
if (tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== '>') {
tag.closeTagStart = tag.end;
} else {
tag.closeTagStart = tag.closeTagEnd - 2;
var closeTagMinPos = tag.children.length > 0 ? tag.children[tag.children.length-1].end : tag.openTagEnd;
if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
while(tag.closeTagStart >= closeTagMinPos) {
if (!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
while (tag.closeTagStart >= closeTagMinPos) {
var char = this.parser.source[tag.closeTagStart];
if(char === ">") {
if (char === '>') {
tag.closeTagStart = -1;
break;
}
if(char === "<") break;
if (char === '<') break;
tag.closeTagStart -= 1;
}
if(tag.closeTagStart < closeTagMinPos) {
if (tag.closeTagStart < closeTagMinPos) {
tag.closeTagStart = tag.end;
}
}
@@ -100,7 +100,7 @@ exports.parseTag = function(source,pos,options) {
orderedAttributes: []
};
// Define our regexps
const reTagName = /([a-zA-Z0-9\-\$\.]+)/y;
var reTagName = /([a-zA-Z0-9\-\$\.]+)/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a less than sign
@@ -148,7 +148,7 @@ exports.parseTag = function(source,pos,options) {
pos = token.end;
// Check for a required line break
if(options.requireLineBreak) {
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
if(!token) {
return null;
}

View File

@@ -113,7 +113,7 @@ exports.parseImage = function(source,pos) {
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Get the source up to the terminating `]]`
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/y);
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g);
if(!token) {
return null;
}

View File

@@ -59,7 +59,6 @@ var listTypes = {
":": {listTag: "dl", itemTag: "dd"},
">": {listTag: "blockquote", itemTag: "div"}
};
exports.listTypes = listTypes;
/*
Parse the most recent match

View File

@@ -29,7 +29,7 @@ exports.findNextMatch = function(startPos) {
var c = this.parser.source.charAt(nextCall.end);
// Ensure EOL after parsed macro
// If we didn't need to support IE, we'd just use /(?:\r?\n|$)/ym
if((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
if ((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
this.nextCall = nextCall;
return nextStart;
}

View File

@@ -42,5 +42,3 @@ exports.parse = function() {
this.parser.pos = call.end;
return [call];
};

View File

@@ -52,10 +52,9 @@ exports.parse = function() {
}
}
// Is the remainder of the \define line blank after the parameter close paren?
var reEnd,isBlock = true;
var reEnd;
if(this.match[3]) {
// If so, it is a multiline definition and the end of the body is marked with \end
isBlock = false;
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?\\s*?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
@@ -80,8 +79,7 @@ exports.parse = function() {
attributes: {},
children: [],
params: params,
isMacroDefinition: true,
isBlock: isBlock && !!endMatch
isMacroDefinition: true
}];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);

View File

@@ -31,7 +31,6 @@ Parse the most recent match
*/
exports.parse = function() {
// Move past the pragma invocation
var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// Parse whitespace delimited tokens terminated by a line break
var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg,
@@ -59,11 +58,6 @@ exports.parse = function() {
this.parser.parseAsInline = true;
}
}
return [{
type: "void",
children: [],
parseAsInline: this.parser.parseAsInline,
start: start,
end: this.parser.pos
}];
// No parse tree nodes to return
return [];
};

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