/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Skywriter. * * The Initial Developer of the Original Code is * Mozilla. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Joe Walker (jwalker@mozilla.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { var console = require('pilot/console'); var Trace = require('pilot/stacktrace').Trace; var oop = require("pilot/oop").oop; var EventEmitter = require("pilot/event_emitter").EventEmitter; exports.startup = function(data, reason) { if (!data.env || !data.env.settings) { return; } var settings = data.env.settings; // TODO register these using new registration functionality // catalog.addExtensionPoint("command", { // "description": // "A command is a bit of functionality with optional typed arguments which can do something small like moving the cursor around the screen, or large like cloning a project from VCS.", // "indexOn": "name" // }); // catalog.addExtensionPoint("addedRequestOutput", { // "description": // "An extension point to be called whenever a new command begins output." // }); // catalog.addExtensionPoint("dimensionsChanged", { // "description": // "A dimensionsChanged is a way to be notified of changes to the dimension of Skywriter" // }); settings.addSetting({ "name": "historyLength", "description": "How many typed commands do we recall for reference?", "type": "number", "defaultValue": 50 }); }; exports.shutdown = function(data, reason) { var settings = data.env.settings; settings.removeSetting('historyLength'); }; exports.Canon = function() { this._commands = {}; }; exports.Canon.prototype = { addCommand: function(options) { if (!options.name) { throw new Error("All registered commands must have a name"); } this._commands[name] = options; }, removeCommand: function(name) { delete this._commands[name]; } }; oop.implement(exports, EventEmitter); /** * Current requirements are around displaying the command line, and provision * of a 'history' command and cursor up|down navigation of history. *

Future requirements could include: *

*

The execute() command doesn't really live here, except as part of that * last future requirement, and because it doesn't really have anywhere else to * live. */ /** * The array of requests that wish to announce their presence */ exports.requests = []; /** * How many requests do we store? */ var maxRequestLength = 100; /** * Called by Request instances when some output (or a cell to async() happens) */ exports.addRequestOutput = function(request) { exports.requests.push(request); // This could probably be optimized with some maths, but 99.99% of the // time we will only be off by one, and I'm feeling lazy. while (exports.requests.length > maxRequestLength) { exports.requests.shiftObject(); } exports._dispatchEvent('addedRequestOutput', { request: request }); }; /** * Execute a new command. * This is basically an error trapping wrapper around request.command(...) */ exports.execute = function(args, request) { // Check the function pointed to in the meta-data exists if (!request.command) { request.doneWithError('Command not found.'); return; } try { request.command(args, request); } catch (ex) { var trace = new Trace(ex, true); console.group('Error executing command \'' + request.typed + '\''); console.log('command=', request.commandExt); console.log('args=', args); console.error(ex); trace.log(3); console.groupEnd(); request.doneWithError(ex); } }; /** * To create an invocation, you need to do something like this (all the ctor * args are optional): *

 * var request = new Request({
 *     command: command,
 *     commandExt: commandExt,
 *     args: args,
 *     typed: typed
 * });
 * 
*/ exports.Request = function(options) { options = options || {}; // Will be used in the keyboard case and the cli case this.command = options.command; this.commandExt = options.commandExt; // Will be used only in the cli case this.args = options.args; this.typed = options.typed; // Have we been initialized? this._begunOutput = false; this.start = new Date(); this.end = null; this.completed = false; this.error = false; }; oop.implement(exports.Request.prototype, EventEmitter); /** * Lazy init to register with the history should only be done on output. * init() is expensive, and won't be used in the majority of cases */ exports.Request.prototype._beginOutput = function() { this._begunOutput = true; this.outputs = []; exports.addRequestOutput(this); }; /** * Sugar for: *
request.error = true; request.done(output);
*/ exports.Request.prototype.doneWithError = function(content) { this.error = true; this.done(content); }; /** * Declares that this function will not be automatically done when * the command exits */ exports.Request.prototype.async = function() { if (!this._begunOutput) { this._beginOutput(); } }; /** * Complete the currently executing command with successful output. * @param output Either DOM node, an SproutCore element or something that * can be used in the content of a DIV to create a DOM node. */ exports.Request.prototype.output = function(content) { if (!this._begunOutput) { this._beginOutput(); } if (typeof content !== 'string' && !(content instanceof Node)) { content = content.toString(); } this.outputs.push(content); this._dispatchEvent('changed', {}); return this; }; /** * All commands that do output must call this to indicate that the command * has finished execution. */ exports.Request.prototype.done = function(content) { this.completed = true; this.end = new Date(); this.duration = this.end.getTime() - this.start.getTime(); if (content) { this.output(content); } this._dispatchEvent('changed', {}); }; });