diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 7c5774456..000000000 --- a/.eslintignore +++ /dev/null @@ -1,10 +0,0 @@ -# Ignore "third party" code whose style we will not change. -/boot/sjcl.js -/core/modules/utils/base64-utf8/base64-utf8.module.js -/core/modules/utils/base64-utf8/base64-utf8.module.min.js -/core/modules/utils/diff-match-patch/diff_match_patch.js -/core/modules/utils/diff-match-patch/diff_match_patch_uncompressed.js -/core/modules/utils/dom/csscolorparser.js -/plugins/tiddlywiki/*/files/ -/tmp/ -/output/ 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 737d523ef..ed16d707d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,17 +5,17 @@ on: - master - tiddlywiki-com env: - NODE_VERSION: "18" + NODE_VERSION: "22" jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "${{ env.NODE_VERSION }}" - run: "./bin/ci-test.sh" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report @@ -30,8 +30,8 @@ jobs: TW5_BUILD_MAIN_EDITION: "./editions/prerelease" TW5_BUILD_OUTPUT: "./output/prerelease" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "${{ env.NODE_VERSION }}" - run: "./bin/ci-pre-build.sh" @@ -62,8 +62,8 @@ jobs: TW5_BUILD_OUTPUT: "./output" TW5_BUILD_ARCHIVE: "./output" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "${{ env.NODE_VERSION }}" - run: "./bin/ci-pre-build.sh" 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/bin/build-site.sh b/bin/build-site.sh index f308518b4..799cfbed3 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.3.3 + TW5_BUILD_VERSION=v5.3.7 fi echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]" @@ -97,7 +97,6 @@ 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 \ @@ -115,7 +114,6 @@ fi node $TW5_BUILD_TIDDLYWIKI \ $TW5_BUILD_MAIN_EDITION \ - --verbose \ --version \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ @@ -128,7 +126,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /tiddlywikicore-.js Core plugin javascript node $TW5_BUILD_TIDDLYWIKI \ ./editions/empty \ - --verbose \ --output $TW5_BUILD_OUTPUT \ --build empty emptyexternalcore \ || exit 1 @@ -137,7 +134,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /test.html Test edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/test \ - --verbose \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all test.html text/plain \ || exit 1 @@ -150,7 +146,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /dev/static/static.css Static stylesheet node $TW5_BUILD_TIDDLYWIKI \ ./editions/dev \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/dev \ --build index favicon static \ @@ -159,29 +154,13 @@ node $TW5_BUILD_TIDDLYWIKI \ # /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 -# /dev/index.html Developer docs -# /dev/favicon.ico Favicon for dev site -# /dev/static.html Static rendering of default tiddlers -# /dev/alltiddlers.html Static rendering of all tiddlers -# /dev/static/* Static single tiddlers -# /dev/static/static.css Static stylesheet -node $TW5_BUILD_TIDDLYWIKI \ - ./editions/dev \ - --verbose \ - --load $TW5_BUILD_OUTPUT/build.tid \ - --output $TW5_BUILD_OUTPUT/dev \ - --build index favicon static \ - || exit 1 - # /share.html Custom edition for sharing via the URL node $TW5_BUILD_TIDDLYWIKI \ ./editions/share \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --build share \ @@ -190,7 +169,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /upgrade.html Custom edition for performing upgrades node $TW5_BUILD_TIDDLYWIKI \ ./editions/upgrade \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --build upgrade \ @@ -199,7 +177,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /encrypted.html Copy of the main file encrypted with the password "password" node $TW5_BUILD_TIDDLYWIKI \ $TW5_BUILD_MAIN_EDITION \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --build encrypted \ @@ -215,7 +192,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/xlsx-utils/index.html xlsx-utils edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/xlsx-utils \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/xlsx-utils/ \ --build index \ @@ -224,7 +200,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/resumebuilder/index.html Resume builder edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/resumebuilder \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/resumebuilder/ \ --build index \ @@ -233,7 +208,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/text-slicer/index.html Text slicer edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/text-slicer \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/text-slicer/ \ --build index \ @@ -242,7 +216,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/translators/index.html Translators edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/translators \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/translators/ \ --build index \ @@ -251,7 +224,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/introduction/index.html Introduction edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/introduction \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/introduction/ \ --build index \ @@ -260,7 +232,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/full/index.html Full edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/full \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/full/ \ --build index \ @@ -269,7 +240,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/tw5.com-docs/index.html tiddlywiki.com docs edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/tw5.com-docs \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/tw5.com-docs/ \ --build index \ @@ -278,7 +248,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /editions/twitter-archivist/index.html Twitter Archivist edition node $TW5_BUILD_TIDDLYWIKI \ ./editions/twitter-archivist \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/editions/twitter-archivist/ \ --build index \ @@ -294,7 +263,6 @@ node $TW5_BUILD_TIDDLYWIKI \ node $TW5_BUILD_TIDDLYWIKI \ ./editions/innerwikidemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/innerwiki/index.html text/plain \ @@ -305,7 +273,6 @@ node $TW5_BUILD_TIDDLYWIKI \ node $TW5_BUILD_TIDDLYWIKI \ ./editions/dynaviewdemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/dynaview/index.html text/plain \ @@ -320,7 +287,6 @@ node $TW5_BUILD_TIDDLYWIKI \ node $TW5_BUILD_TIDDLYWIKI \ ./editions/katexdemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/katex/index.html text/plain \ @@ -331,7 +297,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/tahoelafs \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \ @@ -342,7 +307,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/d3demo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \ @@ -353,7 +317,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/codemirrordemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/codemirror/index.html text/plain \ @@ -364,7 +327,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/markdown/empty.html Empty wiki with Markdown plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/markdowndemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/markdown/index.html text/plain \ @@ -375,7 +337,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/tw2parser/empty.html Empty wiki with tw2parser plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/classicparserdemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/tw2parser/index.html text/plain \ @@ -386,7 +347,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /plugins/tiddlywiki/highlight/empty.html Empty wiki with highlight plugin node $TW5_BUILD_TIDDLYWIKI \ ./editions/highlightdemo \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT \ --rendertiddler $:/core/save/all plugins/tiddlywiki/highlight/index.html text/plain \ @@ -397,7 +357,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /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 \ @@ -425,7 +384,6 @@ rm -rf $TW5_BUILD_OUTPUT/languages/zh-Hant/static/* # /languages/de-AT/empty.html Empty wiki with de-AT language node $TW5_BUILD_TIDDLYWIKI \ ./editions/de-AT \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/de-AT \ --build favicon empty static index \ @@ -435,7 +393,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/de-DE/empty.html Empty wiki with de-DE language node $TW5_BUILD_TIDDLYWIKI \ ./editions/de-DE \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/de-DE \ --build favicon empty static index \ @@ -445,7 +402,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/es-ES/empty.html Empty wiki with es-ES language node $TW5_BUILD_TIDDLYWIKI \ ./editions/es-ES \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/es-ES \ --build favicon empty static index \ @@ -455,7 +411,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/fr-FR/empty.html Empty wiki with fr-FR language node $TW5_BUILD_TIDDLYWIKI \ ./editions/fr-FR \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/fr-FR \ --build favicon empty static index \ @@ -465,7 +420,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/ja-JP/empty.html Empty wiki with ja-JP language node $TW5_BUILD_TIDDLYWIKI \ ./editions/ja-JP \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/ja-JP \ --build empty index \ @@ -475,7 +429,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/ko-KR/empty.html Empty wiki with ko-KR language node $TW5_BUILD_TIDDLYWIKI \ ./editions/ko-KR \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/ko-KR \ --build favicon empty static index \ @@ -485,7 +438,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/zh-Hans/empty.html Empty wiki with zh-Hans language node $TW5_BUILD_TIDDLYWIKI \ ./editions/zh-Hans \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/zh-Hans \ --build empty index \ @@ -495,7 +447,6 @@ node $TW5_BUILD_TIDDLYWIKI \ # /languages/zh-Hant/empty.html Empty wiki with zh-Hant language node $TW5_BUILD_TIDDLYWIKI \ ./editions/zh-Hant \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/languages/zh-Hant \ --build empty index \ @@ -509,7 +460,6 @@ node $TW5_BUILD_TIDDLYWIKI \ node $TW5_BUILD_TIDDLYWIKI \ ./editions/pluginlibrary \ - --verbose \ --load $TW5_BUILD_OUTPUT/build.tid \ --output $TW5_BUILD_OUTPUT/library/$TW5_BUILD_VERSION \ --build library\ 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/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 ea20c83fd..f1f6e4906 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -232,10 +232,10 @@ $tw.utils.error = function(err) { var link = dm("a"), text = JSON.stringify(tiddlers); if(Blob !== undefined) { - var blob = new Blob([text], {type: "text/html"}); + var blob = new Blob([text], {type: "application/json"}); link.setAttribute("href", URL.createObjectURL(blob)); } else { - link.setAttribute("href","data:text/html," + encodeURIComponent(text)); + link.setAttribute("href","data:application/json," + encodeURIComponent(text)); } link.setAttribute("download","emergency-tiddlers-" + (new Date()) + ".json"); document.body.appendChild(link); @@ -386,8 +386,8 @@ $tw.utils.parseDate = function(value) { parseInt(value.substr(10,2)||"00",10), parseInt(value.substr(12,2)||"00",10), parseInt(value.substr(14,3)||"000",10))); - d.setUTCFullYear(year); // See https://stackoverflow.com/a/5870822 - return d; + d.setUTCFullYear(year); // See https://stackoverflow.com/a/5870822 + return d; } else if($tw.utils.isDate(value)) { return value; } else { @@ -1470,17 +1470,15 @@ $tw.Wiki = function(options) { // Unpack the currently registered plugins, creating shadow tiddlers for their constituent tiddlers this.unpackPluginTiddlers = function() { var self = this; - // Sort the plugin titles by the `plugin-priority` field - pluginTiddlers.sort(function(a,b) { - if("plugin-priority" in a.fields && "plugin-priority" in b.fields) { - return a.fields["plugin-priority"] - b.fields["plugin-priority"]; - } else if("plugin-priority" in a.fields) { + // Sort the plugin titles by the `plugin-priority` field, if this field is missing, default to 1 + pluginTiddlers.sort(function(a, b) { + var priorityA = "plugin-priority" in a.fields ? a.fields["plugin-priority"] : 1; + var priorityB = "plugin-priority" in b.fields ? b.fields["plugin-priority"] : 1; + if (priorityA !== priorityB) { + return priorityA - priorityB; + } else if (a.fields.title < b.fields.title) { return -1; - } else if("plugin-priority" in b.fields) { - return +1; - } else if(a.fields.title < b.fields.title) { - return -1; - } else if(a.fields.title === b.fields.title) { + } else if (a.fields.title === b.fields.title) { return 0; } else { return +1; @@ -2465,13 +2463,15 @@ $tw.boot.initStartup = function(options) { $tw.utils.registerFileType("image/webp","base64",".webp",{flags:["image"]}); $tw.utils.registerFileType("image/heic","base64",".heic",{flags:["image"]}); $tw.utils.registerFileType("image/heif","base64",".heif",{flags:["image"]}); + $tw.utils.registerFileType("image/avif","base64",".avif",{flags:["image"]}); $tw.utils.registerFileType("image/svg+xml","utf8",".svg",{flags:["image"]}); $tw.utils.registerFileType("image/vnd.microsoft.icon","base64",".ico",{flags:["image"]}); $tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]}); $tw.utils.registerFileType("application/wasm","base64",".wasm"); - $tw.utils.registerFileType("application/font-woff","base64",".woff"); - $tw.utils.registerFileType("application/x-font-ttf","base64",".woff"); - $tw.utils.registerFileType("application/font-woff2","base64",".woff2"); + $tw.utils.registerFileType("font/woff","base64",".woff"); + $tw.utils.registerFileType("font/woff2","base64",".woff2"); + $tw.utils.registerFileType("font/ttf","base64",".ttf"); + $tw.utils.registerFileType("font/otf","base64",".otf"); $tw.utils.registerFileType("audio/ogg","base64",".ogg"); $tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]); $tw.utils.registerFileType("video/ogg","base64",[".ogm",".ogv",".ogg"]); diff --git a/boot/bootprefix.js b/boot/bootprefix.js index 0ae236c21..4dae1b28f 100644 --- a/boot/bootprefix.js +++ b/boot/bootprefix.js @@ -21,7 +21,7 @@ $tw.boot = $tw.boot || Object.create(null); // Detect platforms if(!("browser" in $tw)) { - $tw.browser = typeof(window) !== "undefined" ? {} : null; + $tw.browser = typeof(window) !== "undefined" && typeof(document) !== "undefined" ? {} : null; } if(!("node" in $tw)) { $tw.node = typeof(process) === "object" ? {} : null; diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 000000000..e8e7664a7 --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1 @@ +

