1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-25 04:14:40 +00:00

Compare commits

..

88 Commits

Author SHA1 Message Date
Jeremy Ruston
ae68650e93 Docs updates 2025-11-22 12:25:59 +00:00
Jeremy Ruston
91fc7bb6cd Update let.js 2025-11-22 12:03:53 +00:00
Jeremy Ruston
5d8f613df8 Typo 2025-11-22 12:03:46 +00:00
Jeremy Ruston
5cce338206 Fix filenames of tests 2025-11-22 12:03:34 +00:00
Jeremy Ruston
1c1547fe59 Add change note 2025-11-22 11:37:54 +00:00
Jeremy Ruston
a5648f829d Merge branch 'master' into let-filter-run-prefix 2025-11-22 11:31:37 +00:00
Jeremy Ruston
5209f212f4 Another incorrect version number 2025-11-22 11:31:31 +00:00
Jeremy Ruston
f2d5c2d83d Fix version numbers of from-version procedures 2025-11-22 11:20:15 +00:00
Bram Chen
ed4a186c9c Add missing changelog for #9375 (#9456) 2025-11-21 09:50:21 +00:00
Maurycy Zarzycki
f16c0d769b Add support for text/tab-separated-values mimetype (#9445)
* Add support for `text/tab-separated-values` mimetype

* Fix eslint issues

* Add the release note for CSV parser change
2025-11-19 16:59:15 +00:00
Andrew Gregory
298508c104 Updates pluginmaker.js to add modification fields (#9401)
* Update pluginmaker.js

Update plugin modified timestamp unless disabled.

* Update pluginmaker.js

Use getModificationFields()

* Update pluginmaker.js

Inline Tiddler creation parameters

* Create #9401.tid

Create release notes tiddler

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-17 14:02:07 +01:00
XLBilly
f1e1532949 Updates change notes (#9413)
* Update missing change notes

* Update change notes

* Add date fields

* Update change notes

* Update change notes

* Add 9253 impact note

* Replace deprecation with performance

* Add change note for 9350
2025-11-17 12:31:08 +01:00
lin onetwo
3e1078eff1 Fix/serialize close html tag (#9437)
* fix: should use tree.isSelfClosing || isVoidElement

* docs: about html

* fix: Void element without self-closing slash (e.g., <br> instead of <br/>)

* Update editions/test/tiddlers/tests/data/serialize/VoidElements.tid

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 21:33:45 +00:00
XLBilly
cc348fee96 Further fix markup not included in external core edition (#9439)
* Further fix markup not included in external core edition

* Update change note

* Typo
2025-11-14 15:57:35 +00:00
XLBilly
24d45fd318 Replace palette category with theme (#9438) 2025-11-14 09:46:14 +00:00
Jeremy Ruston
9fd345ec06 Fixes for #9432 2025-11-13 12:00:21 +00:00
Arlen Beiler
30e9b4f3b7 Add change notes as requested in #9406 (#9435)
* Add change notes as requested in #9406

* fix username casing
2025-11-12 21:41:29 +00:00
lin onetwo
c6556d5207 Update PR validator to v6 (#9434)
* Update pr-validation.yml

* v6

* fix missing filter

* Revert "fix missing filter"

This reverts commit 9f132d8819.
2025-11-12 17:47:50 +00:00
yaisog
a8da7e0207 Add changenotes for PRs #9305 and #9337 2025-11-12 17:25:40 +00:00
Jeremy Ruston
d5762b1fbb Add "filters" to change note categories
https://github.com/TiddlyWiki/TiddlyWiki5/pull/9390#issuecomment-3523005885
2025-11-12 17:20:26 +00:00
aka James4u
2b0739f06e Adds jsondelete operator to fix #9371 (#9390)
* Added jsondelter to fix 9371

* Replacced deprecated utils.isArray with Array.isArray, Refactored to remove duplication between setDataItem() and getDataItem()

* added changenotes for #9371

* Update #9371.tid

Fix key word: links-> github-links

* changed change-category

* updated github-links

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-12 16:33:28 +01:00
lin onetwo
e339f112a4 Fix/Widget css leading space (#9427)
* fix: trim tc-reveal class

* test: " tc-reveal" -> "tc-reveal"

* Create #9427.tid

* fix: more widgets

* Update #9427.tid
2025-11-12 11:57:02 +00:00
Saq Imtiaz
7a9235e9d1 Revert "Create LinOnetwo.tid (#9425)" (#9428)
This reverts commit bbbc8c2c03.
2025-11-12 12:23:05 +01:00
lin onetwo
bbbc8c2c03 Create LinOnetwo.tid (#9425) 2025-11-11 23:20:25 +00:00
Jeremy Ruston
538482e9a9 Release note fixes 2025-11-11 16:53:02 +00:00
Jeremy Ruston
df7973fc3e Merge branch 'tiddlywiki-com' 2025-11-11 16:47:36 +00:00
XLBilly
ae79736e82 Deprecate some CSS property macros (#9242)
* Deprecate some CSS property macros

* Update docs

* Remove -ms and -o prefix

* Revert "Update docs"

This reverts commit cdf535054a.

* Update docs

* Update docs

* Update change note

* Update comment
2025-11-11 16:46:02 +00:00
Mario Pietsch
102c236267 Create PMario community card (#9417) 2025-11-11 16:43:59 +00:00
Saq Imtiaz
06a2923adf Adds edit.tiddlywiki.com to infrastructure team responsibilities (#9419) 2025-11-11 16:37:45 +00:00
cdruan
244251ed44 Submit @cdruan change notes (#9421)
For PR #9295 #9341 #9358.
2025-11-11 16:29:26 +00:00
XLBilly
9164f305e0 Remove Opera & Microsoft prefix in browser.js (#9422)
* Remove Opera & Microsoft prefix in browser.js

* Add change notes
2025-11-11 16:28:27 +00:00
lin onetwo
64f86c2187 ci: Uses TiddlyWiki/cerebrus@v5, and replaces obsolete workflow (#9423)
* refactor: use TiddlyWiki/cerebrus@v5, and rename yml

* continue-on-error: false

* Update .github/workflows/pr-validation.yml to restrict contents permissions

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-11 16:13:41 +01:00
Jeremy Ruston
54e5ef7489 Fix link 2025-11-11 11:41:27 +00:00
Jeremy Ruston
2d2ba61949 Revert "Bot to remind of change note (#9407)"
This reverts commit 845f988ab0.
2025-11-11 09:19:57 +00:00
Jeremy Ruston
0bbe170cf0 Merge branch 'tiddlywiki-com' 2025-11-10 10:22:16 +00:00
Jeremy Ruston
6f306d1ed6 Community Team Card for the Developer Experience Team
@pmario this is a starter list of, please do suggest improvements
2025-11-10 10:21:58 +00:00
lin onetwo
845f988ab0 Bot to remind of change note (#9407)
* let claude write this

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* refactor: move logic to sh file
2025-11-10 09:08:29 +00:00
lin onetwo
234667cc31 Add @linonetwo’s missing change notes (#9409)
* docs

(cherry picked from commit eb5d2c50c0e45712eafda0b0e79bd1816b5f8a5b)
(cherry picked from commit eecfd35336964c756547f9ea622566f8bab02709)

* typo

(cherry picked from commit d9129bcaf3f4face613b3f6ccfe06ce6ed45b2d5)
2025-11-09 21:17:29 +00:00
Jeremy Ruston
f1ce35036e Release Notes: Introduce impact notes attached to change notes (#9385)
* Introduce impact notes attached to change notes

No styles yet

* Add more tickets for demo and testing purposes

* Slightly better description for v5.4.0

* Formatting. More to do.

* Embarrassing typo

* Update field names

* Styling refinements

* Disable the #8702 changenote as it is not yet merged

* Add incompleteness warning
2025-11-09 16:05:00 +00:00
Mario Pietsch
5dfdbc8ea0 [DOCS] Slightly improve the substitution documentation (#9357) 2025-11-07 19:27:20 +00:00
Jeremy Ruston
ac7837a98f Happy linter happy life 2025-11-06 22:45:26 +00:00
Jeremy Ruston
eaaa36fb41 Fix typos 2025-11-06 22:43:22 +00:00
Jeremy Ruston
d4684c2811 Merge branch 'master' into let-filter-run-prefix 2025-11-06 22:43:08 +00:00
Jeremy Ruston
3cf71365a5 Fix reference to PNG version of new release banner 2025-11-05 12:11:20 +00:00
Jeremy Ruston
799618d9f5 Somewhat better new prerelease banner
Also converted it to webp

This doesn't usurp the competition we will hold for the final banner.
2025-11-05 12:02:16 +00:00
XLBilly
4d4d9d9995 [DOCS] Add docs about deprecated classes (#9345)
* Add docs about deprecated classes

* Add Core classes tag

* Deprecate `tc-language-*` class

* Update deprecation warning

* 更新 Deprecated Core Classes.tid

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-04 13:03:38 +01:00
XLBilly
19177964c8 Improve switcher (UI) accessibility (#9347)
* Improve theme switcher accessibility

* Improve view switcher accessibility

* Replace set widgets with filters

* Improve languageswitcher & layoutswitcher accessibility

* Add indentation inside list widget

* Switch to functions
2025-11-01 13:06:19 +00:00
cdruan
c8e41bfade Remove redundant code in format/json.js (#9358)
* Remove redundant code in format/json.js

* Revise json.js
2025-11-01 12:56:22 +00:00
Bram Chen
fb4d417629 Update chinese language files (#9375)
* change camel-case hint text for chinese languages
2025-10-30 09:11:36 +01:00
lin onetwo
20d6be1e23 feat: serialize AST node back to wikitext string (#8258)
* refactor: extract a new $tw.wiki.getParser

* feat: allow $tw.utils.getParseTreeText to render other rules' text

* feat: two example getText handler

* Revert "feat: allow $tw.utils.getParseTreeText to render other rules' text"

This reverts commit 8a12498fa9.

* refactor: keep original getParseTreeText not touched

* refactor: use serialize in rules

* refactor: $tw.utils.extend({},options) -> options || {}

* Update codeinline.js

* Create test-wikitext-serialize.js

* DEBUG: only run my tests for development, remove before PR merge

* lint: if

* feat: add rule: 'parseBlock' metadata

* feat: handle tailing \n that may be missing

* feat: allow recursive

* feat: generate more rule and tests

* feat: generate more rule and tests

* fix: remove pragma:true, otherwise following text will become children of it

* fix: condition manually

Deekseek is silly

* fix: some test

* fix: some test

* feat: $tw.utils.serializeAttribute

* fix: use "" for string param

* feat: list

* refactor: ' -> "

* fix: parsemode don't have node

* fix: render invisible comment and parsemode as data element

* feat: add void: true, in ast node to prevent render

* feat: use void widget, so methods always return a widget

* feat: ast to use new widget type void

* test: add rule: 'parseBlock' and isRuleEnd: true

* lint: quote

* Update widget.js

* fix: void node need to handle its children

* Update test-wikitext-parser.js

* lint: quote

* Update void.js

* Update test-wikitext-parser.js

* fix: macrodef with comment (void node) not working

* lint: ' -> "

* feat: add to styleblock

* feat: styleblock

* feat: styleinline

* Update table.js

* lint: useless comments

* feat: transcludeblock

* refactor: reuse block on inline when possible

* feat: use void node to carry important info for typedblock

* feat: run all tests

* lint: useless ai generated comments

* Update conditional.js to not include space

* Update test-wikitext-serialize.js

* Update conditional.js

* refactor: move tiddlers to /data

* refactor: no need for new $tw.Wiki()

* lint: double quote

* refactor: lowercase the parseblock rule name

* fix: Wiki parser initialize blockRuleClasses only when first new an instance

* feat: restore inline macro def

* fix: macro in widget param

* fix: positional attribute in macro call

* fix: table space and horizrule block new line

* feat: make sure block rule all have \n\n for visiblity

* lint: function param

* fix: empty list item

* feat: add \n\n based on isBlock, if could also be inline

* fix: conditional without elseif

* refactor: use isBlock in macrodef to know inline or block

* fix: link may not have attribute and children

* DEBUG: render result and diff below body only on browser

DEBUG: render result below body only on browser

DEBUG: render result below body

DEBUG: fix build

DEBUG: show render result as ViewTemplate

* fix: remove pad space in />

* test: remove pad space in />

* Revert DEBUG: render result and diff below body only on browser

* refactor: fold commentText variable

* refactor: fold long comment

* fix: double quotes for parameter values

* Update void.js

* refactor: move all exports.serialize = function(tree,serialize) { to plugin

* fix: expost listTypes from core, and require it in plugin

* refactor: move serializeWikitextParseTree to plugin and init it

* refactor: move serializeAttribute util also to the plugin

* fix: Delete unused file

* Update macrodef.js

* Update test-wikitext-parser.js

* lint: fix

* Update plugins/tiddlywiki/wikitext-serialize/rules/filteredtranscludeblock.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update core/modules/widgets/void.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update plugins/tiddlywiki/wikitext-serialize/rules/list.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update plugins/tiddlywiki/wikitext-serialize/rules/list.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update plugins/tiddlywiki/wikitext-serialize/rules/styleblock.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove unused methods from VoidNodeWidget

Deleted render, execute, and refresh methods from VoidNodeWidget as they are no longer needed. The widget now only inherits from the base Widget class and exports the constructor.

* docs: about regex in styleinline.js

* Update parsetree.js

* Update core/modules/widgets/void.js

Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>

* feat: Ensure at least one space after the style/class

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>
2025-10-29 21:21:36 +00:00
Saq Imtiaz
7898cb8446 Revert "Fix 8166 html tag validation (#8176)" (#9374)
This reverts commit 9a5f4cc0ef.
2025-10-29 22:01:03 +01:00
Mario Pietsch
9a5f4cc0ef Fix 8166 html tag validation (#8176)
* wip proposal still contains commented old code - tests are missing

* rename isTagNameSafe to makeTagNameSafe

* remove comments

* remove redundant space

* add htmlCustomPrimitives to the $tw.config object

* remove tag-sanitation from element.js

* WIP - add html-element sanitation to the new makeTagNameSafe function, so it can be used globally

* simplify sanitation logic and fix inline docs

* Move top comment into inline comments
2025-10-29 17:42:28 +00:00
s793016
cda8d7ca8c Aho-Corasick Freelinks Enhancement for Large Wikis and Non-Latin Titles (#9084)
* Enhance Freelinks with Aho-Corasick for long titles and large wikis

Replaces regex with Aho-Corasick, adds chunking (100 titles/chunk), cache toggle, and Chinese full-width symbol support. Tested with 11,714 tiddlers.

* delete comment

* Create AhoCorasick.js

* Update text.js

* Update text.js

* Update AhoCorasick.js

* Update text.js

* Update text.js

* move AhoCorasick to AhoCorasick.js

* update AhoCorasick.js

* Delete core/modules/utils/AhoCorasick.js

wrong place

* Update text.js

* indentation modify

* remove function {}

* remove function {}

* Rename AhoCorasick.js to aho-corasick.js

correct filename

* Update tiddlywiki.info add freelink

* missing a comma here

* clean up comments & use old style

* try add it to editions/tw5.com-server for testing

* try add it to editions/prerelease for testing

* optimized

* optimized

* add setting for "Persist AhoCorasick cache"

* add  dynamic limits

* remove comment

* revert to 5f0b98d1fd

* try sort alphabet

* try sort alphabet

* try sort alphabet

* typo freelink -> freelinks

* typo freelink -> freelinks

* typo freelink -> freelinks

* Update readme.tid

* Update aho-corasick.js

Dynamically adjust limit parameters to avoid problems caused by hard-coded limits.

* Update text.js

Dynamically adjust limit parameters to avoid problems caused by hard-coded limits.

* Update tiddlywiki.info

remove other plugin for test plugin conflict

* Update tiddlywiki.info

* Update tiddlywiki.info

* Update aho-corasick.js

Description of major changes

Improve state transition logic - Ensure to go back to root node correctly in case of mismatch, and check root node for current character transition 
Fix failed link traversal - Add condition node ! == this.trie to avoid infinite loop at root node 
Enhance output collection - collect output not only from current node, but also from all nodes on failed link path, which is key to Aho-Corasick algorithm 
Add safety limit - collectCount < 10 to prevent failed link loops

Translated with DeepL.com (free version)

* Update aho-corasick.js

Word Boundary Check - The isWordBoundaryMatch function checks if the match is on a word boundary:

Alphanumeric characters [a-zA-Z0-9_] are regarded as unicode characters 
At least one non-unicode character must be present before and after the match for it to be considered valid.

* Update text.js

Word Boundary Check - The isWordBoundaryMatch function checks if the match is on a word boundary:

Alphanumeric characters [a-zA-Z0-9_] are regarded as unicode characters 
At least one non-unicode character must be present before and after the match for it to be considered valid.

* Update settings.tid

Word Boundary Check - The isWordBoundaryMatch function checks if the match is on a word boundary:

Alphanumeric characters [a-zA-Z0-9_] are regarded as unicode characters 
At least one non-unicode character must be present before and after the match for it to be considered valid.

* fix Word Boundary logic

* remove PersistentCache @ text.js

* remove PersistentCache @settings.tid

* Update readme.tid for Word Boundary Check

* Update aho-corasick.js Organize and delete comments

* Initial commit of freelinks plugin

* Update settings.tid Organize and delete comments

* Update tiddlywiki.info add back other plugin

* Update tiddlywiki.info alphabet sort

* Update readme.tid for new future

The plugin supports non-Western language tiddler titles (e.g., Chinese) and prioritizes longer tiddler titles for matching, ensuring accurate linking in diverse contexts. 

Furthermore, the current tiddler title within its own content is excluded from generating links to avoid self-referencing.

* Update readme.tid

* Update plugins/tiddlywiki/freelinks/text.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

* Update plugins/tiddlywiki/freelinks/aho-corasick.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

* Update plugins/tiddlywiki/freelinks/aho-corasick.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

* Update plugins/tiddlywiki/freelinks/text.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

* Update plugins/tiddlywiki/freelinks/aho-corasick.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

* Update text.js

Added locale configuration support - Added LOCALE_CONFIG_TIDDLER constant to make the sorting locale configurable instead of hardcoded "zh"
Optimized title processing - Combined the filtering and escaping logic into a single pass to reduce duplication
Added trim() for ignoreCase - Applied .trim() to the ignore case variable for consistency
Enhanced refresh logic - Added locale configuration tiddler to the refresh check
Improved comments - Added explanation for why sorting is necessary (prioritizing longer titles)

* Update text.js

we don't need to specify 'zh' at all

* Update aho-corasick.js

This single line change would add support for:

Accented letters: á, é, í, ó, ú, à, è, ì, ò, ù, ä, ë, ï, ö, ü, ñ, ç, etc.
Most Western European languages (Spanish, French, German, Italian, Portuguese, etc.)

* Update aho-corasick.js useage

* Update readme.tid for Writing Style

* Update tiddlywiki.info

revert all the changes

* Update tiddlywiki.info

revert all the changes

* Update tiddlywiki.info

revert all the changes

* Update tiddlywiki.info

revert

* Update text.js

plugins/tiddlywiki/freelinks/text.js#L25
[ESLint PR code] reported by reviewdog 🐶
Strings must use doublequote.

* Update aho-corasick.js

plugins/tiddlywiki/freelinks/aho-corasick.js#L193
[ESLint PR code] reported by reviewdog 🐶
Strings must use doublequote.

Raw Output:
{"ruleId":"@stylistic/quotes","severity":2,"message":"Strings must use doublequote.","line":193,"column":50,"nodeType":"Literal","messageId":"wrongQuotes","endLine":193,"endColumn":52,"fix":{"range":[5743,5745],"text":"\"\""}}

---------

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>
2025-10-29 17:41:35 +00:00
Jeremy Ruston
f38e9f0822 Introduce browser messaging saver (#5512)
* First commit

* Subscriber: Make onmessage be async

* Don't use a variable before it's defined...
2025-10-29 14:52:21 +00:00
Jeremy Ruston
f2b60d4803 Docs updates 2025-04-30 17:03:40 +01:00
Jeremy Ruston
f371a59c75 Merge branch 'master' into let-filter-run-prefix 2025-04-30 17:03:00 +01:00
Jeremy Ruston
c6b499af98 Update action-log widget to log multi-valued attributes 2025-03-30 21:44:43 +01:00
Jeremy Ruston
f3f232e634 Remove duplicate test 2025-03-30 19:42:39 +01:00
Jeremy Ruston
2c50345653 Extend title operator to allow negated form to use multi-valued variables 2025-03-29 15:35:31 +00:00
Jeremy Ruston
9453e3e2e2 Simplify title operator 2025-03-29 14:45:35 +00:00
Jeremy Ruston
d20b02e606 Allow functions to take multivalued parameters 2025-03-29 14:13:23 +00:00
Jeremy Ruston
22eea56b53 Fix bug with round brackets for 2nd parameter onwards 2025-03-29 12:50:43 +00:00
Jeremy Ruston
e8a2b3eefa Merge branch 'master' into let-filter-run-prefix 2025-03-29 12:32:27 +00:00
Jeremy Ruston
b6a171cf5e Fix bug with missing variable attributes
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2752792329
2025-03-26 20:32:35 +00:00
Jeremy Ruston
f15e9e309e Merge branch 'master' into let-filter-run-prefix 2025-03-21 17:23:27 +00:00
Jeremy Ruston
f914aa6605 Fix bug exposed in "Filter Operators" tiddler
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2740003414
2025-03-20 17:46:12 +00:00
Jeremy Ruston
bb5061c6ff Introduce => as a shortcut syntax for the let filter run prefix
Also relax the requirement for a filter run prefix to be followed by an opening square bracket
2025-03-20 09:29:15 +00:00
Jeremy Ruston
0c24e2f5f1 Introduce round brackets for multi-valued filter operands
Allowing us to drop the varlist operator
2025-03-19 16:14:54 +00:00
Jeremy Ruston
938e72dc34 Bug fix 2025-03-19 11:00:03 +00:00
Jeremy Ruston
7640135a7e Docs update 2025-03-18 16:29:11 +00:00
Jeremy Ruston
ac134ac549 Missed off 211b135265 2025-03-18 15:54:51 +00:00
Jeremy Ruston
211b135265 Refactor the let filter run prefix to assign the input list to the variable named by the filter run
These semantics are much simpler, and allow the variable name to be computed.
2025-03-18 15:53:28 +00:00
Jeremy Ruston
0db188f365 Dispense with the letlist widget
What this PR actually does is rename the letlist widget to "let". The result is the same as using the letlist widget, but it is backwards compatible by virtue of the fact that all existing ways to access variables will only see the single value for the variable.
2025-03-18 12:46:31 +00:00
Jeremy Ruston
a31df85894 varlist operator: fixed crash accessing non-existent variable
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2712068743
2025-03-11 13:59:19 +00:00
Jeremy Ruston
d69ecf4639 Test for multivalued functions 2025-03-11 13:30:17 +00:00
Jeremy Ruston
efce6731e1 Docs update 2025-03-11 12:49:40 +00:00
Jeremy Ruston
a9a9a745d4 Switch to triple brace syntax for assigning filtered lists 2025-03-11 12:47:01 +00:00
Jeremy Ruston
4cfa758d51 Fixed varlist crash with empty parameter 2025-03-10 15:31:28 +00:00
Jeremy Ruston
c5ad429493 Update DefaultTiddlers to highlight the new docs 2025-03-10 12:45:10 +00:00
Jeremy Ruston
76e1c2124b Docs update 2025-03-09 18:21:36 +00:00
Jeremy Ruston
78a7aedd4b Introduce letlist widget for assigning lists to variables
Terrible name. Annoyingly, we can't overload the existing let or set widgets.
2025-03-09 17:50:42 +00:00
Jeremy Ruston
2a1542c4e5 Docs update 2025-03-09 17:01:31 +00:00
Jeremy Ruston
7397f4fa3a Support for saving result lists in a variable
Extend let filter run prefix to store list of results, and add varlist operator for accessing variables as a list.

We already had partial support for variables returning a list of values in order for functions to work, now we extend it so that any variable can be used to store a list

We should extend the set widget so that it returns a result list that can be accessed with the varlist operator
2025-03-09 16:50:48 +00:00
Jeremy Ruston
e7b713c277 Cleanup 2025-03-09 14:56:15 +00:00
Jeremy Ruston
8e9b30fec8 Fix tests 2025-03-08 09:05:59 +00:00
Jeremy Ruston
db1ed0e66b Fix tests 2025-03-08 09:00:32 +00:00
Jeremy Ruston
1acef48a10 Get rid of the special behaviour for all[]
Not needed because the input to the filter run is available
2025-03-08 08:56:12 +00:00
Jeremy Ruston
b8bdb0aeeb Introduce let filter run prefix for assigning filter run result to a variable 2025-03-07 21:48:36 +00:00
267 changed files with 4182 additions and 729 deletions

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: build-size-check
id: get_sizes
uses: TiddlyWiki/cerebrus@v4
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Build and check size
uses: TiddlyWiki/cerebrus@v4
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ inputs.pr_number }}
repo: ${{ github.repository }}

View File

@@ -1,18 +0,0 @@
name: Validate PR Paths
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Validate PR
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}

37
.github/workflows/pr-validation.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: PR Validation
on:
pull_request_target:
types: [opened, reopened, synchronize]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
# Step 1: Validate PR paths
- name: Validate PR Paths
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: rules
continue-on-error: true
# Step 2: Validate change notes
- name: Validate Change Notes
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: changenotes
continue-on-error: false

View File

@@ -0,0 +1,25 @@
avatar: UklGRiwIAABXRUJQVlA4WAoAAAAwAAAAPwAAPwAASUNDUCACAAAAAAIgbGNtcwRAAABtbnRyR1JBWVhZWiAH6QALAAoACwADAAZhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZXNjAAAAzAAAAG5jcHJ0AAABPAAAADZ3dHB0AAABdAAAABRrVFJDAAABiAAAACBkbW5kAAABqAAAACRkbWRkAAABzAAAAFJtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFIAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA81EAAQAAAAEWzHBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbbWx1YwAAAAAAAAABAAAADGVuVVMAAAAIAAAAHABHAEkATQBQbWx1YwAAAAAAAAABAAAADGVuVVMAAAA2AAAAHABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAVlA4TOYFAAAvP8APEDWGgbRtWv+yt/0WImICOBvWn1C4dFi1bStbvpY8Qg2ePANNNAMh3N2db/7A91/7CHBvBBRr25ZFH+4k98ihkqi2CP4tsANvX8a+8y8Ct04dn0nuUt39ZiBJkowqt911M+MJ1G3bNiZJr1iP0DZ+2bbdadsqprOjAqmoUIX9hf3Fl5/uPYV7I3OMeoFzIvrvwG0kRUr3zPLdYMMXaqrMMsp0K4fufKO6c2hFV5Zh7kRROZX0PSCmB/3KWQwpuiekWelSRZDW94d0q750NrxavpFn1eLNQ9EV8nWlmAET6Q8lrCRTcjFLlLImluK3iXJW/hT47KGklS8OlzWUtXLFYDRCSS74ojUjxggqKMoxd6A1lTCyvsvyzC5/d7BsCHb7yIcHyrX2yR/NPnsAdRT2i0Pwp/o0Il6ix8hsRAuJmQgcr4KREfAiMgUVm9KqmfSxL5pOJspVwwTiV6jiIAg1RMhHpERhbvwgGI34Hc49T7UeKZtXwEqJ+BAaoBneperJH0POs1u4dufwv8Gf+qcOfjyvX6ZIVgxE0Rw87YF3BSc9c7jsXfdjOBG7FwmSb39pfGRwu8IuvUjJNoTpFzkEvDg6W3Qt/9nf99ZXPy8HM43IweTKyNR+WVatXcWWyakBksj9cqW+QetplcjsKElvZH/zuOO/PrCx//tL3/6x/O/C1PZZvSKuulLcS4l8M1ewGPR6ef5sllXW2eGQZ7hVSEZiPmcqrSS8e2ElX8o7t1fvB9LFetmEx5hx1Xuye2PpfjZnSjj7QfKTB3bZZo05Zvh6YuivX24cpc8+ddvADWG9odrSwFalVurxUiidDHmTiaoNkkh2gjbcpxMiAbd39aVP119/N9k4+euNKfcNjwaPhZEuUupUsJrHchw1LkPrRC9bQKa3M8Mj/xx903drdnHMpbirj1ENsUre0oo3N+7gat+2ZctKdsIUYc21sRu+Ucdhn+P7DyarftW00iu3Tmbv+hTfdCTmyaIPT4PrYZDFtBN2W8S9m4oTB5Z2P3Oe7weKjVBq86kXX/r0+WuvTAzfjqm1hsYRPWlbxm4n3IaeGOJEizv8orH9w5ejjmSrfOuEq/HxT6eDemtsZ/HTvvG1/8iVspxZILrlkz/cdsIbIroOgJileFSty2xiHNW5t9fbHJ3ze87bp5T9vc8RuqMB0ReDSt464R/BJxspvgpEsrVAJMTsYg2QovPTOHrvQ9et/S2Xx+40z7dY4JBX0Pz/ElH/T73U2DkK8EiqC9hM/zV3frQfzjaAqO16s1l6xCUXnBFlYxyIer3eEdth7u5xsHKxWoGLqzY3wIULt9G3K6soei9jZ+UcF+Ka3M/II9EUWrJ/LLxy+Q9xIh0vOl3NZCrVnBsuFUTOSnJnSioRWZ9q4g+ZDk5XVORoW2qX2hbIkna3JOrdR3jmpHVLovUkLES6grRO010u0GkDlX7SpH1DQ64Wl2zaSUJv1Mtti2G7kx5IyftWMhfDlGClcxvIUhP5crhp9LIb1Vne187oSAWxelcR/kXjYQTZboW+Oj1pqF0gmfZhSDD6bSgzGWrw3s7QLNtCV+2uatYrd/aFtjDI8R52e/DdyKgRKXBhEak3Ev50+GCUA9EFUor39htVMxmWvW8AM6ptG416rZvdWn+MarIEyH5r6ruZSrx8XrWDP370vbfTjqpmZGIbiFPFoihc4jcrlYi9p3ndSuymZ+XLaKza/P/HUWHn5Axdkd9OjBskY0+pIlz4AlFPFs+aStK5PBIRR4MVVJDihsy4JdEA4pVcrVqMZDyL2/8aYocikEAR9Xjc1BNG9zEiJG7n/cGyrtnblkClBhEgMW4Kx21BEBGJjLa0hcOGmTK64KsKLfKr9QyQELclxY3hqowTIZKdZNTSS5BWiBPlKxDWBVSS41bOepkhTkhGDajLfLyUBOKlkMHPgOhx3JoRN/cEiRgSWdgF2yCyDQu4IcbNo8ftTzxveOJ5y+h509h52+h549h569h587/M20f/b1AB
created: 20251110102157310
first-sighting: 2009-11-14
fullname: Mario Pietsch
github: pmario
homepage: https://wikilabs.github.io/
modified: 20251110124935183
tags: Community/Person Community/Team/Contributors
talk.tiddlywiki.org: pmario
title: @pmario
type: text/vnd.tiddlywiki
youtube: https://www.youtube.com/@pmario
''Hi, My name is Mario Pietsch''. Back in 2009 I was ''searching'' for ''a simple presentation tool'' and discovered ~TiddlyWiki Classic, Monkey Pirate ~TiddlyWiki ([[MPTW|https://mptw.tiddlyspot.com/]]) with ~TagglyTagging, Eric Shulman's ~TiddlyTools, Saq Imtiaz's navigation macros, and more. --- ''I was captivated''.
After a deep dive, I combined these elements into my own "Presentation Manager", along [[3 step by step tutorials|https://groups.google.com/g/tiddlywiki/c/qG_tZ1x0MEU/m/-vLA0luMicYJ]] to help others build it.
Thanks to ''the positive spirit'' of the ~TiddlyWiki community, I am proud to be part of it since 2009.
When Jeremy started developing ~TiddlyWiki 5 on ~GitHub, I joined in—opening [[issue no. 1|https://github.com/TiddlyWiki/TiddlyWiki5/issues/1]] all the way up to 13. For what thats good ;) Since then, I have submitted nearly 600 pull requests and more than 500 issues, many of which have been merged or resolved.
My ~TiddlyWiki 5 "laboratory" is at https://wikilabs.github.io, and I also share content on my ''~YouTube'' channel: https://www.youtube.com/@pmario
Have fun!<br>
Mario

View File

@@ -0,0 +1,19 @@
title: Developer Experience Team
tags: Community/Team
modified: 20251109200632671
created: 20251109200632671
leader: @pmario
team: @saqimtiaz
The Developer Experience Team improves the experience of software contributors to the TiddlyWiki project. This includes enhancing documentation, streamlining contribution processes, and providing tools and resources to help developers effectively contribute to TiddlyWiki.
Tools and resources managed by the Developer Experience Team include:
* Advising and assisting contributors, particularly new developers
* Maintenance of developer-focused documentation on the https://tiddlywiki.com/dev/ site, including:
** Development environment setup guides
** Code review processes and best practices
** Contribution guidelines and documentation
* Continuous integration and deployment scripts providing feedback on pull requests
* Devising and implementing labelling systems for issues and pull requests
* Automation scripts to simplify common development tasks

View File

@@ -1,8 +1,8 @@
title: Infrastructure Team
tags: Community/Team
modified: 20250909171928024
created: 20250909171928024
modified: 20251110133437795
tags: Community/Team
team: @MotovunJack
title: Infrastructure Team
The Infrastructure Team is responsible for maintaining and improving the infrastructure that supports the TiddlyWiki project. This includes the hosting, deployment, and management of the TiddlyWiki websites and services, as well as the tools and systems used by the TiddlyWiki community.
@@ -12,3 +12,4 @@ The infrastructure includes:
* github.com/TiddlyWiki
* tiddlywiki.com DNS
* Netlify account for PR previews
* edit.tiddlywiki.com

View File

@@ -0,0 +1,41 @@
/*\
title: $:/core/modules/filterrunprefixes/let.js
type: application/javascript
module-type: filterrunprefix
Assign a value to a variable
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter prefix function
*/
exports.let = function(operationSubFunction,options) {
// Return the filter run prefix function
return function(results,source,widget) {
// Save the result list
var resultList = results.toArray();
// Clear the results
results.clear();
// Evaluate the subfunction to get the variable name
var subFunctionResults = operationSubFunction(source,widget);
if(subFunctionResults.length === 0) {
return;
}
var name = subFunctionResults[0];
if(typeof name !== "string" || name.length === 0) {
return;
}
// Assign the result of the subfunction to the variable
var variables = {};
variables[name] = resultList;
// Return the variables
return {
variables: variables
};
};
};

View File

@@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
operator.prefix = filterString.charAt(p++);
}
// Get the operator name
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
if(nextBracketPos === -1) {
throw "Missing [ in filter expression";
}
@@ -79,6 +79,10 @@ function parseFilterOperation(operators,filterString,p) {
operand.variable = true;
nextBracketPos = filterString.indexOf(">",p);
break;
case "(": // Round brackets
operand.multiValuedVariable = true;
nextBracketPos = filterString.indexOf(")",p);
break;
case "/": // regexp brackets
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
rexMatch = rex.exec(filterString.substring(p));
@@ -112,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
// Check for multiple operands
while(filterString.charAt(p) === ",") {
p++;
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
nextBracketPos = p;
p++;
parseOperand(filterString.charAt(nextBracketPos));
@@ -141,7 +145,15 @@ exports.parseFilter = function(filterString) {
p = 0, // Current position in the filter string
match;
var whitespaceRegExp = /(\s+)/mg,
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
// Groups:
// 1 - entire filter run prefix
// 2 - filter run prefix itself
// 3 - filter run prefix suffixes
// 4 - opening square bracket following filter run prefix
// 5 - double quoted string following filter run prefix
// 6 - single quoted string following filter run prefix
// 7 - anything except for whitespace and square brackets
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
while(p < filterString.length) {
// Skip any whitespace
whitespaceRegExp.lastIndex = p;
@@ -152,38 +164,45 @@ exports.parseFilter = function(filterString) {
// Match the start of the operation
if(p < filterString.length) {
operandRegExp.lastIndex = p;
match = operandRegExp.exec(filterString);
if(!match || match.index !== p) {
throw $tw.language.getString("Error/FilterSyntax");
}
var operation = {
prefix: "",
operators: []
};
if(match[1]) {
operation.prefix = match[1];
p = p + operation.prefix.length;
if(match[2]) {
operation.namedPrefix = match[2];
}
if(match[3]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
match = operandRegExp.exec(filterString);
if(match && match.index === p) {
// If there is a filter run prefix
if(match[1]) {
operation.prefix = match[1];
p = p + operation.prefix.length;
// Name for named prefixes
if(match[2]) {
operation.namedPrefix = match[2];
}
// Suffixes for filter run prefix
if(match[3]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
});
});
});
}
}
// Opening square bracket
if(match[4]) {
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
}
}
if(match[4]) { // Opening square bracket
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
// No filter run prefix
p = parseFilterOperation(operation.operators,filterString,p);
}
// Quoted strings and unquoted title
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
operation.operators.push(
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
@@ -251,6 +270,8 @@ exports.compileFilter = function(filterString) {
results = [];
$tw.utils.each(operation.operators,function(operator) {
var operands = [],
multiValueOperands = [],
isMultiValueOperand = [],
operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
@@ -266,13 +287,29 @@ exports.compileFilter = function(filterString) {
if(operand.indirect) {
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
operand.multiValue = [operand.value];
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
operand.multiValue = [operand.value];
} else if(operand.multiValuedVariable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
operand.value = operand.multiValue[0] || "";
} else {
operand.value = "";
operand.multiValue = [];
}
operand.isMultiValueOperand = true;
} else {
operand.value = operand.text;
operand.multiValue = [operand.value];
}
operands.push(operand.value);
multiValueOperands.push(operand.multiValue);
isMultiValueOperand.push(!!operand.isMultiValueOperand);
});
// Invoke the appropriate filteroperator module
@@ -280,6 +317,8 @@ exports.compileFilter = function(filterString) {
operator: operator.operator,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
multiValueOperands: multiValueOperands,
isMultiValueOperand: isMultiValueOperand,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
@@ -319,6 +358,8 @@ exports.compileFilter = function(filterString) {
return filterRunPrefixes["and"](operationSubFunction, options);
case "~": // This operation is unioned into the result only if the main result so far is empty
return filterRunPrefixes["else"](operationSubFunction, options);
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
return filterRunPrefixes["let"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
@@ -345,7 +386,13 @@ exports.compileFilter = function(filterString) {
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
$tw.utils.each(operationFunctions,function(operationFunction) {
operationFunction(results,source,widget);
var operationResult = operationFunction(results,source,widget);
if(operationResult) {
if(operationResult.variables) {
// If the filter run prefix has returned variables, create a new fake widget with those variables
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
}
}
});
} else {
results.push("/**-- Excessive filter recursion --**/");

View File

@@ -16,12 +16,8 @@ exports.json = function(source,operand,options) {
spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand;
}
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title);
try {
data = JSON.parse(title);
} catch(e) {
data = undefined;
}
var data = $tw.utils.parseJSONSafe(title,function(){return undefined;});
if(data !== undefined) {
results.push(JSON.stringify(data,null,spaces));
}

View File

@@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
params = [],
results;
$tw.utils.each(operator.operands.slice(1),function(param) {
params.push({value: param});
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
});
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});

View File

@@ -113,6 +113,22 @@ exports["jsonset"] = function(source,operator,options) {
return results;
};
exports["jsondelete"] = function(source,operator,options) {
var indexes = operator.operands,
results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
// If parsing failed (data equals original title and is a string), return unchanged
if(data === title && typeof data === "string") {
results.push(title);
} else if(data) {
data = deleteDataItem(data,indexes);
results.push(JSON.stringify(data));
}
});
return results;
};
/*
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
@@ -144,7 +160,7 @@ function convertDataItemValueToStrings(item) {
return ["null"]
} else if(typeof item === "object") {
var results = [],i,t;
if($tw.utils.isArray(item)) {
if(Array.isArray(item)) {
// Return all the items in arrays recursively
for(i=0; i<item.length; i++) {
t = convertDataItemValueToStrings(item[i])
@@ -178,7 +194,7 @@ function convertDataItemKeysToStrings(item) {
return [];
}
var results = [];
if($tw.utils.isArray(item)) {
if(Array.isArray(item)) {
for(var i=0; i<item.length; i++) {
results.push(i.toString());
}
@@ -201,7 +217,7 @@ function getDataItemType(data,indexes) {
return item;
} else if(item === null) {
return "null";
} else if($tw.utils.isArray(item)) {
} else if(Array.isArray(item)) {
return "array";
} else if(typeof item === "object") {
return "object";
@@ -213,7 +229,7 @@ function getDataItemType(data,indexes) {
function getItemAtIndex(item,index) {
if($tw.utils.hop(item,index)) {
return item[index];
} else if($tw.utils.isArray(item)) {
} else if(Array.isArray(item)) {
index = $tw.utils.parseInt(index);
if(index < 0) { index = index + item.length };
return item[index]; // Will be undefined if index was out-of-bounds
@@ -223,15 +239,16 @@ function getItemAtIndex(item,index) {
}
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
Traverse the index chain and return the item at the specified depth.
Returns the item at the end of the traversal, or undefined if traversal fails.
*/
function getDataItem(data,indexes) {
function traverseIndexChain(data,indexes,stopBeforeLast) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Get the item
var item = data;
for(var i=0; i<indexes.length; i++) {
var stopIndex = stopBeforeLast ? indexes.length - 1 : indexes.length;
for(var i = 0; i < stopIndex; i++) {
if(item !== undefined) {
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
item = getItemAtIndex(item,indexes[i]);
@@ -243,6 +260,13 @@ function getDataItem(data,indexes) {
return item;
}
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
function getDataItem(data,indexes) {
return traverseIndexChain(data,indexes,false);
}
/*
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
*/
@@ -255,18 +279,15 @@ function setDataItem(data,indexes,value) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return value;
}
// Traverse the JSON data structure using the index chain
var current = data;
for(var i = 0; i < indexes.length - 1; i++) {
current = getItemAtIndex(current,indexes[i]);
if(current === undefined) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Add the value to the end of the index chain
var lastIndex = indexes[indexes.length - 1];
if($tw.utils.isArray(current)) {
if(Array.isArray(current)) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
}
@@ -276,3 +297,32 @@ function setDataItem(data,indexes,value) {
}
return data;
}
/*
Given a JSON data structure and an array of index strings, return the data structure with the item at the end of the index chain deleted. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then the JSON data structure is returned unmodified.
*/
function deleteDataItem(data,indexes) {
// Check for the root item - don't delete the root
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined || current === null) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Delete the item at the end of the index chain
var lastIndex = indexes[indexes.length - 1];
if(Array.isArray(current) && current !== null) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
// Check if index is valid before splicing
if(lastIndex >= 0 && lastIndex < current.length) {
current.splice(lastIndex,1);
}
} else if(typeof current === "object" && current !== null) {
delete current[lastIndex];
}
return data;
}

View File

@@ -16,12 +16,13 @@ exports.title = function(source,operator,options) {
var results = [];
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(tiddler && tiddler.fields.title !== operator.operand) {
var titleList = operator.multiValueOperands[0] || [];
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
results.push(title);
}
});
} else {
results.push(operator.operand);
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
}
return results;
};

View File

@@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator
if(operator.operator.indexOf(".") !== -1) {
var params = [];
$tw.utils.each(operator.operands,function(param) {
params.push({value: param});
$tw.utils.each(operator.multiValueOperands,function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
});
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
if(variableInfo && variableInfo.srcVariable) {

View File

@@ -11,10 +11,13 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
var CsvParser = function(type,text,options) {
// Special handler for tab-delimited files
if (type === 'text/tab-delimited-values' && !options.separator) {
if(
!options.separator &&
(type === "text/tab-delimited-values" || type === "text/tab-separated-values")
) {
options.separator = "\t";
}
// Table framework
this.tree = [{
"type": "scrollable", "children": [{
@@ -32,7 +35,7 @@ var CsvParser = function(type,text,options) {
$tw.utils.each(lines, function(columns) {
maxColumns = Math.max(columns.length, maxColumns);
});
for(var line=0; line<lines.length; line++) {
var columns = lines[line];
var row = {
@@ -55,3 +58,4 @@ var CsvParser = function(type,text,options) {
exports["text/csv"] = CsvParser;
exports["text/tab-delimited-values"] = CsvParser;
exports["text/tab-separated-values"] = CsvParser;

View File

@@ -22,7 +22,7 @@ Note that the syntax for comments is simplified to an opening "<!--" sequence an
"use strict";
exports.name = "commentblock";
exports.types = {block:true, pragma:true};
exports.types = {block: true, pragma: true};
exports.init = function(parser) {
this.parser = parser;
@@ -43,9 +43,18 @@ exports.findNextMatch = function(startPos) {
return undefined;
};
exports.parse = function() {
// Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex;
// Don't return any elements
return [];
// Return a node representing the comment that is not rendered
var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
children: [],
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
};

View File

@@ -40,6 +40,13 @@ exports.findNextMatch = function(startPos) {
exports.parse = function() {
// Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex;
// Don't return any elements
return [];
// Return a node representing the inline comment
var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
};

View File

@@ -28,11 +28,11 @@ exports.init = function(parser) {
exports.parse = function() {
// Move past the match
var start = this.parser.pos;
var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// Create the link unless it is suppressed
if(this.match[0].substr(0,1) === "~") {
return [{type: "text", text: this.match[0].substr(1)}];
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}];
} else {
return [{
type: "element",

View File

@@ -6,15 +6,15 @@ module-type: wikirule
Wiki pragma rule for function, procedure and widget definitions
```
\function name(param:defaultvalue,param2:defaultvalue)
\function name(param:"defaultvalue", param2:"defaultvalue")
definition text
\end
\procedure name(param:defaultvalue,param2:defaultvalue)
\procedure name(param:"defaultvalue", param2:"defaultvalue")
definition text
\end
\widget $mywidget(param:defaultvalue,param2:defaultvalue)
\widget $mywidget(param:"defaultvalue", param2:"defaultvalue")
definition text
\end
```

View File

@@ -50,6 +50,8 @@ exports.parse = function() {
}
}
} while(match && !match[1]);
// Return the nodes
// Mark first and last node, and return the nodes
if(tree[0]) tree[0].isRuleStart = true;
if(tree[tree.length-1]) tree[tree.length-1].isRuleEnd = true;
return tree;
};

View File

@@ -41,7 +41,7 @@ Parse the most recent match
exports.parse = function() {
// Retrieve the most recent match so that recursive calls don't overwrite it
var tag = this.nextTag;
if (!tag.isSelfClosing) {
if(!tag.isSelfClosing) {
tag.openTagStart = tag.start;
tag.openTagEnd = tag.end;
}
@@ -63,22 +63,22 @@ exports.parse = function() {
}
tag.end = this.parser.pos;
tag.closeTagEnd = tag.end;
if (tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== '>') {
if(tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== ">") {
tag.closeTagStart = tag.end;
} else {
tag.closeTagStart = tag.closeTagEnd - 2;
var closeTagMinPos = tag.children.length > 0 ? tag.children[tag.children.length-1].end : tag.openTagEnd;
if (!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
while (tag.closeTagStart >= closeTagMinPos) {
if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
while(tag.closeTagStart >= closeTagMinPos) {
var char = this.parser.source[tag.closeTagStart];
if (char === '>') {
if(char === ">") {
tag.closeTagStart = -1;
break;
}
if (char === '<') break;
if(char === "<") break;
tag.closeTagStart -= 1;
}
if (tag.closeTagStart < closeTagMinPos) {
if(tag.closeTagStart < closeTagMinPos) {
tag.closeTagStart = tag.end;
}
}

View File

@@ -59,6 +59,7 @@ var listTypes = {
":": {listTag: "dl", itemTag: "dd"},
">": {listTag: "blockquote", itemTag: "div"}
};
exports.listTypes = listTypes;
/*
Parse the most recent match

View File

@@ -29,7 +29,7 @@ exports.findNextMatch = function(startPos) {
var c = this.parser.source.charAt(nextCall.end);
// Ensure EOL after parsed macro
// If we didn't need to support IE, we'd just use /(?:\r?\n|$)/ym
if ((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
if((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
this.nextCall = nextCall;
return nextStart;
}

View File

@@ -42,3 +42,5 @@ exports.parse = function() {
this.parser.pos = call.end;
return [call];
};

View File

@@ -52,9 +52,10 @@ exports.parse = function() {
}
}
// Is the remainder of the \define line blank after the parameter close paren?
var reEnd;
var reEnd,isBlock = true;
if(this.match[3]) {
// If so, it is a multiline definition and the end of the body is marked with \end
isBlock = false;
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?\\s*?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
@@ -79,7 +80,8 @@ exports.parse = function() {
attributes: {},
children: [],
params: params,
isMacroDefinition: true
isMacroDefinition: true,
isBlock: isBlock && !!endMatch
}];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);

View File

@@ -31,6 +31,7 @@ Parse the most recent match
*/
exports.parse = function() {
// Move past the pragma invocation
var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex;
// Parse whitespace delimited tokens terminated by a line break
var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg,
@@ -58,6 +59,11 @@ exports.parse = function() {
this.parser.parseAsInline = true;
}
}
// No parse tree nodes to return
return [];
return [{
type: "void",
children: [],
parseAsInline: this.parser.parseAsInline,
start: start,
end: this.parser.pos
}];
};

View File

@@ -113,3 +113,5 @@ exports.parseLink = function(source,pos) {
node.end = closePos + 2;
return node;
};

View File

@@ -32,7 +32,7 @@ exports.parse = function() {
var text = this.match[1],
link = this.match[2] || text,
textEndPos = this.parser.source.indexOf("|", start);
if (textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
if(textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
textEndPos = this.matchRegExp.lastIndex - 2;
}
var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start;

View File

@@ -54,6 +54,13 @@ exports.parse = function() {
if(tokens.length > 0) {
this.parser.amendRules(tokens[0],tokens.slice(1));
}
// No parse tree nodes to return
return [];
// No widget to render, return void node.
return [{
type: "void",
attributes: {
action: {type: "string", value: tokens[0]},
rules: {type: "string", value: tokens.slice(1).join(" ")}
},
children: []
}];
};

View File

@@ -64,5 +64,8 @@ exports.parse = function() {
$tw.utils.addAttributeToParseTreeNode(tree[t],"style",styles.join(""));
}
}
return tree;
return [{
type: "void",
children: tree
}]
};

View File

@@ -21,7 +21,7 @@ exports.types = {inline: true};
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
// Regexp to match /@@(styles)?\s*(\.class\s+)?/
this.matchRegExp = /@@((?:[^\.\r\n\s:]+:[^\r\n;]+;)+)?(\.(?:[^\r\n\s]+)\s+)?/mg;
};

View File

@@ -60,22 +60,37 @@ exports.parse = function() {
var parser = this.parser.wiki.parseText(parseType,text,{defaultType: "text/plain"});
// If there's no render type, just return the parse tree
if(!renderType) {
return parser.tree;
return [{
type: "void",
children: $tw.utils.isArray(parser.tree) ? parser.tree : [parser.tree],
parseType: parseType,
renderType: renderType,
text: text,
start: start,
end: this.parser.pos
}];
} else {
// Otherwise, render to the rendertype and return in a <PRE> tag
var widgetNode = this.parser.wiki.makeWidget(parser),
container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null);
text = renderType === "text/html" ? container.innerHTML : container.textContent;
var renderResult = renderType === "text/html" ? container.innerHTML : container.textContent;
// Use void node to carry important info for typedblock
return [{
type: "element",
tag: "pre",
type: "void",
children: [{
type: "text",
text: text,
start: start,
end: this.parser.pos
}]
type: "element",
tag: "pre",
children: [{
type: "text",
text: renderResult,
}]
}],
parseType: parseType,
renderType: renderType,
text: text,
start: start,
end: this.parser.pos
}];
}
};

View File

@@ -215,8 +215,8 @@ WikiParser.prototype.parsePragmas = function() {
var subTree = nextMatch.rule.parse();
if(subTree.length > 0) {
// Set the start and end positions of the pragma rule if
if (subTree[0].start === undefined) subTree[0].start = start;
if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
if(subTree[0].start === undefined) subTree[0].start = start;
if(subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
$tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
// Quick hack; we only cope with a single parse tree node being returned, which is true at the moment
currentTreeBranch.push.apply(currentTreeBranch,subTree);
@@ -245,9 +245,9 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
var start = this.pos;
var subTree = nextMatch.rule.parse();
// Set the start and end positions of the first and last blocks if they're not already set
if (subTree.length > 0) {
if (subTree[0].start === undefined) subTree[0].start = start;
if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
if(subTree.length > 0) {
if(subTree[0].start === undefined) subTree[0].start = start;
if(subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
}
$tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
return subTree;
@@ -256,7 +256,7 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
var start = this.pos;
var children = this.parseInlineRun(terminatorRegExp);
var end = this.pos;
return [{type: "element", tag: "p", children: children, start: start, end: end }];
return [{type: "element", tag: "p", children: children, start: start, end: end, rule: "parseblock" }];
};
/*
@@ -350,10 +350,10 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
var start = this.pos;
var subTree = nextMatch.rule.parse();
// Set the start and end positions of the first and last child if they're not already set
if (subTree.length > 0) {
if(subTree.length > 0) {
// Set the start and end positions of the first and last child if they're not already set
if (subTree[0].start === undefined) subTree[0].start = start;
if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
if(subTree[0].start === undefined) subTree[0].start = start;
if(subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
}
$tw.utils.each(subTree, function (node) { node.rule = nextMatch.rule.name; });
tree.push.apply(tree,subTree);
@@ -410,9 +410,9 @@ WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegEx
var start = this.pos;
var subTree = inlineRuleMatch.rule.parse();
// Set the start and end positions of the first and last child if they're not already set
if (subTree.length > 0) {
if (subTree[0].start === undefined) subTree[0].start = start;
if (subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
if(subTree.length > 0) {
if(subTree[0].start === undefined) subTree[0].start = start;
if(subTree[subTree.length - 1].end === undefined) subTree[subTree.length - 1].end = this.pos;
}
$tw.utils.each(subTree, function (node) { node.rule = inlineRuleMatch.rule.name; });
tree.push.apply(tree,subTree);

View File

@@ -61,7 +61,7 @@ exports.convertStyleNameToPropertyName = function(styleName) {
var propertyName = $tw.utils.unHyphenateCss(styleName);
// Then check if it needs a prefix
if($tw.browser && document.body.style[propertyName] === undefined) {
var prefixes = ["O","MS","Moz","webkit"];
var prefixes = ["Moz","webkit"];
for(var t=0; t<prefixes.length; t++) {
var prefixedName = prefixes[t] + propertyName.substr(0,1).toUpperCase() + propertyName.substr(1);
if(document.body.style[prefixedName] !== undefined) {
@@ -112,8 +112,6 @@ var eventNameMappings = {
correspondingCssProperty: "transition",
mappings: {
transition: "transitionend",
OTransition: "oTransitionEnd",
MSTransition: "msTransitionEnd",
MozTransition: "transitionend",
webkitTransition: "webkitTransitionEnd"
}
@@ -122,8 +120,6 @@ var eventNameMappings = {
correspondingCssProperty: "animation",
mappings: {
animation: "animationend",
OAnimation: "oAnimationEnd",
MSAnimation: "msAnimationEnd",
MozAnimation: "animationend",
webkitAnimation: "webkitAnimationEnd"
}
@@ -156,19 +152,15 @@ exports.getFullScreenApis = function() {
result = {
"_requestFullscreen": db.webkitRequestFullscreen !== undefined ? "webkitRequestFullscreen" :
db.mozRequestFullScreen !== undefined ? "mozRequestFullScreen" :
db.msRequestFullscreen !== undefined ? "msRequestFullscreen" :
db.requestFullscreen !== undefined ? "requestFullscreen" : "",
"_exitFullscreen": d.webkitExitFullscreen !== undefined ? "webkitExitFullscreen" :
d.mozCancelFullScreen !== undefined ? "mozCancelFullScreen" :
d.msExitFullscreen !== undefined ? "msExitFullscreen" :
d.exitFullscreen !== undefined ? "exitFullscreen" : "",
"_fullscreenElement": d.webkitFullscreenElement !== undefined ? "webkitFullscreenElement" :
d.mozFullScreenElement !== undefined ? "mozFullScreenElement" :
d.msFullscreenElement !== undefined ? "msFullscreenElement" :
d.fullscreenElement !== undefined ? "fullscreenElement" : "",
"_fullscreenChange": d.webkitFullscreenElement !== undefined ? "webkitfullscreenchange" :
d.mozFullScreenElement !== undefined ? "mozfullscreenchange" :
d.msFullscreenElement !== undefined ? "MSFullscreenChange" :
d.fullscreenElement !== undefined ? "fullscreenchange" : ""
};
if(!result._requestFullscreen || !result._exitFullscreen || !result._fullscreenElement || !result._fullscreenChange) {

View File

@@ -119,3 +119,19 @@ exports.getParseTreeText = function getParseTreeText(tree) {
}
return output.join("");
};
exports.getParser = function(type,options) {
options = options || {};
// Select a parser
var Parser = $tw.Wiki.parsers[type];
if(!Parser && $tw.utils.getFileExtensionInfo(type)) {
Parser = $tw.Wiki.parsers[$tw.utils.getFileExtensionInfo(type).type];
}
if(!Parser) {
Parser = $tw.Wiki.parsers[options.defaultType || "text/vnd.tiddlywiki"];
}
if(!Parser) {
return null;
}
return Parser;
};

View File

@@ -60,7 +60,7 @@ exports.repackPlugin = function(title,additionalTiddlers,excludeTiddlers) {
version += "+" + pluginVersion.build;
}
// Save the tiddler
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version}));
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version},$tw.wiki.getModificationFields()));
// Delete any non-shadow constituent tiddlers
$tw.utils.each(tiddlers,function(title) {
if($tw.wiki.tiddlerExists(title)) {

View File

@@ -49,14 +49,26 @@ exports.warning = function(text) {
};
/*
Log a table of name: value pairs
Log a table of name: value or name: [values...] pairs
*/
exports.logTable = function(data) {
if(console.table) {
var hasArrays = false;
$tw.utils.each(data,function(value,name) {
if($tw.utils.isArray(value)) {
hasArrays = true;
}
});
if(console.table && !hasArrays) {
console.table(data);
} else {
$tw.utils.each(data,function(value,name) {
console.log(name + ": " + value);
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
console.log(`${name}[${t}]: ${value[t]}`);
}
} else {
console.log(`${name}: ${value}`);
}
});
}
}

View File

@@ -51,23 +51,29 @@ LogWidget.prototype.invokeAction = function(triggeringWidget,event) {
};
LogWidget.prototype.log = function() {
var data = {},
var self = this,
data = {}, // Hashmap by attribute name with string or array of string values
dataCount,
allVars = {},
allVars = {}, // Hashmap by variable name with string or array of string values
filteredVars;
$tw.utils.each(this.attributes,function(attribute,name) {
// Collect the attributes to be logged
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(name.substring(0,2) !== "$$") {
data[name] = attribute;
var resultList = self.computeAttribute(attribute,{asList: true});
if(resultList.length <= 1) {
data[name] = resultList[0] || "";
} else {
data[name] = resultList;
}
}
});
// Collect values of all variables, using the source text for functions
for(var v in this.variables) {
var variable = this.parentWidget && this.parentWidget.variables[v];
if(variable && variable.isFunctionDefinition) {
allVars[v] = variable.value;
var variableInfo = this.getVariableInfo(v);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
allVars[v] = variableInfo.text;
} else {
allVars[v] = this.getVariable(v,{defaultValue:""});
allVars[v] = variableInfo.resultList.length > 1 ? variableInfo.resultList : variableInfo.text;
}
}
if(this.filter) {

View File

@@ -177,7 +177,7 @@ DroppableWidget.prototype.execute = function() {
DroppableWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-droppable");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -130,7 +130,7 @@ EventWidget.prototype.execute = function() {
EventWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-eventcatcher");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -49,7 +49,8 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true, configTrimWhiteSpace:false});
if(parser) {
var parseTreeNode = parser.tree[0];
while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) {
// process AST nodes generated by pragma rules.
while(parseTreeNode && ["setvariable","set","parameters","void"].indexOf(parseTreeNode.type) !== -1) {
var node = {
type: "set",
attributes: parseTreeNode.attributes,
@@ -82,7 +83,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
// this widget. If it needs to refresh,
// it'll do so along with the the whole
// importvariable tree.
if (widgetPointer != this) {
if(widgetPointer != this) {
widgetPointer.makeChildWidgets = function(){};
}
widgetPointer = widgetPointer.children[0];
@@ -93,7 +94,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
}
});
if (widgetPointer != this) {
if(widgetPointer != this) {
widgetPointer.parseTreeNode.children = this.parseTreeNode.children;
} else {
widgetPointer.makeChildWidgets();

View File

@@ -110,7 +110,7 @@ KeyboardWidget.prototype.execute = function() {
KeyboardWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-keyboard");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -46,7 +46,7 @@ LetWidget.prototype.computeAttributes = function() {
self = this;
this.currentValueFor = Object.create(null);
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
var value = self.computeAttribute(attribute),
var value = self.computeAttribute(attribute,{asList: true}),
name = attribute.name;
// Now that it's prepped, we're allowed to look this variable up
// when defining later variables
@@ -56,7 +56,7 @@ LetWidget.prototype.computeAttributes = function() {
});
// Run through again, setting variables and looking for differences
$tw.utils.each(this.currentValueFor,function(value,name) {
if (self.attributes[name] !== value) {
if(!$tw.utils.isArrayEqual(self.attributes[name],value)) {
self.attributes[name] = value;
self.setVariable(name,value);
changedAttributes[name] = true;
@@ -69,8 +69,10 @@ LetWidget.prototype.getVariableInfo = function(name,options) {
// Special handling: If this variable exists in this very $let, we can
// use it, but only if it's been staged.
if ($tw.utils.hop(this.currentValueFor,name)) {
var value = this.currentValueFor[name];
return {
text: this.currentValueFor[name]
text: value[0] || "",
resultList: value
};
}
return Widget.prototype.getVariableInfo.call(this,name,options);

View File

@@ -203,7 +203,7 @@ RevealWidget.prototype.readPopupState = function(state) {
RevealWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-reveal");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

23
core/modules/widgets/void.js Executable file
View File

@@ -0,0 +1,23 @@
/*\
title: $:/core/modules/widgets/void.js
type: application/javascript
module-type: widget
Void widget that corresponds to pragma and comment AST nodes, etc. It does not render itself but renders all its children.
\*/
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var VoidNodeWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
VoidNodeWidget.prototype = new Widget();
exports.void = VoidNodeWidget;

View File

@@ -80,7 +80,7 @@ Widget.prototype.execute = function() {
/*
Set the value of a context variable
name: name of the variable
value: value of the variable
value: value of the variable, can be a string or an array
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:
@@ -90,8 +90,10 @@ options includes:
*/
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
options = options || {};
var valueIsArray = $tw.utils.isArray(value);
this.variables[name] = {
value: value,
value: valueIsArray ? (value[0] || "") : value,
resultList: valueIsArray ? value : [value],
params: params,
isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition,
@@ -114,7 +116,7 @@ allowSelfAssigned: if true, includes the current widget in the context chain ins
Returns an object with the following fields:
params: array of {name:,value:} or {value:} of parameters to be applied
params: array of {name:,value:,multiValue:} of parameters to be applied (name is optional)
text: text of variable, with parameters properly substituted
resultList: result of variable evaluation as an array
srcVariable: reference to the object defining the variable
@@ -140,7 +142,9 @@ Widget.prototype.getVariableInfo = function(name,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);
if("name" in param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
}
});
value = self.substituteVariableReferences(value,options);
resultList = [value];
@@ -154,13 +158,20 @@ Widget.prototype.getVariableInfo = function(name,options) {
variables[param.name] = param["default"];
}
});
// Parameters are an array of {value:} or {name:, value:} pairs
// Parameters are an array of {name:, value:, multivalue:} pairs (name and multivalue are optional)
$tw.utils.each(params,function(param) {
variables[param.name] = param.value;
if(param.multiValue) {
variables[param.name] = param.multiValue;
} else {
variables[param.name] = param.value || "";
}
});
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
value = resultList[0] || "";
} else {
if(variable.resultList) {
resultList = variable.resultList;
}
params = variable.params;
}
return {
@@ -192,22 +203,24 @@ Widget.prototype.getVariable = function(name,options) {
/*
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)
actualParams - Array of string values or {name:,value:,multiValue} (name and multiValue is optional)
*/
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
formalParams = formalParams || [];
actualParams = actualParams || [];
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
paramInfo, paramValue,
paramInfo, paramValue, paramMultiValue,
results = [];
// Step through each of the parameters in the macro definition
for(var p=0; p<formalParams.length; p++) {
// Check if we've got a macro call parameter with the same name
paramInfo = formalParams[p];
paramValue = undefined;
paramMultiValue = undefined;
for(var m=0; m<actualParams.length; m++) {
if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
paramValue = actualParams[m].value;
paramMultiValue = actualParams[m].multiValue || [paramValue]
}
}
// If not, use the next available anonymous macro call parameter
@@ -217,11 +230,13 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
var param = actualParams[nextAnonParameter++];
paramValue = typeof param === "string" ? param : param.value;
paramMultiValue = typeof param === "string" ? [param] : (param.multiValue || [paramValue]);
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
paramMultiValue = paramMultiValue || [paramValue];
// Store the parameter name and value
results.push({name: paramInfo.name, value: paramValue});
results.push({name: paramInfo.name, value: paramValue, multiValue: paramMultiValue});
}
return results;
};
@@ -310,7 +325,7 @@ Widget.prototype.getStateQualifier = function(name) {
};
/*
Make a fake widget with specified variables, suitable for variable lookup in filters
Make a fake widget with specified variables, suitable for variable lookup in filters. Each variable can be a string or an array of strings
*/
Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
var self = this,
@@ -318,7 +333,12 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
return {
getVariable: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return variables[name];
var value = variables[name];
if($tw.utils.isArray(value)) {
return value[0];
} else {
return value;
}
} else {
opts = opts || {};
opts.variables = variables;
@@ -327,9 +347,18 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
},
getVariableInfo: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return {
text: variables[name]
};
var value = variables[name];
if($tw.utils.isArray(value)) {
return {
text: value[0],
resultList: value
};
} else {
return {
text: value,
resultList: [value]
};
}
} else {
opts = opts || {};
opts.variables = $tw.utils.extend({},variables,opts.variables);
@@ -366,20 +395,45 @@ Widget.prototype.computeAttributes = function(options) {
return changedAttributes;
};
Widget.prototype.computeAttribute = function(attribute) {
/*
Compute the value of a single attribute. Options include:
asList: boolean if true returns results as an array instead of a single value
*/
Widget.prototype.computeAttribute = function(attribute,options) {
options = options || {};
var self = this,
value;
if(attribute.type === "filtered") {
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
value = this.wiki.filterTiddlers(attribute.filter,this);
if(!options.asList) {
value = value[0] || "";
}
} else if(attribute.type === "indirect") {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
if(value && options.asList) {
value = [value];
}
} else if(attribute.type === "macro") {
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
value = variableInfo.text;
if(options.asList) {
value = variableInfo.resultList;
} else {
value = variableInfo.text;
}
} else if(attribute.type === "substituted") {
value = this.wiki.getSubstitutedText(attribute.rawValue,this) || "";
if(options.asList) {
value = [value];
}
} else { // String attribute
value = attribute.value;
if(options.asList) {
if(value === undefined) {
value = [];
} else {
value = [value];
}
}
}
return value;
};

View File

@@ -1059,17 +1059,7 @@ Options include:
exports.parseText = function(type,text,options) {
text = text || "";
options = options || {};
// Select a parser
var Parser = $tw.Wiki.parsers[type];
if(!Parser && $tw.utils.getFileExtensionInfo(type)) {
Parser = $tw.Wiki.parsers[$tw.utils.getFileExtensionInfo(type).type];
}
if(!Parser) {
Parser = $tw.Wiki.parsers[options.defaultType || "text/vnd.tiddlywiki"];
}
if(!Parser) {
return null;
}
var Parser = $tw.utils.getParser(type,options)
// Return the parser instance
return new Parser(type,text,{
parseAsInline: options.parseAsInline,
@@ -1083,7 +1073,7 @@ exports.parseText = function(type,text,options) {
Parse a tiddler according to its MIME type
*/
exports.parseTiddler = function(title,options) {
options = $tw.utils.extend({},options);
options = options || {};
var cacheType = options.parseAsInline ? "inlineParseTree" : "blockParseTree",
tiddler = this.getTiddler(title),
self = this;

View File

@@ -1,7 +1,7 @@
title: $:/core/templates/tiddlywiki5-external-js.html
<$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] +[[$:/core]plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] [[$:/core]plugintiddlers[]]">
`<!doctype html>
`{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
<head>

View File

@@ -3,27 +3,26 @@ tags: $:/tags/ControlPanel/Appearance
caption: {{$:/language/ControlPanel/LayoutSwitcher/Caption}}
\whitespace trim
\function layout.filter() [all[current]field:title[$:/core/ui/PageTemplate]]
\function layout.empty.filter() [all[current]field:title{$:/layout}]
<$linkcatcher to="$:/layout">
<div class="tc-chooser">
<div class="tc-chooser" role="listbox">
<$list filter="[all[tiddlers+shadows]tag[$:/tags/Layout]] [[$:/core/ui/PageTemplate]] +[!is[draft]sort[name]]">
<$list
filter="[{$:/layout}!has[text]]"
variable="ignore"
emptyMessage="""\whitespace trim
<$set name="cls" filter="[all[current]field:title{$:/layout}]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>>
<$link to={{!!title}}>
''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link></div></$set>
""">
<$set name="cls" filter="[all[current]field:title[$:/core/ui/PageTemplate]]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>>
<$link to={{!!title}}>
''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link>
</div>
</$set>
</$list>
<$list filter="[{$:/layout}!has[text]]" variable="ignore">
<$list-empty>
<div class={{{ [layout.empty.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$link to={{!!title}} role="option" aria-selected={{{ [layout.empty.filter[]then[true]else[false]] }}}>
''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link>
</div>
</$list-empty>
<div class={{{ [layout.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$link to={{!!title}} role="option" aria-selected={{{ [layout.filter[]then[true]else[false]] }}}>
''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link>
</div>
</$list>
</$list>
</div>
</$linkcatcher>

View File

@@ -1,20 +1,20 @@
title: $:/snippets/languageswitcher
\whitespace trim
\function language.filter() [all[current]field:title{$:/language}]
<$linkcatcher to="$:/language">
<div class="tc-chooser tc-language-chooser">
<div class="tc-chooser tc-language-chooser" role="listbox">
<$list filter="[[$:/languages/en-GB]] [plugin-type[language]sort[description]]">
<$set name="cls" filter="[all[current]field:title{$:/language}]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>> lang={{!!name}}>
<$link>
<$view field="description">
<$view field="name">
<$view field="title"/>
</$view>
</$view>
</$link>
</div>
</$set>
<div class={{{ [language.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}} lang={{!!name}}>
<$link role="option" aria-selected={{{ [language.filter[]then[true]else[false]] }}}>
<$view field="description">
<$view field="name">
<$view field="title"/>
</$view>
</$view>
</$link>
</div>
</$list>
</div>
</$linkcatcher>

View File

@@ -13,56 +13,6 @@ tags: $:/tags/Macro
\define color(name) <<colour $name$>>
\define box-shadow(shadow)
``
-webkit-box-shadow: $shadow$;
-moz-box-shadow: $shadow$;
box-shadow: $shadow$;
``
\end
\define filter(filter)
``
-webkit-filter: $filter$;
-moz-filter: $filter$;
filter: $filter$;
``
\end
\define transition(transition)
``
-webkit-transition: $transition$;
-moz-transition: $transition$;
transition: $transition$;
``
\end
\define transform-origin(origin)
``
-webkit-transform-origin: $origin$;
-moz-transform-origin: $origin$;
transform-origin: $origin$;
``
\end
\define background-linear-gradient(gradient)
``
background-image: linear-gradient($gradient$);
background-image: -o-linear-gradient($gradient$);
background-image: -moz-linear-gradient($gradient$);
background-image: -webkit-linear-gradient($gradient$);
background-image: -ms-linear-gradient($gradient$);
``
\end
\define column-count(columns)
``
-moz-column-count: $columns$;
-webkit-column-count: $columns$;
column-count: $columns$;
``
\end
\procedure datauri(title)
<$macrocall $name="makedatauri" type={{{ [<title>get[type]] }}} text={{{ [<title>get[text]] }}} _canonical_uri={{{ [<title>get[_canonical_uri]] }}}/>
\end

View File

@@ -0,0 +1,43 @@
title: $:/core/macros/deprecated
tags: $:/tags/Macro
<!-- Deprecated Macros -->
<!-- DO NOT USE THESE MACROS. THEY MAY BE REMOVED AT ANY MOMENT -->
\define box-shadow(shadow)
``
box-shadow: $shadow$;
``
\end
\define filter(filter)
``
filter: $filter$;
``
\end
\define transition(transition)
``
transition: $transition$;
``
\end
\define transform-origin(origin)
``
transform-origin: $origin$;
``
\end
\define background-linear-gradient(gradient)
``
background-image: linear-gradient($gradient$);
background-image: -moz-linear-gradient($gradient$);
background-image: -webkit-linear-gradient($gradient$);
``
\end
\define column-count(columns)
``
column-count: $columns$;
``
\end

View File

@@ -1,17 +1,18 @@
title: $:/snippets/themeswitcher
\whitespace trim
\function theme.filter() [all[current]field:title{$:/theme}] [[$:/theme]!has[text]addsuffix[s/tiddlywiki/vanilla]field:title<currentTiddler>] +[limit[1]]
<$linkcatcher to="$:/theme">
<div class="tc-chooser">
<div class="tc-chooser" role="listbox">
<$list filter="[plugin-type[theme]sort[title]]">
<$set name="cls" filter="[all[current]field:title{$:/theme}] [[$:/theme]!has[text]addsuffix[s/tiddlywiki/vanilla]field:title<currentTiddler>] +[limit[1]]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>><$link to={{!!title}}>
''<$view field="name" format="text"/>''
&#32;
<$view field="description" format="text"/>
</$link>
</div>
</$set>
<div class={{{ [theme.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$link to={{!!title}} role="option" aria-selected={{{ [theme.filter[]then[true]else[false]] }}}>
''<$view field="name" format="text"/>''
&#32;
<$view field="description" format="text"/>
</$link>
</div>
</$list>
</div>
</$linkcatcher>

View File

@@ -4,13 +4,14 @@ title: $:/snippets/viewswitcher
$:/core/images/storyview-$(storyview)$
\end
\whitespace trim
\function view.filter() [<storyview>prefix{$:/view}]
<$linkcatcher to="$:/view">
<div class="tc-chooser tc-viewswitcher">
<div class="tc-chooser tc-viewswitcher" role="listbox">
<$list filter="[storyviews[]]" variable="storyview">
<$set name="cls" filter="[<storyview>prefix{$:/view}]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item"><div class=<<cls>>>
<$button tag="a" class="tc-tiddlylink tc-btn-invisible" to=<<storyview>>><$transclude tiddler=<<icon>>/><$text text=<<storyview>>/></$button>
<div class={{{ [view.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$button tag="a" class="tc-tiddlylink tc-btn-invisible" role="option" to=<<storyview>> aria-selected={{{ [view.filter[]then[true]else[false]] }}}><$transclude tiddler=<<icon>>/><$text text=<<storyview>>/></$button>
</div>
</$set>
</$list>
</div>
</$linkcatcher>

View File

@@ -1,9 +1,9 @@
{
"tiddlers": [
{
"file": "../../../tw5.com/tiddlers/images/New Release Banner.png",
"file": "../../../tw5.com/tiddlers/images/New Release Banner.webp",
"fields": {
"type": "image/jpeg",
"type": "image/webp",
"title": "New Release Banner",
"tags": "picture"
}

View File

@@ -2,4 +2,4 @@ title: Using TiddlyWiki as a library in another Node.js application
Node.js applications can include TiddlyWiki as a library so that they can use wikitext rendering.
See the demo at https://github.com/TiddlyWiki/TiddlyWiki5DemoApp
See the demo at https://github.com/Jermolene/TiddlyWiki5DemoApp

View File

@@ -2,12 +2,5 @@ created: 20131127215321439
modified: 20140912135951542
title: $:/DefaultTiddlers
[[Hire the founder of TiddlyWiki]]
[[TiddlyWiki Pre-release]]
HelloThere
[[Quick Start]]
[[Find Out More]]
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
[[Multi-Valued Variables]]
[[Let Filter Run Prefix]]

View File

@@ -27,4 +27,4 @@ Block forced inline
+
title: ExpectedResult
<div class=" tc-reveal"><p>Block</p></div><div class=" tc-reveal"><p>Block forced block</p></div><span class=" tc-reveal"><p>Block forced inline</p></span><p><span class=" tc-reveal">Inline</span><div class=" tc-reveal">Inline forced block</div><span class=" tc-reveal">Inline forced inline</span></p>
<div class="tc-reveal"><p>Block</p></div><div class="tc-reveal"><p>Block forced block</p></div><span class="tc-reveal"><p>Block forced inline</p></span><p><span class="tc-reveal">Inline</span><div class="tc-reveal">Inline forced block</div><span class="tc-reveal">Inline forced inline</span></p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ResultList
description: Using the "let" filter run prefix to store result lists, not just single values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [all[tiddlers]] :let[[varname]] [(varname)sort[]join[,]] }}}/>
+
title: ExpectedResult
<p>$:/core,ExpectedResult,Output</p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ResultListUnnamedVariable
description: Using the "let" filter run prefix to store result lists, not just single values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [all[tiddlers]] :let[all[]tag[nothing]] [(varname)sort[]join[,]] }}}/>
+
title: ExpectedResult
<p></p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ShortcutSyntax
description: Simple usage of "let" filter run prefix
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [[magpie]] =>varname [<varname>] +[join[-]] }}}/>
+
title: ExpectedResult
<p>magpie</p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/Simple
description: Simple usage of "let" filter run prefix
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [[magpie]] :let[[varname]] [<varname>] +[join[-]] }}}/>
+
title: ExpectedResult
<p>magpie</p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/Function
description: Multi-valued functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc() [all[tiddlers]sort[]]
<$let varname=<<myfunc>>>
<$text text={{{ [(varname)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,12 @@
title: MultiValuedVariables/MissingVariable
description: Using multivalued operands with a missing variable
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [(varname)] }}}/>
+
title: ExpectedResult
<p></p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/NegatedTitle
description: Multi-valued operands
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$let
exclude={{{ $:/core Output }}}
>
<$text text={{{ [all[tiddlers]!title(exclude)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
ExpectedResult
</p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/Operands
description: Multi-valued operands
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc() [all[tiddlers]sort[]]
<$let varname=<<myfunc>>>
<$text text={{{ [(varname)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,21 @@
title: MultiValuedVariables/Parameters
description: Multi-valued function parameters
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc(input) [(input)sort[]]
<$let
input={{{ [all[tiddlers]] }}}
output={{{ [function[myfunc],(input)] }}}
>
<$text text={{{ [(output)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,21 @@
title: MultiValuedVariables/ParametersShortcut
description: Multi-valued function parameters using shortcut syntax
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function my.func(input) [(input)sort[]]
<$let
input={{{ [all[tiddlers]] }}}
output={{{ [my.func(input)] }}}
>
<$text text={{{ [(output)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,17 @@
title: MultiValuedVariables/Simple
description: Simple usage of multivalued assignments with the "let" widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$let varname={{{ [all[tiddlers]sort[]] }}}
varname2=<<varname>>>
<$text text={{{ [(varname2)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Attribute
type: text/vnd.tiddlywiki
<$macrocall $name="rpn" a=<<rpn 2 2 *>> b="pi" operation="*" decimals="4"/>

View File

@@ -0,0 +1,7 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/BlockRule
type: text/vnd.tiddlywiki
The speed of sound
The light of speed

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/BoldEmphasis
type: text/vnd.tiddlywiki
This is ''bold'' text

View File

@@ -0,0 +1,17 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/CodeBlock
type: text/vnd.tiddlywiki
Simple `JS` and complex
---
```js
var match = reEnd.exec(this.parser.source)
```
```tid
<$list filter="[tag[ExampleTag]sort[title]]"/>
```
end

View File

@@ -0,0 +1,11 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/CommentBlock
type: text/vnd.tiddlywiki
<!-- This is a comment -->
Some text
<!-- Another comment -->
More text

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/CommentInline
type: text/vnd.tiddlywiki
This is some text with an inline comment <!-- This is a comment --> and some more text.

View File

@@ -0,0 +1,21 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Conditional
type: text/vnd.tiddlywiki
This is a <%if [{something}] %>Elephant<%elseif [{else}] %>Pelican<%else%>Crocodile<%endif%> <%if [{something}] %>Elephant<%else%>Crocodile<%endif%>
<%if [{$:/info/url/protocol}match[file:]]%>
Loaded from a file URI
<%elseif [{$:/info/url/protocol}match[https:]]%>
Loaded from an HTTPS URI
<%elseif [{$:/info/url/protocol}match[http:]]%>
Loaded from an HTTP URI
<%else%>
Loaded from an unknown protocol
<%endif%>
Plain text in next paragraph.
<%if [{$:/info/url/protocol}match[file:]]%>
Hidden.
<%endif%>

View File

@@ -0,0 +1,7 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Dash
type: text/vnd.tiddlywiki
This is an en-dash: --
This is an em-dash: ---

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Entity
type: text/vnd.tiddlywiki
This is a copyright symbol: &copy;

View File

@@ -0,0 +1,7 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/ExtLink
type: text/vnd.tiddlywiki
An external link: https://www.tiddlywiki.com/
A suppressed external link: ~http://www.tiddlyspace.com/

View File

@@ -0,0 +1,13 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/FilteredTranscludeBlock
type: text/vnd.tiddlywiki
{{{ [tag[docs]] }}}
{{{ [tag[docs]] |tooltip}}}
{{{ [tag[docs]] ||TemplateTitle}}}
{{{ [tag[docs]] |tooltip||TemplateTitle}}}
{{{ [tag[docs]] }}width:40;height:50;}.class.class

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/FilteredTranscludeInline
type: text/vnd.tiddlywiki
{{{ [tag[docs]] }}} {{{ [tag[docs]] |tooltip}}} {{{ [tag[docs]] ||TemplateTitle}}} {{{ [tag[docs]] |tooltip||TemplateTitle}}} {{{ [tag[docs]] }}width:40;height:50;}.class.class

View File

@@ -0,0 +1,15 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/FunctionDefinition
type: text/vnd.tiddlywiki
\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

View File

@@ -0,0 +1,12 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/HardLineBreaks
type: text/vnd.tiddlywiki
"""
This is some text
That is set like
It is a Poem
When it is
Clearly
Not
"""

View File

@@ -0,0 +1,29 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Heading
type: text/vnd.tiddlywiki
! Heading 1
!! Heading 2
!!! Heading 3
!!!! Heading 4
!!!!! Heading 5
!!!!!! Heading 6
! AAA
!! AAA
!!! AAA
!!!! AAA
!!!!! AAA
!!!!!! AAA
AAA

View File

@@ -0,0 +1,15 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Html
type: text/vnd.tiddlywiki
<aside>
This is an HTML5 aside element
</aside>
<$slider target="MyTiddler">
This is a widget invocation
</$slider>
<$list filter="[tag[ExampleTag]sort[title]]"/>
Plain text in next paragraph.

View File

@@ -0,0 +1,10 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Image
type: text/vnd.tiddlywiki
[img[https://tiddlywiki.com/fractalveg.jpg]]
[img width="23" height="24" [https://tiddlywiki.com/fractalveg.jpg]]
[img width={{!!width}} height={{!!height}} [https://tiddlywiki.com/fractalveg.jpg]]
[img[Description of image|https://tiddlywiki.com/fractalveg.jpg]]
[img[TiddlerTitle]]
[img[Description of image|TiddlerTitle]]

View File

@@ -0,0 +1,6 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Import
type: text/vnd.tiddlywiki
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
\import [[$:/core/ui/PageMacros]]

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/ItalicEmphasis
type: text/vnd.tiddlywiki
This is //italic// text

View File

@@ -0,0 +1,40 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/List
type: text/vnd.tiddlywiki
* This is an unordered list
* It has two items
# This is a numbered list
## With a subitem
# And a third item
; This is a term that is being defined
: This is the definition of that term
#** One
#* Two
#** Three
#**** Four
#**# Five
#**## Six
## Seven
### Eight
## Nine
* List item one
*.active List item two has the class `active`
* List item three
# AAA
## [[BBB]]
### CCC
# AAA
## CCC
## DDD
## EEE
# BBB
## FF `/` FFF
## FFF
## GGG
##

View File

@@ -0,0 +1,9 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/MacroCallBlock
type: text/vnd.tiddlywiki
<<name "value" "value2">>
<<.def "macro calls">>
<<alert "primary" "primary alert" width:"60%">>

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/MacroCallInline
type: text/vnd.tiddlywiki
These are macro calls in a line: <<name "value" "value2">> and <<.def "macro calls">> <<alert "primary" "primary alert" width:"60%">>

View File

@@ -0,0 +1,9 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/MacroDef
type: text/vnd.tiddlywiki
\define name(param:defaultvalue,param2:defaultvalue)
definition text, including $param$ markers
\end
\define lingo-base() $:/language/ControlPanel/Basics/

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Parameters
type: text/vnd.tiddlywiki
\parameters(param:defaultvalue,param2:defaultvalue)

View File

@@ -0,0 +1,9 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/ParserMode
type: text/vnd.tiddlywiki
\parsermode block
\parsermode inline
Test.

View File

@@ -0,0 +1,6 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/PrettyExtLink
type: text/vnd.tiddlywiki
[ext[https://tiddlywiki.com/fractalveg.jpg]]
[ext[Tooltip|https://tiddlywiki.com/fractalveg.jpg]]

View File

@@ -0,0 +1,6 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/PrettyLink
type: text/vnd.tiddlywiki
[[Introduction]]
[[Link description|TiddlerTitle]]

View File

@@ -0,0 +1,7 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/QuoteBlock
type: text/vnd.tiddlywiki
<<<tc-quote
Quote text
<<<

View File

@@ -0,0 +1,6 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/RulesPragma
type: text/vnd.tiddlywiki
\rules except ruleone ruletwo rulethree
\rules only ruleone ruletwo rulethree

View File

@@ -0,0 +1,5 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/SimpleText
type: text/vnd.tiddlywiki
The quick brown fox

View File

@@ -0,0 +1,6 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/SoftLineBreak
type: text/vnd.tiddlywiki
The rain in Spain
falls mainly on the plain

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