Compare commits

..
Author SHA1 Message Date
Jeremy Ruston 2fab886564 Add incompleteness warning 2025-11-09 15:42:33 +00:00
Jeremy Ruston f4db2300be Disable the #8702 changenote as it is not yet merged 2025-11-09 15:42:11 +00:00
Jeremy Ruston 33d3372229 Styling refinements 2025-11-09 15:37:50 +00:00
Jeremy Ruston 7a304df673 Update field names 2025-11-09 15:16:07 +00:00
Jeremy Ruston a273ed05eb Embarrassing typo 2025-11-06 21:26:54 +00:00
Jeremy Ruston a4721ce24a Merge branch 'master' into new-release-note-architecture 2025-11-05 12:37:59 +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
Jeremy Ruston 3ab6302353 Formatting. More to do. 2025-11-05 10:54:32 +00:00
Jeremy Ruston 85ed513bef Slightly better description for v5.4.0 2025-11-05 10:54:15 +00:00
Jeremy Ruston 1832aff565 Add more tickets for demo and testing purposes 2025-11-05 10:54:02 +00:00
Jeremy Ruston a07ecb6156 Introduce impact notes attached to change notes
No styles yet
2025-11-02 12:09:03 +00:00
XLBillyandGitHub 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
cdruanandGitHub 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 ChenandGitHub fb4d417629 Update chinese language files (#9375)
* change camel-case hint text for chinese languages
2025-10-30 09:11:36 +01:00
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 ImtiazandGitHub 7898cb8446 Revert "Fix 8166 html tag validation (#8176)" (#9374)
This reverts commit 9a5f4cc0ef.
2025-10-29 22:01:03 +01:00
Mario PietschandGitHub 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
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 RustonandGitHub 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 5848d66e96 Merge branch 'tiddlywiki-com' 2025-10-29 14:48:14 +00:00
485051951e Allow intercept of audioparser by widget and pass attributes (#9024)
* Initial Commit

* Removes Function wrapper

* Remove Function wrapper from parser

* Convert spaces to tabs in audio.js widget

* Fix indentation

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-10-29 12:19:09 +00:00
XLBillyandGitHub 135e685811 Improve alert accessibility (#9248)
* Improve alert accessibility

* Remove procedure
2025-10-29 12:08:01 +00:00
Simon HuberandGitHub 99682c5731 [Bugfix] - keep focus on Element with tc-keep-focus class (#9233)
* Update factory.js

* add tc-keep-focus class to type input field

* tryna fix with energy
2025-10-29 12:00:40 +00:00
yaisogandGitHub d58eec47c0 Modify output of some math operators for empty inputs (#9337)
* Initial commit

* Adapt test to new behavior
2025-10-29 11:57:39 +00:00
Jeremy RustonandGitHub 059978ec63 Fix crash with empty transclusions (#8145)
* Initial commit

* Remove testing code
2025-10-29 11:33:17 +00:00
Mario PietschandGitHub b5e20a58a6 Muted palette, minor adjustments (#9015)
* Muted palette, minor adjustments

* revert download-background

* Change sidebar tiddler link foreground color

Change sidebar tiddler link foreground colour to make the search dropdown text readable. Using: #aaaaaa
2025-10-29 11:18:31 +00:00
Mario PietschandGitHub e8fe6b98bc Change camel-case hint, since it is disabled by default now. (#9211)
* Change camel-case hint, since it is disabled by default now.

* change camel-case hint text for other languages, where it is obvious
2025-10-29 11:18:00 +00:00
Mario PietschandGitHub 317104774c list-links-draggable defaults to currentTiddler if tiddler parameter is missing (#9254)
* list-links-draggable if tiddler parameter undefined default to currentTiddler

* update list-links-draggable docs with the new behaviour
2025-10-29 11:17:02 +00:00
Simon HuberandGitHub deed8631d8 exports.search: don't break the loop if encoding is base64, just continue (#9247)
This makes it possible to find tiddlers with base64 encoding using `search:*:words[searchterm]` for example

fixes #9246
2025-10-29 11:15:54 +00:00
XLBillyandGitHub d733b77e2f Replace CSS property macros in Snow White theme (#9243) 2025-10-29 11:07:37 +00:00
Simon HuberandGitHub 46c26a64b9 Update framed.js to avoid quirks mode (#9227) 2025-10-29 11:05:59 +00:00
XLBillyandGitHub ec81d6663b [5.4.0] Update highlightjs plugin (#9118)
* Bump highlightjs version to 11.11.1

* highlightblock.js support es6
2025-10-29 11:04:53 +00:00
23f0a9bf79 Improve handling of $link widget's draggable="no" (#9262)
* fix to to improve browser's compliance with user's intent when the widget's draggable attribute is set to no

* Update link.js with comments from the PR review

indentation changed from spaces to tabs
modified else statement into a else if statement for additional specificity

---------

Co-authored-by: Frédéric Demers <fastfreddy@fdemers.ca>
2025-10-29 11:03:19 +00:00
yogoshellandGitHub bad87c405e docs: Correcting typo in description field (#9087) 2025-10-28 22:16:32 +01:00
6f23a078b7 Signing CLA (#9086)
Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-10-28 22:15:04 +01:00
Mario PietschandGitHub c3706b8a79 [Docs] Convert Markdown links into TW-Links. Remove redundant TiddlyWiki links. Add links to current docs. (#9209) 2025-10-28 22:12:05 +01:00
Mohammad RahmaniandGitHub 7ca8fb29af Docs: Fix Issue in Triggering $:/AdvancedSearch (#9244) 2025-10-28 22:11:20 +01:00
Markus SauermannandGitHub d39bb5274e Fix typo in Filter Syntax (#9111) 2025-10-28 22:07:54 +01:00
lin onetwoandGitHub 81b69783c4 docs: How to add new cascade and use it (#8472)
* docs: How to add new cascade and use it

* Update Cascade Mechanism.tid

* docs: update

* Update How to Create a Custom Cascade Entry.tid

* Refine with deepseek
2025-10-27 15:59:14 +00:00
Rishu KumarandGitHub 40cc62f727 fix: renaming of incoming tiddlers being imported (#9368)
* fix: UX on pasting image

* fix: Rename on pasting image from clipboard
2025-10-27 16:13:42 +01:00
Rishu KumarandGitHub b349adde16 Fix Renaming on pasting image from clipboard (#9369)
We can do Renaming on pasting image from clipboard
2025-10-27 16:11:32 +01:00
Saq ImtiazandGitHub d63a1896b3 docs: update more links for PR maker (#9362) 2025-10-23 17:48:57 +02:00
Saq ImtiazandGitHub b65fa11643 Update ContributionBanner to reflect new URI for pr maker 2025-10-23 16:56:21 +02:00
Saq ImtiazandGitHub 4043499633 Update link in make-pr-maker-link procedure
Update URI for PR maker
2025-10-23 16:53:30 +02:00
Saq ImtiazandGitHub 6a39a4e13b Docs: add link to Contributing guidelines in developers tiddler (#9361) 2025-10-23 12:07:37 +01:00
Cameron FischerandGitHub b5153c0066 :reverse was ignored when prefix sorting by version (#9280) 2025-10-23 12:03:35 +01:00
b061f90f87 Get-file web server route now support streaming (#9078)
* Update get-file.js

* Update WebServer API_ Get File.tid

* Update get-file.js

* Update get-file.js

* Update get-file.js

* Update get-file.js

* Update core-server/server/routes/get-file.js

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

* Update core-server/server/routes/get-file.js

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

* Update get-file.js

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-23 11:55:30 +01:00
Jeremy Ruston 8be83cf01b Merge branch 'tiddlywiki-com' 2025-10-21 15:45:21 +01:00
Mario PietschandGitHub 5b5147dade Make ReleaseTemplate use code-body (#9340) 2025-10-21 13:00:06 +02:00
Scott SauyetandGitHub ad6ac480f9 Remove css props rather than setting transition to none (#9284)
* Use style property deletion rather than setting to none

* Remove cecily and slide, fix quotation marks

* Fix returning typo

* Add helper functions, use some timeouts to order events
2025-10-21 11:57:10 +01:00
Cameron FischerandGitHub 8168512e95 Support for downloading base64 files (#9297)
* Support for downloading base64 files

* Added comment explaining why we'd use data urls over blobs

* Using more modern string##includes method
2025-10-21 11:53:32 +01:00
Leilei332andGitHub 276fdc8634 Purge deprecated plugins and editions (#9350) 2025-10-21 11:51:12 +01:00
yaisogandGitHub 0b38ced43a Preserve the module insertion order in forEachModuleOfType (#9305)
* Initial commit

* Update testcase

* Revert "Update testcase"

This reverts commit 0d24c4233f.

* Revert initial commit and swap definition order
2025-10-21 11:37:55 +01:00
cdruanandGitHub d7e48207b9 Fix crash when processing large files from tiddlywiki.files (#9341)
* skip reading file content when _canonical_uri is present

* skip loading file when file size is too large
2025-10-21 11:30:39 +01:00
Mohammad RahmaniandGitHub 2c8fafee48 docs: Fix Slider Plugin Url (#9354) 2025-10-21 12:30:37 +02:00
欧阳andGitHub a6383aaaea feat: specify the space applied between CJK and non-CJK characters (#9349) 2025-10-21 11:26:32 +01:00
Joseph YiandGitHub 61619c07c8 chore: bump checkout action v4 to v5 (#9342) 2025-10-15 21:27:23 +01:00
Joseph YiandGitHub 09a42a54c0 Chore: Signing CLA (#9344) 2025-10-15 21:25:01 +01:00
Mario PietschandGitHub f4f31c37fc Remove tweb.at from community resources (#9330) 2025-10-12 10:55:39 +01:00
Jeremy Ruston 61e638c972 Merge branch 'tiddlywiki-com' 2025-10-11 15:53:49 +01:00
6bdd51d72a Fix (eslint): removes unneeded dependencies (#9332)
* Clean up eslint.config.mjs by removing unused import and comments

Removed commented-out import statements for clarity.

* Remove @eslint/compat from devDependencies

Removed deprecated @eslint/compat dependency.

* Update eslint.config.mjs

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-11 14:11:21 +01:00
Mario PietschandGitHub ed3405672a Rename Intertwingled innovations.webp to Innovations.webp (#9331) 2025-10-11 11:19:55 +02:00
Mario PietschandGitHub e3af967cbb [DOCS] Convert PNG images into WEBP to reduce wiki size by 3MB (#9162)
* convert PNG images into WEBP to reduce wiki size by 3MB

* convert Community Survey 2025.png to webp
2025-10-10 15:02:30 +01:00
Mario PietschandGitHub 6b0d3fab5d [Docs] Fix typo in DefaultTiddlers (#9202) 2025-10-10 15:01:00 +01:00
Mario PietschandGitHub 13f1689e7e [DOC] ContentType Info and Links - Add Compount Tiddler Type (#9249) 2025-10-10 15:00:43 +01:00
Mario PietschandGitHub 4c09a88272 [DOCS] Improve "range Operator" documentation. Make placeholder "terms" consistent with filter strings (#9276)
* [DOCS] Improve "range Operator" documentation. Make placeholder "terms" consistent with filter strings

* update range operator table formatting a bit
2025-10-10 14:59:27 +01:00
Jeremy Ruston fbf110e209 Merge branch 'tiddlywiki-com' 2025-10-10 14:58:29 +01:00
Mario PietschandGitHub 3c1d658fad Adjust Missing Links for Community Resources (#9329)
* Adjust Missing Links for Community Resources

* Update _Lucky Sushi_ online shop by sini-Kit.tid
2025-10-10 14:44:57 +01:00
Cameron FischerandGitHub 5d738673ac Added "Hello There" Thumbnail for TW5-Graph (#9322)
* Added "Hello There" Thumbnail for TW5-Graph

* Preventing TiddlyWiki link in TW5-Graph description

* Added modified date for TW5-Graph post
2025-10-10 14:30:54 +01:00
cdruanandGitHub 91871d2ced Markdown: Fix missing inline support and macrocall args parsing error (#9295)
* Rewrote tw_macrocallinline() parser so macrocall args containing ">"
will be parsed properly.

* Use $tw.log.MARKDOWN flag to print debug messages.

* Fix markdown parser to respect parseAsInline option. (#8917)
2025-10-10 13:31:11 +01:00
Leilei332andGitHub 5143da9cce Fix CTRL-alt-right shortcut not working (#9325) 2025-10-10 13:30:22 +01:00
Leilei332andGitHub 186d1b014a Migrate most deprecated eslint rules (#9328)
* Migrate to eslint stylistic

* Remove & replace some deprecated rules
2025-10-10 13:29:35 +01:00
Saq ImtiazandGitHub 58e41ee0ad Feature: extend server routes to support multiple methods (#9207) 2025-10-10 12:37:09 +01:00
Saq ImtiazandGitHub 97824cc3a3 Feature: extend server route exports to allow ordering (#9206) 2025-10-10 12:36:36 +01:00
Arlen BeilerandGitHub 6e493755be Add support for commands and startups which return promises (#9103) 2025-10-09 16:06:03 +01:00
64fce62075 [v5.4.0] Update configuration defaults (#9107)
* Change default sidebar layout to fluid-fixed

* Default to not wrapping code blocks

* codewrapping should be "pre" not "no"

Co-authored-by: Leilei332 <LeiYiXia29@outlook.com>

* Info panel should stay open by default

---------

Co-authored-by: Leilei332 <LeiYiXia29@outlook.com>
2025-10-08 12:32:34 +01:00
Leilei332andGitHub 5a4ff56477 Fix markup not included in external core edition (#9218) 2025-10-08 10:41:08 +01:00
Leilei332andGitHub 3889a2a0d0 Fix nested span.tc-keyboard element in core (#9235)
* Fix nested `span.keyboard` element in core

* Fix switching search results not working
2025-10-08 10:28:59 +01:00
Leilei332andGitHub 34737f4e28 Improve ARIA support for link widget (#9167) 2025-10-07 15:46:43 +01:00
Leilei332andGitHub 94673a1028 Allow button widget to use all ARIA attibutes (#9154)
* Allow button widget to use all ARIA attibutes

* Add link in docs

* Update docs
2025-10-07 13:05:09 +01:00
Mario PietschandGitHub b462aaa9a3 Modules in draft tidders should not be executed (#9293)
* modules in draft tiddlers should not be defined

* improve warning message and use console.warn
2025-10-07 12:03:11 +01:00
Mario PietschandGitHub 4552c117fc Add th-debug-element hook and data-debug-element minimal useful info (#9281)
* add th-debug-element hook and date-debug-element minimal useful info

* invoke th-dom-rendering-element hook

* improve comment
2025-10-07 12:02:08 +01:00
Leilei332andGitHub dc7f2a57bb Use currentColor to style svg (#9316)
* Replace fill: with currentColor

* Remove fill from wikitext

* Replace fill property with color property for svg selectors

* Further remove fill property

* Replace more fill properties

* Replace fill in seamless theme

* Replace fill in plugins
2025-10-07 11:55:26 +01:00
Jeremy Ruston 7e91bac6b8 Merge branch 'tiddlywiki-com' 2025-10-07 10:43:50 +01:00
Leilei332andGitHub 15ba415a45 Use s instead of strike (#9131)
* Use s instead of strike

* Update docs
2025-10-06 17:28:29 +01:00
Leilei332andGitHub 2405e65308 Remove support for IE in range widget (#9275) 2025-10-06 17:26:34 +01:00
Saq ImtiazandGitHub 865f36a6f5 Removes deprecated attributes from $eventcatcher (#9259)
* fix: removed deprecated attributes

* docs: update for eventcatcher
2025-10-06 17:12:08 +01:00
Saq ImtiazandGitHub a36356a07d Adds info tiddlers for viewport dimensions (#9260)
* feat: added info tiddlers for viewport dimensions

* feat: multi window support for dimensions info tiddlers

* refactor: introduce standalone eventbus and refactor for ES2017

* docs: extended docs for InfoMechanism to cover new additions
2025-10-06 17:00:13 +01:00
Leilei332andGitHub ee23097816 Set modal's mask-closable attribute to yes by default (#9313)
* Set modal's mask-closable attribute to yes by default

* Make backdrop closable only when field value is yes or empty
2025-10-06 16:40:20 +01:00
Mario PietschandGitHub e753851d49 Add output directory to eslint ignore configuration (#9317) 2025-10-06 16:27:11 +01:00
Leilei332andGitHub cae3d0fa2c Switch to native support for base64 utf8 (#9253)
* Switch to native support for base64 utf8

* Fix error on nodejs
2025-10-06 16:25:45 +01:00
Mario PietschandGitHub 7c020d0eb6 Add a CSV Table as Example to Typed Blocks in Wikitext (#9318) 2025-10-06 16:22:31 +01:00
Saq ImtiazandGitHub afe2ac45d9 Fix: Update eslint.yml to not run on push 2025-10-02 22:38:59 +02:00
Saq ImtiazandGitHub 6791f0f130 Create eslint.yml (#9315)
Adds a workflow to lint PRs, and reports issues found in lines modified in the PR.
2025-10-02 22:33:09 +02:00
Mario PietschandGitHub a2e5c2cca2 Add documentation for Encrypted Import Problems (#9100) 2025-10-02 11:49:59 +01:00
Mario PietschandGitHub 87ba87bdd2 Set AES strength to 256 bit (#8249)
* Set AES strength to 256 bit

* Update Encryption tiddler to AES 256
2025-10-02 11:49:45 +01:00
Leilei332andGitHub 619bdfcab5 Reimplement regexp sticky flag (#9119) 2025-10-02 11:42:30 +01:00
Scott SauyetandGitHub 5ff4e02a61 Update TableOfContents.tid (#9312)
Move Welcome back to the top of the TOC.

Third attempt.  Someday I'll get this right.
2025-10-02 11:22:37 +01:00
Arlen BeilerandGitHub f8170cd50a [5.4.0] Update eslint target to 2017 and do initial fixes (#9135)
* eslint manual fixes
- update eslint to 2017
- add self to forbidden globals
- fix a few unfixable bugs caught by eslint
- convert newer features in twitter-archivist

* add eslint plugin to forbid features

* import changes from @saqimtiaz

* add package.json changes
2025-10-01 15:08:00 +01:00
Jeremy Ruston 5389dc0fa7 Merge branch 'tiddlywiki-com' 2025-09-25 16:48:39 +01:00
Jeremy Ruston 2cc7c96eec Remove inadvertently committed files 2025-09-25 16:48:22 +01:00
Jeremy Ruston 8cd3d4e22c HireJeremy: Tweaks 2025-09-20 11:52:39 +01:00
Jeremy Ruston 37e09d1c25 Add Bluesky link for Jermolene 2025-09-17 14:02:13 +01:00
Jeremy Ruston 7a080092d0 Allow community cards to be edited in the browser under Node.js 2025-09-16 11:05:04 +01:00
Jeremy Ruston 810ac42810 Release notes: show current banner for the current release 2025-09-14 14:36:31 +01:00
Jeremy Ruston 88d3f69a3b Merge branch 'tiddlywiki-com' 2025-09-14 14:32:25 +01:00
Jeremy Ruston e956bb32e9 Extend release note mechanism to allow multiple github commits/prs for a change
Also added @Leilei332's credit for #9288
2025-09-14 11:43:22 +01:00
Leilei332andGitHub dd91ac0b82 [DOCS] Button widget role attribute (#9168)
* [DOCS] Button role attribute

* Fix wrong language
2025-09-14 11:27:49 +01:00
Leilei332andGitHub af63a3b920 Further improvements for offloading server components (#9288)
* Move edition-info and repository to core-server

* Update readme

* Set platform to server for filesystem plugin

* Move startup/command.js
2025-09-14 11:21:42 +01:00
Jeremy Ruston e4fb47ee76 Add change note for #9183 2025-09-12 17:45:13 +01:00
Jeremy RustonandGitHub 7944f42467 [v5.4.0] Offload server components from browser builds of TiddlyWiki (#9183)
* Move Node.js specific files out of the core plugin

* Package server files as new $:/core-server plugin

* Missed commander.js

* Fix crash in browser

* Extend server-only mechanism to be usable by other plugins

* in

* Revert "Extend server-only mechanism to be usable by other plugins"

This reverts commit 3faf503073.

* Revert "in"

This reverts commit b80213128f.

* Reapply "Extend server-only mechanism to be usable by other plugins"

This reverts commit c6c83bc18b.

* Fix test failure

* Move filesystem utilities into core-server

* Move old-style release notes out of the way

* Move the 5.4.0 release note into the right place

* Revert "Move the 5.4.0 release note into the right place"

This reverts commit 3f5c2bfba3.

* Revert "Move old-style release notes out of the way"

This reverts commit ee16e48a43.
2025-09-12 15:21:34 +01:00
Jeremy Ruston 578c09e0ce Remove change note for #9183 until it is merged 2025-09-12 15:19:28 +01:00
463 changed files with 5623 additions and 30947 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "${{ env.NODE_VERSION }}" node-version: "${{ env.NODE_VERSION }}"
@@ -30,7 +30,7 @@ jobs:
TW5_BUILD_MAIN_EDITION: "./editions/prerelease" TW5_BUILD_MAIN_EDITION: "./editions/prerelease"
TW5_BUILD_OUTPUT: "./output/prerelease" TW5_BUILD_OUTPUT: "./output/prerelease"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "${{ env.NODE_VERSION }}" node-version: "${{ env.NODE_VERSION }}"
@@ -62,7 +62,7 @@ jobs:
TW5_BUILD_OUTPUT: "./output" TW5_BUILD_OUTPUT: "./output"
TW5_BUILD_ARCHIVE: "./output" TW5_BUILD_ARCHIVE: "./output"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "${{ env.NODE_VERSION }}" node-version: "${{ env.NODE_VERSION }}"
+40
View File
@@ -0,0 +1,40 @@
name: ESLint
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
concurrency:
group: lint-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
permissions:
contents: read
# Needed for GitHub Checks API
checks: write
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install --include=dev
- name: Run ESLint with reviewdog (GitHub Checks)
uses: reviewdog/action-eslint@v1
with:
eslint_flags: '.'
reporter: github-pr-check
fail_level: error
level: error
tool_name: ESLint PR code
-22
View File
@@ -73,10 +73,8 @@ rm $TW5_BUILD_OUTPUT/dev/static/*
echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html
echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html
echo "<a href='./plugins/tiddlywiki/d3/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/d3/index.html</a>" > $TW5_BUILD_OUTPUT/d3demo.html
echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html
echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html
echo "<a href='./plugins/tiddlywiki/tahoelafs/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tahoelafs/index.html</a>" > $TW5_BUILD_OUTPUT/tahoelafs.html
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script) # Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
@@ -301,26 +299,6 @@ node $TW5_BUILD_TIDDLYWIKI \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \ --rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \
|| exit 1 || exit 1
# /plugins/tiddlywiki/tahoelafs/index.html Demo wiki with Tahoe-LAFS plugin
# /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/tahoelafs \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tahoelafs/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/d3/index.html Demo wiki with D3 plugin
# /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/d3demo \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/d3/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin # /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin
# /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin # /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin
node $TW5_BUILD_TIDDLYWIKI \ node $TW5_BUILD_TIDDLYWIKI \
+46 -15
View File
@@ -799,12 +799,13 @@ the password, and to encrypt/decrypt a block of text
$tw.utils.Crypto = function() { $tw.utils.Crypto = function() {
var sjcl = $tw.node ? (global.sjcl || require("./sjcl.js")) : window.sjcl, var sjcl = $tw.node ? (global.sjcl || require("./sjcl.js")) : window.sjcl,
currentPassword = null, currentPassword = null,
callSjcl = function(method,inputText,password) { callSjcl = function(method,inputText,password,options) {
options = options || {};
password = password || currentPassword; password = password || currentPassword;
var outputText; var outputText;
try { try {
if(password) { if(password) {
outputText = sjcl[method](password,inputText); outputText = sjcl[method](password,inputText,options);
} }
} catch(ex) { } catch(ex) {
console.log("Crypto error:" + ex); console.log("Crypto error:" + ex);
@@ -830,7 +831,8 @@ $tw.utils.Crypto = function() {
return !!currentPassword; return !!currentPassword;
} }
this.encrypt = function(text,password) { this.encrypt = function(text,password) {
return callSjcl("encrypt",text,password); // set default ks:256 -- see: http://bitwiseshiftleft.github.io/sjcl/doc/convenience.js.html
return callSjcl("encrypt",text,password,{v:1,iter:10000,ks:256,ts:64,mode:"ccm",adata:"",cipher:"aes"});
}; };
this.decrypt = function(text,password) { this.decrypt = function(text,password) {
return callSjcl("decrypt",text,password); return callSjcl("decrypt",text,password);
@@ -1530,7 +1532,8 @@ Define all modules stored in ordinary tiddlers
*/ */
$tw.Wiki.prototype.defineTiddlerModules = function() { $tw.Wiki.prototype.defineTiddlerModules = function() {
this.each(function(tiddler,title) { this.each(function(tiddler,title) {
if(tiddler.hasField("module-type")) { // Modules in draft tiddlers are disabled
if(tiddler.hasField("module-type") && (!tiddler.hasField("draft.of"))) {
switch(tiddler.fields.type) { switch(tiddler.fields.type) {
case "application/javascript": case "application/javascript":
// We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script // We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script
@@ -1557,6 +1560,11 @@ $tw.Wiki.prototype.defineShadowModules = function() {
this.eachShadow(function(tiddler,title) { this.eachShadow(function(tiddler,title) {
// Don't define the module if it is overidden by an ordinary tiddler // Don't define the module if it is overidden by an ordinary tiddler
if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) { if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) {
if(tiddler.hasField("draft.of")) {
// Report a fundamental problem
console.warn(`TiddlyWiki: Plugins should not contain tiddlers with a 'draft.of' field: ${tiddler.fields.title}`);
return;
}
// Define the module // Define the module
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text); $tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
} }
@@ -1976,22 +1984,41 @@ filepath: pathname of the directory containing the specification file
$tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) { $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var tiddlers = []; var tiddlers = [];
// Read the specification // Read the specification
var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8")); var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"), function(e) {
console.log("Warning: tiddlywiki.files in " + filepath + " invalid: " + e.message);
return {};
});
// Helper to process a file // Helper to process a file
var processFile = function(filename,isTiddlerFile,fields,isEditableFile,rootPath) { var processFile = function(filename,isTiddlerFile,fields,isEditableFile,rootPath) {
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)], var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
type = (extInfo || {}).type || fields.type || "text/plain", type = (extInfo || {}).type || fields.type || "text/plain",
typeInfo = $tw.config.contentTypeInfo[type] || {}, typeInfo = $tw.config.contentTypeInfo[type] || {},
pathname = path.resolve(filepath,filename), pathname = path.resolve(filepath,filename),
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8"),
metadata = $tw.loadMetadataForFile(pathname) || {}, metadata = $tw.loadMetadataForFile(pathname) || {},
fileTiddlers; fileTooLarge = false,
text, fileTiddlers;
if("_canonical_uri" in fields) {
text = "";
} else if(fs.statSync(pathname).size > $tw.config.maxEditFileSize) {
var msg = "File " + pathname + " not loaded because it is too large";
console.log("Warning: " + msg);
fileTooLarge = true;
text = isTiddlerFile ? msg : "";
} else {
text = fs.readFileSync(pathname,typeInfo.encoding || "utf8");
}
if(isTiddlerFile) { if(isTiddlerFile) {
fileTiddlers = $tw.wiki.deserializeTiddlers(path.extname(pathname),text,metadata) || []; fileTiddlers = $tw.wiki.deserializeTiddlers(fileTooLarge ? ".txt" : path.extname(pathname),text,metadata) || [];
} else { } else {
fileTiddlers = [$tw.utils.extend({text: text},metadata)]; fileTiddlers = [$tw.utils.extend({text: text},metadata)];
} }
var combinedFields = $tw.utils.extend({},fields,metadata); var combinedFields = $tw.utils.extend({},fields,metadata);
if(fileTooLarge && isTiddlerFile) {
delete combinedFields.type; // type altered
}
$tw.utils.each(fileTiddlers,function(tiddler) { $tw.utils.each(fileTiddlers,function(tiddler) {
$tw.utils.each(combinedFields,function(fieldInfo,name) { $tw.utils.each(combinedFields,function(fieldInfo,name) {
if(typeof fieldInfo === "string" || $tw.utils.isArray(fieldInfo)) { if(typeof fieldInfo === "string" || $tw.utils.isArray(fieldInfo)) {
@@ -2066,6 +2093,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
} else if(tidInfo.suffix) { } else if(tidInfo.suffix) {
tidInfo.fields.text = {suffix: tidInfo.suffix}; tidInfo.fields.text = {suffix: tidInfo.suffix};
} }
tidInfo.fields = tidInfo.fields || {};
processFile(tidInfo.file,tidInfo.isTiddlerFile,tidInfo.fields); processFile(tidInfo.file,tidInfo.isTiddlerFile,tidInfo.fields);
}); });
// Process any listed directories // Process any listed directories
@@ -2087,6 +2115,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var thisPath = path.relative(filepath, files[t]), var thisPath = path.relative(filepath, files[t]),
filename = path.basename(thisPath); filename = path.basename(thisPath);
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) { if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
dirSpec.fields = dirSpec.fields || {};
processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path); processFile(thisPath,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile,dirSpec.path);
} }
} }
@@ -2549,10 +2578,10 @@ $tw.boot.execStartup = function(options){
if($tw.safeMode) { if($tw.safeMode) {
$tw.wiki.processSafeMode(); $tw.wiki.processSafeMode();
} }
// Register typed modules from the tiddlers we've just loaded // Register typed modules from the tiddlers we've just loaded and any modules within plugins
$tw.wiki.defineTiddlerModules(); // Tiddlers should appear last so that they may overwrite shadows during module registration
// And any modules within plugins
$tw.wiki.defineShadowModules(); $tw.wiki.defineShadowModules();
$tw.wiki.defineTiddlerModules();
// Make sure the crypto state tiddler is up to date // Make sure the crypto state tiddler is up to date
if($tw.crypto) { if($tw.crypto) {
$tw.crypto.updateCryptoStateTiddler(); $tw.crypto.updateCryptoStateTiddler();
@@ -2621,11 +2650,13 @@ $tw.boot.executeNextStartupTask = function(callback) {
$tw.boot.log(s.join(" ")); $tw.boot.log(s.join(" "));
// Execute task // Execute task
if(!$tw.utils.hop(task,"synchronous") || task.synchronous) { if(!$tw.utils.hop(task,"synchronous") || task.synchronous) {
task.startup(); const thenable = task.startup();
if(task.name) { if(thenable && typeof thenable.then === "function"){
$tw.boot.executedStartupModules[task.name] = true; thenable.then(asyncTaskCallback);
return true;
} else {
return asyncTaskCallback();
} }
return $tw.boot.executeNextStartupTask(callback);
} else { } else {
task.startup(asyncTaskCallback); task.startup(asyncTaskCallback);
return true; return true;
+1
View File
@@ -6,6 +6,7 @@ talk.tiddlywiki.org: jeremyruston
github: Jermolene github: Jermolene
linkedin: www.linkedin.com/in/jermy linkedin: www.linkedin.com/in/jermy
flickr: www.flickr.com/photos/jermy/ flickr: www.flickr.com/photos/jermy/
bluesky: https://bsky.app/profile/jermolene.bsky.social
homepage: jermolene.com homepage: jermolene.com
email: jeremy@jermolene.com email: jeremy@jermolene.com
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAtAAEBAAMAAAAAAAAAAAAAAAAHBgIEBQEBAQEBAAAAAAAAAAAAAAAAAgQBBf/aAAwDAQACEAMQAAAANF4uTuPRhD2nBLnUiJvKM0DtMKy//8QAKxAAAgIBAwMDAQkAAAAAAAAAAQIDBBEABRITITEiMkFxFEJRUmFicoGR/9oACAEBAAE/AInTA6gUGP4ZOQbW1bPsmyUq1q+gmvFPUzZPDkPamtwqU75ks04JakroVcg5RwRjg66NUx25KbzqJYyMngfqSuq0M3NZYIebJIvZozIvI/iNPcp/aalSdJXsS4VcKeIzlvU3jVTcYLNiaGISrjkhWQYDfQ63pYAzCDBsOiu7Dsx4EHH6r2w2ttimjd2IsNErhhJHKI04/uzqxuCxpBYVVWKSHqwMyMSQ33SB7dUJFmlkMYRgnqZgCMf7rf8AeEt3A9YOhjXAb2k8u7dtT1RZeOtXmYxiOPj4ZWY/lb51skqUNnNW/wBNzC7IpB6gQeeB/jq/fqGOaLbowuYn5MAQOw8LjW5Vmeo0qIsqYLLKjHIZmwv9fB1//8QAHxEAAQMEAwEAAAAAAAAAAAAAEQABAgMSIWExMkFR/9oACAECAQE/AD9iTy2lJmHUB8BVKM4SNSOj46a29saX/8QAHREAAgICAwEAAAAAAAAAAAAAAQIAAwQRITGBkf/aAAgBAwEBPwDHpFpJZtamVSiBWT2Yt7hmCDsb+TKtsKqpGg3M/9k= avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAtAAEBAAMAAAAAAAAAAAAAAAAHBgIEBQEBAQEBAAAAAAAAAAAAAAAAAgQBBf/aAAwDAQACEAMQAAAANF4uTuPRhD2nBLnUiJvKM0DtMKy//8QAKxAAAgIBAwMDAQkAAAAAAAAAAQIDBBEABRITITEiMkFxFEJRUmFicoGR/9oACAEBAAE/AInTA6gUGP4ZOQbW1bPsmyUq1q+gmvFPUzZPDkPamtwqU75ks04JakroVcg5RwRjg66NUx25KbzqJYyMngfqSuq0M3NZYIebJIvZozIvI/iNPcp/aalSdJXsS4VcKeIzlvU3jVTcYLNiaGISrjkhWQYDfQ63pYAzCDBsOiu7Dsx4EHH6r2w2ttimjd2IsNErhhJHKI04/uzqxuCxpBYVVWKSHqwMyMSQ33SB7dUJFmlkMYRgnqZgCMf7rf8AeEt3A9YOhjXAb2k8u7dtT1RZeOtXmYxiOPj4ZWY/lb51skqUNnNW/wBNzC7IpB6gQeeB/jq/fqGOaLbowuYn5MAQOw8LjW5Vmeo0qIsqYLLKjHIZmwv9fB1//8QAHxEAAQMEAwEAAAAAAAAAAAAAEQABAgMSIWExMkFR/9oACAECAQE/AD9iTy2lJmHUB8BVKM4SNSOj46a29saX/8QAHREAAgICAwEAAAAAAAAAAAAAAQIAAwQRITGBkf/aAAgBAwEBPwDHpFpJZtamVSiBWT2Yt7hmCDsb+TKtsKqpGg3M/9k=
+8 -4
View File
@@ -99,16 +99,18 @@ Commander.prototype.executeNextCommand = function() {
} }
} }
if(command.info.synchronous) { if(command.info.synchronous) {
// Synchronous command // Synchronous command (await thenables)
c = new command.Command(params,this); c = new command.Command(params,this);
err = c.execute(); err = c.execute();
if(err) { if(err && typeof err.then === "function") {
err.then(e => { e ? this.callback(e) : this.executeNextCommand(); });
} else if(err) {
this.callback(err); this.callback(err);
} else { } else {
this.executeNextCommand(); this.executeNextCommand();
} }
} else { } else {
// Asynchronous command // Asynchronous command (await thenables)
c = new command.Command(params,this,function(err) { c = new command.Command(params,this,function(err) {
if(err) { if(err) {
self.callback(err); self.callback(err);
@@ -117,7 +119,9 @@ Commander.prototype.executeNextCommand = function() {
} }
}); });
err = c.execute(); err = c.execute();
if(err) { if(err && typeof err.then === "function") {
err.then(e => { if(e) this.callback(e); });
} else if(err) {
this.callback(err); this.callback(err);
} }
} }
+1
View File
@@ -4,3 +4,4 @@ This plugin contains TiddlyWiki's core components that are only needed on the se
* Commands * Commands
* HTTP server code * HTTP server code
* Utility functions for server
+5 -1
View File
@@ -8,10 +8,14 @@ DELETE /recipes/default/tiddlers/:title
\*/ \*/
"use strict"; "use strict";
exports.method = "DELETE"; exports.methods = ["DELETE"];
exports.path = /^\/bags\/default\/tiddlers\/(.+)$/; exports.path = /^\/bags\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]); var title = $tw.utils.decodeURIComponentSafe(state.params[0]);
state.wiki.deleteTiddler(title); state.wiki.deleteTiddler(title);
+5 -1
View File
@@ -8,10 +8,14 @@ GET /favicon.ico
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/favicon.ico$/; exports.path = /^\/favicon.ico$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var buffer = state.wiki.getTiddlerText("$:/favicon.ico",""); var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64"); state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64");
+46 -15
View File
@@ -8,35 +8,66 @@ GET /files/:filepath
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/files\/(.+)$/; exports.path = /^\/files\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var path = require("path"), var path = require("path"),
fs = require("fs"), fs = require("fs"),
util = require("util"),
suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]), suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]),
baseFilename = path.resolve(state.boot.wikiPath,"files"), baseFilename = path.resolve(state.boot.wikiPath,"files"),
filename = path.resolve(baseFilename,suppliedFilename), filename = path.resolve(baseFilename,suppliedFilename),
extension = path.extname(filename); extension = path.extname(filename);
// Check that the filename is inside the wiki files folder // Check that the filename is inside the wiki files folder
if(path.relative(baseFilename,filename).indexOf("..") !== 0) { if(path.relative(baseFilename,filename).indexOf("..") === 0) {
// Send the file return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
fs.readFile(filename,function(err,content) { }
var status,content,type = "text/plain"; fs.stat(filename, function(err, stats) {
if(err) { if(err) {
console.log("Error accessing file " + filename + ": " + err.toString()); return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
status = 404;
content = "File '" + suppliedFilename + "' not found";
} else { } else {
status = 200; var type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"),
content = content; responseHeaders = {
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"); "Content-Type": type,
"Accept-Ranges": "bytes"
};
var rangeHeader = request.headers.range,
stream;
if(rangeHeader) {
// Handle range requests
var parts = rangeHeader.replace(/bytes=/, "").split("-"),
start = parseInt(parts[0], 10),
end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;
// Validate start and end
if(isNaN(start) || isNaN(end) || start < 0 || end < start || end >= stats.size) {
responseHeaders["Content-Range"] = "bytes */" + stats.size;
return response.writeHead(416, responseHeaders).end();
} }
state.sendResponse(status,{"Content-Type": type},content); var chunksize = (end - start) + 1;
responseHeaders["Content-Range"] = "bytes " + start + "-" + end + "/" + stats.size;
responseHeaders["Content-Length"] = chunksize;
response.writeHead(206, responseHeaders);
stream = fs.createReadStream(filename, {start: start, end: end});
} else {
responseHeaders["Content-Length"] = stats.size;
response.writeHead(200, responseHeaders);
stream = fs.createReadStream(filename);
}
// Common stream error handling
stream.on("error", function(err) {
if(!response.headersSent) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.end("Read error");
} else {
response.destroy();
}
}); });
} else { stream.pipe(response);
state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
} }
});
}; };
+5 -1
View File
@@ -8,10 +8,14 @@ GET /
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/$/; exports.path = /^\/$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")), var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
responseHeaders = { responseHeaders = {
+5 -1
View File
@@ -8,10 +8,14 @@ GET /login-basic -- force a Basic Authentication challenge
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/login-basic$/; exports.path = /^\/login-basic$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
if(!state.authenticatedUsername) { if(!state.authenticatedUsername) {
// Challenge if there's no username // Challenge if there's no username
+5 -1
View File
@@ -8,10 +8,14 @@ GET /status
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/status$/; exports.path = /^\/status$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var text = JSON.stringify({ var text = JSON.stringify({
username: state.authenticatedUsername || state.server.get("anon-username") || "", username: state.authenticatedUsername || state.server.get("anon-username") || "",
@@ -8,10 +8,14 @@ GET /:title
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/([^\/]+)$/; exports.path = /^\/([^\/]+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]), var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
tiddler = state.wiki.getTiddler(title); tiddler = state.wiki.getTiddler(title);
+5 -1
View File
@@ -8,10 +8,14 @@ GET /recipes/default/tiddlers/:title
\*/ \*/
"use strict"; "use strict";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/; exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]), var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
tiddler = state.wiki.getTiddler(title), tiddler = state.wiki.getTiddler(title),
@@ -10,10 +10,14 @@ GET /recipes/default/tiddlers.json?filter=<filter>
var DEFAULT_FILTER = "[all[tiddlers]!is[system]sort[title]]"; var DEFAULT_FILTER = "[all[tiddlers]!is[system]sort[title]]";
exports.method = "GET"; exports.methods = ["GET"];
exports.path = /^\/recipes\/default\/tiddlers.json$/; exports.path = /^\/recipes\/default\/tiddlers.json$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var filter = state.queryParameters.filter || DEFAULT_FILTER; var filter = state.queryParameters.filter || DEFAULT_FILTER;
if(state.wiki.getTiddlerText("$:/config/Server/AllowAllExternalFilters") !== "yes") { if(state.wiki.getTiddlerText("$:/config/Server/AllowAllExternalFilters") !== "yes") {
+5 -1
View File
@@ -8,10 +8,14 @@ PUT /recipes/default/tiddlers/:title
\*/ \*/
"use strict"; "use strict";
exports.method = "PUT"; exports.methods = ["PUT"];
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/; exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.info = {
priority: 100
};
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]), var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
fields = $tw.utils.parseJSONSafe(state.data); fields = $tw.utils.parseJSONSafe(state.data);
+6 -1
View File
@@ -74,6 +74,11 @@ function Server(options) {
// console.log("Loading server route " + title); // console.log("Loading server route " + title);
self.addRoute(routeDefinition); self.addRoute(routeDefinition);
}); });
this.routes.sort((a, b) => {
const priorityA = a.info?.priority ?? 100,
priorityB = b.info?.priority ?? 100;
return priorityB - priorityA;
});
// Initialise the http vs https // Initialise the http vs https
this.listenOptions = null; this.listenOptions = null;
this.protocol = "http"; this.protocol = "http";
@@ -217,7 +222,7 @@ Server.prototype.findMatchingRoute = function(request,state) {
} else { } else {
match = potentialRoute.path.exec(pathname); match = potentialRoute.path.exec(pathname);
} }
if(match && request.method === potentialRoute.method) { if(match && (potentialRoute.methods?.includes(request.method) || potentialRoute.method === request.method)) {
state.params = []; state.params = [];
for(var p=1; p<match.length; p++) { for(var p=1; p<match.length; p++) {
state.params.push(match[p]); state.params.push(match[p]);
+1 -1
View File
@@ -147,7 +147,7 @@ Settings/AutoSave/Disabled/Description: Do not save changes automatically
Settings/AutoSave/Enabled/Description: Save changes automatically Settings/AutoSave/Enabled/Description: Save changes automatically
Settings/AutoSave/Hint: Attempt to automatically save changes during editing when using a supporting saver Settings/AutoSave/Hint: Attempt to automatically save changes during editing when using a supporting saver
Settings/CamelCase/Caption: Camel Case Wiki Links Settings/CamelCase/Caption: Camel Case Wiki Links
Settings/CamelCase/Hint: You can globally disable automatic linking of ~CamelCase phrases. Requires reload to take effect Settings/CamelCase/Hint: Requires reload to take effect
Settings/CamelCase/Description: Enable automatic ~CamelCase linking Settings/CamelCase/Description: Enable automatic ~CamelCase linking
Settings/Caption: Settings Settings/Caption: Settings
Settings/EditorToolbar/Caption: Editor Toolbar Settings/EditorToolbar/Caption: Editor Toolbar
+1 -1
View File
@@ -34,7 +34,7 @@ function FramedEngine(options) {
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette"); var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette");
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light"; var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
this.iframeDoc.open(); this.iframeDoc.open();
this.iframeDoc.write("<meta name='color-scheme' content='" + colorScheme + "'>"); this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
this.iframeDoc.close(); this.iframeDoc.close();
// Style the iframe // Style the iframe
this.iframeNode.className = this.dummyTextArea.className; this.iframeNode.className = this.dummyTextArea.className;
+1 -1
View File
@@ -68,7 +68,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
// Fix height // Fix height
this.engine.fixHeight(); this.engine.fixHeight();
// Focus if required // Focus if required
if(this.editFocus === "true" || this.editFocus === "yes") { if($tw.browser && (this.editFocus === "true" || this.editFocus === "yes") && !$tw.utils.hasClass(this.parentDomNode.ownerDocument.activeElement,"tc-keep-focus")) {
this.engine.focus(); this.engine.focus();
} }
// Add widget message listeners // Add widget message listeners
+2 -6
View File
@@ -16,12 +16,8 @@ exports.json = function(source,operand,options) {
spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand; spaces = /^\d+$/.test(operand) ? parseInt(operand,10) : operand;
} }
source(function(tiddler,title) { source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title); var data = $tw.utils.parseJSONSafe(title,function(){return undefined;});
try {
data = JSON.parse(title);
} catch(e) {
data = undefined;
}
if(data !== undefined) { if(data !== undefined) {
results.push(JSON.stringify(data,null,spaces)); results.push(JSON.stringify(data,null,spaces));
} }
+4
View File
@@ -217,6 +217,10 @@ function makeNumericReducingOperator(fnCalc,initialValue,fnFinal) {
source(function(tiddler,title) { source(function(tiddler,title) {
result.push($tw.utils.parseNumber(title)); result.push($tw.utils.parseNumber(title));
}); });
// We return an empty array if there are no input titles
if(result.length === 0) {
return [];
}
var value = result.reduce(function(accumulator,currentValue) { var value = result.reduce(function(accumulator,currentValue) {
return fnCalc(accumulator,currentValue); return fnCalc(accumulator,currentValue);
},initialValue); },initialValue);
+86
View File
@@ -0,0 +1,86 @@
/*\
title: $:/core/modules/info/windowdimensions.js
type: application/javascript
module-type: info
\*/
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
if(!$tw.browser) {
return [];
}
class WindowDimensionsTracker {
constructor(updateCallback) {
this.updateCallback = updateCallback;
this.resizeHandlers = new Map();
this.dimensionsInfo = [
["outer/width", win => win.outerWidth],
["outer/height", win => win.outerHeight],
["inner/width", win => win.innerWidth],
["inner/height", win => win.innerHeight],
["client/width", win => win.document.documentElement.clientWidth],
["client/height", win => win.document.documentElement.clientHeight]
];
}
buildTiddlers(win,windowId) {
const prefix = `$:/info/browser/window/${windowId}/`;
return this.dimensionsInfo.map(([suffix, getter]) => ({
title: prefix + suffix,
text: String(getter(win))
}));
}
clearTiddlers(windowId) {
const prefix = `$:/info/browser/window/${windowId}/`,
deletions = this.dimensionsInfo.map(([suffix]) => prefix + suffix);
this.updateCallback([], deletions);
}
getUpdateHandler(win,windowId) {
let scheduled = false;
return () => {
if(!scheduled) {
scheduled = true;
requestAnimationFrame(() => {
this.updateCallback(this.buildTiddlers(win,windowId), []);
scheduled = false;
});
}
};
}
trackWindow(win,windowId) {
const handler = this.getUpdateHandler(win, windowId);
handler(); // initial update
win.addEventListener("resize",handler,{passive:true});
this.resizeHandlers.set(windowId,{win, handler});
}
untrackWindow(windowId) {
const entry = this.resizeHandlers.get(windowId);
if(entry) {
entry.win.removeEventListener("resize", entry.handler);
this.resizeHandlers.delete(windowId);
}
this.clearTiddlers(windowId);
}
}
const tracker = new WindowDimensionsTracker(updateInfoTiddlersCallback);
// Track main window
tracker.trackWindow(window,"system/main");
// Hook into event bus for user windows
if($tw.eventBus) {
$tw.eventBus.on("window:opened", ({window: win, windowID}) => {
tracker.trackWindow(win, "user/" + windowID);
});
$tw.eventBus.on("window:closed", ({windowID}) => {
tracker.untrackWindow("user/" + windowID);
});
}
return [];
};
+15 -3
View File
@@ -7,23 +7,34 @@ The audio parser parses an audio tiddler into an embeddable HTML element
\*/ \*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict"; "use strict";
var AudioParser = function(type,text,options) { var AudioParser = function(type,text,options) {
var element = { var element = {
type: "element", type: "element",
tag: "audio", tag: "$audio", // Using $audio to enable widget interception
attributes: { attributes: {
controls: {type: "string", value: "controls"}, controls: {type: "string", value: "controls"},
style: {type: "string", value: "width: 100%; object-fit: contain"} style: {type: "string", value: "width: 100%; object-fit: contain"}
} }
}, };
src;
// Pass through source information
if(options._canonical_uri) { if(options._canonical_uri) {
element.attributes.src = {type: "string", value: options._canonical_uri}; element.attributes.src = {type: "string", value: options._canonical_uri};
element.attributes.type = {type: "string", value: type};
} else if(text) { } else if(text) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text}; element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
element.attributes.type = {type: "string", value: type};
} }
// Pass through tiddler title if available
if(options.title) {
element.attributes.tiddler = {type: "string", value: options.title};
}
this.tree = [element]; this.tree = [element];
this.source = text; this.source = text;
this.type = type; this.type = type;
@@ -33,3 +44,4 @@ exports["audio/ogg"] = AudioParser;
exports["audio/mpeg"] = AudioParser; exports["audio/mpeg"] = AudioParser;
exports["audio/mp3"] = AudioParser; exports["audio/mp3"] = AudioParser;
exports["audio/mp4"] = AudioParser; exports["audio/mp4"] = AudioParser;
+9 -8
View File
@@ -82,6 +82,7 @@ exports.parseTokenString = function(source,pos,token) {
/* /*
Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,} Look for a token matching a regex. Returns null if not found, otherwise returns {type: "regexp", match:, start:, end:,}
Use the "Y" (sticky) flag to avoid searching the entire rest of the string
*/ */
exports.parseTokenRegExp = function(source,pos,reToken) { exports.parseTokenRegExp = function(source,pos,reToken) {
var node = { var node = {
@@ -172,7 +173,7 @@ exports.parseMacroParameter = function(source,pos) {
start: pos start: pos
}; };
// Define our regexp // Define our regexp
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/g; const reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for the parameter // Look for the parameter
@@ -240,7 +241,7 @@ exports.parseMacroInvocation = function(source,pos) {
params: [] params: []
}; };
// Define our regexps // Define our regexps
var reMacroName = /([^\s>"'=]+)/g; const reMacroName = /([^\s>"'=]+)/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a double less than sign // Look for a double less than sign
@@ -277,7 +278,7 @@ exports.parseFilterVariable = function(source) {
params: [], params: [],
}, },
pos = 0, pos = 0,
reName = /([^\s"']+)/g; reName = /([^\s"']+)/y;
// If there is no whitespace or it is an empty string then there are no macro parameters // If there is no whitespace or it is an empty string then there are no macro parameters
if(/^\S*$/.test(source)) { if(/^\S*$/.test(source)) {
node.name = source; node.name = source;
@@ -302,11 +303,11 @@ exports.parseAttribute = function(source,pos) {
start: pos start: pos
}; };
// Define our regexps // Define our regexps
var reAttributeName = /([^\/\s>"'`=]+)/g, const reAttributeName = /([^\/\s>"'`=]+)/y,
reUnquotedAttribute = /([^\/\s<>"'`=]+)/g, reUnquotedAttribute = /([^\/\s<>"'`=]+)/y,
reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g, reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/y,
reIndirectValue = /\{\{([^\}]+)\}\}/g, reIndirectValue = /\{\{([^\}]+)\}\}/y,
reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g; reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Get the attribute name // Get the attribute name
@@ -43,9 +43,18 @@ exports.findNextMatch = function(startPos) {
return undefined; return undefined;
}; };
exports.parse = function() { exports.parse = function() {
// Move past the match // Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex; this.parser.pos = this.endMatchRegExp.lastIndex;
// Don't return any elements // Return a node representing the comment that is not rendered
return []; var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
children: [],
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
}; };
@@ -40,6 +40,13 @@ exports.findNextMatch = function(startPos) {
exports.parse = function() { exports.parse = function() {
// Move past the match // Move past the match
this.parser.pos = this.endMatchRegExp.lastIndex; this.parser.pos = this.endMatchRegExp.lastIndex;
// Don't return any elements // Return a node representing the inline comment
return []; var commentStart = this.match.index;
var commentEnd = this.endMatch.index + this.endMatch[0].length;
return [{
type: "void",
text: this.parser.source.slice(commentStart, commentEnd),
start: commentStart,
end: commentEnd
}];
}; };
@@ -39,7 +39,7 @@ exports.parse = function() {
// Return the classed span // Return the classed span
return [{ return [{
type: "element", type: "element",
tag: "strike", tag: "s",
children: tree children: tree
}]; }];
}; };
@@ -32,7 +32,7 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Create the link unless it is suppressed // Create the link unless it is suppressed
if(this.match[0].substr(0,1) === "~") { 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 { } else {
return [{ return [{
type: "element", type: "element",
@@ -6,15 +6,15 @@ module-type: wikirule
Wiki pragma rule for function, procedure and widget definitions Wiki pragma rule for function, procedure and widget definitions
``` ```
\function name(param:defaultvalue,param2:defaultvalue) \function name(param:"defaultvalue", param2:"defaultvalue")
definition text definition text
\end \end
\procedure name(param:defaultvalue,param2:defaultvalue) \procedure name(param:"defaultvalue", param2:"defaultvalue")
definition text definition text
\end \end
\widget $mywidget(param:defaultvalue,param2:defaultvalue) \widget $mywidget(param:"defaultvalue", param2:"defaultvalue")
definition text definition text
\end \end
``` ```
@@ -50,6 +50,8 @@ exports.parse = function() {
} }
} }
} while(match && !match[1]); } 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; return tree;
}; };
@@ -49,7 +49,7 @@ exports.parse = function() {
// Advance the parser position to past the tag // Advance the parser position to past the tag
this.parser.pos = tag.end; this.parser.pos = tag.end;
// Check for an immediately following double linebreak // Check for an immediately following double linebreak
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g); var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
// Set whether we're in block mode // Set whether we're in block mode
tag.isBlock = this.is.block || hasLineBreak; tag.isBlock = this.is.block || hasLineBreak;
// Parse the body if we need to // Parse the body if we need to
@@ -63,7 +63,7 @@ exports.parse = function() {
} }
tag.end = this.parser.pos; tag.end = this.parser.pos;
tag.closeTagEnd = tag.end; 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; tag.closeTagStart = tag.end;
} else { } else {
tag.closeTagStart = tag.closeTagEnd - 2; tag.closeTagStart = tag.closeTagEnd - 2;
@@ -71,11 +71,11 @@ exports.parse = function() {
if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd; if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
while(tag.closeTagStart >= closeTagMinPos) { while(tag.closeTagStart >= closeTagMinPos) {
var char = this.parser.source[tag.closeTagStart]; var char = this.parser.source[tag.closeTagStart];
if (char === '>') { if(char === ">") {
tag.closeTagStart = -1; tag.closeTagStart = -1;
break; break;
} }
if (char === '<') break; if(char === "<") break;
tag.closeTagStart -= 1; tag.closeTagStart -= 1;
} }
if(tag.closeTagStart < closeTagMinPos) { if(tag.closeTagStart < closeTagMinPos) {
@@ -100,7 +100,7 @@ exports.parseTag = function(source,pos,options) {
orderedAttributes: [] orderedAttributes: []
}; };
// Define our regexps // Define our regexps
var reTagName = /([a-zA-Z0-9\-\$\.]+)/g; const reTagName = /([a-zA-Z0-9\-\$\.]+)/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a less than sign // Look for a less than sign
@@ -148,7 +148,7 @@ exports.parseTag = function(source,pos,options) {
pos = token.end; pos = token.end;
// Check for a required line break // Check for a required line break
if(options.requireLineBreak) { if(options.requireLineBreak) {
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g); token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
if(!token) { if(!token) {
return null; return null;
} }
@@ -113,7 +113,7 @@ exports.parseImage = function(source,pos) {
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Get the source up to the terminating `]]` // Get the source up to the terminating `]]`
token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g); token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/y);
if(!token) { if(!token) {
return null; return null;
} }
@@ -59,6 +59,7 @@ var listTypes = {
":": {listTag: "dl", itemTag: "dd"}, ":": {listTag: "dl", itemTag: "dd"},
">": {listTag: "blockquote", itemTag: "div"} ">": {listTag: "blockquote", itemTag: "div"}
}; };
exports.listTypes = listTypes;
/* /*
Parse the most recent match Parse the most recent match
@@ -42,3 +42,5 @@ exports.parse = function() {
this.parser.pos = call.end; this.parser.pos = call.end;
return [call]; return [call];
}; };
@@ -52,9 +52,10 @@ exports.parse = function() {
} }
} }
// Is the remainder of the \define line blank after the parameter close paren? // Is the remainder of the \define line blank after the parameter close paren?
var reEnd; var reEnd,isBlock = true;
if(this.match[3]) { if(this.match[3]) {
// If so, it is a multiline definition and the end of the body is marked with \end // 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"); reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?\\s*?(?:$|\\r?\\n))","mg");
} else { } else {
// Otherwise, the end of the definition is marked by the end of the line // Otherwise, the end of the definition is marked by the end of the line
@@ -79,7 +80,8 @@ exports.parse = function() {
attributes: {}, attributes: {},
children: [], children: [],
params: params, params: params,
isMacroDefinition: true isMacroDefinition: true,
isBlock: isBlock && !!endMatch
}]; }];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]); $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text); $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
@@ -31,6 +31,7 @@ Parse the most recent match
*/ */
exports.parse = function() { exports.parse = function() {
// Move past the pragma invocation // Move past the pragma invocation
var start = this.parser.pos;
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Parse whitespace delimited tokens terminated by a line break // Parse whitespace delimited tokens terminated by a line break
var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg, var reMatch = /[^\S\n]*(\S+)|(\r?\n)/mg,
@@ -58,6 +59,11 @@ exports.parse = function() {
this.parser.parseAsInline = true; this.parser.parseAsInline = true;
} }
} }
// No parse tree nodes to return return [{
return []; type: "void",
children: [],
parseAsInline: this.parser.parseAsInline,
start: start,
end: this.parser.pos
}];
}; };
@@ -113,3 +113,5 @@ exports.parseLink = function(source,pos) {
node.end = closePos + 2; node.end = closePos + 2;
return node; return node;
}; };
@@ -54,6 +54,13 @@ exports.parse = function() {
if(tokens.length > 0) { if(tokens.length > 0) {
this.parser.amendRules(tokens[0],tokens.slice(1)); this.parser.amendRules(tokens[0],tokens.slice(1));
} }
// No parse tree nodes to return // No widget to render, return void node.
return []; return [{
type: "void",
attributes: {
action: {type: "string", value: tokens[0]},
rules: {type: "string", value: tokens.slice(1).join(" ")}
},
children: []
}];
}; };
@@ -64,5 +64,8 @@ exports.parse = function() {
$tw.utils.addAttributeToParseTreeNode(tree[t],"style",styles.join("")); $tw.utils.addAttributeToParseTreeNode(tree[t],"style",styles.join(""));
} }
} }
return tree; return [{
type: "void",
children: tree
}]
}; };
@@ -21,7 +21,7 @@ exports.types = {inline: true};
exports.init = function(parser) { exports.init = function(parser) {
this.parser = 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; this.matchRegExp = /@@((?:[^\.\r\n\s:]+:[^\r\n;]+;)+)?(\.(?:[^\r\n\s]+)\s+)?/mg;
}; };
@@ -23,6 +23,27 @@ exports.init = function(parser) {
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg; this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
}; };
/*
Reject the match if we don't have a template or text reference
*/
exports.findNextMatch = function(startPos) {
this.matchRegExp.lastIndex = startPos;
this.match = this.matchRegExp.exec(this.parser.source);
if(this.match) {
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
// Bail if we don't have a template or text reference
if(!template && !textRef) {
return undefined;
} else {
return this.match.index;
}
} else {
return undefined;
}
return this.match ? this.match.index : undefined;
};
exports.parse = function() { exports.parse = function() {
// Move past the match // Move past the match
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
@@ -23,6 +23,27 @@ exports.init = function(parser) {
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg; this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
}; };
/*
Reject the match if we don't have a template or text reference
*/
exports.findNextMatch = function(startPos) {
this.matchRegExp.lastIndex = startPos;
this.match = this.matchRegExp.exec(this.parser.source);
if(this.match) {
var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]);
// Bail if we don't have a template or text reference
if(!template && !textRef) {
return undefined;
} else {
return this.match.index;
}
} else {
return undefined;
}
return this.match ? this.match.index : undefined;
};
exports.parse = function() { exports.parse = function() {
// Move past the match // Move past the match
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
@@ -60,22 +60,37 @@ exports.parse = function() {
var parser = this.parser.wiki.parseText(parseType,text,{defaultType: "text/plain"}); var parser = this.parser.wiki.parseText(parseType,text,{defaultType: "text/plain"});
// If there's no render type, just return the parse tree // If there's no render type, just return the parse tree
if(!renderType) { 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 { } else {
// Otherwise, render to the rendertype and return in a <PRE> tag // Otherwise, render to the rendertype and return in a <PRE> tag
var widgetNode = this.parser.wiki.makeWidget(parser), var widgetNode = this.parser.wiki.makeWidget(parser),
container = $tw.fakeDocument.createElement("div"); container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null); 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 [{ return [{
type: "void",
children: [{
type: "element", type: "element",
tag: "pre", tag: "pre",
children: [{ children: [{
type: "text", type: "text",
text: renderResult,
}]
}],
parseType: parseType,
renderType: renderType,
text: text, text: text,
start: start, start: start,
end: this.parser.pos end: this.parser.pos
}]
}]; }];
} }
}; };
@@ -256,7 +256,7 @@ WikiParser.prototype.parseBlock = function(terminatorRegExpString) {
var start = this.pos; var start = this.pos;
var children = this.parseInlineRun(terminatorRegExp); var children = this.parseInlineRun(terminatorRegExp);
var end = this.pos; 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" }];
}; };
/* /*
+3 -1
View File
@@ -35,7 +35,9 @@ DownloadSaver.prototype.save = function(text,method,callback,options) {
} }
// Set up the link // Set up the link
var link = document.createElement("a"); var link = document.createElement("a");
if(Blob !== undefined) { // We prefer Blobs if they're available, unless we're dealing with a tiddler type declaring itself full of base64 encoded content.
// Then we use data urls, because browsers will know to decode the stream and download the actual binary file as intended.
if(Blob !== undefined && !type.includes(";base64")) {
var blob = new Blob([text], {type: type}); var blob = new Blob([text], {type: type});
link.setAttribute("href", URL.createObjectURL(blob)); link.setAttribute("href", URL.createObjectURL(blob));
} else { } else {
+66
View File
@@ -0,0 +1,66 @@
/*\
title: $:/core/modules/savers/postmessage.js
type: application/javascript
module-type: saver
Handles saving changes via window.postMessage() to the window.parent
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Select the appropriate saver module and set it up
*/
var PostMessageSaver = function(wiki) {
this.publisher = new $tw.utils.BrowserMessagingPublisher({type: "SAVE"});
};
PostMessageSaver.prototype.save = function(text,method,callback,options) {
// Fail if the publisher hasn't been fully initialised
if(!this.publisher.canSend()) {
return false;
}
// Send the save request
this.publisher.send({
verb: "SAVE",
body: text
},function(err) {
if(err) {
callback("PostMessageSaver Error: " + err);
} else {
callback(null);
}
});
// Indicate that we handled the save
return true;
};
/*
Information about this saver
*/
PostMessageSaver.prototype.info = {
name: "postmessage",
capabilities: ["save", "autosave"],
priority: 100
};
/*
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
// Provisionally say that we can save
return true;
};
/*
Create an instance of this saver
*/
exports.create = function(wiki) {
return new PostMessageSaver(wiki);
};
})();
+46
View File
@@ -0,0 +1,46 @@
/*\
title: $:/core/modules/startup/eventbus.js
type: application/javascript
module-type: startup
Event bus for cross module communication
\*/
exports.name = "eventbus";
exports.platforms = ["browser"];
exports.before = ["windows"];
exports.synchronous = true;
$tw.eventBus = {
listenersMap: new Map(),
on(event,handler) {
if(!this.listenersMap.has(event)) {
this.listenersMap.set(event,new Set());
}
const listeners = this.listenersMap.get(event);
listeners.add(handler);
},
off(event,handler) {
const listeners = this.listenersMap.get(event);
if(listeners) {
listeners.delete(handler);
}
},
once(event,handler) {
const wrapper = (...args) => {
handler(...args);
this.off(event, wrapper);
};
this.on(event, wrapper);
},
emit(event,data) {
const listeners = this.listenersMap.get(event);
if(listeners) {
listeners.forEach(fn => fn(data));
}
}
};
+10 -8
View File
@@ -19,6 +19,16 @@ exports.synchronous = true;
var FAVICON_TITLE = "$:/favicon.ico"; var FAVICON_TITLE = "$:/favicon.ico";
exports.startup = function() { exports.startup = function() {
var setFavicon = function() {
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
if(tiddler) {
var faviconLink = document.getElementById("faviconLink"),
dataURI = $tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri);
faviconLink.setAttribute("href",dataURI);
$tw.faviconPublisher.send({verb: "FAVICON",body: dataURI});
}
}
$tw.faviconPublisher = new $tw.utils.BrowserMessagingPublisher({type: "FAVICON", onsubscribe: setFavicon});
// Set up the favicon // Set up the favicon
setFavicon(); setFavicon();
// Reset the favicon when the tiddler changes // Reset the favicon when the tiddler changes
@@ -28,11 +38,3 @@ exports.startup = function() {
} }
}); });
}; };
function setFavicon() {
var tiddler = $tw.wiki.getTiddler(FAVICON_TITLE);
if(tiddler) {
var faviconLink = document.getElementById("faviconLink");
faviconLink.setAttribute("href",$tw.utils.makeDataUri(tiddler.fields.text,tiddler.fields.type,tiddler.fields._canonical_uri));
}
}
+10 -4
View File
@@ -19,11 +19,17 @@ var TITLE_INFO_PLUGIN = "$:/temp/info-plugin";
exports.startup = function() { exports.startup = function() {
// Function to bake the info plugin with new tiddlers // Function to bake the info plugin with new tiddlers
var updateInfoPlugin = function(tiddlerFieldsArray) { // additions: array of tiddler field objects
// removals: array of titles to remove
var updateInfoPlugin = function(additions = [], removals = []) {
// Get the existing tiddlers // Get the existing tiddlers
var json = $tw.wiki.getTiddlerData(TITLE_INFO_PLUGIN,{tiddlers: {}}); var json = $tw.wiki.getTiddlerData(TITLE_INFO_PLUGIN,{tiddlers: {}});
// Add the new ones $tw.utils.each(removals,function(title) {
$tw.utils.each(tiddlerFieldsArray,function(fields) { if(json.tiddlers[title]) {
delete json.tiddlers[title];
}
});
$tw.utils.each(additions,function(fields) {
if(fields && fields.title) { if(fields && fields.title) {
json.tiddlers[fields.title] = fields; json.tiddlers[fields.title] = fields;
} }
@@ -47,7 +53,7 @@ exports.startup = function() {
} }
}); });
updateInfoPlugin(tiddlerFieldsArray); updateInfoPlugin(tiddlerFieldsArray);
var changes = $tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]); $tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
$tw.wiki.registerPluginTiddlers("info",[TITLE_INFO_PLUGIN]); $tw.wiki.registerPluginTiddlers("info",[TITLE_INFO_PLUGIN]);
$tw.wiki.unpackPluginTiddlers(); $tw.wiki.unpackPluginTiddlers();
}; };
+6 -1
View File
@@ -33,10 +33,15 @@ exports.startup = function() {
}); });
$tw.titleContainer = $tw.fakeDocument.createElement("div"); $tw.titleContainer = $tw.fakeDocument.createElement("div");
$tw.titleWidgetNode.render($tw.titleContainer,null); $tw.titleWidgetNode.render($tw.titleContainer,null);
var publishTitle = function() {
$tw.titlePublisher.send({verb: "PAGETITLE",body: document.title});
document.title = $tw.titleContainer.textContent; document.title = $tw.titleContainer.textContent;
};
$tw.titlePublisher = new $tw.utils.BrowserMessagingPublisher({type: "PAGETITLE", onsubscribe: publishTitle});
publishTitle();
$tw.wiki.addEventListener("change",function(changes) { $tw.wiki.addEventListener("change",function(changes) {
if($tw.titleWidgetNode.refresh(changes,$tw.titleContainer,null)) { if($tw.titleWidgetNode.refresh(changes,$tw.titleContainer,null)) {
document.title = $tw.titleContainer.textContent; publishTitle();
} }
}); });
// Set up the styles // Set up the styles
+2
View File
@@ -56,9 +56,11 @@ exports.startup = function() {
srcDocument.write("<!DOCTYPE html><head></head><body class='tc-body tc-single-tiddler-window'></body></html>"); srcDocument.write("<!DOCTYPE html><head></head><body class='tc-body tc-single-tiddler-window'></body></html>");
srcDocument.close(); srcDocument.close();
srcDocument.title = windowTitle; srcDocument.title = windowTitle;
$tw.eventBus.emit("window:opened",{windowID, window: srcWindow});
srcWindow.addEventListener("beforeunload",function(event) { srcWindow.addEventListener("beforeunload",function(event) {
delete $tw.windows[windowID]; delete $tw.windows[windowID];
$tw.wiki.removeEventListener("change",refreshHandler); $tw.wiki.removeEventListener("change",refreshHandler);
$tw.eventBus.emit("window:closed",{windowID});
},false); },false);
// Set up the styles // Set up the styles
var styleWidgetNode = $tw.wiki.makeTranscludeWidget("$:/core/ui/PageStylesheet",{ var styleWidgetNode = $tw.wiki.makeTranscludeWidget("$:/core/ui/PageStylesheet",{
+3 -5
View File
@@ -47,16 +47,16 @@ ClassicStoryView.prototype.insert = function(widget) {
// Reset the margin once the transition is over // Reset the margin once the transition is over
setTimeout(function() { setTimeout(function() {
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: ""} {marginBottom: ""}
]); ]);
$tw.utils.removeStyle(targetElement, "transition");
},duration); },duration);
// Set up the initial position of the element // Set up the initial position of the element
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: "none"},
{marginBottom: (-currHeight) + "px"}, {marginBottom: (-currHeight) + "px"},
{opacity: "0.0"} {opacity: "0.0"}
]); ]);
$tw.utils.removeStyle(targetElement, "transition");
$tw.utils.forceLayout(targetElement); $tw.utils.forceLayout(targetElement);
// Transition to the final position // Transition to the final position
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
@@ -94,11 +94,9 @@ ClassicStoryView.prototype.remove = function(widget) {
setTimeout(removeElement,duration); setTimeout(removeElement,duration);
// Animate the closure // Animate the closure
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "translateX(0px)"},
{marginBottom: currMarginBottom + "px"}, {marginBottom: currMarginBottom + "px"},
{opacity: "1.0"}
]); ]);
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
$tw.utils.forceLayout(targetElement); $tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms " + easing + ", " + {transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms " + easing + ", " +
+6 -10
View File
@@ -37,10 +37,7 @@ PopStoryView.prototype.insert = function(widget) {
} }
// Reset once the transition is over // Reset once the transition is over
setTimeout(function() { setTimeout(function() {
$tw.utils.setStyle(targetElement,[ $tw.utils.removeStyles(targetElement, ["transition", "transform"]);
{transition: "none"},
{transform: "none"}
]);
$tw.utils.setStyle(widget.document.body,[ $tw.utils.setStyle(widget.document.body,[
{"overflow-x": ""} {"overflow-x": ""}
]); ]);
@@ -51,10 +48,10 @@ PopStoryView.prototype.insert = function(widget) {
]); ]);
// Set up the initial position of the element // Set up the initial position of the element
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: "none"},
{transform: "scale(2)"}, {transform: "scale(2)"},
{opacity: "0.0"} {opacity: "0.0"}
]); ]);
$tw.utils.removeStyle(targetElement, "transition");
$tw.utils.forceLayout(targetElement); $tw.utils.forceLayout(targetElement);
// Transition to the final position // Transition to the final position
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
@@ -63,6 +60,9 @@ PopStoryView.prototype.insert = function(widget) {
{transform: "scale(1)"}, {transform: "scale(1)"},
{opacity: "1.0"} {opacity: "1.0"}
]); ]);
setTimeout(function() {
$tw.utils.removeStyles(targetElement, ["transition", "transform", "opactity"]);
}, duration)
}; };
PopStoryView.prototype.remove = function(widget) { PopStoryView.prototype.remove = function(widget) {
@@ -81,11 +81,7 @@ PopStoryView.prototype.remove = function(widget) {
// Remove the element at the end of the transition // Remove the element at the end of the transition
setTimeout(removeElement,duration); setTimeout(removeElement,duration);
// Animate the closure // Animate the closure
$tw.utils.setStyle(targetElement,[ $tw.utils.removeStyles(targetElement, ["transition", "transform", "opacity"]);
{transition: "none"},
{transform: "scale(1)"},
{opacity: "1.0"}
]);
$tw.utils.forceLayout(targetElement); $tw.utils.forceLayout(targetElement);
$tw.utils.setStyle(targetElement,[ $tw.utils.setStyle(targetElement,[
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " + {transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " +
+6
View File
@@ -96,6 +96,9 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
{transform: "translateX(0px) translateY(0px) scale(1)"}, {transform: "translateX(0px) translateY(0px) scale(1)"},
{zIndex: "500"}, {zIndex: "500"},
]); ]);
setTimeout(function() {
$tw.utils.removeStyles(targetElement, ["transition", "opacity", "transform", "zIndex"]);
}, duration);
// Transform the previous tiddler out of the way and then hide it // Transform the previous tiddler out of the way and then hide it
if(prevCurrentTiddler && prevCurrentTiddler !== targetElement) { if(prevCurrentTiddler && prevCurrentTiddler !== targetElement) {
scale = zoomBounds.width / sourceBounds.width; scale = zoomBounds.width / sourceBounds.width;
@@ -207,6 +210,9 @@ ZoominListView.prototype.remove = function(widget) {
{opacity: "0"}, {opacity: "0"},
{zIndex: "0"} {zIndex: "0"}
]); ]);
setTimeout(function() {
$tw.utils.removeStyles(toWidgetDomNode, ["transformOrigin", "transform", "transition", "opacity", "zIndex"]);
}, duration);
setTimeout(removeElement,duration); setTimeout(removeElement,duration);
// Now the tiddler we're going back to // Now the tiddler we're going back to
if(toWidgetDomNode) { if(toWidgetDomNode) {
@@ -1,142 +0,0 @@
// From https://gist.github.com/Nijikokun/5192472
//
// UTF8 Module
//
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
(function (name, definition, context, dependencies) {
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
else { context[name] = definition.apply(context); }
})('utf8', function () {
return {
encode: function (string) {
if (typeof string !== 'string') return string;
else string = string.replace(/\r\n/g, "\n");
var output = "", i = 0, charCode;
for (i; i < string.length; i++) {
charCode = string.charCodeAt(i);
if (charCode < 128) {
output += String.fromCharCode(charCode);
} else if ((charCode > 127) && (charCode < 2048)) {
output += String.fromCharCode((charCode >> 6) | 192);
output += String.fromCharCode((charCode & 63) | 128);
} else if ((charCode > 55295) && (charCode < 57344) && string.length > i+1) {
// Surrogate pair
var hiSurrogate = charCode;
var loSurrogate = string.charCodeAt(i+1);
i++; // Skip the low surrogate on the next loop pass
var codePoint = (((hiSurrogate - 55296) << 10) | (loSurrogate - 56320)) + 65536;
output += String.fromCharCode((codePoint >> 18) | 240);
output += String.fromCharCode(((codePoint >> 12) & 63) | 128);
output += String.fromCharCode(((codePoint >> 6) & 63) | 128);
output += String.fromCharCode((codePoint & 63) | 128);
} else {
// Not a surrogate pair, or a dangling surrogate without its partner that we'll just encode as-is
output += String.fromCharCode((charCode >> 12) | 224);
output += String.fromCharCode(((charCode >> 6) & 63) | 128);
output += String.fromCharCode((charCode & 63) | 128);
}
}
return output;
},
decode: function (string) {
if (typeof string !== 'string') return string;
var output = "", i = 0, charCode = 0;
while (i < string.length) {
charCode = string.charCodeAt(i);
if (charCode < 128) {
output += String.fromCharCode(charCode),
i++;
} else if ((charCode > 191) && (charCode < 224)) {
output += String.fromCharCode(((charCode & 31) << 6) | (string.charCodeAt(i + 1) & 63));
i += 2;
} else if ((charCode > 223) && (charCode < 240)) {
output += String.fromCharCode(((charCode & 15) << 12) | ((string.charCodeAt(i + 1) & 63) << 6) | (string.charCodeAt(i + 2) & 63));
i += 3;
} else {
var codePoint = ((charCode & 7) << 18) | ((string.charCodeAt(i + 1) & 63) << 12) | ((string.charCodeAt(i + 2) & 63) << 6) | (string.charCodeAt(i + 3) & 63);
// output += String.fromCodePoint(codePoint); // Can't do this because Internet Explorer doesn't have String.fromCodePoint
output += String.fromCharCode(((codePoint - 65536) >> 10) + 55296) + String.fromCharCode(((codePoint - 65536) & 1023) + 56320); // So we do this instead
i += 4;
}
}
return output;
}
};
}, this);
// Base64 Module
//
// Cleaner, modularized and properly scoped base64 encoding and decoding module for strings.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
(function (name, definition, context, dependencies) {
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
else { context[name] = definition.apply(context); }
})('base64', function (utf8) {
var $this = this;
var $utf8 = utf8 || this.utf8;
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
return {
encode: function (input) {
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
if (typeof input !== 'string') return input;
else input = $utf8.encode(input);
var output = "", a, b, c, d, e, f, g, i = 0;
while (i < input.length) {
a = input.charCodeAt(i++);
b = input.charCodeAt(i++);
c = input.charCodeAt(i++);
d = a >> 2;
e = ((a & 3) << 4) | (b >> 4);
f = ((b & 15) << 2) | (c >> 6);
g = c & 63;
if (isNaN(b)) f = g = 64;
else if (isNaN(c)) g = 64;
output += map.charAt(d) + map.charAt(e) + map.charAt(f) + map.charAt(g);
}
return output;
},
decode: function (input) {
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." };
if (typeof input !== 'string') return input;
else input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
var output = "", a, b, c, d, e, f, g, i = 0;
while (i < input.length) {
d = map.indexOf(input.charAt(i++));
e = map.indexOf(input.charAt(i++));
f = map.indexOf(input.charAt(i++));
g = map.indexOf(input.charAt(i++));
a = (d << 2) | (e >> 4);
b = ((e & 15) << 4) | (f >> 2);
c = ((f & 3) << 6) | g;
output += String.fromCharCode(a);
if (f != 64) output += String.fromCharCode(b);
if (g != 64) output += String.fromCharCode(c);
}
return $utf8.decode(output);
}
}
}, this, [ "utf8" ]);
@@ -1,9 +0,0 @@
// From https://gist.github.com/Nijikokun/5192472
//
// UTF8 Module
//
// Cleaner and modularized utf-8 encoding and decoding library for javascript.
//
// copyright: MIT
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com
!function(r,e,o,t){void 0!==o.module&&o.module.exports?o.module.exports=e.apply(o):void 0!==o.define&&"function"===o.define&&o.define.amd?define("utf8",[],e):o.utf8=e.apply(o)}(0,function(){return{encode:function(r){if("string"!=typeof r)return r;r=r.replace(/\r\n/g,"\n");for(var e,o="",t=0;t<r.length;t++)if((e=r.charCodeAt(t))<128)o+=String.fromCharCode(e);else if(e>127&&e<2048)o+=String.fromCharCode(e>>6|192),o+=String.fromCharCode(63&e|128);else if(e>55295&&e<57344&&r.length>t+1){var i=e,n=r.charCodeAt(t+1);t++;var d=65536+(i-55296<<10|n-56320);o+=String.fromCharCode(d>>18|240),o+=String.fromCharCode(d>>12&63|128),o+=String.fromCharCode(d>>6&63|128),o+=String.fromCharCode(63&d|128)}else o+=String.fromCharCode(e>>12|224),o+=String.fromCharCode(e>>6&63|128),o+=String.fromCharCode(63&e|128);return o},decode:function(r){if("string"!=typeof r)return r;for(var e="",o=0,t=0;o<r.length;)if((t=r.charCodeAt(o))<128)e+=String.fromCharCode(t),o++;else if(t>191&&t<224)e+=String.fromCharCode((31&t)<<6|63&r.charCodeAt(o+1)),o+=2;else if(t>223&&t<240)e+=String.fromCharCode((15&t)<<12|(63&r.charCodeAt(o+1))<<6|63&r.charCodeAt(o+2)),o+=3;else{var i=(7&t)<<18|(63&r.charCodeAt(o+1))<<12|(63&r.charCodeAt(o+2))<<6|63&r.charCodeAt(o+3);e+=String.fromCharCode(55296+(i-65536>>10))+String.fromCharCode(56320+(i-65536&1023)),o+=4}return e}}},this),function(r,e,o,t){if(void 0!==o.module&&o.module.exports){if(t&&o.require)for(var i=0;i<t.length;i++)o[t[i]]=o.require(t[i]);o.module.exports=e.apply(o)}else void 0!==o.define&&"function"===o.define&&o.define.amd?define("base64",t||[],e):o.base64=e.apply(o)}(0,function(r){var e=r||this.utf8,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=e.encode(r);for(var t,i,n,d,f,a,h,C="",c=0;c<r.length;)d=(t=r.charCodeAt(c++))>>2,f=(3&t)<<4|(i=r.charCodeAt(c++))>>4,a=(15&i)<<2|(n=r.charCodeAt(c++))>>6,h=63&n,isNaN(i)?a=h=64:isNaN(n)&&(h=64),C+=o.charAt(d)+o.charAt(f)+o.charAt(a)+o.charAt(h);return C},decode:function(r){if(void 0===e)throw{error:"MissingMethod",message:"UTF8 Module is missing."};if("string"!=typeof r)return r;r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,i,n,d,f,a,h="",C=0;C<r.length;)t=o.indexOf(r.charAt(C++))<<2|(d=o.indexOf(r.charAt(C++)))>>4,i=(15&d)<<4|(f=o.indexOf(r.charAt(C++)))>>2,n=(3&f)<<6|(a=o.indexOf(r.charAt(C++))),h+=String.fromCharCode(t),64!=f&&(h+=String.fromCharCode(i)),64!=a&&(h+=String.fromCharCode(n));return e.decode(h)}}},this,["utf8"]);
@@ -1,14 +0,0 @@
{
"tiddlers": [
{
"file": "base64-utf8.module.min.js",
"fields": {
"type": "application/javascript",
"title": "$:/core/modules/utils/base64-utf8/base64-utf8.module.js",
"module-type": "library"
},
"prefix": "(function(){",
"suffix": "}).call(exports);"
}
]
}
+20
View File
@@ -24,6 +24,26 @@ exports.setStyle = function(element,styles) {
} }
}; };
/*
Remove style properties of an element
element: dom node
styleProperties: ordered array of string property names
*/
exports.removeStyles = function(element, styleProperties) {
for (var i=0; i<styleProperties.length; i++) {
element.style.removeProperty($tw.utils.convertStyleNameToPropertyName(styleProperties[i]));
}
}
/*
Remove single style property of an element
element: dom node
styleProperty: string property name
*/
exports.removeStyle = function(element, styleProperty) {
$tw.utils.removeStyles(element, [styleProperty])
}
/* /*
Converts a standard CSS property name into the local browser-specific equivalent. For example: Converts a standard CSS property name into the local browser-specific equivalent. For example:
"background-color" --> "backgroundColor" "background-color" --> "backgroundColor"
+1 -1
View File
@@ -210,7 +210,7 @@ Modal.prototype.display = function(title,options) {
bodyWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false); bodyWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
footerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false); footerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
// Whether to close the modal dialog when the mask (area outside the modal) is clicked // Whether to close the modal dialog when the mask (area outside the modal) is clicked
if(tiddler.fields && (tiddler.fields["mask-closable"] === "yes" || tiddler.fields["mask-closable"] === "true")) { if(tiddler.fields && (tiddler.fields["mask-closable"] === "yes" || tiddler.fields["mask-closable"] === "true" || tiddler.fields["mask-closable"] === "" || "mask-closable" in tiddler.fields === false)) {
modalBackdrop.addEventListener("click",closeHandler,false); modalBackdrop.addEventListener("click",closeHandler,false);
} }
// Set the initial styles for the message // Set the initial styles for the message
+126
View File
@@ -0,0 +1,126 @@
/*\
title: $:/core/modules/utils/messaging.js
type: application/javascript
module-type: utils-browser
Messaging utilities for use with window.postMessage() etc.
This module intentionally has no dependencies so that it can be included in non-TiddlyWiki projects
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var RESPONSE_TIMEOUT = 2 * 1000;
/*
Class to handle subscribing to publishers
target: Target window (eg iframe.contentWindow)
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
onsubscribe: Function to be invoked with err parameter when the subscription is established, or there is a timeout
onmessage: Function to be invoked when a new message arrives, invoked with (data,callback). The callback is invoked with the argument (response)
*/
function BrowserMessagingSubscriber(options) {
var self = this;
this.target = options.target;
this.type = options.type;
this.onsubscribe = options.onsubscribe || function() {};
this.onmessage = options.onmessage;
this.hasConfirmed = false;
this.channel = new MessageChannel();
this.channel.port1.addEventListener("message",function(event) {
if(this.timerID) {
clearTimeout(this.timerID);
this.timerID = null;
}
if(event.data) {
if(event.data.verb === "SUBSCRIBED") {
self.hasConfirmed = true;
self.onsubscribe(null);
} else if(event.data.verb === self.type) {
self.onmessage(event.data,function(response) {
// Send the response back on the supplied port, and then close it
event.ports[0].postMessage(response);
event.ports[0].close();
});
}
}
});
// Set a timer so that if we don't hear from the iframe before a timeout we alert the user
this.timerID = setTimeout(function() {
if(!self.hasConfirmed) {
self.onsubscribe("NO_RESPONSE");
}
},RESPONSE_TIMEOUT);
this.channel.port1.start();
this.target.postMessage({verb: "SUBSCRIBE",to: self.type},"*",[this.channel.port2]);
}
exports.BrowserMessagingSubscriber = BrowserMessagingSubscriber;
/*
Class to handle publishing subscriptions
type: String indicating type of item for which subscriptions are being provided (eg "SAVING")
onsubscribe: Function to be invoked when a subscription occurs
*/
function BrowserMessagingPublisher(options) {
var self = this;
this.type = options.type;
this.hostIsListening = false;
this.port = null;
// Listen to connection requests from the host
window.addEventListener("message",function(event) {
if(event.data && event.data.verb === "SUBSCRIBE" && event.data.to === self.type) {
self.hostIsListening = true;
// Acknowledge
self.port = event.ports[0];
self.port.postMessage({verb: "SUBSCRIBED", to: self.type});
if(options.onsubscribe) {
options.onsubscribe(event.data);
}
}
});
}
BrowserMessagingPublisher.prototype.canSend = function() {
return !!this.hostIsListening && !!this.port;
};
BrowserMessagingPublisher.prototype.send = function(data,callback) {
var self = this;
callback = callback || function() {};
// Check that we've been initialised by the host
if(!this.hostIsListening || !this.port) {
return false;
}
// Create a channel for the confirmation
var channel = new MessageChannel();
channel.port1.addEventListener("message",function(event) {
if(event.data && event.data.verb === "OK") {
callback(null);
} else {
callback("BrowserMessagingPublisher for " + self.type + " error: " + (event.data || {}).verb);
}
channel.port1.close();
});
channel.port1.start();
// Send the save request with the port for the response
this.port.postMessage(data,[channel.port2]);
};
BrowserMessagingPublisher.prototype.close = function() {
if(this.port) {
this.port.close();
this.hostIsListening = false;
this.port = null;
}
};
exports.BrowserMessagingPublisher = BrowserMessagingPublisher;
})();
+16
View File
@@ -119,3 +119,19 @@ exports.getParseTreeText = function getParseTreeText(tree) {
} }
return output.join(""); 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;
};
+33 -7
View File
@@ -9,8 +9,6 @@ Various static utility functions.
"use strict"; "use strict";
var base64utf8 = require("$:/core/modules/utils/base64-utf8/base64-utf8.module.js");
/* /*
Display a message, in colour if we're on a terminal Display a message, in colour if we're on a terminal
*/ */
@@ -842,22 +840,50 @@ if(typeof window !== 'undefined') {
} }
} }
exports.base64ToBytes = function(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0));
};
exports.bytesToBase64 = function(bytes) {
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = function(str) {
if ($tw.browser) {
return exports.bytesToBase64(new TextEncoder().encode(str));
} else {
const buff = Buffer.from(str, "utf-8");
return buff.toString("base64");
}
};
exports.base64DecodeUtf8 = function(str) {
if ($tw.browser) {
return new TextDecoder().decode(exports.base64ToBytes(str));
} else {
const buff = Buffer.from(str, "base64");
return buff.toString("utf-8");
}
};
/* /*
Decode a base64 string Decode a base64 string
*/ */
exports.base64Decode = function(string64,binary,urlsafe) { exports.base64Decode = function(string64,binary,urlsafe) {
var encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64; const encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
if(binary) return exports.atob(encoded) if(binary) return exports.atob(encoded)
else return base64utf8.base64.decode.call(base64utf8,encoded); else return exports.base64DecodeUtf8(encoded);
}; };
/* /*
Encode a string to base64 Encode a string to base64
*/ */
exports.base64Encode = function(string64,binary,urlsafe) { exports.base64Encode = function(string64,binary,urlsafe) {
var encoded; let encoded;
if(binary) encoded = exports.btoa(string64); if(binary) encoded = exports.btoa(string64);
else encoded = base64utf8.base64.encode.call(base64utf8,string64); else encoded = exports.base64EncodeUtf8(string64);
if(urlsafe) { if(urlsafe) {
encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_'); encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_');
} }
@@ -1023,7 +1049,7 @@ exports.makeCompareFunction = function(type,options) {
return compare(dateA,dateB); return compare(dateA,dateB);
}, },
"version": function(a,b) { "version": function(a,b) {
return $tw.utils.compareVersions(a,b); return compare($tw.utils.compareVersions(a,b),0);
}, },
"alphanumeric": function(a,b) { "alphanumeric": function(a,b) {
if(!isCaseSensitive) { if(!isCaseSensitive) {
+103
View File
@@ -0,0 +1,103 @@
/*\
title: $:/core/modules/widgets/audio.js
type: application/javascript
module-type: widget
Basic Audio widget for displaying audio files.
This is a simple implementation that can be overridden by plugins
for more advanced functionality.
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var AudioWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
AudioWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
AudioWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
// Create audio element
var audioElement = this.document.createElement("audio");
audioElement.setAttribute("controls", this.getAttribute("controls", "controls"));
audioElement.setAttribute("style", this.getAttribute("style", "width: 100%; object-fit: contain"));
audioElement.className = "tw-audio-element";
// Set source
if(this.audioSource) {
if (this.audioSource.indexOf("data:") === 0) {
audioElement.setAttribute("src", this.audioSource);
} else {
var sourceElement = this.document.createElement("source");
sourceElement.setAttribute("src", this.audioSource);
if(this.audioType) {
sourceElement.setAttribute("type", this.audioType);
}
audioElement.appendChild(sourceElement);
}
}
// Insert the audio into the DOM
parent.insertBefore(audioElement, nextSibling);
this.domNodes.push(audioElement);
};
/*
Compute the internal state of the widget
*/
AudioWidget.prototype.execute = function() {
// Get the audio source and type
this.audioSource = this.getAttribute("src");
this.audioType = this.getAttribute("type");
this.audioControls = this.getAttribute("controls", "controls");
// Try to get from tiddler attribute
if(!this.audioSource && this.getAttribute("tiddler")) {
var tiddlerTitle = this.getAttribute("tiddler");
var tiddler = this.wiki.getTiddler(tiddlerTitle);
if(tiddler) {
if(tiddler.fields._canonical_uri) {
this.audioSource = tiddler.fields._canonical_uri;
this.audioType = tiddler.fields.type;
} else if(tiddler.fields.text) {
this.audioSource = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
this.audioType = tiddler.fields.type;
}
}
}
// Make sure we have a tiddler for saving timestamps
this.tiddlerTitle = this.getAttribute("tiddler");
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
AudioWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.src || changedAttributes.type || changedAttributes.controls || changedAttributes.tiddler) {
this.refreshSelf();
return true;
} else {
return false;
}
};
exports.audio = AudioWidget;
+8 -4
View File
@@ -61,6 +61,10 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
sourcePrefix: "data-", sourcePrefix: "data-",
destPrefix: "data-" destPrefix: "data-"
}); });
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
// Assign other attributes // Assign other attributes
if(this.style) { if(this.style) {
domNode.setAttribute("style",this.style); domNode.setAttribute("style",this.style);
@@ -68,9 +72,6 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
if(this.tooltip) { if(this.tooltip) {
domNode.setAttribute("title",this.tooltip); domNode.setAttribute("title",this.tooltip);
} }
if(this["aria-label"]) {
domNode.setAttribute("aria-label",this["aria-label"]);
}
if (this.role) { if (this.role) {
domNode.setAttribute("role", this.role); domNode.setAttribute("role", this.role);
} }
@@ -215,7 +216,6 @@ ButtonWidget.prototype.execute = function() {
this.setTo = this.getAttribute("setTo"); this.setTo = this.getAttribute("setTo");
this.popup = this.getAttribute("popup"); this.popup = this.getAttribute("popup");
this.hover = this.getAttribute("hover"); this.hover = this.getAttribute("hover");
this["aria-label"] = this.getAttribute("aria-label");
this.role = this.getAttribute("role"); this.role = this.getAttribute("role");
this.tooltip = this.getAttribute("tooltip"); this.tooltip = this.getAttribute("tooltip");
this.style = this.getAttribute("style"); this.style = this.getAttribute("style");
@@ -271,6 +271,10 @@ ButtonWidget.prototype.refresh = function(changedTiddlers) {
sourcePrefix: "data-", sourcePrefix: "data-",
destPrefix: "data-" destPrefix: "data-"
}); });
this.assignAttributes(this.domNodes[0],{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
} }
return this.refreshChildren(changedTiddlers); return this.refreshChildren(changedTiddlers);
}; };
+2
View File
@@ -74,6 +74,8 @@ ElementWidget.prototype.render = function(parent,nextSibling) {
// Create the DOM node and render children // Create the DOM node and render children
var domNode = this.document.createElementNS(this.namespace,this.tag); var domNode = this.document.createElementNS(this.namespace,this.tag);
this.assignAttributes(domNode,{excludeEventAttributes: true}); this.assignAttributes(domNode,{excludeEventAttributes: true});
// Allow hooks to manipulate the DOM node. Eg: Add debug info
$tw.hooks.invokeHook("th-dom-rendering-element", domNode, this);
parent.insertBefore(domNode,nextSibling); parent.insertBefore(domNode,nextSibling);
this.renderChildren(domNode,null); this.renderChildren(domNode,null);
this.domNodes.push(domNode); this.domNodes.push(domNode);
+1 -4
View File
@@ -44,7 +44,7 @@ EventWidget.prototype.render = function(parent,nextSibling) {
domNode.addEventListener(type,function(event) { domNode.addEventListener(type,function(event) {
var selector = self.getAttribute("selector"), var selector = self.getAttribute("selector"),
matchSelector = self.getAttribute("matchSelector"), matchSelector = self.getAttribute("matchSelector"),
actions = self.getAttribute("$"+type) || self.getAttribute("actions-"+type), actions = self.getAttribute("$"+type),
stopPropagation = self.getAttribute("stopPropagation","onaction"), stopPropagation = self.getAttribute("stopPropagation","onaction"),
selectedNode = event.target, selectedNode = event.target,
selectedNodeRect, selectedNodeRect,
@@ -122,9 +122,6 @@ EventWidget.prototype.execute = function() {
self.types.push(key.slice(1)); self.types.push(key.slice(1));
} }
}); });
if(!this.types.length) {
this.types = this.getAttribute("events","").split(" ");
}
this.elementTag = this.getAttribute("tag"); this.elementTag = this.getAttribute("tag");
// Make child widgets // Make child widgets
this.makeChildWidgets(); this.makeChildWidgets();
+2 -1
View File
@@ -49,7 +49,8 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true, configTrimWhiteSpace:false}); var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true, configTrimWhiteSpace:false});
if(parser) { if(parser) {
var parseTreeNode = parser.tree[0]; 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 = { var node = {
type: "set", type: "set",
attributes: parseTreeNode.attributes, attributes: parseTreeNode.attributes,
+13 -3
View File
@@ -45,6 +45,10 @@ LinkWidget.prototype.render = function(parent,nextSibling) {
sourcePrefix: "data-", sourcePrefix: "data-",
destPrefix: "data-" destPrefix: "data-"
}); });
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
});
parent.insertBefore(domNode,nextSibling); parent.insertBefore(domNode,nextSibling);
this.renderChildren(domNode,null); this.renderChildren(domNode,null);
this.domNodes.push(domNode); this.domNodes.push(domNode);
@@ -125,9 +129,13 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
}); });
domNode.setAttribute("title",tooltipText); domNode.setAttribute("title",tooltipText);
} }
if(this["aria-label"]) { if(this.role) {
domNode.setAttribute("aria-label",this["aria-label"]); domNode.setAttribute("role",this.role);
} }
this.assignAttributes(domNode,{
sourcePrefix: "aria-",
destPrefix: "aria-"
})
// Add a click event handler // Add a click event handler
$tw.utils.addEventListeners(domNode,[ $tw.utils.addEventListeners(domNode,[
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}, {name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
@@ -139,6 +147,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
dragTiddlerFn: function() {return self.to;}, dragTiddlerFn: function() {return self.to;},
widget: this widget: this
}); });
} else if(this.draggable === "no") {
domNode.setAttribute("draggable","false");
} }
// Assign data- attributes // Assign data- attributes
this.assignAttributes(domNode,{ this.assignAttributes(domNode,{
@@ -188,7 +198,7 @@ LinkWidget.prototype.execute = function() {
// Pick up our attributes // Pick up our attributes
this.to = this.getAttribute("to",this.getVariable("currentTiddler")); this.to = this.getAttribute("to",this.getVariable("currentTiddler"));
this.tooltip = this.getAttribute("tooltip"); this.tooltip = this.getAttribute("tooltip");
this["aria-label"] = this.getAttribute("aria-label"); this.role = this.getAttribute("role");
this.linkClasses = this.getAttribute("class"); this.linkClasses = this.getAttribute("class");
this.overrideClasses = this.getAttribute("overrideClass"); this.overrideClasses = this.getAttribute("overrideClass");
this.tabIndex = this.getAttribute("tabindex"); this.tabIndex = this.getAttribute("tabindex");
-14
View File
@@ -94,8 +94,6 @@ RangeWidget.prototype.getActionVariables = function(options) {
// actionsStart // actionsStart
RangeWidget.prototype.handleMouseDownEvent = function(event) { RangeWidget.prototype.handleMouseDownEvent = function(event) {
this.mouseDown = true; // TODO remove once IE is gone.
this.startValue = this.inputDomNode.value; // TODO remove this line once IE is gone!
this.handleEvent(event); this.handleEvent(event);
// Trigger actions // Trigger actions
if(this.actionsMouseDown) { if(this.actionsMouseDown) {
@@ -106,26 +104,16 @@ RangeWidget.prototype.handleMouseDownEvent = function(event) {
// actionsStop // actionsStop
RangeWidget.prototype.handleMouseUpEvent = function(event) { RangeWidget.prototype.handleMouseUpEvent = function(event) {
this.mouseDown = false; // TODO remove once IE is gone.
this.handleEvent(event); this.handleEvent(event);
// Trigger actions // Trigger actions
if(this.actionsMouseUp) { if(this.actionsMouseUp) {
var variables = this.getActionVariables() var variables = this.getActionVariables()
this.invokeActionString(this.actionsMouseUp,this,event,variables); this.invokeActionString(this.actionsMouseUp,this,event,variables);
} }
// TODO remove the following if() once IE is gone!
if ($tw.browser.isIE) {
if (this.startValue !== this.inputDomNode.value) {
this.handleChangeEvent(event);
this.startValue = this.inputDomNode.value;
}
}
} }
RangeWidget.prototype.handleChangeEvent = function(event) { RangeWidget.prototype.handleChangeEvent = function(event) {
if (this.mouseDown) { // TODO refactor this function once IE is gone.
this.handleInputEvent(event); this.handleInputEvent(event);
}
}; };
RangeWidget.prototype.handleInputEvent = function(event) { RangeWidget.prototype.handleInputEvent = function(event) {
@@ -152,8 +140,6 @@ RangeWidget.prototype.handleEvent = function(event) {
Compute the internal state of the widget Compute the internal state of the widget
*/ */
RangeWidget.prototype.execute = function() { RangeWidget.prototype.execute = function() {
// TODO remove the next 1 lines once IE is gone!
this.mouseUp = true; // Needed for IE10
// Get the parameters from the attributes // Get the parameters from the attributes
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.tiddlerField = this.getAttribute("field","text"); this.tiddlerField = this.getAttribute("field","text");
+1 -1
View File
@@ -181,7 +181,7 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
}; };
ScrollableWidget.prototype.listenerFunction = function(event) { ScrollableWidget.prototype.listenerFunction = function(event) {
self = this; var self = this;
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = setTimeout(function() { this.timeout = setTimeout(function() {
var existingTiddler = self.wiki.getTiddler(self.scrollableBind), var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
+23
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;
+3 -13
View File
@@ -1059,17 +1059,7 @@ Options include:
exports.parseText = function(type,text,options) { exports.parseText = function(type,text,options) {
text = text || ""; text = text || "";
options = options || {}; options = options || {};
// Select a parser var Parser = $tw.utils.getParser(type,options)
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 the parser instance // Return the parser instance
return new Parser(type,text,{ return new Parser(type,text,{
parseAsInline: options.parseAsInline, parseAsInline: options.parseAsInline,
@@ -1083,7 +1073,7 @@ exports.parseText = function(type,text,options) {
Parse a tiddler according to its MIME type Parse a tiddler according to its MIME type
*/ */
exports.parseTiddler = function(title,options) { exports.parseTiddler = function(title,options) {
options = $tw.utils.extend({},options); options = options || {};
var cacheType = options.parseAsInline ? "inlineParseTree" : "blockParseTree", var cacheType = options.parseAsInline ? "inlineParseTree" : "blockParseTree",
tiddler = this.getTiddler(title), tiddler = this.getTiddler(title),
self = this; self = this;
@@ -1443,7 +1433,7 @@ exports.search = function(text,options) {
// Don't search the text field if the content type is binary // Don't search the text field if the content type is binary
var fieldName = searchFields[fieldIndex]; var fieldName = searchFields[fieldIndex];
if(fieldName === "text" && contentTypeInfo.encoding !== "utf8") { if(fieldName === "text" && contentTypeInfo.encoding !== "utf8") {
break; continue;
} }
var str = tiddler.fields[fieldName], var str = tiddler.fields[fieldName],
t; t;
+11 -6
View File
@@ -47,6 +47,7 @@ modal-footer-background: #f5f5f5
modal-footer-border: #dddddd modal-footer-border: #dddddd
modal-header-border: #eeeeee modal-header-border: #eeeeee
muted-foreground: #bbb muted-foreground: #bbb
network-activity-foreground: <<colour primary>>
notification-background: #ffffdd notification-background: #ffffdd
notification-border: #999999 notification-border: #999999
page-background: #6f6f70 page-background: #6f6f70
@@ -56,22 +57,26 @@ primary: #29a6ee
select-tag-background: select-tag-background:
select-tag-foreground: select-tag-foreground:
sidebar-button-foreground: <<colour foreground>> sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000 sidebar-controls-foreground-hover: #222222
sidebar-controls-foreground: #c2c1c2 sidebar-controls-foreground: #c2c1c2
sidebar-foreground-shadow: rgba(255,255,255,0) sidebar-foreground-shadow: transparent
sidebar-foreground: #d3d2d4 sidebar-foreground: #d3d2d4
sidebar-muted-foreground-hover: #444444 sidebar-muted-foreground-hover: #333333
sidebar-muted-foreground: #c0c0c0 sidebar-muted-foreground: #c0c0c0
sidebar-tab-background-selected: #6f6f70 sidebar-tab-background-selected: #6f6f70
sidebar-tab-background: #666667 sidebar-tab-background: #666667
sidebar-tab-border-selected: #999 sidebar-tab-border-selected: #999
sidebar-tab-border: #515151 sidebar-tab-border: #515151
sidebar-tab-divider: #999 sidebar-tab-divider: #999
sidebar-tab-foreground-selected: sidebar-tab-foreground-selected: #bfbfbf
sidebar-tab-foreground: #999 sidebar-tab-foreground: #b0b0b0
sidebar-tiddler-link-foreground-hover: #444444 sidebar-tiddler-link-foreground-hover: #444444
sidebar-tiddler-link-foreground: #d1d0d2 sidebar-tiddler-link-foreground: #aaaaaa
site-title-foreground: <<colour tiddler-title-foreground>> site-title-foreground: <<colour tiddler-title-foreground>>
stability-deprecated: #bf616a
stability-experimental: #d08770
stability-legacy: #88c0d0
stability-stable: #a3be8c
static-alert-foreground: #aaaaaa static-alert-foreground: #aaaaaa
tab-background-selected: #ffffff tab-background-selected: #ffffff
tab-background: #d8d8d8 tab-background: #d8d8d8
@@ -1,12 +1,13 @@
title: $:/core/templates/tiddlywiki5-external-js.html title: $:/core/templates/tiddlywiki5-external-js.html
<$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]"> <$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] +[[$:/core]plugintiddlers[]]">
`<!doctype html> `<!doctype html>
`{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`"> `{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
<head> <head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--~~ Raw markup for the top of the head section ~~--> <!--~~ Raw markup for the top of the head section ~~-->
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}`
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/> <meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta name="application-name" content="TiddlyWiki" /> <meta name="application-name" content="TiddlyWiki" />
<meta name="generator" content="TiddlyWiki" /> <meta name="generator" content="TiddlyWiki" />
@@ -22,13 +23,13 @@ title: $:/core/templates/tiddlywiki5-external-js.html
<!--~~ This is a Tiddlywiki file. The points of interest in the file are marked with this pattern ~~--> <!--~~ This is a Tiddlywiki file. The points of interest in the file are marked with this pattern ~~-->
<!--~~ Raw markup ~~--> <!--~~ Raw markup ~~-->
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/core/wiki/rawmarkup]] ||$:/core/templates/plain-text-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/core/wiki/rawmarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}`
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}`
</head> </head>
<body class="tc-body"> <body class="tc-body">
<!--~~ Raw markup for the top of the body section ~~--> <!--~~ Raw markup for the top of the body section ~~-->
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/TopBody]] ||$:/core/templates/raw-static-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/TopBody]] ||$:/core/templates/raw-static-tiddler}}}`
<!--~~ Static styles ~~--> <!--~~ Static styles ~~-->
<div id="styleArea"> <div id="styleArea">
`{{$:/boot/boot.css||$:/core/templates/css-tiddler}}` `{{$:/boot/boot.css||$:/core/templates/css-tiddler}}`
@@ -42,9 +43,10 @@ title: $:/core/templates/tiddlywiki5-external-js.html
<!--~~ Ordinary tiddlers ~~--> <!--~~ Ordinary tiddlers ~~-->
`{{$:/core/templates/store.area.template.html}}` `{{$:/core/templates/store.area.template.html}}`
<!--~~ Raw markup for the bottom of the body section ~~--> <!--~~ Raw markup for the bottom of the body section ~~-->
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}` `{{{ [enlist<rawMarkupFilter>tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}`
<!--~~ Load external JavaScripts ~~--> <!--~~ Load external JavaScripts ~~-->
<script src="`{{{ [<coreURL>] }}}`" onerror="alert('Error: Cannot load `{{{ [<coreURL>] }}}`');"></script> <script src="`{{{ [<coreURL>] }}}`" onerror="alert('Error: Cannot load `{{{ [<coreURL>] }}}`');"></script>
</body> </body>
</html>` </html>`
</$set> </$set>
</$set>
+9 -3
View File
@@ -62,13 +62,20 @@ caption: {{$:/language/Search/Filter/Caption}}
</$list> </$list>
\end \end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim \whitespace trim
<<lingo Filter/Hint>> <<lingo Filter/Hint>>
<div class="tc-search tc-advanced-search"> <div class="tc-search tc-advanced-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>> class="tc-small-gap-right"> <$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>> class="tc-small-gap-right">
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input" <$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input" tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch" storeTitle="$:/temp/advancedsearch"
@@ -84,7 +91,6 @@ caption: {{$:/language/Search/Filter/Caption}}
inputCancelActions=<<cancel-search-actions>> inputCancelActions=<<cancel-search-actions>>
/> />
</$keyboard> </$keyboard>
</$keyboard>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterButton]!has[draft.of]]"> <$list filter="[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterButton]!has[draft.of]]">
<$transclude/> <$transclude/>
</$list> </$list>
+9 -3
View File
@@ -53,13 +53,20 @@ first-search-filter: [all[shadows]search<userInput>sort[title]limit[250]] -[[$:/
</$list></$list> </$list></$list>
\end \end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim \whitespace trim
<<lingo Shadows/Hint>> <<lingo Shadows/Hint>>
<div class="tc-search"> <div class="tc-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>> <$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input" <$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input" tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch" storeTitle="$:/temp/advancedsearch"
@@ -74,7 +81,6 @@ first-search-filter: [all[shadows]search<userInput>sort[title]limit[250]] -[[$:/
inputAcceptVariantActions=<<input-accept-variant-actions>> inputAcceptVariantActions=<<input-accept-variant-actions>>
filterMinLength={{$:/config/Search/MinLength}}/> filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard> </$keyboard>
</$keyboard>
&#32; &#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text=""> <$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible"> <$button class="tc-btn-invisible">
+13 -7
View File
@@ -17,15 +17,24 @@ caption: {{$:/language/Search/Standard/Caption}}
\procedure input-accept-variant-actions() <$list filter="[{$:/config/Search/NavigateOnEnter/enable}match[yes]]" emptyMessage="<$list filter='[<tiddler>get[text]!is[missing]] :else[<tiddler>get[text]is[shadow]]'><$list filter='[<tiddler>get[text]minlength[1]]'><$action-sendmessage $message='tm-edit-tiddler' $param={{{ [<tiddler>get[text]] }}}/></$list></$list>"><$list filter="[<tiddler>get[text]minlength[1]]"><$action-sendmessage $message="tm-edit-tiddler" $param={{{ [<tiddler>get[text]] }}}/></$list></$list> \procedure input-accept-variant-actions() <$list filter="[{$:/config/Search/NavigateOnEnter/enable}match[yes]]" emptyMessage="<$list filter='[<tiddler>get[text]!is[missing]] :else[<tiddler>get[text]is[shadow]]'><$list filter='[<tiddler>get[text]minlength[1]]'><$action-sendmessage $message='tm-edit-tiddler' $param={{{ [<tiddler>get[text]] }}}/></$list></$list>"><$list filter="[<tiddler>get[text]minlength[1]]"><$action-sendmessage $message="tm-edit-tiddler" $param={{{ [<tiddler>get[text]] }}}/></$list></$list>
\procedure input-actions()
<%if [<event-code>match[ArrowRight]] :and[<modifier>match[alt-shift]] %>
<<next-search-tab>>
<%elseif [<event-code>match[ArrowLeft]] :and[<modifier>match[alt-shift]] %>
<<previous-search-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim \whitespace trim
<<lingo Standard/Hint>> <<lingo Standard/Hint>>
<div class="tc-search"> <div class="tc-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>> <$keyboard key="((input-tab-right)) ((input-tab-left)) shift-alt-Right shift-alt-Left" actions=<<input-actions>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$keyboard key="shift-alt-Right" actions=<<next-search-tab>>>
<$keyboard key="shift-alt-Left" actions=<<previous-search-tab>>>
<$transclude $variable="keyboard-driven-input" <$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input" tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch" storeTitle="$:/temp/advancedsearch"
@@ -40,9 +49,6 @@ caption: {{$:/language/Search/Standard/Caption}}
configTiddlerFilter="[[$:/state/advancedsearch/standard/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]" configTiddlerFilter="[[$:/state/advancedsearch/standard/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]"
filterMinLength={{$:/config/Search/MinLength}}/> filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard> </$keyboard>
</$keyboard>
</$keyboard>
</$keyboard>
&#32; &#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text=""> <$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible"> <$button class="tc-btn-invisible">
+9 -3
View File
@@ -52,13 +52,20 @@ first-search-filter: [is[system]search<userInput>sort[title]limit[250]] :except[
</$list></$list> </$list></$list>
\end \end
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%endif%>
\end
\whitespace trim \whitespace trim
<<lingo System/Hint>> <<lingo System/Hint>>
<div class="tc-search"> <div class="tc-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>> <$keyboard key="((input-tab-right)) ((input-tab-left))" actions=<<input-actions>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input" <$transclude $variable="keyboard-driven-input"
tiddler="$:/temp/advancedsearch/input" tiddler="$:/temp/advancedsearch/input"
storeTitle="$:/temp/advancedsearch" storeTitle="$:/temp/advancedsearch"
@@ -73,7 +80,6 @@ first-search-filter: [is[system]search<userInput>sort[title]limit[250]] :except[
inputAcceptVariantActions=<<input-accept-variant-actions>> inputAcceptVariantActions=<<input-accept-variant-actions>>
filterMinLength={{$:/config/Search/MinLength}}/> filterMinLength={{$:/config/Search/MinLength}}/>
</$keyboard> </$keyboard>
</$keyboard>
&#32; &#32;
<$reveal state="$:/temp/advancedsearch" type="nomatch" text=""> <$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
<$button class="tc-btn-invisible"> <$button class="tc-btn-invisible">
+3 -3
View File
@@ -2,9 +2,9 @@ code-body: yes
title: $:/core/ui/AlertTemplate title: $:/core/ui/AlertTemplate
\whitespace trim \whitespace trim
<div class="tc-alert"> <div class="tc-alert" role="alertdialog">
<div class="tc-alert-toolbar"> <div class="tc-alert-toolbar">
<$button class="tc-btn-invisible"><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/cancel-button}}</$button> <$button class="tc-btn-invisible" aria-label={{$:/language/Buttons/Close/Caption}}><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/cancel-button}}</$button>
</div> </div>
<div class="tc-alert-subtitle"> <div class="tc-alert-subtitle">
<$wikify name="format" text=<<lingo Tiddler/DateFormat>>> <$wikify name="format" text=<<lingo Tiddler/DateFormat>>>
@@ -19,7 +19,7 @@ title: $:/core/ui/AlertTemplate
</$reveal> </$reveal>
</$wikify> </$wikify>
</div> </div>
<div class="tc-alert-body"> <div class="tc-alert-body" role="alert" aria-atomic="true">
<$transclude/> <$transclude/>
+2 -4
View File
@@ -17,13 +17,11 @@ tags: $:/tags/EditTemplate
<$let backgroundColor=<<colour>> > <$let backgroundColor=<<colour>> >
<span class="tc-tag-label tc-tag-list-item tc-small-gap-right" <span class="tc-tag-label tc-tag-list-item tc-small-gap-right"
data-tag-title=<<currentTiddler>> data-tag-title=<<currentTiddler>>
style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;` style=`color:$(foregroundColor)$; background-color:$(backgroundColor)$;`
> >
<$transclude tiddler=<<icon>>/> <$transclude tiddler=<<icon>>/>
<$view field="title" format="text"/> <$view field="title" format="text"/>
<$button class="tc-btn-invisible tc-remove-tag-button" <$button class="tc-btn-invisible tc-remove-tag-button">
style.fill=<<foregroundColor>>
>
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/> <$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/>
{{$:/core/images/close-button}} {{$:/core/images/close-button}}
</$button> </$button>
+1 -1
View File
@@ -10,7 +10,7 @@ first-search-filter: [all[shadows+tiddlers]prefix[$:/language/Docs/Types/]sort[d
<em class="tc-edit tc-small-gap-right"><<lingo Type/Prompt>></em> <em class="tc-edit tc-small-gap-right"><<lingo Type/Prompt>></em>
<div class="tc-type-selector-dropdown-wrapper"> <div class="tc-type-selector-dropdown-wrapper">
<div class="tc-type-selector"><$fieldmangler> <div class="tc-type-selector"><$fieldmangler>
<$transclude $variable="keyboard-driven-input" tiddler=<<currentTiddler>> storeTitle=<<typeInputTiddler>> refreshTitle=<<refreshTitle>> selectionStateTitle=<<typeSelectionTiddler>> field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-edit-texteditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] :else[[false]] }}} cancelPopups="yes" configTiddlerFilter="[[$:/core/ui/EditTemplate/type]]" inputCancelActions=<<input-cancel-actions>>/><$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown tc-small-gap" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button><$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}<$action-deletetiddler $filter="[<typeInputTiddler>] [<storeTitle>] [<refreshTitle>] [<selectionStateTitle>]"/></$button> <$transclude $variable="keyboard-driven-input" tiddler=<<currentTiddler>> storeTitle=<<typeInputTiddler>> refreshTitle=<<refreshTitle>> selectionStateTitle=<<typeSelectionTiddler>> field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-edit-texteditor tc-popup-handle tc-keep-focus" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] :else[[false]] }}} cancelPopups="yes" configTiddlerFilter="[[$:/core/ui/EditTemplate/type]]" inputCancelActions=<<input-cancel-actions>>/><$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown tc-small-gap" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button><$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}<$action-deletetiddler $filter="[<typeInputTiddler>] [<storeTitle>] [<refreshTitle>] [<selectionStateTitle>]"/></$button>
</$fieldmangler></div> </$fieldmangler></div>
<div class="tc-block-dropdown-wrapper"> <div class="tc-block-dropdown-wrapper">
+1 -15
View File
@@ -37,8 +37,6 @@ title: $:/core/ui/ImportListing
\define suppressedField() suppressed-$(payloadTiddler)$ \define suppressedField() suppressed-$(payloadTiddler)$
\define newImportTitleTiddler() $:/temp/NewImportTitle-$(payloadTiddler)$
\define previewPopupState() $(currentTiddler)$!!popup-$(payloadTiddler)$ \define previewPopupState() $(currentTiddler)$!!popup-$(payloadTiddler)$
\define renameFieldState() $(currentTiddler)$!!state-rename-$(payloadTiddler)$ \define renameFieldState() $(currentTiddler)$!!state-rename-$(payloadTiddler)$
@@ -102,19 +100,7 @@ title: $:/core/ui/ImportListing
<$reveal type="match" state=<<renameFieldState>> text="yes" tag="tr"> <$reveal type="match" state=<<renameFieldState>> text="yes" tag="tr">
<td colspan="3"> <td colspan="3">
<div class="tc-flex"> <div class="tc-flex">
<$edit-text tiddler=<<newImportTitleTiddler>> default={{{[subfilter<payloadTitleFilter>]}}} tag="input" class="tc-import-rename tc-flex-grow-1"/> <$edit-text field=<<renameField>> default={{{[<payloadTiddler>]}}} tag="input" class="tc-import-rename tc-flex-grow-1"/>
<span class="tc-small-gap-left">
<$button class="tc-btn-invisible" set=<<renameFieldState>> setTo="no" tooltip={{{[<lingo-base>addsuffix[Listing/Rename/CancelRename]get[text]]}}}>
{{$:/core/images/close-button}}
<$action-deletetiddler $tiddler=<<newImportTitleTiddler>>/>
</$button>
<span class="tc-small-gap-right"/>
</span>
<$button class="tc-btn-invisible" set=<<renameFieldState>> setTo="no" tooltip={{{[<lingo-base>addsuffix[Listing/Rename/ConfirmRename]get[text]]}}}>
{{$:/core/images/done-button}}
<$action-setfield $field=<<renameField>> $value={{{[<newImportTitleTiddler>get[text]minlength[1]else<payloadTiddler>]}}} />
<$action-deletetiddler $tiddler=<<newImportTitleTiddler>>/>
</$button>
</div> </div>
</td> </td>
</$reveal> </$reveal>
+13 -14
View File
@@ -3,26 +3,25 @@ tags: $:/tags/ControlPanel/Appearance
caption: {{$:/language/ControlPanel/LayoutSwitcher/Caption}} caption: {{$:/language/ControlPanel/LayoutSwitcher/Caption}}
\whitespace trim \whitespace trim
\function layout.filter() [all[current]field:title[$:/core/ui/PageTemplate]]
\function layout.empty.filter() [all[current]field:title{$:/layout}]
<$linkcatcher to="$:/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="[all[tiddlers+shadows]tag[$:/tags/Layout]] [[$:/core/ui/PageTemplate]] +[!is[draft]sort[name]]">
<$list <$list filter="[{$:/layout}!has[text]]" variable="ignore">
filter="[{$:/layout}!has[text]]" <$list-empty>
variable="ignore" <div class={{{ [layout.empty.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
emptyMessage="""\whitespace trim <$link to={{!!title}} role="option" aria-selected={{{ [layout.empty.filter[]then[true]else[false]] }}}>
<$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"/> ''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link></div></$set> </$link>
"""> </div>
<$set name="cls" filter="[all[current]field:title[$:/core/ui/PageTemplate]]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item"> </$list-empty>
<div class=<<cls>>> <div class={{{ [layout.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$link to={{!!title}}> <$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"/> ''<$transclude tiddler={{{ [<currentTiddler>get[icon]] }}}/><$transclude field="name"/>''&#32;-&#32;<$transclude field="description"/>
</$link> </$link>
</div> </div>
</$set>
</$list> </$list>
</$list> </$list>
</div> </div>
+2 -2
View File
@@ -1,8 +1,8 @@
title: $:/core/ui/PageTemplate/alerts title: $:/core/ui/PageTemplate/alerts
tags: $:/tags/PageTemplate tags: $:/tags/PageTemplate
<div class="tc-alerts"> <div class="tc-alerts" role="region" aria-label="Alerts">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Alert]!has[draft.of]]" template="$:/core/ui/AlertTemplate" storyview="pop"/> <$list filter="[all[shadows+tiddlers]tag[$:/tags/Alert]!is[draft]]" template="$:/core/ui/AlertTemplate" storyview="pop"/>
</div> </div>
+11 -5
View File
@@ -46,13 +46,21 @@ tags: $:/tags/SideBarSegment
\procedure advanced-search-actions() <$action-setfield $tiddler="$:/temp/advancedsearch" text={{$:/temp/search/input}}/><$action-setfield $tiddler="$:/temp/advancedsearch/input" text={{$:/temp/search/input}}/><<delete-state-tiddlers>><$action-navigate $to="$:/AdvancedSearch"/><$action-setfield $tiddler="$:/temp/advancedsearch/refresh" text="yes"/><$action-sendmessage $message="tm-focus-selector" $param="""[data-tiddler-title="$:/AdvancedSearch"] .tc-search input""" preventScroll="true"/><$action-deletetiddler $filter="$:/temp/search $:/temp/search/input $:/temp/search/refresh [<searchListState>]"/> \procedure advanced-search-actions() <$action-setfield $tiddler="$:/temp/advancedsearch" text={{$:/temp/search/input}}/><$action-setfield $tiddler="$:/temp/advancedsearch/input" text={{$:/temp/search/input}}/><<delete-state-tiddlers>><$action-navigate $to="$:/AdvancedSearch"/><$action-setfield $tiddler="$:/temp/advancedsearch/refresh" text="yes"/><$action-sendmessage $message="tm-focus-selector" $param="""[data-tiddler-title="$:/AdvancedSearch"] .tc-search input""" preventScroll="true"/><$action-deletetiddler $filter="$:/temp/search $:/temp/search/input $:/temp/search/refresh [<searchListState>]"/>
\procedure input-actions()
<%if [<event-key-descriptor>match[((input-tab-right))]] %>
<<set-next-input-tab>>
<%elseif [<event-key-descriptor>match[((input-tab-left))]] %>
<<set-previous-input-tab>>
<%elseif [<event-key-descriptor>match[((advanced-search-sidebar))]] %>
<<advanced-search-actions>>
<%endif%>
\end
<div class="tc-sidebar-lists tc-sidebar-search"> <div class="tc-sidebar-lists tc-sidebar-search">
<$vars editTiddler="$:/temp/search" searchTiddler="$:/temp/search/input" searchListState=<<qualify "$:/state/search-list/selected-item">>> <$vars editTiddler="$:/temp/search" searchTiddler="$:/temp/search/input" searchListState=<<qualify "$:/state/search-list/selected-item">>>
<div class="tc-search"> <div class="tc-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>> <$keyboard key="((input-tab-right)) ((input-tab-left)) ((advanced-search-sidebar))" actions=<<input-actions>>>
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$keyboard key="((advanced-search-sidebar))" actions=<<advanced-search-actions>>>
<form class="tc-form-inline"> <form class="tc-form-inline">
<$transclude $variable="keyboard-driven-input" tiddler=<<editTiddler>> storeTitle=<<searchTiddler>> <$transclude $variable="keyboard-driven-input" tiddler=<<editTiddler>> storeTitle=<<searchTiddler>>
selectionStateTitle=<<searchListState>> refreshTitle="$:/temp/search/refresh" type="search" selectionStateTitle=<<searchListState>> refreshTitle="$:/temp/search/refresh" type="search"
@@ -62,8 +70,6 @@ tags: $:/tags/SideBarSegment
configTiddlerFilter="[[$:/state/search/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]"/> configTiddlerFilter="[[$:/state/search/currentTab]!is[missing]get[text]] :else[{$:/config/SearchResults/Default}]"/>
</form> </form>
</$keyboard> </$keyboard>
</$keyboard>
</$keyboard>
<$reveal state=<<searchTiddler>> type="nomatch" text=""> <$reveal state=<<searchTiddler>> type="nomatch" text="">
<$button tooltip={{$:/language/Buttons/AdvancedSearch/Hint}} aria-label={{$:/language/Buttons/AdvancedSearch/Caption}} class="tc-btn-invisible"> <$button tooltip={{$:/language/Buttons/AdvancedSearch/Hint}} aria-label={{$:/language/Buttons/AdvancedSearch/Caption}} class="tc-btn-invisible">
<<advanced-search-actions>> <<advanced-search-actions>>
+1 -1
View File
@@ -2,7 +2,7 @@ title: $:/core/ui/ViewTemplate/title
tags: $:/tags/ViewTemplate tags: $:/tags/ViewTemplate
\whitespace trim \whitespace trim
\define title-styles() fill:$(foregroundColor)$; \define title-styles() color:$(foregroundColor)$;
<div class="tc-tiddler-title tc-clearfix"> <div class="tc-tiddler-title tc-clearfix">
<div class="tc-titlebar"> <div class="tc-titlebar">
+1 -1
View File
@@ -1,2 +1,2 @@
title: $:/config/TiddlerInfo/Mode title: $:/config/TiddlerInfo/Mode
text: popup text: sticky
+5 -5
View File
@@ -1,12 +1,13 @@
title: $:/snippets/languageswitcher title: $:/snippets/languageswitcher
\whitespace trim \whitespace trim
\function language.filter() [all[current]field:title{$:/language}]
<$linkcatcher to="$:/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]]"> <$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={{{ [language.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}} lang={{!!name}}>
<div class=<<cls>> lang={{!!name}}> <$link role="option" aria-selected={{{ [language.filter[]then[true]else[false]] }}}>
<$link>
<$view field="description"> <$view field="description">
<$view field="name"> <$view field="name">
<$view field="title"/> <$view field="title"/>
@@ -14,7 +15,6 @@ title: $:/snippets/languageswitcher
</$view> </$view>
</$link> </$link>
</div> </div>
</$set>
</$list> </$list>
</div> </div>
</$linkcatcher> </$linkcatcher>
+16 -10
View File
@@ -120,11 +120,21 @@ tags: $:/tags/Macro
\procedure keyboard-driven-input(tiddler,storeTitle,field:"text",index:"",tag:"input",type,focus:"",inputAcceptActions,inputAcceptVariantActions,inputCancelActions,placeholder:"",default:"",class,focusPopup,rows,minHeight,tabindex,size,autoHeight,filterMinLength:"0",refreshTitle,selectionStateTitle,cancelPopups:"",configTiddlerFilter,firstSearchFilterField:"first-search-filter",secondSearchFilterField:"second-search-filter") \procedure keyboard-driven-input(tiddler,storeTitle,field:"text",index:"",tag:"input",type,focus:"",inputAcceptActions,inputAcceptVariantActions,inputCancelActions,placeholder:"",default:"",class,focusPopup,rows,minHeight,tabindex,size,autoHeight,filterMinLength:"0",refreshTitle,selectionStateTitle,cancelPopups:"",configTiddlerFilter,firstSearchFilterField:"first-search-filter",secondSearchFilterField:"second-search-filter")
\whitespace trim \whitespace trim
<$keyboard key="((input-accept))" actions=<<inputAcceptActions>>> \procedure keyboard-driven-input-actions()
<$keyboard key="((input-accept-variant))" actions=<<inputAcceptVariantActions>>> <%if [<event-key-descriptor>match[((input-accept))]] %>
<$keyboard key="((input-up))" actions=<<input-next-actions-before>>> <<inputAcceptActions>>
<$keyboard key="((input-down))" actions=<<input-next-actions-after>>> <%elseif [<event-key-descriptor>match[((input-accept-variant))]] %>
<$keyboard key="((input-cancel))" actions=<<inputCancelActions>>> <<inputAcceptVariantActions>>
<%elseif [<event-key-descriptor>match[((input-up))]] %>
<<input-next-actions-before>>
<%elseif [<event-key-descriptor>match[((input-down))]] %>
<<input-next-actions-after>>
<%elseif [<event-key-descriptor>match[((input-cancel))]] %>
<<inputCancelActions>>
<%endif%>
\end keyboard-driven-input-actions
<$keyboard key="((input-accept)) ((input-accept-variant)) ((input-up)) ((input-down)) ((input-cancel))" actions=<<keyboard-driven-input-actions>>>
<$edit-text <$edit-text
tiddler=<<tiddler>> field=<<field>> index=<<index>> tiddler=<<tiddler>> field=<<field>> index=<<index>>
inputActions=<<keyboard-input-actions>> tag=<<tag>> class=<<class>> inputActions=<<keyboard-input-actions>> tag=<<tag>> class=<<class>>
@@ -134,8 +144,4 @@ tags: $:/tags/Macro
refreshTitle=<<refreshTitle>> cancelPopups=<<cancelPopups>> refreshTitle=<<refreshTitle>> cancelPopups=<<cancelPopups>>
/> />
</$keyboard> </$keyboard>
</$keyboard> \end keyboard-driven-input
</$keyboard>
</$keyboard>
</$keyboard>
\end
+8 -3
View File
@@ -24,10 +24,14 @@ tags: $:/tags/Macro
\define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate) \define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate)
\whitespace trim \whitespace trim
<$set name="_tiddler" value="""$tiddler$""" emptyValue=<<currentTiddler>> >
<$let field-reference={{{ [<_tiddler>] "!!" [[$field$]] +[join[]] }}}
targetTiddler=<<_tiddler>>
targetField="""$field$"""
>
<span class="tc-links-draggable-list"> <span class="tc-links-draggable-list">
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
<$genesis $type=<<__type__>> class="$class$"> <$genesis $type=<<__type__>> class="$class$">
<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>> <$list filter="[list<field-reference>]" emptyMessage=<<__emptyMessage__>>>
<$droppable <$droppable
actions=<<list-links-draggable-drop-actions>> actions=<<list-links-draggable-drop-actions>>
tag="""$subtype$""" tag="""$subtype$"""
@@ -60,8 +64,9 @@ tags: $:/tags/Macro
</$droppable> </$droppable>
</$tiddler> </$tiddler>
</$genesis> </$genesis>
</$vars>
</span> </span>
</$let>
</$set>
\end \end
\define list-tagged-draggable-drop-actions(tag) \define list-tagged-draggable-drop-actions(tag)
-1
View File
@@ -3,7 +3,6 @@ tags: $:/tags/Macro
\define tag-pill-styles() \define tag-pill-styles()
background-color:$(backgroundColor)$; background-color:$(backgroundColor)$;
fill:$(foregroundColor)$;
color:$(foregroundColor)$; color:$(foregroundColor)$;
\end \end
+1 -1
View File
@@ -21,7 +21,7 @@ tags: $:/tags/Macro
style="width:$width$px;height:$height$px;background-color:$background-color$;" style="width:$width$px;height:$height$px;background-color:$background-color$;"
></$reveal></div><div ></$reveal></div><div
class="tc-thumbnail-icon" class="tc-thumbnail-icon"
style="fill:$color$;color:$color$;" style="color:$color$;"
>$icon$</div><div class="tc-thumbnail-caption">$caption$</div></div></$link> >$icon$</div><div class="tc-thumbnail-caption">$caption$</div></div></$link>
\end \end
+5 -4
View File
@@ -1,17 +1,18 @@
title: $:/snippets/themeswitcher title: $:/snippets/themeswitcher
\whitespace trim \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"> <$linkcatcher to="$:/theme">
<div class="tc-chooser"> <div class="tc-chooser" role="listbox">
<$list filter="[plugin-type[theme]sort[title]]"> <$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={{{ [theme.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<div class=<<cls>>><$link to={{!!title}}> <$link to={{!!title}} role="option" aria-selected={{{ [theme.filter[]then[true]else[false]] }}}>
''<$view field="name" format="text"/>'' ''<$view field="name" format="text"/>''
&#32; &#32;
<$view field="description" format="text"/> <$view field="description" format="text"/>
</$link> </$link>
</div> </div>
</$set>
</$list> </$list>
</div> </div>
</$linkcatcher> </$linkcatcher>
+5 -4
View File
@@ -4,13 +4,14 @@ title: $:/snippets/viewswitcher
$:/core/images/storyview-$(storyview)$ $:/core/images/storyview-$(storyview)$
\end \end
\whitespace trim \whitespace trim
\function view.filter() [<storyview>prefix{$:/view}]
<$linkcatcher to="$:/view"> <$linkcatcher to="$:/view">
<div class="tc-chooser tc-viewswitcher"> <div class="tc-chooser tc-viewswitcher" role="listbox">
<$list filter="[storyviews[]]" variable="storyview"> <$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>>> <div class={{{ [view.filter[]then[tc-chooser-item tc-chosen]else[tc-chooser-item]] }}}>
<$button tag="a" class="tc-tiddlylink tc-btn-invisible" to=<<storyview>>><$transclude tiddler=<<icon>>/><$text text=<<storyview>>/></$button> <$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> </div>
</$set>
</$list> </$list>
</div> </div>
</$linkcatcher> </$linkcatcher>
-44
View File
@@ -1,44 +0,0 @@
title: CloudData
type: application/json
[
{"text": "Tokyo/Yokohama", "size": 33.200},
{"text": "New York Metro", "size": 17.800},
{"text": "Sao Paulo", "size": 17.700},
{"text": "Seoul/Incheon", "size": 17.500},
{"text": "Mexico City", "size": 17.400},
{"text": "Osaka/Kobe/Kyoto", "size": 16.425},
{"text": "Manila", "size": 14.750},
{"text": "Mumbai", "size": 14.350},
{"text": "Delhi", "size": 14.300},
{"text": "Jakarta", "size": 14.250},
{"text": "Lagos", "size": 13.400},
{"text": "Kolkata", "size": 12.700},
{"text": "Cairo", "size": 12.200},
{"text": "Los Angeles", "size": 11.789},
{"text": "Buenos Aires", "size": 11.200},
{"text": "Rio de Janeiro", "size": 10.800},
{"text": "Moscow", "size": 10.500},
{"text": "Shanghai", "size": 10.000},
{"text": "Karachi", "size": 9.800},
{"text": "Paris", "size": 9.645},
{"text": "Istanbul", "size": 9.000},
{"text": "Nagoya", "size": 9.000},
{"text": "Beijing", "size": 8.614},
{"text": "Chicago", "size": 8.308},
{"text": "London", "size": 8.278},
{"text": "Shenzhen", "size": 8.000},
{"text": "Essen/Dusseldorf", "size": 7.350},
{"text": "Tehran", "size": 7.250},
{"text": "Bogota", "size": 7.000},
{"text": "Lima", "size": 7.000},
{"text": "Bangkok", "size": 6.500},
{"text": "Johannesburg/East Rand", "size": 6.000},
{"text": "Chennai", "size": 5.950},
{"text": "Taipei", "size": 5.700},
{"text": "Baghdad", "size": 5.500},
{"text": "Santiago", "size": 5.425},
{"text": "Bangalore", "size": 5.400},
{"text": "Hyderabad", "size": 5.300},
{"text": "St Petersburg", "size": 5.300}
]

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