1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-22 19:04:38 +00:00

Compare commits

...

248 Commits

Author SHA1 Message Date
jeremy@jermolene.com
8816efa737 Restore standard sitetitle 2023-04-19 11:51:32 +01:00
jeremy@jermolene.com
093274f0fa Remove custom CI step for this branch 2023-04-19 11:51:21 +01:00
jeremy@jermolene.com
3b3879df03 Update 5.3.0 release note 2023-04-19 11:35:25 +01:00
jeremy@jermolene.com
55e38eb89a Update modification times of doc tiddlers
So that they are at the top of the recent tab
2023-04-19 11:32:34 +01:00
jeremy@jermolene.com
b91872bed1 Merge branch 'master' into parameterised-transclusions 2023-04-18 10:48:39 +01:00
jeremy@jermolene.com
f41051b621 Merge branch 'master' into parameterised-transclusions 2023-04-12 17:11:38 +01:00
jeremy@jermolene.com
3689171198 Merge branch 'master' into parameterised-transclusions 2023-04-10 16:07:54 +01:00
jeremy@jermolene.com
a2cbb6f992 Merge branch 'master' into parameterised-transclusions 2023-04-06 16:11:54 +01:00
jeremy@jermolene.com
5b271bb4f4 Fix nonstandard initialisation code for fill/parameter/slot widgets 2023-04-06 08:50:47 +01:00
jeremy@jermolene.com
bb2dea2414 Remove 5.2.8 release note 2023-04-02 14:42:39 +01:00
jeremy@jermolene.com
7ba666bd53 Merge branch 'master' into parameterised-transclusions 2023-04-02 14:00:47 +01:00
jeremy@jermolene.com
cff3db6eaa Merge branch 'master' into parameterised-transclusions 2023-03-26 08:29:41 +01:00
jeremy@jermolene.com
8c98fc4ab1 Merge branch 'master' into parameterised-transclusions 2023-03-21 08:08:18 +00:00
jeremy@jermolene.com
e2cc32aa75 Merge branch 'master' into parameterised-transclusions 2023-03-18 16:04:20 +00:00
jeremy@jermolene.com
f9751f19b6 Merge branch 'master' into parameterised-transclusions 2023-03-12 16:19:30 +00:00
jeremy@jermolene.com
2a2939f4eb Merge branch 'master' into parameterised-transclusions 2023-03-10 09:10:27 +00:00
jeremy@jermolene.com
f417bdb742 Merge branch 'master' into parameterised-transclusions 2023-03-06 09:06:41 +00:00
jeremy@jermolene.com
ea055f2ba5 Merge branch 'master' into parameterised-transclusions 2023-02-28 08:45:56 +00:00
jeremy@jermolene.com
0d1667c467 Merge branch 'master' into parameterised-transclusions 2023-02-10 16:04:08 +00:00
jeremy@jermolene.com
86569e5ade Merge branch 'master' into parameterised-transclusions 2023-01-30 21:43:00 +00:00
jeremy@jermolene.com
1dea0a03b8 Merge branch 'master' into parameterised-transclusions 2023-01-30 09:43:54 +00:00
jeremy@jermolene.com
59895db097 Merge branch 'master' into parameterised-transclusions 2023-01-23 16:36:56 +00:00
jeremy@jermolene.com
683414f51e Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
2023-01-23 16:34:08 +00:00
jeremy@jermolene.com
34afe4e143 Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
2023-01-21 22:07:34 +00:00
jeremy@jermolene.com
d4ab427ceb Refactor wikified function tests 2023-01-21 21:04:43 +00:00
jeremy@jermolene.com
a62d7f1b0d Merge branch 'master' into parameterised-transclusions 2023-01-21 20:45:21 +00:00
jeremy@jermolene.com
66cf779abb Merge branch 'master' into parameterised-transclusions 2023-01-18 09:05:48 +00:00
jeremy@jermolene.com
81002f9f30 Merge branch 'master' into parameterised-transclusions 2023-01-17 16:40:47 +00:00
jeremy@jermolene.com
923f9084f8 New test missed off a45349cc99 2023-01-11 09:15:40 +00:00
jeremy@jermolene.com
a45349cc99 Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
2023-01-11 08:57:54 +00:00
jeremy@jermolene.com
7a4a8bd3f6 Rename test 2023-01-07 09:41:39 +00:00
jeremy@jermolene.com
4f96465141 Merge branch 'master' into parameterised-transclusions 2023-01-07 09:34:11 +00:00
jeremy@jermolene.com
76e3a341da Merge branch 'master' into parameterised-transclusions 2022-12-21 17:26:10 +00:00
jeremy@jermolene.com
34bd0f0ae5 Merge branch 'master' into parameterised-transclusions 2022-12-13 17:31:51 +00:00
jeremy@jermolene.com
34ce7cacc3 Merge branch 'master' into parameterised-transclusions 2022-12-13 11:08:54 +00:00
jeremy@jermolene.com
2bf9e97eb5 Merge branch 'master' into parameterised-transclusions 2022-12-08 08:29:56 +00:00
jeremy@jermolene.com
be65819441 Docs note about nested macro definitions 2022-12-07 10:01:58 +00:00
jeremy@jermolene.com
5bb7168c02 Merge branch 'master' into parameterised-transclusions 2022-12-07 10:01:38 +00:00
jeremy@jermolene.com
31f4c1f6af Merge branch 'master' into parameterised-transclusions 2022-12-05 17:21:33 +00:00
jeremy@jermolene.com
b802b0cb09 Merge branch 'master' into parameterised-transclusions 2022-11-27 13:56:33 +00:00
jeremy@jermolene.com
b5e4b21707 Merge branch 'master' into parameterised-transclusions 2022-11-27 12:40:51 +00:00
jeremy@jermolene.com
52592a4dbc Add support for named end markers for procedures, functions and widgets 2022-11-23 16:30:20 +00:00
jeremy@jermolene.com
b78f8769a9 Docs: move tiddlers to correct directory 2022-11-23 16:27:39 +00:00
jeremy@jermolene.com
39d88bf156 Docs: Update \define pragma to cover named ends 2022-11-23 09:02:39 +00:00
jeremy@jermolene.com
778b8dec7f Merge branch 'master' into parameterised-transclusions 2022-11-23 09:01:42 +00:00
jeremy@jermolene.com
45958db074 Merge branch 'master' into parameterised-transclusions 2022-11-22 16:29:45 +00:00
jeremy@jermolene.com
846edbfaa4 Merge branch 'master' into parameterised-transclusions 2022-11-16 08:27:00 +00:00
jeremy@jermolene.com
4e3566745e Merge branch 'master' into parameterised-transclusions 2022-11-15 11:46:04 +00:00
jeremy@jermolene.com
70930e16b2 Merge branch 'master' into parameterised-transclusions 2022-11-15 10:39:29 +00:00
jeremy@jermolene.com
149d6c2d73 Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
2022-11-05 10:13:35 +00:00
jeremy@jermolene.com
478b585a8c Merge branch 'master' into parameterised-transclusions 2022-11-02 08:21:17 +00:00
jeremy@jermolene.com
9029588b2c Merge branch 'master' into parameterised-transclusions 2022-10-30 21:21:48 +00:00
jeremy@jermolene.com
38ac1e4bfd Merge branch 'master' into parameterised-transclusions 2022-10-30 21:21:21 +00:00
jeremy@jermolene.com
e7caa0c739 Docs typo
Thanks @twMat
2022-10-30 21:17:55 +00:00
jeremy@jermolene.com
9f42007899 Filter run prefixes should use widget.makeFakeWidgetWithVariables 2022-10-25 14:13:38 +01:00
jeremy@jermolene.com
b426724f35 Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable

Fixes #7001
2022-10-20 15:08:05 +01:00
jeremy@jermolene.com
ffc2594fef Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108

Fixes #6998
2022-10-19 15:35:43 +01:00
jeremy@jermolene.com
c171c441f5 Update release note 2022-10-19 09:42:41 +01:00
jeremy@jermolene.com
33de634606 Adjust modified times of docs tiddlers to make them easier to find 2022-10-19 09:15:38 +01:00
jeremy@jermolene.com
120b93c242 New test for undefined parameters 2022-10-19 08:37:27 +01:00
jeremy@jermolene.com
14e0a271d4 New New Release Banner 2022-10-19 08:37:04 +01:00
jeremy@jermolene.com
bc375c04b9 Temporary new release banner for v5.3.0 2022-10-19 08:14:29 +01:00
jeremy@jermolene.com
55c7eea52d Add release note etc for 5.3.0 2022-10-18 17:40:13 +01:00
jeremy@jermolene.com
45f1cf6cff Attempt to build this branch with CI 2022-10-18 17:27:47 +01:00
jeremy@jermolene.com
32710ef9e6 Merge branch 'master' into parameterised-transclusions 2022-10-18 17:23:57 +01:00
jeremy@jermolene.com
5194b24108 Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
2022-10-17 08:31:07 +01:00
jeremy@jermolene.com
3a4589dc79 Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
2022-10-12 17:15:39 +01:00
jeremy@jermolene.com
76bc3a7358 Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220

Thanks @btheado
2022-10-09 17:48:24 +01:00
jeremy@jermolene.com
85a203bcf2 Revert modified date of docs that have only had minor tweaks 2022-10-09 17:43:38 +01:00
jeremy@jermolene.com
c25129584c Docs updates 2022-10-09 17:34:22 +01:00
jeremy@jermolene.com
1c73460ecc Fix doc file locations 2022-10-09 13:56:36 +01:00
jeremy@jermolene.com
ef93fc29bf More docs updates 2022-10-09 13:54:33 +01:00
jeremy@jermolene.com
9d4aa47356 Clarify/simplify some tests 2022-10-09 13:54:08 +01:00
jeremy@jermolene.com
12a87840fd Merge branch 'master' into parameterised-transclusions 2022-10-07 16:57:54 +01:00
jeremy@jermolene.com
dcbff82460 Docs updates 2022-10-07 16:56:21 +01:00
jeremy@jermolene.com
6be996a858 Docs update 2022-10-06 21:37:14 +01:00
jeremy@jermolene.com
bf41e08c2f Revert change to setwidget docs 2022-10-06 10:30:40 +01:00
jeremy@jermolene.com
9641345b92 Docs updates 2022-10-03 14:40:27 +01:00
jeremy@jermolene.com
93100a1c8f Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c8
2022-10-03 14:39:45 +01:00
jeremy@jermolene.com
b6796863e8 Merge branch 'master' into parameterised-transclusions 2022-10-01 14:05:54 +01:00
jeremy@jermolene.com
0a309f4865 Merge branch 'master' into parameterised-transclusions 2022-10-01 10:18:34 +01:00
jeremy@jermolene.com
668168dfea Some widget.js cleanups 2022-09-27 08:58:10 +01:00
jeremy@jermolene.com
502a1ab70d Merge branch 'master' into parameterised-transclusions 2022-09-27 08:56:58 +01:00
jeremy@jermolene.com
109edd1b76 Docs update 2022-09-24 15:09:12 +01:00
jeremy@jermolene.com
d950835e2f Docs clarification 2022-09-24 15:05:54 +01:00
jeremy@jermolene.com
a482f01352 Merge branch 'master' into parameterised-transclusions 2022-09-24 14:08:44 +01:00
jeremy@jermolene.com
e3d13696c8 Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
2022-09-24 12:56:06 +01:00
jeremy@jermolene.com
1aba8a1f65 Slight optimisation to user defined widgets 2022-09-24 12:41:28 +01:00
jeremy@jermolene.com
fddaa9fdb1 Merge branch 'master' into parameterised-transclusions 2022-09-24 11:07:53 +01:00
jeremy@jermolene.com
0c88467111 Merge branch 'master' into parameterised-transclusions 2022-09-22 10:35:01 +01:00
jeremy@jermolene.com
c3589357bf Huge Documentation Update
Not quite finished, but definitely on the home stretch
2022-09-21 10:41:03 +01:00
jeremy@jermolene.com
0196844f17 Merge branch 'master' into parameterised-transclusions 2022-09-21 09:33:24 +01:00
jeremy@jermolene.com
6847906bea Docs typo 2022-09-16 16:33:04 +01:00
jeremy@jermolene.com
fe6186b1c1 Docs update 2022-09-16 16:07:58 +01:00
jeremy@jermolene.com
3090bb104e Fix visible transclude 2022-09-16 16:07:44 +01:00
jeremy@jermolene.com
8f9d5cabaa Missing tests for parameters widget 2022-09-16 16:07:32 +01:00
jeremy@jermolene.com
fa86631d2e Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
2022-09-13 16:29:09 +01:00
jeremy@jermolene.com
ef677c39c0 Docs tweaks 2022-09-11 10:57:35 +01:00
jeremy@jermolene.com
a3ba6f9370 Tests and docs for function operator 2022-09-11 10:49:02 +01:00
jeremy@jermolene.com
cc7b276787 Remove negation from function operator
This implementation was not useful.
2022-09-11 10:48:37 +01:00
jeremy@jermolene.com
68a1d61f33 Update function operator to return the input list if the function is missing 2022-09-11 10:48:05 +01:00
jeremy@jermolene.com
ecd467496d Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
2022-09-10 11:03:40 +01:00
jeremy@jermolene.com
580e33d30f Merge branch 'master' into parameterised-transclusions 2022-09-10 11:01:45 +01:00
jeremy@jermolene.com
977a133028 Tweak comments 2022-09-09 14:16:08 +01:00
jeremy@jermolene.com
7aba21c1be Docs updates 2022-09-09 14:15:56 +01:00
jeremy@jermolene.com
32a6b6bb90 Docs updates 2022-09-09 12:05:08 +01:00
jeremy@jermolene.com
b751f7e793 Parameters widget: protect against negative $depth 2022-09-09 12:04:50 +01:00
jeremy@jermolene.com
82b22523aa Rejig genesis widget to be easier to use 2022-09-09 10:37:39 +01:00
jeremy@jermolene.com
e3697e29bb When transcluding functions, pass an empty item list to the filter, and just return the first item 2022-09-09 10:22:06 +01:00
jeremy@jermolene.com
7b465888c8 Revert "WIP"
This reverts commit 8654dfc679.
2022-09-04 16:43:40 +01:00
jeremy@jermolene.com
8654dfc679 WIP 2022-09-04 16:41:25 +01:00
jeremy@jermolene.com
5c860ed39b Fix error in 25312b3e32 2022-09-04 16:26:47 +01:00
jeremy@jermolene.com
25312b3e32 Refactor filter recursion detection to avoid an unneeded wrapper function 2022-09-04 16:23:49 +01:00
jeremy@jermolene.com
11e0c66a4c Transcluded functions should operate on the entire store 2022-09-03 20:11:45 +01:00
jeremy@jermolene.com
a439cb58be Fix caching of parse variables/macros/procedures 2022-09-03 20:11:23 +01:00
jeremy@jermolene.com
966707ea1c More comments 2022-09-03 16:57:38 +01:00
jeremy@jermolene.com
1b08cd40bf Coding style tweak 2022-09-03 16:36:36 +01:00
jeremy@jermolene.com
00b6645c94 Disable overriding core widgets in safe mode 2022-09-03 12:58:10 +01:00
jeremy@jermolene.com
f6ee0cdf4c Add test to show that global widgets need not use the _parameters field 2022-09-03 12:57:45 +01:00
jeremy@jermolene.com
36058cba89 Merge branch 'master' into parameterised-transclusions 2022-09-03 10:55:29 +01:00
jeremy@jermolene.com
389330436d Revert changes to parse tree preview
This implementation requires #6666
2022-09-03 10:52:34 +01:00
jeremy@jermolene.com
15ea3ffa9a Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba152
2022-09-02 20:10:11 +01:00
jeremy@jermolene.com
bf4a187ee1 Fix inadvertent whitespace change 2022-09-02 20:05:24 +01:00
jeremy@jermolene.com
2f494ba152 Revert obsolete changes to boot.js 2022-09-02 20:03:33 +01:00
jeremy@jermolene.com
f0fa1af3a8 Merge branch 'master' into parameterised-transclusions 2022-09-02 18:17:32 +01:00
jeremy@jermolene.com
d852123f61 Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
2022-09-02 17:14:50 +01:00
jeremy@jermolene.com
54e1083dd6 Merge branch 'master' into parameterised-transclusions 2022-09-01 09:00:57 +01:00
jeremy@jermolene.com
2b8b133d4a Merge branch 'master' into parameterised-transclusions 2022-08-17 18:11:21 +01:00
jeremy@jermolene.com
9317804464 FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.