This 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.

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..707c34110 100644 --- a/contributing.md +++ b/contributing.md @@ -1,3 +1,3 @@ -

Contributing to TiddlyWiki5

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:

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:

These a poorly worded PR titles:

PR titles may also include a short prefix to indicate the subsystem to which they apply. For example:

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:

praisePraises 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
nitpickNitpicks are small, trivial, but necessary changes. Distinguishing nitpick comments significantly helps direct the reader's attention to comments requiring more involvement
suggestionSuggestions 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
issueIssues represent user-facing problems. If possible, it's great to follow this kind of comment with a suggestion
questionQuestions 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
thoughtThoughts 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
choreChores 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

  1. Navigate to licenses/CLA-individual or licenses/CLA-entity according to whether you are signing as an individual or representative of an organisation
  2. Ensure that the "branch" dropdown at the top left is set to tiddlywiki-com
  3. Click the "edit" button at the top-right corner (clicking this button will fork the project so you can edit the file)
  4. Add your name at the bottom
    • eg: Jeremy Ruston, @Jermolene, 2011/11/22
  5. Below the edit box for the CLA text you should see a box labelled Propose file change
  6. Enter a brief title to explain the change (eg, "Signing the CLA")
  7. Click the green button labelled Propose file change
  8. 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". +

Contributing to TiddlyWiki5

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:

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:

These a poorly worded PR titles:

PR titles may also include a short prefix to indicate the subsystem to which they apply. For example:

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:

