1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-02-15 14:39:50 +00:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Jeremy Ruston
4022532455 Merge branch 'master' into dynamic-build-commands 2025-03-21 17:25:50 +00:00
Jeremy Ruston
b2b00324c2 Merge branch 'master' into dynamic-build-commands 2025-02-18 10:49:45 +00:00
Jeremy Ruston
e3714929f9 Add support for a transformFilter for prompts 2024-10-20 13:02:47 +01:00
Jeremy Ruston
b31e2cd0db Mark version in docs 2024-10-20 12:55:21 +01:00
Jeremy Ruston
8b2afd1cc2 Fix bug processing empty string tokens 2024-10-20 12:55:11 +01:00
Jeremy Ruston
bd86723b72 Enhance terminal colour support 2024-10-19 17:05:34 +01:00
Jeremy Ruston
2d89228d25 Example should match the documentation 2024-10-19 16:16:01 +01:00
Jeremy Ruston
5233d72642 Documentation 2024-10-19 16:13:28 +01:00
Jeremy Ruston
4a6501778a Add support for prompts within build scripts 2024-10-19 15:54:31 +01:00
Jeremy Ruston
50118dbe13 Initial commit
This commit adds support for dynamic tokens within build commands. See the tiddlywiki.info file. Also adds an echo command to make debugging easier.

I also intend to add a node type for prompting the user for a string.
2024-10-19 12:26:11 +01:00
45 changed files with 601 additions and 917 deletions

View File

@@ -105,7 +105,6 @@ node $TW5_BUILD_TIDDLYWIKI \
fi
# /index.html Main site
# /external-(version).html External core version of main site
# /favicon.ico Favicon for main site
# /static.html Static rendering of default tiddlers
# /alltiddlers.html Static rendering of all tiddlers
@@ -118,7 +117,7 @@ node $TW5_BUILD_TIDDLYWIKI \
--version \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build favicon static index external-js \
--build favicon static index \
|| exit 1
# /empty.html Empty

View File