The test in this commit is quite interesting...
2022-08-04 09:28:56 +01:00
jeremy@jermolene.com
6207ec4812 Merge branch 'master' into parameterised-transclusions 2022-08-02 17:49:28 +01:00
jeremy@jermolene.com
0366d6d7ba Merge branch 'master' into parameterised-transclusions 2022-07-21 10:11:23 +01:00
jeremy@jermolene.com
b1cf9f241e Merge branch 'master' into parameterised-transclusions 2022-07-21 10:10:49 +01:00
jeremy@jermolene.com
17c9bf7546 Protect against excessively recursive functions 2022-07-21 09:17:57 +01:00
jeremy@jermolene.com
745707fea3 Parameters widget: Be defensive about negative depths 2022-07-19 10:27:36 +01:00
jeremy@jermolene.com
d4e0eb193b Slot widget: be more defensive about negative depth values 2022-07-19 10:26:20 +01:00
jeremy@jermolene.com
a2182255cc Simplify the fill widget
We can rely on the default processing in the base class
2022-07-19 10:02:09 +01:00
jeremy@jermolene.com
7d90083d14 Docs: Transclude widget 2022-07-18 19:44:53 +01:00
jeremy@jermolene.com
30f7b37cba Make use of type attribute consistent 2022-07-18 19:44:37 +01:00
jeremy@jermolene.com
189fe428b7 Merge branch 'master' into parameterised-transclusions 2022-07-04 21:15:42 +01:00
jeremy@jermolene.com
945c9e619a Merge branch 'master' into parameterised-transclusions 2022-06-22 08:51:46 +01:00
jeremy@jermolene.com
6e1138e504 Merge branch 'master' into parameterised-transclusions 2022-06-17 16:40:54 +01:00
jeremy@jermolene.com
cdf5d7bd4a Fix new selection tracker to return relative coordinates 2022-06-16 17:00:20 +01:00
jeremy@jermolene.com
05d2c029a4 Merge branch 'master' into parameterised-transclusions 2022-06-14 08:34:47 +01:00
jeremy@jermolene.com
4e6efed58f Revert "Fix #6721"
This reverts commit b216579255 which was committed to the wrong branch
2022-06-14 08:34:42 +01:00
Jeremy Ruston
b216579255 Fix #6721 2022-06-13 15:43:28 +00:00
jeremy@jermolene.com
3e33d309ed Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
2022-06-11 17:29:20 +01:00
jeremy@jermolene.com
688c3bc5f6 Docs: JSON operators and tweaks to genesis widget 2022-06-11 15:22:43 +01:00
jeremy@jermolene.com
227079f3da Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
2022-06-10 09:29:56 +01:00
jeremy@jermolene.com
79b20bdaa8 Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
2022-06-09 18:02:47 +01:00
jeremy@jermolene.com
defe3e42e6 Adjust naming of transclusion metaparameter 2022-06-05 16:10:41 +01:00
jeremy@jermolene.com
7680280d87 Fix typo 2022-06-05 15:59:03 +01:00
jeremy@jermolene.com
542df63ccf Simplify metaparameters implementation 2022-06-03 11:21:32 +01:00
jeremy@jermolene.com
b57a6da548 Mark docs as v5.3.0 2022-06-03 09:22:19 +01:00
jeremy@jermolene.com
c42df2233a Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
2022-06-03 08:53:51 +01:00
jeremy@jermolene.com
139047b4f8 Typo from f513b403fe 2022-06-01 10:06:27 +01:00
jeremy@jermolene.com
f513b403fe Remove support for $:/tags/Global
It is not needed now that we have true global variables
2022-06-01 08:24:20 +01:00
jeremy@jermolene.com
613f4af20f Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
2022-05-31 09:19:01 +01:00
jeremy@jermolene.com
006ae6e759 Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
2022-05-31 09:03:20 +01:00
jeremy@jermolene.com
150266c731 Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
2022-05-30 18:38:25 +01:00
jeremy@jermolene.com
3af2cc2691 Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
2022-05-30 18:05:54 +01:00
jeremy@jermolene.com
bd9d7c3d6a Clean up unknown filter 2022-05-30 18:04:37 +01:00
jeremy@jermolene.com
0c3f82855b Minor cleanups 2022-05-29 10:55:15 +01:00
jeremy@jermolene.com
8106ad390d Merge branch 'master' into parameterised-transclusions 2022-05-29 10:52:45 +01:00
jeremy@jermolene.com
9424e9d1f2 Cleaning up after f636349007 2022-05-28 17:32:09 +01:00
jeremy@jermolene.com
f803e00567 Test showing how to un-override a core widget 2022-05-28 16:58:20 +01:00
jeremy@jermolene.com
58f01c9a20 Fix tabs global so it doesn't crash when viewed directly 2022-05-28 13:50:08 +01:00
jeremy@jermolene.com
5d413d3e2d Change to ? for conditional definitions 2022-05-28 13:49:02 +01:00
jeremy@jermolene.com
0d78b3fa38 Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
2022-05-28 12:24:48 +01:00
jeremy@jermolene.com
f636349007 Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.

This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.

In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
2022-05-28 12:23:50 +01:00
jeremy@jermolene.com
a2fbebf509 Add utility function for parsing macro parameter definitions 2022-05-27 18:37:42 +01:00
jeremy@jermolene.com
7fc65d0d1a Remove accidental commit 6cc99fcbe3 2022-05-27 08:36:31 +01:00
jeremy@jermolene.com
6f9f92fa69 Add support for $:/tags/Global 2022-05-26 21:11:53 +01:00
jeremy@jermolene.com
dec45f0fc3 Fix importvariables to work with setvariables as well as set (they are aliases) 2022-05-26 21:11:32 +01:00
jeremy@jermolene.com
45b7b4bc6d Make is[variable] and variables[] operators resilient to fake widgets 2022-05-26 08:23:54 +01:00
jeremy@jermolene.com
e2d45e176a Merge branch 'master' into parameterised-transclusions 2022-05-25 18:08:17 +01:00
jeremy@jermolene.com
0e08150028 Remove unneeded test tiddler 2022-05-25 18:04:32 +01:00
jeremy@jermolene.com
d59eec74a8 Merge branch 'master' into parameterised-transclusions 2022-05-25 15:23:20 +01:00
jeremy@jermolene.com
6cc99fcbe3 WIP 2022-05-25 15:15:17 +01:00
jeremy@jermolene.com
cbce4ebb7b Allow custom functions to be invoked as attributes 2022-05-23 20:18:54 +01:00
jeremy@jermolene.com
9e8d05f699 Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
2022-05-23 16:40:21 +01:00
jeremy@jermolene.com
3e09eacd20 Missed off 22e7ec2381 2022-05-23 16:32:19 +01:00
jeremy@jermolene.com
22e7ec2381 Procedures and widgets inherit whitespace trim setting from their definition 2022-05-23 15:30:33 +01:00
jeremy@jermolene.com
170c4b1799 Merge branch 'master' into parameterised-transclusions 2022-05-23 11:28:12 +01:00
jeremy@jermolene.com
d11893752c Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.

As usual with these experiments, this one is going to require quite a lot more work to finish up:

* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
2022-05-23 10:24:51 +01:00
jeremy@jermolene.com
ec1ec8ccd8 Fix invocation of JS macros 2022-05-21 16:31:34 +01:00
jeremy@jermolene.com
e50101322f Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
2022-05-21 15:47:19 +01:00
jeremy@jermolene.com
bbd9e2f243 Rename <$value> widget to <$fill> 2022-05-13 09:18:25 +01:00
jeremy@jermolene.com
36cf50aa96 Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
2022-05-13 08:49:53 +01:00
jeremy@jermolene.com
904e30a0e2 Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
2022-05-12 16:26:33 +01:00
jeremy@jermolene.com
413dc86d05 Rename internal "unknown" filter operator so that users cannot invoke it 2022-05-11 14:52:25 +01:00
jeremy@jermolene.com
774459fa73 Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
2022-05-11 13:51:11 +01:00
jeremy@jermolene.com
a827290332 Fix stringifying/parsing string arrays containing newlines
A very old bug.

Fixes the ActionListOpsWidget problem @btheado
2022-05-10 10:22:35 +01:00
jeremy@jermolene.com
0b11b499c2 Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
2022-05-10 10:21:56 +01:00
jeremy@jermolene.com
eef7d180a5 Remove obsolete code
Left over after refactoring
2022-05-10 10:21:19 +01:00
jeremy@jermolene.com
8e4c13382b Fix visible transclusion example 2022-05-10 08:10:32 +01:00
jeremy@jermolene.com
e092113f9f Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
2022-05-09 18:00:09 +01:00
jeremy@jermolene.com
64448ae774 Make the macrocall widget delegate to the transclude widget 2022-05-08 20:48:33 +01:00
jeremy@jermolene.com
4f2f689ab9 Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
2022-05-08 16:05:32 +01:00
jeremy@jermolene.com
e9630328f1 Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax 2022-05-08 15:59:20 +01:00
jeremy@jermolene.com
9be05f6f38 Use consistent parse tree node property for params 2022-05-07 13:22:53 +01:00
jeremy@jermolene.com
2fe2d20ddf Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
2022-05-07 11:41:28 +01:00
jeremy@jermolene.com
c5b10d5c1d Add visible transclusions component and demo
Very useful to see transclusions explicitly

Makes a good demo of a super-complicated widget override.
2022-05-06 15:39:45 +01:00
jeremy@jermolene.com
e01dfa1507 Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
2022-05-06 15:01:17 +01:00
jeremy@jermolene.com
a9938a6c67 Improve recursion detection
While retaining backwards compatibility
2022-05-06 15:00:10 +01:00
jeremy@jermolene.com
7caaf82571 Fix variable retrieval bug with test harness 2022-05-06 10:32:20 +01:00
jeremy@jermolene.com
e5164113c4 Don't create variables with value undefined for missing parameters 2022-05-05 14:47:22 +01:00
jeremy@jermolene.com
7ead87bd29 Use enlist:raw to preserve duplicates 2022-05-05 11:34:30 +01:00
jeremy@jermolene.com
f56e316c86 Fix genesis widget example 2022-05-05 11:34:06 +01:00
jeremy@jermolene.com
ea51b8a3fe Merge branch 'master' into parameterised-transclusions 2022-05-05 08:30:48 +01:00
jeremy@jermolene.com
f307f00e32 Fixes to enable the transclude widget itself to be overridden
There are two big changes here:

Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.

We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
2022-05-05 08:20:14 +01:00
jeremy@jermolene.com
35430d09ed Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
2022-05-05 08:16:20 +01:00
jeremy@jermolene.com
e99137f4cc Cache parse trees when transcluding variables 2022-05-03 17:59:45 +01:00
jeremy@jermolene.com
719a5614cb Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
2022-05-03 16:53:00 +01:00
jeremy@jermolene.com
56c2242e4e Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
2022-05-03 12:55:10 +01:00
jeremy@jermolene.com
e1df50d981 Fix parameter handling 2022-05-03 12:54:29 +01:00
jeremy@jermolene.com
c4743ebbec Fix importing of function definitions 2022-05-03 09:39:05 +01:00
jeremy@jermolene.com
8f69c27632 Reuse attribute objects when executing custom widgets 2022-05-03 09:38:41 +01:00
jeremy@jermolene.com
0163c04b7d Fix addAttributeToParseTreeNode handling of ordered attributes 2022-05-03 09:37:49 +01:00
jeremy@jermolene.com
2ea0374b55 Allow the let widget to create variables starting with $ 2022-05-03 08:44:33 +01:00
jeremy@jermolene.com
3000d3339c Remove extraneous code 2022-05-03 08:11:00 +01:00
jeremy@jermolene.com
a888cd57cd Unquoted parameters should not eat a succeeding comma
Fixes #6672
2022-05-02 13:49:41 +01:00
jeremy@jermolene.com
a10106a4a6 Fix crash when transcluding an undefined variable
Thanks @pmario

See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
2022-05-02 11:15:18 +01:00
jeremy@jermolene.com
fbd0357cae Mustn't allow commas in parameter names 2022-05-02 11:04:32 +01:00
jeremy@jermolene.com
689a6c5b6a Rewrite some tests to use the shortcut syntaxes 2022-05-02 10:56:22 +01:00
jeremy@jermolene.com
ea7199600d Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
2022-05-02 10:56:09 +01:00
jeremy@jermolene.com
5f02cc49fd Add support for accessing function parameters as name/value pairs 2022-05-02 10:00:50 +01:00
jeremy@jermolene.com
bbc6661192 Remove erroneous "tag" property 2022-05-02 09:26:18 +01:00
jeremy@jermolene.com
8b867be8ef Add pragma rule for parameters declarations 2022-05-02 09:15:45 +01:00
jeremy@jermolene.com
99750d78b5 Allow brackets to be omitted for function definitions with no parameters 2022-04-30 13:06:28 +01:00
jeremy@jermolene.com
c9bd1b5274 Add wikitext shortcut for new-style function definitions 2022-04-30 12:44:26 +01:00
jeremy@jermolene.com
886c8620f5 Replace ubertransclude widget with transclude widget 2022-04-30 10:00:38 +01:00
jeremy@jermolene.com
aac2d6ccb0 Refactor ubertransclude functionality into transclude widget 2022-04-30 09:54:55 +01:00
jeremy@jermolene.com
c4991fff9e Refactor transclude widget before uberfying it 2022-04-30 09:30:25 +01:00
jeremy@jermolene.com
f78e1f6f7d Importvariables should skip parameters widgets 2022-04-29 22:36:07 +01:00
jeremy@jermolene.com
5bcf7b9edd Add support for shortcut syntax for positional transclusion parameters 2022-04-29 22:35:47 +01:00
jeremy@jermolene.com
89b7a3bd28 Ubertransclusion positional parameters should be based on name, not position 2022-04-29 16:28:03 +01:00
jeremy@jermolene.com
9713da5071 Add support for positional parameters 2022-04-26 21:55:43 +01:00
jeremy@jermolene.com
31c3abb7ab Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
2022-04-26 14:36:05 +01:00
jeremy@jermolene.com
a401732995 Use ts- prefix for system slot names 2022-04-26 14:27:28 +01:00
jeremy@jermolene.com
fae038dcbc Remove need to explicitly clear widget mapping variable when invoking overridden widget 2022-04-26 14:24:31 +01:00
jeremy@jermolene.com
35616a0b39 Don't worry about ordered attributes
The changes in 0bffae2108 mean that we don't need to explicitly maintain the ordered attributes
2022-04-26 14:05:56 +01:00
jeremy@jermolene.com
7299d4fd1d Merge branch 'master' into parameterised-transclusions 2022-04-26 14:03:03 +01:00
jeremy@jermolene.com
66d4b07f7e Add a test for custom action widgets 2022-04-26 13:05:44 +01:00
jeremy@jermolene.com
0e4e037bb7 Changed transclude widget in binary parser to ubertransclude 2022-04-26 13:05:31 +01:00
jeremy@jermolene.com
cd0617f033 Use the ubertransclude widget for the wikitext transclusion syntax 2022-04-26 12:45:05 +01:00
jeremy@jermolene.com
05b582a202 Fix and test missing target handling 2022-04-26 12:32:46 +01:00
jeremy@jermolene.com
54b48876f7 Fix test execution 2022-04-26 12:32:34 +01:00
jeremy@jermolene.com
21b10a225f Initial commit
Everything is draft.
2022-04-24 21:24:38 +01:00
142 changed files with 3869 additions and 653 deletions

View File