praisePraises 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
nitpickNitpicks are small, trivial, but necessary changes. Distinguishing nitpick comments significantly helps direct the reader's attention to comments requiring more involvement
suggestionSuggestions 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
issueIssues represent user-facing problems. If possible, it's great to follow this kind of comment with a suggestion
questionQuestions 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
thoughtThoughts 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
choreChores 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

  1. Navigate to licenses/CLA-individual or licenses/CLA-entity according to whether you are signing as an individual or representative of an organisation
  2. Ensure that the "branch" dropdown at the top left is set to tiddlywiki-com
  3. Click the "edit" button at the top-right corner (clicking this button will fork the project so you can edit the file)
  4. Add your name at the bottom
    • eg: Jeremy Ruston, @Jermolene, 2011/11/22
  5. Below the edit box for the CLA text you should see a box labelled Propose file change
  6. Enter a brief title to explain the change (eg, "Signing the CLA")
  7. Click the green button labelled Propose file change
  8. 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 cb54e3d23..e1015e2c9 100644 --- a/core/acknowledgements.tid +++ b/core/acknowledgements.tid @@ -5,7 +5,3 @@ TiddlyWiki incorporates code from these fine OpenSource projects: * [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]] * [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]] * [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]] - -And media from these projects: - -* World flag icons from [[Wikipedia|http://commons.wikimedia.org/wiki/Category:SVG_flags_by_country]] diff --git a/core/copyright.tid b/core/copyright.tid index 3f52380cc..233295ce2 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-2024, UnaMesa Association +Copyright (c) 2007-2025, UnaMesa Association All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/core/images/discord.tid b/core/images/discord.tid new file mode 100644 index 000000000..7510babb4 --- /dev/null +++ b/core/images/discord.tid @@ -0,0 +1,5 @@ +title: $:/core/images/discord +tags: $:/tags/Image + +\parameters (size:"22pt") +> height=<> class="tc-image-discord tc-image-button" viewBox="0 -28.5 256 256"> \ 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/language.tid b/core/images/language.tid new file mode 100644 index 000000000..bb4061495 --- /dev/null +++ b/core/images/language.tid @@ -0,0 +1,5 @@ +title: $:/core/images/language +tags: $:/tags/Image + +\parameters (size:"22pt") +> height=<> class="tc-image-language tc-image-button" viewBox="0 0 92 92"> \ No newline at end of file diff --git a/core/language/en-GB/Buttons.multids b/core/language/en-GB/Buttons.multids index 3ee898b4f..2fa732fd9 100644 --- a/core/language/en-GB/Buttons.multids +++ b/core/language/en-GB/Buttons.multids @@ -80,6 +80,7 @@ NewMarkdown/Caption: new Markdown tiddler NewMarkdown/Hint: Create a new Markdown tiddler NewTiddler/Caption: new tiddler NewTiddler/Hint: Create a new tiddler +OpenControlPanel/Hint: Open control panel OpenWindow/Caption: open in new window OpenWindow/Hint: Open tiddler in new window Palette/Caption: palette @@ -104,6 +105,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 @@ -130,6 +133,7 @@ Excise/Caption/Replace/Link: link Excise/Caption/Replace/Transclusion: transclusion Excise/Caption/Tag: Tag new tiddler with the title of this tiddler Excise/Caption/TiddlerExists: Warning: tiddler already exists +Excise/DefaultTitle: New Excision Excise/Hint: Excise the selected text into a new tiddler Heading1/Caption: heading 1 Heading1/Hint: Apply heading level 1 formatting to lines containing selection diff --git a/core/language/en-GB/ControlPanel.multids b/core/language/en-GB/ControlPanel.multids index 93cfc3c10..129dab0b4 100644 --- a/core/language/en-GB/ControlPanel.multids +++ b/core/language/en-GB/ControlPanel.multids @@ -96,6 +96,10 @@ Plugins/PluginWillRequireReload: (requires reload) Plugins/Plugins/Caption: Plugins Plugins/Plugins/Hint: Plugins Plugins/Reinstall/Caption: reinstall +Plugins/Stability/Deprecated: DEPRECATED +Plugins/Stability/Experimental: EXPERIMENTAL +Plugins/Stability/Legacy: LEGACY +Plugins/Stability/Stable: STABLE Plugins/Themes/Caption: Themes Plugins/Themes/Hint: Theme plugins Plugins/Update/Caption: update @@ -171,6 +175,8 @@ Settings/NavigationPermalinkviewMode/UpdateAddressBar/Description: Update addres Settings/PerformanceInstrumentation/Caption: Performance Instrumentation Settings/PerformanceInstrumentation/Hint: Displays performance statistics in the browser developer console. Requires reload to take effect Settings/PerformanceInstrumentation/Description: Enable performance instrumentation +Settings/RecentLimit/Caption: Recent Tab Limit +Settings/RecentLimit/Hint: Maximum number of tiddlers to be displayed under the sidebar "Recent" tab Settings/ToolbarButtonStyle/Caption: Toolbar Button Style Settings/ToolbarButtonStyle/Hint: Choose the style for toolbar buttons: Settings/ToolbarButtonStyle/Styles/Borderless: Borderless @@ -198,6 +204,12 @@ Settings/TitleLinks/Yes/Description: Display tiddler titles as links Settings/MissingLinks/Caption: Wiki Links Settings/MissingLinks/Hint: Choose whether to link to tiddlers that do not exist yet Settings/MissingLinks/Description: Enable links to missing tiddlers +SocialCard/Caption: Social Media Card +SocialCard/Domain/Prompt: Domain name to display for the link (for example, ''tiddlywiki.com'') +SocialCard/Hint: This information is used by social and messaging services to display a preview card for links to this ~TiddlyWiki when hosted online +SocialCard/PreviewUrl/Prompt: Full URL to preview image for this ~TiddlyWiki +SocialCard/PreviewUrl/Preview: Preview image: +SocialCard/Url/Prompt: Full URL of this ~TiddlyWiki StoryTiddler/Caption: Story Tiddler StoryTiddler/Hint: This rule cascade is used to dynamically choose the template for displaying a tiddler in the story river. StoryView/Caption: Story View @@ -235,3 +247,7 @@ ViewTemplateBody/Caption: View Template Body ViewTemplateBody/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the body of a tiddler. ViewTemplateTitle/Caption: View Template Title ViewTemplateTitle/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the title of a tiddler. +ViewTemplateSubtitle/Caption: View Template Subtitle +ViewTemplateSubtitle/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the subtitle of a tiddler. +ViewTemplateTags/Caption: View Template Tags +ViewTemplateTags/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the tags area of a tiddler. diff --git a/core/language/en-GB/Docs/PaletteColours.multids b/core/language/en-GB/Docs/PaletteColours.multids index 1c671a67c..bc1b36c3d 100644 --- a/core/language/en-GB/Docs/PaletteColours.multids +++ b/core/language/en-GB/Docs/PaletteColours.multids @@ -65,6 +65,10 @@ 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 diff --git a/core/language/en-GB/EditTemplate.multids b/core/language/en-GB/EditTemplate.multids index c4bfa5e56..8b2e6e17a 100644 --- a/core/language/en-GB/EditTemplate.multids +++ b/core/language/en-GB/EditTemplate.multids @@ -26,6 +26,8 @@ Tags/ClearInput/Caption: clear input Tags/ClearInput/Hint: Clear tag input Tags/Dropdown/Caption: tag list Tags/Dropdown/Hint: Show tag list +Tags/EmptyMessage: No tags found +Tags/EmptyMessage/System: No system tags found Title/BadCharacterWarning: Warning: avoid using any of the characters <> in tiddler titles Title/Exists/Prompt: Target tiddler already exists Title/Relink/Prompt: Update ''<$text text=<>/>'' to ''<$text text=<>/>'' in the //tags// and //list// fields of other tiddlers diff --git a/core/language/en-GB/Search.multids b/core/language/en-GB/Search.multids index 2a57a6416..f5aa478bf 100644 --- a/core/language/en-GB/Search.multids +++ b/core/language/en-GB/Search.multids @@ -6,6 +6,8 @@ Filter/Hint: Search via a [[filter expression|https://tiddlywiki.com/static/Filt Filter/Matches: //<> matches// Matches: //<> matches// Matches/All: All matches: +Matches/NoMatch: //No match// +Matches/NoResult: //No search result// Matches/Title: Title matches: Search: Search Search/TooShort: Search text too short diff --git a/core/language/en-GB/Snippets/FunctionDefinition.tid b/core/language/en-GB/Snippets/FunctionDefinition.tid new file mode 100644 index 000000000..e000e38b1 --- /dev/null +++ b/core/language/en-GB/Snippets/FunctionDefinition.tid @@ -0,0 +1,7 @@ +title: $:/language/Snippets/FunctionDefinition +tags: $:/tags/TextEditor/Snippet +caption: Function definition + +\function f.name(param1,param2:"default value") [!is[blank]else] + +<> diff --git a/core/language/en-GB/Snippets/ProcedureDefinition.tid b/core/language/en-GB/Snippets/ProcedureDefinition.tid new file mode 100644 index 000000000..632abcc01 --- /dev/null +++ b/core/language/en-GB/Snippets/ProcedureDefinition.tid @@ -0,0 +1,7 @@ +title: $:/language/Snippets/ProcedureDefinition +tags: $:/tags/TextEditor/Snippet +caption: Procedure definition + +\procedure procName(param1:"default value",param2) +Your text comes here. +\end diff --git a/core/language/en-GB/Types/image_x-icon.tid b/core/language/en-GB/Types/image_x-icon.tid deleted file mode 100644 index 55420387a..000000000 --- a/core/language/en-GB/Types/image_x-icon.tid +++ /dev/null @@ -1,5 +0,0 @@ -title: $:/language/Docs/Types/image/x-icon -description: ICO icon -name: image/x-icon -group: Image -group-sort: 1 diff --git a/core/language/en-GB/Types/text_vnd.tiddlywiki_multiple.tid b/core/language/en-GB/Types/text_vnd.tiddlywiki_multiple.tid new file mode 100644 index 000000000..af15d7ac3 --- /dev/null +++ b/core/language/en-GB/Types/text_vnd.tiddlywiki_multiple.tid @@ -0,0 +1,5 @@ +title: $:/language/Docs/Types/text/vnd.tiddlywiki-multiple +description: Compound tiddler +name: text/vnd.tiddlywiki-multiple +group: Developer +group-sort: 2 diff --git a/core/language/en-GB/icon.tid b/core/language/en-GB/icon.tid deleted file mode 100644 index 1967b895f..000000000 --- a/core/language/en-GB/icon.tid +++ /dev/null @@ -1,13 +0,0 @@ -title: $:/languages/en-GB/icon -type: image/svg+xml - - - - - - - - - - - diff --git a/core/modules/commands/build.js b/core/modules/commands/build.js index 472248823..48e9a92a2 100644 --- a/core/modules/commands/build.js +++ b/core/modules/commands/build.js @@ -23,7 +23,7 @@ var Command = function(params,commander) { Command.prototype.execute = function() { // Get the build targets defined in the wiki - var buildTargets = $tw.boot.wikiInfo.build; + var buildTargets = $tw.boot.wikiInfo && $tw.boot.wikiInfo.build; if(!buildTargets) { return "No build targets defined"; } diff --git a/core/modules/commands/render.js b/core/modules/commands/render.js index 7f192a734..80d8f5bd5 100644 --- a/core/modules/commands/render.js +++ b/core/modules/commands/render.js @@ -7,56 +7,64 @@ Render individual tiddlers and save the results to the specified files \*/ -/*jslint node: true, browser: true */ -/*global $tw: false */ -"use strict"; - -var widget = require("$:/core/modules/widgets/widget.js"); - -exports.info = { - name: "render", - synchronous: true -}; - -var Command = function(params,commander,callback) { - this.params = params; - this.commander = commander; - this.callback = callback; -}; - -Command.prototype.execute = function() { - if(this.params.length < 1) { - return "Missing tiddler filter"; - } - var self = this, - fs = require("fs"), - path = require("path"), - wiki = this.commander.wiki, - tiddlerFilter = this.params[0], - filenameFilter = this.params[1] || "[is[tiddler]addsuffix[.html]]", - type = this.params[2] || "text/html", - template = this.params[3], - variableList = this.params.slice(4), - tiddlers = wiki.filterTiddlers(tiddlerFilter), - variables = Object.create(null); - while(variableList.length >= 2) { - variables[variableList[0]] = variableList[1]; - variableList = variableList.slice(2); + /*jslint node: true, browser: true */ + /*global $tw: false */ + "use strict"; + + var widget = require("$:/core/modules/widgets/widget.js"); + + exports.info = { + name: "render", + synchronous: true + }; + + var Command = function(params,commander,callback) { + this.params = params; + this.commander = commander; + this.callback = callback; + }; + + Command.prototype.execute = function() { + if(this.params.length < 1) { + return "Missing tiddler filter"; } - $tw.utils.each(tiddlers,function(title) { - var filepath = path.resolve(self.commander.outputPath,wiki.filterTiddlers(filenameFilter,$tw.rootWidget,wiki.makeTiddlerIterator([title]))[0]); - if(self.commander.verbose) { - console.log("Rendering \"" + title + "\" to \"" + filepath + "\""); - } - var parser = wiki.parseTiddler(template || title), - widgetNode = wiki.makeWidget(parser,{variables: $tw.utils.extend({},variables,{currentTiddler: title,storyTiddler: title})}), - container = $tw.fakeDocument.createElement("div"); - widgetNode.render(container,null); - var text = type === "text/html" ? container.innerHTML : container.textContent; - $tw.utils.createFileDirectories(filepath); - fs.writeFileSync(filepath,text,"utf8"); - }); - return null; -}; - -exports.Command = Command; + var self = this, + fs = require("fs"), + path = require("path"), + wiki = this.commander.wiki, + tiddlerFilter = this.params[0], + filenameFilter = this.params[1] || "[is[tiddler]addsuffix[.html]]", + type = this.params[2] || "text/html", + template = this.params[3], + variableList = this.params.slice(4), + tiddlers = wiki.filterTiddlers(tiddlerFilter), + variables = Object.create(null); + while(variableList.length >= 2) { + variables[variableList[0]] = variableList[1]; + variableList = variableList.slice(2); + } + $tw.utils.each(tiddlers,function(title) { + var filenameResults = wiki.filterTiddlers(filenameFilter,$tw.rootWidget,wiki.makeTiddlerIterator([title])); + if(filenameResults.length > 0) { + var filepath = path.resolve(self.commander.outputPath,filenameResults[0]); + if(self.commander.verbose) { + console.log("Rendering \"" + title + "\" to \"" + filepath + "\""); + } + var parser = wiki.parseTiddler(template || title), + widgetNode = wiki.makeWidget(parser,{variables: $tw.utils.extend({},variables,{currentTiddler: title,storyTiddler: title})}), + container = $tw.fakeDocument.createElement("div"); + widgetNode.render(container,null); + var text = type === "text/html" ? container.innerHTML : container.textContent; + $tw.utils.createFileDirectories(filepath); + fs.writeFileSync(filepath,text,"utf8"); + } else { + console.log("Not rendering \"" + title + "\" because the filename filter returned an empty result"); + } + }); + return null; + }; + + exports.Command = Command; + + })(); + \ No newline at end of file diff --git a/core/modules/config.js b/core/modules/config.js index 1847b976d..e0314b098 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, 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.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, nobreak:8288, NoBreak:8288, 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/operations/text/excise.js b/core/modules/editor/operations/text/excise.js index 18584dce4..c3b6c4b4f 100644 --- a/core/modules/editor/operations/text/excise.js +++ b/core/modules/editor/operations/text/excise.js @@ -12,20 +12,27 @@ Text editor operation to excise the selection to a new tiddler /*global $tw: false */ "use strict"; +function isMarkdown(mediaType) { + return mediaType === 'text/markdown' || mediaType === 'text/x-markdown'; +} + exports["excise"] = function(event,operation) { var editTiddler = this.wiki.getTiddler(this.editTitle), - editTiddlerTitle = this.editTitle; + editTiddlerTitle = this.editTitle, + wikiLinks = !isMarkdown(editTiddler.fields.type), + excisionBaseTitle = $tw.language.getString("Buttons/Excise/DefaultTitle"); if(editTiddler && editTiddler.fields["draft.of"]) { editTiddlerTitle = editTiddler.fields["draft.of"]; } - var excisionTitle = event.paramObject.title || this.wiki.generateNewTitle("New Excision"); + var excisionTitle = event.paramObject.title || this.wiki.generateNewTitle(excisionBaseTitle); this.wiki.addTiddler(new $tw.Tiddler( this.wiki.getCreationFields(), this.wiki.getModificationFields(), { title: excisionTitle, text: operation.selection, - tags: event.paramObject.tagnew === "yes" ? [editTiddlerTitle] : [] + tags: event.paramObject.tagnew === "yes" ? [editTiddlerTitle] : [], + type: editTiddler.fields.type } )); operation.replacement = excisionTitle; @@ -34,7 +41,8 @@ exports["excise"] = function(event,operation) { operation.replacement = "{{" + operation.replacement+ "}}"; break; case "link": - operation.replacement = "[[" + operation.replacement+ "]]"; + operation.replacement = wikiLinks ? "[[" + operation.replacement+ "]]" + : ("[" + operation.replacement + "](<#" + operation.replacement + ">)"); break; case "macro": operation.replacement = "<<" + (event.paramObject.macro || "translink") + " \"\"\"" + operation.replacement + "\"\"\">>"; diff --git a/core/modules/editor/operations/text/wrap-selection.js b/core/modules/editor/operations/text/wrap-selection.js index 0276fd573..ad4c96258 100644 --- a/core/modules/editor/operations/text/wrap-selection.js +++ b/core/modules/editor/operations/text/wrap-selection.js @@ -13,37 +13,125 @@ Text editor operation to wrap the selection with the specified prefix and suffix "use strict"; exports["wrap-selection"] = function(event,operation) { - if(operation.selStart === operation.selEnd) { - // No selection; check if we're within the prefix/suffix - if(operation.text.substring(operation.selStart - event.paramObject.prefix.length,operation.selStart + event.paramObject.suffix.length) === event.paramObject.prefix + event.paramObject.suffix) { + var o = operation, + prefix = event.paramObject.prefix, + suffix = event.paramObject.suffix, + trimSelection = event.paramObject.trimSelection || "no", + selLength = o.selEnd - o.selStart; + + // This function detects, if trailing spaces are part of the selection __and__ if the user wants to handle them + // Returns "yes", "start", "end", "no" (default) + // yes .. there are trailing spaces at both ends + // start .. there are trailing spaces at the start + // end .. there are trailing spaces at the end + // no .. no trailing spaces are taken into account + var trailingSpaceAt = function(sel) { + var _start, + _end, + result; + // trimSelection is a user parameter, which this evaluations takes into account + switch(trimSelection) { + case "end": + result = (sel.trimEnd().length !== selLength) ? "end" : "no"; + break; + case "yes": + _start = sel.trimStart().length !== selLength; + _end = sel.trimEnd().length !== selLength; + result = (_start && _end) ? "yes" : (_start) ? "start" : (_end) ? "end" : "no"; + break; + case "start": + result = (sel.trimStart().length !== selLength) ? "start" : "no"; + break; + default: + result = "no"; + break; + } + return result; + } + + function togglePrefixSuffix() { + if(o.text.substring(o.selStart - prefix.length, o.selStart + suffix.length) === prefix + suffix) { // Remove the prefix and suffix - operation.cutStart = operation.selStart - event.paramObject.prefix.length; - operation.cutEnd = operation.selEnd + event.paramObject.suffix.length; - operation.replacement = ""; - operation.newSelStart = operation.cutStart; - operation.newSelEnd = operation.newSelStart; + o.cutStart = o.selStart - prefix.length; + o.cutEnd = o.selEnd + suffix.length; + o.replacement = ""; + o.newSelStart = o.cutStart; + o.newSelEnd = o.newSelStart; } else { // Wrap the cursor instead - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = event.paramObject.prefix + event.paramObject.suffix; - operation.newSelStart = operation.selStart + event.paramObject.prefix.length; - operation.newSelEnd = operation.newSelStart; + o.cutStart = o.selStart; + o.cutEnd = o.selEnd; + o.replacement = prefix + suffix; + o.newSelStart = o.selStart + prefix.length; + o.newSelEnd = o.newSelStart; } - } else if(operation.text.substring(operation.selStart,operation.selStart + event.paramObject.prefix.length) === event.paramObject.prefix && operation.text.substring(operation.selEnd - event.paramObject.suffix.length,operation.selEnd) === event.paramObject.suffix) { + } + + // options: lenPrefix, lenSuffix + function removePrefixSuffix(options) { + options = options || {}; + var _lenPrefix = options.lenPrefix || 0; + var _lenSuffix = options.lenSuffix || 0; + + o.cutStart = o.selStart - _lenPrefix; + o.cutEnd = o.selEnd + _lenSuffix; + o.replacement = (_lenPrefix || _lenSuffix) ? o.selection : o.selection.substring(prefix.length, o.selection.length - suffix.length); + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + } + + function addPrefixSuffix() { + // remove trailing space if requested + switch(trailingSpaceAt(o.selection)) { + case "no": + // has no trailing spaces + o.cutStart = o.selStart; + o.cutEnd = o.selEnd; + o.replacement = prefix + o.selection + suffix; + o.newSelStart = o.selStart; + o.newSelEnd = o.selStart + o.replacement.length; + break; + case "yes": + // handle both ends + o.cutStart = o.selEnd - (o.selection.trimStart().length); + o.cutEnd = o.selection.trimEnd().length + o.selStart; + o.replacement = prefix + o.selection.trim() + suffix; + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + break; + case "start": + // handle leading + o.cutStart = o.selEnd - (o.selection.trimStart().length); + o.cutEnd = o.selEnd; + o.replacement = prefix + o.selection.trimStart() + suffix; + o.newSelStart = o.cutStart; + o.newSelEnd = o.cutStart + o.replacement.length; + break; + case "end": + // handle trailing + o.cutStart = o.selStart; + o.cutEnd = o.selection.trimEnd().length + o.selStart; + o.replacement = prefix + o.selection.trimEnd() + suffix; + o.newSelStart = o.selStart; + o.newSelEnd = o.selStart + o.replacement.length; + break; + } + } + + if(o.selStart === o.selEnd) { + // No selection; Create prefix and suffix. Set cursor in between them: ""|"" + togglePrefixSuffix(); + } else if(o.text.substring(o.selStart, o.selStart + prefix.length) === prefix && + o.text.substring(o.selEnd - suffix.length,o.selEnd) === suffix) { // Prefix and suffix are already present, so remove them - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = operation.selection.substring(event.paramObject.prefix.length,operation.selection.length - event.paramObject.suffix.length); - operation.newSelStart = operation.selStart; - operation.newSelEnd = operation.selStart + operation.replacement.length; + removePrefixSuffix(); + } else if(o.text.substring(o.selStart - prefix.length, o.selStart) === prefix && + o.text.substring(o.selEnd, o.selEnd + suffix.length) === suffix) { + // Prefix and suffix are present BUT not selected -> remove them + removePrefixSuffix({"lenPrefix": prefix.length, "lenSuffix": suffix.length}); } else { // Add the prefix and suffix - operation.cutStart = operation.selStart; - operation.cutEnd = operation.selEnd; - operation.replacement = event.paramObject.prefix + operation.selection + event.paramObject.suffix; - operation.newSelStart = operation.selStart; - operation.newSelEnd = operation.selStart + operation.replacement.length; + addPrefixSuffix(); } }; diff --git a/core/modules/filters/backtranscludes.js b/core/modules/filters/backtranscludes.js index 7d4215073..253b9dd7b 100644 --- a/core/modules/filters/backtranscludes.js +++ b/core/modules/filters/backtranscludes.js @@ -16,11 +16,11 @@ Filter operator for returning all the backtranscludes from a tiddler Export our filter function */ exports.backtranscludes = function(source,operator,options) { - var results = []; + var results = new $tw.utils.LinkedList(); source(function(tiddler,title) { - $tw.utils.pushTop(results,options.wiki.getTiddlerBacktranscludes(title)); + results.pushTop(options.wiki.getTiddlerBacktranscludes(title)); }); - return results; + return results.makeTiddlerIterator(options.wiki); }; })(); diff --git a/core/modules/filters/encodings.js b/core/modules/filters/encodings.js index 429cb06ab..670b637d1 100644 --- a/core/modules/filters/encodings.js +++ b/core/modules/filters/encodings.js @@ -17,8 +17,8 @@ Export our filter functions exports.decodebase64 = function(source,operator,options) { var results = []; - var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1; - var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1; + var binary = operator.suffixes && operator.suffixes[0].indexOf("binary") !== -1; + var urlsafe = operator.suffixes && operator.suffixes[0].indexOf("urlsafe") !== -1; source(function(tiddler,title) { results.push($tw.utils.base64Decode(title,binary,urlsafe)); }); @@ -27,8 +27,8 @@ exports.decodebase64 = function(source,operator,options) { exports.encodebase64 = function(source,operator,options) { var results = []; - var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1; - var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1; + var binary = operator.suffixes && operator.suffixes[0].indexOf("binary") !== -1; + var urlsafe = operator.suffixes && operator.suffixes[0].indexOf("urlsafe") !== -1; source(function(tiddler,title) { results.push($tw.utils.base64Encode(title,binary,urlsafe)); }); diff --git a/core/modules/filters/function.js b/core/modules/filters/function.js index c4e90f7c4..5972f7dce 100644 --- a/core/modules/filters/function.js +++ b/core/modules/filters/function.js @@ -17,19 +17,24 @@ Export our filter function */ exports.function = function(source,operator,options) { var functionName = operator.operands[0], - params = []; + params = [], + results; $tw.utils.each(operator.operands.slice(1),function(param) { params.push({value: param}); }); + // console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`); var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source}); if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { - return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text]; + results = 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); - }); + if(!results) { + results = []; + source(function(tiddler,title) { + results.push(title); + }); + } + // console.log(`function ${functionName} with params ${JSON.stringify(params)} results: ${JSON.stringify(results)}`); return results; }; diff --git a/core/modules/filters/math.js b/core/modules/filters/math.js index 80624cc9e..5dba7c742 100644 --- a/core/modules/filters/math.js +++ b/core/modules/filters/math.js @@ -128,7 +128,7 @@ exports.minall = makeNumericReducingOperator( exports.median = makeNumericArrayOperator( function(values) { var len = values.length, median; - values.sort(); + values.sort(function(a,b) {return a-b}); if(len % 2) { // Odd, return the middle number median = values[(len - 1) / 2]; diff --git a/core/modules/filters/moduleproperty.js b/core/modules/filters/moduleproperty.js index 2a146be99..371a3b84e 100644 --- a/core/modules/filters/moduleproperty.js +++ b/core/modules/filters/moduleproperty.js @@ -18,9 +18,16 @@ Export our filter function exports.moduleproperty = function(source,operator,options) { var results = []; source(function(tiddler,title) { - var value = require(title)[operator.operand || ""]; - if(value !== undefined) { - results.push(value); + try { + var value = require(title)[operator.operand || ""]; + if(value !== undefined) { + if(typeof value !== "string") { + value = JSON.stringify(value); + } + results.push(value); + } + } catch(e) { + // Do nothing. It probably wasn't a module. } }); results.sort(); diff --git a/core/modules/filters/transcludes.js b/core/modules/filters/transcludes.js index bd618296b..8f42b3bae 100644 --- a/core/modules/filters/transcludes.js +++ b/core/modules/filters/transcludes.js @@ -20,7 +20,7 @@ exports.transcludes = function(source,operator,options) { source(function(tiddler,title) { results.pushTop(options.wiki.getTiddlerTranscludes(title)); }); - return results.toArray(); + return results.makeTiddlerIterator(options.wiki); }; })(); diff --git a/core/modules/indexers/back-indexer.js b/core/modules/indexers/back-indexer.js index b9daf3328..77b51b819 100644 --- a/core/modules/indexers/back-indexer.js +++ b/core/modules/indexers/back-indexer.js @@ -75,7 +75,7 @@ BackSubIndexer.prototype._getTarget = function(tiddler) { } var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {}); if(parser) { - return this.wiki[this.extractor](parser.tree); + return this.wiki[this.extractor](parser.tree, tiddler.fields.title); } return []; } diff --git a/core/modules/macros/unusedtitle.js b/core/modules/macros/unusedtitle.js index 36684e9a2..aaf2aea09 100644 --- a/core/modules/macros/unusedtitle.js +++ b/core/modules/macros/unusedtitle.js @@ -16,19 +16,21 @@ exports.name = "unusedtitle"; exports.params = [ {name: "baseName"}, {name: "separator"}, - {name: "template"} + {name: "template"}, + {name: "startCount"} ]; /* Run the macro */ -exports.run = function(baseName,separator,template) { +exports.run = function(baseName,separator,template,startCount) { separator = separator || " "; + startCount = startCount || 0; if(!baseName) { baseName = $tw.language.getString("DefaultNewTiddlerTitle"); } // $tw.wiki.generateNewTitle = function(baseTitle,options) - // options.prefix must be a string! - return this.wiki.generateNewTitle(baseName, {"prefix": separator, "template": template}); + // options.prefix must be a string! + return this.wiki.generateNewTitle(baseName, {"prefix": separator, "template": template, "startCount": startCount}).trim(); }; diff --git a/core/modules/parsers/imageparser.js b/core/modules/parsers/imageparser.js index 3dd49f8b2..b8530da11 100644 --- a/core/modules/parsers/imageparser.js +++ b/core/modules/parsers/imageparser.js @@ -40,6 +40,7 @@ exports["image/gif"] = ImageParser; exports["image/webp"] = ImageParser; exports["image/heic"] = ImageParser; exports["image/heif"] = ImageParser; +exports["image/avif"] = ImageParser; exports["image/x-icon"] = ImageParser; exports["image/vnd.microsoft.icon"] = ImageParser; diff --git a/core/modules/parsers/wikiparser/rules/conditional.js b/core/modules/parsers/wikiparser/rules/conditional.js index 23940fd88..c2d6a43b8 100644 --- a/core/modules/parsers/wikiparser/rules/conditional.js +++ b/core/modules/parsers/wikiparser/rules/conditional.js @@ -6,7 +6,7 @@ module-type: wikirule Conditional shortcut syntax ``` -This is a <% if [{something}] %>Elephant<% elseif [{else}] %>Pelican<% else %>Crocodile<% endif %> +This is a <%if [{something}] %>Elephant<%elseif [{else}] %>Pelican<%else%>Crocodile<%endif%> ``` \*/ @@ -27,7 +27,7 @@ exports.init = function(parser) { }; exports.findNextMatch = function(startPos) { - // Look for the next <% if shortcut + // 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 diff --git a/core/modules/parsers/wikiparser/rules/table.js b/core/modules/parsers/wikiparser/rules/table.js index 63764724c..b03886af7 100644 --- a/core/modules/parsers/wikiparser/rules/table.js +++ b/core/modules/parsers/wikiparser/rules/table.js @@ -93,11 +93,12 @@ var processRow = function(prevColumns) { } // Check whether this is a heading cell var cell; + var start = this.parser.pos; if(chr === "!") { this.parser.pos++; - cell = {type: "element", tag: "th", children: []}; + cell = {type: "element", tag: "th", start: start, children: []}; } else { - cell = {type: "element", tag: "td", children: []}; + cell = {type: "element", tag: "td", start: start, children: []}; } tree.push(cell); // Record information about this cell @@ -121,6 +122,7 @@ var processRow = function(prevColumns) { } // Move back to the closing `|` this.parser.pos--; + cell.end = this.parser.pos; } col++; cellRegExp.lastIndex = this.parser.pos; @@ -169,12 +171,13 @@ exports.parse = function() { rowContainer.children = this.parser.parseInlineRun(rowTermRegExp,{eatTerminator: true}); } else { // Create the row - var theRow = {type: "element", tag: "tr", children: []}; + var theRow = {type: "element", tag: "tr", children: [], start: rowMatch.index}; $tw.utils.addClassToParseTreeNode(theRow,rowCount%2 ? "oddRow" : "evenRow"); rowContainer.children.push(theRow); // Process the row theRow.children = processRow.call(this,prevColumns); this.parser.pos = rowMatch.index + rowMatch[0].length; + theRow.end = this.parser.pos; // Increment the row count rowCount++; } diff --git a/core/modules/saver-handler.js b/core/modules/saver-handler.js index bfbe7bcc5..96ff6bede 100644 --- a/core/modules/saver-handler.js +++ b/core/modules/saver-handler.js @@ -46,8 +46,10 @@ function SaverHandler(options) { // Filter the changes so that we only count changes to tiddlers that we care about var filteredChanges = self.filterFn.call(self.wiki,function(iterator) { $tw.utils.each(changes,function(change,title) { - var tiddler = self.wiki.getTiddler(title); - iterator(tiddler,title); + if(change.normal) { + var tiddler = self.wiki.getTiddler(title); + iterator(tiddler,title); + } }); }); // Adjust the number of changes @@ -95,6 +97,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 +105,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 +151,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); @@ -179,7 +185,7 @@ SaverHandler.prototype.saveWiki = function(options) { // Call the highest priority saver that supports this method for(var t=this.savers.length-1; t>=0; t--) { var saver = this.savers[t]; - if(saver.info.capabilities.indexOf(method) !== -1 && saver.save(text,method,callback,{variables: {filename: variables.filename}})) { + if(saver.info.capabilities.indexOf(method) !== -1 && saver.save(text,method,callback,{variables: {filename: variables.filename, type: variables.type}})) { this.logger.log("Saving wiki with method",method,"through saver",saver.info.name); return true; } diff --git a/core/modules/savers/download.js b/core/modules/savers/download.js index 9849d9ab0..74f3aa059 100644 --- a/core/modules/savers/download.js +++ b/core/modules/savers/download.js @@ -22,6 +22,7 @@ DownloadSaver.prototype.save = function(text,method,callback,options) { options = options || {}; // Get the current filename var filename = options.variables.filename; + var type = options.variables.type; if(!filename) { var p = document.location.pathname.lastIndexOf("/"); if(p !== -1) { @@ -32,13 +33,16 @@ DownloadSaver.prototype.save = function(text,method,callback,options) { if(!filename) { filename = "tiddlywiki.html"; } + if(!type) { + type = "text/html"; + } // Set up the link var link = document.createElement("a"); if(Blob !== undefined) { - var blob = new Blob([text], {type: "text/html"}); + var blob = new Blob([text], {type: type}); link.setAttribute("href", URL.createObjectURL(blob)); } else { - link.setAttribute("href","data:text/html," + encodeURIComponent(text)); + link.setAttribute("href","data:" + type + "," + encodeURIComponent(text)); } link.setAttribute("download",filename); document.body.appendChild(link); diff --git a/core/modules/savers/put.js b/core/modules/savers/put.js index 0493343fb..b2bcfdd67 100644 --- a/core/modules/savers/put.js +++ b/core/modules/savers/put.js @@ -20,7 +20,7 @@ Retrieve ETag if available */ var retrieveETag = function(self) { var headers = { - Accept: "*/*;charset=UTF-8" + Accept: "*/*" }; $tw.utils.httpRequest({ url: self.uri(), @@ -48,14 +48,14 @@ var PutSaver = function(wiki) { var self = this; var uri = this.uri(); // Async server probe. Until probe finishes, save will fail fast - // See also https://github.com/Jermolene/TiddlyWiki5/issues/2276 + // See also https://github.com/TiddlyWiki/TiddlyWiki5/issues/2276 $tw.utils.httpRequest({ url: uri, type: "OPTIONS", callback: function(err,data,xhr) { // Check DAV header http://www.webdav.org/specs/rfc2518.html#rfc.section.9.1 if(!err) { - self.serverAcceptsPuts = xhr.status === 200 && !!xhr.getResponseHeader("dav"); + self.serverAcceptsPuts = xhr.status >= 200 && xhr.status < 300 && !!xhr.getResponseHeader("dav"); } } }); diff --git a/core/modules/server/routes/get-login-basic.js b/core/modules/server/routes/get-login-basic.js index ac14e2517..61e6123b1 100644 --- a/core/modules/server/routes/get-login-basic.js +++ b/core/modules/server/routes/get-login-basic.js @@ -24,7 +24,7 @@ exports.handler = function(request,response,state) { response.end(); } else { // Redirect to the root wiki if login worked - var location = ($tw.syncadaptor && $tw.syncadaptor.host)? $tw.syncadaptor.host: "/"; + var location = ($tw.syncadaptor && $tw.syncadaptor.host)? $tw.syncadaptor.host: `${state.pathPrefix}/`; response.writeHead(302,{ Location: location }); diff --git a/core/modules/startup/plugins.js b/core/modules/startup/plugins.js index 9221c7968..dfa0d21b4 100644 --- a/core/modules/startup/plugins.js +++ b/core/modules/startup/plugins.js @@ -61,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; }); @@ -75,7 +75,7 @@ exports.startup = function() { $tw.wiki.unpackPluginTiddlers(); // Queue change events for the changed shadow tiddlers $tw.utils.each(Object.keys(changedShadowTiddlers),function(title) { - $tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title]); + $tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title], true); }); } } diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js index 5069ac6d9..e55786e3b 100644 --- a/core/modules/startup/rootwidget.js +++ b/core/modules/startup/rootwidget.js @@ -52,7 +52,9 @@ exports.startup = function() { basicAuthUsername: params["basic-auth-username"], basicAuthUsernameFromStore: params["basic-auth-username-from-store"], basicAuthPassword: params["basic-auth-password"], - basicAuthPasswordFromStore: params["basic-auth-password-from-store"] + basicAuthPasswordFromStore: params["basic-auth-password-from-store"], + bearerAuthToken: params["bearer-auth-token"], + bearerAuthTokenFromStore: params["bearer-auth-token-from-store"] }); }); $tw.rootWidget.addEventListener("tm-http-cancel-all-requests",function(event) { @@ -75,8 +77,9 @@ exports.startup = function() { $tw.rootWidget.addEventListener("tm-copy-to-clipboard",function(event) { $tw.utils.copyToClipboard(event.param,{ successNotification: event.paramObject && event.paramObject.successNotification, - failureNotification: event.paramObject && event.paramObject.failureNotification - }); + failureNotification: event.paramObject && event.paramObject.failureNotification, + plainText: event.paramObject && event.paramObject.plainText + },event.paramObject && event.paramObject.type); }); // Install the tm-focus-selector message $tw.rootWidget.addEventListener("tm-focus-selector",function(event) { diff --git a/core/modules/startup/startup.js b/core/modules/startup/startup.js index 0e8fdfa9e..2572990c5 100755 --- a/core/modules/startup/startup.js +++ b/core/modules/startup/startup.js @@ -67,14 +67,6 @@ exports.startup = function() { wiki: $tw.wiki, document: $tw.browser ? document : $tw.fakeDocument }); - // Execute any startup actions - $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction"); - if($tw.browser) { - $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser"); - } - if($tw.node) { - $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node"); - } // Kick off the language manager and switcher $tw.language = new $tw.Language(); $tw.languageSwitcher = new $tw.PluginSwitcher({ @@ -88,8 +80,10 @@ exports.startup = function() { if($tw.browser) { var pluginTiddler = $tw.wiki.getTiddler(plugins[0]); if(pluginTiddler) { + document.documentElement.setAttribute("lang",pluginTiddler.getFieldString("name")); document.documentElement.setAttribute("dir",pluginTiddler.getFieldString("text-direction") || "auto"); } else { + document.documentElement.setAttribute("lang","en-GB"); document.documentElement.removeAttribute("dir"); } } @@ -115,6 +109,14 @@ exports.startup = function() { handlerMethod: "handleKeydownEvent" }]); } + // Execute any startup actions + $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction"); + if($tw.browser) { + $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser"); + } + if($tw.node) { + $tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node"); + } // Clear outstanding tiddler store change events to avoid an unnecessary refresh cycle at startup $tw.wiki.clearTiddlerEventQueue(); // Find a working syncadaptor diff --git a/core/modules/storyviews/classic.js b/core/modules/storyviews/classic.js index 0dc0a1696..1128f163c 100644 --- a/core/modules/storyviews/classic.js +++ b/core/modules/storyviews/classic.js @@ -30,12 +30,8 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) { if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) { return; } - if(duration) { - // Scroll the node into view - this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement}); - } else { - targetElement.scrollIntoView(); - } + // Scroll the node into view + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement}); }; ClassicStoryView.prototype.insert = function(widget) { @@ -82,6 +78,10 @@ ClassicStoryView.prototype.remove = function(widget) { removeElement = function() { widget.removeChildDomNodes(); }; + // Blur the focus if it is within the descendents of the node we are removing + if($tw.utils.domContains(targetElement,targetElement.ownerDocument.activeElement)) { + targetElement.ownerDocument.activeElement.blur(); + } // Abandon if the list entry isn't a DOM element (it might be a text node) if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) { removeElement(); diff --git a/core/modules/tiddler.js b/core/modules/tiddler.js index 01156157a..c21468c88 100644 --- a/core/modules/tiddler.js +++ b/core/modules/tiddler.js @@ -40,10 +40,10 @@ exports.getFieldString = function(field,defaultValue) { }; /* -Get the value of a field as a list +Get the value of a field as an array / list */ exports.getFieldList = function(field) { - var value = this.fields[field]; + var value = this.getFieldString(field,null); // Check for a missing field if(value === undefined || value === null) { return []; diff --git a/core/modules/upgraders/system.js b/core/modules/upgraders/system.js index eebc0797c..539869d97 100644 --- a/core/modules/upgraders/system.js +++ b/core/modules/upgraders/system.js @@ -12,7 +12,7 @@ Upgrader module that suppresses certain system tiddlers that shouldn't be import /*global $tw: false */ "use strict"; -var DONT_IMPORT_LIST = ["$:/Import"], +var DONT_IMPORT_LIST = ["$:/Import", "$:/build"], UNSELECT_PREFIX_LIST = ["$:/temp/","$:/state/","$:/StoryList","$:/HistoryList"], WARN_IMPORT_PREFIX_LIST = ["$:/core/modules/"]; diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js index ba2c90f8d..b5e56f48d 100644 --- a/core/modules/utils/dom/dom.js +++ b/core/modules/utils/dom/dom.js @@ -268,8 +268,10 @@ exports.copyStyles = function(srcDomNode,dstDomNode) { /* Copy plain text to the clipboard on browsers that support it */ -exports.copyToClipboard = function(text,options) { - options = options || {}; +exports.copyToClipboard = function(text,options,type) { + var text = text || ""; + var options = options || {}; + var type = type || "text/plain"; var textArea = document.createElement("textarea"); textArea.style.position = "fixed"; textArea.style.top = 0; @@ -282,10 +284,16 @@ exports.copyToClipboard = function(text,options) { textArea.style.outline = "none"; textArea.style.boxShadow = "none"; textArea.style.background = "transparent"; - textArea.value = text; document.body.appendChild(textArea); textArea.select(); textArea.setSelectionRange(0,text.length); + textArea.addEventListener("copy",function(event) { + event.preventDefault(); + if (options.plainText) { + event.clipboardData.setData("text/plain",options.plainText); + } + event.clipboardData.setData(type,text); + }); var succeeded = false; try { succeeded = document.execCommand("copy"); diff --git a/core/modules/utils/dom/http.js b/core/modules/utils/dom/http.js index e9b02b662..a792b02d9 100644 --- a/core/modules/utils/dom/http.js +++ b/core/modules/utils/dom/http.js @@ -104,6 +104,8 @@ basicAuthUsername: plain username for basic authentication basicAuthUsernameFromStore: name of password store entry containing username basicAuthPassword: plain password for basic authentication basicAuthPasswordFromStore: name of password store entry containing password +bearerAuthToken: plain text token for bearer authentication +bearerAuthTokenFromStore: name of password store entry contain bear authorization token */ function HttpClientRequest(options) { var self = this; @@ -135,8 +137,11 @@ function HttpClientRequest(options) { }); this.basicAuthUsername = options.basicAuthUsername || (options.basicAuthUsernameFromStore && $tw.utils.getPassword(options.basicAuthUsernameFromStore)) || ""; this.basicAuthPassword = options.basicAuthPassword || (options.basicAuthPasswordFromStore && $tw.utils.getPassword(options.basicAuthPasswordFromStore)) || ""; + this.bearerAuthToken = options.bearerAuthToken || (options.bearerAuthTokenFromStore && $tw.utils.getPassword(options.bearerAuthTokenFromStore)) || ""; if(this.basicAuthUsername && this.basicAuthPassword) { this.requestHeaders.Authorization = "Basic " + $tw.utils.base64Encode(this.basicAuthUsername + ":" + this.basicAuthPassword); + } else if(this.bearerAuthToken) { + this.requestHeaders.Authorization = "Bearer " + this.bearerAuthToken; } } @@ -211,11 +216,11 @@ HttpClientRequest.prototype.send = function(callback) { if(lengthComputable) { setBinding(self.bindProgress,"" + Math.floor((loaded/total) * 100)) } - self.wiki.invokeActionString(self.progressActions,undefined,{ + self.wiki.invokeActionString(self.progressActions,undefined,$tw.utils.extend({},self.variables,{ lengthComputable: lengthComputable ? "yes" : "no", loaded: loaded, total: total - },{parentWidget: $tw.rootWidget}); + }),{parentWidget: $tw.rootWidget}); } }); } diff --git a/core/modules/utils/edition-info.js b/core/modules/utils/edition-info.js index 401f5c3b5..edfdef077 100644 --- a/core/modules/utils/edition-info.js +++ b/core/modules/utils/edition-info.js @@ -29,10 +29,14 @@ exports.getEditionInfo = function() { for(var entryIndex=0; entryIndex