remove lib directory from plugins otherwise the

modules cannot be loaded with nodejs
This commit is contained in:
Fabian Jakobs 2010-12-13 10:39:25 +01:00
commit f370a6f7fa
37 changed files with 147 additions and 149 deletions

View file

@ -96,7 +96,8 @@ var setupPlugins = function(config, callback) {
for (i = 0; i < packages.length; i++) {
location.push({
name: packages[i],
main: "index"
main: "index",
lib: "."
});
knownPlugins.push(packages[i]);
}

821
plugins/cockpit/cli.js Normal file
View file

@ -0,0 +1,821 @@
/* ***** 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 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 util = require('pilot/util');
var oop = require('pilot/oop').oop;
var EventEmitter = require('pilot/event_emitter').EventEmitter;
//var keyboard = require('keyboard/keyboard');
var types = require('pilot/types');
var Status = require('pilot/types').Status;
var Conversion = require('pilot/types').Conversion;
var canon = require('pilot/canon');
/**
* The information required to tell the user there is a problem with their
* input.
*/
function Hint(status, message, start, end) {
this.status = status;
this.message = message;
if (typeof start === 'number') {
this.start = start;
this.end = end;
}
else {
var arg = start;
this.start = arg.start;
this.end = arg.end;
}
}
Hint.prototype = {
};
exports.Hint = Hint;
/**
* A Hint that arose as a result of a Conversion
*/
function ConversionHint(conversion, arg) {
this.status = conversion.status;
this.message = conversion.message;
if (arg) {
this.start = arg.start;
this.end = arg.end;
}
else {
this.start = 0;
this.end = 0;
}
this.predictions = conversion.predictions;
};
oop.inherits(ConversionHint, Hint);
/**
* We record where in the input string an argument comes so we can report errors
* against those string positions.
* @constructor
*/
function Argument(text, start, end, priorSpace) {
this.setText(text);
this.start = start;
this.end = end;
this.priorSpace = priorSpace;
}
Argument.prototype = {
/**
* Return the result of merging these arguments
*/
merge: function(following) {
return new Argument(
this.text + following.priorSpace + following.text,
this.start, following.end,
this.priorSpace);
},
setText: function(text) {
if (text == null) {
throw new Error('Illegal text for Argument: ' + text);
}
this.text = text;
}
};
/**
* Merge an array of arguments into a single argument.
*/
Argument.merge = function(argArray, start, end) {
start = (start === undefined) ? 0 : start;
end = (end === undefined) ? argArray.length : end;
var joined;
for (var i = start; i < end; i++) {
var arg = argArray[i];
if (!joined) {
joined = arg;
}
else {
joined = joined.merge(arg);
}
}
return joined;
};
/**
* A link between a parameter and the data for that parameter.
* The data for the parameter is available as in the preferred type and as
* an Argument for the CLI.
* <p>We also record validity information where applicable.
* <p>For values, null and undefined have distinct definitions. null means
* that a value has been provided, undefined means that it has not.
* Thus, null is a valid default value, and common because it identifies an
* parameter that is optional. undefined means there is no value from
* the command line.
* @constructor
*/
function Assignment(param) {
this.param = param;
this.setValue(param.defaultValue);
};
Assignment.prototype = {
/**
* The parameter that we are assigning to
* @readonly
*/
param: undefined,
/**
* The current value (i.e. not the string representation)
* Use setValue() to mutate
*/
value: undefined,
setValue: function(value) {
if (this.value === value) {
return;
}
if (value === undefined) {
value = this.param.defaultValue;
}
this.value = value;
var text = (value == null) ? '' : this.param.type.stringify(value);
if (this.arg) {
this.arg.setText(text);
}
this.conversion = undefined;
//this._dispatchEvent('change', { assignment: this });
},
/**
* The textual representation of the current value
* Use setValue() to mutate
*/
arg: undefined,
setArgument: function(arg) {
if (this.arg === arg) {
return;
}
this.arg = arg;
this.conversion = this.param.type.parse(arg.text);
this.value = this.conversion.value;
//this._dispatchEvent('change', { assignment: this });
},
/**
* Create a list of this hints associated with this parameter assignment
*/
getHints: function() {
var hints = [];
if (this.conversion != null &&
(this.conversion.status !== Status.VALID ||
this.conversion.message)) {
hints.push(new ConversionHint(this.conversion, this.arg));
}
var argProvided = this.arg != null && this.arg.text !== '';
var dataProvided = this.value !== undefined || argProvided;
if (this.param.defaultValue === undefined && !dataProvided) {
// If the there is no data provided, we have no start/end. Use -1
hints.push(new Hint(Status.INVALID,
'Argument for ' + param.name + ' is required'
-1, -1));
}
return hints;
},
/**
* Report on the status of the last parse() conversion.
* @see types.Conversion
*/
conversion: undefined
};
oop.implement(Assignment, EventEmitter);
exports.Assignment = Assignment;
/**
* A Requisition collects the information needed to execute a command.
* There is no point in a requisition for parameter-less commands because there
* is no information to collect. A Requisition is a collection of assignments
* of values to parameters, each handled by an instance of Assignment.
* CliRequisition adds functions for parsing input from a command line to this
* class
* @constructor
*/
function Requisition() {
}
Requisition.prototype = {
/**
* The command that we are about to execute.
* @readonly
*/
command: undefined,
/**
* The count of assignments
* @readonly
*/
assignmentCount: undefined,
/**
* Set a new command. We make no attempt to convert the args in the old
* command to args in the new command. The assignments need to be
* re-entered.
*/
setCommand: function(command) {
if (this.command === command) {
return;
}
this.command = command;
this._assignments = {};
if (command) {
command.params.forEach(function(param) {
this._assignments[param.name] = new Assignment(param);
}, this);
}
this.assignmentCount = Object.keys(this._assignments);
},
/**
* Assignments have an order, so we need to store them in an array.
* But we also need named access ...
*/
getAssignment: function(nameOrNumber) {
var name = (typeof nameOrNumber === 'string') ?
nameOrNumber :
Object.keys(this._assignments)[nameOrNumber];
return this._assignments[name];
},
/**
* Where parameter name == assignment names - they are the same.
*/
getParameterNames: function() {
return Object.keys(this._assignments);
},
/**
* A *shallow* clone of the assignments.
* This is useful for systems that wish to go over all the assignments
* finding values one way or another and wish to trim an array as they go.
*/
cloneAssignments: function() {
return Object.keys(this._assignments).map(function(name) {
return this._assignments[name];
}, this);
},
/**
* Collect the statuses from the Assignments
*/
getHints: function() {
var hints = [];
Object.keys(this._assignments).map(function(name) {
// Append the assignments hints to our list
hints.push.apply(hints, this._assignments[name].getHints());
}, this);
return hints;
},
/**
* Extract the names and values of all the assignments, and return as
* an object.
*/
getArgs: function() {
var args = {};
Object.keys(this._assignments).forEach(function(name) {
args[name] = this.getAssignment(name).value;
}, this);
return args;
},
/**
* Reset all the assignments to their default values
*/
setDefaultValues: function() {
Object.keys(this._assignments).forEach(function(name) {
this._assignments[name].setValue(undefined);
}, this);
},
/**
* Helper to call canon.exec
*/
exec: function() {
canon.exec(this.command, this.getArgs());
}
};
exports.Requisition = Requisition;
/**
* An object used during command line parsing to hold the various intermediate
* data steps.
* <p>The 'output' of the update is held in 2 objects: input.hints which is an
* array of hints to display to the user. In the future this will become a
* single value.
* <p>The other output value is input.requisition which gives access to an
* args object for use in executing the final command.
*
* <p>The majority of the functions in this class are called in sequence by the
* constructor. Their task is to add to <tt>hints</tt> fill out the requisition.
* <p>The general sequence is:<ul>
* <li>_tokenize(): convert _typed into _parts
* <li>_split(): convert _parts into _command and _unparsedArgs
* <li>_assign(): convert _unparsedArgs into requisition
* </ul>
*
* @param typed {string} The instruction as typed by the user so far
* @param options {object} A list of optional named parameters. Can be any of:
* <b>flags</b>: Flags for us to check against the predicates specified with the
* commands. Defaulted to <tt>keyboard.buildFlags({ });</tt>
* if not specified.
* @constructor
*/
function CliRequisition(options) {
if (options && options.flags) {
/**
* TODO: We were using a default of keyboard.buildFlags({ });
* This allowed us to have commands that only existed in certain contexts
* - i.e. Javascript specific commands.
*/
this.flags = options.flags;
}
}
oop.inherits(CliRequisition, Requisition);
(function() {
/**
*
*/
CliRequisition.prototype.update = function(input) {
this.hints = [];
if (util.none(input.typed)) {
this.setCommand(null);
return;
}
var args = _tokenize(input.typed);
if (args.length === 0) {
// We would like to put some initial help here, but for anyone but
// a complete novice a 'type help' message is very annoying, so we
// need to find a way to only display this message once, or for
// until the user click a 'close' button or similar
this._addHint(Status.INCOMPLETE, '', 0, 0);
this.setCommand(null);
return;
}
var command = _split(args);
if (!command) {
// No command found - bail helpfully.
var commandType = types.getType('command');
var conversion = commandType.parse(input.typed);
var arg = Argument.merge(args);
this._addHint(new ConversionHint(conversion, arg));
this.setCommand(null);
}
else {
// The user hasn't started to type any arguments
if (args.length === 0) {
var message = documentCommand(command);
this._addHint(Status.VALID, message, 0, input.typed.length);
}
this.setCommand(command);
this._assign(args);
this._addHint(CliRequisition.super_.getHints.call(this));
}
// Not knowing about cursor positioning, the requisition and assignments
// can't know this, but anything they mark as INCOMPLETE is actually
// INVALID unless the cursor is actually inside that argument.
var c = input.cursor;
this.hints.forEach(function(hint) {
var startInHint = c.start >= hint.start && c.start <= hint.end;
var endInHint = c.end >= hint.start && c.end <= hint.end;
var inHint = startInHint || endInHint;
if (!inHint && hint.status === Status.INCOMPLETE) {
hint.status = Status.INVALID;
}
}, this);
return;
};
CliRequisition.prototype.getHints = function() {
return this.hints;
};
/**
* Some sugar around: 'this.hints.push(new Hint(...));', but you can also
* pass in an array of Hints or the parameters to create a hint
*/
CliRequisition.prototype._addHint = function(status, message, start, end) {
if (status == null) {
return;
}
if (status instanceof Hint) {
this.hints.push(status);
}
else if (Array.isArray(status)) {
this.hints.push.apply(this.hints, status);
}
else {
this.hints.push(new Hint(status, message, start, end));
}
};
/**
* Work out which arguments are applicable to which parameters.
* <p>This takes #_command.params and #_unparsedArgs and creates a map of
* param names to 'assignment' objects, which have the following properties:
* <ul>
* <li>param - The matching parameter.
* <li>index - Zero based index into where the match came from on the input
* <li>value - The matching input
* </ul>
*/
CliRequisition.prototype._assign = function(args) {
if (args.length === 0) {
this.setDefaultValues();
return;
}
// Create an error if the command does not take parameters, but we have
// been given them ...
if (this.assignmentCount === 0) {
// TODO: previously we were doing some extra work to avoid this if
// we determined that we had args that were all whitespace, but
// probably given our tighter tokenize() this won't be an issue?
this._addHint(Status.INVALID,
this.command.name + ' does not take any parameters',
Argument.merge(args));
return;
}
// Special case: if there is only 1 parameter, and that's of type
// text we put all the params into the first param
if (this.assignmentCount == 1) {
var assignment = this.getAssignment(0);
if (assignment.param.type.name === 'text') {
assignment.setArgument(Argument.merge(args));
return;
}
}
var assignments = this.cloneAssignments();
var names = this.getParameterNames();
// Extract all the named parameters
var used = [];
assignments.forEach(function(assignment) {
var namedArgText = '--' + assignment.name;
var i = 0;
while (true) {
var arg = args[i];
if (namedArgText !== arg.text) {
i++;
if (i >= args.length) {
break;
}
continue;
}
// boolean parameters don't have values, default to false
if (assignment.param.type.name === 'boolean') {
assignment.setValue(true);
}
else {
if (i + 1 < args.length) {
// Missing value portion of this named param
this._addHint(Status.INCOMPLETE,
'Missing value for: ' + namedArgText,
args[i]);
}
else {
args.splice(i + 1, 1);
assignment.setArgument(args[i + 1]);
}
}
util.arrayRemove(names, assignment.name);
args.splice(i, 1);
// We don't need to i++ if we splice
}
}, this);
// What's left are positional parameters assign in order
names.forEach(function(name) {
var assignment = this.getAssignment(name);
if (args.length === 0) {
// No more values
assignment.setValue(undefined); // i.e. default
}
else {
var arg = args[0];
args.splice(0, 1);
assignment.setArgument(arg);
}
}, this);
if (args.length > 0) {
var remaining = Argument.merge(args);
this._addHint(Status.INVALID,
'Input \'' + remaining.text + '\' makes no sense.',
remaining);
}
};
})();
exports.CliRequisition = CliRequisition;
/**
* Split up the input taking into account ' and "
*/
function _tokenize(typed) {
var OUTSIDE = 1; // The last character was whitespace
var IN_SIMPLE = 2; // The last character was part of a parameter
var IN_SINGLE_Q = 3; // We're inside a single quote: '
var IN_DOUBLE_Q = 4; // We're inside double quotes: "
var mode = OUTSIDE;
// First we un-escape. This list was taken from:
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Unicode
// We are generally converting to their real values except for \', \"
// and '\ ' which we are converting to unicode private characters so we
// can distinguish them from ', " and ' ', which have special meaning.
// They need swapping back post-split - see unescape()
typed = typed
.replace(/\\\\/g, '\\')
.replace(/\\b/g, '\b')
.replace(/\\f/g, '\f')
.replace(/\\n/g, '\n')
.replace(/\\r/g, '\r')
.replace(/\\t/g, '\t')
.replace(/\\v/g, '\v')
.replace(/\\n/g, '\n')
.replace(/\\r/g, '\r')
.replace(/\\ /g, '\uF000')
.replace(/\\'/g, '\uF001')
.replace(/\\"/g, '\uF002');
function unescape(str) {
return str
.replace(/\uF000/g, ' ')
.replace(/\uF001/g, '\'')
.replace(/\uF002/g, '"');
}
var i = 0;
var start = 0; // Where did this section start?
var priorSpace = '';
var args = [];
while (true) {
if (i >= typed.length) {
// There is no more - tidy up
if (mode !== OUTSIDE) {
var str = unescape(typed.substring(start, i));
args.push(new Argument(str, start, i, priorSpace));
}
break;
}
var c = typed[i];
switch (mode) {
case OUTSIDE:
if (c === '\'') {
priorSpace = typed.substring(start, i);
mode = IN_SINGLE_Q;
start = i + 1;
}
else if (c === '"') {
priorSpace = typed.substring(start, i);
mode = IN_DOUBLE_Q;
start = i + 1;
}
else if (/ /.test(c)) {
// Still whitespace, do nothing
}
else {
priorSpace = typed.substring(start, i);
mode = IN_SIMPLE;
start = i;
}
break;
case IN_SIMPLE:
// There is an edge case of xx'xx which we are assuming to be
// a single parameter (and same with ")
if (c === ' ') {
var str = unescape(typed.substring(start, i));
args.push(new Argument(str, start, i, priorSpace));
mode = OUTSIDE;
start = i;
priorSpace = '';
}
break;
case IN_SINGLE_Q:
if (c === '\'') {
var str = unescape(typed.substring(start, i));
args.push(new Argument(str, start, i, priorSpace));
mode = OUTSIDE;
start = i + 1;
priorSpace = '';
}
break;
case IN_DOUBLE_Q:
if (c === '"') {
var str = unescape(typed.substring(start, i));
args.push(new Argument(str, start, i, priorSpace));
mode = OUTSIDE;
start = i + 1;
priorSpace = '';
}
break;
}
i++;
}
return args;
}
exports._tokenize = _tokenize;
/**
* Looks in the canon for a command extension that matches what has been
* typed at the command line.
*/
function _split(args) {
if (args.length === 0) {
return undefined;
}
var argsUsed = 0;
var lookup = '';
var command;
while (true) {
argsUsed++;
lookup += args.map(function(arg) {
return arg.text;
}).slice(0, argsUsed).join(' ');
command = canon.getCommand(lookup);
if (!command) {
// Not found. break with command == null
return undefined;
}
/*
// Previously we needed a way to hide commands depending context.
// We have not resurrected that feature yet.
if (!keyboard.flagsMatch(command.predicates, this.flags)) {
// If the predicates say 'no match' then go LA LA LA
command = null;
break;
}
*/
if (command.exec) {
// Valid command, break with command valid
break;
}
// command, but no exec - this must be a sub-command
lookup += ' ';
}
// Remove the used args
for (var i = 0; i < argsUsed; i++) {
args.shift();
}
return command;
}
exports._split = _split;
/**
* Provide some documentation for a command.
* TODO: this should return a hint
*/
function documentCommand(command) {
var docs = [];
docs.push('<h1>' + command.name + '</h1>');
docs.push('<h2>Summary</h2>');
docs.push('<p>' + command.description + '</p>');
if (command.manual) {
docs.push('<h2>Description</h2>');
docs.push('<p>' + command.description + '</p>');
}
if (command.params && command.params.length > 0) {
docs.push('<h2>Synopsis</h2>');
docs.push('<pre>');
docs.push(command.name);
var optionalParamCount = 0;
command.params.forEach(function(param) {
if (param.defaultValue === undefined) {
docs.push(' <i>');
docs.push(param.name);
docs.push('</i>');
}
else if (param.defaultValue === null) {
docs.push(' <i>[');
docs.push(param.name);
docs.push(']</i>');
}
else {
optionalParamCount++;
}
}, this);
if (optionalParamCount > 3) {
docs.push(' [options]');
} else if (optionalParamCount > 0) {
command.params.forEach(function(param) {
if (param.defaultValue) {
docs.push(' [--<i>');
docs.push(param.name);
if (param.type.name === 'boolean') {
docs.push('</i>');
}
else {
docs.push('</i> ' + param.type.name);
}
docs.push(']');
}
}, this);
}
docs.push('</pre>');
docs.push('<h2>Parameters</h2>');
command.params.forEach(function(param) {
docs.push('<h3 class="cmd_body"><i>' + param.name + '</i></h3>');
docs.push('<p>' + param.description + '</p>');
if (param.type.defaultValue) {
docs.push('<p>Default: ' + param.type.defaultValue + '</p>');
}
}, this);
}
return docs.join('');
};
exports.documentCommand = documentCommand;
});

59
plugins/cockpit/index.js Normal file
View file

@ -0,0 +1,59 @@
/* ***** 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):
* Kevin Dangoor (kdangoor@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) {
exports.startup = function(data, reason) {
window.testCli = require('cockpit/test/testCli');
var plain = require('cockpit/ui/plain');
plain.startup(data, reason);
};
/*
exports.shutdown(data, reason) {
deps.forEach(function(dep) {
var module = require(dep);
if (typeof module.shutdown === "function") {
module.shutdown(data, reason);
}
});
};
*/
});

