mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-23 19:34:39 +00:00
Compare commits
214 Commits
v5.0.9-bet
...
v5.0.12-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaec1b2854 | ||
|
|
9edebc78e7 | ||
|
|
e228ac42db | ||
|
|
be0bc04929 | ||
|
|
b415b0e90e | ||
|
|
974aecba1f | ||
|
|
47fa18c2e0 | ||
|
|
d5094463f4 | ||
|
|
5951dc5901 | ||
|
|
1d15e4b7d3 | ||
|
|
67e66b4698 | ||
|
|
c00940f2d1 | ||
|
|
81ea326e16 | ||
|
|
c2f52f8a4b | ||
|
|
ca03984663 | ||
|
|
b2ff248d07 | ||
|
|
658bd2b8a3 | ||
|
|
a8b16c9c07 | ||
|
|
157ca0c8aa | ||
|
|
01b3962b2c | ||
|
|
19646c7f95 | ||
|
|
dfb8a07204 | ||
|
|
93566cdc33 | ||
|
|
ad8a20bfed | ||
|
|
00cddd3713 | ||
|
|
23c8902e70 | ||
|
|
818fd598dc | ||
|
|
31e1088aa7 | ||
|
|
f9f8ad725b | ||
|
|
81e1af1d35 | ||
|
|
c9c1b0fbb4 | ||
|
|
f7ce0c0b8d | ||
|
|
92aa1f24be | ||
|
|
e0a6c4e879 | ||
|
|
f72c177ba5 | ||
|
|
9a26c4259a | ||
|
|
6de6a43623 | ||
|
|
2fa3c8a4ed | ||
|
|
295094c9a3 | ||
|
|
da41879fc1 | ||
|
|
f8708874bc | ||
|
|
ad43958571 | ||
|
|
4e07b3335b | ||
|
|
b77d5f9725 | ||
|
|
75fee26b58 | ||
|
|
e8c9d78079 | ||
|
|
d972988a53 | ||
|
|
e83759e86d | ||
|
|
2633247492 | ||
|
|
b3032d452f | ||
|
|
e60fc9f81f | ||
|
|
6ab68e0fca | ||
|
|
e548dd35af | ||
|
|
8b20143b1b | ||
|
|
711b76307c | ||
|
|
b858e9dc69 | ||
|
|
a9411262f7 | ||
|
|
e676156b24 | ||
|
|
c592e658e7 | ||
|
|
6b03789e06 | ||
|
|
51f54b06f9 | ||
|
|
285ab41ccf | ||
|
|
854b739a35 | ||
|
|
abe0ce28b9 | ||
|
|
76e8640c31 | ||
|
|
cc3d44aec1 | ||
|
|
7b6cff0cca | ||
|
|
d1c85f53c0 | ||
|
|
e2b0da0b58 | ||
|
|
7c8c5cf745 | ||
|
|
38c60bd7d4 | ||
|
|
afb92c40fe | ||
|
|
6b45296ca9 | ||
|
|
b84c663215 | ||
|
|
4e101e240c | ||
|
|
56251dc1f8 | ||
|
|
f368175cb0 | ||
|
|
3e49dd65a0 | ||
|
|
f5ada72dac | ||
|
|
986a20b22b | ||
|
|
ffb6c8ab81 | ||
|
|
e58e68fa7c | ||
|
|
95def8b857 | ||
|
|
b24ec8009d | ||
|
|
9965c64b6f | ||
|
|
2a50277219 | ||
|
|
3cce12e13f | ||
|
|
887e9d978b | ||
|
|
a0022a1cd6 | ||
|
|
519e1b4a44 | ||
|
|
b9dec37fb7 | ||
|
|
09156af475 | ||
|
|
5b5621a600 | ||
|
|
78ba57d55d | ||
|
|
749582ede0 | ||
|
|
1c82348edb | ||
|
|
b96aade28a | ||
|
|
2f32621024 | ||
|
|
6ccf02ed96 | ||
|
|
b2c1331c11 | ||
|
|
f81df69395 | ||
|
|
a5e9ef9b5f | ||
|
|
7783378603 | ||
|
|
327b53a641 | ||
|
|
cb914ae853 | ||
|
|
a4294b55f0 | ||
|
|
bced7124e4 | ||
|
|
f5848c395a | ||
|
|
f6bd3b8c37 | ||
|
|
21b2d6fdc7 | ||
|
|
bd3e955821 | ||
|
|
fcb26419a6 | ||
|
|
a6f7da6c1c | ||
|
|
f82f8ae7c6 | ||
|
|
edb8c65d54 | ||
|
|
031c0f4146 | ||
|
|
e8316cf0ac | ||
|
|
0ac4c2b554 | ||
|
|
84cd296c58 | ||
|
|
a90339d1e5 | ||
|
|
d49495ab73 | ||
|
|
5fa6d001f8 | ||
|
|
f17dafefcb | ||
|
|
88314d968a | ||
|
|
28bab707b9 | ||
|
|
075cf544e4 | ||
|
|
a505b6ffc0 | ||
|
|
23a71b433e | ||
|
|
3bbe53a58e | ||
|
|
8556e0ea49 | ||
|
|
3be21853e1 | ||
|
|
d0636f2124 | ||
|
|
5226c7a2fa | ||
|
|
3a78465d2d | ||
|
|
23640d7af4 | ||
|
|
540681b2bc | ||
|
|
8611867930 | ||
|
|
77152ac577 | ||
|
|
f5bd99fa73 | ||
|
|
385c7e207c | ||
|
|
61c204366f | ||
|
|
570cad1c7f | ||
|
|
1d0dc60a2d | ||
|
|
fac0affa7b | ||
|
|
f57241abbd | ||
|
|
e9557b578e | ||
|
|
649f68288a | ||
|
|
37c50bae61 | ||
|
|
9ab31e37ea | ||
|
|
30707e2f19 | ||
|
|
1fb76ae56d | ||
|
|
4008191459 | ||
|
|
463bd0517c | ||
|
|
fb3a31ae43 | ||
|
|
f7e50e0950 | ||
|
|
552657fc58 | ||
|
|
d727046948 | ||
|
|
f6ca7d92e5 | ||
|
|
148e77b0e4 | ||
|
|
cc60ad1428 | ||
|
|
fecf3a556f | ||
|
|
39ef0ad88f | ||
|
|
f75826224b | ||
|
|
52f28a1cc3 | ||
|
|
61c3f8a5ba | ||
|
|
13da54b9bd | ||
|
|
680414d1c5 | ||
|
|
2c7d5c5964 | ||
|
|
3d69c929d9 | ||
|
|
8cbcfee346 | ||
|
|
43173c801f | ||
|
|
523faf71fb | ||
|
|
ad2bbb2c93 | ||
|
|
8756d25d78 | ||
|
|
8a27c2759b | ||
|
|
beddcd7138 | ||
|
|
2db90378a2 | ||
|
|
7684891285 | ||
|
|
821f1f1428 | ||
|
|
4538f081ee | ||
|
|
b4122be50c | ||
|
|
cd76514105 | ||
|
|
ba576d9f1b | ||
|
|
15d0c27e2a | ||
|
|
869cec1ccc | ||
|
|
d6054f1039 | ||
|
|
9fbe72a877 | ||
|
|
89165fc51d | ||
|
|
4758874d13 | ||
|
|
0153fd2a30 | ||
|
|
bb42c0ab36 | ||
|
|
95d291daac | ||
|
|
45b0966013 | ||
|
|
de07da3797 | ||
|
|
aebc1ea943 | ||
|
|
73cfd10218 | ||
|
|
d336ffea02 | ||
|
|
6da28e7365 | ||
|
|
d08a2d109f | ||
|
|
f57e047877 | ||
|
|
df5fe10a40 | ||
|
|
433ac8e96e | ||
|
|
ad4b03506a | ||
|
|
ace57dd205 | ||
|
|
bd4a031df8 | ||
|
|
6db94052c7 | ||
|
|
dd8797223a | ||
|
|
e54b0d7129 | ||
|
|
07ab8c75b8 | ||
|
|
5bcc666f34 | ||
|
|
6bef1d6b30 | ||
|
|
09cf788063 | ||
|
|
4d0b08d464 | ||
|
|
5447420832 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.DS_Store
|
||||
tmp/
|
||||
output/
|
||||
|
||||
16
2bld.cmd
16
2bld.cmd
@@ -2,22 +2,13 @@
|
||||
|
||||
rem build TiddlyWiki 2.x
|
||||
|
||||
rem create a temporary directory if it doesn't already exist
|
||||
setlocal enableextensions
|
||||
mkdir tmp\tw2
|
||||
setlocal disableextensions
|
||||
|
||||
rem Delete any existing content
|
||||
|
||||
del /q /s tmp\tw2
|
||||
echo.
|
||||
|
||||
rem Prepare the readme file from the revelant content in the tw5.com wiki
|
||||
|
||||
node .\tiddlywiki.js ^
|
||||
editions\tw5.com ^
|
||||
--verbose ^
|
||||
--rendertiddler TiddlyWiki2ReadMe editions\tw2\readme.md text/html ^
|
||||
--output editions\tw2 ^
|
||||
--rendertiddler TiddlyWiki2ReadMe readme.md text/html ^
|
||||
|| exit 1
|
||||
|
||||
rem cook the TiddlyWiki 2.x.x index file
|
||||
@@ -25,8 +16,9 @@ rem cook the TiddlyWiki 2.x.x index file
|
||||
node .\tiddlywiki.js ^
|
||||
editions\tw2 ^
|
||||
--verbose ^
|
||||
--output tmp\tw2 ^
|
||||
--load editions\tw2\source\tiddlywiki.com\index.html.recipe ^
|
||||
--rendertiddler $:/core/templates/tiddlywiki2.template.html .\tmp\tw2\index.html text/plain ^
|
||||
--rendertiddler $:/core/templates/tiddlywiki2.template.html index.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
fc tmp\tw2\index.html editions\tw2\target\prebuilt.html
|
||||
|
||||
10
2bld.sh
10
2bld.sh
@@ -2,16 +2,13 @@
|
||||
|
||||
# build TiddlyWiki 2.x
|
||||
|
||||
# create a temporary directory if it doesn't already exist
|
||||
mkdir -p tmp
|
||||
mkdir -p tmp/tw2
|
||||
|
||||
# Prepare the readme file from the revelant content in the tw5.com wiki
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
editions/tw5.com \
|
||||
--verbose \
|
||||
--rendertiddler TiddlyWiki2ReadMe editions/tw2/readme.md text/html \
|
||||
--output editions/tw2 \
|
||||
--rendertiddler TiddlyWiki2ReadMe readme.md text/html \
|
||||
|| exit 1
|
||||
|
||||
# cook the TiddlyWiki 2.x.x index file
|
||||
@@ -19,8 +16,9 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
editions/tw2 \
|
||||
--verbose \
|
||||
--output tmp/tw2 \
|
||||
--load editions/tw2/source/tiddlywiki.com/index.html.recipe \
|
||||
--rendertiddler $:/core/templates/tiddlywiki2.template.html ./tmp/tw2/index.html text/plain \
|
||||
--rendertiddler $:/core/templates/tiddlywiki2.template.html index.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
diff -q tmp/tw2/index.html editions/tw2/target/prebuilt.html
|
||||
|
||||
@@ -25,28 +25,32 @@ echo "tiddlywiki.com" > $TW5_BUILD_OUTPUT/CNAME
|
||||
node ./tiddlywiki.js \
|
||||
./editions/de-AT-DE \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/de-AT-DE.html text/plain \
|
||||
--savetiddler $:/favicon.ico $TW5_BUILD_OUTPUT/favicon.ico \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all de-AT-DE.html text/plain \
|
||||
--savetiddler $:/favicon.ico favicon.ico \
|
||||
|| exit 1
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/zh-Hant \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/zh-Hant.html text/plain \
|
||||
--savetiddler $:/favicon.ico $TW5_BUILD_OUTPUT/favicon.ico \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all zh-Hant.html text/plain \
|
||||
--savetiddler $:/favicon.ico favicon.ico \
|
||||
|| exit 1
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/zh-Hans \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/zh-Hans.html text/plain \
|
||||
--savetiddler $:/favicon.ico $TW5_BUILD_OUTPUT/favicon.ico \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all zh-Hans.html text/plain \
|
||||
--savetiddler $:/favicon.ico favicon.ico \
|
||||
|| exit 1
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/fr-FR \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/fr-FR.html text/plain \
|
||||
--savetiddler $:/favicon.ico $TW5_BUILD_OUTPUT/favicon.ico \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all fr-FR.html text/plain \
|
||||
--savetiddler $:/favicon.ico favicon.ico \
|
||||
|| exit 1
|
||||
|
||||
|
||||
36
bld.cmd
36
bld.cmd
@@ -34,18 +34,20 @@ rem static.html: the static version of the default tiddlers
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\tw5.com ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\index.html text/plain ^
|
||||
--savetiddler $:/favicon.ico %TW5_BUILD_OUTPUT%\favicon.ico ^
|
||||
--output . ^
|
||||
--rendertiddler ReadMe .\readme.md text/html ^
|
||||
--rendertiddler ContributingTemplate .\contributing.md text/html ^
|
||||
--rendertiddler $:/core/copyright.txt .\licenses\copyright.md text/plain ^
|
||||
--rendertiddler $:/editions/tw5.com/download-empty %TW5_BUILD_OUTPUT%\empty.html text/plain ^
|
||||
--rendertiddler $:/editions/tw5.com/download-empty %TW5_BUILD_OUTPUT%\empty.hta text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all index.html text/plain ^
|
||||
--savetiddler $:/favicon.ico favicon.ico ^
|
||||
--rendertiddler $:/editions/tw5.com/download-empty empty.html text/plain ^
|
||||
--rendertiddler $:/editions/tw5.com/download-empty empty.hta text/plain ^
|
||||
--savetiddler $:/green_favicon.ico %TW5_BUILD_OUTPUT%/static/favicon.ico ^
|
||||
--rendertiddler $:/core/templates/static.template.html %TW5_BUILD_OUTPUT%\static.html text/plain ^
|
||||
--rendertiddler $:/core/templates/alltiddlers.template.html %TW5_BUILD_OUTPUT%\alltiddlers.html text/plain ^
|
||||
--rendertiddler $:/core/templates/static.template.css %TW5_BUILD_OUTPUT%\static\static.css text/plain ^
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html %TW5_BUILD_OUTPUT%\static text/plain ^
|
||||
--rendertiddler $:/core/templates/static.template.html static.html text/plain ^
|
||||
--rendertiddler $:/core/templates/alltiddlers.template.html alltiddlers.html text/plain ^
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html static text/plain ^
|
||||
--rendertiddler $:/core/templates/static.template.css static\static.css text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem encrypted.html: a version of the main file encrypted with the password "password"
|
||||
@@ -53,8 +55,9 @@ rem encrypted.html: a version of the main file encrypted with the password "pass
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\tw5.com ^
|
||||
--verbose ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--password password ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\encrypted.html text/plain ^
|
||||
--rendertiddler $:/core/save/all encrypted.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem tahoelafs.html: empty wiki with plugin for Tahoe-LAFS
|
||||
@@ -62,7 +65,8 @@ rem tahoelafs.html: empty wiki with plugin for Tahoe-LAFS
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\tahoelafs ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\tahoelafs.html text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all tahoelafs.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem d3demo.html: wiki to demo d3 plugin
|
||||
@@ -70,7 +74,8 @@ rem d3demo.html: wiki to demo d3 plugin
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\d3demo ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\d3demo.html text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all d3demo.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem codemirrordemo.html: wiki to demo codemirror plugin
|
||||
@@ -78,7 +83,8 @@ rem codemirrordemo.html: wiki to demo codemirror plugin
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\codemirrordemo ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\codemirrordemo.html text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all codemirrordemo.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem markdowndemo.html: wiki to demo markdown plugin
|
||||
@@ -86,7 +92,8 @@ rem markdowndemo.html: wiki to demo markdown plugin
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\markdowndemo ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\markdowndemo.html text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all markdowndemo.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
|
||||
@@ -95,7 +102,8 @@ rem highlightdemo.html: wiki to demo highlight plugin
|
||||
node .\tiddlywiki.js ^
|
||||
.\editions\highlightdemo ^
|
||||
--verbose ^
|
||||
--rendertiddler $:/core/save/all %TW5_BUILD_OUTPUT%\highlightdemo.html text/plain ^
|
||||
--output %TW5_BUILD_OUTPUT% ^
|
||||
--rendertiddler $:/core/save/all highlightdemo.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
rem Make the CNAME file that GitHub Pages requires
|
||||
|
||||
38
bld.sh
38
bld.sh
@@ -35,18 +35,20 @@ rm $TW5_BUILD_OUTPUT/static/*
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tw5.com \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/index.html text/plain \
|
||||
--savetiddler $:/favicon.ico $TW5_BUILD_OUTPUT/favicon.ico \
|
||||
--output . \
|
||||
--rendertiddler ReadMe ./readme.md text/html \
|
||||
--rendertiddler ContributingTemplate ./contributing.md text/html \
|
||||
--rendertiddler $:/core/copyright.txt ./licenses/copyright.md text/plain \
|
||||
--rendertiddler $:/editions/tw5.com/download-empty $TW5_BUILD_OUTPUT/empty.html text/plain \
|
||||
--rendertiddler $:/editions/tw5.com/download-empty $TW5_BUILD_OUTPUT/empty.hta text/plain \
|
||||
--savetiddler $:/green_favicon.ico $TW5_BUILD_OUTPUT/static/favicon.ico \
|
||||
--rendertiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
|
||||
--rendertiddler $:/core/templates/alltiddlers.template.html $TW5_BUILD_OUTPUT/alltiddlers.html text/plain \
|
||||
--rendertiddler $:/core/templates/static.template.css $TW5_BUILD_OUTPUT/static/static.css text/plain \
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html $TW5_BUILD_OUTPUT/static text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all index.html text/plain \
|
||||
--savetiddler $:/favicon.ico favicon.ico \
|
||||
--rendertiddler $:/editions/tw5.com/download-empty empty.html text/plain \
|
||||
--rendertiddler $:/editions/tw5.com/download-empty empty.hta text/plain \
|
||||
--savetiddler $:/green_favicon.ico static/favicon.ico \
|
||||
--rendertiddler $:/core/templates/static.template.html static.html text/plain \
|
||||
--rendertiddler $:/core/templates/alltiddlers.template.html alltiddlers.html text/plain \
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html static text/plain \
|
||||
--rendertiddler $:/core/templates/static.template.css static/static.css text/plain \
|
||||
|| exit 1
|
||||
|
||||
# encrypted.html: a version of the main file encrypted with the password "password"
|
||||
@@ -54,8 +56,9 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tw5.com \
|
||||
--verbose \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--password password \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/encrypted.html text/plain \
|
||||
--rendertiddler $:/core/save/all encrypted.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# tahoelafs.html: empty wiki with plugin for Tahoe-LAFS
|
||||
@@ -63,7 +66,8 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tahoelafs \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/tahoelafs.html text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all tahoelafs.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# d3demo.html: wiki to demo d3 plugin
|
||||
@@ -71,7 +75,8 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/d3demo \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/d3demo.html text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all d3demo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# codemirrordemo.html: wiki to demo codemirror plugin
|
||||
@@ -79,7 +84,8 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/codemirrordemo \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/codemirrordemo.html text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all codemirrordemo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# markdowndemo.html: wiki to demo markdown plugin
|
||||
@@ -87,7 +93,8 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/markdowndemo \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/markdowndemo.html text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all markdowndemo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# highlightdemo.html: wiki to demo highlight plugin
|
||||
@@ -95,7 +102,8 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/highlightdemo \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/save/all $TW5_BUILD_OUTPUT/highlightdemo.html text/plain \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all highlightdemo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Run the test edition to run the Node.js tests and to generate test.html for tests in the browser
|
||||
|
||||
268
boot/boot.js
268
boot/boot.js
@@ -33,7 +33,6 @@ if(!$tw) {
|
||||
}
|
||||
|
||||
$tw.utils = $tw.utils || Object.create(null);
|
||||
$tw.boot = $tw.boot || Object.create(null);
|
||||
|
||||
/////////////////////////// Standard node.js libraries
|
||||
|
||||
@@ -46,6 +45,11 @@ if($tw.node) {
|
||||
|
||||
/////////////////////////// Utility functions
|
||||
|
||||
$tw.boot.log = function(str) {
|
||||
$tw.boot.logMessages = $tw.boot.logMessages || [];
|
||||
$tw.boot.logMessages.push(str);
|
||||
}
|
||||
|
||||
/*
|
||||
Check if an object has a property
|
||||
*/
|
||||
@@ -131,7 +135,7 @@ $tw.utils.error = function(err) {
|
||||
promptMsg = "Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser";
|
||||
// Log the error to the console
|
||||
console.error(err);
|
||||
if($tw.browser) {
|
||||
if($tw.browser && !$tw.node) {
|
||||
// Display an error message to the user
|
||||
var dm = $tw.utils.domMaker,
|
||||
heading = dm("h1",{text: errHeading}),
|
||||
@@ -146,7 +150,7 @@ $tw.utils.error = function(err) {
|
||||
return false;
|
||||
},true);
|
||||
return null;
|
||||
} else {
|
||||
} else if(!$tw.browser) {
|
||||
// Exit if we're under node.js
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -155,7 +159,7 @@ $tw.utils.error = function(err) {
|
||||
/*
|
||||
Use our custom error handler if we're in the browser
|
||||
*/
|
||||
if($tw.browser) {
|
||||
if($tw.boot.tasks.trapErrors) {
|
||||
window.onerror = function(errorMsg,url,lineNumber) {
|
||||
$tw.utils.error(errorMsg);
|
||||
return false;
|
||||
@@ -187,7 +191,7 @@ $tw.utils.deepDefaults = function(object /*, sourceObjectList */) {
|
||||
object[p] = source[p];
|
||||
}
|
||||
if(typeof object[p] === "object" && typeof source[p] === "object") {
|
||||
$tw.utils.deepDefaults(object[p],source[p]);
|
||||
$tw.utils.deepDefaults(object[p],source[p]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +206,14 @@ $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 parts = window.location.href.split('#');
|
||||
return "#" + (parts.length > 1 ? parts[1] : "");
|
||||
};
|
||||
|
||||
/*
|
||||
Pad a string to a given length with "0"s. Length defaults to 2
|
||||
*/
|
||||
@@ -218,8 +230,8 @@ $tw.utils.pad = function(value,length) {
|
||||
$tw.utils.stringifyDate = function(value) {
|
||||
return value.getUTCFullYear() +
|
||||
$tw.utils.pad(value.getUTCMonth() + 1) +
|
||||
$tw.utils.pad(value.getUTCDate()) +
|
||||
$tw.utils.pad(value.getUTCHours()) +
|
||||
$tw.utils.pad(value.getUTCDate()) +
|
||||
$tw.utils.pad(value.getUTCHours()) +
|
||||
$tw.utils.pad(value.getUTCMinutes()) +
|
||||
$tw.utils.pad(value.getUTCSeconds()) +
|
||||
$tw.utils.pad(value.getUTCMilliseconds(),3);
|
||||
@@ -258,14 +270,14 @@ $tw.utils.stringifyList = function(value) {
|
||||
// Parse a string array from a bracketted list. For example "OneTiddler [[Another Tiddler]] LastOne"
|
||||
$tw.utils.parseStringArray = function(value) {
|
||||
if(typeof value === "string") {
|
||||
var memberRegExp = /(?:^|\s)(?:\[\[(.*?)\]\])(?=\s|$)|(\S+)/mg,
|
||||
var memberRegExp = /(?:^|[^\S\xA0])(?:\[\[(.*?)\]\])(?=[^\S\xA0]|$)|([\S\xA0]+)/mg,
|
||||
results = [],
|
||||
match;
|
||||
do {
|
||||
match = memberRegExp.exec(value);
|
||||
if(match) {
|
||||
var item = match[1] || match[2];
|
||||
if(results.indexOf(item) === -1) {
|
||||
if(item !== undefined && results.indexOf(item) === -1) {
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
@@ -301,7 +313,6 @@ name `.` refers to the current directory
|
||||
*/
|
||||
$tw.utils.resolvePath = function(sourcepath,rootpath) {
|
||||
// If the source path starts with ./ or ../ then it is relative to the root
|
||||
|
||||
if(sourcepath.substr(0,2) === "./" || sourcepath.substr(0,3) === "../" ) {
|
||||
var src = sourcepath.split("/"),
|
||||
root = rootpath.split("/");
|
||||
@@ -352,7 +363,7 @@ $tw.utils.parseVersion = function(version) {
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the version string A is greater than the version string B
|
||||
Returns true if the version string A is greater than the version string B. Returns true if the versions are the same
|
||||
*/
|
||||
$tw.utils.checkVersions = function(versionStringA,versionStringB) {
|
||||
var defaultVersion = {
|
||||
@@ -369,7 +380,8 @@ $tw.utils.checkVersions = function(versionStringA,versionStringB) {
|
||||
];
|
||||
return (diff[0] > 0) ||
|
||||
(diff[0] === 0 && diff[1] > 0) ||
|
||||
(diff[0] === 0 && diff[1] === 0 && diff[2] > 0);
|
||||
(diff[0] === 0 && diff[1] === 0 && diff[2] > 0) ||
|
||||
(diff[0] === 0 && diff[1] === 0 && diff[2] === 0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -380,8 +392,8 @@ options: {flags: flags,deserializerType: deserializerType}
|
||||
*/
|
||||
$tw.utils.registerFileType = function(type,encoding,extension,options) {
|
||||
options = options || {};
|
||||
$tw.config.fileExtensionInfo[extension] = {type: type};
|
||||
$tw.config.contentTypeInfo[type] = {encoding: encoding, extension: extension, flags: options.flags || [], deserializerType: options.deserializerType || type};
|
||||
$tw.config.fileExtensionInfo[extension] = {type: type};
|
||||
$tw.config.contentTypeInfo[type] = {encoding: encoding, extension: extension, flags: options.flags || [], deserializerType: options.deserializerType || type};
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -413,7 +425,7 @@ $tw.utils.evalGlobal = function(code,context,filename) {
|
||||
if($tw.browser) {
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
|
||||
} else {
|
||||
fn = vm.runInThisContext(code,filename);
|
||||
fn = vm.runInThisContext(code,filename);
|
||||
}
|
||||
// Call the function and return the exports
|
||||
return fn.apply(null,contextValues);
|
||||
@@ -564,7 +576,7 @@ $tw.utils.Crypto = function() {
|
||||
}
|
||||
} catch(ex) {
|
||||
console.log("Crypto error:" + ex);
|
||||
outputText = null;
|
||||
outputText = null;
|
||||
}
|
||||
return outputText;
|
||||
};
|
||||
@@ -652,7 +664,7 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
|
||||
return window.require(moduleName);
|
||||
} catch(e) {}
|
||||
}
|
||||
throw "Cannot find module named '" + moduleName + "' required by module '" + moduleRoot + "', resolved to " + name;
|
||||
throw "Cannot find module named '" + moduleName + "' required by module '" + moduleRoot + "', resolved to " + name;
|
||||
} else {
|
||||
// If we don't have a module with that name, let node.js try to find it
|
||||
return require(moduleName);
|
||||
@@ -668,6 +680,9 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
|
||||
} else if(typeof moduleInfo.definition === "string") { // String
|
||||
moduleInfo.exports = _exports;
|
||||
$tw.utils.evalSandboxed(moduleInfo.definition,sandbox,tiddler.fields.title);
|
||||
if(sandbox.module.exports) {
|
||||
moduleInfo.exports = sandbox.module.exports; //more codemirror workaround
|
||||
}
|
||||
} else { // Object
|
||||
moduleInfo.exports = moduleInfo.definition;
|
||||
}
|
||||
@@ -749,7 +764,7 @@ $tw.Tiddler = function(/* [fields,] fields */) {
|
||||
var arg = arguments[c],
|
||||
src = (arg instanceof $tw.Tiddler) ? arg.fields : arg;
|
||||
for(var t in src) {
|
||||
if(src[t] === undefined) {
|
||||
if(src[t] === undefined || src[t] === null) {
|
||||
if(t in this.fields) {
|
||||
delete this.fields[t]; // If we get a field that's undefined, delete any previous field value
|
||||
}
|
||||
@@ -764,7 +779,7 @@ $tw.Tiddler = function(/* [fields,] fields */) {
|
||||
}
|
||||
// Freeze the field to keep it immutable
|
||||
if(typeof value === "object") {
|
||||
Object.freeze(value);
|
||||
Object.freeze(value);
|
||||
}
|
||||
this.fields[t] = value;
|
||||
}
|
||||
@@ -834,7 +849,7 @@ $tw.Wiki = function(options) {
|
||||
tiddlers[title] = tiddler;
|
||||
this.clearCache(title);
|
||||
this.clearGlobalCache();
|
||||
this.enqueueTiddlerEvent(title);
|
||||
this.enqueueTiddlerEvent(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -884,6 +899,37 @@ $tw.Wiki = function(options) {
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate through all tiddlers and then the shadows
|
||||
this.eachTiddlerPlusShadows = function(callback) {
|
||||
for(var title in tiddlers) {
|
||||
callback(tiddlers[title],title);
|
||||
}
|
||||
for(var title in shadowTiddlers) {
|
||||
if(!Object.prototype.hasOwnProperty.call(tiddlers,title)) {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate through all the shadows and then the tiddlers
|
||||
this.eachShadowPlusTiddlers = function(callback) {
|
||||
for(var title in shadowTiddlers) {
|
||||
if(Object.prototype.hasOwnProperty.call(tiddlers,title)) {
|
||||
callback(tiddlers[title],title);
|
||||
} else {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
for(var title in tiddlers) {
|
||||
if(!Object.prototype.hasOwnProperty.call(shadowTiddlers,title)) {
|
||||
callback(tiddlers[title],title);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Test for the existence of a tiddler
|
||||
this.tiddlerExists = function(title) {
|
||||
return !!$tw.utils.hop(tiddlers,title);
|
||||
@@ -994,15 +1040,15 @@ $tw.Wiki = function(options) {
|
||||
};
|
||||
|
||||
// Dummy methods that will be filled in after boot
|
||||
$tw.Wiki.prototype.clearCache =
|
||||
$tw.Wiki.prototype.clearGlobalCache =
|
||||
$tw.Wiki.prototype.clearCache =
|
||||
$tw.Wiki.prototype.clearGlobalCache =
|
||||
$tw.Wiki.prototype.enqueueTiddlerEvent = function() {};
|
||||
|
||||
// Add an array of tiddlers
|
||||
$tw.Wiki.prototype.addTiddlers = function(tiddlers) {
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
this.addTiddler(tiddlers[t]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1043,6 +1089,37 @@ $tw.Wiki.prototype.defineShadowModules = function() {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Enable safe mode by deleting any tiddlers that override a shadow tiddler
|
||||
*/
|
||||
$tw.Wiki.prototype.processSafeMode = function() {
|
||||
var self = this,
|
||||
overrides = [];
|
||||
// Find the overriding tiddlers
|
||||
this.each(function(tiddler,title) {
|
||||
if(self.isShadowTiddler(title)) {
|
||||
console.log(title);
|
||||
overrides.push(title);
|
||||
}
|
||||
});
|
||||
// Assemble a report tiddler
|
||||
var titleReportTiddler = "TiddlyWiki Safe Mode",
|
||||
report = [];
|
||||
report.push("TiddlyWiki has been started in [[safe mode|http://tiddlywiki.com/static/SafeMode.html]]. Most customisations have been disabled by renaming the following tiddlers:")
|
||||
// Delete the overrides
|
||||
overrides.forEach(function(title) {
|
||||
var tiddler = self.getTiddler(title),
|
||||
newTitle = "SAFE: " + title;
|
||||
self.deleteTiddler(title);
|
||||
self.addTiddler(new $tw.Tiddler(tiddler, {title: newTitle}));
|
||||
report.push("* [[" + title + "|" + newTitle + "]]");
|
||||
});
|
||||
report.push()
|
||||
this.addTiddler(new $tw.Tiddler({title: titleReportTiddler, text: report.join("\n\n")}));
|
||||
// Set $:/DefaultTiddlers to point to our report
|
||||
this.addTiddler(new $tw.Tiddler({title: "$:/DefaultTiddlers", text: "[[" + titleReportTiddler + "]]"}));
|
||||
};
|
||||
|
||||
/*
|
||||
Extracts tiddlers from a typed block of text, specifying default field values
|
||||
*/
|
||||
@@ -1098,8 +1175,6 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/tid","tiddlerdeserializer",{
|
||||
}
|
||||
if(split.length >= 2) {
|
||||
fields.text = split.slice(1).join("\n\n");
|
||||
} else {
|
||||
fields.text = "";
|
||||
}
|
||||
return [fields];
|
||||
}
|
||||
@@ -1155,7 +1230,7 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/json","tiddlerdeserializer",{
|
||||
|
||||
/////////////////////////// Browser definitions
|
||||
|
||||
if($tw.browser) {
|
||||
if($tw.browser && !$tw.node) {
|
||||
|
||||
/*
|
||||
Decrypt any tiddlers stored within the element with the ID "encryptedArea". The function is asynchronous to allow the user to be prompted for a password
|
||||
@@ -1274,13 +1349,10 @@ $tw.loadTiddlersBrowser = function() {
|
||||
}
|
||||
};
|
||||
|
||||
// End of if($tw.browser)
|
||||
}
|
||||
} else {
|
||||
|
||||
/////////////////////////// Server definitions
|
||||
|
||||
if(!$tw.browser) {
|
||||
|
||||
/*
|
||||
Get any encrypted tiddlers
|
||||
*/
|
||||
@@ -1289,7 +1361,7 @@ $tw.boot.decryptEncryptedTiddlers = function(callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
}
|
||||
} // End of if($tw.browser && !$tw.node)
|
||||
|
||||
/////////////////////////// Node definitions
|
||||
|
||||
@@ -1551,17 +1623,25 @@ $tw.loadTiddlersNode = function() {
|
||||
}
|
||||
};
|
||||
|
||||
// End of if($tw.node)
|
||||
// End of if($tw.node)
|
||||
}
|
||||
|
||||
/////////////////////////// Main startup function called once tiddlers have been decrypted
|
||||
|
||||
/*
|
||||
Startup TiddlyWiki. Options are:
|
||||
readBrowserTiddlers: whether to read tiddlers from the HTML file we're executing within; if not, tiddlers are read from the file system with Node.js APIs
|
||||
Startup TiddlyWiki
|
||||
*/
|
||||
$tw.boot.startup = function(options) {
|
||||
options = options || {};
|
||||
// Get the URL hash and check for safe mode
|
||||
$tw.locationHash = "#";
|
||||
if($tw.browser && !$tw.node) {
|
||||
if(location.hash === "#:safe") {
|
||||
$tw.safeMode = true;
|
||||
} else {
|
||||
$tw.locationHash = $tw.utils.getLocationHash();
|
||||
}
|
||||
}
|
||||
// Initialise some more $tw properties
|
||||
$tw.utils.deepDefaults($tw,{
|
||||
modules: { // Information about each module
|
||||
@@ -1578,12 +1658,13 @@ $tw.boot.startup = function(options) {
|
||||
wikiThemesSubDir: "./themes",
|
||||
wikiLanguagesSubDir: "./languages",
|
||||
wikiTiddlersSubDir: "./tiddlers",
|
||||
wikiOutputSubDir: "./output",
|
||||
jsModuleHeaderRegExpString: "^\\/\\*\\\\(?:\\r?\\n)((?:^[^\\r\\n]*(?:\\r?\\n))+?)(^\\\\\\*\\/$(?:\\r?\\n)?)",
|
||||
fileExtensionInfo: Object.create(null), // Map file extension to {type:}
|
||||
contentTypeInfo: Object.create(null) // Map type to {encoding:,extension:}
|
||||
}
|
||||
});
|
||||
if(!options.readBrowserTiddlers) {
|
||||
if(!$tw.boot.tasks.readBrowserTiddlers) {
|
||||
// For writable tiddler files, a hashmap of title to {filepath:,type:,hasMetaFile:}
|
||||
$tw.boot.files = Object.create(null);
|
||||
// System paths and filenames
|
||||
@@ -1636,7 +1717,7 @@ $tw.boot.startup = function(options) {
|
||||
$tw.Wiki.tiddlerDeserializerModules = Object.create(null);
|
||||
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
|
||||
// Load tiddlers
|
||||
if(options.readBrowserTiddlers) {
|
||||
if($tw.boot.tasks.readBrowserTiddlers) {
|
||||
$tw.loadTiddlersBrowser();
|
||||
} else {
|
||||
$tw.loadTiddlersNode();
|
||||
@@ -1645,6 +1726,10 @@ $tw.boot.startup = function(options) {
|
||||
$tw.wiki.readPluginInfo();
|
||||
$tw.wiki.registerPluginTiddlers("plugin");
|
||||
$tw.wiki.unpackPluginTiddlers();
|
||||
// Process "safe mode"
|
||||
if($tw.safeMode) {
|
||||
$tw.wiki.processSafeMode();
|
||||
}
|
||||
// Register typed modules from the tiddlers we've just loaded
|
||||
$tw.wiki.defineTiddlerModules();
|
||||
// And any modules within plugins
|
||||
@@ -1653,12 +1738,113 @@ $tw.boot.startup = function(options) {
|
||||
if($tw.crypto) {
|
||||
$tw.crypto.updateCryptoStateTiddler();
|
||||
}
|
||||
// Run any startup modules
|
||||
// Gather up any startup modules
|
||||
$tw.boot.remainingStartupModules = []; // Array of startup modules
|
||||
$tw.modules.forEachModuleOfType("startup",function(title,module) {
|
||||
if(module.startup) {
|
||||
module.startup();
|
||||
$tw.boot.remainingStartupModules.push(module);
|
||||
}
|
||||
});
|
||||
// Keep track of the startup tasks that have been executed
|
||||
$tw.boot.executedStartupModules = Object.create(null);
|
||||
$tw.boot.disabledStartupModules = $tw.boot.disabledStartupModules || [];
|
||||
// Repeatedly execute the next eligible task
|
||||
$tw.boot.executeNextStartupTask();
|
||||
};
|
||||
|
||||
/*
|
||||
Execute the remaining eligible startup tasks
|
||||
*/
|
||||
$tw.boot.executeNextStartupTask = function() {
|
||||
// Find the next eligible task
|
||||
var taskIndex = 0;
|
||||
while(taskIndex < $tw.boot.remainingStartupModules.length) {
|
||||
var task = $tw.boot.remainingStartupModules[taskIndex];
|
||||
if($tw.boot.isStartupTaskEligible(task)) {
|
||||
// Remove this task from the list
|
||||
$tw.boot.remainingStartupModules.splice(taskIndex,1);
|
||||
// Assemble log message
|
||||
var s = ["Startup task:",task.name];
|
||||
if(task.platforms) {
|
||||
s.push("platforms:",task.platforms.join(","));
|
||||
}
|
||||
if(task.after) {
|
||||
s.push("after:",task.after.join(","));
|
||||
}
|
||||
if(task.before) {
|
||||
s.push("before:",task.before.join(","));
|
||||
}
|
||||
$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;
|
||||
}
|
||||
return $tw.boot.executeNextStartupTask();
|
||||
} else {
|
||||
task.startup(function() {
|
||||
if(task.name) {
|
||||
$tw.boot.executedStartupModules[task.name] = true;
|
||||
}
|
||||
return $tw.boot.executeNextStartupTask();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
taskIndex++;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if we are running on one platforms specified in a task modules `platforms` array
|
||||
*/
|
||||
$tw.boot.doesTaskMatchPlatform = function(taskModule) {
|
||||
var platforms = taskModule.platforms;
|
||||
if(platforms) {
|
||||
for(var t=0; t<platforms.length; t++) {
|
||||
if((platforms[t] === "browser" && !$tw.browser) || (platforms[t] === "node" && !$tw.node)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
$tw.boot.isStartupTaskEligible = function(taskModule) {
|
||||
var t;
|
||||
// Check that the platform is correct
|
||||
if(!$tw.boot.doesTaskMatchPlatform(taskModule)) {
|
||||
return false;
|
||||
}
|
||||
var name = taskModule.name,
|
||||
remaining = $tw.boot.remainingStartupModules;
|
||||
if(name) {
|
||||
// Fail if this module is disabled
|
||||
if($tw.boot.disabledStartupModules.indexOf(name) !== -1) {
|
||||
return false;
|
||||
}
|
||||
// Check that no other outstanding tasks must be executed before this one
|
||||
for(t=0; t<remaining.length; t++) {
|
||||
var task = remaining[t];
|
||||
if(task.before && task.before.indexOf(name) !== -1) {
|
||||
if($tw.boot.doesTaskMatchPlatform(task) || (task.name && $tw.boot.disabledStartupModules.indexOf(name) !== -1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that all of the tasks that we must be performed after has been done
|
||||
var after = taskModule.after;
|
||||
if(after) {
|
||||
for(t=0; t<after.length; t++) {
|
||||
if(!$tw.boot.executedStartupModules[after[t]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/////////////////////////// Main boot function to decrypt tiddlers and then startup
|
||||
@@ -1667,15 +1853,13 @@ $tw.boot.boot = function() {
|
||||
// Initialise crypto object
|
||||
$tw.crypto = new $tw.utils.Crypto();
|
||||
// Initialise password prompter
|
||||
if($tw.browser) {
|
||||
if($tw.browser && !$tw.node) {
|
||||
$tw.passwordPrompt = new $tw.utils.PasswordPrompt();
|
||||
}
|
||||
// Preload any encrypted tiddlers
|
||||
$tw.boot.decryptEncryptedTiddlers(function() {
|
||||
// Startup
|
||||
$tw.boot.startup({
|
||||
readBrowserTiddlers: !!$tw.browser
|
||||
});
|
||||
$tw.boot.startup();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -16,13 +16,20 @@ var _bootprefix = (function($tw) {
|
||||
|
||||
"use strict";
|
||||
|
||||
$tw = $tw || {};
|
||||
$tw = $tw || Object.create(null);
|
||||
$tw.boot = $tw.boot || Object.create(null);
|
||||
|
||||
// Detect platforms
|
||||
$tw.browser = typeof(window) !== "undefined" ? {} : null;
|
||||
$tw.node = typeof(process) === "object" ? {} : null;
|
||||
$tw.nodeWebKit = $tw.node && global.window && global.window.nwDispatcher ? {} : null;
|
||||
|
||||
// Set default boot tasks
|
||||
$tw.boot.tasks = {
|
||||
trapErrors: !!($tw.browser && !$tw.node),
|
||||
readBrowserTiddlers: !!($tw.browser && !$tw.node)
|
||||
};
|
||||
|
||||
/*
|
||||
Information about each module is kept in an object with these members:
|
||||
moduleType: type of module
|
||||
|
||||
9
core/images/chevron-left.tid
Normal file
9
core/images/chevron-left.tid
Normal file
@@ -0,0 +1,9 @@
|
||||
title: $:/core/images/chevron-left
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tw-image-chevron-left tw-image-button" width="22pt" height="22pt" viewBox="0 0 128 128" version="1.1">
|
||||
<g>
|
||||
<path d="M97.7982405,98.1688105 L152.184015,43.4979042 C155.938662,39.7236635 155.938662,33.6043964 152.184015,29.8301557 C148.429583,26.0559364 142.342116,26.0559364 138.587684,29.8301557 L91.0000107,77.667222 L43.4123803,29.8301557 C39.6578195,26.0559364 33.5704813,26.0559364 29.8159206,29.8301557 C26.0613598,33.6043964 26.0613598,39.7236635 29.8159206,43.4979042 L84.2017595,98.1688105 C87.9563202,101.943051 94.0436798,101.943051 97.7982405,98.1688126 L97.7982405,98.1688105 Z" transform="translate(91.000000, 63.999491) rotate(-270.000000) translate(-91.000000, -63.999491) "></path>
|
||||
<path d="M43.7982405,98.1688105 L98.1840153,43.4979042 C101.938662,39.7236635 101.938662,33.6043964 98.1840153,29.8301557 C94.4295828,26.0559364 88.3421164,26.0559364 84.5876838,29.8301557 L37.0000107,77.667222 L-10.5876197,29.8301557 C-14.3421805,26.0559364 -20.4295187,26.0559364 -24.1840794,29.8301557 C-27.9386402,33.6043964 -27.9386402,39.7236635 -24.1840794,43.4979042 L30.2017595,98.1688105 C33.9563202,101.943051 40.0436798,101.943051 43.7982405,98.1688126 L43.7982405,98.1688105 Z" transform="translate(37.000000, 63.999491) rotate(-270.000000) translate(-37.000000, -63.999491) "></path>
|
||||
</g>
|
||||
</svg>
|
||||
9
core/images/chevron-right.tid
Normal file
9
core/images/chevron-right.tid
Normal file
@@ -0,0 +1,9 @@
|
||||
title: $:/core/images/chevron-right
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tw-image-chevron-right tw-image-button" width="22pt" height="22pt" viewBox="0 0 128 128">
|
||||
<g>
|
||||
<path d="M97.7982405,98.1688105 L152.184015,43.4979042 C155.938662,39.7236635 155.938662,33.6043964 152.184015,29.8301557 C148.429583,26.0559364 142.342116,26.0559364 138.587684,29.8301557 L91.0000107,77.667222 L43.4123803,29.8301557 C39.6578195,26.0559364 33.5704813,26.0559364 29.8159206,29.8301557 C26.0613598,33.6043964 26.0613598,39.7236635 29.8159206,43.4979042 L84.2017595,98.1688105 C87.9563202,101.943051 94.0436798,101.943051 97.7982405,98.1688126 L97.7982405,98.1688105 Z" transform="translate(91.000000, 63.999491) rotate(-90.000000) translate(-91.000000, -63.999491) "></path>
|
||||
<path d="M43.7982405,98.1688105 L98.1840153,43.4979042 C101.938662,39.7236635 101.938662,33.6043964 98.1840153,29.8301557 C94.4295828,26.0559364 88.3421164,26.0559364 84.5876838,29.8301557 L37.0000107,77.667222 L-10.5876197,29.8301557 C-14.3421805,26.0559364 -20.4295187,26.0559364 -24.1840794,29.8301557 C-27.9386402,33.6043964 -27.9386402,39.7236635 -24.1840794,43.4979042 L30.2017595,98.1688105 C33.9563202,101.943051 40.0436798,101.943051 43.7982405,98.1688126 L43.7982405,98.1688105 Z" transform="translate(37.000000, 63.999491) rotate(-90.000000) translate(-37.000000, -63.999491) "></path>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -4,6 +4,17 @@ Advanced/Caption: Advanced
|
||||
Advanced/Hint: Internal information about this TiddlyWiki
|
||||
Advanced/LoadedModules/Caption: Loaded Modules
|
||||
Advanced/LoadedModules/Hint: These are the currently loaded tiddler modules linked to their source tiddlers. Any italicised modules lack a source tiddler, typically because they were setup during the boot process.
|
||||
Advanced/Settings/Caption: Settings
|
||||
Advanced/Settings/Hint: These advanced settings let you customise the behaviour of TiddlyWiki.
|
||||
Advanced/Settings/NavigationAddressBar/Caption: Navigation Address Bar
|
||||
Advanced/Settings/NavigationAddressBar/Hint: Behaviour of the browser address bar when navigating to a tiddler:
|
||||
Advanced/Settings/NavigationAddressBar/No/Description: Do not update the address bar
|
||||
Advanced/Settings/NavigationAddressBar/Permalink/Description: Include the target tiddler
|
||||
Advanced/Settings/NavigationAddressBar/Permaview/Description: Include the target tiddler and the current story sequence
|
||||
Advanced/Settings/NavigationHistory/Caption: Navigation History
|
||||
Advanced/Settings/NavigationHistory/Hint: Update browser history when navigating to a tiddler:
|
||||
Advanced/Settings/NavigationHistory/No/Description: Do not update history
|
||||
Advanced/Settings/NavigationHistory/Yes/Description: Update history
|
||||
Advanced/TiddlerFields/Caption: Tiddler Fields
|
||||
Advanced/TiddlerFields/Hint: This is the full set of TiddlerFields in use in this wiki (including system tiddlers but excluding shadow tiddlers).
|
||||
Appearance/Caption: Appearance
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
title: $:/language/Docs/ModuleTypes/
|
||||
|
||||
animation: Animations that may be used with the RevealWidget.
|
||||
browser-startup: Startup functions that are only executed in the browser.
|
||||
command: Commands that can be executed under Node.js.
|
||||
config: Data to be inserted into `$tw.config`.
|
||||
filteroperator: Individual filter operator methods.
|
||||
|
||||
@@ -8,6 +8,8 @@ Fields/Add/Button: add
|
||||
Fields/Add/Name/Placeholder: field name
|
||||
Fields/Add/Prompt: Add a new field:
|
||||
Fields/Add/Value/Placeholder: field value
|
||||
Shadow/Warning: This is a shadow tiddler. Any changes will override the default version
|
||||
Shadow/OverriddenWarning: This is a modified shadow tiddler. You can revert to the default version by deleting this tiddler
|
||||
Tags/Add/Button: add
|
||||
Tags/Add/Placeholder: tag name
|
||||
Type/Placeholder: content type
|
||||
|
||||
11
core/language/en-GB/Help/build.tid
Normal file
11
core/language/en-GB/Help/build.tid
Normal file
@@ -0,0 +1,11 @@
|
||||
title: $:/language/Help/build
|
||||
description: Automatically run configured commands
|
||||
|
||||
Build the specified build targets for the current wiki. If no build targets are specified then all available targets will be built.
|
||||
|
||||
```
|
||||
--build <target> [<target> ...]
|
||||
```
|
||||
|
||||
Build targets are defined in the `tiddlywiki.info` file of a wiki folder.
|
||||
|
||||
8
core/language/en-GB/Help/clearpassword.tid
Normal file
8
core/language/en-GB/Help/clearpassword.tid
Normal file
@@ -0,0 +1,8 @@
|
||||
title: $:/language/Help/clearpassword
|
||||
description: Clear a password for subsequent crypto operations
|
||||
|
||||
Clear the password for subsequent crypto operations
|
||||
|
||||
```
|
||||
--clearpassword
|
||||
```
|
||||
10
core/language/en-GB/Help/output.tid
Normal file
10
core/language/en-GB/Help/output.tid
Normal file
@@ -0,0 +1,10 @@
|
||||
title: $:/language/Help/output
|
||||
description: Set the base output directory for subsequent commands
|
||||
|
||||
Sets the base output directory for subsequent commands. The default output directory is the `output` subdirectory of the edition directory.
|
||||
|
||||
```
|
||||
--output <pathname>
|
||||
```
|
||||
|
||||
If the specified pathname is relative then it is resolved relative to the current working directory.
|
||||
@@ -6,3 +6,7 @@ Render an individual tiddler as a specified ContentType, defaults to `text/html`
|
||||
```
|
||||
--rendertiddler <title> <filename> [<type>]
|
||||
```
|
||||
|
||||
By default, the filename is resolved relative to the `output` subdirectory of the edition directory. The `--output` command can be used to direct output to a different directory.
|
||||
|
||||
Any missing directories in the path to the filename are automatically created.
|
||||
|
||||
@@ -12,3 +12,7 @@ For example:
|
||||
```
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html ./static text/plain
|
||||
```
|
||||
|
||||
By default, the pathname is resolved relative to the `output` subdirectory of the edition directory. The `--output` command can be used to direct output to a different directory.
|
||||
|
||||
Any files in the target directory are deleted. The target directory is recursively created if it is missing.
|
||||
|
||||
@@ -6,3 +6,7 @@ Saves an individual tiddler in its raw text or binary format to the specified fi
|
||||
```
|
||||
--savetiddler <title> <filename>
|
||||
```
|
||||
|
||||
By default, the filename is resolved relative to the `output` subdirectory of the edition directory. The `--output` command can be used to direct output to a different directory.
|
||||
|
||||
Any missing directories in the path to the filename are automatically created.
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
title: $:/language/
|
||||
|
||||
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 http://tiddlywiki.com/static/Upgrading.html for more details.
|
||||
ClassicWarning/Upgrade/Caption: upgrade
|
||||
CloseAll/Button: close all
|
||||
ConfirmCancelTiddler: Do you wish to discard changes to the tiddler "<$text text=<<title>>/>"?
|
||||
ConfirmDeleteTiddler: Do you wish to delete the tiddler "<$text text=<<title>>/>"?
|
||||
ConfirmOverwriteTiddler: Do you wish to overwrite the tiddler "<$text text=<<title>>/>"?
|
||||
InvalidFieldName: Illegal characters in field name "<$text text=<<fieldName>>/>". Fields can only contain lowercase letters and the characters underscore (`_`), hyphen (`-`) and period (`.`)
|
||||
ConfirmEditShadowTiddler: You are about to edit a ShadowTiddler. Any changes will override the default system making future upgrades non-trivial. Are you sure you want to edit "<$text text=<<title>>/>"?
|
||||
InvalidFieldName: Illegal characters in field name "<$text text=<<fieldName>>/>". Fields can only contain lowercase letters, digits and the characters underscore (`_`), hyphen (`-`) and period (`.`)
|
||||
MissingTiddler/Hint: Missing tiddler "<$text text=<<currentTiddler>>/>" - click {{$:/core/images/edit-button}} to create
|
||||
RecentChanges/DateFormat: DDth MMM YYYY
|
||||
RelativeDate/Future/Days: <<period>> days from now
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
title: $:/language/Search/
|
||||
|
||||
Advanced/Matches: //<small><$count filter={{$:/temp/advancedsearch}}/> matches</small>//
|
||||
Filter/Caption: Filter
|
||||
Filter/Hint: Search via a [[filter expression|http://tiddlywiki.com/static/TiddlerFilters.html]]
|
||||
Filter/Matches: //<small><$count filter={{$:/temp/advancedsearch}}/> matches</small>//
|
||||
Matches: //<small><$count filter="[!is[system]search{$:/temp/search}]"/> matches</small>//
|
||||
Shadows/Caption: Shadows
|
||||
Shadows/Hint: Search for shadow tiddlers
|
||||
Shadows/Matches: //<small><$count filter="[all[shadows]search{$:/temp/advancedsearch}]"/> matches</small>//
|
||||
Standard/Caption: Standard
|
||||
Standard/Hint: Search for standard tiddlers
|
||||
Standard/Matches: //<small><$count filter="[!is[system]search{$:/temp/advancedsearch}]"/> matches</small>//
|
||||
System/Caption: System
|
||||
System/Hint: Search for system tiddlers
|
||||
System/Matches: //<small><$count filter="[is[system]search{$:/temp/advancedsearch}]"/> matches</small>//
|
||||
|
||||
@@ -20,11 +20,20 @@ Parse a sequence of commands
|
||||
callback: a callback invoked as callback(err) where err is null if there was no error
|
||||
*/
|
||||
var Commander = function(commandTokens,callback,wiki,streams) {
|
||||
var path = require("path");
|
||||
this.commandTokens = commandTokens;
|
||||
this.nextToken = 0;
|
||||
this.callback = callback;
|
||||
this.wiki = wiki;
|
||||
this.streams = streams;
|
||||
this.outputPath = path.resolve($tw.boot.wikiPath,$tw.config.wikiOutputSubDir);
|
||||
};
|
||||
|
||||
/*
|
||||
Add a string of tokens to the command queue
|
||||
*/
|
||||
Commander.prototype.addCommandTokens = function(commandTokens) {
|
||||
Array.prototype.push.apply(this.commandTokens,commandTokens);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
52
core/modules/commands/build.js
Normal file
52
core/modules/commands/build.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/build.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Command to build a build target
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "build",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
var Command = function(params,commander) {
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
// Get the build targets defined in the wiki
|
||||
var buildTargets = $tw.boot.wikiInfo.build;
|
||||
if(!buildTargets) {
|
||||
return "No build targets defined"
|
||||
}
|
||||
// Loop through each of the specified targets
|
||||
var targets;
|
||||
if(this.params.length > 0) {
|
||||
targets = this.params;
|
||||
} else {
|
||||
targets = Object.keys(buildTargets);
|
||||
}
|
||||
for(var targetIndex=0; targetIndex<targets.length; targetIndex++) {
|
||||
var target = targets[targetIndex],
|
||||
commands = buildTargets[target];
|
||||
if(!commands) {
|
||||
return "Build target '" + target + "' not found";
|
||||
}
|
||||
// Add the commands to the queue
|
||||
this.commander.addCommandTokens(commands);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
||||
33
core/modules/commands/clearpassword.js
Normal file
33
core/modules/commands/clearpassword.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/clearpassword.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Clear password for crypto operations
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "clearpassword",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
var Command = function(params,commander,callback) {
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
$tw.crypto.setPassword(null);
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
||||
38
core/modules/commands/output.js
Normal file
38
core/modules/commands/output.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/output.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Command to set the default output location (defaults to current working directory)
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "output",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
var Command = function(params,commander,callback) {
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
var fs = require("fs"),
|
||||
path = require("path");
|
||||
if(this.params.length < 1) {
|
||||
return "Missing output path";
|
||||
}
|
||||
this.commander.outputPath = path.resolve(process.cwd(),this.params[0]);
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
||||
@@ -31,8 +31,9 @@ Command.prototype.execute = function() {
|
||||
fs = require("fs"),
|
||||
path = require("path"),
|
||||
title = this.params[0],
|
||||
filename = this.params[1],
|
||||
filename = path.resolve(this.commander.outputPath,this.params[1]),
|
||||
type = this.params[2] || "text/html";
|
||||
$tw.utils.createFileDirectories(filename);
|
||||
fs.writeFile(filename,this.commander.wiki.renderTiddler(type,title),"utf8",function(err) {
|
||||
self.callback(err);
|
||||
});
|
||||
|
||||
@@ -35,10 +35,12 @@ Command.prototype.execute = function() {
|
||||
wiki = this.commander.wiki,
|
||||
filter = this.params[0],
|
||||
template = this.params[1],
|
||||
pathname = this.params[2],
|
||||
pathname = path.resolve(this.commander.outputPath,this.params[2]),
|
||||
type = this.params[3] || "text/html",
|
||||
extension = this.params[4] || ".html",
|
||||
tiddlers = wiki.filterTiddlers(filter);
|
||||
$tw.utils.deleteDirectory(pathname);
|
||||
$tw.utils.createDirectory(pathname);
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var parser = wiki.parseTiddler(template),
|
||||
widgetNode = wiki.makeWidget(parser,{variables: {currentTiddler: title}});
|
||||
|
||||
@@ -31,10 +31,11 @@ Command.prototype.execute = function() {
|
||||
fs = require("fs"),
|
||||
path = require("path"),
|
||||
title = this.params[0],
|
||||
filename = this.params[1],
|
||||
filename = path.resolve(this.commander.outputPath,this.params[1]),
|
||||
tiddler = this.commander.wiki.getTiddler(title),
|
||||
type = tiddler.fields.type || "text/vnd.tiddlywiki",
|
||||
contentTypeInfo = $tw.config.contentTypeInfo[type] || {encoding: "utf8"};
|
||||
$tw.utils.createFileDirectories(filename);
|
||||
fs.writeFile(filename,tiddler.fields.text,contentTypeInfo.encoding,function(err) {
|
||||
self.callback(err);
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ var Command = function(params,commander) {
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
this.commander.verbose = true;
|
||||
// Output the boot message log
|
||||
this.commander.streams.output.write("Boot log:\n " + $tw.boot.logMessages.join("\n ") + "\n");
|
||||
return null; // No error
|
||||
};
|
||||
|
||||
|
||||
@@ -51,13 +51,13 @@ var parseTiddlerDiv = function(text /* [,fields] */) {
|
||||
// Extract the text
|
||||
result.text = text.substring(match.index + match[0].length,endMatch.index);
|
||||
// Process the attributes
|
||||
var attrRegExp = /\s*([^=\s]+)\s*=\s*"([^"]*)"/gi,
|
||||
var attrRegExp = /\s*([^=\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi,
|
||||
attrMatch;
|
||||
do {
|
||||
attrMatch = attrRegExp.exec(match[1]);
|
||||
if(attrMatch) {
|
||||
var name = attrMatch[1];
|
||||
var value = attrMatch[2];
|
||||
var value = attrMatch[2] || attrMatch[3];
|
||||
result[name] = value;
|
||||
}
|
||||
} while(attrMatch);
|
||||
|
||||
@@ -149,22 +149,22 @@ exports.getFilterOperators = function() {
|
||||
return this.filterOperators;
|
||||
};
|
||||
|
||||
exports.filterTiddlers = function(filterString,currTiddlerTitle,source) {
|
||||
exports.filterTiddlers = function(filterString,widget,source) {
|
||||
var fn = this.compileFilter(filterString);
|
||||
return fn.call(this,source,currTiddlerTitle);
|
||||
return fn.call(this,source,widget);
|
||||
};
|
||||
|
||||
/*
|
||||
Compile a filter into a function with the signature fn(source,currTiddlerTitle) where:
|
||||
Compile a filter into a function with the signature fn(source,widget) where:
|
||||
source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title)
|
||||
currTiddlerTitle: the optional name of the current tiddler
|
||||
widget: an optional widget node for retrieving the current tiddler etc.
|
||||
*/
|
||||
exports.compileFilter = function(filterString) {
|
||||
var filterParseTree;
|
||||
try {
|
||||
filterParseTree = this.parseFilter(filterString);
|
||||
} catch(e) {
|
||||
return function(source,currTiddlerTitle) {
|
||||
return function(source,widget) {
|
||||
return ["Filter error: " + e];
|
||||
};
|
||||
}
|
||||
@@ -176,9 +176,10 @@ exports.compileFilter = function(filterString) {
|
||||
var self = this;
|
||||
$tw.utils.each(filterParseTree,function(operation) {
|
||||
// Create a function for the chain of operators in the operation
|
||||
var operationSubFunction = function(source,currTiddlerTitle) {
|
||||
var operationSubFunction = function(source,widget) {
|
||||
var accumulator = source,
|
||||
results = [];
|
||||
results = [],
|
||||
currTiddlerTitle = widget && widget.getVariable("currentTiddler");
|
||||
$tw.utils.each(operation.operators,function(operator) {
|
||||
var operand = operator.operand,
|
||||
operatorFunction;
|
||||
@@ -200,35 +201,47 @@ exports.compileFilter = function(filterString) {
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
currTiddlerTitle: currTiddlerTitle
|
||||
widget: widget
|
||||
});
|
||||
accumulator = self.makeTiddlerIterator(results);
|
||||
if($tw.utils.isArray(results)) {
|
||||
accumulator = self.makeTiddlerIterator(results);
|
||||
} else {
|
||||
accumulator = results;
|
||||
}
|
||||
});
|
||||
return results;
|
||||
if($tw.utils.isArray(results)) {
|
||||
return results;
|
||||
} else {
|
||||
var resultArray = [];
|
||||
results(function(tiddler,title) {
|
||||
resultArray.push(title);
|
||||
});
|
||||
return resultArray;
|
||||
}
|
||||
};
|
||||
// Wrap the operator functions in a wrapper function that depends on the prefix
|
||||
operationFunctions.push((function() {
|
||||
switch(operation.prefix || "") {
|
||||
case "": // No prefix means that the operation is unioned into the result
|
||||
return function(results,source,currTiddlerTitle) {
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,currTiddlerTitle));
|
||||
return function(results,source,widget) {
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
};
|
||||
case "-": // The results of this operation are removed from the main result
|
||||
return function(results,source,currTiddlerTitle) {
|
||||
$tw.utils.removeArrayEntries(results,operationSubFunction(source,currTiddlerTitle));
|
||||
return function(results,source,widget) {
|
||||
$tw.utils.removeArrayEntries(results,operationSubFunction(source,widget));
|
||||
};
|
||||
case "+": // This operation is applied to the main results so far
|
||||
return function(results,source,currTiddlerTitle) {
|
||||
return function(results,source,widget) {
|
||||
// This replaces all the elements of the array, but keeps the actual array so that references to it are preserved
|
||||
source = self.makeTiddlerIterator(results);
|
||||
results.splice(0,results.length);
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,currTiddlerTitle));
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
};
|
||||
}
|
||||
})());
|
||||
});
|
||||
// Return a function that applies the operations to a source iterator of tiddler titles
|
||||
return $tw.perf.measure("filter",function filterFunction(source,currTiddlerTitle) {
|
||||
return $tw.perf.measure("filter",function filterFunction(source,widget) {
|
||||
if(!source) {
|
||||
source = self.each;
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
@@ -236,7 +249,7 @@ exports.compileFilter = function(filterString) {
|
||||
}
|
||||
var results = [];
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,currTiddlerTitle);
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
return results;
|
||||
});
|
||||
|
||||
31
core/modules/filters/after.js
Normal file
31
core/modules/filters/after.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/after.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator returning the tiddler from the current list that is after the tiddler named in the operand.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.after = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
var index = results.indexOf(operator.operand);
|
||||
if(index === -1 || index > (results.length - 2)) {
|
||||
return [];
|
||||
} else {
|
||||
return [results[index + 1]];
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -5,7 +5,7 @@ module-type: filteroperator
|
||||
|
||||
Filter operator for selecting tiddlers
|
||||
|
||||
[all[tiddlers+shadows]]
|
||||
[all[shadows+tiddlers]]
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@@ -33,6 +33,17 @@ exports.all = function(source,operator,options) {
|
||||
// Cycle through the suboperators accumulating their results
|
||||
var results = [],
|
||||
subops = operator.operand.split("+");
|
||||
// Check for common optimisations
|
||||
if(subops.length === 1 && subops[0] === "tiddlers") {
|
||||
return options.wiki.each;
|
||||
} else if(subops.length === 1 && subops[0] === "shadows") {
|
||||
return options.wiki.eachShadow;
|
||||
} else if(subops.length === 2 && subops[0] === "tiddlers" && subops[1] === "shadows") {
|
||||
return options.wiki.eachTiddlerPlusShadows;
|
||||
} else if(subops.length === 2 && subops[0] === "shadows" && subops[1] === "tiddlers") {
|
||||
return options.wiki.eachShadowPlusTiddlers;
|
||||
}
|
||||
// Do it the hard way
|
||||
for(var t=0; t<subops.length; t++) {
|
||||
var subop = allFilterOperators[subops[t]];
|
||||
if(subop) {
|
||||
|
||||
@@ -16,8 +16,9 @@ Filter function for [all[current]]
|
||||
Export our filter function
|
||||
*/
|
||||
exports.current = function(source,prefix,options) {
|
||||
if(options.currTiddlerTitle) {
|
||||
return [options.currTiddlerTitle];
|
||||
var currTiddlerTitle = options.widget && options.widget.getVariable("currentTiddler");
|
||||
if(currTiddlerTitle) {
|
||||
return [currTiddlerTitle];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
31
core/modules/filters/before.js
Normal file
31
core/modules/filters/before.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/before.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator returning the tiddler from the current list that is before the tiddler named in the operand.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.before = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
var index = results.indexOf(operator.operand);
|
||||
if(index <= 0) {
|
||||
return [];
|
||||
} else {
|
||||
return [results[index - 1]];
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
31
core/modules/filters/get.js
Normal file
31
core/modules/filters/get.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/get.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for replacing tiddler titles by the value of the field specified in the operand.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.get = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
var value = tiddler.getFieldString(operator.operand);
|
||||
if(value) {
|
||||
results.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -16,16 +16,17 @@ Filter function for [is[current]]
|
||||
Export our filter function
|
||||
*/
|
||||
exports.current = function(source,prefix,options) {
|
||||
var results = [];
|
||||
var results = [],
|
||||
currTiddlerTitle = options.widget && options.widget.getVariable("currentTiddler");
|
||||
if(prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(title !== options.currTiddlerTitle) {
|
||||
if(title !== currTiddlerTitle) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(title === options.currTiddlerTitle) {
|
||||
if(title === currTiddlerTitle) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
|
||||
37
core/modules/filters/is/tag.js
Normal file
37
core/modules/filters/is/tag.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/is/tag.js
|
||||
type: application/javascript
|
||||
module-type: isfilteroperator
|
||||
|
||||
Filter function for [is[tag]]
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.tag = function(source,prefix,options) {
|
||||
var results = [],
|
||||
tagMap = options.wiki.getTagMap();
|
||||
if(prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(!$tw.utils.hop(tagMap,title)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if($tw.utils.hop(tagMap,title)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -18,7 +18,8 @@ Export our filter function
|
||||
exports.list = function(source,operator,options) {
|
||||
var results = [],
|
||||
tr = $tw.utils.parseTextReference(operator.operand),
|
||||
list = options.wiki.getTiddlerList(tr.title || options.currTiddlerTitle,tr.field,tr.index);
|
||||
currTiddlerTitle = options.widget && options.widget.getVariable("currentTiddler"),
|
||||
list = options.wiki.getTiddlerList(tr.title || currTiddlerTitle,tr.field,tr.index);
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(list.indexOf(title) === -1) {
|
||||
|
||||
@@ -25,10 +25,8 @@ exports.untagged = function(source,operator,options) {
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
if(!tiddler.hasField("tags") || ($tw.utils.isArray(tiddler.fields.tags) && tiddler.fields.tags.length === 0)) {
|
||||
$tw.utils.pushTop(results,title);
|
||||
}
|
||||
if(!tiddler || !tiddler.hasField("tags") || ($tw.utils.isArray(tiddler.fields.tags) && tiddler.fields.tags.length === 0)) {
|
||||
$tw.utils.pushTop(results,title);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
|
||||
var CsvParser = function(type,text,options) {
|
||||
// Table framework
|
||||
this.tree = [{
|
||||
"type": "element", "tag": "$scrollable", "children": [{
|
||||
"type": "scrollable", "children": [{
|
||||
"type": "element", "tag": "table", "children": [{
|
||||
"type": "element", "tag": "tbody", "children": []
|
||||
}], "attributes": {
|
||||
|
||||
268
core/modules/parsers/parseutils.js
Normal file
268
core/modules/parsers/parseutils.js
Normal file
@@ -0,0 +1,268 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/parseutils.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
|
||||
Utility functions concerned with parsing text into tokens.
|
||||
|
||||
Most functions have the following pattern:
|
||||
|
||||
* The parameters are:
|
||||
** `source`: the source string being parsed
|
||||
** `pos`: the current parse position within the string
|
||||
** Any further parameters are used to identify the token that is being parsed
|
||||
* The return value is:
|
||||
** null if the token was not found at the specified position
|
||||
** an object representing the token with the following standard fields:
|
||||
*** `type`: string indicating the type of the token
|
||||
*** `start`: start position of the token in the source string
|
||||
*** `end`: end position of the token in the source string
|
||||
*** Any further fields required to describe the token
|
||||
|
||||
The exception is `skipWhiteSpace`, which just returns the position after the whitespace.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Look for a whitespace token. Returns null if not found, otherwise returns {type: "whitespace", start:, end:,}
|
||||
*/
|
||||
exports.parseWhiteSpace = function(source,pos) {
|
||||
var node = {
|
||||
type: "whitespace",
|
||||
start: pos
|
||||
};
|
||||
var re = /(\s)+/g;
|
||||
re.lastIndex = pos;
|
||||
var match = re.exec(source);
|
||||
if(match && match.index === pos) {
|
||||
node.end = pos + match[0].length;
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Convenience wrapper for parseWhiteSpace. Returns the position after the whitespace
|
||||
*/
|
||||
exports.skipWhiteSpace = function(source,pos) {
|
||||
var whitespace = $tw.utils.parseWhiteSpace(source,pos);
|
||||
if(whitespace) {
|
||||
return whitespace.end;
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a given string token. Returns null if not found, otherwise returns {type: "token", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseTokenString = function(source,pos,token) {
|
||||
var match = source.indexOf(token,pos) === pos;
|
||||
if(match) {
|
||||
return {
|
||||
type: "token",
|
||||
value: token,
|
||||
start: pos,
|
||||
end: pos + token.length
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,}
|
||||
*/
|
||||
exports.parseTokenRegExp = function(source,pos,reToken) {
|
||||
var node = {
|
||||
type: "regexp",
|
||||
start: pos
|
||||
};
|
||||
reToken.lastIndex = pos;
|
||||
node.match = reToken.exec(source);
|
||||
if(node.match && node.match.index === pos) {
|
||||
node.end = pos + node.match[0].length;
|
||||
return node;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a string literal. Returns null if not found, otherwise returns {type: "string", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseStringLiteral = function(source,pos) {
|
||||
var node = {
|
||||
type: "string",
|
||||
start: pos
|
||||
};
|
||||
var reString = /(?:"([^"]*)")|(?:'([^']*)')/g;
|
||||
reString.lastIndex = pos;
|
||||
var match = reString.exec(source);
|
||||
if(match && match.index === pos) {
|
||||
node.value = match[1] === undefined ? match[2] : match[1];
|
||||
node.end = pos + match[0].length;
|
||||
return node;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a macro invocation parameter. Returns null if not found, or {type: "macro-parameter", name:, value:, start:, end:}
|
||||
*/
|
||||
exports.parseMacroParameter = function(source,pos) {
|
||||
var node = {
|
||||
type: "macro-parameter",
|
||||
start: pos
|
||||
};
|
||||
// Define our regexp
|
||||
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^\s>"'=]+)))/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the parameter
|
||||
var token = $tw.utils.parseTokenRegExp(source,pos,reMacroParameter);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Get the parameter details
|
||||
node.value = token.match[2] !== undefined ? token.match[2] : (
|
||||
token.match[3] !== undefined ? token.match[3] : (
|
||||
token.match[4] !== undefined ? token.match[4] : (
|
||||
token.match[5] !== undefined ? token.match[5] : (
|
||||
""
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
if(token.match[1]) {
|
||||
node.name = token.match[1];
|
||||
}
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:}
|
||||
*/
|
||||
exports.parseMacroInvocation = function(source,pos) {
|
||||
var node = {
|
||||
type: "macrocall",
|
||||
start: pos,
|
||||
params: []
|
||||
};
|
||||
// Define our regexps
|
||||
var reMacroName = /([^\s>"'=]+)/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a double less than sign
|
||||
var token = $tw.utils.parseTokenString(source,pos,"<<");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Get the macro name
|
||||
var name = $tw.utils.parseTokenRegExp(source,pos,reMacroName);
|
||||
if(!name) {
|
||||
return null;
|
||||
}
|
||||
node.name = name.match[1];
|
||||
pos = name.end;
|
||||
// Process parameters
|
||||
var parameter = $tw.utils.parseMacroParameter(source,pos);
|
||||
while(parameter) {
|
||||
node.params.push(parameter);
|
||||
pos = parameter.end;
|
||||
// Get the next parameter
|
||||
parameter = $tw.utils.parseMacroParameter(source,pos);
|
||||
}
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a double greater than sign
|
||||
token = $tw.utils.parseTokenString(source,pos,">>");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an HTML attribute definition. Returns null if not found, otherwise returns {type: "attribute", name:, valueType: "string|indirect|macro", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseAttribute = function(source,pos) {
|
||||
var node = {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexps
|
||||
var reAttributeName = /([^\/\s>"'=]+)/g,
|
||||
reUnquotedAttribute = /([^\/\s<>"'=]+)/g,
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the attribute name
|
||||
var name = $tw.utils.parseTokenRegExp(source,pos,reAttributeName);
|
||||
if(!name) {
|
||||
return null;
|
||||
}
|
||||
node.name = name.match[1];
|
||||
pos = name.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for an equals sign
|
||||
var token = $tw.utils.parseTokenString(source,pos,"=");
|
||||
if(token) {
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a string literal
|
||||
var stringLiteral = $tw.utils.parseStringLiteral(source,pos);
|
||||
if(stringLiteral) {
|
||||
pos = stringLiteral.end;
|
||||
node.type = "string";
|
||||
node.value = stringLiteral.value;
|
||||
} else {
|
||||
// Look for an indirect value
|
||||
var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue);
|
||||
if(indirectValue) {
|
||||
pos = indirectValue.end;
|
||||
node.type = "indirect";
|
||||
node.textReference = indirectValue.match[1];
|
||||
} else {
|
||||
// Look for a unquoted value
|
||||
var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute);
|
||||
if(unquotedValue) {
|
||||
pos = unquotedValue.end;
|
||||
node.type = "string";
|
||||
node.value = unquotedValue.match[1];
|
||||
} else {
|
||||
// Look for a macro invocation value
|
||||
var macroInvocation = $tw.utils.parseMacroInvocation(source,pos);
|
||||
if(macroInvocation) {
|
||||
pos = macroInvocation.end;
|
||||
node.type = "macro";
|
||||
node.value = macroInvocation;
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -14,12 +14,11 @@ The plain text parser processes blocks of source text into a degenerate parse tr
|
||||
|
||||
var TextParser = function(type,text,options) {
|
||||
this.tree = [{
|
||||
type: "element",
|
||||
tag: "pre",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: text
|
||||
}]
|
||||
type: "codeblock",
|
||||
attributes: {
|
||||
code: {type: "string", value: text},
|
||||
language: {type: "string", value: type}
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
@@ -46,8 +46,7 @@ exports.parse = function() {
|
||||
}
|
||||
// Return the $codeblock widget
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$codeblock",
|
||||
type: "codeblock",
|
||||
attributes: {
|
||||
code: {type: "string", value: text},
|
||||
language: {type: "string", value: this.match[1]}
|
||||
|
||||
@@ -40,8 +40,7 @@ exports.parse = function() {
|
||||
classes = this.match[5];
|
||||
// Return the list widget
|
||||
var node = {
|
||||
type: "element",
|
||||
tag: "$list",
|
||||
type: "list",
|
||||
attributes: {
|
||||
filter: {type: "string", value: filter}
|
||||
},
|
||||
|
||||
@@ -40,8 +40,7 @@ exports.parse = function() {
|
||||
classes = this.match[5];
|
||||
// Return the list widget
|
||||
var node = {
|
||||
type: "element",
|
||||
tag: "$list",
|
||||
type: "list",
|
||||
attributes: {
|
||||
filter: {type: "string", value: filter}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ exports.parse = function() {
|
||||
// Advance the parser position to past the tag
|
||||
this.parser.pos = tag.end;
|
||||
// Check for an immediately following double linebreak
|
||||
var hasLineBreak = !tag.isSelfClosing && !!this.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n]*\r?\n(?:[^\S\n]*\r?\n|$))/g);
|
||||
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n]*\r?\n(?:[^\S\n]*\r?\n|$))/g);
|
||||
// Set whether we're in block mode
|
||||
tag.isBlock = this.is.block || hasLineBreak;
|
||||
// Parse the body if we need to
|
||||
@@ -71,244 +71,7 @@ exports.parse = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a whitespace token. Returns null if not found, otherwise returns {type: "whitespace", start:, end:,}
|
||||
*/
|
||||
exports.parseWhiteSpace = function(source,pos) {
|
||||
var node = {
|
||||
type: "whitespace",
|
||||
start: pos
|
||||
};
|
||||
var re = /(\s)+/g;
|
||||
re.lastIndex = pos;
|
||||
var match = re.exec(source);
|
||||
if(match && match.index === pos) {
|
||||
node.end = pos + match[0].length;
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Convenience wrapper for parseWhiteSpace
|
||||
*/
|
||||
exports.skipWhiteSpace = function(source,pos) {
|
||||
var whitespace = this.parseWhiteSpace(source,pos);
|
||||
if(whitespace) {
|
||||
return whitespace.end;
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a given string token. Returns null if not found, otherwise returns {type: "token", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseTokenString = function(source,pos,token) {
|
||||
var match = source.indexOf(token,pos) === pos;
|
||||
if(match) {
|
||||
return {
|
||||
type: "token",
|
||||
value: token,
|
||||
start: pos,
|
||||
end: pos + token.length
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,}
|
||||
*/
|
||||
exports.parseTokenRegExp = function(source,pos,reToken) {
|
||||
var node = {
|
||||
type: "regexp",
|
||||
start: pos
|
||||
};
|
||||
reToken.lastIndex = pos;
|
||||
node.match = reToken.exec(source);
|
||||
if(node.match && node.match.index === pos) {
|
||||
node.end = pos + node.match[0].length;
|
||||
return node;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a string literal. Returns null if not found, otherwise returns {type: "string", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseStringLiteral = function(source,pos) {
|
||||
var node = {
|
||||
type: "string",
|
||||
start: pos
|
||||
};
|
||||
var reString = /(?:"([^"]*)")|(?:'([^']*)')/g;
|
||||
reString.lastIndex = pos;
|
||||
var match = reString.exec(source);
|
||||
if(match && match.index === pos) {
|
||||
node.value = match[1] === undefined ? match[2] : match[1];
|
||||
node.end = pos + match[0].length;
|
||||
return node;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a macro invocation parameter. Returns null if not found, or {type: "macro-parameter", name:, value:, start:, end:}
|
||||
*/
|
||||
exports.parseMacroParameter = function(source,pos) {
|
||||
var node = {
|
||||
type: "macro-parameter",
|
||||
start: pos
|
||||
};
|
||||
// Define our regexp
|
||||
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^\s>"'=]+)))/g;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Look for the parameter
|
||||
var token = this.parseTokenRegExp(source,pos,reMacroParameter);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Get the parameter details
|
||||
node.value = token.match[2] !== undefined ? token.match[2] : (
|
||||
token.match[3] !== undefined ? token.match[3] : (
|
||||
token.match[4] !== undefined ? token.match[4] : (
|
||||
token.match[5] !== undefined ? token.match[5] : (
|
||||
""
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
if(token.match[1]) {
|
||||
node.name = token.match[1];
|
||||
}
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:}
|
||||
*/
|
||||
exports.parseMacroInvocation = function(source,pos) {
|
||||
var node = {
|
||||
type: "macrocall",
|
||||
start: pos,
|
||||
params: []
|
||||
};
|
||||
// Define our regexps
|
||||
var reMacroName = /([^\s>"'=]+)/g;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Look for a double less than sign
|
||||
var token = this.parseTokenString(source,pos,"<<");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Get the macro name
|
||||
var name = this.parseTokenRegExp(source,pos,reMacroName);
|
||||
if(!name) {
|
||||
return null;
|
||||
}
|
||||
node.name = name.match[1];
|
||||
pos = name.end;
|
||||
// Process parameters
|
||||
var parameter = this.parseMacroParameter(source,pos);
|
||||
while(parameter) {
|
||||
node.params.push(parameter);
|
||||
pos = parameter.end;
|
||||
// Get the next parameter
|
||||
parameter = this.parseMacroParameter(source,pos);
|
||||
}
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Look for a double greater than sign
|
||||
token = this.parseTokenString(source,pos,">>");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an HTML attribute definition. Returns null if not found, otherwise returns {type: "attribute", name:, valueType: "string|indirect|macro", value:, start:, end:,}
|
||||
*/
|
||||
exports.parseAttribute = function(source,pos) {
|
||||
var node = {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexps
|
||||
var reAttributeName = /([^\/\s>"'=]+)/g,
|
||||
reUnquotedAttribute = /([^\/\s<>"'=]+)/g,
|
||||
reIndirectValue = /\{\{([^\}]+)\}\}/g;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Get the attribute name
|
||||
var name = this.parseTokenRegExp(source,pos,reAttributeName);
|
||||
if(!name) {
|
||||
return null;
|
||||
}
|
||||
node.name = name.match[1];
|
||||
pos = name.end;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Look for an equals sign
|
||||
var token = this.parseTokenString(source,pos,"=");
|
||||
if(token) {
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
// Look for a string literal
|
||||
var stringLiteral = this.parseStringLiteral(source,pos);
|
||||
if(stringLiteral) {
|
||||
pos = stringLiteral.end;
|
||||
node.type = "string";
|
||||
node.value = stringLiteral.value;
|
||||
} else {
|
||||
// Look for an indirect value
|
||||
var indirectValue = this.parseTokenRegExp(source,pos,reIndirectValue);
|
||||
if(indirectValue) {
|
||||
pos = indirectValue.end;
|
||||
node.type = "indirect";
|
||||
node.textReference = indirectValue.match[1];
|
||||
} else {
|
||||
// Look for a unquoted value
|
||||
var unquotedValue = this.parseTokenRegExp(source,pos,reUnquotedAttribute);
|
||||
if(unquotedValue) {
|
||||
pos = unquotedValue.end;
|
||||
node.type = "string";
|
||||
node.value = unquotedValue.match[1];
|
||||
} else {
|
||||
// Look for a macro invocation value
|
||||
var macroInvocation = this.parseMacroInvocation(source,pos);
|
||||
if(macroInvocation) {
|
||||
pos = macroInvocation.end;
|
||||
node.type = "macro";
|
||||
node.value = macroInvocation;
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an HTML tag. Returns null if not found, otherwise returns {type: "tag", name:, attributes: [], isSelfClosing:, start:, end:,}
|
||||
Look for an HTML tag. Returns null if not found, otherwise returns {type: "element", name:, attributes: [], isSelfClosing:, start:, end:,}
|
||||
*/
|
||||
exports.parseTag = function(source,pos,options) {
|
||||
options = options || {};
|
||||
@@ -321,45 +84,48 @@ exports.parseTag = function(source,pos,options) {
|
||||
// Define our regexps
|
||||
var reTagName = /([a-zA-Z0-9\-\$]+)/g;
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a less than sign
|
||||
token = this.parseTokenString(source,pos,"<");
|
||||
token = $tw.utils.parseTokenString(source,pos,"<");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Get the tag name
|
||||
token = this.parseTokenRegExp(source,pos,reTagName);
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,reTagName);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
node.tag = token.match[1];
|
||||
if(node.tag.charAt(0) === "$") {
|
||||
node.type = node.tag.substr(1);
|
||||
}
|
||||
pos = token.end;
|
||||
// Process attributes
|
||||
var attribute = this.parseAttribute(source,pos);
|
||||
var attribute = $tw.utils.parseAttribute(source,pos);
|
||||
while(attribute) {
|
||||
node.attributes[attribute.name] = attribute;
|
||||
pos = attribute.end;
|
||||
// Get the next attribute
|
||||
attribute = this.parseAttribute(source,pos);
|
||||
attribute = $tw.utils.parseAttribute(source,pos);
|
||||
}
|
||||
// Skip whitespace
|
||||
pos = this.skipWhiteSpace(source,pos);
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for a closing slash
|
||||
token = this.parseTokenString(source,pos,"/");
|
||||
token = $tw.utils.parseTokenString(source,pos,"/");
|
||||
if(token) {
|
||||
pos = token.end;
|
||||
node.isSelfClosing = true;
|
||||
}
|
||||
// Look for a greater than sign
|
||||
token = this.parseTokenString(source,pos,">");
|
||||
token = $tw.utils.parseTokenString(source,pos,">");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Check for a required line break
|
||||
if(options.requireLineBreak) {
|
||||
token = this.parseTokenRegExp(source,pos,/([^\S\n]*\r?\n(?:[^\S\n]*\r?\n|$))/g);
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n]*\r?\n(?:[^\S\n]*\r?\n|$))/g);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
@@ -379,7 +145,7 @@ exports.findNextTag = function(source,pos,options) {
|
||||
// Try to parse the candidate as a tag
|
||||
var tag = this.parseTag(source,match.index,options);
|
||||
// Return success
|
||||
if(tag && this.isLegalTag(tag.tag)) {
|
||||
if(tag && this.isLegalTag(tag)) {
|
||||
return tag;
|
||||
}
|
||||
// Look for the next match
|
||||
@@ -391,11 +157,11 @@ exports.findNextTag = function(source,pos,options) {
|
||||
};
|
||||
|
||||
exports.isLegalTag = function(tag) {
|
||||
// If it starts with a $ then we'll let anything go
|
||||
if(tag.charAt(0) === "$") {
|
||||
// Widgets are always OK
|
||||
if(tag.type !== "element") {
|
||||
return true;
|
||||
// If it starts with a dash then it's not legal
|
||||
} else if(tag.charAt(0) === "-") {
|
||||
// If it's an HTML tag that starts with a dash then it's not legal
|
||||
} else if(tag.tag.charAt(0) === "-") {
|
||||
return false;
|
||||
} else {
|
||||
// Otherwise it's OK
|
||||
|
||||
133
core/modules/parsers/wikiparser/rules/image.js
Normal file
133
core/modules/parsers/wikiparser/rules/image.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/image.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki text inline rule for embedding images. For example:
|
||||
|
||||
```
|
||||
[img[http://tiddlywiki.com/fractalveg.jpg]]
|
||||
[img width=23 height=24 [http://tiddlywiki.com/fractalveg.jpg]]
|
||||
[img width={{!!width}} height={{!!height}} [http://tiddlywiki.com/fractalveg.jpg]]
|
||||
[img[Description of image|http://tiddlywiki.com/fractalveg.jpg]]
|
||||
[img[TiddlerTitle]]
|
||||
[img[Description of image|TiddlerTitle]]
|
||||
```
|
||||
|
||||
Generates the `<$image>` widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "image";
|
||||
exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
// Find the next tag
|
||||
this.nextImage = this.findNextImage(this.parser.source,startPos);
|
||||
return this.nextImage ? this.nextImage.start : undefined;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.nextImage.end;
|
||||
var node = {
|
||||
type: "image",
|
||||
attributes: this.nextImage.attributes
|
||||
};
|
||||
return [node];
|
||||
};
|
||||
|
||||
/*
|
||||
Find the next image from the current position
|
||||
*/
|
||||
exports.findNextImage = function(source,pos) {
|
||||
// A regexp for finding candidate HTML tags
|
||||
var reLookahead = /(\[img)/g;
|
||||
// Find the next candidate
|
||||
reLookahead.lastIndex = pos;
|
||||
var match = reLookahead.exec(source);
|
||||
while(match) {
|
||||
// Try to parse the candidate as a tag
|
||||
var tag = this.parseImage(source,match.index);
|
||||
// Return success
|
||||
if(tag) {
|
||||
return tag;
|
||||
}
|
||||
// Look for the next match
|
||||
reLookahead.lastIndex = match.index + 1;
|
||||
match = reLookahead.exec(source);
|
||||
}
|
||||
// Failed
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an image at the specified position. Returns null if not found, otherwise returns {type: "image", attributes: [], isSelfClosing:, start:, end:,}
|
||||
*/
|
||||
exports.parseImage = function(source,pos) {
|
||||
var token,
|
||||
node = {
|
||||
type: "image",
|
||||
start: pos,
|
||||
attributes: {}
|
||||
};
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the `[img`
|
||||
token = $tw.utils.parseTokenString(source,pos,"[img");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Process attributes
|
||||
if(source.charAt(pos) !== "[") {
|
||||
var attribute = $tw.utils.parseAttribute(source,pos);
|
||||
while(attribute) {
|
||||
node.attributes[attribute.name] = attribute;
|
||||
pos = attribute.end;
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
if(source.charAt(pos) !== "[") {
|
||||
// Get the next attribute
|
||||
attribute = $tw.utils.parseAttribute(source,pos);
|
||||
} else {
|
||||
attribute = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the `[` after the attributes
|
||||
token = $tw.utils.parseTokenString(source,pos,"[");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the source up to the terminating `]]`
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
if(token.match[1]) {
|
||||
node.attributes.tooltip = {type: "string", value: token.match[1].trim()};
|
||||
}
|
||||
node.attributes.source = {type: "string", value: (token.match[2] || "").trim()};
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
})();
|
||||
112
core/modules/parsers/wikiparser/rules/prettyextlink.js
Normal file
112
core/modules/parsers/wikiparser/rules/prettyextlink.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/prettyextlink.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki text inline rule for external links. For example:
|
||||
|
||||
```
|
||||
[ext[http://tiddlywiki.com/fractalveg.jpg]]
|
||||
[ext[Tooltip|http://tiddlywiki.com/fractalveg.jpg]]
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "prettyextlink";
|
||||
exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
// Find the next tag
|
||||
this.nextLink = this.findNextLink(this.parser.source,startPos);
|
||||
return this.nextLink ? this.nextLink.start : undefined;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.nextLink.end;
|
||||
return [this.nextLink];
|
||||
};
|
||||
|
||||
/*
|
||||
Find the next link from the current position
|
||||
*/
|
||||
exports.findNextLink = function(source,pos) {
|
||||
// A regexp for finding candidate links
|
||||
var reLookahead = /(\[ext\[)/g;
|
||||
// Find the next candidate
|
||||
reLookahead.lastIndex = pos;
|
||||
var match = reLookahead.exec(source);
|
||||
while(match) {
|
||||
// Try to parse the candidate as a link
|
||||
var link = this.parseLink(source,match.index);
|
||||
// Return success
|
||||
if(link) {
|
||||
return link;
|
||||
}
|
||||
// Look for the next match
|
||||
reLookahead.lastIndex = match.index + 1;
|
||||
match = reLookahead.exec(source);
|
||||
}
|
||||
// Failed
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an link at the specified position. Returns null if not found, otherwise returns {type: "element", tag: "a", attributes: [], isSelfClosing:, start:, end:,}
|
||||
*/
|
||||
exports.parseLink = function(source,pos) {
|
||||
var token,
|
||||
textNode = {
|
||||
type: "text"
|
||||
},
|
||||
node = {
|
||||
type: "element",
|
||||
tag: "a",
|
||||
start: pos,
|
||||
attributes: {},
|
||||
children: [textNode]
|
||||
};
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the `[ext[`
|
||||
token = $tw.utils.parseTokenString(source,pos,"[ext[");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Look ahead for the terminating `]]`
|
||||
var closePos = source.indexOf("]]",pos);
|
||||
if(closePos === -1) {
|
||||
return null;
|
||||
}
|
||||
// Look for a `|` separating the tooltip
|
||||
var splitPos = source.indexOf("|",pos);
|
||||
if(splitPos === -1 || splitPos > closePos) {
|
||||
splitPos = null;
|
||||
}
|
||||
// Pull out the tooltip and URL
|
||||
var tooltip, URL;
|
||||
if(splitPos) {
|
||||
URL = source.substring(splitPos + 1,closePos).trim();
|
||||
textNode.text = source.substring(pos,splitPos).trim();
|
||||
} else {
|
||||
URL = source.substring(pos,closePos).trim();
|
||||
textNode.text = URL;
|
||||
}
|
||||
node.attributes.href = {type: "string", value: URL};
|
||||
node.attributes.target = {type: "string", value: "_blank"};
|
||||
// Update the end position
|
||||
node.end = closePos + 2;
|
||||
return node;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -53,8 +53,7 @@ exports.parse = function() {
|
||||
}];
|
||||
} else {
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
type: "link",
|
||||
attributes: {
|
||||
to: {type: "string", value: link}
|
||||
},
|
||||
|
||||
@@ -36,8 +36,7 @@ exports.parse = function() {
|
||||
textRef = $tw.utils.trim(this.match[1]);
|
||||
// Prepare the transclude widget
|
||||
var transcludeNode = {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
type: "transclude",
|
||||
attributes: {},
|
||||
isBlock: true
|
||||
};
|
||||
@@ -48,8 +47,7 @@ exports.parse = function() {
|
||||
targetField = tr.field,
|
||||
targetIndex = tr.index,
|
||||
tiddlerNode = {
|
||||
type: "element",
|
||||
tag: "$tiddler",
|
||||
type: "tiddler",
|
||||
attributes: {
|
||||
tiddler: {type: "string", value: targetTitle}
|
||||
},
|
||||
|
||||
@@ -34,8 +34,7 @@ exports.parse = function() {
|
||||
textRef = $tw.utils.trim(this.match[1]);
|
||||
// Prepare the transclude widget
|
||||
var transcludeNode = {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
type: "transclude",
|
||||
attributes: {}
|
||||
};
|
||||
// Prepare the tiddler widget
|
||||
@@ -45,8 +44,7 @@ exports.parse = function() {
|
||||
targetField = tr.field,
|
||||
targetIndex = tr.index,
|
||||
tiddlerNode = {
|
||||
type: "element",
|
||||
tag: "$tiddler",
|
||||
type: "tiddler",
|
||||
attributes: {
|
||||
tiddler: {type: "string", value: targetTitle}
|
||||
},
|
||||
|
||||
@@ -64,8 +64,7 @@ exports.parse = function() {
|
||||
}
|
||||
}
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
type: "link",
|
||||
attributes: {
|
||||
to: {type: "string", value: linkText}
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ var AndTidWiki = function(wiki) {
|
||||
|
||||
AndTidWiki.prototype.save = function(text,method,callback) {
|
||||
// Get the pathname of this document
|
||||
var pathname = decodeURIComponent(document.location.toString());
|
||||
var pathname = decodeURIComponent(document.location.toString().split("#")[0]);
|
||||
// Strip the file://
|
||||
if(pathname.indexOf("file://") === 0) {
|
||||
pathname = pathname.substr(7);
|
||||
|
||||
@@ -19,16 +19,31 @@ TiddlyFoxSaver.prototype.save = function(text,method,callback) {
|
||||
var messageBox = document.getElementById("tiddlyfox-message-box");
|
||||
if(messageBox) {
|
||||
// Get the pathname of this document
|
||||
var pathname = document.location.pathname;
|
||||
// Test for a Windows path of the form /x:/blah/blah
|
||||
if(/^\/[A-Z]\:\//i.test(pathname)) {
|
||||
// Remove the leading slash
|
||||
pathname = pathname.substr(1);
|
||||
// Convert slashes to backslashes
|
||||
pathname = pathname.replace(/\//g,"\\");
|
||||
var pathname = document.location.toString().split("#")[0];
|
||||
// Replace file://localhost/ with file:///
|
||||
if(pathname.indexOf("file://localhost/") == 0) {
|
||||
pathname = "file://" + pathname.substr(16);
|
||||
}
|
||||
// Windows path file:///x:/blah/blah --> x:\blah\blah
|
||||
if(/^file\:\/\/\/[A-Z]\:\//i.test(pathname)) {
|
||||
// Remove the leading slash and convert slashes to backslashes
|
||||
pathname = pathname.substr(8).replace(/\//g,"\\");
|
||||
// Firefox Windows network path file://///server/share/blah/blah --> //server/share/blah/blah
|
||||
} else if(pathname.indexOf("file://///") === 0) {
|
||||
pathname = "\\\\" + unescape(pathname.substr(10)).replace(/\//g,"\\");
|
||||
// Mac/Unix local path file:///path/path --> /path/path
|
||||
} else if(pathname.indexOf("file:///") == 0) {
|
||||
pathname = unescape(pathname.substr(7));
|
||||
// Mac/Unix local path file:/path/path --> /path/path
|
||||
} else if(pathname.indexOf("file:/") == 0) {
|
||||
pathname = unescape(pathname.substr(5));
|
||||
// Otherwise Windows networth path file://server/share/path/path --> \\server\share\path\path
|
||||
} else {
|
||||
pathname = "\\\\" + unescape(pathname.substr(7)).replace(new RegExp("/","g"),"\\");
|
||||
}
|
||||
// Create the message element and put it in the message box
|
||||
var message = document.createElement("div");
|
||||
console.log("Pathname",pathname);
|
||||
message.setAttribute("data-tiddlyfox-path",decodeURIComponent(pathname));
|
||||
message.setAttribute("data-tiddlyfox-content",text);
|
||||
messageBox.appendChild(message);
|
||||
|
||||
@@ -12,38 +12,22 @@ This is the main application logic for both the client and server
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "startup";
|
||||
exports.after = ["load-modules"];
|
||||
exports.synchronous = true;
|
||||
|
||||
// Set to `true` to enable performance instrumentation
|
||||
var PERFORMANCE_INSTRUMENTATION = false;
|
||||
|
||||
// Time (in ms) that we defer refreshing changes to draft tiddlers
|
||||
var DRAFT_TIDDLER_TIMEOUT = 400;
|
||||
|
||||
var widget = require("$:/core/modules/widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
var modules,n,m,f,commander;
|
||||
// Load modules
|
||||
$tw.modules.applyMethods("utils",$tw.utils);
|
||||
if($tw.node) {
|
||||
$tw.modules.applyMethods("utils-node",$tw.utils);
|
||||
}
|
||||
$tw.modules.applyMethods("global",$tw);
|
||||
$tw.modules.applyMethods("config",$tw.config);
|
||||
var modules,n,m,f;
|
||||
if($tw.browser) {
|
||||
$tw.utils.getBrowserInfo($tw.browser);
|
||||
$tw.browser.isIE = (/msie|trident/i.test(navigator.userAgent));
|
||||
}
|
||||
$tw.version = $tw.utils.extractVersionInfo();
|
||||
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
|
||||
$tw.modules.applyMethods("tiddlermethod",$tw.Tiddler.prototype);
|
||||
$tw.modules.applyMethods("wikimethod",$tw.Wiki.prototype);
|
||||
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
|
||||
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
|
||||
// Set up the performance framework
|
||||
$tw.perf = new $tw.Performance(PERFORMANCE_INSTRUMENTATION);
|
||||
// Set up the parsers
|
||||
$tw.wiki.initParsers();
|
||||
// Set up the command modules
|
||||
$tw.Commander.initCommands();
|
||||
// Kick off the language manager and switcher
|
||||
$tw.language = new $tw.Language();
|
||||
$tw.languageSwitcher = new $tw.PluginSwitcher({
|
||||
@@ -64,24 +48,8 @@ exports.startup = function() {
|
||||
"$:/themes/tiddlywiki/vanilla"
|
||||
]
|
||||
});
|
||||
// Display the default tiddlers
|
||||
var displayDefaultTiddlers = function() {
|
||||
// Get the default tiddlers
|
||||
var defaultTiddlersTitle = "$:/DefaultTiddlers",
|
||||
defaultTiddlersTiddler = $tw.wiki.getTiddler(defaultTiddlersTitle),
|
||||
defaultTiddlers = [];
|
||||
if(defaultTiddlersTiddler) {
|
||||
defaultTiddlers = $tw.wiki.filterTiddlers(defaultTiddlersTiddler.fields.text);
|
||||
}
|
||||
// Initialise the story
|
||||
var storyTitle = "$:/StoryList",
|
||||
story = [];
|
||||
for(var t=0; t<defaultTiddlers.length; t++) {
|
||||
story[t] = defaultTiddlers[t];
|
||||
}
|
||||
$tw.wiki.addTiddler({title: storyTitle, text: "", list: story},$tw.wiki.getModificationFields());
|
||||
};
|
||||
displayDefaultTiddlers();
|
||||
// Clear outstanding tiddler store change events to avoid an unnecessary refresh cycle at startup
|
||||
$tw.wiki.clearTiddlerEventQueue();
|
||||
// Set up the syncer object
|
||||
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki});
|
||||
// Host-specific startup
|
||||
@@ -101,196 +69,7 @@ exports.startup = function() {
|
||||
});
|
||||
// Install the animator
|
||||
$tw.anim = new $tw.utils.Animator();
|
||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||
$tw.rootWidget = new widget.widget({
|
||||
type: "widget",
|
||||
children: []
|
||||
},{
|
||||
wiki: $tw.wiki,
|
||||
document: document
|
||||
});
|
||||
// Install the modal message mechanism
|
||||
$tw.modal = new $tw.utils.Modal($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tw-modal",function(event) {
|
||||
$tw.modal.display(event.param);
|
||||
});
|
||||
// Install the notification mechanism
|
||||
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tw-notify",function(event) {
|
||||
$tw.notifier.display(event.param);
|
||||
});
|
||||
// Install the scroller
|
||||
$tw.pageScroller = new $tw.utils.PageScroller();
|
||||
$tw.rootWidget.addEventListener("tw-scroll",function(event) {
|
||||
$tw.pageScroller.handleEvent(event);
|
||||
});
|
||||
// Listen for the tw-home message
|
||||
$tw.rootWidget.addEventListener("tw-home",function(event) {
|
||||
displayDefaultTiddlers();
|
||||
});
|
||||
// Install the save action handlers
|
||||
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
method: "autosave",
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-download-file",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
method: "download",
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
// Listen out for login/logout/refresh events in the browser
|
||||
$tw.rootWidget.addEventListener("tw-login",function() {
|
||||
$tw.syncer.handleLoginEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-logout",function() {
|
||||
$tw.syncer.handleLogoutEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-server-refresh",function() {
|
||||
$tw.syncer.handleRefreshEvent();
|
||||
});
|
||||
// Install the crypto event handlers
|
||||
$tw.rootWidget.addEventListener("tw-set-password",function(event) {
|
||||
$tw.passwordPrompt.createPrompt({
|
||||
serviceName: "Set a new password for this TiddlyWiki",
|
||||
noUserName: true,
|
||||
submitText: "Set password",
|
||||
canCancel: true,
|
||||
callback: function(data) {
|
||||
if(data) {
|
||||
$tw.crypto.setPassword(data.password);
|
||||
}
|
||||
return true; // Get rid of the password prompt
|
||||
}
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-clear-password",function(event) {
|
||||
$tw.crypto.setPassword(null);
|
||||
});
|
||||
// Ensure that $:/isEncrypted is maintained properly
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.utils.hop(changes,"$:/isEncrypted")) {
|
||||
$tw.crypto.updateCryptoStateTiddler();
|
||||
}
|
||||
});
|
||||
// Set up the favicon
|
||||
var faviconTitle = "$:/favicon.ico",
|
||||
faviconLink = document.getElementById("faviconLink"),
|
||||
setFavicon = function() {
|
||||
var tiddler = $tw.wiki.getTiddler(faviconTitle);
|
||||
if(tiddler) {
|
||||
faviconLink.setAttribute("href","data:" + tiddler.fields.type + ";base64," + tiddler.fields.text);
|
||||
}
|
||||
};
|
||||
setFavicon();
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.utils.hop(changes,faviconTitle)) {
|
||||
setFavicon();
|
||||
}
|
||||
});
|
||||
// Set up the styles
|
||||
var styleTemplateTitle = "$:/core/ui/PageStylesheet",
|
||||
styleParser = $tw.wiki.parseTiddler(styleTemplateTitle);
|
||||
$tw.styleWidgetNode = $tw.wiki.makeWidget(styleParser,{document: $tw.fakeDocument});
|
||||
$tw.styleContainer = $tw.fakeDocument.createElement("style");
|
||||
$tw.styleWidgetNode.render($tw.styleContainer,null);
|
||||
$tw.styleElement = document.createElement("style");
|
||||
$tw.styleElement.innerHTML = $tw.styleContainer.textContent;
|
||||
document.head.insertBefore($tw.styleElement,document.head.firstChild);
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("styleRefresh",function(changes) {
|
||||
if($tw.styleWidgetNode.refresh(changes,$tw.styleContainer,null)) {
|
||||
$tw.styleElement.innerHTML = $tw.styleContainer.textContent;
|
||||
}
|
||||
}));
|
||||
// Display the $:/PageMacros tiddler to kick off the display
|
||||
renderPage();
|
||||
// Fix up the link between the root widget and the page container
|
||||
$tw.rootWidget.domNodes = [$tw.pageContainer];
|
||||
$tw.rootWidget.children = [$tw.pageWidgetNode];
|
||||
// If we're being viewed on a data: URI then give instructions for how to save
|
||||
if(document.location.protocol === "data:") {
|
||||
$tw.utils.dispatchCustomEvent(document,"tw-modal",{
|
||||
param: "$:/language/Modals/SaveInstructions"
|
||||
});
|
||||
}
|
||||
// Call browser startup modules
|
||||
$tw.modules.forEachModuleOfType("browser-startup",function(title,module) {
|
||||
if(module.startup) {
|
||||
module.startup();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// On the server, start a commander with the command line arguments
|
||||
commander = new $tw.Commander(
|
||||
$tw.boot.argv,
|
||||
function(err) {
|
||||
if(err) {
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
},
|
||||
$tw.wiki,
|
||||
{output: process.stdout, error: process.stderr}
|
||||
);
|
||||
commander.execute();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Main render function for PageMacros, which includes the PageTemplate
|
||||
*/
|
||||
function renderPage() {
|
||||
// Parse and render the template
|
||||
var templateTitle = "$:/core/ui/PageMacros",
|
||||
parser = $tw.wiki.parseTiddler(templateTitle);
|
||||
$tw.perf.report("mainRender",function() {
|
||||
$tw.pageWidgetNode = $tw.wiki.makeWidget(parser,{document: document, parentWidget: $tw.rootWidget});
|
||||
$tw.pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.pageContainer,"tw-page-container-wrapper");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
$tw.pageWidgetNode.render($tw.pageContainer,null);
|
||||
})();
|
||||
// Prepare refresh mechanism
|
||||
var deferredChanges = Object.create(null),
|
||||
timerId;
|
||||
function refresh() {
|
||||
// Process the refresh
|
||||
$tw.pageWidgetNode.refresh(deferredChanges,$tw.pageContainer,null);
|
||||
deferredChanges = Object.create(null);
|
||||
}
|
||||
// Add the change event handler
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {
|
||||
// Check if only drafts have changed
|
||||
var onlyDraftsHaveChanged = true;
|
||||
for(var title in changes) {
|
||||
var tiddler = $tw.wiki.getTiddler(title);
|
||||
if(!tiddler || !tiddler.hasField("draft.of")) {
|
||||
onlyDraftsHaveChanged = false;
|
||||
}
|
||||
}
|
||||
// Defer the change if only drafts have changed
|
||||
if(timerId) {
|
||||
clearTimeout(timerId);
|
||||
}
|
||||
timerId = null;
|
||||
if(onlyDraftsHaveChanged) {
|
||||
timerId = setTimeout(refresh,DRAFT_TIDDLER_TIMEOUT);
|
||||
$tw.utils.extend(deferredChanges,changes);
|
||||
} else {
|
||||
$tw.utils.extend(deferredChanges,changes);
|
||||
refresh();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
37
core/modules/startup/commands.js
Normal file
37
core/modules/startup/commands.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/commands.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Command processing
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "commands";
|
||||
exports.platforms = ["node"];
|
||||
exports.after = ["story"];
|
||||
exports.synchronous = false;
|
||||
|
||||
exports.startup = function(callback) {
|
||||
// On the server, start a commander with the command line arguments
|
||||
var commander = new $tw.Commander(
|
||||
$tw.boot.argv,
|
||||
function(err) {
|
||||
if(err) {
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
callback();
|
||||
},
|
||||
$tw.wiki,
|
||||
{output: process.stdout, error: process.stderr}
|
||||
);
|
||||
commander.execute();
|
||||
};
|
||||
|
||||
})();
|
||||
43
core/modules/startup/favicon.js
Normal file
43
core/modules/startup/favicon.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/favicon.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Favicon handling
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "favicon";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
// Favicon tiddler
|
||||
var FAVICON_TITLE = "$:/favicon.ico";
|
||||
|
||||
exports.startup = function() {
|
||||
// Set up the favicon
|
||||
setFavicon();
|
||||
// Reset the favicon when the tiddler changes
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.utils.hop(changes,FAVICON_TITLE)) {
|
||||
setFavicon();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function setFavicon() {
|
||||
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
|
||||
if(tiddler) {
|
||||
var faviconLink = document.getElementById("faviconLink");
|
||||
faviconLink.setAttribute("href","data:" + tiddler.fields.type + ";base64," + tiddler.fields.text);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
36
core/modules/startup/load-modules.js
Normal file
36
core/modules/startup/load-modules.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/load-modules.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Load core modules
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "load-modules";
|
||||
exports.synchronous = true;
|
||||
|
||||
exports.startup = function() {
|
||||
// Load modules
|
||||
$tw.modules.applyMethods("utils",$tw.utils);
|
||||
if($tw.node && !$tw.browser) {
|
||||
$tw.modules.applyMethods("utils-node",$tw.utils);
|
||||
}
|
||||
$tw.modules.applyMethods("global",$tw);
|
||||
$tw.modules.applyMethods("config",$tw.config);
|
||||
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
|
||||
$tw.modules.applyMethods("tiddlermethod",$tw.Tiddler.prototype);
|
||||
$tw.modules.applyMethods("wikimethod",$tw.Wiki.prototype);
|
||||
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
|
||||
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
|
||||
$tw.wiki.initParsers();
|
||||
$tw.Commander.initCommands();
|
||||
};
|
||||
|
||||
})();
|
||||
47
core/modules/startup/password.js
Normal file
47
core/modules/startup/password.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/password.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Password handling
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "password";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["rootwidget"];
|
||||
exports.synchronous = true;
|
||||
|
||||
exports.startup = function() {
|
||||
$tw.rootWidget.addEventListener("tw-set-password",function(event) {
|
||||
$tw.passwordPrompt.createPrompt({
|
||||
serviceName: "Set a new password for this TiddlyWiki",
|
||||
noUserName: true,
|
||||
submitText: "Set password",
|
||||
canCancel: true,
|
||||
callback: function(data) {
|
||||
if(data) {
|
||||
$tw.crypto.setPassword(data.password);
|
||||
}
|
||||
return true; // Get rid of the password prompt
|
||||
}
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-clear-password",function(event) {
|
||||
$tw.crypto.setPassword(null);
|
||||
});
|
||||
// Ensure that $:/isEncrypted is maintained properly
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.utils.hop(changes,"$:/isEncrypted")) {
|
||||
$tw.crypto.updateCryptoStateTiddler();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
96
core/modules/startup/render.js
Normal file
96
core/modules/startup/render.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/render.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Title, stylesheet and page rendering
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "render";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["story"];
|
||||
exports.synchronous = true;
|
||||
|
||||
// Default story and history lists
|
||||
var PAGE_TITLE_TITLE = "$:/core/wiki/title"
|
||||
var PAGE_STYLESHEET_TITLE = "$:/core/ui/PageStylesheet";
|
||||
var PAGE_TEMPLATE_TITLE = "$:/core/ui/PageMacros";
|
||||
|
||||
// Time (in ms) that we defer refreshing changes to draft tiddlers
|
||||
var DRAFT_TIDDLER_TIMEOUT = 400;
|
||||
|
||||
exports.startup = function() {
|
||||
// Set up the title
|
||||
$tw.titleWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TITLE_TITLE,{document: $tw.fakeDocument, parseAsInline: true});
|
||||
$tw.titleContainer = $tw.fakeDocument.createElement("div");
|
||||
$tw.titleWidgetNode.render($tw.titleContainer,null);
|
||||
document.title = $tw.titleContainer.textContent;
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.titleWidgetNode.refresh(changes,$tw.titleContainer,null)) {
|
||||
document.title = $tw.titleContainer.textContent;
|
||||
}
|
||||
});
|
||||
// Set up the styles
|
||||
$tw.styleWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_STYLESHEET_TITLE,{document: $tw.fakeDocument});
|
||||
$tw.styleContainer = $tw.fakeDocument.createElement("style");
|
||||
$tw.styleWidgetNode.render($tw.styleContainer,null);
|
||||
$tw.styleElement = document.createElement("style");
|
||||
$tw.styleElement.innerHTML = $tw.styleContainer.textContent;
|
||||
document.head.insertBefore($tw.styleElement,document.head.firstChild);
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("styleRefresh",function(changes) {
|
||||
if($tw.styleWidgetNode.refresh(changes,$tw.styleContainer,null)) {
|
||||
$tw.styleElement.innerHTML = $tw.styleContainer.textContent;
|
||||
}
|
||||
}));
|
||||
// Display the $:/core/ui/PageMacros tiddler to kick off the display
|
||||
$tw.perf.report("mainRender",function() {
|
||||
$tw.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget});
|
||||
$tw.pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.pageContainer,"tw-page-container-wrapper");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
$tw.pageWidgetNode.render($tw.pageContainer,null);
|
||||
})();
|
||||
// Prepare refresh mechanism
|
||||
var deferredChanges = Object.create(null),
|
||||
timerId;
|
||||
function refresh() {
|
||||
// Process the refresh
|
||||
$tw.pageWidgetNode.refresh(deferredChanges,$tw.pageContainer,null);
|
||||
deferredChanges = Object.create(null);
|
||||
}
|
||||
// Add the change event handler
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {
|
||||
// Check if only drafts have changed
|
||||
var onlyDraftsHaveChanged = true;
|
||||
for(var title in changes) {
|
||||
var tiddler = $tw.wiki.getTiddler(title);
|
||||
if(!tiddler || !tiddler.hasField("draft.of")) {
|
||||
onlyDraftsHaveChanged = false;
|
||||
}
|
||||
}
|
||||
// Defer the change if only drafts have changed
|
||||
if(timerId) {
|
||||
clearTimeout(timerId);
|
||||
}
|
||||
timerId = null;
|
||||
if(onlyDraftsHaveChanged) {
|
||||
timerId = setTimeout(refresh,DRAFT_TIDDLER_TIMEOUT);
|
||||
$tw.utils.extend(deferredChanges,changes);
|
||||
} else {
|
||||
$tw.utils.extend(deferredChanges,changes);
|
||||
refresh();
|
||||
}
|
||||
}));
|
||||
// Fix up the link between the root widget and the page container
|
||||
$tw.rootWidget.domNodes = [$tw.pageContainer];
|
||||
$tw.rootWidget.children = [$tw.pageWidgetNode];
|
||||
};
|
||||
|
||||
})();
|
||||
78
core/modules/startup/rootwidget.js
Normal file
78
core/modules/startup/rootwidget.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/rootwidget.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Setup the root widget and the core root widget handlers
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "rootwidget";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["load-modules"];
|
||||
exports.before = ["story"];
|
||||
exports.synchronous = true;
|
||||
|
||||
var widget = require("$:/core/modules/widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||
$tw.rootWidget = new widget.widget({
|
||||
type: "widget",
|
||||
children: []
|
||||
},{
|
||||
wiki: $tw.wiki,
|
||||
document: document
|
||||
});
|
||||
// Install the modal message mechanism
|
||||
$tw.modal = new $tw.utils.Modal($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tw-modal",function(event) {
|
||||
$tw.modal.display(event.param);
|
||||
});
|
||||
// Install the notification mechanism
|
||||
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tw-notify",function(event) {
|
||||
$tw.notifier.display(event.param);
|
||||
});
|
||||
// Install the scroller
|
||||
$tw.pageScroller = new $tw.utils.PageScroller();
|
||||
$tw.rootWidget.addEventListener("tw-scroll",function(event) {
|
||||
$tw.pageScroller.handleEvent(event);
|
||||
});
|
||||
// Install the save action handlers
|
||||
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
method: "autosave",
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-download-file",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
method: "download",
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
// If we're being viewed on a data: URI then give instructions for how to save
|
||||
if(document.location.protocol === "data:") {
|
||||
$tw.rootWidget.dispatchEvent({
|
||||
type: "tw-modal",
|
||||
param: "$:/language/Modals/SaveInstructions"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
137
core/modules/startup/story.js
Normal file
137
core/modules/startup/story.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/story.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Load core modules
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "story";
|
||||
exports.after = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
// Default story and history lists
|
||||
var DEFAULT_STORY_TITLE = "$:/StoryList";
|
||||
var DEFAULT_HISTORY_TITLE = "$:/HistoryList";
|
||||
|
||||
// Default tiddlers
|
||||
var DEFAULT_TIDDLERS_TITLE = "$:/DefaultTiddlers";
|
||||
|
||||
// Config
|
||||
var CONFIG_UPDATE_ADDRESS_BAR = "$:/config/Navigation/UpdateAddressBar"; // Can be "no", "permalink", "permaview"
|
||||
var CONFIG_UPDATE_HISTORY = "$:/config/Navigation/UpdateHistory"; // Can be "yes" or "no"
|
||||
|
||||
exports.startup = function() {
|
||||
// Open startup tiddlers
|
||||
openStartupTiddlers();
|
||||
if($tw.browser) {
|
||||
// Set up location hash update
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
if($tw.utils.hop(changes,DEFAULT_STORY_TITLE) || $tw.utils.hop(changes,DEFAULT_HISTORY_TITLE)) {
|
||||
updateLocationHash();
|
||||
}
|
||||
});
|
||||
// Listen for changes to the browser location hash
|
||||
window.addEventListener("hashchange",function() {
|
||||
var hash = $tw.utils.getLocationHash();
|
||||
if(hash !== $tw.locationHash) {
|
||||
$tw.locationHash = hash;
|
||||
openStartupTiddlers({defaultToCurrentStory: true});
|
||||
}
|
||||
},false)
|
||||
// Listen for the tw-home message
|
||||
$tw.rootWidget.addEventListener("tw-home",function(event) {
|
||||
var storyFilter = $tw.wiki.getTiddlerText(DEFAULT_TIDDLERS_TITLE),
|
||||
storyList = $tw.wiki.filterTiddlers(storyFilter);
|
||||
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Process the location hash to open the specified tiddlers. Options:
|
||||
defaultToCurrentStory: If true, the current story is retained as the default, instead of opening the default tiddlers
|
||||
*/
|
||||
function openStartupTiddlers(options) {
|
||||
options = options || {};
|
||||
// Decode the hash portion of our URL
|
||||
var target,
|
||||
storyFilter;
|
||||
if($tw.locationHash.length > 1) {
|
||||
var hash = $tw.locationHash.substr(1),
|
||||
split = hash.indexOf(":");
|
||||
if(split === -1) {
|
||||
target = decodeURIComponent(hash.trim());
|
||||
} else {
|
||||
target = decodeURIComponent(hash.substr(0,split).trim());
|
||||
storyFilter = decodeURIComponent(hash.substr(split + 1).trim());
|
||||
}
|
||||
}
|
||||
// If a target tiddler was specified add it to the history stack
|
||||
if(target && target !== "") {
|
||||
// The target tiddler doesn't need double square brackets, but we'll silently remove them if they're present
|
||||
if(target.indexOf("[[") === 0 && target.substr(-2) === "]]") {
|
||||
target = target.substr(2,target.length - 4);
|
||||
}
|
||||
$tw.wiki.addToHistory(target);
|
||||
}
|
||||
// Use the story filter specified in the hash, or the default tiddlers
|
||||
if(!storyFilter || storyFilter === "") {
|
||||
if(options.defaultToCurrentStory) {
|
||||
var currStoryList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE);
|
||||
storyFilter = $tw.utils.stringifyList(currStoryList);
|
||||
} else {
|
||||
storyFilter = $tw.wiki.getTiddlerText(DEFAULT_TIDDLERS_TITLE);
|
||||
}
|
||||
}
|
||||
var storyList = $tw.wiki.filterTiddlers(storyFilter);
|
||||
// If the target tiddler isn't included then splice it in at the top
|
||||
if(target && storyList.indexOf(target) === -1) {
|
||||
storyList.unshift(target);
|
||||
}
|
||||
// Save the story list
|
||||
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
|
||||
}
|
||||
|
||||
function updateLocationHash() {
|
||||
var updateAddressBar = $tw.wiki.getTiddlerText(CONFIG_UPDATE_ADDRESS_BAR,"permaview").trim();
|
||||
if(updateAddressBar !== "no") {
|
||||
// Get the story and the history stack
|
||||
var storyList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE),
|
||||
historyList = $tw.wiki.getTiddlerData(DEFAULT_HISTORY_TITLE,[]);
|
||||
var targetTiddler = "";
|
||||
// The target tiddler is the one at the top of the stack
|
||||
if(historyList.length > 0) {
|
||||
targetTiddler = historyList[historyList.length-1].title;
|
||||
}
|
||||
// Blank the target tiddler if it isn't present in the story
|
||||
if(storyList.indexOf(targetTiddler) === -1) {
|
||||
targetTiddler = "";
|
||||
}
|
||||
// Assemble the location hash
|
||||
if(updateAddressBar === "permalink") {
|
||||
$tw.locationHash = "#" + encodeURIComponent(targetTiddler)
|
||||
} else {
|
||||
$tw.locationHash = "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList));
|
||||
}
|
||||
// Only change the location hash if we must, thus avoiding unnecessary onhashchange events
|
||||
if($tw.utils.getLocationHash() !== $tw.locationHash) {
|
||||
if($tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim() === "yes") {
|
||||
// Assign the location hash so that history is updated
|
||||
window.location.hash = $tw.locationHash;
|
||||
} else {
|
||||
// We use replace so that browser history isn't affected
|
||||
window.location.replace(window.location.toString().split("#")[0] + $tw.locationHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
34
core/modules/startup/syncer-browser.js
Normal file
34
core/modules/startup/syncer-browser.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/syncer-browser.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Startup handling
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "syncer-browser";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["rootwidget"];
|
||||
exports.synchronous = true;
|
||||
|
||||
exports.startup = function() {
|
||||
// Listen out for login/logout/refresh events in the browser
|
||||
$tw.rootWidget.addEventListener("tw-login",function() {
|
||||
$tw.syncer.handleLoginEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-logout",function() {
|
||||
$tw.syncer.handleLogoutEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-server-refresh",function() {
|
||||
$tw.syncer.handleRefreshEvent();
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -15,10 +15,16 @@ Zooms between individual tiddlers
|
||||
var ZoominListView = function(listWidget) {
|
||||
var self = this;
|
||||
this.listWidget = listWidget;
|
||||
// Make all the tiddlers position absolute, and hide all but the first one
|
||||
// Get the index of the tiddler that is at the top of the history
|
||||
var history = this.listWidget.wiki.getTiddlerData(this.listWidget.historyTitle,[]),
|
||||
targetTiddler;
|
||||
if(history.length > 0) {
|
||||
targetTiddler = history[history.length-1].title;
|
||||
}
|
||||
// Make all the tiddlers position absolute, and hide all but the top (or first) one
|
||||
$tw.utils.each(this.listWidget.children,function(itemWidget,index) {
|
||||
var domNode = itemWidget.findFirstDomNode();
|
||||
if(index) {
|
||||
if(targetTiddler !== itemWidget.parseTreeNode.itemTitle || (!targetTiddler && index)) {
|
||||
domNode.style.display = "none";
|
||||
} else {
|
||||
self.currentTiddlerDomNode = domNode;
|
||||
|
||||
@@ -20,6 +20,10 @@ exports.isPlugin = function() {
|
||||
return this.fields.type === "application/json" && this.hasField("plugin-type");
|
||||
}
|
||||
|
||||
exports.isDraft = function() {
|
||||
return this.hasField("draft.of");
|
||||
};
|
||||
|
||||
exports.getFieldString = function(field) {
|
||||
var value = this.fields[field];
|
||||
// Check for a missing field
|
||||
|
||||
@@ -130,9 +130,4 @@ exports.convertEventName = function(eventName) {
|
||||
return newEventName;
|
||||
};
|
||||
|
||||
// Setup constants for the current browser
|
||||
exports.getBrowserInfo = function(info) {
|
||||
info.isIE = (/msie|trident/i.test(navigator.userAgent));
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -157,17 +157,5 @@ exports.addEventListeners = function(domNode,events) {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Construct and dispatch a custom event
|
||||
*/
|
||||
exports.dispatchCustomEvent = function(target,name,members) {
|
||||
var event = document.createEvent("Event");
|
||||
event.initEvent(name,true,true);
|
||||
$tw.utils.each(members,function(member,name) {
|
||||
event[name] = member;
|
||||
});
|
||||
target.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
})();
|
||||
|
||||
@@ -80,8 +80,7 @@ Modal.prototype.display = function(title,options) {
|
||||
headerWidgetNode.refresh(changes,modalHeader,null);
|
||||
});
|
||||
// Render the body of the message
|
||||
var bodyParser = this.wiki.parseTiddler(title),
|
||||
bodyWidgetNode = this.wiki.makeWidget(bodyParser,{parentWidget: $tw.rootWidget, document: document});
|
||||
var bodyWidgetNode = this.wiki.makeTranscludeWidget(title,{parentWidget: $tw.rootWidget, document: document});
|
||||
bodyWidgetNode.render(modalBody,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
bodyWidgetNode.refresh(changes,modalBody,null);
|
||||
|
||||
@@ -37,8 +37,7 @@ Notifier.prototype.display = function(title,options) {
|
||||
// Add classes
|
||||
$tw.utils.addClass(notification,"tw-notification");
|
||||
// Render the body of the notification
|
||||
var parser = this.wiki.parseTiddler(title),
|
||||
widgetNode = this.wiki.makeWidget(parser,{parentWidget: $tw.rootWidget, document: document});
|
||||
var widgetNode = this.wiki.makeTranscludeWidget(title,{parentWidget: $tw.rootWidget, document: document});
|
||||
widgetNode.render(notification,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
widgetNode.refresh(changes,notification,null);
|
||||
|
||||
@@ -87,9 +87,13 @@ exports.removeTrailingSeparator = function(dirPath) {
|
||||
Recursively create a directory
|
||||
*/
|
||||
exports.createDirectory = function(dirPath) {
|
||||
var parts = dirPath.split(path.sep);
|
||||
for(var component=0; component<parts.length; component++) {
|
||||
var subDirPath = parts.slice(0,component+1).join(path.sep);
|
||||
if(dirPath.substr(dirPath.length-1,1) !== path.sep) {
|
||||
dirPath = dirPath + path.sep;
|
||||
}
|
||||
var pos = 1;
|
||||
pos = dirPath.indexOf(path.sep,pos);
|
||||
while(pos !== -1) {
|
||||
var subDirPath = dirPath.substr(0,pos);
|
||||
if(!$tw.utils.isDirectory(subDirPath)) {
|
||||
try {
|
||||
fs.mkdirSync(subDirPath);
|
||||
@@ -97,6 +101,33 @@ exports.createDirectory = function(dirPath) {
|
||||
return "Error creating directory '" + subDirPath + "'";
|
||||
}
|
||||
}
|
||||
pos = dirPath.indexOf(path.sep,pos + 1);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Recursively create directories needed to contain a specified file
|
||||
*/
|
||||
exports.createFileDirectories = function(filePath) {
|
||||
return $tw.utils.createDirectory(path.dirname(filePath));
|
||||
};
|
||||
|
||||
/*
|
||||
Recursively delete a directory
|
||||
*/
|
||||
exports.deleteDirectory = function(dirPath) {
|
||||
if(fs.existsSync(dirPath)) {
|
||||
var entries = fs.readdirSync(dirPath);
|
||||
for(var entryIndex=0; entryIndex<entries.length; entryIndex++) {
|
||||
var currPath = dirPath + path.sep + entries[entryIndex];
|
||||
if(fs.lstatSync(currPath).isDirectory()) {
|
||||
$tw.utils.deleteDirectory(currPath);
|
||||
} else {
|
||||
fs.unlinkSync(currPath);
|
||||
}
|
||||
}
|
||||
fs.rmdirSync(dirPath);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -13,14 +13,12 @@ Parse tree utility functions.
|
||||
"use strict";
|
||||
|
||||
exports.addAttributeToParseTreeNode = function(node,name,value) {
|
||||
if(node.type === "element") {
|
||||
node.attributes = node.attributes || {};
|
||||
node.attributes[name] = {type: "string", value: value};
|
||||
}
|
||||
node.attributes = node.attributes || {};
|
||||
node.attributes[name] = {type: "string", value: value};
|
||||
};
|
||||
|
||||
exports.getAttributeValueFromParseTreeNode = function(node,name,defaultValue) {
|
||||
if(node.type === "element" && node.attributes && node.attributes[name] && node.attributes[name].value !== undefined) {
|
||||
if(node.attributes && node.attributes[name] && node.attributes[name].value !== undefined) {
|
||||
return node.attributes[name].value;
|
||||
}
|
||||
return defaultValue;
|
||||
@@ -28,29 +26,25 @@ exports.getAttributeValueFromParseTreeNode = function(node,name,defaultValue) {
|
||||
|
||||
exports.addClassToParseTreeNode = function(node,classString) {
|
||||
var classes = [];
|
||||
if(node.type === "element") {
|
||||
node.attributes = node.attributes || {};
|
||||
node.attributes["class"] = node.attributes["class"] || {type: "string", value: ""};
|
||||
if(node.attributes["class"].type === "string") {
|
||||
if(node.attributes["class"].value !== "") {
|
||||
classes = node.attributes["class"].value.split(" ");
|
||||
}
|
||||
if(classString !== "") {
|
||||
$tw.utils.pushTop(classes,classString.split(" "));
|
||||
}
|
||||
node.attributes["class"].value = classes.join(" ");
|
||||
node.attributes = node.attributes || {};
|
||||
node.attributes["class"] = node.attributes["class"] || {type: "string", value: ""};
|
||||
if(node.attributes["class"].type === "string") {
|
||||
if(node.attributes["class"].value !== "") {
|
||||
classes = node.attributes["class"].value.split(" ");
|
||||
}
|
||||
if(classString !== "") {
|
||||
$tw.utils.pushTop(classes,classString.split(" "));
|
||||
}
|
||||
node.attributes["class"].value = classes.join(" ");
|
||||
}
|
||||
};
|
||||
|
||||
exports.addStyleToParseTreeNode = function(node,name,value) {
|
||||
if(node.type === "element") {
|
||||
node.attributes = node.attributes || {};
|
||||
node.attributes["style"] = node.attributes["style"] || {type: "string", value: ""};
|
||||
if(node.attributes["style"].type === "string") {
|
||||
node.attributes["style"].value += name + ":" + value + ";";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.findParseTreeNode = function(nodeArray,search) {
|
||||
|
||||
@@ -33,6 +33,23 @@ exports.count = function(object) {
|
||||
return s;
|
||||
};
|
||||
|
||||
/*
|
||||
Check if an array is equal by value and by reference.
|
||||
*/
|
||||
exports.isArrayEqual = function(array1,array2) {
|
||||
if(array1 === array2) {
|
||||
return true;
|
||||
}
|
||||
array1 = array1 || [];
|
||||
array2 = array2 || [];
|
||||
if(array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
return array1.every(function(value,index) {
|
||||
return value === array2[index];
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Push entries onto an array, removing them first if they already exist in the array
|
||||
array: array to modify (assumed to be free of duplicates)
|
||||
@@ -396,6 +413,18 @@ exports.parseTextReference = function(textRef) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Checks whether a string is a valid fieldname
|
||||
*/
|
||||
exports.isValidFieldName = function(name) {
|
||||
if(!name || typeof name !== "string") {
|
||||
return false;
|
||||
}
|
||||
name = name.toLowerCase().trim();
|
||||
var fieldValidatorRegEx = /^[a-z0-9\-\._]+$/mg;
|
||||
return fieldValidatorRegEx.test(name);
|
||||
};
|
||||
|
||||
/*
|
||||
Extract the version number from the meta tag or from the boot file
|
||||
*/
|
||||
@@ -481,6 +510,6 @@ exports.timer = function(base) {
|
||||
m = m - base;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -43,7 +43,7 @@ CountWidget.prototype.execute = function() {
|
||||
this.filter = this.getAttribute("filter");
|
||||
// Execute the filter
|
||||
if(this.filter) {
|
||||
this.currentCount = this.wiki.filterTiddlers(this.filter,this.getVariable("currentTiddler")).length;
|
||||
this.currentCount = this.wiki.filterTiddlers(this.filter,this).length;
|
||||
} else {
|
||||
this.currentCount = undefined;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ DropZoneWidget.prototype.handleDragEnterEvent = function(event) {
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDragOverEvent = function(event) {
|
||||
// Check for being over a TEXTAREA or INPUT
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
// Tell the browser that we're still interested in the drop
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "copy"; // Explicitly show this is a copy
|
||||
@@ -81,6 +85,10 @@ DropZoneWidget.prototype.handleDragLeaveEvent = function(event) {
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDropEvent = function(event) {
|
||||
// Check for being over a TEXTAREA or INPUT
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
var self = this,
|
||||
dataTransfer = event.dataTransfer;
|
||||
// Reset the enter count
|
||||
|
||||
64
core/modules/widgets/edit-binary.js
Normal file
64
core/modules/widgets/edit-binary.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/edit-binary.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Edit-binary widget; placeholder for editing binary tiddlers
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var EditBinaryWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EditBinaryWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EditBinaryWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EditBinaryWidget.prototype.execute = function() {
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets([{
|
||||
type: "transclude",
|
||||
attributes: {
|
||||
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
||||
}
|
||||
}]);
|
||||
};
|
||||
|
||||
/*
|
||||
Refresh by refreshing our child widget
|
||||
*/
|
||||
EditBinaryWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports["edit-binary"] = EditBinaryWidget;
|
||||
|
||||
})();
|
||||
@@ -45,6 +45,9 @@ EditTextWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(editInfo.value === "" && this.editPlaceholder) {
|
||||
domNode.setAttribute("placeholder",this.editPlaceholder);
|
||||
}
|
||||
if(this.editSize) {
|
||||
domNode.setAttribute("size",this.editSize);
|
||||
}
|
||||
// Assign classes
|
||||
if(this.editClass) {
|
||||
domNode.className = this.editClass;
|
||||
@@ -133,6 +136,7 @@ EditTextWidget.prototype.execute = function() {
|
||||
this.editDefault = this.getAttribute("default");
|
||||
this.editClass = this.getAttribute("class");
|
||||
this.editPlaceholder = this.getAttribute("placeholder");
|
||||
this.editSize = this.getAttribute("size");
|
||||
this.editFocusPopup = this.getAttribute("focusPopup");
|
||||
// Get the editor element tag and type
|
||||
var tag,type;
|
||||
|
||||
@@ -56,7 +56,15 @@ EditWidget.prototype.execute = function() {
|
||||
}
|
||||
type = type || "text/vnd.tiddlywiki";
|
||||
// Choose the appropriate edit widget
|
||||
var editorType = this.wiki.getTiddlerText(EDITOR_MAPPING_PREFIX + type) || "text";
|
||||
var editorType = this.wiki.getTiddlerText(EDITOR_MAPPING_PREFIX + type);
|
||||
if(!editorType) {
|
||||
var typeInfo = $tw.config.contentTypeInfo[type];
|
||||
if(typeInfo && typeInfo.encoding === "base64") {
|
||||
editorType = "binary";
|
||||
} else {
|
||||
editorType = "text";
|
||||
}
|
||||
}
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets([{
|
||||
type: "edit-" + editorType,
|
||||
|
||||
@@ -60,14 +60,8 @@ EncryptWidget.prototype.execute = function() {
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EncryptWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
affectedTiddlers = this.wiki.filterTiddlers(this.filter,null,changedTiddlers);
|
||||
if(changedAttributes.filter || affectedTiddlers.length > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// We don't need to worry about refreshing because the encrypt widget isn't for interactive use
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.encrypt = EncryptWidget;
|
||||
|
||||
@@ -71,12 +71,11 @@ FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle),
|
||||
fieldValidatorRegEx = /^[a-z\-\._]+$/mg;
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle);
|
||||
if(tiddler && typeof event.param === "string") {
|
||||
var name = event.param.toLowerCase().trim();
|
||||
if(name !== "" && !$tw.utils.hop(tiddler.fields,name)) {
|
||||
if(!fieldValidatorRegEx.test(name)) {
|
||||
if(!$tw.utils.isValidFieldName(name)) {
|
||||
alert($tw.language.getString(
|
||||
"InvalidFieldName",
|
||||
{variables:
|
||||
|
||||
123
core/modules/widgets/image.js
Normal file
123
core/modules/widgets/image.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/image.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The image widget displays an image referenced with an external URI or with a local tiddler title.
|
||||
|
||||
```
|
||||
<$image src="TiddlerTitle" width="320" height="400" class="classnames">
|
||||
```
|
||||
|
||||
The image source can be the title of an existing tiddler or the URL of an external image.
|
||||
|
||||
External images always generate an HTML `<img>` tag.
|
||||
|
||||
Tiddlers that have a _canonical_uri field generate an HTML `<img>` tag with the src attribute containing the URI.
|
||||
|
||||
Tiddlers that contain image data generate an HTML `<img>` tag with the src attribute containing a base64 representation of the image.
|
||||
|
||||
Tiddlers that contain wikitext could be rendered to a DIV of the usual size of a tiddler, and then transformed to the size requested.
|
||||
|
||||
The width and height attributes are interpreted as a number of pixels, and do not need to include the "px" suffix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ImageWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ImageWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ImageWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
// Determine what type of image it is
|
||||
var tag = "img", src = "",
|
||||
tiddler = this.wiki.getTiddler(this.imageSource);
|
||||
if(!tiddler) {
|
||||
// The source isn't the title of a tiddler, so we'll assume it's a URL
|
||||
src = this.imageSource;
|
||||
} else {
|
||||
// Check if it is an image tiddler
|
||||
if(this.wiki.isImageTiddler(this.imageSource)) {
|
||||
// Render the appropriate element for the image type
|
||||
var type = tiddler.fields.type,
|
||||
text = tiddler.fields.text;
|
||||
switch(type) {
|
||||
case "application/pdf":
|
||||
tag = "embed";
|
||||
src = "data:application/pdf;base64," + text;
|
||||
break;
|
||||
case "image/svg+xml":
|
||||
src = "data:image/svg+xml," + encodeURIComponent(text);
|
||||
break;
|
||||
default:
|
||||
src = "data:" + type + ";base64," + text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Create the element and assign the attributes
|
||||
var domNode = this.document.createElement(tag);
|
||||
domNode.setAttribute("src",src);
|
||||
if(this.imageClass) {
|
||||
domNode.setAttribute("class",this.imageClass);
|
||||
}
|
||||
if(this.imageWidth) {
|
||||
domNode.setAttribute("width",parseInt(this.imageWidth,10) + "px");
|
||||
}
|
||||
if(this.imageHeight) {
|
||||
domNode.setAttribute("height",parseInt(this.imageHeight,10) + "px");
|
||||
}
|
||||
if(this.imageTooltip) {
|
||||
domNode.setAttribute("title",this.imageTooltip);
|
||||
}
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ImageWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.imageSource = this.getAttribute("source");
|
||||
this.imageWidth = this.getAttribute("width");
|
||||
this.imageHeight = this.getAttribute("height");
|
||||
this.imageClass = this.getAttribute("class");
|
||||
this.imageTooltip = this.getAttribute("tooltip");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ImageWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.image = ImageWidget;
|
||||
|
||||
})();
|
||||
@@ -80,7 +80,7 @@ ListWidget.prototype.execute = function() {
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var defaultFilter = "[!is[system]sort[title]]";
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this.getVariable("currentTiddler"));
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
|
||||
@@ -133,16 +133,7 @@ title: a title string or an array of title strings
|
||||
fromPageRect: page coordinates of the origin of the navigation
|
||||
*/
|
||||
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect) {
|
||||
var titles = $tw.utils.isArray(title) ? title : [title];
|
||||
// Add a new record to the top of the history stack
|
||||
if(this.historyTitle) {
|
||||
var historyList = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
$tw.utils.each(titles,function(title) {
|
||||
historyList.push({title: title, fromPageRect: fromPageRect});
|
||||
});
|
||||
this.wiki.setTiddlerData(this.historyTitle,historyList,{"current-tiddler": titles[titles.length-1]});
|
||||
this.wiki.addTiddler(new $tw.Tiddler());
|
||||
}
|
||||
this.wiki.addToHistory(title,fromPageRect,this.historyTitle);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -181,9 +172,29 @@ NavigatorWidget.prototype.handleCloseOtherTiddlersEvent = function(event) {
|
||||
|
||||
// Place a tiddler in edit mode
|
||||
NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
|
||||
var self = this;
|
||||
function isUnmodifiedShadow(title) {
|
||||
// jshint eqnull:true
|
||||
var tiddler = self.wiki.getTiddler(title);
|
||||
return (
|
||||
self.wiki.isShadowTiddler(title) &&
|
||||
tiddler.fields.modified == null
|
||||
);
|
||||
}
|
||||
function confirmEditShadow(title) {
|
||||
return confirm($tw.language.getString(
|
||||
"ConfirmEditShadowTiddler",
|
||||
{variables:
|
||||
{title: title}
|
||||
}
|
||||
));
|
||||
}
|
||||
var title = event.param || event.tiddlerTitle;
|
||||
if(isUnmodifiedShadow(title) && !confirmEditShadow(title)) {
|
||||
return false;
|
||||
}
|
||||
// Replace the specified tiddler with a draft in edit mode
|
||||
var title = event.param || event.tiddlerTitle,
|
||||
draftTiddler = this.makeDraftTiddler(title),
|
||||
var draftTiddler = this.makeDraftTiddler(title),
|
||||
draftTitle = draftTiddler.fields.title,
|
||||
storyList = this.getStoryList();
|
||||
this.removeTitleFromStory(storyList,draftTitle);
|
||||
@@ -281,8 +292,7 @@ NavigatorWidget.prototype.generateDraftTitle = function(title) {
|
||||
NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
|
||||
var title = event.param || event.tiddlerTitle,
|
||||
tiddler = this.wiki.getTiddler(title),
|
||||
storyList = this.getStoryList(),
|
||||
storyTiddlerModified = false; // We have to special case saving the story tiddler itself
|
||||
storyList = this.getStoryList();
|
||||
// Replace the original tiddler with the draft
|
||||
if(tiddler) {
|
||||
var draftTitle = (tiddler.fields["draft.title"] || "").trim(),
|
||||
@@ -297,12 +307,14 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
|
||||
{title: draftTitle}
|
||||
}
|
||||
));
|
||||
}
|
||||
if(isConfirmed) {
|
||||
} else if(!this.wiki.isDraftModified(title)) {
|
||||
event.type = "tw-cancel-tiddler";
|
||||
this.dispatchEvent(event);
|
||||
} else if(isConfirmed) {
|
||||
// Save the draft tiddler as the real tiddler
|
||||
this.wiki.addTiddler(new $tw.Tiddler(this.wiki.getCreationFields(),tiddler,{
|
||||
title: draftTitle,
|
||||
"draft.title": undefined,
|
||||
"draft.title": undefined,
|
||||
"draft.of": undefined
|
||||
},this.wiki.getModificationFields()));
|
||||
// Remove the draft tiddler
|
||||
|
||||
@@ -89,17 +89,19 @@ defaultValue: default value if the variable is not defined
|
||||
*/
|
||||
Widget.prototype.getVariable = function(name,options) {
|
||||
options = options || {};
|
||||
var actualParams = options.params || [];
|
||||
// If the variable doesn't exist then look for a macro module
|
||||
if(!(name in this.variables)) {
|
||||
return this.evaluateMacroModule(name,actualParams,options.defaultValue);
|
||||
var actualParams = options.params || [],
|
||||
parentWidget = this.parentWidget;
|
||||
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
|
||||
if(parentWidget && name in parentWidget.variables) {
|
||||
var variable = parentWidget.variables[name],
|
||||
value = variable.value;
|
||||
// Substitute any parameters specified in the definition
|
||||
value = this.substituteVariableParameters(value,variable.params,actualParams);
|
||||
value = this.substituteVariableReferences(value);
|
||||
return value;
|
||||
}
|
||||
var variable = this.variables[name],
|
||||
value = variable.value || "";
|
||||
// Substitute any parameters specified in the definition
|
||||
value = this.substituteVariableParameters(value,variable.params,actualParams);
|
||||
value = this.substituteVariableReferences(value);
|
||||
return value;
|
||||
// If the variable doesn't exist in the parent widget then look for a macro module
|
||||
return this.evaluateMacroModule(name,actualParams,options.defaultValue);
|
||||
};
|
||||
|
||||
Widget.prototype.substituteVariableParameters = function(text,formalParams,actualParams) {
|
||||
@@ -199,8 +201,8 @@ Widget.prototype.getStateQualifier = function(name) {
|
||||
name = name || "transclusion";
|
||||
var output = [],
|
||||
node = this;
|
||||
while(node) {
|
||||
if($tw.utils.hop(node.variables,name)) {
|
||||
while(node && node.parentWidget) {
|
||||
if($tw.utils.hop(node.parentWidget.variables,name)) {
|
||||
output.push(node.getVariable(name));
|
||||
}
|
||||
node = node.parentWidget;
|
||||
@@ -342,7 +344,7 @@ Add a list of event listeners from an array [{type:,handler:},...]
|
||||
Widget.prototype.addEventListeners = function(listeners) {
|
||||
var self = this;
|
||||
$tw.utils.each(listeners,function(listenerInfo) {
|
||||
self.addEventListener(listenerInfo.type,listenerInfo.handler);
|
||||
self.addEventListener(listenerInfo.type,listenerInfo.handler);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -458,7 +460,7 @@ Widget.prototype.findFirstDomNode = function() {
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
Widget.prototype.removeChildDomNodes = function() {
|
||||
// If this widget has directly created DOM nodes, delete them and exit. This assumes that any child widgets are contained within the created DOM nodes, which would normally be the case
|
||||
// If this widget has directly created DOM nodes, delete them and exit. This assumes that any child widgets are contained within the created DOM nodes, which would normally be the case
|
||||
if(this.domNodes.length > 0) {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
|
||||
@@ -140,12 +140,19 @@ exports.enqueueTiddlerEvent = function(title,isDeleted) {
|
||||
var changes = self.changedTiddlers;
|
||||
self.changedTiddlers = Object.create(null);
|
||||
self.eventsTriggered = false;
|
||||
self.dispatchEvent("change",changes);
|
||||
if($tw.utils.count(changes) > 0) {
|
||||
self.dispatchEvent("change",changes);
|
||||
}
|
||||
});
|
||||
this.eventsTriggered = true;
|
||||
}
|
||||
};
|
||||
|
||||
exports.clearTiddlerEventQueue = function() {
|
||||
this.changedTiddlers = Object.create(null);
|
||||
this.changeCount = Object.create(null)
|
||||
};
|
||||
|
||||
exports.getChangeCount = function(title) {
|
||||
this.changeCount = this.changeCount || Object.create(null);
|
||||
if($tw.utils.hop(this.changeCount,title)) {
|
||||
@@ -305,28 +312,20 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is
|
||||
a = self.getTiddler(a).fields[sortField] || "";
|
||||
b = self.getTiddler(b).fields[sortField] || "";
|
||||
}
|
||||
if(!isNumeric || isNaN(a) || isNaN(b)) {
|
||||
if(!isCaseSensitive) {
|
||||
if(typeof a === "string") {
|
||||
a = a.toLowerCase();
|
||||
}
|
||||
if(typeof b === "string") {
|
||||
b = b.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
a-= 0;
|
||||
b-= 0;
|
||||
}
|
||||
if(a < b) {
|
||||
return isDescending ? +1 : -1;
|
||||
if(isNumeric) {
|
||||
a = Number(a);
|
||||
b = Number(b);
|
||||
return isDescending ? b - a : a - b;
|
||||
} else if($tw.utils.isDate(a) && $tw.utils.isDate(b)) {
|
||||
return isDescending ? b - a : a - b;
|
||||
} else {
|
||||
if(a > b) {
|
||||
return isDescending ? -1 : +1;
|
||||
} else {
|
||||
return 0;
|
||||
a = String(a);
|
||||
b = String(b);
|
||||
if(!isCaseSensitive) {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
}
|
||||
return isDescending ? b.localeCompare(a) : a.localeCompare(b);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -742,19 +741,6 @@ exports.old_parseTiddler = function(title,options) {
|
||||
}) : null;
|
||||
};
|
||||
|
||||
// We need to tweak parse trees generated by the existing parser because of the change from {type:"element",tag:"$tiddler",...} to {type:"tiddler",...}
|
||||
var tweakParseTreeNode = function(node) {
|
||||
if(node.type === "element" && node.tag.charAt(0) === "$") {
|
||||
node.type = node.tag.substr(1);
|
||||
delete node.tag;
|
||||
}
|
||||
tweakParseTreeNodes(node.children);
|
||||
};
|
||||
|
||||
var tweakParseTreeNodes = function(nodeList) {
|
||||
$tw.utils.each(nodeList,tweakParseTreeNode);
|
||||
};
|
||||
|
||||
var tweakMacroDefinition = function(nodeList) {
|
||||
if(nodeList && nodeList[0] && nodeList[0].type === "macrodef") {
|
||||
nodeList[0].type = "set";
|
||||
@@ -771,8 +757,6 @@ var tweakMacroDefinition = function(nodeList) {
|
||||
var tweakParser = function(parser) {
|
||||
// Move any macro definitions to contain the body tree
|
||||
tweakMacroDefinition(parser.tree);
|
||||
// Tweak widgets
|
||||
tweakParseTreeNodes(parser.tree);
|
||||
};
|
||||
|
||||
exports.parseText = function(type,text,options) {
|
||||
@@ -859,6 +843,25 @@ exports.makeWidget = function(parser,options) {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Make a widget tree for transclusion
|
||||
title: target tiddler title
|
||||
options: as for wiki.makeWidget() (including parseAsInline)
|
||||
*/
|
||||
exports.makeTranscludeWidget = function(title,options) {
|
||||
options = options || {};
|
||||
var parseTree = {tree: [{
|
||||
type: "transclude",
|
||||
attributes: {
|
||||
tiddler: {
|
||||
name: "tiddler",
|
||||
type: "string",
|
||||
value: title}},
|
||||
isBlock: !options.parseAsInline}
|
||||
]};
|
||||
return $tw.wiki.makeWidget(parseTree,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Parse text in a specified format and render it into another format
|
||||
outputType: content type for the output
|
||||
@@ -1068,4 +1071,48 @@ exports.readFile = function(file,callback) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Check whether the specified draft tiddler has been modified
|
||||
*/
|
||||
exports.isDraftModified = function(title) {
|
||||
var tiddler = this.getTiddler(title);
|
||||
if(!tiddler.isDraft()) {
|
||||
return false;
|
||||
}
|
||||
var ignoredFields = ["created", "modified", "title", "draft.title", "draft.of", "tags"],
|
||||
origTiddler = this.getTiddler(tiddler.fields["draft.of"]);
|
||||
if(!origTiddler) {
|
||||
return true;
|
||||
}
|
||||
if(tiddler.fields["draft.title"] !== tiddler.fields["draft.of"]) {
|
||||
return true;
|
||||
}
|
||||
if(!$tw.utils.isArrayEqual(tiddler.fields.tags,origTiddler.fields.tags)) {
|
||||
return true;
|
||||
}
|
||||
return !Object.keys(tiddler.fields).every(function(field) {
|
||||
if(ignoredFields.indexOf(field) >= 0) {
|
||||
return true;
|
||||
}
|
||||
return tiddler.fields[field] === origTiddler.fields[field];
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Add a new record to the top of the history stack
|
||||
title: a title string or an array of title strings
|
||||
fromPageRect: page coordinates of the origin of the navigation
|
||||
historyTitle: title of history tiddler (defaults to $:/HistoryList)
|
||||
*/
|
||||
exports.addToHistory = function(title,fromPageRect,historyTitle) {
|
||||
historyTitle = historyTitle || "$:/HistoryList";
|
||||
var titles = $tw.utils.isArray(title) ? title : [title];
|
||||
// Add a new record to the top of the history stack
|
||||
var historyList = this.getTiddlerData(historyTitle,[]);
|
||||
$tw.utils.each(titles,function(title) {
|
||||
historyList.push({title: title, fromPageRect: fromPageRect});
|
||||
});
|
||||
this.setTiddlerData(historyTitle,historyList,{"current-tiddler": titles[titles.length-1]});
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
title: $:/AdvancedSearch
|
||||
|
||||
<div class="tw-advanced-search">
|
||||
<<tabs "[all[tiddlers+shadows]tag[$:/tags/AdvancedSearch]!has[draft.of]]" "$:/core/ui/AdvancedSearch/System">>
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch]!has[draft.of]]" "$:/core/ui/AdvancedSearch/System">>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ caption: {{$:/language/Search/Filter/Caption}}
|
||||
<div class="tw-block-dropdown-wrapper">
|
||||
<$reveal state=<<qualify "$:/state/filterDropdown">> type="nomatch" text="" default="">
|
||||
<div class="tw-block-dropdown tw-edit-type-dropdown">
|
||||
<$list filter="[all[tiddlers+shadows]tag[$:/tags/Filter]]"><$link to={{!!filter}}><$transclude field="description"/></$link>
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Filter]]"><$link to={{!!filter}}><$transclude field="description"/></$link>
|
||||
</$list>
|
||||
</div>
|
||||
</$reveal>
|
||||
@@ -22,7 +22,7 @@ caption: {{$:/language/Search/Filter/Caption}}
|
||||
|
||||
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
|
||||
<div class="tw-search-results">
|
||||
<<lingo Advanced/Matches>>
|
||||
<<lingo Filter/Matches>>
|
||||
<$list filter={{$:/temp/advancedsearch}} template="$:/core/ui/ListItemTemplate"/>
|
||||
</div>
|
||||
</$reveal>
|
||||
|
||||
@@ -15,7 +15,7 @@ caption: {{$:/language/Search/Shadows/Caption}}
|
||||
|
||||
<div class="tw-search-results">
|
||||
|
||||
<<lingo Advanced/Matches>>
|
||||
<<lingo Shadows/Matches>>
|
||||
|
||||
<$list filter="[all[shadows]search{$:/temp/advancedsearch}sort[title]limit[250]]" template="$:/core/ui/ListItemTemplate"/>
|
||||
|
||||
|
||||
28
core/ui/AdvancedSearch/Standard.tid
Normal file
28
core/ui/AdvancedSearch/Standard.tid
Normal file
@@ -0,0 +1,28 @@
|
||||
title: $:/core/ui/AdvancedSearch/Standard
|
||||
tags: $:/tags/AdvancedSearch
|
||||
caption: {{$:/language/Search/Standard/Caption}}
|
||||
|
||||
\define lingo-base() $:/language/Search/
|
||||
<$linkcatcher to="$:/temp/advancedsearch">
|
||||
|
||||
<<lingo Standard/Hint>>
|
||||
|
||||
<div class="tw-search"><$edit-text tiddler="$:/temp/advancedsearch" type="search" tag="input"/><$reveal state="$:/temp/advancedsearch" type="nomatch" text=""> <$link to="" class="btn-invisible">{{$:/core/images/close-button}}</$link></$reveal></div>
|
||||
|
||||
</$linkcatcher>
|
||||
|
||||
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
|
||||
|
||||
<div class="tw-search-results">
|
||||
|
||||
<<lingo Standard/Matches>>
|
||||
|
||||
<$list filter="[!is[system]search{$:/temp/advancedsearch}sort[title]limit[250]]" template="$:/core/ui/ListItemTemplate"/>
|
||||
|
||||
</div>
|
||||
|
||||
</$reveal>
|
||||
|
||||
<$reveal state="$:/temp/advancedsearch" type="match" text="">
|
||||
|
||||
</$reveal>
|
||||
@@ -15,7 +15,7 @@ caption: {{$:/language/Search/System/Caption}}
|
||||
|
||||
<div class="tw-search-results">
|
||||
|
||||
<<lingo Advanced/Matches>>
|
||||
<<lingo System/Matches>>
|
||||
|
||||
<$list filter="[is[system]search{$:/temp/advancedsearch}sort[title]limit[250]]" template="$:/core/ui/ListItemTemplate"/>
|
||||
|
||||
|
||||
8
core/ui/BinaryWarning.tid
Normal file
8
core/ui/BinaryWarning.tid
Normal file
@@ -0,0 +1,8 @@
|
||||
title: $:/core/ui/BinaryWarning
|
||||
|
||||
\define lingo-base() $:/language/BinaryWarning/
|
||||
<div class="tw-binary-warning">
|
||||
|
||||
<<lingo Prompt>>
|
||||
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
title: $:/ControlPanel
|
||||
|
||||
<div class="tw-control-panel">
|
||||
<<tabs "[all[tiddlers+shadows]tag[$:/tags/ControlPanel]!has[draft.of]]" "$:/core/ui/ControlPanel/Basics">>
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/ControlPanel]!has[draft.of]]" "$:/core/ui/ControlPanel/Basics">>
|
||||
</div>
|
||||
|
||||
@@ -5,5 +5,5 @@ caption: {{$:/language/ControlPanel/Advanced/Caption}}
|
||||
{{$:/language/ControlPanel/Advanced/Hint}}
|
||||
|
||||
<div class="tw-control-panel">
|
||||
<<tabs "[all[tiddlers+shadows]tag[$:/tags/ControlPanel/Advanced]!has[draft.of]]" "$:/core/ui/ControlPanel/Advanced/TiddlerFields">>
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Advanced]!has[draft.of]]" "$:/core/ui/ControlPanel/Advanced/TiddlerFields">>
|
||||
</div>
|
||||
|
||||
19
core/ui/ControlPanel/Advanced/Settings.tid
Normal file
19
core/ui/ControlPanel/Advanced/Settings.tid
Normal file
@@ -0,0 +1,19 @@
|
||||
title: $:/core/ui/ControlPanel/Advanced/Settings
|
||||
tags: $:/tags/ControlPanel/Advanced
|
||||
caption: {{$:/language/ControlPanel/Advanced/Settings/Caption}}
|
||||
|
||||
\define lingo-base() $:/language/ControlPanel/Advanced/Settings/
|
||||
|
||||
<<lingo Hint>>
|
||||
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Advanced/Settings]]">
|
||||
|
||||
<div style="border-top:1px solid #eee;">
|
||||
|
||||
!! <$link><$transclude field="caption"/></$link>
|
||||
|
||||
<$transclude/>
|
||||
|
||||
</div>
|
||||
|
||||
</$list>
|
||||
@@ -0,0 +1,30 @@
|
||||
title: $:/core/ui/ControlPanel/Advanced/Settings/NavigationAddressBar
|
||||
tags: $:/tags/ControlPanel/Advanced/Settings
|
||||
caption: {{$:/language/ControlPanel/Advanced/Settings/NavigationAddressBar/Caption}}
|
||||
|
||||
\define lingo-base() $:/language/ControlPanel/Advanced/Settings/NavigationAddressBar/
|
||||
<<lingo Hint>>
|
||||
<$button popup=<<qualify "$:/state/navaddressbarpopup">> class="btn-invisible btn-dropdown">
|
||||
<$view tiddler="$:/config/Navigation/UpdateAddressBar" field="text"/>:
|
||||
<$reveal state="$:/config/Navigation/UpdateAddressBar" type="match" text="no">
|
||||
''<<lingo No/Description>>''
|
||||
</$reveal>
|
||||
<$reveal state="$:/config/Navigation/UpdateAddressBar" type="match" text="permalink">
|
||||
''<<lingo Permalink/Description>>''
|
||||
</$reveal>
|
||||
<$reveal state="$:/config/Navigation/UpdateAddressBar" type="match" text="permaview">
|
||||
''<<lingo Permaview/Description>>''
|
||||
</$reveal>
|
||||
{{$:/core/images/down-arrow}}
|
||||
</$button>
|
||||
<$reveal state=<<qualify "$:/state/navaddressbarpopup">> type="popup" position="below" animate="yes" default="">
|
||||
<$linkcatcher to="$:/config/Navigation/UpdateAddressBar">
|
||||
<div class="tw-drop-down tw-edit-type-dropdown">
|
||||
<$list filter="no permalink permaview">
|
||||
<$link to={{!!title}}>
|
||||
<$view field="title"/>
|
||||
</$link>
|
||||
</$list>
|
||||
</div>
|
||||
</$linkcatcher>
|
||||
</$reveal>
|
||||
27
core/ui/ControlPanel/Advanced/Settings/NavigationHistory.tid
Normal file
27
core/ui/ControlPanel/Advanced/Settings/NavigationHistory.tid
Normal file
@@ -0,0 +1,27 @@
|
||||
title: $:/core/ui/ControlPanel/Advanced/Settings/NavigationHistory
|
||||
tags: $:/tags/ControlPanel/Advanced/Settings
|
||||
caption: {{$:/language/ControlPanel/Advanced/Settings/NavigationHistory/Caption}}
|
||||
|
||||
\define lingo-base() $:/language/ControlPanel/Advanced/Settings/NavigationHistory/
|
||||
<<lingo Hint>>
|
||||
<$button popup=<<qualify "$:/state/navhistorypopup">> class="btn-invisible btn-dropdown">
|
||||
<$view tiddler="$:/config/Navigation/UpdateHistory" field="text"/>:
|
||||
<$reveal state="$:/config/Navigation/UpdateHistory" type="match" text="no">
|
||||
''<<lingo No/Description>>''
|
||||
</$reveal>
|
||||
<$reveal state="$:/config/Navigation/UpdateHistory" type="match" text="yes">
|
||||
''<<lingo Yes/Description>>''
|
||||
</$reveal>
|
||||
{{$:/core/images/down-arrow}}
|
||||
</$button>
|
||||
<$reveal state=<<qualify "$:/state/navhistorypopup">> type="popup" position="below" animate="yes" default="">
|
||||
<$linkcatcher to="$:/config/Navigation/UpdateHistory">
|
||||
<div class="tw-drop-down tw-edit-type-dropdown">
|
||||
<$list filter="no yes">
|
||||
<$link to={{!!title}}>
|
||||
<$view field="title"/>
|
||||
</$link>
|
||||
</$list>
|
||||
</div>
|
||||
</$linkcatcher>
|
||||
</$reveal>
|
||||
@@ -5,5 +5,5 @@ caption: {{$:/language/ControlPanel/Appearance/Caption}}
|
||||
{{$:/language/ControlPanel/Appearance/Hint}}
|
||||
|
||||
<div class="tw-control-panel">
|
||||
<<tabs "[all[tiddlers+shadows]tag[$:/tags/ControlPanel/Appearance]!has[draft.of]]" "$:/core/ui/ControlPanel/Appearance/Theme">>
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Appearance]!has[draft.of]]" "$:/core/ui/ControlPanel/Appearance/Theme">>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ tw-tiddler-frame tw-tiddler-edit-frame $(missingTiddlerClass)$ $(shadowTiddlerCl
|
||||
<div class=<<frame-classes>>>
|
||||
<$set name="storyTiddler" value=<<currentTiddler>>>
|
||||
<$keyboard key="ctrl+enter" message="tw-save-tiddler">
|
||||
<$list filter="[all[tiddlers+shadows]tag[$:/tags/EditTemplate]!has[draft.of]]" variable="listItem">
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/EditTemplate]!has[draft.of]]" variable="listItem">
|
||||
<$transclude tiddler=<<listItem>>/>
|
||||
</$list>
|
||||
</$keyboard>
|
||||
|
||||
@@ -2,9 +2,9 @@ title: $:/core/ui/EditTemplate/body
|
||||
tags: $:/tags/EditTemplate
|
||||
|
||||
\define lingo-base() $:/language/EditTemplate/
|
||||
<$reveal state="$:/ShowEditPreview" type="match" text="yes">
|
||||
<$reveal state="$:/state/showeditpreview" type="match" text="yes">
|
||||
|
||||
<em class="tw-edit"><<lingo Body/Hint>></em> <$button type="set" set="$:/ShowEditPreview" setTo="no"><<lingo Body/Preview/Button/Hide>></$button>
|
||||
<em class="tw-edit"><<lingo Body/Hint>></em> <$button type="set" set="$:/state/showeditpreview" setTo="no"><<lingo Body/Preview/Button/Hide>></$button>
|
||||
|
||||
<div class="tw-tiddler-preview">
|
||||
<div class="tw-tiddler-preview-preview">
|
||||
@@ -22,9 +22,9 @@ tags: $:/tags/EditTemplate
|
||||
|
||||
</$reveal>
|
||||
|
||||
<$reveal state="$:/ShowEditPreview" type="nomatch" text="yes">
|
||||
<$reveal state="$:/state/showeditpreview" type="nomatch" text="yes">
|
||||
|
||||
<em class="tw-edit"><<lingo Body/Hint>></em> <$button type="set" set="$:/ShowEditPreview" setTo="yes"><<lingo Body/Preview/Button/Show>></$button>
|
||||
<em class="tw-edit"><<lingo Body/Hint>></em> <$button type="set" set="$:/state/showeditpreview" setTo="yes"><<lingo Body/Preview/Button/Show>></$button>
|
||||
<$edit field="text" class="tw-edit-texteditor" placeholder={{$:/language/EditTemplate/Body/Placeholder}}/>
|
||||
|
||||
</$reveal>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
title: $:/core/ui/EditTemplate/controls
|
||||
tags: $:/tags/EditTemplate
|
||||
|
||||
<span class="tw-tiddler-controls titlebar"> <$list filter="[all[tiddlers+shadows]tag[$:/tags/EditToolbar]!has[draft.of]]" variable="listItem"><$transclude tiddler=<<listItem>>/></$list> </span>
|
||||
<span class="tw-tiddler-controls titlebar"> <$list filter="[all[shadows+tiddlers]tag[$:/tags/EditToolbar]!has[draft.of]]" variable="listItem"><$transclude tiddler=<<listItem>>/></$list> </span>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user