Merge branch 'master' into single-tiddler-mode
This commit is contained in:
commit
9d9b00bdb5
11
.travis.yml
11
.travis.yml
|
@ -1,5 +1,6 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
- "node"
|
node_js:
|
||||||
|
- "12.4.0"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- name: test
|
- name: test
|
||||||
|
@ -20,8 +21,8 @@ jobs:
|
||||||
- export TW5_BUILD_DETAILS="Prerelease built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
- export TW5_BUILD_DETAILS="Prerelease built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
||||||
- export TW5_BUILD_MAIN_EDITION='./editions/prerelease'
|
- export TW5_BUILD_MAIN_EDITION='./editions/prerelease'
|
||||||
- export TW5_BUILD_OUTPUT='./output/prerelease'
|
- export TW5_BUILD_OUTPUT='./output/prerelease'
|
||||||
- ./bin/build-site.sh
|
- ./bin/build-site.sh || travis_terminate 1
|
||||||
- ./bin/travis-push.sh
|
- ./bin/travis-push.sh || travis_terminate 1
|
||||||
- stage: build-tiddlywiki-com
|
- stage: build-tiddlywiki-com
|
||||||
script:
|
script:
|
||||||
- ./bin/travis-pre-build.sh
|
- ./bin/travis-pre-build.sh
|
||||||
|
@ -30,5 +31,5 @@ jobs:
|
||||||
- export TW5_BUILD_DETAILS="Built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
- export TW5_BUILD_DETAILS="Built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
|
||||||
- export TW5_BUILD_MAIN_EDITION='./editions/tw5.com'
|
- export TW5_BUILD_MAIN_EDITION='./editions/tw5.com'
|
||||||
- export TW5_BUILD_OUTPUT='./output'
|
- export TW5_BUILD_OUTPUT='./output'
|
||||||
- ./bin/build-site.sh
|
- ./bin/build-site.sh || travis_terminate 1
|
||||||
- ./bin/travis-push.sh
|
- ./bin/travis-push.sh || travis_terminate 1
|
||||||
|
|
|
@ -136,6 +136,15 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||||
--build index favicon static \
|
--build index favicon static \
|
||||||
|| exit 1
|
|| exit 1
|
||||||
|
|
||||||
|
# /share.html Custom edition for sharing via the URL
|
||||||
|
node $TW5_BUILD_TIDDLYWIKI \
|
||||||
|
./editions/share \
|
||||||
|
--verbose \
|
||||||
|
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||||
|
--output $TW5_BUILD_OUTPUT \
|
||||||
|
--build share \
|
||||||
|
|| exit 1
|
||||||
|
|
||||||
# /upgrade.html Custom edition for performing upgrades
|
# /upgrade.html Custom edition for performing upgrades
|
||||||
node $TW5_BUILD_TIDDLYWIKI \
|
node $TW5_BUILD_TIDDLYWIKI \
|
||||||
./editions/upgrade \
|
./editions/upgrade \
|
||||||
|
|
|
@ -2080,9 +2080,9 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||||
for(var title in $tw.boot.files) {
|
for(var title in $tw.boot.files) {
|
||||||
relativePath = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath);
|
relativePath = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath);
|
||||||
output[title] =
|
output[title] =
|
||||||
path.sep === path.posix.sep ?
|
path.sep === "/" ?
|
||||||
relativePath :
|
relativePath :
|
||||||
relativePath.split(path.sep).join(path.posix.sep);
|
relativePath.split(path.sep).join("/");
|
||||||
}
|
}
|
||||||
$tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)});
|
$tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)});
|
||||||
}
|
}
|
||||||
|
@ -2245,6 +2245,7 @@ $tw.boot.startup = function(options) {
|
||||||
$tw.utils.registerFileType("application/json","utf8",".json");
|
$tw.utils.registerFileType("application/json","utf8",".json");
|
||||||
$tw.utils.registerFileType("application/pdf","base64",".pdf",{flags:["image"]});
|
$tw.utils.registerFileType("application/pdf","base64",".pdf",{flags:["image"]});
|
||||||
$tw.utils.registerFileType("application/zip","base64",".zip");
|
$tw.utils.registerFileType("application/zip","base64",".zip");
|
||||||
|
$tw.utils.registerFileType("application/x-zip-compressed","base64",".zip");
|
||||||
$tw.utils.registerFileType("image/jpeg","base64",[".jpg",".jpeg"],{flags:["image"]});
|
$tw.utils.registerFileType("image/jpeg","base64",[".jpg",".jpeg"],{flags:["image"]});
|
||||||
$tw.utils.registerFileType("image/png","base64",".png",{flags:["image"]});
|
$tw.utils.registerFileType("image/png","base64",".png",{flags:["image"]});
|
||||||
$tw.utils.registerFileType("image/gif","base64",".gif",{flags:["image"]});
|
$tw.utils.registerFileType("image/gif","base64",".gif",{flags:["image"]});
|
||||||
|
|
|
@ -107,6 +107,8 @@ Saving/GitService/GitHub/Caption: ~GitHub Saver
|
||||||
Saving/GitService/GitHub/Password: Password, OAUTH token, or personal access token (see [[GitHub help page|https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line]] for details)
|
Saving/GitService/GitHub/Password: Password, OAUTH token, or personal access token (see [[GitHub help page|https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line]] for details)
|
||||||
Saving/GitService/GitLab/Caption: ~GitLab Saver
|
Saving/GitService/GitLab/Caption: ~GitLab Saver
|
||||||
Saving/GitService/GitLab/Password: Personal access token for API (see [[GitLab help page|https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html]] for details)
|
Saving/GitService/GitLab/Password: Personal access token for API (see [[GitLab help page|https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html]] for details)
|
||||||
|
Saving/GitService/Gitea/Caption: Gitea Saver
|
||||||
|
Saving/GitService/Gitea/Password: Personal access token for API (via Gitea’s web interface: `Settings | Applications | Generate New Token`)
|
||||||
Saving/TiddlySpot/Advanced/Heading: Advanced Settings
|
Saving/TiddlySpot/Advanced/Heading: Advanced Settings
|
||||||
Saving/TiddlySpot/BackupDir: Backup Directory
|
Saving/TiddlySpot/BackupDir: Backup Directory
|
||||||
Saving/TiddlySpot/Backups: Backups
|
Saving/TiddlySpot/Backups: Backups
|
||||||
|
|
|
@ -29,6 +29,8 @@ external-link-foreground-hover: External link foreground hover
|
||||||
external-link-foreground-visited: External link foreground visited
|
external-link-foreground-visited: External link foreground visited
|
||||||
external-link-foreground: External link foreground
|
external-link-foreground: External link foreground
|
||||||
foreground: General foreground
|
foreground: General foreground
|
||||||
|
menubar-background: Menu bar background
|
||||||
|
menubar-foreground: Menu bar foreground
|
||||||
message-background: Message box background
|
message-background: Message box background
|
||||||
message-border: Message box border
|
message-border: Message box border
|
||||||
message-foreground: Message box foreground
|
message-foreground: Message box foreground
|
||||||
|
|
|
@ -35,3 +35,4 @@ title: The unique name of a tiddler
|
||||||
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
|
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
|
||||||
type: The content type of a tiddler
|
type: The content type of a tiddler
|
||||||
version: Version information for a plugin
|
version: Version information for a plugin
|
||||||
|
_is_skinny: If present, indicates that the tiddler text field must be loaded from the server
|
||||||
|
|
|
@ -28,6 +28,7 @@ Error/Filter: Filter error
|
||||||
Error/FilterSyntax: Syntax error in filter expression
|
Error/FilterSyntax: Syntax error in filter expression
|
||||||
Error/IsFilterOperator: Filter Error: Unknown operand for the 'is' filter operator
|
Error/IsFilterOperator: Filter Error: Unknown operand for the 'is' filter operator
|
||||||
Error/LoadingPluginLibrary: Error loading plugin library
|
Error/LoadingPluginLibrary: Error loading plugin library
|
||||||
|
Error/NetworkErrorAlert: `<h2>''Network Error''</h2>It looks like the connection to the server has been lost. This may indicate a problem with your network connection. Please attempt to restore network connectivity before continuing.<br><br>''Any unsaved changes will be automatically synchronised when connectivity is restored''.`
|
||||||
Error/RecursiveTransclusion: Recursive transclusion error in transclude widget
|
Error/RecursiveTransclusion: Recursive transclusion error in transclude widget
|
||||||
Error/RetrievingSkinny: Error retrieving skinny tiddler list
|
Error/RetrievingSkinny: Error retrieving skinny tiddler list
|
||||||
Error/SavingToTWEdit: Error saving to TWEdit
|
Error/SavingToTWEdit: Error saving to TWEdit
|
||||||
|
@ -66,6 +67,7 @@ SystemTiddlers/Include/Prompt: Include system tiddlers
|
||||||
TagManager/Colour/Heading: Colour
|
TagManager/Colour/Heading: Colour
|
||||||
TagManager/Count/Heading: Count
|
TagManager/Count/Heading: Count
|
||||||
TagManager/Icon/Heading: Icon
|
TagManager/Icon/Heading: Icon
|
||||||
|
TagManager/Icons/None: None
|
||||||
TagManager/Info/Heading: Info
|
TagManager/Info/Heading: Info
|
||||||
TagManager/Tag/Heading: Tag
|
TagManager/Tag/Heading: Tag
|
||||||
Tiddler/DateFormat: DDth MMM YYYY at hh12:0mmam
|
Tiddler/DateFormat: DDth MMM YYYY at hh12:0mmam
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*\
|
||||||
|
title: $:/core/modules/indexers/backlinks-indexer.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: indexer
|
||||||
|
|
||||||
|
Indexes the tiddlers' backlinks
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global modules: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
|
function BacklinksIndexer(wiki) {
|
||||||
|
this.wiki = wiki;
|
||||||
|
}
|
||||||
|
|
||||||
|
BacklinksIndexer.prototype.init = function() {
|
||||||
|
this.index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BacklinksIndexer.prototype.rebuild = function() {
|
||||||
|
this.index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BacklinksIndexer.prototype._getLinks = function(tiddler) {
|
||||||
|
var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {});
|
||||||
|
if(parser) {
|
||||||
|
return this.wiki.extractLinks(parser.tree);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
BacklinksIndexer.prototype.update = function(updateDescriptor) {
|
||||||
|
if(!this.index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var newLinks = [],
|
||||||
|
oldLinks = [],
|
||||||
|
self = this;
|
||||||
|
if(updateDescriptor.old.exists) {
|
||||||
|
oldLinks = this._getLinks(updateDescriptor.old.tiddler);
|
||||||
|
}
|
||||||
|
if(updateDescriptor.new.exists) {
|
||||||
|
newLinks = this._getLinks(updateDescriptor.new.tiddler);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tw.utils.each(oldLinks,function(link) {
|
||||||
|
if(self.index[link]) {
|
||||||
|
delete self.index[link][updateDescriptor.old.tiddler.fields.title];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$tw.utils.each(newLinks,function(link) {
|
||||||
|
if(!self.index[link]) {
|
||||||
|
self.index[link] = Object.create(null);
|
||||||
|
}
|
||||||
|
self.index[link][updateDescriptor.new.tiddler.fields.title] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BacklinksIndexer.prototype.lookup = function(title) {
|
||||||
|
if(!this.index) {
|
||||||
|
this.index = Object.create(null);
|
||||||
|
var self = this;
|
||||||
|
this.wiki.forEachTiddler(function(title,tiddler) {
|
||||||
|
var links = self._getLinks(tiddler);
|
||||||
|
$tw.utils.each(links, function(link) {
|
||||||
|
if(!self.index[link]) {
|
||||||
|
self.index[link] = Object.create(null);
|
||||||
|
}
|
||||||
|
self.index[link][title] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(this.index[title]) {
|
||||||
|
return Object.keys(this.index[title]);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BacklinksIndexer = BacklinksIndexer;
|
||||||
|
|
||||||
|
})();
|
|
@ -19,14 +19,15 @@ Information about this macro
|
||||||
exports.name = "jsontiddlers";
|
exports.name = "jsontiddlers";
|
||||||
|
|
||||||
exports.params = [
|
exports.params = [
|
||||||
{name: "filter"}
|
{name: "filter"},
|
||||||
|
{name: "spaces"}
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Run the macro
|
Run the macro
|
||||||
*/
|
*/
|
||||||
exports.run = function(filter) {
|
exports.run = function(filter,spaces) {
|
||||||
return this.wiki.getTiddlersAsJson(filter);
|
return this.wiki.getTiddlersAsJson(filter,spaces);
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -22,14 +22,15 @@ exports.name = "makedatauri";
|
||||||
|
|
||||||
exports.params = [
|
exports.params = [
|
||||||
{name: "text"},
|
{name: "text"},
|
||||||
{name: "type"}
|
{name: "type"},
|
||||||
|
{name: "_canonical_uri"}
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Run the macro
|
Run the macro
|
||||||
*/
|
*/
|
||||||
exports.run = function(text,type) {
|
exports.run = function(text,type,_canonical_uri) {
|
||||||
return $tw.utils.makeDataUri(text,type);
|
return $tw.utils.makeDataUri(text,type,_canonical_uri);
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -3,7 +3,7 @@ title: $:/core/modules/parsers/binaryparser.js
|
||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: parser
|
module-type: parser
|
||||||
|
|
||||||
The video parser parses a video tiddler into an embeddable HTML element
|
The binary parser parses a binary tiddler into a warning message and download link
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function(){
|
||||||
|
@ -13,14 +13,57 @@ The video parser parses a video tiddler into an embeddable HTML element
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
||||||
|
var EXPORT_BUTTON_IMAGE = "$:/core/images/export-button";
|
||||||
|
|
||||||
var BinaryParser = function(type,text,options) {
|
var BinaryParser = function(type,text,options) {
|
||||||
this.tree = [{
|
// Transclude the binary data tiddler warning message
|
||||||
type: "transclude",
|
var warn = {
|
||||||
|
type: "element",
|
||||||
|
tag: "p",
|
||||||
|
children: [{
|
||||||
|
type: "transclude",
|
||||||
|
attributes: {
|
||||||
|
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
// Create download link based on binary tiddler title
|
||||||
|
var link = {
|
||||||
|
type: "element",
|
||||||
|
tag: "a",
|
||||||
attributes: {
|
attributes: {
|
||||||
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
title: {type: "indirect", textReference: "!!title"},
|
||||||
}
|
download: {type: "indirect", textReference: "!!title"}
|
||||||
}];
|
},
|
||||||
|
children: [{
|
||||||
|
type: "transclude",
|
||||||
|
attributes: {
|
||||||
|
tiddler: {type: "string", value: EXPORT_BUTTON_IMAGE}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
// Set the link href to external or internal data URI
|
||||||
|
if(options._canonical_uri) {
|
||||||
|
link.attributes.href = {
|
||||||
|
type: "string",
|
||||||
|
value: options._canonical_uri
|
||||||
|
};
|
||||||
|
} else if(text) {
|
||||||
|
link.attributes.href = {
|
||||||
|
type: "string",
|
||||||
|
value: "data:" + type + ";base64," + text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Combine warning message and download link in a div
|
||||||
|
var element = {
|
||||||
|
type: "element",
|
||||||
|
tag: "div",
|
||||||
|
attributes: {
|
||||||
|
class: {type: "string", value: "tc-binary-warning"}
|
||||||
|
},
|
||||||
|
children: [warn, link]
|
||||||
|
}
|
||||||
|
this.tree = [element];
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["application/octet-stream"] = BinaryParser;
|
exports["application/octet-stream"] = BinaryParser;
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*\
|
||||||
|
title: $:/core/modules/savers/gitea.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: saver
|
||||||
|
|
||||||
|
Saves wiki by pushing a commit to the gitea
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Select the appropriate saver module and set it up
|
||||||
|
*/
|
||||||
|
var GiteaSaver = function(wiki) {
|
||||||
|
this.wiki = wiki;
|
||||||
|
};
|
||||||
|
|
||||||
|
GiteaSaver.prototype.save = function(text,method,callback) {
|
||||||
|
var self = this,
|
||||||
|
username = this.wiki.getTiddlerText("$:/Gitea/Username"),
|
||||||
|
password = $tw.utils.getPassword("Gitea"),
|
||||||
|
repo = this.wiki.getTiddlerText("$:/Gitea/Repo"),
|
||||||
|
path = this.wiki.getTiddlerText("$:/Gitea/Path",""),
|
||||||
|
filename = this.wiki.getTiddlerText("$:/Gitea/Filename"),
|
||||||
|
branch = this.wiki.getTiddlerText("$:/Gitea/Branch") || "master",
|
||||||
|
endpoint = this.wiki.getTiddlerText("$:/Gitea/ServerURL") || "https://gitea",
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
|
"Authorization": "Basic " + window.btoa(username + ":" + password)
|
||||||
|
};
|
||||||
|
// Bail if we don't have everything we need
|
||||||
|
if(!username || !password || !repo || !path || !filename) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Make sure the path start and ends with a slash
|
||||||
|
if(path.substring(0,1) !== "/") {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
if(path.substring(path.length - 1) !== "/") {
|
||||||
|
path = path + "/";
|
||||||
|
}
|
||||||
|
// Compose the base URI
|
||||||
|
var uri = endpoint + "/repos/" + repo + "/contents" + path;
|
||||||
|
// Perform a get request to get the details (inc shas) of files in the same path as our file
|
||||||
|
$tw.utils.httpRequest({
|
||||||
|
url: uri,
|
||||||
|
type: "GET",
|
||||||
|
headers: headers,
|
||||||
|
data: {
|
||||||
|
ref: branch
|
||||||
|
},
|
||||||
|
callback: function(err,getResponseDataJson,xhr) {
|
||||||
|
var getResponseData,sha = "";
|
||||||
|
if(err && xhr.status !== 404) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
var use_put = true;
|
||||||
|
if(xhr.status !== 404) {
|
||||||
|
getResponseData = JSON.parse(getResponseDataJson);
|
||||||
|
$tw.utils.each(getResponseData,function(details) {
|
||||||
|
if(details.name === filename) {
|
||||||
|
sha = details.sha;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(sha === ""){
|
||||||
|
use_put = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var data = {
|
||||||
|
message: $tw.language.getRawString("ControlPanel/Saving/GitService/CommitMessage"),
|
||||||
|
content: $tw.utils.base64Encode(text),
|
||||||
|
sha: sha
|
||||||
|
};
|
||||||
|
$tw.utils.httpRequest({
|
||||||
|
url: endpoint + "/repos/" + repo + "/branches/" + branch,
|
||||||
|
type: "GET",
|
||||||
|
headers: headers,
|
||||||
|
callback: function(err,getResponseDataJson,xhr) {
|
||||||
|
if(xhr.status === 404) {
|
||||||
|
callback("Please ensure the branch in the Gitea repo exists");
|
||||||
|
}else{
|
||||||
|
data["branch"] = branch;
|
||||||
|
self.upload(uri + filename, use_put?"PUT":"POST", headers, data, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
GiteaSaver.prototype.upload = function(uri,method,headers,data,callback) {
|
||||||
|
$tw.utils.httpRequest({
|
||||||
|
url: uri,
|
||||||
|
type: method,
|
||||||
|
headers: headers,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
callback: function(err,putResponseDataJson,xhr) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
var putResponseData = JSON.parse(putResponseDataJson);
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Information about this saver
|
||||||
|
*/
|
||||||
|
GiteaSaver.prototype.info = {
|
||||||
|
name: "Gitea",
|
||||||
|
priority: 2000,
|
||||||
|
capabilities: ["save", "autosave"]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Static method that returns true if this saver is capable of working
|
||||||
|
*/
|
||||||
|
exports.canSave = function(wiki) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create an instance of this saver
|
||||||
|
*/
|
||||||
|
exports.create = function(wiki) {
|
||||||
|
return new GiteaSaver(wiki);
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
|
@ -24,7 +24,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
|
||||||
username = this.wiki.getTiddlerText("$:/GitHub/Username"),
|
username = this.wiki.getTiddlerText("$:/GitHub/Username"),
|
||||||
password = $tw.utils.getPassword("github"),
|
password = $tw.utils.getPassword("github"),
|
||||||
repo = this.wiki.getTiddlerText("$:/GitHub/Repo"),
|
repo = this.wiki.getTiddlerText("$:/GitHub/Repo"),
|
||||||
path = this.wiki.getTiddlerText("$:/GitHub/Path"),
|
path = this.wiki.getTiddlerText("$:/GitHub/Path",""),
|
||||||
filename = this.wiki.getTiddlerText("$:/GitHub/Filename"),
|
filename = this.wiki.getTiddlerText("$:/GitHub/Filename"),
|
||||||
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "master",
|
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "master",
|
||||||
endpoint = this.wiki.getTiddlerText("$:/GitHub/ServerURL") || "https://api.github.com",
|
endpoint = this.wiki.getTiddlerText("$:/GitHub/ServerURL") || "https://api.github.com",
|
||||||
|
|
|
@ -25,7 +25,7 @@ GitLabSaver.prototype.save = function(text,method,callback) {
|
||||||
username = this.wiki.getTiddlerText("$:/GitLab/Username"),
|
username = this.wiki.getTiddlerText("$:/GitLab/Username"),
|
||||||
password = $tw.utils.getPassword("gitlab"),
|
password = $tw.utils.getPassword("gitlab"),
|
||||||
repo = this.wiki.getTiddlerText("$:/GitLab/Repo"),
|
repo = this.wiki.getTiddlerText("$:/GitLab/Repo"),
|
||||||
path = this.wiki.getTiddlerText("$:/GitLab/Path"),
|
path = this.wiki.getTiddlerText("$:/GitLab/Path",""),
|
||||||
filename = this.wiki.getTiddlerText("$:/GitLab/Filename"),
|
filename = this.wiki.getTiddlerText("$:/GitLab/Filename"),
|
||||||
branch = this.wiki.getTiddlerText("$:/GitLab/Branch") || "master",
|
branch = this.wiki.getTiddlerText("$:/GitLab/Branch") || "master",
|
||||||
endpoint = this.wiki.getTiddlerText("$:/GitLab/ServerURL") || "https://gitlab.com/api/v4",
|
endpoint = this.wiki.getTiddlerText("$:/GitLab/ServerURL") || "https://gitlab.com/api/v4",
|
||||||
|
|
|
@ -19,22 +19,16 @@ exports.path = /^\/files\/(.+)$/;
|
||||||
exports.handler = function(request,response,state) {
|
exports.handler = function(request,response,state) {
|
||||||
var path = require("path"),
|
var path = require("path"),
|
||||||
fs = require("fs"),
|
fs = require("fs"),
|
||||||
util = require("util");
|
util = require("util"),
|
||||||
var filename = path.resolve($tw.boot.wikiPath,"files",decodeURIComponent(state.params[0])),
|
suppliedFilename = decodeURIComponent(state.params[0]),
|
||||||
|
filename = path.resolve($tw.boot.wikiPath,"files",suppliedFilename),
|
||||||
extension = path.extname(filename);
|
extension = path.extname(filename);
|
||||||
fs.readFile(filename,function(err,content) {
|
fs.readFile(filename,function(err,content) {
|
||||||
var status,content,type = "text/plain";
|
var status,content,type = "text/plain";
|
||||||
if(err) {
|
if(err) {
|
||||||
if(err.code === "ENOENT") {
|
console.log("Error accessing file " + filename + ": " + err.toString());
|
||||||
status = 404;
|
status = 404;
|
||||||
content = "File '" + filename + "' not found";
|
content = "File '" + suppliedFilename + "' not found";
|
||||||
} else if(err.code === "EACCES") {
|
|
||||||
status = 403;
|
|
||||||
content = "You do not have permission to access the file '" + filename + "'";
|
|
||||||
} else {
|
|
||||||
status = 500;
|
|
||||||
content = err.toString();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
status = 200;
|
status = 200;
|
||||||
content = content;
|
content = content;
|
||||||
|
|
|
@ -3,7 +3,7 @@ title: $:/core/modules/server/routes/get-tiddlers-json.js
|
||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: route
|
module-type: route
|
||||||
|
|
||||||
GET /recipes/default/tiddlers/tiddlers.json
|
GET /recipes/default/tiddlers/tiddlers.json?filter=<filter>
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function() {
|
(function() {
|
||||||
|
@ -12,23 +12,34 @@ GET /recipes/default/tiddlers/tiddlers.json
|
||||||
/*global $tw: false */
|
/*global $tw: false */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var DEFAULT_FILTER = "[all[tiddlers]!is[system]sort[title]]";
|
||||||
|
|
||||||
exports.method = "GET";
|
exports.method = "GET";
|
||||||
|
|
||||||
exports.path = /^\/recipes\/default\/tiddlers.json$/;
|
exports.path = /^\/recipes\/default\/tiddlers.json$/;
|
||||||
|
|
||||||
exports.handler = function(request,response,state) {
|
exports.handler = function(request,response,state) {
|
||||||
|
var filter = state.queryParameters.filter || DEFAULT_FILTER;
|
||||||
|
if($tw.wiki.getTiddlerText("$:/config/Server/AllowAllExternalFilters") !== "yes") {
|
||||||
|
if($tw.wiki.getTiddlerText("$:/config/Server/ExternalFilters/" + filter) !== "yes") {
|
||||||
|
console.log("Blocked attempt to GET /recipes/default/tiddlers/tiddlers.json with filter: " + filter);
|
||||||
|
response.writeHead(403);
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var excludeFields = (state.queryParameters.exclude || "text").split(","),
|
||||||
|
titles = state.wiki.filterTiddlers(filter);
|
||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
response.writeHead(200, {"Content-Type": "application/json"});
|
||||||
var tiddlers = [];
|
var tiddlers = [];
|
||||||
state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) {
|
$tw.utils.each(titles,function(title) {
|
||||||
var tiddlerFields = {};
|
var tiddler = state.wiki.getTiddler(title);
|
||||||
$tw.utils.each(tiddler.fields,function(field,name) {
|
if(tiddler) {
|
||||||
if(name !== "text") {
|
var tiddlerFields = tiddler.getFieldStrings({exclude: excludeFields});
|
||||||
tiddlerFields[name] = tiddler.getFieldString(name);
|
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||||
}
|
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||||
});
|
tiddlers.push(tiddlerFields);
|
||||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
}
|
||||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
|
||||||
tiddlers.push(tiddlerFields);
|
|
||||||
});
|
});
|
||||||
var text = JSON.stringify(tiddlers);
|
var text = JSON.stringify(tiddlers);
|
||||||
response.end(text,"utf8");
|
response.end(text,"utf8");
|
||||||
|
|
|
@ -16,7 +16,8 @@ if($tw.node) {
|
||||||
var util = require("util"),
|
var util = require("util"),
|
||||||
fs = require("fs"),
|
fs = require("fs"),
|
||||||
url = require("url"),
|
url = require("url"),
|
||||||
path = require("path");
|
path = require("path"),
|
||||||
|
querystring = require("querystring");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -162,6 +163,7 @@ Server.prototype.requestHandler = function(request,response) {
|
||||||
state.wiki = self.wiki;
|
state.wiki = self.wiki;
|
||||||
state.server = self;
|
state.server = self;
|
||||||
state.urlInfo = url.parse(request.url);
|
state.urlInfo = url.parse(request.url);
|
||||||
|
state.queryParameters = querystring.parse(state.urlInfo.query);
|
||||||
// Get the principals authorized to access this resource
|
// Get the principals authorized to access this resource
|
||||||
var authorizationType = this.methodMappings[request.method] || "readers";
|
var authorizationType = this.methodMappings[request.method] || "readers";
|
||||||
// Check for the CSRF header if this is a write
|
// Check for the CSRF header if this is a write
|
||||||
|
@ -236,6 +238,7 @@ host: optional host address (falls back to value of "host" variable)
|
||||||
prefix: optional prefix (falls back to value of "path-prefix" variable)
|
prefix: optional prefix (falls back to value of "path-prefix" variable)
|
||||||
*/
|
*/
|
||||||
Server.prototype.listen = function(port,host,prefix) {
|
Server.prototype.listen = function(port,host,prefix) {
|
||||||
|
var self = this;
|
||||||
// Handle defaults for port and host
|
// Handle defaults for port and host
|
||||||
port = port || this.get("port");
|
port = port || this.get("port");
|
||||||
host = host || this.get("host");
|
host = host || this.get("host");
|
||||||
|
@ -244,19 +247,24 @@ Server.prototype.listen = function(port,host,prefix) {
|
||||||
if(parseInt(port,10).toString() !== port) {
|
if(parseInt(port,10).toString() !== port) {
|
||||||
port = process.env[port] || 8080;
|
port = process.env[port] || 8080;
|
||||||
}
|
}
|
||||||
$tw.utils.log("Serving on " + this.protocol + "://" + host + ":" + port + prefix,"brown/orange");
|
|
||||||
$tw.utils.log("(press ctrl-C to exit)","red");
|
|
||||||
// Warn if required plugins are missing
|
// Warn if required plugins are missing
|
||||||
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
|
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
|
||||||
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
|
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
|
||||||
}
|
}
|
||||||
// Listen
|
// Create the server
|
||||||
var server;
|
var server;
|
||||||
if(this.listenOptions) {
|
if(this.listenOptions) {
|
||||||
server = this.transport.createServer(this.listenOptions,this.requestHandler.bind(this));
|
server = this.transport.createServer(this.listenOptions,this.requestHandler.bind(this));
|
||||||
} else {
|
} else {
|
||||||
server = this.transport.createServer(this.requestHandler.bind(this));
|
server = this.transport.createServer(this.requestHandler.bind(this));
|
||||||
}
|
}
|
||||||
|
// Display the port number after we've started listening (the port number might have been specified as zero, in which case we will get an assigned port)
|
||||||
|
server.on("listening",function() {
|
||||||
|
var address = server.address();
|
||||||
|
$tw.utils.log("Serving on " + self.protocol + "://" + address.address + ":" + address.port + prefix,"brown/orange");
|
||||||
|
$tw.utils.log("(press ctrl-C to exit)","red");
|
||||||
|
});
|
||||||
|
// Listen
|
||||||
return server.listen(port,host);
|
return server.listen(port,host);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ exports.startup = function() {
|
||||||
// Set up the syncer object if we've got a syncadaptor
|
// Set up the syncer object if we've got a syncadaptor
|
||||||
if($tw.syncadaptor) {
|
if($tw.syncadaptor) {
|
||||||
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki, syncadaptor: $tw.syncadaptor});
|
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki, syncadaptor: $tw.syncadaptor});
|
||||||
}
|
}
|
||||||
// Setup the saver handler
|
// Setup the saver handler
|
||||||
$tw.saverHandler = new $tw.SaverHandler({
|
$tw.saverHandler = new $tw.SaverHandler({
|
||||||
wiki: $tw.wiki,
|
wiki: $tw.wiki,
|
||||||
|
|
|
@ -3,7 +3,7 @@ title: $:/core/modules/syncer.js
|
||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: global
|
module-type: global
|
||||||
|
|
||||||
The syncer tracks changes to the store. If a syncadaptor is used then individual tiddlers are synchronised through it. If there is no syncadaptor then the entire wiki is saved via saver modules.
|
The syncer tracks changes to the store and synchronises them to a remote data store represented as a "sync adaptor"
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function(){
|
||||||
|
@ -23,8 +23,10 @@ Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
|
||||||
Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
|
Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
|
||||||
Syncer.prototype.titleSyncDisableLazyLoading = "$:/config/SyncDisableLazyLoading";
|
Syncer.prototype.titleSyncDisableLazyLoading = "$:/config/SyncDisableLazyLoading";
|
||||||
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
|
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
|
||||||
|
Syncer.prototype.titleSyncThrottleInterval = "$:/config/SyncThrottleInterval";
|
||||||
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
|
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
|
||||||
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
|
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
|
||||||
|
Syncer.prototype.errorRetryInterval = 5 * 1000; // Interval to retry after an error
|
||||||
Syncer.prototype.fallbackInterval = 10 * 1000; // Unless the task is older than 10s
|
Syncer.prototype.fallbackInterval = 10 * 1000; // Unless the task is older than 10s
|
||||||
Syncer.prototype.pollTimerInterval = 60 * 1000; // Interval for polling for changes from the adaptor
|
Syncer.prototype.pollTimerInterval = 60 * 1000; // Interval for polling for changes from the adaptor
|
||||||
|
|
||||||
|
@ -36,6 +38,7 @@ wiki: wiki to be synced
|
||||||
function Syncer(options) {
|
function Syncer(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.wiki = options.wiki;
|
this.wiki = options.wiki;
|
||||||
|
// Save parameters
|
||||||
this.syncadaptor = options.syncadaptor;
|
this.syncadaptor = options.syncadaptor;
|
||||||
this.disableUI = !!options.disableUI;
|
this.disableUI = !!options.disableUI;
|
||||||
this.titleIsLoggedIn = options.titleIsLoggedIn || this.titleIsLoggedIn;
|
this.titleIsLoggedIn = options.titleIsLoggedIn || this.titleIsLoggedIn;
|
||||||
|
@ -43,27 +46,60 @@ function Syncer(options) {
|
||||||
this.titleSyncFilter = options.titleSyncFilter || this.titleSyncFilter;
|
this.titleSyncFilter = options.titleSyncFilter || this.titleSyncFilter;
|
||||||
this.titleSavedNotification = options.titleSavedNotification || this.titleSavedNotification;
|
this.titleSavedNotification = options.titleSavedNotification || this.titleSavedNotification;
|
||||||
this.taskTimerInterval = options.taskTimerInterval || this.taskTimerInterval;
|
this.taskTimerInterval = options.taskTimerInterval || this.taskTimerInterval;
|
||||||
this.throttleInterval = options.throttleInterval || this.throttleInterval;
|
this.throttleInterval = options.throttleInterval || parseInt(this.wiki.getTiddlerText(this.titleSyncThrottleInterval,""),10) || this.throttleInterval;
|
||||||
|
this.errorRetryInterval = options.errorRetryInterval || this.errorRetryInterval;
|
||||||
this.fallbackInterval = options.fallbackInterval || this.fallbackInterval;
|
this.fallbackInterval = options.fallbackInterval || this.fallbackInterval;
|
||||||
this.pollTimerInterval = options.pollTimerInterval || parseInt(this.wiki.getTiddlerText(this.titleSyncPollingInterval,""),10) || this.pollTimerInterval;
|
this.pollTimerInterval = options.pollTimerInterval || parseInt(this.wiki.getTiddlerText(this.titleSyncPollingInterval,""),10) || this.pollTimerInterval;
|
||||||
this.logging = "logging" in options ? options.logging : true;
|
this.logging = "logging" in options ? options.logging : true;
|
||||||
// Make a logger
|
// Make a logger
|
||||||
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : "") + (this.syncadaptor.name ? ("-" + this.syncadaptor.name) : ""),{
|
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : "") + (this.syncadaptor.name ? ("-" + this.syncadaptor.name) : ""),{
|
||||||
colour: "cyan",
|
colour: "cyan",
|
||||||
enable: this.logging
|
enable: this.logging,
|
||||||
});
|
saveHistory: true
|
||||||
|
});
|
||||||
|
// Make another logger for connection errors
|
||||||
|
this.loggerConnection = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : "") + (this.syncadaptor.name ? ("-" + this.syncadaptor.name) : "") + "-connection",{
|
||||||
|
colour: "cyan",
|
||||||
|
enable: this.logging
|
||||||
|
});
|
||||||
|
// Ask the syncadaptor to use the main logger
|
||||||
|
if(this.syncadaptor.setLoggerSaveBuffer) {
|
||||||
|
this.syncadaptor.setLoggerSaveBuffer(this.logger);
|
||||||
|
}
|
||||||
// Compile the dirty tiddler filter
|
// Compile the dirty tiddler filter
|
||||||
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
||||||
// Record information for known tiddlers
|
// Record information for known tiddlers
|
||||||
this.readTiddlerInfo();
|
this.readTiddlerInfo();
|
||||||
// Tasks are {type: "load"/"save"/"delete", title:, queueTime:, lastModificationTime:}
|
this.titlesToBeLoaded = {}; // Hashmap of titles of tiddlers that need loading from the server
|
||||||
this.taskQueue = {}; // Hashmap of tasks yet to be performed
|
this.titlesHaveBeenLazyLoaded = {}; // Hashmap of titles of tiddlers that have already been lazily loaded from the server
|
||||||
this.taskInProgress = {}; // Hash of tasks in progress
|
// Timers
|
||||||
this.taskTimerId = null; // Timer for task dispatch
|
this.taskTimerId = null; // Timer for task dispatch
|
||||||
this.pollTimerId = null; // Timer for polling server
|
this.pollTimerId = null; // Timer for polling server
|
||||||
|
// Number of outstanding requests
|
||||||
|
this.numTasksInProgress = 0;
|
||||||
// Listen out for changes to tiddlers
|
// Listen out for changes to tiddlers
|
||||||
this.wiki.addEventListener("change",function(changes) {
|
this.wiki.addEventListener("change",function(changes) {
|
||||||
self.syncToServer(changes);
|
// Filter the changes to just include ones that are being synced
|
||||||
|
var filteredChanges = self.getSyncedTiddlers(function(callback) {
|
||||||
|
$tw.utils.each(changes,function(change,title) {
|
||||||
|
var tiddler = self.wiki.tiddlerExists(title) && self.wiki.getTiddler(title);
|
||||||
|
callback(tiddler,title);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if(filteredChanges.length > 0) {
|
||||||
|
self.processTaskQueue();
|
||||||
|
} else {
|
||||||
|
// Look for deletions of tiddlers we're already syncing
|
||||||
|
var outstandingDeletion = false
|
||||||
|
$tw.utils.each(changes,function(change,title,object) {
|
||||||
|
if(change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) {
|
||||||
|
outstandingDeletion = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(outstandingDeletion) {
|
||||||
|
self.processTaskQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Browser event handlers
|
// Browser event handlers
|
||||||
if($tw.browser && !this.disableUI) {
|
if($tw.browser && !this.disableUI) {
|
||||||
|
@ -86,6 +122,9 @@ function Syncer(options) {
|
||||||
$tw.rootWidget.addEventListener("tm-server-refresh",function() {
|
$tw.rootWidget.addEventListener("tm-server-refresh",function() {
|
||||||
self.handleRefreshEvent();
|
self.handleRefreshEvent();
|
||||||
});
|
});
|
||||||
|
$tw.rootWidget.addEventListener("tm-copy-syncer-logs-to-clipboard",function() {
|
||||||
|
$tw.utils.copyToClipboard($tw.utils.getSystemInfo() + "\n\nLog:\n" + self.logger.getBuffer());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Listen out for lazyLoad events
|
// Listen out for lazyLoad events
|
||||||
if(!this.disableUI && $tw.wiki.getTiddlerText(this.titleSyncDisableLazyLoading) !== "yes") {
|
if(!this.disableUI && $tw.wiki.getTiddlerText(this.titleSyncDisableLazyLoading) !== "yes") {
|
||||||
|
@ -100,45 +139,87 @@ function Syncer(options) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Show a generic network error alert
|
||||||
|
*/
|
||||||
|
Syncer.prototype.displayError = function(msg,err) {
|
||||||
|
if(err === ($tw.language.getString("Error/XMLHttpRequest") + ": 0")) {
|
||||||
|
this.loggerConnection.alert($tw.language.getString("Error/NetworkErrorAlert"));
|
||||||
|
this.logger.log(msg + ":",err);
|
||||||
|
} else {
|
||||||
|
this.logger.alert(msg + ":",err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return an array of the tiddler titles that are subjected to syncing
|
||||||
|
*/
|
||||||
|
Syncer.prototype.getSyncedTiddlers = function(source) {
|
||||||
|
return this.filterFn.call(this.wiki,source);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return an array of the tiddler titles that are subjected to syncing
|
||||||
|
*/
|
||||||
|
Syncer.prototype.getTiddlerRevision = function(title) {
|
||||||
|
if(this.syncadaptor && this.syncadaptor.getTiddlerRevision) {
|
||||||
|
return this.syncadaptor.getTiddlerRevision(title);
|
||||||
|
} else {
|
||||||
|
return this.wiki.getTiddler(title).fields.revision;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Read (or re-read) the latest tiddler info from the store
|
Read (or re-read) the latest tiddler info from the store
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.readTiddlerInfo = function() {
|
Syncer.prototype.readTiddlerInfo = function() {
|
||||||
// Hashmap by title of {revision:,changeCount:,adaptorInfo:}
|
// Hashmap by title of {revision:,changeCount:,adaptorInfo:}
|
||||||
|
// "revision" is the revision of the tiddler last seen on the server, and "changecount" is the corresponding local changecount
|
||||||
this.tiddlerInfo = {};
|
this.tiddlerInfo = {};
|
||||||
// Record information for known tiddlers
|
// Record information for known tiddlers
|
||||||
var self = this,
|
var self = this,
|
||||||
tiddlers = this.filterFn.call(this.wiki);
|
tiddlers = this.getSyncedTiddlers();
|
||||||
$tw.utils.each(tiddlers,function(title) {
|
$tw.utils.each(tiddlers,function(title) {
|
||||||
var tiddler = self.wiki.getTiddler(title);
|
var tiddler = self.wiki.tiddlerExists(title) && self.wiki.getTiddler(title);
|
||||||
self.tiddlerInfo[title] = {
|
self.tiddlerInfo[title] = {
|
||||||
revision: tiddler.fields.revision,
|
revision: self.getTiddlerRevision(title),
|
||||||
adaptorInfo: self.syncadaptor && self.syncadaptor.getTiddlerInfo(tiddler),
|
adaptorInfo: self.syncadaptor && self.syncadaptor.getTiddlerInfo(tiddler),
|
||||||
changeCount: self.wiki.getChangeCount(title),
|
changeCount: self.wiki.getChangeCount(title)
|
||||||
hasBeenLazyLoaded: false
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
Create an tiddlerInfo structure if it doesn't already exist
|
|
||||||
*/
|
|
||||||
Syncer.prototype.createTiddlerInfo = function(title) {
|
|
||||||
if(!$tw.utils.hop(this.tiddlerInfo,title)) {
|
|
||||||
this.tiddlerInfo[title] = {
|
|
||||||
revision: null,
|
|
||||||
adaptorInfo: {},
|
|
||||||
changeCount: -1,
|
|
||||||
hasBeenLazyLoaded: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.isDirty = function() {
|
Syncer.prototype.isDirty = function() {
|
||||||
return (this.numTasksInQueue() > 0) || (this.numTasksInProgress() > 0);
|
this.logger.log("Checking dirty status");
|
||||||
|
// Check tiddlers that are in the store and included in the filter function
|
||||||
|
var titles = this.getSyncedTiddlers();
|
||||||
|
for(var index=0; index<titles.length; index++) {
|
||||||
|
var title = titles[index],
|
||||||
|
tiddlerInfo = this.tiddlerInfo[title];
|
||||||
|
if(this.wiki.tiddlerExists(title)) {
|
||||||
|
if(tiddlerInfo) {
|
||||||
|
// If the tiddler is known on the server and has been modified locally then it needs to be saved to the server
|
||||||
|
if($tw.wiki.getChangeCount(title) > tiddlerInfo.changeCount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the tiddler isn't known on the server then it needs to be saved to the server
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check tiddlers that are known from the server but not currently in the store
|
||||||
|
titles = Object.keys(this.tiddlerInfo);
|
||||||
|
for(index=0; index<titles.length; index++) {
|
||||||
|
if(!this.wiki.tiddlerExists(titles[index])) {
|
||||||
|
// There must be a pending delete
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -146,23 +227,26 @@ Update the document body with the class "tc-dirty" if the wiki has unsaved/unsyn
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.updateDirtyStatus = function() {
|
Syncer.prototype.updateDirtyStatus = function() {
|
||||||
if($tw.browser && !this.disableUI) {
|
if($tw.browser && !this.disableUI) {
|
||||||
$tw.utils.toggleClass(document.body,"tc-dirty",this.isDirty());
|
var dirty = this.isDirty();
|
||||||
|
$tw.utils.toggleClass(document.body,"tc-dirty",dirty);
|
||||||
|
if(!dirty) {
|
||||||
|
this.loggerConnection.clearAlerts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Save an incoming tiddler in the store, and updates the associated tiddlerInfo
|
Save an incoming tiddler in the store, and updates the associated tiddlerInfo
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.storeTiddler = function(tiddlerFields,hasBeenLazyLoaded) {
|
Syncer.prototype.storeTiddler = function(tiddlerFields) {
|
||||||
// Save the tiddler
|
// Save the tiddler
|
||||||
var tiddler = new $tw.Tiddler(tiddlerFields);
|
var tiddler = new $tw.Tiddler(tiddlerFields);
|
||||||
this.wiki.addTiddler(tiddler);
|
this.wiki.addTiddler(tiddler);
|
||||||
// Save the tiddler revision and changeCount details
|
// Save the tiddler revision and changeCount details
|
||||||
this.tiddlerInfo[tiddlerFields.title] = {
|
this.tiddlerInfo[tiddlerFields.title] = {
|
||||||
revision: tiddlerFields.revision,
|
revision: this.getTiddlerRevision(tiddlerFields.title),
|
||||||
adaptorInfo: this.syncadaptor.getTiddlerInfo(tiddler),
|
adaptorInfo: this.syncadaptor.getTiddlerInfo(tiddler),
|
||||||
changeCount: this.wiki.getChangeCount(tiddlerFields.title),
|
changeCount: this.wiki.getChangeCount(tiddlerFields.title)
|
||||||
hasBeenLazyLoaded: hasBeenLazyLoaded !== undefined ? hasBeenLazyLoaded : true
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,14 +260,14 @@ Syncer.prototype.getStatus = function(callback) {
|
||||||
this.syncadaptor.getStatus(function(err,isLoggedIn,username,isReadOnly,isAnonymous) {
|
this.syncadaptor.getStatus(function(err,isLoggedIn,username,isReadOnly,isAnonymous) {
|
||||||
if(err) {
|
if(err) {
|
||||||
self.logger.alert(err);
|
self.logger.alert(err);
|
||||||
return;
|
} else {
|
||||||
}
|
// Set the various status tiddlers
|
||||||
// Set the various status tiddlers
|
self.wiki.addTiddler({title: self.titleIsReadOnly,text: isReadOnly ? "yes" : "no"});
|
||||||
self.wiki.addTiddler({title: self.titleIsReadOnly,text: isReadOnly ? "yes" : "no"});
|
self.wiki.addTiddler({title: self.titleIsAnonymous,text: isAnonymous ? "yes" : "no"});
|
||||||
self.wiki.addTiddler({title: self.titleIsAnonymous,text: isAnonymous ? "yes" : "no"});
|
self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"});
|
||||||
self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"});
|
if(isLoggedIn) {
|
||||||
if(isLoggedIn) {
|
self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
|
||||||
self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
|
}
|
||||||
}
|
}
|
||||||
// Invoke the callback
|
// Invoke the callback
|
||||||
if(callback) {
|
if(callback) {
|
||||||
|
@ -199,91 +283,110 @@ Syncer.prototype.getStatus = function(callback) {
|
||||||
Synchronise from the server by reading the skinny tiddler list and queuing up loads for any tiddlers that we don't already have up to date
|
Synchronise from the server by reading the skinny tiddler list and queuing up loads for any tiddlers that we don't already have up to date
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.syncFromServer = function() {
|
Syncer.prototype.syncFromServer = function() {
|
||||||
if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
|
var self = this,
|
||||||
this.logger.log("Retrieving skinny tiddler list");
|
cancelNextSync = function() {
|
||||||
var self = this;
|
if(self.pollTimerId) {
|
||||||
if(this.pollTimerId) {
|
clearTimeout(self.pollTimerId);
|
||||||
clearTimeout(this.pollTimerId);
|
self.pollTimerId = null;
|
||||||
this.pollTimerId = null;
|
}
|
||||||
}
|
},
|
||||||
this.syncadaptor.getSkinnyTiddlers(function(err,tiddlers) {
|
triggerNextSync = function() {
|
||||||
// Trigger the next sync
|
|
||||||
self.pollTimerId = setTimeout(function() {
|
self.pollTimerId = setTimeout(function() {
|
||||||
self.pollTimerId = null;
|
self.pollTimerId = null;
|
||||||
self.syncFromServer.call(self);
|
self.syncFromServer.call(self);
|
||||||
},self.pollTimerInterval);
|
},self.pollTimerInterval);
|
||||||
// Check for errors
|
};
|
||||||
|
if(this.syncadaptor && this.syncadaptor.getUpdatedTiddlers) {
|
||||||
|
this.logger.log("Retrieving updated tiddler list");
|
||||||
|
cancelNextSync();
|
||||||
|
this.syncadaptor.getUpdatedTiddlers(self,function(err,updates) {
|
||||||
|
triggerNextSync();
|
||||||
if(err) {
|
if(err) {
|
||||||
self.logger.alert($tw.language.getString("Error/RetrievingSkinny") + ":",err);
|
self.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(updates) {
|
||||||
|
$tw.utils.each(updates.modifications,function(title) {
|
||||||
|
self.titlesToBeLoaded[title] = true;
|
||||||
|
});
|
||||||
|
$tw.utils.each(updates.deletions,function(title) {
|
||||||
|
delete self.tiddlerInfo[title];
|
||||||
|
self.logger.log("Deleting tiddler missing from server:",title);
|
||||||
|
self.wiki.deleteTiddler(title);
|
||||||
|
});
|
||||||
|
if(updates.modifications.length > 0 || updates.deletions.length > 0) {
|
||||||
|
self.processTaskQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
|
||||||
|
this.logger.log("Retrieving skinny tiddler list");
|
||||||
|
cancelNextSync();
|
||||||
|
this.syncadaptor.getSkinnyTiddlers(function(err,tiddlers) {
|
||||||
|
triggerNextSync();
|
||||||
|
// Check for errors
|
||||||
|
if(err) {
|
||||||
|
self.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Keep track of which tiddlers we already know about have been reported this time
|
||||||
|
var previousTitles = Object.keys(self.tiddlerInfo);
|
||||||
// Process each incoming tiddler
|
// Process each incoming tiddler
|
||||||
for(var t=0; t<tiddlers.length; t++) {
|
for(var t=0; t<tiddlers.length; t++) {
|
||||||
// Get the incoming tiddler fields, and the existing tiddler
|
// Get the incoming tiddler fields, and the existing tiddler
|
||||||
var tiddlerFields = tiddlers[t],
|
var tiddlerFields = tiddlers[t],
|
||||||
incomingRevision = tiddlerFields.revision + "",
|
incomingRevision = tiddlerFields.revision + "",
|
||||||
tiddler = self.wiki.getTiddler(tiddlerFields.title),
|
tiddler = self.wiki.tiddlerExists(tiddlerFields.title) && self.wiki.getTiddler(tiddlerFields.title),
|
||||||
tiddlerInfo = self.tiddlerInfo[tiddlerFields.title],
|
tiddlerInfo = self.tiddlerInfo[tiddlerFields.title],
|
||||||
currRevision = tiddlerInfo ? tiddlerInfo.revision : null;
|
currRevision = tiddlerInfo ? tiddlerInfo.revision : null,
|
||||||
|
indexInPreviousTitles = previousTitles.indexOf(tiddlerFields.title);
|
||||||
|
if(indexInPreviousTitles !== -1) {
|
||||||
|
previousTitles.splice(indexInPreviousTitles,1);
|
||||||
|
}
|
||||||
// Ignore the incoming tiddler if it's the same as the revision we've already got
|
// Ignore the incoming tiddler if it's the same as the revision we've already got
|
||||||
if(currRevision !== incomingRevision) {
|
if(currRevision !== incomingRevision) {
|
||||||
// Do a full load if we've already got a fat version of the tiddler
|
// Only load the skinny version if we don't already have a fat version of the tiddler
|
||||||
if(tiddler && tiddler.fields.text !== undefined) {
|
if(!tiddler || tiddler.fields.text === undefined) {
|
||||||
// Do a full load of this tiddler
|
self.storeTiddler(tiddlerFields);
|
||||||
self.enqueueSyncTask({
|
|
||||||
type: "load",
|
|
||||||
title: tiddlerFields.title
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Load the skinny version of the tiddler
|
|
||||||
self.storeTiddler(tiddlerFields,false);
|
|
||||||
}
|
}
|
||||||
|
// Do a full load of this tiddler
|
||||||
|
self.titlesToBeLoaded[tiddlerFields.title] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Delete any tiddlers that were previously reported but missing this time
|
||||||
|
$tw.utils.each(previousTitles,function(title) {
|
||||||
|
delete self.tiddlerInfo[title];
|
||||||
|
self.logger.log("Deleting tiddler missing from server:",title);
|
||||||
|
self.wiki.deleteTiddler(title);
|
||||||
|
});
|
||||||
|
self.processTaskQueue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Synchronise a set of changes to the server
|
Force load a tiddler from the server
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.syncToServer = function(changes) {
|
Syncer.prototype.enqueueLoadTiddler = function(title) {
|
||||||
var self = this,
|
this.titlesToBeLoaded[title] = true;
|
||||||
now = Date.now(),
|
this.processTaskQueue();
|
||||||
filteredChanges = this.filterFn.call(this.wiki,function(callback) {
|
|
||||||
$tw.utils.each(changes,function(change,title) {
|
|
||||||
var tiddler = self.wiki.getTiddler(title);
|
|
||||||
callback(tiddler,title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$tw.utils.each(changes,function(change,title,object) {
|
|
||||||
// Process the change if it is a deletion of a tiddler we're already syncing, or is on the filtered change list
|
|
||||||
if((change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) || filteredChanges.indexOf(title) !== -1) {
|
|
||||||
// Queue a task to sync this tiddler
|
|
||||||
self.enqueueSyncTask({
|
|
||||||
type: change.deleted ? "delete" : "save",
|
|
||||||
title: title
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Lazily load a skinny tiddler if we can
|
Lazily load a skinny tiddler if we can
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.handleLazyLoadEvent = function(title) {
|
Syncer.prototype.handleLazyLoadEvent = function(title) {
|
||||||
|
// Ignore if the syncadaptor doesn't handle it
|
||||||
|
if(!this.syncadaptor.supportsLazyLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Don't lazy load the same tiddler twice
|
// Don't lazy load the same tiddler twice
|
||||||
var info = this.tiddlerInfo[title];
|
if(!this.titlesHaveBeenLazyLoaded[title]) {
|
||||||
if(!info || !info.hasBeenLazyLoaded) {
|
|
||||||
// Don't lazy load if the tiddler isn't included in the sync filter
|
// Don't lazy load if the tiddler isn't included in the sync filter
|
||||||
if(this.filterFn.call(this.wiki).indexOf(title) !== -1) {
|
if(this.getSyncedTiddlers().indexOf(title) !== -1) {
|
||||||
this.createTiddlerInfo(title);
|
// Mark the tiddler as needing loading, and having already been lazily loaded
|
||||||
this.tiddlerInfo[title].hasBeenLazyLoaded = true;
|
this.titlesToBeLoaded[title] = true;
|
||||||
// Queue up a sync task to load this tiddler
|
this.titlesHaveBeenLazyLoaded[title] = true;
|
||||||
this.enqueueSyncTask({
|
|
||||||
type: "load",
|
|
||||||
title: title
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -294,7 +397,7 @@ Dispay a password prompt and allow the user to login
|
||||||
Syncer.prototype.handleLoginEvent = function() {
|
Syncer.prototype.handleLoginEvent = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.getStatus(function(err,isLoggedIn,username) {
|
this.getStatus(function(err,isLoggedIn,username) {
|
||||||
if(!isLoggedIn) {
|
if(!err && !isLoggedIn) {
|
||||||
$tw.passwordPrompt.createPrompt({
|
$tw.passwordPrompt.createPrompt({
|
||||||
serviceName: $tw.language.getString("LoginToTiddlySpace"),
|
serviceName: $tw.language.getString("LoginToTiddlySpace"),
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
|
@ -324,7 +427,7 @@ Syncer.prototype.login = function(username,password,callback) {
|
||||||
}
|
}
|
||||||
self.getStatus(function(err,isLoggedIn,username) {
|
self.getStatus(function(err,isLoggedIn,username) {
|
||||||
if(callback) {
|
if(callback) {
|
||||||
callback(null,isLoggedIn);
|
callback(err,isLoggedIn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -358,189 +461,179 @@ Syncer.prototype.handleRefreshEvent = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Queue up a sync task. If there is already a pending task for the tiddler, just update the last modification time
|
Process the next task
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.enqueueSyncTask = function(task) {
|
Syncer.prototype.processTaskQueue = function() {
|
||||||
var self = this,
|
var self = this;
|
||||||
now = Date.now();
|
// Only process a task if the sync adaptor is fully initialised and we're not already performing
|
||||||
// Set the timestamps on this task
|
// a task. If we are already performing a task then we'll dispatch the next one when it completes
|
||||||
task.queueTime = now;
|
if((!this.syncadaptor.isReady || this.syncadaptor.isReady()) && this.numTasksInProgress === 0) {
|
||||||
task.lastModificationTime = now;
|
// Choose the next task to perform
|
||||||
// Fill in some tiddlerInfo if the tiddler is one we haven't seen before
|
var task = this.chooseNextTask();
|
||||||
this.createTiddlerInfo(task.title);
|
// Perform the task if we had one
|
||||||
// Bail if this is a save and the tiddler is already at the changeCount that the server has
|
if(typeof task === "object" && task !== null) {
|
||||||
if(task.type === "save" && this.wiki.getChangeCount(task.title) <= this.tiddlerInfo[task.title].changeCount) {
|
this.numTasksInProgress += 1;
|
||||||
return;
|
task.run(function(err) {
|
||||||
}
|
self.numTasksInProgress -= 1;
|
||||||
// Check if this tiddler is already in the queue
|
if(err) {
|
||||||
if($tw.utils.hop(this.taskQueue,task.title)) {
|
self.displayError("Sync error while processing " + task.type + " of '" + task.title + "'",err);
|
||||||
// this.logger.log("Re-queueing up sync task with type:",task.type,"title:",task.title);
|
self.updateDirtyStatus();
|
||||||
var existingTask = this.taskQueue[task.title];
|
self.triggerTimeout(self.errorRetryInterval);
|
||||||
// If so, just update the last modification time
|
} else {
|
||||||
existingTask.lastModificationTime = task.lastModificationTime;
|
self.updateDirtyStatus();
|
||||||
// If the new task is a save then we upgrade the existing task to a save. Thus a pending load is turned into a save if the tiddler changes locally in the meantime. But a pending save is not modified to become a load
|
// Process the next task
|
||||||
if(task.type === "save" || task.type === "delete") {
|
self.processTaskQueue.call(self);
|
||||||
existingTask.type = task.type;
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No task is ready so update the status
|
||||||
|
this.updateDirtyStatus();
|
||||||
|
// And trigger a timeout if there is a pending task
|
||||||
|
if(task === true) {
|
||||||
|
this.triggerTimeout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this.logger.log("Queuing up sync task with type:",task.type,"title:",task.title);
|
this.updateDirtyStatus();
|
||||||
// If it is not in the queue, insert it
|
|
||||||
this.taskQueue[task.title] = task;
|
|
||||||
this.updateDirtyStatus();
|
|
||||||
}
|
}
|
||||||
// Process the queue
|
|
||||||
$tw.utils.nextTick(function() {self.processTaskQueue.call(self);});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
Syncer.prototype.triggerTimeout = function(interval) {
|
||||||
Return the number of tasks in progress
|
|
||||||
*/
|
|
||||||
Syncer.prototype.numTasksInProgress = function() {
|
|
||||||
return $tw.utils.count(this.taskInProgress);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return the number of tasks in the queue
|
|
||||||
*/
|
|
||||||
Syncer.prototype.numTasksInQueue = function() {
|
|
||||||
return $tw.utils.count(this.taskQueue);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Trigger a timeout if one isn't already outstanding
|
|
||||||
*/
|
|
||||||
Syncer.prototype.triggerTimeout = function() {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
if(!this.taskTimerId) {
|
if(!this.taskTimerId) {
|
||||||
this.taskTimerId = setTimeout(function() {
|
this.taskTimerId = setTimeout(function() {
|
||||||
self.taskTimerId = null;
|
self.taskTimerId = null;
|
||||||
self.processTaskQueue.call(self);
|
self.processTaskQueue.call(self);
|
||||||
},self.taskTimerInterval);
|
},interval || self.taskTimerInterval);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Process the task queue, performing the next task if appropriate
|
Choose the next sync task. We prioritise saves, then deletes, then loads from the server
|
||||||
*/
|
|
||||||
Syncer.prototype.processTaskQueue = function() {
|
|
||||||
var self = this;
|
|
||||||
// Only process a task if the sync adaptor is fully initialised and we're not already performing a task. If we are already performing a task then we'll dispatch the next one when it completes
|
|
||||||
if((!this.syncadaptor.isReady || this.syncadaptor.isReady()) && this.numTasksInProgress() === 0) {
|
|
||||||
// Choose the next task to perform
|
|
||||||
var task = this.chooseNextTask();
|
|
||||||
// Perform the task if we had one
|
|
||||||
if(task) {
|
|
||||||
// Remove the task from the queue and add it to the in progress list
|
|
||||||
delete this.taskQueue[task.title];
|
|
||||||
this.taskInProgress[task.title] = task;
|
|
||||||
this.updateDirtyStatus();
|
|
||||||
// Dispatch the task
|
|
||||||
this.dispatchTask(task,function(err) {
|
|
||||||
if(err) {
|
|
||||||
self.logger.alert("Sync error while processing '" + task.title + "':\n" + err);
|
|
||||||
}
|
|
||||||
// Mark that this task is no longer in progress
|
|
||||||
delete self.taskInProgress[task.title];
|
|
||||||
self.updateDirtyStatus();
|
|
||||||
// Process the next task
|
|
||||||
self.processTaskQueue.call(self);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Make sure we've set a time if there wasn't a task to perform, but we've still got tasks in the queue
|
|
||||||
if(this.numTasksInQueue() > 0) {
|
|
||||||
this.triggerTimeout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
Returns either a task object, null if there's no upcoming tasks, or the boolean true if there are pending tasks that aren't yet due
|
||||||
Choose the next applicable task
|
|
||||||
*/
|
*/
|
||||||
Syncer.prototype.chooseNextTask = function() {
|
Syncer.prototype.chooseNextTask = function() {
|
||||||
var self = this,
|
var thresholdLastSaved = (new Date()) - this.throttleInterval,
|
||||||
candidateTask = null,
|
havePending = null;
|
||||||
now = Date.now();
|
// First we look for tiddlers that have been modified locally and need saving back to the server
|
||||||
// Select the best candidate task
|
var titles = this.getSyncedTiddlers();
|
||||||
$tw.utils.each(this.taskQueue,function(task,title) {
|
for(var index=0; index<titles.length; index++) {
|
||||||
// Exclude the task if there's one of the same name in progress
|
var title = titles[index],
|
||||||
if($tw.utils.hop(self.taskInProgress,title)) {
|
tiddler = this.wiki.tiddlerExists(title) && this.wiki.getTiddler(title),
|
||||||
return;
|
tiddlerInfo = this.tiddlerInfo[title];
|
||||||
|
if(tiddler) {
|
||||||
|
// If the tiddler is not known on the server, or has been modified locally no more recently than the threshold then it needs to be saved to the server
|
||||||
|
var hasChanged = !tiddlerInfo || $tw.wiki.getChangeCount(title) > tiddlerInfo.changeCount,
|
||||||
|
isReadyToSave = !tiddlerInfo || !tiddlerInfo.timestampLastSaved || tiddlerInfo.timestampLastSaved < thresholdLastSaved;
|
||||||
|
if(hasChanged) {
|
||||||
|
if(isReadyToSave) {
|
||||||
|
return new SaveTiddlerTask(this,title);
|
||||||
|
} else {
|
||||||
|
havePending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Exclude the task if it is a save and the tiddler has been modified recently, but not hit the fallback time
|
}
|
||||||
if(task.type === "save" && (now - task.lastModificationTime) < self.throttleInterval &&
|
// Second, we check tiddlers that are known from the server but not currently in the store, and so need deleting on the server
|
||||||
(now - task.queueTime) < self.fallbackInterval) {
|
titles = Object.keys(this.tiddlerInfo);
|
||||||
return;
|
for(index=0; index<titles.length; index++) {
|
||||||
|
title = titles[index];
|
||||||
|
tiddlerInfo = this.tiddlerInfo[title];
|
||||||
|
tiddler = this.wiki.tiddlerExists(title) && this.wiki.getTiddler(title);
|
||||||
|
if(!tiddler) {
|
||||||
|
return new DeleteTiddlerTask(this,title);
|
||||||
}
|
}
|
||||||
// Exclude the task if it is newer than the current best candidate
|
}
|
||||||
if(candidateTask && candidateTask.queueTime < task.queueTime) {
|
// Check for tiddlers that need loading
|
||||||
return;
|
title = Object.keys(this.titlesToBeLoaded)[0];
|
||||||
}
|
if(title) {
|
||||||
// Now this is our best candidate
|
delete this.titlesToBeLoaded[title];
|
||||||
candidateTask = task;
|
return new LoadTiddlerTask(this,title);
|
||||||
});
|
}
|
||||||
return candidateTask;
|
// No tasks are ready
|
||||||
|
return havePending;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
function SaveTiddlerTask(syncer,title) {
|
||||||
Dispatch a task and invoke the callback
|
this.syncer = syncer;
|
||||||
*/
|
this.title = title;
|
||||||
Syncer.prototype.dispatchTask = function(task,callback) {
|
this.type = "save";
|
||||||
var self = this;
|
}
|
||||||
if(task.type === "save") {
|
|
||||||
var changeCount = this.wiki.getChangeCount(task.title),
|
SaveTiddlerTask.prototype.run = function(callback) {
|
||||||
tiddler = this.wiki.getTiddler(task.title);
|
var self = this,
|
||||||
this.logger.log("Dispatching 'save' task:",task.title);
|
changeCount = this.syncer.wiki.getChangeCount(this.title),
|
||||||
if(tiddler) {
|
tiddler = this.syncer.wiki.tiddlerExists(this.title) && this.syncer.wiki.getTiddler(this.title);
|
||||||
this.syncadaptor.saveTiddler(tiddler,function(err,adaptorInfo,revision) {
|
this.syncer.logger.log("Dispatching 'save' task:",this.title);
|
||||||
if(err) {
|
if(tiddler) {
|
||||||
return callback(err);
|
this.syncer.syncadaptor.saveTiddler(tiddler,function(err,adaptorInfo,revision) {
|
||||||
}
|
// If there's an error, exit without changing any internal state
|
||||||
// Adjust the info stored about this tiddler
|
|
||||||
self.tiddlerInfo[task.title] = {
|
|
||||||
changeCount: changeCount,
|
|
||||||
adaptorInfo: adaptorInfo,
|
|
||||||
revision: revision
|
|
||||||
};
|
|
||||||
// Invoke the callback
|
|
||||||
callback(null);
|
|
||||||
},{
|
|
||||||
tiddlerInfo: self.tiddlerInfo[task.title]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.logger.log(" Not Dispatching 'save' task:",task.title,"tiddler does not exist");
|
|
||||||
return callback(null);
|
|
||||||
}
|
|
||||||
} else if(task.type === "load") {
|
|
||||||
// Load the tiddler
|
|
||||||
this.logger.log("Dispatching 'load' task:",task.title);
|
|
||||||
this.syncadaptor.loadTiddler(task.title,function(err,tiddlerFields) {
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
// Store the tiddler
|
// Adjust the info stored about this tiddler
|
||||||
if(tiddlerFields) {
|
self.syncer.tiddlerInfo[self.title] = {
|
||||||
self.storeTiddler(tiddlerFields,true);
|
changeCount: changeCount,
|
||||||
}
|
adaptorInfo: adaptorInfo,
|
||||||
|
revision: revision,
|
||||||
|
timestampLastSaved: new Date()
|
||||||
|
};
|
||||||
// Invoke the callback
|
// Invoke the callback
|
||||||
callback(null);
|
callback(null);
|
||||||
});
|
});
|
||||||
} else if(task.type === "delete") {
|
} else {
|
||||||
// Delete the tiddler
|
this.syncer.logger.log(" Not Dispatching 'save' task:",this.title,"tiddler does not exist");
|
||||||
this.logger.log("Dispatching 'delete' task:",task.title);
|
$tw.utils.nextTick(callback(null));
|
||||||
this.syncadaptor.deleteTiddler(task.title,function(err) {
|
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
delete self.tiddlerInfo[task.title];
|
|
||||||
// Invoke the callback
|
|
||||||
callback(null);
|
|
||||||
},{
|
|
||||||
tiddlerInfo: self.tiddlerInfo[task.title]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function DeleteTiddlerTask(syncer,title) {
|
||||||
|
this.syncer = syncer;
|
||||||
|
this.title = title;
|
||||||
|
this.type = "delete";
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteTiddlerTask.prototype.run = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
this.syncer.logger.log("Dispatching 'delete' task:",this.title);
|
||||||
|
this.syncer.syncadaptor.deleteTiddler(this.title,function(err) {
|
||||||
|
// If there's an error, exit without changing any internal state
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
// Remove the info stored about this tiddler
|
||||||
|
delete self.syncer.tiddlerInfo[self.title];
|
||||||
|
// Invoke the callback
|
||||||
|
callback(null);
|
||||||
|
},{
|
||||||
|
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function LoadTiddlerTask(syncer,title) {
|
||||||
|
this.syncer = syncer;
|
||||||
|
this.title = title;
|
||||||
|
this.type = "load";
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadTiddlerTask.prototype.run = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
this.syncer.logger.log("Dispatching 'load' task:",this.title);
|
||||||
|
this.syncer.syncadaptor.loadTiddler(this.title,function(err,tiddlerFields) {
|
||||||
|
// If there's an error, exit without changing any internal state
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
// Update the info stored about this tiddler
|
||||||
|
if(tiddlerFields) {
|
||||||
|
self.syncer.storeTiddler(tiddlerFields);
|
||||||
|
}
|
||||||
|
// Invoke the callback
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
exports.Syncer = Syncer;
|
exports.Syncer = Syncer;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -22,6 +22,7 @@ A quick and dirty HTTP function; to be refactored later. Options are:
|
||||||
*/
|
*/
|
||||||
exports.httpRequest = function(options) {
|
exports.httpRequest = function(options) {
|
||||||
var type = options.type || "GET",
|
var type = options.type || "GET",
|
||||||
|
url = options.url,
|
||||||
headers = options.headers || {accept: "application/json"},
|
headers = options.headers || {accept: "application/json"},
|
||||||
returnProp = options.returnProp || "responseText",
|
returnProp = options.returnProp || "responseText",
|
||||||
request = new XMLHttpRequest(),
|
request = new XMLHttpRequest(),
|
||||||
|
@ -36,7 +37,11 @@ exports.httpRequest = function(options) {
|
||||||
$tw.utils.each(options.data,function(dataItem,dataItemTitle) {
|
$tw.utils.each(options.data,function(dataItem,dataItemTitle) {
|
||||||
results.push(dataItemTitle + "=" + encodeURIComponent(dataItem));
|
results.push(dataItemTitle + "=" + encodeURIComponent(dataItem));
|
||||||
});
|
});
|
||||||
data = results.join("&");
|
if(type === "GET" || type === "HEAD") {
|
||||||
|
url += "?" + results.join("&");
|
||||||
|
} else {
|
||||||
|
data = results.join("&");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set up the state change handler
|
// Set up the state change handler
|
||||||
|
@ -52,7 +57,7 @@ exports.httpRequest = function(options) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Make the request
|
// Make the request
|
||||||
request.open(type,options.url,true);
|
request.open(type,url,true);
|
||||||
if(headers) {
|
if(headers) {
|
||||||
$tw.utils.each(headers,function(header,headerTitle,object) {
|
$tw.utils.each(headers,function(header,headerTitle,object) {
|
||||||
request.setRequestHeader(headerTitle,header);
|
request.setRequestHeader(headerTitle,header);
|
||||||
|
|
|
@ -58,6 +58,9 @@ Modal.prototype.display = function(title,options) {
|
||||||
this.adjustPageClass();
|
this.adjustPageClass();
|
||||||
// Add classes
|
// Add classes
|
||||||
$tw.utils.addClass(wrapper,"tc-modal-wrapper");
|
$tw.utils.addClass(wrapper,"tc-modal-wrapper");
|
||||||
|
if(tiddler.fields && tiddler.fields.class) {
|
||||||
|
$tw.utils.addClass(wrapper,tiddler.fields.class);
|
||||||
|
}
|
||||||
$tw.utils.addClass(modalBackdrop,"tc-modal-backdrop");
|
$tw.utils.addClass(modalBackdrop,"tc-modal-backdrop");
|
||||||
$tw.utils.addClass(modalWrapper,"tc-modal");
|
$tw.utils.addClass(modalWrapper,"tc-modal");
|
||||||
$tw.utils.addClass(modalHeader,"tc-modal-header");
|
$tw.utils.addClass(modalHeader,"tc-modal-header");
|
||||||
|
@ -104,7 +107,7 @@ Modal.prototype.display = function(title,options) {
|
||||||
modalBody.appendChild(modalLink);
|
modalBody.appendChild(modalLink);
|
||||||
}
|
}
|
||||||
// Render the footer of the message
|
// Render the footer of the message
|
||||||
if(tiddler && tiddler.fields && tiddler.fields.help) {
|
if(tiddler.fields && tiddler.fields.help) {
|
||||||
var link = this.srcDocument.createElement("a");
|
var link = this.srcDocument.createElement("a");
|
||||||
link.setAttribute("href",tiddler.fields.help);
|
link.setAttribute("href",tiddler.fields.help);
|
||||||
link.setAttribute("target","_blank");
|
link.setAttribute("target","_blank");
|
||||||
|
|
|
@ -22,15 +22,42 @@ function Logger(componentName,options) {
|
||||||
this.componentName = componentName || "";
|
this.componentName = componentName || "";
|
||||||
this.colour = options.colour || "white";
|
this.colour = options.colour || "white";
|
||||||
this.enable = "enable" in options ? options.enable : true;
|
this.enable = "enable" in options ? options.enable : true;
|
||||||
|
this.save = "save" in options ? options.save : true;
|
||||||
|
this.saveLimit = options.saveLimit || 100 * 1024;
|
||||||
|
this.saveBufferLogger = this;
|
||||||
|
this.buffer = "";
|
||||||
|
this.alertCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.prototype.setSaveBuffer = function(logger) {
|
||||||
|
this.saveBufferLogger = logger;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Log a message
|
Log a message
|
||||||
*/
|
*/
|
||||||
Logger.prototype.log = function(/* args */) {
|
Logger.prototype.log = function(/* args */) {
|
||||||
if(this.enable && console !== undefined && console.log !== undefined) {
|
var self = this;
|
||||||
return Function.apply.call(console.log, console, [$tw.utils.terminalColour(this.colour),this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0)).concat($tw.utils.terminalColour()));
|
if(this.enable) {
|
||||||
}
|
if(this.saveBufferLogger.save) {
|
||||||
|
this.saveBufferLogger.buffer += $tw.utils.formatDateString(new Date(),"YYYY MM DD 0hh:0mm:0ss.0XXX") + ":";
|
||||||
|
$tw.utils.each(Array.prototype.slice.call(arguments,0),function(arg,index) {
|
||||||
|
self.saveBufferLogger.buffer += " " + arg;
|
||||||
|
});
|
||||||
|
this.saveBufferLogger.buffer += "\n";
|
||||||
|
this.saveBufferLogger.buffer = this.saveBufferLogger.buffer.slice(-this.saveBufferLogger.saveLimit);
|
||||||
|
}
|
||||||
|
if(console !== undefined && console.log !== undefined) {
|
||||||
|
return Function.apply.call(console.log, console, [$tw.utils.terminalColour(this.colour),this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0)).concat($tw.utils.terminalColour()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read the message buffer
|
||||||
|
*/
|
||||||
|
Logger.prototype.getBuffer = function() {
|
||||||
|
return this.saveBufferLogger.buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,6 +97,7 @@ Logger.prototype.alert = function(/* args */) {
|
||||||
component: this.componentName
|
component: this.componentName
|
||||||
};
|
};
|
||||||
existingCount = 0;
|
existingCount = 0;
|
||||||
|
this.alertCount += 1;
|
||||||
}
|
}
|
||||||
alertFields.modified = new Date();
|
alertFields.modified = new Date();
|
||||||
if(++existingCount > 1) {
|
if(++existingCount > 1) {
|
||||||
|
@ -87,6 +115,22 @@ Logger.prototype.alert = function(/* args */) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Clear outstanding alerts
|
||||||
|
*/
|
||||||
|
Logger.prototype.clearAlerts = function() {
|
||||||
|
var self = this;
|
||||||
|
if($tw.browser && this.alertCount > 0) {
|
||||||
|
$tw.utils.each($tw.wiki.getTiddlersWithTag(ALERT_TAG),function(title) {
|
||||||
|
var tiddler = $tw.wiki.getTiddler(title);
|
||||||
|
if(tiddler.fields.component === self.componentName) {
|
||||||
|
$tw.wiki.deleteTiddler(title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.alertCount = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
exports.Logger = Logger;
|
exports.Logger = Logger;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -728,16 +728,20 @@ exports.timer = function(base) {
|
||||||
/*
|
/*
|
||||||
Convert text and content type to a data URI
|
Convert text and content type to a data URI
|
||||||
*/
|
*/
|
||||||
exports.makeDataUri = function(text,type) {
|
exports.makeDataUri = function(text,type,_canonical_uri) {
|
||||||
type = type || "text/vnd.tiddlywiki";
|
type = type || "text/vnd.tiddlywiki";
|
||||||
var typeInfo = $tw.config.contentTypeInfo[type] || $tw.config.contentTypeInfo["text/plain"],
|
var typeInfo = $tw.config.contentTypeInfo[type] || $tw.config.contentTypeInfo["text/plain"],
|
||||||
isBase64 = typeInfo.encoding === "base64",
|
isBase64 = typeInfo.encoding === "base64",
|
||||||
parts = [];
|
parts = [];
|
||||||
parts.push("data:");
|
if(_canonical_uri) {
|
||||||
parts.push(type);
|
parts.push(_canonical_uri);
|
||||||
parts.push(isBase64 ? ";base64" : "");
|
} else {
|
||||||
parts.push(",");
|
parts.push("data:");
|
||||||
parts.push(isBase64 ? text : encodeURIComponent(text));
|
parts.push(type);
|
||||||
|
parts.push(isBase64 ? ";base64" : "");
|
||||||
|
parts.push(",");
|
||||||
|
parts.push(isBase64 ? text : encodeURIComponent(text));
|
||||||
|
}
|
||||||
return parts.join("");
|
return parts.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -779,4 +783,22 @@ exports.strEndsWith = function(str,ending,position) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return system information useful for debugging
|
||||||
|
*/
|
||||||
|
exports.getSystemInfo = function(str,ending,position) {
|
||||||
|
var results = [],
|
||||||
|
save = function(desc,value) {
|
||||||
|
results.push(desc + ": " + value);
|
||||||
|
};
|
||||||
|
if($tw.browser) {
|
||||||
|
save("User Agent",navigator.userAgent);
|
||||||
|
save("Online Status",window.navigator.onLine);
|
||||||
|
}
|
||||||
|
if($tw.node) {
|
||||||
|
save("Node Version",process.version);
|
||||||
|
}
|
||||||
|
return results.join("\n");
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*\
|
||||||
|
title: $:/core/modules/widgets/action-popup.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: widget
|
||||||
|
|
||||||
|
Action widget to trigger a popup.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||||
|
|
||||||
|
var ActionPopupWidget = function(parseTreeNode,options) {
|
||||||
|
this.initialise(parseTreeNode,options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inherit from the base widget class
|
||||||
|
*/
|
||||||
|
ActionPopupWidget.prototype = new Widget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Render this widget into the DOM
|
||||||
|
*/
|
||||||
|
ActionPopupWidget.prototype.render = function(parent,nextSibling) {
|
||||||
|
this.computeAttributes();
|
||||||
|
this.execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute the internal state of the widget
|
||||||
|
*/
|
||||||
|
ActionPopupWidget.prototype.execute = function() {
|
||||||
|
this.actionState = this.getAttribute("$state");
|
||||||
|
this.actionCoords = this.getAttribute("$coords");
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Refresh the widget by ensuring our attributes are up to date
|
||||||
|
*/
|
||||||
|
ActionPopupWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
|
var changedAttributes = this.computeAttributes();
|
||||||
|
if(changedAttributes["$state"] || changedAttributes["$coords"]) {
|
||||||
|
this.refreshSelf();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.refreshChildren(changedTiddlers);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Invoke the action associated with this widget
|
||||||
|
*/
|
||||||
|
ActionPopupWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
||||||
|
// Trigger the popup
|
||||||
|
var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
|
||||||
|
match = popupLocationRegExp.exec(this.actionCoords);
|
||||||
|
if(match) {
|
||||||
|
$tw.popup.triggerPopup({
|
||||||
|
domNode: null,
|
||||||
|
domNodeRect: {
|
||||||
|
left: parseFloat(match[1]),
|
||||||
|
top: parseFloat(match[2]),
|
||||||
|
width: parseFloat(match[3]),
|
||||||
|
height: parseFloat(match[4])
|
||||||
|
},
|
||||||
|
title: this.actionState,
|
||||||
|
wiki: this.wiki
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true; // Action was invoked
|
||||||
|
};
|
||||||
|
|
||||||
|
exports["action-popup"] = ActionPopupWidget;
|
||||||
|
|
||||||
|
})();
|
|
@ -64,6 +64,10 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||||
if(this["aria-label"]) {
|
if(this["aria-label"]) {
|
||||||
domNode.setAttribute("aria-label",this["aria-label"]);
|
domNode.setAttribute("aria-label",this["aria-label"]);
|
||||||
}
|
}
|
||||||
|
// Set the tabindex
|
||||||
|
if(this.tabIndex) {
|
||||||
|
domNode.setAttribute("tabindex",this.tabIndex);
|
||||||
|
}
|
||||||
// Add a click event handler
|
// Add a click event handler
|
||||||
domNode.addEventListener("click",function (event) {
|
domNode.addEventListener("click",function (event) {
|
||||||
var handled = false;
|
var handled = false;
|
||||||
|
@ -205,6 +209,7 @@ ButtonWidget.prototype.execute = function() {
|
||||||
this.setField = this.getAttribute("setField");
|
this.setField = this.getAttribute("setField");
|
||||||
this.setIndex = this.getAttribute("setIndex");
|
this.setIndex = this.getAttribute("setIndex");
|
||||||
this.popupTitle = this.getAttribute("popupTitle");
|
this.popupTitle = this.getAttribute("popupTitle");
|
||||||
|
this.tabIndex = this.getAttribute("tabindex");
|
||||||
// Make child widgets
|
// Make child widgets
|
||||||
this.makeChildWidgets();
|
this.makeChildWidgets();
|
||||||
};
|
};
|
||||||
|
@ -214,7 +219,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||||
*/
|
*/
|
||||||
ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
var changedAttributes = this.computeAttributes();
|
var changedAttributes = this.computeAttributes();
|
||||||
if(changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle) {
|
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle) {
|
||||||
this.refreshSelf();
|
this.refreshSelf();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||||
*/
|
*/
|
||||||
DraggableWidget.prototype.refresh = function(changedTiddlers) {
|
DraggableWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
var changedAttributes = this.computeAttributes();
|
var changedAttributes = this.computeAttributes();
|
||||||
if(changedTiddlers.tag || changedTiddlers["class"]) {
|
if(changedAttributes.tag || changedAttributes["class"]) {
|
||||||
this.refreshSelf();
|
this.refreshSelf();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,12 +43,14 @@ DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||||
classes.push("tc-droppable");
|
classes.push("tc-droppable");
|
||||||
domNode.className = classes.join(" ");
|
domNode.className = classes.join(" ");
|
||||||
// Add event handlers
|
// Add event handlers
|
||||||
$tw.utils.addEventListeners(domNode,[
|
if(this.droppableEnable) {
|
||||||
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
$tw.utils.addEventListeners(domNode,[
|
||||||
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
||||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
||||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"}
|
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||||
]);
|
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"}
|
||||||
|
]);
|
||||||
|
}
|
||||||
// Insert element
|
// Insert element
|
||||||
parent.insertBefore(domNode,nextSibling);
|
parent.insertBefore(domNode,nextSibling);
|
||||||
this.renderChildren(domNode,null);
|
this.renderChildren(domNode,null);
|
||||||
|
@ -142,6 +144,7 @@ DroppableWidget.prototype.execute = function() {
|
||||||
this.droppableEffect = this.getAttribute("effect","copy");
|
this.droppableEffect = this.getAttribute("effect","copy");
|
||||||
this.droppableTag = this.getAttribute("tag");
|
this.droppableTag = this.getAttribute("tag");
|
||||||
this.droppableClass = this.getAttribute("class");
|
this.droppableClass = this.getAttribute("class");
|
||||||
|
this.droppableEnable = (this.getAttribute("enable") || "yes") === "yes";
|
||||||
// Make child widgets
|
// Make child widgets
|
||||||
this.makeChildWidgets();
|
this.makeChildWidgets();
|
||||||
};
|
};
|
||||||
|
@ -151,7 +154,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||||
*/
|
*/
|
||||||
DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
var changedAttributes = this.computeAttributes();
|
var changedAttributes = this.computeAttributes();
|
||||||
if(changedAttributes["class"] || changedAttributes.tag) {
|
if(changedAttributes["class"] || changedAttributes.tag || changedAttributes.enable) {
|
||||||
this.refreshSelf();
|
this.refreshSelf();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,16 @@ DropZoneWidget.prototype.render = function(parent,nextSibling) {
|
||||||
var domNode = this.document.createElement("div");
|
var domNode = this.document.createElement("div");
|
||||||
domNode.className = "tc-dropzone";
|
domNode.className = "tc-dropzone";
|
||||||
// Add event handlers
|
// Add event handlers
|
||||||
$tw.utils.addEventListeners(domNode,[
|
if(this.dropzoneEnable) {
|
||||||
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
$tw.utils.addEventListeners(domNode,[
|
||||||
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
||||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
||||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"},
|
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||||
{name: "paste", handlerObject: this, handlerMethod: "handlePasteEvent"},
|
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"},
|
||||||
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
|
{name: "paste", handlerObject: this, handlerMethod: "handlePasteEvent"},
|
||||||
]);
|
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
|
||||||
|
]);
|
||||||
|
}
|
||||||
domNode.addEventListener("click",function (event) {
|
domNode.addEventListener("click",function (event) {
|
||||||
},false);
|
},false);
|
||||||
// Insert element
|
// Insert element
|
||||||
|
@ -189,6 +191,7 @@ Compute the internal state of the widget
|
||||||
*/
|
*/
|
||||||
DropZoneWidget.prototype.execute = function() {
|
DropZoneWidget.prototype.execute = function() {
|
||||||
this.dropzoneDeserializer = this.getAttribute("deserializer");
|
this.dropzoneDeserializer = this.getAttribute("deserializer");
|
||||||
|
this.dropzoneEnable = (this.getAttribute("enable") || "yes") === "yes";
|
||||||
// Make child widgets
|
// Make child widgets
|
||||||
this.makeChildWidgets();
|
this.makeChildWidgets();
|
||||||
};
|
};
|
||||||
|
@ -197,6 +200,11 @@ DropZoneWidget.prototype.execute = function() {
|
||||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||||
*/
|
*/
|
||||||
DropZoneWidget.prototype.refresh = function(changedTiddlers) {
|
DropZoneWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
|
var changedAttributes = this.computeAttributes();
|
||||||
|
if(changedAttributes.enable) {
|
||||||
|
this.refreshSelf();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return this.refreshChildren(changedTiddlers);
|
return this.refreshChildren(changedTiddlers);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ Edit-binary widget; placeholder for editing binary tiddlers
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
|
||||||
|
var EXPORT_BUTTON_IMAGE = "$:/core/images/export-button";
|
||||||
|
|
||||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||||
|
|
||||||
|
@ -43,13 +44,55 @@ EditBinaryWidget.prototype.render = function(parent,nextSibling) {
|
||||||
Compute the internal state of the widget
|
Compute the internal state of the widget
|
||||||
*/
|
*/
|
||||||
EditBinaryWidget.prototype.execute = function() {
|
EditBinaryWidget.prototype.execute = function() {
|
||||||
// Construct the child widgets
|
// Get our parameters
|
||||||
this.makeChildWidgets([{
|
var editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||||
type: "transclude",
|
var tiddler = this.wiki.getTiddler(editTitle);
|
||||||
|
var type = tiddler.fields.type;
|
||||||
|
var text = tiddler.fields.text;
|
||||||
|
// Transclude the binary data tiddler warning message
|
||||||
|
var warn = {
|
||||||
|
type: "element",
|
||||||
|
tag: "p",
|
||||||
|
children: [{
|
||||||
|
type: "transclude",
|
||||||
|
attributes: {
|
||||||
|
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
// Create download link based on draft tiddler title
|
||||||
|
var link = {
|
||||||
|
type: "element",
|
||||||
|
tag: "a",
|
||||||
attributes: {
|
attributes: {
|
||||||
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
|
title: {type: "indirect", textReference: "!!draft.title"},
|
||||||
}
|
download: {type: "indirect", textReference: "!!draft.title"}
|
||||||
}]);
|
},
|
||||||
|
children: [{
|
||||||
|
type: "transclude",
|
||||||
|
attributes: {
|
||||||
|
tiddler: {type: "string", value: EXPORT_BUTTON_IMAGE}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
// Set the link href to internal data URI (no external)
|
||||||
|
if(text) {
|
||||||
|
link.attributes.href = {
|
||||||
|
type: "string",
|
||||||
|
value: "data:" + type + ";base64," + text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Combine warning message and download link in a div
|
||||||
|
var element = {
|
||||||
|
type: "element",
|
||||||
|
tag: "div",
|
||||||
|
attributes: {
|
||||||
|
class: {type: "string", value: "tc-binary-warning"}
|
||||||
|
},
|
||||||
|
children: [warn, link]
|
||||||
|
}
|
||||||
|
// Construct the child widgets
|
||||||
|
this.makeChildWidgets([element]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -37,49 +37,52 @@ ImportVariablesWidget.prototype.render = function(parent,nextSibling) {
|
||||||
Compute the internal state of the widget
|
Compute the internal state of the widget
|
||||||
*/
|
*/
|
||||||
ImportVariablesWidget.prototype.execute = function(tiddlerList) {
|
ImportVariablesWidget.prototype.execute = function(tiddlerList) {
|
||||||
var self = this;
|
var widgetPointer = this;
|
||||||
// Get our parameters
|
// Get our parameters
|
||||||
this.filter = this.getAttribute("filter");
|
this.filter = this.getAttribute("filter");
|
||||||
// Compute the filter
|
// Compute the filter
|
||||||
this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this);
|
this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this);
|
||||||
// Accumulate the <$set> widgets from each tiddler
|
// Accumulate the <$set> widgets from each tiddler
|
||||||
var widgetStackStart,widgetStackEnd;
|
|
||||||
function addWidgetNode(widgetNode) {
|
|
||||||
if(widgetNode) {
|
|
||||||
if(!widgetStackStart && !widgetStackEnd) {
|
|
||||||
widgetStackStart = widgetNode;
|
|
||||||
widgetStackEnd = widgetNode;
|
|
||||||
} else {
|
|
||||||
widgetStackEnd.children = [widgetNode];
|
|
||||||
widgetStackEnd = widgetNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$tw.utils.each(this.tiddlerList,function(title) {
|
$tw.utils.each(this.tiddlerList,function(title) {
|
||||||
var parser = self.wiki.parseTiddler(title);
|
var parser = widgetPointer.wiki.parseTiddler(title);
|
||||||
if(parser) {
|
if(parser) {
|
||||||
var parseTreeNode = parser.tree[0];
|
var parseTreeNode = parser.tree[0];
|
||||||
while(parseTreeNode && parseTreeNode.type === "set") {
|
while(parseTreeNode && parseTreeNode.type === "set") {
|
||||||
addWidgetNode({
|
var node = {
|
||||||
type: "set",
|
type: "set",
|
||||||
attributes: parseTreeNode.attributes,
|
attributes: parseTreeNode.attributes,
|
||||||
params: parseTreeNode.params,
|
params: parseTreeNode.params,
|
||||||
isMacroDefinition: parseTreeNode.isMacroDefinition
|
isMacroDefinition: parseTreeNode.isMacroDefinition
|
||||||
});
|
};
|
||||||
|
if (parseTreeNode.isMacroDefinition) {
|
||||||
|
// Macro definitions can be folded into
|
||||||
|
// current widget instead of adding
|
||||||
|
// another link to the chain.
|
||||||
|
var widget = widgetPointer.makeChildWidget(node);
|
||||||
|
widget.computeAttributes();
|
||||||
|
widget.execute();
|
||||||
|
// We SHALLOW copy over all variables
|
||||||
|
// in widget. We can't use
|
||||||
|
// $tw.utils.assign, because that copies
|
||||||
|
// up the prototype chain, which we
|
||||||
|
// don't want.
|
||||||
|
$tw.utils.each(Object.keys(widget.variables), function(key) {
|
||||||
|
widgetPointer.variables[key] = widget.variables[key];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
widgetPointer.makeChildWidgets([node]);
|
||||||
|
widgetPointer = widgetPointer.children[0];
|
||||||
|
}
|
||||||
parseTreeNode = parseTreeNode.children && parseTreeNode.children[0];
|
parseTreeNode = parseTreeNode.children && parseTreeNode.children[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Add our own children to the end of the pile
|
|
||||||
var parseTreeNodes;
|
if (widgetPointer != this) {
|
||||||
if(widgetStackStart && widgetStackEnd) {
|
widgetPointer.parseTreeNode.children = this.parseTreeNode.children;
|
||||||
parseTreeNodes = [widgetStackStart];
|
|
||||||
widgetStackEnd.children = this.parseTreeNode.children;
|
|
||||||
} else {
|
} else {
|
||||||
parseTreeNodes = this.parseTreeNode.children;
|
widgetPointer.makeChildWidgets();
|
||||||
}
|
}
|
||||||
// Construct the child widgets
|
|
||||||
this.makeChildWidgets(parseTreeNodes);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -414,6 +414,30 @@ exports.forEachTiddler = function(/* [options,]callback */) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return an array of tiddler titles that are directly linked within the given parse tree
|
||||||
|
*/
|
||||||
|
exports.extractLinks = function(parseTreeRoot) {
|
||||||
|
// Count up the links
|
||||||
|
var links = [],
|
||||||
|
checkParseTree = function(parseTree) {
|
||||||
|
for(var t=0; t<parseTree.length; t++) {
|
||||||
|
var parseTreeNode = parseTree[t];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(parseTreeNode.children) {
|
||||||
|
checkParseTree(parseTreeNode.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkParseTree(parseTreeRoot);
|
||||||
|
return links;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return an array of tiddler titles that are directly linked from the specified tiddler
|
Return an array of tiddler titles that are directly linked from the specified tiddler
|
||||||
*/
|
*/
|
||||||
|
@ -423,26 +447,10 @@ exports.getTiddlerLinks = function(title) {
|
||||||
return this.getCacheForTiddler(title,"links",function() {
|
return this.getCacheForTiddler(title,"links",function() {
|
||||||
// Parse the tiddler
|
// Parse the tiddler
|
||||||
var parser = self.parseTiddler(title);
|
var parser = self.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 === "link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
|
|
||||||
var value = parseTreeNode.attributes.to.value;
|
|
||||||
if(links.indexOf(value) === -1) {
|
|
||||||
links.push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(parseTreeNode.children) {
|
|
||||||
checkParseTree(parseTreeNode.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if(parser) {
|
if(parser) {
|
||||||
checkParseTree(parser.tree);
|
return self.extractLinks(parser.tree);
|
||||||
}
|
}
|
||||||
return links;
|
return [];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,13 +459,18 @@ Return an array of tiddler titles that link to the specified tiddler
|
||||||
*/
|
*/
|
||||||
exports.getTiddlerBacklinks = function(targetTitle) {
|
exports.getTiddlerBacklinks = function(targetTitle) {
|
||||||
var self = this,
|
var self = this,
|
||||||
|
backlinksIndexer = this.getIndexer("BacklinksIndexer"),
|
||||||
|
backlinks = backlinksIndexer && backlinksIndexer.lookup(targetTitle);
|
||||||
|
|
||||||
|
if(!backlinks) {
|
||||||
backlinks = [];
|
backlinks = [];
|
||||||
this.forEachTiddler(function(title,tiddler) {
|
this.forEachTiddler(function(title,tiddler) {
|
||||||
var links = self.getTiddlerLinks(title);
|
var links = self.getTiddlerLinks(title);
|
||||||
if(links.indexOf(targetTitle) !== -1) {
|
if(links.indexOf(targetTitle) !== -1) {
|
||||||
backlinks.push(title);
|
backlinks.push(title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return backlinks;
|
return backlinks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -660,8 +673,9 @@ exports.getTiddlerAsJson = function(title) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getTiddlersAsJson = function(filter) {
|
exports.getTiddlersAsJson = function(filter,spaces) {
|
||||||
var tiddlers = this.filterTiddlers(filter),
|
var tiddlers = this.filterTiddlers(filter),
|
||||||
|
spaces = (spaces === undefined) ? $tw.config.preferences.jsonSpaces : spaces,
|
||||||
data = [];
|
data = [];
|
||||||
for(var t=0;t<tiddlers.length; t++) {
|
for(var t=0;t<tiddlers.length; t++) {
|
||||||
var tiddler = this.getTiddler(tiddlers[t]);
|
var tiddler = this.getTiddler(tiddlers[t]);
|
||||||
|
@ -673,7 +687,7 @@ exports.getTiddlersAsJson = function(filter) {
|
||||||
data.push(fields);
|
data.push(fields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return JSON.stringify(data,null,$tw.config.preferences.jsonSpaces);
|
return JSON.stringify(data,null,spaces);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1232,9 +1246,9 @@ exports.getTiddlerText = function(title,defaultText) {
|
||||||
if(!tiddler) {
|
if(!tiddler) {
|
||||||
return defaultText;
|
return defaultText;
|
||||||
}
|
}
|
||||||
if(tiddler.fields.text !== undefined) {
|
if(!tiddler.hasField("_is_skinny")) {
|
||||||
// Just return the text if we've got it
|
// Just return the text if we've got it
|
||||||
return tiddler.fields.text;
|
return tiddler.fields.text || "";
|
||||||
} else {
|
} else {
|
||||||
// Tell any listeners about the need to lazily load this tiddler
|
// Tell any listeners about the need to lazily load this tiddler
|
||||||
this.dispatchEvent("lazyLoad",title);
|
this.dispatchEvent("lazyLoad",title);
|
||||||
|
@ -1398,10 +1412,8 @@ fromPageRect: page coordinates of the origin of the navigation
|
||||||
historyTitle: title of history tiddler (defaults to $:/HistoryList)
|
historyTitle: title of history tiddler (defaults to $:/HistoryList)
|
||||||
*/
|
*/
|
||||||
exports.addToHistory = function(title,fromPageRect,historyTitle) {
|
exports.addToHistory = function(title,fromPageRect,historyTitle) {
|
||||||
if(historyTitle) {
|
var story = new $tw.Story({wiki: this, historyTitle: historyTitle});
|
||||||
var story = new $tw.Story({wiki: this, historyTitle: historyTitle});
|
story.addToHistory(title,fromPageRect);
|
||||||
story.addToHistory(title,fromPageRect);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1412,10 +1424,8 @@ storyTitle: title of story tiddler (defaults to $:/StoryList)
|
||||||
options: see story.js
|
options: see story.js
|
||||||
*/
|
*/
|
||||||
exports.addToStory = function(title,fromTitle,storyTitle,options) {
|
exports.addToStory = function(title,fromTitle,storyTitle,options) {
|
||||||
if(storyTitle) {
|
var story = new $tw.Story({wiki: this, storyTitle: storyTitle});
|
||||||
var story = new $tw.Story({wiki: this, storyTitle: storyTitle});
|
story.addToStory(title,fromTitle,options);
|
||||||
story.addToStory(title,fromTitle,options);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
title: $:/palettes/GruvboxDark
|
||||||
|
name: Gruvbox Dark
|
||||||
|
description: Retro groove color scheme
|
||||||
|
tags: $:/tags/Palette
|
||||||
|
type: application/x-tiddler-dictionary
|
||||||
|
license: https://github.com/morhetz/gruvbox
|
||||||
|
|
||||||
|
alert-background: #cc241d
|
||||||
|
alert-border: #cc241d
|
||||||
|
alert-highlight: #d79921
|
||||||
|
alert-muted-foreground: #504945
|
||||||
|
background: #3c3836
|
||||||
|
blockquote-bar: <<colour muted-foreground>>
|
||||||
|
button-background: #504945
|
||||||
|
button-foreground: #fbf1c7
|
||||||
|
button-border: transparent
|
||||||
|
code-background: #504945
|
||||||
|
code-border: #504945
|
||||||
|
code-foreground: #fb4934
|
||||||
|
diff-delete-background: #fb4934
|
||||||
|
diff-delete-foreground: <<colour foreground>>
|
||||||
|
diff-equal-background:
|
||||||
|
diff-equal-foreground: <<colour foreground>>
|
||||||
|
diff-insert-background: #b8bb26
|
||||||
|
diff-insert-foreground: <<colour foreground>>
|
||||||
|
diff-invisible-background:
|
||||||
|
diff-invisible-foreground: <<colour muted-foreground>>
|
||||||
|
dirty-indicator: #fb4934
|
||||||
|
download-background: #b8bb26
|
||||||
|
download-foreground: <<colour background>>
|
||||||
|
dragger-background: <<colour foreground>>
|
||||||
|
dragger-foreground: <<colour background>>
|
||||||
|
dropdown-background: #665c54
|
||||||
|
dropdown-border: <<colour background>>
|
||||||
|
dropdown-tab-background-selected: #ebdbb2
|
||||||
|
dropdown-tab-background: #665c54
|
||||||
|
dropzone-background: #98971a
|
||||||
|
external-link-background-hover: inherit
|
||||||
|
external-link-background-visited: inherit
|
||||||
|
external-link-background: inherit
|
||||||
|
external-link-foreground-hover: inherit
|
||||||
|
external-link-foreground-visited: #d3869b
|
||||||
|
external-link-foreground: #8ec07c
|
||||||
|
foreground: #fbf1c7
|
||||||
|
menubar-background: #504945
|
||||||
|
menubar-foreground: <<colour foreground>>
|
||||||
|
message-background: #83a598
|
||||||
|
message-border: #83a598
|
||||||
|
message-foreground: #3c3836
|
||||||
|
modal-backdrop: <<colour foreground>>
|
||||||
|
modal-background: <<colour background>>
|
||||||
|
modal-border: #504945
|
||||||
|
modal-footer-background: #3c3836
|
||||||
|
modal-footer-border: #3c3836
|
||||||
|
modal-header-border: #3c3836
|
||||||
|
muted-foreground: #d5c4a1
|
||||||
|
notification-background: <<colour primary>>
|
||||||
|
notification-border: <<colour primary>>
|
||||||
|
page-background: #282828
|
||||||
|
pre-background: #504945
|
||||||
|
pre-border: #504945
|
||||||
|
primary: #d79921
|
||||||
|
select-tag-background: #665c54
|
||||||
|
select-tag-foreground: <<colour foreground>>
|
||||||
|
sidebar-button-foreground: <<colour foreground>>
|
||||||
|
sidebar-controls-foreground-hover: #7c6f64
|
||||||
|
sidebar-controls-foreground: #504945
|
||||||
|
sidebar-foreground-shadow: transparent
|
||||||
|
sidebar-foreground: #fbf1c7
|
||||||
|
sidebar-muted-foreground-hover: #7c6f64
|
||||||
|
sidebar-muted-foreground: #504945
|
||||||
|
sidebar-tab-background-selected: #bdae93
|
||||||
|
sidebar-tab-background: #3c3836
|
||||||
|
sidebar-tab-border-selected: <<colour tab-border-selected>>
|
||||||
|
sidebar-tab-border: #bdae93
|
||||||
|
sidebar-tab-divider: <<colour page-background>>
|
||||||
|
sidebar-tab-foreground-selected: #282828
|
||||||
|
sidebar-tab-foreground: <<colour tab-foreground>>
|
||||||
|
sidebar-tiddler-link-foreground-hover: #458588
|
||||||
|
sidebar-tiddler-link-foreground: #98971a
|
||||||
|
site-title-foreground: <<colour tiddler-title-foreground>>
|
||||||
|
static-alert-foreground: #B48EAD
|
||||||
|
tab-background-selected: #ebdbb2
|
||||||
|
tab-background: #665c54
|
||||||
|
tab-border-selected: #665c54
|
||||||
|
tab-border: #665c54
|
||||||
|
tab-divider: #bdae93
|
||||||
|
tab-foreground-selected: #282828
|
||||||
|
tab-foreground: #ebdbb2
|
||||||
|
table-border: #7c6f64
|
||||||
|
table-footer-background: #665c54
|
||||||
|
table-header-background: #504945
|
||||||
|
tag-background: #d3869b
|
||||||
|
tag-foreground: #282828
|
||||||
|
tiddler-background: <<colour background>>
|
||||||
|
tiddler-border: <<colour background>>
|
||||||
|
tiddler-controls-foreground-hover: #7c6f64
|
||||||
|
tiddler-controls-foreground-selected: #7c6f64
|
||||||
|
tiddler-controls-foreground: #665c54
|
||||||
|
tiddler-editor-background: #282828
|
||||||
|
tiddler-editor-border-image: #282828
|
||||||
|
tiddler-editor-border: #282828
|
||||||
|
tiddler-editor-fields-even: #504945
|
||||||
|
tiddler-editor-fields-odd: #7c6f64
|
||||||
|
tiddler-info-background: #32302f
|
||||||
|
tiddler-info-border: #ebdbb2
|
||||||
|
tiddler-info-tab-background: #ebdbb2
|
||||||
|
tiddler-link-background: <<colour background>>
|
||||||
|
tiddler-link-foreground: <<colour primary>>
|
||||||
|
tiddler-subtitle-foreground: #7c6f64
|
||||||
|
tiddler-title-foreground: #a89984
|
||||||
|
toolbar-new-button:
|
||||||
|
toolbar-options-button:
|
||||||
|
toolbar-save-button:
|
||||||
|
toolbar-info-button:
|
||||||
|
toolbar-edit-button:
|
||||||
|
toolbar-close-button:
|
||||||
|
toolbar-delete-button:
|
||||||
|
toolbar-cancel-button:
|
||||||
|
toolbar-done-button:
|
||||||
|
untagged-background: #504945
|
||||||
|
very-muted-foreground: #bdae93
|
|
@ -42,6 +42,8 @@ external-link-foreground-hover: inherit
|
||||||
external-link-foreground-visited: #5E81AC
|
external-link-foreground-visited: #5E81AC
|
||||||
external-link-foreground: #8FBCBB
|
external-link-foreground: #8FBCBB
|
||||||
foreground: #d8dee9
|
foreground: #d8dee9
|
||||||
|
menubar-background: #2E3440
|
||||||
|
menubar-foreground: #d8dee9
|
||||||
message-background: #2E3440
|
message-background: #2E3440
|
||||||
message-border: #2E3440
|
message-border: #2E3440
|
||||||
message-foreground: #547599
|
message-foreground: #547599
|
||||||
|
@ -61,8 +63,8 @@ primary: #5E81AC
|
||||||
select-tag-background: #3b4252
|
select-tag-background: #3b4252
|
||||||
select-tag-foreground: <<colour foreground>>
|
select-tag-foreground: <<colour foreground>>
|
||||||
sidebar-button-foreground: <<colour foreground>>
|
sidebar-button-foreground: <<colour foreground>>
|
||||||
sidebar-controls-foreground-hover: #4C566A
|
sidebar-controls-foreground-hover: #D8DEE9
|
||||||
sidebar-controls-foreground: #3B4252
|
sidebar-controls-foreground: #4C566A
|
||||||
sidebar-foreground-shadow: transparent
|
sidebar-foreground-shadow: transparent
|
||||||
sidebar-foreground: #D8DEE9
|
sidebar-foreground: #D8DEE9
|
||||||
sidebar-muted-foreground-hover: #4C566A
|
sidebar-muted-foreground-hover: #4C566A
|
||||||
|
|
|
@ -117,3 +117,20 @@ toolbar-cancel-button:
|
||||||
toolbar-done-button:
|
toolbar-done-button:
|
||||||
untagged-background: #999999
|
untagged-background: #999999
|
||||||
very-muted-foreground: #888888
|
very-muted-foreground: #888888
|
||||||
|
wikilist-background: #e5e5e5
|
||||||
|
wikilist-item: #fff
|
||||||
|
wikilist-info: #000
|
||||||
|
wikilist-title: #666
|
||||||
|
wikilist-title-svg: <<colour wikilist-title>>
|
||||||
|
wikilist-url: #aaa
|
||||||
|
wikilist-button-open: #4fb82b
|
||||||
|
wikilist-button-open-hover: green
|
||||||
|
wikilist-button-reveal: #5778d8
|
||||||
|
wikilist-button-reveal-hover: blue
|
||||||
|
wikilist-button-remove: #d85778
|
||||||
|
wikilist-button-remove-hover: red
|
||||||
|
wikilist-toolbar-background: #d3d3d3
|
||||||
|
wikilist-toolbar-foreground: #888
|
||||||
|
wikilist-droplink-dragover: rgba(255,192,192,0.5)
|
||||||
|
wikilist-button-background: #acacac
|
||||||
|
wikilist-button-foreground: #000
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
title: $:/core/templates/html-div-skinny-tiddler
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
This template is a variant of $:/core/templates/html-div-tiddler used for saving skinny tiddlers (with no text field)
|
||||||
|
|
||||||
|
-->`<div`<$fields template=' $name$="$encoded_value$"'></$fields>`>
|
||||||
|
<pre></pre>
|
||||||
|
</div>`
|
|
@ -3,4 +3,7 @@ title: $:/core/save/lazy-all
|
||||||
\define saveTiddlerFilter()
|
\define saveTiddlerFilter()
|
||||||
[is[system]] -[prefix[$:/state/popup/]] -[[$:/HistoryList]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]]
|
[is[system]] -[prefix[$:/state/popup/]] -[[$:/HistoryList]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]]
|
||||||
\end
|
\end
|
||||||
|
\define skinnySaveTiddlerFilter()
|
||||||
|
[!is[system]]
|
||||||
|
\end
|
||||||
{{$:/core/templates/tiddlywiki5.html}}
|
{{$:/core/templates/tiddlywiki5.html}}
|
||||||
|
|
|
@ -3,4 +3,7 @@ title: $:/core/save/lazy-images
|
||||||
\define saveTiddlerFilter()
|
\define saveTiddlerFilter()
|
||||||
[is[tiddler]] -[prefix[$:/state/popup/]] -[[$:/HistoryList]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[!is[system]is[image]] +[sort[title]]
|
[is[tiddler]] -[prefix[$:/state/popup/]] -[[$:/HistoryList]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[!is[system]is[image]] +[sort[title]]
|
||||||
\end
|
\end
|
||||||
|
\define skinnySaveTiddlerFilter()
|
||||||
|
[is[image]]
|
||||||
|
\end
|
||||||
{{$:/core/templates/tiddlywiki5.html}}
|
{{$:/core/templates/tiddlywiki5.html}}
|
||||||
|
|
|
@ -3,6 +3,7 @@ title: $:/core/templates/store.area.template.html
|
||||||
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
|
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
|
||||||
`<div id="storeArea" style="display:none;">`
|
`<div id="storeArea" style="display:none;">`
|
||||||
<$list filter=<<saveTiddlerFilter>> template="$:/core/templates/html-div-tiddler"/>
|
<$list filter=<<saveTiddlerFilter>> template="$:/core/templates/html-div-tiddler"/>
|
||||||
|
<$list filter={{{ [<skinnySaveTiddlerFilter>] }}} template="$:/core/templates/html-div-skinny-tiddler"/>
|
||||||
`</div>`
|
`</div>`
|
||||||
</$reveal>
|
</$reveal>
|
||||||
<$reveal type="match" state="$:/isEncrypted" text="yes">
|
<$reveal type="match" state="$:/isEncrypted" text="yes">
|
||||||
|
|
|
@ -2,10 +2,12 @@ title: $:/core/ui/AlertTemplate
|
||||||
|
|
||||||
<div class="tc-alert">
|
<div class="tc-alert">
|
||||||
<div class="tc-alert-toolbar">
|
<div class="tc-alert-toolbar">
|
||||||
<$button class="tc-btn-invisible"><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/delete-button}}</$button>
|
<$button class="tc-btn-invisible"><$action-deletetiddler $tiddler=<<currentTiddler>>/>{{$:/core/images/cancel-button}}</$button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tc-alert-subtitle">
|
<div class="tc-alert-subtitle">
|
||||||
<$view field="component"/> - <$view field="modified" format="date" template="0hh:0mm:0ss DD MM YYYY"/> <$reveal type="nomatch" state="!!count" text=""><span class="tc-alert-highlight">({{$:/language/Count}}: <$view field="count"/>)</span></$reveal>
|
<$wikify name="format" text=<<lingo Tiddler/DateFormat>>>
|
||||||
|
<$view field="component"/> - <$view field="modified" format="date" template=<<format>>/> <$reveal type="nomatch" state="!!count" text=""><span class="tc-alert-highlight">({{$:/language/Count}}: <$view field="count"/>)</span></$reveal>
|
||||||
|
</$wikify>
|
||||||
</div>
|
</div>
|
||||||
<div class="tc-alert-body">
|
<div class="tc-alert-body">
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
title: $:/core/ui/BinaryWarning
|
title: $:/core/ui/BinaryWarning
|
||||||
|
|
||||||
\define lingo-base() $:/language/BinaryWarning/
|
\define lingo-base() $:/language/BinaryWarning/
|
||||||
<div class="tc-binary-warning">
|
|
||||||
|
|
||||||
<<lingo Prompt>>
|
<<lingo Prompt>>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
title: $:/core/ui/ControlPanel/Saving/Gitea
|
||||||
|
tags: $:/tags/ControlPanel/Saving
|
||||||
|
caption: {{$:/language/ControlPanel/Saving/GitService/Gitea/Caption}}
|
||||||
|
|
||||||
|
\define lingo-base() $:/language/ControlPanel/Saving/GitService/
|
||||||
|
\define service-name() ~Gitea
|
||||||
|
|
||||||
|
<<lingo Description>>
|
||||||
|
|
||||||
|
|<<lingo UserName>> |<$edit-text tiddler="$:/Gitea/Username" default="" tag="input"/> |
|
||||||
|
|<<lingo Gitea/Password>> |<$password name="Gitea"/> |
|
||||||
|
|<<lingo Repo>> |<$edit-text tiddler="$:/Gitea/Repo" default="" tag="input"/> |
|
||||||
|
|<<lingo Branch>> |<$edit-text tiddler="$:/Gitea/Branch" default="master" tag="input"/> |
|
||||||
|
|<<lingo Path>> |<$edit-text tiddler="$:/Gitea/Path" default="" tag="input"/> |
|
||||||
|
|<<lingo Filename>> |<$edit-text tiddler="$:/Gitea/Filename" default="" tag="input"/> |
|
||||||
|
|<<lingo ServerURL>> |<$edit-text tiddler="$:/Gitea/ServerURL" default="https://gitea/api/v1" tag="input"/> |
|
|
@ -69,7 +69,7 @@ $value={{{ [<newFieldValueTiddler>get[text]] }}}/>
|
||||||
<$fieldmangler>
|
<$fieldmangler>
|
||||||
<div class="tc-edit-field-add">
|
<div class="tc-edit-field-add">
|
||||||
<em class="tc-edit">
|
<em class="tc-edit">
|
||||||
<<lingo Fields/Add/Prompt>>
|
<<lingo Fields/Add/Prompt>>
|
||||||
</em>
|
</em>
|
||||||
<span class="tc-edit-field-add-name">
|
<span class="tc-edit-field-add-name">
|
||||||
<$edit-text tiddler=<<newFieldNameTiddler>> tag="input" default="" placeholder={{$:/language/EditTemplate/Fields/Add/Name/Placeholder}} focusPopup=<<qualify "$:/state/popup/field-dropdown">> class="tc-edit-texteditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[fields]then[true]] ~[[false]] }}}/>
|
<$edit-text tiddler=<<newFieldNameTiddler>> tag="input" default="" placeholder={{$:/language/EditTemplate/Fields/Add/Name/Placeholder}} focusPopup=<<qualify "$:/state/popup/field-dropdown">> class="tc-edit-texteditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[fields]then[true]] ~[[false]] }}}/>
|
||||||
|
|
|
@ -2,8 +2,9 @@ title: $:/core/ui/EditTemplate/type
|
||||||
tags: $:/tags/EditTemplate
|
tags: $:/tags/EditTemplate
|
||||||
|
|
||||||
\define lingo-base() $:/language/EditTemplate/
|
\define lingo-base() $:/language/EditTemplate/
|
||||||
|
\whitespace trim
|
||||||
<div class="tc-type-selector"><$fieldmangler>
|
<div class="tc-type-selector"><$fieldmangler>
|
||||||
<em class="tc-edit"><<lingo Type/Prompt>></em> <$edit-text field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] ~[[false]] }}}/> <$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button> <$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}</$button>
|
<em class="tc-edit"><<lingo Type/Prompt>></em> <$edit-text field="type" tag="input" default="" placeholder={{$:/language/EditTemplate/Type/Placeholder}} focusPopup=<<qualify "$:/state/popup/type-dropdown">> class="tc-edit-typeeditor tc-edit-texteditor tc-popup-handle" tabindex={{$:/config/EditTabIndex}} focus={{{ [{$:/config/AutoFocus}match[type]then[true]] ~[[false]] }}}/> <$button popup=<<qualify "$:/state/popup/type-dropdown">> class="tc-btn-invisible tc-btn-dropdown" tooltip={{$:/language/EditTemplate/Type/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Type/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button> <$button message="tm-remove-field" param="type" class="tc-btn-invisible tc-btn-icon" tooltip={{$:/language/EditTemplate/Type/Delete/Hint}} aria-label={{$:/language/EditTemplate/Type/Delete/Caption}}>{{$:/core/images/delete-button}}</$button>
|
||||||
</$fieldmangler></div>
|
</$fieldmangler></div>
|
||||||
|
|
||||||
<div class="tc-block-dropdown-wrapper">
|
<div class="tc-block-dropdown-wrapper">
|
||||||
|
|
|
@ -12,19 +12,21 @@ tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
|
||||||
|
|
||||||
<$set name="tv-config-toolbar-class" value={{$:/config/Toolbar/ButtonClass}}>
|
<$set name="tv-config-toolbar-class" value={{$:/config/Toolbar/ButtonClass}}>
|
||||||
|
|
||||||
|
<$set name="tv-enable-drag-and-drop" value={{$:/config/DragAndDrop/Enable}}>
|
||||||
|
|
||||||
|
<$set name="tv-show-missing-links" value={{$:/config/MissingLinks}}>
|
||||||
|
|
||||||
<$set name="storyviewTitle" value={{$:/view}}>
|
<$set name="storyviewTitle" value={{$:/view}}>
|
||||||
|
|
||||||
<$set name="tv-storyview-single-tiddler-mode" value={{{ [<storyviewTitle>getstoryviewmode[singletiddlermode]] }}}>
|
<$set name="tv-storyview-single-tiddler-mode" value={{{ [<storyviewTitle>getstoryviewmode[singletiddlermode]] }}}>
|
||||||
|
|
||||||
<$set name="tv-show-missing-links" value={{$:/config/MissingLinks}}>
|
|
||||||
|
|
||||||
<$set name="languageTitle" value={{{ [{$:/language}get[name]] }}}>
|
<$set name="languageTitle" value={{{ [{$:/language}get[name]] }}}>
|
||||||
|
|
||||||
<div class=<<containerClasses>>>
|
<div class=<<containerClasses>>>
|
||||||
|
|
||||||
<$navigator story="$:/StoryList" history="$:/HistoryList" singleTiddlerMode=<<tv-storyview-single-tiddler-mode>> openLinkFromInsideRiver={{$:/config/Navigation/openLinkFromInsideRiver}} openLinkFromOutsideRiver={{$:/config/Navigation/openLinkFromOutsideRiver}} relinkOnRename={{$:/config/RelinkOnRename}}>
|
<$navigator story="$:/StoryList" history="$:/HistoryList" singleTiddlerMode=<<tv-storyview-single-tiddler-mode>> openLinkFromInsideRiver={{$:/config/Navigation/openLinkFromInsideRiver}} openLinkFromOutsideRiver={{$:/config/Navigation/openLinkFromOutsideRiver}} relinkOnRename={{$:/config/RelinkOnRename}}>
|
||||||
|
|
||||||
<$dropzone>
|
<$dropzone enable=<<tv-enable-drag-and-drop>>>
|
||||||
|
|
||||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/PageTemplate]!has[draft.of]]" variable="listItem">
|
<$list filter="[all[shadows+tiddlers]tag[$:/tags/PageTemplate]!has[draft.of]]" variable="listItem">
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ caption: <$list filter="[<tv-storyview-single-tiddler-mode>prefix[yes]]" emptyMe
|
||||||
|
|
||||||
\define droppable-item(button)
|
\define droppable-item(button)
|
||||||
\whitespace trim
|
\whitespace trim
|
||||||
<$droppable actions=<<drop-actions>>>
|
<$droppable actions=<<drop-actions>> enable=<<tv-allow-drag-and-drop>>>
|
||||||
<<placeholder>>
|
<<placeholder>>
|
||||||
<div>
|
<div>
|
||||||
$button$
|
$button$
|
||||||
|
|
|
@ -4,6 +4,8 @@ color: #bbb
|
||||||
|
|
||||||
\define lingo-base() $:/language/TagManager/
|
\define lingo-base() $:/language/TagManager/
|
||||||
\define iconEditorTab(type)
|
\define iconEditorTab(type)
|
||||||
|
\whitespace trim
|
||||||
|
<$link to=""><<lingo Icons/None>></$link>
|
||||||
<$list filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[sort[title]] +[$type$is[system]]">
|
<$list filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[sort[title]] +[$type$is[system]]">
|
||||||
<$link to={{!!title}}>
|
<$link to={{!!title}}>
|
||||||
<$transclude/> <$view field="title"/>
|
<$transclude/> <$view field="title"/>
|
||||||
|
@ -11,6 +13,7 @@ color: #bbb
|
||||||
</$list>
|
</$list>
|
||||||
\end
|
\end
|
||||||
\define iconEditor(title)
|
\define iconEditor(title)
|
||||||
|
\whitespace trim
|
||||||
<div class="tc-drop-down-wrapper">
|
<div class="tc-drop-down-wrapper">
|
||||||
<$button popupTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} class="tc-btn-invisible tc-btn-dropdown">{{$:/core/images/down-arrow}}</$button>
|
<$button popupTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} class="tc-btn-invisible tc-btn-dropdown">{{$:/core/images/down-arrow}}</$button>
|
||||||
<$reveal stateTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} type="popup" position="belowleft" text="" default="">
|
<$reveal stateTitle={{{ [[$:/state/popup/icon/]addsuffix<__title__>] }}} type="popup" position="belowleft" text="" default="">
|
||||||
|
@ -25,6 +28,7 @@ color: #bbb
|
||||||
</div>
|
</div>
|
||||||
\end
|
\end
|
||||||
\define toggleButton(state)
|
\define toggleButton(state)
|
||||||
|
\whitespace trim
|
||||||
<$reveal stateTitle=<<__state__>> type="match" text="closed" default="closed">
|
<$reveal stateTitle=<<__state__>> type="match" text="closed" default="closed">
|
||||||
<$button setTitle=<<__state__>> setTo="open" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
<$button setTitle=<<__state__>> setTo="open" class="tc-btn-invisible tc-btn-dropdown" selectedClass="tc-selected">
|
||||||
{{$:/core/images/info-button}}
|
{{$:/core/images/info-button}}
|
||||||
|
@ -36,6 +40,7 @@ color: #bbb
|
||||||
</$button>
|
</$button>
|
||||||
</$reveal>
|
</$reveal>
|
||||||
\end
|
\end
|
||||||
|
\whitespace trim
|
||||||
<table class="tc-tag-manager-table">
|
<table class="tc-tag-manager-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
title: $:/core/ui/TopBar/menu
|
title: $:/core/ui/TopBar/menu
|
||||||
tags: $:/tags/TopRightBar
|
tags: $:/tags/TopRightBar
|
||||||
|
|
||||||
<$reveal state="$:/state/sidebar" type="nomatch" text="no">
|
<$list filter="[[$:/state/sidebar]get[text]] +[else[yes]!match[no]]" variable="ignore">
|
||||||
<$button set="$:/state/sidebar" setTo="no" tooltip={{$:/language/Buttons/HideSideBar/Hint}} aria-label={{$:/language/Buttons/HideSideBar/Caption}} class="tc-btn-invisible">{{$:/core/images/chevron-right}}</$button>
|
<$button set="$:/state/sidebar" setTo="no" tooltip={{$:/language/Buttons/HideSideBar/Hint}} aria-label={{$:/language/Buttons/HideSideBar/Caption}} class="tc-btn-invisible">{{$:/core/images/chevron-right}}</$button>
|
||||||
</$reveal>
|
</$list>
|
||||||
<$reveal state="$:/state/sidebar" type="match" text="no">
|
<$list filter="[[$:/state/sidebar]get[text]] +[else[yes]match[no]]" variable="ignore">
|
||||||
<$button set="$:/state/sidebar" setTo="yes" tooltip={{$:/language/Buttons/ShowSideBar/Hint}} aria-label={{$:/language/Buttons/ShowSideBar/Caption}} class="tc-btn-invisible">{{$:/core/images/chevron-left}}</$button>
|
<$button set="$:/state/sidebar" setTo="yes" tooltip={{$:/language/Buttons/ShowSideBar/Hint}} aria-label={{$:/language/Buttons/ShowSideBar/Caption}} class="tc-btn-invisible">{{$:/core/images/chevron-left}}</$button>
|
||||||
</$reveal>
|
</$list>
|
||||||
|
|
|
@ -4,6 +4,6 @@ title: $:/core/ui/ViewTemplate
|
||||||
$:/state/folded/$(currentTiddler)$
|
$:/state/folded/$(currentTiddler)$
|
||||||
\end
|
\end
|
||||||
\import [all[shadows+tiddlers]tag[$:/tags/Macro/View]!has[draft.of]]
|
\import [all[shadows+tiddlers]tag[$:/tags/Macro/View]!has[draft.of]]
|
||||||
<$vars storyTiddler=<<currentTiddler>> tiddlerInfoState=<<qualify "$:/state/popup/tiddler-info">>><div data-tiddler-title=<<currentTiddler>> data-tags={{!!tags}} class={{{ tc-tiddler-frame tc-tiddler-view-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}><$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!has[draft.of]]" variable="listItem"><$transclude tiddler=<<listItem>>/></$list>
|
<$vars storyTiddler=<<currentTiddler>> tiddlerInfoState=<<qualify "$:/state/popup/tiddler-info">>><div data-tiddler-title=<<currentTiddler>> data-tags={{!!tags}} class={{{ tc-tiddler-frame tc-tiddler-view-frame [<currentTiddler>is[tiddler]then[tc-tiddler-exists]] [<currentTiddler>is[missing]!is[shadow]then[tc-tiddler-missing]] [<currentTiddler>is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [<currentTiddler>is[shadow]is[tiddler]then[tc-tiddler-overridden-shadow]] [<currentTiddler>is[system]then[tc-tiddler-system]] [{!!class}] [<currentTiddler>tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}><$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!has[draft.of]]" variable="listItem"><$transclude tiddler=<<listItem>>/></$list>
|
||||||
</div>
|
</div>
|
||||||
</$vars>
|
</$vars>
|
||||||
|
|
|
@ -16,7 +16,9 @@ $:/config/ViewToolbarButtons/Visibility/$(listItem)$
|
||||||
<$link>
|
<$link>
|
||||||
<$set name="foregroundColor" value={{!!color}}>
|
<$set name="foregroundColor" value={{!!color}}>
|
||||||
<span class="tc-tiddler-title-icon" style=<<title-styles>>>
|
<span class="tc-tiddler-title-icon" style=<<title-styles>>>
|
||||||
<$transclude tiddler={{!!icon}}/>
|
<$transclude tiddler={{!!icon}}>
|
||||||
|
<$transclude tiddler={{$:/config/DefaultTiddlerIcon}}/>
|
||||||
|
</$transclude>
|
||||||
</span>
|
</span>
|
||||||
</$set>
|
</$set>
|
||||||
<$list filter="[all[current]removeprefix[$:/]]">
|
<$list filter="[all[current]removeprefix[$:/]]">
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
title: $:/config/Server/ExternalFilters/[all[tiddlers]!is[system]sort[title]]
|
||||||
|
text: yes
|
|
@ -2,7 +2,7 @@ title: $:/core/macros/CSS
|
||||||
tags: $:/tags/Macro
|
tags: $:/tags/Macro
|
||||||
|
|
||||||
\define colour(name)
|
\define colour(name)
|
||||||
<$transclude tiddler={{$:/palette}} index="$name$"><$transclude tiddler="$:/palettes/Vanilla" index="$name$"/></$transclude>
|
<$transclude tiddler={{$:/palette}} index="$name$"><$transclude tiddler="$:/palettes/Vanilla" index="$name$"><$transclude tiddler="$:/config/DefaultColourMappings/$name$"/></$transclude></$transclude>
|
||||||
\end
|
\end
|
||||||
|
|
||||||
\define color(name)
|
\define color(name)
|
||||||
|
@ -60,7 +60,7 @@ column-count: $columns$;
|
||||||
\end
|
\end
|
||||||
|
|
||||||
\define datauri(title)
|
\define datauri(title)
|
||||||
<$macrocall $name="makedatauri" type={{$title$!!type}} text={{$title$}}/>
|
<$macrocall $name="makedatauri" type={{$title$!!type}} text={{$title$}} _canonical_uri={{$title$!!_canonical_uri}}/>
|
||||||
\end
|
\end
|
||||||
|
|
||||||
\define if-sidebar(text)
|
\define if-sidebar(text)
|
||||||
|
|
|
@ -26,7 +26,7 @@ tags: $:/tags/Macro
|
||||||
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
|
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
|
||||||
<$type$ class="$class$">
|
<$type$ class="$class$">
|
||||||
<$list filter="[list[$tiddler$!!$field$]]">
|
<$list filter="[list[$tiddler$!!$field$]]">
|
||||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="""$subtype$""">
|
<$droppable actions=<<list-links-draggable-drop-actions>> tag="""$subtype$""" enable=<<tv-enable-drag-and-drop>>>
|
||||||
<div class="tc-droppable-placeholder"/>
|
<div class="tc-droppable-placeholder"/>
|
||||||
<div>
|
<div>
|
||||||
<$transclude tiddler="""$itemTemplate$""">
|
<$transclude tiddler="""$itemTemplate$""">
|
||||||
|
@ -41,7 +41,7 @@ tags: $:/tags/Macro
|
||||||
</$list>
|
</$list>
|
||||||
</$type$>
|
</$type$>
|
||||||
<$tiddler tiddler="">
|
<$tiddler tiddler="">
|
||||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="div">
|
<$droppable actions=<<list-links-draggable-drop-actions>> tag="div" enable=<<tv-enable-drag-and-drop>>>
|
||||||
<div class="tc-droppable-placeholder">
|
<div class="tc-droppable-placeholder">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,13 +74,13 @@ tags: $:/tags/Macro
|
||||||
</$set>
|
</$set>
|
||||||
\end
|
\end
|
||||||
|
|
||||||
\define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div")
|
\define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div",storyview:"")
|
||||||
\whitespace trim
|
\whitespace trim
|
||||||
<span class="tc-tagged-draggable-list">
|
<span class="tc-tagged-draggable-list">
|
||||||
<$set name="tag" value=<<__tag__>>>
|
<$set name="tag" value=<<__tag__>>>
|
||||||
<$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>>>
|
<$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>> storyview=<<__storyview__>>>
|
||||||
<$elementTag$ class="tc-menu-list-item">
|
<$elementTag$ class="tc-menu-list-item">
|
||||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""">
|
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||||
<$elementTag$ class="tc-droppable-placeholder"/>
|
<$elementTag$ class="tc-droppable-placeholder"/>
|
||||||
<$elementTag$>
|
<$elementTag$>
|
||||||
<$transclude tiddler="""$itemTemplate$""">
|
<$transclude tiddler="""$itemTemplate$""">
|
||||||
|
@ -93,7 +93,7 @@ tags: $:/tags/Macro
|
||||||
</$elementTag$>
|
</$elementTag$>
|
||||||
</$list>
|
</$list>
|
||||||
<$tiddler tiddler="">
|
<$tiddler tiddler="">
|
||||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""">
|
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||||
<$elementTag$ class="tc-droppable-placeholder"/>
|
<$elementTag$ class="tc-droppable-placeholder"/>
|
||||||
<$elementTag$ style="height:0.5em;">
|
<$elementTag$ style="height:0.5em;">
|
||||||
</$elementTag$>
|
</$elementTag$>
|
||||||
|
|
|
@ -15,12 +15,13 @@ tags: $:/tags/Macro
|
||||||
\end
|
\end
|
||||||
|
|
||||||
\define tag-picker-inner()
|
\define tag-picker-inner()
|
||||||
|
\whitespace trim
|
||||||
<div class="tc-edit-add-tag">
|
<div class="tc-edit-add-tag">
|
||||||
<span class="tc-add-tag-name">
|
<span class="tc-add-tag-name">
|
||||||
<$keyboard key="ENTER" actions=<<add-tag-actions>>>
|
<$keyboard key="ENTER" actions=<<add-tag-actions>>>
|
||||||
<$edit-text tiddler=<<newTagNameTiddler>> tag="input" default="" placeholder={{$:/language/EditTemplate/Tags/Add/Placeholder}} focusPopup=<<qualify "$:/state/popup/tags-auto-complete">> class="tc-edit-texteditor tc-popup-handle" tabindex=<<tabIndex>> focus={{{ [{$:/config/AutoFocus}match[tags]then[true]] ~[[false]] }}}/>
|
<$edit-text tiddler=<<newTagNameTiddler>> tag="input" default="" placeholder={{$:/language/EditTemplate/Tags/Add/Placeholder}} focusPopup=<<qualify "$:/state/popup/tags-auto-complete">> class="tc-edit-texteditor tc-popup-handle" tabindex=<<tabIndex>> focus={{{ [{$:/config/AutoFocus}match[tags]then[true]] ~[[false]] }}}/>
|
||||||
</$keyboard>
|
</$keyboard>
|
||||||
</span> <$button popup=<<qualify "$:/state/popup/tags-auto-complete">> class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Tags/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Tags/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button> <span class="tc-add-tag-button">
|
</span> <$button popup=<<qualify "$:/state/popup/tags-auto-complete">> class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Tags/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Tags/Dropdown/Caption}}>{{$:/core/images/down-arrow}}</$button> <span class="tc-add-tag-button">
|
||||||
<$set name="tag" value={{{ [<newTagNameTiddler>get[text]] }}}>
|
<$set name="tag" value={{{ [<newTagNameTiddler>get[text]] }}}>
|
||||||
<$button set="$:/temp/NewTagName" setTo="" class="">
|
<$button set="$:/temp/NewTagName" setTo="" class="">
|
||||||
<<add-tag-actions>>
|
<<add-tag-actions>>
|
||||||
|
@ -49,6 +50,7 @@ tags: $:/tags/Macro
|
||||||
</div>
|
</div>
|
||||||
\end
|
\end
|
||||||
\define tag-picker()
|
\define tag-picker()
|
||||||
|
\whitespace trim
|
||||||
<$list filter="[<newTagNameTiddler>match[]]" emptyMessage=<<tag-picker-inner>>>
|
<$list filter="[<newTagNameTiddler>match[]]" emptyMessage=<<tag-picker-inner>>>
|
||||||
<$set name="newTagNameTiddler" value=<<qualify "$:/temp/NewTagName">>>
|
<$set name="newTagNameTiddler" value=<<qualify "$:/temp/NewTagName">>>
|
||||||
<<tag-picker-inner>>
|
<<tag-picker-inner>>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20130825162100000
|
created: 20130825162100000
|
||||||
modified: 20140814094907624
|
modified: 20200113094126878
|
||||||
tags: dev moduletypes
|
tags: dev moduletypes
|
||||||
title: SyncAdaptorModules
|
title: SyncAdaptorModules
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -14,6 +14,8 @@ SyncAdaptorModules encapsulate storage mechanisms that can be used by the SyncMe
|
||||||
|
|
||||||
SyncAdaptorModules are represented as JavaScript tiddlers with the field `module-type` set to `syncadaptor`.
|
SyncAdaptorModules are represented as JavaScript tiddlers with the field `module-type` set to `syncadaptor`.
|
||||||
|
|
||||||
|
See [[this pull request|https://github.com/Jermolene/TiddlyWiki5/pull/4373]] for background on the evolution of this API.
|
||||||
|
|
||||||
! Exports
|
! Exports
|
||||||
|
|
||||||
The following properties should be exposed via the `exports` object:
|
The following properties should be exposed via the `exports` object:
|
||||||
|
@ -25,7 +27,13 @@ Nothing should be exported if the adaptor detects that it isn't capable of opera
|
||||||
|
|
||||||
! Adaptor Module Methods
|
! Adaptor Module Methods
|
||||||
|
|
||||||
Adaptor modules must handle the following methods.
|
Adaptor modules must handle the methods described below.
|
||||||
|
|
||||||
|
!!! Error Handling
|
||||||
|
|
||||||
|
The syncadaptor must invoke the provided callback with the ''err'' parameter containing a string describing the error.
|
||||||
|
|
||||||
|
The syncer has special handling for connection errors. For backwards compatibilty reasons, the syncer identifies connection errors as the string comprised of the content of the tiddler $:/language/Error/XMLHttpRequest with the string ": 0" appended to the end. For example, in English, the string is "XMLHttpRequest error code: 0" and in Brazilian Portuguese it is "Código de erro XMLHttpRequest: 0".
|
||||||
|
|
||||||
!! `Constructor(options)`
|
!! `Constructor(options)`
|
||||||
|
|
||||||
|
@ -47,12 +55,21 @@ Gets the supplemental information that the adaptor needs to keep track of for a
|
||||||
|
|
||||||
Returns an object storing any additional information required by the adaptor.
|
Returns an object storing any additional information required by the adaptor.
|
||||||
|
|
||||||
|
!! `getTiddlerRevision(title)`
|
||||||
|
|
||||||
|
Gets the revision ID associated with the specified tiddler title.
|
||||||
|
|
||||||
|
|!Parameter |!Description |
|
||||||
|
|title |Tiddler title |
|
||||||
|
|
||||||
|
Returns a revision ID.
|
||||||
|
|
||||||
!! `getStatus(callback)`
|
!! `getStatus(callback)`
|
||||||
|
|
||||||
Retrieves status information from the server. This method is optional.
|
Retrieves status information from the server. This method is optional.
|
||||||
|
|
||||||
|!Parameter |!Description |
|
|!Parameter |!Description |
|
||||||
|callback |Callback function invoked with parameters `err,isLoggedIn,username` |
|
|callback |Callback function invoked with parameters `err,isLoggedIn,username,isReadOnly` |
|
||||||
|
|
||||||
!! `login(username,password,callback)`
|
!! `login(username,password,callback)`
|
||||||
|
|
||||||
|
@ -70,16 +87,39 @@ Attempts to logout of the server. This method is optional.
|
||||||
|!Parameter |!Description |
|
|!Parameter |!Description |
|
||||||
|callback |Callback function invoked with parameter `err` |
|
|callback |Callback function invoked with parameter `err` |
|
||||||
|
|
||||||
|
!! `getUpdatedTiddlers(syncer,callback)`
|
||||||
|
|
||||||
|
Retrieves the titles of tiddlers that need to be updated from the server.
|
||||||
|
|
||||||
|
This method is optional. If an adaptor doesn't implement it then synchronisation will be unidirectional from the TiddlyWiki store to the adaptor, but not the other way.
|
||||||
|
|
||||||
|
The syncer will use the `getUpdatedTiddlers()` method in preference to the `getSkinnyTiddlers()` method.
|
||||||
|
|
||||||
|
|!Parameter |!Description |
|
||||||
|
|syncer |Reference to the syncer object making the call |
|
||||||
|
|callback |Callback function invoked with parameter `err,data` -- see below |
|
||||||
|
|
||||||
|
The data provided by the callback is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
modifications: [<array of title>],
|
||||||
|
deletions: [<array of title>],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
!! `getSkinnyTiddlers(callback)`
|
!! `getSkinnyTiddlers(callback)`
|
||||||
|
|
||||||
Retrieves a list of skinny tiddlers from the server.
|
Retrieves a list of skinny tiddlers from the server.
|
||||||
|
|
||||||
This method is optional. If an adaptor doesn't implement it then synchronisation will be unidirectional from the TiddlyWiki store to the adaptor, but not the other way.
|
This method is optional. If an adaptor doesn't implement it then synchronisation will be unidirectional from the TiddlyWiki store to the adaptor, but not the other way.
|
||||||
|
|
||||||
|
The syncer will use the `getUpdatedTiddlers()` method in preference to the `getSkinnyTiddlers()` method.
|
||||||
|
|
||||||
|!Parameter |!Description |
|
|!Parameter |!Description |
|
||||||
|callback |Callback function invoked with parameter `err,tiddlers`, where `tiddlers` is an array of tiddler field objects |
|
|callback |Callback function invoked with parameter `err,tiddlers`, where `tiddlers` is an array of tiddler field objects |
|
||||||
|
|
||||||
!! `saveTiddler(tiddler,callback,tiddlerInfo)`
|
!! `saveTiddler(tiddler,callback)`
|
||||||
|
|
||||||
Saves a tiddler to the server.
|
Saves a tiddler to the server.
|
||||||
|
|
||||||
|
@ -96,11 +136,16 @@ Loads a tiddler from the server.
|
||||||
|title |Title of tiddler to be retrieved |
|
|title |Title of tiddler to be retrieved |
|
||||||
|callback |Callback function invoked with parameter `err,tiddlerFields` |
|
|callback |Callback function invoked with parameter `err,tiddlerFields` |
|
||||||
|
|
||||||
!! `deleteTiddler(title,callback,tiddlerInfo)`
|
!! `deleteTiddler(title,callback,options)`
|
||||||
|
|
||||||
Delete a tiddler from the server.
|
Delete a tiddler from the server.
|
||||||
|
|
||||||
|!Parameter |!Description |
|
|!Parameter |!Description |
|
||||||
|title |Title of tiddler to be deleted |
|
|title |Title of tiddler to be deleted |
|
||||||
|callback |Callback function invoked with parameter `err` |
|
|callback |Callback function invoked with parameter `err` |
|
||||||
|
|options |See below |
|
||||||
|
|
||||||
|
The options parameter contains the following properties:
|
||||||
|
|
||||||
|
|!Property |!Description |
|
||||||
|tiddlerInfo |The tiddlerInfo maintained by the syncer for this tiddler |
|
|tiddlerInfo |The tiddlerInfo maintained by the syncer for this tiddler |
|
||||||
|
|
|
@ -6,14 +6,9 @@ $(languagePluginTitle)$/icon
|
||||||
|
|
||||||
TiddlyWiki is currently available in <$count filter="[[$:/languages/en-GB]] [plugin-type[language]sort[title]] -[[$:/languages/de-AT]]"/> languages:
|
TiddlyWiki is currently available in <$count filter="[[$:/languages/en-GB]] [plugin-type[language]sort[title]] -[[$:/languages/de-AT]]"/> languages:
|
||||||
|
|
||||||
<div class="tc-drop-down-language-chooser" style="font-size: 10px; line-height: 12px; -moz-columns:2; -webkit-columns:2; columns:2;">
|
<div class="tc-drop-down-language-chooser" style="font-size: 7px; line-height: 8px; -moz-columns:6; -webkit-columns:6; columns:6;">
|
||||||
<$list filter="[[$:/languages/en-GB]] [plugin-type[language]sort[title]] -[[$:/languages/de-AT]]">
|
<$list filter="[[$:/languages/en-GB]] [plugin-type[language]sort[title]] -[[$:/languages/de-AT]]">
|
||||||
<div class="tc-language-list">
|
<div class="tc-language-list">
|
||||||
<span class="tc-language-list-greeting">
|
|
||||||
<$view subtiddler="$:/language/ControlPanel/Basics/Language/Prompt">
|
|
||||||
<$view tiddler="$:/language/ControlPanel/Basics/Language/Prompt"/>
|
|
||||||
</$view>
|
|
||||||
</span>
|
|
||||||
<span class="tc-language-list-name tc-image-button">
|
<span class="tc-language-list-name tc-image-button">
|
||||||
<$set name="languagePluginTitle" value=<<currentTiddler>>>
|
<$set name="languagePluginTitle" value=<<currentTiddler>>>
|
||||||
<$transclude subtiddler=<<flag-title>>>
|
<$transclude subtiddler=<<flag-title>>>
|
||||||
|
@ -27,6 +22,9 @@ TiddlyWiki is currently available in <$count filter="[[$:/languages/en-GB]] [plu
|
||||||
<$view field="title"/>
|
<$view field="title"/>
|
||||||
</$view>
|
</$view>
|
||||||
</$view>
|
</$view>
|
||||||
|
<$view subtiddler="$:/language/ControlPanel/Basics/Language/Prompt">
|
||||||
|
<$view tiddler="$:/language/ControlPanel/Basics/Language/Prompt"/>
|
||||||
|
</$view>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</$list>
|
</$list>
|
||||||
|
|
|
@ -19,7 +19,8 @@ html body .tc-subtitle {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tc-view-toolbar-tools {
|
.tc-view-toolbar-tools {
|
||||||
font-size: 0.7em;
|
font-size: 0.5em;
|
||||||
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tc-view-toolbar-tools p {
|
.tc-view-toolbar-tools p {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
caption: 5.1.22
|
caption: 5.1.22
|
||||||
created: 20190910152413608
|
created: 20200401124910939
|
||||||
modified: 20190910152413608
|
modified: 20200401124910939
|
||||||
tags: ReleaseNotes
|
tags: ReleaseNotes
|
||||||
title: Release 5.1.22
|
title: Release 5.1.22
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -9,6 +9,8 @@ type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
! Major Improvements
|
! Major Improvements
|
||||||
|
|
||||||
|
!! Dynamic Plugin Loading
|
||||||
|
|
||||||
Plugins [[can now|https://github.com/Jermolene/TiddlyWiki5/pull/4259]] be loaded or deleted dynamically, without requiring a reload -- as long as they don't contain any ~JavaScript modules. Plugins that require a reload are indicated in the plugin chooser in [[$:/ControlPanel]].
|
Plugins [[can now|https://github.com/Jermolene/TiddlyWiki5/pull/4259]] be loaded or deleted dynamically, without requiring a reload -- as long as they don't contain any ~JavaScript modules. Plugins that require a reload are indicated in the plugin chooser in [[$:/ControlPanel]].
|
||||||
|
|
||||||
The [[plugin listing|$:/core/ui/ControlPanel/Plugins]] and the [[plugin chooser|$:/core/ui/ControlPanel/Modals/AddPlugins]] in the control panel feature several improvements:
|
The [[plugin listing|$:/core/ui/ControlPanel/Plugins]] and the [[plugin chooser|$:/core/ui/ControlPanel/Modals/AddPlugins]] in the control panel feature several improvements:
|
||||||
|
@ -17,25 +19,97 @@ The [[plugin listing|$:/core/ui/ControlPanel/Plugins]] and the [[plugin chooser|
|
||||||
* Plugins may now declare dependencies that are automatically installed without user intervention
|
* Plugins may now declare dependencies that are automatically installed without user intervention
|
||||||
* All plugins now feature a concise, informal name (in addition to the description and title)
|
* All plugins now feature a concise, informal name (in addition to the description and title)
|
||||||
|
|
||||||
|
!! Client-Server Improvements
|
||||||
|
|
||||||
|
This release sees several improvements to the client-server configuration:
|
||||||
|
|
||||||
|
* Improved resilience to connection errors. Errors encountered by the browser are automatically retried, and when they succeed the associated error messages are automatically removed
|
||||||
|
* Changes to system tiddlers are now synchronised from the server to the browser
|
||||||
|
* Deletions of tiddlers on the server are now synchronised to the browser
|
||||||
|
* New "cloud" page control button with a dropdown menu showing status and additional actions:
|
||||||
|
** "Refresh from server" to force an immediate check for changes from the server
|
||||||
|
** "Logout"
|
||||||
|
** "Save snapshot for offline use" to save a copy of the wiki without the client-server components
|
||||||
|
** "Copy syncer logs to clipboard" to make it easier to ask end users for debugging information
|
||||||
|
* Added "filter" and "exclude" parameters to the [[GET /recipes/default/tiddlers.json|WebServer API: Get All Tiddlers]] endpoint to allow the returned tiddlers and fields to be filtered
|
||||||
|
* Enhanced LazyLoading to include skinny versions of tiddlers in the HTML file, and to avoid unnecessary loads from the server
|
||||||
|
* The official plugin library is now disabled when the tiddlyweb adaptor is active
|
||||||
|
* Internal file system details are no longer exposed to browsers when a file request fails (see https://github.com/Jermolene/TiddlyWiki5/issues/3724)
|
||||||
|
|
||||||
|
See the [[pull request on GitHub for more details|https://github.com/Jermolene/TiddlyWiki5/pull/4373]].
|
||||||
|
|
||||||
|
!! New Menu Bar Plugin
|
||||||
|
|
||||||
|
[[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ad2d4503e584ce9f931dbd1e98f95738b2841e51]] new ''menubar'' plugin that creates a menu bar that is responsive on mobile devices, and supports search, dropdowns and links
|
||||||
|
|
||||||
|
!! New Freelinks Plugin
|
||||||
|
|
||||||
|
[[Added|https://github.com/Jermolene/TiddlyWiki5/commit/447cd56da9db2ee169607f32923081ac47e78354]] new ''freelinks'' plugin to automatically create links from any tiddler title, without having to type double square brackets or use CamelCase.
|
||||||
|
|
||||||
|
!! New Dynannotate Plugin
|
||||||
|
|
||||||
|
[[Added|https://github.com/Jermolene/TiddlyWiki5/commit/9b48a1c82955d1050c426fef559f42f4b8ec56e7]] new ''dynannotate'' plugin containing primitives for overlaying annotations or highlights over other content
|
||||||
|
|
||||||
|
!! New Share Plugin
|
||||||
|
|
||||||
|
[[Added|https://github.com/Jermolene/TiddlyWiki5/commit/005c584d85505b9477169ef822752eed39dde66a]] new ''share'' plugin for sharing tiddlers via the URL location hash
|
||||||
|
|
||||||
|
!! Improved Markdown Plugin
|
||||||
|
|
||||||
|
[[Switched|https://github.com/Jermolene/TiddlyWiki5/pull/3876]] to the newer and improved [[remarkable|https://github.com/jonschlinkert/remarkable]] library
|
||||||
|
|
||||||
! Translation Improvements
|
! Translation Improvements
|
||||||
|
|
||||||
|
* Improved Chinese translation
|
||||||
* Improved Dutch translation
|
* Improved Dutch translation
|
||||||
|
* Improved Portuguese translation
|
||||||
|
|
||||||
|
! Performance Improvements
|
||||||
|
|
||||||
|
* [[Optimised|https://github.com/Jermolene/TiddlyWiki5/pull/4108]] ImportVariablesWidget to improve performance of global macros
|
||||||
|
* [[Optimised|https://github.com/Jermolene/TiddlyWiki5/commit/18f5333e01eb62ece65a86c457bf8a398f156930]] core SVG images to save approximately 100KB from the core plugin
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4421]] indexer for backlinks
|
||||||
|
|
||||||
! Usability Improvements
|
! Usability Improvements
|
||||||
|
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4238]] support for specifying the field that should receive focus when editing or creating a tiddler
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/96eca32b1105416c317101aa91dd284f835a8ba8]] download button for binary tiddlers
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/pull/4293]] ([[and here|https://github.com/Jermolene/TiddlyWiki5/pull/4296]]) spacing for page control buttons
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/pull/4293]] ([[and here|https://github.com/Jermolene/TiddlyWiki5/pull/4296]]) spacing for page control buttons
|
||||||
* [[Improved|https://github.com/Jermolene/TiddlyWiki5/commit/9395d7567179c436d0e8ac26fc976d717eae7f50]] display of icons in documentation
|
* [[Improved|https://github.com/Jermolene/TiddlyWiki5/commit/9395d7567179c436d0e8ac26fc976d717eae7f50]] display of icons in documentation
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4238]] support in control panel for specifying the field that should receive focus when editing or creating a tiddler
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4314]] support in control panel for specifying the tags for new tiddlers and journals
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4314]] support in control panel for specifying the tags for new tiddlers and journals
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/05a3e1ad36c2fb383dd4975a578f95e8a6c3f325]] language attribute to HTML document to improve accessibility
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/pull/3943]] scaling of embedded videos
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4361]] "none" option in icon dropdown in $:/TagManager
|
||||||
|
* Fixed a number of layout issues relating to the edit template and tag picker (see [[18151cc1|https://github.com/Jermolene/TiddlyWiki5/commit/18151cc193e8c0d613663d3ecf6437c63e540c2d]], [[793d84bc|https://github.com/Jermolene/TiddlyWiki5/commit/793d84bcb1bc52ee77c49090268dd242017cdaa9]], [[1cf2d079|https://github.com/Jermolene/TiddlyWiki5/commit/1cf2d0799d7027d58f4bdca857bc342dd778a330]], [[3c365a25|https://github.com/Jermolene/TiddlyWiki5/commit/3c365a2567ebfe12d78b0aed77a40969cd38563e]], [[45def4de|https://github.com/Jermolene/TiddlyWiki5/commit/45def4def441554a06c3e17742adc29a17d2a13c]], [[89728d8a|https://github.com/Jermolene/TiddlyWiki5/commit/89728d8a9a9fd9b888deb78f420dd5758ee63909]], [[4cf96e73|https://github.com/Jermolene/TiddlyWiki5/commit/4cf96e7339b3b7d8e84a5b73d9871cdad50c5a84]], [[bfa3ddd0|https://github.com/Jermolene/TiddlyWiki5/commit/bfa3ddd077404ad4627b9161bcadf3d5a539c1dc]] and [[74172b35|https://github.com/Jermolene/TiddlyWiki5/commit/74172b35ceae84f254e20d6d7ec4dff2cca692b1]])
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4494]] new ''Gruvbox Dark'' palette
|
||||||
|
* [[Improved|https://github.com/Jermolene/TiddlyWiki5/pull/4515]] (and [[https://github.com/Jermolene/TiddlyWiki5/pull/4517|here]])) Vanilla and Nord palettes
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/152125f53b9979bfd5511973038b4358d1ef48c4]] Railroad plugin to use standard palette colours
|
||||||
|
* [[Improved|https://github.com/Jermolene/TiddlyWiki5/commit/16c1cbee292604b2b8be36a15d5828893d132b20]] Comment plugin to add a link on each comment to the original post
|
||||||
|
|
||||||
! Hackability Improvements
|
! Hackability Improvements
|
||||||
|
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4491]] new saver for https://gitea.io/
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/24d2804799b09278c4bb83918d8b75dfa49dbed4]] new ActionPopupWidget for triggering popups
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4442]] ''tabindex'' attribute to the ButtonWidget
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/bfbd886a8bbf2f6ce147ea526eec2098357d3ae7]] ''index'' attribute to the RangeWidget enabling it to be used with DataTiddlers
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/6089c4de2921df0f76f605f1830fb2c04548f73c]] support for RefreshThrottling via the `throttle.refresh` field
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/6089c4de2921df0f76f605f1830fb2c04548f73c]] support for RefreshThrottling via the `throttle.refresh` field
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/1150c87edb7478af6cc943eb0ef52fdf3051c121]] (and [[here|https://github.com/Jermolene/TiddlyWiki5/commit/8c894612914e21cf941a1daa953538c28ce91d8e]]) new `[is[binary]]` operand for the [[is Operator]]
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/1150c87edb7478af6cc943eb0ef52fdf3051c121]] (and [[here|https://github.com/Jermolene/TiddlyWiki5/commit/8c894612914e21cf941a1daa953538c28ce91d8e]]) new `[is[binary]]` operand for the [[is Operator]]
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/68163684a2e57108e160295e445c194268b873c5]] usage of `publishFilter` in save templates -- see SavingMechanism
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/68163684a2e57108e160295e445c194268b873c5]] usage of `publishFilter` in save templates -- see SavingMechanism
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/89716bb81d68b9c68d3c0fd2f2de96afad1b086a]] CSS class identifying the tiddler body editor
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/89716bb81d68b9c68d3c0fd2f2de96afad1b086a]] CSS class identifying the tiddler body editor
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/e9211b0eee8e0b081f9f1597bde6673bf4c55d5c]] CSS classes to identify sidebar tabsets
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/e9211b0eee8e0b081f9f1597bde6673bf4c55d5c]] CSS classes to identify sidebar tabsets
|
||||||
* [[Updated|https://github.com/Jermolene/TiddlyWiki5/pull/4208]] normalize.css from v3.0.0 to v8.0.1
|
* [[Updated|https://github.com/Jermolene/TiddlyWiki5/pull/4208]] normalize.css from v3.0.0 to v8.0.1
|
||||||
|
* [[Updated|https://github.com/Jermolene/TiddlyWiki5/pull/4502]] Highlight plugin to use highlight.js v9.18.1
|
||||||
|
* [[Updated|https://github.com/Jermolene/TiddlyWiki5/pull/4226]] Jasmine to v3
|
||||||
|
* [[Refactored|https://github.com/Jermolene/TiddlyWiki5/commit/7b66df688ae745537929a25799ef4a72d4437fcd]] AndTidWiki saver
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/5a5c967a3943beb6a4fa513cb34d231e46304452]] new [[SystemTag: $:/tags/Macro/View]] for creating macros that are only available within the view template
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4404]] support for embedding `.webm` and `.ogg` video files
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/3df1f9c9d0cc92b596262c0220ecf529c7fbb858]] ''spaces'' parameter to [[jsontiddlers Macro]] for controlling the formatting of the output
|
||||||
|
* [[Updated|https://github.com/Jermolene/TiddlyWiki5/commit/4afde5a722afc91c826305800ba536c5fe8ef2e5]] the [[colour Macro]] to add support for specifying a fallback for colours not in the current colour palette
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/6091b013864af3d9918df69894f4aa05d1b8ffeb]] new [[Hidden Setting: Default Tiddler Icon]]
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/bc687e57834efa312cca126af222613ef241c585]] new [[Hidden Setting: Disable Drag and Drop]]
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ae9ce4f01c6048aeb5604a93b57c2f3e4f959162]] new ''tc-tiddler-overridden-shadow'' class to tiddler frames for tiddlers that override a shadow tiddler
|
||||||
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/pull/4490]] support for a custom class to modal wrapper
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/091bcfce7d1a9c09140992e649f41ae17d27f307]] [[datauri Macro]] to work with tiddlers with a ''_canonical_uri'' field
|
||||||
|
|
||||||
! Bug Fixes
|
! Bug Fixes
|
||||||
|
|
||||||
|
@ -44,12 +118,36 @@ The [[plugin listing|$:/core/ui/ControlPanel/Plugins]] and the [[plugin chooser|
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/issues/4237]] bug with permaview button when placed above the search box
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/issues/4237]] bug with permaview button when placed above the search box
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/a054d100e73db95071299e92c4321c2aa8e42382]] usage of ''count'' parameter of [[WidgetMessage: tm-edit-text-operation]]
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/a054d100e73db95071299e92c4321c2aa8e42382]] usage of ''count'' parameter of [[WidgetMessage: tm-edit-text-operation]]
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/issues/4284]] bug with toc-selective-expandable macro still showing disclosure arrow despite excluding tiddlers
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/issues/4284]] bug with toc-selective-expandable macro still showing disclosure arrow despite excluding tiddlers
|
||||||
* [[Removed|https://github.com/Jermolene/TiddlyWiki5/commit/81f1e6af4e5920c6ff41e7f08171bfddc1b26dfc]] erroneous word break setting for vertical tabs
|
* [[Removed|https://github.com/Jermolene/TiddlyWiki5/commit/81f1e6af4e5920c6ff41e7f08171bfddc1b26dfc]] (and [[here|https://github.com/Jermolene/TiddlyWiki5/commit/46c90af308015242fa0314d85f1524727e2aa7e5]]) erroneous word break setting for vertical tabs
|
||||||
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/d30eacc6520971c95bdabf24f4c4122534d9414a]] problem with programmatically deselecting entries from the SelectWidget in multiple selection mode
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/d30eacc6520971c95bdabf24f4c4122534d9414a]] problem with programmatically deselecting entries from the SelectWidget in multiple selection mode
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/pull/4333]] usage of spans in colour picker
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/e84c422e5091c02f55db4027faa9ba840e2aee6c]] refreshing of RangeWidget when underlying value changes
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/e4eaae14faa1bf867c0f8168e221cf30ac6e2e0d]] problem with splash screen being shown when JavaScript is disabled
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/8f3da69f818940eb5f517da850fb3766b72c7d7d]] problem with millisecond 0XXX DateFormat
|
||||||
|
* [[Improve|https://github.com/Jermolene/TiddlyWiki5/commit/174a36cda63127d19230bcfbe9a5fdde46e5b0ea]] compatibility of single tiddler window vs. main window
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/015306dfc9099f4d5d9415b64266d328a154b119]] problems with some core icons in Internet Explorer
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/3eacdc19fdb4ed7ce864a04dd87a5e1c6492daac]] GitHub and GitLab savers to use default path of `/` if none specified
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/pull/4432]] support for embedding ZIP files in some environments
|
||||||
|
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/665b63ec38b75dfe62009d2f5514682de60e953f]] lack of refresh when ButtonWidget ''actions'' attribute changes
|
||||||
|
|
||||||
! Contributors
|
! Contributors
|
||||||
|
|
||||||
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
|
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
|
||||||
|
|
||||||
|
* [[@activescott|https://github.com/activescott]]
|
||||||
|
* [[@BramChen|https://github.com/BramChen]]
|
||||||
|
* [[@BurningTreeC|https://github.com/BurningTreeC]]
|
||||||
|
* [[@donmor|https://github.com/donmor]]
|
||||||
|
* [[@ento|https://github.com/ento]]
|
||||||
|
* [[@flibbles|https://github.com/flibbles]]
|
||||||
* [[@gernert|https://github.com/gernert]]
|
* [[@gernert|https://github.com/gernert]]
|
||||||
|
* [[@heronils|https://github.com/heronils]]
|
||||||
|
* [[@hoelzro|https://github.com/hoelzro]]
|
||||||
* [[@Janno|https://github.com/Janno]]
|
* [[@Janno|https://github.com/Janno]]
|
||||||
|
* [[@joshuafontany|https://github.com/joshuafontany]]
|
||||||
|
* [[@m42e|https://github.com/m42e]]
|
||||||
|
* [[@MidnightLightning|https://github.com/MidnightLightning]]
|
||||||
|
* [[@pmario|https://github.com/pmario]]
|
||||||
|
* [[@s-l-lee|https://github.com/s-l-lee]]
|
||||||
|
* [[@s-light|https://github.com/s-light]]
|
||||||
|
* [[@saqimtiaz|https://github.com/saqimtiaz]]
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
title: $:/themes/tiddlywiki/vanilla/options/stickytitles
|
title: $:/themes/tiddlywiki/vanilla/options/stickytitles
|
||||||
text: yes
|
text: no
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
"tiddlywiki/powered-by-tiddlywiki",
|
"tiddlywiki/powered-by-tiddlywiki",
|
||||||
"tiddlywiki/internals",
|
"tiddlywiki/internals",
|
||||||
"tiddlywiki/highlight",
|
"tiddlywiki/highlight",
|
||||||
"tiddlywiki/markdown",
|
|
||||||
"tiddlywiki/bibtex",
|
"tiddlywiki/bibtex",
|
||||||
"tiddlywiki/savetrail",
|
"tiddlywiki/savetrail",
|
||||||
"tiddlywiki/external-attachments",
|
"tiddlywiki/external-attachments",
|
||||||
"tiddlywiki/dynaview",
|
"tiddlywiki/dynaview",
|
||||||
|
"tiddlywiki/dynannotate",
|
||||||
"tiddlywiki/codemirror",
|
"tiddlywiki/codemirror",
|
||||||
"tiddlywiki/comments",
|
"tiddlywiki/comments",
|
||||||
"tiddlywiki/browser-storage"
|
"tiddlywiki/menubar"
|
||||||
],
|
],
|
||||||
"themes": [
|
"themes": [
|
||||||
"tiddlywiki/vanilla",
|
"tiddlywiki/vanilla",
|
||||||
|
|
|
@ -12,11 +12,6 @@
|
||||||
"build": {
|
"build": {
|
||||||
"index": [
|
"index": [
|
||||||
"--rendertiddler","$:/plugins/tiddlywiki/tiddlyweb/save/offline","index.html","text/plain"],
|
"--rendertiddler","$:/plugins/tiddlywiki/tiddlyweb/save/offline","index.html","text/plain"],
|
||||||
"externalimages": [
|
|
||||||
"--savetiddlers","[is[image]]","images",
|
|
||||||
"--setfield","[is[image]]","_canonical_uri","$:/core/templates/canonical-uri-external-image","text/plain",
|
|
||||||
"--setfield","[is[image]]","text","","text/plain",
|
|
||||||
"--rendertiddler","$:/plugins/tiddlywiki/tiddlyweb/save/offline","externalimages.html","text/plain"],
|
|
||||||
"static": [
|
"static": [
|
||||||
"--rendertiddler","$:/core/templates/static.template.html","static.html","text/plain",
|
"--rendertiddler","$:/core/templates/static.template.html","static.html","text/plain",
|
||||||
"--rendertiddler","$:/core/templates/alltiddlers.template.html","alltiddlers.html","text/plain",
|
"--rendertiddler","$:/core/templates/alltiddlers.template.html","alltiddlers.html","text/plain",
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"description": "Sharing tiddlers via URLs",
|
||||||
|
"plugins": [
|
||||||
|
"tiddlywiki/share"
|
||||||
|
],
|
||||||
|
"themes": [
|
||||||
|
"tiddlywiki/vanilla",
|
||||||
|
"tiddlywiki/snowwhite"
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"share": [
|
||||||
|
"--rendertiddler","$:/core/save/all","share.html","text/plain"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*\
|
||||||
|
title: test-backlinks.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Tests the backlinks mechanism.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe('Backlinks tests', function() {
|
||||||
|
describe('a tiddler with no links to it', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
it('should have no backlinks', function() {
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A tiddler added to the wiki with a link to it', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'A link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
it('should have a backlink', function() {
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A tiddler that has a link added to it later', function() {
|
||||||
|
it('should have an additional backlink', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'A link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing2',
|
||||||
|
text: 'Nothing yet!'});
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing2',
|
||||||
|
text: 'Updated with link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing,TestOutgoing2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A tiddler that has a link remove from it later', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'A link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
it('should have one fewer backlink', function() {
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'No link to ~TestIncoming'});
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A tiddler linking to another that gets renamed', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'A link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
it('should have its name changed in the backlinks', function() {
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||||
|
|
||||||
|
wiki.renameTiddler('TestOutgoing', 'TestExtroverted');
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestExtroverted');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A tiddler linking to another that gets deleted', function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestIncoming',
|
||||||
|
text: ''});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: 'TestOutgoing',
|
||||||
|
text: 'A link to [[TestIncoming]]'});
|
||||||
|
|
||||||
|
it('should be removed from backlinks', function() {
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||||
|
|
||||||
|
wiki.deleteTiddler('TestOutgoing');
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
|
@ -8,8 +8,10 @@ Tests the filtering mechanism.
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
/* jslint node: true, browser: true */
|
||||||
/*global $tw: false, describe: false, it: false, expect: false, require: false*/
|
/* eslint-env node, browser, jasmine */
|
||||||
|
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
|
||||||
|
/* global $tw, require */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
describe("Filter tests", function() {
|
describe("Filter tests", function() {
|
||||||
|
@ -421,13 +423,8 @@ function runTests(wiki) {
|
||||||
|
|
||||||
var widget = require("$:/core/modules/widgets/widget.js");
|
var widget = require("$:/core/modules/widgets/widget.js");
|
||||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||||
var rootWidget = new widget.widget({
|
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
|
||||||
type: "widget",
|
{ wiki:wiki, document:$tw.document});
|
||||||
children: [{type: "widget", children: []}]
|
|
||||||
},{
|
|
||||||
wiki: wiki,
|
|
||||||
document: $tw.document
|
|
||||||
});
|
|
||||||
rootWidget.makeChildWidgets();
|
rootWidget.makeChildWidgets();
|
||||||
var anchorWidget = rootWidget.children[0];
|
var anchorWidget = rootWidget.children[0];
|
||||||
rootWidget.setVariable("myVar","Tidd");
|
rootWidget.setVariable("myVar","Tidd");
|
||||||
|
@ -470,6 +467,8 @@ function runTests(wiki) {
|
||||||
expect(wiki.filterTiddlers("1 2 3 4 +[min[2]]").join(",")).toBe("1,2,2,2");
|
expect(wiki.filterTiddlers("1 2 3 4 +[min[2]]").join(",")).toBe("1,2,2,2");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* listops filters */
|
||||||
|
|
||||||
it("should handle the allafter operator", function() {
|
it("should handle the allafter operator", function() {
|
||||||
expect(wiki.filterTiddlers("1 2 3 4 +[allafter[]]").join(",")).toBe("");
|
expect(wiki.filterTiddlers("1 2 3 4 +[allafter[]]").join(",")).toBe("");
|
||||||
expect(wiki.filterTiddlers("1 2 3 4 +[allafter:include[]]").join(",")).toBe("");
|
expect(wiki.filterTiddlers("1 2 3 4 +[allafter:include[]]").join(",")).toBe("");
|
||||||
|
@ -500,6 +499,164 @@ function runTests(wiki) {
|
||||||
expect(wiki.filterTiddlers("1 2 3 4 +[allbefore:include[5]]").join(",")).toBe("");
|
expect(wiki.filterTiddlers("1 2 3 4 +[allbefore:include[5]]").join(",")).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should handle the append operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[append[d e]]").join(",")).toBe("a,b,c,d,e");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[append:1[d e]]").join(",")).toBe("a,b,c,d");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[append{TiddlerSeventh!!list}]").join(",")).toBe("a,b,c,TiddlerOne,Tiddler Three,a fourth tiddler,MissingTiddler");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[append:2{TiddlerSeventh!!list}]").join(",")).toBe("a,b,c,TiddlerOne,Tiddler Three");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a [[b c]] +[append{TiddlerSix!!filter}]").join(",")).toBe("a,b c,one,a a,[subfilter{hasList!!list}]");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the insertbefore operator", function() {
|
||||||
|
var widget = require("$:/core/modules/widgets/widget.js");
|
||||||
|
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
|
||||||
|
{ wiki:wiki, document:$tw.document});
|
||||||
|
rootWidget.makeChildWidgets();
|
||||||
|
var anchorWidget = rootWidget.children[0];
|
||||||
|
rootWidget.setVariable("myVar","c");
|
||||||
|
rootWidget.setVariable("tidTitle","e");
|
||||||
|
rootWidget.setVariable("tidList","one tid with spaces");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c d e f +[insertbefore:myVar[f]]",anchorWidget).join(",")).toBe("a,b,f,c,d,e");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e f +[insertbefore:myVar<tidTitle>]",anchorWidget).join(",")).toBe("a,b,e,c,d,f");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e f +[insertbefore:myVar[gg gg]]",anchorWidget).join(",")).toBe("a,b,gg gg,c,d,e,f");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[insertbefore:myVar<tidList>]",anchorWidget).join(",")).toBe("a,b,one tid with spaces,c,d,e");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e f +[insertbefore:tidTitle{TiddlerOne!!tags}]",anchorWidget).join(",")).toBe("a,b,c,d,one,e,f");
|
||||||
|
|
||||||
|
// Next 2 tests do weired things, but will pass - there for compatibility reasons
|
||||||
|
expect(wiki.filterTiddlers("a b c [[with space]] +[insertbefore[b]]").join(",")).toBe("a,c,with space,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[insertbefore:2[b]]").join(",")).toBe("a,c,d,e,b");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the move operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[move[c]]").join(",")).toBe("a,b,d,c,e");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[move:2[c]]").join(",")).toBe("a,b,d,e,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[move:10[c]]").join(",")).toBe("a,b,d,e,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[move:-1[c]]").join(",")).toBe("a,c,b,d,e");
|
||||||
|
expect(wiki.filterTiddlers("a b c d e +[move:-5[c]]").join(",")).toBe("c,a,b,d,e");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the prepend operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend[dd ee]]").join(",")).toBe("dd,ee,a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:3[ff gg]]").join(",")).toBe("ff,gg,a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:1[hh ii]]").join(",")).toBe("hh,a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:0[jj kk]]").join(",")).toBe("a,b,c");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:-0[ll mm]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:-1[nn oo pp qq]]").join(",")).toBe("nn,oo,pp,a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:-2[rr ss tt uu]]").join(",")).toBe("rr,ss,a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:-4[rr ss tt uu]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[prepend:-5[vv ww xx yy]]").join(",")).toBe("a,b,c");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the putafter operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter[b]]").join(",")).toBe("a,b,ee,c,dd");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter:1[b]]").join(",")).toBe("a,b,ee,c,dd");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter:2[b]]").join(",")).toBe("a,b,dd,ee,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter:3[b]]").join(",")).toBe("a,b,c,dd,ee");
|
||||||
|
// It starts to duplicate elements
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter:4[b]]").join(",")).toBe("a,b,b,c,dd,ee");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[putafter:5[b]]").join(",")).toBe("a,b,a,b,c,dd,ee");
|
||||||
|
// There are only 5 elements in the input
|
||||||
|
expect(wiki.filterTiddlers("a b c ff gg +[putafter:6[b]]").join(",")).toBe("a,b,a,b,c,ff,gg");
|
||||||
|
|
||||||
|
// -1 starts to "eat" elements for the left and duplicates b
|
||||||
|
expect(wiki.filterTiddlers("a b c hh ii +[putafter:-1[b]]").join(",")).toBe("a,b,b,c,hh,ii");
|
||||||
|
// -2 moves c, hh, ii behind b, which is not visible
|
||||||
|
expect(wiki.filterTiddlers("a b c hh ii +[putafter:-2[b]]").join(",")).toBe("a,b,c,hh,ii");
|
||||||
|
// only ii is used from input and it's moved behind b
|
||||||
|
expect(wiki.filterTiddlers("a b c hh ii +[putafter:-4[b]]").join(",")).toBe("a,b,ii,c,hh");
|
||||||
|
// wasting time, because there are only 5 elements
|
||||||
|
expect(wiki.filterTiddlers("a b c hh ii +[putafter:-5[b]]").join(",")).toBe("a,b,c,hh,ii");
|
||||||
|
// there are only 5 elements
|
||||||
|
expect(wiki.filterTiddlers("a b c hh ii +[putafter:-10[b]]").join(",")).toBe("a,b,c,hh,ii");
|
||||||
|
|
||||||
|
// use NAN uses default = 1
|
||||||
|
expect(wiki.filterTiddlers("a b c jj kk +[putafter:NAN[b]]").join(",")).toBe("a,b,kk,c,jj");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the putbefore operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c dd +[putbefore[b]]").join(",")).toBe("a,dd,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c ff +[putbefore:1[b]]").join(",")).toBe("a,ff,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c gg +[putbefore:2[b]]").join(",")).toBe("a,c,gg,b");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c [[g g]] +[putbefore:2[b]]").join(",")).toBe("a,c,g g,b");
|
||||||
|
|
||||||
|
// this one is strange
|
||||||
|
expect(wiki.filterTiddlers("a b c ee +[putbefore:0[b]]").join(",")).toBe("a,a,b,c,ee");
|
||||||
|
|
||||||
|
// b is not part of the list anymore, so it will be appended at the end ???
|
||||||
|
expect(wiki.filterTiddlers("a b c hh +[putbefore:3[b]]").join(",")).toBe("a,b,c,hh");
|
||||||
|
expect(wiki.filterTiddlers("a b c ii +[putbefore:4[b]]").join(",")).toBe("a,a,b,c,ii");
|
||||||
|
|
||||||
|
// ????
|
||||||
|
expect(wiki.filterTiddlers("a b c ii +[putbefore:10[b]]").join(",")).toBe("a,a,b,c,ii");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c kk +[putbefore:-1[b]]").join(",")).toBe("a,b,c,kk");
|
||||||
|
expect(wiki.filterTiddlers("a b c ll +[putbefore:-2[b]]").join(",")).toBe("a,c,ll,b");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c mm +[putbefore:-3[b]]").join(",")).toBe("a,mm,b,c");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("a b c nn +[putbefore:-10[b]]").join(",")).toBe("a,b,c,nn");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the putfirst operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst[a b]]").join(",")).toBe("c,a,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst[]]").join(",")).toBe("c,a,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:2[]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:3[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:4[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:-0[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:-1[]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:-2[]]").join(",")).toBe("c,a,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putfirst:-4[]]").join(",")).toBe("a,b,c");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the putlast operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast[d e]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast[]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:1[]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:2[]]").join(",")).toBe("c,a,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:3[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:4[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:-0[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:0[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:-1[]]").join(",")).toBe("c,a,b");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:-2[]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[putlast:-4[]]").join(",")).toBe("a,b,c");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the remove operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[remove[d e]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[remove[a]]").join(",")).toBe("b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[remove[c b a]]").join(",")).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the replace operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c dd +[replace[a]]").join(",")).toBe("dd,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[replace:2[a]]").join(",")).toBe("dd,ee,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[replace:5[c]]").join(",")).toBe("a,b,a,b,c,dd,ee");
|
||||||
|
|
||||||
|
// strange things happen.
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[replace:-1[c]]").join(",")).toBe("a,b,b,c,dd,ee");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[replace:-2[c]]").join(",")).toBe("a,b,c,dd,ee");
|
||||||
|
expect(wiki.filterTiddlers("a b c dd ee +[replace:-2[ee]]").join(",")).toBe("a,b,c,dd,c,dd,ee");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle the sortby operator", function() {
|
||||||
|
expect(wiki.filterTiddlers("a b c +[sortby[d e]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("a b c +[sortby[b c a]]").join(",")).toBe("b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("aa a b c +[sortby[b c a cc]]").join(",")).toBe("aa,b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a bb b c +[sortby[b c a cc]]").join(",")).toBe("bb,b,c,a");
|
||||||
|
expect(wiki.filterTiddlers("a bb cc b c +[sortby[b c a cc]]").join(",")).toBe("bb,b,c,a,cc");
|
||||||
|
|
||||||
|
expect(wiki.filterTiddlers("b a b c +[sortby[]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("b a b c +[sortby[a b b c]]").join(",")).toBe("a,b,c");
|
||||||
|
expect(wiki.filterTiddlers("b a b c +[sortby[b a c b]]").join(",")).toBe("b,a,c");
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -465,6 +465,90 @@ describe("Widget module", function() {
|
||||||
expect(wrapper.innerHTML).toBe("<p>nothing</p>");
|
expect(wrapper.innerHTML).toBe("<p>nothing</p>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**This test confirms that imported set variables properly refresh
|
||||||
|
* if they use transclusion for their value. This relates to PR #4108.
|
||||||
|
*/
|
||||||
|
it("should refresh imported <$set> widgets", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
// Add some tiddlers
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "Raw", text: "Initial value"},
|
||||||
|
{title: "Macro", text: "<$set name='test' value={{Raw}}>\n\ndummy text</$set>"},
|
||||||
|
{title: "Caller", text: text}
|
||||||
|
]);
|
||||||
|
var text = "\\import Macro\n<<test>>";
|
||||||
|
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<p>Initial value</p>");
|
||||||
|
wiki.addTiddler({title: "Raw", text: "New value"});
|
||||||
|
// Refresh
|
||||||
|
refreshWidgetNode(widgetNode,wrapper,["Raw"]);
|
||||||
|
expect(wrapper.innerHTML).toBe("<p>New value</p>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should can mix setWidgets and macros when importing", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
// Add some tiddlers
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "A", text: "\\define A() Aval"},
|
||||||
|
{title: "B", text: "<$set name='B' value='Bval'>\n\ndummy text</$set>"},
|
||||||
|
{title: "C", text: "\\define C() Cval"}
|
||||||
|
]);
|
||||||
|
var text = "\\import A B C\n<<A>> <<B>> <<C>>";
|
||||||
|
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<p>Aval Bval Cval</p>");
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Special case. <$importvariables> has different handling if
|
||||||
|
* it doesn't end up importing any variables. Make sure it
|
||||||
|
* doesn't forget its childrenNodes.
|
||||||
|
*/
|
||||||
|
it("should work when import widget imports nothing", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
var text = "\\import [prefix[XXX]]\nDon't forget me.";
|
||||||
|
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<p>Don't forget me.</p>");
|
||||||
|
});
|
||||||
|
|
||||||
|
/** This test reproduces issue #4504.
|
||||||
|
*
|
||||||
|
* The importvariable widget was creating redundant copies into
|
||||||
|
* itself of variables in widgets higher up in the tree. Normally,
|
||||||
|
* this caused no errors, but it would mess up qualify-macros.
|
||||||
|
* They would find multiple instances of the same transclusion
|
||||||
|
* variable if a transclusion occured higher up in the widget tree
|
||||||
|
* than an importvariables AND that importvariables was importing
|
||||||
|
* at least ONE variable.
|
||||||
|
*/
|
||||||
|
it("adding imported variables doesn't change qualifyers", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
function wikiparse(text) {
|
||||||
|
var tree = parseText(text,wiki);
|
||||||
|
var widgetNode = createWidgetNode(tree,wiki);
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
return wrapper.innerHTML;
|
||||||
|
};
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "body", text: "\\import A\n<<qualify this>>"},
|
||||||
|
{title: "A", text: "\\define unused() ignored"}
|
||||||
|
]);
|
||||||
|
// This transclude wraps "body", which has an
|
||||||
|
// importvariable widget in it.
|
||||||
|
var withA = wikiparse("{{body}}");
|
||||||
|
wiki.addTiddler({title: "A", text: ""});
|
||||||
|
var withoutA = wikiparse("{{body}}");
|
||||||
|
// Importing two different version of "A" shouldn't cause
|
||||||
|
// the <<qualify>> widget to spit out something different.
|
||||||
|
expect(withA).toBe(withoutA);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
created: 20191004112211823
|
||||||
|
list: [[WidgetMessage: tm-unload-plugin-library]] [[WidgetMessage: tm-load-plugin-library]] [[WidgetMessage: tm-load-plugin-from-library]] HelloThere GettingStarted Community
|
||||||
|
modified: 20191004113621710
|
||||||
|
title: $:/StoryList
|
||||||
|
type: text/vnd.tiddlywiki
|
|
@ -6,14 +6,14 @@ tags: About
|
||||||
|
|
||||||
TiddlyWiki is published under a [[permissive BSD 3-Clause License|https://opensource.org/licenses/BSD-3-Clause]] stored in the [[shadow tiddler|ShadowTiddlers]] [[$:/core/copyright.txt]]:
|
TiddlyWiki is published under a [[permissive BSD 3-Clause License|https://opensource.org/licenses/BSD-3-Clause]] stored in the [[shadow tiddler|ShadowTiddlers]] [[$:/core/copyright.txt]]:
|
||||||
|
|
||||||
<div style="font-size: 0.5em;line-height:1.4;">
|
<div style="font-size: 0.7em;line-height:1.4;">
|
||||||
|
|
||||||
{{$:/core/copyright.txt}}
|
{{$:/core/copyright.txt}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
In layman's terms, the license says that you can take TiddlyWiki and do anything you want with it without any license fee payment or other legal obligation to the creators of TiddlyWiki or anyone else. The quid pro quo is that there is no warranty or guarantee with open source projects like TiddlyWiki. You can't sue the contributors to TiddlyWiki for any loss or damage due to the use of TiddlyWiki: even if your data is lost due to a tragic chain of circumstances that involves TiddlyWiki.
|
In layman's terms, the license says that you can take TiddlyWiki and do anything you want with it without any license fee payment or other legal obligation to the creators of ~TiddlyWiki or anyone else. The quid pro quo is that there is no warranty or guarantee with open source projects like ~TiddlyWiki. You can't sue the contributors to ~TiddlyWiki for any loss or damage due to the use of ~TiddlyWiki: even if your data is lost due to a tragic chain of circumstances that involves ~TiddlyWiki.
|
||||||
|
|
||||||
You are respectfully requested that to make an attribution to the project, but there's no obligation to do so.
|
You are respectfully requested that to make an attribution to the project, but there's no obligation to do so.
|
||||||
|
|
||||||
For the avoidance of doubt, any information that you choose to store within your own copy of TiddlyWiki remains yours; using TiddlyWiki to publish content doesn't change whatever rights you may have to that content.
|
For the avoidance of doubt, any information that you choose to store within your own copy of ~TiddlyWiki remains yours; using ~TiddlyWiki to publish content doesn't change whatever rights you may have to that content.
|
||||||
|
|
|
@ -13,7 +13,8 @@ The ~TiddlyWiki discussion groups are mailing lists for talking about ~TiddlyWik
|
||||||
** An enhanced group search facility is available on [[mail-archive.com|https://www.mail-archive.com/tiddlywiki@googlegroups.com/]]
|
** An enhanced group search facility is available on [[mail-archive.com|https://www.mail-archive.com/tiddlywiki@googlegroups.com/]]
|
||||||
* Watch recordings of our regular [[TiddlyWiki Hangouts]]
|
* Watch recordings of our regular [[TiddlyWiki Hangouts]]
|
||||||
* Follow [[@TiddlyWiki on Twitter|http://twitter.com/TiddlyWiki]] for the latest news
|
* Follow [[@TiddlyWiki on Twitter|http://twitter.com/TiddlyWiki]] for the latest news
|
||||||
* ''New: Join us on our live chat at https://gitter.im/TiddlyWiki/public !''
|
* New: Join us on our live chat at https://gitter.im/TiddlyWiki/public !
|
||||||
|
* There is also a discord available at https://discord.gg/HFFZVQ8
|
||||||
|
|
||||||
|
|
||||||
! Developers
|
! Developers
|
||||||
|
|
|
@ -7,6 +7,6 @@ Extend tiddlywiki to parse complex ("nested") json data tiddlers.
|
||||||
|
|
||||||
Json Mangler introduces a new path syntax for indexes of json data tiddlers , and includes many supporting tools, filters, widgets, etc.
|
Json Mangler introduces a new path syntax for indexes of json data tiddlers , and includes many supporting tools, filters, widgets, etc.
|
||||||
|
|
||||||
Example Wiki: https://joshuafontany.github.io/TW5-JsonManglerPlugin/
|
Example Wiki: https://joshuafontany.github.io/TW5-JsonMangler/
|
||||||
|
|
||||||
Source: https://github.com/joshuafontany/TW5-JsonManglerPlugin
|
Source: https://github.com/joshuafontany/TW5-JsonMangler
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20130825213300000
|
created: 20130825213300000
|
||||||
modified: 20191013093910961
|
modified: 20191206152031468
|
||||||
tags: Concepts
|
tags: Concepts
|
||||||
title: TiddlerFields
|
title: TiddlerFields
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -42,11 +42,13 @@ Other fields used by the core are:
|
||||||
|`subtitle` |<<lingo subtitle>> |
|
|`subtitle` |<<lingo subtitle>> |
|
||||||
|`throttle.refresh` |<<lingo throttle.refresh>> |
|
|`throttle.refresh` |<<lingo throttle.refresh>> |
|
||||||
|`toc-link`|<<lingo toc-link>>|
|
|`toc-link`|<<lingo toc-link>>|
|
||||||
|
|`_canonical_uri`|<<lingo _canonical_uri>>|
|
||||||
|
|
||||||
The TiddlyWebAdaptor uses a few more fields:
|
The TiddlyWebAdaptor uses a few more fields:
|
||||||
|
|
||||||
|!Field Name |!Description |
|
|!Field Name |!Description |
|
||||||
|`bag` |<<lingo bag>> |
|
|`bag` |<<lingo bag>> |
|
||||||
|`revision` |<<lingo revision>> |
|
|`revision` |<<lingo revision>> |
|
||||||
|
|`_is_skinny` |<<lingo _is_skinny>> |
|
||||||
|
|
||||||
Details of the fields used in this ~TiddlyWiki are shown in the [[control panel|$:/ControlPanel]] {{$:/core/ui/Buttons/control-panel}} under the <<.controlpanel-tab Info>> tab >> <<.info-tab Advanced>> sub-tab >> Tiddler Fields
|
Details of the fields used in this ~TiddlyWiki are shown in the [[control panel|$:/ControlPanel]] {{$:/core/ui/Buttons/control-panel}} under the <<.controlpanel-tab Info>> tab >> <<.info-tab Advanced>> sub-tab >> Tiddler Fields
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
created: 20130825161100000
|
created: 20130825161100000
|
||||||
modified: 20171110094548887
|
modified: 20200104111952539
|
||||||
tags: Definitions
|
tags: Definitions
|
||||||
title: TiddlyFox
|
title: TiddlyFox
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
TiddlyFox is an extension for older versions of Firefox that allows standalone TiddlyWiki files to save their changes directly to the file system. TiddlyFox works on both desktop and smartphone versions of [[Firefox]]. See [[Saving with TiddlyFox]] or [[Saving with TiddlyFox on Android]] for detailed instructions.
|
TiddlyFox is an extension for older versions of Firefox that allows standalone TiddlyWiki files to save their changes directly to the file system. TiddlyFox works on both desktop and smartphone versions of [[Firefox]]. See [[Saving with TiddlyFox]] or [[Saving with TiddlyFox on Android]] for detailed instructions.
|
||||||
|
|
||||||
TiddlyFox is now obsolete due to its incompatibility with the latest versions of Firefox - see [[Firefox Apocalypse]]. There are many alternatives to TiddlyFox, but none that work in precisely the same way -- see GettingStarted for details.
|
TiddlyFox is now obsolete due to its incompatibility with the latest versions of Firefox - see [[TiddlyFox Apocalypse]]. There are many alternatives to TiddlyFox, but none that work in precisely the same way -- see GettingStarted for details.
|
||||||
|
|
||||||
TiddlyFox can be downloaded from the Mozilla Addons site:
|
TiddlyFox can be downloaded from the Mozilla Addons site:
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@ You can import tiddlers into a ~TiddlyWiki from external files or directly from
|
||||||
|
|
||||||
!! Importing content from external files
|
!! Importing content from external files
|
||||||
|
|
||||||
There are several ways to import content from external files:
|
There are several ways to import content (including text, images, pdf documents, etc.) from external files:
|
||||||
|
|
||||||
* Use the <<.icon $:/core/images/import-button>> ''import'' button (under the ''Tools'' tab in the sidebar) to select a local file
|
* Use the <<.icon $:/core/images/import-button>> ''import'' button (under the ''Tools'' tab in the sidebar) to select a local file
|
||||||
* Drag and drop files from Windows Explorer or OS X Finder etc. into the TiddlyWiki browser window
|
* Drag and drop files from Windows Explorer or OS X Finder etc. into the ~TiddlyWiki browser window
|
||||||
* Paste content directly from the clipboard using the menu or keyboard shortcut (<kbd>ctrl-V</kbd> or <kbd>cmd-V</kbd>)
|
* Paste content directly from the clipboard using the menu or keyboard shortcut (<kbd>ctrl-V</kbd> or <kbd>cmd-V</kbd>)
|
||||||
** Currently supported in Chrome, Firefox and Edge (but not Internet Explorer)
|
** Currently supported in Chrome, Firefox and Edge (but not Internet Explorer)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ The information above should be interpreted as follows:
|
||||||
|
|
||||||
As an example, try switching between the sidebar tabs to compare how long they take to render.
|
As an example, try switching between the sidebar tabs to compare how long they take to render.
|
||||||
|
|
||||||
More detailed information on filter execution timings is also available. With performance instrumentation enabled, ype the following JavaScript command in the browser developer console:
|
More detailed information on filter execution timings is also available. With performance instrumentation enabled, type the following JavaScript command in the browser developer console:
|
||||||
|
|
||||||
```
|
```
|
||||||
$tw.perf.log()
|
$tw.perf.log()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20140419082845576
|
created: 20140419082845576
|
||||||
modified: 20160610082458079
|
modified: 20190912093109517
|
||||||
tags: Features
|
tags: Features
|
||||||
title: SafeMode
|
title: SafeMode
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -8,6 +8,8 @@ type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
Safe mode provides a way to disabling most customisations in TiddlyWiki. This is useful because if TiddlyWiki is customised incorrectly it can be rendered inoperable. A particular issue is that some customisations break when upgrading to a newer core version of TiddlyWiki (especially during the beta).
|
Safe mode provides a way to disabling most customisations in TiddlyWiki. This is useful because if TiddlyWiki is customised incorrectly it can be rendered inoperable. A particular issue is that some customisations break when upgrading to a newer core version of TiddlyWiki (especially during the beta).
|
||||||
|
|
||||||
|
<<.warning "Safe mode should only be used with the single file configuration of TiddlyWiki, and then only with savers that do not autosave. Using safe mode in the client server configuration can lead to data loss.">>
|
||||||
|
|
||||||
! Enabling Safe Mode
|
! Enabling Safe Mode
|
||||||
|
|
||||||
Safe mode is enabled in the browser by starting TiddlyWiki with the URL hash set to the string `#:safe`. For example:
|
Safe mode is enabled in the browser by starting TiddlyWiki with the URL hash set to the string `#:safe`. For example:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20140410103123179
|
created: 20140410103123179
|
||||||
modified: 20170103175939836
|
modified: 20191209085901849
|
||||||
tags: [[Filter Operators]] [[Group Operators]]
|
tags: [[Filter Operators]] [[Group Operators]]
|
||||||
title: each Operator
|
title: each Operator
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -20,6 +20,6 @@ Each input title is processed in turn. The value of field <<.place F>> in the co
|
||||||
;each:value
|
;each:value
|
||||||
:As long as the title is unique it is appended to the output whether or not the corresponding tiddler exists.
|
:As long as the title is unique it is appended to the output whether or not the corresponding tiddler exists.
|
||||||
|
|
||||||
If a tiddler does not contain field <<.place F>>, it is treated as if the value of the field were empty.
|
Note that if a tiddler does not contain field <<.place F>>, it is treated as if the value of the field were empty. Thus, a filter expression such as `[each[motovun]]` will return one tiddler that doesn't have a `motovun` field, as well as one tiddler with each distinct value of that field, if any. To obtain just the tiddlers that have a non-blank value for the `motovun` field one can use `[each[motovun]has[motovun]]`.
|
||||||
|
|
||||||
<<.operator-examples "each">>
|
<<.operator-examples "each">>
|
||||||
|
|
|
@ -8,3 +8,4 @@ type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
<<.operator-example 1 "[list[Days of the Week]allbefore[Wednesday]]">>
|
<<.operator-example 1 "[list[Days of the Week]allbefore[Wednesday]]">>
|
||||||
<<.operator-example 2 "[list[Days of the Week]allbefore:include[Wednesday]]">>
|
<<.operator-example 2 "[list[Days of the Week]allbefore:include[Wednesday]]">>
|
||||||
|
<<.operator-example 3 "A B C D E +[allbefore:include[C]count[]]">>
|
||||||
|
|
|
@ -15,10 +15,12 @@ The following substitutions are made:
|
||||||
|
|
||||||
|!Character |!Replacement |
|
|!Character |!Replacement |
|
||||||
|`\` |`\\` |
|
|`\` |`\\` |
|
||||||
|`"` |`\\` |
|
|`"` |`\\"` |
|
||||||
|`'` |`\\` |
|
|
||||||
|`\r` (carriage return) |`\\r` |
|
|`\r` (carriage return) |`\\r` |
|
||||||
|`\n` (line feed) |`\\n` |
|
|`\n` (line feed) |`\\n` |
|
||||||
|
|`\x08` (backpsace) |`\\b` |
|
||||||
|
|`\x0c` (formfield) |`\\f` |
|
||||||
|
|`\t` (tab) |`\\t` |
|
||||||
|Characters from 0x00 to 0x1f |`\\x##` where ## is two hex digits |
|
|Characters from 0x00 to 0x1f |`\\x##` where ## is two hex digits |
|
||||||
|Characters from 0x80 to 0xffff |`\\u####` where #### is four hex digits |
|
|Characters from 0x80 to 0xffff |`\\u####` where #### is four hex digits |
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ created: 20190613153225735
|
||||||
modified: 20190613153321546
|
modified: 20190613153321546
|
||||||
op-input: a [[selection of titles|Title Selection]]
|
op-input: a [[selection of titles|Title Selection]]
|
||||||
op-output: replaces each input title with its length as an integer
|
op-output: replaces each input title with its length as an integer
|
||||||
op-purpose: returns the lengths of each item in the list
|
op-purpose: returns the number of characters of each item in the list
|
||||||
tags: [[Filter Operators]]
|
tags: [[Filter Operators]]
|
||||||
title: length Operator
|
title: length Operator
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
|
@ -2,8 +2,8 @@ caption: sign
|
||||||
created: 20190613084919354
|
created: 20190613084919354
|
||||||
modified: 20190613085044655
|
modified: 20190613085044655
|
||||||
op-input: a [[selection of titles|Title Selection]]
|
op-input: a [[selection of titles|Title Selection]]
|
||||||
op-output: replaces each input number with -1, 0 or +1 according to whether the number is negative, zero, or positive
|
op-output: replaces each input number with -1, 0 or 1 according to whether the number is negative, zero, or positive
|
||||||
op-purpose: return -1, 0 or +1 for a list of numbers according to whether each number is negative, zero, or positive
|
op-purpose: return -1, 0 or 1 for a list of numbers according to whether each number is negative, zero, or positive
|
||||||
tags: [[Unary Mathematics Operators]] [[Filter Operators]] [[Mathematics Operators]]
|
tags: [[Unary Mathematics Operators]] [[Filter Operators]] [[Mathematics Operators]]
|
||||||
title: sign Operator
|
title: sign Operator
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
created: 20200306145004925
|
||||||
|
modified: 20200306145004925
|
||||||
|
tags: [[Hidden Settings]]
|
||||||
|
title: Hidden Setting: Default Tiddler Icon
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
A default tiddler icon can be specified by creating a tiddler called $:/config/DefaultTiddlerIcon containing the title of the tiddler containing the icon.
|
|
@ -0,0 +1,19 @@
|
||||||
|
created: 20200315143638556
|
||||||
|
modified: 20200315143638556
|
||||||
|
tags: [[Hidden Settings]]
|
||||||
|
title: Hidden Setting: Disable Drag and Drop
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
To disable all the drag and drop operations that are built into the core, set the following tiddler to "no":
|
||||||
|
|
||||||
|
$:/config/DragAndDrop/Enable
|
||||||
|
|
||||||
|
To selectively re-enable drag and drop for an instance of the [[list-tagged-draggable Macro]] or [[list-links-draggable Macro]] you must ensure that the variable `tv-enable-drag-and-drop` is set to `yes` for the scope of the macro invocation. For example, note how it is still possible to use drag and drop within this list even if $:/config/DragAndDrop/Enable is set to "no":
|
||||||
|
|
||||||
|
<$macrocall $name="wikitext-example-without-html" src="""<$set name="tv-enable-drag-and-drop" value="yes">
|
||||||
|
|
||||||
|
<<list-tagged-draggable tag:"HelloThere">>
|
||||||
|
|
||||||
|
</$set>"""/>
|
||||||
|
|
||||||
|
Note that when using the DropzoneWidget and the DroppableWidget directly the ''enable'' attribute works independently of the global setting.
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20150221154058000
|
created: 20150221154058000
|
||||||
modified: 20150221154522000
|
modified: 20200228142855357
|
||||||
tags: Macros [[Core Macros]]
|
tags: Macros [[Core Macros]]
|
||||||
title: colour Macro
|
title: colour Macro
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -7,7 +7,7 @@ caption: colour
|
||||||
|
|
||||||
The <<.def colour>> (or <<.def color>>) [[macro|Macros]] returns the [[CSS|Cascading Style Sheets]] value of one the colours in the current [[palette|ColourPalettes]].
|
The <<.def colour>> (or <<.def color>>) [[macro|Macros]] returns the [[CSS|Cascading Style Sheets]] value of one the colours in the current [[palette|ColourPalettes]].
|
||||||
|
|
||||||
If no such entry exists in the current palette, the [[vanilla palette|$:/palettes/Vanilla]] is used instead.
|
If no such entry exists in the current palette, the [[vanilla palette|$:/palettes/Vanilla]] is used instead. If no such entry exists in the vanilla palette, the system looks for a configuration tiddler with the title `$:/config/DefaultColourMappings/<colour-name>` and transcludes the colour from the text field. This enables to plugins to ship defaults for colours that are not present in the core palettes.
|
||||||
|
|
||||||
!! Parameters
|
!! Parameters
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20150221152226000
|
created: 20150221152226000
|
||||||
modified: 20150221154213000
|
modified: 20200204135513721
|
||||||
tags: Macros [[Core Macros]]
|
tags: Macros [[Core Macros]]
|
||||||
title: jsontiddlers Macro
|
title: jsontiddlers Macro
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -13,3 +13,5 @@ An example can be seen in the [[template tiddler for JSON exports|$:/core/templa
|
||||||
|
|
||||||
;filter
|
;filter
|
||||||
: A [[filter|Filters]] selecting which tiddlers to include
|
: A [[filter|Filters]] selecting which tiddlers to include
|
||||||
|
;spaces
|
||||||
|
: An optional number of spaces to use for formatting the JSON. Defaults to 4, with blank or zero resulting in packed JSON with no formatting spaces
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20131228163141555
|
created: 20131228163141555
|
||||||
modified: 20150221223416000
|
modified: 20200330105334133
|
||||||
tags: Macros [[Core Macros]]
|
tags: Macros [[Core Macros]]
|
||||||
title: makedatauri Macro
|
title: makedatauri Macro
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -15,5 +15,7 @@ The <<.def makedatauri>> [[macro|Macros]] takes a piece of text and an associate
|
||||||
: The text to be converted to a data URI
|
: The text to be converted to a data URI
|
||||||
;type
|
;type
|
||||||
: The ContentType of the text
|
: The ContentType of the text
|
||||||
|
;_canonical_uri
|
||||||
|
: The optional ''_canonical_uri'' address of the content
|
||||||
|
|
||||||
<<.macro-examples "makedatauri">>
|
<<.macro-examples "makedatauri">>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
title: $:/editions/tw5.com/menuitems/gettingstarted
|
||||||
|
caption: ~GettingStarted
|
||||||
|
description: Link to ~GettingStarted
|
||||||
|
tags: $:/tags/MenuBar
|
||||||
|
target: GettingStarted
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
title: $:/tags/MenuBar
|
||||||
|
list:$:/plugins/tiddlywiki/menubar/items/hamburger $:/plugins/tiddlywiki/menubar/items/topleftbar $:/editions/tw5.com/menuitems/gettingstarted $:/plugins/tiddlywiki/menubar/items/contents $:/plugins/tiddlywiki/menubar/items/search $:/plugins/tiddlywiki/menubar/items/pagecontrols $:/plugins/tiddlywiki/menubar/items/server $:/plugins/tiddlywiki/menubar/items/toprightbar
|
|
@ -1,6 +1,6 @@
|
||||||
caption: tm-fold-all-tiddlers
|
caption: tm-fold-all-tiddlers
|
||||||
created: 20160424230908388
|
created: 20160424230908388
|
||||||
modified: 20190205154007291
|
modified: 20191028113838596
|
||||||
tags: Messages
|
tags: Messages
|
||||||
title: WidgetMessage: tm-fold-all-tiddlers
|
title: WidgetMessage: tm-fold-all-tiddlers
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
caption: tm-fold-other-tiddlers
|
caption: tm-fold-other-tiddlers
|
||||||
created: 20160424232355215
|
created: 20160424232355215
|
||||||
modified: 20160424233338710
|
modified: 20191028113932268
|
||||||
tags: Messages
|
tags: Messages
|
||||||
title: WidgetMessage: tm-fold-other-tiddlers
|
title: WidgetMessage: tm-fold-other-tiddlers
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -11,8 +11,8 @@ The `tm-fold-other-tiddlers` message folds all tiddlers in the current story lis
|
||||||
|param|Title of the tiddler that should be ignored by the fold operation. |
|
|param|Title of the tiddler that should be ignored by the fold operation. |
|
||||||
|foldedStatePrefix |Prefix for the state tiddler in which the fold state is stored. |
|
|foldedStatePrefix |Prefix for the state tiddler in which the fold state is stored. |
|
||||||
|
|
||||||
<<.tip "The core uses a foldStatePrefix of '$:/state/folded/' to store the fold states for the default story view.">>
|
<<.tip "The core uses a foldStatePrefix of `$:/state/folded/` to store the fold states for the default story view.">>
|
||||||
<<.warning "The state tiddlers title is computed as 'foldStatePrefix + TiddlerTitle'. If the foldStatePrefix is not set, it will overwrite the text of the tiddler(s) itself, resulting in data loss. ">>
|
<<.warning "The state tiddlers title is computed as `foldStatePrefix + TiddlerTitle`. If the foldStatePrefix is not set, it will overwrite the text of the tiddler(s) itself, resulting in data loss. ">>
|
||||||
|
|
||||||
|
|
||||||
The `tm-fold-other-tiddlers` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
The `tm-fold-other-tiddlers` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
caption: tm-fold-tiddler
|
caption: tm-fold-tiddler
|
||||||
created: 20160424232749223
|
created: 20160424232749223
|
||||||
modified: 20160424233102003
|
modified: 20191028113537119
|
||||||
tags: Messages
|
tags: Messages
|
||||||
title: WidgetMessage: tm-fold-tiddler
|
title: WidgetMessage: tm-fold-tiddler
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
The `tm-fold-tiddler` message folds the tiddler specified in the `param` parameter. It does so by setting the text of a state tiddler to either "<<.value "show">>" or "<<.value "hide">>", according to the fold state.
|
The `tm-fold-tiddler` message toggles the value of a state tiddler. It does so by setting the text of a state tiddler to either "<<.value "show">>" or "<<.value "hide">>", according to the fold state.
|
||||||
|
|
||||||
|!Name |!Description |
|
|!Name |!Description |
|
||||||
|param|Title of the tiddler that should be folded. If the tiddler is already folded, it will be unfolded instead. |
|
|param |ignored ... foldedState must be used |
|
||||||
|foldedStatePrefix |Prefix for the state tiddler in which the fold state is stored. If no state prefix is provided, `tm-fold-tiddler` will do nothing. |
|
|foldedState |State tiddler in which the fold state is stored |
|
||||||
|
|
||||||
<<.tip "The core uses a foldStatePrefix of '$:/state/folded/' to store the fold states for the default story view.">>
|
<<.tip "The core uses a foldState prefix of `$:/state/folded/` + `tiddler title` to store the fold states for the default story view.">>
|
||||||
|
|
||||||
The `tm-fold-tiddler` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
The `tm-fold-tiddler` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
||||||
|
|
||||||
|
The core $:/core/ui/ViewTemplate defines a global variable `folded-state`, that is used with every tiddler. It's created like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
\define folded-state()
|
||||||
|
$:/state/folded/$(currentTiddler)$
|
||||||
|
\end
|
||||||
|
```
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
caption: tm-unfold-all-tiddlers
|
caption: tm-unfold-all-tiddlers
|
||||||
created: 20160424233133261
|
created: 20160424233133261
|
||||||
modified: 20160424233427308
|
modified: 20191028113810219
|
||||||
tags: Messages
|
tags: Messages
|
||||||
title: WidgetMessage: tm-unfold-all-tiddlers
|
title: WidgetMessage: tm-unfold-all-tiddlers
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
The `tm-unfold-all-tiddlers` message unfolds all tiddlers in the current story list. It does so by setting the text of a state tiddler to either "<<.value "show">>" or "<<.value "hide">>", according to the fold state.
|
The `tm-unfold-all-tiddlers` message unfolds all tiddlers in the current story list. It does so by setting the text of all state tiddlers to: "<<.value "show">>".
|
||||||
|
|
||||||
|!Name |!Description |
|
|!Name |!Description |
|
||||||
|foldedStatePrefix |Prefix for the state tiddler in which the fold state is stored. |
|
|foldedStatePrefix |Prefix for the state tiddler in which the fold state is stored. |
|
||||||
|
|
||||||
<<.tip "The core uses a foldStatePrefix of '$:/state/folded/' to store the fold states for the default story view.">>
|
<<.tip "The core uses a foldStatePrefix of `$:/state/folded/` to store the fold states for the default story view.">>
|
||||||
<<.warning "The state tiddlers title is computed as 'foldStatePrefix + TiddlerTitle'. If the foldStatePrefix is not set, it will overwrite the text of the tiddler(s) itself, resulting in data loss. ">>
|
<<.warning "The state tiddlers title is computed as `foldStatePrefix + TiddlerTitle`. If the foldStatePrefix is not set, it will overwrite the text of the tiddler(s) itself, resulting in data loss. ">>
|
||||||
|
|
||||||
The `tm-unfold-all-tiddlers` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
The `tm-unfold-all-tiddlers` message is usually generated with the ButtonWidget and is handled by the surrounding NavigatorWidget.
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
caption: tm-unload-plugin-library
|
||||||
|
created: 20191004112527669
|
||||||
|
modified: 20191004113621714
|
||||||
|
tags: Messages
|
||||||
|
title: WidgetMessage: tm-unload-plugin-library
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
The `tm-unload-plugin-library` message unloads the specified plugin library and deletes associated temporary tiddlers.
|
||||||
|
|
||||||
|
|!Name |!Description |
|
||||||
|
|url |Url specifying the plugin library to be unloaded |
|
||||||
|
|
||||||
|
Unloading a plugin library deletes the following tiddlers:
|
||||||
|
|
||||||
|
* The tiddler titled `$:/temp/ServerConnection/` + `<url>`
|
||||||
|
* All tiddlers with the prefix `$:/temp/RemoteAssetInfo/` + `<url>` + `/`
|
||||||
|
|
||||||
|
The `tm-unload-plugin-library` message is usually generated with the ButtonWidget and is handled by the core itself.
|
|
@ -1,6 +1,6 @@
|
||||||
created: 20150926162849519
|
created: 20150926162849519
|
||||||
modified: 20180701185329863
|
modified: 20191022095509822
|
||||||
tags: [[Installing TiddlyWiki on Node.js]]
|
tags: [[TiddlyWiki on Node.js]]
|
||||||
title: Installing TiddlyWiki Prerelease on Node.js
|
title: Installing TiddlyWiki Prerelease on Node.js
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
created: 20191022095653896
|
||||||
|
modified: 20191102131824766
|
||||||
|
tags: [[TiddlyWiki on Node.js]]
|
||||||
|
title: Installing custom plugins on Node.js
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
There are several ways in which custom plugins that are not part of TiddlyWiki's plugin library can be installed when using TiddlyWiki under Node.js. (See [[Installing a plugin from the plugin library]] for instructions on installing plugins from the library).
|
||||||
|
|
||||||
|
* Arrange the PluginFolders containing the plugins in a convenient shared location and then use [[environment variables|Environment Variables on Node.js]] to tell TiddlyWiki to search those folders. The plugins can be referenced in `tiddlywiki.info` by their name (e.g. `tiddlytools/magic`)
|
||||||
|
* Place the PluginFolders containing the plugins in a `plugins` folder within the [[wiki folder|TiddlyWikiFolders]]
|
||||||
|
* Depending on how TiddlyWiki itself has been installed, plugins can also be installed by copying the plugin folders into the `plugins` folder of the repository. This is only recommended if working with a forked copy of the repo. It is not recommended if TiddlyWiki has been installed with npm because npm is liable to overwrite the installation when performing an update
|
||||||
|
|
||||||
|
Note that including a plugin as an ordinary tiddler (e.g. by dragging and dropping a plugin into the browser) will result in the plugin only being active in the browser, and not available under Node.js.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20131129094353704
|
created: 20131129094353704
|
||||||
modified: 20150412185457193
|
modified: 20200318115527226
|
||||||
tags: Platforms
|
tags: Platforms
|
||||||
title: TiddlyWiki on Node.js
|
title: TiddlyWiki on Node.js
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -10,14 +10,8 @@ Running TiddlyWiki on [[Node.js]] brings several important benefits over and abo
|
||||||
* Individual tiddlers are stored in separate files, which you can organise as you wish
|
* Individual tiddlers are stored in separate files, which you can organise as you wish
|
||||||
* The ability to build multiple wikis that blend different combinations of shared and unique content
|
* The ability to build multiple wikis that blend different combinations of shared and unique content
|
||||||
|
|
||||||
There are a few file system limitations you should be aware of that are related to how TiddlyWiki was designed:
|
<<.warning """Note that TiddlyWiki on Node.js doesn't currently support directly modifying the tiddler files via the file system while it is running. The server must be restarted in order to for changes to take effect. The recommended way to interact with a running wiki is via the HTTP or JavaScript APIs.""">>
|
||||||
|
|
||||||
* The best, most general way to interact with a running wiki is via the HTTP or JavaScript API, rather than manipulating the file store directly
|
|
||||||
* Any modification to the contents of the wiki folder (e.g. images) might thus sometimes require that your restart the node.js server
|
|
||||||
* TiddlyWiki might support manipulating the file store directly in the future
|
|
||||||
|
|
||||||
For more information see:
|
For more information see:
|
||||||
|
|
||||||
* [[Installing TiddlyWiki on Node.js]]
|
<<list-links "[tag[TiddlyWiki on Node.js]]">>
|
||||||
* [[Using TiddlyWiki on Node.js]]
|
|
||||||
* [[Upgrading TiddlyWiki on Node.js]]
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
created: 20160107222352710
|
created: 20160107222352710
|
||||||
modified: 20190927205622498
|
modified: 20191022095637710
|
||||||
tags: Plugins
|
tags: [[TiddlyWiki on Node.js]] Plugins
|
||||||
title: Installing a plugin from the plugin library
|
title: Installing a plugin from the plugin library
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
@ -47,3 +47,5 @@ Follow these instructions when using TiddlyWiki under Node.js:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See also [[Installing custom plugins on Node.js]].
|
|
@ -1,16 +1,16 @@
|
||||||
caption: ~AndTidWiki
|
caption: Tiddloid and Tiddloid Lite
|
||||||
created: 20130825161400000
|
created: 20130825161400000
|
||||||
delivery: App
|
delivery: App
|
||||||
description: Android app for saving changes locally to device storage
|
description: Android app for saving changes locally to device storage
|
||||||
method: save
|
method: save
|
||||||
modified: 20171113105950965
|
modified: 20191013145728306
|
||||||
tags: Saving Android
|
tags: Saving Android
|
||||||
title: Saving on Android
|
title: Saving on Android
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
The AndTidWiki app for Android devices makes it possible to edit and save changes to TiddlyWiki5, including working offline without a network connection. [[Download it here|https://play.google.com/store/apps/details?id=de.mgsimon.android.andtidwiki&hl=en]].
|
The Tiddloid or Tiddloid Lite app for Android devices makes it possible to edit and save changes to TiddlyWiki. Get it from GitHub: [[Tiddloid|https://github.com/donmor/Tiddloid]] [[Tiddloid Lite|https://github.com/donmor/TiddloidLite]].
|
||||||
|
|
||||||
Instructions for use:
|
''Instructions for use:''
|
||||||
|
|
||||||
# [[Download]] an empty TiddlyWiki on another web browser
|
# [[Download]] an empty TiddlyWiki on another web browser
|
||||||
# Move the file you just downloaded to the directory `/sdcard/andtidwiki`
|
# Move the file you just downloaded to the directory `/sdcard/andtidwiki`
|
||||||
|
@ -23,4 +23,5 @@ Instructions for use:
|
||||||
|
|
||||||
''Note:'' You can save your changes by clicking the <<.icon $:/core/images/save-button>> ''save changes'' button in the sidebar even if you have not clicked the <<.icon $:/core/images/done-button>> ''ok'' button to complete editing a tiddler
|
''Note:'' You can save your changes by clicking the <<.icon $:/core/images/save-button>> ''save changes'' button in the sidebar even if you have not clicked the <<.icon $:/core/images/done-button>> ''ok'' button to complete editing a tiddler
|
||||||
|
|
||||||
//Note that AndTidWiki is published independently of TiddlyWiki//
|
* Tiddloid Lite supports new devices better. It also supports files on clouds like GDrive and ~OneDrive, while Tiddloid keeps the compatibility to TiddlyWikiClassic. For more difference between Tiddloid and Tiddloid Lite, please visit [[Tiddloid's homepage|https://github.com/donmor/Tiddloid]].
|
||||||
|
* You should keep the `.html` or `.htm` extension of the files to be imported.
|
||||||
|
|
|
@ -12,7 +12,7 @@ tags: $:/tags/Stylesheet
|
||||||
border: 1px solid #ebefcd;
|
border: 1px solid #ebefcd;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
background: #fcfdf3;
|
background: <<colour code-background>>;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
created: 20181002131215403
|
created: 20181002131215403
|
||||||
modified: 20190903094711346
|
modified: 2020031109590546
|
||||||
tags: [[WebServer API]]
|
tags: [[WebServer API]]
|
||||||
title: WebServer API: Get All Tiddlers
|
title: WebServer API: Get All Tiddlers
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -12,11 +12,23 @@ GET /recipes/default/tiddlers.json
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
* none
|
* ''filter'' - filter identifying tiddlers to be returned (optional, defaults to "[all[tiddlers]!is[system]sort[title]]")
|
||||||
|
* ''exclude'' - comma delimited list of fields to excluded from the returned tiddlers (optional, defaults to "text")
|
||||||
|
|
||||||
|
In order to avoid denial of service attacks with malformed filters in the default configuration the only filter that is accepted is the default filter "[all[tiddlers]!is[system]sort[title]]"; attempts to use any other filter will result in an HTTP 403 error.
|
||||||
|
|
||||||
|
To enable a particular filter, create a tiddler with the title "$:/config/Server/ExternalFilters/" concatenated with the filter text, and the text field set to "yes". For example, the TiddlyWeb plugin includes the following shadow tiddler to enable the filter that it requires:
|
||||||
|
|
||||||
|
```
|
||||||
|
title: $:/config/Server/ExternalFilters/[all[tiddlers]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]]
|
||||||
|
text: yes
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to configure the server to accept any filter by creating a tiddler titled $:/config/Server/AllowAllExternalFilters with the text "yes". This should not be done for public facing servers.
|
||||||
|
|
||||||
Response:
|
Response:
|
||||||
|
|
||||||
|
|
||||||
* 200 OK
|
* 200 OK
|
||||||
*> `Content-Type: application/json`
|
*> `Content-Type: application/json`
|
||||||
*> Body: array of all non-system tiddlers in [[TiddlyWeb JSON tiddler format]]
|
*> Body: array of all non-system tiddlers in [[TiddlyWeb JSON tiddler format]]
|
||||||
|
* 403 Forbidden
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
caption: path-prefix
|
caption: path-prefix
|
||||||
created: 20180630180514893
|
created: 20180630180514893
|
||||||
modified: 20180702154716090
|
modified: 20200311171847360
|
||||||
tags: [[WebServer Parameters]]
|
tags: [[WebServer Parameters]]
|
||||||
title: WebServer Parameter: path-prefix
|
title: WebServer Parameter: path-prefix
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -12,3 +12,5 @@ This example causes the server to serve from http://127.0.0.1/MyApp instead of t
|
||||||
```
|
```
|
||||||
tiddlywiki mywikifolder --listen "path-prefix=/MyApp"
|
tiddlywiki mywikifolder --listen "path-prefix=/MyApp"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that further steps are required to configure the client-side components to use the prefix. See [[Using a custom path prefix with the client-server edition]].
|
|
@ -1,6 +1,6 @@
|
||||||
caption: port
|
caption: port
|
||||||
created: 20180630180552254
|
created: 20180630180552254
|
||||||
modified: 20180702155017130
|
modified: 20191219123751824
|
||||||
tags: [[WebServer Parameters]]
|
tags: [[WebServer Parameters]]
|
||||||
title: WebServer Parameter: port
|
title: WebServer Parameter: port
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
@ -10,6 +10,7 @@ The [[web server configuration parameter|WebServer Parameters]] ''port'' specifi
|
||||||
The ''port'' parameter accepts two types of value:
|
The ''port'' parameter accepts two types of value:
|
||||||
|
|
||||||
* Numerical values are interpreted as a decimal port number
|
* Numerical values are interpreted as a decimal port number
|
||||||
|
** The special value 0 (zero) causes the operating system to assign an available port
|
||||||
* Non-numeric values are interpreted as an environment variable from which the port should be read
|
* Non-numeric values are interpreted as an environment variable from which the port should be read
|
||||||
|
|
||||||
This example configures the server to listen on port 8090:
|
This example configures the server to listen on port 8090:
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
caption: action-popup
|
||||||
|
created: 20200303114556528
|
||||||
|
modified: 20200303114556528
|
||||||
|
tags: Widgets ActionWidgets
|
||||||
|
title: ActionPopupWidget
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
! Introduction
|
||||||
|
|
||||||
|
The ''action-popup'' widget is an [[action widget|ActionWidgets]] that triggers the display of a popup defined via a state tiddler. ActionWidgets are used within triggering widgets such as the ButtonWidget.
|
||||||
|
|
||||||
|
! Content and Attributes
|
||||||
|
|
||||||
|
The ''action-popup'' widget is invisible. Any content within it is ignored.
|
||||||
|
|
||||||
|
|!Attribute |!Description |
|
||||||
|
|$state |The title of the state tiddler for the popup |
|
||||||
|
|$coords |Optional coordinates for the handle to which popup is positioned (in the format `(x,y,w,h)`) |
|
||||||
|
|
||||||
|
! Examples
|
||||||
|
|
||||||
|
Here is an example of button that triggers the "more" button in the sidebar "Tools" tab. You may need to scroll to see the popup
|
||||||
|
|
||||||
|
<$macrocall $name='wikitext-example-without-html'
|
||||||
|
src='<$button>
|
||||||
|
<$action-setfield $tiddler="$:/state/tab/sidebar--595412856" $value="$:/core/ui/SideBar/Tools"/>
|
||||||
|
<$action-popup $state="$:/state/popup/more-2053862905" $coords="(0,20,0,0)"/>
|
||||||
|
Click me!
|
||||||
|
</$button>'/>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue