1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-18 11:29:55 +00:00

Added command handling

The idea is that the same commands will work on the server or the
client, and that the client will be able to remotely run commands on
the server.

Also includes abstraction of the version number of TW5.
This commit is contained in:
Jeremy Ruston 2012-05-02 11:02:47 +01:00
parent 12bf2b6fb9
commit dac06537e5
12 changed files with 228 additions and 4 deletions

View File

@ -35,6 +35,9 @@ if(typeof(window) === "undefined" && !global.$tw) {
global.$tw = {isBrowser: false};
}
// Boot information
$tw.boot = {};
// Modules store registers all the modules the system has seen
$tw.modules = $tw.modules || {};
$tw.modules.titles = $tw.modules.titles || {} // hashmap by module title of {fn:, exports:, moduleType:}
@ -523,6 +526,8 @@ var fs = require("fs"),
path = require("path"),
vm = require("vm");
$tw.boot.bootPath = path.dirname(module.filename);
/*
Load the tiddlers contained in a particular file (and optionally the accompanying .meta file)
*/
@ -574,6 +579,7 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
module: module,
exports: {},
console: console,
process: process,
$tw: $tw,
require: function(title) {
return $tw.modules.execute(title,name);
@ -598,7 +604,7 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
}
// Load plugins from the plugins directory
$tw.plugins.loadPlugins(path.resolve(path.dirname(module.filename),$tw.config.pluginSubDir));
$tw.plugins.loadPlugins(path.resolve($tw.boot.bootPath,$tw.config.pluginSubDir));
// End of if(!$tw.isBrowser)
}

View File

@ -0,0 +1,93 @@
/*\
title: $:/core/commander.js
type: application/javascript
module-type: global
The $tw.Commander class is a command interpreter
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
/*
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
callback: a callback invoked as callback(err) where err is null if there was no error
*/
var Commander = function(commandTokens,callback,wiki,streams) {
this.commandTokens = commandTokens;
this.nextToken = 0;
this.callback = callback;
this.wiki = wiki;
this.streams = streams;
};
/*
Execute the sequence of commands and invoke a callback on completion
*/
Commander.prototype.execute = function() {
this.executeNextCommand();
};
/*
Execute the next command in the sequence
*/
Commander.prototype.executeNextCommand = function() {
// 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++];
if(commandName.substr(0,2) !== "--") {
this.callback("Missing command");
} 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];
if(!command) {
this.callback("Unknown command: " + commandName);
} else {
if(command.info.synchronous) {
var c = new command.Command(params,this);
c.execute();
this.executeNextCommand();
}
}
}
}
};
Commander.initCommands = function(moduleType) {
// Install the command modules
moduleType = moduleType || "command";
$tw.commands = {};
var modules = $tw.plugins.moduleTypes[moduleType],
n,m,f,c;
if(modules) {
for(n=0; n<modules.length; n++) {
m = modules[n];
$tw.commands[m.info.name] = {};
c = $tw.commands[m.info.name];
// Add the methods defined by the module
for(f in m) {
c[f] = m[f];
}
}
}
};
exports.Commander = Commander;
})();

View File

