mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-02-23 14:30:02 +00:00
Merge pull request #178 from Jermolene/widgets_redux2
Rejigged Widgets Redux - finally. Couldn't come soon enough.
This commit is contained in:
commit
bec4403e0f
6
2bld.sh
6
2bld.sh
@ -11,7 +11,7 @@ mkdir -p tmp/tw2
|
||||
node ./tiddlywiki.js \
|
||||
editions/tw5.com \
|
||||
--verbose \
|
||||
--rendertiddler TiddlyWiki2ReadMe editions/tw2/readme.md text/html \
|
||||
--new_rendertiddler TiddlyWiki2ReadMe editions/tw2/readme.md text/html \
|
||||
|| exit 1
|
||||
|
||||
# cook the TiddlyWiki 2.x.x index file
|
||||
@ -20,7 +20,7 @@ node ./tiddlywiki.js \
|
||||
editions/tw2 \
|
||||
--verbose \
|
||||
--load editions/tw2/source/tiddlywiki.com/index.html.recipe \
|
||||
--rendertiddler $:/core/templates/tiddlywiki2.template.html ./tmp/tw2/index.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki2.template.html ./tmp/tw2/index.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
opendiff tmp/tw2/index.html editions/tw2/target/index.2.6.5.html
|
||||
diff -q tmp/tw2/index.html editions/tw2/target/prebuilt.html
|
||||
|
38
bld.sh
38
bld.sh
@ -35,12 +35,12 @@ rm $TW5_BUILD_OUTPUT/static/*
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tw5.com \
|
||||
--verbose \
|
||||
--rendertiddler ReadMe ./readme.md text/html \
|
||||
--rendertiddler ContributingTemplate ./contributing.md text/html \
|
||||
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
|
||||
--rendertiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
|
||||
--rendertiddler $:/core/templates/static.template.css $TW5_BUILD_OUTPUT/static/static.css text/plain \
|
||||
--rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html $TW5_BUILD_OUTPUT/static text/plain \
|
||||
--new_rendertiddler ReadMe ./readme.md text/html \
|
||||
--new_rendertiddler ContributingTemplate ./contributing.md text/html \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/static.template.css $TW5_BUILD_OUTPUT/static/static.css text/plain \
|
||||
--new_rendertiddlers [!is[system]] $:/core/templates/static.tiddler.html $TW5_BUILD_OUTPUT/static text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Second, encrypted.html: a version of the main file encrypted with the password "password"
|
||||
@ -49,7 +49,7 @@ node ./tiddlywiki.js \
|
||||
./editions/tw5.com \
|
||||
--verbose \
|
||||
--password password \
|
||||
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Third, empty.html: empty wiki for reuse
|
||||
@ -57,7 +57,7 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/empty \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Fourth, tahoelafs.html: empty wiki with plugin for Tahoe-LAFS
|
||||
@ -65,7 +65,7 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/tahoelafs \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/tahoelafs.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/tahoelafs.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Fifth, d3demo.html: wiki to demo d3 plugin
|
||||
@ -73,9 +73,25 @@ node ./tiddlywiki.js \
|
||||
node ./tiddlywiki.js \
|
||||
./editions/d3demo \
|
||||
--verbose \
|
||||
--rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/d3demo.html text/plain \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/d3demo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Sixth, run the test edition to run the node.js tests and to generate test.html for tests in the browser
|
||||
# Sixth, codemirrordemo.html: wiki to demo codemirror plugin
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/codemirrordemo \
|
||||
--verbose \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/codemirrordemo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Seventh, codemirrordemo.html: wiki to demo codemirror plugin
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/markdowndemo \
|
||||
--verbose \
|
||||
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/markdowndemo.html text/plain \
|
||||
|| exit 1
|
||||
|
||||
# Eighth, run the test edition to run the node.js tests and to generate test.html for tests in the browser
|
||||
|
||||
./test.sh
|
||||
|
@ -107,6 +107,7 @@ children: array of further child nodes
|
||||
innerHTML: optional HTML for element
|
||||
class: class name(s)
|
||||
document: defaults to current document
|
||||
eventListeners: array of event listeners (this option won't work until $tw.utils.addEventListeners() has been loaded)
|
||||
*/
|
||||
$tw.utils.domMaker = function(tag,options) {
|
||||
var doc = options.document || document;
|
||||
@ -126,6 +127,9 @@ $tw.utils.domMaker = function(tag,options) {
|
||||
$tw.utils.each(options.attributes,function(attribute,name) {
|
||||
element.setAttribute(name,attribute);
|
||||
});
|
||||
if(options.eventListeners) {
|
||||
$tw.utils.addEventListeners(element,options.eventListeners);
|
||||
}
|
||||
return element;
|
||||
};
|
||||
|
||||
@ -227,7 +231,8 @@ $tw.utils.stringifyDate = function(value) {
|
||||
$tw.utils.pad(value.getUTCMonth() + 1) +
|
||||
$tw.utils.pad(value.getUTCDate()) +
|
||||
$tw.utils.pad(value.getUTCHours()) +
|
||||
$tw.utils.pad(value.getUTCMinutes());
|
||||
$tw.utils.pad(value.getUTCMinutes()) +
|
||||
$tw.utils.pad(value.getUTCMilliseconds(),3);
|
||||
};
|
||||
|
||||
// Parse a date from a UTC YYYYMMDDHHMMSSMMM format string
|
||||
@ -722,6 +727,7 @@ $tw.modules.define("$:/boot/tiddlerfields/created","tiddlerfield",{
|
||||
});
|
||||
$tw.modules.define("$:/boot/tiddlerfields/color","tiddlerfield",{
|
||||
name: "color",
|
||||
editTag: "input",
|
||||
editType: "color"
|
||||
});
|
||||
$tw.modules.define("$:/boot/tiddlerfields/tags","tiddlerfield",{
|
||||
|
@ -1,60 +1,8 @@
|
||||
<h1 class=''>
|
||||
Contributing to <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>
|
||||
TiddlyWiki5</a></h1><div class='tw-tiddler'>
|
||||
<div class='tw-transclude'>
|
||||
<p>
|
||||
<a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>
|
||||
TiddlyWiki5</a> welcomes contributions to its code and documentation via <a class='tw-tiddlylink tw-tiddlylink-external' href='https://github.com/Jermolene/TiddlyWiki5'>
|
||||
GitHub</a>. Please take a moment to read these notes to help make the process as smooth as possible.</p><h2 class=''>
|
||||
Bug Reports</h2><p>
|
||||
From the perspective of the developers, a bug report that says little more than "it doesn't work" can be frustrating. For effective debugging, we need as much information as possible. At a minimum, please try to include:</p><ul>
|
||||
<li>
|
||||
A descriptive title</li><li>
|
||||
A summary</li><li>
|
||||
Steps to reproduce</li><li>
|
||||
Expected behaviour</li><li>
|
||||
Context (OS, browser etc.)</li></ul><p>
|
||||
There's a lot of good material on the web about bug reports:</p><ul>
|
||||
<li>
|
||||
<a class='tw-tiddlylink tw-tiddlylink-external' href='http://mhay68.tumblr.com/post/1648223018/what-makes-a-good-bug-report'>
|
||||
http://mhay68.tumblr.com/post/1648223018/what-makes-a-good-bug-report</a></li><li>
|
||||
<a class='tw-tiddlylink tw-tiddlylink-external' href='http://www.chiark.greenend.org.uk/~sgtatham/bugs.html'>
|
||||
http://www.chiark.greenend.org.uk/~sgtatham/bugs.html</a></li></ul><h2 class=''>
|
||||
Pull Requests</h2><p>
|
||||
Like other <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/OpenSource.html'>
|
||||
OpenSource</a> projects, <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>
|
||||
TiddlyWiki5</a> needs a signed <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-missing' href='http://five.tiddlywiki.com/static/ContributorLicenseAgreement.html'>
|
||||
ContributorLicenseAgreement</a> from individual contributors before contributions of code can be accepted. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-missing' href='http://five.tiddlywiki.com/static/UnaMesa.html'>
|
||||
UnaMesa</a> Association (the legal entity that owns <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki.html'>
|
||||
TiddlyWiki</a> on behalf of the community).</p><ul>
|
||||
<li>
|
||||
For individuals use: <a class='tw-tiddlylink tw-tiddlylink-external' href='https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md'>
|
||||
CLA-individual</a></li><li>
|
||||
For entities use: <a class='tw-tiddlylink tw-tiddlylink-external' href='https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md'>
|
||||
CLA-entity</a></li></ul><p>
|
||||
<em>
|
||||
This is a first pass at a CLA for <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki.html'>
|
||||
TiddlyWiki</a>. Please let us know if we missed something important. If we do have to make essential changes to the CLA, there is a possibility that all contributors will need to sign it again</em></p><h3 class=''>
|
||||
How to sign the CLA</h3><pre>
|
||||
git clone https://github.com/Jermolene/TiddlyWiki5.git TiddlyWiki5
|
||||
<h1 class=''>Contributing to <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>TiddlyWiki5</a></h1><p><a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>TiddlyWiki5</a> welcomes contributions to its code and documentation via <a href='https://github.com/Jermolene/TiddlyWiki5' target='_blank'>GitHub</a>. Please take a moment to read these notes to help make the process as smooth as possible.</p><h2 class=''>Bug Reports</h2><p>From the perspective of the developers, a bug report that says little more than "it doesn't work" can be frustrating. For effective debugging, we need as much information as possible. At a minimum, please try to include:</p><ul><li>A descriptive title</li><li>A summary</li><li>Steps to reproduce</li><li>Expected behaviour</li><li>Context (OS, browser etc.)</li></ul><p>There's a lot of good material on the web about bug reports:</p><ul><li><a href='http://mhay68.tumblr.com/post/1648223018/what-makes-a-good-bug-report' target='_blank'>http://mhay68.tumblr.com/post/1648223018/what-makes-a-good-bug-report</a></li><li><a href='http://www.chiark.greenend.org.uk/~sgtatham/bugs.html' target='_blank'>http://www.chiark.greenend.org.uk/~sgtatham/bugs.html</a></li></ul><h2 class=''>Pull Requests</h2><p>Like other <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/OpenSource.html'>OpenSource</a> projects, <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>TiddlyWiki5</a> needs a signed contributor license agreement from individual contributors before contributions of code can be accepted. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the <a class=' tw-tiddlylink tw-tiddlylink-missing' href='http://five.tiddlywiki.com/static/UnaMesa.html'>UnaMesa</a> Association (the legal entity that owns <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki.html'>TiddlyWiki</a> on behalf of the community).</p><ul><li>For individuals use: <a href='https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md' target='_blank'>CLA-individual</a></li><li>For entities use: <a href='https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md' target='_blank'>CLA-entity</a></li></ul><p><em>This is a first pass at a CLA for <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki.html'>TiddlyWiki</a>. Please let us know if we missed something important. If we do have to make essential changes to the CLA, there is a possibility that all contributors will need to sign it again</em></p><h3 class=''>How to sign the CLA</h3><pre>git clone https://github.com/Jermolene/TiddlyWiki5.git TiddlyWiki5
|
||||
cd TiddlyWiki5
|
||||
git checkout -b sign-cla</pre><p>
|
||||
<strong>
|
||||
Add your name and the date to cla-individual.md or cla-entity.md</strong>. Date format (YYYY/MM/DD)
|
||||
eg: <code>
|
||||
Jeremy Ruston, @Jermolene, 2011/11/22</code></p><pre>
|
||||
git add .
|
||||
git checkout -b sign-cla</pre><p><strong>Add your name and the date to cla-individual.md or cla-entity.md</strong>. Date format (YYYY/MM/DD)
|
||||
eg: <code>Jeremy Ruston, @Jermolene, 2011/11/22</code></p><pre>git add .
|
||||
git commit -m "sign contributor license agreement"
|
||||
git push origin sign-cla</pre><p>
|
||||
<strong>
|
||||
Go to your github repo and create a pull request.</strong></p><p>
|
||||
<strong>
|
||||
Thank you!</strong></p><h4 class=''>
|
||||
Attribution</h4><p>
|
||||
The CLA documents used for this project where created using <a class='tw-tiddlylink tw-tiddlylink-external' href='http://www.harmonyagreements.org'>
|
||||
Harmony Project Templates</a>. "HA-CLA-I-LIST Version 1.0" for "CLA-individual" and "HA-CLA-E-LIST Version 1.0" for "CLA-entity"
|
||||
</p></div></div><p>
|
||||
<em>
|
||||
This file was automatically generated by <a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>
|
||||
TiddlyWiki5</a></em>
|
||||
git push origin sign-cla</pre><p><strong>Go to your github repo and create a pull request.</strong></p><p><strong>Thank you!</strong></p><h4 class=''>Attribution</h4><p>The CLA documents used for this project where created using <a href='http://www.harmonyagreements.org' target='_blank'>Harmony Project Templates</a>. "HA-CLA-I-LIST Version 1.0" for "CLA-individual" and "HA-CLA-E-LIST Version 1.0" for "CLA-entity"
|
||||
</p><p><em>This file was automatically generated by <a class=' tw-tiddlylink tw-tiddlylink-resolves' href='http://five.tiddlywiki.com/static/TiddlyWiki5.html'>TiddlyWiki5</a></em>
|
||||
</p>
|
6
core/modules/commands/rendertiddler.js → core/modules/commands/new_rendertiddler.js
Normal file → Executable file
6
core/modules/commands/rendertiddler.js → core/modules/commands/new_rendertiddler.js
Normal file → Executable file
@ -1,5 +1,5 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/rendertiddler.js
|
||||
title: $:/core/modules/commands/new_rendertiddler.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
@ -13,7 +13,7 @@ Command to render a tiddler and save it to a file
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "rendertiddler",
|
||||
name: "new_rendertiddler",
|
||||
synchronous: false
|
||||
};
|
||||
|
||||
@ -33,7 +33,7 @@ Command.prototype.execute = function() {
|
||||
title = this.params[0],
|
||||
filename = this.params[1],
|
||||
type = this.params[2] || "text/html";
|
||||
fs.writeFile(filename,this.commander.wiki.renderTiddler(type,title),"utf8",function(err) {
|
||||
fs.writeFile(filename,this.commander.wiki.new_renderTiddler(type,title),"utf8",function(err) {
|
||||
self.callback(err);
|
||||
});
|
||||
return null;
|
@ -1,5 +1,5 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/rendertiddlers.js
|
||||
title: $:/core/modules/commands/new_rendertiddlers.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
@ -12,8 +12,10 @@ Command to render several tiddlers to a folder of files
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
exports.info = {
|
||||
name: "rendertiddlers",
|
||||
name: "new_rendertiddlers",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
@ -36,13 +38,12 @@ Command.prototype.execute = function() {
|
||||
pathname = this.params[2],
|
||||
type = this.params[3] || "text/html",
|
||||
extension = this.params[4] || ".html",
|
||||
parser = wiki.parseTiddler(template),
|
||||
tiddlers = wiki.filterTiddlers(filter);
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var renderTree = new $tw.WikiRenderTree(parser,{wiki: wiki, context: {tiddlerTitle: title}, document: $tw.document});
|
||||
renderTree.execute();
|
||||
var parser = wiki.new_parseTiddler(template),
|
||||
widgetNode = wiki.makeWidget(parser,{variables: {currentTiddler: title}});
|
||||
var container = $tw.document.createElement("div");
|
||||
renderTree.renderInDom(container);
|
||||
widgetNode.render(container,null);
|
||||
var text = type === "text/html" ? container.innerHTML : container.textContent;
|
||||
fs.writeFileSync(path.resolve(pathname,encodeURIComponent(title) + extension),text,"utf8");
|
||||
});
|
@ -147,7 +147,7 @@ var Command = function(params,commander,callback) {
|
||||
path: /^\/$/,
|
||||
handler: function(request,response,state) {
|
||||
response.writeHead(200, {"Content-Type": state.server.get("serveType")});
|
||||
var text = state.wiki.renderTiddler(state.server.get("renderType"),state.server.get("rootTiddler"));
|
||||
var text = state.wiki.new_renderTiddler(state.server.get("renderType"),state.server.get("rootTiddler"));
|
||||
response.end(text,"utf8");
|
||||
}
|
||||
});
|
||||
|
39
core/modules/filters/modules.js
Normal file
39
core/modules/filters/modules.js
Normal file
@ -0,0 +1,39 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/modules.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning the titles of the modules of a given type in this wiki
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.modules = function(source,operator,options) {
|
||||
var results = [],
|
||||
pushModules = function(type) {
|
||||
$tw.utils.each($tw.modules.types[type],function(moduleInfo,moduleName) {
|
||||
results.push(moduleName);
|
||||
});
|
||||
};
|
||||
// Iterate through the source tiddlers
|
||||
if($tw.utils.isArray(source)) {
|
||||
$tw.utils.each(source,function(title) {
|
||||
pushModules(title);
|
||||
});
|
||||
} else {
|
||||
$tw.utils.each(source,function(element,title) {
|
||||
pushModules(title);
|
||||
});
|
||||
}
|
||||
results.sort();
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
27
core/modules/filters/moduletypes.js
Normal file
27
core/modules/filters/moduletypes.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/moduletypes.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning the names of the module types in this wiki
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.moduletypes = function(source,operator,options) {
|
||||
var results = [];
|
||||
$tw.utils.each($tw.modules.types,function(moduleInfo,type) {
|
||||
results.push(type);
|
||||
});
|
||||
results.sort();
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
30
core/modules/macros/changecount.js
Normal file
30
core/modules/macros/changecount.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*\
|
||||
title: $:/core/modules/macros/changecount.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
|
||||
Macro to return the changecount for the current tiddler
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "changecount";
|
||||
|
||||
exports.params = [];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function() {
|
||||
return this.wiki.getChangeCount(this.getVariable("currentTiddler")) + "";
|
||||
};
|
||||
|
||||
})();
|
44
core/modules/macros/makedatauri.js
Normal file
44
core/modules/macros/makedatauri.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*\
|
||||
title: $:/core/modules/macros/makedatauri.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
|
||||
Macro to convert the content of a tiddler to a data URI
|
||||
|
||||
<<makedatauri text:"Text to be converted" type:"text/vnd.tiddlywiki">>
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "makedatauri";
|
||||
|
||||
exports.params = [
|
||||
{name: "text"},
|
||||
{name: "type"}
|
||||
];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function(text,type) {
|
||||
type = type || "text/vnd.tiddlywiki";
|
||||
var typeInfo = $tw.config.contentTypeInfo[type] || $tw.config.contentTypeInfo["text/plain"],
|
||||
isBase64 = typeInfo.encoding === "base64",
|
||||
parts = [];
|
||||
parts.push("data:");
|
||||
parts.push(type);
|
||||
parts.push(isBase64 ? ";base64" : "");
|
||||
parts.push(",");
|
||||
parts.push(isBase64 ? text : encodeURIComponent(text));
|
||||
return parts.join("");
|
||||
};
|
||||
|
||||
})();
|
32
core/modules/macros/qualify.js
Normal file
32
core/modules/macros/qualify.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*\
|
||||
title: $:/core/modules/macros/qualify.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
|
||||
Macro to qualify a state tiddler title according
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "qualify";
|
||||
|
||||
exports.params = [
|
||||
{name: "title"}
|
||||
];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function(title) {
|
||||
return title + "-" + this.getStateQualifier();
|
||||
};
|
||||
|
||||
})();
|
30
core/modules/macros/version.js
Normal file
30
core/modules/macros/version.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*\
|
||||
title: $:/core/modules/macros/version.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
|
||||
Macro to return the TiddlyWiki core version number
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "version";
|
||||
|
||||
exports.params = [];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function() {
|
||||
return $tw.version;
|
||||
};
|
||||
|
||||
})();
|
78
core/modules/new_widgets/browse.js
Normal file
78
core/modules/new_widgets/browse.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/browse.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Browse widget for browsing for files to import
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var BrowseWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
BrowseWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
BrowseWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var domNode = this.document.createElement("input");
|
||||
domNode.setAttribute("type","file");
|
||||
domNode.setAttribute("multiple","multiple");
|
||||
// Add a click event handler
|
||||
domNode.addEventListener("change",function (event) {
|
||||
self.wiki.readFiles(event.target.files,function(tiddlerFields) {
|
||||
self.dispatchEvent({type: "tw-import-tiddlers", param: JSON.stringify([tiddlerFields])});
|
||||
});
|
||||
return false;
|
||||
},false);
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
BrowseWidget.prototype.execute = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
BrowseWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
BrowseWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.browse = BrowseWidget;
|
||||
|
||||
})();
|
151
core/modules/new_widgets/button.js
Normal file
151
core/modules/new_widgets/button.js
Normal file
@ -0,0 +1,151 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/button.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Button widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var ButtonWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ButtonWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var domNode = this.document.createElement("button");
|
||||
// Assign classes
|
||||
var classes = this["class"].split(" ") || [];
|
||||
if(this.selectedClass) {
|
||||
if(this.set && this.setTo && this.isSelected()) {
|
||||
$tw.utils.pushTop(classes,this.selectedClass.split(" "));
|
||||
}
|
||||
if(this.popup && this.isPoppedUp()) {
|
||||
$tw.utils.pushTop(classes,this.selectedClass.split(" "));
|
||||
}
|
||||
}
|
||||
domNode.className = classes.join(" ");
|
||||
// Assign classes
|
||||
if(this.style) {
|
||||
domNode.setAttribute("style",this.style);
|
||||
}
|
||||
// Add a click event handler
|
||||
domNode.addEventListener("click",function (event) {
|
||||
var handled = false;
|
||||
if(self.message) {
|
||||
self.dispatchMessage(event);
|
||||
handled = true;
|
||||
}
|
||||
if(self.popup) {
|
||||
self.triggerPopup(event);
|
||||
handled = true;
|
||||
}
|
||||
if(self.set) {
|
||||
self.setTiddler();
|
||||
handled = true;
|
||||
}
|
||||
if(handled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
return handled;
|
||||
},false);
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.isSelected = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.set);
|
||||
return tiddler ? tiddler.fields.text === this.setTo : this.defaultSetValue === this.setTo;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.isPoppedUp = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.popup);
|
||||
var result = tiddler && tiddler.fields.text ? $tw.popup.readPopupState(this.popup,tiddler.fields.text) : false;
|
||||
return result;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.dispatchMessage = function(event) {
|
||||
this.dispatchEvent({type: this.message, param: this.param, tiddlerTitle: this.getVariable("currentTiddler")});
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.triggerPopup = function(event) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.domNodes[0],
|
||||
title: this.popup,
|
||||
wiki: this.wiki
|
||||
});
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.setTiddler = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.set);
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: this.set, text: this.setTo}));
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ButtonWidget.prototype.execute = function() {
|
||||
// Get attributes
|
||||
this.message = this.getAttribute("message");
|
||||
this.param = this.getAttribute("param");
|
||||
this.set = this.getAttribute("set");
|
||||
this.setTo = this.getAttribute("setTo");
|
||||
this.popup = this.getAttribute("popup");
|
||||
this.hover = this.getAttribute("hover");
|
||||
this["class"] = this.getAttribute("class","");
|
||||
this.style = this.getAttribute("style");
|
||||
this.selectedClass = this.getAttribute("selectedClass");
|
||||
this.defaultSetValue = this.getAttribute("default");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup])) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
ButtonWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.button = ButtonWidget;
|
||||
|
||||
})();
|
118
core/modules/new_widgets/checkbox.js
Normal file
118
core/modules/new_widgets/checkbox.js
Normal file
@ -0,0 +1,118 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/checkbox.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Checkbox widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var CheckboxWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
CheckboxWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
CheckboxWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Create our elements
|
||||
this.labelDomNode = this.document.createElement("label");
|
||||
this.inputDomNode = this.document.createElement("input");
|
||||
this.inputDomNode.setAttribute("type","checkbox");
|
||||
if(this.getValue()) {
|
||||
this.inputDomNode.setAttribute("checked","true");
|
||||
}
|
||||
this.labelDomNode.appendChild(this.inputDomNode);
|
||||
this.spanDomNode = this.document.createElement("span");
|
||||
this.labelDomNode.appendChild(this.spanDomNode);
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
||||
]);
|
||||
// Insert the label into the DOM and render any children
|
||||
parent.insertBefore(this.labelDomNode,nextSibling);
|
||||
this.renderChildren(this.spanDomNode,null);
|
||||
this.domNodes.push(this.labelDomNode);
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.getValue = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.checkboxTitle);
|
||||
return tiddler ? tiddler.hasTag(this.checkboxTag) : false;
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.handleChangeEvent = function(event) {
|
||||
var checked = this.inputDomNode.checked,
|
||||
tiddler = this.wiki.getTiddler(this.checkboxTitle);
|
||||
if(tiddler && tiddler.hasTag(this.checkboxTag) !== checked) {
|
||||
var newTags = tiddler.fields.tags.slice(0),
|
||||
pos = newTags.indexOf(this.checkboxTag);
|
||||
if(pos !== -1) {
|
||||
newTags.splice(pos,1);
|
||||
}
|
||||
if(checked) {
|
||||
newTags.push(this.checkboxTag);
|
||||
}
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,{tags: newTags},this.wiki.getModificationFields()));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
CheckboxWidget.prototype.execute = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.checkboxTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.checkboxTag = this.getAttribute("tag");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.tag || changedAttributes["class"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
var refreshed = false;
|
||||
if(changedTiddlers[this.checkboxTitle]) {
|
||||
this.inputDomNode.checked = this.getValue();
|
||||
refreshed = true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers) || refreshed;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
CheckboxWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.checkbox = CheckboxWidget;
|
||||
|
||||
})();
|
81
core/modules/new_widgets/count.js
Normal file
81
core/modules/new_widgets/count.js
Normal file
@ -0,0 +1,81 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/count.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Count widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var CountWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
CountWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
CountWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.currentCount);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
CountWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.filter = this.getAttribute("filter");
|
||||
// Execute the filter
|
||||
if(this.filter) {
|
||||
this.currentCount = this.wiki.filterTiddlers(this.filter,this.getVariable("currentTiddler")).length;
|
||||
} else {
|
||||
this.currentCount = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
CountWidget.prototype.refresh = function(changedTiddlers) {
|
||||
// Re-execute the filter to get the count
|
||||
var oldCount = this.currentCount;
|
||||
this.execute();
|
||||
if(this.currentCount !== oldCount) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
CountWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.count = CountWidget;
|
||||
|
||||
})();
|
190
core/modules/new_widgets/dropzone.js
Normal file
190
core/modules/new_widgets/dropzone.js
Normal file
@ -0,0 +1,190 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/dropzone.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Dropzone widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var DropZoneWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
DropZoneWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
DropZoneWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var domNode = this.document.createElement("div");
|
||||
domNode.className = "tw-dropzone";
|
||||
// Add event handlers
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
||||
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"},
|
||||
{name: "paste", handlerObject: this, handlerMethod: "handlePasteEvent"}
|
||||
]);
|
||||
domNode.addEventListener("click",function (event) {
|
||||
},false);
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDragEnterEvent = function(event) {
|
||||
// We count enter/leave events
|
||||
this.dragEnterCount = (this.dragEnterCount || 0) + 1;
|
||||
// If we're entering for the first time we need to apply highlighting
|
||||
if(this.dragEnterCount === 1) {
|
||||
$tw.utils.addClass(this.domNodes[0],"tw-dragover");
|
||||
}
|
||||
// Tell the browser that we're ready to handle the drop
|
||||
event.preventDefault();
|
||||
// Tell the browser not to ripple the drag up to any parent drop handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDragOverEvent = function(event) {
|
||||
// Tell the browser that we're still interested in the drop
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "copy"; // Explicitly show this is a copy
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDragLeaveEvent = function(event) {
|
||||
// Reduce the enter count
|
||||
this.dragEnterCount = (this.dragEnterCount || 0) - 1;
|
||||
// Remove highlighting if we're leaving externally
|
||||
if(this.dragEnterCount <= 0) {
|
||||
$tw.utils.removeClass(this.domNodes[0],"tw-dragover");
|
||||
}
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDropEvent = function(event) {
|
||||
var self = this,
|
||||
dataTransfer = event.dataTransfer;
|
||||
// Reset the enter count
|
||||
this.dragEnterCount = 0;
|
||||
// Remove highlighting
|
||||
$tw.utils.removeClass(this.domNodes[0],"tw-dragover");
|
||||
// Try to import the various data types we understand
|
||||
this.importData(dataTransfer);
|
||||
// Import any files in the drop
|
||||
this.wiki.readFiles(dataTransfer.files,function(tiddlerFields) {
|
||||
self.dispatchEvent({type: "tw-import-tiddlers", param: JSON.stringify([tiddlerFields])});
|
||||
});
|
||||
// Tell the browser that we handled the drop
|
||||
event.preventDefault();
|
||||
// Stop the drop ripple up to any parent handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.importData = function(dataTransfer) {
|
||||
for(var t=0; t<this.importDataTypes.length; t++) {
|
||||
var dataType = this.importDataTypes[t];
|
||||
var data = dataTransfer.getData(dataType.type);
|
||||
if(data !== "") {
|
||||
var tiddlerFields = dataType.convertToFields(data);
|
||||
if(!tiddlerFields.title) {
|
||||
tiddlerFields.title = this.generateTitle("Untitled");
|
||||
}
|
||||
this.dispatchEvent({type: "tw-import-tiddlers", param: JSON.stringify([tiddlerFields])});
|
||||
return;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.importDataTypes = [
|
||||
{type: "text/vnd.tiddler", convertToFields: function(data) {
|
||||
return JSON.parse(data);
|
||||
}},
|
||||
{type: "text/plain", convertToFields: function(data) {
|
||||
return {
|
||||
text: data
|
||||
};
|
||||
}},
|
||||
{type: "text/uri-list", convertToFields: function(data) {
|
||||
return {
|
||||
text: data
|
||||
};
|
||||
}}
|
||||
];
|
||||
|
||||
DropZoneWidget.prototype.handlePasteEvent = function(event) {
|
||||
// Let the browser handle it if we're in a textarea or input box
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1) {
|
||||
var self = this,
|
||||
items = event.clipboardData.items;
|
||||
// Enumerate the clipboard items
|
||||
for(var t = 0; t<items.length; t++) {
|
||||
var item = items[t];
|
||||
if(item.kind === "file") {
|
||||
// Import any files
|
||||
this.wiki.readFile(item.getAsFile(),function(tiddlerFields) {
|
||||
self.dispatchEvent({type: "tw-import-tiddlers", param: JSON.stringify([tiddlerFields])});
|
||||
});
|
||||
} else if(item.kind === "string") {
|
||||
// Create tiddlers from string items
|
||||
item.getAsString(function(str) {
|
||||
var tiddlerFields = {
|
||||
title: self.wiki.generateNewTitle("Untitled"),
|
||||
text: str
|
||||
};
|
||||
self.dispatchEvent({type: "tw-import-tiddlers", param: JSON.stringify([tiddlerFields])});
|
||||
});
|
||||
}
|
||||
}
|
||||
// Tell the browser that we've handled the paste
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
DropZoneWidget.prototype.execute = function() {
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DropZoneWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
DropZoneWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.dropzone = DropZoneWidget;
|
||||
|
||||
})();
|
312
core/modules/new_widgets/edit-bitmap.js
Normal file
312
core/modules/new_widgets/edit-bitmap.js
Normal file
@ -0,0 +1,312 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/edit-bitmap.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Edit-bitmap widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Default image sizes
|
||||
var DEFAULT_IMAGE_WIDTH = 300,
|
||||
DEFAULT_IMAGE_HEIGHT = 185;
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EditBitmapWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EditBitmapWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EditBitmapWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Create our element
|
||||
this.canvasDomNode = $tw.utils.domMaker("canvas",{
|
||||
document: this.document,
|
||||
"class":"tw-edit-bitmapeditor",
|
||||
eventListeners: [{
|
||||
name: "touchstart", handlerObject: this, handlerMethod: "handleTouchStartEvent"
|
||||
},{
|
||||
name: "touchmove", handlerObject: this, handlerMethod: "handleTouchMoveEvent"
|
||||
},{
|
||||
name: "touchend", handlerObject: this, handlerMethod: "handleTouchEndEvent"
|
||||
},{
|
||||
name: "mousedown", handlerObject: this, handlerMethod: "handleMouseDownEvent"
|
||||
},{
|
||||
name: "mousemove", handlerObject: this, handlerMethod: "handleMouseMoveEvent"
|
||||
},{
|
||||
name: "mouseup", handlerObject: this, handlerMethod: "handleMouseUpEvent"
|
||||
}]
|
||||
});
|
||||
this.widthDomNode = $tw.utils.domMaker("input",{
|
||||
document: this.document,
|
||||
"class":"tw-edit-bitmapeditor-width",
|
||||
eventListeners: [{
|
||||
name: "change", handlerObject: this, handlerMethod: "handleWidthChangeEvent"
|
||||
}]
|
||||
});
|
||||
this.heightDomNode = $tw.utils.domMaker("input",{
|
||||
document: this.document,
|
||||
"class":"tw-edit-bitmapeditor-height",
|
||||
eventListeners: [{
|
||||
name: "change", handlerObject: this, handlerMethod: "handleHeightChangeEvent"
|
||||
}]
|
||||
});
|
||||
// Insert the elements into the DOM
|
||||
parent.insertBefore(this.canvasDomNode,nextSibling);
|
||||
parent.insertBefore(this.widthDomNode,nextSibling);
|
||||
parent.insertBefore(this.heightDomNode,nextSibling);
|
||||
this.domNodes.push(this.canvasDomNode,this.widthDomNode,this.heightDomNode);
|
||||
// Load the image into the canvas
|
||||
this.loadCanvas();
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EditBitmapWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
};
|
||||
|
||||
/*
|
||||
Note that the bitmap editor intentionally doesn't try to refresh itself because it would be confusing to have the image changing spontaneously while editting it
|
||||
*/
|
||||
EditBitmapWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
EditBitmapWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.loadCanvas = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle),
|
||||
currImage = new Image();
|
||||
// Set up event handlers for loading the image
|
||||
var self = this;
|
||||
currImage.onload = function() {
|
||||
// Copy the image to the on-screen canvas
|
||||
self.initCanvas(self.canvasDomNode,currImage.width,currImage.height,currImage);
|
||||
// And also copy the current bitmap to the off-screen canvas
|
||||
self.currCanvas = self.document.createElement("canvas");
|
||||
self.initCanvas(self.currCanvas,currImage.width,currImage.height,currImage);
|
||||
// Set the width and height input boxes
|
||||
self.updateSize();
|
||||
};
|
||||
currImage.onerror = function() {
|
||||
// Set the on-screen canvas size and clear it
|
||||
self.initCanvas(self.canvasDomNode,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
|
||||
// Set the off-screen canvas size and clear it
|
||||
self.currCanvas = self.document.createElement("canvas");
|
||||
self.initCanvas(self.currCanvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
|
||||
// Set the width and height input boxes
|
||||
self.updateSize();
|
||||
}
|
||||
// Get the current bitmap into an image object
|
||||
currImage.src = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.initCanvas = function(canvas,width,height,image) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
if(image) {
|
||||
ctx.drawImage(image,0,0);
|
||||
} else {
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.fillRect(0,0,canvas.width,canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Update the input boxes with the actual size of the canvas
|
||||
*/
|
||||
EditBitmapWidget.prototype.updateSize = function() {
|
||||
this.widthDomNode.value = this.currCanvas.width;
|
||||
this.heightDomNode.value = this.currCanvas.height;
|
||||
};
|
||||
|
||||
/*
|
||||
** Change the size of the canvas, preserving the current image
|
||||
*/
|
||||
EditBitmapWidget.prototype.changeCanvasSize = function(newWidth,newHeight) {
|
||||
// Create and size a new canvas
|
||||
var newCanvas = this.document.createElement("canvas");
|
||||
this.initCanvas(newCanvas,newWidth,newHeight);
|
||||
// Copy the old image
|
||||
var ctx = newCanvas.getContext("2d");
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
// Set the new canvas as the current one
|
||||
this.currCanvas = newCanvas;
|
||||
// Set the size of the onscreen canvas
|
||||
this.canvasDomNode.width = newWidth;
|
||||
this.canvasDomNode.height = newHeight;
|
||||
// Paint the onscreen canvas with the offscreen canvas
|
||||
ctx = this.canvasDomNode.getContext("2d");
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleWidthChangeEvent = function(event) {
|
||||
// Get the new width
|
||||
var newWidth = parseInt(this.widthDomNode.value,10);
|
||||
// Update if necessary
|
||||
if(newWidth > 0 && newWidth !== this.currCanvas.width) {
|
||||
this.changeCanvasSize(newWidth,this.currCanvas.height);
|
||||
}
|
||||
// Update the input controls
|
||||
this.updateSize();
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleHeightChangeEvent = function(event) {
|
||||
// Get the new width
|
||||
var newHeight = parseInt(this.heightDomNode.value,10);
|
||||
// Update if necessary
|
||||
if(newHeight > 0 && newHeight !== this.currCanvas.height) {
|
||||
this.changeCanvasSize(this.currCanvas.width,newHeight);
|
||||
}
|
||||
// Update the input controls
|
||||
this.updateSize();
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleTouchStartEvent = function(event) {
|
||||
this.brushDown = true;
|
||||
this.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleTouchMoveEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.strokeMove(event.touches[0].clientX,event.touches[0].clientY);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleTouchEndEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.brushDown = false;
|
||||
this.strokeEnd();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleMouseDownEvent = function(event) {
|
||||
this.strokeStart(event.clientX,event.clientY);
|
||||
this.brushDown = true;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleMouseMoveEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.strokeMove(event.clientX,event.clientY);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.handleMouseUpEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.brushDown = false;
|
||||
this.strokeEnd();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.adjustCoordinates = function(x,y) {
|
||||
var canvasRect = this.canvasDomNode.getBoundingClientRect(),
|
||||
scale = this.canvasDomNode.width/canvasRect.width;
|
||||
return {x: (x - canvasRect.left) * scale, y: (y - canvasRect.top) * scale};
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.strokeStart = function(x,y) {
|
||||
// Start off a new stroke
|
||||
this.stroke = [this.adjustCoordinates(x,y)];
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.strokeMove = function(x,y) {
|
||||
var ctx = this.canvasDomNode.getContext("2d"),
|
||||
t;
|
||||
// Add the new position to the end of the stroke
|
||||
this.stroke.push(this.adjustCoordinates(x,y));
|
||||
// Redraw the previous image
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
// Render the stroke
|
||||
ctx.strokeStyle = "#ff0";
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.stroke[0].x,this.stroke[0].y);
|
||||
for(t=1; t<this.stroke.length-1; t++) {
|
||||
var s1 = this.stroke[t],
|
||||
s2 = this.stroke[t-1],
|
||||
tx = (s1.x + s2.x)/2,
|
||||
ty = (s1.y + s2.y)/2;
|
||||
ctx.quadraticCurveTo(s2.x,s2.y,tx,ty);
|
||||
}
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.strokeEnd = function() {
|
||||
// Copy the bitmap to the off-screen canvas
|
||||
var ctx = this.currCanvas.getContext("2d");
|
||||
ctx.drawImage(this.canvasDomNode,0,0);
|
||||
// Save the image into the tiddler
|
||||
this.saveChanges();
|
||||
};
|
||||
|
||||
EditBitmapWidget.prototype.saveChanges = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle);
|
||||
if(tiddler) {
|
||||
// data URIs look like "data:<type>;base64,<text>"
|
||||
var dataURL = this.canvasDomNode.toDataURL(tiddler.fields.type,1.0),
|
||||
posColon = dataURL.indexOf(":"),
|
||||
posSemiColon = dataURL.indexOf(";"),
|
||||
posComma = dataURL.indexOf(","),
|
||||
type = dataURL.substring(posColon+1,posSemiColon),
|
||||
text = dataURL.substring(posComma+1);
|
||||
var update = {type: type, text: text};
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
|
||||
}
|
||||
};
|
||||
|
||||
exports["edit-bitmap"] = EditBitmapWidget;
|
||||
|
||||
})();
|
259
core/modules/new_widgets/edit-text.js
Normal file
259
core/modules/new_widgets/edit-text.js
Normal file
@ -0,0 +1,259 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/edit-text.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Edit-text widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var MIN_TEXT_AREA_HEIGHT = 100; // Minimum height of textareas in pixels
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EditTextWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EditTextWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EditTextWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Create our element
|
||||
var domNode = this.document.createElement(this.editTag);
|
||||
if(this.editType) {
|
||||
domNode.setAttribute("type",this.editType);
|
||||
}
|
||||
if(this.editPlaceholder) {
|
||||
domNode.setAttribute("placeholder",this.editPlaceholder);
|
||||
}
|
||||
// Assign classes
|
||||
domNode.className = this.editClass;
|
||||
// Set the text
|
||||
var editInfo = this.getEditInfo();
|
||||
if(this.editTag === "textarea") {
|
||||
domNode.appendChild(this.document.createTextNode(editInfo.value));
|
||||
} else {
|
||||
domNode.setAttribute("value",editInfo.value)
|
||||
}
|
||||
// Add an input event handler
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
|
||||
{name: "input", handlerObject: this, handlerMethod: "handleInputEvent"}
|
||||
]);
|
||||
// Insert the element into the DOM
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.domNodes.push(domNode);
|
||||
if(this.postRender) {
|
||||
this.postRender();
|
||||
}
|
||||
// Fix height
|
||||
this.fixHeight();
|
||||
};
|
||||
|
||||
/*
|
||||
Get the tiddler being edited and current value
|
||||
*/
|
||||
EditTextWidget.prototype.getEditInfo = function() {
|
||||
// Get the edit value
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle),
|
||||
value;
|
||||
if(this.editIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(this.editTitle,this.editIndex,this.editDefault);
|
||||
} else {
|
||||
// Get the current tiddler and the field name
|
||||
if(tiddler) {
|
||||
// If we've got a tiddler, the value to display is the field string value
|
||||
value = tiddler.getFieldString(this.editField);
|
||||
} else {
|
||||
// Otherwise, we need to construct a default value for the editor
|
||||
switch(this.editField) {
|
||||
case "text":
|
||||
value = "Type the text for the tiddler '" + this.editTitle + "'";
|
||||
break;
|
||||
case "title":
|
||||
value = this.editTitle;
|
||||
break;
|
||||
default:
|
||||
value = "";
|
||||
break;
|
||||
}
|
||||
if(this.editDefault !== undefined) {
|
||||
value = this.editDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {tiddler: tiddler, value: value};
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EditTextWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.editField = this.getAttribute("field","text");
|
||||
this.editIndex = this.getAttribute("index");
|
||||
this.editDefault = this.getAttribute("default");
|
||||
this.editClass = this.getAttribute("class");
|
||||
this.editPlaceholder = this.getAttribute("placeholder");
|
||||
this.editFocusPopup = this.getAttribute("focusPopup");
|
||||
// Get the editor element tag and type
|
||||
var tag,type;
|
||||
if(this.editField === "text") {
|
||||
tag = "textarea";
|
||||
} else {
|
||||
tag = "input";
|
||||
var fieldModule = $tw.Tiddler.fieldModules[this.editField];
|
||||
if(fieldModule && fieldModule.editTag) {
|
||||
tag = fieldModule.editTag;
|
||||
}
|
||||
if(fieldModule && fieldModule.editType) {
|
||||
type = fieldModule.editType;
|
||||
}
|
||||
type = type || "text";
|
||||
}
|
||||
// Get the rest of our parameters
|
||||
this.editTag = this.getAttribute("tag",tag);
|
||||
this.editType = this.getAttribute("type",type);
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EditTextWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Completely rerender if any of our attributes have changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedTiddlers[this.editTitle]) {
|
||||
this.updateEditor(this.getEditInfo().value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Update the editor with new text. This method is separate from updateEditorDomNode()
|
||||
so that subclasses can override updateEditor() and still use updateEditorDomNode()
|
||||
*/
|
||||
EditTextWidget.prototype.updateEditor = function(text) {
|
||||
this.updateEditorDomNode(text);
|
||||
};
|
||||
|
||||
/*
|
||||
Update the editor dom node with new text
|
||||
*/
|
||||
EditTextWidget.prototype.updateEditorDomNode = function(text) {
|
||||
// Replace the edit value if the tiddler we're editing has changed
|
||||
var domNode = this.domNodes[0];
|
||||
if(!domNode.isTiddlyWikiFakeDom) {
|
||||
if(this.document.activeElement !== domNode) {
|
||||
domNode.value = text;
|
||||
}
|
||||
// Fix the height if needed
|
||||
this.fixHeight();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Fix the height of textareas to fit their content
|
||||
*/
|
||||
EditTextWidget.prototype.fixHeight = function() {
|
||||
var self = this,
|
||||
domNode = this.domNodes[0];
|
||||
if(domNode && !domNode.isTiddlyWikiFakeDom && this.editTag === "textarea") {
|
||||
$tw.utils.nextTick(function() {
|
||||
// Resize the textarea to fit its content, preserving scroll position
|
||||
var scrollPosition = $tw.utils.getScrollPosition(),
|
||||
scrollTop = scrollPosition.y;
|
||||
// Set its height to auto so that it snaps to the correct height
|
||||
domNode.style.height = "auto";
|
||||
// Calculate the revised height
|
||||
var newHeight = Math.max(domNode.scrollHeight + domNode.offsetHeight - domNode.clientHeight,MIN_TEXT_AREA_HEIGHT);
|
||||
// Only try to change the height if it has changed
|
||||
if(newHeight !== domNode.offsetHeight) {
|
||||
domNode.style.height = newHeight + "px";
|
||||
// Make sure that the dimensions of the textarea are recalculated
|
||||
$tw.utils.forceLayout(domNode);
|
||||
// Check that the scroll position is still visible before trying to scroll back to it
|
||||
scrollTop = Math.min(scrollTop,self.document.body.scrollHeight - window.innerHeight);
|
||||
window.scrollTo(scrollPosition.x,scrollTop);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a dom "input" event
|
||||
*/
|
||||
EditTextWidget.prototype.handleInputEvent = function(event) {
|
||||
this.saveChanges(this.domNodes[0].value);
|
||||
this.fixHeight();
|
||||
return true;
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleFocusEvent = function(event) {
|
||||
if(this.editFocusPopup) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.domNodes[0],
|
||||
title: this.editFocusPopup,
|
||||
wiki: this.wiki,
|
||||
force: true
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.saveChanges = function(text) {
|
||||
if(this.editField) {
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle);
|
||||
if(!tiddler) {
|
||||
tiddler = new $tw.Tiddler({title: this.editTitle});
|
||||
}
|
||||
var oldValue = tiddler.getFieldString(this.editField);
|
||||
if(text !== oldValue) {
|
||||
var update = {};
|
||||
update[this.editField] = text;
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
|
||||
}
|
||||
} else {
|
||||
var data = this.wiki.getTiddlerData(this.editTitle,{});
|
||||
if(data[this.editIndex] !== text) {
|
||||
data[this.editIndex] = text;
|
||||
this.wiki.setTiddlerData(this.editTitle,data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
EditTextWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports["edit-text"] = EditTextWidget;
|
||||
|
||||
})();
|
94
core/modules/new_widgets/edit.js
Normal file
94
core/modules/new_widgets/edit.js
Normal file
@ -0,0 +1,94 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/edit.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Edit widget is a meta-widget chooses the appropriate actual editting widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EditWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EditWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EditWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
// Mappings from content type to editor type
|
||||
// TODO: This information should be configurable/extensible
|
||||
var editorTypeMappings = {
|
||||
"text/vnd.tiddlywiki": "text",
|
||||
"image/svg+xml": "text",
|
||||
"image/jpg": "bitmap",
|
||||
"image/jpeg": "bitmap",
|
||||
"image/gif": "bitmap",
|
||||
"image/png": "bitmap"
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EditWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.editField = this.getAttribute("field","text");
|
||||
this.editIndex = this.getAttribute("index");
|
||||
this.editClass = this.getAttribute("class");
|
||||
// Get the content type of the thing we're editing
|
||||
var type;
|
||||
if(this.editField === "text") {
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle);
|
||||
if(tiddler) {
|
||||
type = tiddler.fields.type;
|
||||
}
|
||||
}
|
||||
type = type || "text/vnd.tiddlywiki";
|
||||
// Choose the appropriate edit widget
|
||||
var editorType = editorTypeMappings[type] || "text";
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets([{
|
||||
type: "edit-" + editorType,
|
||||
attributes: {
|
||||
title: {type: "string", value: this.editTitle},
|
||||
field: {type: "string", value: this.editField},
|
||||
index: {type: "string", value: this.editIndex},
|
||||
"class": {type: "string", value: this.editClass}
|
||||
}
|
||||
}]);
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EditWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.edit = EditWidget;
|
||||
|
||||
})();
|
85
core/modules/new_widgets/element.js
Executable file
85
core/modules/new_widgets/element.js
Executable file
@ -0,0 +1,85 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/element.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Element widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var ElementWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ElementWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ElementWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var domNode = this.document.createElementNS(this.namespace,this.parseTreeNode.tag);
|
||||
this.assignAttributes(domNode);
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ElementWidget.prototype.execute = function() {
|
||||
// Select the namespace for the tag
|
||||
var tagNamespaces = {
|
||||
svg: "http://www.w3.org/2000/svg",
|
||||
math: "http://www.w3.org/1998/Math/MathML",
|
||||
body: "http://www.w3.org/1999/xhtml"
|
||||
};
|
||||
this.namespace = tagNamespaces[this.parseTreeNode.tag];
|
||||
if(this.namespace) {
|
||||
this.setVariable("namespace",this.namespace);
|
||||
} else {
|
||||
this.namespace = this.getVariable("namespace",{defaultValue: "http://www.w3.org/1999/xhtml"});
|
||||
}
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ElementWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
hasChangedAttributes = $tw.utils.count(changedAttributes) > 0;
|
||||
if(hasChangedAttributes) {
|
||||
// Update our attributes
|
||||
this.assignAttributes(this.domNodes[0]);
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers) || hasChangedAttributes;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
ElementWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.element = ElementWidget;
|
||||
|
||||
})();
|
85
core/modules/new_widgets/encrypt.js
Normal file
85
core/modules/new_widgets/encrypt.js
Normal file
@ -0,0 +1,85 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/encrypt.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Encrypt widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EncryptWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EncryptWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EncryptWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.encryptedText);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EncryptWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.filter = this.getAttribute("filter","[!is[system]]");
|
||||
// Encrypt the filtered tiddlers
|
||||
var tiddlers = this.wiki.filterTiddlers(this.filter),
|
||||
json = {},
|
||||
self = this;
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var tiddler = self.wiki.getTiddler(title),
|
||||
jsonTiddler = {};
|
||||
for(var f in tiddler.fields) {
|
||||
jsonTiddler[f] = tiddler.getFieldString(f);
|
||||
}
|
||||
json[title] = jsonTiddler;
|
||||
});
|
||||
this.encryptedText = $tw.utils.htmlEncode($tw.crypto.encrypt(JSON.stringify(json)));
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EncryptWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
affectedTiddlers = this.wiki.filterTiddlers(this.filter,null,changedTiddlers);
|
||||
if(changedAttributes.filter || affectedTiddlers.length > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
EncryptWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.encrypt = EncryptWidget;
|
||||
|
||||
})();
|
62
core/modules/new_widgets/entity.js
Executable file
62
core/modules/new_widgets/entity.js
Executable file
@ -0,0 +1,62 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/entity.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
HTML entity widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var EntityWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EntityWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EntityWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode($tw.utils.entityDecode(this.parseTreeNode.entity));
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EntityWidget.prototype.execute = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EntityWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
EntityWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.entity = EntityWidget;
|
||||
|
||||
})();
|
110
core/modules/new_widgets/fieldmangler.js
Normal file
110
core/modules/new_widgets/fieldmangler.js
Normal file
@ -0,0 +1,110 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/fieldmangler.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Field mangler widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var FieldManglerWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tw-remove-field", handler: "handleRemoveFieldEvent"},
|
||||
{type: "tw-add-field", handler: "handleAddFieldEvent"},
|
||||
{type: "tw-remove-tag", handler: "handleRemoveTagEvent"},
|
||||
{type: "tw-add-tag", handler: "handleAddTagEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
FieldManglerWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
FieldManglerWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
FieldManglerWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.mangleTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
FieldManglerWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle),
|
||||
deletion = {};
|
||||
deletion[event.param] = undefined;
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,deletion));
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle);
|
||||
if(tiddler && typeof event.param === "string" && event.param !== "" && !$tw.utils.hop(tiddler.fields,event.param)) {
|
||||
var addition = {};
|
||||
addition[event.param] = "";
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,addition));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleRemoveTagEvent = function(event) {
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle);
|
||||
if(tiddler && tiddler.fields.tags) {
|
||||
var p = tiddler.fields.tags.indexOf(event.param);
|
||||
if(p !== -1) {
|
||||
var modification = {};
|
||||
modification.tags = (tiddler.fields.tags || []).slice(0);
|
||||
modification.tags.splice(p,1);
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddTagEvent = function(event) {
|
||||
var tiddler = this.wiki.getTiddler(this.mangleTitle);
|
||||
if(tiddler && typeof event.param === "string" && event.param !== "") {
|
||||
var modification = {};
|
||||
modification.tags = (tiddler.fields.tags || []).slice(0);
|
||||
$tw.utils.pushTop(modification.tags,event.param);
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.fieldmangler = FieldManglerWidget;
|
||||
|
||||
})();
|
113
core/modules/new_widgets/fields.js
Executable file
113
core/modules/new_widgets/fields.js
Executable file
@ -0,0 +1,113 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/fields.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Fields widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var FieldsWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
FieldsWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
FieldsWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
FieldsWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.template = this.getAttribute("template");
|
||||
this.exclude = this.getAttribute("exclude");
|
||||
this.stripTitlePrefix = this.getAttribute("stripTitlePrefix","no") === "yes";
|
||||
// Get the value to display
|
||||
var tiddler = this.wiki.getTiddler(this.tiddlerTitle);
|
||||
// Get the exclusion list
|
||||
var exclude;
|
||||
if(this.exclude) {
|
||||
exclude = this.exclude.split(" ");
|
||||
} else {
|
||||
exclude = ["text"];
|
||||
}
|
||||
// Compose the template
|
||||
var text = [];
|
||||
if(this.template && tiddler) {
|
||||
var fields = [];
|
||||
for(var fieldName in tiddler.fields) {
|
||||
if(exclude.indexOf(fieldName) === -1) {
|
||||
fields.push(fieldName);
|
||||
}
|
||||
}
|
||||
fields.sort();
|
||||
for(var f=0; f<fields.length; f++) {
|
||||
fieldName = fields[f];
|
||||
if(exclude.indexOf(fieldName) === -1) {
|
||||
var row = this.template,
|
||||
value = tiddler.getFieldString(fieldName);
|
||||
if(this.stripTitlePrefix && fieldName === "title") {
|
||||
var reStrip = /^\{[^\}]+\}(.+)/mg,
|
||||
reMatch = reStrip.exec(value);
|
||||
if(reMatch) {
|
||||
value = reMatch[1];
|
||||
}
|
||||
}
|
||||
row = row.replace("$name$",fieldName);
|
||||
row = row.replace("$value$",value);
|
||||
row = row.replace("$encoded_value$",$tw.utils.htmlEncode(value));
|
||||
text.push(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.text = text.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
FieldsWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.template || changedAttributes.exclude || changedAttributes.stripTitlePrefix || changedTiddlers[this.tiddlerTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
FieldsWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.fields = FieldsWidget;
|
||||
|
||||
})();
|
184
core/modules/new_widgets/link.js
Executable file
184
core/modules/new_widgets/link.js
Executable file
@ -0,0 +1,184 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/link.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Link widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var LinkWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
LinkWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LinkWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Get the value of the tw-wikilinks configuration macro
|
||||
var wikiLinksMacro = this.getVariable("tw-wikilinks"),
|
||||
useWikiLinks = wikiLinksMacro ? !(wikiLinksMacro.trim() === "no") : true;
|
||||
// Render the link if required
|
||||
if(useWikiLinks) {
|
||||
this.renderLink(parent,nextSibling);
|
||||
} else {
|
||||
// Just insert the link text
|
||||
var domNode = this.document.createElement("span");
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Create our element
|
||||
var domNode = this.document.createElement("a");
|
||||
// Assign classes
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink");
|
||||
if(this.isShadow) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-shadow");
|
||||
}
|
||||
if(this.isMissing && !this.isShadow) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-missing");
|
||||
} else {
|
||||
if(!this.isMissing) {
|
||||
$tw.utils.addClass(domNode,"tw-tiddlylink-resolves");
|
||||
}
|
||||
}
|
||||
// Set an href
|
||||
var wikiLinkTemplateMacro = this.getVariable("tw-wikilink-template"),
|
||||
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$",
|
||||
wikiLinkText = wikiLinkTemplate.replace("$uri_encoded$",encodeURIComponent(this.to));
|
||||
wikiLinkText = wikiLinkText.replace("$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
|
||||
domNode.setAttribute("href",wikiLinkText);
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
|
||||
{name: "dragstart", handlerObject: this, handlerMethod: "handleDragStartEvent"},
|
||||
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
|
||||
]);
|
||||
// Insert the link into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleClickEvent = function (event) {
|
||||
// Send the click on it's way as a navigate event
|
||||
var bounds = this.domNodes[0].getBoundingClientRect();
|
||||
this.dispatchEvent({
|
||||
type: "tw-navigate",
|
||||
navigateTo: this.to,
|
||||
navigateFromTitle: this.getVariable("currentTiddler"),
|
||||
navigateFromNode: this,
|
||||
navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
|
||||
}
|
||||
});
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleDragStartEvent = function(event) {
|
||||
if(this.to) {
|
||||
// Set the dragging class on the element being dragged
|
||||
$tw.utils.addClass(event.target,"tw-tiddlylink-dragging");
|
||||
// Create the drag image elements
|
||||
this.dragImage = this.document.createElement("div");
|
||||
this.dragImage.className = "tw-tiddler-dragger";
|
||||
var inner = this.document.createElement("div");
|
||||
inner.className = "tw-tiddler-dragger-inner";
|
||||
inner.appendChild(this.document.createTextNode(this.to));
|
||||
this.dragImage.appendChild(inner);
|
||||
this.document.body.appendChild(this.dragImage);
|
||||
// Astoundingly, we need to cover the dragger up: http://www.kryogenix.org/code/browser/custom-drag-image.html
|
||||
var bounds = this.dragImage.firstChild.getBoundingClientRect(),
|
||||
cover = this.document.createElement("div");
|
||||
cover.className = "tw-tiddler-dragger-cover";
|
||||
cover.style.left = (bounds.left - 16) + "px";
|
||||
cover.style.top = (bounds.top - 16) + "px";
|
||||
cover.style.width = (bounds.width + 32) + "px";
|
||||
cover.style.height = (bounds.height + 32) + "px";
|
||||
this.dragImage.appendChild(cover);
|
||||
// Set the data transfer properties
|
||||
var dataTransfer = event.dataTransfer;
|
||||
dataTransfer.effectAllowed = "copy";
|
||||
dataTransfer.setDragImage(this.dragImage.firstChild,-16,-16);
|
||||
dataTransfer.clearData();
|
||||
dataTransfer.setData("text/vnd.tiddler",this.wiki.getTiddlerAsJson(this.to));
|
||||
dataTransfer.setData("text/plain",this.wiki.getTiddlerText(this.to,""));
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleDragEndEvent = function(event) {
|
||||
// Remove the dragging class on the element being dragged
|
||||
$tw.utils.removeClass(event.target,"tw-tiddlylink-dragging");
|
||||
// Delete the drag image element
|
||||
if(this.dragImage) {
|
||||
this.dragImage.parentNode.removeChild(this.dragImage);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
LinkWidget.prototype.execute = function() {
|
||||
// Get the target tiddler title
|
||||
this.to = this.getAttribute("to",this.getVariable("currentTiddler"));
|
||||
// Determine the link characteristics
|
||||
this.isMissing = !this.wiki.tiddlerExists(this.to);
|
||||
this.isShadow = this.wiki.isShadowTiddler(this.to);
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.to || changedTiddlers[this.to]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
LinkWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.link = LinkWidget;
|
||||
|
||||
})();
|
87
core/modules/new_widgets/linkcatcher.js
Normal file
87
core/modules/new_widgets/linkcatcher.js
Normal file
@ -0,0 +1,87 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/linkcatcher.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Linkcatcher widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var LinkCatcherWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tw-navigate", handler: "handleNavigateEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
LinkCatcherWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LinkCatcherWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
LinkCatcherWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.catchTo = this.getAttribute("to");
|
||||
this.catchMessage = this.getAttribute("message");
|
||||
this.catchSet = this.getAttribute("set");
|
||||
this.catchSetTo = this.getAttribute("setTo");
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
LinkCatcherWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.to || changedAttributes.message || changedAttributes.set || changedAttributes.setTo) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle a tw-navigate event
|
||||
*/
|
||||
LinkCatcherWidget.prototype.handleNavigateEvent = function(event) {
|
||||
if(this.catchTo) {
|
||||
this.wiki.setTextReference(this.catchTo,event.navigateTo,this.getVariable("currentTiddler"));
|
||||
}
|
||||
if(this.catchMessage) {
|
||||
this.dispatchEvent({
|
||||
type: this.catchMessage,
|
||||
param: event.navigateTo
|
||||
});
|
||||
}
|
||||
if(this.catchSet) {
|
||||
var tiddler = this.wiki.getTiddler(this.catchSet);
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: this.catchSet, text: this.catchSetTo}));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.linkcatcher = LinkCatcherWidget;
|
||||
|
||||
})();
|
305
core/modules/new_widgets/list.js
Executable file
305
core/modules/new_widgets/list.js
Executable file
@ -0,0 +1,305 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/list.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
List and list item widgets
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
/*
|
||||
The list widget creates list element sub-widgets that reach back into the list widget for their configuration
|
||||
*/
|
||||
|
||||
var ListWidget = function(parseTreeNode,options) {
|
||||
// Initialise the storyviews if they've not been done already
|
||||
if(!this.storyViews) {
|
||||
ListWidget.prototype.storyViews = {};
|
||||
$tw.modules.applyMethods("storyview",this.storyViews);
|
||||
}
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ListWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
// Construct the storyview
|
||||
var StoryView = this.storyViews[this.storyViewName];
|
||||
this.storyview = StoryView ? new StoryView(this) : null;
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListWidget.prototype.execute = function() {
|
||||
// Get our attributes
|
||||
this.template = this.getAttribute("template");
|
||||
this.editTemplate = this.getAttribute("editTemplate");
|
||||
this.variableName = this.getAttribute("variable","currentTiddler");
|
||||
this.storyViewName = this.getAttribute("storyview");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Compose the list elements
|
||||
this.list = this.getTiddlerList();
|
||||
var members = [],
|
||||
self = this;
|
||||
// Check for an empty list
|
||||
if(this.list.length === 0) {
|
||||
members = this.getEmptyMessage();
|
||||
} else {
|
||||
$tw.utils.each(this.list,function(title,index) {
|
||||
members.push(self.makeItemTemplate(title));
|
||||
});
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(members);
|
||||
// Clear the last history
|
||||
this.history = [];
|
||||
};
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var defaultFilter = "[!is[system]sort[title]]";
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this.getVariable("currentTiddler"));
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
var emptyMessage = this.getAttribute("emptyMessage",""),
|
||||
parser = this.wiki.new_parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
if(parser) {
|
||||
return parser.tree;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compose the template for a list item
|
||||
*/
|
||||
ListWidget.prototype.makeItemTemplate = function(title) {
|
||||
// Check if the tiddler is a draft
|
||||
var tiddler = this.wiki.getTiddler(title),
|
||||
isDraft = tiddler && tiddler.hasField("draft.of"),
|
||||
template = this.template,
|
||||
templateTree;
|
||||
if(isDraft && this.editTemplate) {
|
||||
template = this.editTemplate;
|
||||
}
|
||||
// Compose the transclusion of the template
|
||||
if(template) {
|
||||
templateTree = [{type: "transclude", attributes: {tiddler: {type: "string", value: template}}}];
|
||||
} else {
|
||||
if(this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
} else {
|
||||
// Default template is a link to the title
|
||||
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||
{type: "text", text: title}
|
||||
]}]}];
|
||||
}
|
||||
}
|
||||
// Return the list item
|
||||
return {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree};
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ListWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Completely refresh if any of our attributes have changed
|
||||
if(changedAttributes.filter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
// Handle any changes to the list
|
||||
var hasChanged = this.handleListChanges(changedTiddlers);
|
||||
// Handle any changes to the history stack
|
||||
if(this.historyTitle && changedTiddlers[this.historyTitle]) {
|
||||
this.handleHistoryChanges();
|
||||
}
|
||||
return hasChanged;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle any changes to the history list
|
||||
*/
|
||||
ListWidget.prototype.handleHistoryChanges = function() {
|
||||
// Get the history data
|
||||
var newHistory = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
// Ignore any entries of the history that match the previous history
|
||||
var entry = 0;
|
||||
while(entry < newHistory.length && entry < this.history.length && newHistory[entry].title === this.history[entry].title) {
|
||||
entry++;
|
||||
}
|
||||
// Navigate forwards to each of the new tiddlers
|
||||
while(entry < newHistory.length) {
|
||||
if(this.storyview && this.storyview.navigateTo) {
|
||||
this.storyview.navigateTo(newHistory[entry]);
|
||||
}
|
||||
entry++;
|
||||
}
|
||||
// Update the history
|
||||
this.history = newHistory;
|
||||
};
|
||||
|
||||
/*
|
||||
Process any changes to the list
|
||||
*/
|
||||
ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
// Get the new list
|
||||
var prevList = this.list;
|
||||
this.list = this.getTiddlerList();
|
||||
// Check for an empty list
|
||||
if(this.list.length === 0) {
|
||||
// Check if it was empty before
|
||||
if(prevList.length === 0) {
|
||||
// If so, just refresh the empty message
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
} else {
|
||||
// Replace the previous content with the empty message
|
||||
for(t=this.children.length-1; t>=0; t--) {
|
||||
this.removeListItem(t);
|
||||
}
|
||||
var nextSibling = this.findNextSiblingDomNode();
|
||||
this.makeChildWidgets(this.getEmptyMessage());
|
||||
this.renderChildren(this.parentDomNode,nextSibling);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// If the list was empty then we need to remove the empty message
|
||||
if(prevList.length === 0) {
|
||||
this.removeChildDomNodes();
|
||||
this.children = [];
|
||||
}
|
||||
// Cycle through the list, inserting and removing list items as needed
|
||||
var hasRefreshed = false;
|
||||
for(var t=0; t<this.list.length; t++) {
|
||||
var index = this.findListItem(t,this.list[t]);
|
||||
if(index === undefined) {
|
||||
// The list item must be inserted
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// There are intervening list items that must be removed
|
||||
for(var n=index-1; n>=t; n--) {
|
||||
this.removeListItem(n);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
// Remove any left over items
|
||||
for(t=this.children.length-1; t>=this.list.length; t--) {
|
||||
this.removeListItem(t);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
return hasRefreshed;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Find the list item with a given title, starting from a specified position
|
||||
*/
|
||||
ListWidget.prototype.findListItem = function(startIndex,title) {
|
||||
while(startIndex < this.children.length) {
|
||||
if(this.children[startIndex].parseTreeNode.itemTitle === title) {
|
||||
return startIndex;
|
||||
}
|
||||
startIndex++;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Insert a new list item at the specified index
|
||||
*/
|
||||
ListWidget.prototype.insertListItem = function(index,title) {
|
||||
// Create, insert and render the new child widgets
|
||||
var widget = this.makeChildWidget(this.makeItemTemplate(title));
|
||||
widget.parentDomNode = this.parentDomNode; // Hack to enable findNextSiblingDomNode() to work
|
||||
this.children.splice(index,0,widget);
|
||||
var nextSibling = widget.findNextSiblingDomNode();
|
||||
widget.render(this.parentDomNode,nextSibling);
|
||||
// Animate the insertion if required
|
||||
if(this.storyview && this.storyview.insert) {
|
||||
this.storyview.insert(widget);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove the specified list item
|
||||
*/
|
||||
ListWidget.prototype.removeListItem = function(index) {
|
||||
var widget = this.children[index];
|
||||
// Animate the removal if required
|
||||
if(this.storyview && this.storyview.remove) {
|
||||
this.storyview.remove(widget);
|
||||
} else {
|
||||
widget.removeChildDomNodes();
|
||||
}
|
||||
// Remove the child widget
|
||||
this.children.splice(index,1);
|
||||
};
|
||||
|
||||
exports.list = ListWidget;
|
||||
|
||||
var ListItemWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ListItemWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ListItemWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListItemWidget.prototype.execute = function() {
|
||||
// Set the current list item title
|
||||
this.setVariable(this.parseTreeNode.variableName,this.parseTreeNode.itemTitle);
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ListItemWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.listitem = ListItemWidget;
|
||||
|
||||
})();
|
84
core/modules/new_widgets/macrocall.js
Normal file
84
core/modules/new_widgets/macrocall.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/macrocall.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Macrocall widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var MacroCallWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MacroCallWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MacroCallWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
MacroCallWidget.prototype.execute = function() {
|
||||
// Get the parse type if specified
|
||||
this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki");
|
||||
this.renderOutput = this.getAttribute("$output","text/html");
|
||||
// Merge together the parameters specified in the parse tree with the specified attributes
|
||||
var params = this.parseTreeNode.params ? this.parseTreeNode.params.slice(0) : [];
|
||||
$tw.utils.each(this.attributes,function(attribute,name) {
|
||||
if(name.charAt(0) !== "$") {
|
||||
params.push({name: name, value: attribute});
|
||||
}
|
||||
});
|
||||
// Get the macro value
|
||||
var text = this.getVariable(this.parseTreeNode.name || this.getAttribute("$name"),{params: params}),
|
||||
parseTreeNodes;
|
||||
// Are we rendering to HTML?
|
||||
if(this.renderOutput === "text/html") {
|
||||
// If so we'll return the parsed macro
|
||||
var parser = this.wiki.new_parseText(this.parseType,text,
|
||||
{parseAsInline: !this.parseTreeNode.isBlock});
|
||||
parseTreeNodes = parser ? parser.tree : [];
|
||||
} else {
|
||||
// Otherwise, we'll render the text
|
||||
var plainText = this.wiki.new_renderText("text/plain",this.parseType,text,{parentWidget: this});
|
||||
parseTreeNodes = [{type: "text", text: plainText}];
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(parseTreeNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
MacroCallWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
// Rerender ourselves
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.macrocall = MacroCallWidget;
|
||||
|
||||
})();
|
252
core/modules/widgets/navigator.js → core/modules/new_widgets/navigator.js
Normal file → Executable file
252
core/modules/widgets/navigator.js → core/modules/new_widgets/navigator.js
Normal file → Executable file
@ -1,9 +1,9 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/navigator.js
|
||||
title: $:/core/modules/new_widgets/navigator.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
module-type: new_widget
|
||||
|
||||
Implements the navigator widget.
|
||||
Navigator widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@ -12,51 +12,69 @@ Implements the navigator widget.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var NavigatorWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var NavigatorWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tw-navigate", handler: "handleNavigateEvent"},
|
||||
{type: "tw-edit-tiddler", handler: "handleEditTiddlerEvent"},
|
||||
{type: "tw-delete-tiddler", handler: "handleDeleteTiddlerEvent"},
|
||||
{type: "tw-save-tiddler", handler: "handleSaveTiddlerEvent"},
|
||||
{type: "tw-cancel-tiddler", handler: "handleCancelTiddlerEvent"},
|
||||
{type: "tw-close-tiddler", handler: "handleCloseTiddlerEvent"},
|
||||
{type: "tw-close-all-tiddlers", handler: "handleCloseAllTiddlersEvent"},
|
||||
{type: "tw-new-tiddler", handler: "handleNewTiddlerEvent"},
|
||||
{type: "tw-import-tiddlers", handler: "handleImportTiddlersEvent"},
|
||||
]);
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.generate = function() {
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
NavigatorWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
NavigatorWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
NavigatorWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.storyTitle = this.renderer.getAttribute("story");
|
||||
this.historyTitle = this.renderer.getAttribute("history");
|
||||
// Set the element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": "tw-navigator"
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
this.events = [
|
||||
{name: "tw-navigate", handlerObject: this, handlerMethod: "handleNavigateEvent"},
|
||||
{name: "tw-edit-tiddler", handlerObject: this, handlerMethod: "handleEditTiddlerEvent"},
|
||||
{name: "tw-delete-tiddler", handlerObject: this, handlerMethod: "handleDeleteTiddlerEvent"},
|
||||
{name: "tw-save-tiddler", handlerObject: this, handlerMethod: "handleSaveTiddlerEvent"},
|
||||
{name: "tw-cancel-tiddler", handlerObject: this, handlerMethod: "handleCancelTiddlerEvent"},
|
||||
{name: "tw-close-tiddler", handlerObject: this, handlerMethod: "handleCloseTiddlerEvent"},
|
||||
{name: "tw-close-all-tiddlers", handlerObject: this, handlerMethod: "handleCloseAllTiddlersEvent"},
|
||||
{name: "tw-new-tiddler", handlerObject: this, handlerMethod: "handleNewTiddlerEvent"}
|
||||
];
|
||||
this.storyTitle = this.getAttribute("story");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
NavigatorWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.story || changedAttributes.history) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.getStoryList = function() {
|
||||
this.storyList = this.renderer.renderTree.wiki.getTiddlerList(this.storyTitle);
|
||||
this.storyList = this.wiki.getTiddlerList(this.storyTitle);
|
||||
};
|
||||
|
||||
NavigatorWidget.prototype.saveStoryList = function() {
|
||||
var storyTiddler = this.renderer.renderTree.wiki.getTiddler(this.storyTitle);
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler({
|
||||
var storyTiddler = this.wiki.getTiddler(this.storyTitle);
|
||||
this.wiki.addTiddler(new $tw.Tiddler({
|
||||
title: this.storyTitle
|
||||
},storyTiddler,{list: this.storyList}));
|
||||
};
|
||||
@ -70,7 +88,9 @@ NavigatorWidget.prototype.findTitleInStory = function(title,defaultIndex) {
|
||||
return defaultIndex;
|
||||
};
|
||||
|
||||
// Navigate to a specified tiddler
|
||||
/*
|
||||
Handle a tw-navigate event
|
||||
*/
|
||||
NavigatorWidget.prototype.handleNavigateEvent = function(event) {
|
||||
if(this.storyTitle) {
|
||||
// Update the story tiddler if specified
|
||||
@ -89,11 +109,10 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) {
|
||||
}
|
||||
// Add a new record to the top of the history stack
|
||||
if(this.historyTitle) {
|
||||
var historyList = this.renderer.renderTree.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
var historyList = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
historyList.push({title: event.navigateTo, fromPageRect: event.navigateFromClientRect});
|
||||
this.renderer.renderTree.wiki.setTiddlerData(this.historyTitle,historyList);
|
||||
this.wiki.setTiddlerData(this.historyTitle,historyList);
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -106,7 +125,6 @@ NavigatorWidget.prototype.handleCloseTiddlerEvent = function(event) {
|
||||
this.storyList.splice(slot,1);
|
||||
this.saveStoryList();
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -114,7 +132,6 @@ NavigatorWidget.prototype.handleCloseTiddlerEvent = function(event) {
|
||||
NavigatorWidget.prototype.handleCloseAllTiddlersEvent = function(event) {
|
||||
this.storyList = [];
|
||||
this.saveStoryList();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -122,44 +139,37 @@ NavigatorWidget.prototype.handleCloseAllTiddlersEvent = function(event) {
|
||||
NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
|
||||
this.getStoryList();
|
||||
// Replace the specified tiddler with a draft in edit mode
|
||||
for(var t=0; t<this.storyList.length; t++) {
|
||||
var draftTiddler = this.getDraftTiddler(event.tiddlerTitle),
|
||||
gotOne = false;
|
||||
for(var t=this.storyList.length-1; t>=0; t--) {
|
||||
// Replace the first story instance of the original tiddler name with the draft title
|
||||
if(this.storyList[t] === event.tiddlerTitle) {
|
||||
// Compute the title for the draft
|
||||
var draftTitle = this.generateDraftTitle(event.tiddlerTitle);
|
||||
this.storyList[t] = draftTitle;
|
||||
// Get the current value of the tiddler we're editing
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
|
||||
// Save the initial value of the draft tiddler
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(
|
||||
{
|
||||
text: "Type the text for the tiddler '" + event.tiddlerTitle + "'"
|
||||
},
|
||||
tiddler,
|
||||
{
|
||||
title: draftTitle,
|
||||
"draft.title": event.tiddlerTitle,
|
||||
"draft.of": event.tiddlerTitle
|
||||
},
|
||||
this.renderer.renderTree.wiki.getModificationFields()
|
||||
));
|
||||
if(!gotOne) {
|
||||
this.storyList[t] = draftTiddler.fields.title;
|
||||
gotOne = true;
|
||||
} else {
|
||||
this.storyList.splice(t,1);
|
||||
}
|
||||
} else if(this.storyList[t] === draftTiddler.fields.title) {
|
||||
// Remove any existing references to the draft
|
||||
this.storyList.splice(t,1);
|
||||
}
|
||||
}
|
||||
this.saveStoryList();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
// Delete a tiddler
|
||||
NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
|
||||
// Get the tiddler title we're deleting
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
|
||||
// Get the tiddler we're deleting
|
||||
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
|
||||
// Check if the tiddler we're deleting is in draft mode
|
||||
if(tiddler.hasField("draft.title")) {
|
||||
// Delete the original tiddler
|
||||
this.renderer.renderTree.wiki.deleteTiddler(tiddler.fields["draft.of"]);
|
||||
this.wiki.deleteTiddler(tiddler.fields["draft.of"]);
|
||||
}
|
||||
// Delete this tiddler
|
||||
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
this.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
// Remove the closed tiddler from the story
|
||||
this.getStoryList();
|
||||
// Look for tiddler with this title to close
|
||||
@ -168,10 +178,41 @@ NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
|
||||
this.storyList.splice(slot,1);
|
||||
this.saveStoryList();
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Create/reuse the draft tiddler for a given title
|
||||
*/
|
||||
NavigatorWidget.prototype.getDraftTiddler = function(targetTitle) {
|
||||
// See if there is already a draft tiddler for this tiddler
|
||||
var drafts = [];
|
||||
this.wiki.forEachTiddler(function(title,tiddler) {
|
||||
if(tiddler.fields["draft.title"] && tiddler.fields["draft.of"] === targetTitle) {
|
||||
drafts.push(tiddler);
|
||||
}
|
||||
});
|
||||
if(drafts.length > 0) {
|
||||
return drafts[0];
|
||||
}
|
||||
// Get the current value of the tiddler we're editing
|
||||
var tiddler = this.wiki.getTiddler(targetTitle),
|
||||
draftTitle = this.generateDraftTitle(targetTitle);
|
||||
// Save the initial value of the draft tiddler
|
||||
var draftTiddler = new $tw.Tiddler(
|
||||
{text: "Type the text for the tiddler '" + targetTitle + "'"},
|
||||
tiddler,
|
||||
{
|
||||
title: draftTitle,
|
||||
"draft.title": targetTitle,
|
||||
"draft.of": targetTitle
|
||||
},
|
||||
this.wiki.getModificationFields()
|
||||
);
|
||||
this.wiki.addTiddler(draftTiddler);
|
||||
return draftTiddler;
|
||||
};
|
||||
|
||||
/*
|
||||
Generate a title for the draft of a given tiddler
|
||||
*/
|
||||
@ -180,7 +221,7 @@ NavigatorWidget.prototype.generateDraftTitle = function(title) {
|
||||
do {
|
||||
var draftTitle = "Draft " + (c ? (c + 1) + " " : "") + "of '" + title + "'";
|
||||
c++;
|
||||
} while(this.renderer.renderTree.wiki.tiddlerExists(draftTitle));
|
||||
} while(this.wiki.tiddlerExists(draftTitle));
|
||||
return draftTitle;
|
||||
};
|
||||
|
||||
@ -190,28 +231,28 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
|
||||
var storyTiddlerModified = false; // We have to special case saving the story tiddler itself
|
||||
for(var t=0; t<this.storyList.length; t++) {
|
||||
if(this.storyList[t] === event.tiddlerTitle) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
|
||||
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
|
||||
if(tiddler) {
|
||||
var draftTitle = tiddler.fields["draft.title"],
|
||||
draftOf = tiddler.fields["draft.of"];
|
||||
if(draftTitle) {
|
||||
var isRename = draftOf !== draftTitle,
|
||||
isConfirmed = true;
|
||||
if(isRename && this.renderer.renderTree.wiki.tiddlerExists(draftTitle)) {
|
||||
if(isRename && this.wiki.tiddlerExists(draftTitle)) {
|
||||
isConfirmed = confirm("Do you wish to overwrite the tiddler '" + draftTitle + "'?");
|
||||
}
|
||||
if(isConfirmed) {
|
||||
// Save the draft tiddler as the real tiddler
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(this.renderer.renderTree.wiki.getCreationFields(),tiddler,{
|
||||
this.wiki.addTiddler(new $tw.Tiddler(this.wiki.getCreationFields(),tiddler,{
|
||||
title: draftTitle,
|
||||
"draft.title": undefined,
|
||||
"draft.of": undefined
|
||||
},this.renderer.renderTree.wiki.getModificationFields()));
|
||||
},this.wiki.getModificationFields()));
|
||||
// Remove the draft tiddler
|
||||
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
this.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
// Remove the original tiddler if we're renaming it
|
||||
if(isRename) {
|
||||
this.renderer.renderTree.wiki.deleteTiddler(draftOf);
|
||||
this.wiki.deleteTiddler(draftOf);
|
||||
}
|
||||
// Make the story record point to the newly saved tiddler
|
||||
this.storyList[t] = draftTitle;
|
||||
@ -227,7 +268,6 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
|
||||
if(!storyTiddlerModified) {
|
||||
this.saveStoryList();
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -237,10 +277,10 @@ NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
|
||||
var storyTiddlerModified = false;
|
||||
for(var t=0; t<this.storyList.length; t++) {
|
||||
if(this.storyList[t] === event.tiddlerTitle) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(event.tiddlerTitle);
|
||||
if(tiddler.hasField("draft.title")) {
|
||||
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
|
||||
if(tiddler && tiddler.hasField("draft.title")) {
|
||||
// Remove the draft tiddler
|
||||
this.renderer.renderTree.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
this.wiki.deleteTiddler(event.tiddlerTitle);
|
||||
// Make the story record point to the original tiddler
|
||||
this.storyList[t] = tiddler.fields["draft.title"];
|
||||
// Check if we're modifying the story tiddler itself
|
||||
@ -253,7 +293,6 @@ NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
|
||||
if(!storyTiddlerModified) {
|
||||
this.saveStoryList();
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -265,15 +304,15 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
|
||||
var title;
|
||||
for(var t=0; true; t++) {
|
||||
title = "New Tiddler" + (t ? " " + t : "");
|
||||
if(!this.renderer.renderTree.wiki.tiddlerExists(title)) {
|
||||
if(!this.wiki.tiddlerExists(title)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var tiddler = new $tw.Tiddler(this.renderer.renderTree.wiki.getCreationFields(),{
|
||||
var tiddler = new $tw.Tiddler(this.wiki.getCreationFields(),{
|
||||
title: title,
|
||||
text: "Newly created tiddler"
|
||||
},this.renderer.renderTree.wiki.getModificationFields());
|
||||
this.renderer.renderTree.wiki.addTiddler(tiddler);
|
||||
},this.wiki.getModificationFields());
|
||||
this.wiki.addTiddler(tiddler);
|
||||
// Create the draft tiddler
|
||||
var draftTitle = this.generateDraftTitle(title),
|
||||
draftTiddler = new $tw.Tiddler({
|
||||
@ -281,18 +320,53 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
|
||||
title: draftTitle,
|
||||
"draft.title": title,
|
||||
"draft.of": title
|
||||
},this.renderer.renderTree.wiki.getModificationFields());
|
||||
this.renderer.renderTree.wiki.addTiddler(draftTiddler);
|
||||
},this.wiki.getModificationFields());
|
||||
this.wiki.addTiddler(draftTiddler);
|
||||
// Update the story to insert the new draft at the top
|
||||
var slot = this.findTitleInStory(event.navigateFromTitle,-1) + 1;
|
||||
this.storyList.splice(slot,0,draftTitle);
|
||||
// Save the updated story
|
||||
this.saveStoryList();
|
||||
// Add a new record to the top of the history stack
|
||||
var history = this.renderer.renderTree.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
var history = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
history.push({title: draftTitle});
|
||||
this.renderer.renderTree.wiki.setTiddlerData(this.historyTitle,history);
|
||||
event.stopPropagation();
|
||||
this.wiki.setTiddlerData(this.historyTitle,history);
|
||||
return false;
|
||||
};
|
||||
|
||||
// Import JSON tiddlers
|
||||
NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
|
||||
var self = this;
|
||||
// Get the story and history details
|
||||
this.getStoryList();
|
||||
var history = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
// Get the tiddlers
|
||||
var tiddlers = [];
|
||||
try {
|
||||
tiddlers = JSON.parse(event.param);
|
||||
} catch(e) {
|
||||
}
|
||||
// Process each tiddler
|
||||
$tw.utils.each(tiddlers,function(tiddlerFields) {
|
||||
// Generate a unique title for the tiddler
|
||||
var title = self.wiki.generateNewTitle(tiddlerFields.title);
|
||||
// Add it to the store
|
||||
self.wiki.addTiddler(new $tw.Tiddler(
|
||||
self.wiki.getCreationFields(),
|
||||
tiddlerFields,
|
||||
self.wiki.getModificationFields(),
|
||||
{title: title}
|
||||
));
|
||||
// Add it to the story
|
||||
if(self.storyList.indexOf(title) === -1) {
|
||||
self.storyList.unshift(title);
|
||||
}
|
||||
// And to history
|
||||
history.push({title: title});
|
||||
});
|
||||
// Save the updated story and history
|
||||
this.saveStoryList();
|
||||
this.wiki.setTiddlerData(this.historyTitle,history);
|
||||
return false;
|
||||
};
|
||||
|
92
core/modules/new_widgets/password.js
Normal file
92
core/modules/new_widgets/password.js
Normal file
@ -0,0 +1,92 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/password.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Password widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var PasswordWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
PasswordWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
PasswordWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Get the current password
|
||||
var password = $tw.browser ? $tw.utils.getPassword(this.passwordName) : "";
|
||||
// Create our element
|
||||
var domNode = this.document.createElement("input");
|
||||
domNode.setAttribute("type","password");
|
||||
domNode.setAttribute("value",password);
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
||||
]);
|
||||
// Insert the label into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
PasswordWidget.prototype.handleChangeEvent = function(event) {
|
||||
var password = this.domNodes[0].value;
|
||||
return $tw.utils.savePassword(this.passwordName,password);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
PasswordWidget.prototype.execute = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.passwordName = this.getAttribute("name","");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
PasswordWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.name) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
PasswordWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.password = PasswordWidget;
|
||||
|
||||
})();
|
205
core/modules/new_widgets/reveal.js
Executable file
205
core/modules/new_widgets/reveal.js
Executable file
@ -0,0 +1,205 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/reveal.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Reveal widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var RevealWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
RevealWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
RevealWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var domNode = this.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
|
||||
var classes = this["class"].split(" ") || [];
|
||||
classes.push("tw-reveal");
|
||||
domNode.className = classes.join(" ");
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup" && this.isOpen) {
|
||||
this.positionPopup(domNode);
|
||||
}
|
||||
if(!this.isOpen) {
|
||||
domNode.setAttribute("hidden","true")
|
||||
}
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
RevealWidget.prototype.positionPopup = function(domNode) {
|
||||
domNode.style.position = "absolute";
|
||||
domNode.style.zIndex = "1000";
|
||||
switch(this.position) {
|
||||
case "left":
|
||||
domNode.style.left = (this.popup.left - domNode.offsetWidth) + "px";
|
||||
domNode.style.top = this.popup.top + "px";
|
||||
break;
|
||||
case "above":
|
||||
domNode.style.left = this.popup.left + "px";
|
||||
domNode.style.top = (this.popup.top - domNode.offsetHeight) + "px";
|
||||
break;
|
||||
case "aboveright":
|
||||
domNode.style.left = (this.popup.left + this.popup.width) + "px";
|
||||
domNode.style.top = (this.popup.top + this.popup.height - domNode.offsetHeight) + "px";
|
||||
break;
|
||||
case "right":
|
||||
domNode.style.left = (this.popup.left + this.popup.width) + "px";
|
||||
domNode.style.top = this.popup.top + "px";
|
||||
break;
|
||||
case "belowleft":
|
||||
domNode.style.left = (this.popup.left + this.popup.width - domNode.offsetWidth) + "px";
|
||||
domNode.style.top = (this.popup.top + this.popup.height) + "px";
|
||||
break;
|
||||
default: // Below
|
||||
domNode.style.left = this.popup.left + "px";
|
||||
domNode.style.top = (this.popup.top + this.popup.height) + "px";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
RevealWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.state = this.getAttribute("state");
|
||||
this.type = this.getAttribute("type");
|
||||
this.text = this.getAttribute("text");
|
||||
this.position = this.getAttribute("position");
|
||||
this["class"] = this.getAttribute("class","");
|
||||
this["default"] = this.getAttribute("default","");
|
||||
this.animate = this.getAttribute("animate","no");
|
||||
this.openAnimation = this.animate === "no" ? undefined : "open";
|
||||
this.closeAnimation = this.animate === "no" ? undefined : "close";
|
||||
// Compute the title of the state tiddler and read it
|
||||
this.stateTitle = this.state;
|
||||
this.readState();
|
||||
// Construct the child widgets
|
||||
var childNodes = this.isOpen ? this.parseTreeNode.children : [];
|
||||
this.hasChildNodes = this.isOpen;
|
||||
this.makeChildWidgets(childNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Read the state tiddler
|
||||
*/
|
||||
RevealWidget.prototype.readState = function() {
|
||||
// Read the information from the state tiddler
|
||||
if(this.stateTitle) {
|
||||
var state = this.wiki.getTextReference(this.stateTitle,this["default"],this.getVariable("currentTiddler"));
|
||||
switch(this.type) {
|
||||
case "popup":
|
||||
this.readPopupState(state);
|
||||
break;
|
||||
case "match":
|
||||
this.readMatchState(state);
|
||||
break;
|
||||
case "nomatch":
|
||||
this.readMatchState(state);
|
||||
this.isOpen = !this.isOpen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RevealWidget.prototype.readMatchState = function(state) {
|
||||
this.isOpen = state === this.text;
|
||||
};
|
||||
|
||||
RevealWidget.prototype.readPopupState = function(state) {
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
match = popupLocationRegExp.exec(state);
|
||||
// Check if the state matches the location regexp
|
||||
if(match) {
|
||||
// If so, we're open
|
||||
this.isOpen = true;
|
||||
// Get the location
|
||||
this.popup = {
|
||||
left: parseFloat(match[1]),
|
||||
top: parseFloat(match[2]),
|
||||
width: parseFloat(match[3]),
|
||||
height: parseFloat(match[4])
|
||||
};
|
||||
} else {
|
||||
// If not, we're closed
|
||||
this.isOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
RevealWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.animate) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
var refreshed = false;
|
||||
if(changedTiddlers[this.stateTitle]) {
|
||||
this.updateState();
|
||||
refreshed = true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers) || refreshed;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Called by refresh() to dynamically show or hide the content
|
||||
*/
|
||||
RevealWidget.prototype.updateState = function() {
|
||||
// Read the current state
|
||||
this.readState();
|
||||
// Construct the child nodes if needed
|
||||
var domNode = this.domNodes[0];
|
||||
if(this.isOpen && !this.hasChildNodes) {
|
||||
this.hasChildNodes = true;
|
||||
this.makeChildWidgets(this.parseTreeNode.children);
|
||||
this.renderChildren(domNode,null);
|
||||
}
|
||||
// Animate our DOM node
|
||||
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup" && this.isOpen) {
|
||||
this.positionPopup(domNode);
|
||||
}
|
||||
if(this.isOpen) {
|
||||
domNode.removeAttribute("hidden");
|
||||
$tw.anim.perform(this.openAnimation,domNode);
|
||||
} else {
|
||||
$tw.anim.perform(this.closeAnimation,domNode,{callback: function() {
|
||||
domNode.setAttribute("hidden","true");
|
||||
}});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
RevealWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.reveal = RevealWidget;
|
||||
|
||||
})();
|
64
core/modules/new_widgets/setvariable.js
Executable file
64
core/modules/new_widgets/setvariable.js
Executable file
@ -0,0 +1,64 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/setvariable.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Setvariable widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var SetVariableWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
SetVariableWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
SetVariableWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
SetVariableWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.setName = this.getAttribute("name","currentTiddler");
|
||||
this.setValue = this.getAttribute("value");
|
||||
// Set context variable
|
||||
this.setVariable(this.setName,this.setValue,this.parseTreeNode.params);
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
SetVariableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.name || changedAttributes.value) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.setvariable = SetVariableWidget;
|
||||
|
||||
})();
|
63
core/modules/new_widgets/text.js
Executable file
63
core/modules/new_widgets/text.js
Executable file
@ -0,0 +1,63 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/text.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Text node widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TextNodeWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TextNodeWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TextNodeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.parseTreeNode.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TextNodeWidget.prototype.execute = function() {
|
||||
// Nothing to do for a text node
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TextNodeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
TextNodeWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.text = TextNodeWidget;
|
||||
|
||||
})();
|
66
core/modules/new_widgets/tiddler.js
Executable file
66
core/modules/new_widgets/tiddler.js
Executable file
@ -0,0 +1,66 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/tiddler.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Tiddler widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TiddlerWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TiddlerWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TiddlerWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TiddlerWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
// Set context variables
|
||||
this.setVariable("currentTiddler",this.tiddlerTitle);
|
||||
this.setVariable("missingTiddlerClass",(this.wiki.tiddlerExists(this.tiddlerTitle) || this.wiki.isShadowTiddler(this.tiddlerTitle)) ? "tw-tiddler-exists" : "tw-tiddler-missing");
|
||||
this.setVariable("shadowTiddlerClass",this.wiki.isShadowTiddler(this.tiddlerTitle) ? "tw-tiddler-shadow" : "");
|
||||
this.setVariable("systemTiddlerClass",this.wiki.isSystemTiddler(this.tiddlerTitle) ? "tw-tiddler-system" : "");
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TiddlerWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.tiddler = TiddlerWidget;
|
||||
|
||||
})();
|
95
core/modules/new_widgets/transclude.js
Executable file
95
core/modules/new_widgets/transclude.js
Executable file
@ -0,0 +1,95 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/transclude.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Transclude widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var TranscludeWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
TranscludeWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
TranscludeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
TranscludeWidget.prototype.execute = function() {
|
||||
// Get our parameters
|
||||
this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.transcludeField = this.getAttribute("field");
|
||||
this.transcludeIndex = this.getAttribute("index");
|
||||
// Check for recursion
|
||||
var recursionMarker = this.makeRecursionMarker();;
|
||||
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) {
|
||||
this.makeChildWidgets([{type: "text", text: "Tiddler recursion error in transclude widget"}]);
|
||||
return;
|
||||
}
|
||||
// Set context variables for recursion detection
|
||||
this.setVariable("transclusion",recursionMarker);
|
||||
// Parse the text reference
|
||||
var parser = this.wiki.new_parseTextReference(
|
||||
this.transcludeTitle,
|
||||
this.transcludeField,
|
||||
this.transcludeIndex,
|
||||
{parseAsInline: !this.parseTreeNode.isBlock}),
|
||||
parseTreeNodes = parser ? parser.tree : [];
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets(parseTreeNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Compose a string comprising the title, field and/or index to identify this transclusion for recursion detection
|
||||
*/
|
||||
TranscludeWidget.prototype.makeRecursionMarker = function() {
|
||||
var output = [];
|
||||
output.push("{");
|
||||
output.push(this.getVariable("currentTiddler",{defaultValue: ""}));
|
||||
output.push("|");
|
||||
output.push(this.transcludeTitle || "");
|
||||
output.push("|");
|
||||
output.push(this.transcludeField || "");
|
||||
output.push("|");
|
||||
output.push(this.transcludeIndex || "");
|
||||
output.push("}");
|
||||
return output.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
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]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.transclude = TranscludeWidget;
|
||||
|
||||
})();
|
185
core/modules/new_widgets/view.js
Executable file
185
core/modules/new_widgets/view.js
Executable file
@ -0,0 +1,185 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/view.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
View widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
|
||||
|
||||
var ViewWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ViewWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ViewWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ViewWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.viewTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.viewField = this.getAttribute("field","text");
|
||||
this.viewIndex = this.getAttribute("index");
|
||||
this.viewFormat = this.getAttribute("format","text");
|
||||
this.viewTemplate = this.getAttribute("template","");
|
||||
switch(this.viewFormat) {
|
||||
case "htmlwikified":
|
||||
this.text = this.getValueAsHtmlWikified();
|
||||
break;
|
||||
case "htmlencoded":
|
||||
this.text = this.getValueAsHtmlEncoded();
|
||||
break;
|
||||
case "date":
|
||||
this.text = this.getValueAsDate(this.viewTemplate);
|
||||
break;
|
||||
case "relativedate":
|
||||
this.text = this.getValueAsRelativeDate();
|
||||
break;
|
||||
case "stripcomments":
|
||||
this.text = this.getValueAsStrippedComments();
|
||||
break;
|
||||
case "jsencoded":
|
||||
this.text = this.getValueAsJsEncoded();
|
||||
break;
|
||||
default: // "text"
|
||||
this.text = this.getValueAsText();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
The various formatter functions are baked into this widget for the moment. Eventually they will be replaced by macro functions
|
||||
*/
|
||||
|
||||
ViewWidget.prototype.getValue = function() {
|
||||
// Get the value to display
|
||||
var value,
|
||||
tiddler = this.wiki.getTiddler(this.viewTitle);
|
||||
if(tiddler) {
|
||||
if(this.viewField === "text") {
|
||||
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
||||
value = this.wiki.getTiddlerText(this.viewTitle);
|
||||
} else {
|
||||
if($tw.utils.hop(tiddler.fields,this.viewField)) {
|
||||
value = tiddler.fields[this.viewField];
|
||||
} else {
|
||||
value = "";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(this.viewField === "title") {
|
||||
value = this.viewTitle;
|
||||
} else {
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsText = function() {
|
||||
// Get the value to display
|
||||
var text,
|
||||
tiddler = this.wiki.getTiddler(this.viewTitle);
|
||||
if(tiddler) {
|
||||
if(this.viewField === "text") {
|
||||
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
||||
text = this.wiki.getTiddlerText(this.viewTitle);
|
||||
} else {
|
||||
text = tiddler.getFieldString(this.viewField);
|
||||
}
|
||||
} else { // Use a special value if the tiddler is missing
|
||||
if(this.viewField === "title") {
|
||||
text = this.viewTitle;
|
||||
} else {
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlWikified = function() {
|
||||
return this.wiki.new_renderText("text/html","text/vnd.tiddlywiki",this.getValueAsText(),this);
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlEncoded = function() {
|
||||
return $tw.utils.htmlEncode(this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsDate = function(format) {
|
||||
return $tw.utils.formatDateString(this.getValue(),format);
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsRelativeDate = function(format) {
|
||||
var value = this.getValue();
|
||||
if(value) {
|
||||
return $tw.utils.getRelativeDate((new Date()) - (new Date(value))).description;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsStrippedComments = function() {
|
||||
var lines = this.getValueAsText().split("\n"),
|
||||
out = [];
|
||||
for(var line=0; line<lines.length; line++) {
|
||||
var text = lines[line];
|
||||
if(!/^\s*\/\/#/.test(text)) {
|
||||
out.push(text);
|
||||
}
|
||||
}
|
||||
return out.join("\n");
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsJsEncoded = function() {
|
||||
return $tw.utils.stringify(this.getValueAsText());
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ViewWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.template || changedAttributes.format || changedTiddlers[this.viewTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget
|
||||
*/
|
||||
ViewWidget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.domNodes,function(domNode) {
|
||||
domNode.parentNode.removeChild(domNode);
|
||||
});
|
||||
this.domNodes = [];
|
||||
};
|
||||
|
||||
exports.view = ViewWidget;
|
||||
|
||||
})();
|
460
core/modules/new_widgets/widget.js
Executable file
460
core/modules/new_widgets/widget.js
Executable file
@ -0,0 +1,460 @@
|
||||
/*\
|
||||
title: $:/core/modules/new_widgets/widget.js
|
||||
type: application/javascript
|
||||
module-type: new_widget
|
||||
|
||||
Widget base class
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Create a widget object for a parse tree node
|
||||
parseTreeNode: reference to the parse tree node to be rendered
|
||||
options: see below
|
||||
Options include:
|
||||
wiki: mandatory reference to wiki associated with this render tree
|
||||
variables: optional hashmap of context variables (see below)
|
||||
parentWidget: optional reference to a parent renderer node for the context chain
|
||||
document: optional document object to use instead of global document
|
||||
Context variables include:
|
||||
currentTiddler: title of the tiddler providing the context
|
||||
*/
|
||||
var Widget = function(parseTreeNode,options) {
|
||||
if(arguments.length > 0) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Initialise widget properties. These steps are pulled out of the constructor so that we can reuse them in subclasses
|
||||
*/
|
||||
Widget.prototype.initialise = function(parseTreeNode,options) {
|
||||
options = options || {};
|
||||
// Save widget info
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
this.wiki = options.wiki;
|
||||
this.variables = options.variables || {};
|
||||
this.parentWidget = options.parentWidget;
|
||||
this.document = options.document;
|
||||
this.attributes = {};
|
||||
this.children = [];
|
||||
this.domNodes = [];
|
||||
this.eventListeners = {};
|
||||
// Hashmap of the widget classes
|
||||
if(!this.widgetClasses) {
|
||||
Widget.prototype.widgetClasses = $tw.modules.applyMethods("new_widget");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
Widget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
Widget.prototype.execute = function() {
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Get the prevailing value of a context variable
|
||||
name: name of variable
|
||||
options: see below
|
||||
Options include
|
||||
params: array of {name:, value:} for each parameter
|
||||
defaultValue: default value if the variable is not defined
|
||||
*/
|
||||
Widget.prototype.getVariable = function(name,options) {
|
||||
options = options || {};
|
||||
var actualParams = options.params || [];
|
||||
// Search up the widget tree for the variable name
|
||||
var node = this;
|
||||
while(node && !$tw.utils.hop(node.variables,name)) {
|
||||
node = node.parentWidget;
|
||||
}
|
||||
// If we get to the root then look for a macro module
|
||||
if(!node) {
|
||||
return this.evaluateMacroModule(name,actualParams,options.defaultValue);
|
||||
}
|
||||
// Get the value
|
||||
var value = node.variables[name].value || "";
|
||||
// Substitute any parameters specified in the definition
|
||||
value = this.substituteVariableParameters(value,node.variables[name].params,actualParams);
|
||||
value = this.substituteVariableReferences(value);
|
||||
return value;
|
||||
};
|
||||
|
||||
Widget.prototype.substituteVariableParameters = function(text,formalParams,actualParams) {
|
||||
if(formalParams) {
|
||||
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
|
||||
paramInfo, paramValue;
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<formalParams.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
paramInfo = formalParams[p];
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<actualParams.length; m++) {
|
||||
if(actualParams[m].name === paramInfo.name) {
|
||||
paramValue = actualParams[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
while(nextAnonParameter < actualParams.length && actualParams[nextAnonParameter].name) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
|
||||
paramValue = actualParams[nextAnonParameter++].value;
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Replace any instances of this parameter
|
||||
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
Widget.prototype.substituteVariableReferences = function(text) {
|
||||
var self = this;
|
||||
return text.replace(/\$\(([^\)\$]+)\)\$/g,function(match,p1,offset,string) {
|
||||
return self.getVariable(p1,{defaultValue: ""});
|
||||
});
|
||||
};
|
||||
|
||||
Widget.prototype.evaluateMacroModule = function(name,actualParams,defaultValue) {
|
||||
if($tw.utils.hop($tw.macros,name)) {
|
||||
var macro = $tw.macros[name],
|
||||
args = [];
|
||||
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
|
||||
paramInfo, paramValue;
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<macro.params.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
paramInfo = macro.params[p];
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<actualParams.length; m++) {
|
||||
if(actualParams[m].name === paramInfo.name) {
|
||||
paramValue = actualParams[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
while(nextAnonParameter < actualParams.length && actualParams[nextAnonParameter].name) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
|
||||
paramValue = actualParams[nextAnonParameter++].value;
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Save the parameter
|
||||
args.push(paramValue);
|
||||
}
|
||||
return macro.run.apply(this,args)
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Set the value of a context variable
|
||||
name: name of the variable
|
||||
value: value of the variable
|
||||
params: array of {name:, default:} for each parameter
|
||||
*/
|
||||
Widget.prototype.setVariable = function(name,value,params) {
|
||||
this.variables[name] = {value: value, params: params};
|
||||
};
|
||||
|
||||
/*
|
||||
Check whether a given context variable value exists in the parent chain
|
||||
*/
|
||||
Widget.prototype.hasVariable = function(name,value) {
|
||||
var node = this;
|
||||
while(node) {
|
||||
if($tw.utils.hop(node.variables,name) && node.variables[name].value === value) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentWidget;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Construct a qualifying string based on concatenating the values of a given variable in the parent chain
|
||||
*/
|
||||
Widget.prototype.getStateQualifier = function(name) {
|
||||
name = name || "transclusion";
|
||||
var output = [],
|
||||
node = this;
|
||||
while(node) {
|
||||
if($tw.utils.hop(node.variables,name)) {
|
||||
output.push(node.getVariable(name));
|
||||
}
|
||||
node = node.parentWidget;
|
||||
}
|
||||
return output.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed
|
||||
*/
|
||||
Widget.prototype.computeAttributes = function() {
|
||||
var changedAttributes = {},
|
||||
self = this,
|
||||
value;
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(attribute.type === "indirect") {
|
||||
value = self.wiki.getTextReference(attribute.textReference,"",self.getVariable("currentTiddler"));
|
||||
} else if(attribute.type === "macro") {
|
||||
var text = self.getVariable(attribute.value.name,{params: attribute.value.params});
|
||||
value = self.wiki.new_renderText("text/plain","text/vnd.tiddlywiki",text);
|
||||
} else { // String attribute
|
||||
value = attribute.value;
|
||||
}
|
||||
// Check whether the attribute has changed
|
||||
if(self.attributes[name] !== value) {
|
||||
self.attributes[name] = value;
|
||||
changedAttributes[name] = true;
|
||||
}
|
||||
});
|
||||
return changedAttributes;
|
||||
};
|
||||
|
||||
/*
|
||||
Check for the presence of an attribute
|
||||
*/
|
||||
Widget.prototype.hasAttribute = function(name) {
|
||||
return $tw.utils.hop(this.attributes,name);
|
||||
};
|
||||
|
||||
/*
|
||||
Get the value of an attribute
|
||||
*/
|
||||
Widget.prototype.getAttribute = function(name,defaultText) {
|
||||
if($tw.utils.hop(this.attributes,name)) {
|
||||
return this.attributes[name];
|
||||
} else {
|
||||
return defaultText;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Assign the computed attributes of the widget to a domNode
|
||||
*/
|
||||
Widget.prototype.assignAttributes = function(domNode) {
|
||||
var self = this;
|
||||
$tw.utils.each(this.attributes,function(v,a) {
|
||||
if(v !== undefined) {
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(null,a,v);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Make child widgets correspondng to specified parseTreeNodes
|
||||
*/
|
||||
Widget.prototype.makeChildWidgets = function(parseTreeNodes) {
|
||||
this.children = [];
|
||||
var self = this;
|
||||
$tw.utils.each(parseTreeNodes || (this.parseTreeNode && this.parseTreeNode.children),function(childNode) {
|
||||
self.children.push(self.makeChildWidget(childNode));
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Construct the widget object for a parse tree node
|
||||
*/
|
||||
Widget.prototype.makeChildWidget = function(parseTreeNode) {
|
||||
var WidgetClass = this.widgetClasses[parseTreeNode.type];
|
||||
if(!WidgetClass) {
|
||||
WidgetClass = this.widgetClasses["text"];
|
||||
parseTreeNode = {type: "text", text: "Undefined widget '" + parseTreeNode.type + "'"};
|
||||
}
|
||||
return new WidgetClass(parseTreeNode,{
|
||||
wiki: this.wiki,
|
||||
variables: {},
|
||||
parentWidget: this,
|
||||
document: this.document
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Get the next sibling of this widget
|
||||
*/
|
||||
Widget.prototype.nextSibling = function() {
|
||||
if(this.parentWidget) {
|
||||
var index = this.parentWidget.children.indexOf(this);
|
||||
if(index !== -1 && index < this.parentWidget.children.length-1) {
|
||||
return this.parentWidget.children[index+1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Get the previous sibling of this widget
|
||||
*/
|
||||
Widget.prototype.previousSibling = function() {
|
||||
if(this.parentWidget) {
|
||||
var index = this.parentWidget.children.indexOf(this);
|
||||
if(index !== -1 && index > 0) {
|
||||
return this.parentWidget.children[index-1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Render the children of this widget into the DOM
|
||||
*/
|
||||
Widget.prototype.renderChildren = function(parent,nextSibling) {
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
childWidget.render(parent,nextSibling);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Add a list of event listeners from an array [{type:,handler:},...]
|
||||
*/
|
||||
Widget.prototype.addEventListeners = function(listeners) {
|
||||
var self = this;
|
||||
$tw.utils.each(listeners,function(listenerInfo) {
|
||||
self.addEventListener(listenerInfo.type,listenerInfo.handler);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Add an event listener
|
||||
*/
|
||||
Widget.prototype.addEventListener = function(type,handler) {
|
||||
var self = this;
|
||||
if(typeof handler === "string") { // The handler is a method name on this widget
|
||||
this.eventListeners[type] = function(event) {
|
||||
return self[handler].call(self,event);
|
||||
};
|
||||
} else { // The handler is a function
|
||||
this.eventListeners[type] = function(event) {
|
||||
return handler.call(self,event);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Dispatch an event to a widget. If the widget doesn't handle the event then it is also dispatched to the parent widget
|
||||
*/
|
||||
Widget.prototype.dispatchEvent = function(event) {
|
||||
// Dispatch the event if this widget handles it
|
||||
var listener = this.eventListeners[event.type];
|
||||
if(listener) {
|
||||
// Don't propagate the event if the listener returned false
|
||||
if(!listener(event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Dispatch the event to the parent widget
|
||||
if(this.parentWidget) {
|
||||
return this.parentWidget.dispatchEvent(event);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
Widget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Rebuild a previously rendered widget
|
||||
*/
|
||||
Widget.prototype.refreshSelf = function() {
|
||||
var nextSibling = this.findNextSiblingDomNode();
|
||||
this.removeChildDomNodes();
|
||||
this.render(this.parentDomNode,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Refresh all the children of a widget
|
||||
*/
|
||||
Widget.prototype.refreshChildren = function(changedTiddlers) {
|
||||
var self = this,
|
||||
refreshed = false;
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
refreshed = childWidget.refresh(changedTiddlers) || refreshed;
|
||||
});
|
||||
return refreshed;
|
||||
};
|
||||
|
||||
/*
|
||||
Find the next sibling in the DOM to this widget. This is done by scanning the widget tree through all next siblings and their descendents that share the same parent DOM node
|
||||
*/
|
||||
Widget.prototype.findNextSiblingDomNode = function(startIndex) {
|
||||
// Refer to this widget by its index within its parents children
|
||||
var parent = this.parentWidget,
|
||||
index = startIndex !== undefined ? startIndex : parent.children.indexOf(this);
|
||||
if(index === -1) {
|
||||
throw "node not found in parents children";
|
||||
}
|
||||
// Look for a DOM node in the later siblings
|
||||
while(++index < parent.children.length) {
|
||||
var domNode = parent.children[index].findFirstDomNode();
|
||||
if(domNode) {
|
||||
return domNode;
|
||||
}
|
||||
}
|
||||
// Go back and look for later siblings of our parent if it has the same parent dom node
|
||||
var grandParent = parent.parentWidget;
|
||||
if(grandParent && parent.parentDomNode === this.parentDomNode) {
|
||||
index = grandParent.children.indexOf(parent);
|
||||
return parent.findNextSiblingDomNode(index);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Find the first DOM node generated by a widget or its children
|
||||
*/
|
||||
Widget.prototype.findFirstDomNode = function() {
|
||||
// Return the first dom node of this widget, if we've got one
|
||||
if(this.domNodes.length > 0) {
|
||||
return this.domNodes[0];
|
||||
}
|
||||
// Otherwise, recursively call our children
|
||||
for(var t=0; t<this.children.length; t++) {
|
||||
var domNode = this.children[t].findFirstDomNode();
|
||||
if(domNode) {
|
||||
return domNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove any DOM nodes created by this widget or its children
|
||||
*/
|
||||
Widget.prototype.removeChildDomNodes = function() {
|
||||
$tw.utils.each(this.children,function(childWidget) {
|
||||
childWidget.removeChildDomNodes();
|
||||
});
|
||||
};
|
||||
|
||||
exports.widget = Widget;
|
||||
|
||||
})();
|
@ -38,9 +38,10 @@ exports.parse = function() {
|
||||
} else {
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
tag: "a",
|
||||
attributes: {
|
||||
to: {type: "string", value: this.match[0]}
|
||||
href: {type: "string", value: this.match[0]},
|
||||
target: {type: "string", value: "_blank"}
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: this.match[0]
|
||||
|
@ -27,22 +27,41 @@ exports.init = function(parser) {
|
||||
this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg;
|
||||
};
|
||||
|
||||
var isLinkExternal = function(to) {
|
||||
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s'"]+(?:\/|\b)/i;
|
||||
return externalRegExp.test(to);
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Process the link
|
||||
var text = this.match[1],
|
||||
link = this.match[2] || text;
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
attributes: {
|
||||
to: {type: "string", value: link}
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: text
|
||||
}]
|
||||
}];
|
||||
if(isLinkExternal(link)) {
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "a",
|
||||
attributes: {
|
||||
href: {type: "string", value: link},
|
||||
target: {type: "string", value: "_blank"}
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: text
|
||||
}]
|
||||
}];
|
||||
} else {
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
attributes: {
|
||||
to: {type: "string", value: link}
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: text
|
||||
}]
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -47,7 +47,7 @@ exports.parse = function() {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
attributes: {
|
||||
title: {type: "string", value: template || targetTitle}
|
||||
tiddler: {type: "string", value: template || targetTitle}
|
||||
},
|
||||
isBlock: true
|
||||
};
|
||||
@ -55,7 +55,7 @@ exports.parse = function() {
|
||||
type: "element",
|
||||
tag: "$tiddler",
|
||||
attributes: {
|
||||
title: {type: "string", value: targetTitle}
|
||||
tiddler: {type: "string", value: targetTitle}
|
||||
},
|
||||
isBlock: true,
|
||||
children: [transcludeNode]
|
||||
|
@ -47,14 +47,14 @@ exports.parse = function() {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
attributes: {
|
||||
title: {type: "string", value: template || targetTitle}
|
||||
tiddler: {type: "string", value: template || targetTitle}
|
||||
}
|
||||
};
|
||||
var tiddlerNode = {
|
||||
type: "element",
|
||||
tag: "$tiddler",
|
||||
attributes: {
|
||||
title: {type: "string", value: targetTitle}
|
||||
tiddler: {type: "string", value: targetTitle}
|
||||
},
|
||||
children: [transcludeNode]
|
||||
};
|
||||
|
@ -28,6 +28,8 @@ $$$
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
exports.name = "typedblock";
|
||||
exports.types = {block: true};
|
||||
|
||||
@ -57,16 +59,15 @@ exports.parse = function() {
|
||||
this.parser.pos = this.parser.sourceLength;
|
||||
}
|
||||
// Parse the block according to the specified type
|
||||
var parser = this.parser.wiki.parseText(parseType,text,{defaultType: "text/plain"});
|
||||
var parser = this.parser.wiki.new_parseText(parseType,text,{defaultType: "text/plain"});
|
||||
// If there's no render type, just return the parse tree
|
||||
if(!renderType) {
|
||||
return parser.tree;
|
||||
} else {
|
||||
// Otherwise, render to the rendertype and return in a <PRE> tag
|
||||
var renderTree = new $tw.WikiRenderTree(parser,{wiki: $tw.wiki, document: $tw.document});
|
||||
renderTree.execute();
|
||||
var container = $tw.document.createElement("div");
|
||||
renderTree.renderInDom(container);
|
||||
var widgetNode = this.parser.wiki.makeWidget(parser),
|
||||
container = $tw.document.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
var text = renderType === "text/html" ? container.innerHTML : container.textContent;
|
||||
return [{
|
||||
type: "element",
|
||||
|
@ -1,212 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/element.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Element renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Element widget. A degenerate widget that renders ordinary HTML elements
|
||||
*/
|
||||
var ElementWidget = function(renderer) {
|
||||
this.renderer = renderer;
|
||||
this.tag = this.renderer.parseTreeNode.tag;
|
||||
this.attributes = this.renderer.attributes;
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
this.events = this.renderer.parseTreeNode.events;
|
||||
};
|
||||
|
||||
ElementWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Check if any of our attribute dependencies have changed
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
// Update our attributes
|
||||
this.renderer.assignAttributes();
|
||||
}
|
||||
// Refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Element renderer
|
||||
*/
|
||||
var ElementRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
// Initialise widget classes
|
||||
if(!this.widgetClasses) {
|
||||
ElementRenderer.prototype.widgetClasses = $tw.modules.applyMethods("widget");
|
||||
}
|
||||
// Select the namespace for the tag
|
||||
var tagNameSpaces = {
|
||||
svg: "http://www.w3.org/2000/svg",
|
||||
math: "http://www.w3.org/1998/Math/MathML"
|
||||
};
|
||||
this.namespace = tagNameSpaces[this.parseTreeNode.tag];
|
||||
if(this.namespace) {
|
||||
this.context = this.context || {};
|
||||
this.context.namespace = this.namespace;
|
||||
} else {
|
||||
this.namespace = this.renderTree.getContextVariable(this.parentRenderer,"namespace","http://www.w3.org/1999/xhtml");
|
||||
}
|
||||
// Get the context tiddler title
|
||||
this.tiddlerTitle = this.renderTree.getContextVariable(this.parentRenderer,"tiddlerTitle");
|
||||
// Compute our dependencies
|
||||
this.dependencies = {};
|
||||
var self = this;
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(attribute.type === "indirect") {
|
||||
var tr = $tw.utils.parseTextReference(attribute.textReference);
|
||||
self.dependencies[tr.title ? tr.title : self.tiddlerTitle] = true;
|
||||
}
|
||||
});
|
||||
// Compute our attributes
|
||||
this.attributes = {};
|
||||
this.computeAttributes();
|
||||
// Create the parasite widget object if required
|
||||
if(this.parseTreeNode.tag.charAt(0) === "$") {
|
||||
// Choose the class
|
||||
var WidgetClass = this.widgetClasses[this.parseTreeNode.tag.substr(1)];
|
||||
// Instantiate the widget
|
||||
if(WidgetClass) {
|
||||
this.widget = new WidgetClass(this);
|
||||
} else {
|
||||
WidgetClass = this.widgetClasses.error;
|
||||
if(WidgetClass) {
|
||||
this.widget = new WidgetClass(this,"Unknown widget '<" + this.parseTreeNode.tag + ">'");
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we haven't got a widget, use the generic HTML element widget
|
||||
if(!this.widget) {
|
||||
this.widget = new ElementWidget(this);
|
||||
}
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.computeAttributes = function() {
|
||||
var changedAttributes = {},
|
||||
self = this,
|
||||
value;
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(attribute.type === "indirect") {
|
||||
value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
||||
} else if(attribute.type === "macro") {
|
||||
// Get the macro definition
|
||||
var macro = self.renderTree.findMacroDefinition(self.parentRenderer,attribute.value.name);
|
||||
if(!macro) {
|
||||
value = "";
|
||||
} else {
|
||||
// Substitute the macro parameters
|
||||
value = self.renderTree.substituteParameters(macro,attribute.value);
|
||||
// Parse the text and render it as text
|
||||
value = self.renderTree.wiki.renderText("text/plain","text/vnd.tiddlywiki",value,self.context);
|
||||
}
|
||||
} else { // String attribute
|
||||
value = attribute.value;
|
||||
}
|
||||
// Check whether the attribute has changed
|
||||
if(self.attributes[name] !== value) {
|
||||
self.attributes[name] = value;
|
||||
changedAttributes[name] = true;
|
||||
}
|
||||
});
|
||||
return changedAttributes;
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.hasAttribute = function(name) {
|
||||
return $tw.utils.hop(this.attributes,name);
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.getAttribute = function(name,defaultValue) {
|
||||
if($tw.utils.hop(this.attributes,name)) {
|
||||
return this.attributes[name];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.renderInDom = function() {
|
||||
// Check if our widget is providing an element
|
||||
if(this.widget.tag) {
|
||||
// Create the element
|
||||
this.domNode = this.renderTree.document.createElementNS(this.namespace,this.widget.tag);
|
||||
// Assign any specified event handlers
|
||||
$tw.utils.addEventListeners(this.domNode,this.widget.events);
|
||||
// Assign the attributes
|
||||
this.assignAttributes();
|
||||
// Render any child nodes
|
||||
var self = this;
|
||||
$tw.utils.each(this.widget.children,function(node) {
|
||||
if(node.renderInDom) {
|
||||
self.domNode.appendChild(node.renderInDom());
|
||||
}
|
||||
});
|
||||
// Call postRenderInDom if the widget provides it and we're in the browser
|
||||
if($tw.browser && this.widget.postRenderInDom) {
|
||||
this.widget.postRenderInDom();
|
||||
}
|
||||
// Return the dom node
|
||||
return this.domNode;
|
||||
} else {
|
||||
// If we're not generating an element, just render our first child
|
||||
return this.widget.children[0].renderInDom();
|
||||
}
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.assignAttributes = function() {
|
||||
var self = this;
|
||||
$tw.utils.each(this.widget.attributes,function(v,a) {
|
||||
if(v !== undefined) {
|
||||
if($tw.utils.isArray(v)) { // Ahem, could there be arrays other than className?
|
||||
self.domNode.className = v.join(" ");
|
||||
} else if (typeof v === "object") { // ...or objects other than style?
|
||||
for(var p in v) {
|
||||
self.domNode.style[$tw.utils.unHyphenateCss(p)] = v[p];
|
||||
}
|
||||
} else {
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
self.domNode.setAttributeNS(null,a,v);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.refreshInDom = function(changedTiddlers) {
|
||||
// Update our attributes if required
|
||||
var changedAttributes = {};
|
||||
if($tw.utils.checkDependencies(this.dependencies,changedTiddlers)) {
|
||||
changedAttributes = this.computeAttributes();
|
||||
}
|
||||
// Check if the widget has a refreshInDom method
|
||||
if(this.widget.refreshInDom) {
|
||||
// Let the widget refresh itself
|
||||
this.widget.refreshInDom(changedAttributes,changedTiddlers);
|
||||
} else {
|
||||
// If not, assign the attributes and refresh any child nodes
|
||||
this.assignAttributes();
|
||||
$tw.utils.each(this.widget.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.element = ElementRenderer
|
||||
|
||||
})();
|
@ -1,31 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/entity.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Entity renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Entity renderer
|
||||
*/
|
||||
var EntityRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
};
|
||||
|
||||
EntityRenderer.prototype.renderInDom = function() {
|
||||
return this.renderTree.document.createTextNode($tw.utils.entityDecode(this.parseTreeNode.entity));
|
||||
};
|
||||
|
||||
exports.entity = EntityRenderer
|
||||
|
||||
})();
|
@ -1,65 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/macrocall.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Macro call renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Macro call renderer
|
||||
*/
|
||||
var MacroCallRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
// Find the macro definition
|
||||
var macro = this.renderTree.findMacroDefinition(this.parentRenderer,this.parseTreeNode.name);
|
||||
// Insert an error message if we couldn't find the macro
|
||||
var childTree;
|
||||
if(!macro) {
|
||||
childTree = [{type: "text", text: "<<Undefined macro: " + this.parseTreeNode.name + ">>"}];
|
||||
} else {
|
||||
// Substitute the macro parameters
|
||||
var text = this.renderTree.substituteParameters(macro,this.parseTreeNode);
|
||||
// Parse the text
|
||||
childTree = this.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.parseTreeNode.isBlock}).tree;
|
||||
}
|
||||
// Create the renderers for the child nodes
|
||||
this.children = this.renderTree.createRenderers(this,childTree);
|
||||
};
|
||||
|
||||
MacroCallRenderer.prototype.renderInDom = function() {
|
||||
// Create the element
|
||||
this.domNode = this.renderTree.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
|
||||
this.domNode.setAttribute("data-macro-name",this.parseTreeNode.name);
|
||||
// Render any child nodes
|
||||
var self = this;
|
||||
$tw.utils.each(this.children,function(node,index) {
|
||||
if(node.renderInDom) {
|
||||
self.domNode.appendChild(node.renderInDom());
|
||||
}
|
||||
});
|
||||
// Return the dom node
|
||||
return this.domNode;
|
||||
};
|
||||
|
||||
MacroCallRenderer.prototype.refreshInDom = function(changedTiddlers) {
|
||||
// Refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.macrocall = MacroCallRenderer
|
||||
|
||||
})();
|
@ -1,30 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/macrodef.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Macro definition renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Macro definition renderer
|
||||
*/
|
||||
var MacroDefRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
// Save the macro definition into the context of the rendertree
|
||||
this.renderTree.context.macroDefinitions = this.renderTree.context.macroDefinitions || {};
|
||||
this.renderTree.context.macroDefinitions[this.parseTreeNode.name] = this.parseTreeNode;
|
||||
};
|
||||
|
||||
exports.macrodef = MacroDefRenderer
|
||||
|
||||
})();
|
@ -1,33 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/raw.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Raw HTML renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Raw HTML renderer
|
||||
*/
|
||||
var RawRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
};
|
||||
|
||||
RawRenderer.prototype.renderInDom = function() {
|
||||
var domNode = this.renderTree.document.createElement("div");
|
||||
domNode.innerHTML = this.parseTreeNode.html;
|
||||
return domNode;
|
||||
};
|
||||
|
||||
exports.raw = RawRenderer
|
||||
|
||||
})();
|
@ -1,31 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/renderers/text.js
|
||||
type: application/javascript
|
||||
module-type: wikirenderer
|
||||
|
||||
Text renderer
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Text renderer
|
||||
*/
|
||||
var TextRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
// Store state information
|
||||
this.renderTree = renderTree;
|
||||
this.parentRenderer = parentRenderer;
|
||||
this.parseTreeNode = parseTreeNode;
|
||||
};
|
||||
|
||||
TextRenderer.prototype.renderInDom = function() {
|
||||
return this.renderTree.document.createTextNode(this.parseTreeNode.text);
|
||||
};
|
||||
|
||||
exports.text = TextRenderer
|
||||
|
||||
})();
|
@ -1,198 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/rendertree/wikirendertree.js
|
||||
type: application/javascript
|
||||
module-type: global
|
||||
|
||||
Wiki text render tree
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Create a render tree object for a parse tree
|
||||
parser: reference to the parse tree to be rendered
|
||||
options: see below
|
||||
Options include:
|
||||
wiki: mandatory reference to wiki associated with this render tree
|
||||
context: optional hashmap of context variables (see below)
|
||||
parentRenderer: optional reference to a parent renderer node for the context chain
|
||||
document: optional document object to use instead of global document
|
||||
Context variables include:
|
||||
tiddlerTitle: title of the tiddler providing the context
|
||||
templateTitle: title of the tiddler providing the current template
|
||||
macroDefinitions: hashmap of macro definitions
|
||||
*/
|
||||
var WikiRenderTree = function(parser,options) {
|
||||
this.parser = parser;
|
||||
this.wiki = options.wiki;
|
||||
this.context = options.context || {};
|
||||
this.parentRenderer = options.parentRenderer;
|
||||
this.document = options.document;
|
||||
// Hashmap of the renderer classes
|
||||
if(!this.rendererClasses) {
|
||||
WikiRenderTree.prototype.rendererClasses = $tw.modules.applyMethods("wikirenderer");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Generate the full render tree for this parse tree
|
||||
*/
|
||||
WikiRenderTree.prototype.execute = function() {
|
||||
this.rendererTree = this.createRenderers(this,this.parser.tree);
|
||||
};
|
||||
|
||||
/*
|
||||
Create an array of renderers for an array of parse tree nodes
|
||||
*/
|
||||
WikiRenderTree.prototype.createRenderers = function(parentRenderer,parseTreeNodes) {
|
||||
var rendererNodes = [];
|
||||
if(parseTreeNodes) {
|
||||
for(var t=0; t<parseTreeNodes.length; t++) {
|
||||
rendererNodes.push(this.createRenderer(parentRenderer,parseTreeNodes[t]));
|
||||
}
|
||||
}
|
||||
return rendererNodes;
|
||||
};
|
||||
|
||||
/*
|
||||
Create a renderer node for a parse tree node
|
||||
*/
|
||||
WikiRenderTree.prototype.createRenderer = function(parentRenderer,parseTreeNode) {
|
||||
var RenderNodeClass = this.rendererClasses[parseTreeNode.type];
|
||||
return new RenderNodeClass(this,parentRenderer,parseTreeNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Render to the DOM
|
||||
*/
|
||||
WikiRenderTree.prototype.renderInDom = function(container) {
|
||||
this.container = container;
|
||||
$tw.utils.each(this.rendererTree,function(node) {
|
||||
if(node.renderInDom) {
|
||||
container.appendChild(node.renderInDom());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Update the DOM rendering in the light of a set of changes
|
||||
*/
|
||||
WikiRenderTree.prototype.refreshInDom = function(changes) {
|
||||
$tw.utils.each(this.rendererTree,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changes);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Find the value of a given context variable for a particular renderer node
|
||||
*/
|
||||
WikiRenderTree.prototype.getContextVariable = function(renderer,name,defaultValue) {
|
||||
while(renderer) {
|
||||
if($tw.utils.hop(renderer.context,name)) {
|
||||
return renderer.context[name];
|
||||
}
|
||||
renderer = renderer.parentRenderer;
|
||||
};
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
/*
|
||||
Check for render context recursion from a particular renderer node by returning true if the members of a proposed new render context are already present in the render context chain
|
||||
*/
|
||||
WikiRenderTree.prototype.checkContextRecursion = function(renderer,newContext) {
|
||||
while(renderer) {
|
||||
var context = renderer.context;
|
||||
if(context) {
|
||||
var match = true;
|
||||
for(var member in newContext) {
|
||||
if($tw.utils.hop(context,member)) {
|
||||
if(newContext[member] !== context[member]) {
|
||||
match = false;
|
||||
}
|
||||
} else {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
if(match) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
renderer = renderer.parentRenderer;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
WikiRenderTree.prototype.getContextScopeId = function(renderer) {
|
||||
var guidBits = [],
|
||||
scopeComponents = ["tiddlerTitle","templateTitle"],
|
||||
processContext = function(field,name) {
|
||||
if(scopeComponents.indexOf(name) !== -1) {
|
||||
guidBits.push(name + ":" + field + ";");
|
||||
}
|
||||
};
|
||||
while(renderer) {
|
||||
if(renderer.context) {
|
||||
$tw.utils.each(renderer.context,processContext);
|
||||
guidBits.push("-");
|
||||
}
|
||||
renderer = renderer.parentRenderer;
|
||||
}
|
||||
return guidBits.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Find a named macro definition
|
||||
*/
|
||||
WikiRenderTree.prototype.findMacroDefinition = function(renderer,name) {
|
||||
while(renderer) {
|
||||
if(renderer.context && renderer.context.macroDefinitions && renderer.context.macroDefinitions[name]) {
|
||||
return renderer.context.macroDefinitions[name];
|
||||
}
|
||||
renderer = renderer.parentRenderer;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Expand the parameters of a macro
|
||||
*/
|
||||
WikiRenderTree.prototype.substituteParameters = function(macroDefinition,macroCallParseTreeNode) {
|
||||
var text = macroDefinition.text,
|
||||
nextAnonParameter = 0; // Next candidate anonymous parameter in macro call
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<macroDefinition.params.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
var paramInfo = macroDefinition.params[p],
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<macroCallParseTreeNode.params.length; m++) {
|
||||
if(macroCallParseTreeNode.params[m].name === paramInfo.name) {
|
||||
paramValue = macroCallParseTreeNode.params[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
if(!paramValue && nextAnonParameter < macroCallParseTreeNode.params.length) {
|
||||
while(macroCallParseTreeNode.params[nextAnonParameter].name && nextAnonParameter < macroCallParseTreeNode.params.length-1) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(!macroCallParseTreeNode.params[nextAnonParameter].name) {
|
||||
paramValue = macroCallParseTreeNode.params[nextAnonParameter].value;
|
||||
nextAnonParameter++;
|
||||
}
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Replace any instances of this parameter
|
||||
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
exports.WikiRenderTree = WikiRenderTree;
|
||||
|
||||
})();
|
61
core/modules/startup.js
Normal file → Executable file
61
core/modules/startup.js
Normal file → Executable file
@ -12,6 +12,8 @@ This is the main application logic for both the client and server
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
var modules,n,m,f,commander;
|
||||
// Load modules
|
||||
@ -26,6 +28,7 @@ exports.startup = function() {
|
||||
$tw.modules.applyMethods("tiddlermethod",$tw.Tiddler.prototype);
|
||||
$tw.modules.applyMethods("wikimethod",$tw.Wiki.prototype);
|
||||
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
|
||||
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
|
||||
// Set up the parsers
|
||||
$tw.wiki.initParsers();
|
||||
// Set up the syncer object
|
||||
@ -50,39 +53,44 @@ exports.startup = function() {
|
||||
$tw.wiki.addTiddler({title: storyTitle, text: "", list: story},$tw.wiki.getModificationFields());
|
||||
// Host-specific startup
|
||||
if($tw.browser) {
|
||||
// Call browser startup modules
|
||||
$tw.modules.forEachModuleOfType("browser-startup",function(title,module) {
|
||||
if(module.startup) {
|
||||
module.startup();
|
||||
}
|
||||
});
|
||||
// Install the popup manager
|
||||
$tw.popup = new $tw.utils.Popup({
|
||||
rootElement: document.body
|
||||
});
|
||||
// Install the animator
|
||||
$tw.anim = new $tw.utils.Animator();
|
||||
// Kick off the stylesheet manager
|
||||
$tw.stylesheetManager = new $tw.utils.StylesheetManager($tw.wiki);
|
||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||
$tw.rootWidget = new widget.widget({type: "widget", children: []},{
|
||||
wiki: $tw.wiki,
|
||||
document: document
|
||||
});
|
||||
// Install the modal message mechanism
|
||||
$tw.modal = new $tw.utils.Modal($tw.wiki);
|
||||
document.addEventListener("tw-modal",function(event) {
|
||||
$tw.rootWidget.addEventListener("tw-modal",function(event) {
|
||||
$tw.modal.display(event.param);
|
||||
},false);
|
||||
});
|
||||
// Install the notification mechanism
|
||||
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
|
||||
document.addEventListener("tw-notify",function(event) {
|
||||
$tw.rootWidget.addEventListener("tw-notify",function(event) {
|
||||
$tw.notifier.display(event.param);
|
||||
},false);
|
||||
});
|
||||
// Install the scroller
|
||||
$tw.pageScroller = new $tw.utils.PageScroller();
|
||||
document.addEventListener("tw-scroll",$tw.pageScroller,false);
|
||||
$tw.rootWidget.addEventListener("tw-scroll",function(event) {
|
||||
$tw.pageScroller.handleEvent(event);
|
||||
});
|
||||
// Install the save action handler
|
||||
$tw.wiki.initSavers();
|
||||
document.addEventListener("tw-save-wiki",function(event) {
|
||||
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
|
||||
$tw.wiki.saveWiki({
|
||||
template: event.param,
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
},false);
|
||||
});
|
||||
// Install the crypto event handlers
|
||||
document.addEventListener("tw-set-password",function(event) {
|
||||
$tw.rootWidget.addEventListener("tw-set-password",function(event) {
|
||||
$tw.passwordPrompt.createPrompt({
|
||||
serviceName: "Set a new password for this TiddlyWiki",
|
||||
noUserName: true,
|
||||
@ -93,24 +101,19 @@ exports.startup = function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
document.addEventListener("tw-clear-password",function(event) {
|
||||
$tw.rootWidget.addEventListener("tw-clear-password",function(event) {
|
||||
$tw.crypto.setPassword(null);
|
||||
});
|
||||
// Install the animator
|
||||
$tw.anim = new $tw.utils.Animator();
|
||||
// Kick off the stylesheet manager
|
||||
$tw.stylesheetManager = new $tw.utils.StylesheetManager($tw.wiki);
|
||||
// Display the PageTemplate
|
||||
var templateTitle = "$:/core/ui/PageTemplate",
|
||||
parser = $tw.wiki.parseTiddler(templateTitle),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: $tw.wiki, context: {tiddlerTitle: templateTitle}, document: document});
|
||||
renderTree.execute();
|
||||
// Display the PageMacros, which includes the PageTemplate
|
||||
var templateTitle = "$:/core/ui/PageMacros",
|
||||
parser = $tw.wiki.new_parseTiddler(templateTitle);
|
||||
$tw.pageWidgetNode = $tw.wiki.makeWidget(parser,{document: document, parentWidget: $tw.rootWidget});
|
||||
$tw.pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.pageContainer,"tw-page-container");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
renderTree.renderInDom($tw.pageContainer);
|
||||
$tw.pageWidgetNode.render($tw.pageContainer,null);
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
renderTree.refreshInDom(changes);
|
||||
$tw.pageWidgetNode.refresh(changes,$tw.pageContainer,null);
|
||||
});
|
||||
// If we're being viewed on a data: URI then give instructions for how to save
|
||||
if(document.location.protocol === "data:") {
|
||||
@ -118,6 +121,12 @@ exports.startup = function() {
|
||||
param: "$:/messages/SaveInstructions"
|
||||
});
|
||||
}
|
||||
// Call browser startup modules
|
||||
$tw.modules.forEachModuleOfType("browser-startup",function(title,module) {
|
||||
if(module.startup) {
|
||||
module.startup();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// On the server, start a commander with the command line arguments
|
||||
commander = new $tw.Commander(
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/list/listviews/classic.js
|
||||
title: $:/core/modules/storyviews/classic.js
|
||||
type: application/javascript
|
||||
module-type: listview
|
||||
module-type: storyview
|
||||
|
||||
Views the list as a linear sequence
|
||||
Views the story as a linear sequence
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@ -12,30 +12,28 @@ Views the list as a linear sequence
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ClassicListView = function(listWidget) {
|
||||
var ClassicStoryView = function(listWidget) {
|
||||
this.listWidget = listWidget;
|
||||
}
|
||||
|
||||
ClassicListView.prototype.navigateTo = function(historyInfo) {
|
||||
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title);
|
||||
ClassicStoryView.prototype.navigateTo = function(historyInfo) {
|
||||
var listElementIndex = this.listWidget.findListItem(0,historyInfo.title);
|
||||
if(listElementIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
var listElementNode = this.listWidget.children[listElementIndex],
|
||||
targetElement = listElementNode.domNode;
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// Scroll the node into view
|
||||
var scrollEvent = this.listWidget.renderer.renderTree.document.createEvent("Event");
|
||||
scrollEvent.initEvent("tw-scroll",true,true);
|
||||
targetElement.dispatchEvent(scrollEvent);
|
||||
this.listWidget.dispatchEvent({type: "tw-scroll", target: targetElement});
|
||||
};
|
||||
|
||||
ClassicListView.prototype.insert = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode,
|
||||
ClassicStoryView.prototype.insert = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode(),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Get the current height of the tiddler
|
||||
var currMarginBottom = parseInt(window.getComputedStyle(targetElement).marginBottom,10),
|
||||
currMarginTop = parseInt(window.getComputedStyle(targetElement).marginTop,10),
|
||||
var computedStyle = window.getComputedStyle(targetElement),
|
||||
currMarginBottom = parseInt(computedStyle.marginBottom,10),
|
||||
currMarginTop = parseInt(computedStyle.marginTop,10),
|
||||
currHeight = targetElement.offsetHeight + currMarginTop;
|
||||
// Reset the margin once the transition is over
|
||||
setTimeout(function() {
|
||||
@ -60,20 +58,18 @@ ClassicListView.prototype.insert = function(index) {
|
||||
]);
|
||||
};
|
||||
|
||||
ClassicListView.prototype.remove = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode,
|
||||
ClassicStoryView.prototype.remove = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode(),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Get the current height of the tiddler
|
||||
var currWidth = targetElement.offsetWidth,
|
||||
currMarginBottom = parseInt(window.getComputedStyle(targetElement).marginBottom,10),
|
||||
currMarginTop = parseInt(window.getComputedStyle(targetElement).marginTop,10),
|
||||
computedStyle = window.getComputedStyle(targetElement),
|
||||
currMarginBottom = parseInt(computedStyle.marginBottom,10),
|
||||
currMarginTop = parseInt(computedStyle.marginTop,10),
|
||||
currHeight = targetElement.offsetHeight + currMarginTop;
|
||||
// Remove the element at the end of the transition
|
||||
// Remove the dom nodes of the widget at the end of the transition
|
||||
setTimeout(function() {
|
||||
if(targetElement.parentNode) {
|
||||
targetElement.parentNode.removeChild(targetElement);
|
||||
}
|
||||
widget.removeChildDomNodes();
|
||||
},duration);
|
||||
// Animate the closure
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
@ -91,10 +87,8 @@ ClassicListView.prototype.remove = function(index) {
|
||||
{marginBottom: (-currHeight) + "px"},
|
||||
{opacity: "0.0"}
|
||||
]);
|
||||
// Returning true causes the DOM node not to be deleted
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.classic = ClassicListView;
|
||||
exports.classic = ClassicStoryView;
|
||||
|
||||
})();
|
||||
})();
|
@ -1,7 +1,7 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/list/listviews/pop.js
|
||||
title: $:/core/modules/storyviews/pop.js
|
||||
type: application/javascript
|
||||
module-type: listview
|
||||
module-type: storyview
|
||||
|
||||
Animates list insertions and removals
|
||||
|
||||
@ -12,13 +12,23 @@ Animates list insertions and removals
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var PopListView = function(listWidget) {
|
||||
var PopStoryView = function(listWidget) {
|
||||
this.listWidget = listWidget;
|
||||
}
|
||||
|
||||
PopListView.prototype.insert = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode,
|
||||
PopStoryView.prototype.navigateTo = function(historyInfo) {
|
||||
var listElementIndex = this.listWidget.findListItem(0,historyInfo.title);
|
||||
if(listElementIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// Scroll the node into view
|
||||
this.listWidget.dispatchEvent({type: "tw-scroll", target: targetElement});
|
||||
};
|
||||
|
||||
PopStoryView.prototype.insert = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode(),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Reset once the transition is over
|
||||
setTimeout(function() {
|
||||
@ -43,14 +53,13 @@ PopListView.prototype.insert = function(index) {
|
||||
]);
|
||||
};
|
||||
|
||||
PopListView.prototype.remove = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode,
|
||||
PopStoryView.prototype.remove = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode(),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Remove the element at the end of the transition
|
||||
setTimeout(function() {
|
||||
if(targetElement.parentNode) {
|
||||
targetElement.parentNode.removeChild(targetElement);
|
||||
widget.removeChildDomNodes();
|
||||
}
|
||||
},duration);
|
||||
// Animate the closure
|
||||
@ -66,10 +75,8 @@ PopListView.prototype.remove = function(index) {
|
||||
{transform: "scale(0.1)"},
|
||||
{opacity: "0.0"}
|
||||
]);
|
||||
// Returning true causes the DOM node not to be deleted
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.pop = PopListView;
|
||||
exports.pop = PopStoryView;
|
||||
|
||||
})();
|
@ -1,7 +1,7 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/list/listviews/zoomin.js
|
||||
title: $:/core/modules/storyviews/zoomin.js
|
||||
type: application/javascript
|
||||
module-type: listview
|
||||
module-type: storyview
|
||||
|
||||
Zooms between individual tiddlers
|
||||
|
||||
@ -13,131 +13,32 @@ Zooms between individual tiddlers
|
||||
"use strict";
|
||||
|
||||
var ZoominListView = function(listWidget) {
|
||||
var self = this;
|
||||
this.listWidget = listWidget;
|
||||
this.storyNode = this.listWidget.renderer.domNode;
|
||||
// Set the current tiddler
|
||||
this.currentTiddler = this.listWidget.children[0].domNode;
|
||||
// Make all the tiddlers position absolute, and hide all but the first one
|
||||
this.storyNode.style.position = "relative";
|
||||
for(var t=0; t<this.storyNode.children.length; t++) {
|
||||
if(t) {
|
||||
this.storyNode.children[t].style.display = "none";
|
||||
$tw.utils.each(this.listWidget.children,function(itemWidget,index) {
|
||||
var domNode = itemWidget.findFirstDomNode();
|
||||
if(index) {
|
||||
domNode.style.display = "none";
|
||||
} else {
|
||||
self.currentTiddlerDomNode = domNode;
|
||||
}
|
||||
this.storyNode.children[t].style.position = "absolute";
|
||||
}
|
||||
};
|
||||
domNode.style.position = "absolute";
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Find the first descendent node that is a <$view field="title"> widget
|
||||
*/
|
||||
function findTitleNode(node) {
|
||||
var t,r;
|
||||
// Return true if this node is a view widget with the field attribute set to "title"
|
||||
if(node instanceof $tw.WikiRenderTree.prototype.rendererClasses.element) {
|
||||
if(node.widget instanceof $tw.WikiRenderTree.prototype.rendererClasses.element.prototype.widgetClasses.view && node.attributes.field === "title") {
|
||||
return node;
|
||||
}
|
||||
if(node.widget.children) {
|
||||
for(t=0; t<node.widget.children.length; t++) {
|
||||
var r = findTitleNode(node.widget.children[t]);
|
||||
if(r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(node.children) {
|
||||
for(t=0; t<node.children.length; t++) {
|
||||
var r = findTitleNode(node.children[t]);
|
||||
if(r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function findTitleWidget() {
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoominListView.prototype.insert = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode;
|
||||
// Make the newly inserted node position absolute and hidden
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{display: "none"},
|
||||
{position: "absolute"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Visualise navigating back to the previous tiddler
|
||||
storyElement: story element being closed
|
||||
*/
|
||||
ZoominListView.prototype.remove = function(index) {
|
||||
var listElementNode = this.listWidget.children[index],
|
||||
targetElement = listElementNode.domNode,
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Set up the tiddler that is being closed
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{position: "absolute"},
|
||||
{display: "block"},
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(1)"},
|
||||
{transition: "none"},
|
||||
{zIndex: "0"}
|
||||
]);
|
||||
// We'll move back to the previous or next element in the story
|
||||
var toElement = this.storyNode.children[index - 1];
|
||||
if(!toElement) {
|
||||
toElement = this.storyNode.children[index + 1];
|
||||
}
|
||||
// Set up the tiddler we're moving back in
|
||||
if(toElement) {
|
||||
$tw.utils.setStyle(toElement,[
|
||||
{position: "absolute"},
|
||||
{display: "block"},
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(10)"},
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
|
||||
{opacity: "0"},
|
||||
{zIndex: "500"}
|
||||
]);
|
||||
this.currentTiddler = toElement;
|
||||
}
|
||||
// Animate them both
|
||||
// Force layout
|
||||
$tw.utils.forceLayout(this.storyNode);
|
||||
// First, the tiddler we're closing
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(0.1)"},
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
|
||||
{opacity: "0"},
|
||||
{zIndex: "0"}
|
||||
]);
|
||||
setTimeout(function() {
|
||||
// Delete the DOM node when the transition is over
|
||||
if(targetElement.parentNode) {
|
||||
targetElement.parentNode.removeChild(targetElement);
|
||||
}
|
||||
},duration);
|
||||
// Now the tiddler we're going back to
|
||||
if(toElement) {
|
||||
$tw.utils.setStyle(toElement,[
|
||||
{transform: "translateX(0px) translateY(0px) scale(1)"},
|
||||
{opacity: "1"}
|
||||
]);
|
||||
}
|
||||
return true; // Indicate that we'll delete the DOM node
|
||||
};
|
||||
|
||||
ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
var duration = $tw.utils.getAnimationDuration(),
|
||||
listElementIndex = this.listWidget.findListItem(0,historyInfo.title);
|
||||
if(listElementIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
var listElementNode = this.listWidget.children[listElementIndex],
|
||||
targetElement = listElementNode.domNode;
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// Make the new tiddler be position absolute and visible so that we can measure it
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{position: "absolute"},
|
||||
@ -155,8 +56,8 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
height: window.innerHeight/8
|
||||
};
|
||||
// Try to find the title node in the target tiddler
|
||||
var titleNode = findTitleNode(listElementNode) || listElementNode,
|
||||
zoomBounds = titleNode.domNode.getBoundingClientRect();
|
||||
var titleWidget = findTitleWidget(listItemWidget) || listItemWidget,
|
||||
zoomBounds = titleWidget.findFirstDomNode().getBoundingClientRect();
|
||||
// Compute the transform for the target tiddler to make the title lie over the source rectange
|
||||
var targetBounds = targetElement.getBoundingClientRect(),
|
||||
scale = sourceBounds.width / zoomBounds.width,
|
||||
@ -170,8 +71,8 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
$tw.utils.forceLayout(targetElement);
|
||||
// Apply the ending transitions with a timeout to ensure that the previously applied transformations are applied first
|
||||
var self = this,
|
||||
prevCurrentTiddler = this.currentTiddler;
|
||||
this.currentTiddler = targetElement;
|
||||
prevCurrentTiddler = this.currentTiddlerDomNode;
|
||||
this.currentTiddlerDomNode = targetElement;
|
||||
// Transform the target tiddler to its natural size
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
|
||||
@ -193,7 +94,7 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
]);
|
||||
// Hide the tiddler when the transition has finished
|
||||
setTimeout(function() {
|
||||
if(self.currentTiddler !== prevCurrentTiddler) {
|
||||
if(self.currentTiddlerDomNode !== prevCurrentTiddler) {
|
||||
prevCurrentTiddler.style.display = "none";
|
||||
}
|
||||
},duration);
|
||||
@ -202,6 +103,71 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
// $tw.pageScroller.scrollIntoView(targetElement);
|
||||
};
|
||||
|
||||
ZoominListView.prototype.insert = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode();
|
||||
// Make the newly inserted node position absolute and hidden
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{display: "none"},
|
||||
{position: "absolute"}
|
||||
]);
|
||||
};
|
||||
|
||||
ZoominListView.prototype.remove = function(widget) {
|
||||
var targetElement = widget.findFirstDomNode(),
|
||||
duration = $tw.utils.getAnimationDuration();
|
||||
// Set up the tiddler that is being closed
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{position: "absolute"},
|
||||
{display: "block"},
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(1)"},
|
||||
{transition: "none"},
|
||||
{zIndex: "0"}
|
||||
]);
|
||||
// We'll move back to the previous or next element in the story
|
||||
var toWidget = widget.previousSibling();
|
||||
if(!toWidget) {
|
||||
toWidget = widget.nextSibling();
|
||||
}
|
||||
var toWidgetDomNode = toWidget && toWidget.findFirstDomNode();
|
||||
// Set up the tiddler we're moving back in
|
||||
if(toWidgetDomNode) {
|
||||
$tw.utils.setStyle(toWidgetDomNode,[
|
||||
{position: "absolute"},
|
||||
{display: "block"},
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(10)"},
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
|
||||
{opacity: "0"},
|
||||
{zIndex: "500"}
|
||||
]);
|
||||
this.currentTiddlerDomNode = toWidgetDomNode;
|
||||
}
|
||||
// Animate them both
|
||||
// Force layout
|
||||
$tw.utils.forceLayout(this.listWidget.parentDomNode);
|
||||
// First, the tiddler we're closing
|
||||
$tw.utils.setStyle(targetElement,[
|
||||
{transformOrigin: "50% 50%"},
|
||||
{transform: "translateX(0px) translateY(0px) scale(0.1)"},
|
||||
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in, opacity " + duration + "ms ease-in"},
|
||||
{opacity: "0"},
|
||||
{zIndex: "0"}
|
||||
]);
|
||||
setTimeout(function() {
|
||||
// Delete the DOM node when the transition is over
|
||||
widget.removeChildDomNodes();
|
||||
},duration);
|
||||
// Now the tiddler we're going back to
|
||||
if(toWidgetDomNode) {
|
||||
$tw.utils.setStyle(toWidgetDomNode,[
|
||||
{transform: "translateX(0px) translateY(0px) scale(1)"},
|
||||
{opacity: "1"}
|
||||
]);
|
||||
}
|
||||
return true; // Indicate that we'll delete the DOM node
|
||||
};
|
||||
|
||||
exports.zoomin = ZoominListView;
|
||||
|
||||
})();
|
||||
})();
|
@ -13,7 +13,8 @@ A simple slide animation that varies the height of the element
|
||||
"use strict";
|
||||
|
||||
function slideOpen(domNode,options) {
|
||||
var duration = $tw.utils.getAnimationDuration();
|
||||
options = options || {};
|
||||
var duration = options.duration || $tw.utils.getAnimationDuration();
|
||||
// Get the current height of the domNode
|
||||
var computedStyle = window.getComputedStyle(domNode),
|
||||
currMarginBottom = parseInt(computedStyle.marginBottom,10),
|
||||
@ -65,7 +66,8 @@ function slideOpen(domNode,options) {
|
||||
}
|
||||
|
||||
function slideClosed(domNode,options) {
|
||||
var duration = $tw.utils.getAnimationDuration(),
|
||||
options = options || {};
|
||||
var duration = options.duration || $tw.utils.getAnimationDuration(),
|
||||
currHeight = domNode.offsetHeight;
|
||||
// Clear the properties we've set when the animation is over
|
||||
setTimeout(function() {
|
||||
|
@ -27,10 +27,15 @@ Animator.prototype.perform = function(type,domNode,options) {
|
||||
chosenAnimation = animation[type];
|
||||
}
|
||||
});
|
||||
// Call the animation
|
||||
if(chosenAnimation) {
|
||||
chosenAnimation(domNode,options);
|
||||
if(!chosenAnimation) {
|
||||
chosenAnimation = function(domNode,options) {
|
||||
if(options.callback) {
|
||||
options.callback();
|
||||
}
|
||||
};
|
||||
}
|
||||
// Call the animation
|
||||
chosenAnimation(domNode,options);
|
||||
};
|
||||
|
||||
exports.Animator = Animator;
|
||||
|
@ -117,14 +117,16 @@ exports.getBoundingPageRect = function(element) {
|
||||
Saves a named password in the browser
|
||||
*/
|
||||
exports.savePassword = function(name,password) {
|
||||
localStorage.setItem("tw5-password-" + name,password);
|
||||
if(window.localStorage) {
|
||||
localStorage.setItem("tw5-password-" + name,password);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Retrieve a named password from the browser
|
||||
*/
|
||||
exports.getPassword = function(name) {
|
||||
return localStorage.getItem("tw5-password-" + name);
|
||||
return window.localStorage ? localStorage.getItem("tw5-password-" + name) : "";
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -12,6 +12,8 @@ Modal message mechanism
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
var Modal = function(wiki) {
|
||||
this.wiki = wiki;
|
||||
this.modalCount = 0;
|
||||
@ -71,20 +73,18 @@ Modal.prototype.display = function(title,options) {
|
||||
} else {
|
||||
titleText = title;
|
||||
}
|
||||
var headerParser = this.wiki.parseText("text/vnd.tiddlywiki",titleText,{parseAsInline: true}),
|
||||
headerRenderTree = new $tw.WikiRenderTree(headerParser,{wiki: $tw.wiki, context: {tiddlerTitle: title}, document: document});
|
||||
headerRenderTree.execute();
|
||||
headerRenderTree.renderInDom(headerTitle);
|
||||
var headerParser = this.wiki.new_parseText("text/vnd.tiddlywiki",titleText,{parseAsInline: true}),
|
||||
headerWidgetNode = this.wiki.makeWidget(headerParser,{parentWidget: $tw.rootWidget, document: document});
|
||||
headerWidgetNode.render(headerTitle,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
headerRenderTree.refreshInDom(changes);
|
||||
headerWidgetNode.refresh(changes,modalHeader,null);
|
||||
});
|
||||
// Render the body of the message
|
||||
var bodyParser = this.wiki.parseTiddler(title),
|
||||
bodyRenderTree = new $tw.WikiRenderTree(bodyParser,{wiki: $tw.wiki, context: {tiddlerTitle: title}, document: document});
|
||||
bodyRenderTree.execute();
|
||||
bodyRenderTree.renderInDom(modalBody);
|
||||
var bodyParser = this.wiki.new_parseTiddler(title),
|
||||
bodyWidgetNode = this.wiki.makeWidget(bodyParser,{parentWidget: $tw.rootWidget, document: document});
|
||||
bodyWidgetNode.render(modalBody,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
bodyRenderTree.refreshInDom(changes);
|
||||
bodyWidgetNode.refresh(changes,modalBody,null);
|
||||
});
|
||||
// Setup the link if present
|
||||
if(options.downloadLink) {
|
||||
@ -107,15 +107,14 @@ Modal.prototype.display = function(title,options) {
|
||||
} else {
|
||||
footerText = '<$button message="tw-close-tiddler" class="btn btn-primary">Close</$button>';
|
||||
}
|
||||
var footerParser = this.wiki.parseText("text/vnd.tiddlywiki",footerText,{parseAsInline: true}),
|
||||
footerRenderTree = new $tw.WikiRenderTree(footerParser,{wiki: $tw.wiki, context: {tiddlerTitle: title}, document: document});
|
||||
footerRenderTree.execute();
|
||||
footerRenderTree.renderInDom(modalFooterButtons);
|
||||
var footerParser = this.wiki.new_parseText("text/vnd.tiddlywiki",footerText,{parseAsInline: true}),
|
||||
footerWidgetNode = this.wiki.makeWidget(footerParser,{parentWidget: $tw.rootWidget, document: document});
|
||||
footerWidgetNode.render(modalFooterButtons,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
footerRenderTree.refreshInDom(changes);
|
||||
footerWidgetNode.refresh(changes,modalFooterButtons,null);
|
||||
});
|
||||
// Add the close event handler
|
||||
wrapper.addEventListener("tw-close-tiddler",function(event) {
|
||||
var closeHandler = function(event) {
|
||||
// Decrease the modal count and adjust the body class
|
||||
self.modalCount--;
|
||||
self.adjustPageClass();
|
||||
@ -136,9 +135,11 @@ Modal.prototype.display = function(title,options) {
|
||||
}
|
||||
},duration);
|
||||
// Don't let anyone else handle the tw-close-tiddler message
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},false);
|
||||
};
|
||||
headerWidgetNode.addEventListener("tw-close-tiddler",closeHandler,false);
|
||||
bodyWidgetNode.addEventListener("tw-close-tiddler",closeHandler,false);
|
||||
footerWidgetNode.addEventListener("tw-close-tiddler",closeHandler,false);
|
||||
// Set the initial styles for the message
|
||||
$tw.utils.setStyle(modalBackdrop,[
|
||||
{opacity: "0"}
|
||||
|
@ -12,6 +12,8 @@ Notifier mechanism
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
var Notifier = function(wiki) {
|
||||
this.wiki = wiki;
|
||||
};
|
||||
@ -35,12 +37,11 @@ Notifier.prototype.display = function(title,options) {
|
||||
// Add classes
|
||||
$tw.utils.addClass(notification,"tw-notification");
|
||||
// Render the body of the notification
|
||||
var bodyParser = this.wiki.parseTiddler(title),
|
||||
bodyRenderTree = new $tw.WikiRenderTree(bodyParser,{wiki: $tw.wiki, context: {tiddlerTitle: title}, document: document});
|
||||
bodyRenderTree.execute();
|
||||
bodyRenderTree.renderInDom(notification);
|
||||
var parser = this.wiki.new_parseTiddler(title),
|
||||
widgetNode = this.wiki.makeWidget(parser,{parentWidget: $tw.rootWidget, document: document});
|
||||
widgetNode.render(notification,null);
|
||||
this.wiki.addEventListener("change",function(changes) {
|
||||
bodyRenderTree.refreshInDom(changes);
|
||||
widgetNode.refresh(changes,notification,null);
|
||||
});
|
||||
// Set the initial styles for the notification
|
||||
$tw.utils.setStyle(notification,[
|
||||
|
@ -30,7 +30,7 @@ Popup.prototype.show = function(options) {
|
||||
};
|
||||
|
||||
Popup.prototype.handleEvent = function(event) {
|
||||
if(event.type === "click" && !$tw.utils.domContains(this.anchorDomNode,event.target)) {
|
||||
if(event.type === "click" && this.anchorDomNode !== event.target && !$tw.utils.domContains(this.anchorDomNode,event.target)) {
|
||||
this.cancel();
|
||||
}
|
||||
};
|
||||
@ -54,8 +54,7 @@ Popup.prototype.triggerPopup = function(options) {
|
||||
// Get the current popup state tiddler
|
||||
var value = options.wiki.getTextReference(options.title,"");
|
||||
// Check if the popup is open by checking whether it matches "(<x>,<y>)"
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
state = !popupLocationRegExp.test(value);
|
||||
var state = !this.readPopupState(options.title,value);
|
||||
if("force" in options) {
|
||||
state = options.force;
|
||||
}
|
||||
@ -71,6 +70,18 @@ Popup.prototype.triggerPopup = function(options) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the specified title and text identifies an active popup
|
||||
*/
|
||||
Popup.prototype.readPopupState = function(title,text) {
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
result = false;
|
||||
if(this.title === title) {
|
||||
result = popupLocationRegExp.test(text);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.Popup = Popup;
|
||||
|
||||
})();
|
||||
|
@ -34,12 +34,7 @@ StylesheetManager.prototype.addStylesheet = function(title) {
|
||||
// Record the stylesheet in the hashmap
|
||||
this.stylesheets[title] = true;
|
||||
// Parse the tiddler and render as plain text
|
||||
var parser = this.wiki.parseTiddler(title),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: this.wiki, context: {tiddlerTitle: title}, document: $tw.document});
|
||||
renderTree.execute();
|
||||
var container = $tw.document.createElement("div");
|
||||
renderTree.renderInDom(container);
|
||||
var text = container.textContent;
|
||||
var text = this.wiki.new_renderTiddler("text/plain",title);
|
||||
// Create a style element and put it in the document
|
||||
var styleNode = document.createElement("style");
|
||||
styleNode.setAttribute("type","text/css");
|
||||
|
59
core/modules/utils/fakedom.js
Normal file → Executable file
59
core/modules/utils/fakedom.js
Normal file → Executable file
@ -12,16 +12,29 @@ A barebones implementation of DOM interfaces needed by the rendering mechanism.
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var TW_TextNode = function(text) {
|
||||
this.textContent = text;
|
||||
// Sequence number used to enable us to track objects for testing
|
||||
var sequenceNumber = null;
|
||||
|
||||
var bumpSequenceNumber = function(object) {
|
||||
if(sequenceNumber !== null) {
|
||||
object.sequenceNumber = sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
var TW_Element = function(tag) {
|
||||
var TW_TextNode = function(text) {
|
||||
bumpSequenceNumber(this);
|
||||
this.textContent = text;
|
||||
};
|
||||
|
||||
var TW_Element = function(tag,namespace) {
|
||||
bumpSequenceNumber(this);
|
||||
this.isTiddlyWikiFakeDom = true;
|
||||
this.tag = tag;
|
||||
this.attributes = {};
|
||||
this.isRaw = false;
|
||||
this.children = [];
|
||||
}
|
||||
this.namespaceURI = namespace || "http://www.w3.org/1999/xhtml";
|
||||
};
|
||||
|
||||
TW_Element.prototype.setAttribute = function(name,value) {
|
||||
if(this.isRaw) {
|
||||
@ -34,11 +47,33 @@ TW_Element.prototype.setAttributeNS = function(namespace,name,value) {
|
||||
this.setAttribute(name,value);
|
||||
};
|
||||
|
||||
TW_Element.prototype.removeAttribute = function(name) {
|
||||
if(this.isRaw) {
|
||||
throw "Cannot removeAttribute on a raw TW_Element";
|
||||
}
|
||||
if($tw.utils.hop(this.attributes,name)) {
|
||||
delete this.attributes[name];
|
||||
}
|
||||
};
|
||||
|
||||
TW_Element.prototype.appendChild = function(node) {
|
||||
this.children.push(node);
|
||||
node.parentNode = this;
|
||||
};
|
||||
|
||||
TW_Element.prototype.insertBefore = function(node,nextSibling) {
|
||||
if(nextSibling) {
|
||||
var p = this.children.indexOf(nextSibling);
|
||||
if(p !== -1) {
|
||||
this.children.splice(p,0,node);
|
||||
} else {
|
||||
this.appendChild(node);
|
||||
}
|
||||
} else {
|
||||
this.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
TW_Element.prototype.removeChild = function(node) {
|
||||
var p = this.children.indexOf(node);
|
||||
if(p !== -1) {
|
||||
@ -60,6 +95,15 @@ TW_Element.prototype.addEventListener = function(type,listener,useCapture) {
|
||||
// Do nothing
|
||||
};
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "className", {
|
||||
get: function() {
|
||||
return this.attributes["class"] || "";
|
||||
},
|
||||
set: function(value) {
|
||||
this.attributes["class"] = value;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "outerHTML", {
|
||||
get: function() {
|
||||
var output = [],attr,a,v;
|
||||
@ -77,7 +121,7 @@ Object.defineProperty(TW_Element.prototype, "outerHTML", {
|
||||
}
|
||||
}
|
||||
}
|
||||
output.push(">\n");
|
||||
output.push(">");
|
||||
if($tw.config.htmlVoidElements.indexOf(this.tag) === -1) {
|
||||
output.push(this.innerHTML);
|
||||
output.push("</",this.tag,">");
|
||||
@ -123,8 +167,11 @@ Object.defineProperty(TW_Element.prototype, "textContent", {
|
||||
});
|
||||
|
||||
var document = {
|
||||
setSequenceNumber: function(value) {
|
||||
sequenceNumber = value;
|
||||
},
|
||||
createElementNS: function(namespace,tag) {
|
||||
return new TW_Element(tag);
|
||||
return new TW_Element(tag,namespace);
|
||||
},
|
||||
createElement: function(tag) {
|
||||
return new TW_Element(tag);
|
||||
|
@ -351,7 +351,7 @@ Returns an object with the following fields, all optional:
|
||||
*/
|
||||
exports.parseTextReference = function(textRef) {
|
||||
// Separate out the title, field name and/or JSON indices
|
||||
var reTextRef = /^\s*([^\s!#]+)?(?:(?:!!([^\s]+))|(?:##([^\s]+)))?\s*/mg,
|
||||
var reTextRef = /^\s*([^!#]+)?(?:(?:!!([^\s]+))|(?:##([^\s]+)))?\s*/mg,
|
||||
match = reTextRef.exec(textRef);
|
||||
if(match && reTextRef.lastIndex === textRef.length) {
|
||||
// Return the parts
|
||||
|
@ -1,150 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/button.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the button widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ButtonWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.message = this.renderer.getAttribute("message");
|
||||
this.param = this.renderer.getAttribute("param");
|
||||
this.set = this.renderer.getAttribute("set");
|
||||
this.setTo = this.renderer.getAttribute("setTo");
|
||||
this.popup = this.renderer.getAttribute("popup");
|
||||
this.hover = this.renderer.getAttribute("hover");
|
||||
this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
this.selectedClass = this.renderer.getAttribute("selectedClass");
|
||||
// Compose the button
|
||||
var classes = ["tw-button"];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
var events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
|
||||
if(this.hover === "yes") {
|
||||
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
||||
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
||||
}
|
||||
if(this.set && this.setTo && this.selectedClass) {
|
||||
if(this.isSelected()) {
|
||||
classes.push(this.selectedClass);
|
||||
}
|
||||
}
|
||||
// Set the return element
|
||||
this.tag = "button";
|
||||
this.attributes ={"class": classes.join(" ")};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
this.events = events;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.dispatchMessage = function(event) {
|
||||
$tw.utils.dispatchCustomEvent(event.target,this.message,{
|
||||
param: this.param,
|
||||
tiddlerTitle: this.renderer.tiddlerTitle
|
||||
});
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.triggerPopup = function(event) {
|
||||
var title = this.popup;
|
||||
if(this.qualifyTiddlerTitles) {
|
||||
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
}
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.renderer.domNode,
|
||||
title: title,
|
||||
wiki: this.renderer.renderTree.wiki
|
||||
});
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.isSelected = function() {
|
||||
var title = this.set;
|
||||
if(this.qualifyTiddlerTitles) {
|
||||
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
}
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(title);
|
||||
return tiddler ? tiddler.fields.text === this.setTo : false;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.setTiddler = function() {
|
||||
var title = this.set;
|
||||
if(this.qualifyTiddlerTitles) {
|
||||
title = title + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
}
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(title);
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: title, text: this.setTo}));
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.handleClickEvent = function(event) {
|
||||
var handled = false;
|
||||
if(this.message) {
|
||||
this.dispatchMessage(event);
|
||||
handled = true;
|
||||
}
|
||||
if(this.popup) {
|
||||
this.triggerPopup(event);
|
||||
handled = true;
|
||||
}
|
||||
if(this.set) {
|
||||
this.setTiddler();
|
||||
handled = true;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return handled;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.handleMouseOverOrOutEvent = function(event) {
|
||||
if(this.popup) {
|
||||
this.triggerPopup(event);
|
||||
}
|
||||
event.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
var setTitle = this.set,
|
||||
popupTitle = this.popup;
|
||||
if(this.qualifyTiddlerTitles) {
|
||||
var scopeId = this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
if(setTitle) {
|
||||
setTitle = setTitle + "-" + scopeId;
|
||||
}
|
||||
if(popupTitle) {
|
||||
popupTitle = popupTitle + "-" + scopeId;
|
||||
}
|
||||
}
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"] || (setTitle && changedTiddlers[setTitle]) || (popupTitle && changedTiddlers[popupTitle])) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.button = ButtonWidget;
|
||||
|
||||
})();
|
@ -1,111 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/checkbox.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the checkbox widget.
|
||||
|
||||
```
|
||||
<$checkbox tag="done"/>
|
||||
|
||||
<$checkbox tiddler="HelloThere" tag="red"/>
|
||||
|
||||
<$checkbox tag="done">
|
||||
<$view field="title" format="link"/>
|
||||
</$checkbox>
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var CheckboxWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
this.tagName = this.renderer.getAttribute("tag");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
// Compute classes
|
||||
var classes = ["tw-checkbox"];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
// Create the checkbox and span elements
|
||||
var nodeCheckbox = {
|
||||
type: "element",
|
||||
tag: "input",
|
||||
attributes: {
|
||||
type: {type: "string", value: "checkbox"}
|
||||
}
|
||||
},
|
||||
nodeSpan = {
|
||||
type: "element",
|
||||
tag: "span",
|
||||
children: this.renderer.parseTreeNode.children
|
||||
};
|
||||
// Set the state of the checkbox
|
||||
if(this.getValue()) {
|
||||
$tw.utils.addAttributeToParseTreeNode(nodeCheckbox,"checked","true");
|
||||
}
|
||||
// Set the return element
|
||||
this.tag = "label";
|
||||
this.attributes ={"class": classes.join(" ")};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[nodeCheckbox,nodeSpan]);
|
||||
this.events = [{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}];
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.getValue = function() {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
return tiddler ? tiddler.hasTag(this.tagName) : false;
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.handleChangeEvent = function(event) {
|
||||
var checked = this.children[0].domNode.checked,
|
||||
tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(tiddler && tiddler.hasTag(this.tagName) !== checked) {
|
||||
var newTags = tiddler.fields.tags.slice(0),
|
||||
pos = newTags.indexOf(this.tagName);
|
||||
if(pos !== -1) {
|
||||
newTags.splice(pos,1);
|
||||
}
|
||||
if(checked) {
|
||||
newTags.push(this.tagName);
|
||||
}
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{tags: newTags}));
|
||||
}
|
||||
};
|
||||
|
||||
CheckboxWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.tiddler || changedAttributes.tag || changedAttributes["class"]) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// Update the checkbox if necessary
|
||||
if(changedTiddlers[this.tiddlerTitle]) {
|
||||
this.children[0].domNode.checked = this.getValue();
|
||||
}
|
||||
// Refresh children
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.checkbox = CheckboxWidget;
|
||||
|
||||
})();
|
@ -1,59 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/count.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the count widget that displays the number of tiddlers that match a filter
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var CountWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Execute the filter to get the initial count
|
||||
this.executeFilter();
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
CountWidget.prototype.executeFilter = function() {
|
||||
// Get attributes
|
||||
this.filter = this.renderer.getAttribute("filter");
|
||||
// Execute the filter
|
||||
if(this.filter) {
|
||||
this.currentCount = this.renderer.renderTree.wiki.filterTiddlers(this.filter,this.renderer.tiddlerTitle).length;
|
||||
} else {
|
||||
this.currentCount = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
CountWidget.prototype.generate = function() {
|
||||
// Set the element
|
||||
this.tag = "span";
|
||||
this.attributes = {};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[
|
||||
{type: "text", text: this.currentCount !== undefined ? this.currentCount.toString() : ""}
|
||||
]);
|
||||
};
|
||||
|
||||
CountWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Re-execute the filter to get the count
|
||||
var oldCount = this.currentCount;
|
||||
this.executeFilter();
|
||||
if(this.currentCount !== oldCount) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
}
|
||||
};
|
||||
|
||||
exports.count = CountWidget;
|
||||
|
||||
})();
|
@ -1,53 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/datauri.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The datauri widget displays the contents of a tiddler as a data URI.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var DataUriWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
DataUriWidget.prototype.generate = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
// Compose the data URI
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
|
||||
uri = "";
|
||||
if(tiddler) {
|
||||
var type = tiddler.fields.type || "text/vnd.tiddlywiki",
|
||||
typeInfo = $tw.config.contentTypeInfo[type],
|
||||
isBase64 = typeInfo && typeInfo.encoding === "base64",
|
||||
parts = ["data:"];
|
||||
parts.push(type);
|
||||
parts.push(isBase64 ? ";base64" : "");
|
||||
parts.push(",");
|
||||
parts.push(isBase64 ? tiddler.fields.text : encodeURIComponent(tiddler.fields.text));
|
||||
uri = parts.join("");
|
||||
}
|
||||
// Set the element
|
||||
this.tag = "pre";
|
||||
this.attributes = {
|
||||
"class": "tw-data-uri"
|
||||
};
|
||||
// Create the renderers for the wrapper and the children
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: uri
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.datauri = DataUriWidget;
|
||||
|
||||
})();
|
@ -1,87 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/edit/edit.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The edit widget uses editor plugins to edit tiddlers of different types.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var EditWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Initialise the editors if they've not been done already
|
||||
if(!this.editors) {
|
||||
EditWidget.prototype.editors = {};
|
||||
$tw.modules.applyMethods("editor",this.editors);
|
||||
}
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
EditWidget.prototype.generate = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
this.fieldName = this.renderer.getAttribute("field");
|
||||
this.indexName = this.renderer.getAttribute("index");
|
||||
if(!this.fieldName && !this.indexName) {
|
||||
this.fieldName = "text";
|
||||
}
|
||||
// Choose the editor to use
|
||||
// TODO: Tiddler field modules should be able to specify a field type from which the editor is derived
|
||||
this.editorName = this.chooseEditor();
|
||||
var Editor = this.editors[this.editorName];
|
||||
// Instantiate the editor
|
||||
this.editor = new Editor(this,this.tiddlerTitle,this.fieldName,this.indexName);
|
||||
// Ask the editor to create the widget element
|
||||
this.editor.render();
|
||||
};
|
||||
|
||||
/*
|
||||
Return the name of the editor that should handle this tiddler field
|
||||
*/
|
||||
EditWidget.prototype.chooseEditor = function() {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(this.fieldName === "text" && tiddler && tiddler.fields.type && this.editors[tiddler.fields.type]) {
|
||||
return tiddler.fields.type;
|
||||
}
|
||||
return "text/vnd.tiddlywiki";
|
||||
};
|
||||
|
||||
EditWidget.prototype.postRenderInDom = function() {
|
||||
if(this.editor && this.editor.postRenderInDom) {
|
||||
this.editor.postRenderInDom();
|
||||
}
|
||||
};
|
||||
|
||||
EditWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// We'll completely regenerate ourselves if any of our attributes have changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.format || this.chooseEditor() !== this.editorName) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) {
|
||||
// Refresh the editor if our tiddler has changed
|
||||
if(this.editor && this.editor.refreshInDom) {
|
||||
this.editor.refreshInDom(changedTiddlers);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.edit = EditWidget;
|
||||
|
||||
})();
|
@ -1,318 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/edit/editors/bitmapeditor.js
|
||||
type: application/javascript
|
||||
module-type: editor
|
||||
|
||||
A bitmap editor
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Default images sizes
|
||||
var DEFAULT_IMAGE_WIDTH = 300,
|
||||
DEFAULT_IMAGE_HEIGHT = 185;
|
||||
|
||||
// The elements of the editor UI
|
||||
var DOM_CANVAS = 0,
|
||||
DOM_WIDTH = 1,
|
||||
DOM_HEIGHT = 2;
|
||||
|
||||
var BitmapEditor = function(editWidget,tiddlerTitle,fieldName,indexName) {
|
||||
this.editWidget = editWidget;
|
||||
this.tiddlerTitle = tiddlerTitle;
|
||||
this.fieldName = fieldName;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.render = function() {
|
||||
// Set the element details
|
||||
this.editWidget.tag = "div";
|
||||
this.editWidget.attributes = {
|
||||
"class": "tw-edit-bitmapeditor-wrapper"
|
||||
};
|
||||
var children = [{
|
||||
type: "element",
|
||||
tag: "canvas",
|
||||
attributes: {
|
||||
"class": {type: "string", value: "tw-edit-bitmapeditor"}
|
||||
},
|
||||
events: [{
|
||||
name: "touchstart",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleTouchStartEvent"
|
||||
},{
|
||||
name: "touchmove",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleTouchMoveEvent"
|
||||
},{
|
||||
name: "touchend",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleTouchEndEvent"
|
||||
},{
|
||||
name: "mousedown",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleMouseDownEvent"
|
||||
},{
|
||||
name: "mousemove",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleMouseMoveEvent"
|
||||
},{
|
||||
name: "mouseup",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleMouseUpEvent"
|
||||
}]
|
||||
},{
|
||||
type: "element",
|
||||
tag: "input",
|
||||
attributes: {
|
||||
"class": {type: "string", value: "tw-edit-bitmapeditor-width"},
|
||||
"type": {type: "string", value: "number"},
|
||||
"value": {type: "string", value: ""}
|
||||
},
|
||||
events: [{
|
||||
name: "change",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleWidthChangeEvent"
|
||||
}]
|
||||
},{
|
||||
type: "element",
|
||||
tag: "input",
|
||||
attributes: {
|
||||
"class": {type: "string", value: "tw-edit-bitmapeditor-height"},
|
||||
"type": {type: "string", value: "number"},
|
||||
"value": {type: "string", value: ""}
|
||||
},
|
||||
events: [{
|
||||
name: "change",
|
||||
handlerObject: this,
|
||||
handlerMethod: "handleHeightChangeEvent"
|
||||
}]
|
||||
}];
|
||||
this.editWidget.children = this.editWidget.renderer.renderTree.createRenderers(this.editWidget.renderer,children);
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.postRenderInDom = function() {
|
||||
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
|
||||
canvas = this.getDomNode(DOM_CANVAS),
|
||||
currImage = new Image();
|
||||
// Set up event handlers for loading the image
|
||||
var self = this;
|
||||
currImage.onload = function() {
|
||||
// Copy the image to the on-screen canvas
|
||||
self.initCanvas(canvas,currImage.width,currImage.height,currImage);
|
||||
// And also copy the current bitmap to the off-screen canvas
|
||||
self.currCanvas = self.editWidget.renderer.renderTree.document.createElement("canvas");
|
||||
self.initCanvas(self.currCanvas,currImage.width,currImage.height,currImage);
|
||||
// Set the width and height input boxes
|
||||
self.updateSize();
|
||||
};
|
||||
currImage.onerror = function() {
|
||||
// Set the on-screen canvas size and clear it
|
||||
self.initCanvas(canvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
|
||||
// Set the off-screen canvas size and clear it
|
||||
self.currCanvas = self.editWidget.renderer.renderTree.document.createElement("canvas");
|
||||
self.initCanvas(self.currCanvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
|
||||
// Set the width and height input boxes
|
||||
self.updateSize();
|
||||
}
|
||||
// Get the current bitmap into an image object
|
||||
currImage.src = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.initCanvas = function(canvas,width,height,image) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
if(image) {
|
||||
ctx.drawImage(image,0,0);
|
||||
} else {
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.fillRect(0,0,canvas.width,canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
BitmapEditor.prototype.getDomNode = function(index) {
|
||||
return this.editWidget.renderer.domNode.childNodes[index];
|
||||
};
|
||||
|
||||
/*
|
||||
** Update the input boxes with the actual size of the canvas
|
||||
*/
|
||||
BitmapEditor.prototype.updateSize = function() {
|
||||
this.getDomNode(DOM_WIDTH).value = this.currCanvas.width;
|
||||
this.getDomNode(DOM_HEIGHT).value = this.currCanvas.height;
|
||||
};
|
||||
|
||||
/*
|
||||
** Change the size of the canvas, preserving the current image
|
||||
*/
|
||||
BitmapEditor.prototype.changeCanvasSize = function(newWidth,newHeight) {
|
||||
// Create and size a new canvas
|
||||
var newCanvas = this.editWidget.renderer.renderTree.document.createElement("canvas");
|
||||
this.initCanvas(newCanvas,newWidth,newHeight);
|
||||
// Copy the old image
|
||||
var ctx = newCanvas.getContext("2d");
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
// Set the new canvas as the current one
|
||||
this.currCanvas = newCanvas;
|
||||
// Set the size of the onscreen canvas
|
||||
var canvas = this.getDomNode(DOM_CANVAS);
|
||||
canvas.width = newWidth;
|
||||
canvas.height = newHeight;
|
||||
// Paint the onscreen canvas with the offscreen canvas
|
||||
ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleWidthChangeEvent = function(event) {
|
||||
// Get the new width
|
||||
var newWidth = parseInt(this.getDomNode(DOM_WIDTH).value,10);
|
||||
// Update if necessary
|
||||
if(newWidth > 0 && newWidth !== this.currCanvas.width) {
|
||||
this.changeCanvasSize(newWidth,this.currCanvas.height);
|
||||
}
|
||||
// Update the input controls
|
||||
this.updateSize();
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleHeightChangeEvent = function(event) {
|
||||
// Get the new width
|
||||
var newHeight = parseInt(this.getDomNode(DOM_HEIGHT).value,10);
|
||||
// Update if necessary
|
||||
if(newHeight > 0 && newHeight !== this.currCanvas.height) {
|
||||
this.changeCanvasSize(this.currCanvas.width,newHeight);
|
||||
}
|
||||
// Update the input controls
|
||||
this.updateSize();
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleTouchStartEvent = function(event) {
|
||||
this.brushDown = true;
|
||||
this.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleTouchMoveEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.strokeMove(event.touches[0].clientX,event.touches[0].clientY);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleTouchEndEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.brushDown = false;
|
||||
this.strokeEnd();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleMouseDownEvent = function(event) {
|
||||
this.strokeStart(event.clientX,event.clientY);
|
||||
this.brushDown = true;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleMouseMoveEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.strokeMove(event.clientX,event.clientY);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.handleMouseUpEvent = function(event) {
|
||||
if(this.brushDown) {
|
||||
this.brushDown = false;
|
||||
this.strokeEnd();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.adjustCoordinates = function(x,y) {
|
||||
var canvas = this.getDomNode(DOM_CANVAS),
|
||||
canvasRect = canvas.getBoundingClientRect(),
|
||||
scale = canvas.width/canvasRect.width;
|
||||
return {x: (x - canvasRect.left) * scale, y: (y - canvasRect.top) * scale};
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.strokeStart = function(x,y) {
|
||||
// Start off a new stroke
|
||||
this.stroke = [this.adjustCoordinates(x,y)];
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.strokeMove = function(x,y) {
|
||||
var canvas = this.getDomNode(DOM_CANVAS),
|
||||
ctx = canvas.getContext("2d"),
|
||||
t;
|
||||
// Add the new position to the end of the stroke
|
||||
this.stroke.push(this.adjustCoordinates(x,y));
|
||||
// Redraw the previous image
|
||||
ctx.drawImage(this.currCanvas,0,0);
|
||||
// Render the stroke
|
||||
ctx.strokeStyle = "#ff0";
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.stroke[0].x,this.stroke[0].y);
|
||||
for(t=1; t<this.stroke.length-1; t++) {
|
||||
var s1 = this.stroke[t],
|
||||
s2 = this.stroke[t-1],
|
||||
tx = (s1.x + s2.x)/2,
|
||||
ty = (s1.y + s2.y)/2;
|
||||
ctx.quadraticCurveTo(s2.x,s2.y,tx,ty);
|
||||
}
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.strokeEnd = function() {
|
||||
// Copy the bitmap to the off-screen canvas
|
||||
var canvas = this.getDomNode(DOM_CANVAS),
|
||||
ctx = this.currCanvas.getContext("2d");
|
||||
ctx.drawImage(canvas,0,0);
|
||||
// Save the image into the tiddler
|
||||
this.saveChanges();
|
||||
};
|
||||
|
||||
BitmapEditor.prototype.saveChanges = function() {
|
||||
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(tiddler) {
|
||||
// data URIs look like "data:<type>;base64,<text>"
|
||||
var dataURL = this.getDomNode(DOM_CANVAS).toDataURL(tiddler.fields.type,1.0),
|
||||
posColon = dataURL.indexOf(":"),
|
||||
posSemiColon = dataURL.indexOf(";"),
|
||||
posComma = dataURL.indexOf(","),
|
||||
type = dataURL.substring(posColon+1,posSemiColon),
|
||||
text = dataURL.substring(posComma+1);
|
||||
var update = {type: type, text: text};
|
||||
this.editWidget.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Note that the bitmap editor intentionally doesn't have a refreshInDom method to avoid the situation where a bitmap being editted is modified externally
|
||||
*/
|
||||
|
||||
exports["image/jpg"] = BitmapEditor;
|
||||
exports["image/jpeg"] = BitmapEditor;
|
||||
exports["image/png"] = BitmapEditor;
|
||||
exports["image/gif"] = BitmapEditor;
|
||||
|
||||
})();
|
@ -1,219 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/edit/editors/texteditor.js
|
||||
type: application/javascript
|
||||
module-type: editor
|
||||
|
||||
A plain text editor
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var MIN_TEXT_AREA_HEIGHT = 100;
|
||||
|
||||
var TextEditor = function(editWidget,tiddlerTitle,fieldName,indexName) {
|
||||
this.editWidget = editWidget;
|
||||
this.tiddlerTitle = tiddlerTitle;
|
||||
this.fieldName = fieldName;
|
||||
this.indexName = indexName;
|
||||
};
|
||||
|
||||
/*
|
||||
Get the tiddler being edited and current value
|
||||
*/
|
||||
TextEditor.prototype.getEditInfo = function() {
|
||||
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
|
||||
value;
|
||||
if(this.fieldName) {
|
||||
// Get the current tiddler and the field name
|
||||
if(tiddler) {
|
||||
// If we've got a tiddler, the value to display is the field string value
|
||||
value = tiddler.getFieldString(this.fieldName);
|
||||
} else {
|
||||
// Otherwise, we need to construct a default value for the editor
|
||||
switch(this.fieldName) {
|
||||
case "text":
|
||||
value = "Type the text for the tiddler '" + this.tiddlerTitle + "'";
|
||||
break;
|
||||
case "title":
|
||||
value = this.tiddlerTitle;
|
||||
break;
|
||||
default:
|
||||
value = "";
|
||||
break;
|
||||
}
|
||||
value = this.editWidget.renderer.getAttribute("default",value);
|
||||
}
|
||||
} else {
|
||||
value = this.editWidget.renderer.renderTree.wiki.extractTiddlerDataItem(this.tiddlerTitle,this.indexName,this.editWidget.renderer.getAttribute("default"));
|
||||
}
|
||||
return {tiddler: tiddler, value: value};
|
||||
};
|
||||
|
||||
TextEditor.prototype.render = function() {
|
||||
// Get the initial value of the editor
|
||||
var editInfo = this.getEditInfo();
|
||||
// Create the editor nodes
|
||||
var node = {
|
||||
type: "element",
|
||||
attributes: {}
|
||||
};
|
||||
// Get the edit type associated with this field
|
||||
var type = "input";
|
||||
if(this.fieldName === "text") {
|
||||
type = "textarea";
|
||||
} else {
|
||||
var fieldModule = $tw.Tiddler.fieldModules[this.fieldName];
|
||||
if(fieldModule && fieldModule.editType) {
|
||||
type = fieldModule.editType;
|
||||
}
|
||||
}
|
||||
var type = this.editWidget.renderer.getAttribute("type",type);
|
||||
switch(type) {
|
||||
case "textarea":
|
||||
node.tag = "textarea";
|
||||
node.children = [{
|
||||
type: "text",
|
||||
text: editInfo.value
|
||||
}];
|
||||
break;
|
||||
case "color":
|
||||
node.tag = "input";
|
||||
node.attributes.type = {type: "string", value: "color"};
|
||||
node.attributes.value = {type: "string", value: editInfo.value};
|
||||
break;
|
||||
case "search":
|
||||
node.tag = "input";
|
||||
node.attributes.type = {type: "string", value: "search"};
|
||||
node.attributes.value = {type: "string", value: editInfo.value};
|
||||
break;
|
||||
default: // "input"
|
||||
node.tag = "input";
|
||||
node.attributes.type = {type: "string", value: "text"};
|
||||
node.attributes.value = {type: "string", value: editInfo.value};
|
||||
break;
|
||||
}
|
||||
node.events = [
|
||||
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
|
||||
{name: "blur", handlerObject: this, handlerMethod: "handleBlurEvent"},
|
||||
{name: "input", handlerObject: this, handlerMethod: "handleInputEvent"}
|
||||
];
|
||||
// Add a placeholder if specified
|
||||
if(this.editWidget.renderer.hasAttribute("placeholder")) {
|
||||
node.attributes.placeholder = {type: "string", value: this.editWidget.renderer.getAttribute("placeholder")};
|
||||
}
|
||||
// Set the element details
|
||||
this.editWidget.tag = this.editWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.editWidget.attributes = {
|
||||
"class": "tw-edit-texteditor"
|
||||
};
|
||||
if(this.editWidget.renderer.hasAttribute("class")) {
|
||||
this.editWidget.attributes["class"] += " " + this.editWidget.renderer.getAttribute("class");
|
||||
}
|
||||
if(this.editWidget.renderer.hasAttribute("style")) {
|
||||
this.editWidget.attributes.style = this.editWidget.attributes.style || "";
|
||||
this.editWidget.attributes.style += this.editWidget.renderer.getAttribute("style");
|
||||
}
|
||||
this.editWidget.children = this.editWidget.renderer.renderTree.createRenderers(this.editWidget.renderer,[node]);
|
||||
};
|
||||
|
||||
TextEditor.prototype.setFocus = function() {
|
||||
if(this.editWidget.renderer.hasAttribute("focusSet")) {
|
||||
var title = this.editWidget.renderer.getAttribute("focusSet");
|
||||
if(this.editWidget.renderer.getAttribute("qualifyTiddlerTitles") === "yes") {
|
||||
title = title + "-" + this.editWidget.renderer.renderTree.getContextScopeId(this.editWidget.renderer.parentRenderer);
|
||||
}
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: this.editWidget.renderer.domNode,
|
||||
title: title,
|
||||
wiki: this.editWidget.renderer.renderTree.wiki,
|
||||
force: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
TextEditor.prototype.handleFocusEvent = function(event) {
|
||||
// this.saveChanges();
|
||||
// this.fixHeight();
|
||||
this.setFocus();
|
||||
return true;
|
||||
};
|
||||
|
||||
TextEditor.prototype.handleBlurEvent = function(event) {
|
||||
// this.saveChanges();
|
||||
return true;
|
||||
};
|
||||
|
||||
TextEditor.prototype.handleInputEvent = function(event) {
|
||||
this.saveChanges();
|
||||
this.fixHeight();
|
||||
return true;
|
||||
};
|
||||
|
||||
TextEditor.prototype.saveChanges = function() {
|
||||
var text = this.editWidget.children[0].domNode.value
|
||||
if(this.fieldName) {
|
||||
var tiddler = this.editWidget.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(!tiddler) {
|
||||
tiddler = new $tw.Tiddler({title: this.tiddlerTitle});
|
||||
}
|
||||
var oldValue = tiddler.getFieldString(this.fieldName);
|
||||
if(text !== oldValue) {
|
||||
var update = {};
|
||||
update[this.fieldName] = text;
|
||||
this.editWidget.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
|
||||
}
|
||||
} else {
|
||||
var data = this.editWidget.renderer.renderTree.wiki.getTiddlerData(this.tiddlerTitle,{});
|
||||
if(data[this.indexName] !== text) {
|
||||
data[this.indexName] = text;
|
||||
this.editWidget.renderer.renderTree.wiki.setTiddlerData(this.tiddlerTitle,data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TextEditor.prototype.fixHeight = function() {
|
||||
var self = this;
|
||||
if(this.editWidget.children[0].domNode && this.editWidget.children[0].domNode.type === "textarea") {
|
||||
$tw.utils.nextTick(function() {
|
||||
// Resize the textarea to fit its content
|
||||
var textarea = self.editWidget.children[0].domNode,
|
||||
scrollPosition = $tw.utils.getScrollPosition(),
|
||||
scrollTop = scrollPosition.y;
|
||||
// Set its height to auto so that it snaps to the correct height
|
||||
textarea.style.height = "auto";
|
||||
// Calculate the revised height
|
||||
var newHeight = Math.max(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight,MIN_TEXT_AREA_HEIGHT);
|
||||
// Only try to change the height if it has changed
|
||||
if(newHeight !== textarea.offsetHeight) {
|
||||
textarea.style.height = newHeight + "px";
|
||||
// Make sure that the dimensions of the textarea are recalculated
|
||||
$tw.utils.forceLayout(textarea);
|
||||
// Check that the scroll position is still visible before trying to scroll back to it
|
||||
scrollTop = Math.min(scrollTop,self.editWidget.renderer.renderTree.document.body.scrollHeight - window.innerHeight);
|
||||
window.scrollTo(scrollPosition.x,scrollTop);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
TextEditor.prototype.postRenderInDom = function() {
|
||||
this.fixHeight();
|
||||
};
|
||||
|
||||
TextEditor.prototype.refreshInDom = function() {
|
||||
if(this.editWidget.renderer.renderTree.document.activeElement !== this.editWidget.children[0].domNode) {
|
||||
var editInfo = this.getEditInfo();
|
||||
this.editWidget.children[0].domNode.value = editInfo.value;
|
||||
}
|
||||
// Fix the height if needed
|
||||
this.fixHeight();
|
||||
};
|
||||
|
||||
exports["text/vnd.tiddlywiki"] = TextEditor;
|
||||
exports["text/plain"] = TextEditor;
|
||||
|
||||
})();
|
@ -1,51 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/encrypt.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the encrypt widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var EncryptWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
EncryptWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.filter = this.renderer.getAttribute("filter");
|
||||
// Check whether we've got an encryption password
|
||||
var isEncrypted = $tw.crypto.hasPassword();
|
||||
// Encrypt the filtered tiddlers
|
||||
var tiddlers = this.renderer.renderTree.wiki.filterTiddlers(this.filter),
|
||||
json = {},
|
||||
self = this;
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var tiddler = self.renderer.renderTree.wiki.getTiddler(title),
|
||||
jsonTiddler = {};
|
||||
for(var f in tiddler.fields) {
|
||||
jsonTiddler[f] = tiddler.getFieldString(f);
|
||||
}
|
||||
json[title] = jsonTiddler;
|
||||
});
|
||||
var encryptedText = $tw.utils.htmlEncode($tw.crypto.encrypt(JSON.stringify(json)));
|
||||
// Set the return element
|
||||
this.tag = "pre";
|
||||
this.attributes ={"class": "tw-encrypt"};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: encryptedText
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.encrypt = EncryptWidget;
|
||||
|
||||
})();
|
@ -1,37 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/error.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The error widget displays an error message.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ErrorWidget = function(renderer,errorMessage) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
this.errorMessage = errorMessage;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
ErrorWidget.prototype.generate = function() {
|
||||
// Set the element details
|
||||
this.tag = "span";
|
||||
this.attributes = {
|
||||
"class": "tw-error-widget"
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: this.errorMessage
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.error = ErrorWidget;
|
||||
|
||||
})();
|
@ -1,87 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/fieldmangler.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The fieldmangler widget modifies the fields of the current tiddler in response to messages.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var FieldManglerWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.generate = function() {
|
||||
var self = this;
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
// Set the element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": "tw-fieldmangler"
|
||||
};
|
||||
// Set event handlers
|
||||
this.events = [
|
||||
{name: "tw-remove-field", handlerObject: this, handlerMethod: "handleRemoveFieldEvent"},
|
||||
{name: "tw-add-field", handlerObject: this, handlerMethod: "handleAddFieldEvent"},
|
||||
{name: "tw-remove-tag", handlerObject: this, handlerMethod: "handleRemoveTagEvent"},
|
||||
{name: "tw-add-tag", handlerObject: this, handlerMethod: "handleAddTagEvent"}
|
||||
];
|
||||
// Render the children
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
|
||||
deletion = {};
|
||||
deletion[event.param] = undefined;
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,deletion));
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(tiddler && typeof event.param === "string" && event.param !== "" && !$tw.utils.hop(tiddler.fields,event.param)) {
|
||||
var addition = {};
|
||||
addition[event.param] = "";
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,addition));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleRemoveTagEvent = function(event) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(tiddler && tiddler.fields.tags) {
|
||||
var p = tiddler.fields.tags.indexOf(event.param);
|
||||
if(p !== -1) {
|
||||
var modification = {};
|
||||
modification.tags = (tiddler.fields.tags || []).slice(0);
|
||||
modification.tags.splice(p,1);
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddTagEvent = function(event) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
if(tiddler && typeof event.param === "string" && event.param !== "") {
|
||||
var modification = {};
|
||||
modification.tags = (tiddler.fields.tags || []).slice(0);
|
||||
$tw.utils.pushTop(modification.tags,event.param);
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.fieldmangler = FieldManglerWidget;
|
||||
|
||||
})();
|
@ -1,94 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/fields.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The fields widget displays the fields of a tiddler through a text substitution template.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var FieldsWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
FieldsWidget.prototype.generate = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
this.template = this.renderer.getAttribute("template");
|
||||
this.exclude = this.renderer.getAttribute("exclude");
|
||||
this.stripTitlePrefix = this.renderer.getAttribute("stripTitlePrefix","no") === "yes";
|
||||
// Get the tiddler we're displaying
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle);
|
||||
// Get the exclusion list
|
||||
var exclude;
|
||||
if(this.exclude) {
|
||||
exclude = this.exclude.split(" ");
|
||||
} else {
|
||||
exclude = ["text"];
|
||||
}
|
||||
// Compose the template
|
||||
var text = [];
|
||||
if(this.template && tiddler) {
|
||||
var fields = [];
|
||||
for(var fieldName in tiddler.fields) {
|
||||
if(exclude.indexOf(fieldName) === -1) {
|
||||
fields.push(fieldName);
|
||||
}
|
||||
}
|
||||
fields.sort();
|
||||
for(var f=0; f<fields.length; f++) {
|
||||
fieldName = fields[f];
|
||||
if(exclude.indexOf(fieldName) === -1) {
|
||||
var row = this.template,
|
||||
value = tiddler.getFieldString(fieldName);
|
||||
if(this.stripTitlePrefix && fieldName === "title") {
|
||||
var reStrip = /^\{[^\}]+\}(.+)/mg,
|
||||
reMatch = reStrip.exec(value);
|
||||
if(reMatch) {
|
||||
value = reMatch[1];
|
||||
}
|
||||
}
|
||||
row = row.replace("$name$",fieldName);
|
||||
row = row.replace("$value$",value);
|
||||
row = row.replace("$encoded_value$",$tw.utils.htmlEncode(value));
|
||||
text.push(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the element
|
||||
this.tag = "pre";
|
||||
this.attributes = {
|
||||
"class": "tw-fields"
|
||||
};
|
||||
// Set up the attributes for the wrapper element
|
||||
var classes = [];
|
||||
if(this.renderer.hasAttribute("class")) {
|
||||
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
|
||||
}
|
||||
if(classes.length > 0) {
|
||||
this.attributes["class"] = classes.join(" ");
|
||||
}
|
||||
if(this.renderer.hasAttribute("style")) {
|
||||
this.attributes.style = this.renderer.getAttribute("style");
|
||||
}
|
||||
if(this.renderer.hasAttribute("tooltip")) {
|
||||
this.attributes.title = this.renderer.getAttribute("tooltip");
|
||||
}
|
||||
// Create the renderers for the wrapper and the children
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: text.join("")
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.fields = FieldsWidget;
|
||||
|
||||
})();
|
@ -1,109 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/grid.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The grid widget.
|
||||
|
||||
This example renders a table made up of tiddlers titled `MySheet_A_1`, `MySheet_A_2`, `MySheet_A_3`, ... , `MySheet_B_1`, `MySheet_B_2`, `MySheet_B_3` etc.
|
||||
|
||||
```
|
||||
<$grid prefix="MySheet" rows=20 cols=20/>
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var GridWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate widget elements
|
||||
this.generate();
|
||||
};
|
||||
|
||||
GridWidget.prototype.generate = function() {
|
||||
// Get our attributes
|
||||
this.prefix = this.renderer.getAttribute("prefix","Grid");
|
||||
this.rows = parseInt(this.renderer.getAttribute("rows","10"),10);
|
||||
this.cols = parseInt(this.renderer.getAttribute("cols","10"),10);
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
// Set up the classes
|
||||
var classes = ["tw-grid-frame"];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
// Create the grid table element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": classes.join(" ")
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.generateTable());
|
||||
};
|
||||
|
||||
GridWidget.prototype.generateTable = function() {
|
||||
var rows = [];
|
||||
for(var row=0; row<this.rows; row++) {
|
||||
var tr = {
|
||||
type: "element",
|
||||
tag: "tr",
|
||||
children: []
|
||||
};
|
||||
rows.push(tr);
|
||||
for(var col=0; col<this.cols; col++) {
|
||||
var td = {
|
||||
type: "element",
|
||||
tag: "td",
|
||||
children: [{
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
attributes: {
|
||||
title: {type: "string", value: this.getTableCellTitle(col,row)}
|
||||
}
|
||||
}]
|
||||
};
|
||||
tr.children.push(td);
|
||||
}
|
||||
}
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "table",
|
||||
children: [{
|
||||
type: "element",
|
||||
tag: "tbody",
|
||||
children: rows
|
||||
}]
|
||||
}];
|
||||
};
|
||||
|
||||
GridWidget.prototype.getTableCellTitle = function(col,row) {
|
||||
var c = String.fromCharCode(col % 26 + "A".charCodeAt(0));
|
||||
col = Math.floor(col/26);
|
||||
while(col>0) {
|
||||
c = String.fromCharCode(col % 26 + "A".charCodeAt(0) - 1) + c;
|
||||
col = Math.floor(col/26);
|
||||
}
|
||||
return this.prefix + "_" + c + "_" + (row + 1);
|
||||
};
|
||||
|
||||
GridWidget.prototype.postRenderInDom = function() {
|
||||
};
|
||||
|
||||
GridWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Reexecute the widget if any of our attributes have changed
|
||||
if(true) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
}
|
||||
};
|
||||
|
||||
exports.grid = GridWidget;
|
||||
|
||||
})();
|
@ -1,256 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/import.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the import widget.
|
||||
|
||||
```
|
||||
<$import>
|
||||
Import using the "browse..." button or drag files onto this text
|
||||
</$import>
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ImportWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
ImportWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.browse = this.renderer.getAttribute("browse","yes");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
// Compute classes
|
||||
var classes = ["tw-import"];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
// Create the file input and container elements
|
||||
var fileInput = {
|
||||
type: "element",
|
||||
tag: "input",
|
||||
attributes: {
|
||||
type: {type: "string", value: "file"},
|
||||
style: {type: "string", value: this.browse === "no" ? "display: none;" : "display: block;"}
|
||||
},
|
||||
events: [{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}]
|
||||
},
|
||||
container = {
|
||||
type: "element",
|
||||
tag: "div",
|
||||
children: this.renderer.parseTreeNode.children,
|
||||
events: [
|
||||
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
||||
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"},
|
||||
{name: "paste", handlerObject: this, handlerMethod: "handlePasteEvent"}]
|
||||
};
|
||||
// Set the return element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": classes.join(" ")
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[fileInput,container]);
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handleChangeEvent = function(event) {
|
||||
event.stopPropagation();
|
||||
this.importFiles(event.target.files);
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handleDragEnterEvent = function(event) {
|
||||
// We count enter/leave events
|
||||
this.dragEnterCount = (this.dragEnterCount || 0) + 1;
|
||||
// If we're entering for the first time we need to apply highlighting
|
||||
if(this.dragEnterCount === 1) {
|
||||
$tw.utils.addClass(this.renderer.domNode,"tw-dragover");
|
||||
}
|
||||
// Tell the browser that we're ready to handle the drop
|
||||
event.preventDefault();
|
||||
// Tell the browser not to ripple the drag up to any parent drop handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handleDragOverEvent = function(event) {
|
||||
// Tell the browser that we're still interested in the drop
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handleDragLeaveEvent = function(event) {
|
||||
// Reduce the enter count
|
||||
this.dragEnterCount = (this.dragEnterCount || 0) - 1;
|
||||
// Remove highlighting if we're leaving externally
|
||||
if(this.dragEnterCount <= 0) {
|
||||
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
|
||||
}
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handleDropEvent = function(event) {
|
||||
var dataTransfer = event.dataTransfer;
|
||||
// Reset the enter count
|
||||
this.dragEnterCount = 0;
|
||||
// Remove highlighting
|
||||
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
|
||||
// Try to import the various data types we understand
|
||||
this.importData(dataTransfer);
|
||||
// Import any files in the drop
|
||||
this.importFiles(dataTransfer.files);
|
||||
// Tell the browser that we handled the drop
|
||||
event.preventDefault();
|
||||
// Stop the drop ripple up to any parent handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
ImportWidget.prototype.handlePasteEvent = function(event) {
|
||||
// Let the browser handle it if we're in a textarea or input box
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1) {
|
||||
var self = this,
|
||||
items = event.clipboardData.items;
|
||||
// Enumerate the clipboard items
|
||||
for(var t = 0; t<items.length; t++) {
|
||||
var item = items[t];
|
||||
if(item.kind === "file") {
|
||||
// Import any files
|
||||
var file = item.getAsFile();
|
||||
this.importFiles([file]);
|
||||
} else if(item.kind === "string") {
|
||||
// Create tiddlers from string items
|
||||
item.getAsString(function(str) {
|
||||
var fields = {
|
||||
title: self.generateTitle("Untitled"),
|
||||
text: str
|
||||
};
|
||||
self.storeTiddler(fields);
|
||||
self.openTiddler(fields.title);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Tell the browser that we've handled the paste
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
ImportWidget.prototype.openTiddler = function(title) {
|
||||
$tw.utils.dispatchCustomEvent(this.renderer.domNode,"tw-navigate",{
|
||||
navigateTo: title,
|
||||
navigateFromNode: this.renderer.domNode,
|
||||
navigateFromClientRect: this.renderer.domNode.getBoundingClientRect()
|
||||
});
|
||||
};
|
||||
|
||||
ImportWidget.prototype.importData = function(dataTransfer) {
|
||||
for(var t=0; t<this.importDataTypes.length; t++) {
|
||||
var dataType = this.importDataTypes[t];
|
||||
var data = dataTransfer.getData(dataType.type);
|
||||
if(data !== "") {
|
||||
var fields = dataType.handler(data);
|
||||
if(!fields.title) {
|
||||
fields.title = this.generateTitle("Untitled");
|
||||
}
|
||||
this.storeTiddler(fields);
|
||||
this.openTiddler(fields.title);
|
||||
return;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ImportWidget.prototype.importDataTypes = [
|
||||
{type: "text/vnd.tiddler", handler: function(data) {
|
||||
return JSON.parse(data);
|
||||
}},
|
||||
{type: "text/plain", handler: function(data) {
|
||||
return {
|
||||
text: data
|
||||
};
|
||||
}},
|
||||
{type: "text/uri-list", handler: function(data) {
|
||||
return {
|
||||
text: data
|
||||
};
|
||||
}}
|
||||
];
|
||||
|
||||
ImportWidget.prototype.importFiles = function(files) {
|
||||
var self = this,
|
||||
importFile = function(file) {
|
||||
// Get the type, falling back to the filename extension
|
||||
var type = file.type;
|
||||
if(type === "" || !type) {
|
||||
var dotPos = file.name.lastIndexOf(".");
|
||||
if(dotPos !== -1) {
|
||||
var fileExtensionInfo = $tw.config.fileExtensionInfo[file.name.substr(dotPos)];
|
||||
if(fileExtensionInfo) {
|
||||
type = fileExtensionInfo.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Figure out if we're reading a binary file
|
||||
var contentTypeInfo = $tw.config.contentTypeInfo[type],
|
||||
isBinary = contentTypeInfo ? contentTypeInfo.encoding === "base64" : false;
|
||||
// Create the FileReader
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
// Deserialise the file contents
|
||||
var fields = {
|
||||
title: file.name || "Untitled",
|
||||
type: type};
|
||||
// Are we binary?
|
||||
if(isBinary) {
|
||||
var commaPos = event.target.result.indexOf(",");
|
||||
if(commaPos !== -1) {
|
||||
fields.text = event.target.result.substr(commaPos+1);
|
||||
self.storeTiddler(fields);
|
||||
self.openTiddler(fields.title);
|
||||
}
|
||||
} else {
|
||||
var tiddlers = self.renderer.renderTree.wiki.deserializeTiddlers(type,event.target.result,fields);
|
||||
if(!tiddlers) {
|
||||
console.log("No tiddlers found in file ",file.name);
|
||||
} else {
|
||||
$tw.utils.each(tiddlers,function(tiddlerFields) {
|
||||
tiddlerFields.title = self.generateTitle(tiddlerFields.title);
|
||||
self.storeTiddler(tiddlerFields);
|
||||
self.openTiddler(tiddlerFields.title);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
if(isBinary) {
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
for(var f=0; f<files.length; f++) {
|
||||
importFile(files[f]);
|
||||
};
|
||||
};
|
||||
|
||||
ImportWidget.prototype.storeTiddler = function(fields) {
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields,this.renderer.renderTree.wiki.getModificationFields()));
|
||||
};
|
||||
|
||||
ImportWidget.prototype.generateTitle = function(baseTitle) {
|
||||
var c = 0;
|
||||
do {
|
||||
var title = baseTitle + (c ? " " + (c + 1) : "");
|
||||
c++;
|
||||
} while(this.renderer.renderTree.wiki.tiddlerExists(title));
|
||||
return title;
|
||||
};
|
||||
|
||||
exports.import = ImportWidget;
|
||||
|
||||
})();
|
@ -1,115 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/info.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the info widget that displays various information about a specified tiddler.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var InfoWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
InfoWidget.types = {};
|
||||
|
||||
InfoWidget.types.changecount = function(options) {
|
||||
var text = options.wiki.getChangeCount(options.widget.renderer.tiddlerTitle);
|
||||
return [{type: "text", text: text}];
|
||||
};
|
||||
|
||||
InfoWidget.types.currentfield = function(options) {
|
||||
var fieldName = options.widget.renderer.renderTree.getContextVariable(options.widget.renderer,"field","text");
|
||||
return [{type: "text", text: fieldName}];
|
||||
};
|
||||
|
||||
var FIELD_DESCRIPTION_PREFIX = "$:/docs/fields/";
|
||||
|
||||
InfoWidget.types.currentfielddescription = function(options) {
|
||||
var fieldName = options.widget.renderer.renderTree.getContextVariable(options.widget.renderer,"field","text"),
|
||||
descriptionTitle = FIELD_DESCRIPTION_PREFIX + fieldName;
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
isBlock: false,
|
||||
attributes: {
|
||||
title: {type: "string", value: descriptionTitle}
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
var MODULE_TYPE_DESCRIPTION_PREFIX = "$:/docs/moduletypes/";
|
||||
|
||||
/*
|
||||
Return a list of all the currently loaded modules grouped by type
|
||||
*/
|
||||
InfoWidget.types.modules = function(options) {
|
||||
var output = [],
|
||||
types = [];
|
||||
// Collect and sort the module types
|
||||
$tw.utils.each($tw.modules.types,function(moduleInfo,type) {
|
||||
types.push(type);
|
||||
});
|
||||
types.sort();
|
||||
// Output the module types
|
||||
$tw.utils.each(types,function(moduleType) {
|
||||
// Heading
|
||||
output.push({type: "element", tag: "h2", children: [
|
||||
{type: "text", text: moduleType}
|
||||
]})
|
||||
// Description
|
||||
output.push({
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
isBlock: false,
|
||||
attributes: {
|
||||
title: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType}
|
||||
}
|
||||
});
|
||||
// List each module
|
||||
var list = {type: "element", tag: "ul", children: []},
|
||||
modules = [];
|
||||
$tw.utils.each($tw.modules.types[moduleType],function(moduleInfo,moduleName) {
|
||||
var listItem = {type: "element", tag: "li", children: [
|
||||
{type: "element", tag: "$link", attributes: {
|
||||
to: {type: "string", value: moduleName}
|
||||
}, children: [
|
||||
{type: "text", text: moduleName}
|
||||
]}
|
||||
]}
|
||||
list.children.push(listItem);
|
||||
});
|
||||
output.push(list);
|
||||
});
|
||||
return output;
|
||||
};
|
||||
|
||||
InfoWidget.prototype.generate = function() {
|
||||
// Get attributes
|
||||
this.type = this.renderer.getAttribute("type","changecount").toLowerCase();
|
||||
// Get the appropriate value for the current tiddler
|
||||
var value = [],
|
||||
fn = InfoWidget.types[this.type];
|
||||
if(fn) {
|
||||
value = fn({
|
||||
wiki: this.renderer.renderTree.wiki,
|
||||
widget: this
|
||||
});
|
||||
}
|
||||
// Set the element
|
||||
this.tag = "span";
|
||||
this.attributes = {};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,value);
|
||||
};
|
||||
|
||||
exports.info = InfoWidget;
|
||||
|
||||
})();
|
@ -1,203 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/link.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the link widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var isLinkExternal = function(to) {
|
||||
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s'"]+(?:\/|\b)/i;
|
||||
return externalRegExp.test(to);
|
||||
};
|
||||
|
||||
var LinkWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
LinkWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.to = this.renderer.getAttribute("to",this.renderer.tiddlerTitle);
|
||||
this.hover = this.renderer.getAttribute("hover");
|
||||
this.qualifyHoverTitles = this.renderer.getAttribute("qualifyHoverTitles");
|
||||
// Qualify the hover tiddler title if needed
|
||||
if(this.qualifyHoverTitles) {
|
||||
this.hover = this.hover + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
}
|
||||
// Determine the default link characteristics
|
||||
this.isExternal = isLinkExternal(this.to);
|
||||
if(!this.isExternal) {
|
||||
this.isMissing = !this.renderer.renderTree.wiki.tiddlerExists(this.to);
|
||||
this.isShadow = this.renderer.renderTree.wiki.isShadowTiddler(this.to);
|
||||
}
|
||||
// Compose the link
|
||||
var classes = ["tw-tiddlylink"]
|
||||
if(this.isExternal) {
|
||||
$tw.utils.pushTop(classes,"tw-tiddlylink-external");
|
||||
} else {
|
||||
$tw.utils.pushTop(classes,"tw-tiddlylink-internal");
|
||||
if(this.isShadow) {
|
||||
$tw.utils.pushTop(classes,"tw-tiddlylink-shadow");
|
||||
}
|
||||
if(this.isMissing && !this.isShadow) {
|
||||
$tw.utils.pushTop(classes,"tw-tiddlylink-missing");
|
||||
} else {
|
||||
if(!this.isMissing) {
|
||||
$tw.utils.pushTop(classes,"tw-tiddlylink-resolves");
|
||||
}
|
||||
}
|
||||
}
|
||||
var events = [
|
||||
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
|
||||
{name: "dragstart", handlerObject: this, handlerMethod: "handleDragStartEvent"},
|
||||
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
|
||||
];
|
||||
if(this.hover) {
|
||||
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
||||
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
||||
}
|
||||
// Get the value of the tw-wikilinks configuration macro
|
||||
var wikiLinksMacro = this.renderer.renderTree.findMacroDefinition(this.renderer.parentRenderer,"tw-wikilinks"),
|
||||
useWikiLinks = wikiLinksMacro ? !(wikiLinksMacro.text.trim() === "no") : true;
|
||||
// Set up the element
|
||||
if(useWikiLinks) {
|
||||
this.tag = "a";
|
||||
this.attributes = {
|
||||
"class": classes.join(" ")
|
||||
};
|
||||
if(this.isExternal) {
|
||||
this.attributes.href = this.to;
|
||||
} else {
|
||||
var wikiLinkTemplateMacro = this.renderer.renderTree.findMacroDefinition(this.renderer.parentRenderer,"tw-wikilink-template"),
|
||||
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.text.trim() : "#$uri_encoded$";
|
||||
this.wikiLinkText = wikiLinkTemplate.replace("$uri_encoded$",encodeURIComponent(this.to));
|
||||
this.wikiLinkText = this.wikiLinkText.replace("$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
|
||||
this.attributes.href = this.wikiLinkText;
|
||||
}
|
||||
this.events = events;
|
||||
} else {
|
||||
this.tag = "span";
|
||||
}
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleClickEvent = function(event) {
|
||||
if(isLinkExternal(this.to)) {
|
||||
event.target.setAttribute("target","_blank");
|
||||
return true;
|
||||
} else {
|
||||
var bounds = this.renderer.domNode.getBoundingClientRect();
|
||||
$tw.utils.dispatchCustomEvent(event.target,"tw-navigate",{
|
||||
navigateTo: this.to,
|
||||
navigateFromNode: this,
|
||||
navigateFromClientRect: {
|
||||
top: bounds.top,
|
||||
left: bounds.left,
|
||||
width: bounds.width,
|
||||
right: bounds.right,
|
||||
bottom: bounds.bottom,
|
||||
height: bounds.height
|
||||
}
|
||||
});
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
|
||||
if(this.hover) {
|
||||
$tw.popup.triggerPopup({
|
||||
title: this.hover,
|
||||
domNode: this.renderer.domNode,
|
||||
wiki: this.renderer.renderTree.wiki
|
||||
});
|
||||
}
|
||||
event.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleDragStartEvent = function(event) {
|
||||
if(this.to) {
|
||||
// Set the dragging class on the element being dragged
|
||||
$tw.utils.addClass(event.target,"tw-tiddlylink-dragging");
|
||||
// Create the drag image elements
|
||||
this.dragImage = this.renderer.renderTree.document.createElement("div");
|
||||
this.dragImage.className = "tw-tiddler-dragger";
|
||||
var inner = this.renderer.renderTree.document.createElement("div");
|
||||
inner.className = "tw-tiddler-dragger-inner";
|
||||
inner.appendChild(this.renderer.renderTree.document.createTextNode(this.to));
|
||||
this.dragImage.appendChild(inner);
|
||||
this.renderer.renderTree.document.body.appendChild(this.dragImage);
|
||||
// Astoundingly, we need to cover the dragger up: http://www.kryogenix.org/code/browser/custom-drag-image.html
|
||||
var bounds = this.dragImage.firstChild.getBoundingClientRect(),
|
||||
cover = this.renderer.renderTree.document.createElement("div");
|
||||
cover.className = "tw-tiddler-dragger-cover";
|
||||
cover.style.left = (bounds.left - 8) + "px";
|
||||
cover.style.top = (bounds.top - 8) + "px";
|
||||
cover.style.width = (bounds.width + 16) + "px";
|
||||
cover.style.height = (bounds.height + 16) + "px";
|
||||
this.dragImage.appendChild(cover);
|
||||
// Set the data transfer properties
|
||||
var dataTransfer = event.dataTransfer;
|
||||
dataTransfer.effectAllowed = "copy";
|
||||
dataTransfer.setDragImage(this.dragImage.firstChild,-16,-16);
|
||||
dataTransfer.clearData();
|
||||
dataTransfer.setData("text/vnd.tiddler",this.renderer.renderTree.wiki.getTiddlerAsJson(this.to));
|
||||
dataTransfer.setData("text/plain",this.renderer.renderTree.wiki.getTiddlerText(this.to,""));
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
LinkWidget.prototype.handleDragEndEvent = function(event) {
|
||||
// Remove the dragging class on the element being dragged
|
||||
$tw.utils.removeClass(event.target,"tw-tiddlylink-dragging");
|
||||
// Delete the drag image element
|
||||
if(this.dragImage) {
|
||||
this.dragImage.parentNode.removeChild(this.dragImage);
|
||||
}
|
||||
};
|
||||
|
||||
LinkWidget.prototype.postRenderInDom = function() {
|
||||
// Add the draggable attribute to links (we don't include it in the static HTML representation)
|
||||
if(this.renderer.domNode.tagName === "A") {
|
||||
this.renderer.domNode.setAttribute("draggable",true);
|
||||
}
|
||||
};
|
||||
|
||||
LinkWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Set the class for missing tiddlers
|
||||
if(this.targetTitle && changedTiddlers[this.targetTitle]) {
|
||||
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle));
|
||||
}
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.to || changedAttributes.hover || (this.to && changedTiddlers[this.to]) || (this.hover && changedTiddlers[this.hover])) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.link = LinkWidget;
|
||||
|
||||
})();
|
@ -1,71 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/linkcatcher.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the linkcatcher widget. It intercepts navigation events from its children, preventing normal navigation, and instead stores the name of the target tiddler in the text reference specified in the `to` attribute.
|
||||
|
||||
Using the linkcatcher widget allows the linking mechanism to be used for tasks like selecting the current theme tiddler from a list.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var LinkCatcherWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
LinkCatcherWidget.prototype.generate = function() {
|
||||
// Get our attributes
|
||||
this.to = this.renderer.getAttribute("to");
|
||||
this.message = this.renderer.getAttribute("message");
|
||||
this.set = this.renderer.getAttribute("set");
|
||||
this.setTo = this.renderer.getAttribute("setTo");
|
||||
// Set the element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": "tw-linkcatcher"
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
this.events = [
|
||||
{name: "tw-navigate", handlerObject: this, handlerMethod: "handleNavigateEvent"}
|
||||
];
|
||||
};
|
||||
|
||||
LinkCatcherWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Navigate to a specified tiddler
|
||||
LinkCatcherWidget.prototype.handleNavigateEvent = function(event) {
|
||||
if(this.to) {
|
||||
this.renderer.renderTree.wiki.setTextReference(this.to,event.navigateTo,this.renderer.tiddlerTitle);
|
||||
}
|
||||
if(this.message) {
|
||||
$tw.utils.dispatchCustomEvent(this.renderer.domNode,this.message,{
|
||||
param: event.navigateTo,
|
||||
tiddlerTitle: this.renderer.tiddlerTitle
|
||||
});
|
||||
}
|
||||
if(this.set) {
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.set);
|
||||
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(tiddler,{title: this.set, text: this.setTo}));
|
||||
}
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.linkcatcher = LinkCatcherWidget;
|
||||
|
||||
})();
|
@ -1,408 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/list/list.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The list widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ListWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Initialise the listviews if they've not been done already
|
||||
if(!this.listViews) {
|
||||
ListWidget.prototype.listViews = {};
|
||||
$tw.modules.applyMethods("listview",this.listViews);
|
||||
}
|
||||
// Generate widget elements
|
||||
this.generate();
|
||||
};
|
||||
|
||||
var typeInfoByType = {
|
||||
plain: {
|
||||
frame: {
|
||||
block: "div", inline: "span"
|
||||
},
|
||||
member: {
|
||||
block: "div", inline: "span"
|
||||
}
|
||||
},
|
||||
ul: {
|
||||
frame: {
|
||||
block: "ul", inline: "ul"
|
||||
},
|
||||
member: {
|
||||
block: "li", inline: "li"
|
||||
}
|
||||
},
|
||||
ol: {
|
||||
frame: {
|
||||
block: "ol", inline: "ol"
|
||||
},
|
||||
member: {
|
||||
block: "li", inline: "li"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ListWidget.prototype.generate = function() {
|
||||
// Get our attributes
|
||||
this.macro = this.renderer.getAttribute("macro");
|
||||
this.type = this.renderer.getAttribute("type","plain");
|
||||
this.itemClass = this.renderer.getAttribute("itemClass");
|
||||
this.template = this.renderer.getAttribute("template");
|
||||
this.editTemplate = this.renderer.getAttribute("editTemplate");
|
||||
this.emptyMessage = this.renderer.getAttribute("emptyMessage");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
// Get our type information
|
||||
this.typeInfo = typeInfoByType[this.type] || typeInfoByType.plain;
|
||||
// Set up the classes
|
||||
var classes = ["tw-list-frame"];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
// Get the list of tiddlers object
|
||||
this.getTiddlerList();
|
||||
// Create the list
|
||||
var listMembers = [];
|
||||
if(this.list.length === 0) {
|
||||
// Check for an empty list
|
||||
listMembers = [this.getEmptyMessage()];
|
||||
} else {
|
||||
// Create the list
|
||||
for(var t=0; t<this.list.length; t++) {
|
||||
listMembers.push(this.createListElement(this.list[t]));
|
||||
}
|
||||
}
|
||||
// Create the list frame element
|
||||
this.tag = this.renderer.parseTreeNode.isBlock ? this.typeInfo.frame.block : this.typeInfo.frame.inline;
|
||||
this.attributes = {
|
||||
"class": classes.join(" ")
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,listMembers);
|
||||
};
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var filter;
|
||||
if(this.renderer.hasAttribute("filter")) {
|
||||
filter = this.renderer.getAttribute("filter");
|
||||
}
|
||||
if(!filter) {
|
||||
filter = "[!is[system]]";
|
||||
}
|
||||
this.list = this.renderer.renderTree.wiki.filterTiddlers(filter,this.renderer.tiddlerTitle);
|
||||
};
|
||||
|
||||
/*
|
||||
Create and execute the nodes representing the empty message
|
||||
*/
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
return {
|
||||
type: "element",
|
||||
tag: "span",
|
||||
children: this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",this.emptyMessage,{parseAsInline: true}).tree
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Create a list element representing a given tiddler
|
||||
*/
|
||||
ListWidget.prototype.createListElement = function(title) {
|
||||
// Define an event handler that adds navigation information to the event
|
||||
var handleEvent = function(event) {
|
||||
event.navigateFromTitle = title;
|
||||
return true;
|
||||
},
|
||||
classes = ["tw-list-element"];
|
||||
// Add any specified classes
|
||||
if(this.itemClass) {
|
||||
$tw.utils.pushTop(classes,this.itemClass);
|
||||
}
|
||||
// Return the list element
|
||||
return {
|
||||
type: "element",
|
||||
tag: this.renderer.parseTreeNode.isBlock ? this.typeInfo.member.block : this.typeInfo.member.inline,
|
||||
attributes: {
|
||||
"class": {type: "string", value: classes.join(" ")}
|
||||
},
|
||||
children: [this.createListElementParseTree(title)],
|
||||
events: [
|
||||
{name: "tw-navigate", handlerFunction: handleEvent},
|
||||
{name: "tw-edit-tiddler", handlerFunction: handleEvent},
|
||||
{name: "tw-save-tiddler", handlerFunction: handleEvent},
|
||||
{name: "tw-close-tiddler", handlerFunction: handleEvent},
|
||||
{name: "tw-new-tiddler", handlerFunction: handleEvent}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Create the parse tree nodes needed to represent a given list element
|
||||
*/
|
||||
ListWidget.prototype.createListElementParseTree = function(title) {
|
||||
if(this.macro) {
|
||||
return this.createListElementMacro(title);
|
||||
} else {
|
||||
return this.createListElementTransclusion(title);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Create a macro call to represent a list element
|
||||
*/
|
||||
ListWidget.prototype.createListElementMacro = function(title) {
|
||||
// Create the macrocall rendertree node
|
||||
return {
|
||||
type: "macrocall",
|
||||
name: this.macro,
|
||||
params: [
|
||||
{name: "title", value: title}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Create a transclusion to represent a list element
|
||||
*/
|
||||
ListWidget.prototype.createListElementTransclusion = function(title) {
|
||||
// Check if the tiddler is a draft
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(title),
|
||||
isDraft = tiddler ? tiddler.hasField("draft.of") : false;
|
||||
// Figure out the template to use
|
||||
var template = this.template,
|
||||
templateTree = undefined;
|
||||
if(isDraft && this.editTemplate) {
|
||||
template = this.editTemplate;
|
||||
}
|
||||
// Check for not having a template
|
||||
if(!template) {
|
||||
if(this.renderer.parseTreeNode.children && this.renderer.parseTreeNode.children.length > 0) {
|
||||
// Use our content as the template
|
||||
templateTree = this.renderer.parseTreeNode.children;
|
||||
} else {
|
||||
// Use default content
|
||||
templateTree = [{
|
||||
type: "element",
|
||||
tag: "$view",
|
||||
attributes: {
|
||||
field: {type: "string", value: "title"},
|
||||
format: {type: "string", value: "link"}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
// Create the element widgets
|
||||
if(this.renderer.hasAttribute("hackTemplate")) {
|
||||
return {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
isBlock: this.renderer.parseTreeNode.isBlock,
|
||||
attributes: {
|
||||
title: {type: "string", value: title}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if(!templateTree) {
|
||||
templateTree = [{
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
attributes: {
|
||||
title: {type: "string", value: template}
|
||||
},
|
||||
children: templateTree
|
||||
}];
|
||||
}
|
||||
return {
|
||||
type: "element",
|
||||
tag: "$tiddler",
|
||||
isBlock: this.renderer.parseTreeNode.isBlock,
|
||||
attributes: {
|
||||
title: {type: "string", value: title}
|
||||
},
|
||||
children: templateTree
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove a list element from the list, along with the attendant DOM nodes
|
||||
*/
|
||||
ListWidget.prototype.removeListElement = function(index) {
|
||||
// Get the list element
|
||||
var listElement = this.children[index];
|
||||
// Invoke the listview to animate the removal
|
||||
if(this.listview && this.listview.remove) {
|
||||
if(!this.listview.remove(index)) {
|
||||
// Only delete the DOM element if the listview.remove() returned false
|
||||
listElement.domNode.parentNode.removeChild(listElement.domNode);
|
||||
}
|
||||
} else {
|
||||
// Always remove the DOM node if we didn't invoke the listview
|
||||
listElement.domNode.parentNode.removeChild(listElement.domNode);
|
||||
}
|
||||
// Then delete the actual renderer node
|
||||
this.children.splice(index,1);
|
||||
};
|
||||
|
||||
/*
|
||||
Return the index of the list element that corresponds to a particular title
|
||||
startIndex: index to start search (use zero to search from the top)
|
||||
title: tiddler title to seach for
|
||||
*/
|
||||
ListWidget.prototype.findListElementByTitle = function(startIndex,title) {
|
||||
var testNode = this.macro ? function(node) {
|
||||
// We're looking for a macro list element
|
||||
return node.widget.children[0].parseTreeNode.params[0].value === title;
|
||||
} : (this.renderer.hasAttribute("hackTemplate") ? function(node) {
|
||||
// We're looking for a transclusion list element
|
||||
return node.widget.children[0].attributes.title === title;
|
||||
} : function(node) {
|
||||
// We're looking for a transclusion list element
|
||||
return node.widget.children[0].attributes.title === title;
|
||||
});
|
||||
// Search for the list element
|
||||
while(startIndex < this.children.length) {
|
||||
if(testNode(this.children[startIndex])) {
|
||||
return startIndex;
|
||||
}
|
||||
startIndex++;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
ListWidget.prototype.postRenderInDom = function() {
|
||||
this.listview = this.chooseListView();
|
||||
this.history = [];
|
||||
};
|
||||
|
||||
/*
|
||||
Select the appropriate list viewer
|
||||
*/
|
||||
ListWidget.prototype.chooseListView = function() {
|
||||
// Instantiate the list view
|
||||
var listviewName = this.renderer.getAttribute("listview");
|
||||
var ListView = this.listViews[listviewName];
|
||||
return ListView ? new ListView(this) : null;
|
||||
};
|
||||
|
||||
ListWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Reexecute the widget if any of our attributes have changed
|
||||
if(changedAttributes.itemClass || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.type || changedAttributes.filter || changedAttributes.template || changedAttributes.history || changedAttributes.listview) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// Handle any changes to the list, and refresh any nodes we're reusing
|
||||
this.handleListChanges(changedTiddlers);
|
||||
// Update the history list
|
||||
var history = this.renderer.getAttribute("history");
|
||||
if(history && changedTiddlers[history]) {
|
||||
this.handleHistoryChanges();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
var t,
|
||||
prevListLength = this.list.length,
|
||||
self = this;
|
||||
// Get the list of tiddlers, having saved the previous length
|
||||
this.getTiddlerList();
|
||||
// Check if the list is empty
|
||||
if(this.list.length === 0) {
|
||||
// Check if it was empty before
|
||||
if(prevListLength === 0) {
|
||||
// If so, just refresh the empty message
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
// If the list wasn't empty before, empty it
|
||||
for(t=prevListLength-1; t>=0; t--) {
|
||||
this.removeListElement(t);
|
||||
}
|
||||
// Insert the empty message
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[this.getEmptyMessage()]);
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.renderInDom) {
|
||||
self.renderer.domNode.appendChild(node.renderInDom());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If it is not empty now, but was empty previously, then remove the empty message
|
||||
if(prevListLength === 0) {
|
||||
this.removeListElement(0);
|
||||
}
|
||||
}
|
||||
// Step through the list and adjust our child list elements appropriately
|
||||
for(t=0; t<this.list.length; t++) {
|
||||
// Check to see if the list element is already there
|
||||
var index = this.findListElementByTitle(t,this.list[t]);
|
||||
if(index === undefined) {
|
||||
// The list element isn't there, so we need to insert it
|
||||
this.children.splice(t,0,this.renderer.renderTree.createRenderer(this.renderer,this.createListElement(this.list[t])));
|
||||
var before = this.renderer.domNode.childNodes[t],
|
||||
newNode = this.children[t].renderInDom();
|
||||
if(before) {
|
||||
this.renderer.domNode.insertBefore(newNode,before);
|
||||
} else {
|
||||
this.renderer.domNode.appendChild(newNode);
|
||||
}
|
||||
// Ask the listview to animate the insertion
|
||||
if(this.listview && this.listview.insert) {
|
||||
this.listview.insert(t);
|
||||
}
|
||||
} else {
|
||||
// Delete any list elements preceding the one we want
|
||||
for(var n=index-1; n>=t; n--) {
|
||||
this.removeListElement(n);
|
||||
}
|
||||
// Refresh the node we're reusing
|
||||
this.children[t].refreshInDom(changedTiddlers);
|
||||
}
|
||||
}
|
||||
// Remove any left over elements
|
||||
for(t=this.children.length-1; t>=this.list.length; t--) {
|
||||
this.removeListElement(t);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Handle any changes to the history list
|
||||
*/
|
||||
ListWidget.prototype.handleHistoryChanges = function() {
|
||||
// Get the history data
|
||||
var historyAtt = this.renderer.getAttribute("history"),
|
||||
newHistory = this.renderer.renderTree.wiki.getTiddlerData(historyAtt,[]);
|
||||
// Ignore any entries of the history that match the previous history
|
||||
var entry = 0;
|
||||
while(entry < newHistory.length && entry < this.history.length && newHistory[entry].title === this.history[entry].title) {
|
||||
entry++;
|
||||
}
|
||||
// Navigate forwards to each of the new tiddlers
|
||||
while(entry < newHistory.length) {
|
||||
if(this.listview && this.listview.navigateTo) {
|
||||
this.listview.navigateTo(newHistory[entry]);
|
||||
}
|
||||
entry++;
|
||||
}
|
||||
// Update the history
|
||||
this.history = newHistory;
|
||||
};
|
||||
|
||||
exports.list = ListWidget;
|
||||
|
||||
})();
|
@ -1,31 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/list/listviews/scroller.js
|
||||
type: application/javascript
|
||||
module-type: listview
|
||||
|
||||
A list view that scrolls to newly inserted elements
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var ScrollerListView = function(listWidget) {
|
||||
this.listWidget = listWidget;
|
||||
}
|
||||
|
||||
ScrollerListView.prototype.navigateTo = function(historyInfo) {
|
||||
var listElementIndex = this.listWidget.findListElementByTitle(0,historyInfo.title),
|
||||
listElementNode = this.listWidget.children[listElementIndex],
|
||||
targetElement = listElementNode.domNode;
|
||||
// Scroll the node into view
|
||||
var scrollEvent = this.listWidget.renderer.renderTree.document.createEvent("Event");
|
||||
scrollEvent.initEvent("tw-scroll",true,true);
|
||||
targetElement.dispatchEvent(scrollEvent);
|
||||
};
|
||||
|
||||
exports.scroller = ScrollerListView;
|
||||
|
||||
})();
|
@ -1,45 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/password.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the password widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var PasswordWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
PasswordWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.name = this.renderer.getAttribute("name");
|
||||
// Get the current password
|
||||
var password = $tw.browser ? $tw.utils.getPassword(this.name) : "";
|
||||
// Generate our element
|
||||
this.tag = "input";
|
||||
this.attributes = {
|
||||
type: "password",
|
||||
value: password
|
||||
};
|
||||
this.events = [
|
||||
{name: "keyup", handlerObject: this},
|
||||
{name: "input", handlerObject: this}];
|
||||
};
|
||||
|
||||
PasswordWidget.prototype.handleEvent = function(event) {
|
||||
var password = this.renderer.domNode.value;
|
||||
return $tw.utils.savePassword(this.name,password);
|
||||
};
|
||||
|
||||
exports.password = PasswordWidget;
|
||||
|
||||
})();
|
@ -1,216 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/reveal.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the reveal widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var RevealWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
RevealWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.state = this.renderer.getAttribute("state");
|
||||
this.type = this.renderer.getAttribute("type");
|
||||
this.text = this.renderer.getAttribute("text");
|
||||
this.position = this.renderer.getAttribute("position");
|
||||
this["default"] = this.renderer.getAttribute("default","");
|
||||
this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
this.animate = this.renderer.getAttribute("animate","no");
|
||||
// Compute the title of the state tiddler and read it
|
||||
this.stateTitle = this.state;
|
||||
if(this.qualifyTiddlerTitles) {
|
||||
this.stateTitle = this.stateTitle + "-" + this.renderer.renderTree.getContextScopeId(this.renderer.parentRenderer);
|
||||
}
|
||||
this.readState();
|
||||
// Set up the element attributes
|
||||
var classes = ["tw-reveal"],
|
||||
styles = [];
|
||||
if(this["class"]) {
|
||||
$tw.utils.pushTop(classes,this["class"]);
|
||||
}
|
||||
if(this.isOpen) {
|
||||
$tw.utils.pushTop(classes,"tw-reveal-open");
|
||||
}
|
||||
switch(this.type) {
|
||||
case "popup":
|
||||
styles.push("position:absolute;");
|
||||
classes.push("tw-popup");
|
||||
break;
|
||||
}
|
||||
styles.push("display:" + (this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none") + ";");
|
||||
// Set the element
|
||||
this.tag = "div";
|
||||
this.attributes = {
|
||||
"class": classes.join(" "),
|
||||
style: styles.join("")
|
||||
};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.isOpen ? this.renderer.parseTreeNode.children : []);
|
||||
this.events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
|
||||
};
|
||||
|
||||
/*
|
||||
Read the state tiddler
|
||||
*/
|
||||
RevealWidget.prototype.readState = function() {
|
||||
// Read the information from the state tiddler
|
||||
if(this.stateTitle) {
|
||||
var state = this.renderer.renderTree.wiki.getTextReference(this.stateTitle,this["default"],this.renderer.tiddlerTitle);
|
||||
switch(this.type) {
|
||||
case "popup":
|
||||
this.readPopupState(state);
|
||||
break;
|
||||
case "match":
|
||||
this.readMatchState(state);
|
||||
break;
|
||||
case "nomatch":
|
||||
this.readMatchState(state);
|
||||
this.isOpen = !this.isOpen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RevealWidget.prototype.readMatchState = function(state) {
|
||||
this.isOpen = state === this.text;
|
||||
};
|
||||
|
||||
RevealWidget.prototype.readPopupState = function(state) {
|
||||
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||
match = popupLocationRegExp.exec(state);
|
||||
// Check if the state matches the location regexp
|
||||
if(match) {
|
||||
// If so, we're open
|
||||
this.isOpen = true;
|
||||
// Get the location
|
||||
this.popup = {
|
||||
left: parseFloat(match[1]),
|
||||
top: parseFloat(match[2]),
|
||||
width: parseFloat(match[3]),
|
||||
height: parseFloat(match[4])
|
||||
};
|
||||
} else {
|
||||
// If not, we're closed
|
||||
this.isOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
RevealWidget.prototype.handleClickEvent = function(event) {
|
||||
if(event.type === "click" && this.type === "popup") {
|
||||
// Cancel the popup if we get a click on it
|
||||
if(this.stateTitle) {
|
||||
this.renderer.renderTree.wiki.deleteTextReference(this.stateTitle);
|
||||
}
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
RevealWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
var self = this;
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"]) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
var needChildrenRefresh = true; // Avoid refreshing the children nodes if we don't need to
|
||||
// Get the open state
|
||||
var previousState = this.isOpen
|
||||
this.readState();
|
||||
// Construct the child nodes if required
|
||||
if(this.isOpen && this.children.length === 0) {
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
var parentNode = this.renderer.domNode;
|
||||
$tw.utils.each(this.children,function(child) {
|
||||
parentNode.appendChild(child.renderInDom());
|
||||
});
|
||||
needChildrenRefresh = false;
|
||||
}
|
||||
// Refresh any child nodes
|
||||
if(needChildrenRefresh) {
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Animate the opening or closing
|
||||
if(this.isOpen !== previousState) {
|
||||
if(this.animate !== "no") {
|
||||
if(this.isOpen) {
|
||||
this.renderer.domNode.style.display = this.renderer.parseTreeNode.isBlock ? "block" : "inline";
|
||||
$tw.anim.perform("open",this.renderer.domNode);
|
||||
} else {
|
||||
$tw.anim.perform("close",this.renderer.domNode,{callback: function() {
|
||||
self.renderer.domNode.style.display = "none";
|
||||
}});
|
||||
}
|
||||
} else {
|
||||
this.renderer.domNode.style.display = this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none";
|
||||
}
|
||||
}
|
||||
// Add or remove the tw-reveal-open class
|
||||
$tw.utils.toggleClass(this.renderer.domNode,"tw-reveal-open",this.isOpen);
|
||||
}
|
||||
// Position the content if required
|
||||
if(this.isOpen) {
|
||||
this.postRenderInDom();
|
||||
}
|
||||
};
|
||||
|
||||
RevealWidget.prototype.postRenderInDom = function() {
|
||||
switch(this.type) {
|
||||
case "popup":
|
||||
if(this.isOpen) {
|
||||
this.renderer.domNode.style.position = "absolute";
|
||||
this.renderer.domNode.style.zIndex = "1000";
|
||||
switch(this.position) {
|
||||
case "left":
|
||||
this.renderer.domNode.style.left = (this.popup.left - this.renderer.domNode.offsetWidth) + "px";
|
||||
this.renderer.domNode.style.top = this.popup.top + "px";
|
||||
break;
|
||||
case "above":
|
||||
this.renderer.domNode.style.left = this.popup.left + "px";
|
||||
this.renderer.domNode.style.top = (this.popup.top - this.renderer.domNode.offsetHeight) + "px";
|
||||
break;
|
||||
case "aboveright":
|
||||
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
|
||||
this.renderer.domNode.style.top = (this.popup.top + this.popup.height - this.renderer.domNode.offsetHeight) + "px";
|
||||
break;
|
||||
case "right":
|
||||
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
|
||||
this.renderer.domNode.style.top = this.popup.top + "px";
|
||||
break;
|
||||
case "belowleft":
|
||||
this.renderer.domNode.style.left = (this.popup.left + this.popup.width - this.renderer.domNode.offsetWidth) + "px";
|
||||
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
|
||||
break;
|
||||
default: // Below
|
||||
this.renderer.domNode.style.left = this.popup.left + "px";
|
||||
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
exports.reveal = RevealWidget;
|
||||
|
||||
})();
|
@ -1,58 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/setstyle.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the setstyle widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var SetStyleWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
SetStyleWidget.prototype.generate = function() {
|
||||
// Get the parameters from the attributes
|
||||
this.name = this.renderer.getAttribute("name");
|
||||
this.value = this.renderer.getAttribute("value");
|
||||
this["class"] = this.renderer.getAttribute("class");
|
||||
// Set up the element
|
||||
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.attributes = {
|
||||
style: this.name + ":" + this.value
|
||||
};
|
||||
if(this["class"]) {
|
||||
this.attributes["class"] = this["class"];
|
||||
}
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
};
|
||||
|
||||
SetStyleWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.name || changedAttributes.value || changedAttributes["class"]) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.setstyle = SetStyleWidget;
|
||||
|
||||
})();
|
@ -1,82 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/tiddler.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The tiddler widget sets the current tiddler to a specified title.
|
||||
|
||||
Attributes:
|
||||
title: the title of the current tiddler
|
||||
class: CSS classes
|
||||
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var TiddlerWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
TiddlerWidget.prototype.generate = function() {
|
||||
var self = this;
|
||||
this.tiddlerTitle = this.renderer.getAttribute("title","");
|
||||
// Set up the attributes for the wrapper element
|
||||
var classes = ["tw-tiddler"];
|
||||
if(this.renderer.hasAttribute("class")) {
|
||||
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
|
||||
}
|
||||
if(!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle) && !this.renderer.renderTree.wiki.isShadowTiddler(this.tiddlerTitle)) {
|
||||
$tw.utils.pushTop(classes,"tw-tiddler-missing");
|
||||
}
|
||||
// Save the context for this renderer node
|
||||
this.renderer.context = {
|
||||
tiddlerTitle: this.tiddlerTitle
|
||||
};
|
||||
// Initialise events
|
||||
this.events = [];
|
||||
// Trap and update tag modification events
|
||||
this.events.push({name: "tw-remove-tag", handlerFunction: function(event) {
|
||||
event.currentTag = self.tiddlerTitle;
|
||||
return true;
|
||||
}});
|
||||
// Set the element
|
||||
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.attributes = {};
|
||||
if(classes.length > 0) {
|
||||
this.attributes["class"] = classes.join(" ");
|
||||
}
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
||||
};
|
||||
|
||||
TiddlerWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Set the class for missing tiddlers
|
||||
if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) {
|
||||
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle));
|
||||
}
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.title) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.tiddler = TiddlerWidget;
|
||||
|
||||
})();
|
@ -1,105 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/transclude.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The transclude widget includes another tiddler into the tiddler being rendered.
|
||||
|
||||
Attributes:
|
||||
title: the title of the tiddler to transclude
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var TranscludeWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
TranscludeWidget.prototype.generate = function() {
|
||||
var self = this,
|
||||
templateParseTree;
|
||||
// Get the render target details
|
||||
this.transcludeTitle = this.renderer.getAttribute("title",this.renderer.tiddlerTitle);
|
||||
this.transcludeField = this.renderer.getAttribute("field");
|
||||
this.transcludeIndex = this.renderer.getAttribute("index");
|
||||
// Check for recursion
|
||||
if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{
|
||||
transcludeTitle: this.transcludeTitle,
|
||||
transcludeField: this.transcludeField,
|
||||
transcludeIndex: this.transcludeIndex
|
||||
})) {
|
||||
templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}];
|
||||
} else {
|
||||
var parser;
|
||||
if(this.transcludeField === "text" || (!this.transcludeField && !this.transcludeIndex)) {
|
||||
parser = this.renderer.renderTree.wiki.parseTiddler(this.transcludeTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
|
||||
} else {
|
||||
var tiddler,text;
|
||||
if(this.transcludeField) {
|
||||
tiddler = this.renderer.renderTree.wiki.getTiddler(this.transcludeTitle);
|
||||
text = tiddler ? tiddler.fields[this.transcludeField] : "";
|
||||
if(text === undefined) {
|
||||
text = "";
|
||||
}
|
||||
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
|
||||
} else if(this.transcludeIndex) {
|
||||
text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.transcludeTitle,this.transcludeIndex,"");
|
||||
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
|
||||
}
|
||||
}
|
||||
templateParseTree = parser ? parser.tree : [];
|
||||
}
|
||||
// Set up the attributes for the wrapper element
|
||||
var classes = ["tw-transclude"];
|
||||
if(this.renderer.hasAttribute("class")) {
|
||||
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
|
||||
}
|
||||
// Save the context for this renderer node
|
||||
this.renderer.context = {
|
||||
transcludeTitle: this.transcludeTitle,
|
||||
transcludeField: this.transcludeField,
|
||||
transcludeIndex: this.transcludeIndex
|
||||
};
|
||||
// Set the element
|
||||
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.attributes = {};
|
||||
if(classes.length > 0) {
|
||||
this.attributes["class"] = classes.join(" ");
|
||||
}
|
||||
if(this.renderer.hasAttribute("style")) {
|
||||
this.attributes.style = this.renderer.getAttribute("style");
|
||||
}
|
||||
if(this.renderer.hasAttribute("tooltip")) {
|
||||
this.attributes.title = this.renderer.getAttribute("tooltip");
|
||||
}
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,templateParseTree);
|
||||
};
|
||||
|
||||
TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.transcludeField || changedAttributes.transcludeIndex || (this.transcludeTitle && changedTiddlers[this.transcludeTitle])) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.transclude = TranscludeWidget;
|
||||
|
||||
})();
|
@ -1,34 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/version.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the version widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var VersionWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
VersionWidget.prototype.generate = function() {
|
||||
// Set the element
|
||||
this.tag = "span";
|
||||
this.attributes = {};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: $tw.version
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.version = VersionWidget;
|
||||
|
||||
})();
|
@ -1,70 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/video.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Implements the video widget.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var VideoWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
VideoWidget.prototype.generate = function() {
|
||||
// Get attributes
|
||||
this.src = this.renderer.getAttribute("src");
|
||||
this.type = this.renderer.getAttribute("type","vimeo");
|
||||
this.width = parseInt(this.renderer.getAttribute("width","640"),10);
|
||||
this.height = parseInt(this.renderer.getAttribute("height","360"),10);
|
||||
// Return the appropriate element
|
||||
switch(this.type) {
|
||||
case "vimeo":
|
||||
this.tag = "iframe";
|
||||
this.attributes = {
|
||||
src: "http://player.vimeo.com/video/" + this.src + "?autoplay=0",
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
frameborder: 0
|
||||
};
|
||||
break;
|
||||
case "youtube":
|
||||
this.tag = "iframe";
|
||||
this.attributes = {
|
||||
src: "http://www.youtube.com/embed/" + this.src,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
frameborder: 0
|
||||
};
|
||||
break;
|
||||
case "archiveorg":
|
||||
this.tag = "iframe";
|
||||
this.attributes = {
|
||||
src: "http://www.archive.org/embed/" + this.src,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
frameborder: 0
|
||||
};
|
||||
break;
|
||||
default:
|
||||
this.tag = "div";
|
||||
this.attributes = {};
|
||||
this.children = this.renderer.renderTree.createRenderers(this.renderer,[{
|
||||
type: "text",
|
||||
text: "Unknown video type"
|
||||
}]);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
exports.video = VideoWidget;
|
||||
|
||||
})();
|
@ -1,120 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
The view widget displays a tiddler field.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Define the "text" viewer here so that it is always available
|
||||
*/
|
||||
var TextViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
TextViewer.prototype.render = function() {
|
||||
// Get the value as a string
|
||||
if(this.field !== "text" && this.tiddler) {
|
||||
this.value = this.tiddler.getFieldString(this.field);
|
||||
}
|
||||
var value = "";
|
||||
if(this.value !== undefined && this.value !== null) {
|
||||
value = this.value;
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "span";
|
||||
this.viewWidget.attributes = {};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
|
||||
type: "text",
|
||||
text: value
|
||||
}]);
|
||||
};
|
||||
|
||||
var ViewWidget = function(renderer) {
|
||||
// Save state
|
||||
this.renderer = renderer;
|
||||
// Initialise the field viewers if they've not been done already
|
||||
if(!this.fieldViewers) {
|
||||
ViewWidget.prototype.fieldViewers = {text: TextViewer}; // Start with the built-in text viewer
|
||||
$tw.modules.applyMethods("fieldviewer",this.fieldViewers);
|
||||
}
|
||||
// Generate child nodes
|
||||
this.generate();
|
||||
};
|
||||
|
||||
ViewWidget.prototype.generate = function() {
|
||||
// Get parameters from our attributes
|
||||
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.tiddlerTitle);
|
||||
this.fieldName = this.renderer.getAttribute("field","text");
|
||||
this.format = this.renderer.getAttribute("format","text");
|
||||
// Get the value to display
|
||||
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.tiddlerTitle),
|
||||
value;
|
||||
if(tiddler) {
|
||||
if(this.fieldName === "text") {
|
||||
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
||||
value = this.renderer.renderTree.wiki.getTiddlerText(this.tiddlerTitle);
|
||||
} else {
|
||||
value = tiddler.fields[this.fieldName];
|
||||
}
|
||||
} else { // Use a special value if the tiddler is missing
|
||||
switch(this.fieldName) {
|
||||
case "title":
|
||||
value = this.tiddlerTitle;
|
||||
break;
|
||||
case "modified":
|
||||
case "created":
|
||||
value = new Date();
|
||||
break;
|
||||
default:
|
||||
value = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Choose the viewer to use
|
||||
var Viewer = this.fieldViewers.text;
|
||||
if($tw.utils.hop(this.fieldViewers,this.format)) {
|
||||
Viewer = this.fieldViewers[this.format];
|
||||
}
|
||||
this.viewer = new Viewer(this,tiddler,this.fieldName,value);
|
||||
// Ask the viewer to create the widget element
|
||||
this.viewer.render();
|
||||
};
|
||||
|
||||
ViewWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.format || (this.tiddlerTitle && changedTiddlers[this.tiddlerTitle])) {
|
||||
// Regenerate and rerender the widget and replace the existing DOM node
|
||||
this.generate();
|
||||
var oldDomNode = this.renderer.domNode,
|
||||
newDomNode = this.renderer.renderInDom();
|
||||
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
|
||||
} else {
|
||||
// We don't need to refresh ourselves, so just refresh any child nodes
|
||||
$tw.utils.each(this.children,function(node) {
|
||||
if(node.refreshInDom) {
|
||||
node.refreshInDom(changedTiddlers);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ViewWidget.prototype.postRenderInDom = function() {
|
||||
if(this.viewer && this.viewer.postRenderInDom) {
|
||||
this.viewer.postRenderInDom();
|
||||
}
|
||||
};
|
||||
|
||||
exports.view = ViewWidget;
|
||||
|
||||
})();
|
@ -1,41 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/date.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as a date
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var DateViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
DateViewer.prototype.render = function() {
|
||||
var template = this.viewWidget.renderer.getAttribute("template","DD MMM YYYY"),
|
||||
value = "";
|
||||
if(this.value !== undefined) {
|
||||
value = $tw.utils.formatDateString(this.value,template);
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "span";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-date"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: value
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.date = DateViewer;
|
||||
|
||||
})();
|
@ -1,44 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/htmlencoded.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as HTML encoded text
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var HtmlEncodedViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
HtmlEncodedViewer.prototype.render = function() {
|
||||
// Get the value as a string
|
||||
if(this.field !== "text" && this.tiddler) {
|
||||
this.value = this.tiddler.getFieldString(this.field);
|
||||
}
|
||||
var value = "";
|
||||
if(this.value !== undefined && this.value !== null) {
|
||||
value = this.value;
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "span";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-htmlencoded"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: $tw.utils.htmlEncode(value)
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.htmlencoded = HtmlEncodedViewer;
|
||||
|
||||
})();
|
@ -1,44 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/htmlwikified.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as a textual HTML representation of the wikified text
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var HtmlWikifiedViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
HtmlWikifiedViewer.prototype.render = function() {
|
||||
// Parse the field text
|
||||
var wiki = this.viewWidget.renderer.renderTree.wiki,
|
||||
parser = wiki.parseText("text/vnd.tiddlywiki",this.value),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: wiki, parentRenderer: this.viewWidget.renderer, document: this.viewWidget.renderer.renderTree.document});
|
||||
renderTree.execute();
|
||||
var container = this.viewWidget.renderer.renderTree.document.createElement("div");
|
||||
renderTree.renderInDom(container)
|
||||
var text = container.innerHTML;
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "pre";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-htmlwikified"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: text
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.htmlwikified = HtmlWikifiedViewer;
|
||||
|
||||
})();
|
@ -1,44 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/jsencoded.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as JavaScript stringified text
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var JsEncodedViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
JsEncodedViewer.prototype.render = function() {
|
||||
// Get the value as a string
|
||||
if(this.field !== "text" && this.tiddler) {
|
||||
this.value = this.tiddler.getFieldString(this.field);
|
||||
}
|
||||
var value = "";
|
||||
if(this.value !== undefined && this.value !== null) {
|
||||
value = this.value;
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "pre";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-jsencoded"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: $tw.utils.stringify(value)
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.jsencoded = JsEncodedViewer;
|
||||
|
||||
})();
|
@ -1,44 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/link.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as a link
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var LinkViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
LinkViewer.prototype.render = function() {
|
||||
var text = this.value === undefined ? "" : this.value;
|
||||
// Indicate that we're not generating an element
|
||||
this.viewWidget.tag = this.viewWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-link"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "element",
|
||||
tag: "$link",
|
||||
attributes: {
|
||||
to: {type: "string", value: text}
|
||||
},
|
||||
children: [{
|
||||
type: "text",
|
||||
text: text
|
||||
}]
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.link = LinkViewer;
|
||||
|
||||
})();
|
@ -1,77 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/relativedate.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as a relative date
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var RelativeDateViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
RelativeDateViewer.prototype.render = function() {
|
||||
var template = this.viewWidget.renderer.getAttribute("template","DD MMM YYYY"),
|
||||
value = "";
|
||||
if(this.value !== undefined) {
|
||||
value = $tw.utils.formatDateString(this.value,template);
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "span";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-date"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: value
|
||||
}]);
|
||||
};
|
||||
|
||||
/*
|
||||
Trigger the timer when the relative date is put into the DOM
|
||||
*/
|
||||
RelativeDateViewer.prototype.postRenderInDom = function() {
|
||||
this.update();
|
||||
};
|
||||
|
||||
/*
|
||||
Trigger the timer for the next update of the relative date
|
||||
*/
|
||||
RelativeDateViewer.prototype.setTimer = function() {
|
||||
var self = this;
|
||||
if(this.relativeDate.updatePeriod < 24 * 60 * 60 * 1000) {
|
||||
window.setTimeout(function() {
|
||||
// Only call the update function if the dom node is still in the document
|
||||
if($tw.utils.domContains(self.viewWidget.renderer.renderTree.document,self.viewWidget.renderer.domNode)) {
|
||||
self.update.call(self);
|
||||
}
|
||||
},this.relativeDate.updatePeriod);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Update the relative date display, and trigger the timer for the next update
|
||||
*/
|
||||
RelativeDateViewer.prototype.update = function() {
|
||||
this.relativeDate = $tw.utils.getRelativeDate((new Date()) - this.value);
|
||||
if(this.relativeDate.delta > 0) {
|
||||
while(this.viewWidget.renderer.domNode.hasChildNodes()) {
|
||||
this.viewWidget.renderer.domNode.removeChild(this.viewWidget.renderer.domNode.firstChild);
|
||||
}
|
||||
this.viewWidget.renderer.domNode.appendChild(this.viewWidget.renderer.renderTree.document.createTextNode(this.relativeDate.description));
|
||||
this.setTimer();
|
||||
}
|
||||
};
|
||||
|
||||
exports.relativedate = RelativeDateViewer;
|
||||
|
||||
})();
|
@ -1,44 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/urlencoded.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as url encoded text
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var UrlEncodedViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
UrlEncodedViewer.prototype.render = function() {
|
||||
// Get the value as a string
|
||||
if(this.field !== "text" && this.tiddler) {
|
||||
this.value = this.tiddler.getFieldString(this.field);
|
||||
}
|
||||
var value = "";
|
||||
if(this.value !== undefined && this.value !== null) {
|
||||
value = this.value;
|
||||
}
|
||||
// Set the element details
|
||||
this.viewWidget.tag = "span";
|
||||
this.viewWidget.attributes = {
|
||||
"class": "tw-view-urlencoded"
|
||||
};
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[{
|
||||
type: "text",
|
||||
text: encodeURIComponent(value)
|
||||
}]);
|
||||
};
|
||||
|
||||
exports.urlencoded = UrlEncodedViewer;
|
||||
|
||||
})();
|
@ -1,43 +0,0 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/view/viewers/wikified.js
|
||||
type: application/javascript
|
||||
module-type: fieldviewer
|
||||
|
||||
A viewer for viewing tiddler fields as wikified text
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var WikifiedViewer = function(viewWidget,tiddler,field,value) {
|
||||
this.viewWidget = viewWidget;
|
||||
this.tiddler = tiddler;
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
WikifiedViewer.prototype.render = function() {
|
||||
// Set the element details
|
||||
this.viewWidget.tag = this.viewWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
|
||||
this.viewWidget.attributes = {};
|
||||
var node = {
|
||||
type: "element",
|
||||
tag: "$transclude",
|
||||
attributes: {
|
||||
"class": "tw-view-wikified",
|
||||
field: {type: "string", value: this.field}
|
||||
},
|
||||
isBlock: this.viewWidget.renderer.parseTreeNode.isBlock
|
||||
};
|
||||
if(this.tiddler && this.tiddler.fields.title) {
|
||||
node.attributes.target = {type: "string", value: this.tiddler.fields.title}
|
||||
}
|
||||
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer,[node]);
|
||||
};
|
||||
|
||||
exports.wikified = WikifiedViewer;
|
||||
|
||||
})();
|
224
core/modules/wiki.js
Normal file → Executable file
224
core/modules/wiki.js
Normal file → Executable file
@ -24,6 +24,8 @@ last dispatched. Each entry is a hashmap containing two fields:
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/new_widgets/widget.js");
|
||||
|
||||
var USER_NAME_TITLE = "$:/status/UserName";
|
||||
|
||||
/*
|
||||
@ -168,6 +170,18 @@ exports.tiddlerExists = function(title) {
|
||||
return !!this.tiddlers[title];
|
||||
};
|
||||
|
||||
/*
|
||||
Generate an unused title from the specified base
|
||||
*/
|
||||
exports.generateNewTitle = function(baseTitle) {
|
||||
var c = 0;
|
||||
do {
|
||||
var title = baseTitle + (c ? " " + (c + 1) : "");
|
||||
c++;
|
||||
} while(this.tiddlerExists(title));
|
||||
return title;
|
||||
};
|
||||
|
||||
exports.isSystemTiddler = function(title) {
|
||||
return title.indexOf("$:/") === 0;
|
||||
};
|
||||
@ -315,13 +329,13 @@ exports.getTiddlerLinks = function(title) {
|
||||
// We'll cache the links so they only get computed if the tiddler changes
|
||||
return this.getCacheForTiddler(title,"links",function() {
|
||||
// Parse the tiddler
|
||||
var parser = self.parseTiddler(title);
|
||||
var parser = self.new_parseTiddler(title);
|
||||
// Count up the links
|
||||
var links = [],
|
||||
checkParseTree = function(parseTree) {
|
||||
for(var t=0; t<parseTree.length; t++) {
|
||||
var parseTreeNode = parseTree[t];
|
||||
if(parseTreeNode.type === "element" && parseTreeNode.tag === "$link" && parseTreeNode.attributes.to.type === "string") {
|
||||
if(parseTreeNode.type === "link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
|
||||
var value = parseTreeNode.attributes.to.value;
|
||||
if(links.indexOf(value) === -1) {
|
||||
links.push(value);
|
||||
@ -545,6 +559,10 @@ exports.getTiddlerList = function(title) {
|
||||
|
||||
// Return the named cache object for a tiddler. If the cache doesn't exist then the initializer function is invoked to create it
|
||||
exports.getCacheForTiddler = function(title,cacheName,initializer) {
|
||||
|
||||
// Temporarily disable caching so that tweakParseTreeNode() works
|
||||
return initializer();
|
||||
|
||||
this.caches = this.caches || {};
|
||||
var caches = this.caches[title];
|
||||
if(caches && caches[cacheName]) {
|
||||
@ -588,7 +606,7 @@ Parse a block of text of a specified MIME type
|
||||
Options include:
|
||||
parseAsInline: if true, the text of the tiddler will be parsed as an inline run
|
||||
*/
|
||||
exports.parseText = function(type,text,options) {
|
||||
exports.old_parseText = function(type,text,options) {
|
||||
options = options || {};
|
||||
// Select a parser
|
||||
var Parser = $tw.Wiki.parsers[type];
|
||||
@ -611,28 +629,139 @@ exports.parseText = function(type,text,options) {
|
||||
/*
|
||||
Parse a tiddler according to its MIME type
|
||||
*/
|
||||
exports.parseTiddler = function(title,options) {
|
||||
exports.old_parseTiddler = function(title,options) {
|
||||
options = options || {};
|
||||
var cacheType = options.parseAsInline ? "newInlineParseTree" : "newBlockParseTree",
|
||||
tiddler = this.getTiddler(title),
|
||||
self = this;
|
||||
return tiddler ? this.getCacheForTiddler(title,cacheType,function() {
|
||||
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
|
||||
return self.old_parseText(tiddler.fields.type,tiddler.fields.text,options);
|
||||
}) : null;
|
||||
};
|
||||
|
||||
// We need to tweak parse trees generated by the existing parser because of the change from {type:"element",tag:"$tiddler",...} to {type:"tiddler",...}
|
||||
var tweakParseTreeNode = function(node) {
|
||||
if(node.type === "element" && node.tag.charAt(0) === "$") {
|
||||
node.type = node.tag.substr(1);
|
||||
delete node.tag;
|
||||
}
|
||||
tweakParseTreeNodes(node.children);
|
||||
};
|
||||
|
||||
var tweakParseTreeNodes = function(nodeList) {
|
||||
$tw.utils.each(nodeList,tweakParseTreeNode);
|
||||
};
|
||||
|
||||
var tweakMacroDefinition = function(nodeList) {
|
||||
if(nodeList && nodeList[0] && nodeList[0].type === "macrodef") {
|
||||
nodeList[0].type = "setvariable";
|
||||
nodeList[0].attributes = {
|
||||
name: {type: "string", value: nodeList[0].name},
|
||||
value: {type: "string", value: nodeList[0].text}
|
||||
};
|
||||
nodeList[0].children = nodeList.slice(1);
|
||||
nodeList.splice(1,nodeList.length-1);
|
||||
tweakMacroDefinition(nodeList[0].children);
|
||||
}
|
||||
};
|
||||
|
||||
var tweakParser = function(parser) {
|
||||
// Move any macro definitions to contain the body tree
|
||||
tweakMacroDefinition(parser.tree);
|
||||
// Tweak widgets
|
||||
tweakParseTreeNodes(parser.tree);
|
||||
};
|
||||
|
||||
exports.new_parseText = function(type,text,options) {
|
||||
var parser = this.old_parseText(type,text,options);
|
||||
if(parser) {
|
||||
tweakParser(parser)
|
||||
};
|
||||
return parser;
|
||||
};
|
||||
|
||||
exports.new_parseTiddler = function(title,options) {
|
||||
var parser = this.old_parseTiddler(title,options);
|
||||
if(parser) {
|
||||
tweakParser(parser)
|
||||
};
|
||||
return parser;
|
||||
};
|
||||
|
||||
exports.new_parseTextReference = function(title,field,index,options) {
|
||||
if(field === "text" || (!field && !index)) {
|
||||
return this.new_parseTiddler(title,options);
|
||||
} else {
|
||||
var tiddler,text;
|
||||
if(field) {
|
||||
tiddler = this.getTiddler(title);
|
||||
text = tiddler ? tiddler.fields[field] : "";
|
||||
if(text === undefined) {
|
||||
text = "";
|
||||
}
|
||||
return this.new_parseText("text/vnd.tiddlywiki",text,options);
|
||||
} else if(index) {
|
||||
text = this.extractTiddlerDataItem(title,index,"");
|
||||
return this.new_parseText("text/vnd.tiddlywiki",text,options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Make a widget tree for a parse tree
|
||||
parser: parser object
|
||||
options: see below
|
||||
Options include:
|
||||
document: optional document to use
|
||||
variables: hashmap of variables to set
|
||||
parentWidget: optional parent widget for the root node
|
||||
*/
|
||||
exports.makeWidget = function(parser,options) {
|
||||
options = options || {};
|
||||
var widgetNode = {
|
||||
type: "widget",
|
||||
children: []
|
||||
},
|
||||
currWidgetNode = widgetNode;
|
||||
// Create setvariable widgets for each variable
|
||||
$tw.utils.each(options.variables,function(value,name) {
|
||||
var setVariableWidget = {
|
||||
type: "setvariable",
|
||||
attributes: {
|
||||
name: {type: "string", value: name},
|
||||
value: {type: "string", value: value}
|
||||
},
|
||||
children: []
|
||||
};
|
||||
currWidgetNode.children = [setVariableWidget];
|
||||
currWidgetNode = setVariableWidget;
|
||||
});
|
||||
// Add in the supplied parse tree nodes
|
||||
currWidgetNode.children = parser ? parser.tree : [];
|
||||
// Create the widget
|
||||
return new widget.widget(widgetNode,{
|
||||
wiki: this,
|
||||
document: options.document || $tw.document,
|
||||
parentWidget: options.parentWidget
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Parse text in a specified format and render it into another format
|
||||
outputType: content type for the output
|
||||
textType: content type of the input text
|
||||
text: input text
|
||||
options: see below
|
||||
Options include:
|
||||
variables: hashmap of variables to set
|
||||
parentWidget: optional parent widget for the root node
|
||||
*/
|
||||
exports.renderText = function(outputType,textType,text,context) {
|
||||
var parser = this.parseText(textType,text),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: this, context: context, document: $tw.document});
|
||||
renderTree.execute();
|
||||
exports.new_renderText = function(outputType,textType,text,options) {
|
||||
options = options || {};
|
||||
var parser = $tw.wiki.new_parseText(textType,text),
|
||||
widgetNode = this.makeWidget(parser,options);
|
||||
var container = $tw.document.createElement("div");
|
||||
renderTree.renderInDom(container)
|
||||
widgetNode.render(container,null);
|
||||
return outputType === "text/html" ? container.innerHTML : container.textContent;
|
||||
};
|
||||
|
||||
@ -640,13 +769,17 @@ exports.renderText = function(outputType,textType,text,context) {
|
||||
Parse text from a tiddler and render it into another format
|
||||
outputType: content type for the output
|
||||
title: title of the tiddler to be rendered
|
||||
options: see below
|
||||
Options include:
|
||||
variables: hashmap of variables to set
|
||||
parentWidget: optional parent widget for the root node
|
||||
*/
|
||||
exports.renderTiddler = function(outputType,title,context) {
|
||||
var parser = this.parseTiddler(title),
|
||||
renderTree = new $tw.WikiRenderTree(parser,{wiki: this, context: context, document: $tw.document});
|
||||
renderTree.execute();
|
||||
exports.new_renderTiddler = function(outputType,title,options) {
|
||||
options = options || {};
|
||||
var parser = this.new_parseTiddler(title),
|
||||
widgetNode = this.makeWidget(parser,options);
|
||||
var container = $tw.document.createElement("div");
|
||||
renderTree.renderInDom(container)
|
||||
widgetNode.render(container,null);
|
||||
return outputType === "text/html" ? container.innerHTML : container.textContent;
|
||||
};
|
||||
|
||||
@ -699,7 +832,7 @@ exports.saveWiki = function(options) {
|
||||
options = options || {};
|
||||
var template = options.template || "$:/core/templates/tiddlywiki5.template.html",
|
||||
downloadType = options.downloadType || "text/plain";
|
||||
var text = this.renderTiddler(downloadType,template);
|
||||
var text = this.new_renderTiddler(downloadType,template);
|
||||
this.callSaver("save",text,function(err) {
|
||||
$tw.notifier.display("$:/messages/Saved");
|
||||
});
|
||||
@ -812,4 +945,63 @@ exports.getTiddlerText = function(title,defaultText) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Read an array of browser File objects, invoking callback(tiddlerFields) for each loaded file
|
||||
*/
|
||||
exports.readFiles = function(files,callback) {
|
||||
for(var f=0; f<files.length; f++) {
|
||||
this.readFile(files[f],callback);
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Read a browser File object, invoking callback(tiddlerFields) with the tiddler fields object
|
||||
*/
|
||||
exports.readFile = function(file,callback) {
|
||||
// Get the type, falling back to the filename extension
|
||||
var self = this,
|
||||
type = file.type;
|
||||
if(type === "" || !type) {
|
||||
var dotPos = file.name.lastIndexOf(".");
|
||||
if(dotPos !== -1) {
|
||||
var fileExtensionInfo = $tw.config.fileExtensionInfo[file.name.substr(dotPos)];
|
||||
if(fileExtensionInfo) {
|
||||
type = fileExtensionInfo.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Figure out if we're reading a binary file
|
||||
var contentTypeInfo = $tw.config.contentTypeInfo[type],
|
||||
isBinary = contentTypeInfo ? contentTypeInfo.encoding === "base64" : false;
|
||||
// Create the FileReader
|
||||
var reader = new FileReader();
|
||||
// Onload
|
||||
reader.onload = function(event) {
|
||||
// Deserialise the file contents
|
||||
var tiddlerFields = {title: file.name || "Untitled", type: type};
|
||||
// Are we binary?
|
||||
if(isBinary) {
|
||||
// The base64 section starts after the first comma in the data URI
|
||||
var commaPos = event.target.result.indexOf(",");
|
||||
if(commaPos !== -1) {
|
||||
tiddlerFields.text = event.target.result.substr(commaPos+1);
|
||||
callback(tiddlerFields);
|
||||
}
|
||||
} else {
|
||||
var tiddlers = self.deserializeTiddlers(type,event.target.result,tiddlerFields);
|
||||
if(tiddlers) {
|
||||
$tw.utils.each(tiddlers,function(tiddlerFields) {
|
||||
callback(tiddlerFields);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// Kick off the read
|
||||
if(isBinary) {
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -18,7 +18,7 @@ type: text/vnd.tiddlywiki-html
|
||||
{{{ [tag[$:/tags/stylesheet]] [is[shadow]tag[$:/tags/stylesheet]] ||$:/core/templates/wikified-tiddler}}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body class="tw-body">
|
||||
{{$:/StaticBanner||$:/core/templates/html-tiddler}}
|
||||
{{$:/core/ui/PageTemplate||$:/core/templates/html-tiddler}}
|
||||
</body>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user