View file

@ -0,0 +1,317 @@
/* ***** 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 test = {
success: function(message) {
console.log(message);
},
fail: function(message) {
test._recordThrow("fail", arguments);
},
assertTrue: function(value) {
if (!value) {
test._recordThrow("assertTrue", arguments);
}
},
verifyTrue: function(value) {
if (!value) {
test._recordTrace("verifyTrue", arguments);
}
},
assertFalse: function(value) {
if (value) {
test._recordThrow("assertFalse", arguments);
}
},
verifyFalse: function(value) {
if (value) {
test._recordTrace("verifyFalse", arguments);
}
},
assertNull: function(value) {
if (value !== null) {
test._recordThrow("assertNull", arguments);
}
},
verifyNull: function(value) {
if (value !== null) {
test._recordTrace("verifyNull", arguments);
}
},
assertNotNull: function(value) {
if (value === null) {
test._recordThrow("assertNotNull", arguments);
}
},
verifyNotNull: function(value) {
if (value === null) {
test._recordTrace("verifyNotNull", arguments);
}
},
assertUndefined: function(value) {
if (value !== undefined) {
test._recordThrow("assertUndefined", arguments);
}
},
verifyUndefined: function(value) {
if (value !== undefined) {
test._recordTrace("verifyUndefined", arguments);
}
},
assertNotUndefined: function(value) {
if (value === undefined) {
test._recordThrow("assertNotUndefined", arguments);
}
},
verifyNotUndefined: function(value) {
if (value === undefined) {
test._recordTrace("verifyNotUndefined", arguments);
}
},
assertNaN: function(value) {
if (!isNaN(value)) {
test._recordThrow("assertNaN", arguments);
}
},
verifyNaN: function(value) {
if (!isNaN(value)) {
test._recordTrace("verifyNaN", arguments);
}
},
assertNotNaN: function(value) {
if (isNaN(value)) {
test._recordThrow("assertNotNaN", arguments);
}
},
verifyNotNaN: function(value) {
if (isNaN(value)) {
test._recordTrace("verifyNotNaN", arguments);
}
},
assertEqual: function(expected, actual) {
if (!test._isEqual(expected, actual)) {
test._recordThrow("assertEqual", arguments);
}
},
verifyEqual: function(expected, actual) {
if (!test._isEqual(expected, actual)) {
test._recordTrace("verifyEqual", arguments);
}
},
assertNotEqual: function(expected, actual) {
if (test._isEqual(expected, actual)) {
test._recordThrow("assertNotEqual", arguments);
}
},
verifyNotEqual: function(expected, actual) {
if (test._isEqual(expected, actual)) {
test._recordTrace("verifyNotEqual", arguments);
}
},
assertSame: function(expected, actual) {
if (expected !== actual) {
test._recordThrow("assertSame", arguments);
}
},
verifySame: function(expected, actual) {
if (expected !== actual) {
test._recordTrace("verifySame", arguments);
}
},
assertNotSame: function(expected, actual) {
if (expected !== actual) {
test._recordThrow("assertNotSame", arguments);
}
},
verifyNotSame: function(expected, actual) {
if (expected !== actual) {
test._recordTrace("verifyNotSame", arguments);
}
},
_recordTrace: function() {
test._record.apply(this, arguments);
console.trace();
},
_recordThrow: function() {
test._record.apply(this, arguments);
throw new Error();
},
_record: function() {
console.error(arguments);
var message = arguments[0] + "(";
var data = arguments[1];
if (typeof data == "string") {
message += data;
}
else {
for (var i = 0; i < data.length; i++) {
if (i != 0){message += ", ";}
message += data[i];
}
}
message += ")";
console.log(message);
},
_isEqual: function(expected, actual, depth) {
if (!depth){depth = 0;}
// Rather than failing we assume that it works!
if (depth > 10) {
return true;
}
if (expected == null) {
if (actual != null) {
console.log("expected: null, actual non-null: ", actual);
return false;
}
return true;
}
if (typeof(expected) == "number" && isNaN(expected)) {
if (!(typeof(actual) == "number" && isNaN(actual))) {
console.log("expected: NaN, actual non-NaN: ", actual);
return false;
}
return true;
}
if (actual == null) {
if (expected != null) {
console.log("actual: null, expected non-null: ", expected);
return false;
}
return true; // we wont get here of course ...
}
if (typeof expected == "object") {
if (!(typeof actual == "object")) {
console.log("expected object, actual not an object");
return false;
}
var actualLength = 0;
for (var prop in actual) {
if (typeof actual[prop] != "function" ||
typeof expected[prop] != "function") {
var nest = test._isEqual(actual[prop], expected[prop], depth + 1);
if (typeof nest != "boolean" || !nest) {
console.log("element '" + prop + "' does not match: " + nest);
return false;
}
}
actualLength++;
}
// need to check length too
var expectedLength = 0;
for (prop in expected) expectedLength++;
if (actualLength != expectedLength) {
console.log("expected object size = " + expectedLength +
", actual object size = " + actualLength);
return false;
}
return true;
}
if (actual != expected) {
console.log("expected = " + expected + " (type=" + typeof expected + "), " +
"actual = " + actual + " (type=" + typeof actual + ")");
return false;
}
if (expected instanceof Array) {
if (!(actual instanceof Array)) {
console.log("expected array, actual not an array");
return false;
}
if (actual.length != expected.length) {
console.log("expected array length = " + expected.length +
", actual array length = " + actual.length);
return false;
}
for (var i = 0; i < actual.length; i++) {
var inner = test._isEqual(actual[i], expected[i], depth + 1);
if (typeof inner != "boolean" || !inner) {
console.log("element " + i + " does not match: " + inner);
return false;
}
}
return true;
}
return true;
}
};
exports.test = test;
});

View file

@ -0,0 +1,279 @@
/* ***** 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 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 test = require('cockpit/test/assert').test;
var Status = require('pilot/types').Status;
var settings = require('pilot/settings').settings;
var tokenize = require('cockpit/cli')._tokenize;
var split = require('cockpit/cli')._split;
var CliRequisition = require('cockpit/cli').CliRequisition;
exports.testAll = function() {
exports.testTokenize();
exports.testSplit();
exports.testCli();
return "testAll Completed";
};
exports.testTokenize = function() {
var args = tokenize('');
test.verifyEqual(0, args.length);
args = tokenize('s');
test.verifyEqual(1, args.length);
test.verifyEqual('s', args[0].text);
test.verifyEqual(0, args[0].start);
test.verifyEqual(1, args[0].end);
test.verifyEqual('', args[0].priorSpace);
args = tokenize('s s');
test.verifyEqual(2, args.length);
test.verifyEqual('s', args[0].text);
test.verifyEqual(0, args[0].start);
test.verifyEqual(1, args[0].end);
test.verifyEqual('', args[0].priorSpace);
test.verifyEqual('s', args[1].text);
test.verifyEqual(2, args[1].start);
test.verifyEqual(3, args[1].end);
test.verifyEqual(' ', args[1].priorSpace);
args = tokenize(' 1234 \'12 34\'');
test.verifyEqual(2, args.length);
test.verifyEqual('1234', args[0].text);
test.verifyEqual(1, args[0].start);
test.verifyEqual(5, args[0].end);
test.verifyEqual(' ', args[0].priorSpace);
test.verifyEqual('12 34', args[1].text);
test.verifyEqual(8, args[1].start);
test.verifyEqual(13, args[1].end);
test.verifyEqual(' ', args[1].priorSpace);
args = tokenize('12\'34 "12 34" \\'); // 12'34 "12 34" \
test.verifyEqual(3, args.length);
test.verifyEqual('12\'34', args[0].text);
test.verifyEqual(0, args[0].start);
test.verifyEqual(5, args[0].end);
test.verifyEqual('', args[0].priorSpace);
test.verifyEqual('12 34', args[1].text);
test.verifyEqual(7, args[1].start);
test.verifyEqual(12, args[1].end);
test.verifyEqual(' ', args[1].priorSpace);
test.verifyEqual('\\', args[2].text);
test.verifyEqual(14, args[2].start);
test.verifyEqual(15, args[2].end);
test.verifyEqual(' ', args[2].priorSpace);
args = tokenize('a\\ b \\t\\n\\r \\\'x\\\" \'d'); // a_b \t\n\r \'x\" 'd
test.verifyEqual(4, args.length);
test.verifyEqual('a b', args[0].text);
test.verifyEqual(0, args[0].start);
test.verifyEqual(3, args[0].end);
test.verifyEqual('', args[0].priorSpace);
test.verifyEqual('\t\n\r', args[1].text);
test.verifyEqual(4, args[1].start);
test.verifyEqual(7, args[1].end);
test.verifyEqual(' ', args[1].priorSpace);
test.verifyEqual('\'x"', args[2].text);
test.verifyEqual(8, args[2].start);
test.verifyEqual(11, args[2].end);
test.verifyEqual(' ', args[2].priorSpace);
test.verifyEqual('d', args[3].text);
test.verifyEqual(13, args[3].start);
test.verifyEqual(14, args[3].end);
test.verifyEqual(' ', args[3].priorSpace);
return "testTokenize Completed";
};
exports.testSplit = function() {
var args = tokenize('s');
var command = split(args);
test.verifyEqual(1, args.length);
test.verifyEqual('s', args[0].text);
test.verifyUndefined(command);
var args = tokenize('set');
var command = split(args);
test.verifyEqual([], args);
test.verifyEqual('set', command.name);
var args = tokenize('set a b');
var command = split(args);
test.verifyEqual('set', command.name);
test.verifyEqual(2, args.length);
test.verifyEqual('a', args[0].text);
test.verifyEqual('b', args[1].text);
// TODO: add tests for sub commands
return "testSplit Completed";
};
exports.testCli = function() {
var hints;
var hint0;
var settingAssignment;
var valueAssignment;
var cli = new CliRequisition();
function update(input) {
cli.update(input);
hints = cli.getHints();
hint0 = (hints.length !== 0) ? hints[0] : undefined;
if (cli.command && cli.command.name === 'set') {
settingAssignment = cli.getAssignment('setting');
valueAssignment = cli.getAssignment('value');
}
else {
settingAssignment = undefined;
valueAssignment = undefined;
}
}
var historyLengthSetting = settings.getSetting('historyLength');
update({ typed: '', cursor: { start: 0, end: 0 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
test.verifyEqual(0, hint0.start);
test.verifyEqual(0, hint0.end);
test.verifyNull(cli.command);
update({ typed: 's', cursor: { start: 1, end: 1 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
test.verifyNotEqual(-1, hint0.message.indexOf('possibilities'));
test.verifyEqual(0, hint0.start);
test.verifyEqual(1, hint0.end);
test.verifyTrue(hint0.predictions.length > 0);
// This is slightly fragile because it depends on the configuration
test.verifyTrue(hint0.predictions.length < 20);
test.verifyNotEqual(-1, hint0.predictions.indexOf('set'));
test.verifyNull(cli.command);
update({ typed: 'set', cursor: { start: 3, end: 3 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.VALID, hint0.status);
test.verifyEqual(0, hint0.start);
test.verifyEqual(3, hint0.end);
test.verifyEqual('set', cli.command.name);
update({ typed: 'set ', cursor: { start: 4, end: 4 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.VALID, hint0.status);
test.verifyEqual(0, hint0.start);
// Technically the command ends at 3, but we're returning 4 currently.
// This is caused by us using the whole input to determine the length.
// Maybe one day we should fix this?
//test.verifyEqual(3, hint0.end);
test.verifyEqual('set', cli.command.name);
update({ typed: 'set h', cursor: { start: 5, end: 5 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
test.verifyTrue(hint0.predictions.length > 0);
test.verifyEqual(4, hint0.start);
test.verifyEqual(5, hint0.end);
test.verifyNotEqual(-1, hint0.predictions.indexOf('historyLength'));
test.verifyEqual('set', cli.command.name);
test.verifyEqual('h', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt', cursor: { start: 16, end: 16 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
test.verifyEqual(1, hint0.predictions.length);
test.verifyEqual(4, hint0.start);
test.verifyEqual(16, hint0.end);
test.verifyEqual('historyLength', hint0.predictions[0]);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt', cursor: { start: 1, end: 1 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INVALID, hint0.status);
test.verifyEqual(4, hint0.start);
test.verifyEqual(16, hint0.end);
test.verifyEqual(1, hint0.predictions.length);
test.verifyEqual('historyLength', hint0.predictions[0]);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt ', cursor: { start: 17, end: 17 } });
test.verifyEqual(1, hints.length);
test.verifyEqual(Status.INVALID, hint0.status);
test.verifyEqual(4, hint0.start);
test.verifyEqual(16, hint0.end);
test.verifyEqual(1, hint0.predictions.length);
test.verifyEqual('historyLength', hint0.predictions[0]);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLength', cursor: { start: 17, end: 17 } });
test.verifyEqual(0, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
update({ typed: 'set historyLength ', cursor: { start: 18, end: 18 } });
test.verifyEqual(0, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
update({ typed: 'set historyLength 6', cursor: { start: 19, end: 19 } });
test.verifyEqual(0, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
test.verifyEqual('6', valueAssignment.arg.text);
test.verifyEqual(6, valueAssignment.value);
test.verifyEqual('number', typeof valueAssignment.value);
// TODO: Add test to see that a command without mandatory param causes INVALID
return "testCli Completed";
};
});

View file

@ -0,0 +1,40 @@
/* ***** 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 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) {
});

View file

@ -0,0 +1,2 @@
#cockpit { color: red; }

View file

@ -0,0 +1,98 @@
/* ***** 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 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 editorCss = require("text!cockpit/ui/plain.css");
var dom = require("pilot/dom").dom;
dom.importCssString(editorCss);
var CliRequisition = require('cockpit/cli').CliRequisition;
var keyutil = require('pilot/keyboard/keyutil');
exports.startup = function(data, reason) {
// TODO: we should have a better way to specify command lines???
this.input = document.getElementById('cockpit');
if (!this.input) {
console.log('No element with an id of cockpit. Bailing on plain cli');
return;
}
var cli = new CliRequisition();
/*
// All this does is to kill TABs normal use. I wonder if we can train
// people to use right arrow? Probably not? but ...
keyutil.addKeyDownListener(input, function(ev) {
// env.commandLine = this;
// var handled = keyboardManager.processKeyEvent(ev, this, {
// isCommandLine: true, isKeyUp: false
// });
if (ev.keyCode === keyutil.KeyHelper.KEY.TAB) {
return true;
}
//return handled;
}.bind(this));
*/
this.input.addEventListener('keyup', function(ev) {
/*
var handled = keyboardManager.processKeyEvent(ev, this, {
isCommandLine: true, isKeyUp: true
});
*/
if (ev.keyCode === keyutil.KeyHelper.KEY.RETURN) {
cli.exec();
this.input.value = '';
} else {
cli.update({
typed: this.input.value,
cursor: {
start: this.input.selectionStart,
end: this.input.selectionEnd
}
});
console.log(JSON.stringify(cli.getHints()));
}
// return handled;
}.bind(this), true);
};
});