@ -0,0 +1,30 @@
/*\
title: $:/core/commands/version.js
type: application/javascript
module-type: command
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
exports.info = {
name: "version",
synchronous: true,
params: {}
}
var Command = function(params,commander) {
this.params = params;
this.commander = commander;
};
Command.prototype.execute = function() {
this.commander.streams.output.write($tw.utils.getVersionString() + "\n");
}
exports.Command = Command;
})();

View File

@ -16,7 +16,7 @@ exports.info = {
}
exports.executeMacro = function() {
return [$tw.Tree.Text("5.0.0")];
return [$tw.Tree.Text($tw.utils.getVersionString())];
};
})();

View File

@ -18,6 +18,7 @@ exports.startup = function() {
// Wire up plugin modules
$tw.plugins.applyMethods("config",$tw.config);
$tw.plugins.applyMethods("utils",$tw.utils);
$tw.version = $tw.utils.extractVersionInfo();
$tw.plugins.applyMethods("tiddlermethod",$tw.Tiddler.prototype);
$tw.plugins.applyMethods("wikimethod",$tw.Wiki.prototype);
$tw.plugins.applyMethods("treeutils",$tw.Tree);
@ -26,6 +27,8 @@ exports.startup = function() {
$tw.wiki.initMacros();
$tw.wiki.initEditors();
$tw.wiki.initParsers();
// Set up the command plugins
$tw.Commander.initCommands();
if($tw.isBrowser) {
var renderer = $tw.wiki.parseTiddler("PageTemplate");
@ -36,7 +39,17 @@ if($tw.isBrowser) {
});
console.log("$tw",$tw);
} else {
console.log("$tw",require("util").inspect($tw,false,8));
var commander = new $tw.Commander(
Array.prototype.slice.call(process.argv,2),
function(err) {
if(err) {
console.log("Error: " + err);
}
},
$tw.wiki,
{output: process.stdout, error: process.stderr}
);
commander.execute();
}

View File

@ -219,4 +219,62 @@ exports.applyStyleSheet = function(id,css) {
}
};
/*
Parse a version number string of the form "1.2.3", "1.2.3.a4", or "1.2.3.b4" into:
{major: <number>, minor: <number>, revision: <number>, alpha: <number>, beta: <number>}
*/
exports.parseVersion = function(versionString) {
versionString = versionString || "0.0.0";
var components = versionString.split("."),
version = {
major: components[0] ? parseInt(components[0],10) : null,
minor: components[1] ? parseInt(components[1],10) : null,
revision: components[2] ? parseInt(components[2],10) : null
};
if(components[3]) {
if(components[3].charAt(0) === "a") {
version.alpha = parseInt(components[3].substr(1),10);
} else if(components[3].charAt(0) === "b") {
version.beta = parseInt(components[3].substr(1),10);
}
}
return version;
};
exports.getVersionString = function() {
return ($tw.version.major || "0") + "." +
($tw.version.minor || "0") + "." +
($tw.version.revision || "0") +
($tw.version.alpha ? ".a" + $tw.version.alpha : "") +
($tw.version.beta ? ".b" + $tw.version.beta : "");
};
/*
Extract the version number from the meta tag or from the boot file
*/
if($tw.isBrowser) {
// Browser version
exports.extractVersionInfo = function() {
var metatags = document.getElementsByTagName("meta");
for(var t=0; t<metatags.length; t++) {
var m = metatags[t];
if(m.name === "tiddlywiki-version") {
return $tw.utils.parseVersion(m.content);
}
}
return $tw.utils.parseVersion();
};
} else {
// Server version
exports.extractVersionInfo = function() {
var fs = require("fs");
return $tw.utils.parseVersion(fs.readFileSync($tw.boot.bootPath + "/version.txt").toString("utf8"));
};
}
})();

View File

@ -252,6 +252,21 @@ exports.parseTiddler = function(title) {
}) : null;
};
/*
Parse text in a specified format and render it into another format
outputType: content type for the output
textType: content type of the input text
text: input text
options: see below
Options are:
defaultType: Default MIME type to use if the specified one is unknown
*/
exports.renderText = function(outputType,textType,text,options) {
var renderer = this.parseText(type,text,options);
renderer.execute([]);
return renderer.render(outputType);
};
/*
Install macro plugins into this wiki
moduleType: Plugin type to install (defaults to "macro")

View File

@ -0,0 +1 @@
5.0.0.a2

View File

@ -57,6 +57,7 @@ The 'core' is the boot kernel plus the set of plugin modules that it loads. It c
* `editor` - interactive editors for different types of content
* `parser` - parsers for different types of content
* `wikitextrule` - individual rules for the wikitext parser
* `command` - individual commands for the `$tw.Commander` class
TiddlyWiki5 makes extensive use of JavaScript inheritance:

View File

@ -1,10 +1,12 @@
template: tiddlywiki5.template.html
copyright: copyright.txt
version: core/version.txt
jsbootstart: core/bootprefix.js
jsbootend: core/boot.js
pluginmodule: core/modules/*.js
pluginmodule: core/modules/commands/*.js
pluginmodule: core/modules/macros/*.js
pluginmodule: core/modules/macros/edit/*.js
pluginmodule: core/modules/macros/edit/editors/*.js

View File

@ -1,6 +1,11 @@
<!doctype html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="application-name" content="TiddlyWiki" />
<meta name="generator" content="TiddlyWiki" />
<meta name="tiddlywiki-version" content="
&lt;!--@@version@@--&gt;
" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

View File

@ -4,4 +4,4 @@ tags: Introduction
Welcome to TiddlyWiki5, a reboot of TiddlyWiki, the venerable, reusable non-linear personal web notebook first released in 2004. It is a complete interactive wiki that can run from a single HTML file in the browser or as a powerful [[node.js application|What is node.js?]].
TiddlyWiki5 is currently in early beta, which is to say that it is useful but incomplete. You can try out the prototype at http://tiddlywiki.com/tiddlywiki5, get involved in the [[development on GitHub|https://github.com/Jermolene/TiddlyWiki5]] or the discussions on [[the TiddlyWikiDev Google Group|http://groups.google.com/group/TiddlyWikiDev]].
TiddlyWiki5 is currently at version <<version>> and is under active development, which is to say that it is useful but incomplete. You can try out the prototype at http://tiddlywiki.com/tiddlywiki5, get involved in the [[development on GitHub|https://github.com/Jermolene/TiddlyWiki5]] or the discussions on [[the TiddlyWikiDev Google Group|http://groups.google.com/group/TiddlyWikiDev]].