/*\ title: $:/core/modules/commander.js type: application/javascript module-type: global The $tw.Commander class is a command interpreter \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "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) { var path = require("path"); this.commandTokens = commandTokens; this.nextToken = 0; this.callback = callback; this.wiki = wiki; this.streams = streams; this.outputPath = path.resolve($tw.boot.wikiPath,$tw.config.wikiOutputSubDir); }; /* Log a string if verbose flag is set */ Commander.prototype.log = function(str) { if(this.verbose) { this.streams.output.write(str + "\n"); } }; /* Write a string if verbose flag is set */ Commander.prototype.write = function(str) { if(this.verbose) { this.streams.output.write(str); } }; /* Add a string of tokens to the command queue */ Commander.prototype.addCommandTokens = function(commandTokens) { var params = commandTokens.slice(0); params.unshift(0); params.unshift(this.nextToken); Array.prototype.splice.apply(this.commandTokens,params); }; /* 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() { 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++]; if(commandName.substr(0,2) !== "--") { this.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"); } // 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(); } } else { // Asynchronous command c = new command.Command(params,this,function(err) { if(err) { self.callback(err); } else { self.executeNextCommand(); } }); err = c.execute(); if(err) { this.callback(err); } } } } } }; /* Given an array of parameter strings `params` in name:value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error */ Commander.prototype.extractNamedParameters = function(params,mandatoryParameters) { mandatoryParameters = mandatoryParameters || []; var errors = [], paramsByName = Object.create(null); // Extract the parameters $tw.utils.each(params,function(param) { var index = param.indexOf("="); if(index < 1) { errors.push("malformed named parameter: '" + param + "'"); } paramsByName[param.slice(0,index)] = $tw.utils.trim(param.slice(index+1)); }); // Check the mandatory parameters are present $tw.utils.each(mandatoryParameters,function(mandatoryParameter) { if(!$tw.utils.hop(paramsByName,mandatoryParameter)) { errors.push("missing mandatory parameter: '" + mandatoryParameter + "'"); } }); // Return any errors if(errors.length > 0) { return errors.join(" and\n"); } else { return paramsByName; } }; Commander.initCommands = function(moduleType) { moduleType = moduleType || "command"; $tw.commands = {}; $tw.modules.forEachModuleOfType(moduleType,function(title,module) { var c = $tw.commands[module.info.name] = {}; // Add the methods defined by the module for(var f in module) { if($tw.utils.hop(module,f)) { c[f] = module[f]; } } }); }; exports.Commander = Commander; })();