329
plugins/pilot/canon.js Normal file
View file

@ -0,0 +1,329 @@
/* ***** 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;
var catalog = require('pilot/catalog');
var Status = require('pilot/types').Status;
var types = require('pilot/types');
/*
// TODO: this doesn't belong here - or maybe anywhere?
var dimensionsChangedExtensionSpec = {
name: 'dimensionsChanged',
description: 'A dimensionsChanged is a way to be notified of ' +
'changes to the dimension of Skywriter'
};
exports.startup = function(data, reason) {
catalog.addExtensionSpec(commandExtensionSpec);
};
exports.shutdown = function(data, reason) {
catalog.removeExtensionSpec(commandExtensionSpec);
};
*/
var commandExtensionSpec = {
name: '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'
};
var env;
exports.startup = function(data, reason) {
// TODO: this is probably all kinds of evil, but we need something working
env = data.env;
catalog.addExtensionSpec(commandExtensionSpec);
};
exports.shutdown = function(data, reason) {
catalog.removeExtensionSpec(commandExtensionSpec);
};
/**
* Manage a list of commands in the current canon
*/
/**
* A Command is a discrete action optionally with a set of ways to customize
* how it happens. This is here for documentation purposes.
* TODO: Document better
*/
var thingCommand = {
name: 'thing',
description: 'thing is an example command',
params: [{
name: 'param1',
description: 'an example parameter',
type: 'text',
defaultValue: null
}],
exec: function(env, args, request) {
thing();
}
};
var commands = {};
/**
* This registration method isn't like other Ace registration methods because
* it doesn't return a decorated command because there is no functional
* decoration to be done.
* TODO: Are we sure that in the future there will be no such decoration?
*/
exports.addCommand = function(command) {
if (!command.name) {
throw new Error('All registered commands must have a name');
}
if (command.params == null) {
command.params = [];
}
if (!Array.isArray(command.params)) {
throw new Error('command.params must be an array in ' + command.name);
}
// Replace the type
command.params.forEach(function(param) {
if (!param.name) {
throw new Error('In ' + command.name + ': all params must have a name');
}
var lookup = param.type;
param.type = types.getType(lookup);
if (param.type == null) {
throw new Error('In ' + command.name + '/' + param.name +
': can\'t find type for: ' + JSON.stringify(lookup));
}
}, this);
commands[command.name] = command;
};
exports.removeCommand = function(command) {
if (typeof command === 'string') {
delete commands[command];
}
else {
delete commands[command.name];
}
};
exports.getCommand = function(name) {
return commands[name];
};
exports.getCommandNames = function() {
return Object.keys(commands);
};
/**
* Entry point for keyboard accelerators or anything else that knows
* everything it needs to about the command params
* @param command Either a command, or the name of one
*/
exports.exec = function(command, args) {
if (typeof command === 'string') {
command = commands[command];
}
if (!command) {
// TODO: Should we complain more than returning false?
return false;
}
// TODO: Ugg. really?
env.selection = env.editor.getSelection();
var request = new Request();
command.exec(env, args || {}, request);
return true;
};
/**
* We publish a 'addedRequestOutput' event whenever new command begins output
* TODO: make this more obvious
*/
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.
* <p>Future requirements could include:
* <ul>
* <li>Multiple command lines
* <li>The ability to recall key presses (i.e. requests with no output) which
* will likely be needed for macro recording or similar
* <li>The ability to store the command history either on the server or in the
* browser local storage.
* </ul>
* <p>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 });
};
/**
* To create an invocation, you need to do something like this (all the ctor
* args are optional):
* <pre>
* var request = new Request({
* command: command,
* commandExt: commandExt,
* args: args,
* typed: typed
* });
* </pre>
* @constructor
*/
function Request(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(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
*/
Request.prototype._beginOutput = function() {
this._begunOutput = true;
this.outputs = [];
exports.addRequestOutput(this);
};
/**
* Sugar for:
* <pre>request.error = true; request.done(output);</pre>
*/
Request.prototype.doneWithError = function(content) {
this.error = true;
this.done(content);
};
/**
* Declares that this function will not be automatically done when
* the command exits
*/
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.
*/
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.
*/
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', {});
};
exports.Request = Request;
});

