1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-25 20:33:41 +00:00

Compare commits

..

75 Commits

Author SHA1 Message Date
jeremy@jermolene.com
bbdf9bae89 Merge branch 'master' into publishing-framework 2021-07-14 15:48:54 +01:00
jeremy@jermolene.com
0924ca6365 Update docs that mention store area 2021-07-14 13:08:19 +01:00
jeremy@jermolene.com
39fec2decf Update release note 2021-07-14 10:12:50 +01:00
jeremy@jermolene.com
dbfd45814d Search and replace "v5.1.24" to "v5.2.0"
As discussed in #5708
2021-07-14 09:21:37 +01:00
Jeremy Ruston
d455072f13 Add support for JSON-formatted tiddler store, and make it the default (#5708)
* Add support for JSON-formatted tiddler store, and make it the default

The change to `getTiddlersAsJson()` is to allow experimentation

* Move JSON tiddlers into their own store area, and fix support for encrypted tiddlers

Also add a dummy old-style store area for backwards compatibility

The current arrangement is that JSON tiddlers will always override old-style tiddlers.

* Use the deserialiser mechanism to decode the content

* Refactor $:/core/modules/deserializers.js before we start extending it

Cleaning up the helper function names and ordering

* Drop support for the "systemArea" div

It was only used in really old v5.0.x

* Update deserializer to support JSON store format and add some tests

* Life UI restrictions on characters in fieldnames

* Add another test case

* Correct mis-merge

* Remove toLowerCase() methods applied to fieldnames

* Insert line breaks in output of getTiddlersAsJson (#5786)

Rather than have the entire store on one line, insert a line break
after each tiddler.

* Refactor #5786 for backwards compatibility

* Only read .tiddlywiki-tiddler-store blocks from script tags

Prompted by @simonbaird's comment here: https://github.com/Jermolene/TiddlyWiki5/pull/5708#discussion_r648833367

* Clean up escaping of unsafe script characters

It seems that escaping `<` is sufficient

* Add docs from @saqimtiaz

Thanks @saqimtiaz

* Docs tweaks

* Remove excess whitespace

Thanks @simonbaird

* Fix templates for lazy loading

* Remove obsolete item from release note

* Clean up whitespace

* Docs for the jsontiddler widget

* Fix whitespace

Fixes #5840

* Comments

* Fix newlines in JSON store area

* Remove obsolete docs change

Co-authored-by: Simon Baird <simon.baird@gmail.com>
2021-07-14 09:15:30 +01:00
jeremy@jermolene.com
155525708b Update release note 2021-07-13 17:50:01 +01:00
jeremy@jermolene.com
fdca11dec3 Remove unneeded table class
I think this is a typo @pmario?
2021-07-13 17:49:52 +01:00
jeremy@jermolene.com
f83875331d Update release note 2021-07-12 19:36:36 +01:00
Saq Imtiaz
be6deb054e Update ActionCreateTiddlerWidget.tid (#5871) 2021-07-11 22:39:56 +01:00
Saq Imtiaz
b0604a9bf5 Update MessageCatcher docs to clarify usage (#5870) 2021-07-11 22:39:24 +01:00
Saq Imtiaz
30925ee7bf Update syntax for Eventcatcher (#5868)
* Update syntax for Eventcatcher to be consistent with MessageCatcher while being backwards compatible

* Update docs

* Update docs
2021-07-11 20:21:35 +01:00
Bram Chen
7204f442cd Add chinese translations for ExportTiddlyWikiCore/* (#5856)
* add ExportTiddlyWikiCore/Caption
* add ExportTiddlyWikiCore/Hint
2021-07-07 11:02:33 +01:00
cdruan
23fec9e390 Fix faulty external-js single-file wiki (#5570)
* Fix problems with building single-file wiki using external-js template

* core/templates/external-js/tiddlywiki5-external-js.html.tid,
  core/templates/external-js/save-all-external-js.tid,
  core/templates/external-js/save-offline-external-js.tid
  core/templates/external-js/load-external-js.tid:
  Fix #5343. Exclude client-server plugins in tiddler imports and to
  specify a working URL for loading tiddlywiki5.js from local disk.
  Mirror save/all and save/offline templates in the regular server
  edition.

  Fix #4717 (tiddlywiki5-external-js.html.tid)

* core/modules/saver-handler.js:
  Need the change to make single file autosave work with the external-js
  template.

* editions/server-external-js/tiddlywiki.info:
  Provide external-js related build targets.

* core/language/en-GB/Snippets/ExtJSReadme.tid:
  Temporary doc to supplement TW5.com's external-js section. Demonstrate
  that upgrade could be done on single-file wikis with an externalized
  TW core.

* core/language/en-GB/Snippets/GetTiddlyWikiJS.tid:
  Documentation. Meant to be included in every wiki and to help end
  users acquire tiddlywiki5.js.

* Pre-configure save-wiki template for end-users

* Remove the newline character at the end of the file.

* Trim "template" value in saveWiki()

* Safeguard the code from extraneous whitespaces in transcluded result.

* Rename and add versioning to downloaded tiddlywiki core JS

* Rename "tiddlywiki5.js" to "twcore-VERSION.js"

* Preload $:/config/SaveWikiButton/Template tiddler with the required
  external-js template value.

* Update external-js user documentation

* Add "download tiddlywiki core JS" menu item to the "cloud" button.

* Update build's target defintions associated with external-js template.

* Move the user doc to the tw5.com edition.

* Coding style update

* Undo template name changes

* Correct text & fill colors on some disabled buttons

* Add new "export tiddlywiki core" button under page control tools

This new button can export tiddlywiki's core JS from user's wiki as
long as the wiki is served with the regular "root" template. The
button will be ineffective, thus disabled, if the core has already been
externalized by the "external-js" template.

With this button, a full standalone html wiki can obtain the matching
core JS without TiddlyWiki on node.js. Once this is done, the html wiki
can be converted to using the "external-js" template.

* Alternate version of "save tiddlywiki core for offline use"

This version will fire up a "Save File" dialogue box when clicked,
instead of directing the user to a helper doc for further instruction.
It achieves this by using the "download" attribute of the <a> html tag.
It works on most modern desktop browsers, but older browsers (e.g. IE)
may display the file instead.

* Adjust font-weight to match other menu items

* Merge two user documentations into one

* Add user-browser-cache=yes to --listen command

* Update "export tiddlywiki core" button hint

* Simpler implementation for switching btw online/offline core URL

Shave off one template by using filtered transclusion to control
online/offline core URL.

* Update user doc

Update the user doc to clarify that build index step is not needed to
initialize a new wiki.

* Rename twcore to tiddlywikicore

* Reformat the user doc

* Rework export-tiddlywikicore button

Popup an error message instead of disabling the button when export
core cannot be performed.

* Revert "Correct text & fill colors on some disabled buttons"

This reverts commit e7dbb7e712.
2021-07-06 15:02:21 +01:00
RJ Skerry-Ryan
8d9dc0cd29 Markdown: Don't emit paragraph tags when a paragraph is "tight". (#5848)
* markdown: Don't emit paragraph tags when a paragraph is "tight".

Motivation: Since the upgrade to remarkable.js (#3876), lists are rendered as
HTML like this:

```
<ul>
  <li><p>One</p></li>
  <li><p>Two</p></li>
  <li><p>Three</p></li>
</ul>
```

The paragraph nodes insert blocks that break the visual flow of the list and are
unexpected e.g. compared to WikiText markup's rendering of a bulleted list.

Solution: remarkable.js annotates certain paragraph nodes as "tight", and in the
bulleted list case, the paragraph nodes wrapping the text of each list item are
marked tight.

remarkable uses the tight property to [elide paragraph tags in its
renderer](58b6945f20/lib/rules.js (L136-L142)).

This change implements the equivalent logic in TiddlyWiki's markdown rendering:
If a paragraph is marked tight, then we elide the `<p>` tag wrapping its children.

* Use ES5 Array.concat instead of ES6 spread operator.
2021-07-06 11:33:12 +01:00
RJ Skerry-Ryan
a1d9464011 Add optional KaTeX support to markdown plugin (#5846)
* Add optional KaTeX support to the tiddlywiki/markdown plugin.

Uses the remarkable-katex plugin 1.1.8 by Brad Howes to enable KaTeX support if
the tiddlywiki/katex plugin is installed. Fixes #2984.

TESTED:

Created a test wiki with:
```
$ node tiddlywiki.js test --init markdowndemo
$ node tiddlywiki.js test --listen
```

* Verified markdown support works without the tiddlywiki/katex plugin enabled.
* Verified markdown support works with the tiddlywiki/katex plugin enabled.
* Verified KaTeX (both inline and blocks) work as expected when the
tiddlywiki/katex plugin is enabled.

* Mention remarkable-katex plugin usage in the readme Tiddler.

* Include the remarkable-katex license as a tiddler.

* Include the Remarkable license.

* Include unminified original source of remarkable-katex 1.1.8.
2021-07-06 11:32:32 +01:00
jeremy@jermolene.com
0b71f25f74 Revert "Update sync methods (#5467)"
This reverts commit 8d7930f660.

See the discussion at https://github.com/Jermolene/TiddlyWiki5/pull/5467#issuecomment-873590578https://github.com/Jermolene/TiddlyWiki5/pull/5467#issuecomment-873590578
2021-07-05 19:26:20 +01:00
jeremy@jermolene.com
315464372f Version tags missed off 56068d8215 2021-07-05 10:09:04 +01:00
jeremy@jermolene.com
56068d8215 tm-navigate: add separate properties to access bounds of client rectangle
Makes it easier to use the client rectangle information within an action handler
2021-07-05 09:52:17 +01:00
jeremy@jermolene.com
1b55eb9eee Docs improvements; missed off 3094e06236 2021-07-02 14:41:59 +01:00
jeremy@jermolene.com
3094e06236 Add support for full refreshing of action widgets
Fixes #5791
2021-07-02 14:33:38 +01:00
jeremy@jermolene.com
f87b3bfcdb Extend messagecatcher widget to allow setting multiple handlers at once 2021-06-30 16:17:59 +01:00
jeremy@jermolene.com
a0a0df9655 Update action-navigate widget with metakeys to match link widget 2021-06-30 16:11:21 +01:00
jeremy@jermolene.com
31c1584b9a Extend $tw.utils.removeArrayEntries to return the array 2021-06-30 16:10:52 +01:00
Saq Imtiaz
f1f951e849 Docs for macro parameters in filter operands (#5837) 2021-06-29 23:25:44 +01:00
Saq Imtiaz
041c3e817c Support for macro params in filter operands (#5836)
* Exploratory pass at adding support for macro params in filter operands

* whitspace correction

* rename varInfo to varTree for disambiguation

* Refactored parseMacroInvocation to be re-usable, performance improvements for variables with no params and tests

* Revised regular expression and removed spurious white space changes

* Revised regular expression and removed spurious white space changes

* More whitespace cleanup and added more tests for edge cases

* Added test for macro params with square brackets
2021-06-29 22:21:39 +01:00
jeremy@jermolene.com
70e60cd93f Remove whitespace from plugin text
https://github.com/Jermolene/TiddlyWiki5/pull/5708#issuecomment-870749131

Has no effect on functionality, but makes the prerelease index.html go from 6821151 to 6680944 bytes (saving 6680944-6821151=-140,207 bytes or (6680944-6821151)/6680944=2.1%
2021-06-29 22:17:16 +01:00
Saq Imtiaz
a6990128f1 Fixed bug introduced into transclusions for blank fields in #5736 (#5835) 2021-06-29 12:07:14 +01:00
jeremy@jermolene.com
338b7c92a2 Ensure tiddlers with fieldnames containing colons don't get saved in .tid file format
Prompted by discussion over at https://github.com/Jermolene/TiddlyWiki5/pull/5708#issuecomment-862399985
2021-06-27 21:27:57 +01:00
Mario Pietsch
a409536ad0 Prevent scrolling of the page when modals are displayed (#5816)
* prevent scroll-chaining in modals

* make body overflow hidden to prevent background scrolling
2021-06-27 16:24:06 +01:00
Joe Bordes
c9af04d0e5 New Spanish translation strings (#5822) 2021-06-25 17:57:17 +01:00
Saq Imtiaz
076a04fbfb Added docs for th-closing-tiddler hook (#5820) 2021-06-23 09:51:46 +01:00
jeremy@jermolene.com
83ee363cb4 Add charcode operator to make it easier to generate strings containing control characters
Avoids some confusing hacks. @saqimtiaz I'm guessing you might have already done something like this?
2021-06-22 21:52:00 +01:00
Adam Sherwood
63fa0c4fa4 Hook for closing tiddlers (#3797) 2021-06-22 19:51:35 +01:00
Bram Chen
644062fc21 Add chinese translations for import cancel warning (#5818) 2021-06-22 09:58:38 +01:00
Saq Imtiaz
021e9b8c4d :map filter run prefix with docs and tests (#5813) 2021-06-21 20:59:58 +01:00
Mario Pietsch
afa653a7aa Improve import cancel warning (#5812) 2021-06-21 20:58:58 +01:00
Mario Pietsch
0b56d5fd37 update German translation (#5811) 2021-06-21 12:14:36 +01:00
jeremy@jermolene.com
2da7ae0b73 Action-createtiddler: Ensure child widgets are refreshed before invocation
Fixes #5791
2021-06-14 18:13:51 +01:00
Mario Pietsch
9c0d6a46cc Add "commentpragma" html style rule (#5726)
* html-comment, that can be used in the pragma area

* add commentpragma test

* fix typo

* fix typo and change comments ab bit

* combine html-comment and pragma-comment and add some docs, how to use it

* Make docs simpler by removing caching info

* change h2 wording
2021-06-14 17:39:56 +01:00
Saq Imtiaz
06318b7617 Pass reference to widget to CodeMirror (#5790) 2021-06-14 16:46:39 +01:00
Saq Imtiaz
8f9e8c1dee Keyboard widget: provide variable for shortcut descriptor to actions. (#5782) 2021-06-14 12:03:59 +01:00
Simon Huber
3cd80de5bb Revert #5720 and fix #5770 by removing :root { color-scheme: ... } 2021-06-14 11:01:00 +01:00
Simon Huber
f2e26927c1 Add test for event.event.target being undefined in the tm-focus-selector listener (#5771) 2021-06-14 10:59:31 +01:00
felixhayashi
960160b3a2 word break property when viewing field values (#1661) 2021-06-14 10:37:04 +01:00
Saq Imtiaz
4f33d2f35c Update release notes (#5780) 2021-06-11 18:15:07 +01:00
Odin
b5db488438 Update release notes 5.1.24 to 5.2.0 (#5754) 2021-06-11 17:56:45 +01:00
Simon Huber
6dd1887f0b Use event.event.view.confirm for confirmation messages in navigator.js and action-confirm.js (#5776) 2021-06-11 16:56:06 +01:00
Simon Huber
219beb13cc Add test to storyviews if targetElement is null (#5767)
* Update classic.js

* Update pop.js

* Update zoomin.js

* simplify test in classic.js

* simplify test in pop.js

* simplify test in zoomin.js
2021-06-09 10:18:15 +01:00
Simon Huber
c18b7527a7 Fix #5760 - tm-focus-selector doesn't work in new windows (#5766)
* Pass the original event to invokeActionString

* Update rootwidget.js
2021-06-06 12:42:28 +01:00
Simon Huber
2f1806ab6a Keyboard widget: don't refresh when class changes (#5758) 2021-06-06 11:03:08 +01:00
Simon Huber
afa4ea3d03 Make navigation in new windows work for storyviews (#5759) 2021-06-06 10:47:19 +01:00
Simon Huber
2b911ac11f Make the insert- and remove-animations of storyviews work in new windows (#5755)
* Make classic storyview work in new windows, too

* Make pop storyview work in new windows, too

* Make zoomin storyview insert and remove animation work in new windows, too
2021-06-04 16:59:45 +01:00
jeremy@jermolene.com
056e6541a1 Revert 582b156d5f: Refresh non-action widgets before invocation 2021-06-02 21:47:28 +01:00
jeremy@jermolene.com
753bf8fe62 Revert "Revert "Transclude widget: refresh selectively when needed (#5736)""
This reverts commit 4f9dd50382.
2021-06-02 21:45:06 +01:00
jeremy@jermolene.com
4f9dd50382 Revert "Transclude widget: refresh selectively when needed (#5736)"
This reverts commit 2e695801b1.
2021-06-02 19:14:05 +01:00
Saq Imtiaz
2e695801b1 Transclude widget: refresh selectively when needed (#5736)
* Transclude widget: only refresh when transcluded text reference has changed, includes tests

* Refactor wiki.parseTextReference so it is re-usable for getting the parser info

* Re-arrange methods in wiki.js to improve diff readability
2021-06-02 13:58:30 +01:00
jeremy@jermolene.com
f1d76a1eee Merge branch 'master' into publishing-framework 2021-06-01 13:17:47 +01:00
jeremy@jermolene.com
55c522ab8f Improve comments
As per @pmario's comment at 582b156d5f (commitcomment-51566608)
2021-06-01 11:49:05 +01:00
jeremy@jermolene.com
9faaa31299 Extend action-createtiddler to make new title available as a variable
I'm not sure if the docs are clear, but this is quite a big deal, and along with 582b156d5f makes working with action widgets a lot easier.
2021-06-01 09:41:14 +01:00
jeremy@jermolene.com
582b156d5f Refresh non-action widgets before invoking them
Fixes #5744
2021-06-01 09:28:04 +01:00
jeremy@jermolene.com
160c154ef1 Merge branch 'master' into publishing-framework 2021-05-20 13:53:29 +01:00
jeremy@jermolene.com
184083ad1a Use variable definition in the job/sitemap/route text instead of from var-* fields 2021-04-10 17:42:35 +01:00
jeremy@jermolene.com
9c62bd8030 Fix typo in link widget 2021-04-10 17:41:41 +01:00
jeremy@jermolene.com
b7419dec3a Finish refactoring of server <-> sitemap interface
Continuing c4cdb1ed8c
2021-04-10 13:51:07 +01:00
jeremy@jermolene.com
02d390a673 Add support for slugify to the link widget's tv-wikilink-template handling 2021-04-10 10:58:55 +01:00
jeremy@jermolene.com
c4cdb1ed8c Refactor sitemap route handling
To avoid duplicating the rendering code
2021-04-10 10:58:26 +01:00
jeremy@jermolene.com
5fcff1f1a3 Refactor server route handling to handle default documents properly 2021-04-10 10:57:52 +01:00
jeremy@jermolene.com
a8770d7645 Revert changes to image widget 2021-04-08 14:27:30 +01:00
jeremy@jermolene.com
df2a3fdefd Refactor sitemap handling so we can reuse it in the webserver 2021-04-07 12:16:12 +01:00
jeremy@jermolene.com
a8c248eb3d Revert to using parameterised paths instead of filters
Because we will be able to run parameterised paths in reverse for HTTP serving, which we can't do with filters
2021-04-06 12:19:49 +01:00
jeremy@jermolene.com
96103d5d4c Link to the PR 2021-04-05 17:09:43 +01:00
jeremy@jermolene.com
a025bce21f Clean up before publishing a snapshot to TiddlyHost 2021-04-05 17:07:19 +01:00
jeremy@jermolene.com
45cdd7bdf7 Merge branch 'master' into publishing-framework 2021-04-05 12:31:46 +01:00
jeremy@jermolene.com
509356c696 Add Node.js support, refactor modelling of jobs and sitemaps, and use modals framework 2021-04-04 13:18:41 +01:00
jeremy@jermolene.com
42e10d030a First commit 2021-03-22 09:34:32 +00:00
200 changed files with 2912 additions and 173449 deletions

View File

@@ -5,7 +5,7 @@
# Default to the current version number for building the plugin library
if [ -z "$TW5_BUILD_VERSION" ]; then
TW5_BUILD_VERSION=v5.1.24
TW5_BUILD_VERSION=v5.2.0
fi
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"

View File

@@ -757,7 +757,12 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
tiddler = $tw.wiki.getTiddler(name),
_exports = {},
sandbox = {
module: {exports: _exports},
module: {
exports: _exports,
setStringHandler: function(handler) {
moduleInfo.stringHandler = handler;
}
},
//moduleInfo: moduleInfo,
exports: _exports,
console: console,
@@ -821,7 +826,7 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
moduleInfo.definition(moduleInfo,moduleInfo.exports,sandbox.require);
} else if(typeof moduleInfo.definition === "string") { // String
moduleInfo.exports = _exports;
$tw.utils.evalSandboxed(moduleInfo.definition,sandbox,tiddler.fields.title);
$tw.utils.evalSandboxed(moduleInfo.definition,sandbox,name);
if(sandbox.module.exports) {
moduleInfo.exports = sandbox.module.exports; //more codemirror workaround
}
@@ -918,6 +923,20 @@ $tw.modules.createClassesFromModules = function(moduleType,subType,baseClass) {
return classes;
};
/*
Return a specified module string for a module, null if the module or string is missing
*/
$tw.modules.getModuleString = function(moduleName,stringName,language) {
if(moduleName in $tw.modules.titles) {
$tw.modules.execute(moduleName);
var stringHandler = $tw.modules.titles[moduleName].stringHandler;
if(stringHandler) {
return stringHandler(stringName,language);
}
}
return null;
};
/////////////////////////// Barebones tiddler object
/*
@@ -1724,13 +1743,20 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/dom","tiddlerdeserializer",{
},
t,result = [];
if(node) {
for(t = 0; t < node.childNodes.length; t++) {
var type = (node.getAttribute && node.getAttribute("type")) || null;
if(type) {
// A new-style container with an explicit deserialization type
result = $tw.wiki.deserializeTiddlers(type,node.textContent);
} else {
// An old-style container of classic DIV-based tiddlers
for(t = 0; t < node.childNodes.length; t++) {
var childNode = node.childNodes[t],
tiddlers = extractTextTiddlers(childNode);
tiddlers = tiddlers || extractModuleTiddlers(childNode);
if(tiddlers) {
result.push.apply(result,tiddlers);
}
}
}
}
return result;
@@ -1739,17 +1765,23 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/dom","tiddlerdeserializer",{
$tw.loadTiddlersBrowser = function() {
// In the browser, we load tiddlers from certain elements
var containerIds = [
"libraryModules",
"modules",
"bootKernelPrefix",
"bootKernel",
"styleArea",
"storeArea",
"systemArea"
var containerSelectors = [
// IDs for old-style v5.1.x tiddler stores
"#libraryModules",
"#modules",
"#bootKernelPrefix",
"#bootKernel",
"#styleArea",
"#storeArea",
"#systemArea",
// Classes for new-style v5.2.x JSON tiddler stores
"script.tiddlywiki-tiddler-store"
];
for(var t=0; t<containerIds.length; t++) {
$tw.wiki.addTiddlers($tw.wiki.deserializeTiddlers("(DOM)",document.getElementById(containerIds[t])));
for(var t=0; t<containerSelectors.length; t++) {
var nodes = document.querySelectorAll(containerSelectors[t]);
for(var n=0; n<nodes.length; n++) {
$tw.wiki.addTiddlers($tw.wiki.deserializeTiddlers("(DOM)",nodes[n]));
}
}
};
@@ -1985,7 +2017,7 @@ $tw.loadPluginFolder = function(filepath,excludeRegExp) {
pluginInfo.dependents = pluginInfo.dependents || [];
pluginInfo.type = "application/json";
// Set plugin text
pluginInfo.text = JSON.stringify({tiddlers: pluginInfo.tiddlers},null,4);
pluginInfo.text = JSON.stringify({tiddlers: pluginInfo.tiddlers});
delete pluginInfo.tiddlers;
// Deserialise array fields (currently required for the dependents field)
for(var field in pluginInfo) {

7
core/images/publish.tid Normal file
View File

@@ -0,0 +1,7 @@
title: $:/core/images/publish
tags: $:/tags/Image
<svg width="22pt" height="22pt" class="tc-image-publish tc-image-button" viewBox="0 0 128 128"><g fill-rule="evenodd">
<path d="M64.0434107,46.2358498 C65.8048912,45.8955184 67.6195684,46.7809274 68.4102078,48.4458716 L68.4937877,48.6340507 L98.8972485,122.034498 C99.7426494,124.075476 98.7734424,126.415349 96.7324641,127.26075 C94.7552664,128.079732 92.4975633,127.195747 91.5897922,125.284145 L91.5062123,125.095966 L88.403,117.605598 L79.5048497,126.50485 C78.775871,127.233828 77.8355254,127.622617 76.8810886,127.671216 L76.6764226,127.676423 C75.6527333,127.676423 74.6290441,127.285898 73.8479955,126.50485 L63.999,116.656598 L54.1520045,126.50485 C53.4230259,127.233828 52.4826802,127.622617 51.5282434,127.671216 L51.3235774,127.676423 C50.2998881,127.676423 49.2761989,127.285898 48.4951503,126.50485 L39.596,117.605598 L36.4937877,125.095966 C35.6483868,127.136944 33.3085142,128.106151 31.2675359,127.26075 C29.2265576,126.415349 28.2573506,124.075476 29.1027515,122.034498 L59.5062123,48.6340507 C60.2518688,46.8338766 62.1601511,45.867488 64.0005148,46.2445933 L64.0434107,46.2358498 Z M77.679,102.976598 L69.656,110.999598 L76.676,118.018598 L84.699,109.996598 L77.679,102.976598 Z M50.326,102.983598 L43.307,110.002598 L51.323,118.018598 L58.342,110.999598 L50.326,102.983598 Z M64.006,89.3035977 L55.983,97.3265977 L63.999,105.342598 L72.022,97.3195977 L64.006,89.3035977 Z M55.035,80.3325977 L50.348,91.6475977 L58.349,83.6465977 L55.035,80.3325977 Z M72.968,80.3415977 L69.663,83.6465977 L77.642,91.6255977 L72.968,80.3415977 Z M64,58.6895977 L58.3344072,72.3699713 C58.4295481,72.4423583 58.5221874,72.5195778 58.6119911,72.6016299 L58.7445283,72.7283325 L64.006,77.9895977 L69.2684411,72.7283325 C69.3957231,72.6010505 69.5294489,72.4841397 69.6685683,72.3775999 L64,58.6895977 Z M38.5026635,2.59571369 C40.7118025,2.59571369 42.5026635,4.38657469 42.5026635,6.59571369 C42.5026635,7.7643162 42.0015346,8.8158738 41.2024517,9.54721162 L41.372583,9.372583 C35.581722,15.163444 32,23.163444 32,32 C32,40.8370609 35.5821314,48.8374703 41.3735757,54.6284097 C42.0884306,55.3520538 42.531226,56.3471171 42.531226,57.4456008 C42.531226,59.6547398 40.740365,61.4456008 38.531226,61.4456008 C37.4327422,61.4456008 36.4376789,61.0028055 35.7147885,60.2859673 L35.7048234,60.2963403 C28.472656,53.0586276 24,43.0631255 24,32.0229786 C24,21.1046577 28.3744907,11.2080539 35.4664167,3.99022262 C36.2004724,3.13678606 37.2883808,2.59571369 38.5026635,2.59571369 Z M89.3688013,2.48959773 C90.5097745,2.48959773 91.5391719,2.96731026 92.2678917,3.73363348 L92.2733617,3.72780197 C99.5183488,10.9672382 104,20.9717355 104,32.0229786 C104,42.8488024 99.6993143,52.6701472 92.7132398,59.8717162 C91.9816831,60.8152368 90.8371876,61.4223982 89.5508819,61.4223982 C87.3417429,61.4223982 85.5508819,59.6315372 85.5508819,57.4223982 C85.5508819,56.3502862 85.9726717,55.3766885 86.6593633,54.6584932 L86.627417,54.627417 C92.418278,48.836556 96,40.836556 96,32 C96,23.2702193 92.5043135,15.3568999 86.8363661,9.5834674 C85.9399594,8.85136737 85.3688013,7.73718311 85.3688013,6.48959773 C85.3688013,4.28045873 87.1596623,2.48959773 89.3688013,2.48959773 Z M50.0871028,13.6297119 C52.2962418,13.6297119 54.0871028,15.4205729 54.0871028,17.6297119 C54.0871028,18.8506134 53.5401157,19.9437593 52.6778337,20.6774573 L52.6862915,20.6862915 C49.790861,23.581722 48,27.581722 48,32 C48,36.4187838 49.791271,40.4191937 52.6872859,43.3147028 L52.6777105,43.3251493 C53.4423082,44.0537041 53.918794,45.0819221 53.918794,46.2214294 C53.918794,48.4305684 52.127933,50.2214294 49.918794,50.2214294 C48.6900999,50.2214294 47.5907975,49.6674376 46.8570439,48.7956109 L47.0304281,48.9715536 C42.6867001,44.6283295 40,38.627921 40,32 C40,25.372583 42.6862915,19.372583 47.0294373,15.0294373 L47.0395567,15.0387468 C47.7732533,14.1766 48.8663118,13.6297119 50.0871028,13.6297119 Z M77.9823819,13.700452 C79.1679628,13.700452 80.2330731,14.2162483 80.9655529,15.0356811 L80.9715536,15.0304281 C85.314117,19.3734956 88,25.373087 88,32 C88,38.4619387 85.4461804,44.3274009 81.2927766,48.6421512 L80.9933974,48.9476975 C80.2638884,49.7186971 79.2309977,50.2000547 78.08568,50.2000547 C75.876541,50.2000547 74.08568,48.4091937 74.08568,46.2000547 C74.08568,45.0665777 74.557136,44.0432152 75.314649,43.3153662 L75.3137085,43.3137085 C78.209139,40.418278 80,36.418278 80,32 C80,27.5822253 78.209547,23.5826334 75.314698,20.6872811 C74.4981782,19.9511432 73.9823819,18.8860329 73.9823819,17.700452 C73.9823819,15.491313 75.7732429,13.700452 77.9823819,13.700452 Z M64,24 C68.418278,24 72,27.581722 72,32 C72,36.418278 68.418278,40 64,40 C59.581722,40 56,36.418278 56,32 C56,27.581722 59.581722,24 64,24 Z"></path>
</g>
</svg>

View File

@@ -32,6 +32,8 @@ ExportTiddler/Caption: export tiddler
ExportTiddler/Hint: Export tiddler
ExportTiddlers/Caption: export tiddlers
ExportTiddlers/Hint: Export tiddlers
ExportTiddlyWikiCore/Caption: export TiddlyWiki core
ExportTiddlyWikiCore/Hint: Export the ~TiddlyWiki core code for running with external ~JavaScript
SidebarSearch/Hint: Select the sidebar search field
Fold/Caption: fold tiddler
Fold/Hint: Fold the body of this tiddler
@@ -83,6 +85,8 @@ Permaview/Caption: permaview
Permaview/Hint: Set browser address bar to a direct link to all the tiddlers in this story
Print/Caption: print page
Print/Hint: Print the current page
Publish/Caption: publish
Publish/Hint: Publish from the wiki
Refresh/Caption: refresh
Refresh/Hint: Perform a full refresh of the wiki
Save/Caption: ok

View File

@@ -96,6 +96,8 @@ Plugins/Updates/Caption: Updates
Plugins/Updates/Hint: Available updates to installed plugins
Plugins/Updates/UpdateAll/Caption: Update <<update-count>> plugins
Plugins/SubPluginPrompt: With <<count>> sub-plugins available
Publishing/Caption: Publishing
Publishing/Hint: Settings used for publishing extracts from this TiddlyWiki as separate files through a "publisher" module
Saving/Caption: Saving
Saving/DownloadSaver/AutoSave/Description: Permit automatic saving for the download saver
Saving/DownloadSaver/AutoSave/Hint: Enable Autosave for Download Saver

View File

@@ -23,6 +23,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
* ''writers'' - comma-separated list of principals allowed to write to this wiki
* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no")
* ''sse-enabled'' - set to "yes" to enable Server-sent events (defaults to "no")
* ''sitemap'' - optional sitemap describing how the tiddlers will be served. See [[Publishing]] for more details
* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html")

View File

@@ -3,6 +3,7 @@ title: $:/language/Import/
Editor/Import/Heading: Import images and insert them into the editor.
Imported/Hint: The following tiddlers were imported:
Listing/Cancel/Caption: Cancel
Listing/Cancel/Warning: Do you wish to cancel the import?
Listing/Hint: These tiddlers are ready to import:
Listing/Import/Caption: Import
Listing/Select/Caption: Select

View File

@@ -41,7 +41,6 @@ Error/WhileSaving: Error while saving
Error/XMLHttpRequest: XMLHttpRequest error code
InternalJavaScriptError/Title: Internal JavaScript Error
InternalJavaScriptError/Hint: Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser
InvalidFieldName: Illegal characters in field name "<$text text=<<fieldName>>/>". Fields can only contain lowercase letters, digits and the characters underscore (`_`), hyphen (`-`) and period (`.`)
LayoutSwitcher/Description: Open the layout switcher
LazyLoadingWarning: <p>Trying to load external content from ''<$text text={{!!_canonical_uri}}/>''</p><p>If this message doesn't disappear, either the tiddler content type doesn't match the type of the external content, or you may be using a browser that doesn't support external content for wikis loaded as standalone files. See https://tiddlywiki.com/#ExternalText</p>
LoginToTiddlySpace: Login to TiddlySpace

View File

@@ -0,0 +1,5 @@
title: $:/language/Publishing/Modal
subtitle: Publishing: ''<$transclude field="caption"><$view field="title"/></$transclude>''
footer: <$button message="tm-close-tiddler">Cancel</$button>
Publishing <$text text=<<totalFiles>>/> files via the "{{!!publisher}}" publisher.

View File

@@ -0,0 +1,46 @@
/*\
title: $:/core/modules/commands/publish.js
type: application/javascript
module-type: command
Publish static files
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "publish",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
if(this.params.length < 1) {
return "Missing filename filter";
}
var self = this,
wiki = this.commander.wiki,
jobTiddler = this.params[0],
variableList = this.params.slice(1),
variables = Object.create(null);
while(variableList.length >= 2) {
variables[variableList[0]] = variableList[1];
variableList = variableList.slice(2);
}
$tw.publisherHandler.publish(jobTiddler,this.callback,{commander: this.commander,variables: variables});
return null;
};
exports.Command = Command;
})();

View File

@@ -40,10 +40,10 @@ Command.prototype.execute = function() {
variableList = this.params.slice(4),
tiddlers = wiki.filterTiddlers(tiddlerFilter),
variables = Object.create(null);
while(variableList.length >= 2) {
variables[variableList[0]] = variableList[1];
variableList = variableList.slice(2);
}
while(variableList.length >= 2) {
variables[variableList[0]] = variableList[1];
variableList = variableList.slice(2);
}
$tw.utils.each(tiddlers,function(title) {
var parser = wiki.parseTiddler(template || title);
var widgetNode = wiki.makeWidget(parser,{variables: $tw.utils.extend({},variables,{currentTiddler: title})}),

View File

@@ -12,63 +12,8 @@ Functions to deserialise tiddlers from a block of text
/*global $tw: false */
"use strict";
/*
Utility function to parse an old-style tiddler DIV in a *.tid file. It looks like this:
<div title="Title" creator="JoeBloggs" modifier="JoeBloggs" created="201102111106" modified="201102111310" tags="myTag [[my long tag]]">
<pre>The text of the tiddler (without the expected HTML encoding).
</pre>
</div>
Note that the field attributes are HTML encoded, but that the body of the <PRE> tag is not encoded.
When these tiddler DIVs are encountered within a TiddlyWiki HTML file then the body is encoded in the usual way.
*/
var parseTiddlerDiv = function(text /* [,fields] */) {
// Slot together the default results
var result = {};
if(arguments.length > 1) {
for(var f=1; f<arguments.length; f++) {
var fields = arguments[f];
for(var t in fields) {
result[t] = fields[t];
}
}
}
// Parse the DIV body
var startRegExp = /^\s*<div\s+([^>]*)>(\s*<pre>)?/gi,
endRegExp,
match = startRegExp.exec(text);
if(match) {
// Old-style DIVs don't have the <pre> tag
if(match[2]) {
endRegExp = /<\/pre>\s*<\/div>\s*$/gi;
} else {
endRegExp = /<\/div>\s*$/gi;
}
var endMatch = endRegExp.exec(text);
if(endMatch) {
// Extract the text
result.text = text.substring(match.index + match[0].length,endMatch.index);
// Process the attributes
var attrRegExp = /\s*([^=\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi,
attrMatch;
do {
attrMatch = attrRegExp.exec(match[1]);
if(attrMatch) {
var name = attrMatch[1];
var value = attrMatch[2] !== undefined ? attrMatch[2] : attrMatch[3];
result[name] = value;
}
} while(attrMatch);
return result;
}
}
return undefined;
};
exports["application/x-tiddler-html-div"] = function(text,fields) {
return [parseTiddlerDiv(text,fields)];
return [deserializeTiddlerDiv(text,fields)];
};
exports["application/json"] = function(text,fields) {
@@ -105,30 +50,34 @@ Parse an HTML file into tiddlers. There are three possibilities:
# An ordinary HTML file
*/
exports["text/html"] = function(text,fields) {
// Check if we've got a store area
var results = [];
// Check if we've got an old-style store area
var storeAreaMarkerRegExp = /<div id=["']?storeArea['"]?( style=["']?display:none;["']?)?>/gi,
match = storeAreaMarkerRegExp.exec(text);
if(match) {
// If so, it's either a classic TiddlyWiki file or an unencrypted TW5 file
// First read the normal tiddlers
var results = deserializeTiddlyWikiFile(text,storeAreaMarkerRegExp.lastIndex,!!match[1],fields);
// Then any system tiddlers
var systemAreaMarkerRegExp = /<div id=["']?systemArea['"]?( style=["']?display:none;["']?)?>/gi,
sysMatch = systemAreaMarkerRegExp.exec(text);
if(sysMatch) {
results.push.apply(results,deserializeTiddlyWikiFile(text,systemAreaMarkerRegExp.lastIndex,!!sysMatch[1],fields));
}
storeAreaMatch = storeAreaMarkerRegExp.exec(text);
if(storeAreaMatch) {
// If so, we've got tiddlers in classic TiddlyWiki format or unencrypted old-style TW5 format
results.push.apply(results,deserializeStoreArea(text,storeAreaMarkerRegExp.lastIndex,!!storeAreaMatch[1],fields));
}
// Check for new-style store areas
var newStoreAreaMarkerRegExp = /<script class="tiddlywiki-tiddler-store" type="([^"]*)">/gi,
newStoreAreaMatch = newStoreAreaMarkerRegExp.exec(text),
haveHadNewStoreArea = !!newStoreAreaMatch;
while(newStoreAreaMatch) {
results.push.apply(results,deserializeNewStoreArea(text,newStoreAreaMarkerRegExp.lastIndex,newStoreAreaMatch[1],fields));
newStoreAreaMatch = newStoreAreaMarkerRegExp.exec(text);
}
// Return if we had either an old-style or a new-style store area
if(storeAreaMatch || haveHadNewStoreArea) {
return results;
}
// Otherwise, check whether we've got an encrypted file
var encryptedStoreArea = $tw.utils.extractEncryptedStoreArea(text);
if(encryptedStoreArea) {
// If so, attempt to decrypt it using the current password
return $tw.utils.decryptStoreArea(encryptedStoreArea);
} else {
// Check whether we've got an encrypted file
var encryptedStoreArea = $tw.utils.extractEncryptedStoreArea(text);
if(encryptedStoreArea) {
// If so, attempt to decrypt it using the current password
return $tw.utils.decryptStoreArea(encryptedStoreArea);
} else {
// It's not a TiddlyWiki so we'll return the entire HTML file as a tiddler
return deserializeHtmlFile(text,fields);
}
// It's not a TiddlyWiki so we'll return the entire HTML file as a tiddler
return deserializeHtmlFile(text,fields);
}
};
@@ -142,7 +91,19 @@ function deserializeHtmlFile(text,fields) {
return [result];
}
function deserializeTiddlyWikiFile(text,storeAreaEnd,isTiddlyWiki5,fields) {
function deserializeNewStoreArea(text,storeAreaEnd,type,fields) {
var endOfScriptRegExp = /<\/script>/gi;
endOfScriptRegExp.lastIndex = storeAreaEnd;
var match = endOfScriptRegExp.exec(text);
if(match) {
var scriptContent = text.substring(storeAreaEnd,match.index);
return $tw.wiki.deserializeTiddlers(type,scriptContent);
} else {
return [];
}
}
function deserializeStoreArea(text,storeAreaEnd,isTiddlyWiki5,fields) {
var results = [],
endOfDivRegExp = /(<\/div>\s*)/gi,
startPos = storeAreaEnd,
@@ -151,7 +112,7 @@ function deserializeTiddlyWikiFile(text,storeAreaEnd,isTiddlyWiki5,fields) {
var match = endOfDivRegExp.exec(text);
while(match) {
var endPos = endOfDivRegExp.lastIndex,
tiddlerFields = parseTiddlerDiv(text.substring(startPos,endPos),fields,{type: defaultType});
tiddlerFields = deserializeTiddlerDiv(text.substring(startPos,endPos),fields,{type: defaultType});
if(!tiddlerFields) {
break;
}
@@ -169,4 +130,59 @@ function deserializeTiddlyWikiFile(text,storeAreaEnd,isTiddlyWiki5,fields) {
return results;
}
/*
Utility function to parse an old-style tiddler DIV in a *.tid file. It looks like this:
<div title="Title" creator="JoeBloggs" modifier="JoeBloggs" created="201102111106" modified="201102111310" tags="myTag [[my long tag]]">
<pre>The text of the tiddler (without the expected HTML encoding).
</pre>
</div>
Note that the field attributes are HTML encoded, but that the body of the <PRE> tag is not encoded.
When these tiddler DIVs are encountered within a TiddlyWiki HTML file then the body is encoded in the usual way.
*/
var deserializeTiddlerDiv = function(text /* [,fields] */) {
// Slot together the default results
var result = {};
if(arguments.length > 1) {
for(var f=1; f<arguments.length; f++) {
var fields = arguments[f];
for(var t in fields) {
result[t] = fields[t];
}
}
}
// Parse the DIV body
var startRegExp = /^\s*<div\s+([^>]*)>(\s*<pre>)?/gi,
endRegExp,
match = startRegExp.exec(text);
if(match) {
// Old-style DIVs don't have the <pre> tag
if(match[2]) {
endRegExp = /<\/pre>\s*<\/div>\s*$/gi;
} else {
endRegExp = /<\/div>\s*$/gi;
}
var endMatch = endRegExp.exec(text);
if(endMatch) {
// Extract the text
result.text = text.substring(match.index + match[0].length,endMatch.index);
// Process the attributes
var attrRegExp = /\s*([^=\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi,
attrMatch;
do {
attrMatch = attrRegExp.exec(match[1]);
if(attrMatch) {
var name = attrMatch[1];
var value = attrMatch[2] !== undefined ? attrMatch[2] : attrMatch[3];
result[name] = value;
}
} while(attrMatch);
return result;
}
}
return undefined;
};
})();

View File

@@ -0,0 +1,39 @@
/*\
title: $:/core/modules/filterrunprefixes/map.js
type: application/javascript
module-type: filterrunprefix
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter prefix function
*/
exports.map = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var inputTitles = results.toArray();
results.clear();
$tw.utils.each(inputTitles,function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name) {
switch(name) {
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return widget.getVariable("currentTiddler");
default:
return widget.getVariable(name);
}
}
});
results.push(filtered[0] || "");
});
}
}
};
})();

View File

@@ -253,7 +253,8 @@ exports.compileFilter = function(filterString) {
if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) {
operand.value = widget.getVariable(operand.text,{defaultValue: ""});
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""});
} else {
operand.value = operand.text;
}

View File

@@ -17,7 +17,7 @@ Export our filter function
*/
exports.contains = function(source,operator,options) {
var results = [],
fieldname = (operator.suffix || "list").toLowerCase();
fieldname = operator.suffix || "list";
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(tiddler) {

View File

@@ -17,7 +17,7 @@ Export our filter function
*/
exports.field = function(source,operator,options) {
var results = [],indexedResults,
fieldname = (operator.suffix || operator.operator || "title").toLowerCase();
fieldname = operator.suffix || operator.operator || "title";
if(operator.prefix === "!") {
if(operator.regexp) {
source(function(tiddler,title) {

View File

@@ -0,0 +1,30 @@
/*\
title: $:/core/modules/filters/moduleproperty.js
type: application/javascript
module-type: filteroperator
Filter [[module-name]moduleproperty[name]] retrieve a module property
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.moduleproperty = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var value = require(title)[operator.operand || ""];
if(value !== undefined) {
results.push(value);
}
});
results.sort();
return results;
};
})();

View File

@@ -17,11 +17,23 @@ Export our filter function
*/
exports.modules = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
$tw.utils.each($tw.modules.types[title],function(moduleInfo,moduleName) {
results.push(moduleName);
if(operator.operands.length === 1) {
// Return all the module names without filtering
source(function(tiddler,title) {
$tw.utils.each($tw.modules.types[title],function(moduleInfo,moduleName) {
results.push(moduleName);
});
});
});
} else if(operator.operands.length >= 2) {
// Return the modules that have the module property specified in the first operand with the value in the second operand
source(function(tiddler,title) {
$tw.utils.each($tw.modules.types[title],function(moduleInfo,moduleName) {
if(require(moduleName)[operator.operands[0]] === operator.operands[1]) {
results.push(moduleName);
}
});
});
}
results.sort();
return results;
};

View File

@@ -0,0 +1,30 @@
/*\
title: $:/core/modules/filters/modulestring.js
type: application/javascript
module-type: filteroperator
Filter [[module-name]modulestring[en-gb]] retrieve a module strings in a particular language
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.modulestring = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var s = $tw.modules.getModuleString(title,operator.operands[0] || "",operator.operands[1] || "");
if(s !== null) {
results.push(s);
}
});
results.sort();
return results;
};
})();

View File

@@ -17,7 +17,7 @@ Export our filter function
*/
exports.regexp = function(source,operator,options) {
var results = [],
fieldname = (operator.suffix || "title").toLowerCase(),
fieldname = operator.suffix || "title",
regexpString, regexp, flags = "", match,
getFieldString = function(tiddler,title) {
if(tiddler) {

View File

@@ -172,4 +172,14 @@ exports.pad = function(source,operator,options) {
return results;
}
exports.charcode = function(source,operator,options) {
var chars = [];
$tw.utils.each(operator.operands,function(operand) {
if(operand !== "") {
chars.push(String.fromCharCode($tw.utils.parseInt(operand)));
}
});
return [chars.join("")];
};
})();

View File

@@ -179,7 +179,7 @@ Key descriptors have the following format:
ctrl+enter
ctrl+shift+alt+A
*/
KeyboardManager.prototype.parseKeyDescriptor = function(keyDescriptor) {
KeyboardManager.prototype.parseKeyDescriptor = function(keyDescriptor,options) {
var components = keyDescriptor.split(/\+|\-/),
info = {
keyCode: 0,
@@ -206,6 +206,9 @@ KeyboardManager.prototype.parseKeyDescriptor = function(keyDescriptor) {
info.keyCode = this.namedKeys[s];
}
}
if(options.keyDescriptor) {
info.keyDescriptor = options.keyDescriptor;
}
if(info.keyCode) {
return info;
} else {
@@ -237,6 +240,7 @@ KeyboardManager.prototype.parseKeyDescriptors = function(keyDescriptors,options)
lookupName = function(configName) {
var keyDescriptors = wiki.getTiddlerText("$:/config/" + configName + "/" + name);
if(keyDescriptors) {
options.keyDescriptor = keyDescriptor;
result.push.apply(result,self.parseKeyDescriptors(keyDescriptors,options));
}
};
@@ -245,7 +249,7 @@ KeyboardManager.prototype.parseKeyDescriptors = function(keyDescriptors,options)
});
}
} else {
result.push(self.parseKeyDescriptor(keyDescriptor));
result.push(self.parseKeyDescriptor(keyDescriptor,options));
}
});
return result;
@@ -276,12 +280,16 @@ KeyboardManager.prototype.checkKeyDescriptor = function(event,keyInfo) {
};
KeyboardManager.prototype.checkKeyDescriptors = function(event,keyInfoArray) {
return (this.getMatchingKeyDescriptor(event,keyInfoArray) !== null);
};
KeyboardManager.prototype.getMatchingKeyDescriptor = function(event,keyInfoArray) {
for(var t=0; t<keyInfoArray.length; t++) {
if(this.checkKeyDescriptor(event,keyInfoArray[t])) {
return true;
return keyInfoArray[t];
}
}
return false;
return null;
};
KeyboardManager.prototype.getEventModifierKeyDescriptor = function(event) {
@@ -324,7 +332,7 @@ KeyboardManager.prototype.handleKeydownEvent = function(event) {
if(key !== undefined) {
event.preventDefault();
event.stopPropagation();
$tw.rootWidget.invokeActionString(action,$tw.rootWidget);
$tw.rootWidget.invokeActionString(action,$tw.rootWidget,event);
return true;
}
return false;

View File

@@ -123,6 +123,19 @@ exports.parseStringLiteral = function(source,pos) {
}
};
exports.parseMacroParameters = function(node,source,pos) {
// Process parameters
var parameter = $tw.utils.parseMacroParameter(source,pos);
while(parameter) {
node.params.push(parameter);
pos = parameter.end;
// Get the next parameter
parameter = $tw.utils.parseMacroParameter(source,pos);
}
node.end = pos;
return node;
}
/*
Look for a macro invocation parameter. Returns null if not found, or {type: "macro-parameter", name:, value:, start:, end:}
*/
@@ -187,14 +200,8 @@ exports.parseMacroInvocation = function(source,pos) {
}
node.name = name.match[1];
pos = name.end;
// Process parameters
var parameter = $tw.utils.parseMacroParameter(source,pos);
while(parameter) {
node.params.push(parameter);
pos = parameter.end;
// Get the next parameter
parameter = $tw.utils.parseMacroParameter(source,pos);
}
node = $tw.utils.parseMacroParameters(node,source,pos);
pos = node.end;
// Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a double greater than sign
@@ -208,6 +215,29 @@ exports.parseMacroInvocation = function(source,pos) {
return node;
};
exports.parseFilterVariable = function(source) {
var node = {
name: "",
params: [],
},
pos = 0,
reName = /([^\s"']+)/g;
// If there is no whitespace or it is an empty string then there are no macro parameters
if(/^\S*$/.test(source)) {
node.name = source;
return node;
}
// Get the variable name
var nameMatch = $tw.utils.parseTokenRegExp(source,pos,reName);
if(nameMatch) {
node.name = nameMatch.match[1];
pos = nameMatch.end;
node = $tw.utils.parseMacroParameters(node,source,pos);
delete node.end;
}
return node;
};
/*
Look for an HTML attribute definition. Returns null if not found, otherwise returns {type: "attribute", name:, valueType: "string|indirect|macro", value:, start:, end:,}
*/

View File

@@ -7,6 +7,12 @@ Wiki text block rule for HTML comments. For example:
```
<!-- This is a comment -->
\define macroX()
<!-- This is a comment -->
xxxx
\end
<!-- This is a comment -->
```
Note that the syntax for comments is simplified to an opening "<!--" sequence and a closing "-->" sequence -- HTML itself implements a more complex format (see http://ostermiller.org/findhtmlcomment.html)
@@ -19,7 +25,7 @@ Note that the syntax for comments is simplified to an opening "<!--" sequence an
"use strict";
exports.name = "commentblock";
exports.types = {block: true};
exports.types = {block:true, pragma:true};
exports.init = function(parser) {
this.parser = parser;

View File

@@ -0,0 +1,181 @@
/*\
title: $:/core/modules/publisherhandler.js
type: application/javascript
module-type: global
The publisher manages publishing extracts of wikis as external files
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var PUBLISHING_MODAL_TITLE = "$:/language/Publishing/Modal";
/*
Instantiate the publisher manager with the following options
wiki: wiki object to be used
commander: commander object to be used for output
*/
function PublisherHandler(options) {
this.wiki = options.wiki;
this.commander = options.commander;
}
/*
Publish a job
jobTitle: title of tiddler containing details of the job
callback: completion callback invoked callback(err)
options: Include:
commander: commander object associated with publishing under Node.js
variables: hashmap of variables to be passed to renderings
*/
PublisherHandler.prototype.publish = function(jobTitle,callback,options) {
if(jobTitle) {
var job = new PublishingJob(jobTitle,this,options);
job.publish(callback);
}
};
function PublishingJob(jobTitle,publisherHandler,options) {
options = options || {};
// Save params
this.jobTitle = jobTitle;
this.publisherHandler = publisherHandler;
this.commander = options.commander;
this.publishVariables = options.variables || Object.create(null);
}
/*
Start publishing
*/
PublishingJob.prototype.publish = function(callback) {
var self = this;
// Get the job tiddler and check it is enabled
this.jobTiddler = this.publisherHandler.wiki.getTiddler(this.jobTitle);
if(this.jobTiddler && this.jobTiddler.fields.enabled === "yes") {
// Get the list of tiddlers to be exported, defaulting to all non-system tiddlers
this.exportList = this.publisherHandler.wiki.filterTiddlers(this.jobTiddler.fields["export-filter"] || "[!is[system]]");
// Get publisher
this.publisher = this.getPublisher(this.jobTiddler.fields.publisher);
if(this.publisher) {
// Get the sitemap
this.sitemap = new $tw.Sitemap(this.jobTiddler.fields.sitemap,{
wiki: this.publisherHandler.wiki,
variables: this.publishVariables
});
this.sitemap.load();
// Get the output operations
this.operations = this.sitemap.getAllFileDetails(this.exportList);
// Display the progress modal
if($tw.modal) {
this.progressModal = $tw.modal.display(PUBLISHING_MODAL_TITLE,{
progress: true,
variables: {
currentTiddler: this.jobTitle,
totalFiles: this.operations.length + ""
},
onclose: function(event) {
if(event !== self) {
// The modal was closed other than by us programmatically
self.isCancelled = true;
}
}
});
}
// Send the operations to the publisher
this.executeOperations(function(err) {
if(self.progressModal) {
self.progressModal.closeHandler(self);
}
callback(err);
});
} else {
return callback("Unrecognised publisher");
}
} else {
return callback("Missing or disabled job tiddler");
}
};
/*
Instantiate the required publisher object
*/
PublishingJob.prototype.getPublisher = function(publisherName) {
var publisher;
$tw.modules.forEachModuleOfType("publisher",function(title,module) {
if(module.name === publisherName) {
publisher = module;
}
});
return publisher && publisher.create(this.jobTiddler.fields,this.publisherHandler,this);
};
/*
Execute the operations for this job
*/
PublishingJob.prototype.executeOperations = function(callback) {
var self = this,
report = {overwrites: []},
nextOperation = 0,
performNextOperation = function() {
// Check for having been cancelled
if(self.isCancelled) {
if(self.publisher.publishCancel) {
self.publisher.publishCancel();
}
return callback("CANCELLED");
}
// Update progress
if(self.progressModal) {
self.progressModal.setProgress(nextOperation,self.operations.length);
}
// Check for having finished
if(nextOperation >= self.operations.length) {
$tw.utils.nextTick(function() {
self.publisher.publishEnd(callback);
});
} else {
// Execute this operation
var fileDetails = self.operations[nextOperation]();
nextOperation += 1;
self.publisher.publishFile(fileDetails,function() {
$tw.utils.nextTick(performNextOperation);
});
}
};
// Tell the publisher to start, and get back an array of the existing paths
self.publisher.publishStart(function(existingPaths) {
var paths = {};
$tw.utils.each(self.operations,function(operation) {
if(operation.path in paths) {
report.overwrites.push(operation.path);
}
paths[operation.path] = true;
});
// Run the operations
performNextOperation();
});
};
PublishingJob.prototype.saveReport = function(report) {
// Create the report tiddler
var reportTitle = this.wiki.generateNewTitle("$:/temp/publish-report");
$tw.wiki.addTiddler({
title: reportTitle,
text: "* " + report.overwrites.join("\n* ")
});
// Add the report tiddler title to the list field of the publisher parameters tiddler
var paramsTiddler = $tw.wiki.getTiddler(this.publisherParamsTitle),
list = (paramsTiddler.fields.list || []).slice(0);
list.unshift(reportTitle);
$tw.wiki.addTiddler(new $tw.Tiddler(paramsTiddler,{list: list}));
};
exports.PublisherHandler = PublisherHandler;
})();

View File

@@ -0,0 +1,52 @@
/*\
title: $:/core/modules/publishers/filesystem.js
type: application/javascript
module-type: publisher
Handles publishing to the Node.js filesystem
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "filesystem";
exports.create = function(params,publisherHandler,publishingJob) {
return new FileSystemPublisher(params,publisherHandler,publishingJob);
};
function FileSystemPublisher(params,publisherHandler,publishingJob) {
this.params = params;
this.publisherHandler = publisherHandler;
this.publishingJob = publishingJob;
};
FileSystemPublisher.prototype.publishStart = function(callback) {
console.log("publishStart");
// Returns a list of the previously published files
callback([]);
};
FileSystemPublisher.prototype.publishFile = function(item,callback) {
var fs = require("fs"),
path = require("path"),
filepath = path.resolve(this.publishingJob.commander.outputPath,item.path);
$tw.utils.createFileDirectories(filepath);
fs.writeFile(filepath,item.text,item.isBase64 ? "base64" : "utf8",function(err) {
if(err) {
console.log("File writing error",err)
}
callback(err);
});
};
FileSystemPublisher.prototype.publishEnd = function(callback) {
console.log("publishEnd");
callback(null);
};
})();

View File

@@ -157,7 +157,8 @@ SaverHandler.prototype.saveWiki = function(options) {
return false;
}
var variables = options.variables || {},
template = options.template || "$:/core/save/all",
template = (options.template ||
this.wiki.getTiddlerText("$:/config/SaveWikiButton/Template","$:/core/save/all")).trim(),
downloadType = options.downloadType || "text/plain",
text = this.wiki.renderTiddler(downloadType,template,options),
callback = function(err) {

View File

@@ -14,7 +14,7 @@ GET /
exports.method = "GET";
exports.path = /^\/$/;
exports.path = /^\/index.html$/;
exports.handler = function(request,response,state) {
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),

View File

@@ -63,10 +63,7 @@ function Server(options) {
self.addAuthenticator(authenticatorDefinition.AuthenticatorClass);
});
// Load route handlers
$tw.modules.forEachModuleOfType("route", function(title,routeDefinition) {
// console.log("Loading server route " + title);
self.addRoute(routeDefinition);
});
this.addRouteHandlers();
// Initialise the http vs https
this.listenOptions = null;
this.protocol = "http";
@@ -182,11 +179,43 @@ Server.prototype.addAuthenticator = function(AuthenticatorClass) {
}
};
Server.prototype.findMatchingRoute = function(request,state) {
Server.prototype.addRouteHandlers = function() {
var self = this;
// Load route handlers from sitemap if present, or just load all route modules
if(this.variables.sitemap) {
this.sitemap = new $tw.Sitemap(this.variables.sitemap,{
wiki: this.wiki,
variables: {}
});
this.sitemap.load();
$tw.utils.each(this.sitemap.getServerRoutes(),function(routeInfo) {
self.addRoute({
method: "GET",
path: routeInfo.regexp,
handler: function(request,response,state) {
var fileDetails = routeInfo.handler(state.params);
if(fileDetails) {
response.writeHead(200, {"Content-Type": fileDetails.type});
response.end(fileDetails.text,fileDetails.isBase64 ? "base64" : "utf8");
} else {
response.writeHead(404);
response.end();
}
}
});
});
} else {
$tw.modules.forEachModuleOfType("route",function(title,routeDefinition) {
self.addRoute(routeDefinition);
});
}
};
Server.prototype.findMatchingRoute = function(request,state,options) {
options = options || {};
for(var t=0; t<this.routes.length; t++) {
var potentialRoute = this.routes[t],
pathRegExp = potentialRoute.path,
pathname = state.urlInfo.pathname,
pathname = options.pathname || state.urlInfo.pathname,
match;
if(state.pathPrefix) {
if(pathname.substr(0,state.pathPrefix.length) === state.pathPrefix) {
@@ -263,6 +292,15 @@ Server.prototype.requestHandler = function(request,response,options) {
}
// Find the route that matches this path
var route = self.findMatchingRoute(request,state);
if(!route) {
// Try with the default document
var defaultDocumentPathname = state.urlInfo.pathname;
if(defaultDocumentPathname.substr(-1) !== "/") {
defaultDocumentPathname = defaultDocumentPathname + "/";
}
defaultDocumentPathname = defaultDocumentPathname + "index.html";
route = self.findMatchingRoute(request,state,{pathname: defaultDocumentPathname});
}
// Optionally output debug info
if(self.get("debug-level") !== "none") {
console.log("Request path:",JSON.stringify(state.urlInfo));

236
core/modules/sitemap.js Normal file
View File

@@ -0,0 +1,236 @@
/*\
title: $:/core/modules/sitemap.js
type: application/javascript
module-type: global
Sitemaps are used for static publishing and web serving
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function Sitemap(sitemapTitle,options) {
options = options || {};
this.sitemapTitle = sitemapTitle;
this.wiki = options.wiki;
this.routes = [];
this.variables = $tw.utils.extend({},options.variables);
}
Sitemap.prototype.load = function(sitemapTitle) {
var self = this;
// Get the sitemap
var sitemapTiddler = this.wiki.getTiddler(this.sitemapTitle);
if(sitemapTiddler) {
// Collect each route
$tw.utils.each(sitemapTiddler.fields.list,function(routeTitle) {
var routeTiddler = self.wiki.getTiddler(routeTitle);
if(routeTiddler) {
// Convert the path into a regexp and an array of {field:,function:} for each capture group
var regexpurgatedParameterisedPath = self.regexpurgateParameterisedPath(routeTiddler.fields["route-path"]);
self.routes.push({
title: routeTitle,
params: routeTiddler.getFieldStrings({prefix: "route-"}),
variables: routeTiddler.getFieldStrings({prefix: "var-"}),
regexp: regexpurgatedParameterisedPath.regexp,
captureGroups: regexpurgatedParameterisedPath.captureGroups
});
}
});
}
};
Sitemap.prototype.renderRoute = function(title,route) {
var tiddler = this.wiki.getTiddler(title);
switch(route.params.type) {
case "raw":
return {
path: this.resolveParameterisedPath(route.params.path,title),
text: tiddler.fields.text || "",
type: tiddler.fields.type || "",
isBase64: ($tw.config.contentTypeInfo[tiddler.fields.type] || {}).encoding === "base64"
};
break;
case "render":
var parser = {
tree: [
{
"type": "importvariables",
"attributes": {
"tiddler": {
"name": "tiddler",
"type": "string",
"value": this.sitemapTitle,
}
},
"tag": "$importvariables",
"isBlock": false,
"children": [
{
"type": "importvariables",
"attributes": {
"tiddler": {
"name": "tiddler",
"type": "string",
"value": route.title,
}
},
"tag": "$importvariables",
"isBlock": false,
"children": this.wiki.parseTiddler(route.params.template,{parseAsInline: true}).tree
}
]
}
]
},
widgetNode = this.wiki.makeWidget(parser,{
variables: $tw.utils.extend({},this.variables,{currentTiddler: title})
}),
container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null);
return {
path: this.resolveParameterisedPath(route.params.path,title),
text: container.textContent,
type: route.params["output-type"] || "text/html"
};
break;
}
};
/*
Returns an array of functions that return {path:,text:,type:,isBase64:} for each path
*/
Sitemap.prototype.getAllFileDetails = function(exportTiddlers) {
var self = this,
output = [];
$tw.utils.each(this.routes,function(route) {
var routeFilter = route.params["tiddler-filter"] || "DUMMY_RESULT", // If no filter is provided, use a dummy filter that returns a single result
routeTiddlers = self.wiki.filterTiddlers(routeFilter,null,self.wiki.makeTiddlerIterator(exportTiddlers));
$tw.utils.each(routeTiddlers,function(title) {
output.push(self.renderRoute.bind(self,title,route));
});
});
return output;
};
/*
Returns an array of server routes {regexp:, handler:}
*/
Sitemap.prototype.getServerRoutes = function() {
var self = this,
output = [];
$tw.utils.each(this.routes,function(route) {
output.push({
regexp: route.regexp,
handler: function(params) {
// Locate the tiddler identified by the capture groups, if any
var title = null,
nextParam = 0;
$tw.utils.each(route.captureGroups,function(captureGroup) {
var param = params[nextParam++];
if(captureGroup.field === "title") {
switch(captureGroup.function) {
case "slugify":
var titles = self.wiki.unslugify(param);
if(titles && titles.length > 0) {
title = titles[0];
}
break;
}
}
})
// Check that the tiddler passes the route filter
if(route.params["tiddler-filter"]) {
if(!title) {
return null;
}
var routeTiddlers = self.wiki.filterTiddlers(route.params["tiddler-filter"],null,self.wiki.makeTiddlerIterator([title]));
if(routeTiddlers.indexOf(title) === -1) {
return null;
}
}
// Return the rendering or raw tiddler
return self.renderRoute(title,route);
}
});
});
return output;
};
/*
Apply a tiddler to a parameterised path to create a usable path
*/
Sitemap.prototype.resolveParameterisedPath = function(parameterisedPath,title) {
var self = this;
// Split the path on $*_*$ markers
var tiddler = this.wiki.getTiddler(title),
output = [];
$tw.utils.each(parameterisedPath.split(/(\$[a-z_]+\$)/),function(part) {
var match = part.match(/\$([a-z]+)_([a-z]+)\$/);
if(match) {
var value;
// Get the base value
switch(match[1]) {
case "uri":
case "title":
value = title;
break;
case "type":
value = tiddler.fields.type || "text/vnd.tiddlywiki";
break;
}
// Apply the encoding function
switch(match[2]) {
case "encoded":
value = encodeURIComponent(value);
break;
case "doubleencoded":
value = encodeURIComponent(encodeURIComponent(value));
break;
case "slugify":
value = self.wiki.slugify(value);
break;
case "extension":
value = ($tw.config.contentTypeInfo[value] || {extension: "."}).extension.slice(1);
break;
}
output.push(value);
} else {
output.push(part);
}
});
return output.join("");
};
/*
// Convert the path into a regexp and an array of {field:,function:} for each capture group
*/
Sitemap.prototype.regexpurgateParameterisedPath = function(parameterisedPath) {
var regexpParts = ["\\/"],
captureGroups = [];
$tw.utils.each(parameterisedPath.split(/(\$[a-z_]+\$)/),function(part) {
var match = part.match(/\$([a-z]+)_([a-z]+)\$/);
if(match) {
regexpParts.push("(.+)");
captureGroups.push({
field: match[1],
function: match[2]
});
} else {
regexpParts.push($tw.utils.escapeRegExp(part));
}
});
return {
regexp: new RegExp("^" + regexpParts.join("") + "$"),
captureGroups: captureGroups
};
};
exports.Sitemap = Sitemap;
})();

View File

@@ -40,9 +40,10 @@ exports.startup = function() {
// Install the tm-focus-selector message
$tw.rootWidget.addEventListener("tm-focus-selector",function(event) {
var selector = event.param || "",
element;
element,
doc = event.event && event.event.target ? event.event.target.ownerDocument : document;
try {
element = document.querySelector(selector);
element = doc.querySelector(selector);
} catch(e) {
console.log("Error in selector: ",selector)
}
@@ -72,6 +73,12 @@ exports.startup = function() {
}
});
}
// Hook up events for the publisher handler
$tw.rootWidget.addEventListener("tm-publish",function(event) {
$tw.publisherHandler.publish(event.paramObject.job,function(err) {
console.log("Finished publishing with result:",err);
});
});
// If we're being viewed on a data: URI then give instructions for how to save
if(document.location.protocol === "data:") {
$tw.rootWidget.dispatchEvent({

View File

@@ -129,6 +129,10 @@ exports.startup = function() {
dirtyTracking: !$tw.syncadaptor,
preloadDirty: $tw.boot.preloadDirty || []
});
// Install the publisher handler
$tw.publisherHandler = new $tw.PublisherHandler({
wiki: $tw.wiki
});
// Host-specific startup
if($tw.browser) {
// Install the popup manager

View File

@@ -27,7 +27,7 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) {
var listItemWidget = this.listWidget.children[listElementIndex],
targetElement = listItemWidget.findFirstDomNode();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
if(duration) {
@@ -43,7 +43,7 @@ ClassicStoryView.prototype.insert = function(widget) {
if(duration) {
var targetElement = widget.findFirstDomNode();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
// Get the current height of the tiddler
@@ -83,7 +83,7 @@ ClassicStoryView.prototype.remove = function(widget) {
widget.removeChildDomNodes();
};
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
removeElement();
return;
}
@@ -118,4 +118,4 @@ ClassicStoryView.prototype.remove = function(widget) {
exports.classic = ClassicStoryView;
})();
})();

View File

@@ -24,7 +24,7 @@ PopStoryView.prototype.navigateTo = function(historyInfo) {
var listItemWidget = this.listWidget.children[listElementIndex],
targetElement = listItemWidget.findFirstDomNode();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
// Scroll the node into view
@@ -35,7 +35,7 @@ PopStoryView.prototype.insert = function(widget) {
var targetElement = widget.findFirstDomNode(),
duration = $tw.utils.getAnimationDuration();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
// Reset once the transition is over
@@ -77,7 +77,7 @@ PopStoryView.prototype.remove = function(widget) {
}
};
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
removeElement();
return;
}

View File

@@ -48,7 +48,7 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
var listItemWidget = this.listWidget.children[listElementIndex],
targetElement = listItemWidget.findFirstDomNode();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
// Make the new tiddler be position absolute and visible so that we can measure it
@@ -130,7 +130,7 @@ function findTitleDomNode(widget,targetClass) {
ZoominListView.prototype.insert = function(widget) {
var targetElement = widget.findFirstDomNode();
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
return;
}
// Make the newly inserted node position absolute and hidden
@@ -147,7 +147,7 @@ ZoominListView.prototype.remove = function(widget) {
widget.removeChildDomNodes();
};
// Abandon if the list entry isn't a DOM element (it might be a text node)
if(!(targetElement instanceof Element)) {
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
removeElement();
return;
}

View File

@@ -601,10 +601,7 @@ SaveTiddlerTask.prototype.run = function(callback) {
tiddler = this.syncer.wiki.tiddlerExists(this.title) && this.syncer.wiki.getTiddler(this.title);
this.syncer.logger.log("Dispatching 'save' task:",this.title);
if(tiddler) {
this.syncer.syncadaptor.saveTiddler(tiddler,{
changeCount: changeCount,
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
},function(err,adaptorInfo,revision) {
this.syncer.syncadaptor.saveTiddler(tiddler,function(err,adaptorInfo,revision) {
// If there's an error, exit without changing any internal state
if(err) {
return callback(err);
@@ -618,6 +615,8 @@ SaveTiddlerTask.prototype.run = function(callback) {
};
// Invoke the callback
callback(null);
},{
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
});
} else {
this.syncer.logger.log(" Not Dispatching 'save' task:",this.title,"tiddler does not exist");
@@ -634,9 +633,7 @@ function DeleteTiddlerTask(syncer,title) {
DeleteTiddlerTask.prototype.run = function(callback) {
var self = this;
this.syncer.logger.log("Dispatching 'delete' task:",this.title);
this.syncer.syncadaptor.deleteTiddler(this.title,{
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
},function(err,adaptorInfo) {
this.syncer.syncadaptor.deleteTiddler(this.title,function(err) {
// If there's an error, exit without changing any internal state
if(err) {
return callback(err);
@@ -645,6 +642,8 @@ DeleteTiddlerTask.prototype.run = function(callback) {
delete self.syncer.tiddlerInfo[self.title];
// Invoke the callback
callback(null);
},{
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
});
};

View File

@@ -54,15 +54,27 @@ exports.getFieldList = function(field) {
/*
Get all the fields as a hashmap of strings. Options:
exclude: an array of field names to exclude
prefix: an optional field name prefix. Only fields with the prefix are included, and the prefix is stripped from the name
*/
exports.getFieldStrings = function(options) {
options = options || {};
var exclude = options.exclude || [];
var fields = {};
for(var field in this.fields) {
if($tw.utils.hop(this.fields,field)) {
if(exclude.indexOf(field) === -1) {
fields[field] = this.getFieldString(field);
var exclude = options.exclude || [],
fields = {},
field;
if(options.prefix) {
for(field in this.fields) {
if($tw.utils.hop(this.fields,field)) {
if(exclude.indexOf(field) === -1 && field.substring(0,options.prefix.length) === options.prefix) {
fields[field.substring(options.prefix.length)] = this.getFieldString(field);
}
}
}
} else {
for(field in this.fields) {
if($tw.utils.hop(this.fields,field)) {
if(exclude.indexOf(field) === -1) {
fields[field] = this.getFieldString(field);
}
}
}
}

View File

@@ -12,8 +12,9 @@ Modal message mechanism
/*global $tw: false */
"use strict";
var widget = require("$:/core/modules/widgets/widget.js");
var navigator = require("$:/core/modules/widgets/navigator.js");
var widget = require("$:/core/modules/widgets/widget.js"),
navigator = require("$:/core/modules/widgets/navigator.js"),
dm = $tw.utils.domMaker;
var Modal = function(wiki) {
this.wiki = wiki;
@@ -26,6 +27,10 @@ Display a modal dialogue
options: see below
Options include:
downloadLink: Text of a big download link to include
variables: variables to be passed to the modal
event: optional DOM event that initiated the modal
progress: set to true to add a progress bar
onclose: callback for when the modal is closed
*/
Modal.prototype.display = function(title,options) {
options = options || {};
@@ -47,7 +52,6 @@ Modal.prototype.display = function(title,options) {
"tv-story-list": (options.event && options.event.widget ? options.event.widget.getVariable("tv-story-list") : ""),
"tv-history-list": (options.event && options.event.widget ? options.event.widget.getVariable("tv-history-list") : "")
},options.variables);
// Create the wrapper divs
var wrapper = this.srcDocument.createElement("div"),
modalBackdrop = this.srcDocument.createElement("div"),
@@ -55,6 +59,7 @@ Modal.prototype.display = function(title,options) {
modalHeader = this.srcDocument.createElement("div"),
headerTitle = this.srcDocument.createElement("h3"),
modalBody = this.srcDocument.createElement("div"),
modalProgress = this.srcDocument.createElement("div"),
modalLink = this.srcDocument.createElement("a"),
modalFooter = this.srcDocument.createElement("div"),
modalFooterHelp = this.srcDocument.createElement("span"),
@@ -71,6 +76,7 @@ Modal.prototype.display = function(title,options) {
$tw.utils.addClass(modalWrapper,"tc-modal");
$tw.utils.addClass(modalHeader,"tc-modal-header");
$tw.utils.addClass(modalBody,"tc-modal-body");
$tw.utils.addClass(modalProgress,"tc-modal-progress");
$tw.utils.addClass(modalFooter,"tc-modal-footer");
// Join them together
wrapper.appendChild(modalBackdrop);
@@ -78,6 +84,15 @@ Modal.prototype.display = function(title,options) {
modalHeader.appendChild(headerTitle);
modalWrapper.appendChild(modalHeader);
modalWrapper.appendChild(modalBody);
if(options.progress) {
var modalProgressBar = this.srcDocument.createElement("div");
modalProgressBar.className = "tc-modal-progress-bar";
modalProgress.appendChild(modalProgressBar);
var modalProgressPercent = this.srcDocument.createElement("div");
modalProgressPercent.className = "tc-modal-progress-percent";
modalProgress.appendChild(modalProgressPercent);
modalWrapper.appendChild(modalProgress);
}
modalFooter.appendChild(modalFooterHelp);
modalFooter.appendChild(modalFooterButtons);
modalWrapper.appendChild(modalFooter);
@@ -105,7 +120,6 @@ Modal.prototype.display = function(title,options) {
parentWidget: $tw.rootWidget
});
navigatorWidgetNode.render(modalBody,null);
// Render the title of the message
var headerWidgetNode = this.wiki.makeTranscludeWidget(title,{
field: "subtitle",
@@ -182,6 +196,10 @@ Modal.prototype.display = function(title,options) {
this.wiki.addEventListener("change",refreshHandler);
// Add the close event handler
var closeHandler = function(event) {
// Call the onclose handler
if(options.onclose) {
options.onclose(event);
}
// Remove our refresh handler
self.wiki.removeEventListener("change",refreshHandler);
// Decrease the modal count and adjust the body class
@@ -236,6 +254,22 @@ Modal.prototype.display = function(title,options) {
$tw.utils.setStyle(modalWrapper,[
{transform: "translateY(0px)"}
]);
// Return the wrapper node
return {
domNode: wrapper,
closeHandler: closeHandler,
setProgress: function(numerator,denominator) {
// Remove old progress
while(modalProgressPercent.hasChildNodes()) {
modalProgressPercent.removeChild(modalProgressPercent.firstChild);
}
// Set new text
var percent = (numerator * 100 /denominator).toFixed(2) + "%";
modalProgressPercent.appendChild(self.srcDocument.createTextNode(percent));
// Set bar width
modalProgressBar.style.width = percent;
}
};
};
Modal.prototype.adjustPageClass = function() {
@@ -243,6 +277,7 @@ Modal.prototype.adjustPageClass = function() {
if(windowContainer) {
$tw.utils.toggleClass(windowContainer,"tc-modal-displayed",this.modalCount > 0);
}
$tw.utils.toggleClass(this.srcDocument.body,"tc-modal-prevent-scroll",this.modalCount > 0);
};
exports.Modal = Modal;

View File

@@ -228,6 +228,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
hasUnsafeFields = hasUnsafeFields || /[\x00-\x1F]/mg.test(value);
hasUnsafeFields = hasUnsafeFields || ($tw.utils.trim(value) !== value);
}
hasUnsafeFields = hasUnsafeFields || /:/mg.test(fieldName);
});
// Check for field values
if(hasUnsafeFields) {

View File

@@ -223,6 +223,7 @@ exports.removeArrayEntries = function(array,value) {
array.splice(p,1);
}
}
return array;
};
/*
@@ -740,9 +741,8 @@ exports.isValidFieldName = function(name) {
if(!name || typeof name !== "string") {
return false;
}
name = name.toLowerCase().trim();
var fieldValidatorRegEx = /^[a-z0-9\-\._]+$/mg;
return fieldValidatorRegEx.test(name);
// Since v5.2.x, there are no restrictions on characters in field names
return name;
};
/*

View File

@@ -58,9 +58,10 @@ Invoke the action associated with this widget
*/
ConfirmWidget.prototype.invokeAction = function(triggeringWidget,event) {
var invokeActions = true,
handled = true;
handled = true,
win = event.event && event.event.view ? event.event.view : window;
if(this.prompt) {
invokeActions = confirm(this.message);
invokeActions = win.confirm(this.message);
}
if(invokeActions) {
handled = this.invokeActions(triggeringWidget,event);
@@ -74,4 +75,4 @@ ConfirmWidget.prototype.allowActionPropagation = function() {
exports["action-confirm"] = ConfirmWidget;
})();
})();

View File

@@ -27,8 +27,11 @@ CreateTiddlerWidget.prototype = new Widget();
Render this widget into the DOM
*/
CreateTiddlerWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
// Render children
this.renderChildren(parent,nextSibling);
};
/*
@@ -44,7 +47,8 @@ CreateTiddlerWidget.prototype.execute = function() {
this.actionTemplate = this.getAttribute("$template");
this.useTemplate = !!this.actionTemplate;
this.actionOverwrite = this.getAttribute("$overwrite","no");
// Construct the child widgets
this.makeChildWidgets();
};
/*
@@ -86,18 +90,21 @@ CreateTiddlerWidget.prototype.invokeAction = function(triggeringWidget,event) {
if (!this.hasBase && this.useTemplate) {
title = this.wiki.generateNewTitle(this.actionTemplate);
} else if (!this.hasBase && !this.useTemplate) {
// If NO $basetitle AND NO $template use initial title
// DON'T overwrite any stuff
// If no $basetitle and no $template then use initial title
title = this.wiki.generateNewTitle(title);
}
var templateTiddler = this.wiki.getTiddler(this.actionTemplate) || {};
var tiddler = this.wiki.addTiddler(new $tw.Tiddler(templateTiddler.fields,creationFields,fields,modificationFields,{title: title}));
this.wiki.addTiddler(new $tw.Tiddler(templateTiddler.fields,creationFields,fields,modificationFields,{title: title}));
var draftTitle = this.wiki.generateDraftTitle(title);
if(this.actionSaveTitle) {
this.wiki.setTextReference(this.actionSaveTitle,title,this.getVariable("currentTiddler"));
}
if(this.actionSaveDraftTitle) {
this.wiki.setTextReference(this.actionSaveDraftTitle,this.wiki.generateDraftTitle(title),this.getVariable("currentTiddler"));
this.wiki.setTextReference(this.actionSaveDraftTitle,draftTitle,this.getVariable("currentTiddler"));
}
this.setVariable("createTiddler-title",title);
this.setVariable("createTiddler-draftTitle",draftTitle);
this.refreshChildren();
return true; // Action was invoked
};

View File

@@ -70,7 +70,18 @@ NavigateWidget.prototype.invokeAction = function(triggeringWidget,event) {
navigateFromNode: triggeringWidget,
navigateFromClientRect: bounds && { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
},
navigateSuppressNavigation: suppressNavigation
navigateFromClientTop: bounds && bounds.top,
navigateFromClientLeft: bounds && bounds.left,
navigateFromClientWidth: bounds && bounds.width,
navigateFromClientRight: bounds && bounds.right,
navigateFromClientBottom: bounds && bounds.bottom,
navigateFromClientHeight: bounds && bounds.height,
navigateSuppressNavigation: suppressNavigation,
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,
altKey: event.altKey,
shiftKey: event.shiftKey,
event: event
});
return true; // Action was invoked
};

View File

@@ -46,7 +46,7 @@ EventWidget.prototype.render = function(parent,nextSibling) {
$tw.utils.each(this.types,function(type) {
domNode.addEventListener(type,function(event) {
var selector = self.getAttribute("selector"),
actions = self.getAttribute("actions-"+type),
actions = self.getAttribute("$"+type) || self.getAttribute("actions-"+type),
stopPropagation = self.getAttribute("stopPropagation","onaction"),
selectedNode = event.target,
selectedNodeRect,
@@ -135,7 +135,15 @@ Compute the internal state of the widget
EventWidget.prototype.execute = function() {
var self = this;
// Get attributes that require a refresh on change
this.types = this.getAttribute("events","").split(" ");
this.types = [];
$tw.utils.each(this.attributes,function(value,key) {
if(key.charAt(0) === "$") {
self.types.push(key.slice(1));
}
});
if(!this.types.length) {
this.types = this.getAttribute("events","").split(" ");
}
this.elementTag = this.getAttribute("tag");
// Make child widgets
this.makeChildWidgets();
@@ -151,12 +159,13 @@ EventWidget.prototype.assignDomNodeClasses = function() {
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
EventWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes["events"] || changedAttributes["tag"]) {
var changedAttributes = this.computeAttributes(),
changedAttributesCount = $tw.utils.count(changedAttributes);
if(changedAttributesCount === 1 && changedAttributes["class"]) {
this.assignDomNodeClasses();
} else if(changedAttributesCount > 0) {
this.refreshSelf();
return true;
} else if(changedAttributes["class"]) {
this.assignDomNodeClasses();
}
return this.refreshChildren(changedTiddlers);
};

View File

@@ -73,26 +73,12 @@ FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
var tiddler = this.wiki.getTiddler(this.mangleTitle),
addition = this.wiki.getModificationFields(),
hadInvalidFieldName = false,
addField = function(name,value) {
var trimmedName = name.toLowerCase().trim();
if(!$tw.utils.isValidFieldName(trimmedName)) {
if(!hadInvalidFieldName) {
alert($tw.language.getString(
"InvalidFieldName",
{variables:
{fieldName: trimmedName}
}
));
hadInvalidFieldName = true;
return;
}
} else {
if(!value && tiddler) {
value = tiddler.fields[trimmedName];
}
addition[trimmedName] = value || "";
var trimmedName = name.trim();
if(!value && tiddler) {
value = tiddler.fields[trimmedName];
}
addition[trimmedName] = value || "";
return;
};
addition.title = this.mangleTitle;

View File

@@ -40,10 +40,8 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
var widgetPointer = this;
// Got to flush all the accumulated variables
this.variables = new this.variablesConstructor();
// Get our parameters
this.filter = this.getAttribute("filter");
// Compute the filter
this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this);
this.tiddlerList = tiddlerList || this.getTiddlerList();
// Accumulate the <$set> widgets from each tiddler
$tw.utils.each(this.tiddlerList,function(title) {
var parser = widgetPointer.wiki.parseTiddler(title);
@@ -86,7 +84,6 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
}
}
});
if (widgetPointer != this) {
widgetPointer.parseTreeNode.children = this.parseTreeNode.children;
} else {
@@ -94,13 +91,21 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
}
};
ImportVariablesWidget.prototype.getTiddlerList = function() {
var filter = this.getAttribute("filter"),
title = this.getAttribute("tiddler");
return (filter && this.wiki.filterTiddlers(filter,this)) || (title && [title]) || [];
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
ImportVariablesWidget.prototype.refresh = function(changedTiddlers) {
// Recompute our attributes and the filter list
var changedAttributes = this.computeAttributes(),
tiddlerList = this.wiki.filterTiddlers(this.getAttribute("filter"),this);
filter = this.getAttribute("filter"),
title = this.getAttribute("tiddler"),
tiddlerList = this.getTiddlerList();
// Refresh if the filter has changed, or the list of tiddlers has changed, or any of the tiddlers in the list has changed
function haveListedTiddlersChanged() {
var changed = false;
@@ -111,7 +116,7 @@ ImportVariablesWidget.prototype.refresh = function(changedTiddlers) {
});
return changed;
}
if(changedAttributes.filter || !$tw.utils.isArrayEqual(this.tiddlerList,tiddlerList) || haveListedTiddlersChanged()) {
if(changedAttributes.filter || changedAttributes.tiddler || !$tw.utils.isArrayEqual(this.tiddlerList,tiddlerList) || haveListedTiddlersChanged()) {
// Compute the filter
this.removeChildDomNodes();
this.execute(tiddlerList);

View File

@@ -0,0 +1,84 @@
/*\
title: $:/core/modules/widgets/jsontiddler.js
type: application/javascript
module-type: widget
Render a tiddler as JSON text
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var JSONTiddlerWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
JSONTiddlerWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
JSONTiddlerWidget.prototype.render = function(parent,nextSibling) {
var self = this;
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
// Collect the fields from the optional base tiddler
var fields = {};
if(this.attTiddler) {
var tiddler = this.wiki.getTiddler(this.attTiddler);
if(tiddler) {
fields = tiddler.getFieldStrings({exclude: this.attExclude.split(" ")});
}
}
// Add custom fields specified in attributes starting with $
$tw.utils.each(this.attributes,function(attribute,name) {
if(name.charAt(0) === "$") {
fields[name.slice(1)] = attribute;
}
});
// JSONify
var json = JSON.stringify(fields);
// Escape unsafe script characters
if(this.attEscapeUnsafeScriptChars) {
json = json.replace(/</g,"\\u003C");
}
// Update the DOM
var textNode = this.document.createTextNode(json);
parent.insertBefore(textNode,nextSibling);
this.domNodes.push(textNode);
};
/*
Compute the internal state of the widget
*/
JSONTiddlerWidget.prototype.execute = function() {
this.attTiddler = this.getAttribute("tiddler");
this.attExclude = this.getAttribute("exclude","");
this.attEscapeUnsafeScriptChars = this.getAttribute("escapeUnsafeScriptChars","no") === "yes";
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
JSONTiddlerWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if($tw.utils.count(changedAttributes) > 0 || (this.attTiddler && changedTiddlers[this.attTiddler])) {
this.refreshSelf();
return true;
} else {
return false;
}
};
exports.jsontiddler = JSONTiddlerWidget;
})();

View File

@@ -40,9 +40,8 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
// Create element
var domNode = this.document.createElement(tag);
// Assign classes
var classes = (this["class"] || "").split(" ");
classes.push("tc-keyboard");
domNode.className = classes.join(" ");
this.domNode = domNode;
this.assignDomNodeClasses();
// Add a keyboard event handler
$tw.utils.addEventListeners(domNode,[
{name: "keydown", handlerObject: this, handlerMethod: "handleChangeEvent"}
@@ -54,7 +53,8 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
};
KeyboardWidget.prototype.handleChangeEvent = function(event) {
if($tw.keyboardManager.checkKeyDescriptors(event,this.keyInfoArray)) {
var keyInfo = $tw.keyboardManager.getMatchingKeyDescriptor(event,this.keyInfoArray);
if(keyInfo) {
var handled = this.invokeActions(this,event);
if(this.actions) {
var variables = {
@@ -62,6 +62,9 @@ KeyboardWidget.prototype.handleChangeEvent = function(event) {
"event-code": event.code,
"modifier": $tw.keyboardManager.getEventModifierKeyDescriptor(event)
};
if(keyInfo.keyDescriptor) {
variables["event-key-descriptor"] = keyInfo.keyDescriptor;
}
this.invokeActionString(this.actions,this,event,variables);
}
this.dispatchMessage(event);
@@ -90,7 +93,6 @@ KeyboardWidget.prototype.execute = function() {
this.key = this.getAttribute("key","");
this.tag = this.getAttribute("tag","");
this.keyInfoArray = $tw.keyboardManager.parseKeyDescriptors(this.key);
this["class"] = this.getAttribute("class","");
if(this.key.substr(0,2) === "((" && this.key.substr(-2,2) === "))") {
this.shortcutTiddlers = [];
var name = this.key.substring(2,this.key.length -2);
@@ -102,14 +104,22 @@ KeyboardWidget.prototype.execute = function() {
this.makeChildWidgets();
};
KeyboardWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-keyboard");
this.domNode.className = classes.join(" ");
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
KeyboardWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.message || changedAttributes.param || changedAttributes.key || changedAttributes["class"] || changedAttributes.tag) {
if(changedAttributes.message || changedAttributes.param || changedAttributes.key || changedAttributes.tag) {
this.refreshSelf();
return true;
} else if(changedAttributes["class"]) {
this.assignDomNodeClasses();
}
// Update the keyInfoArray if one of its shortcut-config-tiddlers has changed
if(this.shortcutTiddlers && $tw.utils.hopArray(changedTiddlers,this.shortcutTiddlers)) {

View File

@@ -98,6 +98,7 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
var wikiLinkTemplateMacro = this.getVariable("tv-wikilink-template"),
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$";
wikiLinkText = $tw.utils.replaceString(wikiLinkTemplate,"$uri_encoded$",encodeURIComponent(this.to));
wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$title_slugify$",this.wiki.slugify(this.to));
wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
}
// Override with the value of tv-get-export-link if defined
@@ -154,6 +155,12 @@ LinkWidget.prototype.handleClickEvent = function(event) {
navigateFromNode: this,
navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
},
navigateFromClientTop: bounds.top,
navigateFromClientLeft: bounds.left,
navigateFromClientWidth: bounds.width,
navigateFromClientRight: bounds.right,
navigateFromClientBottom: bounds.bottom,
navigateFromClientHeight: bounds.height,
navigateSuppressNavigation: event.metaKey || event.ctrlKey || (event.button === 1),
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,

View File

@@ -33,12 +33,44 @@ MessageCatcherWidget.prototype.render = function(parent,nextSibling) {
// Compute attributes and execute state
this.computeAttributes();
this.execute();
// Add our message handler
if(this.messageType) {
this.addEventListeners([
{type: this.messageType, handler: "handleEvent"}
]);
// Helper to add an event handler
var addEventHandler = function(type,actions) {
if(type && actions) {
self.addEventListener(
type,
function(event) {
// Collect all the event properties into variables
var collectProps = function(obj,prefix) {
prefix = prefix || "";
var props = {};
$tw.utils.each(obj,function(value,name) {
if(["string","boolean","number"].indexOf(typeof value) !== -1) {
props[prefix + name] = value.toString();
}
});
return props;
};
var variables = $tw.utils.extend(
{},
collectProps(event.paramObject,"event-paramObject-"),
collectProps(event,"event-"),
{
modifier: $tw.keyboardManager.getEventModifierKeyDescriptor(event)
});
self.invokeActionString(actions,self,event,variables);
return false;
}
);
}
}
// Add the main event handler
addEventHandler(this.getAttribute("type"),this.getAttribute("actions"));
// Add any other event handlers
$tw.utils.each(this.attributes,function(value,key) {
if(key.charAt(0) === "$") {
addEventHandler(key.slice(1),value);
}
});
// Render children
this.renderChildren(parent,null);
};
@@ -47,48 +79,16 @@ MessageCatcherWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget
*/
MessageCatcherWidget.prototype.execute = function() {
var self = this;
// Get attributes that require a refresh on change
this.messageType = this.getAttribute("type");
this.messageActions = this.getAttribute("actions");
// Make child widgets
this.makeChildWidgets();
};
/*
Handle an event
*/
MessageCatcherWidget.prototype.handleEvent = function(event) {
if(this.messageActions) {
// Collect all the event properties into variables
var collectProps = function(obj,prefix) {
prefix = prefix || "";
var props = {};
$tw.utils.each(obj,function(value,name) {
if(["string","boolean","number"].indexOf(typeof value) !== -1) {
props[prefix + name] = value.toString();
}
});
return props;
};
var variables = $tw.utils.extend(
{},
collectProps(event.paramObject,"event-paramObject-"),
collectProps(event,"event-"),
{
modifier: $tw.keyboardManager.getEventModifierKeyDescriptor(event)
});
this.invokeActionString(this.messageActions,this,event,variables);
}
return false;
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
MessageCatcherWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes["type"]) {
if($tw.utils.count(changedAttributes) > 0) {
this.refreshSelf();
return true;
}

View File

@@ -160,6 +160,7 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) {
// Close a specified tiddler
NavigatorWidget.prototype.handleCloseTiddlerEvent = function(event) {
event = $tw.hooks.invokeHook("th-closing-tiddler",event);
var title = event.param || event.tiddlerTitle,
storyList = this.getStoryList();
// Look for tiddlers with this title to close
@@ -183,7 +184,8 @@ NavigatorWidget.prototype.handleCloseOtherTiddlersEvent = function(event) {
// Place a tiddler in edit mode
NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
var editTiddler = $tw.hooks.invokeHook("th-editing-tiddler",event);
var editTiddler = $tw.hooks.invokeHook("th-editing-tiddler",event),
win = event.event && event.event.view ? event.event.view : window;
if(!editTiddler) {
return false;
}
@@ -192,7 +194,7 @@ NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
return self.wiki.isShadowTiddler(title) && !self.wiki.tiddlerExists(title);
}
function confirmEditShadow(title) {
return confirm($tw.language.getString(
return win.confirm($tw.language.getString(
"ConfirmEditShadowTiddler",
{variables:
{title: title}
@@ -225,7 +227,8 @@ NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
storyList = this.getStoryList(),
originalTitle = tiddler ? tiddler.fields["draft.of"] : "",
originalTiddler = originalTitle ? this.wiki.getTiddler(originalTitle) : undefined,
confirmationTitle;
confirmationTitle,
win = event.event && event.event.view ? event.event.view : window;
if(!tiddler) {
return false;
}
@@ -238,7 +241,7 @@ NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
confirmationTitle = title;
}
// Seek confirmation
if((this.wiki.getTiddler(originalTitle) || (tiddler.fields.text || "") !== "") && !confirm($tw.language.getString(
if((this.wiki.getTiddler(originalTitle) || (tiddler.fields.text || "") !== "") && !win.confirm($tw.language.getString(
"ConfirmDeleteTiddler",
{variables:
{title: confirmationTitle}
@@ -304,7 +307,8 @@ NavigatorWidget.prototype.generateDraftTitle = function(title) {
NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
var title = event.param || event.tiddlerTitle,
tiddler = this.wiki.getTiddler(title),
storyList = this.getStoryList();
storyList = this.getStoryList(),
win = event.event && event.event.view ? event.event.view : window;
// Replace the original tiddler with the draft
if(tiddler) {
var draftTitle = (tiddler.fields["draft.title"] || "").trim(),
@@ -313,7 +317,7 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
var isRename = draftOf !== draftTitle,
isConfirmed = true;
if(isRename && this.wiki.tiddlerExists(draftTitle)) {
isConfirmed = confirm($tw.language.getString(
isConfirmed = win.confirm($tw.language.getString(
"ConfirmOverwriteTiddler",
{variables:
{title: draftTitle}
@@ -362,6 +366,7 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
// Take a tiddler out of edit mode without saving the changes
NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
event = $tw.hooks.invokeHook("th-cancelling-tiddler", event);
var win = event.event && event.event.view ? event.event.view : window;
// Flip the specified tiddler from draft back to the original
var draftTitle = event.param || event.tiddlerTitle,
draftTiddler = this.wiki.getTiddler(draftTitle),
@@ -372,7 +377,7 @@ NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
originalTiddler = this.wiki.getTiddler(originalTitle),
storyList = this.getStoryList();
if(this.wiki.isDraftModified(draftTitle)) {
isConfirmed = confirm($tw.language.getString(
isConfirmed = win.confirm($tw.language.getString(
"ConfirmCancelTiddler",
{variables:
{title: draftTitle}

View File

@@ -60,6 +60,8 @@ TranscludeWidget.prototype.execute = function() {
subTiddler: this.transcludeSubTiddler
}),
parseTreeNodes = parser ? parser.tree : this.parseTreeNode.children;
this.sourceText = parser ? parser.source : null;
this.parserType = parser? parser.type : null;
// Set context variables for recursion detection
var recursionMarker = this.makeRecursionMarker();
if(this.recursionMarker === "yes") {
@@ -98,12 +100,17 @@ TranscludeWidget.prototype.makeRecursionMarker = function() {
return output.join("");
};
TranscludeWidget.prototype.parserNeedsRefresh = function() {
var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler});
return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType)
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
TranscludeWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedTiddlers[this.transcludeTitle]) {
if(($tw.utils.count(changedAttributes) > 0) || (changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
this.refreshSelf();
return true;
} else {

View File

@@ -568,10 +568,15 @@ Widget.prototype.invokeActions = function(triggeringWidget,event) {
var handled = false;
// For each child widget
for(var t=0; t<this.children.length; t++) {
var child = this.children[t];
// Invoke the child if it is an action widget
if(child.invokeAction) {
var child = this.children[t],
childIsActionWidget = !!child.invokeAction,
actionRefreshPolicy = child.getVariable("tv-action-refresh-policy");
// Refresh the child if required
if(childIsActionWidget || actionRefreshPolicy === "always") {
child.refreshSelf();
}
// Invoke the child if it is an action widget
if(childIsActionWidget) {
if(child.invokeAction(triggeringWidget,event)) {
handled = true;
}

View File

@@ -937,41 +937,57 @@ exports.parseTiddler = function(title,options) {
};
exports.parseTextReference = function(title,field,index,options) {
var tiddler,text;
if(options.subTiddler) {
tiddler = this.getSubTiddler(title,options.subTiddler);
} else {
var tiddler,
text,
parserInfo;
if(!options.subTiddler) {
tiddler = this.getTiddler(title);
if(field === "text" || (!field && !index)) {
this.getTiddlerText(title); // Force the tiddler to be lazily loaded
return this.parseTiddler(title,options);
}
}
parserInfo = this.getTextReferenceParserInfo(title,field,index,options);
if(parserInfo.sourceText !== null) {
return this.parseText(parserInfo.parserType,parserInfo.sourceText,options);
} else {
return null;
}
};
exports.getTextReferenceParserInfo = function(title,field,index,options) {
var tiddler,
parserInfo = {
sourceText : null,
parserType : "text/vnd.tiddlywiki"
};
if(options.subTiddler) {
tiddler = this.getSubTiddler(title,options.subTiddler);
} else {
tiddler = this.getTiddler(title);
}
if(field === "text" || (!field && !index)) {
if(tiddler && tiddler.fields) {
return this.parseText(tiddler.fields.type,tiddler.fields.text,options);
} else {
return null;
parserInfo.sourceText = tiddler.fields.text || "";
if(tiddler.fields.type) {
parserInfo.parserType = tiddler.fields.type;
}
}
} else if(field) {
if(field === "title") {
text = title;
} else {
if(!tiddler || !tiddler.hasField(field)) {
return null;
}
text = tiddler.fields[field];
parserInfo.sourceText = title;
} else if(tiddler && tiddler.fields) {
parserInfo.sourceText = tiddler.hasField(field) ? tiddler.fields[field].toString() : null;
}
return this.parseText("text/vnd.tiddlywiki",text.toString(),options);
} else if(index) {
this.getTiddlerText(title); // Force the tiddler to be lazily loaded
text = this.extractTiddlerDataItem(tiddler,index,undefined);
if(text === undefined) {
return null;
}
return this.parseText("text/vnd.tiddlywiki",text,options);
parserInfo.sourceText = this.extractTiddlerDataItem(tiddler,index,null);
}
};
if(parserInfo.sourceText === null) {
parserInfo.parserType = null;
}
return parserInfo;
}
/*
Make a widget tree for a parse tree
@@ -1559,4 +1575,23 @@ exports.slugify = function(title,options) {
return slug;
};
/*
Return an array of the titles that would generate a specified slug, if any. Options include:
*/
exports.unslugify = function(slug) {
var self = this,
slugToTitle = this.getGlobalCache("slugs",function() {
var map = {};
$tw.utils.each($tw.wiki.allTitles(),function(title) {
var slug = self.slugify(title);
if(!(slug in map)) {
map[slug] = [];
}
map[slug].push(title);
});
return map;
});
return slugToTitle[slug];
};
})();

View File

@@ -4,4 +4,5 @@ title: $:/core/save/all-external-js
\define saveTiddlerFilter()
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
\end
\define coreURL() %24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js
{{$:/core/templates/tiddlywiki5-external-js.html}}

View File

@@ -0,0 +1,8 @@
title: $:/core/save/offline-external-js
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
\define saveTiddlerFilter()
[is[tiddler]] -[prefix[$:/state/popup/]] -[prefix[$:/temp/]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[[$:/core]] -[[$:/plugins/tiddlywiki/filesystem]] -[[$:/plugins/tiddlywiki/tiddlyweb]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
\end
\define coreURL() tiddlywikicore-$(version)$.js
{{$:/core/templates/tiddlywiki5-external-js.html}}

View File

@@ -2,4 +2,8 @@ title: $:/core/templates/tiddlywiki5.js/tiddlers
`
$tw.preloadTiddlerArray(`<$text text=<<jsontiddlers "[[$:/core]]">>/>`);
$tw.preloadTiddlerArray([{
title: "$:/config/SaveWikiButton/Template",
text: "$:/core/save/offline-external-js"
}]);
`

View File

@@ -2,7 +2,7 @@ title: $:/core/templates/tiddlywiki5-external-js.html
\rules only filteredtranscludeinline transcludeinline
<!doctype html>
{{$:/core/templates/MOTW.html}}<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
{{$:/core/templates/MOTW.html}}<html lang="{{{ [{$:/language}get[name]] }}}">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--~~ Raw markup for the top of the head section ~~-->
@@ -43,5 +43,6 @@ title: $:/core/templates/tiddlywiki5-external-js.html
<!--~~ Raw markup for the bottom of the body section ~~-->
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}
</body>
<script src="%24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js" onerror="alert('Error: Cannot load tiddlywiki.js');"></script>
<!--~~ Load external JS ~~-->
<script src="{{{ [<coreURL>] }}}" onerror="alert('Error: Cannot load {{{ [<coreURL>] }}}');"></script>
</html>

View File

@@ -0,0 +1,4 @@
title: $:/core/templates/html-json-skinny-tiddler
<$list filter="[<numTiddlers>compare:number:gteq[1]] ~[<counter>!match[1]]">`,`<$text text=<<newline>>/></$list>
<$jsontiddler tiddler=<<currentTiddler>> exclude="text" escapeUnsafeScriptChars="yes"/>

View File

@@ -0,0 +1,3 @@
title: $:/core/templates/html-json-tiddler
<$list filter="[<counter>!match[1]]">`,`<$text text=<<newline>>/></$list><$jsontiddler tiddler=<<currentTiddler>> escapeUnsafeScriptChars="yes"/>

View File

@@ -1,14 +1,37 @@
title: $:/core/templates/store.area.template.html
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
`<div id="storeArea" style="display:none;">`
<$list filter=<<saveTiddlerFilter>> template="$:/core/templates/html-div-tiddler"/>
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} template="$:/core/templates/html-div-skinny-tiddler"/>
`</div>`
</$reveal>
<$reveal type="match" state="$:/isEncrypted" text="yes">
`<!--~~ Encrypted tiddlers ~~-->`
`<pre id="encryptedStoreArea" type="text/plain" style="display:none;">`
<$encrypt filter=<<saveTiddlerFilter>>/>
`</pre>`
</$reveal>
\whitespace trim
<!-- Unencrypted -->
<$list filter="[[$:/isEncrypted]get[text]else[no]match[no]]">
<$list filter="[[storeAreaFormat]is[variable]getvariable[]else[json]match[json]]">
<!-- New-style JSON store area, with an old-style store area for compatibility with v5.1.x tooling -->
`<script class="tiddlywiki-tiddler-store" type="application/json">[`
<$vars newline={{{ [charcode[10]] }}}>
<$text text=<<newline>>/>
<$list filter=<<saveTiddlerFilter>> counter="counter" template="$:/core/templates/html-json-tiddler"/>
<$vars numTiddlers={{{ [subfilter<saveTiddlerFilter>count[]] }}}>
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} counter="counter" template="$:/core/templates/html-json-skinny-tiddler"/>
</$vars>
<$text text=<<newline>>/>
</$vars>
`]</script>`
`<div id="storeArea" style="display:none;">`
`</div>`
</$list>
<$list filter="[[storeAreaFormat]is[variable]getvariable[]else[json]match[div]]">
<!-- Old-style DIV/PRE-based store area -->
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
`<div id="storeArea" style="display:none;">`
<$list filter=<<saveTiddlerFilter>> template="$:/core/templates/html-div-tiddler"/>
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} template="$:/core/templates/html-div-skinny-tiddler"/>
`</div>`
</$reveal>
</$list>
</$list>
<!-- Encrypted -->
<$list filter="[[$:/isEncrypted]get[text]else[no]match[yes]]">
`<!--~~ Encrypted tiddlers ~~-->`
`<pre id="encryptedStoreArea" type="text/plain" style="display:none;">`
<$encrypt filter=<<saveTiddlerFilter>>/>
`</pre>`
</$list>

View File

@@ -0,0 +1,81 @@
title: $:/core/ui/ControlPanel/Publishing
tags: $:/tags/ControlPanel
caption: {{$:/language/ControlPanel/Publishing/Caption}}
{{$:/language/ControlPanel/Publishing/Hint}}
<div class="tc-publishing-job-listing">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/PublishingJob]]" variable="job">
<div class="tc-publishing-job">
<h2>Job: <$link to=<<job>>><$transclude tiddler=<<job>> field="caption" mode="inline"/></$link></h2>
Publisher: <$select tiddler=<<job>> field="publisher">
<$list filter="[[publisher]modules[]moduleproperty[name]]">
<option value=<<currentTiddler>>><$text text=<<currentTiddler>>/></option>
</$list>
</$select>
Base URL: <$edit-text tiddler=<<job>> size="50" field="baseurl"/>
Logs: <$view tiddler=<<job>> field="list"/>
<$set name="publisher-name" value={{{ [<job>get[publisher]] }}}>
<$set name="publisher-module" value={{{ [[publisher]modules[name],<publisher-name>] }}}>
<div class="tc-publishing-job-publisher">
<h2>Publisher: <$text text=<<publisher-name>>/></h2>
<$set name="publisher-ui" value={{{ [<publisher-module>modulestring[ui],{$:/language}] }}}>
<$tiddler tiddler=<<job>>>
<<publisher-ui>>
</$tiddler>
</$set>
</div>
<$vars sitemap={{{ [<job>get[sitemap]] }}}>
<div class="tc-publishing-sitemap">
<h2>Sitemap: <$link to=<<sitemap>>><$view tiddler=<<sitemap>> field="caption"><$text text=<<sitemap>>/></$view></$link></h2>
<$list filter="[<sitemap>get[list]enlist-input[]]" variable="route">
<div class="tc-publishing-route">
<h2>Route: <$link to=<<route>>><$view tiddler=<<route>> field="caption"><$text text=<<route>>/></$view></$link></h2>
route type: <$view tiddler=<<route>> field="route-type"/>
path: <$edit-text tiddler=<<route>> size="50" field="route-path"/>
filter: <$edit-text tiddler=<<route>> size="50" field="route-tiddler-filter"/>
template: <$edit-text tiddler=<<route>> size="50" field="route-template"/>
</div>
</$list>
</div>
</$vars>
</$set>
</$set>
</div>
</$list>
</div>

View File

@@ -0,0 +1,25 @@
title: $:/core/ui/Buttons/export-tiddlywikicore
tags: $:/tags/PageControls
caption: {{$:/core/images/star-filled}} {{$:/language/Buttons/ExportTiddlyWikiCore/Caption}}
description: {{$:/language/Buttons/ExportTiddlyWikiCore/Hint}}
\whitespace trim
\define jsFileName() tiddlywikicore-$(version)$.js
\define noExportMsg()
It appears that you have a wiki with an external ~TiddlyWiki core. The export action cannot be performed.
<p>You will need to view the page source in your browser. Then go to the very bottom the the source, find the last `<script>`
element, and right-click its `src` URI. Save the link as ''$(jsFileName)$''</p>
\end
<$button tooltip={{$:/language/Buttons/ExportTiddlyWikiCore/Hint}} aria-label={{$:/language/Buttons/ExportTiddlyWikiCore/Caption}} class=<<tv-config-toolbar-class>>>
<$list filter="[[$:/boot/boot.js]is[missing]]" variable="ignore" emptyMessage="""<$action-sendmessage $message="tm-download-file" $param="$:/core/templates/tiddlywiki5.js" filename=<<jsFileName>>/>""" >
<$action-setfield $tiddler=<<qualify "$:/temp/alert">> text=<<noExportMsg>> subtitle="Export ~TiddllyWiki Core"/>
<$action-sendmessage $message="tm-modal" $param=<<qualify "$:/temp/alert">>/>
</$list>
<$list filter="[<tv-config-toolbar-icons>match[yes]]" variable="listItem">
{{$:/core/images/star-filled}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text={{$:/language/Buttons/ExportTiddlyWikiCore/Caption}}/></span>
</$list>
</$button>

View File

@@ -0,0 +1,19 @@
title: $:/core/ui/Buttons/publish
tags: $:/tags/PageControls
caption: {{$:/core/images/publish}} {{$:/language/Buttons/Publish/Caption}}
description: {{$:/language/Buttons/Publish/Hint}}
\define publish-actions()
<$list filter="[all[shadows+tiddlers]tag[$:/tags/PublishingJob]has[enabled]field:enabled[yes]]" variable="job">
<$action-sendmessage $message="tm-publish" job=<<job>>/>
</$list>
\end
<$button actions=<<publish-actions>> tooltip={{$:/language/Buttons/Publish/Hint}} aria-label={{$:/language/Buttons/Publish/Caption}} class=<<tv-config-toolbar-class>>>
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
{{$:/core/images/publish}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text={{$:/language/Buttons/Publish/Caption}}/></span>
</$list>
</$button>

View File

@@ -3,8 +3,15 @@ tags: $:/tags/ViewTemplate
\define lingo-base() $:/language/Import/
\define confirmCancel()
<$action-confirm $message={{$:/language/Import/Listing/Cancel/Warning}} >
<$action-deletetiddler $tiddler=<<currentTiddler>>/>
<$action-sendmessage $message="tm-close-tiddler" title=<<currentTiddler>>/>
</$action-confirm>
\end
\define buttons()
<$button message="tm-delete-tiddler" param=<<currentTiddler>>><<lingo Listing/Cancel/Caption>></$button>
<$button actions=<<confirmCancel>> ><<lingo Listing/Cancel/Caption>></$button>
<$button message="tm-perform-import" param=<<currentTiddler>>><<lingo Listing/Import/Caption>></$button>
<<lingo Listing/Preview>> <$select tiddler="$:/state/importpreviewtype" default="$:/core/ui/ImportPreviews/Text">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ImportPreview]!has[draft.of]]">

View File

@@ -1,6 +1,6 @@
title: $:/config/OfficialPluginLibrary
tags: $:/tags/PluginLibrary
url: https://tiddlywiki.com/library/v5.1.24/index.html
url: https://tiddlywiki.com/library/v5.2.0/index.html
caption: {{$:/language/OfficialPluginLibrary}}
{{$:/language/OfficialPluginLibrary/Hint}}

View File

@@ -4,6 +4,7 @@ core/ui/Buttons/advanced-search: hide
core/ui/Buttons/close-all: hide
core/ui/Buttons/encryption: hide
core/ui/Buttons/export-page: hide
core/ui/Buttons/export-tiddlywikicore: hide
core/ui/Buttons/fold-all: hide
core/ui/Buttons/full-screen: hide
core/ui/Buttons/home: hide

View File

@@ -0,0 +1,9 @@
title: $:/config/PublishingJobs/Default
tags: $:/tags/PublishingJob
caption: Demo static site
publisher: filesystem
sitemap: $:/core/sitemaps/StaticSite
jszip-output-filename: myzipfile.zip
baseurl: https://example.com
enabled: yes
export-filter: [!is[image]!is[system]] [is[image]!is[system]]

View File

@@ -0,0 +1,7 @@
title: $:/core/routes/StaticSite/HTML
caption: Static HTML
tags: $:/tags/Route
route-type: render
route-path: static/$title_slugify$.html
route-tiddler-filter: [!is[system]!is[image]]
route-template: $:/core/templates/static.tiddler.html

View File

@@ -0,0 +1,6 @@
title: $:/core/routes/StaticSite/Images
caption: Images
tags: $:/tags/Route
route-type: raw
route-path: images/$title_slugify$.$type_extension$
route-tiddler-filter: [is[image]]

View File

@@ -0,0 +1,6 @@
title: $:/core/routes/StaticSite/Index
caption: Index
tags: $:/tags/Route
route-type: render
route-path: index.html
route-template: $:/core/save/all

View File

@@ -0,0 +1,7 @@
title: $:/core/routes/StaticSite/Styles
caption: Styles
tags: $:/tags/Route
route-type: render
route-path: static/static.css
route-template: $:/core/templates/static.template.css
route-output-type: text/css

View File

@@ -0,0 +1,9 @@
title: $:/core/sitemaps/StaticSite
caption: Static site map
description: The original TiddlyWiki 5 static file layout
tags: $:/tags/SiteMap
list: $:/core/routes/StaticSite/Index $:/core/routes/StaticSite/HTML $:/core/routes/StaticSite/Images $:/core/routes/StaticSite/Styles
\define tv-wikilink-template()
$title_slugify$.html
\end

View File

@@ -1,2 +1,2 @@
title: $:/tags/PageControls
list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/more-page-actions]]
list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/export-tiddlywikicore]] [[$:/core/ui/Buttons/more-page-actions]]

View File

@@ -8,6 +8,7 @@ Welcome to the developer documentation for TiddlyWiki (https://tiddlywiki.com/).
* An assignment by Christian Jurke and Christian Heigele, two students working on their Master's degree in Information Technology at the Gießen University of Applied Sciences (Technische Hochschule Mittelhessen). Their work can be seen in the [[Introduction]] and the tiddlers that link from it.
* New developer documentation
** [[Data Storage in Single File TiddlyWiki]]
** [[Continuous Deployment]]
** [[GitHub Branches]]
** HookMechanism
@@ -29,4 +30,4 @@ Welcome to the developer documentation for TiddlyWiki (https://tiddlywiki.com/).
** [[Scripts for building tiddlywiki.com]]
** SyncAdaptorModules
** WidgetModules
** WikiRuleModules
** WikiRuleModules

View File

@@ -3,12 +3,4 @@ modified: 20140710081051087
tags: doc
title: Data-Storage
TW has two approaches to save the user data. These approaches depends on way you use TW. either you use node.js as a server for TW its saves the tiddlers as plain text in different files or you use TW as standalone in a browser it persists the data within the HTML-File in two Div-Areas depending on whether the encryption of the TiddlyWiki is activated or not. If the TiddlyWiki is not encrypted the data is stored in the Div-Area called "~StoreArea". Every created Tiddler is stored in a own Div-area with a few custom values. An example of a saved Tiddler is shown below (\prettyref{lst:data-div}).
```html
<div created="20140611153703343" modified="20140611153734589" tags="testTag" testfield="testvalue" title="TestTiddler" type="text/plain">
<pre>testText</pre>
</div>
```
The Div-Area has the same attributes like the standard tillder fields, listed in [[TiddlerFields|https://tiddlywiki.com/#TiddlerFields]], all attributes which are not in this list are parsed as a custom field. The only required attribute is the name attribute, all other attributes are optional.\\
With a activated encryption the data is stored in a special Div-Area called "encryptedStoreArea". TiddlyWiki uses the Standford [[JavaScript Crypto Libary|http://bitwiseshiftleft.github.io/sjcl/]]. The encrypted Tiddlers are saved in a JSON string within this Div-Area.
{{Data Storage}}

View File

@@ -69,7 +69,7 @@ Returns a revision ID.
Retrieves status information from the server. This method is optional.
|!Parameter |!Description |
|callback |Callback function invoked with parameters `err,isLoggedIn,username,isReadOnly,isAnonymous,isPollingDisabled` |
|callback |Callback function invoked with parameters `err,isLoggedIn,username,isReadOnly` |
!! `login(username,password,callback)`
@@ -128,40 +128,33 @@ The syncer will use the `getUpdatedTiddlers()` method in preference to the `getS
|!Parameter |!Description |
|callback |Callback function invoked with parameter `err,tiddlers`, where `tiddlers` is an array of tiddler field objects |
!! `saveTiddler(tiddler,options,callback)`
!! `saveTiddler(tiddler,callback)`
Saves a tiddler to the server.
|!Parameter |!Description |
|tiddler |Tiddler to be saved |
|options |See below |
|callback |Callback function invoked with parameter `err,adaptorInfo,revision` |
|tiddlerInfo |The tiddlerInfo maintained by the syncer for this tiddler |
!! `loadTiddler(title,options,callback)`
!! `loadTiddler(title,callback)`
Loads a tiddler from the server.
|!Parameter |!Description |
|title |Title of tiddler to be retrieved |
|options |See below |
|callback |Callback function invoked with parameter `err,tiddlerFields` |
!! `deleteTiddler(title,options,callback)`
!! `deleteTiddler(title,callback,options)`
Delete a tiddler from the server.
|!Parameter |!Description |
|title |Title of tiddler to be deleted |
|options |See below |
|callback |Callback function invoked with parameter `err` |
|options |See below |
!!! Options
<<.from-version "5.2.0">> The signature of syncadaptor functions that accept callbacks has been changed so that the callback is always the last argument. A check for the old order of arguments means that this change is backwards compatible. The new order should be prefered when updating or writing new plugins.
The options parameter may contain the following properties, depending on the method called.
The options parameter contains the following properties:
|!Property |!Description |
|changeCount |The //new// changeCount value for this tiddler |
|tiddlerInfo |The tiddlerInfo maintained by the syncer for this tiddler |

View File

@@ -0,0 +1,97 @@
created: 20210525165258247
modified: 20210614134705320
tags: Data-Storage doc [[Data Storage]]
title: Data Storage in Single File TiddlyWiki
The single file version of ~TiddlyWiki saves the tiddlers within the HTML file.
Version 5.2.0 of ~TiddlyWiki introduced a new format for how tiddlers are saved within the HTML file.
---
!! Up to and including ~TiddlyWiki v5.1.23
Tiddlers are saved within the HTML file in one of two `<div>` tags depending on whether the TiddlyWiki is configured to encrypt its content or not.
!!! Without encryption
If the ~TiddlyWiki is not encrypted the data is stored in a `<div>` tag with an `id` attribute of "storeArea".
```html
<div id="storeArea" style="display:none;">
```
Within the store area `<div>`, each tiddler is stored in its own div.
//Tiddler DIV//
Each tiddler `<div>` has an attribute corresponding to each of the tiddler's fields, except the text field which is saved within a `pre` tag within the `<div>`. Note that all attributes and therefore field names must be lowercase letters, digits or the characters `-` (dash), `_` (underscore) and `.` (period). The only attribute that is required is `title`, all other attributes are optional. All attribute values and the text field within the `pre` tag are HTML encoded.
Example:
```html
<div id="storeArea" style="display:none;">
<div created="20140611153703343" modified="20140611153734589" tags="testTag" testfield="testvalue" title="TestTiddler" type="text/plain">
<pre>testText</pre>
</div>
</div>
```
!!! With encryption
If the ~TiddlyWiki is configured to encrypt its content, the tiddlers are stored as as an encrypted JSON string in a `<pre>` tag with the `id` attribute "encryptedStoreArea".
```html
<pre id="encryptedStoreArea" type="text/plain" style="display:none;">
```
~TiddlyWiki uses the [[Stanford JavaScript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]].
---
!! From ~TiddlyWiki v5.2.0
From v5.2.0 onwards, ~TiddlyWiki introduces a new JSON based format for the tiddler store area for unencrypted content, while retaining support for the older store area formats described above for backwards compatibility. The store area format remains unchanged for encrypted content.
!!! Without encryption
By default, all tiddlers are now stored as an array of JSON objects inside a `<script>` tag with the `class` attribute "tiddlywiki-tiddler-store" and the `type` "application/json". For better readability, every tiddler object begins on a new line. There are no longer any restrictions on characters that are allowed in tiddler field names. However, `<script>` tag store areas must encode the `<` character to `\u003c`.
```html
<script class="tiddlywiki-tiddler-store" type="application/json">[
{"title":"XLSX Utilities Edition","created":"20161023202301847","modified":"20161023202301847","tags":"Editions","type":"text/vnd.tiddlywiki","text":"The ''XLSX Utilities'' edition of TiddlyWiki contains tools to work with `.XLSX` spreadsheets generated by applications like Microsoft Excel and Google Sheets. It can be used in the browser or under Node.js.\n\nhttps://tiddlywiki.com/editions/xlsx-utils/\r\n"},
{"text":"In accordance with the [[Philosophy of Tiddlers]], documentation tiddlers are typically short and interlinked.\n\nWhen a tiddler seems as if it needs to contain subheadings, this is often a sign that it should in fact be split into several tiddlers. But it is reasonable for a [[reference tiddler|Reference Tiddlers]] to consist of an untitled introductory section followed by a titled section of details.\n\nConsistency of terminology is essential if the reader is not to become confused. Consistent typography and punctuation lend a professional quality to the documentation. Macros can improve the consistency and maintainability of the text.\n\nUse numbered lists for step-by-step instructions, and bullet points for lists whose order is arbitrary. Use a definition list in preference to a bulleted list if each bulleted item would begin with a term and a colon. If at all possible, avoid burdening the reader with a nested list.\n\nUse a table when information naturally falls into three or more columns, and also for lists of parameters, attributes, etc in [[reference tiddlers|Reference Tiddlers]].\n\nAvoid periods at the end of list items, headings and table cell text.\n\nThe documentation describes the current reality of ~TiddlyWiki. Avoid discussing future aspirations.\n","title":"Tiddler Structure","tags":"[[Improving TiddlyWiki Documentation]]","modified":"20210207124737959","created":"20150110183300000"}
]</script>
```
To retain compatibility with external tools that might insert tiddlers by directly manipulating the ~TiddlyWiki HTML file, the older format `<div>` store area is still present in the HTML file immediately after the new `<script>` tag store area:
```html
<script class="tiddlywiki-tiddler-store" type="application/json">[
{"title":"XLSX Utilities Edition","created":"20161023202301847","modified":"20161023202301847","tags":"Editions","type":"text/vnd.tiddlywiki","text":"The ''XLSX Utilities'' edition of TiddlyWiki contains tools to work with `.XLSX` spreadsheets generated by applications like Microsoft Excel and Google Sheets. It can be used in the browser or under Node.js.\n\nhttps://tiddlywiki.com/editions/xlsx-utils/\r\n"},
{"text":"In accordance with the [[Philosophy of Tiddlers]], documentation tiddlers are typically short and interlinked.\n\nWhen a tiddler seems as if it needs to contain subheadings, this is often a sign that it should in fact be split into several tiddlers. But it is reasonable for a [[reference tiddler|Reference Tiddlers]] to consist of an untitled introductory section followed by a titled section of details.\n\nConsistency of terminology is essential if the reader is not to become confused. Consistent typography and punctuation lend a professional quality to the documentation. Macros can improve the consistency and maintainability of the text.\n\nUse numbered lists for step-by-step instructions, and bullet points for lists whose order is arbitrary. Use a definition list in preference to a bulleted list if each bulleted item would begin with a term and a colon. If at all possible, avoid burdening the reader with a nested list.\n\nUse a table when information naturally falls into three or more columns, and also for lists of parameters, attributes, etc in [[reference tiddlers|Reference Tiddlers]].\n\nAvoid periods at the end of list items, headings and table cell text.\n\nThe documentation describes the current reality of ~TiddlyWiki. Avoid discussing future aspirations.\n","title":"Tiddler Structure","tags":"[[Improving TiddlyWiki Documentation]]","modified":"20210207124737959","created":"20150110183300000"}
]</script><div id="storeArea" style="display:none;"></div>
```
Any tiddlers in the older format `<div>` store area are also loaded before tiddlers from the new `<script>` store area.
!!! Multiple store areas and precedence
Tiddlers from the new `<script>` tag store areas are loaded in the order of their store areas in the HTML file. Therefore if the same tiddler exists in two different `<script>` tag store areas, the tiddler from the later store area takes precedence. Note however that tiddlers from `<script>` tag store areas always take precedence over tiddlers from the older format `<div>` store area. Therefore a tiddler from the older `<div>` store area can never overwrite a tiddler from a `<script>` tag store area.
Note that all `<script>` tags with the `class` attribute "tiddlywiki-tiddler-store" have their content parsed as JSON and loaded as tiddlers. This allows external tools to easily insert tiddlers into an HTML file by appending additional `<script>` tag(s) at the very end of the HTML file:
```html
</html>
<script class="tiddlywiki-tiddler-store" type="application/json">[
{"created":"20210525212411223","text":"This is some test text","tags":"[[test tag]] [[another tag]]","title":"My new tiddler to insert","modified":"20210525212430577"}
]</script>
```
Additional topics:
* [[Extracting tiddlers from a single file TiddlyWiki]]

View File

@@ -0,0 +1,10 @@
created: 20140708085814626
modified: 20210525165526997
tags: doc
title: Data Storage
~TiddlyWiki has two approaches to saving the user data (tiddlers) depending on whether you are using the single file version, or are using the node.js server.
~TiddlyWiki on node.js saves each tiddler in a separate text file. For details on the file formats supported for tiddlers on node.js, see [[Tiddler Files|https://tiddlywiki.com/#TiddlerFiles]].
The single file version of ~TiddlyWiki saves the tiddlers within the HTML file. See [[Data Storage in Single File TiddlyWiki]]

View File

@@ -0,0 +1,20 @@
created: 20210623075733310
modified: 20210623081959571
tags: HookMechanism
title: Hook: th-closing-tiddler
type: text/vnd.tiddlywiki
This hook allows plugins to monitor the closing of a tiddler from the story.
Hook function parameters:
* ''event'': Widget message object with the following properties:
** ''event'': DOM event object that triggered the widget message
** ''tiddlerTitle'': the title of the tiddler being closed
** ''widget'': reference to the widget that sent the message.
Return value:
* ''event'': Widget message object
The original event widget message object can be returned unmodified by the hook.

View File

@@ -1,8 +1,8 @@
caption: 5.1.24
created: 20201229120443187
modified: 20201229120443187
caption: 5.2.0
created: 20210714091904613
modified: 20210714091904613
tags: ReleaseNotes
title: Release 5.1.24
title: Release 5.2.0
type: text/vnd.tiddlywiki
\define contributor(username)
@@ -11,61 +11,104 @@ type: text/vnd.tiddlywiki
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.1.23...master]]//
! Unrestricted Fieldnames and the New JSON Store Area
The major change in this release is that the format used to store tiddlers in TiddlyWiki's HTML file has changed from an HTML DIV-based format to a new JSON-based format. The consequences of this change are far-reaching, and justify the move from v5.1.x to v5.2.x:
Firstly, the ''characters that can be used in field names are now unrestricted'' (just like tiddler titles), making it possible to use fieldnames like `My^Field` or `☃️`. This became possible because every other part of TiddlyWiki was already capable of dealing with unrestricted field names apart from saving as an HTML file.
Secondly, external tools that read, write or otherwise process TiddlyWiki HTML files will need modification to understand the new store area format. The details are documented in the [[/dev wiki|https://tiddlywiki.com/prerelease/dev/#Data%20Storage%20in%20Single%20File%20TiddlyWiki]].
For more details, see ticket [[#5708|https://github.com/Jermolene/TiddlyWiki5/pull/5708]] on GitHub.
! Performance Improvements
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5380">> (and again [[here|https://github.com/Jermolene/TiddlyWiki5/pull/5488]]) the efficiency of the linked list implementation
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5362">> [[all Operator]] to use new linked list implementation
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5369">> [[links Operator]] to use new linked list implementation
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5383">> unneeded escaping of double quotes in tiddler DIVs inside single file wikis (saving about 10% from the size of empty.html)
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5380">> (and again [[here|https://github.com/Jermolene/TiddlyWiki5/pull/5488]]) the efficiency of the linked list implementation used in filter processing
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5362">> the [[all Operator]] and ([[here|https://github.com/Jermolene/TiddlyWiki5/pull/5369"]]) the [[links Operator]] to use the new linked list implementation
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5436">> [[ETag|https://en.wikipedia.org/wiki/HTTP_ETag]]-based browser-side caching for all resources and GZip or Deflate compression for all responses to GET requests
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5736">> transclude widget to avoid triggering a refresh unless the transcluded field changes (previously it was also triggered for changes to other fields)
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/70e60cd93fe26e9cde1e350e3236578fa5abfe48">> plugin internal formatting to remove unneeded whitespace, saving about 6% on the size of the $:/core plugin
! Usability Improvements
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/a360adbba924d222c5b55709133c18890c04398d">> dropzone size when story river is empty
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5699">> (and again [[here|https://github.com/Jermolene/TiddlyWiki5/pull/5705]]) ability to drag and drop images in the editor to import and insert
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5816">> modal mechanism to prevent page from scrolling while modals are displayed
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/a360adbba924d222c5b55709133c18890c04398d">> size of dropzone when story river is empty
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5326">> fill colour for "remove tag" button
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5370">> page title so that the separating em-dash is only used if the site subtitle is present
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5397">> broken aria-label in $:/PaletteManager
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5451">> macro calls to use the same parser as that used for widget attributes
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/89546b3357b0696a7047e6915bd6cd137b589de6">> a hidden setting to control sandboxing of tiddlers of type `text/html`
* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/commit/caec6bc3fea9155eb2b0aae64d577c565dd7b088">> SVG optimiser script
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/c325380231a8c592a6e51d4498c1e6c3a241b539">> plus/minus SVG icons: <<.icon $:/core/images/plus-button>> and <<.icon $:/core/images/minus-button>>
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5294">> support for [[dynamic toolbar buttons|How to create dynamic editor toolbar buttons]]
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5612">> [[average Operator]], [[median Operator]], [[variance Operator]] and [[standard-deviation Operator]] for calculating the arithmetic mean of a list of numbers
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/cf56a17f28f1e44dcb62c5e161be4ac29e27c3f2">> unusedtitle macro to use the prefix parameter
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5672">> link to the existing tiddler when the warning "Target tiddler already exists" is displayed in the edit template
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5479">> ability to import previously blocked system tiddlers
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5727">> class to make tag pills in the edit template look like those in the ViewTemplate
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5812">> warning message when cancelling a pending import
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5726">> the HTML comment syntax to enable it to be used before and in between pragmas such as macro definitions
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/1661">> overflow of long field values that do not contain whitespace
! Filter improvements
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5813">> new [[:map filter run prefix|Filter Expression]]
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5653">> new [[:sort filter run prefix|Sort Filter Run Prefix]]
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5612">> [[average Operator]], [[median Operator]], [[variance Operator]] and [[standard-deviation Operator]] for calculating averages of a list of numbers
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5673">> [[deserializers filter Operator|deserializers Operator]] for obtaining a list of the available deserializers
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5665">> [[format:titlelist operator|format Operator]] for selectively adding double square brackets around titles
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/83ee363cb4c19246ac977f55e46439dd8a1508ba">> [[charcode Operator]] for easily accessing special characters like tab and linefeed
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5252">> support for suffixes to filter run prefixes
! Hackability Improvements
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/9eda02868f21e9dd1733ffe26352bd7ac96285b4">> new MessageCatcherWidget
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/3094e062366830bdecfb91e3d852667fa951dc50">> action widget execution to ensure all widgets are refreshed before invocation
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5836">> support for macro parameters within filters (see [[Filter Parameter]])
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5361">> [[unusedtitle Macro]] to add ''separator'' and ''template'' parameters
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5720">> ''color-scheme'' field to all themes to differentiate between light and dark themes
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/d25e540dd2f0decf61c52fdc665a28a5dfeda93f">> support for `image/vnd.microsoft.icon` content type
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5458">> support for throttling refreshes for changed tiddlers with the title prefix $:/temp/volatile/
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5451">> macro calls to use the same parser as that used for widget attributes
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/89546b3357b0696a7047e6915bd6cd137b589de6">> a hidden setting to control sandboxing of tiddlers of type `text/html`
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5707">> text operation [[insert-text|WidgetMessage: tm-edit-text-operation]]
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/e157d16b724172f752da0ff714847e0c0ca9664d">> ''data-tag-title'' attribute to tag pills
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5294">> support for [[dynamic toolbar buttons|How to create dynamic editor toolbar buttons]]
! Widget Improvements
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/9faaa312998d48c56bd50335820b6b881266af4b">> [[ActionCreateTiddlerWidget]] to make the new title available as a variable
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5611">> (and [[here|https://github.com/Jermolene/TiddlyWiki5/commit/4a99e0cc7d4a6b9e7071c0b2a9a0f63c3c7d2492]]) [[ListWidget]] with optional `counter` attribute specifying a variable to contain the numeric index of each list item
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/9eda02868f21e9dd1733ffe26352bd7ac96285b4">> (and [[here|https://github.com/Jermolene/TiddlyWiki5/commit/f87b3bfcdba79b6ad198af286bd827c61044891f]]) new MessageCatcherWidget to catch arbitrary messages and execute action strings
* <<.link-badge-modified "https://github.com/Jermolene/TiddlyWiki5/commit/b9647b2c48152dac069a1099a0822b32375a66cf">> [[FieldManglerWidget]] to ensure it doesn't propogate events that it traps
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5597">> [[DropzoneWidget]] to optionally invoke actions after the `tm-import-tiddlers` message has been sent, and to specify an optional `contentTypesFilter` which determines which content types are accepted by the dropzone.
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5611">> [[ListWidget]] with `index` attribute and here (and [[here| https://github.com/Jermolene/TiddlyWiki5/commit/4a99e0cc7d4a6b9e7071c0b2a9a0f63c3c7d2492]])
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5635">> [[SelectWidget]] refreshing
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5252">> support for suffixes to filter run prefixes
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5653">> :sort filter run prefix
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5644">> [[ActionListopsWidget]] bug by avoiding stitching together filter expressions for the original list values
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5635">> [[SelectWidget]] refreshing
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5644">> [[ActionListopsWidget]] bug by avoiding concatenating fragmnts of filter expressions
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/07caa16e8714afe9a64eb202375e4a2f95da1508">> [[DropzoneWidget]] to also use the specified deserializer for strings either dropped or pasted on to the dropzone
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/44df6fe52f79bee88357afb4fc3d6f4800aa6dde">> issue with widget not being available to filter operator
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/3f986861538a3cc5c3c6da578b45d0d9138a6b2b">> [[ActionPopupWidget]] to create floating popups that must be manually cleared
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5648">> (and [[here|https://github.com/Jermolene/TiddlyWiki5/pull/5782]]) [[KeyboardWidget]] to provide additional information variables
! Client-server Improvements
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/e96a54c7531a2d9e07745e27d2015d8d7d09588f">> crash running in client server configuration when 'etag' header is missing
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/e96a54c7531a2d9e07745e27d2015d8d7d09588f">> crash running in client server configuration when 'ETag' header is missing
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5423">> blank favicon when using lazily loaded images
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/4461">> web server issue with custom path prefix and basic authentication
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/5366">> crash on Node.js with ActionSetFieldWidget when type field is given a value upon new tiddler creation
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5329">> issue with tiddler titles that already end in the required extension
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5416">> crash on Node.js with ActionSetFieldWidget when type field is given a value upon new tiddler creation
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5329">> issue with saving tiddler files with titles that already end in the required extension
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5465">> several consistency issues with the filesystem plugin
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/5483">> issue with encoding of $:/config/OriginalTiddlerPaths outside the wiki folder
* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/5628">> the TiddlySpot Saver settings form
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5638">> 401 and 403 error messages for PUT saver
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d8ac00a10856b1b64311b8e0496344d5b0c1b987">> fixed crash if browser doesn't support Server Sent Events
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5570">> the [[external JavaScript template|Using the external JavaScript template]] to be easier to use and work more smoothly when offline
! Node.js Improvements
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/a38dc1730010c6a2b6a011aff4818bbc67c04055">> RenderCommand to allow multiple variables to be passed
! Plugin Improvements
!! [[XLSX Utilities Plugin]]
!! [[Markdown Plugin]]
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5846">> support for using the [[KaTeX Plugin]] in Markdown
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5848">> extraneous paragraph tags in lists
!! [[XLSX Utilities Plugin|XLSX Utilities Edition]]
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/5400">> crash with the XLSX Utils plugin
@@ -93,10 +136,15 @@ type: text/vnd.tiddlywiki
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/5377">> the Jasmine test suite output
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/9f9ce6595b08032a602981f82940ca113cff8211">> wikitext parser with a subclassing mechanism
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/ef76349c37662e9706acfffc2c2edb51a920183d">> added support for ''utils-browser'' modules
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/5464">> ''th-before-importing'' hook to allow plugins to inspect or modify the `importTiddler` object before any tiddlers are imported
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/3797">> ''th-closing-tiddler'' hook when tiddlers are closed
! Translation improvements
* <<.link-badge-improved>> Chinese translations
* <<.link-badge-improved>> French translations
* <<.link-badge-improved>> German translations
* <<.link-badge-improved>> Spanish translations
! Other Bug Fixes
@@ -110,14 +158,21 @@ type: text/vnd.tiddlywiki
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/5455">> upload saver to optionally work without a username or password
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/5e4430dbf9ff66d9a18fbdf3005abcd716efc07d">> RadioWidget to refresh selectively, and to use the ''checked'' attribute correctly
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5542">> "invert" option of `wiki.search()` method
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/commit/e157d16b724172f752da0ff714847e0c0ca9664d">> ''data-tag-title'' attribute to tag pills
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/7b1a0c6e6a8bd2d3badf8766af0cd3f5f7ac5ec8">> ES5 compatibility issue
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/commit/a38dc1730010c6a2b6a011aff4818bbc67c04055">> RenderCommand to allow multiple variables to be passed
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/7b1a0c6e6a8bd2d3badf8766af0cd3f5f7ac5ec8">> ES5 compatibility issue
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/226df2ad7d2978d3d7400d94767a0503e495cf98">> exporting of tiddlers that begin and end with double quotes
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/d56e8764a1f02a214df5da1cc95191be2da2491b">> accessibility of button widget when controlling a popup
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d6ea369f5ef9d3092a360a4286a99902df37782b">> EditTextWidget to use default text for missing fields
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5552">> css-escape-polyfill to work under Node.js
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/dbd3f835bf8399ed1a3da7cc322ec9b6ab783d53">> crash when sorting by non-string fields
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5711">> (and [[here|https://github.com/Jermolene/TiddlyWiki5/pull/5868]]) some bugs in the [[EventCatcherWidget]], introduced new `stopPorpagation` attribute and new syntax for specifying actions
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5691">> CurrentTiddler variable consistency in subfilters and prefixes
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/485779f5b2136b7bcd739352b56188d94b0eb9e4">> crash when accessing variables in filters that don't have a widget context
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/8fbf52e419e71d726ea32b6c44e3ccfc4245d825">> unnecessary triggering reload warning when javascript tiddlers are not subsequently imported
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5521">> minor issue with import pragma
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5700">> leading and trailing whitespace in themes
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5469">> configuration list of HTML5 block elements
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5692">> shape and color for disabled button to work with `tc-btn-invisible` class
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/5473">> inconsistent spacing of view toolbar items
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
@@ -130,6 +185,7 @@ type: text/vnd.tiddlywiki
* <<contributor CodaCodr>>
* <<contributor dixonge>>
* <<contributor donmor>>
* <<contributor felixhayashi>>
* <<contributor FlashSystems>>
* <<contributor flibbles>>
* <<contributor hoelzro>>
@@ -144,6 +200,7 @@ type: text/vnd.tiddlywiki
* <<contributor NicolasPetton>>
* <<contributor OdinJorna>>
* <<contributor pmario>>
* <<contributor rryan>>
* <<contributor saqimtiaz>>
* <<contributor simonbaird>>
* <<contributor slaymaker1907>>

View File

@@ -2,7 +2,5 @@ created: 20131127215321439
modified: 20140912135951542
title: $:/DefaultTiddlers
[[TiddlyWiki Pre-release]]
HelloThere
GettingStarted
Community
Publishing
$:/core/ui/ControlPanel/Publishing

View File

@@ -1,6 +1,6 @@
title: $:/config/OfficialPluginLibrary
tags: $:/tags/PluginLibrary
url: https://tiddlywiki.com/prerelease/library/v5.1.24/index.html
url: https://tiddlywiki.com/prerelease/library/v5.2.0/index.html
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease)
The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.

View File

@@ -11,13 +11,16 @@
],
"build": {
"listen": [
"--listen","root-tiddler=$:/core/save/all-external-js"],
"--listen","root-tiddler=$:/core/save/all-external-js","use-browser-cache=yes"],
"index": [
"--rendertiddler","$:/plugins/tiddlywiki/tiddlyweb/save/offline","index.html","text/plain"],
"--rendertiddler","$:/core/save/offline-external-js","index.html","text/plain",
"--render","$:/core/templates/tiddlywiki5.js","[[tiddlywikicore-]addsuffix<version>addsuffix[.js]]","text/plain"],
"static": [
"--rendertiddler","$:/core/templates/static.template.html","static.html","text/plain",
"--rendertiddler","$:/core/templates/alltiddlers.template.html","alltiddlers.html","text/plain",
"--rendertiddlers","[!is[system]]","$:/core/templates/static.tiddler.html","static","text/plain",
"--rendertiddler","$:/core/templates/static.template.css","static/static.css","text/plain"]
"--rendertiddler","$:/core/templates/static.template.css","static/static.css","text/plain"],
"tiddlywikicore": [
"--render","$:/core/templates/tiddlywiki5.js","[[tiddlywikicore-]addsuffix<version>addsuffix[.js]]","text/plain"]
}
}
}

View File

@@ -0,0 +1,4 @@
title: dezerializer test data case 1
type: text/html
<!doctype html>

View File

@@ -0,0 +1,12 @@
title: dezerializer test data case 2
type: text/html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,18 @@
title: dezerializer test data case 3
type: text/html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
</head>
<body>
<!--~~ Ordinary tiddlers ~~-->
<div id="storeArea" style="display:none;"><div title="Hello &quot;There&quot;" type="text/vnd.tiddlywiki">
<pre>Abacus</pre>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,19 @@
title: dezerializer test data case 4
type: text/html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
</head>
<body>
<!--~~ Ordinary tiddlers ~~-->
<div id="storeArea" style="display:none;"><div title="Hello &quot;There&quot;" type="text/vnd.tiddlywiki">
<pre>Abacus</pre>
</div>
</div>
<script class="tiddlywiki-tiddler-store" type="application/json">[{"title":"Hello \"There\"","text":"Calculator"}]</script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
title: dezerializer test data case 5
type: text/html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
</head>
<body>
<!--~~ Ordinary tiddlers ~~-->
<div id="storeArea" style="display:none;"><div title="Hello &quot;There&quot;" type="text/vnd.tiddlywiki">
<pre>Abacus</pre>
</div>
</div>
<script class="tiddlywiki-tiddler-store" type="application/json">[{"title":"Hello \"There\"","text":"Calculator"}]</script>
</body>
</html>
<script class="tiddlywiki-tiddler-store" type="application/json">[{"title":"Hello \"There\"","text":"Protractor"}]</script>

View File

@@ -0,0 +1,39 @@
/*\
title: test-deserializers.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests various core deserializers
\*/
(function(){
/* jslint node: true, browser: true */
/* eslint-env node, browser, jasmine */
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
/* global $tw, require */
"use strict";
describe("deserializer tests", function() {
function executeTestCase(title,expectedOutput) {
it("test case " + title, function() {
var tiddler = $tw.wiki.getTiddler(title);
expect($tw.wiki.deserializeTiddlers(tiddler.fields.type,tiddler.fields.text)).toEqual(expectedOutput);
});
}
executeTestCase("dezerializer test data case 1",[ { text: '<!doctype html>\n', type: 'text/html' } ]);
executeTestCase("dezerializer test data case 2",[ { text: '<!doctype html>\n<html lang="en">\n<head>\n\t<meta charset="utf-8">\n\t<title>Test Data</title>\n</head>\n<body>\n</body>\n</html>\n', type: 'text/html' } ]);
executeTestCase("dezerializer test data case 3",[ { title: 'Hello "There"', text: 'Abacus', type: 'text/vnd.tiddlywiki' } ]);
executeTestCase("dezerializer test data case 4",[ { title: 'Hello "There"', text: 'Abacus', type: 'text/vnd.tiddlywiki' }, { title: 'Hello "There"', text: 'Calculator'} ]);
executeTestCase("dezerializer test data case 5",[ { title: 'Hello "There"', text: 'Abacus', type: 'text/vnd.tiddlywiki' }, { title: 'Hello "There"', text: 'Calculator'} , { title: 'Hello "There"', text: 'Protractor'} ]);
});
})();

View File

@@ -818,6 +818,48 @@ function runTests(wiki) {
it("should handle the deserializers operator", function() {
expect(wiki.filterTiddlers("[deserializers[]]").join(",")).toBe("application/javascript,application/json,application/x-tiddler,application/x-tiddler-html-div,application/x-tiddlers,text/css,text/html,text/plain");
});
it("should handle the charcode operator", function() {
expect(wiki.filterTiddlers("[charcode[9]]").join(" ")).toBe(String.fromCharCode(9));
expect(wiki.filterTiddlers("[charcode[9],[10]]").join(" ")).toBe(String.fromCharCode(9) + String.fromCharCode(10));
expect(wiki.filterTiddlers("[charcode[]]").join(" ")).toBe("");
});
it("should parse filter variable parameters", function(){
expect($tw.utils.parseFilterVariable("currentTiddler")).toEqual(
{ name: 'currentTiddler', params: [ ] }
);
expect($tw.utils.parseFilterVariable("now DDMM")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', start: 3, value: 'DDMM', end: 8 }] }
);
expect($tw.utils.parseFilterVariable("now DDMM UTC")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', start: 3, value: 'DDMM', end: 8 }, { type: 'macro-parameter', start: 8, value: 'UTC', end: 12 }] }
);
expect($tw.utils.parseFilterVariable("now format:DDMM")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', name:'format', start: 3, value: 'DDMM', end: 15 }] }
);
expect($tw.utils.parseFilterVariable("now format:'DDMM'")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', name:'format', start: 3, value: 'DDMM', end: 17 }] }
);
expect($tw.utils.parseFilterVariable("nowformat:'DDMM'")).toEqual(
{ name: 'nowformat:\'DDMM\'', params: [] }
);
expect($tw.utils.parseFilterVariable("nowformat:'DD MM'")).toEqual(
{ name: 'nowformat:', params: [{ type: 'macro-parameter', start: 10, value: 'DD MM', end: 17 }] }
);
expect($tw.utils.parseFilterVariable("now [UTC]YYYY0MM0DD0hh0mm0ssXXX")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', start: 3, value: '[UTC]YYYY0MM0DD0hh0mm0ssXXX', end: 31 }] }
);
expect($tw.utils.parseFilterVariable("now '[UTC]YYYY0MM0DD0hh0mm0ssXXX'")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', start: 3, value: '[UTC]YYYY0MM0DD0hh0mm0ssXXX', end: 33 }] }
);
expect($tw.utils.parseFilterVariable("now format:'[UTC]YYYY0MM0DD0hh0mm0ssXXX'")).toEqual(
{ name: 'now', params: [{ type: 'macro-parameter', start: 3, name:'format', value: '[UTC]YYYY0MM0DD0hh0mm0ssXXX', end: 40 }] }
);
expect($tw.utils.parseFilterVariable("")).toEqual(
{ name: '', params: [] }
);
});
}

View File

@@ -0,0 +1,132 @@
/*\
title: test-parsetextreference.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests for source attribute in parser returned from wiki.parseTextReference
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
describe("Wiki.parseTextReference tests", function() {
// Create a wiki
var wiki = new $tw.Wiki();
wiki.addTiddler({
title: "TiddlerOne",
text: "The quick brown fox in $:/TiddlerTwo",
tags: ["one"],
authors: "Joe Bloggs",
modifier: "JoeBloggs",
modified: "201304152222"});
wiki.addTiddler({
title: "$:/TiddlerTwo",
tags: ["two"],
authors: "[[John Doe]]",
modifier: "John",
modified: "201304152211"});
wiki.addTiddler({
title: "Tiddler Three",
text: '{"oct":31,"nov":30,"dec":31,"jan":""}',
tags: ["one","two"],
type: "application/json",
modifier: "John",
modified: "201304162202"});
wiki.addTiddler({
title: "TiddlerFour",
text: "The quick brown fox in $:/TiddlerTwo",
tags: ["one"],
type: "text/vnd.tiddlywiki",
authors: "",
modifier: "JoeBloggs",
modified: "201304152222"});
// Add a plugin containing some shadow tiddlers
var shadowTiddlers = {
tiddlers: {
"$:/TiddlerFive": {
title: "$:/TiddlerFive",
text: "Everything in federation",
tags: ["two"]
},
"TiddlerSix": {
title: "TiddlerSix",
text: "Missing inaction from TiddlerOne",
filter: "[[one]] [[a a]] [subfilter{hasList!!list}]",
tags: []
},
"TiddlerSeventh": {
title: "TiddlerSeventh",
text: "",
list: "TiddlerOne [[Tiddler Three]] [[a fourth tiddler]] MissingTiddler",
tags: ["one"]
},
"Tiddler8": {
title: "Tiddler8",
text: "Tidd",
tags: ["one"],
"test-field": "JoeBloggs",
"myfield":""
}
}
};
wiki.addTiddler({
title: "$:/ShadowPlugin",
text: JSON.stringify(shadowTiddlers),
"plugin-type": "plugin",
type: "application/json"});
wiki.addTiddler({
title: "TiddlerNine",
text: "this is plain text",
type: "text/plain"
});
// Define a parsing shortcut for souce attribute of parser returned by wiki.parseTextReference
var parseAndGetSource = function(title,field,index,subTiddler) {
var parser = wiki.parseTextReference(title,field,index,{subTiddler: subTiddler});
return parser ? parser.source : null;
};
it("should parse text references and return correct source attribute", function(){
// Existing tiddler with a text field, no field argument specified
expect(parseAndGetSource("TiddlerOne")).toEqual("The quick brown fox in $:/TiddlerTwo");
// Existing tiddler with a text field, field argument specified as text
expect(parseAndGetSource("TiddlerOne","text")).toEqual("The quick brown fox in $:/TiddlerTwo");
// Existing tiddler with no text field
expect(parseAndGetSource("$:/TiddlerTwo")).toEqual("");
// Existing tiddler, field argument specified as authors
expect(parseAndGetSource("TiddlerOne","authors")).toEqual("Joe Bloggs");
// Non-existent tiddler, no field argument
expect(parseAndGetSource("MissingTiddler")).toEqual(null);
// Non-existent tiddler, field argument
expect(parseAndGetSource("MissingTiddler","missing-field")).toEqual(null);
// Non-existent tiddler, index specified
expect(parseAndGetSource("MissingTiddler",null,"missing-index")).toEqual(null);
// Existing tiddler with non existent field
expect(parseAndGetSource("TiddlerOne","missing-field")).toEqual(null);
// Existing tiddler with blank field
expect(parseAndGetSource("TiddlerFour","authors")).toEqual("");
// Data tiddler with index specified
expect(parseAndGetSource("Tiddler Three",null,"oct")).toEqual("31");
// Data tiddler with blank index
expect(parseAndGetSource("Tiddler Three",null,"jan")).toEqual("");
// Data tiddler with non-existent index
expect(parseAndGetSource("Tiddler Three",null,"feb")).toEqual(null);
// Existing tiddler with a text field, type set to vnd.tiddlywiki
expect(parseAndGetSource("TiddlerFour")).toEqual("The quick brown fox in $:/TiddlerTwo");
// Existing subtiddler of a plugin
expect(parseAndGetSource("$:/ShadowPlugin","text",null,"Tiddler8")).toEqual("Tidd");
// Existing blank field of a subtiddler of a plugin
expect(parseAndGetSource("$:/ShadowPlugin","myfield",null,"Tiddler8")).toEqual("");
// Non-existent subtiddler of a plugin
expect(parseAndGetSource("$:/ShadowPlugin","text",null,"MyMissingTiddler")).toEqual(null);
// Plain text tiddler
expect(parseAndGetSource("TiddlerNine")).toEqual(undefined);
});
});
})();

View File

@@ -220,6 +220,7 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
wiki.addTiddler({
title: "Brownies",
text: "//This is a sample shopping list item for the [[Shopping List Example]]//",
description: "A square of rich chocolate cake",
tags: ["shopping","food"],
price: "4.99",
quantity: "1"
@@ -228,6 +229,7 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
title: "Chick Peas",
text: "//This is a sample shopping list item for the [[Shopping List Example]]//",
tags: ["shopping","food"],
description: "a round yellow seed",
price: "1.32",
quantity: "5"
});
@@ -242,6 +244,7 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
title: "Rice Pudding",
price: "2.66",
quantity: "4",
description: "",
tags: ["shopping", "dairy"],
text: "//This is a sample shopping list item for the [[Shopping List Example]]//"
});
@@ -374,6 +377,14 @@ describe("'reduce' and 'intersection' filter prefix tests", function() {
expect(wiki.filterTiddlers("[tag[cakes]] :sort:string:casesensitive[{!!title}]").join(",")).toBe("Cheesecake,Chocolate Cake,Persian love cake,Pound cake,cheesecake,chocolate cake");
expect(wiki.filterTiddlers("[tag[cakes]] :sort:string:casesensitive,reverse[{!!title}]").join(",")).toBe("chocolate cake,cheesecake,Pound cake,Persian love cake,Chocolate Cake,Cheesecake");
});
it("should handle the :map prefix", function() {
expect(wiki.filterTiddlers("[tag[shopping]] :map[get[title]]").join(",")).toBe("Brownies,Chick Peas,Milk,Rice Pudding");
expect(wiki.filterTiddlers("[tag[shopping]] :map[get[description]]").join(",")).toBe("A square of rich chocolate cake,a round yellow seed,,");
expect(wiki.filterTiddlers("[tag[shopping]] :map[get[description]else{!!title}]").join(",")).toBe("A square of rich chocolate cake,a round yellow seed,Milk,Rice Pudding");
// Return the first title from :map if the filter returns more than one result
expect(wiki.filterTiddlers("[tag[shopping]] :map[tags[]]").join(",")).toBe("shopping,shopping,shopping,shopping");
});
});
})();

View File

@@ -117,7 +117,14 @@ describe("WikiText parser tests", function() {
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ]
);
});
it("should parse comment in pragma area. Comment will be INVISIBLE", function() {
expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'aMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ]
);
});
it("should parse inline macro calls", function() {

View File

@@ -1,6 +1,6 @@
created: 20140912145537860
footer: <$button message="tm-close-tiddler">Close</$button>
modified: 20140912145537861
modified: 20210627054504823
subtitle: I'm a modal wizard
title: SampleWizard
type: text/vnd.tiddlywiki

View File

@@ -1,5 +1,5 @@
created: 20130825150000000
modified: 20190609154450433
modified: 20210714130751701
tags: [[TiddlyWiki on Node.js]]
title: TiddlerFiles
type: text/vnd.tiddlywiki
@@ -78,11 +78,22 @@ The ContentType `application/json` is used internally for these files.
Note that JSON files that do not conform to the tiddler structure will instead be imported as a single tiddler containing the JSON data.
!! TiddlyWiki HTML files
!! New JSON-based format for TiddlyWiki HTML files
TiddlyWiki HTML files contain a collection of tiddlers encoded in `<DIV>` format.
The new format for TiddlyWiki HTML files embeds the tiddlers in JSON format within a script tag:
For TiddlyWiki to import an unencrypted HTML file, it requires a `<div id="storeArea">` containing tiddler DIVs as explained above. For example:
```json
<script class="tiddlywiki-tiddler-store" type="application/json">[
{"title": "A","text": "One"},
{"title": "B","text": "Two"}
]</script>
```
!! Old DIV-based format for TiddlyWiki HTML files
TiddlyWiki Classic and TiddlyWiki 5 prior to version v5.2.0 stored tiddlers encoded in `<DIV>` format.
For TiddlyWiki to import an unencrypted DIV-based HTML file, it requires a `<div id="storeArea">` containing tiddler DIVs as explained above. For example:
```
<div id="storeArea">

View File

@@ -8,6 +8,6 @@ tags: [[Reducing Mathematics Operators]] [[Filter Operators]] [[Mathematics Oper
title: average Operator
type: text/vnd.tiddlywiki
<<.from-version "5.1.24">> See [[Mathematics Operators]] for an overview.
<<.from-version "5.2.0">> See [[Mathematics Operators]] for an overview.
<<.operator-examples "average">>

View File

@@ -0,0 +1,17 @@
caption: charcode
created: 20210622214425635
modified: 20210622214425635
op-input: ignored
op-output: a string formed from concatenating the characters specified by the numeric codes given in the operand(s)
op-parameter: numeric character code
op-parameter-name: C
op-purpose: generates string characters from their numeric character codes
tags: [[Filter Operators]]
title: charcode Operator
type: text/vnd.tiddlywiki
<<.from-version "5.2.0">>
This operator returns a string formed from concatenating the characters specified by the numeric codes given in one or more operands. It is useful for generating special characters such as tab (`charcode[9]`) or new line (`charcode[13],[10]`).
<<.operator-examples "charcode">>

View File

@@ -1,6 +1,6 @@
caption: deserializers
created: 20210506115203172
from-version: 5.1.24
from-version: 5.2.0
modified: 20210506130322593
op-input: ignored
op-output: the title of each available deserializer

View File

@@ -0,0 +1,7 @@
created: 20210622214849214
modified: 20210622214849214
tags: [[charcode Operator]] [[Operator Examples]]
title: charcode Operator (Examples)
type: text/vnd.tiddlywiki
<<.operator-example 1 "[charcode[65]match[A]]">>

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