mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-02-21 17:39:51 +00:00
Compare commits
789 Commits
v5.1.15
...
wikitext-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b799d924f0 | ||
|
|
1c436cbbf3 | ||
|
|
94607aa9cd | ||
|
|
62477c9fbb | ||
|
|
b1edcdb757 | ||
|
|
d38b42f7c7 | ||
|
|
fffd0ee9e1 | ||
|
|
9f0d726f7d | ||
|
|
7709192cc8 | ||
|
|
02ae8969b2 | ||
|
|
a01dc6cb3d | ||
|
|
f99fb4f9f5 | ||
|
|
e52a616891 | ||
|
|
a89991cc46 | ||
|
|
f4fe254038 | ||
|
|
9f630b5829 | ||
|
|
c904aeb05f | ||
|
|
efd7aa3a53 | ||
|
|
a4b24670f6 | ||
|
|
2e0b2c8045 | ||
|
|
d068cb3625 | ||
|
|
0d8c749f37 | ||
|
|
0303b7871b | ||
|
|
44d1a00bb0 | ||
|
|
784db30614 | ||
|
|
26c6d6035e | ||
|
|
b82cf3d070 | ||
|
|
f07ffb2b41 | ||
|
|
c9b16d88f3 | ||
|
|
256321cd42 | ||
|
|
71aed78e2e | ||
|
|
0ab29e2c09 | ||
|
|
648c7ccd1f | ||
|
|
2dc70682cd | ||
|
|
441011885c | ||
|
|
449fd02cda | ||
|
|
a7dc8b5583 | ||
|
|
7478ba9165 | ||
|
|
12630d4a91 | ||
|
|
07635d7b3e | ||
|
|
e61c71961b | ||
|
|
f047e7cbf5 | ||
|
|
2c2d03f7a7 | ||
|
|
c036c22826 | ||
|
|
6de13e2f60 | ||
|
|
0c2689dd78 | ||
|
|
a9e595c3f6 | ||
|
|
348a0bc8bc | ||
|
|
477c41f843 | ||
|
|
0b1ce30a04 | ||
|
|
e8b50df4a6 | ||
|
|
2104017249 | ||
|
|
148c1a0355 | ||
|
|
dd4305d520 | ||
|
|
638eb53429 | ||
|
|
b6e2985ac6 | ||
|
|
87eab62b7e | ||
|
|
fb0713ae78 | ||
|
|
16bb474fef | ||
|
|
77e6f5c0e3 | ||
|
|
41e338dc41 | ||
|
|
a6875df7ef | ||
|
|
655fc31cee | ||
|
|
18c00c2ef0 | ||
|
|
b862348b06 | ||
|
|
36e76429b1 | ||
|
|
181897514f | ||
|
|
810033bd71 | ||
|
|
6aab4d5524 | ||
|
|
72f2a94251 | ||
|
|
6dedcd958d | ||
|
|
f97d18bb6e | ||
|
|
9d7d3fefa0 | ||
|
|
6ff7a7d261 | ||
|
|
2710fae71d | ||
|
|
0e6855eba8 | ||
|
|
8b04cfd4d5 | ||
|
|
2c37c25c28 | ||
|
|
adb07ccba0 | ||
|
|
b64216cfa7 | ||
|
|
9601e570af | ||
|
|
5c5628e10b | ||
|
|
cdeabbaf56 | ||
|
|
5577f7a4fe | ||
|
|
51b4f63c5c | ||
|
|
7df58a6813 | ||
|
|
158f96a207 | ||
|
|
dc29acd656 | ||
|
|
af40485e37 | ||
|
|
9b2d52716a | ||
|
|
64b665e706 | ||
|
|
f709641967 | ||
|
|
94f143bf64 | ||
|
|
22fe4451c8 | ||
|
|
1c982fba2d | ||
|
|
0ea00b59b0 | ||
|
|
5e9fc661a9 | ||
|
|
e5f3301c1c | ||
|
|
b08a7e3f04 | ||
|
|
44c66b98a9 | ||
|
|
ce256102a7 | ||
|
|
6f5f9ca2fb | ||
|
|
e31c5563ff | ||
|
|
70f5dff81e | ||
|
|
689e172e79 | ||
|
|
5ffcd7e5da | ||
|
|
5a37a84a54 | ||
|
|
4228c3f9cc | ||
|
|
a4eb139f99 | ||
|
|
7bc1458749 | ||
|
|
92b8368115 | ||
|
|
c4bb2110e3 | ||
|
|
f7d938e4bc | ||
|
|
8defe41458 | ||
|
|
6753a1485a | ||
|
|
7f97e3080c | ||
|
|
b6d901f888 | ||
|
|
90b7961629 | ||
|
|
c5aa855ce6 | ||
|
|
036ea400c5 | ||
|
|
049244e8a8 | ||
|
|
95aca11719 | ||
|
|
02fbae4200 | ||
|
|
fc3227831f | ||
|
|
72ec4d05e4 | ||
|
|
07aeaaa1a4 | ||
|
|
790d606885 | ||
|
|
c6f1fb7627 | ||
|
|
44816ea0da | ||
|
|
ca7b62a5f6 | ||
|
|
353821f442 | ||
|
|
4c1e3aa8d6 | ||
|
|
952826e0fa | ||
|
|
e6aaafbc8d | ||
|
|
79eb6a5ec1 | ||
|
|
7c0528340d | ||
|
|
273768da1d | ||
|
|
52a30be7c6 | ||
|
|
479726c46e | ||
|
|
3afb251b3a | ||
|
|
1f829dd5a8 | ||
|
|
95dbdff08a | ||
|
|
bfd09133de | ||
|
|
dc74fc4306 | ||
|
|
557053ab35 | ||
|
|
317d75171c | ||
|
|
0e568fe69a | ||
|
|
e2fe40a282 | ||
|
|
193fb32423 | ||
|
|
e14e69bedc | ||
|
|
af9f90e8cd | ||
|
|
b899563971 | ||
|
|
d0afbe50a1 | ||
|
|
0aac98f361 | ||
|
|
fa638b8b5b | ||
|
|
4d5a79acb8 | ||
|
|
fcfdb0632b | ||
|
|
d14d7f0168 | ||
|
|
83bea2a270 | ||
|
|
2b8efdc3ea | ||
|
|
27d0b2b491 | ||
|
|
824930682a | ||
|
|
d8104e2c41 | ||
|
|
06f1f1a532 | ||
|
|
6121c0b5a7 | ||
|
|
2c5b1c4a8c | ||
|
|
3d687a7935 | ||
|
|
5d20a54713 | ||
|
|
14623d33d2 | ||
|
|
37b335a136 | ||
|
|
d5a25c9a96 | ||
|
|
6c48c24332 | ||
|
|
86e27d64f2 | ||
|
|
decbe33f1f | ||
|
|
8205893af9 | ||
|
|
75158c0366 | ||
|
|
a2d4239db0 | ||
|
|
1d9c932e16 | ||
|
|
58f7e7bd85 | ||
|
|
8f54231d62 | ||
|
|
97f7f8f111 | ||
|
|
4e0b03b19f | ||
|
|
097b0e2d4a | ||
|
|
87f077314b | ||
|
|
7a07c84eac | ||
|
|
512d5662cc | ||
|
|
32974eebdb | ||
|
|
eca5df9f01 | ||
|
|
c42eba3308 | ||
|
|
77d286213f | ||
|
|
da864e23d6 | ||
|
|
d58bc33df4 | ||
|
|
c97c3cea74 | ||
|
|
e9f2a24f30 | ||
|
|
0b7f72ce5c | ||
|
|
52d328ee1b | ||
|
|
ddf22ec2a4 | ||
|
|
6b4294923f | ||
|
|
2dd76007d3 | ||
|
|
31470f676c | ||
|
|
d6bffad8ab | ||
|
|
6195069c5e | ||
|
|
8e3f9d6aba | ||
|
|
1793dc5e34 | ||
|
|
21f4d05ca0 | ||
|
|
653604ae86 | ||
|
|
1aea7b075d | ||
|
|
1dce674e51 | ||
|
|
5b6a468757 | ||
|
|
4f71c9072f | ||
|
|
04ba91ce2f | ||
|
|
9acff8f21c | ||
|
|
ed714ab731 | ||
|
|
f3e17c365a | ||
|
|
98a395095b | ||
|
|
5f1e8acbc0 | ||
|
|
3827feee3f | ||
|
|
172751605e | ||
|
|
d8b291bc04 | ||
|
|
d5618ca60a | ||
|
|
500fd8c062 | ||
|
|
d5ac2ee49e | ||
|
|
a4937224b7 | ||
|
|
42cf077639 | ||
|
|
f71a3b72d7 | ||
|
|
eea036f803 | ||
|
|
2cfdf59b77 | ||
|
|
a8b8fb3b6f | ||
|
|
aa1c6a9872 | ||
|
|
a7cdb94957 | ||
|
|
f6b1e27281 | ||
|
|
de808ac66d | ||
|
|
18ed2faf82 | ||
|
|
38b088eabf | ||
|
|
c4b39af052 | ||
|
|
1c73783ea4 | ||
|
|
c065dfa4bb | ||
|
|
a36d8471a8 | ||
|
|
e3c0616326 | ||
|
|
d00d46a772 | ||
|
|
40eeba20ef | ||
|
|
f163a1f12c | ||
|
|
018f7628f5 | ||
|
|
bc124c0645 | ||
|
|
d4716a6f2b | ||
|
|
ad799dbb61 | ||
|
|
31e88dd2c6 | ||
|
|
e509291b18 | ||
|
|
70500140b9 | ||
|
|
87b3e470c2 | ||
|
|
e466f62e7e | ||
|
|
e17456e3bc | ||
|
|
c058378da0 | ||
|
|
b9df224f99 | ||
|
|
0f3912ba95 | ||
|
|
11f02dc362 | ||
|
|
d711ef25ed | ||
|
|
419ea9a243 | ||
|
|
288d25e733 | ||
|
|
0ecc7c6071 | ||
|
|
80f44e880c | ||
|
|
4a9e2696d6 | ||
|
|
6e59d2597a | ||
|
|
7502ef875e | ||
|
|
8c367cdb21 | ||
|
|
63031bb3fc | ||
|
|
2bf6203cf5 | ||
|
|
6e674fe9db | ||
|
|
a82800050d | ||
|
|
9cd58caafc | ||
|
|
ea91ab1632 | ||
|
|
17232cfe91 | ||
|
|
603c564872 | ||
|
|
b18c85b85c | ||
|
|
62cbbf1db4 | ||
|
|
0396af849a | ||
|
|
907d498baf | ||
|
|
ea76a868bf | ||
|
|
addc7c0176 | ||
|
|
9268a8c3ca | ||
|
|
fe52d5462f | ||
|
|
5b14a97e0f | ||
|
|
545c508138 | ||
|
|
42f7c03824 | ||
|
|
9232279a79 | ||
|
|
ee8821a5b4 | ||
|
|
e0126b2f77 | ||
|
|
19c49ae18a | ||
|
|
ad4107a94b | ||
|
|
8542ebaecb | ||
|
|
cd0ce0cde5 | ||
|
|
01407fa8f9 | ||
|
|
09ea59240a | ||
|
|
2e7faf3439 | ||
|
|
1700eb4ba7 | ||
|
|
536ab10790 | ||
|
|
f6334723f6 | ||
|
|
a83cd3f984 | ||
|
|
078df9c157 | ||
|
|
dc972237a2 | ||
|
|
32c8ef1d62 | ||
|
|
3f91d5b3a1 | ||
|
|
f32cb52ba6 | ||
|
|
0622d03beb | ||
|
|
9d0083d8dc | ||
|
|
8134f8de28 | ||
|
|
2541b9b090 | ||
|
|
d55a498fca | ||
|
|
ce7becd64f | ||
|
|
09330968cc | ||
|
|
02f26d94a1 | ||
|
|
7729649f0e | ||
|
|
430be4ec30 | ||
|
|
b009a60b69 | ||
|
|
8832409666 | ||
|
|
6d67dc8eb8 | ||
|
|
f4ca295086 | ||
|
|
b629b1412d | ||
|
|
eea034c32d | ||
|
|
f373d8f2bf | ||
|
|
4f78d3d81b | ||
|
|
72679d2041 | ||
|
|
33ba69e852 | ||
|
|
3aae643e14 | ||
|
|
e983936c30 | ||
|
|
849844be12 | ||
|
|
2b6514ddc2 | ||
|
|
ddc76622f2 | ||
|
|
882cad1a0d | ||
|
|
33dd367a65 | ||
|
|
db3f41db29 | ||
|
|
d317bdf3da | ||
|
|
9752531b61 | ||
|
|
43c8a0f485 | ||
|
|
4f7297645d | ||
|
|
c5d4990cc5 | ||
|
|
1f296951d4 | ||
|
|
88664f0286 | ||
|
|
0ff96f9caf | ||
|
|
c82edbe6bc | ||
|
|
55b5b6dd56 | ||
|
|
8ae62c90df | ||
|
|
277bc92f92 | ||
|
|
196992167f | ||
|
|
cb74536b3c | ||
|
|
22caef9e34 | ||
|
|
d64fc5d8cd | ||
|
|
9f8a74c6d9 | ||
|
|
6c9dfd7f62 | ||
|
|
4f8e32a647 | ||
|
|
4d1127ed5b | ||
|
|
16eb5e1e32 | ||
|
|
abda6dd078 | ||
|
|
729c1e1030 | ||
|
|
88c47d9df4 | ||
|
|
899fe7608b | ||
|
|
7cd1c48643 | ||
|
|
d7914e3f3e | ||
|
|
698224556e | ||
|
|
aeaf5ee5b6 | ||
|
|
97b098b059 | ||
|
|
457f03798c | ||
|
|
3592333cb8 | ||
|
|
b584295831 | ||
|
|
dff5315afe | ||
|
|
6dcdc2049a | ||
|
|
d6a0b06f02 | ||
|
|
a75434a347 | ||
|
|
78f5465a47 | ||
|
|
baddd89abb | ||
|
|
c0c1b557eb | ||
|
|
b95f6b523b | ||
|
|
6b03ba9876 | ||
|
|
d50e2df57b | ||
|
|
9c849eb10a | ||
|
|
d8007386cf | ||
|
|
5dcdff4b37 | ||
|
|
240bd7bec4 | ||
|
|
eeb453d471 | ||
|
|
f0c6a09ea7 | ||
|
|
c208c55a22 | ||
|
|
83a245ed21 | ||
|
|
107b0c17c0 | ||
|
|
f162f4bc7b | ||
|
|
a9dd8c2f52 | ||
|
|
d5da7fd57c | ||
|
|
7022a98d5a | ||
|
|
fe85845c3c | ||
|
|
f61a61c060 | ||
|
|
86f2de0dda | ||
|
|
cfb2d7c9c8 | ||
|
|
806df86434 | ||
|
|
7282bf4721 | ||
|
|
287a83c1cc | ||
|
|
b776f9fe90 | ||
|
|
7368cc74e1 | ||
|
|
f98b4d5956 | ||
|
|
e4e7a0912d | ||
|
|
d650784dd6 | ||
|
|
b76c5011cf | ||
|
|
e5550b91e6 | ||
|
|
587fe9d10e | ||
|
|
874318091e | ||
|
|
f9eed0dc87 | ||
|
|
f2a38960fc | ||
|
|
3d10a35fb7 | ||
|
|
8321d2e6fc | ||
|
|
4f39e69e9d | ||
|
|
d7b8c1c298 | ||
|
|
19f7287a53 | ||
|
|
65af4e7748 | ||
|
|
25727df649 | ||
|
|
4ec8881c2b | ||
|
|
4b630de4bd | ||
|
|
e237d8fa97 | ||
|
|
0f7ce7b67f | ||
|
|
3bfa9c6f10 | ||
|
|
63ad284784 | ||
|
|
7dbe117bc5 | ||
|
|
72e2238dc9 | ||
|
|
7a6213dcbf | ||
|
|
03602215c4 | ||
|
|
0151d8e564 | ||
|
|
b7558f98f4 | ||
|
|
09112ed455 | ||
|
|
5756f30edd | ||
|
|
59b6cc134f | ||
|
|
ee28f66b0a | ||
|
|
9a2e2cd385 | ||
|
|
d96c844264 | ||
|
|
11529ab399 | ||
|
|
fbaceaa8bd | ||
|
|
0c965175aa | ||
|
|
e92eb6a945 | ||
|
|
dce564c238 | ||
|
|
68b063ab24 | ||
|
|
fe527b7eaf | ||
|
|
e72c72f04c | ||
|
|
f218c946f1 | ||
|
|
b1f9ff3f6c | ||
|
|
7d0b255a2a | ||
|
|
5a2e87eb09 | ||
|
|
b55a3102be | ||
|
|
8a38685de9 | ||
|
|
f210b75a30 | ||
|
|
763f8afaf2 | ||
|
|
3140ff9e49 | ||
|
|
b89e8d1635 | ||
|
|
c48aff2c87 | ||
|
|
7b9dc7557c | ||
|
|
c87c18be96 | ||
|
|
bb9e2de861 | ||
|
|
0a5633dd4a | ||
|
|
fcc5a6e796 | ||
|
|
ec18a55033 | ||
|
|
097c87fa7b | ||
|
|
2d231a2e07 | ||
|
|
0285eb600e | ||
|
|
4b9bc1b766 | ||
|
|
a3a4c28143 | ||
|
|
6cfd973fbd | ||
|
|
794be7ffd7 | ||
|
|
49e9789d9c | ||
|
|
8520c9d8fd | ||
|
|
be58de8409 | ||
|
|
34e04b7ca6 | ||
|
|
aa8b2e11bb | ||
|
|
8b787cd806 | ||
|
|
1317e13974 | ||
|
|
e554561f95 | ||
|
|
57d6215fda | ||
|
|
fcccc85994 | ||
|
|
e0be9a3d09 | ||
|
|
d7001c6f6b | ||
|
|
c05c0d3df6 | ||
|
|
9735e13dea | ||
|
|
c72e4f01f1 | ||
|
|
19b54fe905 | ||
|
|
2f425c303f | ||
|
|
bef3242075 | ||
|
|
f54a0a11bc | ||
|
|
929b0c9833 | ||
|
|
acaa07a964 | ||
|
|
23797b05a1 | ||
|
|
1ce9973bed | ||
|
|
c29f5a1b61 | ||
|
|
2a3f1b4403 | ||
|
|
7557b8b5b7 | ||
|
|
35cbb127a3 | ||
|
|
aba9c94f5a | ||
|
|
bacf500d50 | ||
|
|
2e51f08bef | ||
|
|
29606c6d24 | ||
|
|
17e73befde | ||
|
|
89f99151b3 | ||
|
|
d2682b71ff | ||
|
|
121e868ca4 | ||
|
|
4b42173962 | ||
|
|
01bdaff005 | ||
|
|
edcf1b1d41 | ||
|
|
05af050cbf | ||
|
|
6b14969cf6 | ||
|
|
f0b7c9a3d5 | ||
|
|
33453039fc | ||
|
|
33e636cbe7 | ||
|
|
760c80c655 | ||
|
|
1436196758 | ||
|
|
c252d7c945 | ||
|
|
4fd980debe | ||
|
|
38b24906c1 | ||
|
|
9e8a0653b0 | ||
|
|
398b715bb8 | ||
|
|
d54f599269 | ||
|
|
f355f07ff9 | ||
|
|
6e2b4ebf3e | ||
|
|
c5511854ab | ||
|
|
577a87a502 | ||
|
|
731759e20b | ||
|
|
85648590e5 | ||
|
|
944010c3bf | ||
|
|
78e734797b | ||
|
|
98bafd0b69 | ||
|
|
4e12c93b90 | ||
|
|
280cc39434 | ||
|
|
7468ad7acf | ||
|
|
737e9ae4cb | ||
|
|
e11282cc08 | ||
|
|
2beae0db2e | ||
|
|
523060e823 | ||
|
|
b95aa6ec6a | ||
|
|
e6466b2c32 | ||
|
|
0ab9ec1ad3 | ||
|
|
715cb1d1bc | ||
|
|
230c13129a | ||
|
|
a2606781b7 | ||
|
|
4c89bbabbe | ||
|
|
3816819705 | ||
|
|
f9be417204 | ||
|
|
d4b4c3c936 | ||
|
|
c64fc164fc | ||
|
|
2c7f467514 | ||
|
|
46e8e4343a | ||
|
|
38baa70bc6 | ||
|
|
36d321746b | ||
|
|
ab5e5795e8 | ||
|
|
8464101430 | ||
|
|
c569df4bd4 | ||
|
|
215c8bb9ea | ||
|
|
55453d463a | ||
|
|
b2173d11ea | ||
|
|
c95a32abdf | ||
|
|
ac26c8829d | ||
|
|
8feb07e429 | ||
|
|
3aaa7357f6 | ||
|
|
895447c40c | ||
|
|
5626148202 | ||
|
|
aab408109f | ||
|
|
34ce15638c | ||
|
|
d84a03c2d7 | ||
|
|
9499af9a70 | ||
|
|
d77a3795b7 | ||
|
|
75709d745f | ||
|
|
3e707f6a51 | ||
|
|
c397004cfb | ||
|
|
466566502c | ||
|
|
7f2ad15464 | ||
|
|
df50d675f2 | ||
|
|
19085a1277 | ||
|
|
200e854814 | ||
|
|
6bbf36f903 | ||
|
|
c974858cf5 | ||
|
|
f26bcb273b | ||
|
|
61f9adb710 | ||
|
|
7e58866499 | ||
|
|
7f1541464a | ||
|
|
141629c63b | ||
|
|
07887c1226 | ||
|
|
8bf7dd7172 | ||
|
|
df809bcb87 | ||
|
|
848d9cc4cb | ||
|
|
462d0a9402 | ||
|
|
5ea6c9a273 | ||
|
|
2163302190 | ||
|
|
3af0487f29 | ||
|
|
99d8da9fc4 | ||
|
|
06a10d7180 | ||
|
|
d4fc913012 | ||
|
|
afe14b47b5 | ||
|
|
b783d0f5af | ||
|
|
f092d08358 | ||
|
|
ea763d0eab | ||
|
|
f4496d8fd3 | ||
|
|
df1f7e9798 | ||
|
|
ce7cb020fb | ||
|
|
1d6edfbfcb | ||
|
|
4139690447 | ||
|
|
6cf508ef27 | ||
|
|
3c92512a2d | ||
|
|
a07fe6f016 | ||
|
|
3d64d7d126 | ||
|
|
02529a51d0 | ||
|
|
67bab83902 | ||
|
|
bafe447e1c | ||
|
|
0c3e549235 | ||
|
|
038f19ca9d | ||
|
|
df0b8b5a1e | ||
|
|
df9b8c5505 | ||
|
|
9e03264ebe | ||
|
|
53f2cc9814 | ||
|
|
0080728d36 | ||
|
|
47cdf55133 | ||
|
|
52319ee88a | ||
|
|
43b4ded2cc | ||
|
|
2e8eb15536 | ||
|
|
e4660a8cc1 | ||
|
|
6401b5c886 | ||
|
|
501ad2798f | ||
|
|
a8bd22f363 | ||
|
|
e19a9e6ff6 | ||
|
|
274bea1258 | ||
|
|
1280984e78 | ||
|
|
633208958c | ||
|
|
f4fd5e76f8 | ||
|
|
be375c40f2 | ||
|
|
b7746b0c55 | ||
|
|
019cb6a542 | ||
|
|
44a7ca7e43 | ||
|
|
1c6942402e | ||
|
|
28e713caac | ||
|
|
1b31004941 | ||
|
|
8e9330e484 | ||
|
|
033feda02d | ||
|
|
f1b38c42f9 | ||
|
|
1dc7647640 | ||
|
|
a62e7d218c | ||
|
|
6963a54c66 | ||
|
|
ac154e1452 | ||
|
|
45bf58b920 | ||
|
|
c4f6ba3a3f | ||
|
|
54f2c4c34f | ||
|
|
e92e109162 | ||
|
|
92c1a82492 | ||
|
|
bbccb60c0b | ||
|
|
aa7b18427f | ||
|
|
0d354fe57f | ||
|
|
b43ff430b7 | ||
|
|
f78190ef61 | ||
|
|
47264c8cd5 | ||
|
|
11b21d3583 | ||
|
|
de984366b9 | ||
|
|
eae573b9dd | ||
|
|
c9b8319801 | ||
|
|
de6e0d1c1e | ||
|
|
c0569849d2 | ||
|
|
22a15bed67 | ||
|
|
cb28065d8e | ||
|
|
bc87c3e3ae | ||
|
|
a5f3968304 | ||
|
|
2e9f0b29d0 | ||
|
|
0a1152b83a | ||
|
|
dbfe28094c | ||
|
|
7cb54f32c6 | ||
|
|
de6c9aff9d | ||
|
|
33cdf1550a | ||
|
|
1a52fdd626 | ||
|
|
6acf36e898 | ||
|
|
3361d749bf | ||
|
|
540cd1a286 | ||
|
|
634eb222ca | ||
|
|
25d667868d | ||
|
|
5fe80ec122 | ||
|
|
80e9c39b75 | ||
|
|
25fc4aa3ae | ||
|
|
ee0ccd4cc4 | ||
|
|
8030ea0fd6 | ||
|
|
22cd017c2f | ||
|
|
74c1fe16c8 | ||
|
|
51ad1deaf2 | ||
|
|
308c644fc1 | ||
|
|
521c1f6068 | ||
|
|
7f5a2f96ef | ||
|
|
8a5c357f23 | ||
|
|
8abe626957 | ||
|
|
64ca68e8e9 | ||
|
|
29e4965d2b | ||
|
|
fb3b7aa1cd | ||
|
|
2b0204422d | ||
|
|
cc39a86430 | ||
|
|
da0ffce2d6 | ||
|
|
b252abd4d1 | ||
|
|
24c6eece1b | ||
|
|
b1cd450889 | ||
|
|
0b9a3923bf | ||
|
|
5341d2887f | ||
|
|
4435afc726 | ||
|
|
042e9185a9 | ||
|
|
cda43f2ef8 | ||
|
|
220f73fde6 | ||
|
|
5963adf92e | ||
|
|
477cb0c47e | ||
|
|
86266c506f | ||
|
|
5754216479 | ||
|
|
38cf4c7ccf | ||
|
|
1c8170463c | ||
|
|
7534a97518 | ||
|
|
ab2ac78620 | ||
|
|
55d7f43f0c | ||
|
|
3e81921eea | ||
|
|
028888edff | ||
|
|
c194cec520 | ||
|
|
327fed30c8 | ||
|
|
42660b05a7 | ||
|
|
ff6aa0570f | ||
|
|
6350f55e25 | ||
|
|
eaf0869c94 | ||
|
|
df7f4fedb2 | ||
|
|
460a07ca03 | ||
|
|
4e1f9bf5ed | ||
|
|
a88e28cb5a | ||
|
|
ad116be7f6 | ||
|
|
949c178f45 | ||
|
|
a4623a8ebb | ||
|
|
293c2fce07 | ||
|
|
92870c0b2d | ||
|
|
4e433966f4 | ||
|
|
43108926de | ||
|
|
302b6a93de | ||
|
|
48a98bad02 | ||
|
|
89e8f689bb | ||
|
|
b4c7c59d5d | ||
|
|
a3dae612fe | ||
|
|
6fa1602c2c | ||
|
|
3b34f60b90 | ||
|
|
25f76a5996 | ||
|
|
5fee52beac | ||
|
|
413894e3e7 | ||
|
|
50950d8143 | ||
|
|
a7a3748bca | ||
|
|
de4eb5ca89 | ||
|
|
3f5e8aed3d | ||
|
|
17bd6b76a8 | ||
|
|
edeab188a9 | ||
|
|
4bc592c9e8 | ||
|
|
6a96cd1284 | ||
|
|
47d065b239 | ||
|
|
642a6505a1 | ||
|
|
57e1fc6cd8 | ||
|
|
358d416526 | ||
|
|
e4b10d42f9 | ||
|
|
7e71fcfab8 | ||
|
|
254e1ca7f7 | ||
|
|
e27497d43d | ||
|
|
cbfbd0c2bc | ||
|
|
ca43a28d0e | ||
|
|
34306983b2 | ||
|
|
1b6a06a4d7 | ||
|
|
6c7f8e5381 | ||
|
|
c83231871d | ||
|
|
d2ff164c07 | ||
|
|
e344c38349 | ||
|
|
f128650c6e | ||
|
|
d96b4f4d90 | ||
|
|
f478fa97fc | ||
|
|
49b3ed4770 | ||
|
|
cd32b627b3 | ||
|
|
6928c411d6 | ||
|
|
2b21b74c50 | ||
|
|
7ba9cbc052 | ||
|
|
451150ebd6 | ||
|
|
56940e49c2 | ||
|
|
920d225f37 | ||
|
|
a2c764d407 | ||
|
|
feace8d26b | ||
|
|
38fbc4f35a | ||
|
|
661bff4f5b | ||
|
|
4d2d202935 | ||
|
|
9b2d916946 | ||
|
|
be4c108ed9 | ||
|
|
abec14af9b | ||
|
|
b02ee28968 | ||
|
|
c507d4e2dd | ||
|
|
525e07f259 | ||
|
|
bd2ff7a4c9 | ||
|
|
e64c42e661 | ||
|
|
06fa483b7d | ||
|
|
ca2225b940 | ||
|
|
c65ca8060c | ||
|
|
5acfc012cb | ||
|
|
0b3e637880 |
34
.travis.yml
Normal file
34
.travis.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
language: node_js
|
||||
- "node"
|
||||
|
||||
stages:
|
||||
- name: test
|
||||
- name: build-prerelease
|
||||
if: branch = "master"
|
||||
- name: build-tiddlywiki-com
|
||||
if: branch = "tiddlywiki-com"
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
script: ./bin/test.sh
|
||||
- stage: build-prerelease
|
||||
script:
|
||||
- ./bin/travis-pre-build.sh
|
||||
- export TW5_BUILD_TIDDLYWIKI='./tiddlywiki.js'
|
||||
- export TW5_BUILD_VERSION=$(./bin/get-plugin-library-version-number)
|
||||
- export TW5_BUILD_DETAILS="Prerelease built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
||||
- export TW5_BUILD_MAIN_EDITION='./editions/prerelease'
|
||||
- export TW5_BUILD_OUTPUT='./output/prerelease'
|
||||
- ./bin/build-site.sh
|
||||
- ./bin/travis-push.sh
|
||||
- stage: build-tiddlywiki-com
|
||||
script:
|
||||
- ./bin/travis-pre-build.sh
|
||||
- export TW5_BUILD_TIDDLYWIKI='./node_modules/tiddlywiki/tiddlywiki.js'
|
||||
- export TW5_BUILD_VERSION=$(./bin/get-plugin-library-version-number)
|
||||
- export TW5_BUILD_DETAILS="Built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
||||
- export TW5_BUILD_MAIN_EDITION='./editions/tw5.com'
|
||||
- export TW5_BUILD_OUTPUT='./output'
|
||||
- ./bin/build-site.sh
|
||||
- ./bin/travis-push.sh
|
||||
15
bin/2bld.cmd
15
bin/2bld.cmd
@@ -1,15 +0,0 @@
|
||||
@echo off
|
||||
|
||||
rem build TiddlyWiki 2.x
|
||||
|
||||
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 index.html text/plain ^
|
||||
|| exit 1
|
||||
|
||||
fc tmp\tw2\index.html editions\tw2\target\prebuilt.html
|
||||
15
bin/2bld.sh
15
bin/2bld.sh
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# build TiddlyWiki 2.x
|
||||
|
||||
# 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 index.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
diff -q tmp/tw2/index.html editions/tw2/target/prebuilt.html
|
||||
443
bin/build-site.sh
Executable file
443
bin/build-site.sh
Executable file
@@ -0,0 +1,443 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build all tiddlywiki.com assets.
|
||||
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.1.19
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
|
||||
# Default to using tw5.com as the main edition for /index.html
|
||||
|
||||
if [ -z "$TW5_BUILD_MAIN_EDITION" ]; then
|
||||
TW5_BUILD_MAIN_EDITION=./editions/tw5.com
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_MAIN_EDITION as [$TW5_BUILD_MAIN_EDITION]"
|
||||
|
||||
# Default to the version of TiddlyWiki installed in this repo
|
||||
|
||||
if [ -z "$TW5_BUILD_TIDDLYWIKI" ]; then
|
||||
TW5_BUILD_TIDDLYWIKI=./tiddlywiki.js
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_TIDDLYWIKI as [$TW5_BUILD_TIDDLYWIKI]"
|
||||
|
||||
# Set up the build details
|
||||
|
||||
if [ -z "$TW5_BUILD_DETAILS" ]; then
|
||||
TW5_BUILD_DETAILS="$(git symbolic-ref --short HEAD)-$(git rev-parse HEAD) from $(git remote get-url origin)"
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_DETAILS as [$TW5_BUILD_DETAILS]"
|
||||
|
||||
# Set up the build output directory
|
||||
|
||||
if [ -z "$TW5_BUILD_OUTPUT" ]; then
|
||||
TW5_BUILD_OUTPUT=./output
|
||||
fi
|
||||
|
||||
mkdir -p $TW5_BUILD_OUTPUT
|
||||
|
||||
if [ ! -d "$TW5_BUILD_OUTPUT" ]; then
|
||||
echo 'A valid TW5_BUILD_OUTPUT environment variable must be set'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_OUTPUT as [$TW5_BUILD_OUTPUT]"
|
||||
|
||||
echo "Build details: $TW5_BUILD_DETAILS"
|
||||
|
||||
# Make the CNAME file that GitHub Pages requires
|
||||
|
||||
echo "tiddlywiki.com" > $TW5_BUILD_OUTPUT/CNAME
|
||||
|
||||
# Delete any existing static content
|
||||
|
||||
mkdir -p $TW5_BUILD_OUTPUT/static
|
||||
mkdir -p $TW5_BUILD_OUTPUT/dev
|
||||
mkdir -p $TW5_BUILD_OUTPUT/dev/static
|
||||
rm $TW5_BUILD_OUTPUT/static/*
|
||||
rm $TW5_BUILD_OUTPUT/dev/static/*
|
||||
|
||||
# Redirects
|
||||
|
||||
echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/d3/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/d3/index.html</a>" > $TW5_BUILD_OUTPUT/d3demo.html
|
||||
echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html
|
||||
echo "<a href='./plugins/tiddlywiki/tahoelafs/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tahoelafs/index.html</a>" > $TW5_BUILD_OUTPUT/tahoelafs.html
|
||||
|
||||
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
|
||||
|
||||
echo -e -n "title: $:/build\n\n$TW5_BUILD_DETAILS\n" > $TW5_BUILD_OUTPUT/build.tid
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Core distribution
|
||||
#
|
||||
######################################################
|
||||
|
||||
# /index.html Main site
|
||||
# /favicon.ico Favicon for main site
|
||||
# /static.html Static rendering of default tiddlers
|
||||
# /alltiddlers.html Static rendering of all tiddlers
|
||||
# /static/* Static single tiddlers
|
||||
# /static/static.css Static stylesheet
|
||||
# /static/favicon.ico Favicon for static pages
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--version \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build favicon static index \
|
||||
|| exit 1
|
||||
|
||||
# /empty.html Empty
|
||||
# /empty.hta For Internet Explorer
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/empty \
|
||||
--verbose \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build empty \
|
||||
|| exit 1
|
||||
|
||||
|
||||
# /test.html Test edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/test \
|
||||
--verbose \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all test.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /dev/index.html Developer docs
|
||||
# /dev/favicon.ico Favicon for dev site
|
||||
# /dev/static.html Static rendering of default tiddlers
|
||||
# /dev/alltiddlers.html Static rendering of all tiddlers
|
||||
# /dev/static/* Static single tiddlers
|
||||
# /dev/static/static.css Static stylesheet
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/dev \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/dev \
|
||||
--build index favicon static \
|
||||
|| exit 1
|
||||
|
||||
# /upgrade.html Custom edition for performing upgrades
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/upgrade \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build upgrade \
|
||||
|| exit 1
|
||||
|
||||
# /encrypted.html Copy of the main file encrypted with the password "password"
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build encrypted \
|
||||
|| exit 1
|
||||
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Editions
|
||||
#
|
||||
######################################################
|
||||
|
||||
# /editions/xlsx-utils/index.html xlsx-utils edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/xlsx-utils \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/xlsx-utils/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/resumebuilder/index.html Resume builder edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/resumebuilder \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/resumebuilder/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/text-slicer/index.html Text slicer edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/text-slicer \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/text-slicer/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/translators/index.html Translators edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/translators \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/translators/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/introduction/index.html Introduction edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/introduction \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/introduction/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/full/index.html Full edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/full \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/full/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
# /editions/tw5.com-docs/index.html tiddlywiki.com docs edition
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/tw5.com-docs \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/editions/tw5.com-docs/ \
|
||||
--build index \
|
||||
|| exit 1
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Plugin demos
|
||||
#
|
||||
######################################################
|
||||
|
||||
# /plugins/tiddlywiki/innerwiki/index.html Demo wiki with Innerwiki plugin
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/innerwikidemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/innerwiki/index.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/dynaview/index.html Demo wiki with DynaView plugin
|
||||
# /plugins/tiddlywiki/dynaview/empty.html Empty wiki with DynaView plugin
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/dynaviewdemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/dynaview/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/dynaview/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/katex/index.html Demo wiki with KaTeX plugin
|
||||
# /plugins/tiddlywiki/katex/empty.html Empty wiki with KaTeX plugin
|
||||
|
||||
# TODO: Build the static file with the release of 5.1.3
|
||||
# --rendertiddler $:/core/templates/static.template.html plugins/tiddlywiki/katex/static.html text/plain \
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/katexdemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/katex/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/tahoelafs/index.html Demo wiki with Tahoe-LAFS plugin
|
||||
# /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/tahoelafs \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tahoelafs/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/d3/index.html Demo wiki with D3 plugin
|
||||
# /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/d3demo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/d3/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin
|
||||
# /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/codemirrordemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/codemirror/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/codemirror/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/markdown/index.html Demo wiki with Markdown plugin
|
||||
# /plugins/tiddlywiki/markdown/empty.html Empty wiki with Markdown plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/markdowndemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/markdown/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/markdown/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/tw2parser/index.html Demo wiki with tw2parser plugin
|
||||
# /plugins/tiddlywiki/tw2parser/empty.html Empty wiki with tw2parser plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/classicparserdemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/tw2parser/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tw2parser/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# /plugins/tiddlywiki/highlight/index.html Demo wiki with highlight plugin
|
||||
# /plugins/tiddlywiki/highlight/empty.html Empty wiki with highlight plugin
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/highlightdemo \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--rendertiddler $:/core/save/all plugins/tiddlywiki/highlight/index.html text/plain \
|
||||
--rendertiddler $:/core/save/empty plugins/tiddlywiki/highlight/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Language editions
|
||||
#
|
||||
######################################################
|
||||
|
||||
# Delete any existing static content
|
||||
|
||||
rm $TW5_BUILD_OUTPUT/languages/de-AT/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/de-DE/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/es-ES/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/fr-FR/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/ja-JP/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/ko-KR/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/zh-Hans/static/*
|
||||
rm $TW5_BUILD_OUTPUT/languages/zh-Hant/static/*
|
||||
|
||||
# /languages/de-AT/index.html Demo wiki with de-AT language
|
||||
# /languages/de-AT/empty.html Empty wiki with de-AT language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/de-AT \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/de-AT \
|
||||
--build favicon empty static index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/de-DE/index.html Demo wiki with de-DE language
|
||||
# /languages/de-DE/empty.html Empty wiki with de-DE language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/de-DE \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/de-DE \
|
||||
--build favicon empty static index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/es-ES/index.html Demo wiki with es-ES language
|
||||
# /languages/es-ES/empty.html Empty wiki with es-ES language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/es-ES \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/es-ES \
|
||||
--build favicon empty static index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/fr-FR/index.html Demo wiki with fr-FR language
|
||||
# /languages/fr-FR/empty.html Empty wiki with fr-FR language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/fr-FR \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/fr-FR \
|
||||
--build favicon empty static index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/ja-JP/index.html Demo wiki with ja-JP language
|
||||
# /languages/ja-JP/empty.html Empty wiki with ja-JP language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/ja-JP \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/ja-JP \
|
||||
--build empty index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/ko-KR/index.html Demo wiki with ko-KR language
|
||||
# /languages/ko-KR/empty.html Empty wiki with ko-KR language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/ko-KR \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/ko-KR \
|
||||
--build favicon empty static index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/zh-Hans/index.html Demo wiki with zh-Hans language
|
||||
# /languages/zh-Hans/empty.html Empty wiki with zh-Hans language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/zh-Hans \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/zh-Hans \
|
||||
--build empty index \
|
||||
|| exit 1
|
||||
|
||||
# /languages/zh-Hant/index.html Demo wiki with zh-Hant language
|
||||
# /languages/zh-Hant/empty.html Empty wiki with zh-Hant language
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/zh-Hant \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/languages/zh-Hant \
|
||||
--build empty index \
|
||||
|| exit 1
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Plugin library
|
||||
#
|
||||
######################################################
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/pluginlibrary \
|
||||
--verbose \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_OUTPUT/library/$TW5_BUILD_VERSION \
|
||||
--build \
|
||||
|| exit 1
|
||||
|
||||
# Delete the temporary build tiddler
|
||||
|
||||
rm $TW5_BUILD_OUTPUT/build.tid || exit 1
|
||||
19
bin/get-plugin-library-version-number
Executable file
19
bin/get-plugin-library-version-number
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Extract raw version number from package.json (without the optional "-prerelease" suffix)
|
||||
|
||||
if(!process.env["TW5_BUILD_TIDDLYWIKI"]) {
|
||||
throw "TW5_BUILD_TIDDLYWIKI environment variable not set";
|
||||
}
|
||||
|
||||
var fs = require("fs"),
|
||||
path = require("path");
|
||||
|
||||
var filename = path.resolve(path.dirname(process.env["TW5_BUILD_TIDDLYWIKI"]),"./package.json"),
|
||||
json = JSON.parse(fs.readFileSync(filename,"utf8"));
|
||||
|
||||
if(!json.version) {
|
||||
throw "Missing version number in package.json";
|
||||
}
|
||||
|
||||
process.stdout.write("v" + json.version.split("-")[0]);
|
||||
@@ -7,6 +7,7 @@
|
||||
node ./tiddlywiki.js \
|
||||
./editions/test \
|
||||
--verbose \
|
||||
--version \
|
||||
--rendertiddler $:/core/save/all test.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
|
||||
10
bin/travis-pre-build.sh
Executable file
10
bin/travis-pre-build.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install latest current release from npm
|
||||
# (we need to force because otherwise npm will refuse to install a module of the same name)
|
||||
|
||||
npm --force install tiddlywiki || exit 1
|
||||
|
||||
# Pull existing GitHub pages content
|
||||
|
||||
git clone --depth=1 --branch=master "https://github.com/Jermolene/jermolene.github.io.git" output
|
||||
20
bin/travis-push.sh
Executable file
20
bin/travis-push.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Push output back to GitHub
|
||||
|
||||
|
||||
cd output || exit 1
|
||||
|
||||
git config --global user.email "travis@travis-ci.org" || exit 1
|
||||
|
||||
git config --global user.name "Travis CI" || exit 1
|
||||
|
||||
git add -A . || exit 1
|
||||
|
||||
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER of $TRAVIS_BRANCH ($(date +'%F %T %Z'))" || exit 1
|
||||
|
||||
git remote add deploy "https://$GH_TOKEN@github.com/Jermolene/jermolene.github.io.git" &>/dev/null || exit 1
|
||||
|
||||
git push deploy master &>/dev/null || exit 1
|
||||
|
||||
cd .. || exit 1
|
||||
@@ -9,37 +9,50 @@ Basic styles used before we boot up the parsing engine
|
||||
Error message and password prompt
|
||||
*/
|
||||
|
||||
.tc-password-wrapper, .tc-error-form {
|
||||
.tc-error-form {
|
||||
font-family: sans-serif;
|
||||
color: #fff;
|
||||
z-index: 20000;
|
||||
position: fixed;
|
||||
background-color: rgb(255, 75, 75);
|
||||
border: 8px solid rgb(255, 0, 0);
|
||||
border-radius: 8px;
|
||||
width: 50%;
|
||||
margin-left: 25%;
|
||||
margin-top: 4em;
|
||||
padding: 0 2em 1em 2em;
|
||||
}
|
||||
|
||||
.tc-error-form h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tc-error-prompt {
|
||||
text-align: center;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.tc-error-message {
|
||||
overflow: auto;
|
||||
max-height: 40em;
|
||||
padding-right: 1em;
|
||||
margin: 1em 0;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.tc-password-wrapper {
|
||||
font-family: sans-serif;
|
||||
z-index: 20000;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
top: 4em;
|
||||
left: 50%;
|
||||
margin-left: -144px; /* - width/2 - paddingHorz/2 - border */
|
||||
left: 50%;
|
||||
margin-left: -144px; /* - width/2 - paddingHorz/2 - border */
|
||||
padding: 16px 16px 16px 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tc-error-form {
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
|
||||
background-color: rgb(255, 75, 75);
|
||||
border: 8px solid rgb(255, 0, 0);
|
||||
width: 480px;
|
||||
margin-left: -244px; /* - width/2 - paddingHorz/2 - border */
|
||||
}
|
||||
|
||||
.tc-error-form div {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.tc-error-prompt {
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.tc-password-wrapper {
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
|
||||
209
boot/boot.js
209
boot/boot.js
@@ -51,6 +51,63 @@ $tw.utils.isArray = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Array]";
|
||||
};
|
||||
|
||||
/*
|
||||
Check if an array is equal by value and by reference.
|
||||
*/
|
||||
$tw.utils.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)
|
||||
value: a single value to push or an array of values to push
|
||||
*/
|
||||
$tw.utils.pushTop = function(array,value) {
|
||||
var t,p;
|
||||
if($tw.utils.isArray(value)) {
|
||||
// Remove any array entries that are duplicated in the new values
|
||||
if(value.length !== 0) {
|
||||
if(array.length !== 0) {
|
||||
if(value.length < array.length) {
|
||||
for(t=0; t<value.length; t++) {
|
||||
p = array.indexOf(value[t]);
|
||||
if(p !== -1) {
|
||||
array.splice(p,1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(t=array.length-1; t>=0; t--) {
|
||||
p = value.indexOf(array[t]);
|
||||
if(p !== -1) {
|
||||
array.splice(t,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Push the values on top of the main array
|
||||
array.push.apply(array,value);
|
||||
}
|
||||
} else {
|
||||
p = array.indexOf(value);
|
||||
if(p !== -1) {
|
||||
array.splice(p,1);
|
||||
}
|
||||
array.push(value);
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine if a value is a date
|
||||
*/
|
||||
@@ -89,7 +146,9 @@ Helper for making DOM elements
|
||||
tag: tag name
|
||||
options: see below
|
||||
Options include:
|
||||
namespace: defaults to http://www.w3.org/1999/xhtml
|
||||
attributes: hashmap of attribute values
|
||||
style: hashmap of styles
|
||||
text: text to add as a child node
|
||||
children: array of further child nodes
|
||||
innerHTML: optional HTML for element
|
||||
@@ -99,7 +158,7 @@ eventListeners: array of event listeners (this option won't work until $tw.utils
|
||||
*/
|
||||
$tw.utils.domMaker = function(tag,options) {
|
||||
var doc = options.document || document;
|
||||
var element = doc.createElement(tag);
|
||||
var element = doc.createElementNS(options.namespace || "http://www.w3.org/1999/xhtml",tag);
|
||||
if(options["class"]) {
|
||||
element.className = options["class"];
|
||||
}
|
||||
@@ -115,6 +174,9 @@ $tw.utils.domMaker = function(tag,options) {
|
||||
$tw.utils.each(options.attributes,function(attribute,name) {
|
||||
element.setAttribute(name,attribute);
|
||||
});
|
||||
$tw.utils.each(options.style,function(value,name) {
|
||||
element.style[name] = value;
|
||||
});
|
||||
if(options.eventListeners) {
|
||||
$tw.utils.addEventListeners(element,options.eventListeners);
|
||||
}
|
||||
@@ -135,8 +197,8 @@ $tw.utils.error = function(err) {
|
||||
var dm = $tw.utils.domMaker,
|
||||
heading = dm("h1",{text: errHeading}),
|
||||
prompt = dm("div",{text: promptMsg, "class": "tc-error-prompt"}),
|
||||
message = dm("div",{text: err}),
|
||||
button = dm("button",{text: ( $tw.language == undefined ? "close" : $tw.language.getString("Buttons/Close/Caption") )}),
|
||||
message = dm("div",{text: err, "class":"tc-error-message"}),
|
||||
button = dm("div",{children: [dm("button",{text: ( $tw.language == undefined ? "close" : $tw.language.getString("Buttons/Close/Caption") )})], "class": "tc-error-prompt"}),
|
||||
form = dm("form",{children: [heading,prompt,message,button], "class": "tc-error-form"});
|
||||
document.body.insertBefore(form,document.body.firstChild);
|
||||
form.addEventListener("submit",function(event) {
|
||||
@@ -268,7 +330,7 @@ $tw.utils.stringifyList = function(value) {
|
||||
};
|
||||
|
||||
// Parse a string array from a bracketted list. For example "OneTiddler [[Another Tiddler]] LastOne"
|
||||
$tw.utils.parseStringArray = function(value) {
|
||||
$tw.utils.parseStringArray = function(value, allowDuplicate) {
|
||||
if(typeof value === "string") {
|
||||
var memberRegExp = /(?:^|[^\S\xA0])(?:\[\[(.*?)\]\])(?=[^\S\xA0]|$)|([\S\xA0]+)/mg,
|
||||
results = [], names = {},
|
||||
@@ -277,7 +339,7 @@ $tw.utils.parseStringArray = function(value) {
|
||||
match = memberRegExp.exec(value);
|
||||
if(match) {
|
||||
var item = match[1] || match[2];
|
||||
if(item !== undefined && !$tw.utils.hop(names,item)) {
|
||||
if(item !== undefined && (!$tw.utils.hop(names,item) || allowDuplicate)) {
|
||||
results.push(item);
|
||||
names[item] = true;
|
||||
}
|
||||
@@ -862,6 +924,61 @@ $tw.Tiddler.prototype.hasField = function(field) {
|
||||
return $tw.utils.hop(this.fields,field);
|
||||
};
|
||||
|
||||
/*
|
||||
Compare two tiddlers for equality
|
||||
tiddler: the tiddler to compare
|
||||
excludeFields: array of field names to exclude from the comparison
|
||||
*/
|
||||
$tw.Tiddler.prototype.isEqual = function(tiddler,excludeFields) {
|
||||
if(!(tiddler instanceof $tw.Tiddler)) {
|
||||
return false;
|
||||
}
|
||||
excludeFields = excludeFields || [];
|
||||
var self = this,
|
||||
differences = []; // Fields that have differences
|
||||
// Add to the differences array
|
||||
function addDifference(fieldName) {
|
||||
// Check for this field being excluded
|
||||
if(excludeFields.indexOf(fieldName) === -1) {
|
||||
// Save the field as a difference
|
||||
$tw.utils.pushTop(differences,fieldName);
|
||||
}
|
||||
}
|
||||
// Returns true if the two values of this field are equal
|
||||
function isFieldValueEqual(fieldName) {
|
||||
var valueA = self.fields[fieldName],
|
||||
valueB = tiddler.fields[fieldName];
|
||||
// Check for identical string values
|
||||
if(typeof(valueA) === "string" && typeof(valueB) === "string" && valueA === valueB) {
|
||||
return true;
|
||||
}
|
||||
// Check for identical array values
|
||||
if($tw.utils.isArray(valueA) && $tw.utils.isArray(valueB) && $tw.utils.isArrayEqual(valueA,valueB)) {
|
||||
return true;
|
||||
}
|
||||
// Check for identical date values
|
||||
if($tw.utils.isDate(valueA) && $tw.utils.isDate(valueB) && valueA.getTime() === valueB.getTime()) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise the fields must be different
|
||||
return false;
|
||||
}
|
||||
// Compare our fields
|
||||
for(var fieldName in this.fields) {
|
||||
if(!isFieldValueEqual(fieldName)) {
|
||||
addDifference(fieldName);
|
||||
}
|
||||
}
|
||||
// There's a difference for every field in the other tiddler that we don't have
|
||||
for(fieldName in tiddler.fields) {
|
||||
if(!(fieldName in this.fields)) {
|
||||
addDifference(fieldName);
|
||||
}
|
||||
}
|
||||
// Return whether there were any differences
|
||||
return differences.length === 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Register and install the built in tiddler field modules
|
||||
*/
|
||||
@@ -902,9 +1019,23 @@ $tw.Wiki = function(options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
tiddlers = Object.create(null), // Hashmap of tiddlers
|
||||
tiddlerTitles = null, // Array of tiddler titles
|
||||
getTiddlerTitles = function() {
|
||||
if(!tiddlerTitles) {
|
||||
tiddlerTitles = Object.keys(tiddlers);
|
||||
}
|
||||
return tiddlerTitles;
|
||||
},
|
||||
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
|
||||
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
|
||||
shadowTiddlers = options.shadowTiddlers || Object.create(null); // Hashmap by title of {source:, tiddler:}
|
||||
shadowTiddlers = options.shadowTiddlers || Object.create(null), // Hashmap by title of {source:, tiddler:}
|
||||
shadowTiddlerTitles = null,
|
||||
getShadowTiddlerTitles = function() {
|
||||
if(!shadowTiddlerTitles) {
|
||||
shadowTiddlerTitles = Object.keys(shadowTiddlers);
|
||||
}
|
||||
return shadowTiddlerTitles;
|
||||
};
|
||||
|
||||
// Add a tiddler to the store
|
||||
this.addTiddler = function(tiddler) {
|
||||
@@ -918,6 +1049,9 @@ $tw.Wiki = function(options) {
|
||||
// Uncomment the following line for detailed logs of all tiddler writes
|
||||
// console.log("Adding",title,tiddler)
|
||||
tiddlers[title] = tiddler;
|
||||
if(tiddlerTitles && tiddlerTitles.indexOf(title) === -1) {
|
||||
tiddlerTitles.push(title);
|
||||
}
|
||||
this.clearCache(title);
|
||||
this.clearGlobalCache();
|
||||
this.enqueueTiddlerEvent(title);
|
||||
@@ -931,6 +1065,12 @@ $tw.Wiki = function(options) {
|
||||
// console.log("Deleting",title)
|
||||
if($tw.utils.hop(tiddlers,title)) {
|
||||
delete tiddlers[title];
|
||||
if(tiddlerTitles) {
|
||||
var index = tiddlerTitles.indexOf(title);
|
||||
if(index !== -1) {
|
||||
tiddlerTitles.splice(index,1);
|
||||
}
|
||||
}
|
||||
this.clearCache(title);
|
||||
this.clearGlobalCache();
|
||||
this.enqueueTiddlerEvent(title,true);
|
||||
@@ -943,7 +1083,7 @@ $tw.Wiki = function(options) {
|
||||
var t = tiddlers[title];
|
||||
if(t instanceof $tw.Tiddler) {
|
||||
return t;
|
||||
} else if(title !== undefined && Object.prototype.hasOwnProperty.call(shadowTiddlers,title)) {
|
||||
} else if(title !== undefined && shadowTiddlers[title]) {
|
||||
return shadowTiddlers[title].tiddler;
|
||||
}
|
||||
return undefined;
|
||||
@@ -952,12 +1092,12 @@ $tw.Wiki = function(options) {
|
||||
|
||||
// Get an array of all tiddler titles
|
||||
this.allTitles = function() {
|
||||
return Object.keys(tiddlers);
|
||||
return getTiddlerTitles().slice(0);
|
||||
};
|
||||
|
||||
// Iterate through all tiddler titles
|
||||
this.each = function(callback) {
|
||||
var titles = Object.keys(tiddlers),
|
||||
var titles = getTiddlerTitles(),
|
||||
index,titlesLength,title;
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
@@ -967,12 +1107,12 @@ $tw.Wiki = function(options) {
|
||||
|
||||
// Get an array of all shadow tiddler titles
|
||||
this.allShadowTitles = function() {
|
||||
return Object.keys(shadowTiddlers);
|
||||
return getShadowTiddlerTitles().slice(0);
|
||||
};
|
||||
|
||||
// Iterate through all shadow tiddler titles
|
||||
this.eachShadow = function(callback) {
|
||||
var titles = Object.keys(shadowTiddlers),
|
||||
var titles = getShadowTiddlerTitles(),
|
||||
index,titlesLength,title;
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
@@ -983,16 +1123,16 @@ $tw.Wiki = function(options) {
|
||||
|
||||
// Iterate through all tiddlers and then the shadows
|
||||
this.eachTiddlerPlusShadows = function(callback) {
|
||||
var titles = Object.keys(tiddlers),
|
||||
index,titlesLength,title;
|
||||
var index,titlesLength,title,
|
||||
titles = getTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
callback(tiddlers[title],title);
|
||||
}
|
||||
titles = Object.keys(shadowTiddlers);
|
||||
titles = getShadowTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(!Object.prototype.hasOwnProperty.call(tiddlers,title)) {
|
||||
if(!tiddlers[title]) {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
@@ -1001,21 +1141,21 @@ $tw.Wiki = function(options) {
|
||||
|
||||
// Iterate through all the shadows and then the tiddlers
|
||||
this.eachShadowPlusTiddlers = function(callback) {
|
||||
var titles = Object.keys(shadowTiddlers),
|
||||
index,titlesLength,title;
|
||||
var index,titlesLength,title,
|
||||
titles = getShadowTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(Object.prototype.hasOwnProperty.call(tiddlers,title)) {
|
||||
if(tiddlers[title]) {
|
||||
callback(tiddlers[title],title);
|
||||
} else {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
titles = Object.keys(tiddlers);
|
||||
titles = getTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(!Object.prototype.hasOwnProperty.call(shadowTiddlers,title)) {
|
||||
if(!shadowTiddlers[title]) {
|
||||
callback(tiddlers[title],title);
|
||||
}
|
||||
}
|
||||
@@ -1130,6 +1270,7 @@ $tw.Wiki = function(options) {
|
||||
});
|
||||
}
|
||||
});
|
||||
shadowTiddlerTitles = null;
|
||||
this.clearCache(null);
|
||||
this.clearGlobalCache();
|
||||
};
|
||||
@@ -1257,8 +1398,7 @@ $tw.Wiki.prototype.deserializeTiddlers = function(type,text,srcFields,options) {
|
||||
/*
|
||||
Register the built in tiddler deserializer modules
|
||||
*/
|
||||
$tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
|
||||
"application/javascript": function(text,fields) {
|
||||
var deserializeHeaderComment = function(text,fields) {
|
||||
var headerCommentRegExp = new RegExp($tw.config.jsModuleHeaderRegExpString,"mg"),
|
||||
match = headerCommentRegExp.exec(text);
|
||||
fields.text = text;
|
||||
@@ -1266,7 +1406,12 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
|
||||
fields = $tw.utils.parseFields(match[1].split(/\r?\n\r?\n/mg)[0],fields);
|
||||
}
|
||||
return [fields];
|
||||
}
|
||||
};
|
||||
$tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
|
||||
"application/javascript": deserializeHeaderComment
|
||||
});
|
||||
$tw.modules.define("$:/boot/tiddlerdeserializer/css","tiddlerdeserializer",{
|
||||
"text/css": deserializeHeaderComment
|
||||
});
|
||||
$tw.modules.define("$:/boot/tiddlerdeserializer/tid","tiddlerdeserializer",{
|
||||
"application/x-tiddler": function(text,fields) {
|
||||
@@ -1806,9 +1951,13 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||
// Save the original tiddler file locations if requested
|
||||
var config = wikiInfo.config || {};
|
||||
if(config["retain-original-tiddler-path"]) {
|
||||
var output = {};
|
||||
var output = {}, relativePath;
|
||||
for(var title in $tw.boot.files) {
|
||||
output[title] = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath);
|
||||
relativePath = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath);
|
||||
output[title] =
|
||||
path.sep === path.posix.sep ?
|
||||
relativePath :
|
||||
relativePath.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
$tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)});
|
||||
}
|
||||
@@ -1914,7 +2063,7 @@ $tw.boot.startup = function(options) {
|
||||
// For writable tiddler files, a hashmap of title to {filepath:,type:,hasMetaFile:}
|
||||
$tw.boot.files = Object.create(null);
|
||||
// System paths and filenames
|
||||
$tw.boot.bootPath = path.dirname(module.filename);
|
||||
$tw.boot.bootPath = options.bootPath || path.dirname(module.filename);
|
||||
$tw.boot.corePath = path.resolve($tw.boot.bootPath,"../core");
|
||||
// If there's no arguments then default to `--help`
|
||||
if($tw.boot.argv.length === 0) {
|
||||
@@ -1953,9 +2102,13 @@ $tw.boot.startup = function(options) {
|
||||
$tw.utils.registerFileType("image/jpeg","base64",[".jpg",".jpeg"],{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/png","base64",".png",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/gif","base64",".gif",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/webp","base64",".webp",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/heic","base64",".heic",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/heif","base64",".heif",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/svg+xml","utf8",".svg",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]});
|
||||
$tw.utils.registerFileType("application/font-woff","base64",".woff");
|
||||
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
|
||||
$tw.utils.registerFileType("audio/ogg","base64",".ogg");
|
||||
$tw.utils.registerFileType("video/mp4","base64",".mp4");
|
||||
$tw.utils.registerFileType("audio/mp3","base64",".mp3");
|
||||
@@ -1966,8 +2119,10 @@ $tw.boot.startup = function(options) {
|
||||
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.wordprocessingml.document","base64",".docx");
|
||||
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","base64",".xlsx");
|
||||
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.presentationml.presentation","base64",".pptx");
|
||||
$tw.utils.registerFileType("text/x-bibtex","utf8",".bib",{deserializerType:"application/x-bibtex"});
|
||||
$tw.utils.registerFileType("application/x-bibtex","utf8",".bib");
|
||||
$tw.utils.registerFileType("application/epub+zip","base64",".epub");
|
||||
$tw.utils.registerFileType("application/octet-stream","base64",".octet-stream");
|
||||
// Create the wiki store for the app
|
||||
$tw.wiki = new $tw.Wiki();
|
||||
// Install built in tiddler fields modules
|
||||
@@ -1999,6 +2154,8 @@ $tw.boot.startup = function(options) {
|
||||
if($tw.preloadTiddlers) {
|
||||
$tw.wiki.addTiddlers($tw.preloadTiddlers);
|
||||
}
|
||||
// Give hooks a chance to modify the store
|
||||
$tw.hooks.invokeHook("th-boot-tiddlers-loaded");
|
||||
// Unpack plugin tiddlers
|
||||
$tw.wiki.readPluginInfo();
|
||||
$tw.wiki.registerPluginTiddlers("plugin",$tw.safeMode ? ["$:/core"] : undefined);
|
||||
|
||||
@@ -4,7 +4,7 @@ type: text/plain
|
||||
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
|
||||
|
||||
Copyright (c) 2004-2007, Jeremy Ruston
|
||||
Copyright (c) 2007-2017, UnaMesa Association
|
||||
Copyright (c) 2007-2018, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
4
core/images/add-comment.tid
Normal file
4
core/images/add-comment.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/add-comment
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tc-image-add-comment tc-image-button" width="22pt" height="22pt" viewBox="0 0 128 128"><path d="M56 56H36a8 8 0 1 0 0 16h20v20a8 8 0 1 0 16 0V72h20a8 8 0 1 0 0-16H72V36a8 8 0 1 0-16 0v20zm-12.595 58.362c-6.683 7.659-20.297 12.903-36.006 12.903-2.196 0-4.35-.102-6.451-.3 9.652-3.836 17.356-12.24 21.01-22.874C8.516 94.28 0 79.734 0 63.5 0 33.953 28.206 10 63 10s63 23.953 63 53.5S97.794 117 63 117c-6.841 0-13.428-.926-19.595-2.638z" fill-rule="evenodd"/></svg>
|
||||
9
core/images/gitter.tid
Normal file
9
core/images/gitter.tid
Normal file
@@ -0,0 +1,9 @@
|
||||
title: $:/core/images/gitter
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tc-image-gitter tc-image-button" width="22pt" height="22pt" viewBox="0 0 18 25">
|
||||
<rect x="15" y="5" width="2" height="10"></rect>
|
||||
<rect x="10" y="5" width="2" height="20"></rect>
|
||||
<rect x="5" y="5" width="2" height="20"></rect>
|
||||
<rect width="2" height="15"></rect>
|
||||
</svg>
|
||||
4
core/images/linkify.tid
Normal file
4
core/images/linkify.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/linkify
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tc-linkify-button tc-image-button" viewBox="0 0 128 128" width="22pt" height="22pt"><path d="M17.031 31.919H9.048V96.85h7.983v6.92H0V25h17.031v6.919zm24.66 0h-7.983V96.85h7.983v6.92H24.66V25h17.03v6.919zM67.77 56.422l11.975-3.903 2.306 7.096-12.063 3.903 7.628 10.379-6.12 4.435-7.63-10.467-7.45 10.2-5.943-4.523L58.1 63.518 45.95 59.35l2.306-7.096 12.064 4.17V43.825h7.45v12.596zM86.31 96.85h7.982V31.92H86.31V25h17.031v78.77H86.31v-6.92zm24.659 0h7.983V31.92h-7.983V25H128v78.77h-17.031v-6.92z" fill-rule="evenodd"/></svg>
|
||||
4
core/images/rotate-left.tid
Normal file
4
core/images/rotate-left.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/rotate-left
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tc-image-rotate-left tc-image-button" width="22pt" height="22pt" viewBox="0 0 128 128"><g fill-rule="evenodd"><rect width="32" height="80" rx="8"/><rect x="48" y="96" width="80" height="32" rx="8"/><path d="M61.32 36.65c19.743 2.45 35.023 19.287 35.023 39.693a4 4 0 0 1-8 0c0-15.663-11.254-28.698-26.117-31.46l3.916 3.916a4 4 0 1 1-5.657 5.657L49.172 43.142a4 4 0 0 1 0-5.657l11.313-11.313a4 4 0 1 1 5.657 5.656l-4.821 4.822z"/></g></svg>
|
||||
4
core/images/transcludify.tid
Normal file
4
core/images/transcludify.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/transcludify
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg class="tc-transcludify-button tc-image-button" viewBox="0 0 128 128" width="22pt" height="22pt"><path d="M0 59.482c.591 0 1.36-.089 2.306-.266a10.417 10.417 0 0 0 2.75-.932 6.762 6.762 0 0 0 2.306-1.907c.651-.828.976-1.863.976-3.104V35.709c0-2.01.414-3.74 1.242-5.19.828-1.448 1.833-2.66 3.016-3.636s2.425-1.7 3.726-2.173c1.3-.473 2.424-.71 3.37-.71h8.073v7.451h-4.88c-1.241 0-2.232.207-2.97.621-.74.414-1.302.932-1.686 1.552a4.909 4.909 0 0 0-.71 1.996c-.089.71-.133 1.39-.133 2.04v16.677c0 1.715-.325 3.134-.976 4.258-.65 1.123-1.434 2.025-2.35 2.705-.917.68-1.863 1.168-2.839 1.464-.976.296-1.818.473-2.528.532v.178c.71.059 1.552.207 2.528.443.976.237 1.922.68 2.839 1.33.916.651 1.7 1.583 2.35 2.795.65 1.212.976 2.853.976 4.923v16.144c0 .65.044 1.33.133 2.04.089.71.325 1.375.71 1.996.384.621.946 1.139 1.685 1.553.74.414 1.73.62 2.972.62h4.879v7.452h-8.073c-.946 0-2.07-.237-3.37-.71-1.301-.473-2.543-1.197-3.726-2.173-1.183-.976-2.188-2.188-3.016-3.637-.828-1.449-1.242-3.179-1.242-5.19V74.119c0-1.42-.325-2.572-.976-3.46-.65-.886-1.419-1.581-2.306-2.084a8.868 8.868 0 0 0-2.75-1.02C1.36 67.377.591 67.288 0 67.288v-7.806zm24.66 0c.591 0 1.36-.089 2.306-.266a10.417 10.417 0 0 0 2.75-.932 6.762 6.762 0 0 0 2.306-1.907c.65-.828.976-1.863.976-3.104V35.709c0-2.01.414-3.74 1.242-5.19.828-1.448 1.833-2.66 3.016-3.636s2.425-1.7 3.726-2.173c1.3-.473 2.424-.71 3.37-.71h8.073v7.451h-4.88c-1.241 0-2.232.207-2.97.621-.74.414-1.302.932-1.686 1.552a4.909 4.909 0 0 0-.71 1.996c-.089.71-.133 1.39-.133 2.04v16.677c0 1.715-.325 3.134-.976 4.258-.65 1.123-1.434 2.025-2.35 2.705-.917.68-1.863 1.168-2.839 1.464-.976.296-1.818.473-2.528.532v.178c.71.059 1.552.207 2.528.443.976.237 1.922.68 2.839 1.33.916.651 1.7 1.583 2.35 2.795.65 1.212.976 2.853.976 4.923v16.144c0 .65.044 1.33.133 2.04.089.71.325 1.375.71 1.996.384.621.946 1.139 1.685 1.553.74.414 1.73.62 2.972.62h4.879v7.452h-8.073c-.946 0-2.07-.237-3.37-.71-1.301-.473-2.543-1.197-3.726-2.173-1.183-.976-2.188-2.188-3.016-3.637-.828-1.449-1.242-3.179-1.242-5.19V74.119c0-1.42-.325-2.572-.976-3.46-.65-.886-1.419-1.581-2.306-2.084a8.868 8.868 0 0 0-2.75-1.02c-.946-.177-1.715-.266-2.306-.266v-7.806zm43.965-3.538L80.6 52.041l2.306 7.097-12.063 3.903 7.628 10.378-6.12 4.435-7.63-10.467-7.45 10.201-5.943-4.524 7.628-10.023-12.152-4.17 2.306-7.096 12.064 4.17V43.347h7.451v12.596zm34.425 11.344c-.65 0-1.449.089-2.395.266-.946.177-1.863.488-2.75.931a6.356 6.356 0 0 0-2.262 1.908c-.62.828-.931 1.862-.931 3.104v17.564c0 2.01-.414 3.74-1.242 5.189-.828 1.449-1.833 2.661-3.016 3.637s-2.425 1.7-3.726 2.173c-1.3.473-2.424.71-3.37.71h-8.073v-7.451h4.88c1.241 0 2.232-.207 2.97-.621.74-.414 1.302-.932 1.686-1.553a4.9 4.9 0 0 0 .71-1.995c.089-.71.133-1.39.133-2.04V72.432c0-1.715.325-3.134.976-4.258.65-1.124 1.434-2.01 2.35-2.661.917-.65 1.863-1.124 2.839-1.42.976-.295 1.818-.502 2.528-.62v-.178c-.71-.059-1.552-.207-2.528-.443-.976-.237-1.922-.68-2.839-1.33-.916-.651-1.7-1.583-2.35-2.795-.65-1.212-.976-2.853-.976-4.923V37.66c0-.651-.044-1.331-.133-2.04a4.909 4.909 0 0 0-.71-1.997c-.384-.62-.946-1.138-1.685-1.552-.74-.414-1.73-.62-2.972-.62h-4.879V24h8.073c.946 0 2.07.237 3.37.71 1.301.473 2.543 1.197 3.726 2.173 1.183.976 2.188 2.188 3.016 3.637.828 1.449 1.242 3.178 1.242 5.189v16.943c0 1.419.31 2.572.931 3.46a6.897 6.897 0 0 0 2.262 2.084 8.868 8.868 0 0 0 2.75 1.02c.946.177 1.745.266 2.395.266v7.806zm24.66 0c-.65 0-1.449.089-2.395.266-.946.177-1.863.488-2.75.931a6.356 6.356 0 0 0-2.262 1.908c-.62.828-.931 1.862-.931 3.104v17.564c0 2.01-.414 3.74-1.242 5.189-.828 1.449-1.833 2.661-3.016 3.637s-2.425 1.7-3.726 2.173c-1.3.473-2.424.71-3.37.71h-8.073v-7.451h4.88c1.241 0 2.232-.207 2.97-.621.74-.414 1.302-.932 1.686-1.553a4.9 4.9 0 0 0 .71-1.995c.089-.71.133-1.39.133-2.04V72.432c0-1.715.325-3.134.976-4.258.65-1.124 1.434-2.01 2.35-2.661.917-.65 1.863-1.124 2.839-1.42.976-.295 1.818-.502 2.528-.62v-.178c-.71-.059-1.552-.207-2.528-.443-.976-.237-1.922-.68-2.839-1.33-.916-.651-1.7-1.583-2.35-2.795-.65-1.212-.976-2.853-.976-4.923V37.66c0-.651-.044-1.331-.133-2.04a4.909 4.909 0 0 0-.71-1.997c-.384-.62-.946-1.138-1.685-1.552-.74-.414-1.73-.62-2.972-.62h-4.879V24h8.073c.946 0 2.07.237 3.37.71 1.301.473 2.543 1.197 3.726 2.173 1.183.976 2.188 2.188 3.016 3.637.828 1.449 1.242 3.178 1.242 5.189v16.943c0 1.419.31 2.572.931 3.46a6.897 6.897 0 0 0 2.262 2.084 8.868 8.868 0 0 0 2.75 1.02c.946.177 1.745.266 2.395.266v7.806z" fill-rule="evenodd"/></svg>
|
||||
@@ -14,6 +14,8 @@ CloseOthers/Caption: close others
|
||||
CloseOthers/Hint: Close other tiddlers
|
||||
ControlPanel/Caption: control panel
|
||||
ControlPanel/Hint: Open control panel
|
||||
CopyToClipboard/Caption: copy to clipboard
|
||||
CopyToClipboard/Hint: Copy this text to the clipboard
|
||||
Delete/Caption: delete
|
||||
Delete/Hint: Delete this tiddler
|
||||
Edit/Caption: edit
|
||||
@@ -139,6 +141,8 @@ LineWidth/Caption: line width
|
||||
LineWidth/Hint: Set line width for painting
|
||||
Link/Caption: link
|
||||
Link/Hint: Create wikitext link
|
||||
Linkify/Caption: wikilink
|
||||
Linkify/Hint: Wrap selection in square brackets
|
||||
ListBullet/Caption: bulleted list
|
||||
ListBullet/Hint: Apply bulleted list formatting to lines containing selection
|
||||
ListNumber/Caption: numbered list
|
||||
@@ -159,6 +163,8 @@ PreviewType/Caption: preview type
|
||||
PreviewType/Hint: Choose preview type
|
||||
Quote/Caption: quote
|
||||
Quote/Hint: Apply quoted text formatting to lines containing selection
|
||||
RotateLeft/Caption: rotate left
|
||||
RotateLeft/Hint: Rotate image left by 90 degrees
|
||||
Size/Caption: image size
|
||||
Size/Caption/Height: Height:
|
||||
Size/Caption/Resize: Resize image
|
||||
@@ -175,5 +181,7 @@ Subscript/Caption: subscript
|
||||
Subscript/Hint: Apply subscript formatting to selection
|
||||
Superscript/Caption: superscript
|
||||
Superscript/Hint: Apply superscript formatting to selection
|
||||
Transcludify/Caption: transclusion
|
||||
Transcludify/Hint: Wrap selection in curly brackets
|
||||
Underline/Caption: underline
|
||||
Underline/Hint: Apply underline formatting to selection
|
||||
|
||||
@@ -13,6 +13,7 @@ Basics/Language/Prompt: Hello! Current language:
|
||||
Basics/NewJournal/Title/Prompt: Title of new journal tiddlers
|
||||
Basics/NewJournal/Text/Prompt: Text for new journal tiddlers
|
||||
Basics/NewJournal/Tags/Prompt: Tags for new journal tiddlers
|
||||
Basics/NewTiddler/Title/Prompt: Title of new tiddlers
|
||||
Basics/OverriddenShadowTiddlers/Prompt: Number of overridden shadow tiddlers:
|
||||
Basics/ShadowTiddlers/Prompt: Number of shadow tiddlers:
|
||||
Basics/Subtitle/Prompt: Subtitle:
|
||||
@@ -125,6 +126,10 @@ Settings/NavigationHistory/Caption: Navigation History
|
||||
Settings/NavigationHistory/Hint: Update browser history when navigating to a tiddler:
|
||||
Settings/NavigationHistory/No/Description: Do not update history
|
||||
Settings/NavigationHistory/Yes/Description: Update history
|
||||
Settings/NavigationPermalinkviewMode/Caption: Permalink/permaview Mode
|
||||
Settings/NavigationPermalinkviewMode/Hint: Choose how permalink/permaview is handled:
|
||||
Settings/NavigationPermalinkviewMode/CopyToClipboard/Description: Copy permalink/permaview URL to clipboard
|
||||
Settings/NavigationPermalinkviewMode/UpdateAddressBar/Description: Update address bar with permalink/permaview URL
|
||||
Settings/PerformanceInstrumentation/Caption: Performance Instrumentation
|
||||
Settings/PerformanceInstrumentation/Hint: Displays performance statistics in the browser developer console. Requires reload to take effect
|
||||
Settings/PerformanceInstrumentation/Description: Enable performance instrumentation
|
||||
@@ -139,6 +144,8 @@ Settings/ToolbarButtons/Icons/Description: Include icon
|
||||
Settings/ToolbarButtons/Text/Description: Include text
|
||||
Settings/DefaultSidebarTab/Caption: Default Sidebar Tab
|
||||
Settings/DefaultSidebarTab/Hint: Specify which sidebar tab is displayed by default
|
||||
Settings/DefaultMoreSidebarTab/Caption: Default More Sidebar Tab
|
||||
Settings/DefaultMoreSidebarTab/Hint: Specify which More sidebar tab is displayed by default
|
||||
Settings/LinkToBehaviour/Caption: Tiddler Opening Behaviour
|
||||
Settings/LinkToBehaviour/InsideRiver/Hint: Navigation from //within// the story river
|
||||
Settings/LinkToBehaviour/OutsideRiver/Hint: Navigation from //outside// the story river
|
||||
@@ -155,6 +162,10 @@ Settings/MissingLinks/Hint: Choose whether to link to tiddlers that do not exist
|
||||
Settings/MissingLinks/Description: Enable links to missing tiddlers
|
||||
StoryView/Caption: Story View
|
||||
StoryView/Prompt: Current view:
|
||||
Stylesheets/Caption: Stylesheets
|
||||
Stylesheets/Expand/Caption: Expand All
|
||||
Stylesheets/Hint: This is the rendered CSS of the current stylesheet tiddlers tagged with <<tag "$:/tags/Stylesheet">>
|
||||
Stylesheets/Restore/Caption: Restore
|
||||
Theme/Caption: Theme
|
||||
Theme/Prompt: Current theme:
|
||||
TiddlerFields/Caption: Tiddler Fields
|
||||
|
||||
@@ -2,6 +2,7 @@ title: $:/language/Docs/ModuleTypes/
|
||||
|
||||
allfilteroperator: A sub-operator for the ''all'' filter operator.
|
||||
animation: Animations that may be used with the RevealWidget.
|
||||
authenticator: Defines how requests are authenticated by the built-in HTTP server.
|
||||
bitmapeditoroperation: A bitmap editor toolbar operation.
|
||||
command: Commands that can be executed under Node.js.
|
||||
config: Data to be inserted into `$tw.config`.
|
||||
@@ -12,6 +13,7 @@ isfilteroperator: Operands for the ''is'' filter operator.
|
||||
library: Generic module type for general purpose JavaScript modules.
|
||||
macro: JavaScript macro definitions.
|
||||
parser: Parsers for different content types.
|
||||
route: Defines how individual URL patterns are handled by the built-in HTTP server.
|
||||
saver: Savers handle different methods for saving files from the browser.
|
||||
startup: Startup functions.
|
||||
storyview: Story views customise the animation and behaviour of list widgets.
|
||||
|
||||
@@ -45,6 +45,8 @@ page-background: Page background
|
||||
pre-background: Preformatted code background
|
||||
pre-border: Preformatted code border
|
||||
primary: General primary
|
||||
select-tag-background: `<select>` element background
|
||||
select-tag-foreground: `<select>` element text
|
||||
sidebar-button-foreground: Sidebar button foreground
|
||||
sidebar-controls-foreground-hover: Sidebar controls foreground hover
|
||||
sidebar-controls-foreground: Sidebar controls foreground
|
||||
|
||||
@@ -5,6 +5,8 @@ Body/Placeholder: Type the text for this tiddler
|
||||
Body/Preview/Type/Output: output
|
||||
Field/Remove/Caption: remove field
|
||||
Field/Remove/Hint: Remove field
|
||||
Field/Dropdown/Caption: field list
|
||||
Field/Dropdown/Hint: Show field list
|
||||
Fields/Add/Button: add
|
||||
Fields/Add/Name/Placeholder: field name
|
||||
Fields/Add/Prompt: Add a new field:
|
||||
@@ -20,6 +22,7 @@ Tags/Dropdown/Hint: Show tag list
|
||||
Title/BadCharacterWarning: Warning: avoid using any of the characters <<bad-chars>> in tiddler titles
|
||||
Title/Exists/Prompt: Target tiddler already exists
|
||||
Title/Relink/Prompt: Update ''<$text text=<<fromTitle>>/>'' to ''<$text text=<<toTitle>>/>'' in the //tags// and //list// fields of other tiddlers
|
||||
Title/References/Prompt: The following references to this tiddler will not be automatically updated:
|
||||
Type/Dropdown/Caption: content type list
|
||||
Type/Dropdown/Hint: Show content type list
|
||||
Type/Delete/Caption: delete content type
|
||||
|
||||
@@ -14,11 +14,12 @@ draft.of: For draft tiddlers, contains the title of the tiddler of which this is
|
||||
draft.title: For draft tiddlers, contains the proposed new title of the tiddler
|
||||
footer: The footer text for a wizard
|
||||
hack-to-give-us-something-to-compare-against: A temporary storage field used in [[$:/core/templates/static.content]]
|
||||
hide-body: The view template will hide bodies of tiddlers if set to: ''yes''
|
||||
icon: The title of the tiddler containing the icon associated with a tiddler
|
||||
library: If set to "yes" indicates that a tiddler should be saved as a JavaScript library
|
||||
library: Indicates that a tiddler should be saved as a JavaScript library if set to: ''yes''
|
||||
list: An ordered list of tiddler titles associated with a tiddler
|
||||
list-before: If set, the title of a tiddler before which this tiddler should be added to the ordered list of tiddler titles, or at the start of the list if this field is present but empty
|
||||
list-after: If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles
|
||||
list-after: If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles, or at the end of the list if this field is present but empty
|
||||
modified: The date and time at which a tiddler was last modified
|
||||
modifier: The tiddler title associated with the person who last modified a tiddler
|
||||
name: The human readable name associated with a plugin tiddler
|
||||
@@ -31,5 +32,6 @@ subtitle: The subtitle text for a wizard
|
||||
tags: A list of tags associated with a tiddler
|
||||
text: The body text of a tiddler
|
||||
title: The unique name of a tiddler
|
||||
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
|
||||
type: The content type of a tiddler
|
||||
version: Version information for a plugin
|
||||
|
||||
@@ -10,6 +10,7 @@ Orphans: Orphan tiddlers
|
||||
SystemTiddlers: System tiddlers
|
||||
ShadowTiddlers: Shadow tiddlers
|
||||
OverriddenShadowTiddlers: Overridden shadow tiddlers
|
||||
SessionTiddlers: Tiddlers modified since the wiki was loaded
|
||||
SystemTags: System tags
|
||||
StoryList: Tiddlers in the story river, excluding <$text text="$:/AdvancedSearch"/>
|
||||
TypedTiddlers: Non wiki-text tiddlers
|
||||
@@ -3,7 +3,7 @@ title: GettingStarted
|
||||
\define lingo-base() $:/language/ControlPanel/Basics/
|
||||
Welcome to ~TiddlyWiki and the ~TiddlyWiki community
|
||||
|
||||
Before you start storing important information in ~TiddlyWiki it is important to make sure that you can reliably save changes. See https://tiddlywiki.com/#GettingStarted for details
|
||||
Before you start storing important information in ~TiddlyWiki it is vital to make sure that you can reliably save changes. See https://tiddlywiki.com/#GettingStarted for details
|
||||
|
||||
!! Set up this ~TiddlyWiki
|
||||
|
||||
|
||||
34
core/language/en-GB/Help/listen.tid
Normal file
34
core/language/en-GB/Help/listen.tid
Normal file
@@ -0,0 +1,34 @@
|
||||
title: $:/language/Help/listen
|
||||
description: Provides an HTTP server interface to TiddlyWiki
|
||||
|
||||
Serves a wiki over HTTP.
|
||||
|
||||
The listen command uses NamedCommandParameters:
|
||||
|
||||
```
|
||||
--listen [<name>=<value>]...
|
||||
```
|
||||
|
||||
All parameters are optional with safe defaults, and can be specified in any order. The recognised parameters are:
|
||||
|
||||
* ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost")
|
||||
* ''path-prefix'' - optional prefix for paths
|
||||
* ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080")
|
||||
* ''credentials'' - pathname of credentials CSV file (relative to wiki folder)
|
||||
* ''anon-username'' - the username for signing edits for anonymous users
|
||||
* ''username'' - optional username for basic authentication
|
||||
* ''password'' - optional password for basic authentication
|
||||
* ''authenticated-user-header'' - optional name of header to be used for trusted authentication
|
||||
* ''readers'' - comma separated list of principals allowed to read from this wiki
|
||||
* ''writers'' - comma separated list of principals allowed to write to this wiki
|
||||
* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no")
|
||||
* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
|
||||
* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
|
||||
* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html")
|
||||
* ''tls-cert'' - pathname of TLS certificate file (relative to wiki folder)
|
||||
* ''tls-key'' - pathname of TLS key file (relative to wiki folder)
|
||||
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
|
||||
* ''gzip'' - set to "yes" to enable gzip compression for some http endpoints (defaults to "no")
|
||||
|
||||
For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
|
||||
|
||||
@@ -4,10 +4,12 @@ description: Load tiddlers from a file
|
||||
Load tiddlers from TiddlyWiki (`.html`), `.tiddler`, `.tid`, `.json` or other local files. The processing applied to incoming files is determined by the file extension. Use the alternative `import` command if you need to specify the deserializer and encoding explicitly.
|
||||
|
||||
```
|
||||
--load <filepath>
|
||||
--load <dirpath>
|
||||
--load <filepath> [noerror]
|
||||
--load <dirpath> [noerror]
|
||||
```
|
||||
|
||||
By default, the load command raises an error if no tiddlers are found. The error can be suppressed by providing the optional "noerror" parameter.
|
||||
|
||||
To load tiddlers from an encrypted TiddlyWiki file you should first specify the password with the PasswordCommand. For example:
|
||||
|
||||
```
|
||||
|
||||
@@ -13,8 +13,8 @@ A name and value for an additional variable may optionally also be specified.
|
||||
|
||||
* ''tiddler-filter'': A filter identifying the tiddler(s) to be rendered
|
||||
* ''filename-filter'': Optional filter transforming tiddler titles into pathnames. If omitted, defaults to `[is[tiddler]addsuffix[.html]]`, which uses the unchanged tiddler title as the filename
|
||||
* ''template'': Optional template through which each tiddler is rendered
|
||||
* ''render-type'': Optional render type: `text/html` (the default) returns the full HTML text and `text/plain` just returns the text content (ie it ignores HTML tags and other unprintable material)
|
||||
* ''template'': Optional template through which each tiddler is rendered
|
||||
* ''name'': Name of optional variable
|
||||
* ''value'': Value of optional variable
|
||||
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
title: $:/language/Help/server
|
||||
description: Provides an HTTP server interface to TiddlyWiki
|
||||
description: Provides an HTTP server interface to TiddlyWiki (deprecated in favour of the new listen command)
|
||||
|
||||
The server built in to TiddlyWiki5 is very simple. Although compatible with TiddlyWeb it doesn't support many of the features needed for robust Internet-facing usage.
|
||||
|
||||
At the root, it serves a rendering of a specified tiddler. Away from the root, it serves individual tiddlers encoded in JSON, and supports the basic HTTP operations for `GET`, `PUT` and `DELETE`.
|
||||
Legacy command to serve a wiki over HTTP.
|
||||
|
||||
```
|
||||
--server <port> <roottiddler> <rendertype> <servetype> <username> <password> <host> <pathprefix>
|
||||
--server <port> <root-tiddler> <root-render-type> <root-serve-type> <username> <password> <host> <path-prefix> <debug-level>
|
||||
```
|
||||
|
||||
The parameters are:
|
||||
|
||||
* ''port'' - port number to serve from (defaults to "8080")
|
||||
* ''roottiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
|
||||
* ''rendertype'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
|
||||
* ''servetype'' - the content type with which the root tiddler should be served (defaults to "text/html")
|
||||
* ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080")
|
||||
* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
|
||||
* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
|
||||
* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html")
|
||||
* ''username'' - the default username for signing edits
|
||||
* ''password'' - optional password for basic authentication
|
||||
* ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost")
|
||||
* ''pathprefix'' - optional prefix for paths
|
||||
* ''path-prefix'' - optional prefix for paths
|
||||
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
|
||||
|
||||
If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation isn't suitable for general use.
|
||||
If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation should only be used on a trusted network or over HTTPS.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -28,10 +27,17 @@ For example:
|
||||
--server 8080 $:/core/save/all text/plain text/html MyUserName passw0rd
|
||||
```
|
||||
|
||||
The username and password can be specified as empty strings if you need to set the hostname or pathprefix and don't want to require a password:
|
||||
The username and password can be specified as empty strings if you need to set the hostname or pathprefix and don't want to require a password.
|
||||
|
||||
|
||||
```
|
||||
--server 8080 $:/core/save/all text/plain text/html "" "" 192.168.0.245
|
||||
```
|
||||
|
||||
To run multiple TiddlyWiki servers at the same time you'll need to put each one on a different port.
|
||||
Using an address like this exposes your system to the local network. For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
|
||||
|
||||
To run multiple TiddlyWiki servers at the same time you'll need to put each one on a different port. It can be useful to use an environment variable to pass the port number to the Node.js process. This example references an environment variable called "MY_PORT_NUMBER":
|
||||
|
||||
```
|
||||
--server MY_PORT_NUMBER $:/core/save/all text/plain text/html MyUserName passw0rd
|
||||
```
|
||||
|
||||
@@ -7,9 +7,17 @@ Listing/Import/Caption: Import
|
||||
Listing/Select/Caption: Select
|
||||
Listing/Status/Caption: Status
|
||||
Listing/Title/Caption: Title
|
||||
Listing/Preview: Preview:
|
||||
Listing/Preview/Text: Text
|
||||
Listing/Preview/TextRaw: Text (Raw)
|
||||
Listing/Preview/Fields: Fields
|
||||
Listing/Preview/Diff: Diff
|
||||
Listing/Preview/DiffFields: Diff (Fields)
|
||||
Upgrader/Plugins/Suppressed/Incompatible: Blocked incompatible or obsolete plugin
|
||||
Upgrader/Plugins/Suppressed/Version: Blocked plugin (due to incoming <<incoming>> being older than existing <<existing>>)
|
||||
Upgrader/Plugins/Upgraded: Upgraded plugin from <<incoming>> to <<upgraded>>
|
||||
Upgrader/State/Suppressed: Blocked temporary state tiddler
|
||||
Upgrader/System/Suppressed: Blocked system tiddler
|
||||
Upgrader/System/Warning: Core module tiddler
|
||||
Upgrader/System/Alert: You are about to import a tiddler that will overwrite a core module tiddler. This is not recommended as it may make the system unstable
|
||||
Upgrader/ThemeTweaks/Created: Migrated theme tweak from <$text text=<<from>>/>
|
||||
|
||||
@@ -12,6 +12,7 @@ ConfirmOverwriteTiddler: Do you wish to overwrite the tiddler "<$text text=<<tit
|
||||
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>>/>"?
|
||||
Count: count
|
||||
DefaultNewTiddlerTitle: New Tiddler
|
||||
Diffs/CountMessage: <<diff-count>> differences
|
||||
DropMessage: Drop here (or use the 'Escape' key to cancel)
|
||||
Encryption/Cancel: Cancel
|
||||
Encryption/ConfirmClearPassword: Do you wish to clear the password? This will remove the encryption applied when saving this wiki
|
||||
|
||||
@@ -2,3 +2,5 @@ title: $:/language/Notifications/
|
||||
|
||||
Save/Done: Saved wiki
|
||||
Save/Starting: Starting to save wiki
|
||||
CopiedToClipboard/Succeeded: Copied to clipboard!
|
||||
CopiedToClipboard/Failed: Failed to copy to clipboard!
|
||||
|
||||
@@ -7,7 +7,7 @@ Options/SidebarLayout: Sidebar layout
|
||||
Options/SidebarLayout/Fixed-Fluid: Fixed story, fluid sidebar
|
||||
Options/SidebarLayout/Fluid-Fixed: Fluid story, fixed sidebar
|
||||
Options/StickyTitles: Sticky titles
|
||||
Options/StickyTitles/Hint: Causes tiddler titles to "stick" to the top of the browser window. Caution: Does not work at all with Chrome, and causes some layout issues in Firefox
|
||||
Options/StickyTitles/Hint: Causes tiddler titles to "stick" to the top of the browser window
|
||||
Options/CodeWrapping: Wrap long lines in code blocks
|
||||
Settings: Settings
|
||||
Settings/FontFamily: Font family
|
||||
|
||||
@@ -94,6 +94,13 @@ Commander.prototype.executeNextCommand = function() {
|
||||
if(this.verbose) {
|
||||
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
|
||||
}
|
||||
// Parse named parameters if required
|
||||
if(command.info.namedParameterMode) {
|
||||
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
|
||||
if(typeof params === "string") {
|
||||
return this.callback(params);
|
||||
}
|
||||
}
|
||||
if(command.info.synchronous) {
|
||||
// Synchronous command
|
||||
c = new command.Command(params,this);
|
||||
@@ -122,6 +129,35 @@ Commander.prototype.executeNextCommand = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Given an array of parameter strings `params` in name:value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error
|
||||
*/
|
||||
Commander.prototype.extractNamedParameters = function(params,mandatoryParameters) {
|
||||
mandatoryParameters = mandatoryParameters || [];
|
||||
var errors = [],
|
||||
paramsByName = Object.create(null);
|
||||
// Extract the parameters
|
||||
$tw.utils.each(params,function(param) {
|
||||
var index = param.indexOf("=");
|
||||
if(index < 1) {
|
||||
errors.push("malformed named parameter: '" + param + "'");
|
||||
}
|
||||
paramsByName[param.slice(0,index)] = $tw.utils.trim(param.slice(index+1));
|
||||
});
|
||||
// Check the mandatory parameters are present
|
||||
$tw.utils.each(mandatoryParameters,function(mandatoryParameter) {
|
||||
if(!$tw.utils.hop(paramsByName,mandatoryParameter)) {
|
||||
errors.push("missing mandatory parameter: '" + mandatoryParameter + "'");
|
||||
}
|
||||
});
|
||||
// Return any errors
|
||||
if(errors.length > 0) {
|
||||
return errors.join(" and\n");
|
||||
} else {
|
||||
return paramsByName;
|
||||
}
|
||||
};
|
||||
|
||||
Commander.initCommands = function(moduleType) {
|
||||
moduleType = moduleType || "command";
|
||||
$tw.commands = {};
|
||||
|
||||
48
core/modules/commands/listen.js
Normal file
48
core/modules/commands/listen.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/listen.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Listen for HTTP requests and serve tiddlers
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Server = require("$:/core/modules/server/server.js").Server;
|
||||
|
||||
exports.info = {
|
||||
name: "listen",
|
||||
synchronous: true,
|
||||
namedParameterMode: true,
|
||||
mandatoryParameters: [],
|
||||
};
|
||||
|
||||
var Command = function(params,commander,callback) {
|
||||
var self = this;
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
var self = this;
|
||||
if(!$tw.boot.wikiTiddlersPath) {
|
||||
$tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file");
|
||||
}
|
||||
// Set up server
|
||||
this.server = new Server({
|
||||
wiki: this.commander.wiki,
|
||||
variables: self.params
|
||||
});
|
||||
var nodeServer = this.server.listen();
|
||||
$tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer,"tiddlywiki");
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
||||
@@ -38,7 +38,7 @@ Command.prototype.execute = function() {
|
||||
count++;
|
||||
});
|
||||
});
|
||||
if(!count) {
|
||||
if(!count && self.params[1] !== "noerror") {
|
||||
self.callback("No tiddlers found in file \"" + self.params[0] + "\"");
|
||||
} else {
|
||||
self.callback(null);
|
||||
|
||||
@@ -3,7 +3,7 @@ title: $:/core/modules/commands/server.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Serve tiddlers over http
|
||||
Deprecated legacy command for serving tiddlers
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@@ -12,300 +12,41 @@ Serve tiddlers over http
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
if($tw.node) {
|
||||
var util = require("util"),
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
http = require("http");
|
||||
}
|
||||
var Server = require("$:/core/modules/server/server.js").Server;
|
||||
|
||||
exports.info = {
|
||||
name: "server",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
/*
|
||||
A simple HTTP server with regexp-based routes
|
||||
*/
|
||||
function SimpleServer(options) {
|
||||
this.routes = options.routes || [];
|
||||
this.wiki = options.wiki;
|
||||
this.variables = options.variables || {};
|
||||
}
|
||||
|
||||
SimpleServer.prototype.set = function(obj) {
|
||||
var self = this;
|
||||
$tw.utils.each(obj,function(value,name) {
|
||||
self.variables[name] = value;
|
||||
});
|
||||
};
|
||||
|
||||
SimpleServer.prototype.get = function(name) {
|
||||
return this.variables[name];
|
||||
};
|
||||
|
||||
SimpleServer.prototype.addRoute = function(route) {
|
||||
this.routes.push(route);
|
||||
};
|
||||
|
||||
SimpleServer.prototype.findMatchingRoute = function(request,state) {
|
||||
var pathprefix = this.get("pathprefix") || "";
|
||||
for(var t=0; t<this.routes.length; t++) {
|
||||
var potentialRoute = this.routes[t],
|
||||
pathRegExp = potentialRoute.path,
|
||||
pathname = state.urlInfo.pathname,
|
||||
match;
|
||||
if(pathprefix) {
|
||||
if(pathname.substr(0,pathprefix.length) === pathprefix) {
|
||||
pathname = pathname.substr(pathprefix.length);
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
} else {
|
||||
match = false;
|
||||
}
|
||||
} else {
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
}
|
||||
if(match && request.method === potentialRoute.method) {
|
||||
state.params = [];
|
||||
for(var p=1; p<match.length; p++) {
|
||||
state.params.push(match[p]);
|
||||
}
|
||||
return potentialRoute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
SimpleServer.prototype.checkCredentials = function(request,incomingUsername,incomingPassword) {
|
||||
var header = request.headers.authorization || "",
|
||||
token = header.split(/\s+/).pop() || "",
|
||||
auth = $tw.utils.base64Decode(token),
|
||||
parts = auth.split(/:/),
|
||||
username = parts[0],
|
||||
password = parts[1];
|
||||
if(incomingUsername === username && incomingPassword === password) {
|
||||
return "ALLOWED";
|
||||
} else {
|
||||
return "DENIED";
|
||||
}
|
||||
};
|
||||
|
||||
SimpleServer.prototype.requestHandler = function(request,response) {
|
||||
// Compose the state object
|
||||
var self = this;
|
||||
var state = {};
|
||||
state.wiki = self.wiki;
|
||||
state.server = self;
|
||||
state.urlInfo = url.parse(request.url);
|
||||
// Find the route that matches this path
|
||||
var route = self.findMatchingRoute(request,state);
|
||||
// Check for the username and password if we've got one
|
||||
var username = self.get("username"),
|
||||
password = self.get("password");
|
||||
if(username && password) {
|
||||
// Check they match
|
||||
if(self.checkCredentials(request,username,password) !== "ALLOWED") {
|
||||
var servername = state.wiki.getTiddlerText("$:/SiteTitle") || "TiddlyWiki5";
|
||||
response.writeHead(401,"Authentication required",{
|
||||
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + servername + '"'
|
||||
});
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Return a 404 if we didn't find a route
|
||||
if(!route) {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
// Set the encoding for the incoming request
|
||||
// TODO: Presumably this would need tweaking if we supported PUTting binary tiddlers
|
||||
request.setEncoding("utf8");
|
||||
// Dispatch the appropriate method
|
||||
switch(request.method) {
|
||||
case "GET": // Intentional fall-through
|
||||
case "DELETE":
|
||||
route.handler(request,response,state);
|
||||
break;
|
||||
case "PUT":
|
||||
var data = "";
|
||||
request.on("data",function(chunk) {
|
||||
data += chunk.toString();
|
||||
});
|
||||
request.on("end",function() {
|
||||
state.data = data;
|
||||
route.handler(request,response,state);
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
SimpleServer.prototype.listen = function(port,host) {
|
||||
http.createServer(this.requestHandler.bind(this)).listen(port,host);
|
||||
};
|
||||
|
||||
var Command = function(params,commander,callback) {
|
||||
var self = this;
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
this.callback = callback;
|
||||
// Set up server
|
||||
this.server = new SimpleServer({
|
||||
wiki: this.commander.wiki
|
||||
});
|
||||
// Add route handlers
|
||||
this.server.addRoute({
|
||||
method: "PUT",
|
||||
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
|
||||
handler: function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]),
|
||||
fields = JSON.parse(state.data);
|
||||
// Pull up any subfields in the `fields` object
|
||||
if(fields.fields) {
|
||||
$tw.utils.each(fields.fields,function(field,name) {
|
||||
fields[name] = field;
|
||||
});
|
||||
delete fields.fields;
|
||||
}
|
||||
// Remove any revision field
|
||||
if(fields.revision) {
|
||||
delete fields.revision;
|
||||
}
|
||||
state.wiki.addTiddler(new $tw.Tiddler(state.wiki.getCreationFields(),fields,{title: title},state.wiki.getModificationFields()));
|
||||
var changeCount = state.wiki.getChangeCount(title).toString();
|
||||
response.writeHead(204, "OK",{
|
||||
Etag: "\"default/" + encodeURIComponent(title) + "/" + changeCount + ":\"",
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "DELETE",
|
||||
path: /^\/bags\/default\/tiddlers\/(.+)$/,
|
||||
handler: function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]);
|
||||
state.wiki.deleteTiddler(title);
|
||||
response.writeHead(204, "OK", {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "GET",
|
||||
path: /^\/$/,
|
||||
handler: function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": state.server.get("serveType")});
|
||||
var text = state.wiki.renderTiddler(state.server.get("renderType"),state.server.get("rootTiddler"));
|
||||
response.end(text,"utf8");
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "GET",
|
||||
path: /^\/status$/,
|
||||
handler: function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
var text = JSON.stringify({
|
||||
username: state.server.get("username"),
|
||||
space: {
|
||||
recipe: "default"
|
||||
},
|
||||
tiddlywiki_version: $tw.version
|
||||
});
|
||||
response.end(text,"utf8");
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "GET",
|
||||
path: /^\/favicon.ico$/,
|
||||
handler: function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "image/x-icon"});
|
||||
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
|
||||
response.end(buffer,"base64");
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "GET",
|
||||
path: /^\/recipes\/default\/tiddlers.json$/,
|
||||
handler: function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
var tiddlers = [];
|
||||
state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) {
|
||||
var tiddlerFields = {};
|
||||
$tw.utils.each(tiddler.fields,function(field,name) {
|
||||
if(name !== "text") {
|
||||
tiddlerFields[name] = tiddler.getFieldString(name);
|
||||
}
|
||||
});
|
||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||
tiddlers.push(tiddlerFields);
|
||||
});
|
||||
var text = JSON.stringify(tiddlers);
|
||||
response.end(text,"utf8");
|
||||
}
|
||||
});
|
||||
this.server.addRoute({
|
||||
method: "GET",
|
||||
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
|
||||
handler: function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]),
|
||||
tiddler = state.wiki.getTiddler(title),
|
||||
tiddlerFields = {},
|
||||
knownFields = [
|
||||
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
|
||||
];
|
||||
if(tiddler) {
|
||||
$tw.utils.each(tiddler.fields,function(field,name) {
|
||||
var value = tiddler.getFieldString(name);
|
||||
if(knownFields.indexOf(name) !== -1) {
|
||||
tiddlerFields[name] = value;
|
||||
} else {
|
||||
tiddlerFields.fields = tiddlerFields.fields || {};
|
||||
tiddlerFields.fields[name] = value;
|
||||
}
|
||||
});
|
||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
response.end(JSON.stringify(tiddlerFields),"utf8");
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
if(!$tw.boot.wikiTiddlersPath) {
|
||||
$tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file");
|
||||
}
|
||||
var port = this.params[0] || "8080",
|
||||
rootTiddler = this.params[1] || "$:/core/save/all",
|
||||
renderType = this.params[2] || "text/plain",
|
||||
serveType = this.params[3] || "text/html",
|
||||
username = this.params[4],
|
||||
password = this.params[5],
|
||||
host = this.params[6] || "127.0.0.1",
|
||||
pathprefix = this.params[7];
|
||||
this.server.set({
|
||||
rootTiddler: rootTiddler,
|
||||
renderType: renderType,
|
||||
serveType: serveType,
|
||||
username: username,
|
||||
password: password,
|
||||
pathprefix: pathprefix
|
||||
// Set up server
|
||||
this.server = new Server({
|
||||
wiki: this.commander.wiki,
|
||||
variables: {
|
||||
port: this.params[0],
|
||||
host: this.params[6],
|
||||
"root-tiddler": this.params[1],
|
||||
"root-render-type": this.params[2],
|
||||
"root-serve-type": this.params[3],
|
||||
username: this.params[4],
|
||||
password: this.params[5],
|
||||
"path-prefix": this.params[7],
|
||||
"debug-level": this.params[8]
|
||||
}
|
||||
});
|
||||
this.server.listen(port,host);
|
||||
$tw.utils.log("Serving on " + host + ":" + port,"brown/orange");
|
||||
$tw.utils.log("(press ctrl-C to exit)","red");
|
||||
// Warn if required plugins are missing
|
||||
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
|
||||
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
|
||||
}
|
||||
var nodeServer = this.server.listen();
|
||||
$tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer,"tiddlywiki");
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -70,10 +70,14 @@ function FramedEngine(options) {
|
||||
if(this.widget.editRows) {
|
||||
this.domNode.setAttribute("rows",this.widget.editRows);
|
||||
}
|
||||
if(this.widget.editTabIndex) {
|
||||
this.iframeNode.setAttribute("tabindex",this.widget.editTabIndex);
|
||||
}
|
||||
// Copy the styles from the dummy textarea
|
||||
this.copyStyles();
|
||||
// Add event listeners
|
||||
$tw.utils.addEventListeners(this.domNode,[
|
||||
{name: "click",handlerObject: this,handlerMethod: "handleClickEvent"},
|
||||
{name: "input",handlerObject: this,handlerMethod: "handleInputEvent"},
|
||||
{name: "keydown",handlerObject: this.widget,handlerMethod: "handleKeydownEvent"}
|
||||
]);
|
||||
@@ -147,6 +151,14 @@ FramedEngine.prototype.focus = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a click
|
||||
*/
|
||||
FramedEngine.prototype.handleClickEvent = function(event) {
|
||||
this.fixHeight();
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a dom "input" event which occurs when the text has changed
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,9 @@ function SimpleEngine(options) {
|
||||
if(this.widget.editClass) {
|
||||
this.domNode.className = this.widget.editClass;
|
||||
}
|
||||
if(this.widget.editTabIndex) {
|
||||
this.domNode.setAttribute("tabindex",this.widget.editTabIndex);
|
||||
}
|
||||
// Add an input event handler
|
||||
$tw.utils.addEventListeners(this.domNode,[
|
||||
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
|
||||
|
||||
@@ -176,6 +176,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
this.editMinHeight = this.getAttribute("minHeight",DEFAULT_MIN_TEXT_AREA_HEIGHT);
|
||||
this.editFocusPopup = this.getAttribute("focusPopup");
|
||||
this.editFocus = this.getAttribute("focus");
|
||||
this.editTabIndex = this.getAttribute("tabindex");
|
||||
// Get the default editor element tag and type
|
||||
var tag,type;
|
||||
if(this.editField === "text") {
|
||||
@@ -198,7 +199,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
this.makeChildWidgets();
|
||||
// Determine whether to show the toolbar
|
||||
this.editShowToolbar = this.wiki.getTiddlerText(ENABLE_TOOLBAR_TITLE,"yes");
|
||||
this.editShowToolbar = (this.editShowToolbar === "yes") && !!(this.children && this.children.length > 0);
|
||||
this.editShowToolbar = (this.editShowToolbar === "yes") && !!(this.children && this.children.length > 0) && (!this.document.isTiddlyWikiFakeDom);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -207,7 +208,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
EditTextWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Completely rerender if any of our attributes have changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE]) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedTiddlers[this.editTitle]) {
|
||||
@@ -216,7 +217,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
}
|
||||
this.engine.fixHeight();
|
||||
if(this.editShowToolbar) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -266,7 +267,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
el.dispatchEvent(clickEvent);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
core/modules/editor/operations/bitmap/rotate-left.js
Normal file
24
core/modules/editor/operations/bitmap/rotate-left.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/*\
|
||||
title: $:/core/modules/editor/operations/bitmap/rotate-left.js
|
||||
type: application/javascript
|
||||
module-type: bitmapeditoroperation
|
||||
|
||||
Bitmap editor operation to rotate the image left by 90 degrees
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["rotate-left"] = function(event) {
|
||||
// Rotate the canvas left by 90 degrees
|
||||
this.rotateCanvasLeft();
|
||||
// Update the input controls
|
||||
this.refreshToolbar();
|
||||
// Save the image into the tiddler
|
||||
this.saveChanges();
|
||||
};
|
||||
|
||||
})();
|
||||
23
core/modules/editor/operations/text/save-selection.js
Normal file
23
core/modules/editor/operations/text/save-selection.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/*\
|
||||
title: $:/core/modules/editor/operations/text/save-selection.js
|
||||
type: application/javascript
|
||||
module-type: texteditoroperation
|
||||
|
||||
Text editor operation to save the current selection in a specified tiddler
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["save-selection"] = function(event,operation) {
|
||||
var tiddler = event.paramObject.tiddler,
|
||||
field = event.paramObject.field || "text";
|
||||
if(tiddler && field) {
|
||||
this.wiki.setText(tiddler,field,null,operation.text.substring(operation.selStart,operation.selEnd));
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -16,14 +16,12 @@ exports["wrap-selection"] = function(event,operation) {
|
||||
if(operation.selStart === operation.selEnd) {
|
||||
// No selection; check if we're within the prefix/suffix
|
||||
if(operation.text.substring(operation.selStart - event.paramObject.prefix.length,operation.selStart + event.paramObject.suffix.length) === event.paramObject.prefix + event.paramObject.suffix) {
|
||||
// Remove the prefix and suffix unless they comprise the entire text
|
||||
if(operation.selStart > event.paramObject.prefix.length || (operation.selEnd + event.paramObject.suffix.length) < operation.text.length ) {
|
||||
operation.cutStart = operation.selStart - event.paramObject.prefix.length;
|
||||
operation.cutEnd = operation.selEnd + event.paramObject.suffix.length;
|
||||
operation.replacement = "";
|
||||
operation.newSelStart = operation.cutStart;
|
||||
operation.newSelEnd = operation.newSelStart;
|
||||
}
|
||||
// Remove the prefix and suffix
|
||||
operation.cutStart = operation.selStart - event.paramObject.prefix.length;
|
||||
operation.cutEnd = operation.selEnd + event.paramObject.suffix.length;
|
||||
operation.replacement = "";
|
||||
operation.newSelStart = operation.cutStart;
|
||||
operation.newSelEnd = operation.newSelStart;
|
||||
} else {
|
||||
// Wrap the cursor instead
|
||||
operation.cutStart = operation.selStart;
|
||||
|
||||
@@ -40,12 +40,23 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
nextBracketPos += p;
|
||||
var bracket = filterString.charAt(nextBracketPos);
|
||||
operator.operator = filterString.substring(p,nextBracketPos);
|
||||
|
||||
// Any suffix?
|
||||
var colon = operator.operator.indexOf(':');
|
||||
if(colon > -1) {
|
||||
// The raw suffix for older filters
|
||||
operator.suffix = operator.operator.substring(colon + 1);
|
||||
operator.operator = operator.operator.substring(0,colon) || "field";
|
||||
// The processed suffix for newer filters
|
||||
operator.suffixes = [];
|
||||
$tw.utils.each(operator.suffix.split(":"),function(subsuffix) {
|
||||
operator.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operator.suffixes[operator.suffixes.length - 1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// Empty operator means: title
|
||||
else if(operator.operator === "") {
|
||||
@@ -108,7 +119,7 @@ exports.parseFilter = function(filterString) {
|
||||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
operandRegExp = /((?:\+|\-|~)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
@@ -208,6 +219,7 @@ exports.compileFilter = function(filterString) {
|
||||
operand: operand,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
@@ -247,6 +259,13 @@ exports.compileFilter = function(filterString) {
|
||||
results.splice(0,results.length);
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
};
|
||||
case "~": // This operation is unioned into the result only if the main result so far is empty
|
||||
return function(results,source,widget) {
|
||||
if(results.length === 0) {
|
||||
// Main result so far is empty
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
}
|
||||
};
|
||||
}
|
||||
})());
|
||||
});
|
||||
|
||||
45
core/modules/filters/contains.js
Normal file
45
core/modules/filters/contains.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/contains.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for finding values in array fields
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.contains = function(source,operator,options) {
|
||||
var results = [],
|
||||
fieldname = (operator.suffix || "list").toLowerCase();
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
var list = tiddler.getFieldList(fieldname);
|
||||
if(list.indexOf(operator.operand) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
} else {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
var list = tiddler.getFieldList(fieldname);
|
||||
if(list.indexOf(operator.operand) !== -1) {
|
||||
results.push(title);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -18,18 +18,34 @@ Export our filter function
|
||||
*/
|
||||
exports.each = function(source,operator,options) {
|
||||
var results =[] ,
|
||||
value,values = {},
|
||||
field = operator.operand || "title";
|
||||
if(operator.suffix !== "list-item") {
|
||||
value,values = {},
|
||||
field = operator.operand || "title";
|
||||
if(operator.suffix === "value" && field === "title") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
value = (field === "title") ? title : tiddler.getFieldString(field);
|
||||
if(!$tw.utils.hop(values,value)) {
|
||||
values[value] = true;
|
||||
results.push(title);
|
||||
}
|
||||
if(!$tw.utils.hop(values,title)) {
|
||||
values[title] = true;
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else if(operator.suffix !== "list-item") {
|
||||
if(field === "title") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && !$tw.utils.hop(values,title)) {
|
||||
values[title] = true;
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
value = tiddler.getFieldString(field);
|
||||
if(!$tw.utils.hop(values,value)) {
|
||||
values[value] = true;
|
||||
results.push(title);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
|
||||
@@ -19,7 +19,12 @@ Export our filter functions
|
||||
exports.decodeuricomponent = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(decodeURIComponent(title));
|
||||
var value = title;
|
||||
try {
|
||||
value = decodeURIComponent(title);
|
||||
} catch(e) {
|
||||
}
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
@@ -35,7 +40,12 @@ exports.encodeuricomponent = function(source,operator,options) {
|
||||
exports.decodeuri = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(decodeURI(title));
|
||||
var value = title;
|
||||
try {
|
||||
value = decodeURI(title);
|
||||
} catch(e) {
|
||||
}
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -45,4 +45,4 @@ exports.is = function(source,operator,options) {
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
36
core/modules/filters/is/variable.js
Normal file
36
core/modules/filters/is/variable.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/is/variable.js
|
||||
type: application/javascript
|
||||
module-type: isfilteroperator
|
||||
|
||||
Filter function for [is[variable]]
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.variable = function(source,prefix,options) {
|
||||
var results = [];
|
||||
if(prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(!(title in options.widget.variables)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(title in options.widget.variables) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -24,7 +24,7 @@ exports.order = function(source,operator,options) {
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
@@ -44,7 +44,7 @@ exports.reverse = function(source,operator,options) {
|
||||
First entry/entries in list
|
||||
*/
|
||||
exports.first = function(source,operator,options) {
|
||||
var count = parseInt(operator.operand) || 1,
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
@@ -56,7 +56,7 @@ exports.first = function(source,operator,options) {
|
||||
Last entry/entries in list
|
||||
*/
|
||||
exports.last = function(source,operator,options) {
|
||||
var count = parseInt(operator.operand) || 1,
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
@@ -68,7 +68,7 @@ exports.last = function(source,operator,options) {
|
||||
All but the first entry/entries of the list
|
||||
*/
|
||||
exports.rest = function(source,operator,options) {
|
||||
var count = parseInt(operator.operand) || 1,
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
@@ -82,7 +82,7 @@ exports.bf = exports.rest;
|
||||
All but the last entry/entries of the list
|
||||
*/
|
||||
exports.butlast = function(source,operator,options) {
|
||||
var count = parseInt(operator.operand) || 1,
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
@@ -95,7 +95,7 @@ exports.bl = exports.butlast;
|
||||
The nth member of the list
|
||||
*/
|
||||
exports.nth = function(source,operator,options) {
|
||||
var count = parseInt(operator.operand) || 1,
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
|
||||
142
core/modules/filters/math.js
Normal file
142
core/modules/filters/math.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/math.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operators for math. Unary/binary operators work on each item in turn, and return a new item list.
|
||||
|
||||
Sum/product/maxall/minall operate on the entire list, returning a single item.
|
||||
|
||||
Note that strings are converted to numbers automatically. Trailing non-digits are ignored.
|
||||
|
||||
* "" converts to 0
|
||||
* "12kk" converts to 12
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.negate = makeNumericBinaryOperator(
|
||||
function(a) {return -a}
|
||||
);
|
||||
|
||||
exports.abs = makeNumericBinaryOperator(
|
||||
function(a) {return Math.abs(a)}
|
||||
);
|
||||
|
||||
exports.ceil = makeNumericBinaryOperator(
|
||||
function(a) {return Math.ceil(a)}
|
||||
);
|
||||
|
||||
exports.floor = makeNumericBinaryOperator(
|
||||
function(a) {return Math.floor(a)}
|
||||
);
|
||||
|
||||
exports.round = makeNumericBinaryOperator(
|
||||
function(a) {return Math.round(a)}
|
||||
);
|
||||
|
||||
exports.trunc = makeNumericBinaryOperator(
|
||||
function(a) {return Math.trunc(a)}
|
||||
);
|
||||
|
||||
exports.sign = makeNumericBinaryOperator(
|
||||
function(a) {return Math.sign(a)}
|
||||
);
|
||||
|
||||
exports.add = makeNumericBinaryOperator(
|
||||
function(a,b) {return a + b;}
|
||||
);
|
||||
|
||||
exports.subtract = makeNumericBinaryOperator(
|
||||
function(a,b) {return a - b;}
|
||||
);
|
||||
|
||||
exports.multiply = makeNumericBinaryOperator(
|
||||
function(a,b) {return a * b;}
|
||||
);
|
||||
|
||||
exports.divide = makeNumericBinaryOperator(
|
||||
function(a,b) {return a / b;}
|
||||
);
|
||||
|
||||
exports.remainder = makeNumericBinaryOperator(
|
||||
function(a,b) {return a % b;}
|
||||
);
|
||||
|
||||
exports.max = makeNumericBinaryOperator(
|
||||
function(a,b) {return Math.max(a,b);}
|
||||
);
|
||||
|
||||
exports.min = makeNumericBinaryOperator(
|
||||
function(a,b) {return Math.min(a,b);}
|
||||
);
|
||||
|
||||
exports.fixed = makeNumericBinaryOperator(
|
||||
function(a,b) {return Number.prototype.toFixed.call(a,b);}
|
||||
);
|
||||
|
||||
exports.precision = makeNumericBinaryOperator(
|
||||
function(a,b) {return Number.prototype.toPrecision.call(a,b);}
|
||||
);
|
||||
|
||||
exports.exponential = makeNumericBinaryOperator(
|
||||
function(a,b) {return Number.prototype.toExponential.call(a,b);}
|
||||
);
|
||||
|
||||
exports.sum = makeNumericArrayOperator(
|
||||
function(accumulator,value) {return accumulator + value},
|
||||
0 // Initial value
|
||||
);
|
||||
|
||||
exports.product = makeNumericArrayOperator(
|
||||
function(accumulator,value) {return accumulator * value},
|
||||
1 // Initial value
|
||||
);
|
||||
|
||||
exports.maxall = makeNumericArrayOperator(
|
||||
function(accumulator,value) {return Math.max(accumulator,value)},
|
||||
-Infinity // Initial value
|
||||
);
|
||||
|
||||
exports.minall = makeNumericArrayOperator(
|
||||
function(accumulator,value) {return Math.min(accumulator,value)},
|
||||
Infinity // Initial value
|
||||
);
|
||||
|
||||
function makeNumericBinaryOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var result = [],
|
||||
numOperand = parseNumber(operator.operand);
|
||||
source(function(tiddler,title) {
|
||||
result.push(stringifyNumber(fnCalc(parseNumber(title),numOperand)));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function makeNumericArrayOperator(fnCalc,initialValue) {
|
||||
initialValue = initialValue || 0;
|
||||
return function(source,operator,options) {
|
||||
var result = [];
|
||||
source(function(tiddler,title) {
|
||||
result.push(title);
|
||||
});
|
||||
return [stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,parseNumber(currentValue));
|
||||
},initialValue))];
|
||||
};
|
||||
}
|
||||
|
||||
function parseNumber(str) {
|
||||
return parseFloat(str) || 0;
|
||||
}
|
||||
|
||||
function stringifyNumber(num) {
|
||||
return num + "";
|
||||
}
|
||||
|
||||
})();
|
||||
99
core/modules/filters/range.js
Normal file
99
core/modules/filters/range.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/range.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for generating a numeric range.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.range = function(source,operator,options) {
|
||||
var results = [];
|
||||
// Split the operand into numbers delimited by these symbols
|
||||
var parts = operator.operand.split(/[,:;]/g),
|
||||
beg, end, inc, i, fixed = 0;
|
||||
for (i=0; i<parts.length; i++) {
|
||||
// Validate real number
|
||||
if(!/^\s*[+-]?((\d+(\.\d*)?)|(\.\d+))\s*$/.test(parts[i])) {
|
||||
return ["range: bad number \"" + parts[i] + "\""];
|
||||
}
|
||||
// Count digits; the most precise number determines decimal places in output.
|
||||
var frac = /\.\d+/.exec(parts[i]);
|
||||
if(frac) {
|
||||
fixed = Math.max(fixed,frac[0].length-1);
|
||||
}
|
||||
parts[i] = parseFloat(parts[i]);
|
||||
}
|
||||
switch(parts.length) {
|
||||
case 1:
|
||||
end = parts[0];
|
||||
if (end >= 1) {
|
||||
beg = 1;
|
||||
}
|
||||
else if (end <= -1) {
|
||||
beg = -1;
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
inc = 1;
|
||||
break;
|
||||
case 2:
|
||||
beg = parts[0];
|
||||
end = parts[1];
|
||||
inc = 1;
|
||||
break;
|
||||
case 3:
|
||||
beg = parts[0];
|
||||
end = parts[1];
|
||||
inc = Math.abs(parts[2]);
|
||||
break;
|
||||
}
|
||||
if(inc === 0) {
|
||||
return ["range: increment 0 causes infinite loop"];
|
||||
}
|
||||
// May need to count backwards
|
||||
var direction = ((end < beg) ? -1 : 1);
|
||||
inc *= direction;
|
||||
// Estimate number of resulting elements
|
||||
if((end - beg) / inc > 10000) {
|
||||
return ["range: too many steps (over 10K)"];
|
||||
}
|
||||
// Avoid rounding error on last step
|
||||
end += direction * 0.5 * Math.pow(0.1,fixed);
|
||||
var safety = 10010;
|
||||
// Enumerate the range
|
||||
if (end<beg) {
|
||||
for(i=beg; i>end; i+=inc) {
|
||||
results.push(i.toFixed(fixed));
|
||||
if(--safety<0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(i=beg; i<end; i+=inc) {
|
||||
results.push(i.toFixed(fixed));
|
||||
if(--safety<0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(safety<0) {
|
||||
return ["range: unexpectedly large output"];
|
||||
}
|
||||
// Reverse?
|
||||
if(operator.prefix === "!") {
|
||||
results.reverse();
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -18,7 +18,7 @@ Export our filter function
|
||||
exports.removesuffix = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
if(title.substr(-operator.operand.length) === operator.operand) {
|
||||
if(title && title.substr(-operator.operand.length) === operator.operand) {
|
||||
results.push(title.substr(0,title.length - operator.operand.length));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,11 +17,34 @@ Export our filter function
|
||||
*/
|
||||
exports.search = function(source,operator,options) {
|
||||
var invert = operator.prefix === "!";
|
||||
if(operator.suffix) {
|
||||
if(operator.suffixes) {
|
||||
var hasFlag = function(flag) {
|
||||
return (operator.suffixes[1] || []).indexOf(flag) !== -1;
|
||||
},
|
||||
excludeFields = false,
|
||||
fieldList = operator.suffixes[0] || [],
|
||||
firstField = fieldList[0] || "",
|
||||
firstChar = firstField.charAt(0),
|
||||
fields;
|
||||
if(firstChar === "-") {
|
||||
fields = [firstField.slice(1)].concat(fieldList.slice(1));
|
||||
excludeFields = true;
|
||||
} else if(fieldList[0] === "*"){
|
||||
fields = [];
|
||||
excludeFields = true;
|
||||
} else {
|
||||
fields = fieldList.slice(0);
|
||||
}
|
||||
return options.wiki.search(operator.operand,{
|
||||
source: source,
|
||||
invert: invert,
|
||||
field: operator.suffix
|
||||
field: fields,
|
||||
excludeField: excludeFields,
|
||||
caseSensitive: hasFlag("casesensitive"),
|
||||
literal: hasFlag("literal"),
|
||||
whitespace: hasFlag("whitespace"),
|
||||
regexp: hasFlag("regexp"),
|
||||
words: hasFlag("words")
|
||||
});
|
||||
} else {
|
||||
return options.wiki.search(operator.operand,{
|
||||
|
||||
@@ -27,6 +27,12 @@ exports.nsort = function(source,operator,options) {
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.sortan = function(source, operator, options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results, operator.operand || "title", operator.prefix === "!",false,false,true);
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.sortcs = function(source,operator,options) {
|
||||
var results = prepare_results(source);
|
||||
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,false);
|
||||
|
||||
70
core/modules/filters/strings.js
Normal file
70
core/modules/filters/strings.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/strings.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operators for strings. Unary/binary operators work on each item in turn, and return a new item list.
|
||||
|
||||
Sum/product/maxall/minall operate on the entire list, returning a single item.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.length = makeStringBinaryOperator(
|
||||
function(a) {return ["" + ("" + a).length];}
|
||||
);
|
||||
|
||||
exports.uppercase = makeStringBinaryOperator(
|
||||
function(a) {return [("" + a).toUpperCase()];}
|
||||
);
|
||||
|
||||
exports.lowercase = makeStringBinaryOperator(
|
||||
function(a) {return [("" + a).toLowerCase()];}
|
||||
);
|
||||
|
||||
exports.trim = makeStringBinaryOperator(
|
||||
function(a) {return [$tw.utils.trim(a)];}
|
||||
);
|
||||
|
||||
exports.concat = makeStringBinaryOperator(
|
||||
function(a,b) {return ["" + a + b];}
|
||||
);
|
||||
|
||||
exports.split = makeStringBinaryOperator(
|
||||
function(a,b) {return ("" + a).split(b).filter(function(str) {return !!str;});}
|
||||
);
|
||||
|
||||
exports.join = makeStringArrayOperator(
|
||||
function(accumulator,value,operand) {
|
||||
return "" + (accumulator ? accumulator + (operand || "") + value : value);
|
||||
}
|
||||
);
|
||||
|
||||
function makeStringBinaryOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var result = [];
|
||||
source(function(tiddler,title) {
|
||||
Array.prototype.push.apply(result,fnCalc(title,operator.operand || ""));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function makeStringArrayOperator(fnCalc,initialValue) {
|
||||
initialValue = initialValue || "";
|
||||
return function(source,operator,options) {
|
||||
var result = [];
|
||||
source(function(tiddler,title) {
|
||||
result.push(title);
|
||||
});
|
||||
return [result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,currentValue,operator.operand || "");
|
||||
},initialValue)];
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
||||
33
core/modules/filters/subfilter.js
Normal file
33
core/modules/filters/subfilter.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/subfilter.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator returning its operand evaluated as a filter
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.subfilter = function(source,operator,options) {
|
||||
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source);
|
||||
if(operator.prefix === "!") {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
if(list.indexOf(title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
31
core/modules/filters/subtiddlerfields.js
Normal file
31
core/modules/filters/subtiddlerfields.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/subtiddlerfields.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning the names of the fields on the selected subtiddlers of the plugin named in the operand
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.subtiddlerfields = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var subtiddler = options.wiki.getSubTiddler(operator.operand,title);
|
||||
if(subtiddler) {
|
||||
for(var fieldName in subtiddler.fields) {
|
||||
$tw.utils.pushTop(results,fieldName);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -25,17 +25,18 @@ exports.tag = function(source,operator,options) {
|
||||
});
|
||||
} else {
|
||||
// Old semantics:
|
||||
var tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
|
||||
if(operator.prefix === "!") {
|
||||
// Returns a copy of the input if operator.operand is missing
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && !tiddler.hasTag(operator.operand)) {
|
||||
if(tiddlers.indexOf(title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Returns empty results if operator.operand is missing
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && tiddler.hasTag(operator.operand)) {
|
||||
if(tiddlers.indexOf(title) !== -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ Extended filter operators to manipulate the current list.
|
||||
exports.putbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = parseInt(operator.suffix) || 1;
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index, -count));
|
||||
@@ -41,7 +41,7 @@ Extended filter operators to manipulate the current list.
|
||||
exports.putafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = parseInt(operator.suffix) || 1;
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index + 1).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
@@ -53,7 +53,7 @@ Extended filter operators to manipulate the current list.
|
||||
exports.replace = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = parseInt(operator.suffix) || 1;
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -count) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
@@ -64,7 +64,7 @@ Extended filter operators to manipulate the current list.
|
||||
*/
|
||||
exports.putfirst = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || 1;
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(-count).concat(results.slice(0, -count));
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ Extended filter operators to manipulate the current list.
|
||||
*/
|
||||
exports.putlast = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || 1;
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(count).concat(results.slice(0, count));
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ Extended filter operators to manipulate the current list.
|
||||
exports.move = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = parseInt(operator.suffix) || 1,
|
||||
count = $tw.utils.getInt(operator.suffix,1),
|
||||
marker = results.splice(index, 1),
|
||||
offset = (index + count) > 0 ? index + count : 0;
|
||||
return results.slice(0, offset).concat(marker).concat(results.slice(offset));
|
||||
@@ -129,7 +129,7 @@ Extended filter operators to manipulate the current list.
|
||||
exports.prepend = function (source, operator) {
|
||||
var prepend = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || prepend.length;
|
||||
count = $tw.utils.getInt(operator.suffix,prepend.length);
|
||||
return (prepend.length === 0) ? results :
|
||||
(operator.prefix) ? prepend.slice(-count).concat(results) :
|
||||
prepend.slice(0, count).concat(results);
|
||||
|
||||
@@ -18,8 +18,8 @@ exports.getInfoTiddlerFields = function() {
|
||||
// Basics
|
||||
infoTiddlerFields.push({title: "$:/info/browser", text: mapBoolean(!!$tw.browser)});
|
||||
infoTiddlerFields.push({title: "$:/info/node", text: mapBoolean(!!$tw.node)});
|
||||
// Document location
|
||||
if($tw.browser) {
|
||||
// Document location
|
||||
var setLocationProperty = function(name,value) {
|
||||
infoTiddlerFields.push({title: "$:/info/url/" + name, text: value});
|
||||
},
|
||||
@@ -32,6 +32,11 @@ exports.getInfoTiddlerFields = function() {
|
||||
setLocationProperty("pathname", location.pathname);
|
||||
setLocationProperty("search", location.search);
|
||||
setLocationProperty("origin", location.origin);
|
||||
// Screen size
|
||||
infoTiddlerFields.push({title: "$:/info/browser/screen/width", text: window.screen.width.toString()});
|
||||
infoTiddlerFields.push({title: "$:/info/browser/screen/height", text: window.screen.height.toString()});
|
||||
// Language
|
||||
infoTiddlerFields.push({title: "$:/info/browser/language", text: navigator.language || ""});
|
||||
}
|
||||
return infoTiddlerFields;
|
||||
};
|
||||
|
||||
@@ -138,6 +138,17 @@ function KeyboardManager(options) {
|
||||
});
|
||||
// Save the platform-specific name of the "meta" key
|
||||
this.metaKeyName = $tw.platform.isMac ? "cmd-" : "win-";
|
||||
this.shortcutKeysList = [], // Stores the shortcut-key descriptors
|
||||
this.shortcutActionList = [], // Stores the corresponding action strings
|
||||
this.shortcutParsedList = []; // Stores the parsed key descriptors
|
||||
this.lookupNames = ["shortcuts"];
|
||||
this.lookupNames.push($tw.platform.isMac ? "shortcuts-mac" : "shortcuts-not-mac")
|
||||
this.lookupNames.push($tw.platform.isWindows ? "shortcuts-windows" : "shortcuts-not-windows");
|
||||
this.lookupNames.push($tw.platform.isLinux ? "shortcuts-linux" : "shortcuts-not-linux");
|
||||
this.updateShortcutLists(this.getShortcutTiddlerList());
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
self.handleShortcutChanges(changes);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -229,10 +240,9 @@ KeyboardManager.prototype.parseKeyDescriptors = function(keyDescriptors,options)
|
||||
result.push.apply(result,self.parseKeyDescriptors(keyDescriptors,options));
|
||||
}
|
||||
};
|
||||
lookupName("shortcuts");
|
||||
lookupName($tw.platform.isMac ? "shortcuts-mac" : "shortcuts-not-mac");
|
||||
lookupName($tw.platform.isWindows ? "shortcuts-windows" : "shortcuts-not-windows");
|
||||
lookupName($tw.platform.isLinux ? "shortcuts-linux" : "shortcuts-not-linux");
|
||||
$tw.utils.each(self.lookupNames,function(platformDescriptor) {
|
||||
lookupName(platformDescriptor);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
result.push(self.parseKeyDescriptor(keyDescriptor));
|
||||
@@ -274,6 +284,70 @@ KeyboardManager.prototype.checkKeyDescriptors = function(event,keyInfoArray) {
|
||||
return false;
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.getShortcutTiddlerList = function() {
|
||||
return $tw.wiki.getTiddlersWithTag("$:/tags/KeyboardShortcut");
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.updateShortcutLists = function(tiddlerList) {
|
||||
this.shortcutTiddlers = tiddlerList;
|
||||
for(var i=0; i<tiddlerList.length; i++) {
|
||||
var title = tiddlerList[i],
|
||||
tiddlerFields = $tw.wiki.getTiddler(title).fields;
|
||||
this.shortcutKeysList[i] = tiddlerFields.key !== undefined ? tiddlerFields.key : undefined;
|
||||
this.shortcutActionList[i] = tiddlerFields.text;
|
||||
this.shortcutParsedList[i] = this.shortcutKeysList[i] !== undefined ? this.parseKeyDescriptors(this.shortcutKeysList[i]) : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.handleKeydownEvent = function(event) {
|
||||
var key, action;
|
||||
for(var i=0; i<this.shortcutTiddlers.length; i++) {
|
||||
if(this.shortcutParsedList[i] !== undefined && this.checkKeyDescriptors(event,this.shortcutParsedList[i])) {
|
||||
key = this.shortcutParsedList[i];
|
||||
action = this.shortcutActionList[i];
|
||||
}
|
||||
}
|
||||
if(key !== undefined) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
$tw.rootWidget.invokeActionString(action,$tw.rootWidget);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.detectNewShortcuts = function(changedTiddlers) {
|
||||
var shortcutConfigTiddlers = [],
|
||||
handled = false;
|
||||
$tw.utils.each(this.lookupNames,function(platformDescriptor) {
|
||||
var descriptorString = "$:/config/" + platformDescriptor + "/";
|
||||
Object.keys(changedTiddlers).forEach(function(configTiddler) {
|
||||
var configString = configTiddler.substr(0, configTiddler.lastIndexOf("/") + 1);
|
||||
if(configString === descriptorString) {
|
||||
shortcutConfigTiddlers.push(configTiddler);
|
||||
handled = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
if(handled) {
|
||||
return $tw.utils.hopArray(changedTiddlers,shortcutConfigTiddlers);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.handleShortcutChanges = function(changedTiddlers) {
|
||||
var newList = this.getShortcutTiddlerList();
|
||||
var hasChanged = $tw.utils.hopArray(changedTiddlers,this.shortcutTiddlers) ? true :
|
||||
($tw.utils.hopArray(changedTiddlers,newList) ? true :
|
||||
(this.detectNewShortcuts(changedTiddlers))
|
||||
);
|
||||
// Re-cache shortcuts if something changed
|
||||
if(hasChanged) {
|
||||
this.updateShortcutLists(newList);
|
||||
}
|
||||
};
|
||||
|
||||
exports.KeyboardManager = KeyboardManager;
|
||||
|
||||
})();
|
||||
|
||||
@@ -26,19 +26,7 @@ exports.params = [
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function(filter) {
|
||||
var tiddlers = this.wiki.filterTiddlers(filter),
|
||||
data = [];
|
||||
for(var t=0;t<tiddlers.length; t++) {
|
||||
var tiddler = this.wiki.getTiddler(tiddlers[t]);
|
||||
if(tiddler) {
|
||||
var fields = new Object();
|
||||
for(var field in tiddler.fields) {
|
||||
fields[field] = tiddler.getFieldString(field);
|
||||
}
|
||||
data.push(fields);
|
||||
}
|
||||
}
|
||||
return JSON.stringify(data,null,$tw.config.preferences.jsonSpaces);
|
||||
return this.wiki.getTiddlersAsJson(filter);
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
34
core/modules/macros/uniquetitle.js
Normal file
34
core/modules/macros/uniquetitle.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/*\
|
||||
title: $:/core/modules/macros/unusedtitle.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
Macro to return a new title that is unused in the wiki. It can be given a name as a base.
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "unusedtitle";
|
||||
|
||||
exports.params = [
|
||||
{name: "baseName"},
|
||||
{name: "options"}
|
||||
];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function(baseName, options) {
|
||||
if(!baseName) {
|
||||
baseName = $tw.language.getString("DefaultNewTiddlerTitle");
|
||||
}
|
||||
return this.wiki.generateNewTitle(baseName, options);
|
||||
};
|
||||
|
||||
})();
|
||||
29
core/modules/parsers/binaryparser.js
Normal file
29
core/modules/parsers/binaryparser.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/binaryparser.js
|
||||
type: application/javascript
|
||||
module-type: parser
|
||||
|
||||
The video parser parses a video tiddler into an embeddable HTML element
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
||||
|
||||
var BinaryParser = function(type,text,options) {
|
||||
this.tree = [{
|
||||
type: "transclude",
|
||||
attributes: {
|
||||
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
exports["application/octet-stream"] = BinaryParser;
|
||||
|
||||
})();
|
||||
|
||||
@@ -17,8 +17,7 @@ var ImageParser = function(type,text,options) {
|
||||
type: "element",
|
||||
tag: "img",
|
||||
attributes: {}
|
||||
},
|
||||
src;
|
||||
};
|
||||
if(options._canonical_uri) {
|
||||
element.attributes.src = {type: "string", value: options._canonical_uri};
|
||||
} else if(text) {
|
||||
@@ -36,6 +35,9 @@ exports["image/jpg"] = ImageParser;
|
||||
exports["image/jpeg"] = ImageParser;
|
||||
exports["image/png"] = ImageParser;
|
||||
exports["image/gif"] = ImageParser;
|
||||
exports["image/webp"] = ImageParser;
|
||||
exports["image/heic"] = ImageParser;
|
||||
exports["image/heif"] = ImageParser;
|
||||
exports["image/x-icon"] = ImageParser;
|
||||
|
||||
})();
|
||||
|
||||
13
core/modules/parsers/wikiparser/macros/prettylink.tid
Normal file
13
core/modules/parsers/wikiparser/macros/prettylink.tid
Normal file
@@ -0,0 +1,13 @@
|
||||
title: $:/core/macros/shortcuts/prettylink
|
||||
tags: $:/tags/Macro
|
||||
|
||||
\define x-tm-prettylink-internal(to,text)
|
||||
<$link to=<<__to__>>><$text text=<<__text__>>/></$link>
|
||||
\end
|
||||
|
||||
\define tm-prettylink-internal(to,text)
|
||||
<$button>
|
||||
<$action-navigate $to=<<__to__>>/>
|
||||
<$text text=<<__text__>>/>
|
||||
</$button>
|
||||
\end
|
||||
@@ -97,6 +97,9 @@ exports.parseTag = function(source,pos,options) {
|
||||
return null;
|
||||
}
|
||||
node.tag = token.match[1];
|
||||
if(node.tag.slice(1).indexOf("$") !== -1) {
|
||||
return null;
|
||||
}
|
||||
if(node.tag.charAt(0) === "$") {
|
||||
node.type = node.tag.substr(1);
|
||||
}
|
||||
|
||||
53
core/modules/parsers/wikiparser/rules/import.js
Normal file
53
core/modules/parsers/wikiparser/rules/import.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/import.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki pragma rule for importing variable definitions
|
||||
|
||||
```
|
||||
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "import";
|
||||
exports.types = {pragma: true};
|
||||
|
||||
/*
|
||||
Instantiate parse rule
|
||||
*/
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /^\\import[^\S\n]/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
var self = this;
|
||||
// Move past the pragma invocation
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Parse the filter terminated by a line break
|
||||
var reMatch = /(.*)(\r?\n)|$/mg;
|
||||
reMatch.lastIndex = this.parser.pos;
|
||||
var match = reMatch.exec(this.parser.source);
|
||||
this.parser.pos = reMatch.lastIndex;
|
||||
// Parse tree nodes to return
|
||||
return [{
|
||||
type: "importvariables",
|
||||
attributes: {
|
||||
filter: {type: "string", value: match[1]}
|
||||
},
|
||||
children: []
|
||||
}];
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -84,7 +84,8 @@ exports.parse = function() {
|
||||
value: {type: "string", value: text}
|
||||
},
|
||||
children: [],
|
||||
params: params
|
||||
params: params,
|
||||
isMacroDefinition: true
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
@@ -49,13 +49,12 @@ exports.parse = function() {
|
||||
}];
|
||||
} else {
|
||||
return [{
|
||||
type: "link",
|
||||
attributes: {
|
||||
to: {type: "string", value: link}
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: text
|
||||
}]
|
||||
type: "macrocall",
|
||||
name: "tm-prettylink-internal",
|
||||
params: [
|
||||
{name: "to", value: link},
|
||||
{name: "text", value: text}
|
||||
]
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ function SaverHandler(options) {
|
||||
var self = this;
|
||||
this.wiki = options.wiki;
|
||||
this.dirtyTracking = options.dirtyTracking;
|
||||
this.preloadDirty = options.preloadDirty || [];
|
||||
this.pendingAutoSave = false;
|
||||
// Make a logger
|
||||
this.logger = new $tw.utils.Logger("saver-handler");
|
||||
@@ -33,7 +34,13 @@ function SaverHandler(options) {
|
||||
// Compile the dirty tiddler filter
|
||||
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
||||
// Count of changes that have not yet been saved
|
||||
this.numChanges = 0;
|
||||
var filteredChanges = self.filterFn.call(self.wiki,function(iterator) {
|
||||
$tw.utils.each(self.preloadDirty,function(title) {
|
||||
var tiddler = self.wiki.getTiddler(title);
|
||||
iterator(tiddler,title);
|
||||
});
|
||||
});
|
||||
this.numChanges = filteredChanges.length;
|
||||
// Listen out for changes to tiddlers
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
// Filter the changes so that we only count changes to tiddlers that we care about
|
||||
@@ -144,8 +151,12 @@ Save the wiki contents. Options are:
|
||||
SaverHandler.prototype.saveWiki = function(options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
method = options.method || "save",
|
||||
variables = options.variables || {},
|
||||
method = options.method || "save";
|
||||
// Ignore autosave if disabled
|
||||
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
|
||||
return false;
|
||||
}
|
||||
var variables = options.variables || {},
|
||||
template = options.template || "$:/core/save/all",
|
||||
downloadType = options.downloadType || "text/plain",
|
||||
text = this.wiki.renderTiddler(downloadType,template,options),
|
||||
@@ -164,10 +175,6 @@ SaverHandler.prototype.saveWiki = function(options) {
|
||||
}
|
||||
}
|
||||
};
|
||||
// Ignore autosave if disabled
|
||||
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
|
||||
return false;
|
||||
}
|
||||
// Call the highest priority saver that supports this method
|
||||
for(var t=this.savers.length-1; t>=0; t--) {
|
||||
var saver = this.savers[t];
|
||||
|
||||
@@ -51,7 +51,7 @@ BeakerSaver.prototype.info = {
|
||||
Static method that returns true if this saver is capable of working
|
||||
*/
|
||||
exports.canSave = function(wiki) {
|
||||
return !!window.DatArchive;
|
||||
return !!window.DatArchive && location.protocol==="dat:";
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,6 +15,31 @@ to the current URL, such as a WebDAV server.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Retrieve ETag if available
|
||||
*/
|
||||
var retrieveETag = function(self) {
|
||||
var headers = {
|
||||
Accept: "*/*;charset=UTF-8"
|
||||
};
|
||||
$tw.utils.httpRequest({
|
||||
url: self.uri(),
|
||||
type: "HEAD",
|
||||
headers: headers,
|
||||
callback: function(err,data,xhr) {
|
||||
if(err) {
|
||||
return;
|
||||
}
|
||||
var etag = xhr.getResponseHeader("ETag");
|
||||
if(!etag) {
|
||||
return;
|
||||
}
|
||||
self.etag = etag.replace(/^W\//,"");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Select the appropriate saver module and set it up
|
||||
*/
|
||||
@@ -27,23 +52,14 @@ var PutSaver = function(wiki) {
|
||||
$tw.utils.httpRequest({
|
||||
url: uri,
|
||||
type: "OPTIONS",
|
||||
callback: function(err, data, xhr) {
|
||||
callback: function(err,data,xhr) {
|
||||
// Check DAV header http://www.webdav.org/specs/rfc2518.html#rfc.section.9.1
|
||||
if(!err) {
|
||||
self.serverAcceptsPuts = xhr.status === 200 && !!xhr.getResponseHeader("dav");
|
||||
}
|
||||
}
|
||||
});
|
||||
// Retrieve ETag if available
|
||||
$tw.utils.httpRequest({
|
||||
url: uri,
|
||||
type: "HEAD",
|
||||
callback: function(err, data, xhr) {
|
||||
if(!err) {
|
||||
self.etag = xhr.getResponseHeader("ETag");
|
||||
}
|
||||
}
|
||||
});
|
||||
retrieveETag(this);
|
||||
};
|
||||
|
||||
PutSaver.prototype.uri = function() {
|
||||
@@ -53,12 +69,14 @@ PutSaver.prototype.uri = function() {
|
||||
// TODO: in case of edit conflict
|
||||
// Prompt: Do you want to save over this? Y/N
|
||||
// Merging would be ideal, and may be possible using future generic merge flow
|
||||
PutSaver.prototype.save = function(text, method, callback) {
|
||||
PutSaver.prototype.save = function(text,method,callback) {
|
||||
if(!this.serverAcceptsPuts) {
|
||||
return false;
|
||||
}
|
||||
var self = this;
|
||||
var headers = { "Content-Type": "text/html;charset=UTF-8" };
|
||||
var headers = {
|
||||
"Content-Type": "text/html;charset=UTF-8"
|
||||
};
|
||||
if(this.etag) {
|
||||
headers["If-Match"] = this.etag;
|
||||
}
|
||||
@@ -67,17 +85,22 @@ PutSaver.prototype.save = function(text, method, callback) {
|
||||
type: "PUT",
|
||||
headers: headers,
|
||||
data: text,
|
||||
callback: function(err, data, xhr) {
|
||||
callback: function(err,data,xhr) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} if(xhr.status === 200 || xhr.status === 201) {
|
||||
self.etag = xhr.getResponseHeader("ETag");
|
||||
callback(null); // success
|
||||
} else if(xhr.status === 412) { // edit conflict
|
||||
var message = $tw.language.getString("Error/EditConflict");
|
||||
callback(message);
|
||||
// response is textual: "XMLHttpRequest error code: 412"
|
||||
var status = Number(err.substring(err.indexOf(':') + 2, err.length))
|
||||
if(status === 412) { // edit conflict
|
||||
var message = $tw.language.getString("Error/EditConflict");
|
||||
callback(message);
|
||||
} else {
|
||||
callback(err); // fail
|
||||
}
|
||||
} else {
|
||||
callback(xhr.responseText); // fail
|
||||
self.etag = xhr.getResponseHeader("ETag");
|
||||
if(self.etag == null) {
|
||||
retrieveETag(self);
|
||||
}
|
||||
callback(null); // success
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -90,7 +113,7 @@ Information about this saver
|
||||
PutSaver.prototype.info = {
|
||||
name: "put",
|
||||
priority: 2000,
|
||||
capabilities: ["save", "autosave"]
|
||||
capabilities: ["save","autosave"]
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
94
core/modules/server/authenticators/basic.js
Normal file
94
core/modules/server/authenticators/basic.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/authenticators/basic.js
|
||||
type: application/javascript
|
||||
module-type: authenticator
|
||||
|
||||
Authenticator for WWW basic authentication
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
if($tw.node) {
|
||||
var util = require("util"),
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
path = require("path");
|
||||
}
|
||||
|
||||
function BasicAuthenticator(server) {
|
||||
this.server = server;
|
||||
this.credentialsData = [];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns true if the authenticator is active, false if it is inactive, or a string if there is an error
|
||||
*/
|
||||
BasicAuthenticator.prototype.init = function() {
|
||||
// Read the credentials data
|
||||
this.credentialsFilepath = this.server.get("credentials");
|
||||
if(this.credentialsFilepath) {
|
||||
var resolveCredentialsFilepath = path.resolve($tw.boot.wikiPath,this.credentialsFilepath);
|
||||
if(fs.existsSync(resolveCredentialsFilepath) && !fs.statSync(resolveCredentialsFilepath).isDirectory()) {
|
||||
var credentialsText = fs.readFileSync(resolveCredentialsFilepath,"utf8"),
|
||||
credentialsData = $tw.utils.parseCsvStringWithHeader(credentialsText);
|
||||
if(typeof credentialsData === "string") {
|
||||
return "Error: " + credentialsData + " reading credentials from '" + resolveCredentialsFilepath + "'";
|
||||
} else {
|
||||
this.credentialsData = credentialsData;
|
||||
}
|
||||
} else {
|
||||
return "Error: Unable to load user credentials from '" + credentialsFilepath + "'";
|
||||
}
|
||||
}
|
||||
// Add the hardcoded username and password if specified
|
||||
if(this.server.get("username") && this.server.get("password")) {
|
||||
this.credentialsData = this.credentialsData || [];
|
||||
this.credentialsData.push({
|
||||
username: this.server.get("username"),
|
||||
password: this.server.get("password")
|
||||
});
|
||||
}
|
||||
return this.credentialsData.length > 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable.
|
||||
Returns false if the request couldn't be authenticated having sent an appropriate response to the browser
|
||||
*/
|
||||
BasicAuthenticator.prototype.authenticateRequest = function(request,response,state) {
|
||||
// Extract the incoming username and password from the request
|
||||
var header = request.headers.authorization || "";
|
||||
if(!header && state.allowAnon) {
|
||||
// If there's no header and anonymous access is allowed then we don't set authenticatedUsername
|
||||
return true;
|
||||
}
|
||||
var token = header.split(/\s+/).pop() || "",
|
||||
auth = $tw.utils.base64Decode(token),
|
||||
parts = auth.split(/:/),
|
||||
incomingUsername = parts[0],
|
||||
incomingPassword = parts[1];
|
||||
// Check that at least one of the credentials matches
|
||||
var matchingCredentials = this.credentialsData.find(function(credential) {
|
||||
return credential.username === incomingUsername && credential.password === incomingPassword;
|
||||
});
|
||||
if(matchingCredentials) {
|
||||
// If so, add the authenticated username to the request state
|
||||
state.authenticatedUsername = incomingUsername;
|
||||
return true;
|
||||
} else {
|
||||
// If not, return an authentication challenge
|
||||
response.writeHead(401,"Authentication required",{
|
||||
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"'
|
||||
});
|
||||
response.end();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.AuthenticatorClass = BasicAuthenticator;
|
||||
|
||||
})();
|
||||
47
core/modules/server/authenticators/header.js
Normal file
47
core/modules/server/authenticators/header.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/authenticators/header.js
|
||||
type: application/javascript
|
||||
module-type: authenticator
|
||||
|
||||
Authenticator for trusted header authentication
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
function HeaderAuthenticator(server) {
|
||||
this.server = server;
|
||||
this.header = server.get("authenticated-user-header");
|
||||
}
|
||||
|
||||
/*
|
||||
Returns true if the authenticator is active, false if it is inactive, or a string if there is an error
|
||||
*/
|
||||
HeaderAuthenticator.prototype.init = function() {
|
||||
return !!this.header;
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable.
|
||||
Returns false if the request couldn't be authenticated having sent an appropriate response to the browser
|
||||
*/
|
||||
HeaderAuthenticator.prototype.authenticateRequest = function(request,response,state) {
|
||||
// Otherwise, authenticate as the username in the specified header
|
||||
var username = request.headers[this.header];
|
||||
if(!username && !state.allowAnon) {
|
||||
response.writeHead(401,"Authorization header required to login to '" + state.server.servername + "'");
|
||||
response.end();
|
||||
return false;
|
||||
} else {
|
||||
// authenticatedUsername will be undefined for anonymous users
|
||||
state.authenticatedUsername = username;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
exports.AuthenticatorClass = HeaderAuthenticator;
|
||||
|
||||
})();
|
||||
28
core/modules/server/routes/delete-tiddler.js
Normal file
28
core/modules/server/routes/delete-tiddler.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/delete-tiddler.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
DELETE /recipes/default/tiddlers/:title
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "DELETE";
|
||||
|
||||
exports.path = /^\/bags\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]);
|
||||
state.wiki.deleteTiddler(title);
|
||||
response.writeHead(204, "OK", {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.end();
|
||||
};
|
||||
|
||||
}());
|
||||
25
core/modules/server/routes/get-favicon.js
Normal file
25
core/modules/server/routes/get-favicon.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-favicon.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /favicon.ico
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/favicon.ico$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "image/x-icon"});
|
||||
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
|
||||
response.end(buffer,"base64");
|
||||
};
|
||||
|
||||
}());
|
||||
50
core/modules/server/routes/get-file.js
Normal file
50
core/modules/server/routes/get-file.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-file.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /files/:filepath
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/files\/(.+)$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var path = require("path"),
|
||||
fs = require("fs"),
|
||||
util = require("util");
|
||||
var filename = path.resolve($tw.boot.wikiPath,"files",decodeURIComponent(state.params[0])),
|
||||
extension = path.extname(filename);
|
||||
fs.readFile(filename,function(err,content) {
|
||||
var status,content,type = "text/plain";
|
||||
if(err) {
|
||||
if(err.code === "ENOENT") {
|
||||
status = 404;
|
||||
content = "File '" + filename + "' not found";
|
||||
} else if(err.code === "EACCES") {
|
||||
status = 403;
|
||||
content = "You do not have permission to access the file '" + filename + "'";
|
||||
} else {
|
||||
status = 500;
|
||||
content = err.toString();
|
||||
}
|
||||
} else {
|
||||
status = 200;
|
||||
content = content;
|
||||
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
|
||||
}
|
||||
response.writeHead(status,{
|
||||
"Content-Type": type
|
||||
});
|
||||
response.end(content);
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
51
core/modules/server/routes/get-index.js
Normal file
51
core/modules/server/routes/get-index.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-index.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var zlib = require('zlib');
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var acceptEncoding = request.headers['accept-encoding'];
|
||||
if (!acceptEncoding) { acceptEncoding = ''; }
|
||||
|
||||
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler"));
|
||||
|
||||
var responseHeaders = {
|
||||
"Content-Type": state.server.get("root-serve-type")
|
||||
};
|
||||
|
||||
/*
|
||||
If the gzip=yes flag for `listen` is set, check if the user agent permits
|
||||
compression. If so, compress our response. Note that we use the synchronous
|
||||
functions from zlib to stay in the imperative style. The current `Server`
|
||||
doesn't depend on this, and we may just as well use the async versions.
|
||||
*/
|
||||
if(state.server.enableGzip) {
|
||||
if (/\bdeflate\b/.test(acceptEncoding)) {
|
||||
responseHeaders['Content-Encoding'] = 'deflate';
|
||||
text = zlib.deflateSync(text);
|
||||
} else if (/\bgzip\b/.test(acceptEncoding)) {
|
||||
responseHeaders['Content-Encoding'] = 'gzip';
|
||||
text = zlib.gzipSync(text);
|
||||
}
|
||||
}
|
||||
|
||||
response.writeHead(200, responseHeaders);
|
||||
response.end(text);
|
||||
};
|
||||
|
||||
}());
|
||||
35
core/modules/server/routes/get-login-basic.js
Normal file
35
core/modules/server/routes/get-login-basic.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-login-basic.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /login-basic -- force a Basic Authentication challenge
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/login-basic$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
if(!state.authenticatedUsername) {
|
||||
// Challenge if there's no username
|
||||
response.writeHead(401,{
|
||||
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"'
|
||||
});
|
||||
response.end();
|
||||
} else {
|
||||
// Redirect to the root wiki if login worked
|
||||
response.writeHead(302,{
|
||||
Location: "/"
|
||||
});
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
||||
33
core/modules/server/routes/get-status.js
Normal file
33
core/modules/server/routes/get-status.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-status.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /status
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/status$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
var text = JSON.stringify({
|
||||
username: state.authenticatedUsername || state.server.get("anon-username") || "",
|
||||
anonymous: !state.authenticatedUsername,
|
||||
read_only: !state.server.isAuthorized("writers",state.authenticatedUsername),
|
||||
space: {
|
||||
recipe: "default"
|
||||
},
|
||||
tiddlywiki_version: $tw.version
|
||||
});
|
||||
response.end(text,"utf8");
|
||||
};
|
||||
|
||||
}());
|
||||
44
core/modules/server/routes/get-tiddler-html.js
Normal file
44
core/modules/server/routes/get-tiddler-html.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-tiddler-html.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /:title
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/([^\/]+)$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]),
|
||||
tiddler = state.wiki.getTiddler(title);
|
||||
if(tiddler) {
|
||||
var renderType = tiddler.getFieldString("_render_type"),
|
||||
renderTemplate = tiddler.getFieldString("_render_template");
|
||||
// Tiddler fields '_render_type' and '_render_template' overwrite
|
||||
// system wide settings for render type and template
|
||||
if(state.wiki.isSystemTiddler(title)) {
|
||||
renderType = renderType || state.server.get("system-tiddler-render-type");
|
||||
renderTemplate = renderTemplate || state.server.get("system-tiddler-render-template");
|
||||
} else {
|
||||
renderType = renderType || state.server.get("tiddler-render-type");
|
||||
renderTemplate = renderTemplate || state.server.get("tiddler-render-template");
|
||||
}
|
||||
var text = state.wiki.renderTiddler(renderType,renderTemplate,{parseAsInline: true, variables: {currentTiddler: title}});
|
||||
// Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS
|
||||
response.writeHead(200);
|
||||
response.end(text,"utf8");
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
||||
46
core/modules/server/routes/get-tiddler.js
Normal file
46
core/modules/server/routes/get-tiddler.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-tiddler.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /recipes/default/tiddlers/:title
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]),
|
||||
tiddler = state.wiki.getTiddler(title),
|
||||
tiddlerFields = {},
|
||||
knownFields = [
|
||||
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
|
||||
];
|
||||
if(tiddler) {
|
||||
$tw.utils.each(tiddler.fields,function(field,name) {
|
||||
var value = tiddler.getFieldString(name);
|
||||
if(knownFields.indexOf(name) !== -1) {
|
||||
tiddlerFields[name] = value;
|
||||
} else {
|
||||
tiddlerFields.fields = tiddlerFields.fields || {};
|
||||
tiddlerFields.fields[name] = value;
|
||||
}
|
||||
});
|
||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
response.end(JSON.stringify(tiddlerFields),"utf8");
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
||||
37
core/modules/server/routes/get-tiddlers-json.js
Normal file
37
core/modules/server/routes/get-tiddlers-json.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/get-tiddlers-json.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
GET /recipes/default/tiddlers/tiddlers.json
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers.json$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
var tiddlers = [];
|
||||
state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) {
|
||||
var tiddlerFields = {};
|
||||
$tw.utils.each(tiddler.fields,function(field,name) {
|
||||
if(name !== "text") {
|
||||
tiddlerFields[name] = tiddler.getFieldString(name);
|
||||
}
|
||||
});
|
||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||
tiddlers.push(tiddlerFields);
|
||||
});
|
||||
var text = JSON.stringify(tiddlers);
|
||||
response.end(text,"utf8");
|
||||
};
|
||||
|
||||
}());
|
||||
42
core/modules/server/routes/put-tiddler.js
Normal file
42
core/modules/server/routes/put-tiddler.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/routes/put-tiddler.js
|
||||
type: application/javascript
|
||||
module-type: route
|
||||
|
||||
PUT /recipes/default/tiddlers/:title
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "PUT";
|
||||
|
||||
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var title = decodeURIComponent(state.params[0]),
|
||||
fields = JSON.parse(state.data);
|
||||
// Pull up any subfields in the `fields` object
|
||||
if(fields.fields) {
|
||||
$tw.utils.each(fields.fields,function(field,name) {
|
||||
fields[name] = field;
|
||||
});
|
||||
delete fields.fields;
|
||||
}
|
||||
// Remove any revision field
|
||||
if(fields.revision) {
|
||||
delete fields.revision;
|
||||
}
|
||||
state.wiki.addTiddler(new $tw.Tiddler(state.wiki.getCreationFields(),fields,{title: title},state.wiki.getModificationFields()));
|
||||
var changeCount = state.wiki.getChangeCount(title).toString();
|
||||
response.writeHead(204, "OK",{
|
||||
Etag: "\"default/" + encodeURIComponent(title) + "/" + changeCount + ":\"",
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.end();
|
||||
};
|
||||
|
||||
}());
|
||||
265
core/modules/server/server.js
Normal file
265
core/modules/server/server.js
Normal file
@@ -0,0 +1,265 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/server.js
|
||||
type: application/javascript
|
||||
module-type: library
|
||||
|
||||
Serve tiddlers over http
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
if($tw.node) {
|
||||
var util = require("util"),
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
path = require("path");
|
||||
}
|
||||
|
||||
/*
|
||||
A simple HTTP server with regexp-based routes
|
||||
options: variables - optional hashmap of variables to set (a misnomer - they are really constant parameters)
|
||||
routes - optional array of routes to use
|
||||
wiki - reference to wiki object
|
||||
*/
|
||||
function Server(options) {
|
||||
var self = this;
|
||||
this.routes = options.routes || [];
|
||||
this.authenticators = options.authenticators || [];
|
||||
this.wiki = options.wiki;
|
||||
this.servername = $tw.utils.transliterateToSafeASCII(this.wiki.getTiddlerText("$:/SiteTitle") || "TiddlyWiki5");
|
||||
// Initialise the variables
|
||||
this.variables = $tw.utils.extend({},this.defaultVariables);
|
||||
if(options.variables) {
|
||||
for(var variable in options.variables) {
|
||||
if(options.variables[variable]) {
|
||||
this.variables[variable] = options.variables[variable];
|
||||
}
|
||||
}
|
||||
}
|
||||
$tw.utils.extend({},this.defaultVariables,options.variables);
|
||||
// Initialise CSRF
|
||||
this.csrfDisable = this.get("csrf-disable") === "yes";
|
||||
// Initialize Gzip compression
|
||||
this.enableGzip = this.get("gzip") === "yes";
|
||||
// Initialise authorization
|
||||
var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)";
|
||||
this.authorizationPrincipals = {
|
||||
readers: (this.get("readers") || authorizedUserName).split(",").map($tw.utils.trim),
|
||||
writers: (this.get("writers") || authorizedUserName).split(",").map($tw.utils.trim)
|
||||
}
|
||||
// Load and initialise authenticators
|
||||
$tw.modules.forEachModuleOfType("authenticator", function(title,authenticatorDefinition) {
|
||||
// console.log("Loading server route " + title);
|
||||
self.addAuthenticator(authenticatorDefinition.AuthenticatorClass);
|
||||
});
|
||||
// Load route handlers
|
||||
$tw.modules.forEachModuleOfType("route", function(title,routeDefinition) {
|
||||
// console.log("Loading server route " + title);
|
||||
self.addRoute(routeDefinition);
|
||||
});
|
||||
// Initialise the http vs https
|
||||
this.listenOptions = null;
|
||||
this.protocol = "http";
|
||||
var tlsKeyFilepath = this.get("tls-key"),
|
||||
tlsCertFilepath = this.get("tls-cert");
|
||||
if(tlsCertFilepath && tlsKeyFilepath) {
|
||||
this.listenOptions = {
|
||||
key: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsKeyFilepath),"utf8"),
|
||||
cert: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsCertFilepath),"utf8")
|
||||
};
|
||||
this.protocol = "https";
|
||||
}
|
||||
this.transport = require(this.protocol);
|
||||
}
|
||||
|
||||
Server.prototype.defaultVariables = {
|
||||
port: "8080",
|
||||
host: "127.0.0.1",
|
||||
"root-tiddler": "$:/core/save/all",
|
||||
"root-render-type": "text/plain",
|
||||
"root-serve-type": "text/html",
|
||||
"tiddler-render-type": "text/html",
|
||||
"tiddler-render-template": "$:/core/templates/server/static.tiddler.html",
|
||||
"system-tiddler-render-type": "text/plain",
|
||||
"system-tiddler-render-template": "$:/core/templates/wikified-tiddler",
|
||||
"debug-level": "none",
|
||||
"gzip": "no"
|
||||
};
|
||||
|
||||
Server.prototype.get = function(name) {
|
||||
return this.variables[name];
|
||||
};
|
||||
|
||||
Server.prototype.addRoute = function(route) {
|
||||
this.routes.push(route);
|
||||
};
|
||||
|
||||
Server.prototype.addAuthenticator = function(AuthenticatorClass) {
|
||||
// Instantiate and initialise the authenticator
|
||||
var authenticator = new AuthenticatorClass(this),
|
||||
result = authenticator.init();
|
||||
if(typeof result === "string") {
|
||||
$tw.utils.error("Error: " + result);
|
||||
} else if(result) {
|
||||
// Only use the authenticator if it initialised successfully
|
||||
this.authenticators.push(authenticator);
|
||||
}
|
||||
};
|
||||
|
||||
Server.prototype.findMatchingRoute = function(request,state) {
|
||||
var pathprefix = this.get("path-prefix") || "";
|
||||
for(var t=0; t<this.routes.length; t++) {
|
||||
var potentialRoute = this.routes[t],
|
||||
pathRegExp = potentialRoute.path,
|
||||
pathname = state.urlInfo.pathname,
|
||||
match;
|
||||
if(pathprefix) {
|
||||
if(pathname.substr(0,pathprefix.length) === pathprefix) {
|
||||
pathname = pathname.substr(pathprefix.length) || "/";
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
} else {
|
||||
match = false;
|
||||
}
|
||||
} else {
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
}
|
||||
if(match && request.method === potentialRoute.method) {
|
||||
state.params = [];
|
||||
for(var p=1; p<match.length; p++) {
|
||||
state.params.push(match[p]);
|
||||
}
|
||||
return potentialRoute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Server.prototype.methodMappings = {
|
||||
"GET": "readers",
|
||||
"OPTIONS": "readers",
|
||||
"HEAD": "readers",
|
||||
"PUT": "writers",
|
||||
"POST": "writers",
|
||||
"DELETE": "writers"
|
||||
};
|
||||
|
||||
/*
|
||||
Check whether a given user is authorized for the specified authorizationType ("readers" or "writers"). Pass null or undefined as the username to check for anonymous access
|
||||
*/
|
||||
Server.prototype.isAuthorized = function(authorizationType,username) {
|
||||
var principals = this.authorizationPrincipals[authorizationType] || [];
|
||||
return principals.indexOf("(anon)") !== -1 || (username && (principals.indexOf("(authenticated)") !== -1 || principals.indexOf(username) !== -1));
|
||||
}
|
||||
|
||||
Server.prototype.requestHandler = function(request,response) {
|
||||
// Compose the state object
|
||||
var self = this;
|
||||
var state = {};
|
||||
state.wiki = self.wiki;
|
||||
state.server = self;
|
||||
state.urlInfo = url.parse(request.url);
|
||||
// Get the principals authorized to access this resource
|
||||
var authorizationType = this.methodMappings[request.method] || "readers";
|
||||
// Check for the CSRF header if this is a write
|
||||
if(!this.csrfDisable && authorizationType === "writers" && request.headers["x-requested-with"] !== "TiddlyWiki") {
|
||||
response.writeHead(403,"'X-Requested-With' header required to login to '" + this.servername + "'");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
// Check whether anonymous access is granted
|
||||
state.allowAnon = this.isAuthorized(authorizationType,null);
|
||||
// Authenticate with the first active authenticator
|
||||
if(this.authenticators.length > 0) {
|
||||
if(!this.authenticators[0].authenticateRequest(request,response,state)) {
|
||||
// Bail if we failed (the authenticator will have sent the response)
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Authorize with the authenticated username
|
||||
if(!this.isAuthorized(authorizationType,state.authenticatedUsername)) {
|
||||
response.writeHead(401,"'" + state.authenticatedUsername + "' is not authorized to access '" + this.servername + "'");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
// Find the route that matches this path
|
||||
var route = self.findMatchingRoute(request,state);
|
||||
// Optionally output debug info
|
||||
if(self.get("debug-level") !== "none") {
|
||||
console.log("Request path:",JSON.stringify(state.urlInfo));
|
||||
console.log("Request headers:",JSON.stringify(request.headers));
|
||||
console.log("authenticatedUsername:",state.authenticatedUsername);
|
||||
}
|
||||
// Return a 404 if we didn't find a route
|
||||
if(!route) {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
// Receive the request body if necessary and hand off to the route handler
|
||||
if(route.bodyFormat === "stream" || request.method === "GET" || request.method === "HEAD") {
|
||||
// Let the route handle the request stream itself
|
||||
route.handler(request,response,state);
|
||||
} else if(route.bodyFormat === "string" || !route.bodyFormat) {
|
||||
// Set the encoding for the incoming request
|
||||
request.setEncoding("utf8");
|
||||
var data = "";
|
||||
request.on("data",function(chunk) {
|
||||
data += chunk.toString();
|
||||
});
|
||||
request.on("end",function() {
|
||||
state.data = data;
|
||||
route.handler(request,response,state);
|
||||
});
|
||||
} else if(route.bodyFormat === "buffer") {
|
||||
var data = [];
|
||||
request.on("data",function(chunk) {
|
||||
data.push(chunk);
|
||||
});
|
||||
request.on("end",function() {
|
||||
state.data = Buffer.concat(data);
|
||||
route.handler(request,response,state);
|
||||
})
|
||||
} else {
|
||||
response.writeHead(400,"Invalid bodyFormat " + route.bodyFormat + " in route " + route.method + " " + route.path.source);
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Listen for requests
|
||||
port: optional port number (falls back to value of "port" variable)
|
||||
host: optional host address (falls back to value of "host" variable)
|
||||
prefix: optional prefix (falls back to value of "path-prefix" variable)
|
||||
*/
|
||||
Server.prototype.listen = function(port,host,prefix) {
|
||||
// Handle defaults for port and host
|
||||
port = port || this.get("port");
|
||||
host = host || this.get("host");
|
||||
prefix = prefix || this.get("path-prefix") || "";
|
||||
// Check for the port being a string and look it up as an environment variable
|
||||
if(parseInt(port,10).toString() !== port) {
|
||||
port = process.env[port] || 8080;
|
||||
}
|
||||
$tw.utils.log("Serving on " + this.protocol + "://" + host + ":" + port + prefix,"brown/orange");
|
||||
$tw.utils.log("(press ctrl-C to exit)","red");
|
||||
// Warn if required plugins are missing
|
||||
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
|
||||
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
|
||||
}
|
||||
// Listen
|
||||
var server;
|
||||
if(this.listenOptions) {
|
||||
server = this.transport.createServer(this.listenOptions,this.requestHandler.bind(this));
|
||||
} else {
|
||||
server = this.transport.createServer(this.requestHandler.bind(this));
|
||||
}
|
||||
return server.listen(port,host);
|
||||
};
|
||||
|
||||
exports.Server = Server;
|
||||
|
||||
})();
|
||||
@@ -57,14 +57,24 @@ exports.startup = function() {
|
||||
$tw.utils.addClass($tw.pageContainer,"tc-page-container-wrapper");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
$tw.pageWidgetNode.render($tw.pageContainer,null);
|
||||
$tw.hooks.invokeHook("th-page-refreshed");
|
||||
})();
|
||||
// Remove any splash screen elements
|
||||
var removeList = document.querySelectorAll(".tc-remove-when-wiki-loaded");
|
||||
$tw.utils.each(removeList,function(removeItem) {
|
||||
if(removeItem.parentNode) {
|
||||
removeItem.parentNode.removeChild(removeItem);
|
||||
}
|
||||
});
|
||||
// Prepare refresh mechanism
|
||||
var deferredChanges = Object.create(null),
|
||||
timerId;
|
||||
function refresh() {
|
||||
// Process the refresh
|
||||
$tw.hooks.invokeHook("th-page-refreshing");
|
||||
$tw.pageWidgetNode.refresh(deferredChanges);
|
||||
deferredChanges = Object.create(null);
|
||||
$tw.hooks.invokeHook("th-page-refreshed");
|
||||
}
|
||||
// Add the change event handler
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {
|
||||
|
||||
@@ -23,13 +23,17 @@ exports.startup = function() {
|
||||
// Install the modal message mechanism
|
||||
$tw.modal = new $tw.utils.Modal($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tm-modal",function(event) {
|
||||
$tw.modal.display(event.param,{variables: event.paramObject});
|
||||
$tw.modal.display(event.param,{variables: event.paramObject, event: event});
|
||||
});
|
||||
// Install the notification mechanism
|
||||
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tm-notify",function(event) {
|
||||
$tw.notifier.display(event.param,{variables: event.paramObject});
|
||||
});
|
||||
// Install the copy-to-clipboard mechanism
|
||||
$tw.rootWidget.addEventListener("tm-copy-to-clipboard",function(event) {
|
||||
$tw.utils.copyToClipboard(event.param);
|
||||
});
|
||||
// Install the scroller
|
||||
$tw.pageScroller = new $tw.utils.PageScroller();
|
||||
$tw.rootWidget.addEventListener("tm-scroll",function(event) {
|
||||
@@ -38,10 +42,17 @@ exports.startup = function() {
|
||||
var fullscreen = $tw.utils.getFullScreenApis();
|
||||
if(fullscreen) {
|
||||
$tw.rootWidget.addEventListener("tm-full-screen",function(event) {
|
||||
if(document[fullscreen._fullscreenElement]) {
|
||||
document[fullscreen._exitFullscreen]();
|
||||
var fullScreenDocument = event.event ? event.event.target.ownerDocument : document;
|
||||
if(event.param === "enter") {
|
||||
fullScreenDocument.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else if(event.param === "exit") {
|
||||
fullScreenDocument[fullscreen._exitFullscreen]();
|
||||
} else {
|
||||
document.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
|
||||
if(fullScreenDocument[fullscreen._fullscreenElement]) {
|
||||
fullScreenDocument[fullscreen._exitFullscreen]();
|
||||
} else {
|
||||
fullScreenDocument.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ exports.startup = function() {
|
||||
if($tw.browser) {
|
||||
$tw.platform.isMac = /Mac/.test(navigator.platform);
|
||||
$tw.platform.isWindows = /win/i.test(navigator.platform);
|
||||
$tw.platform.isLinux = /Linux/i.test(navigator.appVersion);
|
||||
$tw.platform.isLinux = /Linux/i.test(navigator.platform);
|
||||
} else {
|
||||
switch(require("os").platform()) {
|
||||
case "darwin":
|
||||
@@ -55,6 +55,27 @@ exports.startup = function() {
|
||||
$tw.version = $tw.utils.extractVersionInfo();
|
||||
// Set up the performance framework
|
||||
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
|
||||
// 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: $tw.browser ? document : $tw.fakeDocument
|
||||
});
|
||||
// Execute any startup actions
|
||||
var executeStartupTiddlers = function(tag) {
|
||||
$tw.utils.each($tw.wiki.filterTiddlers("[all[shadows+tiddlers]tag[" + tag + "]!has[draft.of]]"),function(title) {
|
||||
$tw.rootWidget.invokeActionString($tw.wiki.getTiddlerText(title),$tw.rootWidget);
|
||||
});
|
||||
};
|
||||
executeStartupTiddlers("$:/tags/StartupAction");
|
||||
if($tw.browser) {
|
||||
executeStartupTiddlers("$:/tags/StartupAction/Browser");
|
||||
}
|
||||
if($tw.node) {
|
||||
executeStartupTiddlers("$:/tags/StartupAction/Node");
|
||||
}
|
||||
// Kick off the language manager and switcher
|
||||
$tw.language = new $tw.Language();
|
||||
$tw.languageSwitcher = new $tw.PluginSwitcher({
|
||||
@@ -62,7 +83,7 @@ exports.startup = function() {
|
||||
pluginType: "language",
|
||||
controllerTitle: "$:/language",
|
||||
defaultPlugins: [
|
||||
"$:/languages/en-US"
|
||||
"$:/languages/en-GB"
|
||||
],
|
||||
onSwitch: function(plugins) {
|
||||
if($tw.browser) {
|
||||
@@ -87,18 +108,16 @@ exports.startup = function() {
|
||||
});
|
||||
// Kick off the keyboard manager
|
||||
$tw.keyboardManager = new $tw.KeyboardManager();
|
||||
// Listen for shortcuts
|
||||
if($tw.browser) {
|
||||
$tw.utils.addEventListeners(document,[{
|
||||
name: "keydown",
|
||||
handlerObject: $tw.keyboardManager,
|
||||
handlerMethod: "handleKeydownEvent"
|
||||
}]);
|
||||
}
|
||||
// Clear outstanding tiddler store change events to avoid an unnecessary refresh cycle at startup
|
||||
$tw.wiki.clearTiddlerEventQueue();
|
||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||
if($tw.browser) {
|
||||
$tw.rootWidget = new widget.widget({
|
||||
type: "widget",
|
||||
children: []
|
||||
},{
|
||||
wiki: $tw.wiki,
|
||||
document: document
|
||||
});
|
||||
}
|
||||
// Find a working syncadaptor
|
||||
$tw.syncadaptor = undefined;
|
||||
$tw.modules.forEachModuleOfType("syncadaptor",function(title,module) {
|
||||
@@ -111,7 +130,11 @@ exports.startup = function() {
|
||||
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki, syncadaptor: $tw.syncadaptor});
|
||||
}
|
||||
// Setup the saver handler
|
||||
$tw.saverHandler = new $tw.SaverHandler({wiki: $tw.wiki, dirtyTracking: !$tw.syncadaptor});
|
||||
$tw.saverHandler = new $tw.SaverHandler({
|
||||
wiki: $tw.wiki,
|
||||
dirtyTracking: !$tw.syncadaptor,
|
||||
preloadDirty: $tw.boot.preloadDirty || []
|
||||
});
|
||||
// Host-specific startup
|
||||
if($tw.browser) {
|
||||
// Install the popup manager
|
||||
|
||||
@@ -27,10 +27,18 @@ 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"
|
||||
var CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD = "$:/config/Navigation/Permalinkview/CopyToClipboard"; // Can be "yes" (default) or "no"
|
||||
var CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR = "$:/config/Navigation/Permalinkview/UpdateAddressBar"; // Can be "yes" (default) or "no"
|
||||
|
||||
|
||||
// Links to help, if there is no param
|
||||
var HELP_OPEN_EXTERNAL_WINDOW = "http://tiddlywiki.com/#WidgetMessage%3A%20tm-open-external-window";
|
||||
|
||||
exports.startup = function() {
|
||||
// Open startup tiddlers
|
||||
openStartupTiddlers();
|
||||
openStartupTiddlers({
|
||||
disableHistory: $tw.boot.disableStartupNavigation
|
||||
});
|
||||
if($tw.browser) {
|
||||
// Set up location hash update
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
@@ -53,6 +61,14 @@ exports.startup = function() {
|
||||
$tw.rootWidget.addEventListener("tm-browser-refresh",function(event) {
|
||||
window.location.reload(true);
|
||||
});
|
||||
// Listen for tm-open-external-window message
|
||||
$tw.rootWidget.addEventListener("tm-open-external-window",function(event) {
|
||||
var paramObject = event.paramObject || {},
|
||||
strUrl = event.param || HELP_OPEN_EXTERNAL_WINDOW,
|
||||
strWindowName = paramObject.windowName,
|
||||
strWindowFeatures = paramObject.windowFeatures;
|
||||
window.open(strUrl, strWindowName, strWindowFeatures);
|
||||
});
|
||||
// Listen for the tm-print message
|
||||
$tw.rootWidget.addEventListener("tm-print",function(event) {
|
||||
(event.event.view || window).print();
|
||||
@@ -66,30 +82,33 @@ exports.startup = function() {
|
||||
storyList = $tw.hooks.invokeHook("th-opening-default-tiddlers-list",storyList);
|
||||
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
|
||||
if(storyList[0]) {
|
||||
$tw.wiki.addToHistory(storyList[0]);
|
||||
$tw.wiki.addToHistory(storyList[0]);
|
||||
}
|
||||
});
|
||||
// Listen for the tm-permalink message
|
||||
$tw.rootWidget.addEventListener("tm-permalink",function(event) {
|
||||
updateLocationHash({
|
||||
updateAddressBar: "permalink",
|
||||
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permalink" : "none",
|
||||
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
|
||||
targetTiddler: event.param || event.tiddlerTitle
|
||||
targetTiddler: event.param || event.tiddlerTitle,
|
||||
copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permalink" : "none"
|
||||
});
|
||||
});
|
||||
// Listen for the tm-permaview message
|
||||
$tw.rootWidget.addEventListener("tm-permaview",function(event) {
|
||||
updateLocationHash({
|
||||
updateAddressBar: "permaview",
|
||||
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permaview" : "none",
|
||||
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
|
||||
targetTiddler: event.param || event.tiddlerTitle
|
||||
});
|
||||
targetTiddler: event.param || event.tiddlerTitle,
|
||||
copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permaview" : "none"
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Process the location hash to open the specified tiddlers. Options:
|
||||
disableHistory: if true $:/History is NOT updated
|
||||
defaultToCurrentStory: If true, the current story is retained as the default, instead of opening the default tiddlers
|
||||
*/
|
||||
function openStartupTiddlers(options) {
|
||||
@@ -130,15 +149,18 @@ function openStartupTiddlers(options) {
|
||||
}
|
||||
// Save the story list
|
||||
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
|
||||
// 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);
|
||||
} else if(storyList.length > 0) {
|
||||
$tw.wiki.addToHistory(storyList[0]);
|
||||
// Update history
|
||||
if(!options.disableHistory) {
|
||||
// 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);
|
||||
} else if(storyList.length > 0) {
|
||||
$tw.wiki.addToHistory(storyList[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,41 +168,52 @@ function openStartupTiddlers(options) {
|
||||
options: See below
|
||||
options.updateAddressBar: "permalink", "permaview" or "no" (defaults to "permaview")
|
||||
options.updateHistory: "yes" or "no" (defaults to "no")
|
||||
options.copyToClipboard: "permalink", "permaview" or "no" (defaults to "no")
|
||||
options.targetTiddler: optional title of target tiddler for permalink
|
||||
*/
|
||||
function updateLocationHash(options) {
|
||||
if(options.updateAddressBar !== "no") {
|
||||
// Get the story and the history stack
|
||||
var storyList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE),
|
||||
historyList = $tw.wiki.getTiddlerData(DEFAULT_HISTORY_TITLE,[]),
|
||||
// Get the story and the history stack
|
||||
var storyList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE),
|
||||
historyList = $tw.wiki.getTiddlerData(DEFAULT_HISTORY_TITLE,[]),
|
||||
targetTiddler = "";
|
||||
if(options.targetTiddler) {
|
||||
targetTiddler = options.targetTiddler;
|
||||
} else {
|
||||
// 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 = "";
|
||||
if(options.targetTiddler) {
|
||||
targetTiddler = options.targetTiddler;
|
||||
} else {
|
||||
// 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(options.updateAddressBar === "permalink") {
|
||||
}
|
||||
// Assemble the location hash
|
||||
switch(options.updateAddressBar) {
|
||||
case "permalink":
|
||||
$tw.locationHash = "#" + encodeURIComponent(targetTiddler);
|
||||
} else {
|
||||
break;
|
||||
case "permaview":
|
||||
$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(options.updateHistory === "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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Copy URL to the clipboard
|
||||
switch(options.copyToClipboard) {
|
||||
case "permalink":
|
||||
$tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler));
|
||||
break;
|
||||
case "permaview":
|
||||
$tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList)));
|
||||
break;
|
||||
}
|
||||
// Only change the location hash if we must, thus avoiding unnecessary onhashchange events
|
||||
if($tw.utils.getLocationHash() !== $tw.locationHash) {
|
||||
if(options.updateHistory === "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,16 @@ exports.startup = function() {
|
||||
height = paramObject.height || "600",
|
||||
variables = $tw.utils.extend({},paramObject,{currentTiddler: title});
|
||||
// Open the window
|
||||
var srcWindow = window.open("","external-" + title,"scrollbars,width=" + width + ",height=" + height),
|
||||
var srcWindow,
|
||||
srcDocument;
|
||||
// In case that popup blockers deny opening a new window
|
||||
try {
|
||||
srcWindow = window.open("","external-" + title,"scrollbars,width=" + width + ",height=" + height),
|
||||
srcDocument = srcWindow.document;
|
||||
}
|
||||
catch(e) {
|
||||
return;
|
||||
}
|
||||
windows[title] = srcWindow;
|
||||
// Check for reopening the same window
|
||||
if(srcWindow.haveInitialisedWindow) {
|
||||
@@ -70,6 +78,16 @@ exports.startup = function() {
|
||||
widgetNode.refresh(changes);
|
||||
};
|
||||
$tw.wiki.addEventListener("change",refreshHandler);
|
||||
// Listen for keyboard shortcuts
|
||||
$tw.utils.addEventListeners(srcDocument,[{
|
||||
name: "keydown",
|
||||
handlerObject: $tw.keyboardManager,
|
||||
handlerMethod: "handleKeydownEvent"
|
||||
},{
|
||||
name: "click",
|
||||
handlerObject: $tw.popup,
|
||||
handlerMethod: "handleEvent"
|
||||
}]);
|
||||
srcWindow.haveInitialisedWindow = true;
|
||||
});
|
||||
// Close open windows when unloading main window
|
||||
|
||||
@@ -16,8 +16,11 @@ The syncer tracks changes to the store. If a syncadaptor is used then individual
|
||||
Defaults
|
||||
*/
|
||||
Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn";
|
||||
Syncer.prototype.titleIsAnonymous = "$:/status/IsAnonymous";
|
||||
Syncer.prototype.titleIsReadOnly = "$:/status/IsReadOnly";
|
||||
Syncer.prototype.titleUserName = "$:/status/UserName";
|
||||
Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
|
||||
Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
|
||||
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
|
||||
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
|
||||
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
|
||||
@@ -41,7 +44,7 @@ function Syncer(options) {
|
||||
this.taskTimerInterval = options.taskTimerInterval || this.taskTimerInterval;
|
||||
this.throttleInterval = options.throttleInterval || this.throttleInterval;
|
||||
this.fallbackInterval = options.fallbackInterval || this.fallbackInterval;
|
||||
this.pollTimerInterval = options.pollTimerInterval || this.pollTimerInterval;
|
||||
this.pollTimerInterval = options.pollTimerInterval || parseInt(this.wiki.getTiddlerText(this.titleSyncPollingInterval,""),10) || this.pollTimerInterval;
|
||||
this.logging = "logging" in options ? options.logging : true;
|
||||
// Make a logger
|
||||
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : "") + (this.syncadaptor.name ? ("-" + this.syncadaptor.name) : ""),{
|
||||
@@ -151,7 +154,7 @@ Save an incoming tiddler in the store, and updates the associated tiddlerInfo
|
||||
*/
|
||||
Syncer.prototype.storeTiddler = function(tiddlerFields,hasBeenLazyLoaded) {
|
||||
// Save the tiddler
|
||||
var tiddler = new $tw.Tiddler(this.wiki.getTiddler(tiddlerFields.title),tiddlerFields);
|
||||
var tiddler = new $tw.Tiddler(tiddlerFields);
|
||||
this.wiki.addTiddler(tiddler);
|
||||
// Save the tiddler revision and changeCount details
|
||||
this.tiddlerInfo[tiddlerFields.title] = {
|
||||
@@ -169,17 +172,17 @@ Syncer.prototype.getStatus = function(callback) {
|
||||
// Mark us as not logged in
|
||||
this.wiki.addTiddler({title: this.titleIsLoggedIn,text: "no"});
|
||||
// Get login status
|
||||
this.syncadaptor.getStatus(function(err,isLoggedIn,username) {
|
||||
this.syncadaptor.getStatus(function(err,isLoggedIn,username,isReadOnly,isAnonymous) {
|
||||
if(err) {
|
||||
self.logger.alert(err);
|
||||
return;
|
||||
}
|
||||
// Set the various status tiddlers
|
||||
self.wiki.addTiddler({title: self.titleIsReadOnly,text: isReadOnly ? "yes" : "no"});
|
||||
self.wiki.addTiddler({title: self.titleIsAnonymous,text: isAnonymous ? "yes" : "no"});
|
||||
self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"});
|
||||
if(isLoggedIn) {
|
||||
self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
|
||||
} else {
|
||||
self.wiki.deleteTiddler(self.titleUserName);
|
||||
}
|
||||
// Invoke the callback
|
||||
if(callback) {
|
||||
@@ -271,13 +274,16 @@ Syncer.prototype.handleLazyLoadEvent = function(title) {
|
||||
// Don't lazy load the same tiddler twice
|
||||
var info = this.tiddlerInfo[title];
|
||||
if(!info || !info.hasBeenLazyLoaded) {
|
||||
this.createTiddlerInfo(title);
|
||||
this.tiddlerInfo[title].hasBeenLazyLoaded = true;
|
||||
// Queue up a sync task to load this tiddler
|
||||
this.enqueueSyncTask({
|
||||
type: "load",
|
||||
title: title
|
||||
});
|
||||
// Don't lazy load if the tiddler isn't included in the sync filter
|
||||
if(this.filterFn.call(this.wiki).indexOf(title) !== -1) {
|
||||
this.createTiddlerInfo(title);
|
||||
this.tiddlerInfo[title].hasBeenLazyLoaded = true;
|
||||
// Queue up a sync task to load this tiddler
|
||||
this.enqueueSyncTask({
|
||||
type: "load",
|
||||
title: title
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -39,6 +39,18 @@ exports.getFieldString = function(field) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get the value of a field as a list
|
||||
*/
|
||||
exports.getFieldList = function(field) {
|
||||
var value = this.fields[field];
|
||||
// Check for a missing field
|
||||
if(value === undefined || value === null) {
|
||||
return [];
|
||||
}
|
||||
return $tw.utils.parseStringArray(value);
|
||||
};
|
||||
|
||||
/*
|
||||
Get all the fields as a hashmap of strings. Options:
|
||||
exclude: an array of field names to exclude
|
||||
@@ -75,57 +87,6 @@ exports.getFieldStringBlock = function(options) {
|
||||
return fields.join("\n");
|
||||
};
|
||||
|
||||
/*
|
||||
Compare two tiddlers for equality
|
||||
tiddler: the tiddler to compare
|
||||
excludeFields: array of field names to exclude from the comparison
|
||||
*/
|
||||
exports.isEqual = function(tiddler,excludeFields) {
|
||||
if(!(tiddler instanceof $tw.Tiddler)) {
|
||||
return false;
|
||||
}
|
||||
excludeFields = excludeFields || [];
|
||||
var self = this,
|
||||
differences = []; // Fields that have differences
|
||||
// Add to the differences array
|
||||
function addDifference(fieldName) {
|
||||
// Check for this field being excluded
|
||||
if(excludeFields.indexOf(fieldName) === -1) {
|
||||
// Save the field as a difference
|
||||
$tw.utils.pushTop(differences,fieldName);
|
||||
}
|
||||
}
|
||||
// Returns true if the two values of this field are equal
|
||||
function isFieldValueEqual(fieldName) {
|
||||
var valueA = self.fields[fieldName],
|
||||
valueB = tiddler.fields[fieldName];
|
||||
// Check for identical string values
|
||||
if(typeof(valueA) === "string" && typeof(valueB) === "string" && valueA === valueB) {
|
||||
return true;
|
||||
}
|
||||
// Check for identical array values
|
||||
if($tw.utils.isArray(valueA) && $tw.utils.isArray(valueB) && $tw.utils.isArrayEqual(valueA,valueB)) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise the fields must be different
|
||||
return false;
|
||||
}
|
||||
// Compare our fields
|
||||
for(var fieldName in this.fields) {
|
||||
if(!isFieldValueEqual(fieldName)) {
|
||||
addDifference(fieldName);
|
||||
}
|
||||
}
|
||||
// There's a difference for every field in the other tiddler that we don't have
|
||||
for(fieldName in tiddler.fields) {
|
||||
if(!(fieldName in this.fields)) {
|
||||
addDifference(fieldName);
|
||||
}
|
||||
}
|
||||
// Return whether there were any differences
|
||||
return differences.length === 0;
|
||||
};
|
||||
|
||||
exports.getFieldDay = function(field) {
|
||||
if(this.cache && this.cache.day && $tw.utils.hop(this.cache.day,field) ) {
|
||||
return this.cache.day[field];
|
||||
|
||||
@@ -13,11 +13,13 @@ Upgrader module that suppresses certain system tiddlers that shouldn't be import
|
||||
"use strict";
|
||||
|
||||
var DONT_IMPORT_LIST = ["$:/StoryList","$:/HistoryList"],
|
||||
DONT_IMPORT_PREFIX_LIST = ["$:/temp/","$:/state/"];
|
||||
DONT_IMPORT_PREFIX_LIST = ["$:/temp/","$:/state/","$:/Import"],
|
||||
WARN_IMPORT_PREFIX_LIST = ["$:/core/modules/"];
|
||||
|
||||
exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
var self = this,
|
||||
messages = {};
|
||||
messages = {},
|
||||
showAlert = false;
|
||||
// Check for tiddlers on our list
|
||||
$tw.utils.each(titles,function(title) {
|
||||
if(DONT_IMPORT_LIST.indexOf(title) !== -1) {
|
||||
@@ -31,6 +33,17 @@ exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/State/Suppressed");
|
||||
}
|
||||
}
|
||||
for(var t=0; t<WARN_IMPORT_PREFIX_LIST.length; t++) {
|
||||
var prefix = WARN_IMPORT_PREFIX_LIST[t];
|
||||
if(title.substr(0,prefix.length) === prefix) {
|
||||
showAlert = true;
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/System/Warning");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(showAlert) {
|
||||
var logger = new $tw.utils.Logger("import");
|
||||
logger.alert($tw.language.getString("Import/Upgrader/System/Alert"));
|
||||
}
|
||||
});
|
||||
return messages;
|
||||
|
||||
46
core/modules/utils/csv.js
Normal file
46
core/modules/utils/csv.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/csv.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
|
||||
A barebones CSV parser
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Parse a CSV string with a header row and return an array of hashmaps.
|
||||
*/
|
||||
exports.parseCsvStringWithHeader = function(text,options) {
|
||||
options = options || {};
|
||||
var separator = options.separator || ",",
|
||||
rows = text.split(/\r?\n/mg).map(function(row) {
|
||||
return $tw.utils.trim(row);
|
||||
}).filter(function(row) {
|
||||
return row !== "";
|
||||
});
|
||||
if(rows.length < 1) {
|
||||
return "Missing header row";
|
||||
}
|
||||
var headings = rows[0].split(separator),
|
||||
results = [];
|
||||
for(var row=1; row<rows.length; row++) {
|
||||
var columns = rows[row].split(separator),
|
||||
columnResult = Object.create(null);
|
||||
if(columns.length !== headings.length) {
|
||||
return "Malformed CSV row '" + rows[row] + "'";
|
||||
}
|
||||
for(var column=0; column<columns.length; column++) {
|
||||
var columnName = headings[column];
|
||||
columnResult[columnName] = $tw.utils.trim(columns[column] || "");
|
||||
}
|
||||
results.push(columnResult);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
})();
|
||||
202
core/modules/utils/diff-match-patch/LICENSE
Executable file
202
core/modules/utils/diff-match-patch/LICENSE
Executable file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
41
core/modules/utils/diff-match-patch/README.md
Executable file
41
core/modules/utils/diff-match-patch/README.md
Executable file
@@ -0,0 +1,41 @@
|
||||
The Diff Match and Patch libraries offer robust algorithms to perform the
|
||||
operations required for synchronizing plain text.
|
||||
|
||||
1. Diff:
|
||||
* Compare two blocks of plain text and efficiently return a list of differences.
|
||||
* [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
|
||||
2. Match:
|
||||
* Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
|
||||
* [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
|
||||
3. Patch:
|
||||
* Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
|
||||
* [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
|
||||
|
||||
Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
|
||||
|
||||
### Reference
|
||||
|
||||
* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
|
||||
* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
|
||||
* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
|
||||
* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
|
||||
* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
|
||||
|
||||
### Languages
|
||||
Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
|
||||
|
||||
* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
|
||||
* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
|
||||
* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
|
||||
* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
|
||||
* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
|
||||
* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
|
||||
* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
|
||||
* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
|
||||
|
||||
A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
|
||||
|
||||
### Algorithms
|
||||
This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
|
||||
|
||||
This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
|
||||
53
core/modules/utils/diff-match-patch/diff_match_patch.js
Executable file
53
core/modules/utils/diff-match-patch/diff_match_patch.js
Executable file
@@ -0,0 +1,53 @@
|
||||
function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32}var DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;
|
||||
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[DIFF_EQUAL,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
|
||||
b,e,d);c&&a.unshift([DIFF_EQUAL,c]);g&&a.push([DIFF_EQUAL,g]);this.diff_cleanupMerge(a);return a};
|
||||
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[DIFF_INSERT,b]];if(!b)return[[DIFF_DELETE,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[DIFF_INSERT,e.substring(0,g)],[DIFF_EQUAL,f],[DIFF_INSERT,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[[DIFF_DELETE,a],[DIFF_INSERT,b]]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([[DIFF_EQUAL,
|
||||
a]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,d):this.diff_bisect_(a,b,d)};
|
||||
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([DIFF_EQUAL,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case DIFF_INSERT:e++;g+=a[b][1];break;case DIFF_DELETE:d++;f+=a[b][1];break;case DIFF_EQUAL:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=
|
||||
d.length}d=e=0;g=f=""}b++}a.pop();return a};
|
||||
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=2*f,h=Array(g),l=Array(g),k=0;k<g;k++)h[k]=-1,l[k]=-1;h[f+1]=0;l[f+1]=0;k=d-e;for(var m=0!=k%2,p=0,x=0,w=0,q=0,t=0;t<f&&!((new Date).getTime()>c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]<h[n+1]?h[n+1]:h[n-1]+1;for(var y=r-v;r<d&&y<e&&a.charAt(r)==b.charAt(y);)r++,y++;h[n]=r;if(r>d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n<g&&-1!=l[n])){var u=d-l[n];if(r>=
|
||||
u)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]<l[n+1]?l[n+1]:l[n-1]+1;for(r=u-v;u<d&&r<e&&a.charAt(d-u-1)==b.charAt(e-r-1);)u++,r++;l[n]=u;if(u>d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n<g&&-1!=h[n]&&(r=h[n],y=f+r-n,u=d-u,r>=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[[DIFF_DELETE,a],[DIFF_INSERT,b]]};
|
||||
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
|
||||
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var h=a.substring(c,f+1);c=f+1;(e.hasOwnProperty?e.hasOwnProperty(h):void 0!==e[h])?b+=String.fromCharCode(e[h]):(b+=String.fromCharCode(g),e[h]=g,d[g++]=h)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
|
||||
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;d=0;for(var e=1;;){var f=a.substring(c-e);f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
|
||||
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,k,l,m;-1!=(e=b.indexOf(d,e+1));){var p=f.diff_commonPrefix(a.substring(c),b.substring(e)),u=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<u+p&&(g=b.substring(e-u,e)+b.substring(e,e+p),h=a.substring(0,c-u),k=a.substring(c+p),l=b.substring(0,e-u),m=b.substring(e+p))}return 2*g.length>=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null;
|
||||
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4));d=c(d,e,Math.ceil(d.length/2));if(g||d)g=d?g?g[4].length>d[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]};
|
||||
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f<a.length;)a[f][0]==DIFF_EQUAL?(c[d++]=f,g=l,h=k,k=l=0,e=a[f][1]):(a[f][0]==DIFF_INSERT?l+=a[f][1].length:k+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(l,k)&&(a.splice(c[d-1],0,[DIFF_DELETE,e]),a[c[d-1]+1][0]=DIFF_INSERT,d--,d--,f=0<d?c[d-1]:-1,k=l=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(a[f-1][0]==
|
||||
DIFF_DELETE&&a[f][0]==DIFF_INSERT){b=a[f-1][1];c=a[f][1];d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[DIFF_EQUAL,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[DIFF_EQUAL,b.substring(0,e)]),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE,a[f+1][1]=b.substring(e),f++;f++}f++}};
|
||||
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_);
|
||||
return k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(a[c-1][0]==DIFF_EQUAL&&a[c+1][0]==DIFF_EQUAL){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g){var h=e.substring(e.length-g);d=d.substring(0,d.length-g);e=h+e.substring(0,e.length-g);f=h+f}g=d;h=e;for(var l=f,k=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){d+=e.charAt(0);e=e.substring(1)+f.charAt(0);f=f.substring(1);var m=b(d,e)+b(e,f);m>=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-
|
||||
1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
|
||||
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;f<a.length;)a[f][0]==DIFF_EQUAL?(a[f][1].length<this.Diff_EditCost&&(l||k)?(c[d++]=f,g=l,h=k,e=a[f][1]):(d=0,e=null),l=k=!1):(a[f][0]==DIFF_DELETE?k=!0:l=!0,e&&(g&&h&&l&&k||e.length<this.Diff_EditCost/2&&3==g+h+l+k)&&(a.splice(c[d-1],0,[DIFF_DELETE,e]),a[c[d-1]+1][0]=DIFF_INSERT,d--,e=null,g&&h?(l=k=!0,d=0):(d--,f=0<d?c[d-1]:-1,l=k=!1),b=!0)),f++;b&&this.diff_cleanupMerge(a)};
|
||||
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([DIFF_EQUAL,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case DIFF_INSERT:d++;f+=a[b][1];b++;break;case DIFF_DELETE:c++;e+=a[b][1];b++;break;case DIFF_EQUAL:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&a[b-c-d-1][0]==DIFF_EQUAL?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[DIFF_EQUAL,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-
|
||||
g)+a[b][1],f=f.substring(0,f.length-g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[DIFF_INSERT,f]):0===d?a.splice(b-c,c+d,[DIFF_DELETE,e]):a.splice(b-c-d,c+d,[DIFF_DELETE,e],[DIFF_INSERT,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&a[b-1][0]==DIFF_EQUAL?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)a[b-1][0]==DIFF_EQUAL&&a[b+1][0]==DIFF_EQUAL&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,
|
||||
a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};
|
||||
diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){a[g][0]!==DIFF_INSERT&&(c+=a[g][1].length);a[g][0]!==DIFF_DELETE&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)};
|
||||
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],l=a[g][1].replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case DIFF_INSERT:b[g]='<ins style="background:#e6ffe6;">'+l+"</ins>";break;case DIFF_DELETE:b[g]='<del style="background:#ffe6e6;">'+l+"</del>";break;case DIFF_EQUAL:b[g]="<span>"+l+"</span>"}}return b.join("")};
|
||||
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_INSERT&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_DELETE&&(b[c]=a[c][1]);return b.join("")};
|
||||
diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][1];switch(a[e][0]){case DIFF_INSERT:c+=f.length;break;case DIFF_DELETE:d+=f.length;break;case DIFF_EQUAL:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
|
||||
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case DIFF_INSERT:b[c]="+"+encodeURI(a[c][1]);break;case DIFF_DELETE:b[c]="-"+a[c][1].length;break;case DIFF_EQUAL:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
|
||||
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[DIFF_INSERT,decodeURI(h)]}catch(k){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var l=parseInt(h,10);if(isNaN(l)||0>l)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=l);"="==f[g].charAt(0)?c[d++]=[DIFF_EQUAL,h]:c[d++]=[DIFF_DELETE,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
|
||||
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
|
||||
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance?e+g/f.Match_Distance:g?1:e}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<<b.length-1;h=-1;for(var k,m,p=b.length+a.length,x,w=0;w<b.length;w++){k=0;for(m=p;k<m;)d(w,
|
||||
c+m)<=g?k=m:p=m,m=Math.floor((p-k)/2+k);p=m;k=Math.max(1,c-m+1);var q=Math.min(c+m,a.length)+b.length;m=Array(q+2);for(m[q+1]=(1<<w)-1;q>=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h};
|
||||
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
|
||||
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([DIFF_EQUAL,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([DIFF_EQUAL,d]);a.start1-=c.length;a.start2-=
|
||||
c.length;a.length1+=c.length+d.length;a.length2+=c.length+d.length}};
|
||||
diff_match_patch.prototype.patch_make=function(a,b,c){if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c){var d=a;b=this.diff_main(d,b,!0);2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b))}else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
|
||||
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,l=0;l<b.length;l++){var k=b[l][0],m=b[l][1];e||k===DIFF_EQUAL||(a.start1=f,a.start2=g);switch(k){case DIFF_INSERT:a.diffs[e++]=b[l];a.length2+=m.length;d=d.substring(0,g)+m+d.substring(g);break;case DIFF_DELETE:a.length1+=m.length;a.diffs[e++]=b[l];d=d.substring(0,g)+d.substring(g+m.length);break;case DIFF_EQUAL:m.length<=2*this.Patch_Margin&&e&&b.length!=l+1?(a.diffs[e++]=b[l],a.length1+=m.length,a.length2+=m.length):
|
||||
m.length>=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};
|
||||
diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
|
||||
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),l=-1;if(h.length>this.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h,
|
||||
g);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;l<a[f].diffs.length;l++){var p=a[f].diffs[l];p[0]!==DIFF_EQUAL&&(m=this.diff_xIndex(g,h));p[0]===
|
||||
DIFF_INSERT?b=b.substring(0,k+m)+p[1]+b.substring(k+m):p[0]===DIFF_DELETE&&(b=b.substring(0,k+m)+b.substring(k+this.diff_xIndex(g,h+p[1].length)));p[0]!==DIFF_DELETE&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
|
||||
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;d=a[0];var e=d.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift([DIFF_EQUAL,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||e[e.length-
|
||||
1][0]!=DIFF_EQUAL?(e.push([DIFF_EQUAL,c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
|
||||
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,l=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([DIFF_EQUAL,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){g=d.diffs[0][0];var k=d.diffs[0][1];g===DIFF_INSERT?(h.length2+=k.length,f+=k.length,h.diffs.push(d.diffs.shift()),
|
||||
l=!1):g===DIFF_DELETE&&1==h.diffs.length&&h.diffs[0][0]==DIFF_EQUAL&&k.length>2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push([g,k]),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push([g,k]),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,
|
||||
this.Patch_Margin);""!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push([DIFF_EQUAL,k]));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
|
||||
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
|
||||
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([DIFF_DELETE,g]);else if("+"==e)f.diffs.push([DIFF_INSERT,g]);else if(" "==e)f.diffs.push([DIFF_EQUAL,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
|
||||
diff_match_patch.patch_obj.prototype.toString=function(){for(var a=["@@ -"+(0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1)+" +"+(0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2)+" @@\n"],b,c=0;c<this.diffs.length;c++){switch(this.diffs[c][0]){case DIFF_INSERT:b="+";break;case DIFF_DELETE:b="-";break;case DIFF_EQUAL:b=" "}a[c+1]=b+encodeURI(this.diffs[c][1])+"\n"}return a.join("").replace(/%20/g," ")};
|
||||
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=DIFF_DELETE;this.DIFF_INSERT=DIFF_INSERT;this.DIFF_EQUAL=DIFF_EQUAL;
|
||||
2192
core/modules/utils/diff-match-patch/diff_match_patch_uncompressed.js
Executable file
2192
core/modules/utils/diff-match-patch/diff_match_patch_uncompressed.js
Executable file
File diff suppressed because it is too large
Load Diff
14
core/modules/utils/diff-match-patch/tiddlywiki.files
Normal file
14
core/modules/utils/diff-match-patch/tiddlywiki.files
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"tiddlers": [
|
||||
{
|
||||
"file": "diff_match_patch.js",
|
||||
"fields": {
|
||||
"type": "application/javascript",
|
||||
"title": "$:/core/modules/utils/diff-match-patch/diff_match_patch.js",
|
||||
"module-type": "library"
|
||||
},
|
||||
"prefix": "(function(){",
|
||||
"suffix": "}).call(exports);"
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user