65
plugins/pilot/catalog.js Normal file
View file

@ -0,0 +1,65 @@
/* ***** 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):
* Julian Viereck (jviereck@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 extensionSpecs = {};
exports.addExtensionSpec = function(extensionSpec) {
extensionSpecs[extensionSpec.name] = extensionSpec;
};
exports.removeExtensionSpec = function(extensionSpec) {
if (typeof extensionSpec === "string") {
delete extensionSpecs[extensionSpec];
}
else {
delete extensionSpecs[extensionSpec.name];
}
};
exports.getExtensionSpec = function(name) {
return extensionSpecs[name];
};
exports.getExtensionSpecs = function() {
return Object.keys(extensionSpecs);
};
});

View file

@ -0,0 +1,146 @@
/* ***** 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 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):
* Skywriter Team (skywriter@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 setCommandSpec = {
name: 'set',
params: [
{
name: 'setting',
type: 'setting',
description: 'The name of the setting to display or alter',
defaultValue: null
},
{
name: 'value',
type: 'settingValue',
description: 'The new value for the chosen setting',
defaultValue: null
}
],
description: 'define and show settings',
exec: function(env, args, request) {
var html;
if (!args.setting) {
// 'set' by itself lists all the settings
var settingsList = env.settings._list();
html = '';
// first sort the settingsList based on the key
settingsList.sort(function(a, b) {
if (a.key < b.key) {
return -1;
} else if (a.key == b.key) {
return 0;
} else {
return 1;
}
});
var url = 'https://wiki.mozilla.org/Labs/Skywriter/Settings#' +
setting.key;
settingsList.forEach(function(setting) {
html += '<a class="setting" href="' + url +
'" title="View external documentation on setting: ' +
setting.key +
'" target="_blank">' +
setting.key +
'</a> = ' +
setting.value +
'<br/>';
});
} else {
var setting = env.settings.get(args.setting);
if (!setting) {
request.doneWithError('No setting with the name <strong>' +
setting.name + '</strong>.');
return;
}
// set with only a setting, shows the value for that setting
if (args.value === undefined) {
html = '<strong>' + setting.name + '</strong> = ' +
setting.get();
} else {
// Actually change the setting
setting.set(args.value);
html = 'Setting: <strong>' + setting.name + '</strong> = ' +
setting.get();
}
}
request.done(html);
}
};
var unsetCommandSpec = {
name: 'unset',
params: [
{
name: 'setting',
type: 'setting',
description: 'The name of the setting to return to defaults'
}
],
description: 'unset a setting entirely',
exec: function(env, args, request) {
var setting = env.settings.get(args.setting);
if (!setting) {
request.doneWithError('No setting with the name <strong>' +
args.setting + '</strong>.');
return;
}
setting.reset();
request.done('Reset ' + setting.name + ' to default: ' +
env.settings.get(args.setting));
}
};
var canon = require('pilot/canon');
exports.startup = function(data, reason) {
canon.addCommand(setCommandSpec);
canon.addCommand(unsetCommandSpec);
};
exports.shutdown = function(data, reason) {
canon.removeCommand(setCommandSpec);
canon.removeCommand(unsetCommandSpec);
};
});

76
plugins/pilot/console.js Normal file
View file

@ -0,0 +1,76 @@
/* ***** 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)
* Patrick Walton (pwalton@mozilla.com)
* Julian Viereck (jviereck@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) {
/**
* This object represents a "safe console" object that forwards debugging
* messages appropriately without creating a dependency on Firebug in Firefox.
*/
var noop = function() {};
// These are the functions that are available in Chrome 4/5, Safari 4
// and Firefox 3.6. Don't add to this list without checking browser support
var NAMES = [
"assert", "count", "debug", "dir", "dirxml", "error", "group", "groupEnd",
"info", "log", "profile", "profileEnd", "time", "timeEnd", "trace", "warn"
];
if (typeof(window) === 'undefined') {
// We're in a web worker. Forward to the main thread so the messages
// will show up.
NAMES.forEach(function(name) {
exports[name] = function() {
var args = Array.prototype.slice.call(arguments);
var msg = { op: 'log', method: name, args: args };
postMessage(JSON.stringify(msg));
};
});
} else {
// For each of the console functions, copy them if they exist, stub if not
NAMES.forEach(function(name) {
if (window.console && window.console[name]) {
exports[name] = window.console[name].bind(window.console);
} else {
exports[name] = noop;
}
});
}
});