@@ -25,20 +25,10 @@ exports.cascade = function(operationSubFunction,options) {
if(!filterFnList[index]) {
filterFnList[index] = options.wiki.compileFilter(filter);
}
var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler","")
}));
if(output.length !== 0) {
result = output[0];
return false;

View File

@@ -19,23 +19,13 @@ exports.filter = function(operationSubFunction,options) {
var resultsToRemove = [],
index = 0;
results.each(function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler",""),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length
}));
if(filtered.length === 0) {
resultsToRemove.push(title);
}

View File

@@ -21,23 +21,13 @@ exports.map = function(operationSubFunction,options) {
flatten = (suffixes[0] && suffixes[0][0] === "flat") ? true : false;
results.clear();
$tw.utils.each(inputTitles,function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (inputTitles.length - 1 - index),
"length": "" + inputTitles.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler",""),
"index": "" + index,
"revIndex": "" + (inputTitles.length - 1 - index),
"length": "" + inputTitles.length
}));
if(filtered.length && flatten) {
$tw.utils.each(filtered,function(value) {
results.push(value);

View File

@@ -18,24 +18,14 @@ exports.reduce = function(operationSubFunction,options) {
var accumulator = "",
index = 0;
results.each(function(title) {
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length,
"accumulator": "" + accumulator
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler"),
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length,
"accumulator": "" + accumulator
}));
if(list.length > 0) {
accumulator = "" + list[0];
}

View File

@@ -25,20 +25,10 @@ exports.sort = function(operationSubFunction,options) {
indexes = new Array(inputTitles.length),
compareFn;
results.each(function(title) {
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
opts.variables = {
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
}));
sortKeys.push(key[0] || "");
});
results.clear();

View File

@@ -255,19 +255,21 @@ exports.compileFilter = function(filterString) {
var operands = [],
operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
operatorFunction = filterOperators.title;
} else if(!filterOperators[operator.operator]) {
operatorFunction = filterOperators.field;
// Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
operatorFunction = filterOperators["[unknown]"];
} else {
// Use the operator function
operatorFunction = filterOperators[operator.operator];
}
$tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""});
operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || "";
} else {
operand.value = operand.text;
}

View File

@@ -20,19 +20,10 @@ exports.filter = function(source,operator,options) {
results = [],
target = operator.prefix !== "!";
source(function(tiddler,title) {
var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return options.widget.getVariable("currentTiddler");
default:
return options.widget.getVariable(name,opts);
}
}
});
var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),options.widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": options.widget.getVariable("currentTiddler","")
}));
if((list.length > 0) === target) {
results.push(title);
}

View File

@@ -0,0 +1,32 @@
/*\
title: $:/core/modules/filters/function.js
type: application/javascript
module-type: filteroperator
Filter operator returning those input titles that are returned from a function
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source});
}
// Return the input list if the function wasn't found
var results = [];
source(function(tiddler,title) {
results.push(title);
});
return results;
};
})();

View File

@@ -26,27 +26,14 @@ exports.reduce = function(source,operator,options) {
accumulator = operator.operands[1] || "";
for(var index=0; index<results.length; index++) {
var title = results[index],
list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name,opts) {
opts = opts || {};
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return options.widget.getVariable("currentTiddler");
case "accumulator":
return "" + accumulator;
case "index":
return "" + index;
case "revIndex":
return "" + (results.length - 1 - index);
case "length":
return "" + results.length;
default:
return options.widget.getVariable(name,opts);
}
}
});
list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),options.widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": options.widget.getVariable("currentTiddler"),
"accumulator": "" + accumulator,
"index": "" + index,
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length
}));
if(list.length > 0) {
accumulator = "" + list[0];
}

View File

@@ -25,19 +25,10 @@ exports.sortsub = function(source,operator,options) {
inputTitles.push(title);
var r = filterFn.call(options.wiki,function(iterator) {
iterator(options.wiki.getTiddler(title),title);
},{
getVariable: function(name,opts) {
opts = opts || {};
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return options.widget.getVariable("currentTiddler");
default:
return options.widget.getVariable(name,opts);
}
}
});
},options.widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title,
"..currentTiddler": options.widget.getVariable("currentTiddler")
}));
sortKeys.push(r[0] || "");
});
// Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays

View File

@@ -0,0 +1,45 @@
/*\
title: $:/core/modules/filters/unknown.js
type: application/javascript
module-type: filteroperator
Filter operator for handling unknown filter operators.
Not intended to be used directly by end users, hence the square brackets around the name.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var fieldFilterOperatorFn = require("$:/core/modules/filters/field.js").field;
/*
Export our filter function
*/
exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator
if(operator.operator.charAt(0) === ".") {
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source});
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {
if(list.indexOf(title) === -1) {
results.push(title);
}
});
return results;
} else {
return list;
}
}
}
// Otherwise, use the "field" operator
return fieldFilterOperatorFn(source,operator,options);
};
})();

View File

@@ -28,6 +28,8 @@ var AudioParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
}
this.tree = [element];
this.source = text;
this.type = type;
};
exports["audio/ogg"] = AudioParser;

View File

@@ -23,7 +23,7 @@ var BinaryParser = function(type,text,options) {
children: [{
type: "transclude",
attributes: {
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
"$tiddler": {type: "string", value: BINARY_WARNING_MESSAGE}
}
}]
};
@@ -38,7 +38,7 @@ var BinaryParser = function(type,text,options) {
children: [{
type: "transclude",
attributes: {
tiddler: {type: "string", value: EXPORT_BUTTON_IMAGE}
"$tiddler": {type: "string", value: EXPORT_BUTTON_IMAGE}
}
}]
};
@@ -64,6 +64,8 @@ var BinaryParser = function(type,text,options) {
children: [warn, link]
}
this.tree = [element];
this.source = text;
this.type = type;
};
exports["application/octet-stream"] = BinaryParser;

View File

@@ -52,6 +52,8 @@ var CsvParser = function(type,text,options) {
tag = "td";
this.tree[0].children[0].children[0].children.push(row);
}
this.source = text;
this.type = type;
};
exports["text/csv"] = CsvParser;

View File

@@ -29,6 +29,8 @@ var HtmlParser = function(type,text,options) {
if($tw.wiki.getTiddlerText("$:/config/HtmlParser/DisableSandbox","no") !== "yes") {
this.tree[0].attributes.sandbox = {type: "string", value: $tw.wiki.getTiddlerText("$:/config/HtmlParser/SandboxTokens","")};
}
this.source = text;
this.type = type;
};
exports["text/html"] = HtmlParser;

View File

@@ -28,6 +28,8 @@ var ImageParser = function(type,text,options) {
}
}
this.tree = [element];
this.source = text;
this.type = type;
};
exports["image/svg+xml"] = ImageParser;

View File

@@ -123,6 +123,36 @@ exports.parseStringLiteral = function(source,pos) {
}
};
/*
Returns an array of {name:} with an optional "default" property. Options include:
requireParenthesis: require the parameter definition to be wrapped in parenthesis
*/
exports.parseParameterDefinition = function(paramString,options) {
options = options || {};
if(options.requireParenthesis) {
var parenMatch = /^\s*\((.*)\)\s*$/g.exec(paramString);
if(!parenMatch) {
return [];
}
paramString = parenMatch[1];
}
var params = [],
reParam = /\s*([^:),\s]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|([^,"'\s]+)))?/mg,
paramMatch = reParam.exec(paramString);
while(paramMatch) {
// Save the parameter details
var paramInfo = {name: paramMatch[1]},
defaultValue = paramMatch[2] || paramMatch[3] || paramMatch[4] || paramMatch[5];
if(defaultValue !== undefined) {
paramInfo["default"] = defaultValue;
}
params.push(paramInfo);
// Look for the next parameter
paramMatch = reParam.exec(paramString);
}
return params;
};
exports.parseMacroParameters = function(node,source,pos) {
// Process parameters
var parameter = $tw.utils.parseMacroParameter(source,pos);
@@ -175,7 +205,36 @@ exports.parseMacroParameter = function(source,pos) {
};
/*
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:}
Look for a macro invocation. Returns null if not found, or {type: "transclude", attributes:, start:, end:}
*/
exports.parseMacroInvocationAsTransclusion = function(source,pos) {
var node = $tw.utils.parseMacroInvocation(source,pos);
if(node) {
var positionalName = 0,
transclusion = {
type: "transclude",
start: node.start,
end: node.end
};
$tw.utils.addAttributeToParseTreeNode(transclusion,"$variable",node.name);
$tw.utils.each(node.params,function(param) {
var name = param.name;
if(name) {
if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(transclusion,{name: name,type: "string", value: param.value, start: param.start, end: param.end});
} else {
$tw.utils.addAttributeToParseTreeNode(transclusion,{name: (positionalName++) + "",type: "string", value: param.value, start: param.start, end: param.end});
}
});
return transclusion;
}
return node;
};
/*
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, params:, start:, end:}
*/
exports.parseMacroInvocation = function(source,pos) {
var node = {

View File

@@ -25,6 +25,8 @@ var ImageParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:application/pdf;base64," + text};
}
this.tree = [element];
this.source = text;
this.type = type;
};
exports["application/pdf"] = ImageParser;

View File

@@ -20,6 +20,8 @@ var TextParser = function(type,text,options) {
language: {type: "string", value: type}
}
}];
this.source = text;
this.type = type;
};
exports["text/plain"] = TextParser;

View File

@@ -28,6 +28,8 @@ var VideoParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
}
this.tree = [element];
this.source = text;
this.type = type;
};
exports["video/ogg"] = VideoParser;

View File

@@ -0,0 +1,97 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/fnprocdef.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for function, procedure and widget definitions
```
\function name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\procedure name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\widget $mywidget(param:defaultvalue,param2:defaultvalue)
definition text
\end
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "fnprocdef";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg;
};
/*
Parse the most recent match
*/
exports.parse = function() {
// Move past the macro name and parameters
this.parser.pos = this.matchRegExp.lastIndex;
// Parse the parameters
var params = [];
if(this.match[3]) {
params = $tw.utils.parseParameterDefinition(this.match[4]);
}
// Is this a multiline definition?
var reEnd;
if(this.match[5]) {
// If so, the end of the body is marked with \end
reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
reEnd = /($|\r?\n)/mg;
// Move past any whitespace
this.parser.pos = $tw.utils.skipWhiteSpace(this.parser.source,this.parser.pos);
}
// Find the end of the definition
reEnd.lastIndex = this.parser.pos;
var text,
endMatch = reEnd.exec(this.parser.source);
if(endMatch) {
text = this.parser.source.substring(this.parser.pos,endMatch.index);
this.parser.pos = endMatch.index + endMatch[0].length;
} else {
// We didn't find the end of the definition, so we'll make it blank
text = "";
}
// Save the macro definition
var parseTreeNodes = [{
type: "set",
attributes: {},
children: [],
params: params
}];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[2]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
if(this.match[1] === "function") {
parseTreeNodes[0].isFunctionDefinition = true;
} else if(this.match[1] === "procedure") {
parseTreeNodes[0].isProcedureDefinition = true;
} else if(this.match[1] === "widget") {
parseTreeNodes[0].isWidgetDefinition = true;
}
if(this.parser.configTrimWhiteSpace) {
parseTreeNodes[0].configTrimWhiteSpace = true;
}
return parseTreeNodes;
};
})();

View File

@@ -93,9 +93,6 @@ exports.parseTag = function(source,pos,options) {
return null;
}
node.tag = token.match[1];
if(node.tag.slice(1).indexOf("$") !== -1) {
return null;
}
if(node.tag.charAt(0) === "$") {
node.type = node.tag.substr(1);
}

View File

@@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
var nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
var nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(nextCall) {
var c = this.parser.source.charAt(nextCall.end);
// Ensure EOL after parsed macro

View File

@@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
this.nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
this.nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(this.nextCall) {
return nextStart;
}

View File

@@ -77,16 +77,16 @@ exports.parse = function() {
text = "";
}
// Save the macro definition
return [{
var parseTreeNodes = [{
type: "set",
attributes: {
name: {type: "string", value: this.match[1]},
value: {type: "string", value: text}
},
attributes: {},
children: [],
params: params,
isMacroDefinition: true
}];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
return parseTreeNodes;
};
})();

View File

@@ -0,0 +1,60 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/parameters.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for parameter definitions
```
\parameters(param:defaultvalue,param2:defaultvalue)
definition text
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "parameters";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\parameters\s*\(([^)]*)\)\s*\r?\n/mg;
};
/*
Parse the most recent match
*/
exports.parse = function() {
// Move past the macro name and parameters
this.parser.pos = this.matchRegExp.lastIndex;
// Parse the parameters
var params = $tw.utils.parseParameterDefinition(this.match[1]);
var attributes = Object.create(null),
orderedAttributes = [];
$tw.utils.each(params,function(param) {
var name = param.name;
// Parameter names starting with dollar must be escaped to double dollars for the parameters widget
if(name.charAt(0) === "$") {
name = "$" + name;
}
var attribute = {name: name, type: "string", value: param["default"] || ""};
attributes[name] = attribute;
orderedAttributes.push(attribute);
});
// Save the macro definition
return [{
type: "parameters",
attributes: attributes,
orderedAttributes: orderedAttributes
}];
};
})();

View File

