зеркало из https://github.com/mozilla/pilot.git
Merge remote-tracking branch 'joewalker/master'
This commit is contained in:
Коммит
aa57f474b3
|
@ -1,638 +0,0 @@
|
||||||
/* ***** 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');
|
|
||||||
var useragent = require('pilot/useragent');
|
|
||||||
var keyUtil = require('pilot/keys');
|
|
||||||
var EventEmitter = require('pilot/event_emitter').EventEmitter;
|
|
||||||
var typecheck = require('pilot/typecheck');
|
|
||||||
var catalog = require('pilot/catalog');
|
|
||||||
var Status = require('pilot/types').Status;
|
|
||||||
var types = require('pilot/types');
|
|
||||||
var lang = require('pilot/lang');
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 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'
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.startup = function(data, reason) {
|
|
||||||
// TODO: this is probably all kinds of evil, but we need something working
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lookup hash of our registered commands
|
|
||||||
*/
|
|
||||||
var commands = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lookup has for command key bindings that use a string as sender.
|
|
||||||
*/
|
|
||||||
var commmandKeyBinding = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array with command key bindings that use a function to determ the sender.
|
|
||||||
*/
|
|
||||||
var commandKeyBindingFunc = { };
|
|
||||||
|
|
||||||
function splitSafe(s, separator, limit, bLowerCase) {
|
|
||||||
return (bLowerCase && s.toLowerCase() || s)
|
|
||||||
.replace(/(?:^\s+|\n|\s+$)/g, "")
|
|
||||||
.split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseKeys(keys, val, ret) {
|
|
||||||
var key,
|
|
||||||
hashId = 0,
|
|
||||||
parts = splitSafe(keys, "\\-", null, true),
|
|
||||||
i = 0,
|
|
||||||
l = parts.length;
|
|
||||||
|
|
||||||
for (; i < l; ++i) {
|
|
||||||
if (keyUtil.KEY_MODS[parts[i]])
|
|
||||||
hashId = hashId | keyUtil.KEY_MODS[parts[i]];
|
|
||||||
else
|
|
||||||
key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == null) {
|
|
||||||
return {
|
|
||||||
key: key,
|
|
||||||
hashId: hashId
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(ret[hashId] || (ret[hashId] = {}))[key] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var platform = useragent.isMac ? "mac" : "win";
|
|
||||||
function buildKeyHash(command) {
|
|
||||||
var binding = command.bindKey,
|
|
||||||
key = binding[platform],
|
|
||||||
ckb = commmandKeyBinding,
|
|
||||||
ckbf = commandKeyBindingFunc
|
|
||||||
|
|
||||||
if (!binding.sender) {
|
|
||||||
throw new Error('All key bindings must have a sender');
|
|
||||||
}
|
|
||||||
if (!binding.mac && binding.mac !== null) {
|
|
||||||
throw new Error('All key bindings must have a mac key binding');
|
|
||||||
}
|
|
||||||
if (!binding.win && binding.win !== null) {
|
|
||||||
throw new Error('All key bindings must have a windows key binding');
|
|
||||||
}
|
|
||||||
if(!binding[platform]) {
|
|
||||||
// No keymapping for this platform.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof binding.sender == 'string') {
|
|
||||||
var targets = splitSafe(binding.sender, "\\|", null, true);
|
|
||||||
targets.forEach(function(target) {
|
|
||||||
if (!ckb[target]) {
|
|
||||||
ckb[target] = { };
|
|
||||||
}
|
|
||||||
key.split("|").forEach(function(keyPart) {
|
|
||||||
parseKeys(keyPart, command, ckb[target]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else if (typecheck.isFunction(binding.sender)) {
|
|
||||||
var val = {
|
|
||||||
command: command,
|
|
||||||
sender: binding.sender
|
|
||||||
};
|
|
||||||
|
|
||||||
keyData = parseKeys(key);
|
|
||||||
if (!ckbf[keyData.hashId]) {
|
|
||||||
ckbf[keyData.hashId] = { };
|
|
||||||
}
|
|
||||||
if (!ckbf[keyData.hashId][keyData.key]) {
|
|
||||||
ckbf[keyData.hashId][keyData.key] = [ val ];
|
|
||||||
} else {
|
|
||||||
ckbf[keyData.hashId][keyData.key].push(val);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Key binding must have a sender that is a string or function');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findKeyCommand(env, sender, hashId, textOrKey) {
|
|
||||||
// Convert keyCode to the string representation.
|
|
||||||
if (typecheck.isNumber(textOrKey)) {
|
|
||||||
textOrKey = keyUtil.keyCodeToString(textOrKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check bindings with functions as sender first.
|
|
||||||
var bindFuncs = (commandKeyBindingFunc[hashId] || {})[textOrKey] || [];
|
|
||||||
for (var i = 0; i < bindFuncs.length; i++) {
|
|
||||||
if (bindFuncs[i].sender(env, sender, hashId, textOrKey)) {
|
|
||||||
return bindFuncs[i].command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ckbr = commmandKeyBinding[sender]
|
|
||||||
return ckbr && ckbr[hashId] && ckbr[hashId][textOrKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
function execKeyCommand(env, sender, hashId, textOrKey) {
|
|
||||||
var command = findKeyCommand(env, sender, hashId, textOrKey);
|
|
||||||
if (command) {
|
|
||||||
return exec(command, env, sender, { });
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sorted list of command names, we regularly want them in order, so pre-sort
|
|
||||||
*/
|
|
||||||
var commandNames = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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?
|
|
||||||
*/
|
|
||||||
function addCommand(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');
|
|
||||||
}
|
|
||||||
upgradeType(command.name, param);
|
|
||||||
}, this);
|
|
||||||
commands[command.name] = command;
|
|
||||||
|
|
||||||
if (command.bindKey) {
|
|
||||||
buildKeyHash(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
commandNames.push(command.name);
|
|
||||||
commandNames.sort();
|
|
||||||
};
|
|
||||||
|
|
||||||
function upgradeType(name, param) {
|
|
||||||
var lookup = param.type;
|
|
||||||
param.type = types.getType(lookup);
|
|
||||||
if (param.type == null) {
|
|
||||||
throw new Error('In ' + name + '/' + param.name +
|
|
||||||
': can\'t find type for: ' + JSON.stringify(lookup));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeCommand(command) {
|
|
||||||
var name = (typeof command === 'string' ? command : command.name);
|
|
||||||
delete commands[name];
|
|
||||||
lang.arrayRemove(commandNames, name);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getCommand(name) {
|
|
||||||
return commands[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
function getCommandNames() {
|
|
||||||
return commandNames;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default ArgumentProvider that is used if no ArgumentProvider is provided
|
|
||||||
* by the command's sender.
|
|
||||||
*/
|
|
||||||
function defaultArgsProvider(request, callback) {
|
|
||||||
var args = request.args,
|
|
||||||
params = request.command.params;
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
var param = params[i];
|
|
||||||
|
|
||||||
// If the parameter is already valid, then don't ask for it anymore.
|
|
||||||
if (request.getParamStatus(param) != Status.VALID ||
|
|
||||||
// Ask for optional parameters as well.
|
|
||||||
param.defaultValue === null)
|
|
||||||
{
|
|
||||||
var paramPrompt = param.description;
|
|
||||||
if (param.defaultValue === null) {
|
|
||||||
paramPrompt += " (optional)";
|
|
||||||
}
|
|
||||||
var value = prompt(paramPrompt, param.defaultValue || "");
|
|
||||||
// No value but required -> nope.
|
|
||||||
if (!value) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
args[param.name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry point for keyboard accelerators or anything else that wants to execute
|
|
||||||
* a command. A new request object is created and a check performed, if the
|
|
||||||
* passed in arguments are VALID/INVALID or INCOMPLETE. If they are INCOMPLETE
|
|
||||||
* the ArgumentProvider on the sender is called or otherwise the default
|
|
||||||
* ArgumentProvider to get the still required arguments.
|
|
||||||
* If they are valid (or valid after the ArgumentProvider is done), the command
|
|
||||||
* is executed.
|
|
||||||
*
|
|
||||||
* @param command Either a command, or the name of one
|
|
||||||
* @param env Current environment to execute the command in
|
|
||||||
* @param sender String that should be the same as the senderObject stored on
|
|
||||||
* the environment in env[sender]
|
|
||||||
* @param args Arguments for the command
|
|
||||||
* @param typed (Optional)
|
|
||||||
*/
|
|
||||||
function exec(command, env, sender, args, typed) {
|
|
||||||
if (typeof command === 'string') {
|
|
||||||
command = commands[command];
|
|
||||||
}
|
|
||||||
if (!command) {
|
|
||||||
// TODO: Should we complain more than returning false?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new Request({
|
|
||||||
sender: sender,
|
|
||||||
command: command,
|
|
||||||
args: args || {},
|
|
||||||
typed: typed
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the command and ensures request.done is called on the request in
|
|
||||||
* case it's not marked to be done already or async.
|
|
||||||
*/
|
|
||||||
function execute() {
|
|
||||||
command.exec(env, request.args, request);
|
|
||||||
|
|
||||||
// If the request isn't asnync and isn't done, then make it done.
|
|
||||||
if (!request.isAsync && !request.isDone) {
|
|
||||||
request.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (request.getStatus() == Status.INVALID) {
|
|
||||||
console.error("Canon.exec: Invalid parameter(s) passed to " +
|
|
||||||
command.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If the request isn't complete yet, try to complete it.
|
|
||||||
else if (request.getStatus() == Status.INCOMPLETE) {
|
|
||||||
// Check if the sender has a ArgsProvider, otherwise use the default
|
|
||||||
// build in one.
|
|
||||||
var argsProvider;
|
|
||||||
var senderObj = env[sender];
|
|
||||||
if (!senderObj || !senderObj.getArgsProvider ||
|
|
||||||
!(argsProvider = senderObj.getArgsProvider()))
|
|
||||||
{
|
|
||||||
argsProvider = defaultArgsProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask the paramProvider to complete the request.
|
|
||||||
argsProvider(request, function() {
|
|
||||||
if (request.getStatus() == Status.VALID) {
|
|
||||||
execute();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
execute();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.removeCommand = removeCommand;
|
|
||||||
exports.addCommand = addCommand;
|
|
||||||
exports.getCommand = getCommand;
|
|
||||||
exports.getCommandNames = getCommandNames;
|
|
||||||
exports.findKeyCommand = findKeyCommand;
|
|
||||||
exports.exec = exec;
|
|
||||||
exports.execKeyCommand = execKeyCommand;
|
|
||||||
exports.upgradeType = upgradeType;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We publish a 'output' 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
|
|
||||||
*/
|
|
||||||
var requests = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many requests do we store?
|
|
||||||
*/
|
|
||||||
var maxRequestLength = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To create an invocation, you need to do something like this (all the ctor
|
|
||||||
* args are optional):
|
|
||||||
* <pre>
|
|
||||||
* var request = new Request({
|
|
||||||
* command: command,
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the status of a parameter on the request object.
|
|
||||||
*/
|
|
||||||
Request.prototype.getParamStatus = function(param) {
|
|
||||||
var args = this.args || {};
|
|
||||||
|
|
||||||
// Check if there is already a value for this parameter.
|
|
||||||
if (param.name in args) {
|
|
||||||
// If there is no value set and then the value is VALID if it's not
|
|
||||||
// required or INCOMPLETE if not set yet.
|
|
||||||
if (args[param.name] == null) {
|
|
||||||
if (param.defaultValue === null) {
|
|
||||||
return Status.VALID;
|
|
||||||
} else {
|
|
||||||
return Status.INCOMPLETE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the parameter value is valid.
|
|
||||||
var reply,
|
|
||||||
// The passed in value when parsing a type is a string.
|
|
||||||
argsValue = args[param.name].toString();
|
|
||||||
|
|
||||||
// Type.parse can throw errors.
|
|
||||||
try {
|
|
||||||
reply = param.type.parse(argsValue);
|
|
||||||
} catch (e) {
|
|
||||||
return Status.INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply.status != Status.VALID) {
|
|
||||||
return reply.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if the param is marked as required.
|
|
||||||
else if (param.defaultValue === undefined) {
|
|
||||||
// The parameter is not set on the args object but it's required,
|
|
||||||
// which means, things are invalid.
|
|
||||||
return Status.INCOMPLETE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status.VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the status of a parameter name on the request object.
|
|
||||||
*/
|
|
||||||
Request.prototype.getParamNameStatus = function(paramName) {
|
|
||||||
var params = this.command.params || [];
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
if (params[i].name == paramName) {
|
|
||||||
return this.getParamStatus(params[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw "Parameter '" + paramName +
|
|
||||||
"' not defined on command '" + this.command.name + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if all required arguments are set on the request such that it can
|
|
||||||
* get executed.
|
|
||||||
*/
|
|
||||||
Request.prototype.getStatus = function() {
|
|
||||||
var args = this.args || {},
|
|
||||||
params = this.command.params;
|
|
||||||
|
|
||||||
// If there are not parameters, then it's valid.
|
|
||||||
if (!params || params.length == 0) {
|
|
||||||
return Status.VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
var status = [];
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
status.push(this.getParamStatus(params[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status.combine(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = [];
|
|
||||||
|
|
||||||
requests.push(this);
|
|
||||||
// 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 (requests.length > maxRequestLength) {
|
|
||||||
requests.shiftObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
exports._dispatchEvent('output', { requests: requests, request: 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() {
|
|
||||||
this.isAsync = true;
|
|
||||||
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.isDone = true;
|
|
||||||
this._dispatchEvent('output', {});
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure to finish the request only once.
|
|
||||||
if (!this.isDone) {
|
|
||||||
this.isDone = true;
|
|
||||||
this._dispatchEvent('output', {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exports.Request = Request;
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,65 +0,0 @@
|
||||||
/* ***** 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,274 +0,0 @@
|
||||||
/* ***** 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 checks = require("pilot/typecheck");
|
|
||||||
var canon = require('pilot/canon');
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var helpMessages = {
|
|
||||||
plainPrefix:
|
|
||||||
'<h2>Welcome to Skywriter - Code in the Cloud</h2><ul>' +
|
|
||||||
'<li><a href="http://labs.mozilla.com/projects/skywriter" target="_blank">Home Page</a></li>' +
|
|
||||||
'<li><a href="https://wiki.mozilla.org/Labs/Skywriter" target="_blank">Wiki</a></li>' +
|
|
||||||
'<li><a href="https://wiki.mozilla.org/Labs/Skywriter/UserGuide" target="_blank">User Guide</a></li>' +
|
|
||||||
'<li><a href="https://wiki.mozilla.org/Labs/Skywriter/Tips" target="_blank">Tips and Tricks</a></li>' +
|
|
||||||
'<li><a href="https://wiki.mozilla.org/Labs/Skywriter/FAQ" target="_blank">FAQ</a></li>' +
|
|
||||||
'<li><a href="https://wiki.mozilla.org/Labs/Skywriter/DeveloperGuide" target="_blank">Developers Guide</a></li>' +
|
|
||||||
'</ul>',
|
|
||||||
plainSuffix:
|
|
||||||
'For more information, see the <a href="https://wiki.mozilla.org/Labs/Skywriter">Skywriter Wiki</a>.'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'help' command
|
|
||||||
*/
|
|
||||||
var helpCommandSpec = {
|
|
||||||
name: 'help',
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
name: 'search',
|
|
||||||
type: 'text',
|
|
||||||
description: 'Search string to narrow the output.',
|
|
||||||
defaultValue: null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
description: 'Get help on the available commands.',
|
|
||||||
exec: function(env, args, request) {
|
|
||||||
var output = [];
|
|
||||||
|
|
||||||
var command = canon.getCommand(args.search);
|
|
||||||
if (command && command.exec) {
|
|
||||||
// caught a real command
|
|
||||||
output.push(command.description ?
|
|
||||||
command.description :
|
|
||||||
'No description for ' + args.search);
|
|
||||||
} else {
|
|
||||||
var showHidden = false;
|
|
||||||
|
|
||||||
if (!args.search && helpMessages.plainPrefix) {
|
|
||||||
output.push(helpMessages.plainPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command) {
|
|
||||||
// We must be looking at sub-commands
|
|
||||||
output.push('<h2>Sub-Commands of ' + command.name + '</h2>');
|
|
||||||
output.push('<p>' + command.description + '</p>');
|
|
||||||
}
|
|
||||||
else if (args.search) {
|
|
||||||
if (args.search == 'hidden') { // sneaky, sneaky.
|
|
||||||
args.search = '';
|
|
||||||
showHidden = true;
|
|
||||||
}
|
|
||||||
output.push('<h2>Commands starting with \'' + args.search + '\':</h2>');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
output.push('<h2>Available Commands:</h2>');
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandNames = canon.getCommandNames();
|
|
||||||
commandNames.sort();
|
|
||||||
|
|
||||||
output.push('<table>');
|
|
||||||
for (var i = 0; i < commandNames.length; i++) {
|
|
||||||
command = canon.getCommand(commandNames[i]);
|
|
||||||
if (!showHidden && command.hidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (command.description === undefined) {
|
|
||||||
// Ignore editor actions
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (args.search && command.name.indexOf(args.search) !== 0) {
|
|
||||||
// Filtered out by the user
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!args.search && command.name.indexOf(' ') != -1) {
|
|
||||||
// sub command
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (command && command.name == args.search) {
|
|
||||||
// sub command, and we've already given that help
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo add back a column with parameter information, perhaps?
|
|
||||||
|
|
||||||
output.push('<tr>');
|
|
||||||
output.push('<th class="right">' + command.name + '</th>');
|
|
||||||
output.push('<td>' + command.description + '</td>');
|
|
||||||
output.push('</tr>');
|
|
||||||
}
|
|
||||||
output.push('</table>');
|
|
||||||
|
|
||||||
if (!args.search && helpMessages.plainSuffix) {
|
|
||||||
output.push(helpMessages.plainSuffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.done(output.join(''));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'eval' command
|
|
||||||
*/
|
|
||||||
var evalCommandSpec = {
|
|
||||||
name: 'eval',
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
name: 'javascript',
|
|
||||||
type: 'text',
|
|
||||||
description: 'The JavaScript to evaluate'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
description: 'evals given js code and show the result',
|
|
||||||
hidden: true,
|
|
||||||
exec: function(env, args, request) {
|
|
||||||
var result;
|
|
||||||
var javascript = args.javascript;
|
|
||||||
try {
|
|
||||||
result = eval(javascript);
|
|
||||||
} catch (e) {
|
|
||||||
result = '<b>Error: ' + e.message + '</b>';
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = '';
|
|
||||||
var type = '';
|
|
||||||
var x;
|
|
||||||
|
|
||||||
if (checks.isFunction(result)) {
|
|
||||||
// converts the function to a well formated string
|
|
||||||
msg = (result + '').replace(/\n/g, '<br>').replace(/ /g, ' ');
|
|
||||||
type = 'function';
|
|
||||||
} else if (checks.isObject(result)) {
|
|
||||||
if (Array.isArray(result)) {
|
|
||||||
type = 'array';
|
|
||||||
} else {
|
|
||||||
type = 'object';
|
|
||||||
}
|
|
||||||
|
|
||||||
var items = [];
|
|
||||||
var value;
|
|
||||||
|
|
||||||
for (x in result) {
|
|
||||||
if (result.hasOwnProperty(x)) {
|
|
||||||
if (checks.isFunction(result[x])) {
|
|
||||||
value = '[function]';
|
|
||||||
} else if (checks.isObject(result[x])) {
|
|
||||||
value = '[object]';
|
|
||||||
} else {
|
|
||||||
value = result[x];
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push({name: x, value: value});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
items.sort(function(a,b) {
|
|
||||||
return (a.name.toLowerCase() < b.name.toLowerCase()) ? -1 : 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (x = 0; x < items.length; x++) {
|
|
||||||
msg += '<b>' + items[x].name + '</b>: ' + items[x].value + '<br>';
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
msg = result;
|
|
||||||
type = typeof result;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.done('Result for eval <b>\'' + javascript + '\'</b>' +
|
|
||||||
' (type: '+ type+'): <br><br>'+ msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'version' command
|
|
||||||
*/
|
|
||||||
var versionCommandSpec = {
|
|
||||||
name: 'version',
|
|
||||||
description: 'show the Skywriter version',
|
|
||||||
hidden: true,
|
|
||||||
exec: function(env, args, request) {
|
|
||||||
var version = 'Skywriter ' + skywriter.versionNumber + ' (' +
|
|
||||||
skywriter.versionCodename + ')';
|
|
||||||
request.done(version);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'skywriter' command
|
|
||||||
*/
|
|
||||||
var skywriterCommandSpec = {
|
|
||||||
name: 'skywriter',
|
|
||||||
hidden: true,
|
|
||||||
exec: function(env, args, request) {
|
|
||||||
var index = Math.floor(Math.random() * messages.length);
|
|
||||||
request.done('Skywriter ' + messages[index]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var messages = [
|
|
||||||
'really wants you to trick it out in some way.',
|
|
||||||
'is your Web editor.',
|
|
||||||
'would love to be like Emacs on the Web.',
|
|
||||||
'is written on the Web platform, so you can tweak it.'
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
var canon = require('pilot/canon');
|
|
||||||
|
|
||||||
exports.startup = function(data, reason) {
|
|
||||||
canon.addCommand(helpCommandSpec);
|
|
||||||
canon.addCommand(evalCommandSpec);
|
|
||||||
// canon.addCommand(versionCommandSpec);
|
|
||||||
canon.addCommand(skywriterCommandSpec);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function(data, reason) {
|
|
||||||
canon.removeCommand(helpCommandSpec);
|
|
||||||
canon.removeCommand(evalCommandSpec);
|
|
||||||
// canon.removeCommand(versionCommandSpec);
|
|
||||||
canon.removeCommand(skywriterCommandSpec);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,117 +0,0 @@
|
||||||
/* ***** 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) {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI 'up'
|
|
||||||
* Decrement the 'current entry' pointer
|
|
||||||
*/
|
|
||||||
var historyPreviousCommandSpec = {
|
|
||||||
name: "historyPrevious",
|
|
||||||
predicates: { isCommandLine: true, isKeyUp: true },
|
|
||||||
key: "up",
|
|
||||||
exec: function(args, request) {
|
|
||||||
if (pointer > 0) {
|
|
||||||
pointer--;
|
|
||||||
}
|
|
||||||
|
|
||||||
var display = history.requests[pointer].typed;
|
|
||||||
env.commandLine.setInput(display);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI 'down'
|
|
||||||
* Increment the 'current entry' pointer
|
|
||||||
*/
|
|
||||||
var historyNextCommandSpec = {
|
|
||||||
name: "historyNext",
|
|
||||||
predicates: { isCommandLine: true, isKeyUp: true },
|
|
||||||
key: "down",
|
|
||||||
exec: function(args, request) {
|
|
||||||
if (pointer < history.requests.length) {
|
|
||||||
pointer++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var display = (pointer === history.requests.length)
|
|
||||||
? ''
|
|
||||||
: history.requests[pointer].typed;
|
|
||||||
|
|
||||||
env.commandLine.setInput(display);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'history' command
|
|
||||||
*/
|
|
||||||
var historyCommandSpec = {
|
|
||||||
name: "history",
|
|
||||||
description: "Show history of the commands",
|
|
||||||
exec: function(args, request) {
|
|
||||||
var output = [];
|
|
||||||
output.push('<table>');
|
|
||||||
var count = 1;
|
|
||||||
|
|
||||||
history.requests.forEach(function(request) {
|
|
||||||
output.push('<tr>');
|
|
||||||
output.push('<th>' + count + '</th>');
|
|
||||||
output.push('<td>' + request.typed + '</td>');
|
|
||||||
output.push('</tr>');
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
output.push('</table>');
|
|
||||||
|
|
||||||
request.done(output.join(''));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The pointer to the command that we show on up|down
|
|
||||||
*/
|
|
||||||
var pointer = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the pointer to the latest command execution
|
|
||||||
*/
|
|
||||||
exports.addedRequestOutput = function() {
|
|
||||||
pointer = history.requests.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,135 +0,0 @@
|
||||||
/* ***** 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 names = env.settings.getSettingNames();
|
|
||||||
html = '';
|
|
||||||
// first sort the settingsList based on the name
|
|
||||||
names.sort(function(name1, name2) {
|
|
||||||
return name1.localeCompare(name2);
|
|
||||||
});
|
|
||||||
|
|
||||||
names.forEach(function(name) {
|
|
||||||
var setting = env.settings.getSetting(name);
|
|
||||||
var url = 'https://wiki.mozilla.org/Labs/Skywriter/Settings#' +
|
|
||||||
setting.name;
|
|
||||||
html += '<a class="setting" href="' + url +
|
|
||||||
'" title="View external documentation on setting: ' +
|
|
||||||
setting.name +
|
|
||||||
'" target="_blank">' +
|
|
||||||
setting.name +
|
|
||||||
'</a> = ' +
|
|
||||||
setting.value +
|
|
||||||
'<br/>';
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 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
|
|
||||||
args.setting.set(args.value);
|
|
||||||
html = 'Setting: <strong>' + args.setting.name + '</strong> = ' +
|
|
||||||
args.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);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -224,18 +224,33 @@ exports.scrollbarWidth = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimized set innerHTML. This is faster than plain innerHTML if the element
|
* Using setInnerHtml(foo) rather than innerHTML = foo allows us to enable
|
||||||
* already contains a lot of child elements.
|
* tweaks in XHTML documents (see implementation in GCLI/mini/pilot).
|
||||||
*
|
* This implementation isn't here for performance reasons.
|
||||||
|
*/
|
||||||
|
exports.setInnerHtml = function(el, html) {
|
||||||
|
el.innerHTML = html;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like setInnerHtml except that it uses performance tweaks which have the side
|
||||||
|
* effect or removing the original node from the dom and replacing with a clone
|
||||||
|
* which kills listeners and breaks references.
|
||||||
* See http://blog.stevenlevithan.com/archives/faster-than-innerhtml for details
|
* See http://blog.stevenlevithan.com/archives/faster-than-innerhtml for details
|
||||||
*/
|
*/
|
||||||
exports.setInnerHtml = function(el, innerHtml) {
|
exports.rebuildWith = function(el, html) {
|
||||||
var element = el.cloneNode(false);//document.createElement("div");
|
var element = el.cloneNode(false);
|
||||||
element.innerHTML = innerHtml;
|
element.innerHTML = html;
|
||||||
el.parentNode.replaceChild(element, el);
|
el.parentNode.replaceChild(element, el);
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.clearElement = function(el) {
|
||||||
|
while (el.hasChildNodes()) {
|
||||||
|
el.removeChild(el.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
exports.setInnerText = function(el, innerText) {
|
exports.setInnerText = function(el, innerText) {
|
||||||
if (document.body && "textContent" in document.body)
|
if (document.body && "textContent" in document.body)
|
||||||
el.textContent = innerText;
|
el.textContent = innerText;
|
||||||
|
|
|
@ -1,404 +0,0 @@
|
||||||
/* ***** 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 DomTemplate.
|
|
||||||
*
|
|
||||||
* 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) (original author)
|
|
||||||
*
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
|
|
||||||
// WARNING: do not 'use_strict' without reading the notes in envEval;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A templater that allows one to quickly template DOM nodes.
|
|
||||||
*/
|
|
||||||
function Templater() {
|
|
||||||
this.scope = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursive function to walk the tree processing the attributes as it goes.
|
|
||||||
* @param node the node to process. If you pass a string in instead of a DOM
|
|
||||||
* element, it is assumed to be an id for use with document.getElementById()
|
|
||||||
* @param data the data to use for node processing.
|
|
||||||
*/
|
|
||||||
Templater.prototype.processNode = function(node, data) {
|
|
||||||
if (typeof node === 'string') {
|
|
||||||
node = document.getElementById(node);
|
|
||||||
}
|
|
||||||
if (data === null || data === undefined) {
|
|
||||||
data = {};
|
|
||||||
}
|
|
||||||
this.scope.push(node.nodeName + (node.id ? '#' + node.id : ''));
|
|
||||||
try {
|
|
||||||
// Process attributes
|
|
||||||
if (node.attributes && node.attributes.length) {
|
|
||||||
// We need to handle 'foreach' and 'if' first because they might stop
|
|
||||||
// some types of processing from happening, and foreach must come first
|
|
||||||
// because it defines new data on which 'if' might depend.
|
|
||||||
if (node.hasAttribute('foreach')) {
|
|
||||||
this.processForEach(node, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (node.hasAttribute('if')) {
|
|
||||||
if (!this.processIf(node, data)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only make the node available once we know it's not going away
|
|
||||||
data.__element = node;
|
|
||||||
// It's good to clean up the attributes when we've processed them,
|
|
||||||
// but if we do it straight away, we mess up the array index
|
|
||||||
var attrs = Array.prototype.slice.call(node.attributes);
|
|
||||||
for (var i = 0; i < attrs.length; i++) {
|
|
||||||
var value = attrs[i].value;
|
|
||||||
var name = attrs[i].name;
|
|
||||||
this.scope.push(name);
|
|
||||||
try {
|
|
||||||
if (name === 'save') {
|
|
||||||
// Save attributes are a setter using the node
|
|
||||||
value = this.stripBraces(value);
|
|
||||||
this.property(value, data, node);
|
|
||||||
node.removeAttribute('save');
|
|
||||||
} else if (name.substring(0, 2) === 'on') {
|
|
||||||
// Event registration relies on property doing a bind
|
|
||||||
value = this.stripBraces(value);
|
|
||||||
var func = this.property(value, data);
|
|
||||||
if (typeof func !== 'function') {
|
|
||||||
this.handleError('Expected ' + value +
|
|
||||||
' to resolve to a function, but got ' + typeof func);
|
|
||||||
}
|
|
||||||
node.removeAttribute(name);
|
|
||||||
var capture = node.hasAttribute('capture' + name.substring(2));
|
|
||||||
node.addEventListener(name.substring(2), func, capture);
|
|
||||||
if (capture) {
|
|
||||||
node.removeAttribute('capture' + name.substring(2));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Replace references in all other attributes
|
|
||||||
var self = this;
|
|
||||||
var newValue = value.replace(/\$\{[^}]*\}/g, function(path) {
|
|
||||||
return self.envEval(path.slice(2, -1), data, value);
|
|
||||||
});
|
|
||||||
// Remove '_' prefix of attribute names so the DOM won't try
|
|
||||||
// to use them before we've processed the template
|
|
||||||
if (name.charAt(0) === '_') {
|
|
||||||
node.removeAttribute(name);
|
|
||||||
node.setAttribute(name.substring(1), newValue);
|
|
||||||
} else if (value !== newValue) {
|
|
||||||
attrs[i].value = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through our children calling processNode. First clone them, so the
|
|
||||||
// set of nodes that we visit will be unaffected by additions or removals.
|
|
||||||
var childNodes = Array.prototype.slice.call(node.childNodes);
|
|
||||||
for (var j = 0; j < childNodes.length; j++) {
|
|
||||||
this.processNode(childNodes[j], data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
|
||||||
this.processTextNode(node, data);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle <x if="${...}">
|
|
||||||
* @param node An element with an 'if' attribute
|
|
||||||
* @param data The data to use with envEval
|
|
||||||
* @returns true if processing should continue, false otherwise
|
|
||||||
*/
|
|
||||||
Templater.prototype.processIf = function(node, data) {
|
|
||||||
this.scope.push('if');
|
|
||||||
try {
|
|
||||||
var originalValue = node.getAttribute('if');
|
|
||||||
var value = this.stripBraces(originalValue);
|
|
||||||
var recurse = true;
|
|
||||||
try {
|
|
||||||
var reply = this.envEval(value, data, originalValue);
|
|
||||||
recurse = !!reply;
|
|
||||||
} catch (ex) {
|
|
||||||
this.handleError('Error with \'' + value + '\'', ex);
|
|
||||||
recurse = false;
|
|
||||||
}
|
|
||||||
if (!recurse) {
|
|
||||||
node.parentNode.removeChild(node);
|
|
||||||
}
|
|
||||||
node.removeAttribute('if');
|
|
||||||
return recurse;
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle <x foreach="param in ${array}"> and the special case of
|
|
||||||
* <loop foreach="param in ${array}">
|
|
||||||
* @param node An element with a 'foreach' attribute
|
|
||||||
* @param data The data to use with envEval
|
|
||||||
*/
|
|
||||||
Templater.prototype.processForEach = function(node, data) {
|
|
||||||
this.scope.push('foreach');
|
|
||||||
try {
|
|
||||||
var originalValue = node.getAttribute('foreach');
|
|
||||||
var value = originalValue;
|
|
||||||
|
|
||||||
var paramName = 'param';
|
|
||||||
if (value.charAt(0) === '$') {
|
|
||||||
// No custom loop variable name. Use the default: 'param'
|
|
||||||
value = this.stripBraces(value);
|
|
||||||
} else {
|
|
||||||
// Extract the loop variable name from 'NAME in ${ARRAY}'
|
|
||||||
var nameArr = value.split(' in ');
|
|
||||||
paramName = nameArr[0].trim();
|
|
||||||
value = this.stripBraces(nameArr[1].trim());
|
|
||||||
}
|
|
||||||
node.removeAttribute('foreach');
|
|
||||||
try {
|
|
||||||
var self = this;
|
|
||||||
// Process a single iteration of a loop
|
|
||||||
var processSingle = function(member, clone, ref) {
|
|
||||||
ref.parentNode.insertBefore(clone, ref);
|
|
||||||
data[paramName] = member;
|
|
||||||
self.processNode(clone, data);
|
|
||||||
delete data[paramName];
|
|
||||||
};
|
|
||||||
|
|
||||||
// processSingle is no good for <loop> nodes where we want to work on
|
|
||||||
// the childNodes rather than the node itself
|
|
||||||
var processAll = function(scope, member) {
|
|
||||||
self.scope.push(scope);
|
|
||||||
try {
|
|
||||||
if (node.nodeName === 'LOOP') {
|
|
||||||
for (var i = 0; i < node.childNodes.length; i++) {
|
|
||||||
var clone = node.childNodes[i].cloneNode(true);
|
|
||||||
processSingle(member, clone, node);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var clone = node.cloneNode(true);
|
|
||||||
clone.removeAttribute('foreach');
|
|
||||||
processSingle(member, clone, node);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
self.scope.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var reply = this.envEval(value, data, originalValue);
|
|
||||||
if (Array.isArray(reply)) {
|
|
||||||
reply.forEach(function(data, i) {
|
|
||||||
processAll('' + i, data);
|
|
||||||
}, this);
|
|
||||||
} else {
|
|
||||||
for (var param in reply) {
|
|
||||||
if (reply.hasOwnProperty(param)) {
|
|
||||||
processAll(param, param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.parentNode.removeChild(node);
|
|
||||||
} catch (ex) {
|
|
||||||
this.handleError('Error with \'' + value + '\'', ex);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a text node and replace it with another text node with the ${...}
|
|
||||||
* sections parsed out. We replace the node by altering node.parentNode but
|
|
||||||
* we could probably use a DOM Text API to achieve the same thing.
|
|
||||||
* @param node The Text node to work on
|
|
||||||
* @param data The data to use in calls to envEval
|
|
||||||
*/
|
|
||||||
Templater.prototype.processTextNode = function(node, data) {
|
|
||||||
// Replace references in other attributes
|
|
||||||
var value = node.data;
|
|
||||||
// We can't use the string.replace() with function trick (see generic
|
|
||||||
// attribute processing in processNode()) because we need to support
|
|
||||||
// functions that return DOM nodes, so we can't have the conversion to a
|
|
||||||
// string.
|
|
||||||
// Instead we process the string as an array of parts. In order to split
|
|
||||||
// the string up, we first replace '${' with '\uF001$' and '}' with '\uF002'
|
|
||||||
// We can then split using \uF001 or \uF002 to get an array of strings
|
|
||||||
// where scripts are prefixed with $.
|
|
||||||
// \uF001 and \uF002 are just unicode chars reserved for private use.
|
|
||||||
value = value.replace(/\$\{([^}]*)\}/g, '\uF001$$$1\uF002');
|
|
||||||
var parts = value.split(/\uF001|\uF002/);
|
|
||||||
if (parts.length > 1) {
|
|
||||||
parts.forEach(function(part) {
|
|
||||||
if (part === null || part === undefined || part === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (part.charAt(0) === '$') {
|
|
||||||
part = this.envEval(part.slice(1), data, node.data);
|
|
||||||
}
|
|
||||||
// It looks like this was done a few lines above but see envEval
|
|
||||||
if (part === null) {
|
|
||||||
part = "null";
|
|
||||||
}
|
|
||||||
if (part === undefined) {
|
|
||||||
part = "undefined";
|
|
||||||
}
|
|
||||||
// if (isDOMElement(part)) { ... }
|
|
||||||
if (typeof part.cloneNode !== 'function') {
|
|
||||||
part = node.ownerDocument.createTextNode(part.toString());
|
|
||||||
}
|
|
||||||
node.parentNode.insertBefore(part, node);
|
|
||||||
}, this);
|
|
||||||
node.parentNode.removeChild(node);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warn of string does not begin '${' and end '}'
|
|
||||||
* @param str the string to check.
|
|
||||||
* @return The string stripped of ${ and }, or untouched if it does not match
|
|
||||||
*/
|
|
||||||
Templater.prototype.stripBraces = function(str) {
|
|
||||||
if (!str.match(/\$\{.*\}/g)) {
|
|
||||||
this.handleError('Expected ' + str + ' to match ${...}');
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
return str.slice(2, -1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combined getter and setter that works with a path through some data set.
|
|
||||||
* For example:
|
|
||||||
* <ul>
|
|
||||||
* <li>property('a.b', { a: { b: 99 }}); // returns 99
|
|
||||||
* <li>property('a', { a: { b: 99 }}); // returns { b: 99 }
|
|
||||||
* <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
|
|
||||||
* input data to be { a: { b: 42 }}
|
|
||||||
* </ul>
|
|
||||||
* @param path An array of strings indicating the path through the data, or
|
|
||||||
* a string to be cut into an array using <tt>split('.')</tt>
|
|
||||||
* @param data An object to look in for the <tt>path</tt> argument
|
|
||||||
* @param newValue (optional) If defined, this value will replace the
|
|
||||||
* original value for the data at the path specified.
|
|
||||||
* @return The value pointed to by <tt>path</tt> before any
|
|
||||||
* <tt>newValue</tt> is applied.
|
|
||||||
*/
|
|
||||||
Templater.prototype.property = function(path, data, newValue) {
|
|
||||||
this.scope.push(path);
|
|
||||||
try {
|
|
||||||
if (typeof path === 'string') {
|
|
||||||
path = path.split('.');
|
|
||||||
}
|
|
||||||
var value = data[path[0]];
|
|
||||||
if (path.length === 1) {
|
|
||||||
if (newValue !== undefined) {
|
|
||||||
data[path[0]] = newValue;
|
|
||||||
}
|
|
||||||
if (typeof value === 'function') {
|
|
||||||
return function() {
|
|
||||||
return value.apply(data, arguments);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (!value) {
|
|
||||||
this.handleError('Can\'t find path=' + path);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this.property(path.slice(1), value, newValue);
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like eval, but that creates a context of the variables in <tt>env</tt> in
|
|
||||||
* which the script is evaluated.
|
|
||||||
* WARNING: This script uses 'with' which is generally regarded to be evil.
|
|
||||||
* The alternative is to create a Function at runtime that takes X parameters
|
|
||||||
* according to the X keys in the env object, and then call that function using
|
|
||||||
* the values in the env object. This is likely to be slow, but workable.
|
|
||||||
* @param script The string to be evaluated.
|
|
||||||
* @param env The environment in which to eval the script.
|
|
||||||
* @param context Optional debugging string in case of failure
|
|
||||||
* @return The return value of the script, or the error message if the script
|
|
||||||
* execution failed.
|
|
||||||
*/
|
|
||||||
Templater.prototype.envEval = function(script, env, context) {
|
|
||||||
with (env) {
|
|
||||||
try {
|
|
||||||
this.scope.push(context);
|
|
||||||
return eval(script);
|
|
||||||
} catch (ex) {
|
|
||||||
this.handleError('Template error evaluating \'' + script + '\'', ex);
|
|
||||||
return script;
|
|
||||||
} finally {
|
|
||||||
this.scope.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic way of reporting errors, for easy overloading in different
|
|
||||||
* environments.
|
|
||||||
* @param message the error message to report.
|
|
||||||
* @param ex optional associated exception.
|
|
||||||
*/
|
|
||||||
Templater.prototype.handleError = function(message, ex) {
|
|
||||||
this.logError(message);
|
|
||||||
this.logError('In: ' + this.scope.join(' > '));
|
|
||||||
if (ex) {
|
|
||||||
this.logError(ex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic way of reporting errors, for easy overloading in different
|
|
||||||
* environments.
|
|
||||||
* @param message the error message to report.
|
|
||||||
*/
|
|
||||||
Templater.prototype.logError = function(message) {
|
|
||||||
window.console && window.console.log && console.log(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Templater = Templater;
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,54 +0,0 @@
|
||||||
/* ***** 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 DomTemplate.
|
|
||||||
*
|
|
||||||
* 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) (original author)
|
|
||||||
*
|
|
||||||
* 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 settings = require("pilot/settings").settings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an environment object
|
|
||||||
*/
|
|
||||||
function create() {
|
|
||||||
return {
|
|
||||||
settings: settings
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.create = create;
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -37,7 +37,6 @@
|
||||||
|
|
||||||
define(function(require, exports, module) {
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
var keys = require("pilot/keys");
|
|
||||||
var useragent = require("pilot/useragent");
|
var useragent = require("pilot/useragent");
|
||||||
var dom = require("pilot/dom");
|
var dom = require("pilot/dom");
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@ exports.preventDefault = function(e) {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getDocumentX = function(e) {
|
exports.getDocumentX = function(e) {
|
||||||
if (e.clientX) {
|
if (e.clientX) {
|
||||||
return e.clientX + dom.getPageScrollLeft();
|
return e.clientX + dom.getPageScrollLeft();
|
||||||
} else {
|
} else {
|
||||||
return e.pageX;
|
return e.pageX;
|
||||||
|
@ -110,7 +109,7 @@ exports.getButton = function(e) {
|
||||||
return 0;
|
return 0;
|
||||||
else if (e.type == "contextmenu")
|
else if (e.type == "contextmenu")
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
// DOM Event
|
// DOM Event
|
||||||
if (e.preventDefault) {
|
if (e.preventDefault) {
|
||||||
return e.button;
|
return e.button;
|
||||||
|
@ -225,6 +224,17 @@ exports.addMultiMouseDownListener = function(el, button, count, timeout, callbac
|
||||||
useragent.isIE && exports.addListener(el, "dblclick", listener);
|
useragent.isIE && exports.addListener(el, "dblclick", listener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var MODIFIER_KEYS = { 16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta' };
|
||||||
|
var FUNCTION_KEYS = {
|
||||||
|
8: "Backspace", 9: "Tab", 13: "Return", 19: "Pause", 27: "Esc",
|
||||||
|
32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home",
|
||||||
|
37: "Left", 38: "Up", 39: "Right", 40: "Down",
|
||||||
|
44: "Print", 45: "Insert", 46: "Delete",
|
||||||
|
112: "F1", 113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6",
|
||||||
|
118: "F7", 119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12",
|
||||||
|
144: "Numlock", 145: "Scrolllock"
|
||||||
|
};
|
||||||
|
|
||||||
function normalizeCommandKeys(callback, e, keyCode) {
|
function normalizeCommandKeys(callback, e, keyCode) {
|
||||||
var hashId = 0;
|
var hashId = 0;
|
||||||
if (useragent.isOpera && useragent.isMac) {
|
if (useragent.isOpera && useragent.isMac) {
|
||||||
|
@ -235,14 +245,14 @@ function normalizeCommandKeys(callback, e, keyCode) {
|
||||||
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
|
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyCode in keys.MODIFIER_KEYS) {
|
if (keyCode in MODIFIER_KEYS) {
|
||||||
switch (keys.MODIFIER_KEYS[keyCode]) {
|
switch (MODIFIER_KEYS[keyCode]) {
|
||||||
case "Alt":
|
case "Alt":
|
||||||
hashId = 2;
|
hashId = 2;
|
||||||
break;
|
break;
|
||||||
case "Shift":
|
case "Shift":
|
||||||
hashId = 4;
|
hashId = 4;
|
||||||
break
|
break;
|
||||||
case "Ctrl":
|
case "Ctrl":
|
||||||
hashId = 1;
|
hashId = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -260,7 +270,7 @@ function normalizeCommandKeys(callback, e, keyCode) {
|
||||||
// If there is no hashID and the keyCode is not a function key, then
|
// If there is no hashID and the keyCode is not a function key, then
|
||||||
// we don't call the callback as we don't handle a command key here
|
// we don't call the callback as we don't handle a command key here
|
||||||
// (it's a normal key/character input).
|
// (it's a normal key/character input).
|
||||||
if (hashId == 0 && !(keyCode in keys.FUNCTION_KEYS)) {
|
if (hashId == 0 && !(keyCode in FUNCTION_KEYS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,27 +43,34 @@ var EventEmitter = {};
|
||||||
|
|
||||||
EventEmitter._emit =
|
EventEmitter._emit =
|
||||||
EventEmitter._dispatchEvent = function(eventName, e) {
|
EventEmitter._dispatchEvent = function(eventName, e) {
|
||||||
this._eventRegistry = this._eventRegistry || {};
|
if (!this.hasOwnProperty('_eventRegistry')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var listeners = this._eventRegistry[eventName];
|
var listeners = this._eventRegistry[eventName];
|
||||||
if (!listeners || !listeners.length) return;
|
if (!listeners || !listeners.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = e || {};
|
var e = e || {};
|
||||||
e.type = eventName;
|
e.type = eventName;
|
||||||
|
|
||||||
for (var i=0; i<listeners.length; i++) {
|
for (var i = 0; i < listeners.length; i++) {
|
||||||
listeners[i](e);
|
listeners[i](e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitter.on =
|
EventEmitter.on =
|
||||||
EventEmitter.addEventListener = function(eventName, callback) {
|
EventEmitter.addEventListener = function(eventName, callback) {
|
||||||
this._eventRegistry = this._eventRegistry || {};
|
if (!this.hasOwnProperty('_eventRegistry')) {
|
||||||
|
this._eventRegistry = {};
|
||||||
|
}
|
||||||
|
|
||||||
var listeners = this._eventRegistry[eventName];
|
var listeners = this._eventRegistry[eventName];
|
||||||
if (!listeners) {
|
if (!listeners) {
|
||||||
var listeners = this._eventRegistry[eventName] = [];
|
var listeners = this._eventRegistry[eventName] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listeners.indexOf(callback) == -1) {
|
if (listeners.indexOf(callback) == -1) {
|
||||||
listeners.push(callback);
|
listeners.push(callback);
|
||||||
}
|
}
|
||||||
|
@ -71,12 +78,15 @@ EventEmitter.addEventListener = function(eventName, callback) {
|
||||||
|
|
||||||
EventEmitter.removeListener =
|
EventEmitter.removeListener =
|
||||||
EventEmitter.removeEventListener = function(eventName, callback) {
|
EventEmitter.removeEventListener = function(eventName, callback) {
|
||||||
this._eventRegistry = this._eventRegistry || {};
|
if (!this.hasOwnProperty('_eventRegistry')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var listeners = this._eventRegistry[eventName];
|
var listeners = this._eventRegistry[eventName];
|
||||||
if (!listeners) {
|
if (!listeners) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = listeners.indexOf(callback);
|
var index = listeners.indexOf(callback);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
listeners.splice(index, 1);
|
listeners.splice(index, 1);
|
||||||
|
@ -84,8 +94,10 @@ EventEmitter.removeEventListener = function(eventName, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitter.removeAllListeners = function(eventName) {
|
EventEmitter.removeAllListeners = function(eventName) {
|
||||||
if (this._eventRegistry) this._eventRegistry[eventName] = [];
|
if (this.hasOwnProperty('_eventRegistry')) {
|
||||||
}
|
this._eventRegistry[eventName] = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
exports.EventEmitter = EventEmitter;
|
exports.EventEmitter = EventEmitter;
|
||||||
|
|
||||||
|
|
|
@ -835,4 +835,6 @@ if (!String.prototype.trim) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
exports.globalsLoaded = true;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
/* ***** 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) {
|
|
||||||
require('pilot/fixoldbrowsers');
|
|
||||||
|
|
||||||
require('pilot/types/basic').startup(data, reason);
|
|
||||||
require('pilot/types/command').startup(data, reason);
|
|
||||||
require('pilot/types/settings').startup(data, reason);
|
|
||||||
require('pilot/commands/settings').startup(data, reason);
|
|
||||||
require('pilot/commands/basic').startup(data, reason);
|
|
||||||
// require('pilot/commands/history').startup(data, reason);
|
|
||||||
require('pilot/settings/canon').startup(data, reason);
|
|
||||||
require('pilot/canon').startup(data, reason);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function(data, reason) {
|
|
||||||
require('pilot/types/basic').shutdown(data, reason);
|
|
||||||
require('pilot/types/command').shutdown(data, reason);
|
|
||||||
require('pilot/types/settings').shutdown(data, reason);
|
|
||||||
require('pilot/commands/settings').shutdown(data, reason);
|
|
||||||
require('pilot/commands/basic').shutdown(data, reason);
|
|
||||||
// require('pilot/commands/history').shutdown(data, reason);
|
|
||||||
require('pilot/settings/canon').shutdown(data, reason);
|
|
||||||
require('pilot/canon').shutdown(data, reason);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,118 +0,0 @@
|
||||||
/*! @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 oop = require("pilot/oop");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper functions and hashes for key handling.
|
|
||||||
*/
|
|
||||||
var Keys = (function() {
|
|
||||||
var ret = {
|
|
||||||
MODIFIER_KEYS: {
|
|
||||||
16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta'
|
|
||||||
},
|
|
||||||
|
|
||||||
KEY_MODS: {
|
|
||||||
"ctrl": 1, "alt": 2, "option" : 2,
|
|
||||||
"shift": 4, "meta": 8, "command": 8
|
|
||||||
},
|
|
||||||
|
|
||||||
FUNCTION_KEYS : {
|
|
||||||
8 : "Backspace",
|
|
||||||
9 : "Tab",
|
|
||||||
13 : "Return",
|
|
||||||
19 : "Pause",
|
|
||||||
27 : "Esc",
|
|
||||||
32 : "Space",
|
|
||||||
33 : "PageUp",
|
|
||||||
34 : "PageDown",
|
|
||||||
35 : "End",
|
|
||||||
36 : "Home",
|
|
||||||
37 : "Left",
|
|
||||||
38 : "Up",
|
|
||||||
39 : "Right",
|
|
||||||
40 : "Down",
|
|
||||||
44 : "Print",
|
|
||||||
45 : "Insert",
|
|
||||||
46 : "Delete",
|
|
||||||
112: "F1",
|
|
||||||
113: "F2",
|
|
||||||
114: "F3",
|
|
||||||
115: "F4",
|
|
||||||
116: "F5",
|
|
||||||
117: "F6",
|
|
||||||
118: "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: '\"'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A reverse map of FUNCTION_KEYS
|
|
||||||
for (i in ret.FUNCTION_KEYS) {
|
|
||||||
var name = ret.FUNCTION_KEYS[i].toUpperCase();
|
|
||||||
ret[name] = parseInt(i, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the MODIFIER_KEYS, FUNCTION_KEYS and PRINTABLE_KEYS to the KEY
|
|
||||||
// variables as well.
|
|
||||||
oop.mixin(ret, ret.MODIFIER_KEYS);
|
|
||||||
oop.mixin(ret, ret.PRINTABLE_KEYS);
|
|
||||||
oop.mixin(ret, ret.FUNCTION_KEYS);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
})();
|
|
||||||
oop.mixin(exports, Keys);
|
|
||||||
|
|
||||||
exports.keyCodeToString = function(keyCode) {
|
|
||||||
return (Keys[keyCode] || String.fromCharCode(keyCode)).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
|
@ -101,10 +101,10 @@ exports.deferredCall = function(fcn) {
|
||||||
timer = setTimeout(callback, timeout || 0);
|
timer = setTimeout(callback, timeout || 0);
|
||||||
}
|
}
|
||||||
return deferred;
|
return deferred;
|
||||||
}
|
};
|
||||||
|
|
||||||
deferred.schedule = deferred;
|
deferred.schedule = deferred;
|
||||||
|
|
||||||
deferred.call = function() {
|
deferred.call = function() {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
fcn();
|
fcn();
|
||||||
|
@ -116,7 +116,7 @@ exports.deferredCall = function(fcn) {
|
||||||
timer = null;
|
timer = null;
|
||||||
return deferred;
|
return deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
return deferred;
|
return deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ exports.inherits = (function() {
|
||||||
ctor.super_ = superCtor.prototype;
|
ctor.super_ = superCtor.prototype;
|
||||||
ctor.prototype = new tempCtor();
|
ctor.prototype = new tempCtor();
|
||||||
ctor.prototype.constructor = ctor;
|
ctor.prototype.constructor = ctor;
|
||||||
}
|
};
|
||||||
}());
|
}());
|
||||||
|
|
||||||
exports.mixin = function(obj, mixin) {
|
exports.mixin = function(obj, mixin) {
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
/* ***** 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):
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
var Promise = require("pilot/promise").Promise;
|
|
||||||
|
|
||||||
exports.REASONS = {
|
|
||||||
APP_STARTUP: 1,
|
|
||||||
APP_SHUTDOWN: 2,
|
|
||||||
PLUGIN_ENABLE: 3,
|
|
||||||
PLUGIN_DISABLE: 4,
|
|
||||||
PLUGIN_INSTALL: 5,
|
|
||||||
PLUGIN_UNINSTALL: 6,
|
|
||||||
PLUGIN_UPGRADE: 7,
|
|
||||||
PLUGIN_DOWNGRADE: 8
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Plugin = function(name) {
|
|
||||||
this.name = name;
|
|
||||||
this.status = this.INSTALLED;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Plugin.prototype = {
|
|
||||||
/**
|
|
||||||
* constants for the state
|
|
||||||
*/
|
|
||||||
NEW: 0,
|
|
||||||
INSTALLED: 1,
|
|
||||||
REGISTERED: 2,
|
|
||||||
STARTED: 3,
|
|
||||||
UNREGISTERED: 4,
|
|
||||||
SHUTDOWN: 5,
|
|
||||||
|
|
||||||
install: function(data, reason) {
|
|
||||||
var pr = new Promise();
|
|
||||||
if (this.status > this.NEW) {
|
|
||||||
pr.resolve(this);
|
|
||||||
return pr;
|
|
||||||
}
|
|
||||||
require([this.name], function(pluginModule) {
|
|
||||||
if (pluginModule.install) {
|
|
||||||
pluginModule.install(data, reason);
|
|
||||||
}
|
|
||||||
this.status = this.INSTALLED;
|
|
||||||
pr.resolve(this);
|
|
||||||
}.bind(this));
|
|
||||||
return pr;
|
|
||||||
},
|
|
||||||
|
|
||||||
register: function(data, reason) {
|
|
||||||
var pr = new Promise();
|
|
||||||
if (this.status != this.INSTALLED) {
|
|
||||||
pr.resolve(this);
|
|
||||||
return pr;
|
|
||||||
}
|
|
||||||
require([this.name], function(pluginModule) {
|
|
||||||
if (pluginModule.register) {
|
|
||||||
pluginModule.register(data, reason);
|
|
||||||
}
|
|
||||||
this.status = this.REGISTERED;
|
|
||||||
pr.resolve(this);
|
|
||||||
}.bind(this));
|
|
||||||
return pr;
|
|
||||||
},
|
|
||||||
|
|
||||||
startup: function(data, reason) {
|
|
||||||
reason = reason || exports.REASONS.APP_STARTUP;
|
|
||||||
var pr = new Promise();
|
|
||||||
if (this.status != this.REGISTERED) {
|
|
||||||
pr.resolve(this);
|
|
||||||
return pr;
|
|
||||||
}
|
|
||||||
require([this.name], function(pluginModule) {
|
|
||||||
if (pluginModule.startup) {
|
|
||||||
pluginModule.startup(data, reason);
|
|
||||||
}
|
|
||||||
this.status = this.STARTED;
|
|
||||||
pr.resolve(this);
|
|
||||||
}.bind(this));
|
|
||||||
return pr;
|
|
||||||
},
|
|
||||||
|
|
||||||
shutdown: function(data, reason) {
|
|
||||||
if (this.status != this.STARTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pluginModule = require(this.name);
|
|
||||||
if (pluginModule.shutdown) {
|
|
||||||
pluginModule.shutdown(data, reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.PluginCatalog = function() {
|
|
||||||
this.plugins = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.PluginCatalog.prototype = {
|
|
||||||
registerPlugins: function(pluginList, data, reason) {
|
|
||||||
var registrationPromises = [];
|
|
||||||
pluginList.forEach(function(pluginName) {
|
|
||||||
var plugin = this.plugins[pluginName];
|
|
||||||
if (plugin === undefined) {
|
|
||||||
plugin = new exports.Plugin(pluginName);
|
|
||||||
this.plugins[pluginName] = plugin;
|
|
||||||
registrationPromises.push(plugin.register(data, reason));
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
return Promise.group(registrationPromises);
|
|
||||||
},
|
|
||||||
|
|
||||||
startupPlugins: function(data, reason) {
|
|
||||||
var startupPromises = [];
|
|
||||||
for (var pluginName in this.plugins) {
|
|
||||||
var plugin = this.plugins[pluginName];
|
|
||||||
startupPromises.push(plugin.startup(data, reason));
|
|
||||||
}
|
|
||||||
return Promise.group(startupPromises);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.catalog = new exports.PluginCatalog();
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,264 +0,0 @@
|
||||||
/* ***** 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A promise can be in one of 2 states.
|
|
||||||
* The ERROR and SUCCESS states are terminal, the PENDING state is the only
|
|
||||||
* start state.
|
|
||||||
*/
|
|
||||||
var ERROR = -1;
|
|
||||||
var PENDING = 0;
|
|
||||||
var SUCCESS = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We give promises and ID so we can track which are outstanding
|
|
||||||
*/
|
|
||||||
var _nextId = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debugging help if 2 things try to complete the same promise.
|
|
||||||
* This can be slow (especially on chrome due to the stack trace unwinding) so
|
|
||||||
* we should leave this turned off in normal use.
|
|
||||||
*/
|
|
||||||
var _traceCompletion = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Outstanding promises. Handy list for debugging only.
|
|
||||||
*/
|
|
||||||
var _outstanding = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recently resolved promises. Also for debugging only.
|
|
||||||
*/
|
|
||||||
var _recent = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an unfulfilled promise
|
|
||||||
*/
|
|
||||||
Promise = function () {
|
|
||||||
this._status = PENDING;
|
|
||||||
this._value = undefined;
|
|
||||||
this._onSuccessHandlers = [];
|
|
||||||
this._onErrorHandlers = [];
|
|
||||||
|
|
||||||
// Debugging help
|
|
||||||
this._id = _nextId++;
|
|
||||||
//this._createTrace = new Trace(new Error());
|
|
||||||
_outstanding[this._id] = this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Yeay for RTTI.
|
|
||||||
*/
|
|
||||||
Promise.prototype.isPromise = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Have we either been resolve()ed or reject()ed?
|
|
||||||
*/
|
|
||||||
Promise.prototype.isComplete = function() {
|
|
||||||
return this._status != PENDING;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Have we resolve()ed?
|
|
||||||
*/
|
|
||||||
Promise.prototype.isResolved = function() {
|
|
||||||
return this._status == SUCCESS;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Have we reject()ed?
|
|
||||||
*/
|
|
||||||
Promise.prototype.isRejected = function() {
|
|
||||||
return this._status == ERROR;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take the specified action of fulfillment of a promise, and (optionally)
|
|
||||||
* a different action on promise rejection.
|
|
||||||
*/
|
|
||||||
Promise.prototype.then = function(onSuccess, onError) {
|
|
||||||
if (typeof onSuccess === 'function') {
|
|
||||||
if (this._status === SUCCESS) {
|
|
||||||
onSuccess.call(null, this._value);
|
|
||||||
} else if (this._status === PENDING) {
|
|
||||||
this._onSuccessHandlers.push(onSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof onError === 'function') {
|
|
||||||
if (this._status === ERROR) {
|
|
||||||
onError.call(null, this._value);
|
|
||||||
} else if (this._status === PENDING) {
|
|
||||||
this._onErrorHandlers.push(onError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like then() except that rather than returning <tt>this</tt> we return
|
|
||||||
* a promise which
|
|
||||||
*/
|
|
||||||
Promise.prototype.chainPromise = function(onSuccess) {
|
|
||||||
var chain = new Promise();
|
|
||||||
chain._chainedFrom = this;
|
|
||||||
this.then(function(data) {
|
|
||||||
try {
|
|
||||||
chain.resolve(onSuccess(data));
|
|
||||||
} catch (ex) {
|
|
||||||
chain.reject(ex);
|
|
||||||
}
|
|
||||||
}, function(ex) {
|
|
||||||
chain.reject(ex);
|
|
||||||
});
|
|
||||||
return chain;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supply the fulfillment of a promise
|
|
||||||
*/
|
|
||||||
Promise.prototype.resolve = function(data) {
|
|
||||||
return this._complete(this._onSuccessHandlers, SUCCESS, data, 'resolve');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renege on a promise
|
|
||||||
*/
|
|
||||||
Promise.prototype.reject = function(data) {
|
|
||||||
return this._complete(this._onErrorHandlers, ERROR, data, 'reject');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method to be called on resolve() or reject().
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
Promise.prototype._complete = function(list, status, data, name) {
|
|
||||||
// Complain if we've already been completed
|
|
||||||
if (this._status != PENDING) {
|
|
||||||
console.group('Promise already closed');
|
|
||||||
console.error('Attempted ' + name + '() with ', data);
|
|
||||||
console.error('Previous status = ', this._status,
|
|
||||||
', previous value = ', this._value);
|
|
||||||
console.trace();
|
|
||||||
|
|
||||||
if (this._completeTrace) {
|
|
||||||
console.error('Trace of previous completion:');
|
|
||||||
this._completeTrace.log(5);
|
|
||||||
}
|
|
||||||
console.groupEnd();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_traceCompletion) {
|
|
||||||
this._completeTrace = new Trace(new Error());
|
|
||||||
}
|
|
||||||
|
|
||||||
this._status = status;
|
|
||||||
this._value = data;
|
|
||||||
|
|
||||||
// Call all the handlers, and then delete them
|
|
||||||
list.forEach(function(handler) {
|
|
||||||
handler.call(null, this._value);
|
|
||||||
}, this);
|
|
||||||
this._onSuccessHandlers.length = 0;
|
|
||||||
this._onErrorHandlers.length = 0;
|
|
||||||
|
|
||||||
// Remove the given {promise} from the _outstanding list, and add it to the
|
|
||||||
// _recent list, pruning more than 20 recent promises from that list.
|
|
||||||
delete _outstanding[this._id];
|
|
||||||
_recent.push(this);
|
|
||||||
while (_recent.length > 20) {
|
|
||||||
_recent.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes an array of promises and returns a promise that that is fulfilled once
|
|
||||||
* all the promises in the array are fulfilled
|
|
||||||
* @param group The array of promises
|
|
||||||
* @return the promise that is fulfilled when all the array is fulfilled
|
|
||||||
*/
|
|
||||||
Promise.group = function(promiseList) {
|
|
||||||
if (!(promiseList instanceof Array)) {
|
|
||||||
promiseList = Array.prototype.slice.call(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the original array has nothing in it, return now to avoid waiting
|
|
||||||
if (promiseList.length === 0) {
|
|
||||||
return new Promise().resolve([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupPromise = new Promise();
|
|
||||||
var results = [];
|
|
||||||
var fulfilled = 0;
|
|
||||||
|
|
||||||
var onSuccessFactory = function(index) {
|
|
||||||
return function(data) {
|
|
||||||
results[index] = data;
|
|
||||||
fulfilled++;
|
|
||||||
// If the group has already failed, silently drop extra results
|
|
||||||
if (groupPromise._status !== ERROR) {
|
|
||||||
if (fulfilled === promiseList.length) {
|
|
||||||
groupPromise.resolve(results);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
promiseList.forEach(function(promise, index) {
|
|
||||||
var onSuccess = onSuccessFactory(index);
|
|
||||||
var onError = groupPromise.reject.bind(groupPromise);
|
|
||||||
promise.then(onSuccess, onError);
|
|
||||||
});
|
|
||||||
|
|
||||||
return groupPromise;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Promise = Promise;
|
|
||||||
exports._outstanding = _outstanding;
|
|
||||||
exports._recent = _recent;
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,83 +0,0 @@
|
||||||
/* ***** 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 Promise = require('pilot/promise').Promise;
|
|
||||||
|
|
||||||
exports.xhr = function(method, url, async, beforeSendCallback) {
|
|
||||||
var pr = new Promise();
|
|
||||||
|
|
||||||
if (!skywriter.proxy || !skywriter.proxy.xhr) {
|
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
req.onreadystatechange = function() {
|
|
||||||
if (req.readyState !== 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var status = req.status;
|
|
||||||
if (status !== 0 && status !== 200) {
|
|
||||||
var error = new Error(req.responseText + ' (Status ' + req.status + ")");
|
|
||||||
error.xhr = req;
|
|
||||||
pr.reject(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.resolve(req.responseText);
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
req.open("GET", url, async);
|
|
||||||
if (beforeSendCallback) {
|
|
||||||
beforeSendCallback(req);
|
|
||||||
}
|
|
||||||
req.send();
|
|
||||||
} else {
|
|
||||||
skywriter.proxy.xhr.call(this, method, url, async, beforeSendCallback, pr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pr;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Worker = function(url) {
|
|
||||||
if (!skywriter.proxy || !skywriter.proxy.worker) {
|
|
||||||
return new Worker(url);
|
|
||||||
} else {
|
|
||||||
return new skywriter.proxy.worker(url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,185 +0,0 @@
|
||||||
/* ***** 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):
|
|
||||||
* Patrick Walton (pwalton@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 util = require("util/util");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the result of adding the two positions.
|
|
||||||
*/
|
|
||||||
exports.addPositions = function(a, b) {
|
|
||||||
return { row: a.row + b.row, col: a.col + b.col };
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Returns a copy of the given range. */
|
|
||||||
exports.cloneRange = function(range) {
|
|
||||||
var oldStart = range.start, oldEnd = range.end;
|
|
||||||
var newStart = { row: oldStart.row, col: oldStart.col };
|
|
||||||
var newEnd = { row: oldEnd.row, col: oldEnd.col };
|
|
||||||
return { start: newStart, end: newEnd };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given two positions a and b, returns a negative number if a < b, 0 if a = b,
|
|
||||||
* or a positive number if a > b.
|
|
||||||
*/
|
|
||||||
exports.comparePositions = function(positionA, positionB) {
|
|
||||||
var rowDiff = positionA.row - positionB.row;
|
|
||||||
return rowDiff === 0 ? positionA.col - positionB.col : rowDiff;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the two ranges are equal and false otherwise.
|
|
||||||
*/
|
|
||||||
exports.equal = function(rangeA, rangeB) {
|
|
||||||
return (exports.comparePositions(rangeA.start, rangeB.start) === 0 &&
|
|
||||||
exports.comparePositions(rangeA.end, rangeB.end) === 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.extendRange = function(range, delta) {
|
|
||||||
var end = range.end;
|
|
||||||
return {
|
|
||||||
start: range.start,
|
|
||||||
end: {
|
|
||||||
row: end.row + delta.row,
|
|
||||||
col: end.col + delta.col
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given two sets of ranges, returns the ranges of characters that exist in one
|
|
||||||
* of the sets but not both.
|
|
||||||
*/
|
|
||||||
exports.intersectRangeSets = function(setA, setB) {
|
|
||||||
var stackA = util.clone(setA), stackB = util.clone(setB);
|
|
||||||
var result = [];
|
|
||||||
while (stackA.length > 0 && stackB.length > 0) {
|
|
||||||
var rangeA = stackA.shift(), rangeB = stackB.shift();
|
|
||||||
var startDiff = exports.comparePositions(rangeA.start, rangeB.start);
|
|
||||||
var endDiff = exports.comparePositions(rangeA.end, rangeB.end);
|
|
||||||
|
|
||||||
if (exports.comparePositions(rangeA.end, rangeB.start) < 0) {
|
|
||||||
// A is completely before B
|
|
||||||
result.push(rangeA);
|
|
||||||
stackB.unshift(rangeB);
|
|
||||||
} else if (exports.comparePositions(rangeB.end, rangeA.start) < 0) {
|
|
||||||
// B is completely before A
|
|
||||||
result.push(rangeB);
|
|
||||||
stackA.unshift(rangeA);
|
|
||||||
} else if (startDiff < 0) { // A starts before B
|
|
||||||
result.push({ start: rangeA.start, end: rangeB.start });
|
|
||||||
stackA.unshift({ start: rangeB.start, end: rangeA.end });
|
|
||||||
stackB.unshift(rangeB);
|
|
||||||
} else if (startDiff === 0) { // A and B start at the same place
|
|
||||||
if (endDiff < 0) { // A ends before B
|
|
||||||
stackB.unshift({ start: rangeA.end, end: rangeB.end });
|
|
||||||
} else if (endDiff > 0) { // A ends after B
|
|
||||||
stackA.unshift({ start: rangeB.end, end: rangeA.end });
|
|
||||||
}
|
|
||||||
} else if (startDiff > 0) { // A starts after B
|
|
||||||
result.push({ start: rangeB.start, end: rangeA.start });
|
|
||||||
stackA.unshift(rangeA);
|
|
||||||
stackB.unshift({ start: rangeA.start, end: rangeB.end });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.concat(stackA, stackB);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.isZeroLength = function(range) {
|
|
||||||
return range.start.row === range.end.row &&
|
|
||||||
range.start.col === range.end.col;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the greater of the two positions.
|
|
||||||
*/
|
|
||||||
exports.maxPosition = function(a, b) {
|
|
||||||
return exports.comparePositions(a, b) > 0 ? a : b;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a range with swapped 'end' and 'start' values into one with the
|
|
||||||
* values in the correct order.
|
|
||||||
*
|
|
||||||
* TODO: Unit test.
|
|
||||||
*/
|
|
||||||
exports.normalizeRange = function(range) {
|
|
||||||
return this.comparePositions(range.start, range.end) < 0 ? range :
|
|
||||||
{ start: range.end, end: range.start };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a single range that spans the entire given set of ranges.
|
|
||||||
*/
|
|
||||||
exports.rangeSetBoundaries = function(rangeSet) {
|
|
||||||
return {
|
|
||||||
start: rangeSet[0].start,
|
|
||||||
end: rangeSet[rangeSet.length - 1].end
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.toString = function(range) {
|
|
||||||
var start = range.start, end = range.end;
|
|
||||||
return '[ ' + start.row + ', ' + start.col + ' ' + end.row + ',' + + end.col +' ]';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the union of the two ranges.
|
|
||||||
*/
|
|
||||||
exports.unionRanges = function(a, b) {
|
|
||||||
return {
|
|
||||||
start: a.start.row < b.start.row ||
|
|
||||||
(a.start.row === b.start.row && a.start.col < b.start.col) ?
|
|
||||||
a.start : b.start,
|
|
||||||
end: a.end.row > b.end.row ||
|
|
||||||
(a.end.row === b.end.row && a.end.col > b.end.col) ?
|
|
||||||
a.end : b.end
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.isPosition = function(pos) {
|
|
||||||
return !util.none(pos) && !util.none(pos.row) && !util.none(pos.col);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.isRange = function(range) {
|
|
||||||
return (!util.none(range) && exports.isPosition(range.start) &&
|
|
||||||
exports.isPosition(range.end));
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,327 +0,0 @@
|
||||||
/* vim:ts=4:sts=4:sw=4:
|
|
||||||
* ***** 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)
|
|
||||||
* Julian Viereck (jviereck@mozilla.com)
|
|
||||||
* Kevin Dangoor (kdangoor@mozilla.com)
|
|
||||||
* Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.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 plug-in manages settings.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var console = require('pilot/console');
|
|
||||||
var oop = require('pilot/oop');
|
|
||||||
var types = require('pilot/types');
|
|
||||||
var EventEmitter = require('pilot/event_emitter').EventEmitter;
|
|
||||||
var catalog = require('pilot/catalog');
|
|
||||||
|
|
||||||
var settingExtensionSpec = {
|
|
||||||
name: 'setting',
|
|
||||||
description: 'A setting is something that the application offers as a ' +
|
|
||||||
'way to customize how it works',
|
|
||||||
register: 'env.settings.addSetting',
|
|
||||||
indexOn: 'name'
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.startup = function(data, reason) {
|
|
||||||
catalog.addExtensionSpec(settingExtensionSpec);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function(data, reason) {
|
|
||||||
catalog.removeExtensionSpec(settingExtensionSpec);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new setting.
|
|
||||||
* @param settingSpec An object literal that looks like this:
|
|
||||||
* {
|
|
||||||
* name: 'thing',
|
|
||||||
* description: 'Thing is an example setting',
|
|
||||||
* type: 'string',
|
|
||||||
* defaultValue: 'something'
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
function Setting(settingSpec, settings) {
|
|
||||||
this._settings = settings;
|
|
||||||
|
|
||||||
Object.keys(settingSpec).forEach(function(key) {
|
|
||||||
this[key] = settingSpec[key];
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.type = types.getType(this.type);
|
|
||||||
if (this.type == null) {
|
|
||||||
throw new Error('In ' + this.name +
|
|
||||||
': can\'t find type for: ' + JSON.stringify(settingSpec.type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.name) {
|
|
||||||
throw new Error('Setting.name == undefined. Ignoring.', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.defaultValue === undefined) {
|
|
||||||
throw new Error('Setting.defaultValue == undefined', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.onChange) {
|
|
||||||
this.on('change', this.onChange.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set(this.defaultValue);
|
|
||||||
}
|
|
||||||
Setting.prototype = {
|
|
||||||
get: function() {
|
|
||||||
return this.value;
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function(value) {
|
|
||||||
if (this.value === value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.value = value;
|
|
||||||
if (this._settings.persister) {
|
|
||||||
this._settings.persister.persistValue(this._settings, this.name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._dispatchEvent('change', { setting: this, value: value });
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the value of the <code>key</code> setting to it's default
|
|
||||||
*/
|
|
||||||
resetValue: function() {
|
|
||||||
this.set(this.defaultValue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
oop.implement(Setting.prototype, EventEmitter);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base class for all the various methods of storing settings.
|
|
||||||
* <p>Usage:
|
|
||||||
* <pre>
|
|
||||||
* // Create manually, or require 'settings' from the container.
|
|
||||||
* // This is the manual version:
|
|
||||||
* var settings = plugins.catalog.getObject('settings');
|
|
||||||
* // Add a new setting
|
|
||||||
* settings.addSetting({ name:'foo', ... });
|
|
||||||
* // Display the default value
|
|
||||||
* alert(settings.get('foo'));
|
|
||||||
* // Alter the value, which also publishes the change etc.
|
|
||||||
* settings.set('foo', 'bar');
|
|
||||||
* // Reset the value to the default
|
|
||||||
* settings.resetValue('foo');
|
|
||||||
* </pre>
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function Settings(persister) {
|
|
||||||
// Storage for deactivated values
|
|
||||||
this._deactivated = {};
|
|
||||||
|
|
||||||
// Storage for the active settings
|
|
||||||
this._settings = {};
|
|
||||||
// We often want sorted setting names. Cache
|
|
||||||
this._settingNames = [];
|
|
||||||
|
|
||||||
if (persister) {
|
|
||||||
this.setPersister(persister);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Settings.prototype = {
|
|
||||||
/**
|
|
||||||
* Function to add to the list of available settings.
|
|
||||||
* <p>Example usage:
|
|
||||||
* <pre>
|
|
||||||
* var settings = plugins.catalog.getObject('settings');
|
|
||||||
* settings.addSetting({
|
|
||||||
* name: 'tabsize', // For use in settings.get('X')
|
|
||||||
* type: 'number', // To allow value checking.
|
|
||||||
* defaultValue: 4 // Default value for use when none is directly set
|
|
||||||
* });
|
|
||||||
* </pre>
|
|
||||||
* @param {object} settingSpec Object containing name/type/defaultValue members.
|
|
||||||
*/
|
|
||||||
addSetting: function(settingSpec) {
|
|
||||||
var setting = new Setting(settingSpec, this);
|
|
||||||
this._settings[setting.name] = setting;
|
|
||||||
this._settingNames.push(setting.name);
|
|
||||||
this._settingNames.sort();
|
|
||||||
},
|
|
||||||
|
|
||||||
addSettings: function addSettings(settings) {
|
|
||||||
Object.keys(settings).forEach(function (name) {
|
|
||||||
var setting = settings[name];
|
|
||||||
if (!('name' in setting)) setting.name = name;
|
|
||||||
this.addSetting(setting);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeSetting: function(setting) {
|
|
||||||
var name = (typeof setting === 'string' ? setting : setting.name);
|
|
||||||
setting = this._settings[name];
|
|
||||||
delete this._settings[name];
|
|
||||||
util.arrayRemove(this._settingNames, name);
|
|
||||||
settings.removeAllListeners('change');
|
|
||||||
},
|
|
||||||
|
|
||||||
removeSettings: function removeSettings(settings) {
|
|
||||||
Object.keys(settings).forEach(function(name) {
|
|
||||||
var setting = settings[name];
|
|
||||||
if (!('name' in setting)) setting.name = name;
|
|
||||||
this.removeSettings(setting);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSettingNames: function() {
|
|
||||||
return this._settingNames;
|
|
||||||
},
|
|
||||||
|
|
||||||
getSetting: function(name) {
|
|
||||||
return this._settings[name];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Persister is able to store settings. It is an object that defines
|
|
||||||
* two functions:
|
|
||||||
* loadInitialValues(settings) and persistValue(settings, key, value).
|
|
||||||
*/
|
|
||||||
setPersister: function(persister) {
|
|
||||||
this._persister = persister;
|
|
||||||
if (persister) {
|
|
||||||
persister.loadInitialValues(this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
resetAll: function() {
|
|
||||||
this.getSettingNames().forEach(function(key) {
|
|
||||||
this.resetValue(key);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a list of the known settings and their values
|
|
||||||
*/
|
|
||||||
_list: function() {
|
|
||||||
var reply = [];
|
|
||||||
this.getSettingNames().forEach(function(setting) {
|
|
||||||
reply.push({
|
|
||||||
'key': setting,
|
|
||||||
'value': this.getSetting(setting).get()
|
|
||||||
});
|
|
||||||
}, this);
|
|
||||||
return reply;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prime the local cache with the defaults.
|
|
||||||
*/
|
|
||||||
_loadDefaultValues: function() {
|
|
||||||
this._loadFromObject(this._getDefaultValues());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility to load settings from an object
|
|
||||||
*/
|
|
||||||
_loadFromObject: function(data) {
|
|
||||||
// We iterate over data rather than keys so we don't forget values
|
|
||||||
// which don't have a setting yet.
|
|
||||||
for (var key in data) {
|
|
||||||
if (data.hasOwnProperty(key)) {
|
|
||||||
var setting = this._settings[key];
|
|
||||||
if (setting) {
|
|
||||||
var value = setting.type.parse(data[key]);
|
|
||||||
this.set(key, value);
|
|
||||||
} else {
|
|
||||||
this.set(key, data[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility to grab all the settings and export them into an object
|
|
||||||
*/
|
|
||||||
_saveToObject: function() {
|
|
||||||
return this.getSettingNames().map(function(key) {
|
|
||||||
return this._settings[key].type.stringify(this.get(key));
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default initial settings
|
|
||||||
*/
|
|
||||||
_getDefaultValues: function() {
|
|
||||||
return this.getSettingNames().map(function(key) {
|
|
||||||
return this._settings[key].spec.defaultValue;
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exports.settings = new Settings();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the settings in a cookie
|
|
||||||
* This code has not been tested since reboot
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function CookiePersister() {
|
|
||||||
};
|
|
||||||
|
|
||||||
CookiePersister.prototype = {
|
|
||||||
loadInitialValues: function(settings) {
|
|
||||||
settings._loadDefaultValues();
|
|
||||||
var data = cookie.get('settings');
|
|
||||||
settings._loadFromObject(JSON.parse(data));
|
|
||||||
},
|
|
||||||
|
|
||||||
persistValue: function(settings, key, value) {
|
|
||||||
try {
|
|
||||||
var stringData = JSON.stringify(settings._saveToObject());
|
|
||||||
cookie.set('settings', stringData);
|
|
||||||
} catch (ex) {
|
|
||||||
console.error('Unable to JSONify the settings! ' + ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.CookiePersister = CookiePersister;
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,57 +0,0 @@
|
||||||
/* ***** 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 historyLengthSetting = {
|
|
||||||
name: "historyLength",
|
|
||||||
description: "How many typed commands do we recall for reference?",
|
|
||||||
type: "number",
|
|
||||||
defaultValue: 50
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.startup = function(data, reason) {
|
|
||||||
data.env.settings.addSetting(historyLengthSetting);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function(data, reason) {
|
|
||||||
data.env.settings.removeSetting(historyLengthSetting);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,332 +0,0 @@
|
||||||
define(function(require, exports, module) {
|
|
||||||
|
|
||||||
var ua = require("pilot/useragent");
|
|
||||||
var console = require('pilot/console');
|
|
||||||
|
|
||||||
// Changed to suit the specific needs of running within Skywriter
|
|
||||||
|
|
||||||
// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
|
|
||||||
// Luke Smith http://lucassmith.name/ (2008)
|
|
||||||
// Loic Dachary <loic@dachary.org> (2008)
|
|
||||||
// Johan Euphrosine <proppy@aminche.com> (2008)
|
|
||||||
// Øyvind Sean Kinsey http://kinsey.no/blog
|
|
||||||
//
|
|
||||||
// Information and discussions
|
|
||||||
// http://jspoker.pokersource.info/skin/test-printstacktrace.html
|
|
||||||
// http://eriwen.com/javascript/js-stack-trace/
|
|
||||||
// http://eriwen.com/javascript/stacktrace-update/
|
|
||||||
// http://pastie.org/253058
|
|
||||||
// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// guessFunctionNameFromLines comes from firebug
|
|
||||||
//
|
|
||||||
// Software License Agreement (BSD License)
|
|
||||||
//
|
|
||||||
// Copyright (c) 2007, Parakey Inc.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use of this software in source and binary forms, with or without modification,
|
|
||||||
// are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above
|
|
||||||
// copyright notice, this list of conditions and the
|
|
||||||
// following disclaimer.
|
|
||||||
//
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the
|
|
||||||
// following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// * Neither the name of Parakey Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products
|
|
||||||
// derived from this software without specific prior
|
|
||||||
// written permission of Parakey Inc.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
||||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
||||||
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
||||||
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Different browsers create stack traces in different ways.
|
|
||||||
* <strike>Feature</strike> Browser detection baby ;).
|
|
||||||
*/
|
|
||||||
var mode = (function() {
|
|
||||||
|
|
||||||
// We use SC's browser detection here to avoid the "break on error"
|
|
||||||
// functionality provided by Firebug. Firebug tries to do the right
|
|
||||||
// thing here and break, but it happens every time you load the page.
|
|
||||||
// bug 554105
|
|
||||||
if (ua.isGecko) {
|
|
||||||
return 'firefox';
|
|
||||||
} else if (ua.isOpera) {
|
|
||||||
return 'opera';
|
|
||||||
} else {
|
|
||||||
return 'other';
|
|
||||||
}
|
|
||||||
|
|
||||||
// SC doesn't do any detection of Chrome at this time.
|
|
||||||
|
|
||||||
// this is the original feature detection code that is used as a
|
|
||||||
// fallback.
|
|
||||||
try {
|
|
||||||
(0)();
|
|
||||||
} catch (e) {
|
|
||||||
if (e.arguments) {
|
|
||||||
return 'chrome';
|
|
||||||
}
|
|
||||||
if (e.stack) {
|
|
||||||
return 'firefox';
|
|
||||||
}
|
|
||||||
if (window.opera && !('stacktrace' in e)) { //Opera 9-
|
|
||||||
return 'opera';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'other';
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function stringifyArguments(args) {
|
|
||||||
for (var i = 0; i < args.length; ++i) {
|
|
||||||
var argument = args[i];
|
|
||||||
if (typeof argument == 'object') {
|
|
||||||
args[i] = '#object';
|
|
||||||
} else if (typeof argument == 'function') {
|
|
||||||
args[i] = '#function';
|
|
||||||
} else if (typeof argument == 'string') {
|
|
||||||
args[i] = '"' + argument + '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract a stack trace from the format emitted by each browser.
|
|
||||||
*/
|
|
||||||
var decoders = {
|
|
||||||
chrome: function(e) {
|
|
||||||
var stack = e.stack;
|
|
||||||
if (!stack) {
|
|
||||||
console.log(e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return stack.replace(/^.*?\n/, '').
|
|
||||||
replace(/^.*?\n/, '').
|
|
||||||
replace(/^.*?\n/, '').
|
|
||||||
replace(/^[^\(]+?[\n$]/gm, '').
|
|
||||||
replace(/^\s+at\s+/gm, '').
|
|
||||||
replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@').
|
|
||||||
split('\n');
|
|
||||||
},
|
|
||||||
|
|
||||||
firefox: function(e) {
|
|
||||||
var stack = e.stack;
|
|
||||||
if (!stack) {
|
|
||||||
console.log(e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
// stack = stack.replace(/^.*?\n/, '');
|
|
||||||
stack = stack.replace(/(?:\n@:0)?\s+$/m, '');
|
|
||||||
stack = stack.replace(/^\(/gm, '{anonymous}(');
|
|
||||||
return stack.split('\n');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Opera 7.x and 8.x only!
|
|
||||||
opera: function(e) {
|
|
||||||
var lines = e.message.split('\n'), ANON = '{anonymous}',
|
|
||||||
lineRE = /Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i, i, j, len;
|
|
||||||
|
|
||||||
for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
|
|
||||||
if (lineRE.test(lines[i])) {
|
|
||||||
lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) +
|
|
||||||
' -- ' +
|
|
||||||
lines[i + 1].replace(/^\s+/, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.splice(j, lines.length - j);
|
|
||||||
return lines;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Safari, Opera 9+, IE, and others
|
|
||||||
other: function(curr) {
|
|
||||||
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], j = 0, fn, args;
|
|
||||||
|
|
||||||
var maxStackSize = 10;
|
|
||||||
while (curr && stack.length < maxStackSize) {
|
|
||||||
fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
|
|
||||||
args = Array.prototype.slice.call(curr['arguments']);
|
|
||||||
stack[j++] = fn + '(' + stringifyArguments(args) + ')';
|
|
||||||
|
|
||||||
//Opera bug: if curr.caller does not exist, Opera returns curr (WTF)
|
|
||||||
if (curr === curr.caller && window.opera) {
|
|
||||||
//TODO: check for same arguments if possible
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
curr = curr.caller;
|
|
||||||
}
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function NameGuesser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
NameGuesser.prototype = {
|
|
||||||
|
|
||||||
sourceCache: {},
|
|
||||||
|
|
||||||
ajax: function(url) {
|
|
||||||
var req = this.createXMLHTTPObject();
|
|
||||||
if (!req) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
req.open('GET', url, false);
|
|
||||||
req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
|
|
||||||
req.send('');
|
|
||||||
return req.responseText;
|
|
||||||
},
|
|
||||||
|
|
||||||
createXMLHTTPObject: function() {
|
|
||||||
// Try XHR methods in order and store XHR factory
|
|
||||||
var xmlhttp, XMLHttpFactories = [
|
|
||||||
function() {
|
|
||||||
return new XMLHttpRequest();
|
|
||||||
}, function() {
|
|
||||||
return new ActiveXObject('Msxml2.XMLHTTP');
|
|
||||||
}, function() {
|
|
||||||
return new ActiveXObject('Msxml3.XMLHTTP');
|
|
||||||
}, function() {
|
|
||||||
return new ActiveXObject('Microsoft.XMLHTTP');
|
|
||||||
}
|
|
||||||
];
|
|
||||||
for (var i = 0; i < XMLHttpFactories.length; i++) {
|
|
||||||
try {
|
|
||||||
xmlhttp = XMLHttpFactories[i]();
|
|
||||||
// Use memoization to cache the factory
|
|
||||||
this.createXMLHTTPObject = XMLHttpFactories[i];
|
|
||||||
return xmlhttp;
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getSource: function(url) {
|
|
||||||
if (!(url in this.sourceCache)) {
|
|
||||||
this.sourceCache[url] = this.ajax(url).split('\n');
|
|
||||||
}
|
|
||||||
return this.sourceCache[url];
|
|
||||||
},
|
|
||||||
|
|
||||||
guessFunctions: function(stack) {
|
|
||||||
for (var i = 0; i < stack.length; ++i) {
|
|
||||||
var reStack = /{anonymous}\(.*\)@(\w+:\/\/([-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
|
|
||||||
var frame = stack[i], m = reStack.exec(frame);
|
|
||||||
if (m) {
|
|
||||||
var file = m[1], lineno = m[4]; //m[7] is character position in Chrome
|
|
||||||
if (file && lineno) {
|
|
||||||
var functionName = this.guessFunctionName(file, lineno);
|
|
||||||
stack[i] = frame.replace('{anonymous}', functionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stack;
|
|
||||||
},
|
|
||||||
|
|
||||||
guessFunctionName: function(url, lineNo) {
|
|
||||||
try {
|
|
||||||
return this.guessFunctionNameFromLines(lineNo, this.getSource(url));
|
|
||||||
} catch (e) {
|
|
||||||
return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
guessFunctionNameFromLines: function(lineNo, source) {
|
|
||||||
var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
|
|
||||||
var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
|
|
||||||
// Walk backwards from the first line in the function until we find the line which
|
|
||||||
// matches the pattern above, which is the function definition
|
|
||||||
var line = '', maxLines = 10;
|
|
||||||
for (var i = 0; i < maxLines; ++i) {
|
|
||||||
line = source[lineNo - i] + line;
|
|
||||||
if (line !== undefined) {
|
|
||||||
var m = reGuessFunction.exec(line);
|
|
||||||
if (m) {
|
|
||||||
return m[1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m = reFunctionArgNames.exec(line);
|
|
||||||
}
|
|
||||||
if (m && m[1]) {
|
|
||||||
return m[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '(?)';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var guesser = new NameGuesser();
|
|
||||||
|
|
||||||
var frameIgnorePatterns = [
|
|
||||||
/http:\/\/localhost:4020\/sproutcore.js:/
|
|
||||||
];
|
|
||||||
|
|
||||||
exports.ignoreFramesMatching = function(regex) {
|
|
||||||
frameIgnorePatterns.push(regex);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a stack trace from an exception
|
|
||||||
* @param ex {Error} The error to create a stacktrace from (optional)
|
|
||||||
* @param guess {Boolean} If we should try to resolve the names of anonymous functions
|
|
||||||
*/
|
|
||||||
exports.Trace = function Trace(ex, guess) {
|
|
||||||
this._ex = ex;
|
|
||||||
this._stack = decoders[mode](ex);
|
|
||||||
|
|
||||||
if (guess) {
|
|
||||||
this._stack = guesser.guessFunctions(this._stack);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log to the console a number of lines (default all of them)
|
|
||||||
* @param lines {number} Maximum number of lines to wrote to console
|
|
||||||
*/
|
|
||||||
exports.Trace.prototype.log = function(lines) {
|
|
||||||
if (lines <= 0) {
|
|
||||||
// You aren't going to have more lines in your stack trace than this
|
|
||||||
// and it still fits in a 32bit integer
|
|
||||||
lines = 999999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
var printed = 0;
|
|
||||||
for (var i = 0; i < this._stack.length && printed < lines; i++) {
|
|
||||||
var frame = this._stack[i];
|
|
||||||
var display = true;
|
|
||||||
frameIgnorePatterns.forEach(function(regex) {
|
|
||||||
if (regex.test(frame)) {
|
|
||||||
display = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (display) {
|
|
||||||
console.debug(frame);
|
|
||||||
printed++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,163 +0,0 @@
|
||||||
require.def(['require', 'exports', 'module',
|
|
||||||
'rangeutils/tests/plugindev',
|
|
||||||
'rangeutils/tests/utils/range'
|
|
||||||
], function(require, exports, module,
|
|
||||||
t,
|
|
||||||
Range
|
|
||||||
) {
|
|
||||||
|
|
||||||
/* ***** 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 ***** */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.testAddPositions = function() {
|
|
||||||
t.deepEqual(Range.addPositions({ row: 0, col: 0 },
|
|
||||||
{ row: 0, col: 0 }), { row: 0, col: 0 }, '0,0 + 0,0 and 0,0');
|
|
||||||
t.deepEqual(Range.addPositions({ row: 1, col: 0 },
|
|
||||||
{ row: 2, col: 0 }), { row: 3, col: 0 }, '1,0 + 2,0 and 3,0');
|
|
||||||
t.deepEqual(Range.addPositions({ row: 0, col: 1 },
|
|
||||||
{ row: 0, col: 1 }), { row: 0, col: 2 }, '0,1 + 0,1 and 0,2');
|
|
||||||
t.deepEqual(Range.addPositions({ row: 1, col: 2 },
|
|
||||||
{ row: -1, col: -2 }), { row: 0, col: 0 }, '1,2 + -1,-2 and 0,0');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testCloneRange = function() {
|
|
||||||
var oldRange = { start: { row: 1, col: 2 }, end: { row: 3, col: 4 } };
|
|
||||||
var newRange = Range.cloneRange(oldRange);
|
|
||||||
t.deepEqual(oldRange, newRange, "the old range and the new range");
|
|
||||||
t.ok(oldRange.start !== newRange.start, "the old range's start position " +
|
|
||||||
"is distinct from the new range's start position");
|
|
||||||
t.ok(oldRange.end !== newRange.end, "the old range's end position is " +
|
|
||||||
"distinct from the new range's end position");
|
|
||||||
t.ok(oldRange !== newRange, "the old range is distinct from the new " +
|
|
||||||
"range");
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testComparePositions = function() {
|
|
||||||
t.equal(Range.comparePositions({ row: 0, col: 0 },
|
|
||||||
{ row: 0, col: 0 }), 0, '0,0 = 0,0');
|
|
||||||
t.ok(Range.comparePositions({ row: 0, col: 0 },
|
|
||||||
{ row: 1, col: 0 }) < 0, '0,0 < 1,0');
|
|
||||||
t.ok(Range.comparePositions({ row: 0, col: 0 },
|
|
||||||
{ row: 0, col: 1 }) < 0, '0,0 < 0,1');
|
|
||||||
t.ok(Range.comparePositions({ row: 1, col: 0 },
|
|
||||||
{ row: 0, col: 0 }) > 0, '1,0 > 0,0');
|
|
||||||
t.ok(Range.comparePositions({ row: 0, col: 1 },
|
|
||||||
{ row: 0, col: 0 }) > 0, '0,1 > 0,0');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testExtendRange = function() {
|
|
||||||
t.deepEqual(Range.extendRange({
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 3, col: 4 }
|
|
||||||
}, { row: 5, col: 6 }), {
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 8, col: 10 }
|
|
||||||
}, '[ 1,2 3,4 ] extended by 5,6 = [ 1,2 8,10 ]');
|
|
||||||
t.deepEqual(Range.extendRange({
|
|
||||||
start: { row: 7, col: 8 },
|
|
||||||
end: { row: 9, col: 10 }
|
|
||||||
}, { row: 0, col: 0 }), {
|
|
||||||
start: { row: 7, col: 8 },
|
|
||||||
end: { row: 9, col: 10 }
|
|
||||||
}, '[ 7,8 9,10 ] extended by 0,0 remains the same');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testMaxPosition = function() {
|
|
||||||
t.deepEqual(Range.maxPosition({ row: 0, col: 0 },
|
|
||||||
{ row: 0, col: 0 }), { row: 0, col: 0 }, 'max(0,0 0,0) = 0,0');
|
|
||||||
t.deepEqual(Range.maxPosition({ row: 0, col: 0 },
|
|
||||||
{ row: 1, col: 0 }), { row: 1, col: 0 }, 'max(0,0 1,0) = 1,0');
|
|
||||||
t.deepEqual(Range.maxPosition({ row: 0, col: 0 },
|
|
||||||
{ row: 0, col: 1 }), { row: 0, col: 1 }, 'max(0,0 0,1) = 0,1');
|
|
||||||
t.deepEqual(Range.maxPosition({ row: 1, col: 0 },
|
|
||||||
{ row: 0, col: 0 }), { row: 1, col: 0 }, 'max(1,0 0,0) = 1,0');
|
|
||||||
t.deepEqual(Range.maxPosition({ row: 0, col: 1 },
|
|
||||||
{ row: 0, col: 0 }), { row: 0, col: 1 }, 'max(0,1 0,0) = 0,1');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testNormalizeRange = function() {
|
|
||||||
t.deepEqual(Range.normalizeRange({
|
|
||||||
start: { row: 0, col: 0 },
|
|
||||||
end: { row: 0, col: 0 }
|
|
||||||
}), {
|
|
||||||
start: { row: 0, col: 0 },
|
|
||||||
end: { row: 0, col: 0 }
|
|
||||||
}, 'normalize(0,0 0,0) and (0,0 0,0)');
|
|
||||||
t.deepEqual(Range.normalizeRange({
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 3, col: 4 }
|
|
||||||
}), {
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 3, col: 4 }
|
|
||||||
}, 'normalize(1,2 3,4) and (1,2 3,4)');
|
|
||||||
t.deepEqual(Range.normalizeRange({
|
|
||||||
start: { row: 4, col: 3 },
|
|
||||||
end: { row: 2, col: 1 }
|
|
||||||
}), {
|
|
||||||
start: { row: 2, col: 1 },
|
|
||||||
end: { row: 4, col: 3 }
|
|
||||||
}, 'normalize(4,3 2,1) and (2,1 4,3)');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.testUnionRanges = function() {
|
|
||||||
t.deepEqual(Range.unionRanges({
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 3, col: 4 }
|
|
||||||
}, {
|
|
||||||
start: { row: 5, col: 6 },
|
|
||||||
end: { row: 7, col: 8 }
|
|
||||||
}), {
|
|
||||||
start: { row: 1, col: 2 },
|
|
||||||
end: { row: 7, col: 8 }
|
|
||||||
}, '[ 1,2 3,4 ] union [ 5,6 7,8 ] = [ 1,2 7,8 ]');
|
|
||||||
t.deepEqual(Range.unionRanges({
|
|
||||||
start: { row: 4, col: 4 },
|
|
||||||
end: { row: 5, col: 5 }
|
|
||||||
}, {
|
|
||||||
start: { row: 3, col: 3 },
|
|
||||||
end: { row: 4, col: 5 }
|
|
||||||
}), {
|
|
||||||
start: { row: 3, col: 3 },
|
|
||||||
end: { row: 5, col: 5 }
|
|
||||||
}, '[ 4,4 5,5 ] union [ 3,3 4,5 ] = [ 3,3 5,5 ]');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,282 +0,0 @@
|
||||||
/* ***** 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) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some types can detect validity, that is to say they can distinguish between
|
|
||||||
* valid and invalid values.
|
|
||||||
* TODO: Change these constants to be numbers for more performance?
|
|
||||||
*/
|
|
||||||
var Status = {
|
|
||||||
/**
|
|
||||||
* The conversion process worked without any problem, and the value is
|
|
||||||
* valid. There are a number of failure states, so the best way to check
|
|
||||||
* for failure is (x !== Status.VALID)
|
|
||||||
*/
|
|
||||||
VALID: {
|
|
||||||
toString: function() { return 'VALID'; },
|
|
||||||
valueOf: function() { return 0; }
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A conversion process failed, however it was noted that the string
|
|
||||||
* provided to 'parse()' could be VALID by the addition of more characters,
|
|
||||||
* so the typing may not be actually incorrect yet, just unfinished.
|
|
||||||
* @see Status.INVALID
|
|
||||||
*/
|
|
||||||
INCOMPLETE: {
|
|
||||||
toString: function() { return 'INCOMPLETE'; },
|
|
||||||
valueOf: function() { return 1; }
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The conversion process did not work, the value should be null and a
|
|
||||||
* reason for failure should have been provided. In addition some completion
|
|
||||||
* values may be available.
|
|
||||||
* @see Status.INCOMPLETE
|
|
||||||
*/
|
|
||||||
INVALID: {
|
|
||||||
toString: function() { return 'INVALID'; },
|
|
||||||
valueOf: function() { return 2; }
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A combined status is the worser of the provided statuses
|
|
||||||
*/
|
|
||||||
combine: function(statuses) {
|
|
||||||
var combined = Status.VALID;
|
|
||||||
for (var i = 0; i < statuses.length; i++) {
|
|
||||||
if (statuses[i].valueOf() > combined.valueOf()) {
|
|
||||||
combined = statuses[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exports.Status = Status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type.parse() method returns a Conversion to inform the user about not
|
|
||||||
* only the result of a Conversion but also about what went wrong.
|
|
||||||
* We could use an exception, and throw if the conversion failed, but that
|
|
||||||
* seems to violate the idea that exceptions should be exceptional. Typos are
|
|
||||||
* not. Also in order to store both a status and a message we'd still need
|
|
||||||
* some sort of exception type...
|
|
||||||
*/
|
|
||||||
function Conversion(value, status, message, predictions) {
|
|
||||||
/**
|
|
||||||
* The result of the conversion process. Will be null if status != VALID
|
|
||||||
*/
|
|
||||||
this.value = value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The status of the conversion.
|
|
||||||
* @see Status
|
|
||||||
*/
|
|
||||||
this.status = status || Status.VALID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message to go with the conversion. This could be present for any status
|
|
||||||
* including VALID in the case where we want to note a warning for example.
|
|
||||||
* I18N: On the one hand this nasty and un-internationalized, however with
|
|
||||||
* a command line it is hard to know where to start.
|
|
||||||
*/
|
|
||||||
this.message = message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A array of strings which are the systems best guess at better inputs than
|
|
||||||
* the one presented.
|
|
||||||
* We generally expect there to be about 7 predictions (to match human list
|
|
||||||
* comprehension ability) however it is valid to provide up to about 20,
|
|
||||||
* or less. It is the job of the predictor to decide a smart cut-off.
|
|
||||||
* For example if there are 4 very good matches and 4 very poor ones,
|
|
||||||
* probably only the 4 very good matches should be presented.
|
|
||||||
*/
|
|
||||||
this.predictions = predictions || [];
|
|
||||||
}
|
|
||||||
exports.Conversion = Conversion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Most of our types are 'static' e.g. there is only one type of 'text', however
|
|
||||||
* some types like 'selection' and 'deferred' are customizable. The basic
|
|
||||||
* Type type isn't useful, but does provide documentation about what types do.
|
|
||||||
*/
|
|
||||||
function Type() {
|
|
||||||
};
|
|
||||||
Type.prototype = {
|
|
||||||
/**
|
|
||||||
* Convert the given <tt>value</tt> to a string representation.
|
|
||||||
* Where possible, there should be round-tripping between values and their
|
|
||||||
* string representations.
|
|
||||||
*/
|
|
||||||
stringify: function(value) { throw new Error("not implemented"); },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the given <tt>str</tt> to an instance of this type.
|
|
||||||
* Where possible, there should be round-tripping between values and their
|
|
||||||
* string representations.
|
|
||||||
* @return Conversion
|
|
||||||
*/
|
|
||||||
parse: function(str) { throw new Error("not implemented"); },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plug-in system, and other things need to know what this type is
|
|
||||||
* called. The name alone is not enough to fully specify a type. Types like
|
|
||||||
* 'selection' and 'deferred' need extra data, however this function returns
|
|
||||||
* only the name, not the extra data.
|
|
||||||
* <p>In old bespin, equality was based on the name. This may turn out to be
|
|
||||||
* important in Ace too.
|
|
||||||
*/
|
|
||||||
name: undefined,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is some concept of a higher value, return it,
|
|
||||||
* otherwise return undefined.
|
|
||||||
*/
|
|
||||||
increment: function(value) {
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is some concept of a lower value, return it,
|
|
||||||
* otherwise return undefined.
|
|
||||||
*/
|
|
||||||
decrement: function(value) {
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* There is interesting information (like predictions) in a conversion of
|
|
||||||
* nothing, the output of this can sometimes be customized.
|
|
||||||
* @return Conversion
|
|
||||||
*/
|
|
||||||
getDefault: function() {
|
|
||||||
return this.parse('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exports.Type = Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private registry of types
|
|
||||||
* Invariant: types[name] = type.name
|
|
||||||
*/
|
|
||||||
var types = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new type to the list available to the system.
|
|
||||||
* You can pass 2 things to this function - either an instance of Type, in
|
|
||||||
* which case we return this instance when #getType() is called with a 'name'
|
|
||||||
* that matches type.name.
|
|
||||||
* Also you can pass in a constructor (i.e. function) in which case when
|
|
||||||
* #getType() is called with a 'name' that matches Type.prototype.name we will
|
|
||||||
* pass the typeSpec into this constructor. See #reconstituteType().
|
|
||||||
*/
|
|
||||||
exports.registerType = function(type) {
|
|
||||||
if (typeof type === 'object') {
|
|
||||||
if (type instanceof Type) {
|
|
||||||
if (!type.name) {
|
|
||||||
throw new Error('All registered types must have a name');
|
|
||||||
}
|
|
||||||
types[type.name] = type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Can\'t registerType using: ' + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeof type === 'function') {
|
|
||||||
if (!type.prototype.name) {
|
|
||||||
throw new Error('All registered types must have a name');
|
|
||||||
}
|
|
||||||
types[type.prototype.name] = type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Unknown type: ' + type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.registerTypes = function registerTypes(types) {
|
|
||||||
Object.keys(types).forEach(function (name) {
|
|
||||||
var type = types[name];
|
|
||||||
type.name = name;
|
|
||||||
exports.registerType(type);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a type from the list available to the system
|
|
||||||
*/
|
|
||||||
exports.deregisterType = function(type) {
|
|
||||||
delete types[type.name];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See description of #exports.registerType()
|
|
||||||
*/
|
|
||||||
function reconstituteType(name, typeSpec) {
|
|
||||||
if (name.substr(-2) === '[]') { // i.e. endsWith('[]')
|
|
||||||
var subtypeName = name.slice(0, -2);
|
|
||||||
return new types['array'](subtypeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = types[name];
|
|
||||||
if (typeof type === 'function') {
|
|
||||||
type = new type(typeSpec);
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a type, previously registered using #registerType()
|
|
||||||
*/
|
|
||||||
exports.getType = function(typeSpec) {
|
|
||||||
if (typeof typeSpec === 'string') {
|
|
||||||
return reconstituteType(typeSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof typeSpec === 'object') {
|
|
||||||
if (!typeSpec.name) {
|
|
||||||
throw new Error('Missing \'name\' member to typeSpec');
|
|
||||||
}
|
|
||||||
return reconstituteType(typeSpec.name, typeSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can\'t extract type from ' + typeSpec);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,332 +0,0 @@
|
||||||
/* ***** 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)
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
var types = require("pilot/types");
|
|
||||||
var Type = types.Type;
|
|
||||||
var Conversion = types.Conversion;
|
|
||||||
var Status = types.Status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These are the basic types that we accept. They are vaguely based on the
|
|
||||||
* Jetpack settings system (https://wiki.mozilla.org/Labs/Jetpack/JEP/24)
|
|
||||||
* although clearly more restricted.
|
|
||||||
*
|
|
||||||
* <p>In addition to these types, Jetpack also accepts range, member, password
|
|
||||||
* that we are thinking of adding.
|
|
||||||
*
|
|
||||||
* <p>This module probably should not be accessed directly, but instead used
|
|
||||||
* through types.js
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'text' is the default if no type is given.
|
|
||||||
*/
|
|
||||||
var text = new Type();
|
|
||||||
|
|
||||||
text.stringify = function(value) {
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
text.parse = function(value) {
|
|
||||||
if (typeof value != 'string') {
|
|
||||||
throw new Error('non-string passed to text.parse()');
|
|
||||||
}
|
|
||||||
return new Conversion(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
text.name = 'text';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We don't currently plan to distinguish between integers and floats
|
|
||||||
*/
|
|
||||||
var number = new Type();
|
|
||||||
|
|
||||||
number.stringify = function(value) {
|
|
||||||
if (!value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return '' + value;
|
|
||||||
};
|
|
||||||
|
|
||||||
number.parse = function(value) {
|
|
||||||
if (typeof value != 'string') {
|
|
||||||
throw new Error('non-string passed to number.parse()');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.replace(/\s/g, '').length === 0) {
|
|
||||||
return new Conversion(null, Status.INCOMPLETE, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
var reply = new Conversion(parseInt(value, 10));
|
|
||||||
if (isNaN(reply.value)) {
|
|
||||||
reply.status = Status.INVALID;
|
|
||||||
reply.message = 'Can\'t convert "' + value + '" to a number.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
};
|
|
||||||
|
|
||||||
number.decrement = function(value) {
|
|
||||||
return value - 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
number.increment = function(value) {
|
|
||||||
return value + 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
number.name = 'number';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One of a known set of options
|
|
||||||
*/
|
|
||||||
function SelectionType(typeSpec) {
|
|
||||||
if (!Array.isArray(typeSpec.data) && typeof typeSpec.data !== 'function') {
|
|
||||||
throw new Error('instances of SelectionType need typeSpec.data to be an array or function that returns an array:' + JSON.stringify(typeSpec));
|
|
||||||
}
|
|
||||||
Object.keys(typeSpec).forEach(function(key) {
|
|
||||||
this[key] = typeSpec[key];
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype = new Type();
|
|
||||||
|
|
||||||
SelectionType.prototype.stringify = function(value) {
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype.parse = function(str) {
|
|
||||||
if (typeof str != 'string') {
|
|
||||||
throw new Error('non-string passed to parse()');
|
|
||||||
}
|
|
||||||
if (!this.data) {
|
|
||||||
throw new Error('Missing data on selection type extension.');
|
|
||||||
}
|
|
||||||
var data = (typeof(this.data) === 'function') ? this.data() : this.data;
|
|
||||||
|
|
||||||
// The matchedValue could be the boolean value false
|
|
||||||
var hasMatched = false;
|
|
||||||
var matchedValue;
|
|
||||||
var completions = [];
|
|
||||||
data.forEach(function(option) {
|
|
||||||
if (str == option) {
|
|
||||||
matchedValue = this.fromString(option);
|
|
||||||
hasMatched = true;
|
|
||||||
}
|
|
||||||
else if (option.indexOf(str) === 0) {
|
|
||||||
completions.push(this.fromString(option));
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
if (hasMatched) {
|
|
||||||
return new Conversion(matchedValue);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// This is something of a hack it basically allows us to tell the
|
|
||||||
// setting type to forget its last setting hack.
|
|
||||||
if (this.noMatch) {
|
|
||||||
this.noMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (completions.length > 0) {
|
|
||||||
var msg = 'Possibilities' +
|
|
||||||
(str.length === 0 ? '' : ' for \'' + str + '\'');
|
|
||||||
return new Conversion(null, Status.INCOMPLETE, msg, completions);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var msg = 'Can\'t use \'' + str + '\'.';
|
|
||||||
return new Conversion(null, Status.INVALID, msg, completions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype.fromString = function(str) {
|
|
||||||
return str;
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype.decrement = function(value) {
|
|
||||||
var data = (typeof this.data === 'function') ? this.data() : this.data;
|
|
||||||
var index;
|
|
||||||
if (value == null) {
|
|
||||||
index = data.length - 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var name = this.stringify(value);
|
|
||||||
var index = data.indexOf(name);
|
|
||||||
index = (index === 0 ? data.length - 1 : index - 1);
|
|
||||||
}
|
|
||||||
return this.fromString(data[index]);
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype.increment = function(value) {
|
|
||||||
var data = (typeof this.data === 'function') ? this.data() : this.data;
|
|
||||||
var index;
|
|
||||||
if (value == null) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var name = this.stringify(value);
|
|
||||||
var index = data.indexOf(name);
|
|
||||||
index = (index === data.length - 1 ? 0 : index + 1);
|
|
||||||
}
|
|
||||||
return this.fromString(data[index]);
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectionType.prototype.name = 'selection';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SelectionType is a base class for other types
|
|
||||||
*/
|
|
||||||
exports.SelectionType = SelectionType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true/false values
|
|
||||||
*/
|
|
||||||
var bool = new SelectionType({
|
|
||||||
name: 'bool',
|
|
||||||
data: [ 'true', 'false' ],
|
|
||||||
stringify: function(value) {
|
|
||||||
return '' + value;
|
|
||||||
},
|
|
||||||
fromString: function(str) {
|
|
||||||
return str === 'true' ? true : false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A we don't know right now, but hope to soon.
|
|
||||||
*/
|
|
||||||
function DeferredType(typeSpec) {
|
|
||||||
if (typeof typeSpec.defer !== 'function') {
|
|
||||||
throw new Error('Instances of DeferredType need typeSpec.defer to be a function that returns a type');
|
|
||||||
}
|
|
||||||
Object.keys(typeSpec).forEach(function(key) {
|
|
||||||
this[key] = typeSpec[key];
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeferredType.prototype = new Type();
|
|
||||||
|
|
||||||
DeferredType.prototype.stringify = function(value) {
|
|
||||||
return this.defer().stringify(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeferredType.prototype.parse = function(value) {
|
|
||||||
return this.defer().parse(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeferredType.prototype.decrement = function(value) {
|
|
||||||
var deferred = this.defer();
|
|
||||||
return (deferred.decrement ? deferred.decrement(value) : undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeferredType.prototype.increment = function(value) {
|
|
||||||
var deferred = this.defer();
|
|
||||||
return (deferred.increment ? deferred.increment(value) : undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeferredType.prototype.name = 'deferred';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DeferredType is a base class for other types
|
|
||||||
*/
|
|
||||||
exports.DeferredType = DeferredType;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of objects of the same type
|
|
||||||
*/
|
|
||||||
function ArrayType(typeSpec) {
|
|
||||||
if (typeSpec instanceof Type) {
|
|
||||||
this.subtype = typeSpec;
|
|
||||||
}
|
|
||||||
else if (typeof typeSpec === 'string') {
|
|
||||||
this.subtype = types.getType(typeSpec);
|
|
||||||
if (this.subtype == null) {
|
|
||||||
throw new Error('Unknown array subtype: ' + typeSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Can\' handle array subtype');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ArrayType.prototype = new Type();
|
|
||||||
|
|
||||||
ArrayType.prototype.stringify = function(values) {
|
|
||||||
// TODO: Check for strings with spaces and add quotes
|
|
||||||
return values.join(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
ArrayType.prototype.parse = function(value) {
|
|
||||||
return this.defer().parse(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
ArrayType.prototype.name = 'array';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration and de-registration.
|
|
||||||
*/
|
|
||||||
var isStarted = false;
|
|
||||||
exports.startup = function() {
|
|
||||||
if (isStarted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isStarted = true;
|
|
||||||
types.registerType(text);
|
|
||||||
types.registerType(number);
|
|
||||||
types.registerType(bool);
|
|
||||||
types.registerType(SelectionType);
|
|
||||||
types.registerType(DeferredType);
|
|
||||||
types.registerType(ArrayType);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function() {
|
|
||||||
isStarted = false;
|
|
||||||
types.unregisterType(text);
|
|
||||||
types.unregisterType(number);
|
|
||||||
types.unregisterType(bool);
|
|
||||||
types.unregisterType(SelectionType);
|
|
||||||
types.unregisterType(DeferredType);
|
|
||||||
types.unregisterType(ArrayType);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,75 +0,0 @@
|
||||||
/* ***** 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)
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
var canon = require("pilot/canon");
|
|
||||||
var SelectionType = require("pilot/types/basic").SelectionType;
|
|
||||||
var types = require("pilot/types");
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select from the available commands
|
|
||||||
*/
|
|
||||||
var command = new SelectionType({
|
|
||||||
name: 'command',
|
|
||||||
data: function() {
|
|
||||||
return canon.getCommandNames();
|
|
||||||
},
|
|
||||||
stringify: function(command) {
|
|
||||||
return command.name;
|
|
||||||
},
|
|
||||||
fromString: function(str) {
|
|
||||||
return canon.getCommand(str);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration and de-registration.
|
|
||||||
*/
|
|
||||||
exports.startup = function() {
|
|
||||||
types.registerType(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function() {
|
|
||||||
types.unregisterType(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,139 +0,0 @@
|
||||||
/* ***** 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)
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
var SelectionType = require('pilot/types/basic').SelectionType;
|
|
||||||
var DeferredType = require('pilot/types/basic').DeferredType;
|
|
||||||
var types = require('pilot/types');
|
|
||||||
var settings = require('pilot/settings').settings;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EVIL: This relies on us using settingValue in the same event as setting
|
|
||||||
* The alternative is to have some central place where we store the current
|
|
||||||
* command line, but this might be a lesser evil for now.
|
|
||||||
*/
|
|
||||||
var lastSetting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select from the available settings
|
|
||||||
*/
|
|
||||||
var setting = new SelectionType({
|
|
||||||
name: 'setting',
|
|
||||||
data: function() {
|
|
||||||
return env.settings.getSettingNames();
|
|
||||||
},
|
|
||||||
stringify: function(setting) {
|
|
||||||
lastSetting = setting;
|
|
||||||
return setting.name;
|
|
||||||
},
|
|
||||||
fromString: function(str) {
|
|
||||||
lastSetting = settings.getSetting(str);
|
|
||||||
return lastSetting;
|
|
||||||
},
|
|
||||||
noMatch: function() {
|
|
||||||
lastSetting = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Something of a hack to allow the set command to give a clearer definition
|
|
||||||
* of the type to the command line.
|
|
||||||
*/
|
|
||||||
var settingValue = new DeferredType({
|
|
||||||
name: 'settingValue',
|
|
||||||
defer: function() {
|
|
||||||
if (lastSetting) {
|
|
||||||
return lastSetting.type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return types.getType('text');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Promote the current value in any list of predictions, and add it if
|
|
||||||
* there are none.
|
|
||||||
*/
|
|
||||||
getDefault: function() {
|
|
||||||
var conversion = this.parse('');
|
|
||||||
if (lastSetting) {
|
|
||||||
var current = lastSetting.get();
|
|
||||||
if (conversion.predictions.length === 0) {
|
|
||||||
conversion.predictions.push(current);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Remove current from predictions
|
|
||||||
var removed = false;
|
|
||||||
while (true) {
|
|
||||||
var index = conversion.predictions.indexOf(current);
|
|
||||||
if (index === -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
conversion.predictions.splice(index, 1);
|
|
||||||
removed = true;
|
|
||||||
}
|
|
||||||
// If the current value wasn't something we would predict, leave it
|
|
||||||
if (removed) {
|
|
||||||
conversion.predictions.push(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conversion;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var env;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration and de-registration.
|
|
||||||
*/
|
|
||||||
exports.startup = function(data, reason) {
|
|
||||||
// TODO: this is probably all kinds of evil, but we need something working
|
|
||||||
env = data.env;
|
|
||||||
types.registerType(setting);
|
|
||||||
types.registerType(settingValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.shutdown = function(data, reason) {
|
|
||||||
types.unregisterType(setting);
|
|
||||||
types.unregisterType(settingValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
Загрузка…
Ссылка в новой задаче