63
plugins/pilot/core.js Normal file
View file

@ -0,0 +1,63 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
*
* 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 core = {};
var os = (navigator.platform.match(/mac|win|linux/i) || ["other"])[0].toLowerCase();
core.isWin = (os == "win");
core.isMac = (os == "mac");
core.isLinux = (os == "linux");
core.isIE = ! + "\v1";
core.isGecko = window.controllers && window.navigator.product === "Gecko";
core.provide = function(namespace) {
var parts = namespace.split(".");
var obj = window;
for (var i=0; i<parts.length; i++) {
var part = parts[i];
if (!obj[part]) {
obj[part] = {};
}
obj = obj[part];
}
};
exports.core = core;
});

141
plugins/pilot/dom.js Normal file
View file

@ -0,0 +1,141 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
*
* 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 lang = require("pilot/lang").lang;
var dom = {};
dom.setText = function(elem, text) {
if (elem.innerText !== undefined) {
elem.innerText = text;
}
if (elem.textContent !== undefined) {
elem.textContent = text;
}
};
dom.hasCssClass = function(el, name) {
var classes = el.className.split(/\s+/g);
return lang.arrayIndexOf(classes, name) !== -1;
};
dom.addCssClass = function(el, name) {
if (!dom.hasCssClass(el, name)) {
el.className += " " + name;
}
};
dom.removeCssClass = function(el, name) {
var classes = el.className.split(/\s+/g);
while (true) {
var index = lang.arrayIndexOf(classes, name);
if (index == -1) {
break;
}
classes.splice(index, 1);
}
el.className = classes.join(" ");
};
dom.importCssString = function(cssText, doc){
doc = doc || document;
if (doc.createStyleSheet) {
var sheet = doc.createStyleSheet();
sheet.cssText = cssText;
}
else {
var style = doc.createElement("style");
style.appendChild(doc.createTextNode(cssText));
doc.getElementsByTagName("head")[0].appendChild(style);
}
};
dom.getInnerWidth = function(element) {
return (parseInt(dom.computedStyle(element, "paddingLeft"))
+ parseInt(dom.computedStyle(element, "paddingRight")) + element.clientWidth);
};
dom.getInnerHeight = function(element) {
return (parseInt(dom.computedStyle(element, "paddingTop"))
+ parseInt(dom.computedStyle(element, "paddingBottom")) + element.clientHeight);
};
dom.computedStyle = function(element, style) {
if (window.getComputedStyle) {
return (window.getComputedStyle(element, "") || {})[style] || "";
}
else {
return element.currentStyle[style];
}
};
dom.scrollbarWidth = function() {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement("div");
var style = outer.style;
style.position = "absolute";
style.left = "-10000px";
style.overflow = "hidden";
style.width = "200px";
style.height = "150px";
outer.appendChild(inner);
document.body.appendChild(outer);
var noScrollbar = inner.offsetWidth;
style.overflow = "scroll";
var withScrollbar = inner.offsetWidth;
if (noScrollbar == withScrollbar) {
withScrollbar = outer.clientWidth;
}
document.body.removeChild(outer);
return noScrollbar-withScrollbar;
};
exports.dom = dom;
});