@@ -23,7 +23,7 @@ exports.types = {block: true};
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}(?:\r?\n|$)/mg;
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
};
exports.parse = function() {
@@ -31,13 +31,22 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
textRef = $tw.utils.trim(this.match[1]),
params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget
var transcludeNode = {
type: "transclude",
attributes: {},
isBlock: true
};
$tw.utils.each(params,function(paramValue,index) {
var name = "" + index;
transcludeNode.attributes[name] = {
name: name,
type: "string",
value: paramValue
}
});
// Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) {
@@ -48,14 +57,14 @@ exports.parse = function() {
tiddlerNode = {
type: "tiddler",
attributes: {
tiddler: {type: "string", value: targetTitle}
tiddler: {name: "tiddler", type: "string", value: targetTitle}
},
isBlock: true,
children: [transcludeNode]
};
}
if(template) {
transcludeNode.attributes.tiddler = {type: "string", value: template};
transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) {
return [tiddlerNode];
} else {
@@ -63,12 +72,12 @@ exports.parse = function() {
}
} else {
if(textRef) {
transcludeNode.attributes.tiddler = {type: "string", value: targetTitle};
transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) {
transcludeNode.attributes.field = {type: "string", value: targetField};
transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
}
if(targetIndex) {
transcludeNode.attributes.index = {type: "string", value: targetIndex};
transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
}
return [tiddlerNode];
} else {

View File

@@ -23,7 +23,7 @@ exports.types = {inline: true};
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}/mg;
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
};
exports.parse = function() {
@@ -31,12 +31,21 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
textRef = $tw.utils.trim(this.match[1]),
params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget
var transcludeNode = {
type: "transclude",
attributes: {}
};
$tw.utils.each(params,function(paramValue,index) {
var name = "" + index;
transcludeNode.attributes[name] = {
name: name,
type: "string",
value: paramValue
}
});
// Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) {
@@ -47,13 +56,13 @@ exports.parse = function() {
tiddlerNode = {
type: "tiddler",
attributes: {
tiddler: {type: "string", value: targetTitle}
tiddler: {name: "tiddler", type: "string", value: targetTitle}
},
children: [transcludeNode]
};
}
if(template) {
transcludeNode.attributes.tiddler = {type: "string", value: template};
transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) {
return [tiddlerNode];
} else {
@@ -61,12 +70,12 @@ exports.parse = function() {
}
} else {
if(textRef) {
transcludeNode.attributes.tiddler = {type: "string", value: targetTitle};
transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) {
transcludeNode.attributes.field = {type: "string", value: targetField};
transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
}
if(targetIndex) {
transcludeNode.attributes.index = {type: "string", value: targetIndex};
transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
}
return [tiddlerNode];
} else {

View File

@@ -32,6 +32,7 @@ options: see below:
parseAsInline: true to parse text as inline instead of block
wiki: reference to wiki to use
_canonical_uri: optional URI of content if text is missing or empty
configTrimWhiteSpace: true to trim whitespace
*/
var WikiParser = function(type,text,options) {
this.wiki = options.wiki;
@@ -46,7 +47,7 @@ var WikiParser = function(type,text,options) {
this.source = text || "";
this.sourceLength = this.source.length;
// Flag for ignoring whitespace
this.configTrimWhiteSpace = false;
this.configTrimWhiteSpace = options.configTrimWhiteSpace !== undefined ? options.configTrimWhiteSpace : false;
// Parser mode
this.parseAsInline = options.parseAsInline;
// Set current parse position

View File

@@ -0,0 +1,30 @@
/*\
title: $:/core/modules/widgets/fill.js
type: application/javascript
module-type: widget
Sub-widget used by the transclude widget for specifying values for slots within transcluded content. It doesn't do anything by itself because the transclude widget only ever deals with the parse tree nodes, and doesn't instantiate the widget itself
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var FillWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
FillWidget.prototype = new Widget();
exports.fill = FillWidget;
})();

View File

@@ -52,38 +52,44 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true});
if(parser) {
var parseTreeNode = parser.tree[0];
while(parseTreeNode && parseTreeNode.type === "set") {
while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) {
var node = {
type: "set",
attributes: parseTreeNode.attributes,
params: parseTreeNode.params,
isMacroDefinition: parseTreeNode.isMacroDefinition
isMacroDefinition: parseTreeNode.isMacroDefinition,
isFunctionDefinition: parseTreeNode.isFunctionDefinition,
isProcedureDefinition: parseTreeNode.isProcedureDefinition,
isWidgetDefinition: parseTreeNode.isWidgetDefinition,
configTrimWhiteSpace: parseTreeNode.configTrimWhiteSpace
};
if (parseTreeNode.isMacroDefinition) {
// Macro definitions can be folded into
// current widget instead of adding
// another link to the chain.
var widget = widgetPointer.makeChildWidget(node);
widget.computeAttributes();
widget.execute();
// We SHALLOW copy over all variables
// in widget. We can't use
// $tw.utils.assign, because that copies
// up the prototype chain, which we
// don't want.
$tw.utils.each(Object.keys(widget.variables), function(key) {
widgetPointer.variables[key] = widget.variables[key];
});
} else {
widgetPointer.children = [widgetPointer.makeChildWidget(node)];
// No more regenerating children for
// this widget. If it needs to refresh,
// it'll do so along with the the whole
// importvariable tree.
if (widgetPointer != this) {
widgetPointer.makeChildWidgets = function(){};
if(parseTreeNode.type === "set" || parseTreeNode.type === "setvariable") {
if(parseTreeNode.isMacroDefinition || parseTreeNode.isProcedureDefinition || parseTreeNode.isWidgetDefinition || parseTreeNode.isFunctionDefinition) {
// Macro definitions can be folded into
// current widget instead of adding
// another link to the chain.
var widget = widgetPointer.makeChildWidget(node);
widget.computeAttributes();
widget.execute();
// We SHALLOW copy over all variables
// in widget. We can't use
// $tw.utils.assign, because that copies
// up the prototype chain, which we
// don't want.
$tw.utils.each(Object.keys(widget.variables), function(key) {
widgetPointer.variables[key] = widget.variables[key];
});
} else {
widgetPointer.children = [widgetPointer.makeChildWidget(node)];
// No more regenerating children for
// this widget. If it needs to refresh,
// it'll do so along with the the whole
// importvariable tree.
if (widgetPointer != this) {
widgetPointer.makeChildWidgets = function(){};
}
widgetPointer = widgetPointer.children[0];
}
widgetPointer = widgetPointer.children[0];
}
parseTreeNode = parseTreeNode.children && parseTreeNode.children[0];
}

View File

@@ -37,7 +37,7 @@ MacroCallWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget
*/
MacroCallWidget.prototype.execute = function() {
// Get the parse type if specified
this.macroName = this.parseTreeNode.name || this.getAttribute("$name"),
this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki");
this.renderOutput = this.getAttribute("$output","text/html");
// Merge together the parameters specified in the parse tree with the specified attributes
@@ -47,49 +47,26 @@ MacroCallWidget.prototype.execute = function() {
params.push({name: name, value: attribute});
}
});
// Get the macro value
var macroName = this.parseTreeNode.name || this.getAttribute("$name"),
variableInfo = this.getVariableInfo(macroName,{params: params}),
text = variableInfo.text,
parseTreeNodes;
// Are we rendering to HTML?
if(this.renderOutput === "text/html") {
// If so we'll return the parsed macro
// Check if we've already cached parsing this macro
var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser",
parser;
if(variableInfo.srcVariable && variableInfo.srcVariable[mode]) {
parser = variableInfo.srcVariable[mode];
} else {
parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
if(variableInfo.isCacheable && variableInfo.srcVariable) {
variableInfo.srcVariable[mode] = parser;
}
}
var parseTreeNodes = parser ? parser.tree : [];
// Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
var attributes = {};
$tw.utils.each(variableInfo.params,function(param) {
var name = "__" + param.name + "__";
attributes[name] = {
name: name,
type: "string",
value: param.value
};
});
// Make a transclude widget
var positionalName = 0,
parseTreeNodes = [{
type: "vars",
attributes: attributes,
children: parseTreeNodes
type: "transclude",
isBlock: this.parseTreeNode.isBlock
}];
} else if(this.renderOutput === "text/raw") {
parseTreeNodes = [{type: "text", text: text}];
} else {
// Otherwise, we'll render the text
var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this});
parseTreeNodes = [{type: "text", text: plainText}];
}
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$variable",this.macroName);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$type",this.parseType);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$output",this.renderOutput);
$tw.utils.each(params,function(param) {
var name = param.name;
if(name) {
if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],name,param.value);
} else {
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],(positionalName++) + "",param.value);
}
});
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};

View File