@@ -1068,10 +1068,6 @@ $tw.Tiddler.prototype.hasField = function(field) {
return $tw.utils.hop(this.fields,field);
};
$tw.Tiddler.prototype.isPlugin = function() {
return this.fields.type === "application/json" && this.hasField("plugin-type") && !this.hasField("draft.of");
};
/*
Compare two tiddlers for equality
tiddler: the tiddler to compare
@@ -1166,21 +1162,21 @@ enableIndexers - Array of indexer names to enable, or null to use all available
$tw.Wiki = function(options) {
options = options || {};
var self = this,
tiddlerStore = Object.create(null), // Hashmap of tiddlers
tiddlerTitles = null, // Array of tiddler titles, calculated and cached when needed
tiddlers = Object.create(null), // Hashmap of tiddlers
tiddlerTitles = null, // Array of tiddler titles
getTiddlerTitles = function() {
if(!tiddlerTitles) {
tiddlerTitles = Object.keys(tiddlerStore).sort(function(a,b) {return a.localeCompare(b);});
tiddlerTitles = Object.keys(tiddlers).sort(function(a,b) {return a.localeCompare(b);});
}
return tiddlerTitles;
},
pluginTiddlersInfo = [], // Array of tiddler fields for all plugins and sub-plugins, ordered by priority
pluginContents = Object.create(null), // Hashmap by title of {contents:<parsed plugin contents>,fields:<fields>,subPluginTitles:<list>}
shadowTiddlerInfo = Object.create(null), // Hashmap by title of {source:, tiddler:}
shadowTiddlerTitles = null, // Array of tiddler titles, calculated and cached when needed
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
shadowTiddlerTitles = null,
getShadowTiddlerTitles = function() {
if(!shadowTiddlerTitles) {
shadowTiddlerTitles = Object.keys(shadowTiddlerInfo).sort(function(a,b) {return a.localeCompare(b);});
shadowTiddlerTitles = Object.keys(shadowTiddlers).sort(function(a,b) {return a.localeCompare(b);});
}
return shadowTiddlerTitles;
},
@@ -1222,7 +1218,7 @@ $tw.Wiki = function(options) {
}
}
// Save the new tiddler
tiddlerStore[title] = tiddler;
tiddlers[title] = tiddler;
// Check we've got the title
tiddlerTitles = $tw.utils.insertSortedArray(tiddlerTitles || [],title);
// Record the new tiddler state
@@ -1247,7 +1243,7 @@ $tw.Wiki = function(options) {
this.deleteTiddler = function(title) {
// Uncomment the following line for detailed logs of all tiddler deletions
// console.log("Deleting",title)
if($tw.utils.hop(tiddlerStore,title)) {
if($tw.utils.hop(tiddlers,title)) {
// Record the old tiddler state
var updateDescriptor = {
old: {
@@ -1257,7 +1253,7 @@ $tw.Wiki = function(options) {
}
}
// Delete the tiddler
delete tiddlerStore[title];
delete tiddlers[title];
// Delete it from the list of titles
if(tiddlerTitles) {
var index = tiddlerTitles.indexOf(title);
@@ -1285,11 +1281,11 @@ $tw.Wiki = function(options) {
// Get a tiddler from the store
this.getTiddler = function(title) {
if(title) {
var t = tiddlerStore[title];
var t = tiddlers[title];
if(t !== undefined) {
return t;
} else {
var s = shadowTiddlerInfo[title];
var s = shadowTiddlers[title];
if(s !== undefined) {
return s.tiddler;
}
@@ -1309,7 +1305,7 @@ $tw.Wiki = function(options) {
index,titlesLength,title;
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
callback(tiddlerStore[title],title);
callback(tiddlers[title],title);
}
};
@@ -1324,10 +1320,10 @@ $tw.Wiki = function(options) {
index,titlesLength,title;
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(tiddlerStore[title]) {
callback(tiddlerStore[title],title);
if(tiddlers[title]) {
callback(tiddlers[title],title);
} else {
var shadowInfo = shadowTiddlerInfo[title];
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
@@ -1339,13 +1335,13 @@ $tw.Wiki = function(options) {
titles = getTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
callback(tiddlerStore[title],title);
callback(tiddlers[title],title);
}
titles = getShadowTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(!tiddlerStore[title]) {
var shadowInfo = shadowTiddlerInfo[title];
if(!tiddlers[title]) {
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
@@ -1357,35 +1353,35 @@ $tw.Wiki = function(options) {
titles = getShadowTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(tiddlerStore[title]) {
callback(tiddlerStore[title],title);
if(tiddlers[title]) {
callback(tiddlers[title],title);
} else {
var shadowInfo = shadowTiddlerInfo[title];
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
titles = getTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(!shadowTiddlerInfo[title]) {
callback(tiddlerStore[title],title);
if(!shadowTiddlers[title]) {
callback(tiddlers[title],title);
}
}
};
// Test for the existence of a tiddler (excludes shadow tiddlers)
this.tiddlerExists = function(title) {
return !!$tw.utils.hop(tiddlerStore,title);
return !!$tw.utils.hop(tiddlers,title);
};
// Determines if a tiddler is a shadow tiddler, regardless of whether it has been overridden by a real tiddler
this.isShadowTiddler = function(title) {
return $tw.utils.hop(shadowTiddlerInfo,title);
return $tw.utils.hop(shadowTiddlers,title);
};
this.getShadowSource = function(title) {
if($tw.utils.hop(shadowTiddlerInfo,title)) {
return shadowTiddlerInfo[title].source;
if($tw.utils.hop(shadowTiddlers,title)) {
return shadowTiddlers[title].source;
}
return null;
};
@@ -1393,8 +1389,8 @@ $tw.Wiki = function(options) {
// Get an array of all the currently recognised plugin types
this.getPluginTypes = function() {
var types = [];
$tw.utils.each(pluginTiddlersInfo,function(pluginTiddlerFields) {
var pluginType = pluginTiddlerFields["plugin-type"];
$tw.utils.each(pluginTiddlers,function(pluginTiddler) {
var pluginType = pluginTiddler.fields["plugin-type"];
if(pluginType && types.indexOf(pluginType) === -1) {
types.push(pluginType);
}
@@ -1402,45 +1398,22 @@ $tw.Wiki = function(options) {
return types;
};
// Read plugin contents for all plugins, or just an array of titles. Returns the list of updated plugin titles, the list of deleted plugin titles and
// a hashmap of the deleted plugin contents. Clears the pluginContents cache for any plugins that have been deleted
// Read plugin info for all plugins, or just an array of titles. Returns the number of plugins updated or deleted
this.readPluginInfo = function(titles) {
var self = this,
results = {
var results = {
modifiedPlugins: [],
deletedPlugins: [],
deletedPluginContents: {}
deletedPlugins: []
};
$tw.utils.each(titles || getTiddlerTitles(),function(title) {
var tiddler = self.getTiddler(title);
if(!tiddler) {
for(var t=0; t<pluginContents.length; t++) {
if(pluginContents[t].subPluginTitles.indexOf(title) !== -1) {
tiddler = new $tw.Tiddler(pluginContents[t].fields);
break;
}
}
}
var tiddler = tiddlers[title];
if(tiddler) {
if(tiddler.isPlugin() && tiddler.fields.text) {
var contents = $tw.utils.parseJSONSafe(tiddler.fields.text).tiddlers;
var subPluginTitles = [];
// Read any sub-plugins
$tw.utils.each(contents,function(subPluginFields,subPluginTitle) {
if(subPluginFields["plugin-type"] && subPluginFields["type"] === "application/json") {
var contents = $tw.utils.parseJSONSafe(subPluginFields.text).tiddlers
pluginContents[subPluginTitle] = {contents: contents,fields: subPluginFields};
results.modifiedPlugins.push(subPluginTitle);
subPluginTitles.push(subPluginTitle)
}
});
pluginContents[title] = {contents: contents,fields: tiddler.fields,subPluginTitles: subPluginTitles};
results.modifiedPlugins.push(title);
if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type") && tiddler.fields.text) {
pluginInfo[tiddler.fields.title] = $tw.utils.parseJSONSafe(tiddler.fields.text);
results.modifiedPlugins.push(tiddler.fields.title);
}
} else {
if(pluginContents[title]) {
results.deletedPluginContents[title] = {tiddlers: pluginContents[title].contents};
delete pluginContents[title];
if(pluginInfo[title]) {
delete pluginInfo[title];
results.deletedPlugins.push(title);
}
}
@@ -1448,42 +1421,34 @@ $tw.Wiki = function(options) {
return results;
};
this.isSubPlugin = function(title) {
for(var t=0; t<pluginContents.length; t++) {
if(pluginContents[t].subPluginTitles.indexOf(title) !== -1) {
return true;
}
}
return false;
};
// Get plugin info for a plugin
this.getPluginInfo = function(title) {
var pluginContent = pluginContents[title];
if(pluginContent) {
return {tiddlers: pluginContent.contents, subPluginTitles: pluginContent.subPluginTitles, fields: pluginContent.fields};
} else {
return null;
}
return pluginInfo[title];
};
// Register the plugin tiddlers of a particular type, or null/undefined for any type, optionally restricting registration to an array of tiddler titles. Return the array of titles affected
this.registerPluginTiddlers = function(pluginType,titles) {
var self = this,
registeredTitles = [];
$tw.utils.each(pluginContents,function(pluginContent,pluginTitle) {
var pluginFields = pluginContent.fields;
if(!titles || titles.indexOf(pluginTitle) !== -1) {
if(!pluginType || pluginFields["plugin-type"] === pluginType) {
var disablingTiddler = self.getTiddler("$:/config/Plugins/Disabled/" + pluginTitle);
if(pluginTitle === "$:/core" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
self.unregisterPluginTiddlers(null,[pluginTitle]); // Unregister the plugin if it's already registered
pluginTiddlersInfo.push(pluginFields);
registeredTitles.push(pluginTitle);
registeredTitles = [],
checkTiddler = function(tiddler,title) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] && (!pluginType || tiddler.fields["plugin-type"] === pluginType)) {
var disablingTiddler = self.getTiddler("$:/config/Plugins/Disabled/" + title);
if(title === "$:/core" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
self.unregisterPluginTiddlers(null,[title]); // Unregister the plugin if it's already registered
pluginTiddlers.push(tiddler);
registeredTitles.push(tiddler.fields.title);
}
}
}
});
};
if(titles) {
$tw.utils.each(titles,function(title) {
checkTiddler(self.getTiddler(title),title);
});
} else {
this.each(function(tiddler,title) {
checkTiddler(tiddler,title);
});
}
return registeredTitles;
};
@@ -1492,11 +1457,11 @@ $tw.Wiki = function(options) {
var self = this,
unregisteredTitles = [];
// Remove any previous registered plugins of this type
for(var t=pluginTiddlersInfo.length-1; t>=0; t--) {
var pluginFields = pluginTiddlersInfo[t];
if(pluginFields["plugin-type"] && (!pluginType || pluginFields["plugin-type"] === pluginType) && (!titles || titles.indexOf(pluginFields.title) !== -1)) {
unregisteredTitles.push(pluginFields.title);
pluginTiddlersInfo.splice(t,1);
for(var t=pluginTiddlers.length-1; t>=0; t--) {
var tiddler = pluginTiddlers[t];
if(tiddler.fields["plugin-type"] && (!pluginType || tiddler.fields["plugin-type"] === pluginType) && (!titles || titles.indexOf(tiddler.fields.title) !== -1)) {
unregisteredTitles.push(tiddler.fields.title);
pluginTiddlers.splice(t,1);
}
}
return unregisteredTitles;
@@ -1506,33 +1471,36 @@ $tw.Wiki = function(options) {
this.unpackPluginTiddlers = function() {
var self = this;
// Sort the plugin titles by the `plugin-priority` field, if this field is missing, default to 1
pluginTiddlersInfo.sort(function(a, b) {
var priorityA = "plugin-priority" in a ? a["plugin-priority"] : 1,
priorityB = "plugin-priority" in b ? b["plugin-priority"] : 1;
pluginTiddlers.sort(function(a, b) {
var priorityA = "plugin-priority" in a.fields ? a.fields["plugin-priority"] : 1;
var priorityB = "plugin-priority" in b.fields ? b.fields["plugin-priority"] : 1;
if (priorityA !== priorityB) {
return priorityA - priorityB;
} else if (a.title < b.title) {
} else if (a.fields.title < b.fields.title) {
return -1;
} else if (a.title === b.title) {
} else if (a.fields.title === b.fields.title) {
return 0;
} else {
return +1;
}
});
// Now go through the plugins in ascending order and assign the shadows
shadowTiddlerInfo = Object.create(null);
$tw.utils.each(pluginTiddlersInfo,function(tiddlerFields) {
var contents = pluginContents[tiddlerFields.title].contents;
shadowTiddlers = Object.create(null);
$tw.utils.each(pluginTiddlers,function(tiddler) {
// Extract the constituent tiddlers
$tw.utils.each(contents,function(constituentTiddler,constituentTitle) {
// Save the tiddler object
shadowTiddlerInfo[constituentTitle] = {
source: tiddlerFields.title,
tiddler: new $tw.Tiddler(constituentTiddler,{title: constituentTitle})
};
});
if($tw.utils.hop(pluginInfo,tiddler.fields.title)) {
$tw.utils.each(pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) {
// Save the tiddler object
if(constituentTitle) {
shadowTiddlers[constituentTitle] = {
source: tiddler.fields.title,
tiddler: new $tw.Tiddler(constituentTiddler,{title: constituentTitle})
};
}
});
}
});
shadowTiddlerTitles = null; // Force regeneration of the shadow tiddler titles list
shadowTiddlerTitles = null;
this.clearCache(null);
this.clearGlobalCache();
$tw.utils.each(indexers,function(indexer) {
@@ -1976,7 +1944,7 @@ $tw.boot.excludeRegExp = /^\.DS_Store$|^.*\.meta$|^\..*\.swp$|^\._.*$|^\.git$|^\
/*
Load all the tiddlers recursively from a directory, including honouring `tiddlywiki.files` files for drawing in external files. Returns an array of {filepath:,type:,tiddlers: [{..fields...}],hasMetaFile:}. Note that no file information is returned for externally loaded tiddlers, just the `tiddlers` property.
*/
$tw.loadTiddlersFromPath = function(filepath,excludeRegExp,excludePluginInfo) {
$tw.loadTiddlersFromPath = function(filepath,excludeRegExp) {
excludeRegExp = excludeRegExp || $tw.boot.excludeRegExp;
var tiddlers = [];
if(fs.existsSync(filepath)) {
@@ -1986,17 +1954,11 @@ $tw.loadTiddlersFromPath = function(filepath,excludeRegExp,excludePluginInfo) {
// Look for a tiddlywiki.files file
if(files.indexOf("tiddlywiki.files") !== -1) {
Array.prototype.push.apply(tiddlers,$tw.loadTiddlersFromSpecification(filepath,excludeRegExp));
} else if(files.indexOf("plugin.info") !== -1 && !excludePluginInfo) {
// Load sub-plugin
var tiddler = $tw.loadPluginFolder(filepath);
if(tiddler) {
tiddlers.push({tiddlers: [tiddler]});
}
} else {
// If not, read all the files in the directory
$tw.utils.each(files,function(file) {
if(!excludeRegExp.test(file) && file !== "plugin.info") {
tiddlers.push.apply(tiddlers,$tw.loadTiddlersFromPath(path.join(filepath,file),excludeRegExp));
tiddlers.push.apply(tiddlers,$tw.loadTiddlersFromPath(filepath + path.sep + file,excludeRegExp));
}
});
}
@@ -2143,69 +2105,54 @@ Load the tiddlers from a plugin folder, and package them up into a proper JSON p
*/
$tw.loadPluginFolder = function(filepath,excludeRegExp) {
excludeRegExp = excludeRegExp || $tw.boot.excludeRegExp;
function readPluginFields(filepath) {
if(!(fs.existsSync(filepath) && fs.statSync(filepath).isDirectory())) {
return null;
}
var infoPath = path.join(filepath,"plugin.info");
// Read the plugin information file
var infoPath = filepath + path.sep + "plugin.info";
if(fs.existsSync(filepath) && fs.statSync(filepath).isDirectory()) {
// Read the plugin information
if(!fs.existsSync(infoPath) || !fs.statSync(infoPath).isFile()) {
console.log("Warning: missing plugin.info file in " + filepath);
return null;
}
var pluginFields = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"),{});
if(!(pluginFields.title && pluginFields.name)) {
return null;
var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"),function() {return null;});
if(!pluginInfo) {
console.log("warning: invalid JSON in plugin.info file at " + infoPath);
pluginInfo = {};
}
// Give the plugin the same version number as the core if it doesn't have one
if(!("version" in pluginFields)) {
pluginFields.version = $tw.packageInfo.version;
}
// Use "plugin" as the plugin-type if we don't have one
if(!("plugin-type" in pluginFields)) {
pluginFields["plugin-type"] = "plugin";
}
// Set the dependents and type
pluginFields.dependents = pluginFields.dependents || [];
pluginFields.type = "application/json";
// Deserialise array fields (currently required for the dependents field)
for(var field in pluginFields) {
if($tw.utils.isArray(pluginFields[field])) {
pluginFields[field] = $tw.utils.stringifyList(pluginFields[field]);
}
}
return pluginFields;
}
function readPluginTiddlers(tiddlersPath) {
var pluginFiles = $tw.loadTiddlersFromPath(tiddlersPath,excludeRegExp,true),
pluginTiddlers = {};
// Save the plugin tiddlers into the plugin payload
// Read the plugin files
var pluginFiles = $tw.loadTiddlersFromPath(filepath,excludeRegExp);
// Save the plugin tiddlers into the plugin info
pluginInfo.tiddlers = pluginInfo.tiddlers || Object.create(null);
for(var f=0; f<pluginFiles.length; f++) {
var tiddlers = pluginFiles[f].tiddlers;
if(!tiddlers) {
console.log(`Gosh ${JSON.stringify(pluginFiles[f])}`)
}
for(var t=0; t<tiddlers.length; t++) {
var tiddler= tiddlers[t];
if(tiddler.title) {
pluginTiddlers[tiddler.title] = tiddler;
pluginInfo.tiddlers[tiddler.title] = tiddler;
}
}
}
return pluginTiddlers;
// Give the plugin the same version number as the core if it doesn't have one
if(!("version" in pluginInfo)) {
pluginInfo.version = $tw.packageInfo.version;
}
// Use "plugin" as the plugin-type if we don't have one
if(!("plugin-type" in pluginInfo)) {
pluginInfo["plugin-type"] = "plugin";
}
pluginInfo.dependents = pluginInfo.dependents || [];
pluginInfo.type = "application/json";
// Set plugin text
pluginInfo.text = JSON.stringify({tiddlers: pluginInfo.tiddlers});
delete pluginInfo.tiddlers;
// Deserialise array fields (currently required for the dependents field)
for(var field in pluginInfo) {
if($tw.utils.isArray(pluginInfo[field])) {
pluginInfo[field] = $tw.utils.stringifyList(pluginInfo[field]);
}
}
return pluginInfo;
} else {
return null;
}
// Get the primary plugin fields
var pluginFields = readPluginFields(filepath);
if(!pluginFields) {
console.log("Warning: missing or invalid plugin.info file in " + filepath);
return null;
}
// The payload to be stored in the plugin text field in JSON
var pluginPayload = {tiddlers: {}};
// Get the constituent tiddlers of the plugin
pluginPayload.tiddlers = readPluginTiddlers(filepath);
// Set plugin text
pluginFields.text = JSON.stringify(pluginPayload);
return pluginFields;
};
/*

View File

@@ -3,8 +3,5 @@ name: en-GB
description: English (British)
author: JeremyRuston
core-version: >=5.0.0"
plugin-type: language
type: application/json
hidden: yes
{tidddlers:{}}
Stub pseudo-plugin for the default language

View File

@@ -0,0 +1,8 @@
title: $:/language/Help/echo
description: Displays all the passed arguments
Displays all the passed arguments to a command. Useful for debugging.
```
--echo <text> *
```

View File

@@ -13,7 +13,7 @@ The $tw.Commander class is a command interpreter
Parse a sequence of commands
commandTokens: an array of command string tokens
wiki: reference to the wiki store object
streams: {output:, error:}, each of which has a write(string) method
streams: {output:, input:, error:}
callback: a callback invoked as callback(err) where err is null if there was no error
*/
var Commander = function(commandTokens,callback,wiki,streams) {
@@ -61,69 +61,172 @@ Commander.prototype.execute = function() {
this.executeNextCommand();
};
/*
Returns the next string token without consuming it, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.peekNextToken = function(callback) {
var self = this;
if(this.nextToken >= this.commandTokens.length) {
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken,function(err,data) {
if(!err) {
// Save the stringified token for next time so that we don't run prompts twice
self.commandTokens[self.nextToken] = data;
}
callback(err,data);
});
}
};
/*
Returns and consumes the next string token, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.getNextToken = function(callback) {
if(this.nextToken >= this.commandTokens.length) {
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken++,callback);
}
};
/*
Returns and consumes the string tokens until the end of the token stream or the first token that starts with "--".
Callback invoked(err,tokenArray)
*/
Commander.prototype.getTokensUntilCommand = function(callback) {
var self = this,
tokens = [];
function processNextToken() {
self.peekNextToken(function(err,data) {
if(err) {
return callback(err);
}
if(data === null || data.substr(0,2) === "--") {
return callback(null,tokens);
} else {
self.getNextToken(function(err,data) {
if(err) {
return callback(err);
}
tokens.push(data);
processNextToken();
});
}
});
}
processNextToken();
};
/*
Returns a specified stringified token, or null if the index does not exist. Callback invoked(err,data)
*/
Commander.prototype.stringifyToken = function(index,callback) {
var self = this;
if(index >= this.commandTokens.length) {
return callback(null,null);
} else {
var token = this.commandTokens[index];
if(typeof token === "string") {
return callback(null,token);
} else if(typeof token === "object") {
switch(token.type) {
case "filter":
return callback(null,this.wiki.filterTiddlers(token.text)[0] || "");
case "wikify":
return callback(null,this.wiki.renderText("text/plain","text/vnd.tiddlywiki",token.text,{
parseAsInline: false,
parentWidget: $tw.rootWidget
}));
case "prompt":
$tw.utils.terminalQuestion({
promptText: token.prompt || "Please enter a value",
defaultResult: token["default"] || "",
callback: function(err,userText) {
if(err) {
callback(err);
} else {
if(token.transformFilter) {
userText = self.wiki.filterTiddlers(token.transformFilter,null,self.wiki.makeTiddlerIterator([userText]))[0] || "";
}
callback(null,userText);
}
},
input: self.streams.input,
output: self.streams.output,
});
break;
default:
throw "Unknown dynamic command token type: " + token.type;
}
}
}
};
/*
Execute the next command in the sequence
*/
Commander.prototype.executeNextCommand = function() {
var self = this;
// Invoke the callback if there are no more commands
if(this.nextToken >= this.commandTokens.length) {
this.callback(null);
} else {
// Get and check the command token
var commandName = this.commandTokens[this.nextToken++];
// Get and check the command token
var commandName = this.getNextToken(function(err,commandName) {
if(err) {
return self.callback(err);
}
if(!commandName) {
return self.callback(null);
}
if(commandName.substr(0,2) !== "--") {
this.callback("Missing command: " + commandName);
return self.callback("Missing command: " + commandName);
} else {
commandName = commandName.substr(2); // Trim off the --
// Accumulate the parameters to the command
var params = [];
while(this.nextToken < this.commandTokens.length &&
this.commandTokens[this.nextToken].substr(0,2) !== "--") {
params.push(this.commandTokens[this.nextToken++]);
}
// Get the command info
var command = $tw.commands[commandName],
c,err;
if(!command) {
this.callback("Unknown command: " + commandName);
} else {
if(this.verbose) {
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
// Get the parameters to the command
self.getTokensUntilCommand(function(err,params) {
if(err) {
return self.callback(err);
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return this.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,this);
err = c.execute();
if(err) {
this.callback(err);
} else {
this.executeNextCommand();
}
var command = $tw.commands[commandName],
c,err;
if(!command) {
self.callback("Unknown command: " + commandName);
} else {
// Asynchronous command
c = new command.Command(params,this,function(err) {
if(self.verbose) {
self.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = self.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return self.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,self);
err = c.execute();
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
});
err = c.execute();
if(err) {
this.callback(err);
} else {
// Asynchronous command
c = new command.Command(params,self,function(err) {
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
});
err = c.execute();
if(err) {
self.callback(err);
}
}
}
}
});
}
}
});
};
/*

View File

@@ -0,0 +1,32 @@
/*\
title: $:/core/modules/commands/echo.js
type: application/javascript
module-type: command
Command to echo input parameters
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "echo",
synchronous: true
};
var Command = function(params,commander) {
this.params = params;
this.commander = commander;
};
Command.prototype.execute = function() {
this.commander.streams.output.write(JSON.stringify(this.params,null,4) + "\n");
return null;
};
exports.Command = Command;
})();

View File

@@ -1,36 +0,0 @@
/*\
title: $:/core/modules/filters/is/plugin.js
type: application/javascript
module-type: isfilteroperator
Filter function for [is[plugin]]
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.plugin = function(source,prefix,options) {
var results = [];
if(prefix === "!") {
source(function(tiddler,title) {
if(!tiddler.isPlugin()) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(tiddler.isPlugin()) {
results.push(title);
}
});
}
return results;
};
})();

View File

@@ -35,7 +35,6 @@ function PluginSwitcher(options) {
}
PluginSwitcher.prototype.switchPlugins = function() {
var self = this;
// Get the name of the current theme
var selectedPluginTitle = this.wiki.getTiddlerText(this.controllerTitle);
// If it doesn't exist, then fallback to one of the default themes
@@ -50,51 +49,22 @@ PluginSwitcher.prototype.switchPlugins = function() {
var tiddler = self.wiki.getTiddler(title);
if(tiddler && tiddler.isPlugin() && plugins.indexOf(title) === -1) {
plugins.push(title);
var dependents = $tw.utils.parseStringArray(tiddler.fields.dependents || "");
var pluginInfo = $tw.utils.parseJSONSafe(self.wiki.getTiddlerText(title)),
dependents = $tw.utils.parseStringArray(tiddler.fields.dependents || "");
$tw.utils.each(dependents,function(title) {
accumulatePlugin(title);
});
}
};
accumulatePlugin(selectedPluginTitle);
var selectedPluginTiddler = this.wiki.getTiddler(selectedPluginTitle);
// Accumulate any other plugins of the same type with the same name
this.wiki.eachTiddlerPlusShadows(function(tiddler,title) {
if(tiddler.isPlugin() && tiddler.fields["plugin-type"] === self.pluginType && tiddler.fields.name === selectedPluginTiddler.fields.name) {
accumulatePlugin(title);
}
});
// Read the plugin info for the incoming plugins
var changedPluginInfo = this.wiki.readPluginInfo(plugins);
// Collect the shadow tiddlers of any deleted plugins
var changedShadowTiddlers = {};
$tw.utils.each(changedPluginInfo.deletedPlugins,function(pluginTitle) {
var contents = changedPluginInfo.deletedPluginContents[pluginTitle];
if(contents && contents.tiddlers) {
$tw.utils.each(Object.keys(contents.tiddlers),function(title) {
changedShadowTiddlers[title] = true;
});
}
});
// Collect the shadow tiddlers of any modified plugins
$tw.utils.each(changedPluginInfo.modifiedPlugins,function(pluginTitle) {
var pluginInfo = self.wiki.getPluginInfo(pluginTitle);
if(pluginInfo && pluginInfo.tiddlers) {
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
changedShadowTiddlers[title] = false;
});
}
});
// Unregister any existing theme/language tiddlers
var unregisteredTiddlers = this.wiki.unregisterPluginTiddlers(this.pluginType);
// Register any new theme/language tiddlers
var registeredTiddlers = this.wiki.registerPluginTiddlers(this.pluginType,plugins);
// Unpack the current theme/language tiddlers
this.wiki.unpackPluginTiddlers();
// Queue change events for the changed shadow tiddlers
$tw.utils.each(changedShadowTiddlers,function(status,title) {
self.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title], true);
});
var changes = $tw.wiki.readPluginInfo(plugins);
// Unregister any existing theme tiddlers
var unregisteredTiddlers = $tw.wiki.unregisterPluginTiddlers(this.pluginType);
// Register any new theme tiddlers
var registeredTiddlers = $tw.wiki.registerPluginTiddlers(this.pluginType,plugins);
// Unpack the current theme tiddlers
$tw.wiki.unpackPluginTiddlers();
// Call the switch handler
if(this.onSwitch) {
this.onSwitch(plugins);

View File

@@ -26,7 +26,11 @@ exports.startup = function(callback) {
callback();
},
$tw.wiki,
{output: process.stdout, error: process.stderr}
{
output: process.stdout,
input: process.stdin,
error: process.stderr
}
);
commander.execute();
};

View File

@@ -15,7 +15,67 @@ exports.after = ["load-modules"];
exports.before = ["startup"];
exports.synchronous = true;
var TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE = "$:/status/RequireReloadDueToPluginChange";
var PREFIX_CONFIG_REGISTER_PLUGIN_TYPE = "$:/config/RegisterPluginType/";
exports.startup = function() {
$tw.utils.installPluginChangeHandler($tw.wiki);
$tw.wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "no"});
$tw.wiki.addEventListener("change",function(changes) {
// Work out which of the changed tiddlers are plugins that we need to reregister
var changesToProcess = [],
requireReloadDueToPluginChange = false;
$tw.utils.each(Object.keys(changes),function(title) {
var tiddler = $tw.wiki.getTiddler(title),
requiresReload = $tw.wiki.doesPluginRequireReload(title);
if(requiresReload) {
requireReloadDueToPluginChange = true;
} else if(tiddler) {
var pluginType = tiddler.fields["plugin-type"];
if($tw.wiki.getTiddlerText(PREFIX_CONFIG_REGISTER_PLUGIN_TYPE + (tiddler.fields["plugin-type"] || ""),"no") === "yes") {
changesToProcess.push(title);
}
}
});
// Issue warning if any of the tiddlers require a reload
if(requireReloadDueToPluginChange) {
$tw.wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "yes"});
}
// Read or delete the plugin info of the changed tiddlers
if(changesToProcess.length > 0) {
var changes = $tw.wiki.readPluginInfo(changesToProcess);
if(changes.modifiedPlugins.length > 0 || changes.deletedPlugins.length > 0) {
var changedShadowTiddlers = {};
// Collect the shadow tiddlers of any deleted plugins
$tw.utils.each(changes.deletedPlugins,function(pluginTitle) {
var pluginInfo = $tw.wiki.getPluginInfo(pluginTitle);
if(pluginInfo) {
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
changedShadowTiddlers[title] = true;
});
}
});
// Collect the shadow tiddlers of any modified plugins
$tw.utils.each(changes.modifiedPlugins,function(pluginTitle) {
var pluginInfo = $tw.wiki.getPluginInfo(pluginTitle);
if(pluginInfo && pluginInfo.tiddlers) {
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
changedShadowTiddlers[title] = false;
});
}
});
// (Re-)register any modified plugins
$tw.wiki.registerPluginTiddlers(null,changes.modifiedPlugins);
// Unregister any deleted plugins
$tw.wiki.unregisterPluginTiddlers(null,changes.deletedPlugins);
// Unpack the shadow tiddlers
$tw.wiki.unpackPluginTiddlers();
// Queue change events for the changed shadow tiddlers
$tw.utils.each(Object.keys(changedShadowTiddlers),function(title) {
$tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title], true);
});
}
}
});
};

View File

@@ -13,6 +13,10 @@ exports.hasTag = function(tag) {
return this.fields.tags && this.fields.tags.indexOf(tag) !== -1;
};
exports.isPlugin = function() {
return this.fields.type === "application/json" && this.hasField("plugin-type");
};
exports.isDraft = function() {
return this.hasField("draft.of");
};

View File

@@ -1,95 +0,0 @@
/*\
title: $:/core/modules/utils/plugins.js
type: application/javascript
module-type: utils
Plugin utilities
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE = "$:/status/RequireReloadDueToPluginChange";
var PREFIX_CONFIG_REGISTER_PLUGIN_TYPE = "$:/config/RegisterPluginType/";
exports.installPluginChangeHandler = function(wiki) {
wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "no"});
wiki.addEventListener("change",function(changes) {
// Work out which of the changed tiddlers are plugins that we need to (re)register
var changesToProcess = [];
$tw.utils.each(Object.keys(changes),function(title) {
var tiddler = wiki.getTiddler(title);
if(tiddler) {
// It is a plugin that has been added or modified and is of a type that we need to register
if(tiddler.isPlugin() && wiki.getTiddlerText(PREFIX_CONFIG_REGISTER_PLUGIN_TYPE + (tiddler.fields["plugin-type"] || ""),"no") === "yes") {
changesToProcess.push(title);
}
} else {
if(wiki.isSubPlugin(title)) {
// It is a sub-plugin
changesToProcess.push(title);
} else {
// It is a plugin that has been deleted
var pluginInfo = wiki.getPluginInfo(title)
if(pluginInfo) {
changesToProcess.push(title);
}
}
}
});
if(changesToProcess.length > 0) {
// Read the plugin info of the changed tiddlers
var changedPluginInfo = wiki.readPluginInfo(changesToProcess);
if(changedPluginInfo.modifiedPlugins.length > 0 || changedPluginInfo.deletedPlugins.length > 0) {
var changedShadowTiddlers = {},
requireReloadDueToPluginChange = false;
// Collect the shadow tiddlers of any deleted plugins
$tw.utils.each(changedPluginInfo.deletedPlugins,function(pluginTitle) {
var contents = changedPluginInfo.deletedPluginContents[pluginTitle];
if(contents && contents.tiddlers) {
$tw.utils.each(Object.keys(contents.tiddlers),function(title) {
changedShadowTiddlers[title] = true;
if(contents.tiddlers[title].type === "application/javascript") {
requireReloadDueToPluginChange = true;
}
});
}
});
// Collect the shadow tiddlers of any modified plugins
$tw.utils.each(changedPluginInfo.modifiedPlugins,function(pluginTitle) {
var pluginInfo = wiki.getPluginInfo(pluginTitle);
if(pluginInfo && pluginInfo.tiddlers) {
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
changedShadowTiddlers[title] = false;
if(pluginInfo.tiddlers[title].type === "application/javascript") {
requireReloadDueToPluginChange = true;
}
});
}
});
// (Re-)register any modified plugins
wiki.registerPluginTiddlers(null,changedPluginInfo.modifiedPlugins);
// Unregister any deleted plugins
wiki.unregisterPluginTiddlers(null,changedPluginInfo.deletedPlugins);
// Unpack the shadow tiddlers
wiki.unpackPluginTiddlers();
// Queue change events for the changed shadow tiddlers
$tw.utils.each(changedShadowTiddlers,function(status,title) {
wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title], true);
});
// Issue warning if any of the tiddlers require a reload
if(requireReloadDueToPluginChange) {
wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "yes"});
}
}
}
});
};
})();

View File

@@ -18,29 +18,127 @@ exports.log = function(text,colour) {
console.log($tw.node ? exports.terminalColour(colour) + text + exports.terminalColour() : text);
};
exports.terminalColour = function(colour) {
/*
Prompts the user for input and handles the input using a callback function. Options:
promptText: Prompt text
defaultResult: Default result returned if the user types enter
callback: callback invoked (err,usertext)
input: optional input stream
output: optional output stream
*/
exports.terminalQuestion = function(options) {
var readline = require("readline"),
promptText = options.promptText,
defaultResult = options.defaultResult,
callback = options.callback,
inputStream = options.input || process.stdin,
outputStream = options.output || process.stdout;
var rl = readline.createInterface({
input: inputStream,
output: outputStream
});
// Handle errors on the input stream
rl.on("error",function(err) {
rl.close();
callback(err); // Pass the error to the callback
});
// Prompt user for input
var prompt = exports.terminalColourText(promptText,["yellow","bold"]);
if(defaultResult) {
prompt += " ";
prompt += exports.terminalColourText("(" + defaultResult + ")",["blue","underline"]);
}
prompt += exports.terminalColourText(": ");
rl.question(prompt,function(input) {
// Use default value for the empty string
if(input === "" && defaultResult) {
input = defaultResult;
}
callback(null,input); // Pass the input to the callback
rl.close();
});
};
/*
Wrap a string in colour codes. Colour can be an array
*/
exports.terminalColourText = function(text,colour) {
if(!$tw.utils.isArray(colour)) {
colour = [colour];
}
$tw.utils.each(colour,function(code) {
text = exports.terminalColour(code) + text + exports.terminalColour(code,true);
});
return text;
};
/*
Returns a terminal colour string. Set "closed" to true to return the closing code
*/
exports.terminalColour = function(colour,closed) {
if(!$tw.browser && $tw.node && process.stdout.isTTY) {
if(colour) {
var code = exports.terminalColourLookup[colour];
if(code) {
return "\x1b[" + code + "m";
return "\x1b[" + code[closed ? 1 : 0] + "m";
}
} else {
return "\x1b[0m"; // Cancel colour
}
return "\x1b[0m"; // Reset
}
return "";
};
exports.terminalColourLookup = {
"black": "0;30",
"red": "0;31",
"green": "0;32",
"brown/orange": "0;33",
"blue": "0;34",
"purple": "0;35",
"cyan": "0;36",
"light gray": "0;37"
// Modifiers
"reset": [0,0],
"bold": [1,22],
"dim": [2,22],
"italic": [3,23],
"underline": [4,24],
"overline": [53,55],
"inverse": [7,27],
"hidden": [8,28],
"strikethrough": [9,29],
// Colours
"black": [30,39],
"red": [31,39],
"green": [32,39],
"yellow": [33,39],
"brown/orange": [33,39], // Backwards compatbility
"blue": [34,39],
"magenta": [35,39], // Backwards compatbility
"purple": [35,39],
"cyan": [36,39],
"white": [37,39],
"gray": [90,39],
"light red": [91,39],
"light green": [92,39],
"light yellow": [93,39],
"light blue": [94,39],
"light magenta": [95,39],
"light purple": [95,39], // Backwards compatbility
"light cyan": [96,39],
"light gray": [97,39],
// Background colours
"black background": [40,49],
"red background": [41,49],
"green background": [42,49],
"yellow background": [43,49],
"brown/orange background": [43,49], // Backwards compatbility
"blue background": [44,49],
"magenta background": [45,49], // Backwards compatbility
"purple background": [45,49],
"cyan background": [46,49],
"white background": [47,49],
"gray background": [100,49],
"light red background": [101,49],
"light green background": [102,49],
"light yellow background": [103,49],
"light blue background": [104,49],
"light magenta background": [105,49],
"light purple background": [105,49], // Backwards compatbility
"light cyan background": [106,49],
"light gray background": [107,49]
};
/*

View File

@@ -66,24 +66,11 @@ TestCaseWidget.prototype.render = function(parent,nextSibling) {
});
var jsonPayload = JSON.stringify(tiddlers);
this.testcaseWiki.addTiddlers(tiddlers);
// Suppress next tick dispatch for wiki change events
this.testcaseWiki.setDispatchMode(true);
// Unpack plugin tiddlers
this.testcaseWiki.readPluginInfo();
this.testcaseWiki.registerPluginTiddlers("plugin");
this.testcaseWiki.unpackPluginTiddlers();
this.testcaseWiki.addIndexersToWiki();
// Install the plugin change event handler
$tw.utils.installPluginChangeHandler(this.testcaseWiki);
// Install the language switcher
var languageSwitcher = new $tw.PluginSwitcher({
wiki: this.testcaseWiki,
pluginType: "language",
controllerTitle: "$:/language",
defaultPlugins: [
"$:/languages/en-GB"
]
});
// Generate a `transclusion` variable that depends on the values of the payload tiddlers so that the template can easily make unique state tiddlers
this.setVariable("transclusion",$tw.utils.hashString(jsonPayload));
// Generate a `payloadTiddlers` variable that contains the payload in JSON format
@@ -105,20 +92,13 @@ TestCaseWidget.prototype.render = function(parent,nextSibling) {
}
});
testcaseOutputWidget.render(testcaseOutputContainer);
// Install the wiki change event handler
this.testcaseWiki.addEventListener("change",function(changes) {
testcaseOutputWidget.refresh(changes,testcaseOutputContainer);
});
}
// Clear changes queue
this.testcaseWiki.clearTiddlerEventQueue();
// Run the actions if provided
if(this.testcaseWiki.tiddlerExists(this.testcaseTestActions)) {
testcaseOutputWidget.invokeActionString(this.testcaseWiki.getTiddlerText(this.testcaseTestActions));
// Make sure all wiki events have been cleared
while(this.testcaseWiki.eventsTriggered) {
this.testcaseWiki.processOutstandingTiddlerEvents();
}
testcaseOutputWidget.refresh(this.testcaseWiki.changedTiddlers,testcaseOutputContainer);
}
// Set up the test result variables
var testResult = "",
@@ -148,8 +128,6 @@ TestCaseWidget.prototype.render = function(parent,nextSibling) {
parentWidget: this
});
rootWidget.render(domNode);
// Re-enable next tick dispatch for wiki change events
this.testcaseWiki.setDispatchMode(false);
// Trap changes in the wiki and refresh the rendering
this.testcaseWiki.addEventListener("change",function(changes) {
rootWidget.refresh(changes,domNode);

View File

@@ -132,14 +132,6 @@ exports.dispatchEvent = function(type /*, args */) {
}
};
/*
false (default) to dispatch event tick as usual
true to suppress dispatching the event tick and allow outstanding events to be processed manually
*/
exports.setDispatchMode = function(mode) {
this.dispatchMode = mode;
}
/*
Causes a tiddler to be marked as changed, incrementing the change count, and triggers event handlers.
This method should be called after the changes it describes have been made to the wiki.tiddlers[] array.
@@ -164,18 +156,17 @@ exports.enqueueTiddlerEvent = function(title,isDeleted,isShadow) {
}
// Trigger events
this.eventListeners = this.eventListeners || {};
if(!this.eventsTriggered && !this.dispatchMode) {
$tw.utils.nextTick(this.processOutstandingTiddlerEvents.bind(this));
}
this.eventsTriggered = true;
};
exports.processOutstandingTiddlerEvents = function() {
var changes = this.changedTiddlers;
this.changedTiddlers = Object.create(null);
this.eventsTriggered = false;
if($tw.utils.count(changes) > 0) {
this.dispatchEvent("change",changes);
if(!this.eventsTriggered) {
var self = this;
$tw.utils.nextTick(function() {
var changes = self.changedTiddlers;
self.changedTiddlers = Object.create(null);
self.eventsTriggered = false;
if($tw.utils.count(changes) > 0) {
self.dispatchEvent("change",changes);
}
});
this.eventsTriggered = true;
}
};
@@ -1749,7 +1740,7 @@ exports.invokeUpgraders = function(titles,tiddlers) {
// Determine whether a plugin by title is dynamically loadable
exports.doesPluginRequireReload = function(title) {
var tiddler = this.getTiddler(title);
if(tiddler && tiddler.isPlugin()) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"]) {
if(tiddler.fields["plugin-type"] === "import") {
// The import plugin never requires reloading
return false;

View File

@@ -8,7 +8,7 @@ caption: {{$:/language/ControlPanel/Plugins/Caption}}
\whitespace trim
<$set name="plugin-type" value="""$type$""">
<$set name="qualified-state" value=<<qualify "$:/state/plugin-info">>>
<$list filter="[all[tiddlers+shadows]!has[draft.of]plugin-type[$type$]!field:hidden[yes]sort[name]]" emptyMessage=<<lingo "Empty/Hint">> template="$:/core/ui/Components/plugin-info"/>
<$list filter="[!has[draft.of]plugin-type[$type$]sort[name]]" emptyMessage=<<lingo "Empty/Hint">> template="$:/core/ui/Components/plugin-info"/>
</$set>
</$set>
\end

View File

@@ -1,5 +1,5 @@
title: $:/core/ui/ControlPanel/Plugins/Installed/Languages
tags: $:/tags/ControlPanel/Plugins
caption: {{$:/language/ControlPanel/Plugins/Languages/Caption}} (<$count filter="[all[tiddlers+shadows]is[plugin]!field:hidden[yes]plugin-type[language]]"/>)
caption: {{$:/language/ControlPanel/Plugins/Languages/Caption}} (<$count filter="[!has[draft.of]plugin-type[language]]"/>)
<<plugin-table language>>

View File

@@ -1,5 +1,5 @@
title: $:/core/ui/ControlPanel/Plugins/Installed/Plugins
tags: $:/tags/ControlPanel/Plugins
caption: {{$:/language/ControlPanel/Plugins/Plugins/Caption}} (<$count filter="[all[tiddlers+shadows]is[plugin]plugin-type[plugin]]"/>)
caption: {{$:/language/ControlPanel/Plugins/Plugins/Caption}} (<$count filter="[!has[draft.of]plugin-type[plugin]]"/>)
<<plugin-table plugin>>

View File

@@ -1,5 +1,5 @@
title: $:/core/ui/ControlPanel/Plugins/Installed/Themes
tags: $:/tags/ControlPanel/Plugins
caption: {{$:/language/ControlPanel/Plugins/Themes/Caption}} (<$count filter="[all[tiddlers+shadows]is[plugin]plugin-type[theme]]"/>)
caption: {{$:/language/ControlPanel/Plugins/Themes/Caption}} (<$count filter="[!has[draft.of]plugin-type[theme]]"/>)
<<plugin-table theme>>

View File

@@ -103,8 +103,7 @@ code-body: yes
-Description
-Narrative
-[[$:/temp/testcase/draft-title]]
-[[$:/status/RequireReloadDueToPluginChange]]
-[[$:/core]]
-[has[plugin-type]]
-[prefix<tf.state>]
-[prefix[$:/state/popup/export]]
-[prefix[$:/HistoryList]]

View File

@@ -1,4 +1,4 @@
title: $:/core/ui/testcases/actions/Export
tags: $:/tags/TestCase/Actions
<$macrocall $name="exportButton" exportFilter="[all[tiddlers]sort[]] -[prefix[$:/state/]] -Description -Narrative -ExpectedResult -[[$:/core]]" lingoBase="$:/language/Buttons/ExportTiddlers/"/>
<$macrocall $name="exportButton" exportFilter="[all[tiddlers]sort[]] -[prefix[$:/state/]] -Description -Narrative -ExpectedResult -[has[plugin-type]]" lingoBase="$:/language/Buttons/ExportTiddlers/"/>

View File

@@ -1,36 +0,0 @@
title: Plugins/DynamicJSPluginLoad
description: Dynamic JS plugin loading
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{$:/status/RequireReloadDueToPluginChange}}
+
title: Actions
<$action-createtiddler $basetitle="$:/plugins/tiddlywiki/test-js-plugin" $template="$:/plugins/tiddlywiki/test-js-plugin/raw" type="application/json"/>
+
title: $:/plugins/tiddlywiki/test-js-plugin/raw
list: readme
name: bundled-subplugin-tests
plugin-type: plugin
type: text/plain
{
"tiddlers": {
"$:/plugins/tiddlywiki/test-js-plugin/readme": {
"title": "$:/plugins/tiddlywiki/test-js-plugin/readme",
"text": "Readme from $:/plugins/tiddlywiki/test-js-plugin\n\n"
},
"$:/plugins/tiddlywiki/test-js-plugin/js": {
"title": "$:/plugins/tiddlywiki/test-js-plugin/js",
"text": "2+2",
"type": "application/javascript"
}
}
}
+
title: ExpectedResult
<p>yes</p>

View File

@@ -1,47 +0,0 @@
title: Plugins/DynamicLoadLanguage
description: Dynamically loading of language plugin
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{First}}
+
title: Actions
<$action-createtiddler $basetitle="$:/languages/fr-FR" $template="$:/languages/fr-FR/raw" type="application/json"/>
<$action-setfield $tiddler="$:/languages/fr-FR" plugin-type="language"/>
<$action-setfield $tiddler="$:/language" text="$:/languages/fr-FR"/>
+
title: $:/languages/en-GB
list: readme
name: en-GB
plugin-type: language
type: application/json
{
"tiddlers": {
"First": {
"title": "First",
"text": "This is English"
}
}
}
+
title: $:/languages/fr-FR/raw
list: readme
name: fr-FR
type: plain/text
{
"tiddlers": {
"First": {
"title": "First",
"text": "This is French"
}
}
}
+
title: ExpectedResult
<p>This is French</p>

View File

@@ -1,46 +0,0 @@
title: Plugins/Language
description: Loading of correct language plugin at startup
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{First}}
+
title: $:/language
$:/languages/fr-FR
+
title: $:/languages/en-GB
list: readme
name: en-GB
plugin-type: language
type: application/json
{
"tiddlers": {
"First": {
"title": "First",
"text": "This is English"
}
}
}
+
title: $:/languages/fr-FR
list: readme
name: fr-FR
plugin-type: language
type: application/json
{
"tiddlers": {
"First": {
"title": "First",
"text": "This is French"
}
}
}
+
title: ExpectedResult
<p>This is French</p>

View File

@@ -1,57 +0,0 @@
title: Plugins/LanguageAddonSubPlugin
description: Loading of correct language subplugin at startup
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{First}}
{{Second}}
+
title: $:/language
$:/languages/fr-FR-subplugin
+
title: $:/languages/fr-FR-container
list: readme
name: fr-FR
plugin-type: plugin
type: application/json
{
"tiddlers": {
"$:/languages/fr-FR-container/readme": {
"title": "$:/languages/fr-FR-container/readme",
"text": "Readme from ~$:/languages/fr-FR-container\n\n"
},
"$:/languages/fr-FR-subplugin": {
"title": "$:/languages/fr-FR-subplugin",
"name": "fr-FR",
"description": "fr-FR subplugin",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL",
"version": "5.3.7-prerelease",
"plugin-type": "language",
"dependents": "",
"type": "application/json",
"text": "{\"tiddlers\":{\"First\":{\"title\":\"First\",\"text\":\"First from ~$:/languages/fr-FR-subplugin\"}}}"
},
"$:/languages/fr-FR-subplugin2": {
"title": "$:/languages/fr-FR-subplugin2",
"name": "fr-FR",
"description": "fr-FR subplugin2",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL",
"version": "5.3.7-prerelease",
"plugin-type": "language",
"dependents": "",
"type": "application/json",
"text": "{\"tiddlers\":{\"Second\":{\"title\":\"Second\",\"text\":\"Second from ~$:/languages/fr-FR-subplugin2\"}}}"
}
}
}
+
title: ExpectedResult
<p>First from $:/languages/fr-FR-subplugin</p><p>Second from $:/languages/fr-FR-subplugin2</p>

View File

@@ -1,45 +0,0 @@
title: Plugins/LanguageSubPlugin
description: Loading of correct language subplugin at startup
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{First}}
{{$:/languages/fr-FR-container/readme}}
+
title: $:/language
$:/languages/fr-FR-subplugin
+
title: $:/languages/fr-FR-container
list: readme
name: fr-FR
plugin-type: plugin
type: application/json
{
"tiddlers": {
"$:/languages/fr-FR-container/readme": {
"title": "$:/languages/fr-FR-container/readme",
"text": "Readme from ~$:/languages/fr-FR-container\n\n"
},
"$:/languages/fr-FR-subplugin": {
"title": "$:/languages/fr-FR-subplugin",
"name": "fr-FR",
"description": "fr-FR subplugin",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL",
"version": "5.3.7-prerelease",
"plugin-type": "language",
"dependents": "",
"type": "application/json",
"text": "{\"tiddlers\":{\"First\":{\"title\":\"First\",\"text\":\"First from ~$:/languages/fr-FR-subplugin\"}}}"
}
}
}
+
title: ExpectedResult
<p>First from $:/languages/fr-FR-subplugin</p><p>Readme from $:/languages/fr-FR-container</p>

View File

@@ -1,43 +0,0 @@
title: SubPlugins/DynamicSubPluginLoad
description: Dynamic sub-plugin loading
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{$:/plugins/tiddlywiki/test-subplugin/readme}}
+
title: Actions
<$action-createtiddler $basetitle="$:/plugins/tiddlywiki/bundled-subplugin-tests" $template="$:/plugins/tiddlywiki/bundled-subplugin-tests/raw" type="application/json"/>
+
title: $:/plugins/tiddlywiki/bundled-subplugin-tests/raw
list: readme
name: bundled-subplugin-tests
plugin-type: plugin
type: text/plain
{
"tiddlers": {
"$:/plugins/tiddlywiki/bundled-subplugin-tests/readme": {
"title": "$:/plugins/tiddlywiki/bundled-subplugin-tests/readme",
"text": "Readme from $:/plugins/tiddlywiki/bundled-subplugin-tests\n\n"
},
"$:/plugins/tiddlywiki/test-subplugin": {
"title": "$:/plugins/tiddlywiki/test-subplugin",
"name": "test-subplugin",
"description": "Test subplugin",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL",
"version": "5.3.7-prerelease",
"plugin-type": "plugin",
"dependents": "",
"type": "application/json",
"text": "{\"tiddlers\":{\"$:/plugins/tiddlywiki/test-subplugin/readme\":{\"title\":\"$:/plugins/tiddlywiki/test-subplugin/readme\",\"text\":\"Readme from $:/plugins/tiddlywiki/test-subplugin\"},\"$:/plugins/tiddlywiki/test-subsubplugin\":{\"title\":\"$:/plugins/tiddlywiki/test-subsubplugin\",\"name\":\"test-subsubplugin\",\"description\":\"Test subsubplugin\",\"list\":\"readme readme2\",\"stability\":\"STABILITY_1_EXPERIMENTAL\",\"version\":\"5.3.7-prerelease\",\"plugin-type\":\"plugin\",\"dependents\":\"\",\"type\":\"application/json\",\"text\":\"{\\\"tiddlers\\\":{\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\":{\\\"title\\\":\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\",\\\"text\\\":\\\"Readme from $:/plugins/tiddlywiki/test-subsubplugin\\\"}}}\"}}}"
}
}
}
+
title: ExpectedResult
<p>Readme from <a class="tc-tiddlylink tc-tiddlylink-shadow" href="#%24%3A%2Fplugins%2Ftiddlywiki%2Ftest-subplugin">$:/plugins/tiddlywiki/test-subplugin</a></p>

View File

@@ -1,47 +0,0 @@
title: SubPlugins/DynamicSubPluginUnload
description: Dynamic sub-plugin unloading
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$transclude $tiddler="$:/plugins/tiddlywiki/test-subplugin/readme">
NO
</$transclude>
+
title: Actions
<$action-deletetiddler $tiddler="$:/plugins/tiddlywiki/bundled-subplugin-tests"/>
+
title: $:/plugins/tiddlywiki/bundled-subplugin-tests
list: readme
name: bundled-subplugin-tests
plugin-type: plugin
type: application/json
{
"tiddlers": {
"$:/plugins/tiddlywiki/bundled-subplugin-tests/readme": {
"title": "$:/plugins/tiddlywiki/bundled-subplugin-tests/readme",
"text": "Readme from $:/plugins/tiddlywiki/bundled-subplugin-tests\n\n"
},
"$:/plugins/tiddlywiki/test-subplugin": {
"title": "$:/plugins/tiddlywiki/test-subplugin",
"name": "test-subplugin",
"description": "Test subplugin",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL",
"version": "5.3.7-prerelease",
"plugin-type": "plugin",
"dependents": "",
"type": "application/json",
"text": "{\"tiddlers\":{\"$:/plugins/tiddlywiki/test-subplugin/readme\":{\"title\":\"$:/plugins/tiddlywiki/test-subplugin/readme\",\"text\":\"Readme from $:/plugins/tiddlywiki/test-subplugin\"},\"$:/plugins/tiddlywiki/test-subsubplugin\":{\"title\":\"$:/plugins/tiddlywiki/test-subsubplugin\",\"name\":\"test-subsubplugin\",\"description\":\"Test subsubplugin\",\"list\":\"readme readme2\",\"stability\":\"STABILITY_1_EXPERIMENTAL\",\"version\":\"5.3.7-prerelease\",\"plugin-type\":\"plugin\",\"dependents\":\"\",\"type\":\"application/json\",\"text\":\"{\\\"tiddlers\\\":{\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\":{\\\"title\\\":\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\",\\\"text\\\":\\\"Readme from $:/plugins/tiddlywiki/test-subsubplugin\\\"}}}\"}}}"
}
}
}
+
title: ExpectedResult
<p>
NO
</p>

View File

@@ -1,21 +0,0 @@
title: SubPlugins/SimpleSubPlugin
description: Simple sub-plugin
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
{{$:/plugins/tiddlywiki/test-subplugin/readme}}
+
title: $:/plugins/tiddlywiki/bundled-subplugin-tests
list: readme
name: bundled-subplugin-tests
plugin-type: plugin
type: application/json
{"tiddlers":{"$:/plugins/tiddlywiki/bundled-subplugin-tests/readme":{"title":"$:/plugins/tiddlywiki/bundled-subplugin-tests/readme","text":"Readme from $:/plugins/tiddlywiki/bundled-subplugin-tests\n\n"},"$:/plugins/tiddlywiki/test-subplugin":{"title":"$:/plugins/tiddlywiki/test-subplugin","name":"test-subplugin","description":"Test subplugin","list":"readme","stability":"STABILITY_1_EXPERIMENTAL","version":"5.3.7-prerelease","plugin-type":"plugin","dependents":"","type":"application/json","text":"{\"tiddlers\":{\"$:/plugins/tiddlywiki/test-subplugin/readme\":{\"title\":\"$:/plugins/tiddlywiki/test-subplugin/readme\",\"text\":\"Readme from $:/plugins/tiddlywiki/test-subplugin\"},\"$:/plugins/tiddlywiki/test-subsubplugin\":{\"title\":\"$:/plugins/tiddlywiki/test-subsubplugin\",\"name\":\"test-subsubplugin\",\"description\":\"Test subsubplugin\",\"list\":\"readme readme2\",\"stability\":\"STABILITY_1_EXPERIMENTAL\",\"version\":\"5.3.7-prerelease\",\"plugin-type\":\"plugin\",\"dependents\":\"\",\"type\":\"application/json\",\"text\":\"{\\\"tiddlers\\\":{\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\":{\\\"title\\\":\\\"$:/plugins/tiddlywiki/test-subsubplugin/readme\\\",\\\"text\\\":\\\"Readme from $:/plugins/tiddlywiki/test-subsubplugin\\\"}}}\"}}}"}}}
+
title: ExpectedResult
<p>Readme from <a class="tc-tiddlylink tc-tiddlylink-shadow" href="#%24%3A%2Fplugins%2Ftiddlywiki%2Ftest-subplugin">$:/plugins/tiddlywiki/test-subplugin</a></p>

View File

@@ -0,0 +1,8 @@
caption: echo
created: 20241019150907690
modified: 20241019150907690
tags: Commands
title: EchoCommand
type: text/vnd.tiddlywiki
{{$:/language/Help/echo}}

View File

@@ -6,12 +6,12 @@ type: text/vnd.tiddlywiki
[[TiddlyWikiFolders]] are configured with a single `tiddlywiki.info` file in the root of the wiki folder. It should contain a JSON object comprising the following properties:
* ''plugins'' - an array of plugin names to be included in the wiki
* ''themes'' - an array of theme names to be included in the wiki
* ''languages'' - an array of language names to be included in the wiki
* ''includeWikis'' - an array of references to external wiki folders to be included in the wiki
* ''build'' - a hashmap of named build targets, each defined by an array of command tokens (see BuildCommand)
* ''config'' - an optional hashmap of configuration options (see below)
* ''plugins'' - optional array of plugin names to be included in the wiki
* ''themes'' - optional array of theme names to be included in the wiki
* ''languages'' - optional array of language names to be included in the wiki
* ''includeWikis'' - optional array of references to external wiki folders to be included in the wiki
* ''build'' - optional hashmap of named build targets, each defined by an array of command tokens (see BuildCommand)
* ''config'' - optional hashmap of configuration options (see below)
!!! ''includeWikis''
@@ -22,8 +22,45 @@ The entries in the ''includeWikis'' array can be either a string specifying the
!!! ''build''
The ''build'' property contains a hashmap of named build targets. Each of the build targets is defined as an array of command tokens.
Note that the build targets of included wikis are merged if a target of that name isn't defined in the current `tiddlywiki.info` file.
Command tokens can be a simple string or <<.from-version "5.3.6">> a command token object with a ''type'' property. The following types are defined to allow the command token to be dynamically defined:
* ''filter'': the value of the first result of a filter expression specified in the ''text'' property of the command token object
* ''wikify'': the result of wikifying a text string specified in the ''text'' property of the command token object
* ''prompt'': the result of prompting the user for a string. The ''prompt'' property of the command token object specifies the textual prompt to be used. The optional ''default'' property specifies a string value to be used if the user presses enter in response to the prompt. The optional ''transformFilter'' property specifies a filter to be applied to the user input to transform it into a command token. The string input by the user is available in the variable `user-input`
The EchoCommand is useful for debugging complex dynamic command tokens.
For example:
```
"build": {
"dynamic": [
"--echo","testing",
"the following argument is wikified",
{
"type": "wikify",
"text": "<<version>>-prod.html"
},
"the following argument is a filter result",
{
"type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]"
},
"the following argument was provided by the user",
{
"type": "prompt",
"prompt": "Please enter some text and type enter",
"default": "Nothing"
}
],
...
```
!!! ''config''
Configuration options include:

View File

@@ -7,9 +7,7 @@
"tiddlywiki/menubar",
"tiddlywiki/confetti",
"tiddlywiki/dynannotate",
"tiddlywiki/tour",
"tiddlywiki/codemirror",
"tiddlywiki/bundled-subplugin-tests"
"tiddlywiki/tour"
],
"themes": [
"tiddlywiki/vanilla",
@@ -23,9 +21,28 @@
"tiddlywiki/readonly"
],
"languages": [
"fr-FR"
],
"build": {
"dynamic-example": [
"--echo","testing",
"the following argument is wikified",
{
"type": "wikify",
"text": "<<version>>-prod.html"
},
"the following argument is a filter result",
{
"type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]"
},
"the following argument was provided by the user",
{
"type": "prompt",
"prompt": "Please enter some text and type enter",
"default": "Nothing",
"transformFilter": "[addprefix[testing ]]"
}
],
"index": [
"--savetiddlers","[tag[external-image]]","images",
"--render","[tag[external-text]]","[encodeuricomponent[]addprefix[text/]addsuffix[.tid]]","text/plain","$:/core/templates/tid-tiddler",

View File

@@ -1,7 +0,0 @@
{
"title": "$:/plugins/tiddlywiki/bundled-subplugin-tests",
"name": "bundled-subplugin-tests",
"description": "Bundled subplugin tests",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -1,4 +0,0 @@
title: $:/plugins/tiddlywiki/bundled-subplugin-tests/readme
Readme from $:/plugins/tiddlywiki/bundled-subplugin-tests

View File

@@ -1,7 +0,0 @@
{
"title": "$:/plugins/tiddlywiki/test-subplugin",
"name": "test-subplugin",
"description": "Test subplugin",
"list": "readme",
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -1,3 +0,0 @@
title: $:/plugins/tiddlywiki/test-subplugin/readme
Readme from $:/plugins/tiddlywiki/test-subplugin

View File

@@ -1,7 +0,0 @@
{
"title": "$:/plugins/tiddlywiki/test-subsubplugin",
"name": "test-subsubplugin",
"description": "Test subsubplugin",
"list": "readme readme2",
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -1,3 +0,0 @@
title: $:/plugins/tiddlywiki/test-subsubplugin/readme
Readme from $:/plugins/tiddlywiki/test-subsubplugin

View File

@@ -1,7 +0,0 @@
{
"title": "$:/language/codemirror/en-GB",
"name": "en-GB",
"plugin-type": "language",
"description": "English (UK) translations for CodeMirror",
"list": "readme"
}

View File

@@ -1,3 +0,0 @@
title: $:/language/codemirror/en-GB/readme
English (UK) translations for CodeMirror

View File

@@ -1,29 +0,0 @@
title: $:/language/codemirror/
homeUrl: http://codemirror.net
addOnUrl: http://codemirror.net/doc/manual.html#addons
configUrl: http://codemirror.net/doc/manual.html#config
controlPanel/hint: Tʜᴇᴇ ꜱᴇᴛᴛɪɴɢꜱ ʟᴇᴛ ʏ ᴄᴜꜱᴛᴏᴍɪꜱᴇ ᴛʜᴇ ʙᴇʜᴀᴠɪᴏᴜʀ ᴏꜰ [[CᴅᴇMɪʀʀʀ|$:/plugins/tiddlywiki/codemirror]].
controlPanel/keyboard: Kᴇʏʙᴀʀᴅ ꜱʜᴏʀᴛᴄᴜᴛꜱ
controlPanel/usage: Uᴀɢᴇ ɪɴꜰᴏʀᴍᴀᴛɪᴏɴ
cursorBlinkRate/hint: Cʀʀ ʙʟɪɴᴋ ʀᴀᴛᴇ
editorFont/hint: Eᴅɪʀ ꜰᴏɴᴛ ꜰᴀᴍɪʟʏ
editorFont/info: Sᴇᴛ ᴛʜᴇ ꜰᴏɴᴛ ꜰᴀᴍɪʟʏ ꜰᴏʀ ᴛʜᴇ ~CᴅᴇMɪʀʀʀ ᴛᴇxᴛ-ᴇᴅɪᴛᴏʀ
indentUnit/hint: H ᴍᴀɴʏ ꜱᴘᴀᴄᴇꜱ ᴀ ʙʟᴏᴄᴋ ꜱʜᴏᴜʟᴅ ʙᴇ ɪɴᴅᴇɴᴛᴇᴅ
indentWithTabs/hint: Eɴᴀʙʟᴇ ɪɴᴅᴇɴᴛɪɴɢ ᴡɪᴛʜ ᴛᴀʙꜱ
indentWithTabs/info: Wʜᴇᴛʜᴇʀ, ᴡʜᴇɴ ɪɴᴅᴇɴᴛɪɴɢ, ᴛʜᴇ ꜰɪʀꜱᴛ N*`ᴛᴀʙSɪᴇ` ꜱᴘᴀᴄᴇꜱ ꜱʜᴏᴜʟᴅ ʙᴇ ʀᴇᴘʟᴀᴄᴇᴅ ʙʏ N ᴛᴀʙꜱ.
keyMap/hint: ~CᴅᴇMɪʀʀʀ ᴋᴇʏᴍᴀᴘ
keyMap/info: ~Tʜᴇ Kᴇʏʙᴀʀᴅ KᴇʏMᴀᴘ ᴜꜱᴇᴅ ᴡɪᴛʜɪɴ ᴛʜᴇ ~CᴅᴇMɪʀʀʀ ᴛᴇxᴛ-ᴇᴅɪᴛᴏʀ
lineNumbers/hint: Eɴᴀʙʟᴇ ʟɪɴᴇ ɴᴜᴍʙᴇʀꜱ
lineNumbers/info: Wʜᴇᴛʜᴇʀ ᴛᴏ ꜱʜᴏᴡ ʟɪɴᴇ ɴᴜᴍʙᴇʀꜱ ᴛᴏ ᴛʜᴇ ʟᴇꜰᴛ ᴏꜰ ᴛʜᴇ ᴇᴅɪᴛᴏʀ.
lineWrapping/hint: Eɴᴀʙʟᴇ ʟɪɴᴇ ᴡʀᴀᴘᴘɪɴɢ
lineWrapping/info: Wʜᴇᴛʜᴇʀ CᴅᴇMɪʀʀʀ ꜱʜᴏᴜʟᴅ ꜱᴄʀᴏʟʟ ᴏʀ ᴡʀᴀᴘ ꜰᴏʀ ʟᴏɴɢ ʟɪɴᴇꜱ. Dᴇꜰᴀʟᴛ ᴛᴏ `ꜰᴀʟꜱᴇ` (ꜱᴄʀᴏʟʟ).
showCursorWhenSelecting/hint: Sʜ ᴄᴜʀꜱᴏʀ, ᴡʜᴇɴ ꜱᴇʟᴇᴄᴛɪɴɢ
showCursorWhenSelecting/info: Wʜᴇᴛʜᴇʀ ᴛʜᴇ ᴄᴜʀꜱᴏʀ ꜱʜᴏᴜʟᴅ ʙᴇ ᴅʀᴀᴡɴ ᴡʜᴇɴ ᴀ ꜱᴇʟᴇᴄᴛɪᴏɴ ɪ ᴀᴄᴛɪᴠᴇ.
smartIndent/hint: Eɴᴀʙʟᴇ ꜱᴍᴀʀᴛ ɪɴᴅᴇɴᴛ
smartIndent/info: Wʜᴇᴛʜᴇʀ ᴛᴏ ᴜꜱᴇ ᴛʜᴇ ɴᴛᴇxᴛ-ꜱᴇɴꜱɪᴛɪᴠᴇ ɪɴᴅᴇɴᴛᴀᴛɪᴏɴ ᴛʜᴀᴛ ᴛʜᴇ ᴍᴏᴅᴇ ᴘʀᴏᴠɪᴅᴇꜱ (ᴏʀ ᴊᴜꜱᴛ ɪɴᴅᴇɴᴛ ᴛʜᴇ ꜱᴀᴍᴇ ᴀꜱ ᴛʜᴇ ʟɪɴᴇ ʙᴇꜰᴏʀᴇ). Dᴇꜰᴀʟᴛ ᴛᴏ `ᴛʀᴜᴇ`.
styleActiveLine/hint: Hɪɢʜʟɪɢʜᴛ ᴀᴄᴛɪᴠᴇ ʟɪɴᴇ
styleActiveLine/info: Wʜᴇᴛʜᴇʀ ᴏʀ ɴᴏᴛ ᴛᴏ ʜɪɢʜʟɪɢʜᴛ ᴛʜᴇ ᴀᴄᴛɪᴠᴇ ᴛᴇxᴛ-ᴇᴅɪᴛᴏʀ ʟɪɴᴇ
tabSize/hint: Wɪᴅᴛʜ ᴏꜰ ᴀ ᴛᴀʙ ᴄʜᴀʀᴀᴄᴛᴇʀ
theme/hint: Sᴇʟᴇᴛ ᴀ ᴛʜᴇᴍᴇ
theme/info: Cʜᴇ ʙᴇᴛᴡᴇᴇɴ ~CᴅᴇMɪʀʀʀ ᴛʜᴇᴍᴇꜱ

View File

@@ -1,7 +0,0 @@
{
"title": "$:/language/codemirror/fr-FR",
"name": "fr-FR",
"plugin-type": "language",
"description": "French translations for CodeMirror",
"list": "readme"
}

View File

@@ -1,3 +0,0 @@
title: $:/language/codemirror/fr-FR/readme
French translations for CodeMirror

View File

@@ -21,32 +21,17 @@ describe("Wiki-based tests", function() {
var tiddler = $tw.wiki.getTiddler(title);
it(tiddler.fields.title + ": " + tiddler.fields.description, function() {
// Add our tiddlers
var wiki = new $tw.Wiki();
// Suppress next tick dispatch for wiki change events
wiki.setDispatchMode(true);
// Add the core plugin
var coreTiddler = $tw.wiki.getTiddler("$:/core")
var wiki = new $tw.Wiki(),
coreTiddler = $tw.wiki.getTiddler("$:/core");
if(coreTiddler) {
wiki.addTiddler(coreTiddler);
}
// Add other tiddlers
wiki.addTiddlers(readMultipleTiddlersTiddler(title));
// Unpack plugin tiddlers
wiki.readPluginInfo();
wiki.registerPluginTiddlers("plugin");
wiki.unpackPluginTiddlers();
wiki.addIndexersToWiki();
// Install the language switcher
var languageSwitcher = new $tw.PluginSwitcher({
wiki: wiki,
pluginType: "language",
controllerTitle: "$:/language",
defaultPlugins: [
"$:/languages/en-GB"
]
});
// Install the plugin change event handler
$tw.utils.installPluginChangeHandler(wiki);
// Clear changes queue
wiki.clearTiddlerEventQueue();
// Complain if we don't have the ouput and expected results
@@ -59,17 +44,12 @@ describe("Wiki-based tests", function() {
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
// Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode);
// Install the wiki change event handler
wiki.addEventListener("change",function(changes) {
widgetNode.refresh(changes,wrapper);
});
// Clear changes queue
wiki.clearTiddlerEventQueue();
// Run the actions if provided
if(wiki.tiddlerExists("Actions")) {
widgetNode.invokeActionString(wiki.getTiddlerText("Actions"));
}
// Make sure all wiki events have been cleared
while(wiki.eventsTriggered) {
wiki.processOutstandingTiddlerEvents();
refreshWidgetNode(widgetNode,wrapper);
}
// Test the rendering
expect(wrapper.innerHTML).toBe(wiki.getTiddlerText("ExpectedResult"));
@@ -110,4 +90,9 @@ describe("Wiki-based tests", function() {
return wrapper;
}
function refreshWidgetNode(widgetNode,wrapper) {
widgetNode.refresh(widgetNode.wiki.changedTiddlers,wrapper);
// console.log(require("util").inspect(wrapper,{depth: 8}));
}
});