242
plugins/pilot/event.js Normal file
View file

@ -0,0 +1,242 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
*
* 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 core = require("pilot/core").core;
var event = {};
event.addListener = function(elem, type, callback) {
if (elem.addEventListener) {
return elem.addEventListener(type, callback, false);
}
if (elem.attachEvent) {
var wrapper = function() {
callback(window.event);
};
callback._wrapper = wrapper;
elem.attachEvent("on" + type, wrapper);
}
};
event.removeListener = function(elem, type, callback) {
if (elem.removeEventListener) {
return elem.removeEventListener(type, callback, false);
}
if (elem.detachEvent) {
elem.detachEvent("on" + type, callback._wrapper || callback);
}
};
event.stopEvent = function(e) {
event.stopPropagation(e);
event.preventDefault(e);
return false;
};
event.stopPropagation = function(e) {
if (e.stopPropagation)
e.stopPropagation();
else
e.cancelBubble = true;
};
event.preventDefault = function(e) {
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
};
event.getDocumentX = function(e) {
if (e.clientX) {
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
return e.clientX + scrollLeft;
} else {
return e.pageX;
}
};
event.getDocumentY = function(e) {
if (e.clientY) {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
return e.clientY + scrollTop;
} else {
return e.pageX;
}
};
/**
* @return {Number} 0 for left button, 1 for middle button, 2 for right button
*/
event.getButton = function(e) {
// DOM Event
if (e.preventDefault) {
return e.button;
}
// old IE
else {
return Math.max(e.button - 1, 2);
}
};
if (document.documentElement.setCapture) {
event.capture = function(el, eventHandler, releaseCaptureHandler) {
function onMouseMove(e) {
eventHandler(e);
return event.stopPropagation(e);
}
function onReleaseCapture(e) {
eventHandler && eventHandler(e);
releaseCaptureHandler && releaseCaptureHandler();
event.removeListener(el, "mousemove", eventHandler);
event.removeListener(el, "mouseup", onReleaseCapture);
event.removeListener(el, "losecapture", onReleaseCapture);
el.releaseCapture();
}
event.addListener(el, "mousemove", eventHandler);
event.addListener(el, "mouseup", onReleaseCapture);
event.addListener(el, "losecapture", onReleaseCapture);
el.setCapture();
};
}
else {
event.capture = function(el, eventHandler, releaseCaptureHandler) {
function onMouseMove(e) {
eventHandler(e);
e.stopPropagation();
}
function onMouseUp(e) {
eventHandler && eventHandler(e);
releaseCaptureHandler && releaseCaptureHandler();
document.removeEventListener("mousemove", onMouseMove, true);
document.removeEventListener("mouseup", onMouseUp, true);
e.stopPropagation();
}
document.addEventListener("mousemove", onMouseMove, true);
document.addEventListener("mouseup", onMouseUp, true);
};
}
event.addMouseWheelListener = function(el, callback) {
var listener = function(e) {
if (e.wheelDelta !== undefined) {
if (e.wheelDeltaX !== undefined) {
e.wheelX = -e.wheelDeltaX / 8;
e.wheelY = -e.wheelDeltaY / 8;
} else {
e.wheelX = 0;
e.wheelY = -e.wheelDelta / 8;
}
}
else {
if (e.axis && e.axis == e.HORIZONTAL_AXIS) {
e.wheelX = (e.detail || 0) * 5;
e.wheelY = 0;
} else {
e.wheelX = 0;
e.wheelY = (e.detail || 0) * 5;
}
}
callback(e);
};
event.addListener(el, "DOMMouseScroll", listener);
event.addListener(el, "mousewheel", listener);
};
event.addMultiMouseDownListener = function(el, button, count, timeout, callback) {
var clicks = 0;
var startX, startY;
var listener = function(e) {
clicks += 1;
if (clicks == 1) {
startX = e.clientX;
startY = e.clientY;
setTimeout(function() {
clicks = 0;
}, timeout || 600);
}
if (event.getButton(e) != button
|| Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5)
clicks = 0;
if (clicks == count) {
clicks = 0;
callback(e);
}
return event.preventDefault(e);
};
event.addListener(el, "mousedown", listener);
core.isIE && event.addListener(el, "dblclick", listener);
};
event.addKeyListener = function(el, callback) {
var lastDown = null;
event.addListener(el, "keydown", function(e) {
lastDown = e.keyIdentifier || e.keyCode;
return callback(e);
});
// repeated keys are fired as keypress and not keydown events
if (core.isMac && core.isGecko) {
event.addListener(el, "keypress", function(e) {
var keyId = e.keyIdentifier || e.keyCode;
if (lastDown !== keyId) {
return callback(e);
} else {
lastDown = null;
}
});
}
};
exports.event = event;
});

View file

@ -0,0 +1,86 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
*
* 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 lang = require('pilot/lang').lang;
var EventEmitter = {};
EventEmitter._dispatchEvent = function(eventName, e) {
this._eventRegistry = this._eventRegistry || {};
var listeners = this._eventRegistry[eventName];
if (!listeners || !listeners.length) return;
var e = e || {};
e.type = eventName;
for (var i=0; i<listeners.length; i++) {
listeners[i](e);
}
};
EventEmitter.on =
EventEmitter.addEventListener = function(eventName, callback) {
this._eventRegistry = this._eventRegistry || {};
var listeners = this._eventRegistry[eventName];
if (!listeners) {
var listeners = this._eventRegistry[eventName] = [];
}
if (lang.arrayIndexOf(listeners, callback) == -1) {
listeners.push(callback);
}
};
EventEmitter.removeEventListener = function(eventName, callback) {
this._eventRegistry = this._eventRegistry || {};
var listeners = this._eventRegistry[eventName];
if (!listeners) {
return;
}
var index = lang.arrayIndexOf(listeners, callback);
if (index !== -1) {
listeners.splice(index, 1);
}
};
exports.EventEmitter = EventEmitter;
});

View file

@ -0,0 +1,145 @@
/* ***** 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):
* Kevin Dangoor (kdangoor@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) {
// Narwhal's shim for ES5 defineProperty
// ES5 15.2.3.6
if (!Object.defineProperty) {
Object.defineProperty = function(object, property, descriptor) {
var has = Object.prototype.hasOwnProperty;
if (typeof descriptor == "object" && object.__defineGetter__) {
if (has.call(descriptor, "value")) {
if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property)) {
// data property defined and no pre-existing accessors
object[property] = descriptor.value;
}
if (has.call(descriptor, "get") || has.call(descriptor, "set")) {
// descriptor has a value property but accessor already exists
throw new TypeError("Object doesn't support this action");
}
}
// fail silently if "writable", "enumerable", or "configurable"
// are requested but not supported
/*
// alternate approach:
if ( // can't implement these features; allow false but not true
!(has.call(descriptor, "writable") ? descriptor.writable : true) ||
!(has.call(descriptor, "enumerable") ? descriptor.enumerable : true) ||
!(has.call(descriptor, "configurable") ? descriptor.configurable : true)
)
throw new RangeError(
"This implementation of Object.defineProperty does not " +
"support configurable, enumerable, or writable."
);
*/
else if (typeof descriptor.get == "function") {
object.__defineGetter__(property, descriptor.get);
}
if (typeof descriptor.set == "function") {
object.__defineSetter__(property, descriptor.set);
}
}
return object;
};
}
// ES5 15.2.3.7
if (!Object.defineProperties) {
Object.defineProperties = function(object, properties) {
for (var property in properties) {
if (Object.prototype.hasOwnProperty.call(properties, property)) {
Object.defineProperty(object, property, properties[property]);
}
}
return object;
};
}
/**
* Array detector.
* Firefox 3.5 and Safari 4 have this already. Chrome 4 however ...
* Note to Dojo - your isArray is still broken: instanceof doesn't work with
* Arrays taken from a different frame/window.
*/
if (!Array.isArray) {
Array.isArray = function(data) {
return data && Object.prototype.toString.call(data) === "[object Array]";
};
}
/**
* Retrieves the list of keys on an object.
*/
if (!Object.keys) {
Object.keys = function(obj) {
var k, ret = [];
for (k in obj) {
if (obj.hasOwnProperty(k)) {
ret.push(k);
}
}
return ret;
};
}
if (!Function.prototype.bind) {
// From Narwhal
Function.prototype.bind = function () {
var args = Array.prototype.slice.call(arguments);
var self = this;
var bound = function () {
return self.call.apply(
self,
args.concat(
Array.prototype.slice.call(arguments)
)
);
};
bound.name = this.name;
bound.displayName = this.displayName;
bound.length = this.length;
bound.unbound = self;
return bound;
};
}
exports.globalsLoaded = true;
});

72
plugins/pilot/index.js Normal file
View file