@@ -0,0 +1,96 @@
/*\
title: $:/core/modules/widgets/parameters.js
type: application/javascript
module-type: widget
Widget for definition of transclusion parameters
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude;
var ParametersWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
ParametersWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
ParametersWidget.prototype.render = function(parent,nextSibling) {
// Call the constructor
Widget.call(this);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
ParametersWidget.prototype.execute = function() {
var self = this;
this.parametersDepth = Math.max(parseInt(this.getAttribute("$depth","1"),10) || 1,1);
// Find the parent transclusions
var pointer = this.parentWidget,
depth = this.parametersDepth;
while(pointer) {
if(pointer instanceof TranscludeWidget) {
depth--;
if(depth <= 0) {
break;
}
}
pointer = pointer.parentWidget;
}
// Process each parameter
if(pointer instanceof TranscludeWidget) {
// Get the value for each defined parameter
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(self.parseTreeNode),function(attr,index) {
var name = attr.name;
// If the attribute name starts with $$ then reduce to a single dollar
if(name.substr(0,2) === "$$") {
name = name.substr(1);
}
var value = pointer.getTransclusionParameter(name,index,self.getAttribute(attr.name,""));
self.setVariable(name,value);
});
// Assign any metaparameters
$tw.utils.each(pointer.getTransclusionMetaParameters(),function(getValue,name) {
var variableName = self.getAttribute("$" + name);
if(variableName) {
self.setVariable(variableName,getValue(name));
}
});
}
// Construct the child widgets
this.makeChildWidgets();
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
ParametersWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(Object.keys(changedAttributes).length) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports.parameters = ParametersWidget;
})();

View File

@@ -48,7 +48,17 @@ SetWidget.prototype.execute = function() {
this.setValue = this.getAttribute("value");
this.setEmptyValue = this.getAttribute("emptyValue");
// Set context variable
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,!!this.parseTreeNode.isMacroDefinition);
if(this.parseTreeNode.isMacroDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,true);
} else if(this.parseTreeNode.isFunctionDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isFunctionDefinition: true});
} else if(this.parseTreeNode.isProcedureDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isProcedureDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace});
} else if(this.parseTreeNode.isWidgetDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isWidgetDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace});
} else {
this.setVariable(this.setName,this.getValue());
}
// Construct the child widgets
this.makeChildWidgets();
};

View File

@@ -0,0 +1,82 @@
/*\
title: $:/core/modules/widgets/slot.js
type: application/javascript
module-type: widget
Widget for definition of slots within transcluded content. The values provided by the translusion are passed to the slot.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude;
var SlotWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
SlotWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
SlotWidget.prototype.render = function(parent,nextSibling) {
// Call the constructor
Widget.call(this);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
SlotWidget.prototype.execute = function() {
var self = this;
this.slotName = this.getAttribute("$name");
this.slotDepth = parseInt(this.getAttribute("$depth","1"),10) || 1;
// Find the parent transclusions
var pointer = this.parentWidget,
depth = this.slotDepth;
while(pointer) {
if(pointer instanceof TranscludeWidget) {
depth--;
if(depth <= 0) {
break;
}
}
pointer = pointer.parentWidget;
}
var parseTreeNodes = [{type: "text", attributes: {text: {type: "string", value: "Missing slot reference!"}}}];
if(pointer instanceof TranscludeWidget) {
// Get the parse tree nodes comprising the slot contents
parseTreeNodes = pointer.getTransclusionSlotFill(this.slotName,this.parseTreeNode.children);
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
SlotWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes["$name"] || changedAttributes["$depth"]) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports.slot = SlotWidget;
})();

View File

@@ -37,46 +37,347 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget
*/
TranscludeWidget.prototype.execute = function() {
// Get our parameters
this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.transcludeSubTiddler = this.getAttribute("subtiddler");
this.transcludeField = this.getAttribute("field");
this.transcludeIndex = this.getAttribute("index");
this.transcludeMode = this.getAttribute("mode");
this.recursionMarker = this.getAttribute("recursionMarker","yes");
// Parse the text reference
// Get our attributes, string parameters, and slot values into properties of the widget object
this.collectAttributes();
this.collectStringParameters();
this.collectSlotFillParameters();
// Get the parse tree nodes that we are transcluding
var target = this.getTransclusionTarget(),
parseTreeNodes = target.parseTreeNodes;
this.sourceText = target.text;
this.sourceType = target.type;
this.parseAsInline = target.parseAsInline;
// Process the transclusion according to the output type
switch(this.transcludeOutput || "text/html") {
case "text/html":
// No further processing required
break;
case "text/raw":
// Just return the raw text
parseTreeNodes = [{type: "text", text: this.sourceText}];
break;
default:
// text/plain
var plainText = this.wiki.renderText("text/plain",this.sourceType,this.sourceText,{parentWidget: this});
parseTreeNodes = [{type: "text", text: plainText}];
break;
}
// Set the legacy transclusion context variables only if we're not transcluding a variable
if(!this.transcludeVariable) {
var recursionMarker = this.makeRecursionMarker();
this.setVariable("transclusion",recursionMarker);
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};
/*
Collect the attributes we need, in the process determining whether we're being used in legacy mode
*/
TranscludeWidget.prototype.collectAttributes = function() {
var self = this;
// Detect legacy mode
this.legacyMode = true;
$tw.utils.each(this.attributes,function(value,name) {
if(name.charAt(0) === "$") {
self.legacyMode = false;
}
});
// Get the attributes for the appropriate mode
if(this.legacyMode) {
this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.transcludeSubTiddler = this.getAttribute("subtiddler");
this.transcludeField = this.getAttribute("field");
this.transcludeIndex = this.getAttribute("index");
this.transcludeMode = this.getAttribute("mode");
this.recursionMarker = this.getAttribute("recursionMarker","yes");
} else {
this.transcludeVariable = this.getAttribute("$variable");
this.transcludeType = this.getAttribute("$type");
this.transcludeOutput = this.getAttribute("$output","text/html");
this.transcludeTitle = this.getAttribute("$tiddler",this.getVariable("currentTiddler"));
this.transcludeSubTiddler = this.getAttribute("$subtiddler");
this.transcludeField = this.getAttribute("$field");
this.transcludeIndex = this.getAttribute("$index");
this.transcludeMode = this.getAttribute("$mode");
this.recursionMarker = this.getAttribute("$recursionMarker","yes");
}
};
/*
Collect string parameters
*/
TranscludeWidget.prototype.collectStringParameters = function() {
var self = this;
this.stringParametersByName = Object.create(null);
if(!this.legacyMode) {
$tw.utils.each(this.attributes,function(value,name) {
if(name.charAt(0) === "$") {
if(name.charAt(1) === "$") {
// Attributes starting $$ represent parameters starting with a single $
name = name.slice(1);
} else {
// Attributes starting with a single $ are reserved for the widget
return;
}
}
self.stringParametersByName[name] = value;
});
}
};
/*
Collect slot value parameters
*/
TranscludeWidget.prototype.collectSlotFillParameters = function() {
var self = this;
this.slotFillParseTrees = Object.create(null);
if(this.legacyMode) {
this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children;
} else {
this.slotFillParseTrees["ts-raw"] = this.parseTreeNode.children;
var noFillWidgetsFound = true,
searchParseTreeNodes = function(nodes) {
$tw.utils.each(nodes,function(node) {
if(node.type === "fill") {
if(node.attributes["$name"] && node.attributes["$name"].type === "string") {
var slotValueName = node.attributes["$name"].value;
self.slotFillParseTrees[slotValueName] = node.children || [];
}
noFillWidgetsFound = false;
} else {
searchParseTreeNodes(node.children);
}
});
};
searchParseTreeNodes(this.parseTreeNode.children);
if(noFillWidgetsFound) {
this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children;
}
}
};
/*
Get transcluded parse tree nodes as an object {parser:,text:,type:}
*/
TranscludeWidget.prototype.getTransclusionTarget = function() {
var self = this;
// Determine whether we're being used in inline or block mode
var parseAsInline = !this.parseTreeNode.isBlock;
if(this.transcludeMode === "inline") {
parseAsInline = true;
} else if(this.transcludeMode === "block") {
parseAsInline = false;
}
var parser = this.wiki.parseTextReference(
var parser;
// Get the parse tree
if(this.transcludeVariable) {
// Transcluding a variable
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
srcVariable = variableInfo && variableInfo.srcVariable;
if(srcVariable) {
if(srcVariable.isFunctionDefinition) {
// Function to return parameters by name or position
var fnGetParam = function(name,index) {
// Parameter names starting with dollar must be escaped to double dollars
if(name.charAt(0) === "$") {
name = "$" + name;
}
// Look for the parameter by name
if(self.hasAttribute(name)) {
return self.getAttribute(name);
// Look for the parameter by index
} else if(self.hasAttribute(index + "")) {
return self.getAttribute(index + "");
} else {
return undefined;
}
},
result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || "";
parser = {
tree: [{
type: "text",
text: result
}],
source: result,
type: "text/vnd.tiddlywiki"
};
if(parseAsInline) {
parser.tree[0] = {
type: "text",
text: result
};
} else {
parser.tree[0] = {
type: "element",
tag: "p",
children: [{
type: "text",
text: result
}]
}
}
} else {
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
parser = srcVariable[cacheKey];
} else {
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
if(variableInfo.isCacheable) {
srcVariable[cacheKey] = parser;
}
}
}
if(parser) {
// Add parameters widget for procedures and custom widgets
if(srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition) {
parser = {
tree: [
{
type: "parameters",
children: parser.tree
}
],
source: parser.source,
type: parser.type
}
$tw.utils.each(srcVariable.params,function(param) {
var name = param.name;
// Parameter names starting with dollar must be escaped to double dollars
if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
});
} else {
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
parser = {
tree: [
{
type: "vars",
children: parser.tree
}
],
source: parser.source,
type: parser.type
}
$tw.utils.each(variableInfo.params,function(param) {
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],"__" + param.name + "__",param.value)
});
}
}
}
} else {
// Transcluding a text reference
parser = this.wiki.parseTextReference(
this.transcludeTitle,
this.transcludeField,
this.transcludeIndex,
{
parseAsInline: parseAsInline,
subTiddler: this.transcludeSubTiddler
}),
parseTreeNodes = parser ? parser.tree : this.parseTreeNode.children;
this.sourceText = parser ? parser.source : null;
this.parserType = parser? parser.type : null;
// Set context variables for recursion detection
var recursionMarker = this.makeRecursionMarker();
if(this.recursionMarker === "yes") {
this.setVariable("transclusion",recursionMarker);
subTiddler: this.transcludeSubTiddler,
defaultType: this.transcludeType
});
}
// Check for recursion
// Return the parse tree
if(parser) {
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) {
parseTreeNodes = [{type: "error", attributes: {
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
}}];
return {
parser: parser,
parseTreeNodes: parser.tree,
parseAsInline: parseAsInline,
text: parser.source,
type: parser.type
};
} else {
// If there's no parse tree then return the missing slot value
return {
parser: null,
parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []),
parseAsInline: parseAsInline,
text: null,
type: null
};
}
};
/*
Fetch all the string parameters as an ordered array of {name:, value:} where the name is optional
*/
TranscludeWidget.prototype.getOrderedTransclusionParameters = function() {
var result = [];
// Collect the parameters
for(var name in this.stringParametersByName) {
var value = this.stringParametersByName[name];
result.push({name: name, value: value});
}
// Sort numerical parameter names first
result.sort(function(a,b) {
var aIsNumeric = !isNaN(a.name),
bIsNumeric = !isNaN(b.name);
if(aIsNumeric && bIsNumeric) {
return a.name - b.name;
} else if(aIsNumeric) {
return -1;
} else if(bIsNumeric) {
return 1;
} else {
return a.name === b.name ? 0 : (a.name < b.name ? -1 : 1);
}
});
// Remove names from numerical parameters
$tw.utils.each(result,function(param,index) {
if(!isNaN(param.name)) {
delete param.name;
}
});
return result;
};
/*
Fetch the value of a parameter
*/
TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaultValue) {
if(name in this.stringParametersByName) {
return this.stringParametersByName[name];
} else {
var name = "" + index;
if(name in this.stringParametersByName) {
return this.stringParametersByName[name];
}
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
return defaultValue;
};
/*
Get one of the special parameters to be provided by the parameters widget
*/
TranscludeWidget.prototype.getTransclusionMetaParameters = function() {
var self = this;
return {
"parseMode": function() {
return self.parseAsInline ? "inline" : "block";
},
"parseTreeNodes": function() {
return JSON.stringify(self.parseTreeNode.children || []);
},
"slotFillParseTreeNodes": function() {
return JSON.stringify(self.slotFillParseTrees);
},
"params": function() {
return JSON.stringify(self.stringParametersByName);
}
};
};
/*
Fetch the value of a slot
*/
TranscludeWidget.prototype.getTransclusionSlotFill = function(name,defaultParseTreeNodes) {
if(name && this.slotFillParseTrees[name] && this.slotFillParseTrees[name].length > 0) {
return this.slotFillParseTrees[name];
} else {
return defaultParseTreeNodes || [];
}
};
/*
@@ -99,6 +400,7 @@ TranscludeWidget.prototype.makeRecursionMarker = function() {
};
TranscludeWidget.prototype.parserNeedsRefresh = function() {
// Doesn't need to consider transcluded variables because a parent variable can't change once a widget has been created
var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler});
return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType)
};
@@ -108,7 +410,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
TranscludeWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(($tw.utils.count(changedAttributes) > 0) || (changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
if(($tw.utils.count(changedAttributes) > 0) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
this.refreshSelf();
return true;
} else {

View File

@@ -41,10 +41,7 @@ Widget.prototype.initialise = function(parseTreeNode,options) {
this.parseTreeNode = parseTreeNode;
this.wiki = options.wiki;
this.parentWidget = options.parentWidget;
this.variables = Object.create(null);
if(this.parentWidget) {
Object.setPrototypeOf(this.variables,this.parentWidget.variables);
}
this.variables = Object.create(this.parentWidget ? this.parentWidget.variables : null);
this.document = options.document;
this.attributes = {};
this.children = [];
@@ -92,9 +89,22 @@ name: name of the variable
value: value of the variable
params: array of {name:, default:} for each parameter
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
options includes:
isProcedureDefinition: true if the variable is set via a \procedure pragma (and hence should not have variable substitution performed)
isFunctionDefinition: true if the variable is set via a \function pragma (and hence should not have variable substitution performed)
isWidgetDefinition: true if the variable is set via a \widget pragma (and hence should not have variable substitution performed)
*/
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition) {
this.variables[name] = {value: value, params: params, isMacroDefinition: !!isMacroDefinition};
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
options = options || {};
this.variables[name] = {
value: value,
params: params,
isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition,
isProcedureDefinition: !!options.isProcedureDefinition,
isWidgetDefinition: !!options.isWidgetDefinition,
configTrimWhiteSpace: !!options.configTrimWhiteSpace
};
};
/*
@@ -104,6 +114,7 @@ options: see below
Options include
params: array of {name:, value:} for each parameter
defaultValue: default value if the variable is not defined
allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent
Returns an object with the following fields:
@@ -112,21 +123,27 @@ text: text of variable, with parameters properly substituted
*/
Widget.prototype.getVariableInfo = function(name,options) {
options = options || {};
var actualParams = options.params || [],
parentWidget = this.parentWidget;
var self = this,
actualParams = options.params || [],
variable;
if(options.allowSelfAssigned) {
variable = this.variables[name];
} else {
variable = this.parentWidget && this.parentWidget.variables[name];
}
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
if(parentWidget && name in parentWidget.variables) {
var variable = parentWidget.variables[name],
originalValue = variable.value,
if(variable) {
var originalValue = variable.value,
value = originalValue,
params = this.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
// Only substitute variable references if this variable was defined with the \define pragma
params = [];
// Only substitute parameter and variable references if this variable was defined with the \define pragma
if(variable.isMacroDefinition) {
value = this.substituteVariableReferences(value,options);
params = self.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
value = self.substituteVariableReferences(value,options);
}
return {
text: value,
@@ -136,8 +153,13 @@ Widget.prototype.getVariableInfo = function(name,options) {
};
}
// If the variable doesn't exist in the parent widget then look for a macro module
var text = this.evaluateMacroModule(name,actualParams);
if(text === undefined) {
text = options.defaultValue;
}
return {
text: this.evaluateMacroModule(name,actualParams,options.defaultValue)
text: text,
srcVariable: {}
};
};
@@ -148,6 +170,11 @@ Widget.prototype.getVariable = function(name,options) {
return this.getVariableInfo(name,options).text;
};
/*
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
formalParams - Array of {name:,default:} (default value is optional)
actualParams - Array of string values or {name:,value:} (name is optional)
*/
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
formalParams = formalParams || [];
actualParams = actualParams || [];
@@ -160,7 +187,7 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
paramInfo = formalParams[p];
paramValue = undefined;
for(var m=0; m<actualParams.length; m++) {
if(actualParams[m].name === paramInfo.name) {
if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
paramValue = actualParams[m].value;
}
}
@@ -169,7 +196,8 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
nextAnonParameter++;
}
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
paramValue = actualParams[nextAnonParameter++].value;
var param = actualParams[nextAnonParameter++];
paramValue = typeof param === "string" ? param : param.value;
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
@@ -263,12 +291,103 @@ Widget.prototype.getStateQualifier = function(name) {
};
/*
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed
Make a fake widget with specified variables, suitable for variable lookup in filters
*/
Widget.prototype.computeAttributes = function() {
Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
var self = this;
return {
getVariable: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return variables[name];
} else {
opts = opts || {};
opts.variables = variables;
return self.getVariable(name,opts);
};
},
getVariableInfo: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return {
text: variables[name]
};
} else {
opts = opts || {};
opts.variables = variables;
return self.getVariableInfo(name,opts);
};
},
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables,
evaluateVariable: self.evaluateVariable,
resolveVariableParameters: self.resolveVariableParameters,
wiki: self.wiki
};
};
/*
Evaluate a variable and associated actual parameters and result the resulting array.
The way that the variable is evaluated depends upon its type:
* Functions are evaluated as parameterised filter strings
* Macros are returned as plain text with substitution of parameters
* Procedures and widgets are returned as plain text
Options are:
params - the actual parameters may be one of:
* an array of values that may be an anonymous string value, or a {name:, value:} pair
* a hashmap of {name: value} pairs
* a function invoked with parameters (name,index) that returns a parameter value by name or position
source - iterator for source tiddlers
*/
Widget.prototype.evaluateVariable = function(name,options) {
options = options || {};
var params = options.params || [];
// Get the details of the variable (includes processing text substitution for macros
var variableInfo = this.getVariableInfo(name,{params: params,defaultValue: ""});
// Process function parameters
var variables = Object.create(null);
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
// Apply default parameter values
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
if(param["default"]) {
variables[param.name] = param["default"];
}
});
if($tw.utils.isArray(params)) {
// Parameters are an array of values or {name:, value:} pairs
$tw.utils.each(this.resolveVariableParameters(variableInfo.srcVariable.params,params),function(param) {
variables[param.name] = param.value;
});
} else if(typeof params === "function") {
// Parameters are passed via a function
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
variables[param.name] = params(param.name,index) || param["default"] || "";
});
} else {
// Parameters are a hashmap
$tw.utils.each(params,function(value,name) {
variables[name] = value;
});
}
return this.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables),options.source);
} else {
return [variableInfo.text];
}
};
/*
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed.
Options include:
filterFn: only include attributes where filterFn(name) returns true
*/
Widget.prototype.computeAttributes = function(options) {
options = options || {};
var changedAttributes = {},
self = this;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(options.filterFn) {
if(!options.filterFn(name)) {
return;
}
}
var value = self.computeAttribute(attribute);
if(self.attributes[name] !== value) {
self.attributes[name] = value;
@@ -279,13 +398,21 @@ Widget.prototype.computeAttributes = function() {
};
Widget.prototype.computeAttribute = function(attribute) {
var value;
var self = this,
value;
if(attribute.type === "filtered") {
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
} else if(attribute.type === "indirect") {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
} else if(attribute.type === "macro") {
value = this.getVariable(attribute.value.name,{params: attribute.value.params});
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
// It is a function definition. Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter
var paramArray = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params);
value = this.evaluateVariable(attribute.value.name,{params: paramArray})[0] || "";
} else {
value = variableInfo.text;
}
} else { // String attribute
value = attribute.value;
}
@@ -413,7 +540,34 @@ options include:
variables: optional hashmap of variables to wrap around the widget
*/
Widget.prototype.makeChildWidget = function(parseTreeNode,options) {
var self = this;
options = options || {};
// Check whether this node type is defined by a custom widget definition
var variableDefinitionName = "$" + parseTreeNode.type;
if(this.variables[variableDefinitionName]) {
var isOverrideable = function() {
// Widget is overrideable if it has a double dollar user defined name, or if it is an existing JS widget and we're not in safe mode
return parseTreeNode.type.charAt(0) === "$" || (!!self.widgetClasses[parseTreeNode.type] && !$tw.safeMode);
};
if(!parseTreeNode.isNotRemappable && isOverrideable()) {
var variableInfo = this.getVariableInfo(variableDefinitionName,{allowSelfAssigned: true});
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.value && variableInfo.srcVariable.isWidgetDefinition) {
var newParseTreeNode = {
type: "transclude",
children: parseTreeNode.children,
isBlock: parseTreeNode.isBlock
};
$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,"$variable",variableDefinitionName);
$tw.utils.each(parseTreeNode.attributes,function(attr,name) {
// If the attribute starts with a dollar then add an extra dollar so that it doesn't clash with the $xxx attributes of transclude
name = name.charAt(0) === "$" ? "$" + name : name;
$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,$tw.utils.extend({},attr,{name: name}));
});
parseTreeNode = newParseTreeNode;
}
}
}
// Get the widget class for this node type
var WidgetClass = this.widgetClasses[parseTreeNode.type];
if(!WidgetClass) {
WidgetClass = this.widgetClasses.text;

View File

@@ -988,7 +988,8 @@ exports.parseText = function(type,text,options) {
return new Parser(type,text,{
parseAsInline: options.parseAsInline,
wiki: this,
_canonical_uri: options._canonical_uri
_canonical_uri: options._canonical_uri,
configTrimWhiteSpace: options.configTrimWhiteSpace
});
};
@@ -1028,10 +1029,11 @@ exports.parseTextReference = function(title,field,index,options) {
};
exports.getTextReferenceParserInfo = function(title,field,index,options) {
var tiddler,
var defaultType = options.defaultType || "text/vnd.tiddlywiki",
tiddler,
parserInfo = {
sourceText : null,
parserType : "text/vnd.tiddlywiki"
parserType : defaultType
};
if(options.subTiddler) {
tiddler = this.getSubTiddler(title,options.subTiddler);
@@ -1077,19 +1079,20 @@ exports.makeWidget = function(parser,options) {
children: []
},
currWidgetNode = widgetNode;
// Create set variable widgets for each variable
$tw.utils.each(options.variables,function(value,name) {
var setVariableWidget = {
type: "set",
// Create let variable widget for variables
if($tw.utils.count(options.variables) > 0) {
var letVariableWidget = {
type: "let",
attributes: {
name: {type: "string", value: name},
value: {type: "string", value: value}
},
children: []
};
currWidgetNode.children = [setVariableWidget];
currWidgetNode = setVariableWidget;
});
$tw.utils.each(options.variables,function(value,name) {
$tw.utils.addAttributeToParseTreeNode(letVariableWidget,name,"" + value);
});
currWidgetNode.children = [letVariableWidget];
currWidgetNode = letVariableWidget;
}
// Add in the supplied parse tree nodes
currWidgetNode.children = parser ? parser.tree : [];
// Create the widget

View File

@@ -0,0 +1,48 @@
title: $:/core/ui/VisibleTransclude
<!--
Import this component to make all the child transclusions visible.
Block transclusions are shown in red, and inline transclusions are shown in green.
-->
\widget $transclude()
<!-- Use a parameters widget so that we can access the `$params` data -->
<$parameters tiddler="" $$tiddler="" mode="" $$mode="" $parseMode="@parseMode" $params="@params">
<!-- Replicate the logic of the transclude widget to determine the output mode, and hence the tag and colour to use for output -->
<$let
mode={{{ [[$mode]is[variable]then<$mode>!is[blank]] :else[[mode]is[variable]then<mode>!is[blank]] :else[<@parseMode>] }}}
outputTag={{{ [<mode>match[inline]then[span]else[div]] }}}
outputColour={{{ [<mode>match[inline]then[green]else[red]] }}}
>
<!-- Use divs or spans according to the mode -->
<$genesis $type=<<outputTag>> style="color:white;padding:4px;" style.background=<<outputColour>>>
<$genesis $type=<<outputTag>> style="display: inline-block;">
<div style="background:white;color:black;font-size: 12px;line-height:1.2;text-align:left;font-weight:normal;padding:4px;margin:4px;">
<!-- Render the parameters to the transclusion -->
<$list filter="[<@params>jsonindexes[]]" emptyMessage="(none)">
<div>
<$text text=<<currentTiddler>>/><$text text=": "/><$text text={{{ [<@params>jsonget<currentTiddler>] }}}/>
</div>
</$list>
</div>
</$genesis>
<$genesis $type=<<outputTag>> style="background:white;color:black;padding:4px;">
<!-- Look for a parameter starting with $ to determine if we are in legacy mode -->
<$list filter="[<@params>jsonindexes[]] :filter[<currentTiddler>prefix[$]] +[limit[1]]" variable="ignore" emptyMessage="""
<!-- Legacy mode: we render the transclusion without a dollar sign for recursionMarker and mode -->
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" recursionMarker="no" mode=<<mode>>>
<!-- Reach back up to the grandparent transclusion to get the correct slot value -->
<$slot $name="ts-raw" $depth="2"/>
</$genesis>
""">
<!-- Non-legacy mode: we use dollar signs for the recursionMarker and mode -->
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" $$recursionMarker="no" $$mode=<<mode>>>
<!-- Reach back up to the grandparent transclusion to get the correct slot fill value -->
<$slot $name="ts-raw" $depth="2"/>
</$genesis>
</$list>
</$genesis>
</$genesis>
</$let>
</$parameters>
\end

View File

@@ -60,4 +60,4 @@ code-body: yes
</div>
</$let>
</$qualify>
\end
\end

View File

@@ -1,60 +0,0 @@
caption: 5.2.8
created: 20230326093239710
modified: 20230326093239710
tags: ReleaseNotes
title: Release 5.2.8
type: text/vnd.tiddlywiki
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.7...master]]//
! Major Improvements
! Translation Improvements
Improvements to the following translations:
*
! Plugin Improvements
*
! Accessibility Improvements
*
! Usability Improvements
*
! Widget Improvements
*
! Filter improvements
*
! Hackability Improvements
*
! Bug Fixes
*
! Node.js Improvements
*
! Performance Improvements
*
! Acknowledgements
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
<<.contributors """
""">>

View File

@@ -0,0 +1,83 @@
caption: 5.3.0
created: 20230419103154368
modified: 20230419103154368
tags: ReleaseNotes
title: Release 5.3.0
type: text/vnd.tiddlywiki
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/master...parameterised-transclusions]]//
! About v5.3.0
This pre-release introduces a number of significant improvements and new features related to some of TiddlyWiki's most fundamental components: macros, widgets, operators and transclusion.
! Introduction to v5.3.0
The motivation of these changes is to fix one of ~TiddlyWiki 5's early design flaws: the reliance on macros using textual substitution as the primary way to modularise and reuse wikitext and filters.
Experience has shown that while macros are a good match for a small number of tasks, they are brittle and error prone for many common operations. See [[Macro Pitfalls]] for a discussion of the problems that accompany this approach. Over the years we have introduced mitigations for the worst problems but these have come at a cost of increased complexity.
The changes in this release provide powerful new ways to achieve common tasks, and unlock completely new capabilities that were previously impossible in wikitext.
* [[Procedures]], which are essentially what macros should have been; they work in exactly the same way except that parameters are exposed as simple variables (without the double underscores) and no textual substitution takes place
* [[Custom Widgets]], allowing the creation of widgets in wikitext, and the redefinition of built-in widgets
* [[Functions]], a new way to encapsulate filter expressions with named parameters, including the ability to make custom filter operators
* Parameterised [[Transclusions|Transclusion]], allowing strings and wikitext trees to be passed to transclusions
The approach taken by this release is to add new functionality by extending and augmenting the system without disturbing existing functionality. All of these changes are thus intended to be backwards compatible. While they represent a new field of opportunities for wikitext authors, it is possible for authors to ignore all these new features and continue to use ~TiddlyWiki 5 in the way that they have always done.
These changes lay the groundwork for macros and related features to be deprecated (which is the point at which users are advised not to use old features, and instead given clear pointers to the equivalent modern functionality).
The new transclusion architecture is not by itself sufficient to enable us to fully deprecate macros yet. To handle the remaining use cases we propose a new backtick quoted attribute format that allows for the substitution of variable values. See https://github.com/Jermolene/TiddlyWiki5/issues/6663 for details.
! Plugin Improvements
*
! Translation improvement
Improvements to the following translations:
*
! Accessibility Improvements
*
! Usability Improvements
*
! Widget Improvements
*
! Filter improvements
*
! Hackability Improvements
*
! Bug Fixes
*
! Developer Improvements
*
! Node.js Improvements
*
! Performance Improvements
*
! Acknowledgements
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
<<.contributors """
""">>

