mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-22 02:44:36 +00:00
Compare commits
207 Commits
saqimtiaz-
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb3a80968e | ||
|
|
62ae4b24bc | ||
|
|
ded76aa84f | ||
|
|
e42ed6808e | ||
|
|
844564180f | ||
|
|
a670de0e95 | ||
|
|
faee49ee01 | ||
|
|
dd20be49f0 | ||
|
|
a27f74bbdc | ||
|
|
ae4e99951a | ||
|
|
be84dee26b | ||
|
|
00e17874f0 | ||
|
|
9041f099a3 | ||
|
|
3ba31be2a8 | ||
|
|
99d8afd515 | ||
|
|
2ab5f26644 | ||
|
|
419fe68ee2 | ||
|
|
0e765bdbdb | ||
|
|
855d8a9638 | ||
|
|
afcf108d29 | ||
|
|
8f9acc0ca2 | ||
|
|
5e4b8fbb3c | ||
|
|
8e301178a4 | ||
|
|
a72d3a09bf | ||
|
|
56634ffe29 | ||
|
|
24c317e1ab | ||
|
|
07329c6849 | ||
|
|
47ab3476f6 | ||
|
|
f0e64660f2 | ||
|
|
9663e65f4b | ||
|
|
7cb422242a | ||
|
|
f075f24e6b | ||
|
|
5fa1098c03 | ||
|
|
92dc927c7b | ||
|
|
98a61f01bb | ||
|
|
f3fa69e229 | ||
|
|
e2fb22ade0 | ||
|
|
7fb8560908 | ||
|
|
ff7814360e | ||
|
|
2d9303c6ff | ||
|
|
6aee5eb0c7 | ||
|
|
6eb881bffe | ||
|
|
2a5ce95d99 | ||
|
|
bd4fdd8f2e | ||
|
|
09379abd5d | ||
|
|
c6906120d8 | ||
|
|
c4c60933f4 | ||
|
|
a3979cda9c | ||
|
|
3c8ee86e23 | ||
|
|
921c0174fb | ||
|
|
4196d96adc | ||
|
|
743e99d12d | ||
|
|
6beeb23d10 | ||
|
|
838fad916d | ||
|
|
935e89bd93 | ||
|
|
1b8a2caa23 | ||
|
|
ecba671bcf | ||
|
|
fc1e53a777 | ||
|
|
2b6bdcbf97 | ||
|
|
119706a918 | ||
|
|
86d15585b6 | ||
|
|
4b824795c8 | ||
|
|
cd173a959d | ||
|
|
86d61e09bd | ||
|
|
7c197f6ecc | ||
|
|
8f8b46dab7 | ||
|
|
7b013af240 | ||
|
|
05f3e6d5a0 | ||
|
|
48eeb4603a | ||
|
|
fc74219c0b | ||
|
|
86c4770a28 | ||
|
|
ffde2da16c | ||
|
|
da41a55f29 | ||
|
|
846ac9a0dd | ||
|
|
65edda224b | ||
|
|
41dac42f3b | ||
|
|
87d9754a4e | ||
|
|
5cd3084298 | ||
|
|
4c27c09b4d | ||
|
|
4bb0bc5527 | ||
|
|
6cb333b65b | ||
|
|
3378497816 | ||
|
|
033d5cf225 | ||
|
|
52d73eb1a8 | ||
|
|
5d1c1eaf87 | ||
|
|
ed4a186c9c | ||
|
|
f16c0d769b | ||
|
|
298508c104 | ||
|
|
f1e1532949 | ||
|
|
3e1078eff1 | ||
|
|
cc348fee96 | ||
|
|
24d45fd318 | ||
|
|
9fd345ec06 | ||
|
|
30e9b4f3b7 | ||
|
|
c6556d5207 | ||
|
|
a8da7e0207 | ||
|
|
d5762b1fbb | ||
|
|
2b0739f06e | ||
|
|
d81204c6ab | ||
|
|
e339f112a4 | ||
|
|
e001c21bf5 | ||
|
|
7a9235e9d1 | ||
|
|
bbbc8c2c03 | ||
|
|
538482e9a9 | ||
|
|
df7973fc3e | ||
|
|
ae79736e82 | ||
|
|
102c236267 | ||
|
|
06a2923adf | ||
|
|
244251ed44 | ||
|
|
9164f305e0 | ||
|
|
64f86c2187 | ||
|
|
54e5ef7489 | ||
|
|
2d2ba61949 | ||
|
|
0bbe170cf0 | ||
|
|
6f306d1ed6 | ||
|
|
845f988ab0 | ||
|
|
234667cc31 | ||
|
|
f1ce35036e | ||
|
|
5dfdbc8ea0 | ||
|
|
3cf71365a5 | ||
|
|
799618d9f5 | ||
|
|
4d4d9d9995 | ||
|
|
19177964c8 | ||
|
|
c8e41bfade | ||
|
|
fb4d417629 | ||
|
|
20d6be1e23 | ||
|
|
7898cb8446 | ||
|
|
9a5f4cc0ef | ||
|
|
cda8d7ca8c | ||
|
|
f38e9f0822 | ||
|
|
5848d66e96 | ||
|
|
485051951e | ||
|
|
135e685811 | ||
|
|
99682c5731 | ||
|
|
d58eec47c0 | ||
|
|
059978ec63 | ||
|
|
b5e20a58a6 | ||
|
|
e8fe6b98bc | ||
|
|
317104774c | ||
|
|
deed8631d8 | ||
|
|
d733b77e2f | ||
|
|
46c26a64b9 | ||
|
|
ec81d6663b | ||
|
|
23f0a9bf79 | ||
|
|
bad87c405e | ||
|
|
6f23a078b7 | ||
|
|
c3706b8a79 | ||
|
|
7ca8fb29af | ||
|
|
d39bb5274e | ||
|
|
81b69783c4 | ||
|
|
40cc62f727 | ||
|
|
b349adde16 | ||
|
|
d63a1896b3 | ||
|
|
b65fa11643 | ||
|
|
4043499633 | ||
|
|
6a39a4e13b | ||
|
|
b5153c0066 | ||
|
|
b061f90f87 | ||
|
|
8be83cf01b | ||
|
|
5b5147dade | ||
|
|
ad6ac480f9 | ||
|
|
8168512e95 | ||
|
|
276fdc8634 | ||
|
|
0b38ced43a | ||
|
|
d7e48207b9 | ||
|
|
2c8fafee48 | ||
|
|
a6383aaaea | ||
|
|
61619c07c8 | ||
|
|
09a42a54c0 | ||
|
|
f4f31c37fc | ||
|
|
61e638c972 | ||
|
|
6bdd51d72a | ||
|
|
ed3405672a | ||
|
|
e3af967cbb | ||
|
|
6b0d3fab5d | ||
|
|
13f1689e7e | ||
|
|
4c09a88272 | ||
|
|
fbf110e209 | ||
|
|
3c1d658fad | ||
|
|
5d738673ac | ||
|
|
91871d2ced | ||
|
|
5143da9cce | ||
|
|
186d1b014a | ||
|
|
58e41ee0ad | ||
|
|
97824cc3a3 | ||
|
|
6e493755be | ||
|
|
64fce62075 | ||
|
|
5a4ff56477 | ||
|
|
3889a2a0d0 | ||
|
|
34737f4e28 | ||
|
|
94673a1028 | ||
|
|
b462aaa9a3 | ||
|
|
4552c117fc | ||
|
|
dc7f2a57bb | ||
|
|
7e91bac6b8 | ||
|
|
15ba415a45 | ||
|
|
2405e65308 | ||
|
|
865f36a6f5 | ||
|
|
a36356a07d | ||
|
|
ee23097816 | ||
|
|
e753851d49 | ||
|
|
cae3d0fa2c | ||
|
|
7c020d0eb6 | ||
|
|
afe2ac45d9 | ||
|
|
6791f0f130 | ||
|
|
a2e5c2cca2 | ||
|
|
5ff4e02a61 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
boot/** -linguist-generated
|
||||
**/tiddlywiki.files linguist-language=JSON
|
||||
**/tiddlywiki.info linguist-language=JSON
|
||||
**/plugin.info linguist-language=JSON
|
||||
62
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
62
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve TiddlyWiki 5
|
||||
title: "[Report] "
|
||||
type: report
|
||||
|
||||
---
|
||||
|
||||
<!-- Remove elements, that you do not need -->
|
||||
<!-- Add screenshots where needed -->
|
||||
|
||||
**Problem Description**
|
||||
<!-- Describe your problem: A clear and concise description of what your problem is -->
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. At https://tiddlywiki.com
|
||||
2. Click on ...
|
||||
3. Scroll down to ...
|
||||
4. See ...
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
As a user,
|
||||
<!-- As a developer, -->
|
||||
I would expect ...
|
||||
|
||||
|
||||
**TiddlyWiki Configuration**
|
||||
<!-- Please complete the following information -->
|
||||
|
||||
- Report created with: [Wiki Information](https://tiddlywiki.com/#%24%3A%2Fcore%2Fui%2FControlPanel%2FWikiInformation)
|
||||
|
||||
<!-- Your report comes here -->
|
||||
<!-- or -->
|
||||
<!-- Add it manually -->
|
||||
|
||||
- Version: <!-- e.g. v5.3.8 -->
|
||||
- Saving mechanism: <!-- e.g. Node.js, TiddlyDesktop, TiddlyHost etc -->
|
||||
- Plugins installed: <!-- e.g. Freelinks, TiddlyMap ... other 3rd party plugins -->
|
||||
|
||||
|
||||
**Desktop**
|
||||
<!-- Please complete the following information -->
|
||||
|
||||
- OS: <!-- e.g. iOS -->
|
||||
- Browser: <!-- e.g. chrome, safari, FireFox -- Version: -->
|
||||
|
||||
**Smartphone**
|
||||
<!-- Please complete the following information -->
|
||||
|
||||
- Device: <!-- e.g. iPhone6 -->
|
||||
- OS: <!-- e.g. iOS8.1 -->
|
||||
- Browser: <!-- e.g. stock browser, safari, FireFox -- Version: -->
|
||||
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the problem here. -->
|
||||
67
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
67
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve TiddlyWiki 5
|
||||
title: "[BUG] "
|
||||
body:
|
||||
- type: textarea
|
||||
id: Describe
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: Reproduce
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: "Steps to reproduce the behavior:"
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: Screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your problem.
|
||||
placeholder: Drag image here to upload screenshot!
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: Configuration
|
||||
attributes:
|
||||
label: TiddlyWiki Configuration
|
||||
description: please complete the following information
|
||||
placeholder: |
|
||||
- Version [e.g. v5.1.24]
|
||||
- Saving mechanism [e.g. Node.js, TiddlyDesktop, TiddlyHost etc]
|
||||
- Plugins installed [e.g. Freelinks, TiddlyMap]
|
||||
|
||||
### Desktop (please complete the following information):
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
### Smartphone (please complete the following information):
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -4,17 +4,23 @@ about: Suggest an idea for TiddlyWiki 5
|
||||
title: "[IDEA]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
type: idea
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
**Is your idea related to a problem? Please describe.**
|
||||
|
||||
A clear and concise description of what the problem is. Eg:
|
||||
As a user, I would like [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "${{ env.NODE_VERSION }}"
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
TW5_BUILD_MAIN_EDITION: "./editions/prerelease"
|
||||
TW5_BUILD_OUTPUT: "./output/prerelease"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "${{ env.NODE_VERSION }}"
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
TW5_BUILD_OUTPUT: "./output"
|
||||
TW5_BUILD_ARCHIVE: "./output"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "${{ env.NODE_VERSION }}"
|
||||
|
||||
40
.github/workflows/eslint.yml
vendored
Normal file
40
.github/workflows/eslint.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: ESLint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: lint-${{ github.event.pull_request.number || github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed for GitHub Checks API
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --include=dev
|
||||
|
||||
- name: Run ESLint with reviewdog (GitHub Checks)
|
||||
uses: reviewdog/action-eslint@v1
|
||||
with:
|
||||
eslint_flags: '.'
|
||||
reporter: github-pr-check
|
||||
fail_level: error
|
||||
level: error
|
||||
tool_name: ESLint PR code
|
||||
2
.github/workflows/pr-check-build-size.yml
vendored
2
.github/workflows/pr-check-build-size.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- name: build-size-check
|
||||
id: get_sizes
|
||||
uses: TiddlyWiki/cerebrus@v4
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
|
||||
2
.github/workflows/pr-comment-build-size.yml
vendored
2
.github/workflows/pr-comment-build-size.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Build and check size
|
||||
uses: TiddlyWiki/cerebrus@v4
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ inputs.pr_number }}
|
||||
repo: ${{ github.repository }}
|
||||
|
||||
18
.github/workflows/pr-path-validation.yml
vendored
18
.github/workflows/pr-path-validation.yml
vendored
@@ -1,18 +0,0 @@
|
||||
name: Validate PR Paths
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
validate-pr:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Validate PR
|
||||
uses: TiddlyWiki/cerebrus@v4
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
base_ref: ${{ github.base_ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
37
.github/workflows/pr-validation.yml
vendored
Normal file
37
.github/workflows/pr-validation.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: PR Validation
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
jobs:
|
||||
validate-pr:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Step 1: Validate PR paths
|
||||
- name: Validate PR Paths
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
base_ref: ${{ github.event.pull_request.base.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
mode: rules
|
||||
continue-on-error: true
|
||||
|
||||
# Step 2: Validate change notes
|
||||
- name: Validate Change Notes
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
base_ref: ${{ github.event.pull_request.base.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
mode: changenotes
|
||||
continue-on-error: false
|
||||
|
||||
@@ -73,10 +73,8 @@ rm $TW5_BUILD_OUTPUT/dev/static/*
|
||||
|
||||
echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/d3/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/d3/index.html</a>" > $TW5_BUILD_OUTPUT/d3demo.html
|
||||
echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/tahoelafs/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tahoelafs/index.html</a>" > $TW5_BUILD_OUTPUT/tahoelafs.html
|
||||
|
||||
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
|
||||
|
||||
@@ -301,26 +299,6 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/tahoelafs/index.html Demo wiki with Tahoe-LAFS plugin
|
||||
# /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/tahoelafs \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tahoelafs/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/d3/index.html Demo wiki with D3 plugin
|
||||
# /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/d3demo \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/d3/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin
|
||||
# /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
|
||||
139
boot/boot.js
139
boot/boot.js
@@ -8,6 +8,8 @@ On the server this file is executed directly to boot TiddlyWiki. In the browser,
|
||||
|
||||
\*/
|
||||
|
||||
/* eslint-disable @stylistic/indent */
|
||||
|
||||
var _boot = (function($tw) {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
@@ -44,12 +46,8 @@ $tw.utils.hop = function(object,property) {
|
||||
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine if a value is an array
|
||||
*/
|
||||
$tw.utils.isArray = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Array]";
|
||||
};
|
||||
/** @deprecated Use Array.isArray instead */
|
||||
$tw.utils.isArray = value => Array.isArray(value);
|
||||
|
||||
/*
|
||||
Check if an array is equal by value and by reference.
|
||||
@@ -128,35 +126,22 @@ $tw.utils.pushTop = function(array,value) {
|
||||
return array;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine if a value is a date
|
||||
*/
|
||||
$tw.utils.isDate = function(value) {
|
||||
return Object.prototype.toString.call(value) === "[object Date]";
|
||||
};
|
||||
/** @deprecated Use instanceof Date instead */
|
||||
$tw.utils.isDate = value => value instanceof Date;
|
||||
|
||||
/*
|
||||
Iterate through all the own properties of an object or array. Callback is invoked with (element,title,object)
|
||||
*/
|
||||
/** @deprecated Use array iterative methods instead */
|
||||
$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; f<length; f++) {
|
||||
next = callback(object[f],f,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(Array.isArray(object)) {
|
||||
object.every((element,index,array) => {
|
||||
const next = callback(element,index,array);
|
||||
return next !== false;
|
||||
});
|
||||
} else {
|
||||
var keys = Object.keys(object);
|
||||
for(f=0, length=keys.length; f<length; f++) {
|
||||
var key = keys[f];
|
||||
next = callback(object[key],key,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Object.entries(object).every(entry => {
|
||||
const next = callback(entry[1], entry[0], object);
|
||||
return next !== false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -331,32 +316,13 @@ $tw.utils.htmlDecode = function(s) {
|
||||
return s.toString().replace(/</mg,"<").replace(/ /mg,"\xA0").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
|
||||
};
|
||||
|
||||
/*
|
||||
Get the browser location.hash. We don't use location.hash because of the way that Firefox auto-urldecodes it (see http://stackoverflow.com/questions/1703552/encoding-of-window-location-hash)
|
||||
*/
|
||||
$tw.utils.getLocationHash = function() {
|
||||
var href = window.location.href;
|
||||
var idx = href.indexOf('#');
|
||||
if(idx === -1) {
|
||||
return "#";
|
||||
} else if(href.substr(idx + 1,1) === "#" || href.substr(idx + 1,3) === "%23") {
|
||||
// Special case: ignore location hash if it itself starts with a #
|
||||
return "#";
|
||||
} else {
|
||||
return href.substring(idx);
|
||||
}
|
||||
};
|
||||
/** @deprecated Use window.location.hash instead. */
|
||||
$tw.utils.getLocationHash = () => window.location.hash;
|
||||
|
||||
/*
|
||||
Pad a string to a given length with "0"s. Length defaults to 2
|
||||
*/
|
||||
$tw.utils.pad = function(value,length) {
|
||||
length = length || 2;
|
||||
var s = value.toString();
|
||||
if(s.length < length) {
|
||||
s = "000000000000000000000000000".substr(0,length - s.length) + s;
|
||||
}
|
||||
return s;
|
||||
/** @deprecated Pad a string to a given length with "0"s. Length defaults to 2 */
|
||||
$tw.utils.pad = function(value,length = 2) {
|
||||
const s = value.toString();
|
||||
return s.padStart(length, "0");
|
||||
};
|
||||
|
||||
// Convert a date into UTC YYYYMMDDHHMMSSmmm format
|
||||
@@ -630,7 +596,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
|
||||
// Compile the code into a function
|
||||
var fn;
|
||||
if($tw.browser) {
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename); // eslint-disable-line no-eval -- See https://github.com/TiddlyWiki/TiddlyWiki5/issues/6839
|
||||
} else {
|
||||
if(sandbox){
|
||||
fn = vm.runInContext(code,sandbox,filename)
|
||||
@@ -641,7 +607,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
|
||||
// Call the function and return the exports
|
||||
return fn.apply(null,contextValues);
|
||||
};
|
||||
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
|
||||
$tw.utils.sandbox = !$tw.browser ? vm.createContext({}) : undefined;
|
||||
/*
|
||||
Run code in a sandbox with only the specified context variables in scope
|
||||
*/
|
||||
@@ -1532,7 +1498,8 @@ Define all modules stored in ordinary tiddlers
|
||||
*/
|
||||
$tw.Wiki.prototype.defineTiddlerModules = function() {
|
||||
this.each(function(tiddler,title) {
|
||||
if(tiddler.hasField("module-type")) {
|
||||
// Modules in draft tiddlers are disabled
|
||||
if(tiddler.hasField("module-type") && (!tiddler.hasField("draft.of"))) {
|
||||
switch(tiddler.fields.type) {
|
||||
case "application/javascript":
|
||||
// We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script
|
||||
@@ -1559,6 +1526,11 @@ $tw.Wiki.prototype.defineShadowModules = function() {
|
||||
this.eachShadow(function(tiddler,title) {
|
||||
// Don't define the module if it is overidden by an ordinary tiddler
|
||||
if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) {
|
||||
if(tiddler.hasField("draft.of")) {
|
||||
// Report a fundamental problem
|
||||
console.warn(`TiddlyWiki: Plugins should not contain tiddlers with a 'draft.of' field: ${tiddler.fields.title}`);
|
||||
return;
|
||||
}
|
||||
// Define the module
|
||||
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
|
||||
}
|
||||
@@ -1907,7 +1879,7 @@ $tw.loadTiddlersFromFile = function(filepath,fields) {
|
||||
fileSize = fs.statSync(filepath).size,
|
||||
data;
|
||||
if(fileSize > $tw.config.maxEditFileSize) {
|
||||
data = "File " + filepath + "not loaded because it is too large";
|
||||
data = "File " + filepath + " not loaded because it is too large";
|
||||
console.log("Warning: " + data);
|
||||
ext = ".txt";
|
||||
} else {
|
||||
@@ -1978,22 +1950,41 @@ filepath: pathname of the directory containing the specification file
|
||||
$tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
var tiddlers = [];
|
||||
// Read the specification
|
||||
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"));
|
||||
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"), function(e) {
|
||||
console.log("Warning: tiddlywiki.files in " + filepath + " invalid: " + e.message);
|
||||
return {};
|
||||
});
|
||||
|
||||
// Helper to process a file
|
||||
var processFile = function(filename,isTiddlerFile,fields,isEditableFile,rootPath) {
|
||||
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
|
||||
type = (extInfo || {}).type || fields.type || "text/plain",
|
||||
typeInfo = $tw.config.contentTypeInfo[type] || {},
|
||||
pathname = path.resolve(filepath,filename),
|
||||
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8"),
|
||||
metadata = $tw.loadMetadataForFile(pathname) || {},
|
||||
fileTiddlers;
|
||||
fileTooLarge = false,
|
||||
text, fileTiddlers;
|
||||
|
||||
if("_canonical_uri" in fields) {
|
||||
text = "";
|
||||
} else if(fs.statSync(pathname).size > $tw.config.maxEditFileSize) {
|
||||
var msg = "File " + pathname + " not loaded because it is too large";
|
||||
console.log("Warning: " + msg);
|
||||
fileTooLarge = true;
|
||||
text = isTiddlerFile ? msg : "";
|
||||
} else {
|
||||
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8");
|
||||
}
|
||||
|
||||
if(isTiddlerFile) {
|
||||
fileTiddlers = $tw.wiki.deserializeTiddlers(path.extname(pathname),text,metadata) || [];
|
||||
fileTiddlers = $tw.wiki.deserializeTiddlers(fileTooLarge ? ".txt" : path.extname(pathname),text,metadata) || [];
|
||||
} else {
|
||||
fileTiddlers = [$tw.utils.extend({text: text},metadata)];
|
||||
}
|
||||
var combinedFields = $tw.utils.extend({},fields,metadata);
|
||||
if(fileTooLarge && isTiddlerFile) {
|
||||
delete combinedFields.type; // type altered
|
||||
}
|
||||
$tw.utils.each(fileTiddlers,function(tiddler) {
|
||||
$tw.utils.each(combinedFields,function(fieldInfo,name) {
|
||||
if(typeof fieldInfo === "string" || $tw.utils.isArray(fieldInfo)) {
|
||||
@@ -2068,6 +2059,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
} else if(tidInfo.suffix) {
|
||||
tidInfo.fields.text = {suffix: tidInfo.suffix};
|
||||
}
|
||||
tidInfo.fields = tidInfo.fields || {};
|
||||
processFile(tidInfo.file,tidInfo.isTiddlerFile,tidInfo.fields);
|
||||
});
|
||||
// Process any listed directories
|
||||
@@ -2089,6 +2081,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
var thisPath = path.relative(filepath, files[t]),
|
||||
filename = path.basename(thisPath);
|
||||
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
|
||||
dirSpec.fields = dirSpec.fields || {};
|
||||
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path);
|
||||
}
|
||||
}
|
||||
@@ -2551,10 +2544,10 @@ $tw.boot.execStartup = function(options){
|
||||
if($tw.safeMode) {
|
||||
$tw.wiki.processSafeMode();
|
||||
}
|
||||
// Register typed modules from the tiddlers we've just loaded
|
||||
$tw.wiki.defineTiddlerModules();
|
||||
// And any modules within plugins
|
||||
// Register typed modules from the tiddlers we've just loaded and any modules within plugins
|
||||
// Tiddlers should appear last so that they may overwrite shadows during module registration
|
||||
$tw.wiki.defineShadowModules();
|
||||
$tw.wiki.defineTiddlerModules();
|
||||
// Make sure the crypto state tiddler is up to date
|
||||
if($tw.crypto) {
|
||||
$tw.crypto.updateCryptoStateTiddler();
|
||||
@@ -2623,11 +2616,13 @@ $tw.boot.executeNextStartupTask = function(callback) {
|
||||
$tw.boot.log(s.join(" "));
|
||||
// Execute task
|
||||
if(!$tw.utils.hop(task,"synchronous") || task.synchronous) {
|
||||
task.startup();
|
||||
if(task.name) {
|
||||
$tw.boot.executedStartupModules[task.name] = true;
|
||||
const thenable = task.startup();
|
||||
if(thenable && typeof thenable.then === "function"){
|
||||
thenable.then(asyncTaskCallback);
|
||||
return true;
|
||||
} else {
|
||||
return asyncTaskCallback();
|
||||
}
|
||||
return $tw.boot.executeNextStartupTask(callback);
|
||||
} else {
|
||||
task.startup(asyncTaskCallback);
|
||||
return true;
|
||||
@@ -2772,6 +2767,8 @@ return $tw;
|
||||
|
||||
});
|
||||
|
||||
/* eslint-enable @stylistic/indent */
|
||||
|
||||
if(typeof(exports) !== "undefined") {
|
||||
exports.TiddlyWiki = _boot;
|
||||
} else {
|
||||
|
||||
@@ -12,6 +12,8 @@ See Boot.js for further details of the boot process.
|
||||
|
||||
\*/
|
||||
|
||||
/* eslint-disable @stylistic/indent */
|
||||
|
||||
var _bootprefix = (function($tw) {
|
||||
|
||||
"use strict";
|
||||
@@ -114,6 +116,8 @@ return $tw;
|
||||
|
||||
});
|
||||
|
||||
/* eslint-enable @stylistic/indent */
|
||||
|
||||
if(typeof(exports) === "undefined") {
|
||||
// Set up $tw global for the browser
|
||||
window.$tw = _bootprefix(window.$tw);
|
||||
|
||||
14
community/people/ChristianByron.tid
Normal file
14
community/people/ChristianByron.tid
Normal file
@@ -0,0 +1,14 @@
|
||||
title: @Christian_Byron
|
||||
tags: Community/Person
|
||||
fullname: Christian Byron
|
||||
talk.tiddlywiki.org: Christian_Byron
|
||||
github: ceebeetree
|
||||
linkedin: www.linkedin.com/in/christian-byron-b84a594/
|
||||
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEBAAMBAAAAAAAAAAAAAAAHBgEDBQQBAAMBAAAAAAAAAAAAAAAAAAABAwX/2gAMAwEAAhADEAAAADv2xtJlY03sqePW3ARS1RSydIhcH//EACcQAAICAgIBAgYDAAAAAAAAAAECAwQFEQASMRMhBhBBk8HRIzJx/9oACAEBAAE/AMFQxs+NExqJLMCwYE+SOT4bF3qr+hAIpRsDQ6lWH0Yco4S/eVniRVQHXZzrZ5dwGQpQtNII2RfJVvHMRl5cbKxC94n/ALp+RxfiKpNcgMMUqPIwjcnWip/I5XtUowaL3Ujir/xt79Glb6/4OZ7MV5oEpUzuIa7MPB14A5jpoYLsEsydo1bbLre+CWEEEYab7Uf74ZYSSThpvtR/vmRmhnuzywp1jZtquta+VPM49qlcy24lf017At7g8uZnHrUsGK3Ez+m3UBvcnXy//8QAHhEAAgEFAAMAAAAAAAAAAAAAAQIDAAQRIkEyUaH/2gAIAQIBAT8AmiuVlZkLEeQOflJPcvMAF0z65V+h0YIW52rBDuxUrztf/8QAIxEBAAEDAwMFAAAAAAAAAAAAAgEAAxEEBSMSQcEiMVJxof/aAAgBAwEBPwC/Z1ZvNBOYz1Gc/lDUat3ySPRM/H2P3W4hcbIldpxnxW3BcjQk9oznzX//2Q==
|
||||
|
||||
Hello ~TiddlyWikiers - I have been a long time fan, recent contributor to the TW community.
|
||||
Recently I have volunteered to run the [[TiddlyWiki Newsletter|https://tiddlywiki.substack.com/]] to spread the great news about TW.
|
||||
|
||||
I have been in the IT industry for about thirty years, mostly as a consultant and technical arcitect.
|
||||
More recently I went back to study a masters in IT focussing on AI and data science.
|
||||
Now my partner and I have started our own business ([[Sphere Innovations|https://sphere-innovations.com.au]]) - in consulting and building web applications for small to medium size businesses here in Australia.
|
||||
19
community/people/LinOnetwo.tid
Normal file
19
community/people/LinOnetwo.tid
Normal file
@@ -0,0 +1,19 @@
|
||||
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEAAwEBAAAAAAAAAAAAAAAGAwQHAgUBAAMBAAAAAAAAAAAAAAAAAAACAwT/2gAMAwEAAhADEAAAAOfCWAMdKKetM4wOvY5OcvZnrYf/xAApEAACAQQBBAECBwAAAAAAAAABAgMABAURQQYSIVETFCIxMkJicYKh/9oACAEBAAE/AEtysaStr7mPaPeuazWdMM4gEnfPryW8hBUuZvou2RXRxyreBWPmgyNqs8f8MOQalhdY7Vz+R4/s/qfP+1edNi/zl7HDcFbmS3E8CcMR4INP0PkBhklIm+sZNtFtQiV0nj57Owl+dSrSTFgD6/CtH4VV9lU3oAbPngAVY389lc5URuUZkMxhnR4pvW0VwDqsP1FNmLWYqCpikMbngmliJNY+aKzyTxXS6lRAyg/u5rq+5x2RsuyTa3MQMlvKniRGThTUd1JYXUdzAwDvqVxGdRXMbfrVOD7HBrG3mNEsU8z98TRhl9eRzX//xAAcEQACAgIDAAAAAAAAAAAAAAABAgARAzESIVH/2gAIAQIBAT8ARuXZPsul3Eoje5lBQWBP/8QAGREAAwEBAQAAAAAAAAAAAAAAAAECEiER/9oACAEDAQE/AM98Lk7LJe20z//Z
|
||||
created: 20251110102157310
|
||||
first-sighting: 2019-03-01
|
||||
fullname: Lin Onetwo
|
||||
github: linonetwo
|
||||
homepage: https://wiki.onetwo.website/
|
||||
modified: 20251111184556193
|
||||
tags: Community/Person Community/Team/Contributors
|
||||
talk.tiddlywiki.org: linonetwo
|
||||
title: @linonetwo
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Since 2014, when I started college, I've been on a quest for a lifelong PKM tool. I cherish my life and all my experiences, and I don’t want to forget any of them. When I’m deeply focused on a task, it’s easy to lose sight of other important parts of my life—so I needed a system to help me stay balanced.
|
||||
|
||||
Early on, I tried TiddlyWiki several times, but I was initially put off by its save mechanism and markup editing. That changed when I discovered an auto-backup script, which gave me the confidence to fully commit. Over time, I improved the script and eventually transitioned to using TidGi-Desktop and TidGi-Mobile.
|
||||
|
||||
Today, my TiddlyWiki holds all my game design ideas and progress logs—it has truly become my second brain. With the help of LLM-powered programming tools, I’ve enhanced it with numerous plugins, allowing me to manage my mind in a more programmable and structured way. As a game developer, TiddlyWiki isn't the core of my professional work; But I've invested so much time because it's fundamentally about upgrading my mind.
|
||||
|
||||
Most of my notes are open by default and shared publicly on my homepage as a digital garden.
|
||||
25
community/people/PMario.tid
Normal file
25
community/people/PMario.tid
Normal file
@@ -0,0 +1,25 @@
|
||||
avatar: UklGRiwIAABXRUJQVlA4WAoAAAAwAAAAPwAAPwAASUNDUCACAAAAAAIgbGNtcwRAAABtbnRyR1JBWVhZWiAH6QALAAoACwADAAZhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZXNjAAAAzAAAAG5jcHJ0AAABPAAAADZ3dHB0AAABdAAAABRrVFJDAAABiAAAACBkbW5kAAABqAAAACRkbWRkAAABzAAAAFJtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFIAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA81EAAQAAAAEWzHBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbbWx1YwAAAAAAAAABAAAADGVuVVMAAAAIAAAAHABHAEkATQBQbWx1YwAAAAAAAAABAAAADGVuVVMAAAA2AAAAHABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAVlA4TOYFAAAvP8APEDWGgbRtWv+yt/0WImICOBvWn1C4dFi1bStbvpY8Qg2ePANNNAMh3N2db/7A91/7CHBvBBRr25ZFH+4k98ihkqi2CP4tsANvX8a+8y8Ct04dn0nuUt39ZiBJkowqt911M+MJ1G3bNiZJr1iP0DZ+2bbdadsqprOjAqmoUIX9hf3Fl5/uPYV7I3OMeoFzIvrvwG0kRUr3zPLdYMMXaqrMMsp0K4fufKO6c2hFV5Zh7kRROZX0PSCmB/3KWQwpuiekWelSRZDW94d0q750NrxavpFn1eLNQ9EV8nWlmAET6Q8lrCRTcjFLlLImluK3iXJW/hT47KGklS8OlzWUtXLFYDRCSS74ojUjxggqKMoxd6A1lTCyvsvyzC5/d7BsCHb7yIcHyrX2yR/NPnsAdRT2i0Pwp/o0Il6ix8hsRAuJmQgcr4KREfAiMgUVm9KqmfSxL5pOJspVwwTiV6jiIAg1RMhHpERhbvwgGI34Hc49T7UeKZtXwEqJ+BAaoBneperJH0POs1u4dufwv8Gf+qcOfjyvX6ZIVgxE0Rw87YF3BSc9c7jsXfdjOBG7FwmSb39pfGRwu8IuvUjJNoTpFzkEvDg6W3Qt/9nf99ZXPy8HM43IweTKyNR+WVatXcWWyakBksj9cqW+QetplcjsKElvZH/zuOO/PrCx//tL3/6x/O/C1PZZvSKuulLcS4l8M1ewGPR6ef5sllXW2eGQZ7hVSEZiPmcqrSS8e2ElX8o7t1fvB9LFetmEx5hx1Xuye2PpfjZnSjj7QfKTB3bZZo05Zvh6YuivX24cpc8+ddvADWG9odrSwFalVurxUiidDHmTiaoNkkh2gjbcpxMiAbd39aVP119/N9k4+euNKfcNjwaPhZEuUupUsJrHchw1LkPrRC9bQKa3M8Mj/xx903drdnHMpbirj1ENsUre0oo3N+7gat+2ZctKdsIUYc21sRu+Ucdhn+P7DyarftW00iu3Tmbv+hTfdCTmyaIPT4PrYZDFtBN2W8S9m4oTB5Z2P3Oe7weKjVBq86kXX/r0+WuvTAzfjqm1hsYRPWlbxm4n3IaeGOJEizv8orH9w5ejjmSrfOuEq/HxT6eDemtsZ/HTvvG1/8iVspxZILrlkz/cdsIbIroOgJileFSty2xiHNW5t9fbHJ3ze87bp5T9vc8RuqMB0ReDSt464R/BJxspvgpEsrVAJMTsYg2QovPTOHrvQ9et/S2Xx+40z7dY4JBX0Pz/ElH/T73U2DkK8EiqC9hM/zV3frQfzjaAqO16s1l6xCUXnBFlYxyIer3eEdth7u5xsHKxWoGLqzY3wIULt9G3K6soei9jZ+UcF+Ka3M/II9EUWrJ/LLxy+Q9xIh0vOl3NZCrVnBsuFUTOSnJnSioRWZ9q4g+ZDk5XVORoW2qX2hbIkna3JOrdR3jmpHVLovUkLES6grRO010u0GkDlX7SpH1DQ64Wl2zaSUJv1Mtti2G7kx5IyftWMhfDlGClcxvIUhP5crhp9LIb1Vne187oSAWxelcR/kXjYQTZboW+Oj1pqF0gmfZhSDD6bSgzGWrw3s7QLNtCV+2uatYrd/aFtjDI8R52e/DdyKgRKXBhEak3Ev50+GCUA9EFUor39htVMxmWvW8AM6ptG416rZvdWn+MarIEyH5r6ruZSrx8XrWDP370vbfTjqpmZGIbiFPFoihc4jcrlYi9p3ndSuymZ+XLaKza/P/HUWHn5Axdkd9OjBskY0+pIlz4AlFPFs+aStK5PBIRR4MVVJDihsy4JdEA4pVcrVqMZDyL2/8aYocikEAR9Xjc1BNG9zEiJG7n/cGyrtnblkClBhEgMW4Kx21BEBGJjLa0hcOGmTK64KsKLfKr9QyQELclxY3hqowTIZKdZNTSS5BWiBPlKxDWBVSS41bOepkhTkhGDajLfLyUBOKlkMHPgOhx3JoRN/cEiRgSWdgF2yCyDQu4IcbNo8ftTzxveOJ5y+h509h52+h549h569h587/M20f/b1AB
|
||||
created: 20251110102157310
|
||||
first-sighting: 2009-11-14
|
||||
fullname: Mario Pietsch
|
||||
github: pmario
|
||||
homepage: https://wikilabs.github.io/
|
||||
modified: 20251110124935183
|
||||
tags: Community/Person Community/Team/Contributors
|
||||
talk.tiddlywiki.org: pmario
|
||||
title: @pmario
|
||||
type: text/vnd.tiddlywiki
|
||||
youtube: https://www.youtube.com/@pmario
|
||||
|
||||
''Hi, My name is Mario Pietsch''. Back in 2009 I was ''searching'' for ''a simple presentation tool'' and discovered ~TiddlyWiki Classic, Monkey Pirate ~TiddlyWiki ([[MPTW|https://mptw.tiddlyspot.com/]]) with ~TagglyTagging, Eric Shulman's ~TiddlyTools, Saq Imtiaz's navigation macros, and more. --- ''I was captivated''.
|
||||
|
||||
After a deep dive, I combined these elements into my own "Presentation Manager", along [[3 step by step tutorials|https://groups.google.com/g/tiddlywiki/c/qG_tZ1x0MEU/m/-vLA0luMicYJ]] to help others build it.
|
||||
|
||||
Thanks to ''the positive spirit'' of the ~TiddlyWiki community, I am proud to be part of it since 2009.
|
||||
|
||||
When Jeremy started developing ~TiddlyWiki 5 on ~GitHub, I joined in—opening [[issue no. 1|https://github.com/TiddlyWiki/TiddlyWiki5/issues/1]] all the way up to 13. For what that’s good ;) Since then, I have submitted nearly 600 pull requests and more than 500 issues, many of which have been merged or resolved.
|
||||
|
||||
My ~TiddlyWiki 5 "laboratory" is at https://wikilabs.github.io, and I also share content on my ''~YouTube'' channel: https://www.youtube.com/@pmario
|
||||
|
||||
Have fun!<br>
|
||||
Mario
|
||||
19
community/project/teams/Developer Experience Team.tid
Normal file
19
community/project/teams/Developer Experience Team.tid
Normal file
@@ -0,0 +1,19 @@
|
||||
title: Developer Experience Team
|
||||
tags: Community/Team
|
||||
modified: 20251109200632671
|
||||
created: 20251109200632671
|
||||
leader: @pmario
|
||||
team: @saqimtiaz
|
||||
|
||||
The Developer Experience Team improves the experience of software contributors to the TiddlyWiki project. This includes enhancing documentation, streamlining contribution processes, and providing tools and resources to help developers effectively contribute to TiddlyWiki.
|
||||
|
||||
Tools and resources managed by the Developer Experience Team include:
|
||||
|
||||
* Advising and assisting contributors, particularly new developers
|
||||
* Maintenance of developer-focused documentation on the https://tiddlywiki.com/dev/ site, including:
|
||||
** Development environment setup guides
|
||||
** Code review processes and best practices
|
||||
** Contribution guidelines and documentation
|
||||
* Continuous integration and deployment scripts providing feedback on pull requests
|
||||
* Devising and implementing labelling systems for issues and pull requests
|
||||
* Automation scripts to simplify common development tasks
|
||||
@@ -1,8 +1,8 @@
|
||||
title: Infrastructure Team
|
||||
tags: Community/Team
|
||||
modified: 20250909171928024
|
||||
created: 20250909171928024
|
||||
modified: 20251110133437795
|
||||
tags: Community/Team
|
||||
team: @MotovunJack
|
||||
title: Infrastructure Team
|
||||
|
||||
The Infrastructure Team is responsible for maintaining and improving the infrastructure that supports the TiddlyWiki project. This includes the hosting, deployment, and management of the TiddlyWiki websites and services, as well as the tools and systems used by the TiddlyWiki community.
|
||||
|
||||
@@ -12,3 +12,4 @@ The infrastructure includes:
|
||||
* github.com/TiddlyWiki
|
||||
* tiddlywiki.com DNS
|
||||
* Netlify account for PR previews
|
||||
* edit.tiddlywiki.com
|
||||
@@ -1,6 +0,0 @@
|
||||
title: Newsletter Team
|
||||
tags: Community/Team
|
||||
modified: 20250909171928024
|
||||
created: 20250909171928024
|
||||
|
||||
The Newsletter Team is responsible for producing the TiddlyWiki Newsletter, a monthly email newsletter that highlights news, updates, and community contributions related to TiddlyWiki.
|
||||
11
community/project/teams/Quality Assurance Team.tid
Normal file
11
community/project/teams/Quality Assurance Team.tid
Normal file
@@ -0,0 +1,11 @@
|
||||
title: Quality Assurance Team
|
||||
created: 20251112125742296
|
||||
modified: 20251112125742296
|
||||
tags: Community/Team
|
||||
team:
|
||||
leader: @Leilei332
|
||||
|
||||
title: Quality Assurance Team
|
||||
|
||||
The Quality Assurance Team is responsible for ensuring the quality and reliability of TiddlyWiki releases. This includes reviewing code submissions, testing new features, identifying bugs, and verifying that fixes are effective.
|
||||
|
||||
15
community/project/teams/TiddlyWiki Newsletter Team.tid
Normal file
15
community/project/teams/TiddlyWiki Newsletter Team.tid
Normal file
@@ -0,0 +1,15 @@
|
||||
title: TiddlyWiki Newsletter Team
|
||||
tags: Community/Team
|
||||
modified: 20251219090709874
|
||||
created: 20250909171928024
|
||||
leader: @Christian_Byron
|
||||
|
||||
The Newsletter Team is responsible for producing the [[TiddlyWiki Newsletter]]. We would love to have your help if you would like to get involved.
|
||||
|
||||
! Audience
|
||||
|
||||
The newsletter is intended for TiddlyWiki end users who do not track all the discussions on https://talk.tiddlywiki.org/.
|
||||
|
||||
Coverage of developer topics such as JavaScript and intricate wikitext should be handled thoughtfully to avoid alienating the core audience of end users.
|
||||
|
||||
Subscribing to the newsletter is intended to give people confidence that they will not miss any important developments.
|
||||
@@ -1,5 +1,5 @@
|
||||
title: Community/Team
|
||||
modified: 20250909171928024
|
||||
created: 20250909171928024
|
||||
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Infrastructure Team]] [[Succession Team]]
|
||||
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[Quality Assurance Team]] [[Infrastructure Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Succession Team]]
|
||||
|
||||
|
||||
@@ -99,16 +99,18 @@ Commander.prototype.executeNextCommand = function() {
|
||||
}
|
||||
}
|
||||
if(command.info.synchronous) {
|
||||
// Synchronous command
|
||||
// Synchronous command (await thenables)
|
||||
c = new command.Command(params,this);
|
||||
err = c.execute();
|
||||
if(err) {
|
||||
if(err && typeof err.then === "function") {
|
||||
err.then(e => { e ? this.callback(e) : this.executeNextCommand(); });
|
||||
} else if(err) {
|
||||
this.callback(err);
|
||||
} else {
|
||||
this.executeNextCommand();
|
||||
}
|
||||
} else {
|
||||
// Asynchronous command
|
||||
// Asynchronous command (await thenables)
|
||||
c = new command.Command(params,this,function(err) {
|
||||
if(err) {
|
||||
self.callback(err);
|
||||
@@ -117,7 +119,9 @@ Commander.prototype.executeNextCommand = function() {
|
||||
}
|
||||
});
|
||||
err = c.execute();
|
||||
if(err) {
|
||||
if(err && typeof err.then === "function") {
|
||||
err.then(e => { if(e) this.callback(e); });
|
||||
} else if(err) {
|
||||
this.callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,14 @@ DELETE /recipes/default/tiddlers/:title
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "DELETE";
|
||||
exports.methods = ["DELETE"];
|
||||
|
||||
exports.path = /^\/bags\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = $tw.utils.decodeURIComponentSafe(state.params[0]);
|
||||
state.wiki.deleteTiddler(title);
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /favicon.ico
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/favicon.ico$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
|
||||
state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64");
|
||||
|
||||
@@ -8,35 +8,66 @@ GET /files/:filepath
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/files\/(.+)$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var path = require("path"),
|
||||
fs = require("fs"),
|
||||
util = require("util"),
|
||||
suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]),
|
||||
baseFilename = path.resolve(state.boot.wikiPath,"files"),
|
||||
filename = path.resolve(baseFilename,suppliedFilename),
|
||||
extension = path.extname(filename);
|
||||
// Check that the filename is inside the wiki files folder
|
||||
if(path.relative(baseFilename,filename).indexOf("..") !== 0) {
|
||||
// Send the file
|
||||
fs.readFile(filename,function(err,content) {
|
||||
var status,content,type = "text/plain";
|
||||
if(err) {
|
||||
console.log("Error accessing file " + filename + ": " + err.toString());
|
||||
status = 404;
|
||||
content = "File '" + suppliedFilename + "' not found";
|
||||
} else {
|
||||
status = 200;
|
||||
content = content;
|
||||
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
|
||||
}
|
||||
state.sendResponse(status,{"Content-Type": type},content);
|
||||
});
|
||||
} else {
|
||||
state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
|
||||
if(path.relative(baseFilename,filename).indexOf("..") === 0) {
|
||||
return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
|
||||
}
|
||||
fs.stat(filename, function(err, stats) {
|
||||
if(err) {
|
||||
return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
|
||||
} else {
|
||||
var type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"),
|
||||
responseHeaders = {
|
||||
"Content-Type": type,
|
||||
"Accept-Ranges": "bytes"
|
||||
};
|
||||
var rangeHeader = request.headers.range,
|
||||
stream;
|
||||
if(rangeHeader) {
|
||||
// Handle range requests
|
||||
var parts = rangeHeader.replace(/bytes=/, "").split("-"),
|
||||
start = parseInt(parts[0], 10),
|
||||
end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;
|
||||
// Validate start and end
|
||||
if(isNaN(start) || isNaN(end) || start < 0 || end < start || end >= stats.size) {
|
||||
responseHeaders["Content-Range"] = "bytes */" + stats.size;
|
||||
return response.writeHead(416, responseHeaders).end();
|
||||
}
|
||||
var chunksize = (end - start) + 1;
|
||||
responseHeaders["Content-Range"] = "bytes " + start + "-" + end + "/" + stats.size;
|
||||
responseHeaders["Content-Length"] = chunksize;
|
||||
response.writeHead(206, responseHeaders);
|
||||
stream = fs.createReadStream(filename, {start: start, end: end});
|
||||
} else {
|
||||
responseHeaders["Content-Length"] = stats.size;
|
||||
response.writeHead(200, responseHeaders);
|
||||
stream = fs.createReadStream(filename);
|
||||
}
|
||||
// Common stream error handling
|
||||
stream.on("error", function(err) {
|
||||
if(!response.headersSent) {
|
||||
response.writeHead(500, {"Content-Type": "text/plain"});
|
||||
response.end("Read error");
|
||||
} else {
|
||||
response.destroy();
|
||||
}
|
||||
});
|
||||
stream.pipe(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
|
||||
responseHeaders = {
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /login-basic -- force a Basic Authentication challenge
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/login-basic$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
if(!state.authenticatedUsername) {
|
||||
// Challenge if there's no username
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /status
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/status$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var text = JSON.stringify({
|
||||
username: state.authenticatedUsername || state.server.get("anon-username") || "",
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /:title
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/([^\/]+)$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
|
||||
tiddler = state.wiki.getTiddler(title);
|
||||
|
||||
@@ -8,10 +8,14 @@ GET /recipes/default/tiddlers/:title
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
|
||||
tiddler = state.wiki.getTiddler(title),
|
||||
|
||||
@@ -10,10 +10,14 @@ GET /recipes/default/tiddlers.json?filter=<filter>
|
||||
|
||||
var DEFAULT_FILTER = "[all[tiddlers]!is[system]sort[title]]";
|
||||
|
||||
exports.method = "GET";
|
||||
exports.methods = ["GET"];
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers.json$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var filter = state.queryParameters.filter || DEFAULT_FILTER;
|
||||
if(state.wiki.getTiddlerText("$:/config/Server/AllowAllExternalFilters") !== "yes") {
|
||||
|
||||
@@ -8,10 +8,14 @@ PUT /recipes/default/tiddlers/:title
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
exports.method = "PUT";
|
||||
exports.methods = ["PUT"];
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.info = {
|
||||
priority: 100
|
||||
};
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
|
||||
fields = $tw.utils.parseJSONSafe(state.data);
|
||||
|
||||
@@ -74,6 +74,11 @@ function Server(options) {
|
||||
// console.log("Loading server route " + title);
|
||||
self.addRoute(routeDefinition);
|
||||
});
|
||||
this.routes.sort((a, b) => {
|
||||
const priorityA = a.info?.priority ?? 100,
|
||||
priorityB = b.info?.priority ?? 100;
|
||||
return priorityB - priorityA;
|
||||
});
|
||||
// Initialise the http vs https
|
||||
this.listenOptions = null;
|
||||
this.protocol = "http";
|
||||
@@ -217,7 +222,7 @@ Server.prototype.findMatchingRoute = function(request,state) {
|
||||
} else {
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
}
|
||||
if(match && request.method === potentialRoute.method) {
|
||||
if(match && (potentialRoute.methods?.includes(request.method) || potentialRoute.method === request.method)) {
|
||||
state.params = [];
|
||||
for(var p=1; p<match.length; p++) {
|
||||
state.params.push(match[p]);
|
||||
|
||||
30
core-server/utils/base64.js
Normal file
30
core-server/utils/base64.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/*\
|
||||
title: $:/core-modules/modules/utils/base64.js
|
||||
type: application/javascript
|
||||
module-type: utils-node
|
||||
|
||||
Base64 UTF-8 utlity functions.
|
||||
|
||||
\*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const{ TextEncoder, TextDecoder } = require("node:util");
|
||||
|
||||
exports.btoa = binstr => Buffer.from(binstr, "binary").toString("base64");
|
||||
|
||||
exports.atob = b64 => Buffer.from(b64, "base64").toString("binary");
|
||||
|
||||
function base64ToBytes(base64) {
|
||||
const binString = exports.atob(base64);
|
||||
return Uint8Array.from(binString, m => m.codePointAt(0));
|
||||
};
|
||||
|
||||
function bytesToBase64(bytes) {
|
||||
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join("");
|
||||
return exports.btoa(binString);
|
||||
};
|
||||
|
||||
exports.base64EncodeUtf8 = str => bytesToBase64(new TextEncoder().encode(str));
|
||||
|
||||
exports.base64DecodeUtf8 = str => new TextDecoder().decode(base64ToBytes(str));
|
||||
95
core-server/utils/escapecss.js
Normal file
95
core-server/utils/escapecss.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/*\
|
||||
title: $:/core-server/modules/utils/escapecss.js
|
||||
type: application/javascript
|
||||
module-type: utils-node
|
||||
|
||||
Provides CSS.escape() functionality.
|
||||
|
||||
\*/
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.escapeCSS = (function() {
|
||||
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
|
||||
/* eslint-disable */
|
||||
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
|
||||
return function(value) {
|
||||
if (arguments.length == 0) {
|
||||
throw new TypeError('`CSS.escape` requires an argument.');
|
||||
}
|
||||
var string = String(value);
|
||||
var length = string.length;
|
||||
var index = -1;
|
||||
var codeUnit;
|
||||
var result = '';
|
||||
var firstCodeUnit = string.charCodeAt(0);
|
||||
while (++index < length) {
|
||||
codeUnit = string.charCodeAt(index);
|
||||
// Note: there’s no need to special-case astral symbols, surrogate
|
||||
// pairs, or lone surrogates.
|
||||
|
||||
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
|
||||
// (U+FFFD).
|
||||
if (codeUnit == 0x0000) {
|
||||
result += '\uFFFD';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
|
||||
// U+007F, […]
|
||||
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
|
||||
// If the character is the first character and is in the range [0-9]
|
||||
// (U+0030 to U+0039), […]
|
||||
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
|
||||
// If the character is the second character and is in the range [0-9]
|
||||
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
|
||||
(
|
||||
index == 1 &&
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
|
||||
firstCodeUnit == 0x002D
|
||||
)
|
||||
) {
|
||||
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
|
||||
result += '\\' + codeUnit.toString(16) + ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
// If the character is the first character and is a `-` (U+002D), and
|
||||
// there is no second character, […]
|
||||
index == 0 &&
|
||||
length == 1 &&
|
||||
codeUnit == 0x002D
|
||||
) {
|
||||
result += '\\' + string.charAt(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the character is not handled by one of the above rules and is
|
||||
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
|
||||
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
|
||||
// U+005A), or [a-z] (U+0061 to U+007A), […]
|
||||
if (
|
||||
codeUnit >= 0x0080 ||
|
||||
codeUnit == 0x002D ||
|
||||
codeUnit == 0x005F ||
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
|
||||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
|
||||
codeUnit >= 0x0061 && codeUnit <= 0x007A
|
||||
) {
|
||||
// the character itself
|
||||
result += string.charAt(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, the escaped character.
|
||||
// https://drafts.csswg.org/cssom/#escape-a-character
|
||||
result += '\\' + string.charAt(index);
|
||||
|
||||
}
|
||||
return result;
|
||||
};
|
||||
/* eslint-enable */
|
||||
})();
|
||||
@@ -5,3 +5,4 @@ TiddlyWiki incorporates code from these fine OpenSource projects:
|
||||
* [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]]
|
||||
* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]]
|
||||
* [[modern-normalize by Sindre Sorhus|https://github.com/sindresorhus/modern-normalize]]
|
||||
* [[diff-match-patch-es by antfu|https://github.com/antfu/diff-match-patch-es]]
|
||||
|
||||
@@ -147,7 +147,7 @@ Settings/AutoSave/Disabled/Description: Do not save changes automatically
|
||||
Settings/AutoSave/Enabled/Description: Save changes automatically
|
||||
Settings/AutoSave/Hint: Attempt to automatically save changes during editing when using a supporting saver
|
||||
Settings/CamelCase/Caption: Camel Case Wiki Links
|
||||
Settings/CamelCase/Hint: You can globally disable automatic linking of ~CamelCase phrases. Requires reload to take effect
|
||||
Settings/CamelCase/Hint: Requires reload to take effect
|
||||
Settings/CamelCase/Description: Enable automatic ~CamelCase linking
|
||||
Settings/Caption: Settings
|
||||
Settings/EditorToolbar/Caption: Editor Toolbar
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
title: $:/language/
|
||||
|
||||
Alerts: Alerts
|
||||
AboveStory/ClassicPlugin/Warning: It looks like you are trying to load a plugin designed for ~TiddlyWiki Classic. Please note that [[these plugins do not work with TiddlyWiki version 5.x.x|https://tiddlywiki.com/#TiddlyWikiClassic]]. ~TiddlyWiki Classic plugins detected:
|
||||
BinaryWarning/Prompt: This tiddler contains binary data
|
||||
ClassicWarning/Hint: This tiddler is written in TiddlyWiki Classic wiki text format, which is not fully compatible with TiddlyWiki version 5. See https://tiddlywiki.com/static/Upgrading.html for more details.
|
||||
|
||||
@@ -34,7 +34,7 @@ function FramedEngine(options) {
|
||||
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette");
|
||||
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
|
||||
this.iframeDoc.open();
|
||||
this.iframeDoc.write("<meta name='color-scheme' content='" + colorScheme + "'>");
|
||||
this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
|
||||
this.iframeDoc.close();
|
||||
// Style the iframe
|
||||
this.iframeNode.className = this.dummyTextArea.className;
|
||||
@@ -156,8 +156,8 @@ Fix the height of textarea to fit content
|
||||
FramedEngine.prototype.fixHeight = function() {
|
||||
// Make sure styles are updated
|
||||
this.copyStyles();
|
||||
// Adjust height
|
||||
if(this.widget.editTag === "textarea") {
|
||||
// If .editRows is initialised, it takes precedence
|
||||
if(this.widget.editTag === "textarea" && !this.widget.editRows) {
|
||||
if(this.widget.editAutoHeight) {
|
||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||
|
||||
@@ -100,7 +100,8 @@ SimpleEngine.prototype.getText = function() {
|
||||
Fix the height of textarea to fit content
|
||||
*/
|
||||
SimpleEngine.prototype.fixHeight = function() {
|
||||
if(this.widget.editTag === "textarea") {
|
||||
// If .editRows is initialised, it takes precedence
|
||||
if((this.widget.editTag === "textarea") && !this.widget.editRows) {
|
||||
if(this.widget.editAutoHeight) {
|
||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||
|
||||
@@ -68,7 +68,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
// Fix height
|
||||
this.engine.fixHeight();
|
||||
// Focus if required
|
||||
if(this.editFocus === "true" || this.editFocus === "yes") {
|
||||
if($tw.browser && (this.editFocus === "true" || this.editFocus === "yes") && !$tw.utils.hasClass(this.parentDomNode.ownerDocument.activeElement,"tc-keep-focus")) {
|
||||
this.engine.focus();
|
||||
}
|
||||
// Add widget message listeners
|
||||
|
||||
41
core/modules/filterrunprefixes/let.js
Normal file
41
core/modules/filterrunprefixes/let.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/let.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Assign a value to a variable
|
||||
|
||||
\*/
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.let = function(operationSubFunction,options) {
|
||||
// Return the filter run prefix function
|
||||
return function(results,source,widget) {
|
||||
// Save the result list
|
||||
var resultList = results.toArray();
|
||||
// Clear the results
|
||||
results.clear();
|
||||
// Evaluate the subfunction to get the variable name
|
||||
var subFunctionResults = operationSubFunction(source,widget);
|
||||
if(subFunctionResults.length === 0) {
|
||||
return;
|
||||
}
|
||||
var name = subFunctionResults[0];
|
||||
if(typeof name !== "string" || name.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Assign the result of the subfunction to the variable
|
||||
var variables = {};
|
||||
variables[name] = resultList;
|
||||
// Return the variables
|
||||
return {
|
||||
variables: variables
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
operator.prefix = filterString.charAt(p++);
|
||||
}
|
||||
// Get the operator name
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
|
||||
if(nextBracketPos === -1) {
|
||||
throw "Missing [ in filter expression";
|
||||
}
|
||||
@@ -79,13 +79,17 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
operand.variable = true;
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "(": // Round brackets
|
||||
operand.multiValuedVariable = true;
|
||||
nextBracketPos = filterString.indexOf(")",p);
|
||||
break;
|
||||
case "/": // regexp brackets
|
||||
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
rexMatch = rex.exec(filterString.substring(p));
|
||||
if(rexMatch) {
|
||||
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
|
||||
// DEPRECATION WARNING
|
||||
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
|
||||
// DEPRECATION WARNING
|
||||
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
|
||||
nextBracketPos = p + rex.lastIndex - 1;
|
||||
}
|
||||
else {
|
||||
@@ -112,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
// Check for multiple operands
|
||||
while(filterString.charAt(p) === ",") {
|
||||
p++;
|
||||
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
|
||||
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
|
||||
nextBracketPos = p;
|
||||
p++;
|
||||
parseOperand(filterString.charAt(nextBracketPos));
|
||||
@@ -141,7 +145,15 @@ exports.parseFilter = function(filterString) {
|
||||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
// Groups:
|
||||
// 1 - entire filter run prefix
|
||||
// 2 - filter run prefix itself
|
||||
// 3 - filter run prefix suffixes
|
||||
// 4 - opening square bracket following filter run prefix
|
||||
// 5 - double quoted string following filter run prefix
|
||||
// 6 - single quoted string following filter run prefix
|
||||
// 7 - anything except for whitespace and square brackets
|
||||
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
@@ -152,38 +164,45 @@ exports.parseFilter = function(filterString) {
|
||||
// Match the start of the operation
|
||||
if(p < filterString.length) {
|
||||
operandRegExp.lastIndex = p;
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(!match || match.index !== p) {
|
||||
throw $tw.language.getString("Error/FilterSyntax");
|
||||
}
|
||||
var operation = {
|
||||
prefix: "",
|
||||
operators: []
|
||||
};
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(match && match.index === p) {
|
||||
// If there is a filter run prefix
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
// Name for named prefixes
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
// Suffixes for filter run prefix
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// Opening square bracket
|
||||
if(match[4]) {
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
}
|
||||
}
|
||||
if(match[4]) { // Opening square bracket
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
// No filter run prefix
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
}
|
||||
// Quoted strings and unquoted title
|
||||
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
|
||||
operation.operators.push(
|
||||
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
|
||||
@@ -213,7 +232,12 @@ exports.getFilterRunPrefixes = function() {
|
||||
|
||||
exports.filterTiddlers = function(filterString,widget,source) {
|
||||
var fn = this.compileFilter(filterString);
|
||||
return fn.call(this,source,widget);
|
||||
try {
|
||||
const fnResult = fn.call(this,source,widget);
|
||||
return fnResult;
|
||||
} catch(e) {
|
||||
return [`${$tw.language.getString("Error/Filter")}: ${e}`];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -251,6 +275,8 @@ exports.compileFilter = function(filterString) {
|
||||
results = [];
|
||||
$tw.utils.each(operation.operators,function(operator) {
|
||||
var operands = [],
|
||||
multiValueOperands = [],
|
||||
isMultiValueOperand = [],
|
||||
operatorFunction;
|
||||
if(!operator.operator) {
|
||||
// Use the "title" operator if no operator is specified
|
||||
@@ -266,28 +292,46 @@ exports.compileFilter = function(filterString) {
|
||||
if(operand.indirect) {
|
||||
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.variable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.multiValuedVariable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
|
||||
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
|
||||
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
|
||||
operand.value = operand.multiValue[0] || "";
|
||||
} else {
|
||||
operand.value = "";
|
||||
operand.multiValue = [];
|
||||
}
|
||||
operand.isMultiValueOperand = true;
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
operand.multiValue = [operand.value];
|
||||
}
|
||||
operands.push(operand.value);
|
||||
multiValueOperands.push(operand.multiValue);
|
||||
isMultiValueOperand.push(!!operand.isMultiValueOperand);
|
||||
});
|
||||
|
||||
// Invoke the appropriate filteroperator module
|
||||
results = operatorFunction(accumulator,{
|
||||
operator: operator.operator,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
widget: widget
|
||||
});
|
||||
operator: operator.operator,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
multiValueOperands: multiValueOperands,
|
||||
isMultiValueOperand: isMultiValueOperand,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
widget: widget
|
||||
});
|
||||
if($tw.utils.isArray(results)) {
|
||||
accumulator = self.makeTiddlerIterator(results);
|
||||
} else {
|
||||
@@ -319,6 +363,8 @@ exports.compileFilter = function(filterString) {
|
||||
return filterRunPrefixes["and"](operationSubFunction, options);
|
||||
case "~": // This operation is unioned into the result only if the main result so far is empty
|
||||
return filterRunPrefixes["else"](operationSubFunction, options);
|
||||
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
|
||||
return filterRunPrefixes["let"](operationSubFunction, options);
|
||||
default:
|
||||
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
|
||||
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
|
||||
@@ -345,7 +391,13 @@ exports.compileFilter = function(filterString) {
|
||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
var operationResult = operationFunction(results,source,widget);
|
||||
if(operationResult) {
|
||||
if(operationResult.variables) {
|
||||
// If the filter run prefix has returned variables, create a new fake widget with those variables
|
||||
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push("/**-- Excessive filter recursion --**/");
|
||||
|
||||
@@ -16,12 +16,8 @@ exports.json = function(source,operand,options) {
|
||||
spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand;
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title);
|
||||
try {
|
||||
data = JSON.parse(title);
|
||||
} catch(e) {
|
||||
data = undefined;
|
||||
}
|
||||
var data = $tw.utils.parseJSONSafe(title,function(){return undefined;});
|
||||
|
||||
if(data !== undefined) {
|
||||
results.push(JSON.stringify(data,null,spaces));
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
|
||||
var functionName = operator.operands[0],
|
||||
params = [],
|
||||
results;
|
||||
$tw.utils.each(operator.operands.slice(1),function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
|
||||
|
||||
@@ -113,6 +113,22 @@ exports["jsonset"] = function(source,operator,options) {
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsondelete"] = function(source,operator,options) {
|
||||
var indexes = operator.operands,
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
// If parsing failed (data equals original title and is a string), return unchanged
|
||||
if(data === title && typeof data === "string") {
|
||||
results.push(title);
|
||||
} else if(data) {
|
||||
data = deleteDataItem(data,indexes);
|
||||
results.push(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -144,7 +160,7 @@ function convertDataItemValueToStrings(item) {
|
||||
return ["null"]
|
||||
} else if(typeof item === "object") {
|
||||
var results = [],i,t;
|
||||
if($tw.utils.isArray(item)) {
|
||||
if(Array.isArray(item)) {
|
||||
// Return all the items in arrays recursively
|
||||
for(i=0; i<item.length; i++) {
|
||||
t = convertDataItemValueToStrings(item[i])
|
||||
@@ -178,7 +194,7 @@ function convertDataItemKeysToStrings(item) {
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
if($tw.utils.isArray(item)) {
|
||||
if(Array.isArray(item)) {
|
||||
for(var i=0; i<item.length; i++) {
|
||||
results.push(i.toString());
|
||||
}
|
||||
@@ -201,7 +217,7 @@ function getDataItemType(data,indexes) {
|
||||
return item;
|
||||
} else if(item === null) {
|
||||
return "null";
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
} else if(Array.isArray(item)) {
|
||||
return "array";
|
||||
} else if(typeof item === "object") {
|
||||
return "object";
|
||||
@@ -213,7 +229,7 @@ function getDataItemType(data,indexes) {
|
||||
function getItemAtIndex(item,index) {
|
||||
if($tw.utils.hop(item,index)) {
|
||||
return item[index];
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
} else if(Array.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
|
||||
@@ -223,15 +239,16 @@ function getItemAtIndex(item,index) {
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
Traverse the index chain and return the item at the specified depth.
|
||||
Returns the item at the end of the traversal, or undefined if traversal fails.
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
function traverseIndexChain(data,indexes,stopBeforeLast) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Get the item
|
||||
var item = data;
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
var stopIndex = stopBeforeLast ? indexes.length - 1 : indexes.length;
|
||||
for(var i = 0; i < stopIndex; i++) {
|
||||
if(item !== undefined) {
|
||||
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
|
||||
item = getItemAtIndex(item,indexes[i]);
|
||||
@@ -243,6 +260,13 @@ function getDataItem(data,indexes) {
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
return traverseIndexChain(data,indexes,false);
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
|
||||
*/
|
||||
@@ -255,18 +279,15 @@ function setDataItem(data,indexes,value) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return value;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain
|
||||
var current = data;
|
||||
for(var i = 0; i < indexes.length - 1; i++) {
|
||||
current = getItemAtIndex(current,indexes[i]);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain up to the parent
|
||||
var current = traverseIndexChain(data,indexes,true);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Add the value to the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if($tw.utils.isArray(current)) {
|
||||
if(Array.isArray(current)) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
}
|
||||
@@ -276,3 +297,32 @@ function setDataItem(data,indexes,value) {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the data structure with the item at the end of the index chain deleted. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then the JSON data structure is returned unmodified.
|
||||
*/
|
||||
function deleteDataItem(data,indexes) {
|
||||
// Check for the root item - don't delete the root
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain up to the parent
|
||||
var current = traverseIndexChain(data,indexes,true);
|
||||
if(current === undefined || current === null) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Delete the item at the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if(Array.isArray(current) && current !== null) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
// Check if index is valid before splicing
|
||||
if(lastIndex >= 0 && lastIndex < current.length) {
|
||||
current.splice(lastIndex,1);
|
||||
}
|
||||
} else if(typeof current === "object" && current !== null) {
|
||||
delete current[lastIndex];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -217,6 +217,10 @@ function makeNumericReducingOperator(fnCalc,initialValue,fnFinal) {
|
||||
source(function(tiddler,title) {
|
||||
result.push($tw.utils.parseNumber(title));
|
||||
});
|
||||
// We return an empty array if there are no input titles
|
||||
if(result.length === 0) {
|
||||
return [];
|
||||
}
|
||||
var value = result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,currentValue);
|
||||
},initialValue);
|
||||
|
||||
@@ -14,31 +14,31 @@ Export our filter function
|
||||
*/
|
||||
exports.sort = function(source,operator,options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,false);
|
||||
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,false,undefined,operator.operands[1]);
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.nsort = function(source,operator,options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,true);
|
||||
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,true,undefined,operator.operands[1]);
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.sortan = function(source, operator, options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results, operator.operand || "title", operator.prefix === "!",false,false,true);
|
||||
options.wiki.sortTiddlers(results, operator.operands[0] || "title", operator.prefix === "!",false,false,true,operator.operands[1]);
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.sortcs = function(source,operator,options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,false);
|
||||
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,false,undefined,operator.operands[1]);
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.nsortcs = function(source,operator,options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,true);
|
||||
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,true,undefined,operator.operands[1]);
|
||||
return results;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,14 +37,14 @@ exports.trim = function(source,operator,options) {
|
||||
operand = (operator.operand || ""),
|
||||
fnCalc;
|
||||
if(suffix === "prefix") {
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];}
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];};
|
||||
} else if(suffix === "suffix") {
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];}
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];};
|
||||
} else {
|
||||
if(operand === "") {
|
||||
fnCalc = function(a) {return [$tw.utils.trim(a)];}
|
||||
fnCalc = function(a) {return [$tw.utils.trim(a)];};
|
||||
} else {
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];}
|
||||
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];};
|
||||
}
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
@@ -71,107 +71,53 @@ exports.join = makeStringReducingOperator(
|
||||
},null
|
||||
);
|
||||
|
||||
var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
|
||||
const 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) + ""];
|
||||
const diffs = dmp.diffMain(a,b);
|
||||
return [dmp.diffLevenshtein(diffs).toString()];
|
||||
}
|
||||
);
|
||||
|
||||
// these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
|
||||
// this function is 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 a = $tw.utils.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);
|
||||
var diffs = dmp.diffMain(lineText1,lineText2,false);
|
||||
dmp.diffCharsToLines(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 || "",
|
||||
var 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)]);
|
||||
});
|
||||
source(function(tiddler,title) {
|
||||
let diffs, patches;
|
||||
if(suffix === "lines" || suffix === "words") {
|
||||
diffs = diffLineWordMode(title,operator.operand,suffix);
|
||||
patches = dmp.patchMake(title,diffs);
|
||||
} else {
|
||||
patches = dmp.patchMake(title,operator.operand);
|
||||
}
|
||||
Array.prototype.push.apply(result,[dmp.patchToText(patches)]);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.applypatches = makeStringBinaryOperator(
|
||||
function(a,b) {
|
||||
var dmpObject = new dmp.diff_match_patch(),
|
||||
patches;
|
||||
let patches;
|
||||
try {
|
||||
patches = dmpObject.patch_fromText(b);
|
||||
patches = dmp.patchFromText(b);
|
||||
} catch(e) {
|
||||
}
|
||||
if(patches) {
|
||||
return [dmpObject.patch_apply(patches,a)[0]];
|
||||
return [dmp.patchApply(patches,a)[0]];
|
||||
} else {
|
||||
return [a];
|
||||
}
|
||||
@@ -279,7 +225,7 @@ exports.pad = function(source,operator,options) {
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
exports.charcode = function(source,operator,options) {
|
||||
var chars = [];
|
||||
|
||||
@@ -16,12 +16,13 @@ exports.title = function(source,operator,options) {
|
||||
var results = [];
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && tiddler.fields.title !== operator.operand) {
|
||||
var titleList = operator.multiValueOperands[0] || [];
|
||||
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push(operator.operand);
|
||||
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
|
||||
// Check for a user defined filter operator
|
||||
if(operator.operator.indexOf(".") !== -1) {
|
||||
var params = [];
|
||||
$tw.utils.each(operator.operands,function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands,function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
|
||||
if(variableInfo && variableInfo.srcVariable) {
|
||||
|
||||
86
core/modules/info/dimensions.js
Normal file
86
core/modules/info/dimensions.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/*\
|
||||
title: $:/core/modules/info/windowdimensions.js
|
||||
type: application/javascript
|
||||
module-type: info
|
||||
\*/
|
||||
|
||||
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
|
||||
if(!$tw.browser) {
|
||||
return [];
|
||||
}
|
||||
|
||||
class WindowDimensionsTracker {
|
||||
constructor(updateCallback) {
|
||||
this.updateCallback = updateCallback;
|
||||
this.resizeHandlers = new Map();
|
||||
this.dimensionsInfo = [
|
||||
["outer/width", win => win.outerWidth],
|
||||
["outer/height", win => win.outerHeight],
|
||||
["inner/width", win => win.innerWidth],
|
||||
["inner/height", win => win.innerHeight],
|
||||
["client/width", win => win.document.documentElement.clientWidth],
|
||||
["client/height", win => win.document.documentElement.clientHeight]
|
||||
];
|
||||
}
|
||||
|
||||
buildTiddlers(win,windowId) {
|
||||
const prefix = `$:/info/browser/window/${windowId}/`;
|
||||
return this.dimensionsInfo.map(([suffix, getter]) => ({
|
||||
title: prefix + suffix,
|
||||
text: String(getter(win))
|
||||
}));
|
||||
}
|
||||
|
||||
clearTiddlers(windowId) {
|
||||
const prefix = `$:/info/browser/window/${windowId}/`,
|
||||
deletions = this.dimensionsInfo.map(([suffix]) => prefix + suffix);
|
||||
this.updateCallback([], deletions);
|
||||
}
|
||||
|
||||
getUpdateHandler(win,windowId) {
|
||||
let scheduled = false;
|
||||
return () => {
|
||||
if(!scheduled) {
|
||||
scheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.updateCallback(this.buildTiddlers(win,windowId), []);
|
||||
scheduled = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trackWindow(win,windowId) {
|
||||
const handler = this.getUpdateHandler(win, windowId);
|
||||
handler(); // initial update
|
||||
win.addEventListener("resize",handler,{passive:true});
|
||||
this.resizeHandlers.set(windowId,{win, handler});
|
||||
}
|
||||
|
||||
untrackWindow(windowId) {
|
||||
const entry = this.resizeHandlers.get(windowId);
|
||||
if(entry) {
|
||||
entry.win.removeEventListener("resize", entry.handler);
|
||||
this.resizeHandlers.delete(windowId);
|
||||
}
|
||||
this.clearTiddlers(windowId);
|
||||
}
|
||||
}
|
||||
|
||||
const tracker = new WindowDimensionsTracker(updateInfoTiddlersCallback);
|
||||
|
||||
// Track main window
|
||||
tracker.trackWindow(window,"system/main");
|
||||
|
||||
// Hook into event bus for user windows
|
||||
if($tw.eventBus) {
|
||||
$tw.eventBus.on("window:opened", ({window: win, windowID}) => {
|
||||
tracker.trackWindow(win, "user/" + windowID);
|
||||
});
|
||||
$tw.eventBus.on("window:closed", ({windowID}) => {
|
||||
tracker.untrackWindow("user/" + windowID);
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -7,23 +7,34 @@ The audio parser parses an audio tiddler into an embeddable HTML element
|
||||
|
||||
\*/
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var AudioParser = function(type,text,options) {
|
||||
var element = {
|
||||
type: "element",
|
||||
tag: "audio",
|
||||
tag: "$audio", // Using $audio to enable widget interception
|
||||
attributes: {
|
||||
controls: {type: "string", value: "controls"},
|
||||
style: {type: "string", value: "width: 100%; object-fit: contain"}
|
||||
}
|
||||
},
|
||||
src;
|
||||
};
|
||||
|
||||
// Pass through source information
|
||||
if(options._canonical_uri) {
|
||||
element.attributes.src = {type: "string", value: options._canonical_uri};
|
||||
element.attributes.type = {type: "string", value: type};
|
||||
} else if(text) {
|
||||
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
|
||||
element.attributes.type = {type: "string", value: type};
|
||||
}
|
||||
|
||||
// Pass through tiddler title if available
|
||||
if(options.title) {
|
||||
element.attributes.tiddler = {type: "string", value: options.title};
|
||||
}
|
||||
|
||||
this.tree = [element];
|
||||
this.source = text;
|
||||
this.type = type;
|
||||
@@ -33,3 +44,4 @@ exports["audio/ogg"] = AudioParser;
|
||||
exports["audio/mpeg"] = AudioParser;
|
||||
exports["audio/mp3"] = AudioParser;
|
||||
exports["audio/mp4"] = AudioParser;
|
||||
|
||||
@@ -11,10 +11,13 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
|
||||
|
||||
var CsvParser = function(type,text,options) {
|
||||
// Special handler for tab-delimited files
|
||||
if (type === 'text/tab-delimited-values' && !options.separator) {
|
||||
if(
|
||||
!options.separator &&
|
||||
(type === "text/tab-delimited-values" || type === "text/tab-separated-values")
|
||||
) {
|
||||
options.separator = "\t";
|
||||
}
|
||||
|
||||
|
||||
// Table framework
|
||||
this.tree = [{
|
||||
"type": "scrollable", "children": [{
|
||||
@@ -32,7 +35,7 @@ var CsvParser = function(type,text,options) {
|
||||
$tw.utils.each(lines, function(columns) {
|
||||
maxColumns = Math.max(columns.length, maxColumns);
|
||||
});
|
||||
|
||||
|
||||
for(var line=0; line<lines.length; line++) {
|
||||
var columns = lines[line];
|
||||
var row = {
|
||||
@@ -55,3 +58,4 @@ var CsvParser = function(type,text,options) {
|
||||
|
||||
exports["text/csv"] = CsvParser;
|
||||
exports["text/tab-delimited-values"] = CsvParser;
|
||||
exports["text/tab-separated-values"] = CsvParser;
|
||||
|
||||
@@ -11,17 +11,16 @@ The image parser parses an image into an embeddable HTML element
|
||||
|
||||
var ImageParser = function(type,text,options) {
|
||||
var element = {
|
||||
type: "element",
|
||||
tag: "img",
|
||||
attributes: {}
|
||||
};
|
||||
type: "image",
|
||||
attributes: {}
|
||||
};
|
||||
if(options._canonical_uri) {
|
||||
element.attributes.src = {type: "string", value: options._canonical_uri};
|
||||
element.attributes.source = {type: "string", value: options._canonical_uri};
|
||||
} else if(text) {
|
||||
if(type === "image/svg+xml" || type === ".svg") {
|
||||
element.attributes.src = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
|
||||
element.attributes.source = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
|
||||
} else {
|
||||
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
|
||||
element.attributes.source = {type: "string", value: "data:" + type + ";base64," + text};
|
||||
}
|
||||
}
|
||||
this.tree = [element];
|
||||
|
||||
@@ -22,7 +22,7 @@ Note that the syntax for comments is simplified to an opening "<!--" sequence an
|
||||
"use strict";
|
||||
|
||||
exports.name = "commentblock";
|
||||
exports.types = {block:true, pragma:true};
|
||||
exports.types = {block: true, pragma: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
@@ -43,9 +43,18 @@ exports.findNextMatch = function(startPos) {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.endMatchRegExp.lastIndex;
|
||||
// Don't return any elements
|
||||
return [];
|
||||
// Return a node representing the comment that is not rendered
|
||||
var commentStart = this.match.index;
|
||||
var commentEnd = this.endMatch.index + this.endMatch[0].length;
|
||||
return [{
|
||||
type: "void",
|
||||
children: [],
|
||||
text: this.parser.source.slice(commentStart, commentEnd),
|
||||
start: commentStart,
|
||||
end: commentEnd
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -40,6 +40,13 @@ exports.findNextMatch = function(startPos) {
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.endMatchRegExp.lastIndex;
|
||||
// Don't return any elements
|
||||
return [];
|
||||
// Return a node representing the inline comment
|
||||
var commentStart = this.match.index;
|
||||
var commentEnd = this.endMatch.index + this.endMatch[0].length;
|
||||
return [{
|
||||
type: "void",
|
||||
text: this.parser.source.slice(commentStart, commentEnd),
|
||||
start: commentStart,
|
||||
end: commentEnd
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ exports.parse = function() {
|
||||
// Return the classed span
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "strike",
|
||||
tag: "s",
|
||||
children: tree
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -28,11 +28,11 @@ exports.init = function(parser) {
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
var start = this.parser.pos;
|
||||
var start = this.parser.pos;
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Create the link unless it is suppressed
|
||||
if(this.match[0].substr(0,1) === "~") {
|
||||
return [{type: "text", text: this.match[0].substr(1)}];
|
||||
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}];
|
||||
} else {
|
||||
return [{
|
||||
type: "element",
|
||||
|
||||
@@ -6,15 +6,15 @@ module-type: wikirule
|
||||
Wiki pragma rule for function, procedure and widget definitions
|
||||
|
||||
```
|
||||
\function name(param:defaultvalue,param2:defaultvalue)
|
||||
\function name(param:"defaultvalue", param2:"defaultvalue")
|
||||
definition text
|
||||
\end
|
||||
|
||||
\procedure name(param:defaultvalue,param2:defaultvalue)
|
||||
\procedure name(param:"defaultvalue", param2:"defaultvalue")
|
||||
definition text
|
||||
\end
|
||||
|
||||
\widget $mywidget(param:defaultvalue,param2:defaultvalue)
|
||||
\widget $mywidget(param:"defaultvalue", param2:"defaultvalue")
|
||||
definition text
|
||||
\end
|
||||
```
|
||||
|
||||
@@ -50,6 +50,8 @@ exports.parse = function() {
|
||||
}
|
||||
}
|
||||
} while(match && !match[1]);
|
||||
// Return the nodes
|
||||
// Mark first and last node, and return the nodes
|
||||
if(tree[0]) tree[0].isRuleStart = true;
|
||||
if(tree[tree.length-1]) tree[tree.length-1].isRuleEnd = true;
|
||||
return tree;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ Parse the most recent match
|
||||
exports.parse = function() {
|
||||
// Retrieve the most recent match so that recursive calls don't overwrite it
|
||||
var tag = this.nextTag;
|
||||
if (!tag.isSelfClosing) {
|
||||
if(!tag.isSelfClosing) {
|
||||
tag.openTagStart = tag.start;
|
||||
tag.openTagEnd = tag.end;
|
||||
}
|
||||
@@ -63,22 +63,22 @@ exports.parse = function() {
|
||||
}
|
||||
tag.end = this.parser.pos;
|
||||
tag.closeTagEnd = tag.end;
|
||||
if (tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== '>') {
|
||||
if(tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== ">") {
|
||||
tag.closeTagStart = tag.end;
|
||||
} else {
|
||||
tag.closeTagStart = tag.closeTagEnd - 2;
|
||||
var closeTagMinPos = tag.children.length > 0 ? tag.children[tag.children.length-1].end : tag.openTagEnd;
|
||||
if (!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
|
||||
while (tag.closeTagStart >= closeTagMinPos) {
|
||||
if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
|
||||
while(tag.closeTagStart >= closeTagMinPos) {
|
||||
var char = this.parser.source[tag.closeTagStart];
|
||||
if (char === '>') {
|
||||
if(char === ">") {
|
||||
tag.closeTagStart = -1;
|
||||
break;
|
||||
}
|
||||
if (char === '<') break;
|
||||
if(char === "<") break;
|
||||
tag.closeTagStart -= 1;
|
||||
}
|
||||
if (tag.closeTagStart < closeTagMinPos) {
|
||||
if(tag.closeTagStart < closeTagMinPos) {
|
||||
tag.closeTagStart = tag.end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ var listTypes = {
|
||||
":": {listTag: "dl", itemTag: "dd"},
|
||||
">": {listTag: "blockquote", itemTag: "div"}
|
||||
};
|
||||
exports.listTypes = listTypes;
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
|
||||
@@ -29,7 +29,7 @@ exports.findNextMatch = function(startPos) {
|
||||
var c = this.parser.source.charAt(nextCall.end);
|
||||
// Ensure EOL after parsed macro
|
||||
// If we didn't need to support IE, we'd just use /(?:\r?\n|$)/ym
|
||||
if ((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
|
||||
if((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
|
||||
this.nextCall = nextCall;
|
||||
return nextStart;
|
||||
}
|
||||
|
||||
@@ -42,3 +42,5 @@ exports.parse = function() {
|
||||
this.parser.pos = call.end;
|
||||
return [call];
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -52,9 +52,10 @@ exports.parse = function() {
|
||||
}
|
||||
}
|
||||
// Is the remainder of the \define line blank after the parameter close paren?
|
||||
var reEnd;
|
||||
var reEnd,isBlock = true;
|
||||
if(this.match[3]) {
|
||||
// If so, it is a multiline definition and the end of the body is marked with \end
|
||||
isBlock = false;
|
||||
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?\\s*?(?:$|\\r?\\n))","mg");
|
||||
} else {
|
||||
// Otherwise, the end of the definition is marked by the end of the line
|
||||
@@ -79,7 +80,8 @@ exports.parse = function() {
|
||||
attributes: {},
|
||||
children: [],
|
||||
params: params,
|
||||
isMacroDefinition: true
|
||||
isMacroDefinition: true,
|
||||
isBlock: isBlock && !!endMatch
|
||||
}];
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
|
||||
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
|
||||
|
||||
@@ -31,6 +31,7 @@ Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Move past the pragma invocation
|
||||
var start = this.parser.pos;
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Parse whitespace delimited tokens terminated by a line break
|
||||
var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg,
|
||||
@@ -58,6 +59,11 @@ exports.parse = function() {
|
||||
this.parser.parseAsInline = true;
|
||||
}
|
||||
}
|
||||
// No parse tree nodes to return
|
||||
return [];
|
||||
return [{
|
||||
type: "void",
|
||||
children: [],
|
||||
parseAsInline: this.parser.parseAsInline,
|
||||
start: start,
|
||||
end: this.parser.pos
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -113,3 +113,5 @@ exports.parseLink = function(source,pos) {
|
||||
node.end = closePos + 2;
|
||||
return node;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ exports.parse = function() {
|
||||
var text = this.match[1],
|
||||
link = this.match[2] || text,
|
||||
textEndPos = this.parser.source.indexOf("|", start);
|
||||
if (textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
|
||||
if(textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
|
||||
textEndPos = this.matchRegExp.lastIndex - 2;
|
||||
}
|
||||
var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start;
|
||||
|
||||
@@ -54,6 +54,13 @@ exports.parse = function() {
|
||||
if(tokens.length > 0) {
|
||||
this.parser.amendRules(tokens[0],tokens.slice(1));
|
||||
}
|
||||
// No parse tree nodes to return
|
||||
return [];
|
||||
// No widget to render, return void node.
|
||||
return [{
|
||||
type: "void",
|
||||
attributes: {
|
||||
action: {type: "string", value: tokens[0]},
|
||||
rules: {type: "string", value: tokens.slice(1).join(" ")}
|
||||
},
|
||||
children: []
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -64,5 +64,8 @@ exports.parse = function() {
|
||||
$tw.utils.addAttributeToParseTreeNode(tree[t],"style",styles.join(""));
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
return [{
|
||||
type: "void",
|
||||
children: tree
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
// Regexp to match /@@(styles)?\s*(\.class\s+)?/
|
||||
this.matchRegExp = /@@((?:[^\.\r\n\s:]+:[^\r\n;]+;)+)?(\.(?:[^\r\n\s]+)\s+)?/mg;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,27 @@ exports.init = function(parser) {
|
||||
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
Reject the match if we don't have a template or text reference
|
||||
*/
|
||||
exports.findNextMatch = function(startPos) {
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
if(this.match) {
|
||||
var template = $tw.utils.trim(this.match[2]),
|
||||
textRef = $tw.utils.trim(this.match[1]);
|
||||
// Bail if we don't have a template or text reference
|
||||
if(!template && !textRef) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this.match.index;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
return this.match ? this.match.index : undefined;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
|
||||
@@ -23,6 +23,27 @@ exports.init = function(parser) {
|
||||
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
Reject the match if we don't have a template or text reference
|
||||
*/
|
||||
exports.findNextMatch = function(startPos) {
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
if(this.match) {
|
||||
var template = $tw.utils.trim(this.match[2]),
|
||||
textRef = $tw.utils.trim(this.match[1]);
|
||||
// Bail if we don't have a template or text reference
|
||||
if(!template && !textRef) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this.match.index;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
return this.match ? this.match.index : undefined;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
|
||||
@@ -60,22 +60,37 @@ exports.parse = function() {
|
||||
var parser = this.parser.wiki.parseText(parseType,text,{defaultType: "text/plain"});
|
||||
// If there's no render type, just return the parse tree
|
||||
if(!renderType) {
|
||||
return parser.tree;
|
||||
return [{
|
||||
type: "void",
|
||||
children: $tw.utils.isArray(parser.tree) ? parser.tree : [parser.tree],
|
||||
parseType: parseType,
|
||||
renderType: renderType,
|
||||
text: text,
|
||||
start: start,
|
||||
end: this.parser.pos
|
||||
}];
|
||||
} else {
|
||||
// Otherwise, render to the rendertype and return in a <PRE> tag
|
||||
var widgetNode = this.parser.wiki.makeWidget(parser),
|
||||
container = $tw.fakeDocument.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
text = renderType === "text/html" ? container.innerHTML : container.textContent;
|
||||
var renderResult = renderType === "text/html" ? container.innerHTML : container.textContent;
|
||||
// Use void node to carry important info for typedblock
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "pre",
|
||||
type: "void",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: text,
|
||||
start: start,
|
||||
end: this.parser.pos
|
||||
}]
|
||||
type: "element",
|
||||
tag: "pre",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: renderResult,
|
||||
}]
|
||||
}],
|
||||
parseType: parseType,
|
||||
renderType: renderType,
|
||||
text: text,
|
||||
start: start,
|
||||
end: this.parser.pos
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -215,8 +215,8 @@ WikiParser.prototype.parsePragmas = function() {
|
||||
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;
|
||||
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);
|
||||
@@ -245,9 +245,9 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
|
||||
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;
|
||||
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;
|
||||
@@ -256,7 +256,7 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
|
||||
var start = this.pos;
|
||||
var children = this.parseInlineRun(terminatorRegExp);
|
||||
var end = this.pos;
|
||||
return [{type: "element", tag: "p", children: children, start: start, end: end }];
|
||||
return [{type: "element", tag: "p", children: children, start: start, end: end, rule: "parseblock" }];
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -350,10 +350,10 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
@@ -410,9 +410,9 @@ WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegEx
|
||||
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;
|
||||
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);
|
||||
|
||||
@@ -35,7 +35,9 @@ DownloadSaver.prototype.save = function(text,method,callback,options) {
|
||||
}
|
||||
// Set up the link
|
||||
var link = document.createElement("a");
|
||||
if(Blob !== undefined) {
|
||||
// We prefer Blobs if they're available, unless we're dealing with a tiddler type declaring itself full of base64 encoded content.
|
||||
// Then we use data urls, because browsers will know to decode the stream and download the actual binary file as intended.
|
||||
if(Blob !== undefined && !type.includes(";base64")) {
|
||||
var blob = new Blob([text], {type: type});
|
||||
link.setAttribute("href", URL.createObjectURL(blob));
|
||||
} else {
|
||||
|
||||
66
core/modules/savers/postmessage.js
Normal file
66
core/modules/savers/postmessage.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/*\
|
||||
title: $:/core/modules/savers/postmessage.js
|
||||
type: application/javascript
|
||||
module-type: saver
|
||||
|
||||
Handles saving changes via window.postMessage() to the window.parent
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Select the appropriate saver module and set it up
|
||||
*/
|
||||
var PostMessageSaver = function(wiki) {
|
||||
this.publisher = new $tw.utils.BrowserMessagingPublisher({type: "SAVE"});
|
||||
};
|
||||
|
||||
PostMessageSaver.prototype.save = function(text,method,callback,options) {
|
||||
// Fail if the publisher hasn't been fully initialised
|
||||
if(!this.publisher.canSend()) {
|
||||
return false;
|
||||
}
|
||||
// Send the save request
|
||||
this.publisher.send({
|
||||
verb: "SAVE",
|
||||
body: text
|
||||
},function(err) {
|
||||
if(err) {
|
||||
callback("PostMessageSaver Error: " + err);
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
// Indicate that we handled the save
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Information about this saver
|
||||
*/
|
||||
PostMessageSaver.prototype.info = {
|
||||
name: "postmessage",
|
||||
capabilities: ["save", "autosave"],
|
||||
priority: 100
|
||||
};
|
||||
|
||||
/*
|
||||
Static method that returns true if this saver is capable of working
|
||||
*/
|
||||
exports.canSave = function(wiki) {
|
||||
// Provisionally say that we can save
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Create an instance of this saver
|
||||
*/
|
||||
exports.create = function(wiki) {
|
||||
return new PostMessageSaver(wiki);
|
||||
};
|
||||
|
||||
})();
|
||||
46
core/modules/startup/eventbus.js
Normal file
46
core/modules/startup/eventbus.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/eventbus.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Event bus for cross module communication
|
||||
\*/
|
||||
|
||||
exports.name = "eventbus";
|
||||
exports.platforms = ["browser"];
|
||||
exports.before = ["windows"];
|
||||
exports.synchronous = true;
|
||||
|
||||
$tw.eventBus = {
|
||||
listenersMap: new Map(),
|
||||
|
||||
on(event,handler) {
|
||||
if(!this.listenersMap.has(event)) {
|
||||
this.listenersMap.set(event,new Set());
|
||||
}
|
||||
const listeners = this.listenersMap.get(event);
|
||||
listeners.add(handler);
|
||||
},
|
||||
|
||||
off(event,handler) {
|
||||
const listeners = this.listenersMap.get(event);
|
||||
if(listeners) {
|
||||
listeners.delete(handler);
|
||||
}
|
||||
},
|
||||
|
||||
once(event,handler) {
|
||||
const wrapper = (...args) => {
|
||||
handler(...args);
|
||||
this.off(event, wrapper);
|
||||
};
|
||||
this.on(event, wrapper);
|
||||
},
|
||||
|
||||
emit(event,data) {
|
||||
const listeners = this.listenersMap.get(event);
|
||||
if(listeners) {
|
||||
listeners.forEach(fn => fn(data));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -19,6 +19,16 @@ exports.synchronous = true;
|
||||
var FAVICON_TITLE = "$:/favicon.ico";
|
||||
|
||||
exports.startup = function() {
|
||||
var setFavicon = function() {
|
||||
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
|
||||
if(tiddler) {
|
||||
var faviconLink = document.getElementById("faviconLink"),
|
||||
dataURI = $tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri);
|
||||
faviconLink.setAttribute("href",dataURI);
|
||||
$tw.faviconPublisher.send({verb: "FAVICON",body: dataURI});
|
||||
}
|
||||
}
|
||||
$tw.faviconPublisher = new $tw.utils.BrowserMessagingPublisher({type: "FAVICON", onsubscribe: setFavicon});
|
||||
// Set up the favicon
|
||||
setFavicon();
|
||||
// Reset the favicon when the tiddler changes
|
||||
@@ -28,11 +38,3 @@ exports.startup = function() {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function setFavicon() {
|
||||
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
|
||||
if(tiddler) {
|
||||
var faviconLink = document.getElementById("faviconLink");
|
||||
faviconLink.setAttribute("href",$tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,17 @@ var TITLE_INFO_PLUGIN = "$:/temp/info-plugin";
|
||||
|
||||
exports.startup = function() {
|
||||
// Function to bake the info plugin with new tiddlers
|
||||
var updateInfoPlugin = function(tiddlerFieldsArray) {
|
||||
// additions: array of tiddler field objects
|
||||
// removals: array of titles to remove
|
||||
var updateInfoPlugin = function(additions = [], removals = []) {
|
||||
// Get the existing tiddlers
|
||||
var json = $tw.wiki.getTiddlerData(TITLE_INFO_PLUGIN,{tiddlers: {}});
|
||||
// Add the new ones
|
||||
$tw.utils.each(tiddlerFieldsArray,function(fields) {
|
||||
$tw.utils.each(removals,function(title) {
|
||||
if(json.tiddlers[title]) {
|
||||
delete json.tiddlers[title];
|
||||
}
|
||||
});
|
||||
$tw.utils.each(additions,function(fields) {
|
||||
if(fields && fields.title) {
|
||||
json.tiddlers[fields.title] = fields;
|
||||
}
|
||||
@@ -47,7 +53,7 @@ exports.startup = function() {
|
||||
}
|
||||
});
|
||||
updateInfoPlugin(tiddlerFieldsArray);
|
||||
var changes = $tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.registerPluginTiddlers("info",[TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.unpackPluginTiddlers();
|
||||
};
|
||||
|
||||
@@ -33,10 +33,15 @@ exports.startup = function() {
|
||||
});
|
||||
$tw.titleContainer = $tw.fakeDocument.createElement("div");
|
||||
$tw.titleWidgetNode.render($tw.titleContainer,null);
|
||||
document.title = $tw.titleContainer.textContent;
|
||||
var publishTitle = function() {
|
||||
$tw.titlePublisher.send({verb: "PAGETITLE",body: document.title});
|
||||
document.title = $tw.titleContainer.textContent;
|
||||
};
|
||||
$tw.titlePublisher = new $tw.utils.BrowserMessagingPublisher({type: "PAGETITLE", onsubscribe: publishTitle});
|
||||
publishTitle();
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.titleWidgetNode.refresh(changes,$tw.titleContainer,null)) {
|
||||
document.title = $tw.titleContainer.textContent;
|
||||
publishTitle();
|
||||
}
|
||||
});
|
||||
// Set up the styles
|
||||
|
||||
@@ -56,9 +56,11 @@ exports.startup = function() {
|
||||
srcDocument.write("<!DOCTYPE html><head></head><body class='tc-body tc-single-tiddler-window'></body></html>");
|
||||
srcDocument.close();
|
||||
srcDocument.title = windowTitle;
|
||||
$tw.eventBus.emit("window:opened",{windowID, window: srcWindow});
|
||||
srcWindow.addEventListener("beforeunload",function(event) {
|
||||
delete $tw.windows[windowID];
|
||||
$tw.wiki.removeEventListener("change",refreshHandler);
|
||||
$tw.eventBus.emit("window:closed",{windowID});
|
||||
},false);
|
||||
// Set up the styles
|
||||
var styleWidgetNode = $tw.wiki.makeTranscludeWidget("$:/core/ui/PageStylesheet",{
|
||||
|
||||
@@ -47,16 +47,16 @@ ClassicStoryView.prototype.insert = function(widget) {
|
||||
// Reset the margin once the transition is over
|
||||
setTimeout(function() {
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{marginBottom: ""}
|
||||
]);
|
||||
$tw.utils.removeStyle(targetElement, "transition");
|
||||
},duration);
|
||||
// Set up the initial position of the element
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{marginBottom: (-currHeight) + "px"},
|
||||
{opacity: "0.0"}
|
||||
]);
|
||||
$tw.utils.removeStyle(targetElement, "transition");
|
||||
$tw.utils.forceLayout(targetElement);
|
||||
// Transition to the final position
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
@@ -64,7 +64,7 @@ ClassicStoryView.prototype.insert = function(widget) {
|
||||
"margin-bottom " + duration + "ms " + easing},
|
||||
{marginBottom: currMarginBottom + "px"},
|
||||
{opacity: "1.0"}
|
||||
]);
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -94,11 +94,9 @@ ClassicStoryView.prototype.remove = function(widget) {
|
||||
setTimeout(removeElement,duration);
|
||||
// Animate the closure
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{transform: "translateX(0px)"},
|
||||
{marginBottom: currMarginBottom + "px"},
|
||||
{opacity: "1.0"}
|
||||
]);
|
||||
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
|
||||
$tw.utils.forceLayout(targetElement);
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms " + easing + ", " +
|
||||
@@ -113,4 +111,4 @@ ClassicStoryView.prototype.remove = function(widget) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.classic = ClassicStoryView;
|
||||
exports.classic = ClassicStoryView;
|
||||
@@ -37,10 +37,7 @@ PopStoryView.prototype.insert = function(widget) {
|
||||
}
|
||||
// Reset once the transition is over
|
||||
setTimeout(function() {
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{transform: "none"}
|
||||
]);
|
||||
$tw.utils.removeStyles(targetElement, ["transition", "transform"]);
|
||||
$tw.utils.setStyle(widget.document.body,[
|
||||
{"overflow-x": ""}
|
||||
]);
|
||||
@@ -51,10 +48,10 @@ PopStoryView.prototype.insert = function(widget) {
|
||||
]);
|
||||
// Set up the initial position of the element
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{transform: "scale(2)"},
|
||||
{opacity: "0.0"}
|
||||
]);
|
||||
$tw.utils.removeStyle(targetElement, "transition");
|
||||
$tw.utils.forceLayout(targetElement);
|
||||
// Transition to the final position
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
@@ -63,6 +60,9 @@ PopStoryView.prototype.insert = function(widget) {
|
||||
{transform: "scale(1)"},
|
||||
{opacity: "1.0"}
|
||||
]);
|
||||
setTimeout(function() {
|
||||
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opactity"]);
|
||||
}, duration)
|
||||
};
|
||||
|
||||
PopStoryView.prototype.remove = function(widget) {
|
||||
@@ -81,11 +81,7 @@ PopStoryView.prototype.remove = function(widget) {
|
||||
// Remove the element at the end of the transition
|
||||
setTimeout(removeElement,duration);
|
||||
// Animate the closure
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: "none"},
|
||||
{transform: "scale(1)"},
|
||||
{opacity: "1.0"}
|
||||
]);
|
||||
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
|
||||
$tw.utils.forceLayout(targetElement);
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
|
||||
@@ -95,4 +91,4 @@ PopStoryView.prototype.remove = function(widget) {
|
||||
]);
|
||||
};
|
||||
|
||||
exports.pop = PopStoryView;
|
||||
exports.pop = PopStoryView;
|
||||
@@ -96,6 +96,9 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
{transform: "translateX(0px) translateY(0px) scale(1)"},
|
||||
{zIndex: "500"},
|
||||
]);
|
||||
setTimeout(function() {
|
||||
$tw.utils.removeStyles(targetElement, ["transition", "opacity", "transform", "zIndex"]);
|
||||
}, duration);
|
||||
// Transform the previous tiddler out of the way and then hide it
|
||||
if(prevCurrentTiddler && prevCurrentTiddler !== targetElement) {
|
||||
scale = zoomBounds.width / sourceBounds.width;
|
||||
@@ -207,6 +210,9 @@ ZoominListView.prototype.remove = function(widget) {
|
||||
{opacity: "0"},
|
||||
{zIndex: "0"}
|
||||
]);
|
||||
setTimeout(function() {
|
||||
$tw.utils.removeStyles(toWidgetDomNode, ["transformOrigin", "transform", "transition", "opacity", "zIndex"]);
|
||||
}, duration);
|
||||
setTimeout(removeElement,duration);
|
||||
// Now the tiddler we're going back to
|
||||
if(toWidgetDomNode) {
|
||||
@@ -222,4 +228,4 @@ ZoominListView.prototype.logTextNodeRoot = function(node) {
|
||||
this.textNodeLogger.log($tw.language.getString("Error/ZoominTextNode") + " " + node.textContent);
|
||||
};
|
||||
|
||||
exports.zoomin = ZoominListView;
|
||||
exports.zoomin = ZoominListView;
|
||||
@@ -1,142 +0,0 @@
|
||||
// From https://gist.github.com/Nijikokun/5192472
|
||||
//
|
||||
// UTF8 Module
|
||||
//
|
||||
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
|
||||
//
|
||||
// copyright: MIT
|
||||
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
|
||||
(function (name, definition, context, dependencies) {
|
||||
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
|
||||
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
|
||||
else { context[name] = definition.apply(context); }
|
||||
})('utf8', function () {
|
||||
return {
|
||||
encode: function (string) {
|
||||
if (typeof string !== 'string') return string;
|
||||
else string = string.replace(/\r\n/g, "\n");
|
||||
var output = "", i = 0, charCode;
|
||||
|
||||
for (i; i < string.length; i++) {
|
||||
charCode = string.charCodeAt(i);
|
||||
|
||||
if (charCode < 128) {
|
||||
output += String.fromCharCode(charCode);
|
||||
} else if ((charCode > 127) && (charCode < 2048)) {
|
||||
output += String.fromCharCode((charCode >> 6) | 192);
|
||||
output += String.fromCharCode((charCode & 63) | 128);
|
||||
} else if ((charCode > 55295) && (charCode < 57344) && string.length > i+1) {
|
||||
// Surrogate pair
|
||||
var hiSurrogate = charCode;
|
||||
var loSurrogate = string.charCodeAt(i+1);
|
||||
i++; // Skip the low surrogate on the next loop pass
|
||||
var codePoint = (((hiSurrogate - 55296) << 10) | (loSurrogate - 56320)) + 65536;
|
||||
output += String.fromCharCode((codePoint >> 18) | 240);
|
||||
output += String.fromCharCode(((codePoint >> 12) & 63) | 128);
|
||||
output += String.fromCharCode(((codePoint >> 6) & 63) | 128);
|
||||
output += String.fromCharCode((codePoint & 63) | 128);
|
||||
} else {
|
||||
// Not a surrogate pair, or a dangling surrogate without its partner that we'll just encode as-is
|
||||
output += String.fromCharCode((charCode >> 12) | 224);
|
||||
output += String.fromCharCode(((charCode >> 6) & 63) | 128);
|
||||
output += String.fromCharCode((charCode & 63) | 128);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
decode: function (string) {
|
||||
if (typeof string !== 'string') return string;
|
||||
var output = "", i = 0, charCode = 0;
|
||||
|
||||
while (i < string.length) {
|
||||
charCode = string.charCodeAt(i);
|
||||
|
||||
if (charCode < 128) {
|
||||
output += String.fromCharCode(charCode),
|
||||
i++;
|
||||
} else if ((charCode > 191) && (charCode < 224)) {
|
||||
output += String.fromCharCode(((charCode & 31) << 6) | (string.charCodeAt(i + 1) & 63));
|
||||
i += 2;
|
||||
} else if ((charCode > 223) && (charCode < 240)) {
|
||||
output += String.fromCharCode(((charCode & 15) << 12) | ((string.charCodeAt(i + 1) & 63) << 6) | (string.charCodeAt(i + 2) & 63));
|
||||
i += 3;
|
||||
} else {
|
||||
var codePoint = ((charCode & 7) << 18) | ((string.charCodeAt(i + 1) & 63) << 12) | ((string.charCodeAt(i + 2) & 63) << 6) | (string.charCodeAt(i + 3) & 63);
|
||||
// output += String.fromCodePoint(codePoint); // Can't do this because Internet Explorer doesn't have String.fromCodePoint
|
||||
output += String.fromCharCode(((codePoint - 65536) >> 10) + 55296) + String.fromCharCode(((codePoint - 65536) & 1023) + 56320); // So we do this instead
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
};
|
||||
}, this);
|
||||
|
||||
// Base64 Module
|
||||
//
|
||||
// Cleaner, modularized and properly scoped base64 encoding and decoding module for strings.
|
||||
//
|
||||
// copyright: MIT
|
||||
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
|
||||
(function (name, definition, context, dependencies) {
|
||||
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
|
||||
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
|
||||
else { context[name] = definition.apply(context); }
|
||||
})('base64', function (utf8) {
|
||||
var $this = this;
|
||||
var $utf8 = utf8 || this.utf8;
|
||||
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
return {
|
||||
encode: function (input) {
|
||||
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
|
||||
if (typeof input !== 'string') return input;
|
||||
else input = $utf8.encode(input);
|
||||
var output = "", a, b, c, d, e, f, g, i = 0;
|
||||
|
||||
while (i < input.length) {
|
||||
a = input.charCodeAt(i++);
|
||||
b = input.charCodeAt(i++);
|
||||
c = input.charCodeAt(i++);
|
||||
d = a >> 2;
|
||||
e = ((a & 3) << 4) | (b >> 4);
|
||||
f = ((b & 15) << 2) | (c >> 6);
|
||||
g = c & 63;
|
||||
|
||||
if (isNaN(b)) f = g = 64;
|
||||
else if (isNaN(c)) g = 64;
|
||||
|
||||
output += map.charAt(d) + map.charAt(e) + map.charAt(f) + map.charAt(g);
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
decode: function (input) {
|
||||
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
|
||||
if (typeof input !== 'string') return input;
|
||||
else input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
var output = "", a, b, c, d, e, f, g, i = 0;
|
||||
|
||||
while (i < input.length) {
|
||||
d = map.indexOf(input.charAt(i++));
|
||||
e = map.indexOf(input.charAt(i++));
|
||||
f = map.indexOf(input.charAt(i++));
|
||||
g = map.indexOf(input.charAt(i++));
|
||||
|
||||
a = (d << 2) | (e >> 4);
|
||||
b = ((e & 15) << 4) | (f >> 2);
|
||||
c = ((f & 3) << 6) | g;
|
||||
|
||||
output += String.fromCharCode(a);
|
||||
if (f != 64) output += String.fromCharCode(b);
|
||||
if (g != 64) output += String.fromCharCode(c);
|
||||
}
|
||||
|
||||
return $utf8.decode(output);
|
||||
}
|
||||
}
|
||||
}, this, [ "utf8" ]);
|
||||
@@ -1,9 +0,0 @@
|
||||
// From https://gist.github.com/Nijikokun/5192472
|
||||
//
|
||||
// UTF8 Module
|
||||
//
|
||||
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
|
||||
//
|
||||
// copyright: MIT
|
||||
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
|
||||
!function(r,e,o,t){void 0!==o.module&&o.module.exports?o.module.exports=e.apply(o):void 0!==o.define&&"function"===o.define&&o.define.amd?define("utf8",[],e):o.utf8=e.apply(o)}(0,function(){return{encode:function(r){if("string"!=typeof r)return r;r=r.replace(/\r\n/g,"\n");for(var e,o="",t=0;t<r.length;t++)if((e=r.charCodeAt(t))<128)o+=String.fromCharCode(e);else if(e>127&&e<2048)o+=String.fromCharCode(e>>6|192),o+=String.fromCharCode(63&e|128);else if(e>55295&&e<57344&&r.length>t+1){var i=e,n=r.charCodeAt(t+1);t++;var d=65536+(i-55296<<10|n-56320);o+=String.fromCharCode(d>>18|240),o+=String.fromCharCode(d>>12&63|128),o+=String.fromCharCode(d>>6&63|128),o+=String.fromCharCode(63&d|128)}else o+=String.fromCharCode(e>>12|224),o+=String.fromCharCode(e>>6&63|128),o+=String.fromCharCode(63&e|128);return o},decode:function(r){if("string"!=typeof r)return r;for(var e="",o=0,t=0;o<r.length;)if((t=r.charCodeAt(o))<128)e+=String.fromCharCode(t),o++;else if(t>191&&t<224)e+=String.fromCharCode((31&t)<<6|63&r.charCodeAt(o+1)),o+=2;else if(t>223&&t<240)e+=String.fromCharCode((15&t)<<12|(63&r.charCodeAt(o+1))<<6|63&r.charCodeAt(o+2)),o+=3;else{var i=(7&t)<<18|(63&r.charCodeAt(o+1))<<12|(63&r.charCodeAt(o+2))<<6|63&r.charCodeAt(o+3);e+=String.fromCharCode(55296+(i-65536>>10))+String.fromCharCode(56320+(i-65536&1023)),o+=4}return e}}},this),function(r,e,o,t){if(void 0!==o.module&&o.module.exports){if(t&&o.require)for(var i=0;i<t.length;i++)o[t[i]]=o.require(t[i]);o.module.exports=e.apply(o)}else void 0!==o.define&&"function"===o.define&&o.define.amd?define("base64",t||[],e):o.base64=e.apply(o)}(0,function(r){var e=r||this.utf8,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=e.encode(r);for(var t,i,n,d,f,a,h,C="",c=0;c<r.length;)d=(t=r.charCodeAt(c++))>>2,f=(3&t)<<4|(i=r.charCodeAt(c++))>>4,a=(15&i)<<2|(n=r.charCodeAt(c++))>>6,h=63&n,isNaN(i)?a=h=64:isNaN(n)&&(h=64),C+=o.charAt(d)+o.charAt(f)+o.charAt(a)+o.charAt(h);return C},decode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,i,n,d,f,a,h="",C=0;C<r.length;)t=o.indexOf(r.charAt(C++))<<2|(d=o.indexOf(r.charAt(C++)))>>4,i=(15&d)<<4|(f=o.indexOf(r.charAt(C++)))>>2,n=(3&f)<<6|(a=o.indexOf(r.charAt(C++))),h+=String.fromCharCode(t),64!=f&&(h+=String.fromCharCode(i)),64!=a&&(h+=String.fromCharCode(n));return e.decode(h)}}},this,["utf8"]);
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"tiddlers": [
|
||||
{
|
||||
"file": "base64-utf8.module.min.js",
|
||||
"fields": {
|
||||
"type": "application/javascript",
|
||||
"title": "$:/core/modules/utils/base64-utf8/base64-utf8.module.js",
|
||||
"module-type": "library"
|
||||
},
|
||||
"prefix": "(function(){",
|
||||
"suffix": "}).call(exports);"
|
||||
}
|
||||
]
|
||||
}
|
||||
31
core/modules/utils/base64.js
Normal file
31
core/modules/utils/base64.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/base64.js
|
||||
type: application/javascript
|
||||
module-type: utils-browser
|
||||
|
||||
Base64 utility functions
|
||||
|
||||
\*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Base64 utility functions that work in either browser or Node.js
|
||||
*/
|
||||
|
||||
exports.btoa = binstr => window.btoa(binstr);
|
||||
exports.atob = b64 => window.atob(b64);
|
||||
|
||||
function base64ToBytes(base64) {
|
||||
const binString = exports.atob(base64);
|
||||
return Uint8Array.from(binString, m => m.codePointAt(0));
|
||||
};
|
||||
|
||||
function bytesToBase64(bytes) {
|
||||
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join("");
|
||||
return exports.btoa(binString);
|
||||
};
|
||||
|
||||
exports.base64EncodeUtf8 = str => bytesToBase64(new TextEncoder().encode(str));
|
||||
|
||||
exports.base64DecodeUtf8 = str => new TextDecoder().decode(base64ToBytes(str));
|
||||
58
core/modules/utils/deprecated.js
Normal file
58
core/modules/utils/deprecated.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/deprecated.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
|
||||
Deprecated util functions
|
||||
|
||||
\*/
|
||||
|
||||
exports.logTable = data => console.table(data);
|
||||
|
||||
exports.repeat = (str,count) => str.repeat(count);
|
||||
|
||||
exports.startsWith = (str,search) => str.startsWith(search);
|
||||
|
||||
exports.endsWith = (str,search) => str.endsWith(search);
|
||||
|
||||
exports.trim = function(str) {
|
||||
if(typeof str === "string") {
|
||||
return str.trim();
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
exports.hopArray = (object,array) => array.some(element => $tw.utils.hop(object,element));
|
||||
|
||||
exports.sign = Math.sign;
|
||||
|
||||
exports.strEndsWith = (str,ending,position) => str.endsWith(ending,position);
|
||||
|
||||
exports.stringifyNumber = num => num.toString();
|
||||
|
||||
exports.tagToCssSelector = function(tagName) {
|
||||
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
|
||||
return "\\" + c;
|
||||
});
|
||||
};
|
||||
|
||||
exports.domContains = (a,b) => a.compareDocumentPosition(b) & 16;
|
||||
|
||||
exports.domMatchesSelector = (node,selector) => node.matches(selector);
|
||||
|
||||
exports.hasClass = (el,className) => el.classList && el.classList.contains(className);
|
||||
|
||||
exports.addClass = function(el,className) {
|
||||
el.classList && className && el.classList.add(className);
|
||||
};
|
||||
|
||||
exports.removeClass = function(el,className) {
|
||||
el.classList && className && el.classList.remove(className);
|
||||
};
|
||||
|
||||
exports.toggleClass = function(el,className,status) {
|
||||
el.classList && className && el.classList.toggle(className, status);
|
||||
};
|
||||
|
||||
exports.getLocationPath = () => window.location.origin + window.location.pathname;
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,26 @@ exports.setStyle = function(element,styles) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove style properties of an element
|
||||
element: dom node
|
||||
styleProperties: ordered array of string property names
|
||||
*/
|
||||
exports.removeStyles = function(element, styleProperties) {
|
||||
for (var i=0; i<styleProperties.length; i++) {
|
||||
element.style.removeProperty($tw.utils.convertStyleNameToPropertyName(styleProperties[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Remove single style property of an element
|
||||
element: dom node
|
||||
styleProperty: string property name
|
||||
*/
|
||||
exports.removeStyle = function(element, styleProperty) {
|
||||
$tw.utils.removeStyles(element, [styleProperty])
|
||||
}
|
||||
|
||||
/*
|
||||
Converts a standard CSS property name into the local browser-specific equivalent. For example:
|
||||
"background-color" --> "backgroundColor"
|
||||
@@ -41,7 +61,7 @@ exports.convertStyleNameToPropertyName = function(styleName) {
|
||||
var propertyName = $tw.utils.unHyphenateCss(styleName);
|
||||
// Then check if it needs a prefix
|
||||
if($tw.browser && document.body.style[propertyName] === undefined) {
|
||||
var prefixes = ["O","MS","Moz","webkit"];
|
||||
var prefixes = ["Moz","webkit"];
|
||||
for(var t=0; t<prefixes.length; t++) {
|
||||
var prefixedName = prefixes[t] + propertyName.substr(0,1).toUpperCase() + propertyName.substr(1);
|
||||
if(document.body.style[prefixedName] !== undefined) {
|
||||
@@ -92,8 +112,6 @@ var eventNameMappings = {
|
||||
correspondingCssProperty: "transition",
|
||||
mappings: {
|
||||
transition: "transitionend",
|
||||
OTransition: "oTransitionEnd",
|
||||
MSTransition: "msTransitionEnd",
|
||||
MozTransition: "transitionend",
|
||||
webkitTransition: "webkitTransitionEnd"
|
||||
}
|
||||
@@ -102,8 +120,6 @@ var eventNameMappings = {
|
||||
correspondingCssProperty: "animation",
|
||||
mappings: {
|
||||
animation: "animationend",
|
||||
OAnimation: "oAnimationEnd",
|
||||
MSAnimation: "msAnimationEnd",
|
||||
MozAnimation: "animationend",
|
||||
webkitAnimation: "webkitAnimationEnd"
|
||||
}
|
||||
@@ -136,19 +152,15 @@ exports.getFullScreenApis = function() {
|
||||
result = {
|
||||
"_requestFullscreen": db.webkitRequestFullscreen !== undefined ? "webkitRequestFullscreen" :
|
||||
db.mozRequestFullScreen !== undefined ? "mozRequestFullScreen" :
|
||||
db.msRequestFullscreen !== undefined ? "msRequestFullscreen" :
|
||||
db.requestFullscreen !== undefined ? "requestFullscreen" : "",
|
||||
"_exitFullscreen": d.webkitExitFullscreen !== undefined ? "webkitExitFullscreen" :
|
||||
d.mozCancelFullScreen !== undefined ? "mozCancelFullScreen" :
|
||||
d.msExitFullscreen !== undefined ? "msExitFullscreen" :
|
||||
d.exitFullscreen !== undefined ? "exitFullscreen" : "",
|
||||
"_fullscreenElement": d.webkitFullscreenElement !== undefined ? "webkitFullscreenElement" :
|
||||
d.mozFullScreenElement !== undefined ? "mozFullScreenElement" :
|
||||
d.msFullscreenElement !== undefined ? "msFullscreenElement" :
|
||||
d.fullscreenElement !== undefined ? "fullscreenElement" : "",
|
||||
"_fullscreenChange": d.webkitFullscreenElement !== undefined ? "webkitfullscreenchange" :
|
||||
d.mozFullScreenElement !== undefined ? "mozfullscreenchange" :
|
||||
d.msFullscreenElement !== undefined ? "MSFullscreenChange" :
|
||||
d.fullscreenElement !== undefined ? "fullscreenchange" : ""
|
||||
};
|
||||
if(!result._requestFullscreen || !result._exitFullscreen || !result._fullscreenElement || !result._fullscreenChange) {
|
||||
|
||||
@@ -11,19 +11,6 @@ Various static DOM-related utility functions.
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
/*
|
||||
Determines whether element 'a' contains element 'b'
|
||||
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
|
||||
*/
|
||||
exports.domContains = function(a,b) {
|
||||
return a.contains ?
|
||||
a !== b && a.contains(b) :
|
||||
!!(a.compareDocumentPosition(b) & 16);
|
||||
};
|
||||
|
||||
exports.domMatchesSelector = function(node,selector) {
|
||||
return node.matches ? node.matches(selector) : node.msMatchesSelector(selector);
|
||||
};
|
||||
|
||||
/*
|
||||
Select text in a an input or textarea (setSelectionRange crashes on certain input types)
|
||||
@@ -49,38 +36,6 @@ exports.removeChildren = function(node) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.hasClass = function(el,className) {
|
||||
return el && el.hasAttribute && el.hasAttribute("class") && el.getAttribute("class").split(" ").indexOf(className) !== -1;
|
||||
};
|
||||
|
||||
exports.addClass = function(el,className) {
|
||||
var c = (el.getAttribute("class") || "").split(" ");
|
||||
if(c.indexOf(className) === -1) {
|
||||
c.push(className);
|
||||
el.setAttribute("class",c.join(" "));
|
||||
}
|
||||
};
|
||||
|
||||
exports.removeClass = function(el,className) {
|
||||
var c = (el.getAttribute("class") || "").split(" "),
|
||||
p = c.indexOf(className);
|
||||
if(p !== -1) {
|
||||
c.splice(p,1);
|
||||
el.setAttribute("class",c.join(" "));
|
||||
}
|
||||
};
|
||||
|
||||
exports.toggleClass = function(el,className,status) {
|
||||
if(status === undefined) {
|
||||
status = !exports.hasClass(el,className);
|
||||
}
|
||||
if(status) {
|
||||
exports.addClass(el,className);
|
||||
} else {
|
||||
exports.removeClass(el,className);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get the first parent element that has scrollbars or use the body as fallback.
|
||||
*/
|
||||
@@ -297,10 +252,6 @@ exports.copyToClipboard = function(text,options) {
|
||||
document.body.removeChild(textArea);
|
||||
};
|
||||
|
||||
exports.getLocationPath = function() {
|
||||
return window.location.toString().split("#")[0];
|
||||
};
|
||||
|
||||
/*
|
||||
Collect DOM variables
|
||||
*/
|
||||
|
||||
@@ -210,7 +210,7 @@ Modal.prototype.display = function(title,options) {
|
||||
bodyWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
||||
footerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
||||
// Whether to close the modal dialog when the mask (area outside the modal) is clicked
|
||||
if(tiddler.fields && (tiddler.fields["mask-closable"] === "yes" || tiddler.fields["mask-closable"] === "true")) {
|
||||
if(tiddler.fields && (tiddler.fields["mask-closable"] === "yes" || tiddler.fields["mask-closable"] === "true" || tiddler.fields["mask-closable"] === "" || "mask-closable" in tiddler.fields === false)) {
|
||||
modalBackdrop.addEventListener("click",closeHandler,false);
|
||||
}
|
||||
// Set the initial styles for the message
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/escapecss.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
module-type: utils-browser
|
||||
|
||||
Provides CSS.escape() functionality.
|
||||
|
||||
@@ -9,92 +9,6 @@ Provides CSS.escape() functionality.
|
||||
|
||||
"use strict";
|
||||
|
||||
// TODO -- resolve this construction
|
||||
exports.escapeCSS = (function() {
|
||||
// use browser's native CSS.escape() function if available
|
||||
if ($tw.browser && window.CSS && window.CSS.escape) {
|
||||
return window.CSS.escape;
|
||||
}
|
||||
|
||||
// otherwise, a utility method is provided
|
||||
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
|
||||
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
|
||||
return function(value) {
|
||||
if (arguments.length == 0) {
|
||||
throw new TypeError('`CSS.escape` requires an argument.');
|
||||
}
|
||||
var string = String(value);
|
||||
var length = string.length;
|
||||
var index = -1;
|
||||
var codeUnit;
|
||||
var result = '';
|
||||
var firstCodeUnit = string.charCodeAt(0);
|
||||
while (++index < length) {
|
||||
codeUnit = string.charCodeAt(index);
|
||||
// Note: there’s no need to special-case astral symbols, surrogate
|
||||
// pairs, or lone surrogates.
|
||||
|
||||
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
|
||||
// (U+FFFD).
|
||||
if (codeUnit == 0x0000) {
|
||||
result += '\uFFFD';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
|
||||
// U+007F, […]
|
||||
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
|
||||
// If the character is the first character and is in the range [0-9]
|
||||
// (U+0030 to U+0039), […]
|
||||
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
|
||||
// If the character is the second character and is in the range [0-9]
|
||||
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
|
||||
(
|
||||
index == 1 &&
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
|
||||
firstCodeUnit == 0x002D
|
||||
)
|
||||
) {
|
||||
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
|
||||
result += '\\' + codeUnit.toString(16) + ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
// If the character is the first character and is a `-` (U+002D), and
|
||||
// there is no second character, […]
|
||||
index == 0 &&
|
||||
length == 1 &&
|
||||
codeUnit == 0x002D
|
||||
) {
|
||||
result += '\\' + string.charAt(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the character is not handled by one of the above rules and is
|
||||
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
|
||||
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
|
||||
// U+005A), or [a-z] (U+0061 to U+007A), […]
|
||||
if (
|
||||
codeUnit >= 0x0080 ||
|
||||
codeUnit == 0x002D ||
|
||||
codeUnit == 0x005F ||
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
|
||||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
|
||||
codeUnit >= 0x0061 && codeUnit <= 0x007A
|
||||
) {
|
||||
// the character itself
|
||||
result += string.charAt(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, the escaped character.
|
||||
// https://drafts.csswg.org/cssom/#escape-a-character
|
||||
result += '\\' + string.charAt(index);
|
||||
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return window.CSS.escape;
|
||||
})();
|
||||
|
||||
126
core/modules/utils/messaging.js
Normal file
126
core/modules/utils/messaging.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/messaging.js
|
||||
type: application/javascript
|
||||
module-type: utils-browser
|
||||
|
||||
Messaging utilities for use with window.postMessage() etc.
|
||||
|
||||
This module intentionally has no dependencies so that it can be included in non-TiddlyWiki projects
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var RESPONSE_TIMEOUT = 2 * 1000;
|
||||
|
||||
/*
|
||||
Class to handle subscribing to publishers
|
||||
|
||||
target: Target window (eg iframe.contentWindow)
|
||||
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
|
||||
onsubscribe: Function to be invoked with err parameter when the subscription is established, or there is a timeout
|
||||
onmessage: Function to be invoked when a new message arrives, invoked with (data,callback). The callback is invoked with the argument (response)
|
||||
*/
|
||||
function BrowserMessagingSubscriber(options) {
|
||||
var self = this;
|
||||
this.target = options.target;
|
||||
this.type = options.type;
|
||||
this.onsubscribe = options.onsubscribe || function() {};
|
||||
this.onmessage = options.onmessage;
|
||||
this.hasConfirmed = false;
|
||||
this.channel = new MessageChannel();
|
||||
this.channel.port1.addEventListener("message",function(event) {
|
||||
if(this.timerID) {
|
||||
clearTimeout(this.timerID);
|
||||
this.timerID = null;
|
||||
}
|
||||
if(event.data) {
|
||||
if(event.data.verb === "SUBSCRIBED") {
|
||||
self.hasConfirmed = true;
|
||||
self.onsubscribe(null);
|
||||
} else if(event.data.verb === self.type) {
|
||||
self.onmessage(event.data,function(response) {
|
||||
// Send the response back on the supplied port, and then close it
|
||||
event.ports[0].postMessage(response);
|
||||
event.ports[0].close();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// Set a timer so that if we don't hear from the iframe before a timeout we alert the user
|
||||
this.timerID = setTimeout(function() {
|
||||
if(!self.hasConfirmed) {
|
||||
self.onsubscribe("NO_RESPONSE");
|
||||
}
|
||||
},RESPONSE_TIMEOUT);
|
||||
this.channel.port1.start();
|
||||
this.target.postMessage({verb: "SUBSCRIBE",to: self.type},"*",[this.channel.port2]);
|
||||
}
|
||||
|
||||
exports.BrowserMessagingSubscriber = BrowserMessagingSubscriber;
|
||||
|
||||
/*
|
||||
Class to handle publishing subscriptions
|
||||
|
||||
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
|
||||
onsubscribe: Function to be invoked when a subscription occurs
|
||||
*/
|
||||
function BrowserMessagingPublisher(options) {
|
||||
var self = this;
|
||||
this.type = options.type;
|
||||
this.hostIsListening = false;
|
||||
this.port = null;
|
||||
// Listen to connection requests from the host
|
||||
window.addEventListener("message",function(event) {
|
||||
if(event.data && event.data.verb === "SUBSCRIBE" && event.data.to === self.type) {
|
||||
self.hostIsListening = true;
|
||||
// Acknowledge
|
||||
self.port = event.ports[0];
|
||||
self.port.postMessage({verb: "SUBSCRIBED", to: self.type});
|
||||
if(options.onsubscribe) {
|
||||
options.onsubscribe(event.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BrowserMessagingPublisher.prototype.canSend = function() {
|
||||
return !!this.hostIsListening && !!this.port;
|
||||
};
|
||||
|
||||
BrowserMessagingPublisher.prototype.send = function(data,callback) {
|
||||
var self = this;
|
||||
callback = callback || function() {};
|
||||
// Check that we've been initialised by the host
|
||||
if(!this.hostIsListening || !this.port) {
|
||||
return false;
|
||||
}
|
||||
// Create a channel for the confirmation
|
||||
var channel = new MessageChannel();
|
||||
channel.port1.addEventListener("message",function(event) {
|
||||
if(event.data && event.data.verb === "OK") {
|
||||
callback(null);
|
||||
} else {
|
||||
callback("BrowserMessagingPublisher for " + self.type + " error: " + (event.data || {}).verb);
|
||||
}
|
||||
channel.port1.close();
|
||||
});
|
||||
channel.port1.start();
|
||||
// Send the save request with the port for the response
|
||||
this.port.postMessage(data,[channel.port2]);
|
||||
};
|
||||
|
||||
BrowserMessagingPublisher.prototype.close = function() {
|
||||
if(this.port) {
|
||||
this.port.close();
|
||||
this.hostIsListening = false;
|
||||
this.port = null;
|
||||
}
|
||||
};
|
||||
|
||||
exports.BrowserMessagingPublisher = BrowserMessagingPublisher;
|
||||
|
||||
})();
|
||||
@@ -119,3 +119,19 @@ exports.getParseTreeText = function getParseTreeText(tree) {
|
||||
}
|
||||
return output.join("");
|
||||
};
|
||||
|
||||
exports.getParser = function(type,options) {
|
||||
options = options || {};
|
||||
// Select a parser
|
||||
var Parser = $tw.Wiki.parsers[type];
|
||||
if(!Parser && $tw.utils.getFileExtensionInfo(type)) {
|
||||
Parser = $tw.Wiki.parsers[$tw.utils.getFileExtensionInfo(type).type];
|
||||
}
|
||||
if(!Parser) {
|
||||
Parser = $tw.Wiki.parsers[options.defaultType || "text/vnd.tiddlywiki"];
|
||||
}
|
||||
if(!Parser) {
|
||||
return null;
|
||||
}
|
||||
return Parser;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user