@ -0,0 +1,72 @@
/* ***** 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):
* Kevin Dangoor (kdangoor@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 ***** */
var deps = [
"pilot/fixoldbrowsers",
"pilot/types/basic",
"pilot/types/command",
"pilot/types/settings",
"pilot/commands/settings",
"pilot/settings/canon",
"pilot/canon"
];
var packages = deps.slice();
packages.unshift("require", "exports", "module");
define(packages, function(require, exports, module) {
exports.startup = function(data, reason) {
deps.forEach(function(dep) {
console.log("test startup for " + dep);
var module = require(dep);
if (typeof module.startup === "function") {
module.startup(data, reason);
}
});
};
/*
exports.shutdown(data, reason) {
deps.forEach(function(dep) {
var module = require(dep);
if (typeof module.shutdown === "function") {
module.shutdown(data, reason);
}
});
};
*/
});

View file

@ -0,0 +1,459 @@
/* ***** 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 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):
* Skywriter Team (skywriter@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(request, exports, module) {
var console = require('pilot/console');
var Trace = require('pilot/stacktrace').Trace;
var keyutil = require('pilot/keyboard/keyutil');
var history = require('canon/history');
var Request = require('canon/request').Request;
var env = require('environment').env;
exports.keymappings = {};
exports.addKeymapping = function(mapping) {
exports.keymappings[mapping.name] = mapping;
};
exports.removeKeymapping = function(name) {
delete exports.keymapping[name];
};
exports.startup = function(data, reason) {
var settings = data.env.settings;
// TODO register this
// catalog.addExtensionSpec("keymapping", {
// "description": "A keymapping defines how keystrokes are interpreted.",
// "params": [
// {
// "name": "states",
// "required": true,
// "description":
// "Holds the states and all the informations about the keymapping. See docs: pluginguide/keymapping"
// }
// ]
// });
settings.settingChange.add({
match: "customKeymapping",
ref: exports.keyboardManager,
func: exports.keyboardManager._customKeymappingChanged
.bind(exports.keyboardManager)
});
};
exports.shutdown = function(data, reason) {
var settings = data.env.settings;
settings.settingChange.remove(exports.keyboardManager);
};
/*
* Things to do to sanitize this code:
* - 'no command' is a bizarre special value at the very least it should be a
* constant to make typos more obvious, but it would be better to refactor
* so that a natural value like null worked.
* - sender seems to be totally customized to the editor case, and the functions
* that we assume that it has make no sense for the commandLine case. We
* should either document and implement the same function set for both cases
* or admit that the cases are different enough to have separate
* implementations.
* - remove remaining sproutcore-isms
* - fold buildFlags into processKeyEvent or something better, preferably the
* latter. We don't want the environment to become a singleton
*/
/**
* Every time we call processKeyEvent, we pass in some flags that require the
* same processing to set them up. This function can be called to do that
* setup.
* @param env Probably environment.env
* @param flags Probably {} (but check other places where this is called)
*/
exports.buildFlags = function(flags) {
flags.context = env.contexts[0];
return flags;
};
/**
* The canon, or the repository of commands, contains functions to process
* events and dispatch command messages to targets.
* @class
*/
var KeyboardManager = function() { };
KeyboardManager.prototype = {
_customKeymappingCache: { states: {} },
/**
* Searches through the command canon for an event matching the given flags
* with a key equivalent matching the given SproutCore event, and, if the
* command is found, sends a message to the appropriate target.
*
* This will get a couple of upgrades in the not-too-distant future:
* 1. caching in the Canon for fast lookup based on key
* 2. there will be an extra layer in between to allow remapping via
* user preferences and keyboard mapping plugins
*
* @return True if a matching command was found, false otherwise.
*/
processKeyEvent: function(evt, sender, flags) {
// Use our modified commandCodes function to detect the meta key in
// more circumstances than SproutCore alone does.
var symbolicName = keyutil.commandCodes(evt, true)[0];
if (util.none(symbolicName)) {
return false;
}
// TODO: Maybe it should be the job of our caller to do this?
exports.buildFlags(flags);
flags.isCommandKey = true;
return this._matchCommand(symbolicName, sender, flags);
},
_matchCommand: function(symbolicName, sender, flags) {
var match = this._findCommandExtension(symbolicName, sender, flags);
if (match && match.commandExt !== 'no command') {
if (flags.isTextView) {
sender.resetKeyBuffers();
}
var commandExt = match.commandExt;
commandExt.load(function(command) {
var request = new Request({
command: command,
commandExt: commandExt
});
history.execute(match.args, request);
});
return true;
}
// 'no command' is returned if a keyevent is handled but there is no
// command executed (for example when switchting the keyboard state).
if (match && match.commandExt === 'no command') {
return true;
} else {
return false;
}
},
_buildBindingsRegex: function(bindings) {
// Escape a given Regex string.
bindings.forEach(function(binding) {
if (!util.none(binding.key)) {
binding.key = new RegExp('^' + binding.key + '$');
} else if (Array.isArray(binding.regex)) {
binding.key = new RegExp('^' + binding.regex[1] + '$');
binding.regex = new RegExp(binding.regex.join('') + '$');
} else {
binding.regex = new RegExp(binding.regex + '$');
}
});
},
/**
* Build the RegExp from the keymapping as RegExp can't stored directly
* in the metadata JSON and as the RegExp used to match the keys/buffer
* need to be adapted.
*/
_buildKeymappingRegex: function(keymapping) {
for (state in keymapping.states) {
this._buildBindingsRegex(keymapping.states[state]);
}
keymapping._convertedRegExp = true;
},
/**
* Loop through the commands in the canon, looking for something that
* matches according to #_commandMatches, and return that.
*/
_findCommandExtension: function(symbolicName, sender, flags) {
// If the flags indicate that we handle the textView's input then take
// a look at keymappings as well.
if (flags.isTextView) {
var currentState = sender._keyState;
// Don't add the symbolic name to the key buffer if the alt_ key is
// part of the symbolic name. If it starts with alt_, this means
// that the user hit an alt keycombo and there will be a single,
// new character detected after this event, which then will be
// added to the buffer (e.g. alt_j will result in ∆).
if (!flags.isCommandKey || symbolicName.indexOf('alt_') === -1) {
sender._keyBuffer +=
symbolicName.replace(/ctrl_meta|meta/,'ctrl');
sender._keyMetaBuffer += symbolicName;
}
// List of all the keymappings to look at.
var ak = [ this._customKeymappingCache ];
// Get keymapping extension points.
ak = ak.concat(catalog.getExtensions('keymapping'));
for (var i = 0; i < ak.length; i++) {
// Check if the keymapping has the current state.
if (util.none(ak[i].states[currentState])) {
continue;
}
if (util.none(ak[i]._convertedRegExp)) {
this._buildKeymappingRegex(ak[i]);
}
// Try to match the current mapping.
var result = this._bindingsMatch(
symbolicName,
flags,
sender,
ak[i]);
if (!util.none(result)) {
return result;
}
}
}
var commandExts = catalog.getExtensions('command');
var reply = null;
var args = {};
symbolicName = symbolicName.replace(/ctrl_meta|meta/,'ctrl');
commandExts.some(function(commandExt) {
if (this._commandMatches(commandExt, symbolicName, flags)) {
reply = commandExt;
return true;
}
return false;
}.bind(this));
return util.none(reply) ? null : { commandExt: reply, args: args };
},
/**
* Checks if the given parameters fit to one binding in the given bindings.
* Returns the command and arguments if a command was matched.
*/
_bindingsMatch: function(symbolicName, flags, sender, keymapping) {
var match;
var commandExt = null;
var args = {};
var bufferToUse;
if (!util.none(keymapping.hasMetaKey)) {
bufferToUse = sender._keyBuffer;
} else {
bufferToUse = sender._keyMetaBuffer;
}
// Add the alt_key to the buffer as we don't want it to be in the buffer
// that is saved but for matching, it needs to be there.
if (symbolicName.indexOf('alt_') === 0 && flags.isCommandKey) {
bufferToUse += symbolicName;
}
// Loop over all the bindings of the keymapp until a match is found.
keymapping.states[sender._keyState].some(function(binding) {
// Check if the key matches.
if (binding.key && !binding.key.test(symbolicName)) {
return false;
}
// Check if the regex matches.
if (binding.regex && !(match = binding.regex.exec(bufferToUse))) {
return false;
}
// Check for disallowed matches.
if (binding.disallowMatches) {
for (var i = 0; i < binding.disallowMatches.length; i++) {
if (!!match[binding.disallowMatches[i]]) {
return true;
}
}
}
// Check predicates.
if (!exports.flagsMatch(binding.predicates, flags)) {
return false;
}
// If there is a command to execute, then figure out the
// comand and the arguments.
if (binding.exec) {
// Get the command.
commandExt = catalog.getExtensionByKey('command', binding.exec);
if (util.none(commandExt)) {
throw new Error('Can\'t find command ' + binding.exec +
' in state=' + sender._keyState +
', symbolicName=' + symbolicName);
}
// Bulid the arguments.
if (binding.params) {
var value;
binding.params.forEach(function(param) {
if (!util.none(param.match) && !util.none(match)) {
value = match[param.match] || param.defaultValue;
} else {
value = param.defaultValue;
}
if (param.type === 'number') {
value = parseInt(value, 10);
}
args[param.name] = value;
});
}
sender.resetKeyBuffers();
}
// Handle the 'then' property.
if (binding.then) {
sender._keyState = binding.then;
sender.resetKeyBuffers();
}
// If there is no command matched now, then return a 'false'
// command to stop matching.
if (util.none(commandExt)) {
commandExt = 'no command';
}
return true;
});
if (util.none(commandExt)) {
return null;
}
return { commandExt: commandExt, args: args };
},
/**
* Check that the given command fits the given key name and flags.
*/
_commandMatches: function(commandExt, symbolicName, flags) {
var mappedKeys = commandExt.key;
if (!mappedKeys) {
return false;
}
// Check predicates
if (!exports.flagsMatch(commandExt.predicates, flags)) {
return false;
}
if (typeof(mappedKeys) === 'string') {
if (mappedKeys != symbolicName) {
return false;
}
return true;
}
if (!Array.isArray(mappedKeys)) {
mappedKeys = [mappedKeys];
commandExt.key = mappedKeys;
}
for (var i = 0; i < mappedKeys.length; i++) {
var keymap = mappedKeys[i];
if (typeof(keymap) === 'string') {
if (keymap == symbolicName) {
return true;
}
continue;
}
if (keymap.key != symbolicName) {
continue;
}
return exports.flagsMatch(keymap.predicates, flags);
}
return false;
},
/**
* Build a cache of custom keymappings whenever the associated setting
* changes.
*/
_customKeymappingChanged: function(settingName, value) {
var ckc = this._customKeymappingCache =
JSON.parse(value);
ckc.states = ckc.states || {};
for (state in ckc.states) {
this._buildBindingsRegex(ckc.states[state]);
}
ckc._convertedRegExp = true;
}
};
/**
*
*/
exports.flagsMatch = function(predicates, flags) {
if (util.none(predicates)) {
return true;
}
if (!flags) {
return false;
}
for (var flagName in predicates) {
if (flags[flagName] !== predicates[flagName]) {
return false;
}
}
return true;
};
/**
* The global exported KeyboardManager
*/
exports.keyboardManager = new KeyboardManager();
});