View File

@@ -0,0 +1,24 @@
title: CustomOperators/NestedParameterised
description: Nested parameterised custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(first:ignored,factor:0.5)
[divide[2]multiply<factor>]
\end
\function .multiplebysomething(first:ignored,factor:2)
[multiply[2].dividebysomething[],<factor>]
\end
<$text text={{{ [[123].multiplebysomething[]] }}}/>
-
<$text text={{{ [[123].multiplebysomething[x],[4]] }}}/>
+
title: ExpectedResult
<p>246-492</p>

View File

@@ -0,0 +1,24 @@
title: CustomOperators/Parameterised
description: Parameterised custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .multiplybysomething(first:ignored,factor:2)
[multiply[2]multiply<factor>]
\end
<$text text={{{ [[123].multiplybysomething[]] }}}/>
-
<$text text={{{ [[123].multiplybysomething[x],[4]] }}}/>
|
<$text text={{{ [[123]function[.multiplybysomething]] }}}/>
-
<$text text={{{ [[123]function[.multiplybysomething],[x],[4]] }}}/>
+
title: ExpectedResult
<p>492-984|492-984</p>

View File

@@ -0,0 +1,21 @@
title: CustomOperators/Simple
description: Simple custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .multiplybytwo()
[multiply[2]]
\end
<$text text={{{ [[123].multiplybytwo[]] }}}/>
|
<$text text={{{ [[123]function[.multiplybytwo]] }}}/>
+
title: ExpectedResult
<p>246|246</p>

View File

@@ -0,0 +1,24 @@
title: Functions/FunctionAttributes
description: Attributes specified as function invocations
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(factor:0.5)
[divide<factor>]
\end
\function multiplebysomething(first:ignored,factor:2)
[<factor>multiply[2].dividebysomething[0.25]]
\end
<$text text=<<multiplebysomething>>/>
|
<$text text=<<multiplebysomething "nothing" "4">>/>
+
title: ExpectedResult
<p>16|32</p>

View File

@@ -0,0 +1,24 @@
title: Functions/FunctionOperator
description: Calling a function via the function operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(factor:0.5)
[divide<factor>]
\end
\function multiplebysomething(first:ignored,factor:2)
[multiply<factor>multiply[2].dividebysomething[0.25]]
\end
<$text text={{{ [[4]function[multiplebysomething]] }}}/>
|
<$text text={{{ [[6]function[multiplebysomething],[ignored],[4]] }}}/>
+
title: ExpectedResult
<p>64|192</p>

View File

@@ -0,0 +1,15 @@
title: Functions/MissingFunction
description: Calling a missing function via the function operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$text text={{{ [[23]function[missing]] }}}/>
+
title: ExpectedResult
23

View File

@@ -0,0 +1,18 @@
title: Functions/RunawayRecursiveFunctions
description: Runaway recursive functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .buffalo(p)
[.buffalo<p>]
\end
<$text text=<<.buffalo 8>>/>
+
title: ExpectedResult
/**-- Excessive filter recursion --**/

View File

@@ -0,0 +1,22 @@
title: Functions/UndefinedParameters
description: Undefined function parameters
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function greet(who)
[[hello ]addsuffix<who>]
\end
<$text text={{{[function[greet],[world]]}}}/>
<<greet world>>
<$text text={{{[function[greet]]}}}/>
<<greet>>
+
title: ExpectedResult
hello world<p>hello world</p>hello <p>hello </p>

View File

@@ -0,0 +1,36 @@
title: Functions/WikifiedFunctions
description: Wikified functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function fn-buffalo(param)
[<param>addsuffix[ with a ''buffalo'']]
\end
\procedure proc-buffalo(param)
<<param>> with a ''buffalo''
\end
\define macro-buffalo(param)
$param$ with a ''buffalo''
\end
<<fn-buffalo "Going to lunch">>
<<proc-buffalo "Going to breakfast">>
<<macro-buffalo "Going to dinner">>
<$transclude $variable="fn-buffalo" param="Going to lunch" $output="text/plain"/>
<$transclude $variable="proc-buffalo" param="Going to breakfast" $output="text/plain"/>
<$transclude $variable="macro-buffalo" param="Going to dinner" $output="text/plain"/>
+
title: ExpectedResult
<p>Going to lunch with a ''buffalo''</p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo

View File

@@ -0,0 +1,31 @@
title: Genesis/RedefineLet
description: Using the genesis widget to override the let widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $let()
\whitespace trim
<$parameters $params="@params">
<$setmultiplevariables $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>addprefix[--]addsuffix[--]]">
<$slot $name="ts-raw"/>
</$setmultiplevariables>
</$parameters>
\end
<$let
one="Elephant"
$two="Kangaroo"
$$three="Giraffe"
>
(<$text text=<<one>>/>)
(<$text text=<<$two>>/>)
(<$text text=<<$$three>>/>)
</$let>
+
title: ExpectedResult
<p>(--Elephant--)
(--Kangaroo--)
(--Giraffe--)</p>

View File

@@ -0,0 +1,20 @@
title: Procedures/Nested
description: Nested Procedures
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure alpha(x)
\procedure beta(y)
<$text text=<<y>>/>
\end beta
<$transclude $variable="beta" y={{{ [<x>addprefix<x>] }}}/>
\end alpha
<<alpha "Elephant">>
+
title: ExpectedResult
<p>ElephantElephant</p>

View File

@@ -0,0 +1,27 @@
title: Transclude/CustomWidget/ActionWidget
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='Result'>
</$transclude>
+
title: Actions
\whitespace trim
<!-- Define the <$$action-mywidget> widget by defining a transcludable variable with that name -->
\widget $$action-mywidget(one:'Jaguar')
\whitespace trim
<$action-setfield $tiddler="Result" $field="text" $value=<<one>>/>
\end
<$$action-mywidget one="Dingo">
Crocodile
</$$action-mywidget>
+
title: ExpectedResult
<p>Dingo</p>

View File

@@ -0,0 +1,26 @@
title: Transclude/CustomWidget/Fail
description: Custom widget failed definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<!-- Attempt to define the <$non-existent-widget> widget by defining a transcludable variable with that name -->
\widget $non-existent-widget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$non-existent-widget one="Dingo">
Crocodile
</$non-existent-widget>
<$non-existent-widget one="BumbleBee">
Squirrel
</$non-existent-widget>
+
title: ExpectedResult
<p>Undefined widget 'non-existent-widget'Undefined widget 'non-existent-widget'</p>

View File

@@ -0,0 +1,29 @@
title: CustomWidget-Override-Codeblock
description: Usage of genesis widget with attributes starting with dollar signs
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\import Definition
<$codeblock code="Kangaroo"/>
<$codeblock code={{Subject}}/>
<$let test="Tiger">
<$codeblock code=<<test>>/>
</$let>
+
title: Definition
\whitespace trim
\widget $codeblock(code)
<$genesis $type="$codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/>
\end
+
title: Subject
Python
+
title: ExpectedResult
<p><pre><code>£Kangaroo@</code></pre><pre><code>£Python@</code></pre><pre><code>£Tiger@</code></pre></p>

View File

@@ -0,0 +1,33 @@
title: Transclude/CustomWidget/OverrideTransclude
description: Custom widget definition attempting to override transclude
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerZero
Antelope
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$transclude> widget by defining a transcludable variable with that name -->
\widget $transclude(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="body">
Whale
</$slot>
\end
<$genesis $type="$transclude" $remappable="no" $$tiddler="TiddlerZero">
Crocodile
</$genesis>
+
title: ExpectedResult
<p>Antelope</p>

View File

@@ -0,0 +1,33 @@
title: Transclude/CustomWidget/Simple
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Define the <$$mywidget> widget by defining a transcludable variable with that name -->
\widget $$mywidget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$$mywidget one="Dingo">
Crocodile
</$$mywidget>
<$$mywidget one="BumbleBee">
Squirrel
</$$mywidget>
<$$mywidget/>
+
title: ExpectedResult
<p>DingoCrocodileBumbleBeeSquirrelJaguarWhale</p>

View File

@@ -0,0 +1,20 @@
title: CustomWidget/Slotted/Empty
description: Custom widget with empty slotted values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $$mywidget()
<$slot $name=ts-raw>the body is empty</$slot>
\end
#<$$mywidget/>
#<$$mywidget></$$mywidget>
#<$$mywidget>the body is not empty</$$mywidget>
+
title: ExpectedResult
<ol><li>the body is empty</li><li>the body is empty</li><li>the body is not empty</li></ol>

View File

@@ -0,0 +1,27 @@
title: Transclude/CustomWidget/Slotted
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $$mywidget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-stuff">
Whale
</$slot>
\end
<$$mywidget one="Dingo">
<$fill $name="ts-stuff">
Crocodile
</$fill>
</$$mywidget>
<$$mywidget one="BumbleBee">
Squirrel
</$$mywidget>
+
title: ExpectedResult
<p>DingoCrocodileBumbleBeeWhale</p>

View File

@@ -0,0 +1,27 @@
title: Transclude/CustomWidget/TextWidgetOverride
description: Custom widget definition redefining the text widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefining the text widget only works when it is explicitly invoked with the <$text> syntax, and not implicitly via typed text -->
\widget $text(text:'Jaguar')
\whitespace trim
<$genesis $type="$text" $remappable="no" text={{{ [<text>addprefix[≤]addsuffix[≥]] }}}/>
\end
<$text text="Dingo"/>
Crocodile
+
title: ExpectedResult
<p>≤Dingo≥≤Jaguar≥</p>

View File

@@ -0,0 +1,31 @@
title: Transclude/CustomWidget/TextWidgetOverrideWithSlot
description: Custom widget definition redefining the text widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$text> widget by defining a transcludable variable with that name -->
\widget $text(text:'Jaguar')
\whitespace trim
<$genesis $type="$text" $remappable="no" text=<<text>>/>
<$set name="$text" value="">
<$slot $name="ts-raw">
Whale
</$slot>
</$set>
\end
<$text text="Dingo">
Crocodile
</$text>
+
title: ExpectedResult
<p>DingoCrocodile</p>

View File

@@ -0,0 +1,31 @@
title: CustomWidget-Unoverride-Codeblock
description: Usage of genesis widget with attributes starting with dollar signs, and unoverriding a core widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\import Definition
<$let $codeblock="">
<$codeblock code="Kangaroo"/>
<$codeblock code={{Subject}}/>
<$let test="Tiger">
<$codeblock code=<<test>>/>
</$let>
</$let>
+
title: Definition
\whitespace trim
\widget $codeblock(code)
<$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/>
\end
+
title: Subject
Python
+
title: ExpectedResult
<p><pre><code>Kangaroo</code></pre><pre><code>Python</code></pre><pre><code>Tiger</code></pre></p>

View File

@@ -0,0 +1,29 @@
title: Transclude/CustomWidget/VariableAttribute
description: Custom widget definition using an attribute called $variable
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$$mywidget> widget by defining a transcludable variable with that name -->
\widget $$mywidget($variable:'Jaguar')
\whitespace trim
<$text text=<<$variable>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$$mywidget $variable="Dingo">
Crocodile
</$$mywidget>
+
title: ExpectedResult
<p>DingoCrocodile</p>

View File

@@ -0,0 +1,17 @@
title: Transclude/Macro/JavaScript
description: Transcluding a javascript macro
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<<makedatauri text:"Wildebeest" type:"text/plain">>
<$macrocall $name="makedatauri" text="Wildebeest" type="text/plain"/>
+
title: ExpectedResult
<p><a class="tc-tiddlylink-external" href="data:text/plain,Wildebeest" rel="noopener noreferrer" target="_blank">data:text/plain,Wildebeest</a></p><p><a class="tc-tiddlylink-external" href="data:text/plain,Wildebeest" rel="noopener noreferrer" target="_blank">data:text/plain,Wildebeest</a></p>

View File

@@ -0,0 +1,17 @@
title: Transclude/Macro/Plain
description: Transcluding a macro as plain text
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$let currentTab="Jeremy">
<$macrocall $name="currentTab" $type="text/plain" $output="text/plain"/>
|
<$transclude $variable="currentTab" $type="text/plain" $output="text/plain"/>
</$let>
+
title: ExpectedResult
<p>Jeremy|Jeremy</p>

View File

@@ -0,0 +1,26 @@
title: Transclude/Macro/Simple
description: Transcluding a macro
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\define mamacro(one:"red",two:"green")
It is $one$ and $two$ or <<__one__>> and <<__two__>>.
\end
<$macrocall $name="mamacro"/>
<$transclude $variable="mamacro"/>
<$transclude $variable="mamacro" one="orange"/>
<$transclude $variable="mamacro" 0="pink"/>
<$transclude $variable="mamacro" one="purple" 1="pink"/>
+
title: ExpectedResult
<p>It is red and green or red and green.</p><p>It is red and green or red and green.</p><p>It is orange and green or orange and green.</p><p>It is pink and green or pink and green.</p><p>It is purple and pink or purple and pink.</p>

View File

@@ -0,0 +1,48 @@
title: Transclude/MissingTarget
description: Transcluding a missing target
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$transclude>
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="ts-missing">
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$fill>
</$transclude>
<$transclude $tiddler='MissingTiddler' one='Ferret'>
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$transclude>
<$transclude $tiddler='MissingTiddler' one='Ferret'>
<$fill $name="ts-missing">
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Kangaroo'>
Piranha
<$text text=<<one>>/>
</$parameters>
+
title: ExpectedResult
<p>PiranhaFerretPiranhaFerretBadgerFerretBadgerFerret</p>

View File

@@ -0,0 +1,34 @@
title: Transclude/Parameterised/Depth
description: Parameterised transclusion using the $depth attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
|
<$transclude $tiddler='TiddlerOne'/>
|
<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/>
|
<$transclude $tiddler='TiddlerOne' $$two="Falcon"/>
+
title: TiddlerOne
\whitespace trim
{{TiddlerTwo}}
+
title: TiddlerTwo
\whitespace trim
<$parameters one='Jaguar' $$two='Piranha' $depth="2">
<$text text=<<one>>/>:<$text text=<<$two>>/>
</$parameters>
<$parameters one='Leopard' $$two='Coelacanth'>
(<$text text=<<one>>/>|<$text text=<<$two>>/>)
</$parameters>
+
title: ExpectedResult
<p>Ferret:Piranha(Leopard|Coelacanth)|Jaguar:Piranha(Leopard|Coelacanth)|Ferret:Osprey(Leopard|Coelacanth)|Jaguar:Falcon(Leopard|Coelacanth)</p>

View File

@@ -0,0 +1,29 @@
title: Transclude/Parameterised/Mode
description: Parameterised transclusion using the $parseMode attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
This is a block
</$transclude>
<$transclude $tiddler='TiddlerOne'>
This is inline
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $parseMode="@parseMode">
<$text text=<<@parseMode>>/>
</$parameters>
+
title: ExpectedResult
<p>block</p><p>inline</p>

View File

@@ -0,0 +1,34 @@
title: Transclude/Parameterised/Name/Values
description: Parameterised transclusion accessing parameters as name/value pairs
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler="TiddlerOne" 0="" 1="" 2=""/>
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar' $$one='Lizard' two='Mole' $params="@params">
<$list filter="[<@params>jsonindexes[]]">
{<$text text=<<currentTiddler>>/>: <$text text={{{ [<@params>jsonget<currentTiddler>] }}}/>}
</$list>
</$parameters>
+
title: TiddlerTwo
\whitespace trim
\parameters(zero:'Mouse',$one:'Horse',two:'Owl')
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> $$one=<<$one>> two=<<two>>/>)
+
title: ExpectedResult
<p>{0:}{1:}{2:}</p><p></p><p>{0:Ferret}</p><p>{0:Butterfly}{1:Moth}</p><p>{0:Beetle}{1:Scorpion}{2:Snake}</p><p>({$one:Scorpion}{two:Snake}{zero:Beetle})</p>

