diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 1e644e161..286a842bc 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -21,7 +21,7 @@ body:
attributes:
label: To Reproduce
description: "Steps to reproduce the behavior:"
- value: |
+ placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -41,7 +41,7 @@ body:
attributes:
label: TiddlyWiki Configuration
description: please complete the following information
- value: |
+ placeholder: |
- Version [e.g. v5.1.24]
- Saving mechanism [e.g. Node.js, TiddlyDesktop, TiddlyHost etc]
- Plugins installed [e.g. Freelinks, TiddlyMap]
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 556b93919..dca23b783 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Discuss feature request
- url: https://github.com/Jermolene/TiddlyWiki5/discussions
+ url: https://github.com/TiddlyWiki/TiddlyWiki5/discussions
about: Open new discussion about new feature
- name: Talk.Tiddlywiki Forum
url: https://talk.tiddlywiki.org
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8daf2f468..737d523ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,7 +5,7 @@ on:
- master
- tiddlywiki-com
env:
- NODE_VERSION: "12"
+ NODE_VERSION: "18"
jobs:
test:
runs-on: ubuntu-latest
@@ -14,7 +14,13 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: "${{ env.NODE_VERSION }}"
- - run: "./bin/test.sh"
+ - run: "./bin/ci-test.sh"
+ - uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
build-prerelease:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
@@ -54,6 +60,7 @@ jobs:
TW5_BUILD_TIDDLYWIKI: "./node_modules/tiddlywiki/tiddlywiki.js"
TW5_BUILD_MAIN_EDITION: "./editions/tw5.com"
TW5_BUILD_OUTPUT: "./output"
+ TW5_BUILD_ARCHIVE: "./output"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml
new file mode 100644
index 000000000..331727b71
--- /dev/null
+++ b/.github/workflows/cla-check.yml
@@ -0,0 +1,30 @@
+name: Check CLA Signature
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+ paths-ignore:
+ - 'licenses/cla-individual.md'
+jobs:
+ check_cla:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ if: ${{ (github.event.pull_request.user.login != github.repository_owner) }}
+ steps:
+ - run: |
+ if ! curl -s https://raw.githubusercontent.com/Jermolene/TiddlyWiki5/tiddlywiki-com/licenses/cla-individual.md | grep -io "@$USER,"; then
+ echo "CLA not signed"
+ gh pr comment "$NUMBER" -b "@$USER It appears that this is your first contribution to the project, welcome.
+
+ With apologies for the bureaucracy, please could you prepare a separate PR to the 'tiddlywiki-com' branch with your signature for the Contributor License Agreement (see [contributing.md](https://github.com/TiddlyWiki/TiddlyWiki5/blob/master/contributing.md))."
+ else
+ echo "CLA already signed"
+ gh pr comment "$NUMBER" -b "Confirmed: **$USER** has already signed the Contributor License Agreement (see [contributing.md](https://github.com/TiddlyWiki/TiddlyWiki5/blob/master/contributing.md))"
+ fi
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_REPO: ${{ github.repository }}
+ NUMBER: ${{ github.event.pull_request.number }}
+ USER: ${{ github.actor }}
diff --git a/.github/workflows/cla-signed.yml b/.github/workflows/cla-signed.yml
new file mode 100644
index 000000000..01d57d014
--- /dev/null
+++ b/.github/workflows/cla-signed.yml
@@ -0,0 +1,70 @@
+name: CLA Signed
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - closed
+ paths:
+ - 'licenses/cla-individual.md'
+
+env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_REPO: ${{ github.repository }}
+ NUMBER: ${{ github.event.pull_request.number }}
+ AUTHOR: ${{ github.event.pull_request.user.login }}
+
+jobs:
+ # check if PRs updating the CLA are targetting the tiddlywiki-com branch
+ check-signature-branch:
+ if: (github.event.pull_request.merged != true) && (github.event.pull_request.user.login != github.repository_owner)
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - run: |
+ if [[ "$BRANCH" != "tiddlywiki-com" ]]; then
+ echo "This CLA signature targets the wrong branch: $BRANCH"
+ gh pr comment "$NUMBER" -b "@$AUTHOR Signatures to the CLA must target the 'tiddlywiki-com' branch."
+ fi
+ env:
+ BRANCH: ${{ github.event.pull_request.base.ref }}
+
+ # leave a comment on each open PR by a given author when their signature is added to the CLA
+ cla-signed:
+ if: (github.event.pull_request.merged == true) && (github.event.pull_request.user.login != github.repository_owner)
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: List open PRs by user
+ id: list-prs
+ uses: actions/github-script@v6
+ with:
+ result-encoding: string
+ script: |
+ const owner = context.repo.owner,
+ repo = context.repo.repo,
+ author = context.payload.pull_request.user.login;
+
+ const { data: pullRequests } = await github.rest.pulls.list({
+ owner: owner,
+ repo: repo,
+ state: 'open',
+ sort: 'created',
+ direction: 'desc',
+ per_page: 100
+ });
+ const userPullRequests = pullRequests.filter(pr => pr.user.login === author),
+ prNumbers = userPullRequests.map(pr => pr.number).join(',');
+ console.log(`Open pull requests by ${author}:${prNumbers}`);
+ return prNumbers;
+
+ - name: Comment open PRs by the same author
+ run: |
+ prs=($(echo ${{ steps.list-prs.outputs.result }} | tr "," "\n"))
+
+ for number in "${prs[@]}"
+ do
+ gh pr comment "$number" -b "**$AUTHOR** has signed the Contributor License Agreement (see [contributing.md](https://github.com/TiddlyWiki/TiddlyWiki5/blob/master/contributing.md))"
+ done
diff --git a/.gitignore b/.gitignore
index ad7e8e07f..412759161 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,11 @@
.DS_Store
.c9/
+.vs/
.vscode/
tmp/
output/
node_modules/
-
+/test-results/
+/playwright-report/
+/playwright/.cache/
+$__StoryList.tid
diff --git a/bin/build-site.sh b/bin/build-site.sh
index cd5267389..43ba7b9d7 100755
--- a/bin/build-site.sh
+++ b/bin/build-site.sh
@@ -5,7 +5,7 @@
# Default to the current version number for building the plugin library
if [ -z "$TW5_BUILD_VERSION" ]; then
- TW5_BUILD_VERSION=v5.2.6
+ TW5_BUILD_VERSION=v5.3.5
fi
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
@@ -84,10 +84,27 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n"
######################################################
#
-# Core distribution
+# Core distributions
#
######################################################
+# Conditionally build archive if $TW5_BUILD_ARCHIVE variable is set, otherwise do nothing
+#
+# /archive/Empty-TiddlyWiki-.html Empty archived version
+# /archive/TiddlyWiki-.html Full archived version
+
+if [ -n "$TW5_BUILD_ARCHIVE" ]; then
+
+node $TW5_BUILD_TIDDLYWIKI \
+ $TW5_BUILD_MAIN_EDITION \
+ --verbose \
+ --version \
+ --load $TW5_BUILD_OUTPUT/build.tid \
+ --output $TW5_BUILD_ARCHIVE \
+ --build archive \
+ || exit 1
+fi
+
# /index.html Main site
# /favicon.ico Favicon for main site
# /static.html Static rendering of default tiddlers
@@ -95,6 +112,7 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n"
# /static/* Static single tiddlers
# /static/static.css Static stylesheet
# /static/favicon.ico Favicon for static pages
+
node $TW5_BUILD_TIDDLYWIKI \
$TW5_BUILD_MAIN_EDITION \
--verbose \
@@ -104,13 +122,15 @@ node $TW5_BUILD_TIDDLYWIKI \
--build favicon static index \
|| exit 1
-# /empty.html Empty
-# /empty.hta For Internet Explorer
+# /empty.html Empty
+# /empty.hta For Internet Explorer
+# /empty-external-core.html External core empty
+# /tiddlywikicore-.js Core plugin javascript
node $TW5_BUILD_TIDDLYWIKI \
- $TW5_BUILD_MAIN_EDITION \
+ ./editions/empty \
--verbose \
--output $TW5_BUILD_OUTPUT \
- --build empty \
+ --build empty emptyexternalcore \
|| exit 1
@@ -136,6 +156,14 @@ node $TW5_BUILD_TIDDLYWIKI \
--build index favicon static \
|| exit 1
+# /tour.html tour edition
+node $TW5_BUILD_TIDDLYWIKI \
+ ./editions/tour \
+ --verbose \
+ --output $TW5_BUILD_OUTPUT \
+ --rendertiddler $:/core/save/all tour.html text/plain \
+ || exit 1
+
# /share.html Custom edition for sharing via the URL
node $TW5_BUILD_TIDDLYWIKI \
./editions/share \
@@ -351,6 +379,17 @@ node $TW5_BUILD_TIDDLYWIKI \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/highlight/empty.html text/plain \
|| exit 1
+# /plugins/tiddlywiki/geospatial/index.html Demo wiki with geospatial plugin
+# /plugins/tiddlywiki/geospatial/empty.html Empty wiki with geospatial plugin
+node $TW5_BUILD_TIDDLYWIKI \
+ ./editions/geospatialdemo \
+ --verbose \
+ --load $TW5_BUILD_OUTPUT/build.tid \
+ --output $TW5_BUILD_OUTPUT \
+ --rendertiddler $:/core/save/all plugins/tiddlywiki/geospatial/index.html text/plain \
+ --rendertiddler $:/core/save/empty plugins/tiddlywiki/geospatial/empty.html text/plain \
+ || exit 1
+
######################################################
#
# Language editions
@@ -359,14 +398,14 @@ node $TW5_BUILD_TIDDLYWIKI \
# Delete any existing static content
-rm $TW5_BUILD_OUTPUT/languages/de-AT/static/*
-rm $TW5_BUILD_OUTPUT/languages/de-DE/static/*
-rm $TW5_BUILD_OUTPUT/languages/es-ES/static/*
-rm $TW5_BUILD_OUTPUT/languages/fr-FR/static/*
-rm $TW5_BUILD_OUTPUT/languages/ja-JP/static/*
-rm $TW5_BUILD_OUTPUT/languages/ko-KR/static/*
-rm $TW5_BUILD_OUTPUT/languages/zh-Hans/static/*
-rm $TW5_BUILD_OUTPUT/languages/zh-Hant/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/de-AT/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/de-DE/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/es-ES/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/fr-FR/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/ja-JP/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/ko-KR/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/zh-Hans/static/*
+rm -rf $TW5_BUILD_OUTPUT/languages/zh-Hant/static/*
# /languages/de-AT/index.html Demo wiki with de-AT language
# /languages/de-AT/empty.html Empty wiki with de-AT language
diff --git a/bin/ci-pre-build.sh b/bin/ci-pre-build.sh
index 6f4b0ca78..a11b8e0c4 100755
--- a/bin/ci-pre-build.sh
+++ b/bin/ci-pre-build.sh
@@ -7,4 +7,4 @@ npm --force install tiddlywiki || exit 1
# Pull existing GitHub pages content
-git clone --depth=1 --branch=master "https://github.com/Jermolene/jermolene.github.io.git" output
+git clone --depth=1 --branch=master "https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages.git" output
diff --git a/bin/ci-push.sh b/bin/ci-push.sh
index dff297c80..fe8373785 100755
--- a/bin/ci-push.sh
+++ b/bin/ci-push.sh
@@ -10,6 +10,6 @@ git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
git add -A .
git commit --message "GitHub build: $GITHUB_RUN_NUMBER of $TW5_BUILD_BRANCH ($(date +'%F %T %Z'))"
-git remote add deploy "https://$GH_TOKEN@github.com/Jermolene/jermolene.github.io.git" &>/dev/null
+git remote add deploy "https://$GH_TOKEN@github.com/TiddlyWiki/tiddlywiki.com-gh-pages.git" &>/dev/null
git push deploy master &>/dev/null
cd ..
diff --git a/bin/ci-test.sh b/bin/ci-test.sh
new file mode 100755
index 000000000..ffcae66b2
--- /dev/null
+++ b/bin/ci-test.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# test TiddlyWiki5 for tiddlywiki.com
+
+node ./tiddlywiki.js \
+ ./editions/test \
+ --verbose \
+ --version \
+ --rendertiddler $:/core/save/all test.html text/plain \
+ --test \
+ || exit 1
+
+npm install playwright @playwright/test
+npx playwright install chromium firefox --with-deps
+
+npx playwright test
diff --git a/bin/clean.sh b/bin/clean.sh
index 522479edb..5a56e1971 100755
--- a/bin/clean.sh
+++ b/bin/clean.sh
@@ -2,4 +2,4 @@
# Remove any output files
-find . -regex "^./editions/[a-z0-9\.-]*/output/.*" -delete
+find . -regex "^./editions/.*/output/.*" -delete
diff --git a/bin/readme-bld.sh b/bin/readme-bld.sh
index 198c3abd0..e7c9df564 100755
--- a/bin/readme-bld.sh
+++ b/bin/readme-bld.sh
@@ -15,3 +15,11 @@ node $TW5_BUILD_TIDDLYWIKI \
--output . \
--build readmes \
|| exit 1
+
+# tw.org readmes
+node $TW5_BUILD_TIDDLYWIKI \
+ editions/tw.org \
+ --verbose \
+ --output . \
+ --build readmes \
+ || exit 1
diff --git a/boot/boot.js b/boot/boot.js
index aeaa919fe..ea20c83fd 100644
--- a/boot/boot.js
+++ b/boot/boot.js
@@ -142,15 +142,15 @@ $tw.utils.each = function(object,callback) {
var next,f,length;
if(object) {
if(Object.prototype.toString.call(object) == "[object Array]") {
- for (f=0, length=object.length; fThis community exists because TiddlyWiki is more useful when people share and work together.
This community is a beautiful but fragile thing: a collection of diverse people from all over the planet, united in their interest in the project, and their commitment to helping one another achieve and learn more.
We try to make the community as broad and welcoming as possible by remembering some basic principles of culture and behaviour.
These principles guide technical and non-technical decisions, and help contributors and leaders support our project and community.
We are optimistic and hopeful We aim to foster a learning environment that is collaborative and safe for everyone We recognise that the motivation for sharing and helping is usually for appreciation, and not financial gain, and so we take care to acknowledge and thank the people who enrich the community by sharing what they have created While we are united in our interest in TiddlyWiki, we differ in every other conceivable way. We choose to focus on what unites us, and avoid unnecessarily mixing contentious topics like religion and politics We treat each other with respect, and start with the assumption that others are acting in good faith We avoid discriminatory language We try to use our strength as a community to help others We avoid responding when angry or upset because we try to de-escalate conflict We make sure we critique ideas, not people When we disagree with others we do so graciously, and treat others with dignity and respoect We do not tolerate intolerance towards others We seek first to understand others, and then to be understood We have fun Our discussions are in English. It is not the first language of many people in the community, nor do we all share the same cultural background and reference points. So we take care to use language that is clear and unambigous, and avoid cultural references or jokes that will not be widely understood.
It is not acceptable to make jokes or other comments that discriminate by race, gender, sexuality, or other protected characteristic.
As an inclusive community, we are committed to making sure that TiddlyWiki is an accessible tool that understands the needs of people with disabilities.
\ No newline at end of file
diff --git a/contributing.md b/contributing.md
index 9dc10d0da..549e862a6 100644
--- a/contributing.md
+++ b/contributing.md
@@ -1,3 +1,3 @@
-Here we focus on contributions via GitHub Pull Requests but there are many other ways that anyone can help the TiddlyWiki project, such as reporting bugs or helping to improve our documentation .
Rules for Pull Requests PRs must meet these minimum requirements before they can be considered for merging:
The material in the PR must be free of licensing restrictions. Which means that either:The author must hold the copyright in all of the material themselves The material must be licensed under a license compatible with TiddlyWiki 's BSD license The author must sign the Contributors License Agreement (see below) Each PR should only make a single feature change The title of the PR should be 50 characters or less The title of the PR should be capitalised, and should not end with a period The title of the PR should be written in the imperative mood. See below Adequate explanation in the body of the PR for the motivation and implementation of the change. Focus on the why and what , rather than the how PRs must be self-contained. Although they can link to material elsewhere, everything needed to understand the intention of the PR should be included Any visual changes introduced by the PR should be noted and illustrated with before/after screenshots Documentation as appropriate for end-users or developers Observe the coding style Read the developers documentation Please open a consultation issue prior to investing time in making a large PR Imperative Mood for PR Titles The "imperative mood" means written as if giving a command or instruction. See this post for more details, but the gist is that the title of the PR should make sense when used to complete the sentence "If applied, this commit will...". So for example, these are good PR titles:
If applied, this commit will update the contributing guidelines If applied, this commit will change css-escape-polyfill to a $tw.utils method If applied, this commit will make it easier to subclass the wikitext parser with a custom rule set These a poorly worded PR titles:
If applied, this commit will edit text widgets should use default text for missing fields If applied, this commit will signing the CLA If applied, this commit will don't crash if options.event is missing PR titles may also include a short prefix to indicate the subsystem to which they apply. For example:
Menu plugin: Include menu text in aerial rotator Commenting on Pull Requests One of the principles of open source is that many pairs of eyes on the code can improve quality. So, we welcome comments and critiques of pending PRs. Conventional Comments has some techniques to help make comments as constructive and actionable as possible. Notably, they recommend prefixing a comment with a label to clarify the intention:
praise Praises highlight something positive. Try to leave at least one of these comments per review. Do not leave false praise (which can actually be damaging). Do look for something to sincerely praise nitpick Nitpicks are small, trivial, but necessary changes. Distinguishing nitpick comments significantly helps direct the reader's attention to comments requiring more involvement suggestion Suggestions are specific requests to improve the subject under review. It is assumed that we all want to do what's best, so these comments are never dismissed as “mere suggestions”, but are taken seriously issue Issues represent user-facing problems. If possible, it's great to follow this kind of comment with a suggestion question Questions are appropriate if you have a potential concern but are not quite sure if it's relevant or not. Asking the author for clarification or investigation can lead to a quick resolution thought Thoughts represent an idea that popped up from reviewing. These comments are non-blocking by nature, but they are extremely valuable and can lead to more focused initiatives and mentoring opportunities chore Chores are simple tasks that must be done before the subject can be “officially” accepted. Usually, these comments reference some common process. Try to leave a link to the process description so that the reader knows how to resolve the chore
Contributor License Agreement Like other OpenSource projects, TiddlyWiki5 needs a signed contributor license agreement from individual contributors. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the UnaMesa Association (the legal entity that owns TiddlyWiki on behalf of the community).
How to sign the CLA Create a GitHub pull request to add your name to cla-individual.md
or cla-entity.md
, with the date in the format (YYYY/MM/DD).
step by step
Navigate to licenses/CLA-individual or licenses/CLA-entity according to whether you are signing as an individual or representative of an organisation Ensure that the "branch" dropdown at the top left is set to tiddlywiki-com
Click the "edit" button at the top-right corner (clicking this button will fork the project so you can edit the file) Add your name at the bottomeg: Jeremy Ruston, @Jermolene, 2011/11/22
Below the edit box for the CLA text you should see a box labelled Propose file change Enter a brief title to explain the change (eg, "Signing the CLA") Click the green button labelled Propose file change On the following screen, click the green button labelled Create pull request The CLA documents used for this project were created using Harmony Project Templates . "HA-CLA-I-LIST Version 1.0" for "CLA-individual" and "HA-CLA-E-LIST Version 1.0" for "CLA-entity".
+
Here we focus on contributions via GitHub Pull Requests but there are many other ways that anyone can help the TiddlyWiki project, such as reporting bugs or helping to improve our documentation .
Rules for Pull Requests PRs must meet these minimum requirements before they can be considered for merging:
The material in the PR must be free of licensing restrictions. Which means that either:The author must hold the copyright in all of the material themselves The material must be licensed under a license compatible with TiddlyWiki 's BSD license The author must sign the Contributors License Agreement (see below) Each PR should only make a single feature change The title of the PR should be 50 characters or less The title of the PR should be capitalised, and should not end with a period The title of the PR should be written in the imperative mood. See below Adequate explanation in the body of the PR for the motivation and implementation of the change. Focus on the why and what , rather than the how PRs must be self-contained. Although they can link to material elsewhere, everything needed to understand the intention of the PR should be included Any visual changes introduced by the PR should be noted and illustrated with before/after screenshots Documentation as appropriate for end-users or developers Observe the coding style Read the developers documentation Please open a consultation issue prior to investing time in making a large PR Imperative Mood for PR Titles The "imperative mood" means written as if giving a command or instruction. See this post for more details, but the gist is that the title of the PR should make sense when used to complete the sentence "If applied, this commit will...". So for example, these are good PR titles:
If applied, this commit will update the contributing guidelines If applied, this commit will change css-escape-polyfill to a $tw.utils method If applied, this commit will make it easier to subclass the wikitext parser with a custom rule set These a poorly worded PR titles:
If applied, this commit will edit text widgets should use default text for missing fields If applied, this commit will signing the CLA If applied, this commit will don't crash if options.event is missing PR titles may also include a short prefix to indicate the subsystem to which they apply. For example:
Menu plugin: Include menu text in aerial rotator Commenting on Pull Requests One of the principles of open source is that many pairs of eyes on the code can improve quality. So, we welcome comments and critiques of pending PRs. Conventional Comments has some techniques to help make comments as constructive and actionable as possible. Notably, they recommend prefixing a comment with a label to clarify the intention:
praise Praises highlight something positive. Try to leave at least one of these comments per review. Do not leave false praise (which can actually be damaging). Do look for something to sincerely praise nitpick Nitpicks are small, trivial, but necessary changes. Distinguishing nitpick comments significantly helps direct the reader's attention to comments requiring more involvement suggestion Suggestions are specific requests to improve the subject under review. It is assumed that we all want to do what's best, so these comments are never dismissed as “mere suggestions”, but are taken seriously issue Issues represent user-facing problems. If possible, it's great to follow this kind of comment with a suggestion question Questions are appropriate if you have a potential concern but are not quite sure if it's relevant or not. Asking the author for clarification or investigation can lead to a quick resolution thought Thoughts represent an idea that popped up from reviewing. These comments are non-blocking by nature, but they are extremely valuable and can lead to more focused initiatives and mentoring opportunities chore Chores are simple tasks that must be done before the subject can be “officially” accepted. Usually, these comments reference some common process. Try to leave a link to the process description so that the reader knows how to resolve the chore
Contributor License Agreement Like other OpenSource projects, TiddlyWiki5 needs a signed contributor license agreement from individual contributors. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the UnaMesa Association (the legal entity that owns TiddlyWiki on behalf of the community).
How to sign the CLA Create a GitHub pull request to add your name to cla-individual.md
or cla-entity.md
, with the date in the format (YYYY/MM/DD).
step by step
Navigate to licenses/CLA-individual or licenses/CLA-entity according to whether you are signing as an individual or representative of an organisation Ensure that the "branch" dropdown at the top left is set to tiddlywiki-com
Click the "edit" button at the top-right corner (clicking this button will fork the project so you can edit the file) Add your name at the bottomeg: Jeremy Ruston, @Jermolene, 2011/11/22
Below the edit box for the CLA text you should see a box labelled Propose file change Enter a brief title to explain the change (eg, "Signing the CLA") Click the green button labelled Propose file change On the following screen, click the green button labelled Create pull request The CLA documents used for this project were created using Harmony Project Templates . "HA-CLA-I-LIST Version 1.0" for "CLA-individual" and "HA-CLA-E-LIST Version 1.0" for "CLA-entity".
This file was automatically generated by TiddlyWiki5
\ No newline at end of file
diff --git a/core/acknowledgements.tid b/core/acknowledgements.tid
index e7acee129..cb54e3d23 100644
--- a/core/acknowledgements.tid
+++ b/core/acknowledgements.tid
@@ -3,7 +3,7 @@ title: $:/Acknowledgements
TiddlyWiki incorporates code from these fine OpenSource projects:
* [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]]
-* [[The Jasmine JavaScript Test Framework|http://pivotal.github.io/jasmine/]]
+* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]]
* [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]]
And media from these projects:
diff --git a/core/copyright.tid b/core/copyright.tid
index ce0d6b02f..3f52380cc 100644
--- a/core/copyright.tid
+++ b/core/copyright.tid
@@ -4,7 +4,7 @@ type: text/plain
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
Copyright (c) 2004-2007, Jeremy Ruston
-Copyright (c) 2007-2023, UnaMesa Association
+Copyright (c) 2007-2024, UnaMesa Association
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/core/icon.tid b/core/icon.tid
index b327440df..21246d176 100644
--- a/core/icon.tid
+++ b/core/icon.tid
@@ -1,4 +1,5 @@
title: $:/core/icon
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/add-comment.tid b/core/images/add-comment.tid
index 178221806..a118506ed 100644
--- a/core/images/add-comment.tid
+++ b/core/images/add-comment.tid
@@ -1,4 +1,5 @@
title: $:/core/images/add-comment
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-add-comment tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/advanced-search-button.tid b/core/images/advanced-search-button.tid
index 6fda3fe8b..8e5699c4d 100755
--- a/core/images/advanced-search-button.tid
+++ b/core/images/advanced-search-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/advanced-search-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-advanced-search-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/auto-height.tid b/core/images/auto-height.tid
index 78f95418b..76deecbad 100755
--- a/core/images/auto-height.tid
+++ b/core/images/auto-height.tid
@@ -1,4 +1,5 @@
title: $:/core/images/auto-height
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-auto-height tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/blank.tid b/core/images/blank.tid
index 731b55a5a..565ef6bec 100755
--- a/core/images/blank.tid
+++ b/core/images/blank.tid
@@ -1,4 +1,5 @@
title: $:/core/images/blank
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-blank tc-image-button" viewBox="0 0 128 128"/>
\ No newline at end of file
diff --git a/core/images/bold.tid b/core/images/bold.tid
index 67a00f894..d9259e4a1 100755
--- a/core/images/bold.tid
+++ b/core/images/bold.tid
@@ -1,4 +1,5 @@
title: $:/core/images/bold
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-bold tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/cancel-button.tid b/core/images/cancel-button.tid
index c55620b06..3bb982bc1 100755
--- a/core/images/cancel-button.tid
+++ b/core/images/cancel-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/cancel-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-cancel-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/chevron-down.tid b/core/images/chevron-down.tid
index f1b363dfc..df28c87a4 100755
--- a/core/images/chevron-down.tid
+++ b/core/images/chevron-down.tid
@@ -1,4 +1,5 @@
title: $:/core/images/chevron-down
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-chevron-down tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/chevron-left.tid b/core/images/chevron-left.tid
index e4c69d95a..b7f9ad7ae 100755
--- a/core/images/chevron-left.tid
+++ b/core/images/chevron-left.tid
@@ -1,4 +1,5 @@
title: $:/core/images/chevron-left
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-chevron-left tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/chevron-right.tid b/core/images/chevron-right.tid
index 6ff5b6c0d..9ec7c96fe 100755
--- a/core/images/chevron-right.tid
+++ b/core/images/chevron-right.tid
@@ -1,4 +1,5 @@
title: $:/core/images/chevron-right
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-chevron-right tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/chevron-up.tid b/core/images/chevron-up.tid
index 9acbdec40..45366f286 100755
--- a/core/images/chevron-up.tid
+++ b/core/images/chevron-up.tid
@@ -1,4 +1,5 @@
title: $:/core/images/chevron-up
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-chevron-up tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/clone-button.tid b/core/images/clone-button.tid
index 9ff4903ad..cc0ed7595 100755
--- a/core/images/clone-button.tid
+++ b/core/images/clone-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/clone-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-clone-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/close-all-button.tid b/core/images/close-all-button.tid
index 3334c5dbc..02d25fa6f 100755
--- a/core/images/close-all-button.tid
+++ b/core/images/close-all-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/close-all-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-close-all-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/close-button.tid b/core/images/close-button.tid
index c462c9bac..a05669ccd 100755
--- a/core/images/close-button.tid
+++ b/core/images/close-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/close-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-close-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/close-others-button.tid b/core/images/close-others-button.tid
index 1cd54d797..6eb779018 100755
--- a/core/images/close-others-button.tid
+++ b/core/images/close-others-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/close-others-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-close-others-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/copy-clipboard.tid b/core/images/copy-clipboard.tid
index e4e55b6e9..d6514ad7c 100644
--- a/core/images/copy-clipboard.tid
+++ b/core/images/copy-clipboard.tid
@@ -1,4 +1,5 @@
title: $:/core/images/copy-clipboard
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-copy-clipboard tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/delete-button.tid b/core/images/delete-button.tid
index e8c9b6108..99f83b3ba 100755
--- a/core/images/delete-button.tid
+++ b/core/images/delete-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/delete-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-delete-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/done-button.tid b/core/images/done-button.tid
index 265528d06..7672b7d27 100755
--- a/core/images/done-button.tid
+++ b/core/images/done-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/done-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-done-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/down-arrow.tid b/core/images/down-arrow.tid
index 4cac65a00..7f1273b2c 100755
--- a/core/images/down-arrow.tid
+++ b/core/images/down-arrow.tid
@@ -1,4 +1,5 @@
title: $:/core/images/down-arrow
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-down-arrow tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/download-button.tid b/core/images/download-button.tid
index e3a549639..8b06356ab 100755
--- a/core/images/download-button.tid
+++ b/core/images/download-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/download-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-download-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/edit-button.tid b/core/images/edit-button.tid
index 190dffc41..e4644ef7c 100755
--- a/core/images/edit-button.tid
+++ b/core/images/edit-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/edit-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-edit-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/erase.tid b/core/images/erase.tid
index 22b31e7cc..6cc2011a9 100755
--- a/core/images/erase.tid
+++ b/core/images/erase.tid
@@ -1,4 +1,5 @@
title: $:/core/images/erase
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-erase tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/excise.tid b/core/images/excise.tid
index e6c2c9404..fa351baff 100755
--- a/core/images/excise.tid
+++ b/core/images/excise.tid
@@ -1,4 +1,5 @@
title: $:/core/images/excise
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-excise tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/export-button.tid b/core/images/export-button.tid
index eb3284c80..b9f0dca7f 100755
--- a/core/images/export-button.tid
+++ b/core/images/export-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/export-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-export-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/file.tid b/core/images/file.tid
index 67ebc22ac..682796fbf 100755
--- a/core/images/file.tid
+++ b/core/images/file.tid
@@ -1,4 +1,5 @@
title: $:/core/images/file
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-file tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/fixed-height.tid b/core/images/fixed-height.tid
index c15162267..b2b5083b9 100755
--- a/core/images/fixed-height.tid
+++ b/core/images/fixed-height.tid
@@ -1,4 +1,5 @@
title: $:/core/images/fixed-height
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-fixed-height tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/fold-all-button.tid b/core/images/fold-all-button.tid
index edff8b22b..260efe9b8 100755
--- a/core/images/fold-all-button.tid
+++ b/core/images/fold-all-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/fold-all-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-fold-all tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/fold-button.tid b/core/images/fold-button.tid
index f0b2b474d..cfc2291a5 100755
--- a/core/images/fold-button.tid
+++ b/core/images/fold-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/fold-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-fold tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/fold-others-button.tid b/core/images/fold-others-button.tid
index 5cb086f85..a5457c461 100755
--- a/core/images/fold-others-button.tid
+++ b/core/images/fold-others-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/fold-others-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-fold-others tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/folder.tid b/core/images/folder.tid
index 4b89418ff..8c5d7efb1 100755
--- a/core/images/folder.tid
+++ b/core/images/folder.tid
@@ -1,4 +1,5 @@
title: $:/core/images/folder
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-folder tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/full-screen-button.tid b/core/images/full-screen-button.tid
index 8572ff646..7e20183b7 100755
--- a/core/images/full-screen-button.tid
+++ b/core/images/full-screen-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/full-screen-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-full-screen-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/github.tid b/core/images/github.tid
index 6389f5cdc..b82c54e72 100755
--- a/core/images/github.tid
+++ b/core/images/github.tid
@@ -1,4 +1,5 @@
title: $:/core/images/github
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-github tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/gitter.tid b/core/images/gitter.tid
index 876fc3da1..4af54ddba 100644
--- a/core/images/gitter.tid
+++ b/core/images/gitter.tid
@@ -1,4 +1,5 @@
title: $:/core/images/gitter
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-gitter tc-image-button" viewBox="0 0 18 25">
\ No newline at end of file
diff --git a/core/images/globe.tid b/core/images/globe.tid
index 9448ed7a4..0e2c56b40 100755
--- a/core/images/globe.tid
+++ b/core/images/globe.tid
@@ -1,4 +1,5 @@
title: $:/core/images/globe
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-globe tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-1.tid b/core/images/heading-1.tid
index f8a98123b..d160284c9 100755
--- a/core/images/heading-1.tid
+++ b/core/images/heading-1.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-1
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-1 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-2.tid b/core/images/heading-2.tid
index ef0022cc6..8daad0f10 100755
--- a/core/images/heading-2.tid
+++ b/core/images/heading-2.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-2
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-2 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-3.tid b/core/images/heading-3.tid
index d706d067b..8a489e799 100755
--- a/core/images/heading-3.tid
+++ b/core/images/heading-3.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-3
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-3 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-4.tid b/core/images/heading-4.tid
index 16d4440db..5aee3f3f8 100755
--- a/core/images/heading-4.tid
+++ b/core/images/heading-4.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-4
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-4 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-5.tid b/core/images/heading-5.tid
index 8f34b7058..c9a54a335 100755
--- a/core/images/heading-5.tid
+++ b/core/images/heading-5.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-5
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-5 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/heading-6.tid b/core/images/heading-6.tid
index b348c70af..1034e7116 100755
--- a/core/images/heading-6.tid
+++ b/core/images/heading-6.tid
@@ -1,4 +1,5 @@
title: $:/core/images/heading-6
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-heading-6 tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/help.tid b/core/images/help.tid
index 07167ae93..5c0cf13ab 100755
--- a/core/images/help.tid
+++ b/core/images/help.tid
@@ -1,4 +1,5 @@
title: $:/core/images/help
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-help tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/home-button.tid b/core/images/home-button.tid
index 10c00626a..952ab9a60 100755
--- a/core/images/home-button.tid
+++ b/core/images/home-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/home-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-home-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/import-button.tid b/core/images/import-button.tid
index 6850513ef..a42c416e5 100755
--- a/core/images/import-button.tid
+++ b/core/images/import-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/import-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-import-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/info-button.tid b/core/images/info-button.tid
index 2679a135a..e8f8e98c4 100755
--- a/core/images/info-button.tid
+++ b/core/images/info-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/info-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-info-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/input-button.tid b/core/images/input-button.tid
new file mode 100644
index 000000000..731a1e0cd
--- /dev/null
+++ b/core/images/input-button.tid
@@ -0,0 +1,5 @@
+title: $:/core/images/input-button
+tags: $:/tags/Image
+
+\parameters (size:"22pt")
+> height=<> class="tc-image-input-button tc-image-button" viewBox="0 0 22 22">
\ No newline at end of file
diff --git a/core/images/italic.tid b/core/images/italic.tid
index 06aada589..766d3707a 100755
--- a/core/images/italic.tid
+++ b/core/images/italic.tid
@@ -1,4 +1,5 @@
title: $:/core/images/italic
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-italic tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/layout-button.tid b/core/images/layout-button.tid
index 19371cde1..a859edbc5 100755
--- a/core/images/layout-button.tid
+++ b/core/images/layout-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/layout-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-layout-button tc-image-button" viewBox="0 0 24 24" stroke-width="1" stroke="none">
\ No newline at end of file
diff --git a/core/images/left-arrow.tid b/core/images/left-arrow.tid
index a418581cd..b64fc2ab6 100755
--- a/core/images/left-arrow.tid
+++ b/core/images/left-arrow.tid
@@ -3,4 +3,5 @@ modified: 20150315235324760
tags: $:/tags/Image
title: $:/core/images/left-arrow
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-left-arrow tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/line-width.tid b/core/images/line-width.tid
index f77763ce6..9cecc33cb 100755
--- a/core/images/line-width.tid
+++ b/core/images/line-width.tid
@@ -1,4 +1,5 @@
title: $:/core/images/line-width
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-line-width tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/link.tid b/core/images/link.tid
index 395307c42..c1d134f7e 100644
--- a/core/images/link.tid
+++ b/core/images/link.tid
@@ -1,4 +1,5 @@
title: $:/core/images/link
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-link tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/linkify.tid b/core/images/linkify.tid
index 40acdc19a..d616c2ac9 100644
--- a/core/images/linkify.tid
+++ b/core/images/linkify.tid
@@ -1,4 +1,5 @@
title: $:/core/images/linkify
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-linkify-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/list-bullet.tid b/core/images/list-bullet.tid
index 322dd4ae6..065d96c4f 100755
--- a/core/images/list-bullet.tid
+++ b/core/images/list-bullet.tid
@@ -1,4 +1,5 @@
title: $:/core/images/list-bullet
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-list-bullet tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/list-number.tid b/core/images/list-number.tid
index 161917823..b70d4db15 100755
--- a/core/images/list-number.tid
+++ b/core/images/list-number.tid
@@ -1,4 +1,5 @@
title: $:/core/images/list-number
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-list-number tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/list.tid b/core/images/list.tid
index 22b784bfe..793b47957 100644
--- a/core/images/list.tid
+++ b/core/images/list.tid
@@ -1,4 +1,5 @@
title: $:/core/images/list
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-list tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/locked-padlock.tid b/core/images/locked-padlock.tid
index 14d95560c..f6722cddd 100755
--- a/core/images/locked-padlock.tid
+++ b/core/images/locked-padlock.tid
@@ -1,4 +1,5 @@
title: $:/core/images/locked-padlock
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-locked-padlock tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/mail.tid b/core/images/mail.tid
index cf0f4eab7..9d46d6eff 100755
--- a/core/images/mail.tid
+++ b/core/images/mail.tid
@@ -1,4 +1,5 @@
title: $:/core/images/mail
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-mail tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/mastodon.tid b/core/images/mastodon.tid
index b27c751fd..a6b41d355 100644
--- a/core/images/mastodon.tid
+++ b/core/images/mastodon.tid
@@ -1,6 +1,7 @@
title: $:/core/images/mastodon
tags: $:/tags/Image
-
+\parameters (size:"22pt")
+> height=<> class="tc-image-mastodon tc-image-button" viewBox="0 0 128 128">
diff --git a/core/images/menu-button.tid b/core/images/menu-button.tid
index 6d1872e4c..958721d31 100755
--- a/core/images/menu-button.tid
+++ b/core/images/menu-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/menu-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-menu-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/minus-button.tid b/core/images/minus-button.tid
index 7132ed3e9..40ee75a8e 100644
--- a/core/images/minus-button.tid
+++ b/core/images/minus-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/minus-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-minus-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/mono-block.tid b/core/images/mono-block.tid
index f8695302b..bf58400aa 100755
--- a/core/images/mono-block.tid
+++ b/core/images/mono-block.tid
@@ -1,4 +1,5 @@
title: $:/core/images/mono-block
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-mono-block tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/mono-line.tid b/core/images/mono-line.tid
index 09cfe513e..1ab73b829 100755
--- a/core/images/mono-line.tid
+++ b/core/images/mono-line.tid
@@ -1,4 +1,5 @@
title: $:/core/images/mono-line
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-mono-line tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/network-activity.tid b/core/images/network-activity.tid
new file mode 100644
index 000000000..2efdfd4d4
--- /dev/null
+++ b/core/images/network-activity.tid
@@ -0,0 +1,11 @@
+title: $:/core/images/network-activity
+tags: $:/tags/Image
+
+
+<$list filter="[{$:/state/http-requests}match[0]]" variable="ignore">
+
+$list>
+<$list filter="[{$:/state/http-requests}!match[0]]" variable="ignore">
+
+$list>
+
\ No newline at end of file
diff --git a/core/images/new-button.tid b/core/images/new-button.tid
index 6e592ada9..d4cfd34f6 100755
--- a/core/images/new-button.tid
+++ b/core/images/new-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/new-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-new-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/new-here-button.tid b/core/images/new-here-button.tid
index ab0f7a6f3..8e304f5e7 100755
--- a/core/images/new-here-button.tid
+++ b/core/images/new-here-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/new-here-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-new-here-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/new-image-button.tid b/core/images/new-image-button.tid
index 16b63c3c3..53b10d481 100755
--- a/core/images/new-image-button.tid
+++ b/core/images/new-image-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/new-image-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-new-image-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/new-journal-button.tid b/core/images/new-journal-button.tid
index fb67c8007..5b793deb5 100755
--- a/core/images/new-journal-button.tid
+++ b/core/images/new-journal-button.tid
@@ -1,4 +1,4 @@
title: $:/core/images/new-journal-button
tags: $:/tags/Image
-<>
\ No newline at end of file
+<$parameters size="22pt" day=<>>> height=<> class="tc-image-new-journal-button tc-image-button" viewBox="0 0 128 128"><$text text=<>/> $parameters>
\ No newline at end of file
diff --git a/core/images/opacity.tid b/core/images/opacity.tid
index e9a29aea2..0211644f9 100755
--- a/core/images/opacity.tid
+++ b/core/images/opacity.tid
@@ -1,4 +1,5 @@
title: $:/core/images/opacity
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-opacity tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/open-window.tid b/core/images/open-window.tid
index 14b556484..d918b1fc8 100755
--- a/core/images/open-window.tid
+++ b/core/images/open-window.tid
@@ -1,4 +1,5 @@
title: $:/core/images/open-window
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-open-window tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/options-button.tid b/core/images/options-button.tid
index bd0ffcb1a..18fbf8b00 100755
--- a/core/images/options-button.tid
+++ b/core/images/options-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/options-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-options-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/paint.tid b/core/images/paint.tid
index bb536b53a..660fda2bf 100755
--- a/core/images/paint.tid
+++ b/core/images/paint.tid
@@ -1,4 +1,5 @@
title: $:/core/images/paint
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-paint tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/palette.tid b/core/images/palette.tid
index d605fd853..1c7903549 100755
--- a/core/images/palette.tid
+++ b/core/images/palette.tid
@@ -1,4 +1,5 @@
title: $:/core/images/palette
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-palette tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/permalink-button.tid b/core/images/permalink-button.tid
index e8fd0aecd..f1cf38b41 100755
--- a/core/images/permalink-button.tid
+++ b/core/images/permalink-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/permalink-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-permalink-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/permaview-button.tid b/core/images/permaview-button.tid
index b2ad9a408..82e533475 100755
--- a/core/images/permaview-button.tid
+++ b/core/images/permaview-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/permaview-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-permaview-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/picture.tid b/core/images/picture.tid
index 7d035e2fc..2af427e2e 100755
--- a/core/images/picture.tid
+++ b/core/images/picture.tid
@@ -1,4 +1,5 @@
title: $:/core/images/picture
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-picture tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/plugin-generic-language.tid b/core/images/plugin-generic-language.tid
index 5c777d98d..d663d1563 100755
--- a/core/images/plugin-generic-language.tid
+++ b/core/images/plugin-generic-language.tid
@@ -1,4 +1,5 @@
title: $:/core/images/plugin-generic-language
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> viewBox="0 0 128 128" class="tc-image-plugin-generic-language tc-image-button">
\ No newline at end of file
diff --git a/core/images/plugin-generic-plugin.tid b/core/images/plugin-generic-plugin.tid
index ab2e6670e..06073dd7c 100755
--- a/core/images/plugin-generic-plugin.tid
+++ b/core/images/plugin-generic-plugin.tid
@@ -1,4 +1,5 @@
title: $:/core/images/plugin-generic-plugin
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> viewBox="0 0 128 128" class="tc-image-plugin-generic-plugin tc-image-button">
\ No newline at end of file
diff --git a/core/images/plugin-generic-theme.tid b/core/images/plugin-generic-theme.tid
index 9ae3cd779..ab899b3e4 100755
--- a/core/images/plugin-generic-theme.tid
+++ b/core/images/plugin-generic-theme.tid
@@ -1,4 +1,5 @@
title: $:/core/images/plugin-generic-theme
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> viewBox="0 0 128 128" class="tc-image-plugin-generic-theme tc-image-button">
\ No newline at end of file
diff --git a/core/images/plus-button.tid b/core/images/plus-button.tid
index b001f3e2f..c9a696d43 100644
--- a/core/images/plus-button.tid
+++ b/core/images/plus-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/plus-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-plus-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/preview-closed.tid b/core/images/preview-closed.tid
index 5986d8966..cf17730d5 100755
--- a/core/images/preview-closed.tid
+++ b/core/images/preview-closed.tid
@@ -1,4 +1,5 @@
title: $:/core/images/preview-closed
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-preview-closed tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/preview-open.tid b/core/images/preview-open.tid
index 4664990b4..cb30bf474 100755
--- a/core/images/preview-open.tid
+++ b/core/images/preview-open.tid
@@ -1,4 +1,5 @@
title: $:/core/images/preview-open
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-preview-open tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/print-button.tid b/core/images/print-button.tid
index 55b33c896..12bffd41d 100644
--- a/core/images/print-button.tid
+++ b/core/images/print-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/print-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-print-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/quote.tid b/core/images/quote.tid
index 7134306a6..0c4fcf25a 100755
--- a/core/images/quote.tid
+++ b/core/images/quote.tid
@@ -1,4 +1,5 @@
title: $:/core/images/quote
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-quote tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/refresh-button.tid b/core/images/refresh-button.tid
index 2422b0679..f8e3fc69e 100755
--- a/core/images/refresh-button.tid
+++ b/core/images/refresh-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/refresh-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-refresh-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/right-arrow.tid b/core/images/right-arrow.tid
index 42e7dea56..64f839b55 100755
--- a/core/images/right-arrow.tid
+++ b/core/images/right-arrow.tid
@@ -1,4 +1,5 @@
title: $:/core/images/right-arrow
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-right-arrow tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/rotate-left.tid b/core/images/rotate-left.tid
index 188d3b45c..da6034b19 100644
--- a/core/images/rotate-left.tid
+++ b/core/images/rotate-left.tid
@@ -1,4 +1,5 @@
title: $:/core/images/rotate-left
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-rotate-left tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/save-button-dynamic.tid b/core/images/save-button-dynamic.tid
new file mode 100644
index 000000000..7a351d617
--- /dev/null
+++ b/core/images/save-button-dynamic.tid
@@ -0,0 +1,13 @@
+title: $:/core/images/save-button-dynamic
+tags: $:/tags/Image
+
+\parameters (size:"22pt")
+> height=<> class="tc-image-save-button-dynamic tc-image-button" viewBox="0 0 128 128">
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/images/save-button.tid b/core/images/save-button.tid
index a66756616..912ad248c 100755
--- a/core/images/save-button.tid
+++ b/core/images/save-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/save-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-save-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/size.tid b/core/images/size.tid
index db84ecf9b..ea9aa1094 100755
--- a/core/images/size.tid
+++ b/core/images/size.tid
@@ -1,4 +1,5 @@
title: $:/core/images/size
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-size tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/spiral.tid b/core/images/spiral.tid
index ca4684cab..f3a5271ac 100755
--- a/core/images/spiral.tid
+++ b/core/images/spiral.tid
@@ -1,4 +1,5 @@
title: $:/core/images/spiral
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-spiral tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/stamp.tid b/core/images/stamp.tid
index ba385aaae..8511a457f 100755
--- a/core/images/stamp.tid
+++ b/core/images/stamp.tid
@@ -1,4 +1,5 @@
title: $:/core/images/stamp
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-stamp tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/standard-layout.tid b/core/images/standard-layout.tid
new file mode 100644
index 000000000..1b83375c9
--- /dev/null
+++ b/core/images/standard-layout.tid
@@ -0,0 +1,7 @@
+title: $:/core/images/standard-layout
+tags: $:/tags/Image
+
+\parameters (size:"22pt")
+> height=<> class="tc-image-standard-layout tc-image-button" viewBox="0 0 128 128">
+
+
\ No newline at end of file
diff --git a/core/images/star-filled.tid b/core/images/star-filled.tid
index 10b8f1c3d..262448d51 100755
--- a/core/images/star-filled.tid
+++ b/core/images/star-filled.tid
@@ -1,4 +1,5 @@
title: $:/core/images/star-filled
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-star-filled tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/storyview-classic.tid b/core/images/storyview-classic.tid
index 86872817b..457434bc9 100755
--- a/core/images/storyview-classic.tid
+++ b/core/images/storyview-classic.tid
@@ -1,4 +1,5 @@
title: $:/core/images/storyview-classic
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-storyview-classic tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/storyview-pop.tid b/core/images/storyview-pop.tid
index a610c89d5..a4a9fb9ee 100755
--- a/core/images/storyview-pop.tid
+++ b/core/images/storyview-pop.tid
@@ -1,4 +1,5 @@
title: $:/core/images/storyview-pop
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-storyview-pop tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/storyview-zoomin.tid b/core/images/storyview-zoomin.tid
index 61b7ff273..725f36e71 100755
--- a/core/images/storyview-zoomin.tid
+++ b/core/images/storyview-zoomin.tid
@@ -1,4 +1,5 @@
title: $:/core/images/storyview-zoomin
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-storyview-zoomin tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/strikethrough.tid b/core/images/strikethrough.tid
index 1f7a1c202..de4eefeec 100755
--- a/core/images/strikethrough.tid
+++ b/core/images/strikethrough.tid
@@ -1,4 +1,5 @@
title: $:/core/images/strikethrough
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-strikethrough tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/subscript.tid b/core/images/subscript.tid
index 96548bdb5..76ec35399 100755
--- a/core/images/subscript.tid
+++ b/core/images/subscript.tid
@@ -1,4 +1,5 @@
title: $:/core/images/subscript
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-subscript tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/superscript.tid b/core/images/superscript.tid
index 149e44893..ab0d0d1b2 100755
--- a/core/images/superscript.tid
+++ b/core/images/superscript.tid
@@ -1,4 +1,5 @@
title: $:/core/images/superscript
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-superscript tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/tag-button.tid b/core/images/tag-button.tid
index 9f6cad8b4..ab407f780 100755
--- a/core/images/tag-button.tid
+++ b/core/images/tag-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/tag-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-tag-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/theme-button.tid b/core/images/theme-button.tid
index d80a0e82a..6b7aab2af 100755
--- a/core/images/theme-button.tid
+++ b/core/images/theme-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/theme-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-theme-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/timestamp-off.tid b/core/images/timestamp-off.tid
index 0d20f28d6..33e018f3a 100644
--- a/core/images/timestamp-off.tid
+++ b/core/images/timestamp-off.tid
@@ -1,4 +1,5 @@
title: $:/core/images/timestamp-off
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-timestamp-off tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/timestamp-on.tid b/core/images/timestamp-on.tid
index 3a16df7eb..cad04aada 100644
--- a/core/images/timestamp-on.tid
+++ b/core/images/timestamp-on.tid
@@ -1,4 +1,5 @@
title: $:/core/images/timestamp-on
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-timestamp-on tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/tip.tid b/core/images/tip.tid
index f8109ada4..025918306 100755
--- a/core/images/tip.tid
+++ b/core/images/tip.tid
@@ -1,4 +1,5 @@
title: $:/core/images/tip
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-tip tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/transcludify.tid b/core/images/transcludify.tid
index 0579feb10..5102d1370 100644
--- a/core/images/transcludify.tid
+++ b/core/images/transcludify.tid
@@ -1,4 +1,5 @@
title: $:/core/images/transcludify
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-transcludify-button tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/twitter.tid b/core/images/twitter.tid
index 28cfccdbd..e3ae13dc1 100755
--- a/core/images/twitter.tid
+++ b/core/images/twitter.tid
@@ -1,4 +1,5 @@
title: $:/core/images/twitter
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-twitter tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/underline.tid b/core/images/underline.tid
index 768d2a199..341b27cfa 100755
--- a/core/images/underline.tid
+++ b/core/images/underline.tid
@@ -1,4 +1,5 @@
title: $:/core/images/underline
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-underline tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/unfold-all-button.tid b/core/images/unfold-all-button.tid
index e496bdd89..e44000b18 100755
--- a/core/images/unfold-all-button.tid
+++ b/core/images/unfold-all-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/unfold-all-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-unfold-all tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/unfold-button.tid b/core/images/unfold-button.tid
index cfad70570..57f53fd6f 100755
--- a/core/images/unfold-button.tid
+++ b/core/images/unfold-button.tid
@@ -1,4 +1,5 @@
title: $:/core/images/unfold-button
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-unfold tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/unlocked-padlock.tid b/core/images/unlocked-padlock.tid
index c5367c085..941f7bec5 100755
--- a/core/images/unlocked-padlock.tid
+++ b/core/images/unlocked-padlock.tid
@@ -1,4 +1,5 @@
title: $:/core/images/unlocked-padlock
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-unlocked-padlock tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/up-arrow.tid b/core/images/up-arrow.tid
index a1bd132bf..4d157352b 100755
--- a/core/images/up-arrow.tid
+++ b/core/images/up-arrow.tid
@@ -3,4 +3,5 @@ modified: 20150316000831867
tags: $:/tags/Image
title: $:/core/images/up-arrow
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-up-arrow tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/video.tid b/core/images/video.tid
index 3bf0bb259..b4fef628d 100755
--- a/core/images/video.tid
+++ b/core/images/video.tid
@@ -1,4 +1,5 @@
title: $:/core/images/video
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-video tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/images/warning.tid b/core/images/warning.tid
index 53bff59ec..1822da89f 100755
--- a/core/images/warning.tid
+++ b/core/images/warning.tid
@@ -1,4 +1,5 @@
title: $:/core/images/warning
tags: $:/tags/Image
-
\ No newline at end of file
+\parameters (size:"22pt")
+> height=<> class="tc-image-warning tc-image-button" viewBox="0 0 128 128">
\ No newline at end of file
diff --git a/core/language/en-GB/Buttons.multids b/core/language/en-GB/Buttons.multids
index 85a71ac08..15273d18e 100644
--- a/core/language/en-GB/Buttons.multids
+++ b/core/language/en-GB/Buttons.multids
@@ -28,6 +28,7 @@ Encryption/ClearPassword/Caption: clear password
Encryption/ClearPassword/Hint: Clear the password and save this wiki without encryption
Encryption/SetPassword/Caption: set password
Encryption/SetPassword/Hint: Set a password for saving this wiki with encryption
+EmergencyDownload/Caption: download tiddlers as json
ExportPage/Caption: export all
ExportPage/Hint: Export all tiddlers
ExportTiddler/Caption: export tiddler
@@ -67,6 +68,8 @@ More/Caption: more
More/Hint: More actions
NewHere/Caption: new here
NewHere/Hint: Create a new tiddler tagged with this one
+NetworkActivity/Caption: network activity
+NetworkActivity/Hint: Cancel all network activity
NewJournal/Caption: new journal
NewJournal/Hint: Create a new journal tiddler
NewJournalHere/Caption: new journal here
@@ -101,6 +104,8 @@ ShowSideBar/Caption: show sidebar
ShowSideBar/Hint: Show sidebar
TagManager/Caption: tag manager
TagManager/Hint: Open tag manager
+TestCaseImport/Caption: import tiddlers
+TestCaseImport/Hint: Import tiddlers
Timestamp/Caption: timestamps
Timestamp/Hint: Choose whether modifications update timestamps
Timestamp/On/Caption: timestamps are on
diff --git a/core/language/en-GB/ControlPanel.multids b/core/language/en-GB/ControlPanel.multids
index d8321edbf..93cfc3c10 100644
--- a/core/language/en-GB/ControlPanel.multids
+++ b/core/language/en-GB/ControlPanel.multids
@@ -206,6 +206,12 @@ Stylesheets/Caption: Stylesheets
Stylesheets/Expand/Caption: Expand All
Stylesheets/Hint: This is the rendered CSS of the current stylesheet tiddlers tagged with <>
Stylesheets/Restore/Caption: Restore
+TestCases/Caption: Test Cases
+TestCases/Hint: Test cases are self contained examples for testing and learning
+TestCases/All/Caption: All Test Cases
+TestCases/All/Hint: All Test Cases
+TestCases/Failed/Caption: Failed Test Cases
+TestCases/Failed/Hint: Only Failed Test Cases
Theme/Caption: Theme
Theme/Prompt: Current theme:
TiddlerFields/Caption: Tiddler Fields
diff --git a/core/language/en-GB/Docs/ModuleTypes.multids b/core/language/en-GB/Docs/ModuleTypes.multids
index 9a03d8887..5d5902c76 100644
--- a/core/language/en-GB/Docs/ModuleTypes.multids
+++ b/core/language/en-GB/Docs/ModuleTypes.multids
@@ -9,7 +9,7 @@ config: Data to be inserted into `$tw.config`.
filteroperator: Individual filter operator methods.
global: Global data to be inserted into `$tw`.
info: Publishes system information via the [[$:/temp/info-plugin]] pseudo-plugin.
-isfilteroperator: Operands for the ''is'' filter operator.
+isfilteroperator: Parameters for the ''is'' filter operator.
library: Generic module type for general purpose JavaScript modules.
macro: JavaScript macro definitions.
parser: Parsers for different content types.
diff --git a/core/language/en-GB/Docs/PaletteColours.multids b/core/language/en-GB/Docs/PaletteColours.multids
index 98addbf85..bc1b36c3d 100644
--- a/core/language/en-GB/Docs/PaletteColours.multids
+++ b/core/language/en-GB/Docs/PaletteColours.multids
@@ -65,6 +65,13 @@ sidebar-tab-foreground-selected: Sidebar tab foreground for selected tabs
sidebar-tab-foreground: Sidebar tab foreground
sidebar-tiddler-link-foreground-hover: Sidebar tiddler link foreground hover
sidebar-tiddler-link-foreground: Sidebar tiddler link foreground
+stability-stable: Badge for stability level "stable"
+stability-experimental: Badge for stability level "experimental"
+stability-deprecated: Badge for stability level "deprecated"
+stability-legacy: Badge for stability level "legacy"
+testcase-accent-level-1: Test case accent colour with no nesting
+testcase-accent-level-2: Test case accent colour with 2nd level nesting
+testcase-accent-level-3: Test case accent colour with 3rd level nesting or higher
site-title-foreground: Site title foreground
static-alert-foreground: Static alert foreground
tab-background-selected: Tab background for selected tabs
diff --git a/core/language/en-GB/Exporters.multids b/core/language/en-GB/Exporters.multids
index e455b8bf1..6ac52efe7 100644
--- a/core/language/en-GB/Exporters.multids
+++ b/core/language/en-GB/Exporters.multids
@@ -3,4 +3,4 @@ title: $:/language/Exporters/
StaticRiver: Static HTML
JsonFile: JSON file
CsvFile: CSV file
-TidFile: ".tid" file
+TidFile: TID text file
diff --git a/core/language/en-GB/Fields.multids b/core/language/en-GB/Fields.multids
index c094580d5..9830e96c1 100644
--- a/core/language/en-GB/Fields.multids
+++ b/core/language/en-GB/Fields.multids
@@ -1,11 +1,14 @@
title: $:/language/Docs/Fields/
_canonical_uri: The full URI of an external image tiddler
+author: Name of the author of a plugin
bag: The name of the bag from which a tiddler came
caption: The text to be displayed on a tab or button
+class: The CSS class applied to a tiddler when rendering it - see [[Custom styles by user-class]]. Also used for [[Modals]]
code-body: The view template will display the tiddler as code if set to ''yes''
color: The CSS color value associated with a tiddler
component: The name of the component responsible for an [[alert tiddler|AlertMechanism]]
+core-version: For a plugin, indicates what version of TiddlyWiki with which it is compatible
current-tiddler: Used to cache the top tiddler in a [[history list|HistoryMechanism]]
created: The date a tiddler was created
creator: The name of the person who created a tiddler
@@ -22,9 +25,12 @@ list-before: If set, the title of a tiddler before which this tiddler should be
list-after: If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles, or at the end of the list if this field is present but empty
modified: The date and time at which a tiddler was last modified
modifier: The tiddler title associated with the person who last modified a tiddler
+module-type: For javascript tiddlers, specifies what kind of module it is
name: The human readable name associated with a plugin tiddler
+parent-plugin: For a plugin, specifies which plugin of which it is a sub-plugin
plugin-priority: A numerical value indicating the priority of a plugin tiddler
plugin-type: The type of plugin in a plugin tiddler
+stability: The development status of a plugin: deprecated, experimental, stable, or legacy
revision: The revision of the tiddler held at the server
released: Date of a TiddlyWiki release
source: The source URL associated with a tiddler
diff --git a/core/language/en-GB/Help/commands.tid b/core/language/en-GB/Help/commands.tid
index 454159b44..7551885f0 100644
--- a/core/language/en-GB/Help/commands.tid
+++ b/core/language/en-GB/Help/commands.tid
@@ -10,7 +10,7 @@ Sequentially run the command tokens returned from a filter
Examples
```
---commands "[enlist{$:/build-commands-as-text}]"
+--commands "[enlist:raw{$:/build-commands-as-text}]"
```
```
diff --git a/core/language/en-GB/Help/listen.tid b/core/language/en-GB/Help/listen.tid
index 45df72381..d9f6a247f 100644
--- a/core/language/en-GB/Help/listen.tid
+++ b/core/language/en-GB/Help/listen.tid
@@ -18,7 +18,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
* ''anon-username'' - the username for signing edits for anonymous users
* ''username'' - optional username for basic authentication
* ''password'' - optional password for basic authentication
-* ''authenticated-user-header'' - optional name of header to be used for trusted authentication
+* ''authenticated-user-header'' - optional name of request header to be used for trusted authentication.
* ''readers'' - comma-separated list of principals allowed to read from this wiki
* ''writers'' - comma-separated list of principals allowed to write to this wiki
* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no")
diff --git a/core/language/en-GB/Help/savewikifolder.tid b/core/language/en-GB/Help/savewikifolder.tid
index bda1d19a3..82565f7bc 100644
--- a/core/language/en-GB/Help/savewikifolder.tid
+++ b/core/language/en-GB/Help/savewikifolder.tid
@@ -4,7 +4,7 @@ description: Saves a wiki to a new wiki folder
<<.from-version "5.1.20">> Saves the current wiki as a wiki folder, including tiddlers, plugins and configuration:
```
---savewikifolder []
+--savewikifolder [] [ [=] ]*
```
* The target wiki folder must be empty or non-existent
@@ -12,8 +12,23 @@ description: Saves a wiki to a new wiki folder
* Plugins from the official plugin library are replaced with references to those plugins in the `tiddlywiki.info` file
* Custom plugins are unpacked into their own folder
+The following options are supported:
+
+* ''filter'': a filter expression that defines the tiddlers to include in the output.
+* ''explodePlugins'': defaults to "yes"
+** ''yes'' will "explode" plugins into separate tiddler files and save them to the plugin directory within the wiki folder
+** ''no'' will suppress exploding plugins into their constituent tiddler files. It will save the plugin as a single JSON tiddler in the tiddlers folder
+
+Note that both ''explodePlugins'' options will produce wiki folders that build the exact same original wiki. The difference lies in how plugins are represented in the wiki folder.
+
A common usage is to convert a TiddlyWiki HTML file into a wiki folder:
```
tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder
```
+
+Save the plugin to the tiddlers directory of the target wiki folder:
+
+```
+tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder explodePlugins=no
+```
diff --git a/core/language/en-GB/Help/server.tid b/core/language/en-GB/Help/server.tid
index 78e9c8ab1..da6865031 100644
--- a/core/language/en-GB/Help/server.tid
+++ b/core/language/en-GB/Help/server.tid
@@ -1,5 +1,5 @@
title: $:/language/Help/server
-description: Provides an HTTP server interface to TiddlyWiki (deprecated in favour of the new listen command)
+description: (deprecated: see 'listen' command) Provides an HTTP server interface to TiddlyWiki
Legacy command to serve a wiki over HTTP.
diff --git a/core/language/en-GB/Misc.multids b/core/language/en-GB/Misc.multids
index 00cb3c99c..d8c091375 100644
--- a/core/language/en-GB/Misc.multids
+++ b/core/language/en-GB/Misc.multids
@@ -25,10 +25,12 @@ Encryption/RepeatPassword: Repeat password
Encryption/PasswordNoMatch: Passwords do not match
Encryption/SetPassword: Set password
Error/Caption: Error
+Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'deserialize' operator
+Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator
Error/Filter: Filter error
Error/FilterSyntax: Syntax error in filter expression
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
-Error/IsFilterOperator: Filter Error: Unknown operand for the 'is' filter operator
+Error/IsFilterOperator: Filter Error: Unknown parameter for the 'is' filter operator
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator
Error/LoadingPluginLibrary: Error loading plugin library
Error/NetworkErrorAlert: `''Network Error'' It looks like the connection to the server has been lost. This may indicate a problem with your network connection. Please attempt to restore network connectivity before continuing. ''Any unsaved changes will be automatically synchronised when connectivity is restored''.`
@@ -40,6 +42,7 @@ Error/RetrievingSkinny: Error retrieving skinny tiddler list
Error/SavingToTWEdit: Error saving to TWEdit
Error/WhileSaving: Error while saving
Error/XMLHttpRequest: XMLHttpRequest error code
+Error/ZoominTextNode: Story View Error: It appears you tried to interact with a tiddler that displays in a custom container. This is most likely caused by using `$:/tags/StoryTiddlerTemplateFilter` with a template that contains text or whitespace at the start. Please use the pragma `\whitespace trim` and ensure the whole contents of the tiddler is wrapped in a single HTML element. The text that caused this issue:
InternalJavaScriptError/Title: Internal JavaScript Error
InternalJavaScriptError/Hint: Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser
LayoutSwitcher/Description: Open the layout switcher
@@ -67,7 +70,7 @@ No: No
OfficialPluginLibrary: Official ~TiddlyWiki Plugin Library
OfficialPluginLibrary/Hint: The official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
PageTemplate/Description: the default ~TiddlyWiki layout
-PageTemplate/Name: Default ~PageTemplate
+PageTemplate/Name: Standard Layout
PluginReloadWarning: Please save {{$:/core/ui/Buttons/save-wiki}} and reload {{$:/core/ui/Buttons/refresh}} to allow changes to ~JavaScript plugins to take effect
RecentChanges/DateFormat: DDth MMM YYYY
Shortcuts/Input/AdvancedSearch/Hint: Open the ~AdvancedSearch panel from within the sidebar search field
diff --git a/core/language/en-GB/Modals/SaveInstructions.tid b/core/language/en-GB/Modals/SaveInstructions.tid
deleted file mode 100644
index 45028fc45..000000000
--- a/core/language/en-GB/Modals/SaveInstructions.tid
+++ /dev/null
@@ -1,21 +0,0 @@
-title: $:/language/Modals/SaveInstructions
-subtitle: Save your work
-footer: <$button message="tm-close-tiddler">Close$button>
-help: https://tiddlywiki.com/static/SavingChanges.html
-
-Your changes to this wiki need to be saved as a ~TiddlyWiki HTML file.
-
-!!! Desktop browsers
-
-# Select ''Save As'' from the ''File'' menu
-# Choose a filename and location
-#* Some browsers also require you to explicitly specify the file saving format as ''Webpage, HTML only'' or similar
-# Close this tab
-
-!!! Smartphone browsers
-
-# Create a bookmark to this page
-#* If you've got iCloud or Google Sync set up then the bookmark will automatically sync to your desktop where you can open it and save it as above
-# Close this tab
-
-//If you open the bookmark again in Mobile Safari you will see this message again. If you want to go ahead and use the file, just click the ''close'' button below//
diff --git a/core/language/en-GB/SiteTitle.tid b/core/language/en-GB/SiteTitle.tid
index 9f522664a..a32da7dfe 100644
--- a/core/language/en-GB/SiteTitle.tid
+++ b/core/language/en-GB/SiteTitle.tid
@@ -1,3 +1,3 @@
title: $:/SiteTitle
-My ~TiddlyWiki
\ No newline at end of file
+My TiddlyWiki
\ No newline at end of file
diff --git a/core/language/en-GB/Types/image_svg_xml.tid b/core/language/en-GB/Types/image_svg_xml.tid
index 9f7c23ba3..94c3ea949 100644
--- a/core/language/en-GB/Types/image_svg_xml.tid
+++ b/core/language/en-GB/Types/image_svg_xml.tid
@@ -1,5 +1,5 @@
title: $:/language/Docs/Types/image/svg+xml
-description: Structured Vector Graphics image
+description: SVG image
name: image/svg+xml
group: Image
group-sort: 1
diff --git a/core/language/en-GB/Types/image_x-icon.tid b/core/language/en-GB/Types/image_x-icon.tid
index 6ae32331c..55420387a 100644
--- a/core/language/en-GB/Types/image_x-icon.tid
+++ b/core/language/en-GB/Types/image_x-icon.tid
@@ -1,5 +1,5 @@
title: $:/language/Docs/Types/image/x-icon
-description: ICO format icon file
+description: ICO icon
name: image/x-icon
group: Image
group-sort: 1
diff --git a/core/modules/commands/listen.js b/core/modules/commands/listen.js
index 3c5f6a63a..ca6e6e076 100644
--- a/core/modules/commands/listen.js
+++ b/core/modules/commands/listen.js
@@ -18,7 +18,7 @@ exports.info = {
name: "listen",
synchronous: true,
namedParameterMode: true,
- mandatoryParameters: [],
+ mandatoryParameters: []
};
var Command = function(params,commander,callback) {
diff --git a/core/modules/commands/makelibrary.js b/core/modules/commands/makelibrary.js
index 36a1399a2..3af2e4943 100644
--- a/core/modules/commands/makelibrary.js
+++ b/core/modules/commands/makelibrary.js
@@ -27,33 +27,8 @@ var Command = function(params,commander,callback) {
Command.prototype.execute = function() {
var wiki = this.commander.wiki,
- fs = require("fs"),
- path = require("path"),
upgradeLibraryTitle = this.params[0] || UPGRADE_LIBRARY_TITLE,
- tiddlers = {};
- // Collect up the library plugins
- var collectPlugins = function(folder) {
- var pluginFolders = $tw.utils.getSubdirectories(folder) || [];
- for(var p=0; p []
+--savewikifolder [ [=] ]*
+
+The following options are supported:
+
+* ''filter'': a filter expression defining the tiddlers to be included in the output
+* ''explodePlugins'': set to "no" to suppress exploding plugins into their constituent shadow tiddlers (defaults to "yes")
+
+Supports backward compatibility with --savewikifolder [] [ [=] ]*
\*/
(function(){
@@ -35,14 +42,28 @@ Command.prototype.execute = function() {
if(this.params.length < 1) {
return "Missing wiki folder path";
}
- var wikifoldermaker = new WikiFolderMaker(this.params[0],this.params[1],this.commander);
+ var regFilter = /^[a-zA-Z0-9\.\-_]+=/g, // dynamic parameters
+ namedParames,
+ tiddlerFilter,
+ options = {};
+ if (regFilter.test(this.params[1])) {
+ namedParames = this.commander.extractNamedParameters(this.params.slice(1));
+ tiddlerFilter = namedParames.filter || "[all[tiddlers]]";
+ } else {
+ namedParames = this.commander.extractNamedParameters(this.params.slice(2));
+ tiddlerFilter = this.params[1];
+ }
+ tiddlerFilter = tiddlerFilter || "[all[tiddlers]]";
+ options.explodePlugins = namedParames.explodePlugins || "yes";
+ var wikifoldermaker = new WikiFolderMaker(this.params[0],tiddlerFilter,this.commander,options);
return wikifoldermaker.save();
};
-function WikiFolderMaker(wikiFolderPath,wikiFilter,commander) {
+function WikiFolderMaker(wikiFolderPath,wikiFilter,commander,options) {
this.wikiFolderPath = wikiFolderPath;
- this.wikiFilter = wikiFilter || "[all[tiddlers]]";
+ this.wikiFilter = wikiFilter;
this.commander = commander;
+ this.explodePlugins = options.explodePlugins;
this.wiki = commander.wiki;
this.savedPaths = []; // So that we can detect filename clashes
}
@@ -93,10 +114,13 @@ WikiFolderMaker.prototype.save = function() {
self.log("Adding built-in plugin: " + libraryDetails.name);
newWikiInfo[libraryDetails.type] = newWikiInfo[libraryDetails.type] || [];
$tw.utils.pushTop(newWikiInfo[libraryDetails.type],libraryDetails.name);
- } else {
+ } else if(self.explodePlugins !== "no") {
// A custom plugin
self.log("Processing custom plugin: " + title);
self.saveCustomPlugin(tiddler);
+ } else if(self.explodePlugins === "no") {
+ self.log("Processing custom plugin to tiddlders folder: " + title);
+ self.saveTiddler("tiddlers", tiddler);
}
} else {
// Ordinary tiddler
@@ -152,7 +176,10 @@ WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) {
this.saveJSONFile(directory + path.sep + "plugin.info",pluginInfo);
self.log("Writing " + directory + path.sep + "plugin.info: " + JSON.stringify(pluginInfo,null,$tw.config.preferences.jsonSpaces));
var pluginTiddlers = $tw.utils.parseJSONSafe(pluginTiddler.fields.text).tiddlers; // A hashmap of tiddlers in the plugin
- $tw.utils.each(pluginTiddlers,function(tiddler) {
+ $tw.utils.each(pluginTiddlers,function(tiddler,title) {
+ if(!tiddler.title) {
+ tiddler.title = title;
+ }
self.saveTiddler(directory,new $tw.Tiddler(tiddler));
});
};
diff --git a/core/modules/config.js b/core/modules/config.js
index 77e74edf8..399af598b 100644
--- a/core/modules/config.js
+++ b/core/modules/config.js
@@ -30,7 +30,7 @@ exports.textPrimitives.wikiLink = exports.textPrimitives.upperLetter + "+" +
exports.textPrimitives.upperLetter +
exports.textPrimitives.anyLetter + "*";
-exports.htmlEntities = {quot:34, amp:38, apos:39, lt:60, gt:62, nbsp:160, iexcl:161, cent:162, pound:163, curren:164, yen:165, brvbar:166, sect:167, uml:168, copy:169, ordf:170, laquo:171, not:172, shy:173, reg:174, macr:175, deg:176, plusmn:177, sup2:178, sup3:179, acute:180, micro:181, para:182, middot:183, cedil:184, sup1:185, ordm:186, raquo:187, frac14:188, frac12:189, frac34:190, iquest:191, Agrave:192, Aacute:193, Acirc:194, Atilde:195, Auml:196, Aring:197, AElig:198, Ccedil:199, Egrave:200, Eacute:201, Ecirc:202, Euml:203, Igrave:204, Iacute:205, Icirc:206, Iuml:207, ETH:208, Ntilde:209, Ograve:210, Oacute:211, Ocirc:212, Otilde:213, Ouml:214, times:215, Oslash:216, Ugrave:217, Uacute:218, Ucirc:219, Uuml:220, Yacute:221, THORN:222, szlig:223, agrave:224, aacute:225, acirc:226, atilde:227, auml:228, aring:229, aelig:230, ccedil:231, egrave:232, eacute:233, ecirc:234, euml:235, igrave:236, iacute:237, icirc:238, iuml:239, eth:240, ntilde:241, ograve:242, oacute:243, ocirc:244, otilde:245, ouml:246, divide:247, oslash:248, ugrave:249, uacute:250, ucirc:251, uuml:252, yacute:253, thorn:254, yuml:255, OElig:338, oelig:339, Scaron:352, scaron:353, Yuml:376, fnof:402, circ:710, tilde:732, Alpha:913, Beta:914, Gamma:915, Delta:916, Epsilon:917, Zeta:918, Eta:919, Theta:920, Iota:921, Kappa:922, Lambda:923, Mu:924, Nu:925, Xi:926, Omicron:927, Pi:928, Rho:929, Sigma:931, Tau:932, Upsilon:933, Phi:934, Chi:935, Psi:936, Omega:937, alpha:945, beta:946, gamma:947, delta:948, epsilon:949, zeta:950, eta:951, theta:952, iota:953, kappa:954, lambda:955, mu:956, nu:957, xi:958, omicron:959, pi:960, rho:961, sigmaf:962, sigma:963, tau:964, upsilon:965, phi:966, chi:967, psi:968, omega:969, thetasym:977, upsih:978, piv:982, ensp:8194, emsp:8195, thinsp:8201, zwnj:8204, zwj:8205, lrm:8206, rlm:8207, ndash:8211, mdash:8212, lsquo:8216, rsquo:8217, sbquo:8218, ldquo:8220, rdquo:8221, bdquo:8222, dagger:8224, Dagger:8225, bull:8226, hellip:8230, permil:8240, prime:8242, Prime:8243, lsaquo:8249, rsaquo:8250, oline:8254, frasl:8260, euro:8364, image:8465, weierp:8472, real:8476, trade:8482, alefsym:8501, larr:8592, uarr:8593, rarr:8594, darr:8595, harr:8596, crarr:8629, lArr:8656, uArr:8657, rArr:8658, dArr:8659, hArr:8660, forall:8704, part:8706, exist:8707, empty:8709, nabla:8711, isin:8712, notin:8713, ni:8715, prod:8719, sum:8721, minus:8722, lowast:8727, radic:8730, prop:8733, infin:8734, ang:8736, and:8743, or:8744, cap:8745, cup:8746, int:8747, there4:8756, sim:8764, cong:8773, asymp:8776, ne:8800, equiv:8801, le:8804, ge:8805, sub:8834, sup:8835, nsub:8836, sube:8838, supe:8839, oplus:8853, otimes:8855, perp:8869, sdot:8901, lceil:8968, rceil:8969, lfloor:8970, rfloor:8971, lang:9001, rang:9002, loz:9674, spades:9824, clubs:9827, hearts:9829, diams:9830 };
+exports.htmlEntities = {quot:34, dollar:36, amp:38, apos:39, lt:60, gt:62, nbsp:160, iexcl:161, cent:162, pound:163, curren:164, yen:165, brvbar:166, sect:167, uml:168, copy:169, ordf:170, laquo:171, not:172, shy:173, reg:174, macr:175, deg:176, plusmn:177, sup2:178, sup3:179, acute:180, micro:181, para:182, middot:183, cedil:184, sup1:185, ordm:186, raquo:187, frac14:188, frac12:189, frac34:190, iquest:191, Agrave:192, Aacute:193, Acirc:194, Atilde:195, Auml:196, Aring:197, AElig:198, Ccedil:199, Egrave:200, Eacute:201, Ecirc:202, Euml:203, Igrave:204, Iacute:205, Icirc:206, Iuml:207, ETH:208, Ntilde:209, Ograve:210, Oacute:211, Ocirc:212, Otilde:213, Ouml:214, times:215, Oslash:216, Ugrave:217, Uacute:218, Ucirc:219, Uuml:220, Yacute:221, THORN:222, szlig:223, agrave:224, aacute:225, acirc:226, atilde:227, auml:228, aring:229, aelig:230, ccedil:231, egrave:232, eacute:233, ecirc:234, euml:235, igrave:236, iacute:237, icirc:238, iuml:239, eth:240, ntilde:241, ograve:242, oacute:243, ocirc:244, otilde:245, ouml:246, divide:247, oslash:248, ugrave:249, uacute:250, ucirc:251, uuml:252, yacute:253, thorn:254, yuml:255, OElig:338, oelig:339, Scaron:352, scaron:353, Yuml:376, fnof:402, circ:710, tilde:732, Alpha:913, Beta:914, Gamma:915, Delta:916, Epsilon:917, Zeta:918, Eta:919, Theta:920, Iota:921, Kappa:922, Lambda:923, Mu:924, Nu:925, Xi:926, Omicron:927, Pi:928, Rho:929, Sigma:931, Tau:932, Upsilon:933, Phi:934, Chi:935, Psi:936, Omega:937, alpha:945, beta:946, gamma:947, delta:948, epsilon:949, zeta:950, eta:951, theta:952, iota:953, kappa:954, lambda:955, mu:956, nu:957, xi:958, omicron:959, pi:960, rho:961, sigmaf:962, sigma:963, tau:964, upsilon:965, phi:966, chi:967, psi:968, omega:969, thetasym:977, upsih:978, piv:982, ensp:8194, emsp:8195, thinsp:8201, zwnj:8204, zwj:8205, lrm:8206, rlm:8207, ndash:8211, mdash:8212, lsquo:8216, rsquo:8217, sbquo:8218, ldquo:8220, rdquo:8221, bdquo:8222, dagger:8224, Dagger:8225, bull:8226, hellip:8230, permil:8240, prime:8242, Prime:8243, lsaquo:8249, rsaquo:8250, oline:8254, frasl:8260, euro:8364, image:8465, weierp:8472, real:8476, trade:8482, alefsym:8501, larr:8592, uarr:8593, rarr:8594, darr:8595, harr:8596, crarr:8629, lArr:8656, uArr:8657, rArr:8658, dArr:8659, hArr:8660, forall:8704, part:8706, exist:8707, empty:8709, nabla:8711, isin:8712, notin:8713, ni:8715, prod:8719, sum:8721, minus:8722, lowast:8727, radic:8730, prop:8733, infin:8734, ang:8736, and:8743, or:8744, cap:8745, cup:8746, int:8747, there4:8756, sim:8764, cong:8773, asymp:8776, ne:8800, equiv:8801, le:8804, ge:8805, sub:8834, sup:8835, nsub:8836, sube:8838, supe:8839, oplus:8853, otimes:8855, perp:8869, sdot:8901, lceil:8968, rceil:8969, lfloor:8970, rfloor:8971, lang:9001, rang:9002, loz:9674, spades:9824, clubs:9827, hearts:9829, diams:9830 };
exports.htmlVoidElements = "area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",");
diff --git a/core/modules/editor/engines/framed.js b/core/modules/editor/engines/framed.js
index 10ab90b39..34d11dec7 100644
--- a/core/modules/editor/engines/framed.js
+++ b/core/modules/editor/engines/framed.js
@@ -60,7 +60,7 @@ function FramedEngine(options) {
this.domNode.value = this.value;
}
// Set the attributes
- if(this.widget.editType) {
+ if(this.widget.editType && this.widget.editTag !== "textarea") {
this.domNode.setAttribute("type",this.widget.editType);
}
if(this.widget.editPlaceholder) {
@@ -118,6 +118,8 @@ FramedEngine.prototype.copyStyles = function() {
this.domNode.style.margin = "0";
// In Chrome setting -webkit-text-fill-color overrides the placeholder text colour
this.domNode.style["-webkit-text-fill-color"] = "currentcolor";
+ // Ensure we don't force text direction to LTR
+ this.domNode.style.removeProperty("direction");
};
/*
@@ -162,13 +164,13 @@ FramedEngine.prototype.fixHeight = function() {
if(this.widget.editAutoHeight) {
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
- this.iframeNode.style.height = (newHeight + 14) + "px"; // +14 for the border on the textarea
+ this.iframeNode.style.height = newHeight + "px";
}
} else {
var fixedHeight = parseInt(this.widget.wiki.getTiddlerText(HEIGHT_VALUE_TITLE,"400px"),10);
fixedHeight = Math.max(fixedHeight,20);
this.domNode.style.height = fixedHeight + "px";
- this.iframeNode.style.height = (fixedHeight + 14) + "px";
+ this.iframeNode.style.height = fixedHeight + "px";
}
}
};
@@ -177,9 +179,11 @@ FramedEngine.prototype.fixHeight = function() {
Focus the engine node
*/
FramedEngine.prototype.focus = function() {
- if(this.domNode.focus && this.domNode.select) {
+ if(this.domNode.focus) {
this.domNode.focus();
- this.domNode.select();
+ }
+ if(this.domNode.select) {
+ $tw.utils.setSelectionByPosition(this.domNode,this.widget.editFocusSelectFromStart,this.widget.editFocusSelectFromEnd);
}
};
diff --git a/core/modules/editor/engines/simple.js b/core/modules/editor/engines/simple.js
index e8be19a75..809dc58ea 100644
--- a/core/modules/editor/engines/simple.js
+++ b/core/modules/editor/engines/simple.js
@@ -34,7 +34,7 @@ function SimpleEngine(options) {
this.domNode.value = this.value;
}
// Set the attributes
- if(this.widget.editType) {
+ if(this.widget.editType && this.widget.editTag !== "textarea") {
this.domNode.setAttribute("type",this.widget.editType);
}
if(this.widget.editPlaceholder) {
@@ -119,10 +119,12 @@ SimpleEngine.prototype.fixHeight = function() {
/*
Focus the engine node
*/
-SimpleEngine.prototype.focus = function() {
- if(this.domNode.focus && this.domNode.select) {
+SimpleEngine.prototype.focus = function() {
+ if(this.domNode.focus) {
this.domNode.focus();
- this.domNode.select();
+ }
+ if(this.domNode.select) {
+ $tw.utils.setSelectionByPosition(this.domNode,this.widget.editFocusSelectFromStart,this.widget.editFocusSelectFromEnd);
}
};
diff --git a/core/modules/editor/factory.js b/core/modules/editor/factory.js
index 90ab66ae9..6157ec67f 100644
--- a/core/modules/editor/factory.js
+++ b/core/modules/editor/factory.js
@@ -180,6 +180,8 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
this.editMinHeight = this.getAttribute("minHeight",DEFAULT_MIN_TEXT_AREA_HEIGHT);
this.editFocusPopup = this.getAttribute("focusPopup");
this.editFocus = this.getAttribute("focus");
+ this.editFocusSelectFromStart = $tw.utils.parseNumber(this.getAttribute("focusSelectFromStart","0"));
+ this.editFocusSelectFromEnd = $tw.utils.parseNumber(this.getAttribute("focusSelectFromEnd","0"));
this.editTabIndex = this.getAttribute("tabindex");
this.editCancelPopups = this.getAttribute("cancelPopups","") === "yes";
this.editInputActions = this.getAttribute("inputActions");
@@ -218,7 +220,7 @@ 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["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] || 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 (changedTiddlers[this.editRefreshTitle]) {
diff --git a/core/modules/filterrunprefixes/cascade.js b/core/modules/filterrunprefixes/cascade.js
index da6894d21..486e75f45 100644
--- a/core/modules/filterrunprefixes/cascade.js
+++ b/core/modules/filterrunprefixes/cascade.js
@@ -25,20 +25,10 @@ exports.cascade = function(operationSubFunction,options) {
if(!filterFnList[index]) {
filterFnList[index] = options.wiki.compileFilter(filter);
}
- var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),{
- getVariable: function(name,opts) {
- opts = opts || {};
- opts.variables = {
- "currentTiddler": "" + title,
- "..currentTiddler": widget.getVariable("currentTiddler")
- };
- if(name in opts.variables) {
- return opts.variables[name];
- } else {
- return widget.getVariable(name,opts);
- }
- }
- });
+ var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": widget.getVariable("currentTiddler","")
+ }));
if(output.length !== 0) {
result = output[0];
return false;
diff --git a/core/modules/filterrunprefixes/filter.js b/core/modules/filterrunprefixes/filter.js
index 783b699c2..4ab057109 100644
--- a/core/modules/filterrunprefixes/filter.js
+++ b/core/modules/filterrunprefixes/filter.js
@@ -19,23 +19,13 @@ exports.filter = function(operationSubFunction,options) {
var resultsToRemove = [],
index = 0;
results.each(function(title) {
- var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
- getVariable: function(name,opts) {
- opts = opts || {};
- opts.variables = {
- "currentTiddler": "" + title,
- "..currentTiddler": widget.getVariable("currentTiddler"),
- "index": "" + index,
- "revIndex": "" + (results.length - 1 - index),
- "length": "" + results.length
- };
- if(name in opts.variables) {
- return opts.variables[name];
- } else {
- return widget.getVariable(name,opts);
- }
- }
- });
+ var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": widget.getVariable("currentTiddler",""),
+ "index": "" + index,
+ "revIndex": "" + (results.length - 1 - index),
+ "length": "" + results.length
+ }));
if(filtered.length === 0) {
resultsToRemove.push(title);
}
diff --git a/core/modules/filterrunprefixes/map.js b/core/modules/filterrunprefixes/map.js
index efcb5b534..b756d6699 100644
--- a/core/modules/filterrunprefixes/map.js
+++ b/core/modules/filterrunprefixes/map.js
@@ -21,23 +21,13 @@ exports.map = function(operationSubFunction,options) {
flatten = (suffixes[0] && suffixes[0][0] === "flat") ? true : false;
results.clear();
$tw.utils.each(inputTitles,function(title) {
- var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
- getVariable: function(name,opts) {
- opts = opts || {};
- opts.variables = {
- "currentTiddler": "" + title,
- "..currentTiddler": widget.getVariable("currentTiddler"),
- "index": "" + index,
- "revIndex": "" + (inputTitles.length - 1 - index),
- "length": "" + inputTitles.length
- };
- if(name in opts.variables) {
- return opts.variables[name];
- } else {
- return widget.getVariable(name,opts);
- }
- }
- });
+ var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": widget.getVariable("currentTiddler",""),
+ "index": "" + index,
+ "revIndex": "" + (inputTitles.length - 1 - index),
+ "length": "" + inputTitles.length
+ }));
if(filtered.length && flatten) {
$tw.utils.each(filtered,function(value) {
results.push(value);
diff --git a/core/modules/filterrunprefixes/reduce.js b/core/modules/filterrunprefixes/reduce.js
index 8fe819e3f..ee2998837 100644
--- a/core/modules/filterrunprefixes/reduce.js
+++ b/core/modules/filterrunprefixes/reduce.js
@@ -18,24 +18,14 @@ exports.reduce = function(operationSubFunction,options) {
var accumulator = "",
index = 0;
results.each(function(title) {
- var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
- getVariable: function(name,opts) {
- opts = opts || {};
- opts.variables = {
- "currentTiddler": "" + title,
- "..currentTiddler": widget.getVariable("currentTiddler"),
- "index": "" + index,
- "revIndex": "" + (results.length - 1 - index),
- "length": "" + results.length,
- "accumulator": "" + accumulator
- };
- if(name in opts.variables) {
- return opts.variables[name];
- } else {
- return widget.getVariable(name,opts);
- }
- }
- });
+ var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": widget.getVariable("currentTiddler"),
+ "index": "" + index,
+ "revIndex": "" + (results.length - 1 - index),
+ "length": "" + results.length,
+ "accumulator": "" + accumulator
+ }));
if(list.length > 0) {
accumulator = "" + list[0];
}
diff --git a/core/modules/filterrunprefixes/sort.js b/core/modules/filterrunprefixes/sort.js
index 6865b175c..d8d376126 100644
--- a/core/modules/filterrunprefixes/sort.js
+++ b/core/modules/filterrunprefixes/sort.js
@@ -25,20 +25,10 @@ exports.sort = function(operationSubFunction,options) {
indexes = new Array(inputTitles.length),
compareFn;
results.each(function(title) {
- var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
- getVariable: function(name,opts) {
- opts = opts || {};
- opts.variables = {
- "currentTiddler": "" + title,
- "..currentTiddler": widget.getVariable("currentTiddler")
- };
- if(name in opts.variables) {
- return opts.variables[name];
- } else {
- return widget.getVariable(name,opts);
- }
- }
- });
+ var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": widget.getVariable("currentTiddler")
+ }));
sortKeys.push(key[0] || "");
});
results.clear();
diff --git a/core/modules/filterrunprefixes/then.js b/core/modules/filterrunprefixes/then.js
new file mode 100644
index 000000000..9f6d5c76a
--- /dev/null
+++ b/core/modules/filterrunprefixes/then.js
@@ -0,0 +1,32 @@
+/*\
+title: $:/core/modules/filterrunprefixes/then.js
+type: application/javascript
+module-type: filterrunprefix
+
+Replace results of previous runs unless empty
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+/*
+Export our filter prefix function
+*/
+exports.then = function(operationSubFunction) {
+ return function(results,source,widget) {
+ if(results.length !== 0) {
+ // Only run if previous run(s) produced results
+ var thisRunResult = operationSubFunction(source,widget);
+ if(thisRunResult.length !== 0) {
+ // Replace results only if this run actually produces a result
+ results.clear();
+ results.pushTop(thisRunResult);
+ }
+ }
+ };
+};
+
+})();
diff --git a/core/modules/filters.js b/core/modules/filters.js
index 1bb5fe9ff..aa82a352a 100644
--- a/core/modules/filters.js
+++ b/core/modules/filters.js
@@ -12,6 +12,8 @@ Adds tiddler filtering methods to the $tw.Wiki object.
/*global $tw: false */
"use strict";
+var widgetClass = require("$:/core/modules/widgets/widget.js").widget;
+
/* Maximum permitted filter recursion depth */
var MAX_FILTER_DEPTH = 300;
@@ -255,19 +257,21 @@ exports.compileFilter = function(filterString) {
var operands = [],
operatorFunction;
if(!operator.operator) {
+ // Use the "title" operator if no operator is specified
operatorFunction = filterOperators.title;
} else if(!filterOperators[operator.operator]) {
- operatorFunction = filterOperators.field;
+ // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
+ operatorFunction = filterOperators["[unknown]"];
} else {
+ // Use the operator function
operatorFunction = filterOperators[operator.operator];
}
-
$tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
- operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""});
+ operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
} else {
operand.value = operand.text;
}
diff --git a/core/modules/filters/all.js b/core/modules/filters/all.js
index a36749e92..3554a74b3 100644
--- a/core/modules/filters/all.js
+++ b/core/modules/filters/all.js
@@ -28,12 +28,8 @@ function getAllFilterOperators() {
Export our filter function
*/
exports.all = function(source,operator,options) {
- // Get our suboperators
- var allFilterOperators = getAllFilterOperators();
- // Cycle through the suboperators accumulating their results
- var results = new $tw.utils.LinkedList(),
- subops = operator.operand.split("+");
// Check for common optimisations
+ var subops = operator.operand.split("+");
if(subops.length === 1 && subops[0] === "") {
return source;
} else if(subops.length === 1 && subops[0] === "tiddlers") {
@@ -46,6 +42,10 @@ exports.all = function(source,operator,options) {
return options.wiki.eachShadowPlusTiddlers;
}
// Do it the hard way
+ // Get our suboperators
+ var allFilterOperators = getAllFilterOperators();
+ // Cycle through the suboperators accumulating their results
+ var results = new $tw.utils.LinkedList();
for(var t=0; t 0) === target) {
results.push(title);
}
diff --git a/core/modules/filters/format/timestamp.js b/core/modules/filters/format/timestamp.js
new file mode 100644
index 000000000..d6f5afe30
--- /dev/null
+++ b/core/modules/filters/format/timestamp.js
@@ -0,0 +1,25 @@
+/*\
+title: $:/core/modules/filters/format/timestamp.js
+type: application/javascript
+module-type: formatfilteroperator
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+/*
+Export our filter function
+*/
+exports.timestamp = function(source,operand,options) {
+ var results = [];
+ source(function(tiddler,title) {
+ if (title.match(/^-?\d+$/)) {
+ var value = new Date(Number(title));
+ results.push($tw.utils.formatDateString(value,operand || "[UTC]YYYY0MM0DD0hh0mm0ss0XXX"));
+ }
+ });
+ return results;
+};
+})();
\ No newline at end of file
diff --git a/core/modules/filters/function.js b/core/modules/filters/function.js
new file mode 100644
index 000000000..79210fb78
--- /dev/null
+++ b/core/modules/filters/function.js
@@ -0,0 +1,36 @@
+/*\
+title: $:/core/modules/filters/function.js
+type: application/javascript
+module-type: filteroperator
+
+Filter operator returning those input titles that are returned from a function
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+/*
+Export our filter function
+*/
+exports.function = function(source,operator,options) {
+ var functionName = operator.operands[0],
+ params = [];
+ $tw.utils.each(operator.operands.slice(1),function(param) {
+ params.push({value: param});
+ });
+ var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
+ if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
+ return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
+ }
+ // Return the input list if the function wasn't found
+ var results = [];
+ source(function(tiddler,title) {
+ results.push(title);
+ });
+ return results;
+};
+
+})();
diff --git a/core/modules/filters/json-ops.js b/core/modules/filters/json-ops.js
index 2be9ec754..0c58964eb 100644
--- a/core/modules/filters/json-ops.js
+++ b/core/modules/filters/json-ops.js
@@ -68,6 +68,54 @@ exports["jsontype"] = function(source,operator,options) {
return results;
};
+exports["jsonset"] = function(source,operator,options) {
+ var suffixes = operator.suffixes || [],
+ type = suffixes[0] && suffixes[0][0],
+ indexes = operator.operands.slice(0,-1),
+ value = operator.operands[operator.operands.length - 1],
+ results = [];
+ if(operator.operands.length === 1 && operator.operands[0] === "") {
+ value = undefined; // Prevents the value from being assigned
+ }
+ switch(type) {
+ case "string":
+ // Use value unchanged
+ break;
+ case "boolean":
+ value = (value === "true" ? true : (value === "false" ? false : undefined));
+ break;
+ case "number":
+ value = $tw.utils.parseNumber(value);
+ break;
+ case "array":
+ indexes = operator.operands;
+ value = [];
+ break;
+ case "object":
+ indexes = operator.operands;
+ value = {};
+ break;
+ case "null":
+ indexes = operator.operands;
+ value = null;
+ break;
+ case "json":
+ value = $tw.utils.parseJSONSafe(value,function() {return undefined;});
+ break;
+ default:
+ // Use value unchanged
+ break;
+ }
+ source(function(tiddler,title) {
+ var data = $tw.utils.parseJSONSafe(title,title);
+ if(data) {
+ data = setDataItem(data,indexes,value);
+ results.push(JSON.stringify(data));
+ }
+ });
+ return results;
+};
+
/*
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
@@ -165,6 +213,18 @@ function getDataItemType(data,indexes) {
}
}
+function getItemAtIndex(item,index) {
+ if($tw.utils.hop(item,index)) {
+ return item[index];
+ } else if($tw.utils.isArray(item)) {
+ index = $tw.utils.parseInt(index);
+ if(index < 0) { index = index + item.length };
+ return item[index]; // Will be undefined if index was out-of-bounds
+ } else {
+ return undefined;
+ }
+}
+
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
@@ -177,7 +237,7 @@ function getDataItem(data,indexes) {
for(var i=0; i 0) {
accumulator = "" + list[0];
}
diff --git a/core/modules/filters/sortsub.js b/core/modules/filters/sortsub.js
index e9f676daa..d328be09c 100644
--- a/core/modules/filters/sortsub.js
+++ b/core/modules/filters/sortsub.js
@@ -25,19 +25,10 @@ exports.sortsub = function(source,operator,options) {
inputTitles.push(title);
var r = filterFn.call(options.wiki,function(iterator) {
iterator(options.wiki.getTiddler(title),title);
- },{
- getVariable: function(name,opts) {
- opts = opts || {};
- switch(name) {
- case "currentTiddler":
- return "" + title;
- case "..currentTiddler":
- return options.widget.getVariable("currentTiddler");
- default:
- return options.widget.getVariable(name,opts);
- }
- }
- });
+ },options.widget.makeFakeWidgetWithVariables({
+ "currentTiddler": "" + title,
+ "..currentTiddler": options.widget.getVariable("currentTiddler")
+ }));
sortKeys.push(r[0] || "");
});
// Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays
diff --git a/core/modules/filters/strings.js b/core/modules/filters/strings.js
index 60a36265d..11f7634b7 100644
--- a/core/modules/filters/strings.js
+++ b/core/modules/filters/strings.js
@@ -74,6 +74,113 @@ exports.join = makeStringReducingOperator(
},null
);
+var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
+
+exports.levenshtein = makeStringBinaryOperator(
+ function(a,b) {
+ var dmpObject = new dmp.diff_match_patch(),
+ diffs = dmpObject.diff_main(a,b);
+ return [dmpObject.diff_levenshtein(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 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 = 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 dmpObject = new dmp.diff_match_patch(),
+ suffix = operator.suffix || "",
+ result = [];
+
+ 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) {
+ var dmpObject = new dmp.diff_match_patch(),
+ patches;
+ try {
+ patches = dmpObject.patch_fromText(b);
+ } catch(e) {
+ }
+ if(patches) {
+ return [dmpObject.patch_apply(patches,a)[0]];
+ } else {
+ return [a];
+ }
+ }
+);
+
function makeStringBinaryOperator(fnCalc) {
return function(source,operator,options) {
var result = [];
@@ -110,7 +217,10 @@ exports.splitregexp = function(source,operator,options) {
return ["RegExp error: " + ex];
}
source(function(tiddler,title) {
- Array.prototype.push.apply(result,title.split(regExp));
+ var parts = title.split(regExp).map(function(part){
+ return part || ""; // make sure it's a string
+ });
+ Array.prototype.push.apply(result,parts);
});
return result;
};
@@ -157,7 +267,7 @@ exports.pad = function(source,operator,options) {
} else {
var padString = "",
padStringLength = targetLength - title.length;
- while (padStringLength > padString.length) {
+ while(padStringLength > padString.length) {
padString += fill;
}
//make sure we do not exceed the specified length
@@ -184,4 +294,4 @@ exports.charcode = function(source,operator,options) {
return [chars.join("")];
};
-})();
+})();
\ No newline at end of file
diff --git a/core/modules/filters/substitute.js b/core/modules/filters/substitute.js
new file mode 100644
index 000000000..655ef7321
--- /dev/null
+++ b/core/modules/filters/substitute.js
@@ -0,0 +1,36 @@
+/*\
+title: $:/core/modules/filters/substitute.js
+type: application/javascript
+module-type: filteroperator
+
+Filter operator for substituting variables and embedded filter expressions with their corresponding values
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+/*
+Export our filter function
+*/
+exports.substitute = function(source,operator,options) {
+ var results = [],
+ operands = [];
+ $tw.utils.each(operator.operands,function(operand,index){
+ operands.push({
+ name: (index + 1).toString(),
+ value: operand
+ });
+ });
+ source(function(tiddler,title) {
+ if(title) {
+ results.push(options.wiki.getSubstitutedText(title,options.widget,{substitutions:operands}));
+ }
+ });
+ return results;
+};
+
+})();
+
\ No newline at end of file
diff --git a/core/modules/filters/transcludes.js b/core/modules/filters/transcludes.js
new file mode 100644
index 000000000..8f42b3bae
--- /dev/null
+++ b/core/modules/filters/transcludes.js
@@ -0,0 +1,26 @@
+/*\
+title: $:/core/modules/filters/transcludes.js
+type: application/javascript
+module-type: filteroperator
+
+Filter operator for returning all the transcludes from a tiddler
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+/*
+Export our filter function
+*/
+exports.transcludes = function(source,operator,options) {
+ var results = new $tw.utils.LinkedList();
+ source(function(tiddler,title) {
+ results.pushTop(options.wiki.getTiddlerTranscludes(title));
+ });
+ return results.makeTiddlerIterator(options.wiki);
+};
+
+})();
diff --git a/core/modules/filters/unknown.js b/core/modules/filters/unknown.js
new file mode 100644
index 000000000..6ae5baaf0
--- /dev/null
+++ b/core/modules/filters/unknown.js
@@ -0,0 +1,49 @@
+/*\
+title: $:/core/modules/filters/unknown.js
+type: application/javascript
+module-type: filteroperator
+
+Filter operator for handling unknown filter operators.
+
+Not intended to be used directly by end users, hence the square brackets around the name.
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var fieldFilterOperatorFn = require("$:/core/modules/filters/field.js").field;
+
+/*
+Export our filter function
+*/
+exports["[unknown]"] = function(source,operator,options) {
+ // Check for a user defined filter operator
+ if(operator.operator.indexOf(".") !== -1) {
+ var params = [];
+ $tw.utils.each(operator.operands,function(param) {
+ params.push({value: param});
+ });
+ var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
+ if(variableInfo && variableInfo.srcVariable) {
+ var list = variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
+ if(operator.prefix === "!") {
+ var results = [];
+ source(function(tiddler,title) {
+ if(list.indexOf(title) === -1) {
+ results.push(title);
+ }
+ });
+ return results;
+ } else {
+ return list;
+ }
+ }
+ }
+ // Otherwise, use the "field" operator
+ return fieldFilterOperatorFn(source,operator,options);
+};
+
+})();
diff --git a/core/modules/filters/x-listops.js b/core/modules/filters/x-listops.js
index 760f581a1..ae17297a5 100644
--- a/core/modules/filters/x-listops.js
+++ b/core/modules/filters/x-listops.js
@@ -202,7 +202,7 @@ Extended filter operators to manipulate the current list.
}
if(resultsIndex !== -1) {
i = i + step;
- nextOperandIndex = (i < opLength ? i : i - opLength);
+ nextOperandIndex = (i < opLength ? i : i % opLength);
if(operands.length > 1) {
results.splice(resultsIndex,1,operands[nextOperandIndex]);
} else {
diff --git a/core/modules/indexers/back-indexer.js b/core/modules/indexers/back-indexer.js
new file mode 100644
index 000000000..77b51b819
--- /dev/null
+++ b/core/modules/indexers/back-indexer.js
@@ -0,0 +1,122 @@
+/*\
+title: $:/core/modules/indexers/back-indexer.js
+type: application/javascript
+module-type: indexer
+
+By parsing the tiddler text, indexes the tiddlers' back links, back transclusions, block level back links.
+
+\*/
+function BackIndexer(wiki) {
+ this.wiki = wiki;
+}
+
+BackIndexer.prototype.init = function() {
+ this.subIndexers = {
+ link: new BackSubIndexer(this,"extractLinks"),
+ transclude: new BackSubIndexer(this,"extractTranscludes"),
+ };
+};
+
+BackIndexer.prototype.rebuild = function() {
+ $tw.utils.each(this.subIndexers,function(subIndexer) {
+ subIndexer.rebuild();
+ });
+};
+
+BackIndexer.prototype.update = function(updateDescriptor) {
+ $tw.utils.each(this.subIndexers,function(subIndexer) {
+ subIndexer.update(updateDescriptor);
+ });
+};
+function BackSubIndexer(indexer,extractor) {
+ this.wiki = indexer.wiki;
+ this.indexer = indexer;
+ this.extractor = extractor;
+ /**
+ * {
+ * [target title, e.g. tiddler title being linked to]:
+ * {
+ * [source title, e.g. tiddler title that has link syntax in its text]: true
+ * }
+ * }
+ */
+ this.index = null;
+}
+
+BackSubIndexer.prototype.init = function() {
+ // lazy init until first lookup
+ this.index = null;
+}
+
+BackSubIndexer.prototype._init = function() {
+ this.index = Object.create(null);
+ var self = this;
+ this.wiki.forEachTiddler(function(sourceTitle,tiddler) {
+ var newTargets = self._getTarget(tiddler);
+ $tw.utils.each(newTargets, function(target) {
+ if(!self.index[target]) {
+ self.index[target] = Object.create(null);
+ }
+ self.index[target][sourceTitle] = true;
+ });
+ });
+}
+
+BackSubIndexer.prototype.rebuild = function() {
+ this.index = null;
+}
+
+/*
+* Get things that is being referenced in the text, e.g. tiddler names in the link syntax.
+*/
+BackSubIndexer.prototype._getTarget = function(tiddler) {
+ if(this.wiki.isBinaryTiddler(tiddler.fields.text)) {
+ return [];
+ }
+ var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {});
+ if(parser) {
+ return this.wiki[this.extractor](parser.tree, tiddler.fields.title);
+ }
+ return [];
+}
+
+BackSubIndexer.prototype.update = function(updateDescriptor) {
+ // lazy init/update until first lookup
+ if(!this.index) {
+ return;
+ }
+ var newTargets = [],
+ oldTargets = [],
+ self = this;
+ if(updateDescriptor.old.exists) {
+ oldTargets = this._getTarget(updateDescriptor.old.tiddler);
+ }
+ if(updateDescriptor.new.exists) {
+ newTargets = this._getTarget(updateDescriptor.new.tiddler);
+ }
+
+ $tw.utils.each(oldTargets,function(target) {
+ if(self.index[target]) {
+ delete self.index[target][updateDescriptor.old.tiddler.fields.title];
+ }
+ });
+ $tw.utils.each(newTargets,function(target) {
+ if(!self.index[target]) {
+ self.index[target] = Object.create(null);
+ }
+ self.index[target][updateDescriptor.new.tiddler.fields.title] = true;
+ });
+}
+
+BackSubIndexer.prototype.lookup = function(title) {
+ if(!this.index) {
+ this._init();
+ }
+ if(this.index[title]) {
+ return Object.keys(this.index[title]);
+ } else {
+ return [];
+ }
+}
+
+exports.BackIndexer = BackIndexer;
diff --git a/core/modules/indexers/backlinks-index.js b/core/modules/indexers/backlinks-index.js
deleted file mode 100644
index 5902e2829..000000000
--- a/core/modules/indexers/backlinks-index.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*\
-title: $:/core/modules/indexers/backlinks-indexer.js
-type: application/javascript
-module-type: indexer
-
-Indexes the tiddlers' backlinks
-
-\*/
-(function(){
-
-/*jslint node: true, browser: true */
-/*global modules: false */
-"use strict";
-
-
-function BacklinksIndexer(wiki) {
- this.wiki = wiki;
-}
-
-BacklinksIndexer.prototype.init = function() {
- this.index = null;
-}
-
-BacklinksIndexer.prototype.rebuild = function() {
- this.index = null;
-}
-
-BacklinksIndexer.prototype._getLinks = function(tiddler) {
- var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {});
- if(parser) {
- return this.wiki.extractLinks(parser.tree);
- }
- return [];
-}
-
-BacklinksIndexer.prototype.update = function(updateDescriptor) {
- if(!this.index) {
- return;
- }
- var newLinks = [],
- oldLinks = [],
- self = this;
- if(updateDescriptor.old.exists) {
- oldLinks = this._getLinks(updateDescriptor.old.tiddler);
- }
- if(updateDescriptor.new.exists) {
- newLinks = this._getLinks(updateDescriptor.new.tiddler);
- }
-
- $tw.utils.each(oldLinks,function(link) {
- if(self.index[link]) {
- delete self.index[link][updateDescriptor.old.tiddler.fields.title];
- }
- });
- $tw.utils.each(newLinks,function(link) {
- if(!self.index[link]) {
- self.index[link] = Object.create(null);
- }
- self.index[link][updateDescriptor.new.tiddler.fields.title] = true;
- });
-}
-
-BacklinksIndexer.prototype.lookup = function(title) {
- if(!this.index) {
- this.index = Object.create(null);
- var self = this;
- this.wiki.forEachTiddler(function(title,tiddler) {
- var links = self._getLinks(tiddler);
- $tw.utils.each(links, function(link) {
- if(!self.index[link]) {
- self.index[link] = Object.create(null);
- }
- self.index[link][title] = true;
- });
- });
- }
- if(this.index[title]) {
- return Object.keys(this.index[title]);
- } else {
- return [];
- }
-}
-
-exports.BacklinksIndexer = BacklinksIndexer;
-
-})();
diff --git a/core/modules/indexers/field-indexer.js b/core/modules/indexers/field-indexer.js
index e49583610..3aefc99ac 100644
--- a/core/modules/indexers/field-indexer.js
+++ b/core/modules/indexers/field-indexer.js
@@ -32,18 +32,18 @@ FieldIndexer.prototype.setMaxIndexedValueLength = function(length) {
FieldIndexer.prototype.addIndexMethods = function() {
var self = this;
+ // get all tiddlers, including those overwrite shadow tiddlers
this.wiki.each.byField = function(name,value) {
- var titles = self.wiki.allTitles(),
- lookup = self.lookup(name,value);
+ var lookup = self.lookup(name,value);
return lookup && lookup.filter(function(title) {
- return titles.indexOf(title) !== -1;
+ return self.wiki.tiddlerExists(title)
});
};
+ // get shadow tiddlers, including shadow tiddlers that is overwritten
this.wiki.eachShadow.byField = function(name,value) {
- var titles = self.wiki.allShadowTitles(),
- lookup = self.lookup(name,value);
+ var lookup = self.lookup(name,value);
return lookup && lookup.filter(function(title) {
- return titles.indexOf(title) !== -1;
+ return self.wiki.isShadowTiddler(title)
});
};
this.wiki.eachTiddlerPlusShadows.byField = function(name,value) {
diff --git a/core/modules/macros/csvtiddlers.js b/core/modules/macros/csvtiddlers.js
index 7b34ce04d..a492fd81c 100644
--- a/core/modules/macros/csvtiddlers.js
+++ b/core/modules/macros/csvtiddlers.js
@@ -35,9 +35,11 @@ exports.run = function(filter,format) {
// Collect all the fields
for(t=0;t"'=]+)/g,
- reUnquotedAttribute = /([^\/\s<>"'=]+)/g,
+ var reAttributeName = /([^\/\s>"'`=]+)/g,
+ reUnquotedAttribute = /([^\/\s<>"'`=]+)/g,
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g,
- reIndirectValue = /\{\{([^\}]+)\}\}/g;
+ reIndirectValue = /\{\{([^\}]+)\}\}/g,
+ reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Get the attribute name
@@ -302,8 +362,15 @@ exports.parseAttribute = function(source,pos) {
node.type = "macro";
node.value = macroInvocation;
} else {
- node.type = "string";
- node.value = "true";
+ var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
+ if(substitutedValue) {
+ pos = substitutedValue.end;
+ node.type = "substituted";
+ node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
+ } else {
+ node.type = "string";
+ node.value = "true";
+ }
}
}
}
diff --git a/core/modules/parsers/pdfparser.js b/core/modules/parsers/pdfparser.js
index 95d74ef4b..c7830bf69 100644
--- a/core/modules/parsers/pdfparser.js
+++ b/core/modules/parsers/pdfparser.js
@@ -15,7 +15,7 @@ The PDF parser embeds a PDF viewer
var ImageParser = function(type,text,options) {
var element = {
type: "element",
- tag: "embed",
+ tag: "iframe",
attributes: {}
},
src;
@@ -25,6 +25,8 @@ var ImageParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:application/pdf;base64," + text};
}
this.tree = [element];
+ this.source = text;
+ this.type = type;
};
exports["application/pdf"] = ImageParser;
diff --git a/core/modules/parsers/textparser.js b/core/modules/parsers/textparser.js
index 4f55f6f0c..17f9bde10 100644
--- a/core/modules/parsers/textparser.js
+++ b/core/modules/parsers/textparser.js
@@ -14,12 +14,16 @@ The plain text parser processes blocks of source text into a degenerate parse tr
var TextParser = function(type,text,options) {
this.tree = [{
- type: "codeblock",
+ type: "genesis",
attributes: {
- code: {type: "string", value: text},
- language: {type: "string", value: type}
+ $type: {name: "$type", type: "string", value: "$codeblock"},
+ code: {name: "code", type: "string", value: text},
+ language: {name: "language", type: "string", value: type},
+ $remappable: {name: "$remappable", type:"string", value: "no"}
}
}];
+ this.source = text;
+ this.type = type;
};
exports["text/plain"] = TextParser;
@@ -30,4 +34,3 @@ exports["text/css"] = TextParser;
exports["application/x-tiddler-dictionary"] = TextParser;
})();
-
diff --git a/core/modules/parsers/videoparser.js b/core/modules/parsers/videoparser.js
index f1c281c7c..1c8a38bb2 100644
--- a/core/modules/parsers/videoparser.js
+++ b/core/modules/parsers/videoparser.js
@@ -28,6 +28,8 @@ var VideoParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
}
this.tree = [element];
+ this.source = text;
+ this.type = type;
};
exports["video/ogg"] = VideoParser;
diff --git a/core/modules/parsers/wikiparser/rules/codeblock.js b/core/modules/parsers/wikiparser/rules/codeblock.js
index 262038f87..6c3480566 100644
--- a/core/modules/parsers/wikiparser/rules/codeblock.js
+++ b/core/modules/parsers/wikiparser/rules/codeblock.js
@@ -29,13 +29,16 @@ exports.init = function(parser) {
exports.parse = function() {
var reEnd = /(\r?\n```$)/mg;
+ var languageStart = this.parser.pos + 3,
+ languageEnd = languageStart + this.match[1].length;
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;
// Look for the end of the block
reEnd.lastIndex = this.parser.pos;
var match = reEnd.exec(this.parser.source),
- text;
+ text,
+ codeStart = this.parser.pos;
// Process the block
if(match) {
text = this.parser.source.substring(this.parser.pos,match.index);
@@ -48,8 +51,8 @@ exports.parse = function() {
return [{
type: "codeblock",
attributes: {
- code: {type: "string", value: text},
- language: {type: "string", value: this.match[1]}
+ code: {type: "string", value: text, start: codeStart, end: this.parser.pos},
+ language: {type: "string", value: this.match[1], start: languageStart, end: languageEnd}
}
}];
};
diff --git a/core/modules/parsers/wikiparser/rules/codeinline.js b/core/modules/parsers/wikiparser/rules/codeinline.js
index ee9149833..048fc051c 100644
--- a/core/modules/parsers/wikiparser/rules/codeinline.js
+++ b/core/modules/parsers/wikiparser/rules/codeinline.js
@@ -33,7 +33,8 @@ exports.parse = function() {
// Look for the end marker
reEnd.lastIndex = this.parser.pos;
var match = reEnd.exec(this.parser.source),
- text;
+ text,
+ start = this.parser.pos;
// Process the text
if(match) {
text = this.parser.source.substring(this.parser.pos,match.index);
@@ -47,7 +48,9 @@ exports.parse = function() {
tag: "code",
children: [{
type: "text",
- text: text
+ text: text,
+ start: start,
+ end: this.parser.pos
}]
}];
};
diff --git a/core/modules/parsers/wikiparser/rules/conditional.js b/core/modules/parsers/wikiparser/rules/conditional.js
new file mode 100644
index 000000000..23940fd88
--- /dev/null
+++ b/core/modules/parsers/wikiparser/rules/conditional.js
@@ -0,0 +1,120 @@
+/*\
+title: $:/core/modules/parsers/wikiparser/rules/conditional.js
+type: application/javascript
+module-type: wikirule
+
+Conditional shortcut syntax
+
+```
+This is a <% if [{something}] %>Elephant<% elseif [{else}] %>Pelican<% else %>Crocodile<% endif %>
+```
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+exports.name = "conditional";
+exports.types = {inline: true, block: true};
+
+exports.init = function(parser) {
+ this.parser = parser;
+ // Regexp to match
+ this.matchRegExp = /\<\%\s*if\s+/mg;
+ this.terminateIfRegExp = /\%\>/mg;
+};
+
+exports.findNextMatch = function(startPos) {
+ // Look for the next <% if shortcut
+ this.matchRegExp.lastIndex = startPos;
+ this.match = this.matchRegExp.exec(this.parser.source);
+ // If not found then return no match
+ if(!this.match) {
+ return undefined;
+ }
+ // Check for the next %>
+ this.terminateIfRegExp.lastIndex = this.match.index;
+ this.terminateIfMatch = this.terminateIfRegExp.exec(this.parser.source);
+ // If not found then return no match
+ if(!this.terminateIfMatch) {
+ return undefined;
+ }
+ // Return the position at which the construction was found
+ return this.match.index;
+};
+
+/*
+Parse the most recent match
+*/
+exports.parse = function() {
+ // Get the filter condition
+ var filterCondition = this.parser.source.substring(this.match.index + this.match[0].length,this.terminateIfMatch.index);
+ // Advance the parser position to past the %>
+ this.parser.pos = this.terminateIfMatch.index + this.terminateIfMatch[0].length;
+ // Parse the if clause
+ return this.parseIfClause(filterCondition);
+};
+
+exports.parseIfClause = function(filterCondition) {
+ // Create the list widget
+ var listWidget = {
+ type: "list",
+ tag: "$list",
+ isBlock: this.is.block,
+ children: [
+ {
+ type: "list-template",
+ tag: "$list-template"
+ },
+ {
+ type: "list-empty",
+ tag: "$list-empty"
+ }
+ ]
+ };
+ $tw.utils.addAttributeToParseTreeNode(listWidget,"filter",filterCondition);
+ $tw.utils.addAttributeToParseTreeNode(listWidget,"variable","condition");
+ $tw.utils.addAttributeToParseTreeNode(listWidget,"limit","1");
+ // Check for an immediately following double linebreak
+ var hasLineBreak = !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
+ // Parse the body looking for else or endif
+ var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>|\\<\\%\\s*(else)\\s*\\%\\>|\\<\\%\\s*(elseif)\\s+([\\s\\S]+?)\\%\\>",
+ ex;
+ if(hasLineBreak) {
+ ex = this.parser.parseBlocksTerminatedExtended(reEndString);
+ } else {
+ var reEnd = new RegExp(reEndString,"mg");
+ ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
+ }
+ // Put the body into the list template
+ listWidget.children[0].children = ex.tree;
+ // Check for an else or elseif
+ if(ex.match) {
+ if(ex.match[1] === "endif") {
+ // Nothing to do if we just found an endif
+ } else if(ex.match[2] === "else") {
+ // Check for an immediately following double linebreak
+ hasLineBreak = !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
+ // If we found an else then we need to parse the body looking for the endif
+ var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>",
+ ex;
+ if(hasLineBreak) {
+ ex = this.parser.parseBlocksTerminatedExtended(reEndString);
+ } else {
+ var reEnd = new RegExp(reEndString,"mg");
+ ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
+ }
+ // Put the parsed content inside the list empty template
+ listWidget.children[1].children = ex.tree;
+ } else if(ex.match[3] === "elseif") {
+ // Parse the elseif clause by reusing this parser, passing the new filter condition
+ listWidget.children[1].children = this.parseIfClause(ex.match[4]);
+ }
+ }
+ // Return the parse tree node
+ return [listWidget];
+};
+
+})();
diff --git a/core/modules/parsers/wikiparser/rules/extlink.js b/core/modules/parsers/wikiparser/rules/extlink.js
index e06f88d8d..5b9f57adf 100644
--- a/core/modules/parsers/wikiparser/rules/extlink.js
+++ b/core/modules/parsers/wikiparser/rules/extlink.js
@@ -31,6 +31,7 @@ exports.init = function(parser) {
exports.parse = function() {
// Move past the match
+ 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) === "~") {
@@ -46,7 +47,7 @@ exports.parse = function() {
rel: {type: "string", value: "noopener noreferrer"}
},
children: [{
- type: "text", text: this.match[0]
+ type: "text", text: this.match[0], start: start, end: this.parser.pos
}]
}];
}
diff --git a/core/modules/parsers/wikiparser/rules/filteredtranscludeblock.js b/core/modules/parsers/wikiparser/rules/filteredtranscludeblock.js
index 7ab4801bf..73bdff813 100644
--- a/core/modules/parsers/wikiparser/rules/filteredtranscludeblock.js
+++ b/core/modules/parsers/wikiparser/rules/filteredtranscludeblock.js
@@ -31,6 +31,16 @@ exports.init = function(parser) {
exports.parse = function() {
// Move past the match
+ var filterStart = this.parser.pos + 3;
+ var filterEnd = filterStart + this.match[1].length;
+ var toolTipStart = filterEnd + 1;
+ var toolTipEnd = toolTipStart + (this.match[2] ? this.match[2].length : 0);
+ var templateStart = toolTipEnd + 2;
+ var templateEnd = templateStart + (this.match[3] ? this.match[3].length : 0);
+ var styleStart = templateEnd + 2;
+ var styleEnd = styleStart + (this.match[4] ? this.match[4].length : 0);
+ var classesStart = styleEnd + 1;
+ var classesEnd = classesStart + (this.match[5] ? this.match[5].length : 0);
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
var filter = this.match[1],
@@ -42,21 +52,21 @@ exports.parse = function() {
var node = {
type: "list",
attributes: {
- filter: {type: "string", value: filter}
+ filter: {type: "string", value: filter, start: filterStart, end: filterEnd},
},
isBlock: true
};
if(tooltip) {
- node.attributes.tooltip = {type: "string", value: tooltip};
+ node.attributes.tooltip = {type: "string", value: tooltip, start: toolTipStart, end: toolTipEnd};
}
if(template) {
- node.attributes.template = {type: "string", value: template};
+ node.attributes.template = {type: "string", value: template, start: templateStart, end: templateEnd};
}
if(style) {
- node.attributes.style = {type: "string", value: style};
+ node.attributes.style = {type: "string", value: style, start: styleStart, end: styleEnd};
}
if(classes) {
- node.attributes.itemClass = {type: "string", value: classes.split(".").join(" ")};
+ node.attributes.itemClass = {type: "string", value: classes.split(".").join(" "), start: classesStart, end: classesEnd};
}
return [node];
};
diff --git a/core/modules/parsers/wikiparser/rules/filteredtranscludeinline.js b/core/modules/parsers/wikiparser/rules/filteredtranscludeinline.js
index 029fd6802..c0b19a941 100644
--- a/core/modules/parsers/wikiparser/rules/filteredtranscludeinline.js
+++ b/core/modules/parsers/wikiparser/rules/filteredtranscludeinline.js
@@ -30,6 +30,16 @@ exports.init = function(parser) {
};
exports.parse = function() {
+ var filterStart = this.parser.pos + 3;
+ var filterEnd = filterStart + this.match[1].length;
+ var toolTipStart = filterEnd + 1;
+ var toolTipEnd = toolTipStart + (this.match[2] ? this.match[2].length : 0);
+ var templateStart = toolTipEnd + 2;
+ var templateEnd = templateStart + (this.match[3] ? this.match[3].length : 0);
+ var styleStart = templateEnd + 2;
+ var styleEnd = styleStart + (this.match[4] ? this.match[4].length : 0);
+ var classesStart = styleEnd + 1;
+ var classesEnd = classesStart + (this.match[5] ? this.match[5].length : 0);
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
@@ -42,20 +52,20 @@ exports.parse = function() {
var node = {
type: "list",
attributes: {
- filter: {type: "string", value: filter}
+ filter: {type: "string", value: filter, start: filterStart, end: filterEnd},
}
};
if(tooltip) {
- node.attributes.tooltip = {type: "string", value: tooltip};
+ node.attributes.tooltip = {type: "string", value: tooltip, start: toolTipStart, end: toolTipEnd};
}
if(template) {
- node.attributes.template = {type: "string", value: template};
+ node.attributes.template = {type: "string", value: template, start: templateStart, end: templateEnd};
}
if(style) {
- node.attributes.style = {type: "string", value: style};
+ node.attributes.style = {type: "string", value: style, start: styleStart, end: styleEnd};
}
if(classes) {
- node.attributes.itemClass = {type: "string", value: classes.split(".").join(" ")};
+ node.attributes.itemClass = {type: "string", value: classes.split(".").join(" "), start: classesStart, end: classesEnd};
}
return [node];
};
diff --git a/core/modules/parsers/wikiparser/rules/fnprocdef.js b/core/modules/parsers/wikiparser/rules/fnprocdef.js
new file mode 100644
index 000000000..85bd14d5c
--- /dev/null
+++ b/core/modules/parsers/wikiparser/rules/fnprocdef.js
@@ -0,0 +1,97 @@
+/*\
+title: $:/core/modules/parsers/wikiparser/rules/fnprocdef.js
+type: application/javascript
+module-type: wikirule
+
+Wiki pragma rule for function, procedure and widget definitions
+
+```
+\function name(param:defaultvalue,param2:defaultvalue)
+definition text
+\end
+
+\procedure name(param:defaultvalue,param2:defaultvalue)
+definition text
+\end
+
+\widget $mywidget(param:defaultvalue,param2:defaultvalue)
+definition text
+\end
+```
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+exports.name = "fnprocdef";
+exports.types = {pragma: true};
+
+/*
+Instantiate parse rule
+*/
+exports.init = function(parser) {
+ this.parser = parser;
+ // Regexp to match
+ this.matchRegExp = /\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg;
+};
+
+/*
+Parse the most recent match
+*/
+exports.parse = function() {
+ // Move past the macro name and parameters
+ this.parser.pos = this.matchRegExp.lastIndex;
+ // Parse the parameters
+ var params = [];
+ if(this.match[3]) {
+ params = $tw.utils.parseParameterDefinition(this.match[4]);
+ }
+ // Is the remainder of the line blank after the parameter close paren?
+ var reEnd;
+ if(this.match[5]) {
+ // If so, it is a multiline definition and the end of the body is marked with \end
+ reEnd = new RegExp("((:?^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
+ } else {
+ // Otherwise, the end of the definition is marked by the end of the line
+ reEnd = /($|\r?\n)/mg;
+ // Move past any whitespace
+ this.parser.pos = $tw.utils.skipWhiteSpace(this.parser.source,this.parser.pos);
+ }
+ // Find the end of the definition
+ reEnd.lastIndex = this.parser.pos;
+ var text,
+ endMatch = reEnd.exec(this.parser.source);
+ if(endMatch) {
+ text = this.parser.source.substring(this.parser.pos,endMatch.index);
+ this.parser.pos = endMatch.index + endMatch[0].length;
+ } else {
+ // We didn't find the end of the definition, so we'll make it blank
+ text = "";
+ }
+ // Save the macro definition
+ var parseTreeNodes = [{
+ type: "set",
+ attributes: {},
+ children: [],
+ params: params
+ }];
+ $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[2]);
+ $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
+ if(this.match[1] === "function") {
+ parseTreeNodes[0].isFunctionDefinition = true;
+ } else if(this.match[1] === "procedure") {
+ parseTreeNodes[0].isProcedureDefinition = true;
+ } else if(this.match[1] === "widget") {
+ parseTreeNodes[0].isWidgetDefinition = true;
+ }
+ if(this.parser.configTrimWhiteSpace) {
+ parseTreeNodes[0].configTrimWhiteSpace = true;
+ }
+ return parseTreeNodes;
+};
+
+})();
+
\ No newline at end of file
diff --git a/core/modules/parsers/wikiparser/rules/hardlinebreaks.js b/core/modules/parsers/wikiparser/rules/hardlinebreaks.js
index c278686b4..94f517cd4 100644
--- a/core/modules/parsers/wikiparser/rules/hardlinebreaks.js
+++ b/core/modules/parsers/wikiparser/rules/hardlinebreaks.js
@@ -45,10 +45,11 @@ exports.parse = function() {
reEnd.lastIndex = this.parser.pos;
match = reEnd.exec(this.parser.source);
if(match) {
+ var start = this.parser.pos;
this.parser.pos = reEnd.lastIndex;
// Add a line break if the terminator was a line break
if(match[2]) {
- tree.push({type: "element", tag: "br"});
+ tree.push({type: "element", tag: "br", start: start, end: this.parser.pos});
}
}
} while(match && !match[1]);
diff --git a/core/modules/parsers/wikiparser/rules/heading.js b/core/modules/parsers/wikiparser/rules/heading.js
index de4e45c27..7a0ecb9db 100644
--- a/core/modules/parsers/wikiparser/rules/heading.js
+++ b/core/modules/parsers/wikiparser/rules/heading.js
@@ -30,15 +30,17 @@ exports.parse = function() {
// Move past the !s
this.parser.pos = this.matchRegExp.lastIndex;
// Parse any classes, whitespace and then the heading itself
+ var classStart = this.parser.pos;
var classes = this.parser.parseClasses();
+ var classEnd = this.parser.pos;
this.parser.skipWhitespace({treatNewlinesAsNonWhitespace: true});
var tree = this.parser.parseInlineRun(/(\r?\n)/mg);
// Return the heading
return [{
type: "element",
- tag: "h" + headingLevel,
+ tag: "h" + headingLevel,
attributes: {
- "class": {type: "string", value: classes.join(" ")}
+ "class": {type: "string", value: classes.join(" "), start: classStart, end: classEnd}
},
children: tree
}];
diff --git a/core/modules/parsers/wikiparser/rules/html.js b/core/modules/parsers/wikiparser/rules/html.js
index 7fc4bb96e..61c4ad9e1 100644
--- a/core/modules/parsers/wikiparser/rules/html.js
+++ b/core/modules/parsers/wikiparser/rules/html.js
@@ -44,6 +44,10 @@ 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) {
+ tag.openTagStart = tag.start;
+ tag.openTagEnd = tag.end;
+ }
this.nextTag = null;
// Advance the parser position to past the tag
this.parser.pos = tag.end;
@@ -60,6 +64,27 @@ exports.parse = function() {
var reEnd = new RegExp("(" + reEndString + ")","mg");
tag.children = this.parser.parseInlineRun(reEnd,{eatTerminator: true});
}
+ tag.end = this.parser.pos;
+ tag.closeTagEnd = tag.end;
+ 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) {
+ var char = this.parser.source[tag.closeTagStart];
+ if (char === '>') {
+ tag.closeTagStart = -1;
+ break;
+ }
+ if (char === '<') break;
+ tag.closeTagStart -= 1;
+ }
+ if (tag.closeTagStart < closeTagMinPos) {
+ tag.closeTagStart = tag.end;
+ }
+ }
}
// Return the tag
return [tag];
@@ -78,7 +103,7 @@ exports.parseTag = function(source,pos,options) {
orderedAttributes: []
};
// Define our regexps
- var reTagName = /([a-zA-Z0-9\-\$]+)/g;
+ var reTagName = /([a-zA-Z0-9\-\$\.]+)/g;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a less than sign
@@ -93,9 +118,6 @@ exports.parseTag = function(source,pos,options) {
return null;
}
node.tag = token.match[1];
- if(node.tag.slice(1).indexOf("$") !== -1) {
- return null;
- }
if(node.tag.charAt(0) === "$") {
node.type = node.tag.substr(1);
}
@@ -141,7 +163,7 @@ exports.parseTag = function(source,pos,options) {
exports.findNextTag = function(source,pos,options) {
// A regexp for finding candidate HTML tags
- var reLookahead = /<([a-zA-Z\-\$]+)/g;
+ var reLookahead = /<([a-zA-Z\-\$\.]+)/g;
// Find the next candidate
reLookahead.lastIndex = pos;
var match = reLookahead.exec(source);
diff --git a/core/modules/parsers/wikiparser/rules/image.js b/core/modules/parsers/wikiparser/rules/image.js
index 6b379d9c5..6f58225e0 100644
--- a/core/modules/parsers/wikiparser/rules/image.js
+++ b/core/modules/parsers/wikiparser/rules/image.js
@@ -122,9 +122,9 @@ exports.parseImage = function(source,pos) {
}
pos = token.end;
if(token.match[1]) {
- node.attributes.tooltip = {type: "string", value: token.match[1].trim()};
+ node.attributes.tooltip = {type: "string", value: token.match[1].trim(),start: token.start,end:token.start + token.match[1].length - 1};
}
- node.attributes.source = {type: "string", value: (token.match[2] || "").trim()};
+ node.attributes.source = {type: "string", value: (token.match[2] || "").trim(), start: token.start + (token.match[1] ? token.match[1].length : 0), end: token.end - 2};
// Update the end position
node.end = pos;
return node;
diff --git a/core/modules/parsers/wikiparser/rules/import.js b/core/modules/parsers/wikiparser/rules/import.js
index 9556c7af5..bb1832255 100644
--- a/core/modules/parsers/wikiparser/rules/import.js
+++ b/core/modules/parsers/wikiparser/rules/import.js
@@ -25,7 +25,7 @@ Instantiate parse rule
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /^\\import[^\S\n]/mg;
+ this.matchRegExp = /\\import[^\S\n]/mg;
};
/*
@@ -38,13 +38,14 @@ exports.parse = function() {
// Parse the filter terminated by a line break
var reMatch = /(.*)(?:$|\r?\n)/mg;
reMatch.lastIndex = this.parser.pos;
+ var filterStart = this.parser.source;
var match = reMatch.exec(this.parser.source);
this.parser.pos = reMatch.lastIndex;
// Parse tree nodes to return
return [{
type: "importvariables",
attributes: {
- filter: {type: "string", value: match[1]}
+ filter: {type: "string", value: match[1], start: filterStart, end: this.parser.pos}
},
children: []
}];
diff --git a/core/modules/parsers/wikiparser/rules/list.js b/core/modules/parsers/wikiparser/rules/list.js
index 17eab6dad..d89c201b9 100644
--- a/core/modules/parsers/wikiparser/rules/list.js
+++ b/core/modules/parsers/wikiparser/rules/list.js
@@ -74,6 +74,7 @@ exports.parse = function() {
// Match the list marker
var reMatch = /([\*#;:>]+)/mg;
reMatch.lastIndex = this.parser.pos;
+ var start = this.parser.pos;
var match = reMatch.exec(this.parser.source);
if(!match || match.index !== this.parser.pos) {
break;
@@ -94,9 +95,21 @@ exports.parse = function() {
}
// Construct the list element or reuse the previous one at this level
if(listStack.length <= t) {
- var listElement = {type: "element", tag: listInfo.listTag, children: [
- {type: "element", tag: listInfo.itemTag, children: []}
- ]};
+ var listElement = {
+ type: "element",
+ tag: listInfo.listTag,
+ children: [
+ {
+ type: "element",
+ tag: listInfo.itemTag,
+ children: [],
+ start: start,
+ end: this.parser.pos,
+ }
+ ],
+ start: start,
+ end: this.parser.pos,
+ };
// Link this list element into the last child item of the parent list item
if(t) {
var prevListItem = listStack[t-1].children[listStack[t-1].children.length-1];
@@ -105,21 +118,33 @@ exports.parse = function() {
// Save this element in the stack
listStack[t] = listElement;
} else if(t === (match[0].length - 1)) {
- listStack[t].children.push({type: "element", tag: listInfo.itemTag, children: []});
+ listStack[t].children.push({
+ type: "element",
+ tag: listInfo.itemTag,
+ children: [],
+ start: start,
+ end: this.parser.pos,
+ });
}
}
if(listStack.length > match[0].length) {
listStack.splice(match[0].length,listStack.length - match[0].length);
}
// Process the body of the list item into the last list item
+ var classStart = this.parser.pos;
var lastListChildren = listStack[listStack.length-1].children,
lastListItem = lastListChildren[lastListChildren.length-1],
classes = this.parser.parseClasses();
+ var classEnd = this.parser.pos;
this.parser.skipWhitespace({treatNewlinesAsNonWhitespace: true});
var tree = this.parser.parseInlineRun(/(\r?\n)/mg);
lastListItem.children.push.apply(lastListItem.children,tree);
+ lastListItem.end = this.parser.pos;
+ listStack[listStack.length-1].end = this.parser.pos;
if(classes.length > 0) {
$tw.utils.addClassToParseTreeNode(lastListItem,classes.join(" "));
+ lastListItem.attributes.class.start = classStart;
+ lastListItem.attributes.class.end = classEnd;
}
// Consume any whitespace following the list item
this.parser.skipWhitespace();
diff --git a/core/modules/parsers/wikiparser/rules/macrocallblock.js b/core/modules/parsers/wikiparser/rules/macrocallblock.js
index 6f50fdbb0..a2c10e04a 100644
--- a/core/modules/parsers/wikiparser/rules/macrocallblock.js
+++ b/core/modules/parsers/wikiparser/rules/macrocallblock.js
@@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
- var nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
+ var nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(nextCall) {
var c = this.parser.source.charAt(nextCall.end);
// Ensure EOL after parsed macro
diff --git a/core/modules/parsers/wikiparser/rules/macrocallinline.js b/core/modules/parsers/wikiparser/rules/macrocallinline.js
index 165a70dce..e9f79f09e 100644
--- a/core/modules/parsers/wikiparser/rules/macrocallinline.js
+++ b/core/modules/parsers/wikiparser/rules/macrocallinline.js
@@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
- this.nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
+ this.nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(this.nextCall) {
return nextStart;
}
diff --git a/core/modules/parsers/wikiparser/rules/macrodef.js b/core/modules/parsers/wikiparser/rules/macrodef.js
index 22a7b5ba7..2001f70d5 100644
--- a/core/modules/parsers/wikiparser/rules/macrodef.js
+++ b/core/modules/parsers/wikiparser/rules/macrodef.js
@@ -27,7 +27,7 @@ Instantiate parse rule
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /^\\define\s+([^(\s]+)\(\s*([^)]*)\)(\s*\r?\n)?/mg;
+ this.matchRegExp = /\\define\s+([^(\s]+)\(\s*([^)]*)\)(\s*\r?\n)?/mg;
};
/*
@@ -54,11 +54,11 @@ exports.parse = function() {
paramMatch = reParam.exec(paramString);
}
}
- // Is this a multiline definition?
+ // Is the remainder of the \define line blank after the parameter close paren?
var reEnd;
if(this.match[3]) {
- // If so, the end of the body is marked with \end
- reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
+ // If so, it is a multiline definition and the end of the body is marked with \end
+ reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
reEnd = /($|\r?\n)/mg;
@@ -77,16 +77,16 @@ exports.parse = function() {
text = "";
}
// Save the macro definition
- return [{
+ var parseTreeNodes = [{
type: "set",
- attributes: {
- name: {type: "string", value: this.match[1]},
- value: {type: "string", value: text}
- },
+ attributes: {},
children: [],
params: params,
isMacroDefinition: true
}];
+ $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
+ $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
+ return parseTreeNodes;
};
})();
diff --git a/core/modules/parsers/wikiparser/rules/parameters.js b/core/modules/parsers/wikiparser/rules/parameters.js
new file mode 100644
index 000000000..60bbd8901
--- /dev/null
+++ b/core/modules/parsers/wikiparser/rules/parameters.js
@@ -0,0 +1,60 @@
+/*\
+title: $:/core/modules/parsers/wikiparser/rules/parameters.js
+type: application/javascript
+module-type: wikirule
+
+Wiki pragma rule for parameter definitions
+
+```
+\parameters(param:defaultvalue,param2:defaultvalue)
+definition text
+```
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+exports.name = "parameters";
+exports.types = {pragma: true};
+
+/*
+Instantiate parse rule
+*/
+exports.init = function(parser) {
+ this.parser = parser;
+ // Regexp to match
+ this.matchRegExp = /\\parameters\s*\(([^)]*)\)(\s*\r?\n)?/mg;
+};
+
+/*
+Parse the most recent match
+*/
+exports.parse = function() {
+ // Move past the macro name and parameters
+ this.parser.pos = this.matchRegExp.lastIndex;
+ // Parse the parameters
+ var params = $tw.utils.parseParameterDefinition(this.match[1]);
+ var attributes = Object.create(null),
+ orderedAttributes = [];
+ $tw.utils.each(params,function(param) {
+ var name = param.name;
+ // Parameter names starting with dollar must be escaped to double dollars for the parameters widget
+ if(name.charAt(0) === "$") {
+ name = "$" + name;
+ }
+ var attribute = {name: name, type: "string", value: param["default"] || ""};
+ attributes[name] = attribute;
+ orderedAttributes.push(attribute);
+ });
+ // Save the macro definition
+ return [{
+ type: "parameters",
+ attributes: attributes,
+ orderedAttributes: orderedAttributes
+ }];
+};
+
+})();
diff --git a/core/modules/parsers/wikiparser/rules/parsermode.js b/core/modules/parsers/wikiparser/rules/parsermode.js
index ad4287f99..72a6de067 100644
--- a/core/modules/parsers/wikiparser/rules/parsermode.js
+++ b/core/modules/parsers/wikiparser/rules/parsermode.js
@@ -26,7 +26,7 @@ Instantiate parse rule
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /^\\parsermode[^\S\n]/mg;
+ this.matchRegExp = /\\parsermode[^\S\n]/mg;
};
/*
diff --git a/core/modules/parsers/wikiparser/rules/prettyextlink.js b/core/modules/parsers/wikiparser/rules/prettyextlink.js
index 4c497c257..4707fa0d0 100644
--- a/core/modules/parsers/wikiparser/rules/prettyextlink.js
+++ b/core/modules/parsers/wikiparser/rules/prettyextlink.js
@@ -96,15 +96,20 @@ exports.parseLink = function(source,pos) {
splitPos = null;
}
// Pull out the tooltip and URL
- var tooltip, URL;
+ var tooltip, URL, urlStart;
+ textNode.start = pos;
if(splitPos) {
+ urlStart = splitPos + 1;
URL = source.substring(splitPos + 1,closePos).trim();
textNode.text = source.substring(pos,splitPos).trim();
+ textNode.end = splitPos;
} else {
+ urlStart = pos;
URL = source.substring(pos,closePos).trim();
textNode.text = URL;
+ textNode.end = closePos;
}
- node.attributes.href = {type: "string", value: URL};
+ node.attributes.href = {type: "string", value: URL, start: urlStart, end: closePos};
node.attributes.target = {type: "string", value: "_blank"};
node.attributes.rel = {type: "string", value: "noopener noreferrer"};
// Update the end position
diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js
index 56a2850a3..66c19dc88 100644
--- a/core/modules/parsers/wikiparser/rules/prettylink.js
+++ b/core/modules/parsers/wikiparser/rules/prettylink.js
@@ -29,32 +29,39 @@ exports.init = function(parser) {
exports.parse = function() {
// Move past the match
+ var start = this.parser.pos + 2;
this.parser.pos = this.matchRegExp.lastIndex;
// Process the link
var text = this.match[1],
- link = this.match[2] || text;
+ link = this.match[2] || text,
+ textEndPos = this.parser.source.indexOf("|", start);
+ if (textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
+ textEndPos = this.matchRegExp.lastIndex - 2;
+ }
+ var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start;
+ var linkEnd = linkStart + link.length;
if($tw.utils.isLinkExternal(link)) {
return [{
type: "element",
tag: "a",
attributes: {
- href: {type: "string", value: link},
+ href: {type: "string", value: link, start: linkStart, end: linkEnd},
"class": {type: "string", value: "tc-tiddlylink-external"},
target: {type: "string", value: "_blank"},
rel: {type: "string", value: "noopener noreferrer"}
},
children: [{
- type: "text", text: text
+ type: "text", text: text, start: start, end: textEndPos
}]
}];
} else {
return [{
type: "link",
attributes: {
- to: {type: "string", value: link}
+ to: {type: "string", value: link, start: linkStart, end: linkEnd}
},
children: [{
- type: "text", text: text
+ type: "text", text: text, start: start, end: textEndPos
}]
}];
}
diff --git a/core/modules/parsers/wikiparser/rules/quoteblock.js b/core/modules/parsers/wikiparser/rules/quoteblock.js
index 71b689680..fdd6c860b 100644
--- a/core/modules/parsers/wikiparser/rules/quoteblock.js
+++ b/core/modules/parsers/wikiparser/rules/quoteblock.js
@@ -3,30 +3,7 @@ title: $:/core/modules/parsers/wikiparser/rules/quoteblock.js
type: application/javascript
module-type: wikirule
-Wiki text rule for quote blocks. For example:
-
-```
- <<<.optionalClass(es) optional cited from
- a quote
- <<<
-
- <<<.optionalClass(es)
- a quote
- <<< optional cited from
-```
-
-Quotes can be quoted by putting more 0) {
tree.unshift({
type: "element",
tag: "cite",
- children: cite
+ children: cite,
+ start: citeStart,
+ end: citeEnd
});
}
// Parse any optional cite
this.parser.skipWhitespace({treatNewlinesAsNonWhitespace: true});
+ citeStart = this.parser.pos;
cite = this.parser.parseInlineRun(/(\r?\n)/mg);
+ citeEnd = this.parser.pos;
// If we got a cite, push it
if(cite.length > 0) {
tree.push({
type: "element",
tag: "cite",
- children: cite
+ children: cite,
+ start: citeStart,
+ end: citeEnd
});
}
// Return the blockquote element
@@ -81,7 +67,7 @@ exports.parse = function() {
type: "element",
tag: "blockquote",
attributes: {
- class: { type: "string", value: classes.join(" ") },
+ class: { type: "string", value: classes.join(" "), start: classStart, end: classEnd },
},
children: tree
}];
diff --git a/core/modules/parsers/wikiparser/rules/rules.js b/core/modules/parsers/wikiparser/rules/rules.js
index cfc03330b..1d0f80f29 100644
--- a/core/modules/parsers/wikiparser/rules/rules.js
+++ b/core/modules/parsers/wikiparser/rules/rules.js
@@ -26,7 +26,7 @@ Instantiate parse rule
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /^\\rules[^\S\n]/mg;
+ this.matchRegExp = /\\rules[^\S\n]/mg;
};
/*
diff --git a/core/modules/parsers/wikiparser/rules/syslink.js b/core/modules/parsers/wikiparser/rules/syslink.js
index 6eb2cdcd4..6bcbee384 100644
--- a/core/modules/parsers/wikiparser/rules/syslink.js
+++ b/core/modules/parsers/wikiparser/rules/syslink.js
@@ -29,10 +29,11 @@ exports.init = function(parser) {
exports.parse = function() {
var match = this.match[0];
// Move past the match
+ var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// Create the link unless it is suppressed
if(match.substr(0,1) === "~") {
- return [{type: "text", text: match.substr(1)}];
+ return [{type: "text", text: match.substr(1), start: start+1, end: this.parser.pos}];
} else {
return [{
type: "link",
@@ -41,10 +42,12 @@ exports.parse = function() {
},
children: [{
type: "text",
- text: match
+ text: match,
+ start: start,
+ end: this.parser.pos
}]
}];
}
};
-})();
\ No newline at end of file
+})();
diff --git a/core/modules/parsers/wikiparser/rules/table.js b/core/modules/parsers/wikiparser/rules/table.js
index 61cd71948..59aa81e91 100644
--- a/core/modules/parsers/wikiparser/rules/table.js
+++ b/core/modules/parsers/wikiparser/rules/table.js
@@ -150,7 +150,7 @@ exports.parse = function() {
} else {
// Otherwise, create a new row if this one is of a different type
if(rowType !== currRowType) {
- rowContainer = {type: "element", tag: rowContainerTypes[rowType], children: []};
+ rowContainer = {type: "element", tag: rowContainerTypes[rowType], children: [], start: this.parser.pos, end: this.parser.pos};
table.children.push(rowContainer);
currRowType = rowType;
}
@@ -178,6 +178,7 @@ exports.parse = function() {
// Increment the row count
rowCount++;
}
+ rowContainer.end = this.parser.pos;
}
rowMatch = rowRegExp.exec(this.parser.source);
}
diff --git a/core/modules/parsers/wikiparser/rules/transcludeblock.js b/core/modules/parsers/wikiparser/rules/transcludeblock.js
index 56a4f63b8..c033c2440 100644
--- a/core/modules/parsers/wikiparser/rules/transcludeblock.js
+++ b/core/modules/parsers/wikiparser/rules/transcludeblock.js
@@ -23,7 +23,7 @@ exports.types = {block: true};
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}(?:\r?\n|$)/mg;
+ this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
};
exports.parse = function() {
@@ -31,13 +31,22 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
var template = $tw.utils.trim(this.match[2]),
- textRef = $tw.utils.trim(this.match[1]);
+ textRef = $tw.utils.trim(this.match[1]),
+ params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget
var transcludeNode = {
type: "transclude",
attributes: {},
isBlock: true
};
+ $tw.utils.each(params,function(paramValue,index) {
+ var name = "" + index;
+ transcludeNode.attributes[name] = {
+ name: name,
+ type: "string",
+ value: paramValue
+ }
+ });
// Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) {
@@ -48,14 +57,14 @@ exports.parse = function() {
tiddlerNode = {
type: "tiddler",
attributes: {
- tiddler: {type: "string", value: targetTitle}
+ tiddler: {name: "tiddler", type: "string", value: targetTitle}
},
isBlock: true,
children: [transcludeNode]
};
}
if(template) {
- transcludeNode.attributes.tiddler = {type: "string", value: template};
+ transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) {
return [tiddlerNode];
} else {
@@ -63,12 +72,12 @@ exports.parse = function() {
}
} else {
if(textRef) {
- transcludeNode.attributes.tiddler = {type: "string", value: targetTitle};
+ transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) {
- transcludeNode.attributes.field = {type: "string", value: targetField};
+ transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
}
if(targetIndex) {
- transcludeNode.attributes.index = {type: "string", value: targetIndex};
+ transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
}
return [tiddlerNode];
} else {
diff --git a/core/modules/parsers/wikiparser/rules/transcludeinline.js b/core/modules/parsers/wikiparser/rules/transcludeinline.js
index dbf39bfb6..3ce9dc78e 100644
--- a/core/modules/parsers/wikiparser/rules/transcludeinline.js
+++ b/core/modules/parsers/wikiparser/rules/transcludeinline.js
@@ -23,7 +23,7 @@ exports.types = {inline: true};
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}/mg;
+ this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
};
exports.parse = function() {
@@ -31,12 +31,21 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
var template = $tw.utils.trim(this.match[2]),
- textRef = $tw.utils.trim(this.match[1]);
+ textRef = $tw.utils.trim(this.match[1]),
+ params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget
var transcludeNode = {
type: "transclude",
attributes: {}
};
+ $tw.utils.each(params,function(paramValue,index) {
+ var name = "" + index;
+ transcludeNode.attributes[name] = {
+ name: name,
+ type: "string",
+ value: paramValue
+ }
+ });
// Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) {
@@ -47,13 +56,13 @@ exports.parse = function() {
tiddlerNode = {
type: "tiddler",
attributes: {
- tiddler: {type: "string", value: targetTitle}
+ tiddler: {name: "tiddler", type: "string", value: targetTitle}
},
children: [transcludeNode]
};
}
if(template) {
- transcludeNode.attributes.tiddler = {type: "string", value: template};
+ transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) {
return [tiddlerNode];
} else {
@@ -61,12 +70,12 @@ exports.parse = function() {
}
} else {
if(textRef) {
- transcludeNode.attributes.tiddler = {type: "string", value: targetTitle};
+ transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) {
- transcludeNode.attributes.field = {type: "string", value: targetField};
+ transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
}
if(targetIndex) {
- transcludeNode.attributes.index = {type: "string", value: targetIndex};
+ transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
}
return [tiddlerNode];
} else {
diff --git a/core/modules/parsers/wikiparser/rules/typedblock.js b/core/modules/parsers/wikiparser/rules/typedblock.js
index 4195e57e5..07c88be15 100644
--- a/core/modules/parsers/wikiparser/rules/typedblock.js
+++ b/core/modules/parsers/wikiparser/rules/typedblock.js
@@ -46,6 +46,7 @@ exports.parse = function() {
renderType = this.match[2];
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;
+ var start = this.parser.pos;
// Look for the end of the block
reEnd.lastIndex = this.parser.pos;
var match = reEnd.exec(this.parser.source),
@@ -74,7 +75,9 @@ exports.parse = function() {
tag: "pre",
children: [{
type: "text",
- text: text
+ text: text,
+ start: start,
+ end: this.parser.pos
}]
}];
}
diff --git a/core/modules/parsers/wikiparser/rules/whitespace.js b/core/modules/parsers/wikiparser/rules/whitespace.js
index e3b0c8e3f..5fd9d4b83 100644
--- a/core/modules/parsers/wikiparser/rules/whitespace.js
+++ b/core/modules/parsers/wikiparser/rules/whitespace.js
@@ -26,7 +26,7 @@ Instantiate parse rule
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
- this.matchRegExp = /^\\whitespace[^\S\n]/mg;
+ this.matchRegExp = /\\whitespace[^\S\n]/mg;
};
/*
diff --git a/core/modules/parsers/wikiparser/rules/wikilink.js b/core/modules/parsers/wikiparser/rules/wikilink.js
index fadc4587e..6b195f9ff 100644
--- a/core/modules/parsers/wikiparser/rules/wikilink.js
+++ b/core/modules/parsers/wikiparser/rules/wikilink.js
@@ -36,6 +36,7 @@ exports.parse = function() {
// Get the details of the match
var linkText = this.match[0];
// Move past the macro call
+ var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// If the link starts with the unwikilink character then just output it as plain text
if(linkText.substr(0,1) === $tw.config.textPrimitives.unWikiLink) {
@@ -57,7 +58,9 @@ exports.parse = function() {
},
children: [{
type: "text",
- text: linkText
+ text: linkText,
+ start: start,
+ end: this.parser.pos
}]
}];
};
diff --git a/core/modules/parsers/wikiparser/rules/wikilinkprefix.js b/core/modules/parsers/wikiparser/rules/wikilinkprefix.js
new file mode 100644
index 000000000..60cb3d992
--- /dev/null
+++ b/core/modules/parsers/wikiparser/rules/wikilinkprefix.js
@@ -0,0 +1,40 @@
+/*\
+title: $:/core/modules/parsers/wikiparser/rules/wikilinkprefix.js
+type: application/javascript
+module-type: wikirule
+
+Wiki text inline rule for suppressed wiki links. For example:
+
+```
+~SuppressedLink
+```
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+exports.name = "wikilinkprefix";
+exports.types = {inline: true};
+
+exports.init = function(parser) {
+ this.parser = parser;
+ // Regexp to match
+ this.matchRegExp = new RegExp($tw.config.textPrimitives.unWikiLink + $tw.config.textPrimitives.wikiLink,"mg");
+};
+
+/*
+Parse the most recent match
+*/
+exports.parse = function() {
+ // Get the details of the match
+ var linkText = this.match[0];
+ // Move past the wikilink
+ this.parser.pos = this.matchRegExp.lastIndex;
+ // Return the link without unwikilink character as plain text
+ return [{type: "text", text: linkText.substr(1)}];
+};
+
+})();
diff --git a/core/modules/parsers/wikiparser/wikiparser.js b/core/modules/parsers/wikiparser/wikiparser.js
index 4c7419030..854171d19 100644
--- a/core/modules/parsers/wikiparser/wikiparser.js
+++ b/core/modules/parsers/wikiparser/wikiparser.js
@@ -32,6 +32,7 @@ options: see below:
parseAsInline: true to parse text as inline instead of block
wiki: reference to wiki to use
_canonical_uri: optional URI of content if text is missing or empty
+ configTrimWhiteSpace: true to trim whitespace
*/
var WikiParser = function(type,text,options) {
this.wiki = options.wiki;
@@ -46,7 +47,7 @@ var WikiParser = function(type,text,options) {
this.source = text || "";
this.sourceLength = this.source.length;
// Flag for ignoring whitespace
- this.configTrimWhiteSpace = false;
+ this.configTrimWhiteSpace = options.configTrimWhiteSpace !== undefined ? options.configTrimWhiteSpace : false;
// Parser mode
this.parseAsInline = options.parseAsInline;
// Set current parse position
@@ -90,6 +91,11 @@ var WikiParser = function(type,text,options) {
} else {
topBranch.push.apply(topBranch,this.parseBlocks());
}
+ // Build rules' name map
+ this.usingRuleMap = {};
+ $tw.utils.each(this.pragmaRules, function (ruleInfo) { self.usingRuleMap[ruleInfo.rule.name] = Object.getPrototypeOf(ruleInfo.rule); });
+ $tw.utils.each(this.blockRules, function (ruleInfo) { self.usingRuleMap[ruleInfo.rule.name] = Object.getPrototypeOf(ruleInfo.rule); });
+ $tw.utils.each(this.inlineRules, function (ruleInfo) { self.usingRuleMap[ruleInfo.rule.name] = Object.getPrototypeOf(ruleInfo.rule); });
// Return the parse tree
};
@@ -193,6 +199,7 @@ Parse any pragmas at the beginning of a block of parse text
WikiParser.prototype.parsePragmas = function() {
var currentTreeBranch = this.tree;
while(true) {
+ var savedPos = this.pos;
// Skip whitespace
this.skipWhitespace();
// Check for the end of the text
@@ -203,16 +210,24 @@ WikiParser.prototype.parsePragmas = function() {
var nextMatch = this.findNextMatch(this.pragmaRules,this.pos);
// If not, just exit
if(!nextMatch || nextMatch.matchIndex !== this.pos) {
+ this.pos = savedPos;
break;
}
// Process the pragma rule
+ var start = this.pos;
var subTree = nextMatch.rule.parse();
if(subTree.length > 0) {
+ // Set the start and end positions of the pragma rule if
+ if (subTree[0].start === undefined) subTree[0].start = start;
+ if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
+ $tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
// Quick hack; we only cope with a single parse tree node being returned, which is true at the moment
currentTreeBranch.push.apply(currentTreeBranch,subTree);
subTree[0].children = [];
currentTreeBranch = subTree[0].children;
}
+ // Skip whitespace after the pragma
+ this.skipWhitespace();
}
return currentTreeBranch;
};
@@ -222,7 +237,7 @@ Parse a block from the current position
terminatorRegExpString: optional regular expression string that identifies the end of plain paragraphs. Must not include capturing parenthesis
*/
WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
- var terminatorRegExp = terminatorRegExpString ? new RegExp("(" + terminatorRegExpString + "|\\r?\\n\\r?\\n)","mg") : /(\r?\n\r?\n)/mg;
+ var terminatorRegExp = terminatorRegExpString ? new RegExp(terminatorRegExpString + "|\\r?\\n\\r?\\n","mg") : /(\r?\n\r?\n)/mg;
this.skipWhitespace();
if(this.pos >= this.sourceLength) {
return [];
@@ -230,7 +245,15 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
// Look for a block rule that applies at the current position
var nextMatch = this.findNextMatch(this.blockRules,this.pos);
if(nextMatch && nextMatch.matchIndex === this.pos) {
- return nextMatch.rule.parse();
+ var start = this.pos;
+ var subTree = nextMatch.rule.parse();
+ // Set the start and end positions of the first and last blocks if they're not already set
+ if (subTree.length > 0) {
+ if (subTree[0].start === undefined) subTree[0].start = start;
+ if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
+ }
+ $tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
+ return subTree;
}
// Treat it as a paragraph if we didn't find a block rule
var start = this.pos;
@@ -263,11 +286,21 @@ WikiParser.prototype.parseBlocksUnterminated = function() {
};
/*
-Parse blocks of text until a terminating regexp is encountered
+Parse blocks of text until a terminating regexp is encountered. Wrapper for parseBlocksTerminatedExtended that just returns the parse tree
*/
WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
- var terminatorRegExp = new RegExp("(" + terminatorRegExpString + ")","mg"),
- tree = [];
+ var ex = this.parseBlocksTerminatedExtended(terminatorRegExpString);
+ return ex.tree;
+};
+
+/*
+Parse blocks of text until a terminating regexp is encountered
+*/
+WikiParser.prototype.parseBlocksTerminatedExtended = function(terminatorRegExpString) {
+ var terminatorRegExp = new RegExp(terminatorRegExpString,"mg"),
+ result = {
+ tree: []
+ };
// Skip any whitespace
this.skipWhitespace();
// Check if we've got the end marker
@@ -276,7 +309,7 @@ WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
// Parse the text into blocks
while(this.pos < this.sourceLength && !(match && match.index === this.pos)) {
var blocks = this.parseBlock(terminatorRegExpString);
- tree.push.apply(tree,blocks);
+ result.tree.push.apply(result.tree,blocks);
// Skip any whitespace
this.skipWhitespace();
// Check if we've got the end marker
@@ -285,8 +318,9 @@ WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
}
if(match && match.index === this.pos) {
this.pos = match.index + match[0].length;
+ result.match = match;
}
- return tree;
+ return result;
};
/*
@@ -316,7 +350,16 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
this.pos = nextMatch.matchIndex;
}
// Process the run rule
- tree.push.apply(tree,nextMatch.rule.parse());
+ var start = this.pos;
+ var subTree = nextMatch.rule.parse();
+ // Set the start and end positions of the first and last child if they're not already set
+ if (subTree.length > 0) {
+ // Set the start and end positions of the first and last child if they're not already set
+ if (subTree[0].start === undefined) subTree[0].start = start;
+ if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
+ }
+ $tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
+ tree.push.apply(tree,subTree);
// Look for the next run rule
nextMatch = this.findNextMatch(this.inlineRules,this.pos);
}
@@ -329,6 +372,11 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
};
WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,options) {
+ var ex = this.parseInlineRunTerminatedExtended(terminatorRegExp,options);
+ return ex.tree;
+};
+
+WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegExp,options) {
options = options || {};
var tree = [];
// Find the next occurrence of the terminator
@@ -348,7 +396,10 @@ WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
if(options.eatTerminator) {
this.pos += terminatorMatch[0].length;
}
- return tree;
+ return {
+ match: terminatorMatch,
+ tree: tree
+ };
}
}
// Process any inline rule, along with the text preceding it
@@ -359,7 +410,15 @@ WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
this.pos = inlineRuleMatch.matchIndex;
}
// Process the inline rule
- tree.push.apply(tree,inlineRuleMatch.rule.parse());
+ var start = this.pos;
+ var subTree = inlineRuleMatch.rule.parse();
+ // Set the start and end positions of the first and last child if they're not already set
+ if (subTree.length > 0) {
+ if (subTree[0].start === undefined) subTree[0].start = start;
+ if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
+ }
+ $tw.utils.each(subTree, function (node) { node.rule = inlineRuleMatch.rule.name; });
+ tree.push.apply(tree,subTree);
// Look for the next inline rule
inlineRuleMatch = this.findNextMatch(this.inlineRules,this.pos);
// Look for the next terminator match
@@ -372,7 +431,9 @@ WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
this.pushTextWidget(tree,this.source.substr(this.pos),this.pos,this.sourceLength);
}
this.pos = this.sourceLength;
- return tree;
+ return {
+ tree: tree
+ };
};
/*
@@ -383,7 +444,7 @@ WikiParser.prototype.pushTextWidget = function(array,text,start,end) {
text = $tw.utils.trim(text);
}
if(text) {
- array.push({type: "text", text: text, start: start, end: end});
+ array.push({type: "text", text: text, start: start, end: end});
}
};
@@ -436,4 +497,3 @@ WikiParser.prototype.amendRules = function(type,names) {
exports["text/vnd.tiddlywiki"] = WikiParser;
})();
-
diff --git a/core/modules/saver-handler.js b/core/modules/saver-handler.js
index 119c3e67a..23056bcc2 100644
--- a/core/modules/saver-handler.js
+++ b/core/modules/saver-handler.js
@@ -95,6 +95,7 @@ function SaverHandler(options) {
if($tw.browser) {
$tw.rootWidget.addEventListener("tm-save-wiki",function(event) {
self.saveWiki({
+ wiki: event.widget.wiki,
template: event.param,
downloadType: "text/plain",
variables: event.paramObject
@@ -102,6 +103,7 @@ function SaverHandler(options) {
});
$tw.rootWidget.addEventListener("tm-download-file",function(event) {
self.saveWiki({
+ wiki: event.widget.wiki,
method: "download",
template: event.param,
downloadType: "text/plain",
@@ -147,20 +149,22 @@ Save the wiki contents. Options are:
method: "save", "autosave" or "download"
template: the tiddler containing the template to save
downloadType: the content type for the saved file
+ wiki: optional wiki, overriding the default wiki specified in the constructor
*/
SaverHandler.prototype.saveWiki = function(options) {
options = options || {};
var self = this,
+ wiki = options.wiki || this.wiki,
method = options.method || "save";
// Ignore autosave if disabled
- if(method === "autosave" && ($tw.config.disableAutoSave || this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes")) {
+ if(method === "autosave" && ($tw.config.disableAutoSave || wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes")) {
return false;
}
var variables = options.variables || {},
template = (options.template ||
- this.wiki.getTiddlerText("$:/config/SaveWikiButton/Template","$:/core/save/all")).trim(),
+ wiki.getTiddlerText("$:/config/SaveWikiButton/Template","$:/core/save/all")).trim(),
downloadType = options.downloadType || "text/plain",
- text = this.wiki.renderTiddler(downloadType,template,options),
+ text = wiki.renderTiddler(downloadType,template,options),
callback = function(err) {
if(err) {
alert($tw.language.getString("Error/WhileSaving") + ":\n\n" + err);
diff --git a/core/modules/savers/beaker.js b/core/modules/savers/beaker.js
deleted file mode 100644
index dc24ef67f..000000000
--- a/core/modules/savers/beaker.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*\
-title: $:/core/modules/savers/beaker.js
-type: application/javascript
-module-type: saver
-
-Saves files using the Beaker browser's (https://beakerbrowser.com) Dat protocol (https://datproject.org/)
-Compatible with beaker >= V0.7.2
-
-\*/
-(function(){
-
-/*jslint node: true, browser: true */
-/*global $tw: false */
-"use strict";
-
-/*
-Set up the saver
-*/
-var BeakerSaver = function(wiki) {
- this.wiki = wiki;
-};
-
-BeakerSaver.prototype.save = function(text,method,callback) {
- var dat = new DatArchive("" + window.location),
- pathname = ("" + window.location.pathname).split("#")[0];
- dat.stat(pathname).then(function(value) {
- if(value.isDirectory()) {
- pathname = pathname + "/index.html";
- }
- dat.writeFile(pathname,text,"utf8").then(function(value) {
- callback(null);
- },function(reason) {
- callback("Beaker Saver Write Error: " + reason);
- });
- },function(reason) {
- callback("Beaker Saver Stat Error: " + reason);
- });
- return true;
-};
-
-/*
-Information about this saver
-*/
-BeakerSaver.prototype.info = {
- name: "beaker",
- priority: 3000,
- capabilities: ["save", "autosave"]
-};
-
-/*
-Static method that returns true if this saver is capable of working
-*/
-exports.canSave = function(wiki) {
- return !!window.DatArchive && location.protocol==="dat:";
-};
-
-/*
-Create an instance of this saver
-*/
-exports.create = function(wiki) {
- return new BeakerSaver(wiki);
-};
-
-})();
diff --git a/core/modules/savers/github.js b/core/modules/savers/github.js
index f9b87263d..c0a34f2d6 100644
--- a/core/modules/savers/github.js
+++ b/core/modules/savers/github.js
@@ -31,7 +31,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
headers = {
"Accept": "application/vnd.github.v3+json",
"Content-Type": "application/json;charset=UTF-8",
- "Authorization": "Basic " + window.btoa(username + ":" + password),
+ "Authorization": "Basic " + $tw.utils.base64Encode(username + ":" + password),
"If-None-Match": ""
};
// Bail if we don't have everything we need
diff --git a/core/modules/savers/hyperdrive.js b/core/modules/savers/hyperdrive.js
deleted file mode 100644
index 232392672..000000000
--- a/core/modules/savers/hyperdrive.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*\
-title: $:/core/modules/savers/hyperdrive.js
-type: application/javascript
-module-type: saver
-
-Saves files using the Hyperdrive Protocol (https://hypercore-protocol.org/#hyperdrive) Beaker browser beta-1.0 and later (https://beakerbrowser.com)
-Compatible with beaker >= V1.0.0
-
-\*/
-(function(){
-
-/*jslint node: true, browser: true */
-/*global $tw: false */
-"use strict";
-
-/*
-Set up the saver
-*/
-var HyperdriveSaver = function(wiki) {
- this.wiki = wiki;
-};
-
-HyperdriveSaver.prototype.save = function(text,method,callback) {
- var dat = beaker.hyperdrive.drive("" + window.location),
- pathname = ("" + window.location.pathname).split("#")[0];
- dat.stat(pathname).then(function(value) {
- if(value.isDirectory()) {
- pathname = pathname + "/index.html";
- }
- dat.writeFile(pathname,text,"utf8").then(function(value) {
- callback(null);
- },function(reason) {
- callback("Hyperdrive Saver Write Error: " + reason);
- });
- },function(reason) {
- callback("Hyperdrive Saver Stat Error: " + reason);
- });
- return true;
-};
-
-/*
-Information about this saver
-*/
-HyperdriveSaver.prototype.info = {
- name: "beaker-1.x",
- priority: 3000,
- capabilities: ["save", "autosave"]
-};
-
-/*
-Static method that returns true if this saver is capable of working
-*/
-exports.canSave = function(wiki) {
- return !!window.beaker && !!beaker.hyperdrive && location.protocol==="hyper:";
-};
-
-/*
-Create an instance of this saver
-*/
-exports.create = function(wiki) {
- return new HyperdriveSaver(wiki);
-};
-
-})();
diff --git a/core/modules/server/authenticators/header.js b/core/modules/server/authenticators/header.js
index 78ae6cb0a..cc1d6bdaf 100644
--- a/core/modules/server/authenticators/header.js
+++ b/core/modules/server/authenticators/header.js
@@ -37,7 +37,9 @@ HeaderAuthenticator.prototype.authenticateRequest = function(request,response,st
return false;
} else {
// authenticatedUsername will be undefined for anonymous users
- state.authenticatedUsername = username;
+ if(username) {
+ state.authenticatedUsername = $tw.utils.decodeURIComponentSafe(username);
+ }
return true;
}
};
diff --git a/core/modules/server/server.js b/core/modules/server/server.js
index 258ddfa31..d3c98f8fc 100644
--- a/core/modules/server/server.js
+++ b/core/modules/server/server.js
@@ -140,6 +140,11 @@ function sendResponse(request,response,statusCode,headers,data,encoding) {
return;
}
}
+ } else {
+ // RFC 7231, 6.1. Overview of Status Codes:
+ // Browser clients may cache 200, 203, 204, 206, 300, 301,
+ // 404, 405, 410, 414, and 501 unless given explicit cache controls
+ headers["Cache-Control"] = headers["Cache-Control"] || "no-store";
}
/*
If the gzip=yes is set, check if the user agent permits compression. If so,
diff --git a/core/modules/startup/plugins.js b/core/modules/startup/plugins.js
index cad61b104..fc8ba9589 100644
--- a/core/modules/startup/plugins.js
+++ b/core/modules/startup/plugins.js
@@ -15,6 +15,7 @@ Startup logic concerned with managing plugins
// Export name and synchronous status
exports.name = "plugins";
exports.after = ["load-modules"];
+exports.before = ["startup"];
exports.synchronous = true;
var TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE = "$:/status/RequireReloadDueToPluginChange";
@@ -60,7 +61,7 @@ exports.startup = function() {
// Collect the shadow tiddlers of any modified plugins
$tw.utils.each(changes.modifiedPlugins,function(pluginTitle) {
var pluginInfo = $tw.wiki.getPluginInfo(pluginTitle);
- if(pluginInfo) {
+ if(pluginInfo && pluginInfo.tiddlers) {
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
changedShadowTiddlers[title] = false;
});
diff --git a/core/modules/startup/render.js b/core/modules/startup/render.js
index e50512463..7206a51d0 100644
--- a/core/modules/startup/render.js
+++ b/core/modules/startup/render.js
@@ -29,7 +29,11 @@ var THROTTLE_REFRESH_TIMEOUT = 400;
exports.startup = function() {
// Set up the title
- $tw.titleWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TITLE_TITLE,{document: $tw.fakeDocument, parseAsInline: true});
+ $tw.titleWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TITLE_TITLE, {
+ document: $tw.fakeDocument,
+ parseAsInline: true,
+ importPageMacros: true,
+ });
$tw.titleContainer = $tw.fakeDocument.createElement("div");
$tw.titleWidgetNode.render($tw.titleContainer,null);
document.title = $tw.titleContainer.textContent;
@@ -81,6 +85,8 @@ exports.startup = function() {
deferredChanges = Object.create(null);
$tw.hooks.invokeHook("th-page-refreshed");
}
+ var throttledRefresh = $tw.perf.report("throttledRefresh",refresh);
+
// Add the change event handler
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {
// Check if only tiddlers that are throttled have changed
@@ -101,7 +107,7 @@ exports.startup = function() {
if(isNaN(timeout)) {
timeout = THROTTLE_REFRESH_TIMEOUT;
}
- timerId = setTimeout(refresh,timeout);
+ timerId = setTimeout(throttledRefresh,timeout);
$tw.utils.extend(deferredChanges,changes);
} else {
$tw.utils.extend(deferredChanges,changes);
diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js
index 41f3fe03f..d81e07aee 100644
--- a/core/modules/startup/rootwidget.js
+++ b/core/modules/startup/rootwidget.js
@@ -20,6 +20,44 @@ exports.before = ["story"];
exports.synchronous = true;
exports.startup = function() {
+ // Install the HTTP client event handler
+ $tw.httpClient = new $tw.utils.HttpClient();
+ var getPropertiesWithPrefix = function(properties,prefix) {
+ var result = Object.create(null);
+ $tw.utils.each(properties,function(value,name) {
+ if(name.indexOf(prefix) === 0) {
+ result[name.substring(prefix.length)] = properties[name];
+ }
+ });
+ return result;
+ };
+ $tw.rootWidget.addEventListener("tm-http-request",function(event) {
+ var params = event.paramObject || {};
+ $tw.httpClient.initiateHttpRequest({
+ wiki: event.widget.wiki,
+ url: params.url,
+ method: params.method,
+ body: params.body,
+ binary: params.binary,
+ useDefaultHeaders: params.useDefaultHeaders,
+ oncompletion: params.oncompletion,
+ onprogress: params.onprogress,
+ bindStatus: params["bind-status"],
+ bindProgress: params["bind-progress"],
+ variables: getPropertiesWithPrefix(params,"var-"),
+ headers: getPropertiesWithPrefix(params,"header-"),
+ passwordHeaders: getPropertiesWithPrefix(params,"password-header-"),
+ queryStrings: getPropertiesWithPrefix(params,"query-"),
+ passwordQueryStrings: getPropertiesWithPrefix(params,"password-query-"),
+ basicAuthUsername: params["basic-auth-username"],
+ basicAuthUsernameFromStore: params["basic-auth-username-from-store"],
+ basicAuthPassword: params["basic-auth-password"],
+ basicAuthPasswordFromStore: params["basic-auth-password-from-store"]
+ });
+ });
+ $tw.rootWidget.addEventListener("tm-http-cancel-all-requests",function(event) {
+ $tw.httpClient.cancelAllHttpRequests();
+ });
// Install the modal message mechanism
$tw.modal = new $tw.utils.Modal($tw.wiki);
$tw.rootWidget.addEventListener("tm-modal",function(event) {
@@ -35,18 +73,17 @@ exports.startup = function() {
});
// Install the copy-to-clipboard mechanism
$tw.rootWidget.addEventListener("tm-copy-to-clipboard",function(event) {
- $tw.utils.copyToClipboard(event.param);
+ $tw.utils.copyToClipboard(event.param,{
+ successNotification: event.paramObject && event.paramObject.successNotification,
+ failureNotification: event.paramObject && event.paramObject.failureNotification
+ });
});
// Install the tm-focus-selector message
$tw.rootWidget.addEventListener("tm-focus-selector",function(event) {
var selector = event.param || "",
element,
- doc = event.event && event.event.target ? event.event.target.ownerDocument : document;
- try {
- element = doc.querySelector(selector);
- } catch(e) {
- console.log("Error in selector: ",selector)
- }
+ baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document;
+ element = $tw.utils.querySelectorSafe(selector,baseElement);
if(element && element.focus) {
element.focus(event.paramObject);
}
@@ -87,13 +124,6 @@ exports.startup = function() {
}
});
}
- // If we're being viewed on a data: URI then give instructions for how to save
- if(document.location.protocol === "data:") {
- $tw.rootWidget.dispatchEvent({
- type: "tm-modal",
- param: "$:/language/Modals/SaveInstructions"
- });
- }
};
})();
diff --git a/core/modules/startup/startup.js b/core/modules/startup/startup.js
index e0990228f..e09f6393f 100755
--- a/core/modules/startup/startup.js
+++ b/core/modules/startup/startup.js
@@ -27,6 +27,11 @@ exports.startup = function() {
if($tw.browser) {
$tw.browser.isIE = (/msie|trident/i.test(navigator.userAgent));
$tw.browser.isFirefox = !!document.mozFullScreenEnabled;
+ // 2023-07-21 Edge returns UA below. So we use "isChromeLike"
+ //'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/114.0.0.0 safari/537.36 edg/114.0.1823.82'
+ $tw.browser.isChromeLike = navigator.userAgent.toLowerCase().indexOf("chrome") > -1;
+ $tw.browser.hasTouch = !!window.matchMedia && window.matchMedia("(pointer: coarse)").matches;
+ $tw.browser.isMobileChrome = $tw.browser.isChromeLike && $tw.browser.hasTouch;
}
// Platform detection
$tw.platform = {};
diff --git a/core/modules/startup/story.js b/core/modules/startup/story.js
index 734f6ae76..c58c759c3 100644
--- a/core/modules/startup/story.js
+++ b/core/modules/startup/story.js
@@ -93,7 +93,9 @@ exports.startup = function() {
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permalink" : "none",
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
targetTiddler: event.param || event.tiddlerTitle,
- copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permalink" : "none"
+ copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permalink" : "none",
+ successNotification: event.paramObject && event.paramObject.successNotification,
+ failureNotification: event.paramObject && event.paramObject.failureNotification
});
});
// Listen for the tm-permaview message
@@ -102,7 +104,9 @@ exports.startup = function() {
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permaview" : "none",
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
targetTiddler: event.param || event.tiddlerTitle,
- copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permaview" : "none"
+ copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permaview" : "none",
+ successNotification: event.paramObject && event.paramObject.successNotification,
+ failureNotification: event.paramObject && event.paramObject.failureNotification
});
});
}
@@ -177,6 +181,8 @@ options.updateAddressBar: "permalink", "permaview" or "no" (defaults to "permavi
options.updateHistory: "yes" or "no" (defaults to "no")
options.copyToClipboard: "permalink", "permaview" or "no" (defaults to "no")
options.targetTiddler: optional title of target tiddler for permalink
+options.successNotification: optional title of tiddler to use as the notification in case of success
+options.failureNotification: optional title of tiddler to use as the notification in case of failure
*/
function updateLocationHash(options) {
// Get the story and the history stack
@@ -205,14 +211,18 @@ function updateLocationHash(options) {
break;
}
// Copy URL to the clipboard
+ var url = "";
switch(options.copyToClipboard) {
case "permalink":
- $tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler));
+ url = $tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler);
break;
case "permaview":
- $tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList)));
+ url = $tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList));
break;
}
+ if(url) {
+ $tw.utils.copyToClipboard(url,{successNotification: options.successNotification, failureNotification: options.failureNotification});
+ }
// Only change the location hash if we must, thus avoiding unnecessary onhashchange events
if($tw.utils.getLocationHash() !== $tw.locationHash) {
if(options.updateHistory === "yes") {
diff --git a/core/modules/startup/windows.js b/core/modules/startup/windows.js
index 384961b7b..34f45d7a5 100644
--- a/core/modules/startup/windows.js
+++ b/core/modules/startup/windows.js
@@ -40,7 +40,7 @@ exports.startup = function() {
variables = $tw.utils.extend({},paramObject,{currentTiddler: title, "tv-window-id": windowID});
// Open the window
var srcWindow,
- srcDocument;
+ srcDocument;
// In case that popup blockers deny opening a new window
try {
srcWindow = window.open("","external-" + windowID,"scrollbars,width=" + width + ",height=" + height + (top ? ",top=" + top : "" ) + (left ? ",left=" + left : "" )),
@@ -52,10 +52,11 @@ exports.startup = function() {
$tw.windows[windowID] = srcWindow;
// Check for reopening the same window
if(srcWindow.haveInitialisedWindow) {
+ srcWindow.focus();
return;
}
// Initialise the document
- srcDocument.write("");
+ srcDocument.write("