View file

@ -0,0 +1,273 @@
/*! @license
==========================================================================
SproutCore -- JavaScript Application Framework
copyright 2006-2009, Sprout Systems Inc., Apple Inc. and contributors.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
SproutCore and the SproutCore logo are trademarks of Sprout Systems, Inc.
For more information about SproutCore, visit http://www.sproutcore.com
==========================================================================
@license */
// Most of the following code is taken from SproutCore with a few changes.
define(function(require, exports, module) {
var util = require('pilot/util');
/**
* Helper functions and hashes for key handling.
*/
exports.KeyHelper = function() {
var ret = {
MODIFIER_KEYS: {
16: 'shift', 17: 'ctrl', 18: 'alt', 224: 'meta'
},
FUNCTION_KEYS : {
8: 'backspace', 9: 'tab', 13: 'return', 19: 'pause',
27: 'escape', 33: 'pageup', 34: 'pagedown', 35: 'end',
36: 'home', 37: 'left', 38: 'up', 39: 'right',
40: 'down', 44: 'printscreen', 45: 'insert', 46: 'delete',
112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4',
116: 'f5', 117: 'f7', 119: 'f8', 120: 'f9',
121: 'f10', 122: 'f11', 123: 'f12', 144: 'numlock',
145: 'scrolllock'
},
PRINTABLE_KEYS: {
32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5',
54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a',
66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h',
73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o',
80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v',
87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.',
188: ',', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\',
221: ']', 222: '\"'
},
/**
* Create the lookup table for Firefox to convert charCodes to keyCodes
* in the keyPress event.
*/
PRINTABLE_KEYS_CHARCODE: {},
/**
* Allow us to lookup keyCodes by symbolic name rather than number
*/
KEY: {}
};
// Create the PRINTABLE_KEYS_CHARCODE hash.
for (var i in ret.PRINTABLE_KEYS) {
var k = ret.PRINTABLE_KEYS[i];
ret.PRINTABLE_KEYS_CHARCODE[k.charCodeAt(0)] = i;
if (k.toUpperCase() != k) {
ret.PRINTABLE_KEYS_CHARCODE[k.toUpperCase().charCodeAt(0)] = i;
}
}
// A reverse map of FUNCTION_KEYS
for (i in ret.FUNCTION_KEYS) {
var name = ret.FUNCTION_KEYS[i].toUpperCase();
ret.KEY[name] = parseInt(i, 10);
}
return ret;
}();
/**
* Determines if the keyDown event is a non-printable or function key.
* These kinds of events are processed as keyboard shortcuts.
* If no shortcut handles the event, then it will be sent as a regular
* keyDown event.
* @private
*/
var isFunctionOrNonPrintableKey = function(evt) {
return !!(evt.altKey || evt.ctrlKey || evt.metaKey ||
((evt.charCode !== evt.which) &&
exports.KeyHelper.FUNCTION_KEYS[evt.which]));
};
/**
* Returns character codes for the event.
* The first value is the normalized code string, with any Shift or Ctrl
* characters added to the beginning.
* The second value is the char string by itself.
* @return {Array}
*/
exports.commandCodes = function(evt, dontIgnoreMeta) {
var code = evt._keyCode || evt.keyCode;
var charCode = (evt._charCode === undefined ? evt.charCode : evt._charCode);
var ret = null;
var key = null;
var modifiers = '';
var lowercase;
var allowShift = true;
// Absent a value for 'keyCode' or 'which', we can't compute the
// command codes. Bail out.
if (code === 0 && evt.which === 0) {
return false;
}
// If the charCode is not zero, then we do not handle a command key
// here. Bail out.
if (charCode !== 0) {
return false;
}
// Check for modifier keys.
if (exports.KeyHelper.MODIFIER_KEYS[charCode]) {
return [exports.KeyHelper.MODIFIER_KEYS[charCode], null];
}
// handle function keys.
if (code) {
ret = exports.KeyHelper.FUNCTION_KEYS[code];
if (!ret && (evt.altKey || evt.ctrlKey || evt.metaKey)) {
ret = exports.KeyHelper.PRINTABLE_KEYS[code];
// Don't handle the shift key if the combo is
// (meta_|ctrl_)<number>
// This is necessary for the French keyboard. On that keyboard,
// you have to hold down the shift key to access the number
// characters.
if (code > 47 && code < 58) {
allowShift = evt.altKey;
}
}
if (ret) {
if (evt.altKey) {
modifiers += 'alt_';
}
if (evt.ctrlKey) {
modifiers += 'ctrl_';
}
if (evt.metaKey) {
modifiers += 'meta_';
}
} else if (evt.ctrlKey || evt.metaKey) {
return false;
}
}
// otherwise just go get the right key.
if (!ret) {
code = evt.which;
key = ret = String.fromCharCode(code);
lowercase = ret.toLowerCase();
if (evt.metaKey) {
modifiers = 'meta_';
ret = lowercase;
} else ret = null;
}
if (evt.shiftKey && ret && allowShift) {
modifiers += 'shift_';
}
if (ret) {
ret = modifiers + ret;
}
if (!dontIgnoreMeta && ret) {
ret = ret.replace(/ctrl_meta|meta/,'ctrl');
}
return [ret, key];
};
// Note: Most of the following code is taken from SproutCore with a few changes.
/**
* Firefox sends a few key events twice: the first time to the keydown event
* and then later again to the keypress event. To handle them correct, they
* should be processed only once. Due to this, we will skip these events
* in keydown and handle them then in keypress.
*/
exports.addKeyDownListener = function(element, boundFunction) {
var handleBoundFunction = function(ev) {
var handled = boundFunction(ev);
// If the boundFunction returned true, then stop the event.
if (handled) {
util.stopEvent(ev);
}
return handled;
};
element.addEventListener('keydown', function(ev) {
if (util.isMozilla) {
// Check for function keys (like DELETE, TAB, LEFT, RIGHT...)
if (exports.KeyHelper.FUNCTION_KEYS[ev.keyCode]) {
return true;
// Check for command keys (like ctrl_c, ctrl_z...)
} else if ((ev.ctrlKey || ev.metaKey) &&
exports.KeyHelper.PRINTABLE_KEYS[ev.keyCode]) {
return true;
}
}
if (isFunctionOrNonPrintableKey(ev)) {
return handleBoundFunction(ev);
}
return true;
}, false);
element.addEventListener('keypress', function(ev) {
if (util.isMozilla) {
// If this is a function key, we have to use the keyCode.
if (exports.KeyHelper.FUNCTION_KEYS[ev.keyCode]) {
return handleBoundFunction(ev);
} else if ((ev.ctrlKey || ev.metaKey) &&
exports.KeyHelper.PRINTABLE_KEYS_CHARCODE[ev.charCode]){
// Check for command keys (like ctrl_c, ctrl_z...).
// For command keys have to convert the charCode to a keyCode
// as it has been sent from the keydown event to be in line
// with the other browsers implementations.
// FF does not allow let you change the keyCode or charCode
// property. Store to a custom keyCode/charCode variable.
// The getCommandCodes() function takes care of these
// special variables.
ev._keyCode = exports.KeyHelper.PRINTABLE_KEYS_CHARCODE[ev.charCode];
ev._charCode = 0;
return handleBoundFunction(ev);
}
}
// normal processing: send keyDown for printable keys.
if (ev.charCode !== undefined && ev.charCode === 0) {
return true;
}
return handleBoundFunction(ev);
}, false);
};
});

Some files were not shown because too many files have changed in this diff Show more