View File

@@ -0,0 +1,29 @@
title: Transclude/Parameterised/ParseTreeNodes
description: Parameterised transclusion using the $parseTreeNodes attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
This is a block
</$transclude>
<$transclude $tiddler='TiddlerOne'>
This is inline
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $parseTreeNodes="@parseTreeNodes">
<$text text=<<@parseTreeNodes>>/>
</$parameters>
+
title: ExpectedResult
<p>[{"type":"element","tag":"p","children":[{"type":"text","text":"This is a block","start":68,"end":83}],"start":68,"end":83}]</p><p>[{"type":"text","text":"This is inline","start":136,"end":152}]</p>

View File

@@ -0,0 +1,29 @@
title: Transclude/Parameterised/Positional/Shortcut/Parameters
description: Positional parameterised transclusion using shortcut syntax and parameters pragma
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
\parameters(zero:Jaguar,one:'Lizard',two:'Mole')
[{<$text text=<<zero>>/>}{<$text text=<<one>>/>}{<$text text=<<two>>/>}]
+
title: TiddlerTwo
\whitespace trim
\parameters(zero:'Mouse',one:Horse,two:'Owl')
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> one=<<one>> two=<<two>>/>)
+
title: ExpectedResult
<p>[{Jaguar}{Lizard}{Mole}]</p><p>[{Ferret}{Lizard}{Mole}]</p><p>[{Butterfly}{Moth}{Mole}]</p><p>[{Beetle}{Scorpion}{Snake}]</p><p>([{Beetle}{Scorpion}{Snake}])</p>

View File

@@ -0,0 +1,29 @@
title: Transclude/Parameterised/Positional/Shortcut
description: Positional parameterised transclusion using shortcut syntax
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar' one='Lizard' two='Mole'>[{<$text text=<<zero>>/>}{<$text text=<<one>>/>}{<$text text=<<two>>/>}]</$parameters>
+
title: TiddlerTwo
\whitespace trim
<$parameters zero='Mouse' one='Horse' two='Owl'>
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> one=<<one>> two=<<two>>/>)
</$parameters>
+
title: ExpectedResult
<p>[{Jaguar}{Lizard}{Mole}]</p><p>[{Ferret}{Lizard}{Mole}]</p><p>[{Butterfly}{Moth}{Mole}]</p><p>[{Beetle}{Scorpion}{Snake}]</p><p>([{Beetle}{Scorpion}{Snake}])</p>

View File

@@ -0,0 +1,30 @@
title: Transclude/Parameterised/Positional/Variables
description: Positional parameterised transclusion of variables
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function myfunction(alpha:"apple",beta:"banana",gamma:"grenadine") [<alpha>]
\define mymacro(alpha:"apple",beta:"banana",gamma:"grenadine") $beta$
\function f(a) [<a>]
(Functions:
<$text text={{{ [<myfunction gamma:"unused" f1>] }}}/>
,
<$text text=<<myfunction gamma:"unused" f1>>/>
,
<<myfunction gamma:"unused" f1>>
)(Macros:
<$text text={{{ [<mymacro gamma:"unused" f1>] }}}/>
,
<$text text=<<mymacro gamma:"unused" f1>>/>
,
<<mymacro gamma:"unused" f1>>
)
+
title: ExpectedResult
<p>(Functions:f1,f1,f1)(Macros:banana,banana,banana)</p>

View File

@@ -0,0 +1,26 @@
title: Transclude/Parameterised/Positional
description: Positional parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' zero='Ferret'/>
<$transclude zero='Ferret' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne' 0='Pigeon'/>
<$transclude 0='Pigeon' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne' zero='Ferret' 0='Pigeon'/>
<$transclude zero='Ferret' 0='Pigeon' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne'/>
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar'>
<$text text=<<zero>>/>
</$parameters>
+
title: ExpectedResult
<p>FerretFerretPigeonPigeonFerretFerretJaguar</p>

View File

@@ -0,0 +1,20 @@
title: Transclude/Parameterised/Shortcut/Parameters
description: Simple parameterised transclusion using the parameters pragma
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
<$transclude $tiddler='TiddlerOne'/>
+
title: TiddlerOne
\whitespace trim
\parameters(one:'Jaguar')
<$text text=<<one>>/>
+
title: ExpectedResult
<p>FerretJaguar</p>

View File

@@ -0,0 +1,21 @@
title: Transclude/Parameterised/Shortcut
description: Simple parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure test(one:'Jaguar')
{<$text text=<<one>>/>}
\end
<$transclude $variable='test' one='Ferret'/>
<$transclude $variable='test'/>
<<test "Rat">>
<<test one:"Mouse">>
+
title: ExpectedResult
<p>{Ferret}{Jaguar}{Rat}{Mouse}</p>

View File

@@ -0,0 +1,26 @@
title: Transclude/Parameterised/Simple
description: Simple parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
|
<$transclude $tiddler='TiddlerOne'/>
|
<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/>
|
<$transclude $tiddler='TiddlerOne' $$two="Falcon"/>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar' $$two='Piranha'>
<$text text=<<one>>/>:<$text text=<<$two>>/>
</$parameters>
+
title: ExpectedResult
<p>Ferret:Piranha|Jaguar:Piranha|Ferret:Osprey|Jaguar:Falcon</p>

View File

@@ -0,0 +1,29 @@
title: Transclude/Parameterised/SlotFillParseTreeNodes
description: Parameterised transclusion using the $slotFillParseTreeNodes attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="one">This is first</$fill>
<$fill $name="two">But this is second</$fill>
</$transclude>
<$transclude $tiddler='TiddlerOne'>
<$fill $name="one">This is first
<$fill $name="two">But this is second</$fill></$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $slotFillParseTreeNodes="@slotFillParseTreeNodes">
<$text text={{{ [<@slotFillParseTreeNodes>jsonindexes[]join[,]] }}}/>
</$parameters>
+
title: ExpectedResult
<p>one,ts-raw,two</p><p>one,ts-raw</p>

View File

@@ -0,0 +1,24 @@
title: Transclude/Parameterised/Slotted/Missing
description: Parameterised transclusion with slotted missing values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar'>
<$text text=<<one>>/>
<$slot $name="content">
Whale
</$slot>
</$parameters>
+
title: ExpectedResult
<p>FerretWhale</p>

View File

@@ -0,0 +1,27 @@
title: Transclude/Parameterised/Slotted
description: Parameterised transclusion with slotted values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="content">
Hippopotamus
</$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar'>
<$text text=<<one>>/>
<$slot $name="content">
Whale
</$slot>
</$parameters>
+
title: ExpectedResult
<p>FerretHippopotamus</p>

View File

@@ -0,0 +1,25 @@
title: Transclude/Procedures/Whitespace
description: Procedures should inherit whitespace settings from definition site
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure testproc()
This is a sentence
\end
\define testmacro()
This is a sentence
\end
This is a sentence
[<<testproc>>]
[<<testmacro>>]
+
title: ExpectedResult
<p>This is a sentence
[This is a sentence]
[This is a sentence ]</p>

View File

@@ -0,0 +1,38 @@
title: Transclude/Typed
description: Typed transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\procedure testproc()
This is ''wikitext''
\end
<$transclude $variable="testproc"/>
-
<$transclude $variable="testproc" $type="text/plain"/>
<$transclude $tiddler="Data" $index="testindex"/>
-
<$transclude $tiddler="Data" $index="testindex" $type="text/plain"/>
<$transclude $tiddler="Data" $field="custom"/>
-
<$transclude $tiddler="Data" $field="custom" $type="text/plain"/>
+
title: Data
type: application/x-tiddler-dictionary
custom: This is ''wikitext''
testindex: This is ''wikitext''
+
title: ExpectedResult
<p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p><p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p><p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p>

View File

@@ -422,7 +422,7 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,Tiddler8,TiddlerOne,TiddlerSeventh");
expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8");
expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three");
var fakeWidget = {getVariable: function() {return "one";}};
var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "one": undefined;}};
expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8");
});
@@ -625,7 +625,7 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("[{!!title}]").join(",")).toBe("");
expect(wiki.filterTiddlers("[prefix{Tiddler8}] +[sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne");
expect(wiki.filterTiddlers("[modifier{Tiddler8!!test-field}] +[sort[title]]").join(",")).toBe("TiddlerOne");
var fakeWidget = {wiki: wiki, getVariable: function() {return "Tiddler Three";}};
var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "Tiddler Three": undefined;}};
expect(wiki.filterTiddlers("[modifier{!!modifier}] +[sort[title]]",fakeWidget).join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three");
});

View File

@@ -124,7 +124,7 @@ describe("Wiki.parseTextReference tests", function() {
// Non-existent subtiddler of a plugin
expect(parseAndGetSource("$:/ShadowPlugin","text",null,"MyMissingTiddler")).toEqual(null);
// Plain text tiddler
expect(parseAndGetSource("TiddlerNine")).toEqual(undefined);
expect(parseAndGetSource("TiddlerNine")).toEqual("this is plain text");
});
});

View File

@@ -188,4 +188,4 @@ describe("Utility tests", function() {
});
})();
})();

View File

@@ -143,7 +143,7 @@ describe("Widget module", function() {
var wiki = new $tw.Wiki();
// Add a tiddler
wiki.addTiddlers([
{title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>\n"},
{title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>"},
{title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"}
]);
// Test parse tree
@@ -157,7 +157,7 @@ describe("Widget module", function() {
// Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode);
// Test the rendering
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>\n");
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
});
it("should deal with SVG elements", function() {
@@ -683,7 +683,7 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("<p>New value</p>");
});
it("should can mix setWidgets and macros when importing", function() {
it("should support mixed setWidgets and macros when importing", function() {
var wiki = new $tw.Wiki();
// Add some tiddlers
wiki.addTiddlers([
@@ -699,6 +699,20 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("<p>Aval Bval Cval</p>");
});
it("should skip parameters widgets when importing", function() {
var wiki = new $tw.Wiki();
// Add some tiddlers
wiki.addTiddlers([
{title: "B", text: "<$parameters bee=nothing><$set name='B' value='Bval'>\n\ndummy text</$set></$parameters>"},
]);
var text = "\\import B\n<<B>>";
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
// Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode);
// Test the rendering
expect(wrapper.innerHTML).toBe("<p>Bval</p>");
});
it("can have more than one macroDef variable imported", function() {
var wiki = new $tw.Wiki();
wiki.addTiddlers([

View File

@@ -19,7 +19,8 @@ describe("WikiText parser tests", function() {
// Define a parsing shortcut
var parse = function(text) {
return wiki.parseText("text/vnd.tiddlywiki",text).tree;
var tree = wiki.parseText("text/vnd.tiddlywiki",text).tree;
return tree;
};
it("should parse tags", function() {
@@ -114,7 +115,70 @@ describe("WikiText parser tests", function() {
it("should parse macro definitions", function() {
expect(parse("\\define myMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ]
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}]}]
);
});
it("should parse procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro()\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse single line procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro() nothing\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse procedure definitions with parameters", function() {
expect(parse("\\procedure myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse procedure definitions", function() {
expect(parse("\\procedure myMacro(one:'Jaguar')\n<$text text=<<one>>/>\n\\end\n\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<<one>>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<<one>>/>"}],"isProcedureDefinition":true}]
);
}); it("should parse function definitions with no parameters", function() {
expect(parse("\\function myMacro()\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse single line function definitions with no parameters", function() {
expect(parse("\\function myMacro() nothing\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse function definitions with parameters", function() {
expect(parse("\\function myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse function definitions", function() {
expect(parse("\\function myMacro(one:'Jaguar')\n<$text text=<<one>>/>\n\\end\n\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<<one>>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<<one>>/>"}],"isFunctionDefinition":true}]
);
});
@@ -122,7 +186,7 @@ describe("WikiText parser tests", function() {
it("should parse comment in pragma area. Comment will be invisible", function() {
expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'aMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ]
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"aMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"aMacro"},{"name":"value","type":"string","value":"nothing"}]}]
);
});
@@ -143,38 +207,38 @@ describe("WikiText parser tests", function() {
it("should parse inline macro calls", function() {
expect(parse("<<john>><<paul>><<george>><<ringo>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 35, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'macrocall', start: 8, params: [ ], name: 'paul', end: 16 }, { type: 'macrocall', start: 16, params: [ ], name: 'george', end: 26 }, { type: 'macrocall', start: 26, params: [ ], name: 'ringo', end: 35 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"transclude","start":8,"end":16,"attributes":{"$variable":{"name":"$variable","type":"string","value":"paul"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"paul"}]},{"type":"transclude","start":16,"end":26,"attributes":{"$variable":{"name":"$variable","type":"string","value":"george"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"george"}]},{"type":"transclude","start":26,"end":35,"attributes":{"$variable":{"name":"$variable","type":"string","value":"ringo"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"ringo"}]}],"start":0,"end":35}]
);
expect(parse("text <<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[{ type: 'element', tag: 'p', start: 0, end: 92, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: 'john', start: 5, params: [ { type: 'macro-parameter', start: 11, value: 'val1', name: 'one', end: 20 }, { type: 'macro-parameter', start: 20, value: 'val "2"', name: 'two', end: 35 }, { type: 'macro-parameter', start: 35, value: 'val \'3\'', name: 'three', end: 52 }, { type: 'macro-parameter', start: 52, value: 'val 4"5\'', name: 'four', end: 73 }, { type: 'macro-parameter', start: 73, value: 'val 5', name: 'five', end: 89 } ], end: 92 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":92,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":11,"end":20},"two":{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},"three":{"name":"three","type":"string","value":"val '3'","start":35,"end":52},"four":{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},"five":{"name":"five","type":"string","value":"val 5","start":73,"end":89}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":11,"end":20},{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},{"name":"three","type":"string","value":"val '3'","start":35,"end":52},{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},{"name":"five","type":"string","value":"val 5","start":73,"end":89}]}],"start":0,"end":92}]
);
expect(parse("ignored << carrots <<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 27, children: [ { type: 'text', text: 'ignored << carrots ', start: 0, end: 19 }, { type: 'macrocall', name: 'john', start: 19, params: [ ], end: 27 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"text","text":"ignored << carrots ","start":0,"end":19},{"type":"transclude","start":19,"end":27,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":27}]
);
expect(parse("text <<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 14, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: '<john', start: 5, params: [ ], end: 14 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":14,"attributes":{"$variable":{"name":"$variable","type":"string","value":"<john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"<john"}]}],"start":0,"end":14}]
);
expect(parse("before\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'text', text: 'before\n', start: 0, end: 7 }, { type: 'macrocall', start: 7, params: [ ], name: 'john', end: 15 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"text","text":"before\n","start":0,"end":7},{"type":"transclude","start":7,"end":15,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":15}]
);
// A single space will cause it to be inline
expect(parse("<<john>> ")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 9, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'text', text: ' ', start: 8, end: 9 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"text","text":" ","start":8,"end":9}],"start":0,"end":9}]
);
expect(parse("text <<outie one:'my <<innie>>' >>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 34, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', start: 5, params: [ { type: 'macro-parameter', start: 12, value: 'my <<innie>>', name: 'one', end: 31 } ], name: 'outie', end: 34 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":34,"attributes":{"$variable":{"name":"$variable","type":"string","value":"outie"},"one":{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}]}],"start":0,"end":34}]
);
@@ -183,37 +247,37 @@ describe("WikiText parser tests", function() {
it("should parse block macro calls", function() {
expect(parse("<<john>>\n<<paul>>\r\n<<george>>\n<<ringo>>")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'macrocall', start: 9, name: 'paul', params: [ ], end: 17, isBlock: true }, { type: 'macrocall', start: 19, name: 'george', params: [ ], end: 29, isBlock: true }, { type: 'macrocall', start: 30, name: 'ringo', params: [ ], end: 39, isBlock: true } ]
[ { type: 'transclude', start: 0, attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: 'transclude', start: 9, attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: 'transclude', start: 19, attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: 'transclude', start: 30, attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ]
);
expect(parse("<<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ { type: 'macro-parameter', start: 6, value: 'val1', name: 'one', end: 15 }, { type: 'macro-parameter', start: 15, value: 'val "2"', name: 'two', end: 30 }, { type: 'macro-parameter', start: 30, value: 'val \'3\'', name: 'three', end: 47 }, { type: 'macro-parameter', start: 47, value: 'val 4"5\'', name: 'four', end: 68 }, { type: 'macro-parameter', start: 68, value: 'val 5', name: 'five', end: 84 }], end: 87, isBlock: true } ]
[{"type":"transclude","start":0,"end":87,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":6,"end":15},"two":{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},"three":{"name":"three","type":"string","value":"val '3'","start":30,"end":47},"four":{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},"five":{"name":"five","type":"string","value":"val 5","start":68,"end":84}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":6,"end":15},{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},{"name":"three","type":"string","value":"val '3'","start":30,"end":47},{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},{"name":"five","type":"string","value":"val 5","start":68,"end":84}],"isBlock":true}]
);
expect(parse("<< carrots\n\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'macrocall', start: 12, params: [ ], name: 'john', end: 20, isBlock: true } ]
[ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'transclude', start: 12, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ]
);
expect(parse("before\n\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'macrocall', start: 8, name: 'john', params: [ ], end: 16, isBlock: true } ]
[ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'transclude', start: 8, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ]
);
expect(parse("<<john>>\nafter")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ]
[ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ]
);
expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 11, value: '\n\nwikitext\n', name: 'arg', end: 33 } ], name: 'multiline', end: 36, isBlock: true }]
[{"type":"transclude","start":0,"end":36,"attributes":{"$variable":{"name":"$variable","type":"string","value":"multiline"},"arg":{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}},"orderedAttributes":[{"name":"$variable","type":"string","value":"multiline"},{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}],"isBlock":true}]
);
expect(parse("<<outie one:'my <<innie>>' >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 7, value: 'my <<innie>>', name: 'one', end: 26 } ], name: 'outie', end: 29, isBlock: true } ]
[ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} ], end: 29, isBlock: true } ]
);
});
@@ -221,23 +285,23 @@ describe("WikiText parser tests", function() {
it("should parse tricky macrocall parameters", function() {
expect(parse("<<john pa>am>>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'pa>am', end: 12 } ], name: 'john', end: 14, isBlock: true } ]
[{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"pa>am","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"pa>am","start":6,"end":12}],"isBlock":true}]
);
expect(parse("<<john param> >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param>', end: 13 } ], name: 'john', end: 16, isBlock: true } ]
[{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"param>","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param>","start":6,"end":13}],"isBlock":true}]
);
expect(parse("<<john param>>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param', end: 12 } ], name: 'john', end: 14 }, { type: 'text', text: '>', start: 14, end: 15 } ] } ]
[{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"param","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param","start":6,"end":12}]},{"type":"text","text":">","start":14,"end":15}],"start":0,"end":15}]
);
// equals signs should be allowed
expect(parse("<<john var>=4 >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'var>=4', end: 13 } ], name: 'john', end: 16, isBlock: true } ]
[{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"var>=4","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"var>=4","start":6,"end":13}],"isBlock":true}]
);

View File

@@ -1,7 +1,7 @@
/*\
title: test-wikitext-tabs-macro.js
type: application/javascript
tags: [[$:/tags/test-spec]]
tags: [[$:/tags/test-spec-disabled]]
Tests the core tabs macro by comparing the HTML output with a stored template.
Intended to permit future readability improvements.
@@ -74,7 +74,7 @@ describe("Tabs-macro HTML tests", function() {
expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal")).toBe(expected.fields.text.replace(/\n/g,""));
});
it("should render 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() {
it("should render all 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() {
expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal-all")).toBe(expectedAll.fields.text.replace(/\n/g,""));
});

View File

@@ -10,5 +10,5 @@ WikiText syntax uses a number of different types of brackets. Their names are sh
|`()` |Round brackets |Parenthesis |Not used in WikiText |
|`[]` |Square brackets |Brackets |[[Links|Linking in WikiText]], [[Filters|Filters]] |
|`{}` |Curly brackets |Braces |[[Text references|TextReference]], [[Filtered attributes|HTML in WikiText]] |
|`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros|Macros in WikiText]] |
|`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros]] |

View File

@@ -1,31 +1,42 @@
created: 20140211171341271
modified: 20220505082754270
modified: 20230419103154328
tags: Concepts Reference
title: Macros
type: text/vnd.tiddlywiki
A <<.def macro>> is a named snippet of text. WikiText can use the name as a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters that get substituted for special placeholders within the snippet.
!! Introduction
For the syntax, see [[Macros in WikiText]].
A <<.def macro>> is a named snippet of text. They are typically defined with the [[Pragma: \define]]:
Most macros are in fact just parameterised [[variables|Variables]].
```
\define my-macro(parameter:"Default value")
This is the macro, and the parameter is $parameter$.
\end
```
They are created using the `\define` [[pragma|Pragma]]. (Behind the scenes, this is transformed into a <<.wlink SetWidget>>, i.e. macros and variables are two sides of the same coin.)
The name wrapped in double angled [[brackets|Brackets]] is used a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters:
The snippet and its incoming parameter values are treated as simple strings of characters with no WikiText meaning, at least until the placeholders have been filled in and the macro call has returned. This means that a macro can assemble and return the complete syntax of a ~WikiText component, such as a [[link|Linking in WikiText]]. (See [[Transclusion and Substitution]] for further discussion of this.)
```
<<my-macro>>
<<my-macro "The parameter">>
```
Within a snippet itself, the only markup detected is `$name$` (a placeholder for a macro parameter) and `$(name)$` (a placeholder for a [[variable|Variables]]).
The parameters that are specified in the macro call are substituted for special placeholders within the snippet:
The <<.mlink dumpvariables>> macro lists all variables (including macros) that are available at that position in the widget tree.
* `$parameter-name$` is replaced with the value of the named parameter
* `$(variable-name)$` is replaced with the value of the named [[variable|Variables]]).
An <<.wlink ImportVariablesWidget>> widget can be used to copy macro definitions to another branch of the [[widget tree|Widgets]]. ~TiddlyWiki uses this technique internally to implement global macros -- namely any macros defined in tiddlers with the <<.tag $:/tags/Macro>> tag.
<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Custom Widgets]] and [[Functions]] which together provide more robust and flexible ways to encapsulate and re-use code. It is now recommended to only use macros when textual substitution is specifically required.
The tag <<.tag $:/tags/Macro/View>> is used to define macros that should only be available within the main view template and the preview panel.
!! How Macros Work
The tag <<.tag $:/tags/Macro/View/Body>> is used to define macros that should only be available within the main view template body and the preview panel.
Macros are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled.
For maximum flexibility, macros can also be <<.js-macro-link "written as JavaScript modules">>.
!! Using Macros
A similar effect to a parameterised macro call can be produced by setting [[variables|Variables]] around a [[transclusion|Transclusion]].
* [[Macro Definitions]] describes how to create macros
* [[Macro Calls]] describes how to use macros
* [[Macro Parameter Handling]] describes how macro parameters work
* [[Macro Pitfalls]] describes some of the pitfalls of using macros
* [[Core Macros]] lists the built-in core macros
~TiddlyWiki's core has [[several macros|Core Macros]] built in.

View File

@@ -1,24 +1,8 @@
created: 20150219175930000
modified: 20230117112239663
tags: Concepts [[WikiText Parser Modes]]
modified: 20220122182842041
tags:
title: Pragma
type: text/vnd.tiddlywiki
A <<.def pragma>> is a special component of WikiText that provides control over the way the remaining text is parsed.
Pragmas occupy lines that start with `\`. They can only appear at the start of the text, but blank lines are allowed between them. If a pragma line appears in the main body of the text, it is treated as if it was ordinary text.
<<.from-version "5.2.6">> Pragmas can have preceding optional whitespace characters.
The following pragmas are available:
;`\define`
: for defining a [[macro|Macros]]
;`\rules`
: for adjusting the set of rules used to parse the text
;`\whitespace trim` or `\whitespace notrim`
: <<.from-version "5.1.15">> Control whether whitespace is trimmed from the start and end of text runs (the default is ''notrim''). This setting can be useful when the whitespace generated by linebreaks disturbs formatting
;`\import <filter-expression>`
: <<.from-version "5.1.18">> Import macro definitions from tiddlers identified by a filter expression
;`\parsermode block` or `\parsermode inline`
: <<.from-version "5.2.4">> Adjust whether the remaining text is parsed in block mode or inline mode.
See [[Pragmas]].

View File

@@ -33,7 +33,7 @@ The initial startup actions are useful for customising TiddlyWiki according to e
<$action-setfield $tiddler="$:/language" text={{{ [[$:/languages/en-GB]] [plugin-type[language]sort[description]removeprefix[$:/languages/]] +[prefix{$:/info/browser/language}] ~[[en-GB]] +[addprefix[$:/languages/]] }}}/>
```
Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an ''import'' [[pragma|Pragma]] at the top of the tiddler:
Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an [[Pragma: \import]] at the top of the tiddler:
```
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]

View File

@@ -0,0 +1,21 @@
caption: function
created: 20220909111836951
modified: 20230419103154328
op-input: a [[selection of titles|Title Selection]] passed as input to the function <<.place F>>
op-output: the [[selection of titles|Title Selection]] returned from the function <<.place F>>
op-parameter: first parameter is the [[function name|Functions]], subsequent parameters are passed to the function by position
op-parameter-name: F
op-purpose: apply a [[function|Functions]] to the input list, and return the result
tags: [[Filter Operators]]
title: function Operator
type: text/vnd.tiddlywiki
<<.from-version "5.3.0">> The <<.op function>> operator applies a named [[function|Functions]] to the input titles, and returns the results from the function. The function is invoked once with all of the input titles (in contrast, the [[filter Operator]] invokes its function separately for each input title).
The first parameter of the <<.op function>> operator specifies the name of the function to be called. Subsequent parameters are passed to the function.
The mapping between the parameters is //positional//, with each consecutive parameter specified in the function call mapped to the corresponding parameter in the function definition. Any parameters that are not provided are given their default values.
<<.tip "Compare with the similar [[filter|filter Operator]] and [[subfilter|subfilter Operator]] operators which take a filter strings as their parameter instead of a named function, and does not permit parameters to be passed">>
<<.operator-examples "function">>

View File

@@ -0,0 +1,28 @@
created: 20221009124003601
modified: 20230419103154328
tags: Concepts Reference
title: Functions
type: text/vnd.tiddlywiki
!! Introduction
<<.from-version "5.3.0">> A <<.def function>> is a named snippet of text containing a [[Filter Expression]]. Functions can have named parameters which are available within the function as variables.
Functions are usually defined with the [[Pragma: \function]]:
```
\function my-function(parameter:"2")
[<parameter>multiply[1.5]]
\end
```
Functions can be invoked in several ways:
* Directly transclude functions with the syntax `<<myfn param:"value">>`
* Assign functions to widget attributes with the syntax `<div class=<<myfn param:"value">>>`
* Invoke functions via the [[function Operator]] with the syntax `[function[myfn],[value],...]`
* Directly invoke functions whose names start with a period as custom filter operators with the syntax `[.myfn[value]]`
!! How Functions Work
Functions are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled.

View File

@@ -0,0 +1,14 @@
created: 20220909111836951
modified: 20230419103154329
tags: Learning
title: Visible Transclusions
type: text/vnd.tiddlywiki
!! Visible Transclusions
Block transclusions are shown in red, and inline transclusions are shown in green.
<$button>
<$action-setfield $tiddler="$:/temp/VisibleTransclusions" tags="$:/tags/Macro/View/Body" text={{$:/core/ui/VisibleTransclude}}/>
Click here to make transclusions visible within story river tiddlers
</$button>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,3 +1,4 @@
code-body: yes
created: 20150221145447000
modified: 20150221145626000
title: $:/editions/tw5.com/macro-examples/say-hi-using-variables

View File

@@ -1,8 +1,9 @@
code-body: yes
created: 20150221145803000
modified: 20150221221536000
title: $:/editions/tw5.com/macro-examples/say-hi
type: text/vnd.tiddlywiki
\define sayhi(name:"Bugs Bunny" address:"Rabbit Hole Hill")
\define sayhi(name:"Bugs Bunny",address:"Rabbit Hole Hill")
Hi, I'm $name$ and I live in $address$.
\end

Some files were not shown because too many files have changed in this diff Show More