From 1e24a23ca57badb983dc7da62e0926f5d1910927 Mon Sep 17 00:00:00 2001 From: Joe Walker Date: Tue, 29 Mar 2011 17:59:53 +0100 Subject: [PATCH] moving files from pilot to ace where they more properly belong --- Makefile.dryice.js | 4 +- Makefile.dryice.textarea.js | 4 +- build_support/boot.js | 6 +- build_support/boot_textarea.js | 6 +- demo/boot.js | 39 ++- demo/demo.js | 20 +- lib/ace/commands/settings.js | 139 ++++++++++ lib/ace/environment.js | 51 ++++ lib/ace/{defaults.js => index.js} | 21 +- lib/ace/keyboard/hash_handler.js | 10 +- lib/ace/keyboard/keybinding.js | 5 +- lib/ace/keys.js | 118 ++++++++ lib/ace/plugin_manager.js | 159 +++++++++++ lib/ace/promise.js | 247 +++++++++++++++++ lib/ace/settings.js | 385 +++++++++++++++++++++++++++ lib/ace/settings/default-settings.js | 4 +- support/cockpit | 2 +- 17 files changed, 1176 insertions(+), 44 deletions(-) create mode 100644 lib/ace/commands/settings.js create mode 100644 lib/ace/environment.js rename lib/ace/{defaults.js => index.js} (80%) create mode 100644 lib/ace/keys.js create mode 100644 lib/ace/plugin_manager.js create mode 100644 lib/ace/promise.js create mode 100644 lib/ace/settings.js diff --git a/Makefile.dryice.js b/Makefile.dryice.js index 8e31d329..523a47e3 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -75,8 +75,8 @@ copy({ require: [ "pilot/fixoldbrowsers", "pilot/index", - "pilot/plugin_manager", - "pilot/environment", + "ace/plugin_manager", + "ace/environment", "ace/editor", "ace/edit_session", "ace/undomanager", diff --git a/Makefile.dryice.textarea.js b/Makefile.dryice.textarea.js index 1d615466..008f046f 100755 --- a/Makefile.dryice.textarea.js +++ b/Makefile.dryice.textarea.js @@ -75,8 +75,8 @@ copy({ require: [ "pilot/fixoldbrowsers", "pilot/index", - "pilot/plugin_manager", - "pilot/environment", + "ace/plugin_manager", + "ace/environment", "ace/editor", "ace/edit_session", "ace/undomanager", diff --git a/build_support/boot.js b/build_support/boot.js index 0488fcae..59504132 100644 --- a/build_support/boot.js +++ b/build_support/boot.js @@ -38,8 +38,8 @@ var deps = [ "pilot/fixoldbrowsers", "pilot/index", - "pilot/plugin_manager", - "pilot/environment", + "ace/plugin_manager", + "ace/environment", "ace/editor", "ace/edit_session", "ace/virtual_renderer", @@ -72,7 +72,7 @@ require(deps, function() { var editor = new Editor(new Renderer(el, "ace/theme/textmate")); editor.setSession(doc); - var env = require("pilot/environment").create(); + var env = require("ace/environment").create(); catalog.startupPlugins({ env: env }).then(function() { env.document = doc; env.editor = editor; diff --git a/build_support/boot_textarea.js b/build_support/boot_textarea.js index 06eda277..aa373b4e 100644 --- a/build_support/boot_textarea.js +++ b/build_support/boot_textarea.js @@ -42,8 +42,8 @@ var require = window.__ace_shadowed__.require; var deps = [ "pilot/fixoldbrowsers", "pilot/index", - "pilot/plugin_manager", - "pilot/environment", + "ace/plugin_manager", + "ace/environment", "ace/editor", "ace/edit_session", "ace/virtual_renderer", @@ -77,7 +77,7 @@ window.__ace_shadowed__.edit = function(el) { var editor = new Editor(new Renderer(el, "ace/theme/textmate")); editor.setSession(doc); - var env = require("pilot/environment").create(); + var env = require("ace/environment").create(); catalog.startupPlugins({ env: env }).then(function() { env.document = doc; env.editor = env; diff --git a/demo/boot.js b/demo/boot.js index e3e55293..e23f017f 100644 --- a/demo/boot.js +++ b/demo/boot.js @@ -35,6 +35,7 @@ * * ***** END LICENSE BLOCK ***** */ +/* require({ paths: { demo: "../demo", @@ -44,16 +45,42 @@ require({ } }); -var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", - "pilot/environment", "demo/demo" ]; +var deps = [ "pilot/fixoldbrowsers", "ace/plugin_manager", + "ace/environment", "demo/demo" ]; -var plugins = [ "pilot/index", "cockpit/index", "ace/defaults" ]; +var plugins = [ "pilot/index", "cockpit/index", "ace/index" ]; require(deps, function() { - var catalog = require("pilot/plugin_manager").catalog; + var catalog = require("ace/plugin_manager").catalog; catalog.registerPlugins(plugins).then(function() { - var env = require("pilot/environment").create(); + var env = require("ace/environment").create(); catalog.startupPlugins({ env: env }).then(function() { require("demo/demo").launch(env); }); }); -}); \ No newline at end of file +}); +*/ + +require({ + paths: { + demo: "../demo", + ace: "../lib/ace", + cockpit: "../support/cockpit/lib/cockpit", + pilot: "../support/cockpit/support/pilot/lib/pilot" + } +}); + +var deps = [ + "ace/environment", "pilot/index", "cockpit/index", "ace/index", + "pilot/fixoldbrowsers" +]; + +require(deps, function(environment, pilot, cockpit) { + var data = { env: environment.create() }; + pilot.startup(data); + cockpit.startup(data); + new cockpit.CliView(data.env); + + require([ "demo/demo" ], function(demo) { + demo.launch(data.env); + }); +}.bind(this)); diff --git a/demo/demo.js b/demo/demo.js index b2bc257a..7b106579 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -77,7 +77,7 @@ exports.launch = function(env) { custom: new HashHandler({ "gotoright": "Tab" }) - } + }; var docs = {}; @@ -88,7 +88,7 @@ exports.launch = function(env) { } docs.plain = new EditSession(loreIpsum); docs.plain.setUseWrapMode(true); - docs.plain.setWrapLimitRange(80, 80) + docs.plain.setWrapLimitRange(80, 80); docs.plain.setMode(new TextMode()); docs.plain.setUndoManager(new UndoManager()); @@ -139,7 +139,7 @@ exports.launch = function(env) { docs.svg = new EditSession(document.getElementById("svgtext").innerHTML.replace("<", "<")); docs.svg.setMode(new SvgMode()); docs.svg.setUndoManager(new UndoManager()); - + docs.textile = new EditSession(document.getElementById("textiletext").innerHTML); docs.textile.setMode(new TextileMode()); docs.textile.setUndoManager(new UndoManager()); @@ -383,11 +383,11 @@ exports.launch = function(env) { }); window.env = env; - + /** * This demonstrates how you can define commands and bind shortcuts to them. */ - + // Command to focus the command line from the editor. canon.addCommand({ name: "focuscli", @@ -400,7 +400,7 @@ exports.launch = function(env) { env.cli.cliView.element.focus(); } }); - + // Command to focus the editor line from the command line. canon.addCommand({ name: "focuseditor", @@ -413,7 +413,7 @@ exports.launch = function(env) { env.editor.focus(); } }); - + // Fake-Save, works from the editor and the command line. canon.addCommand({ name: "save", @@ -426,7 +426,7 @@ exports.launch = function(env) { alert("Fake Save File"); } }); - + // Fake-Print with custom lookup-sender-match function. canon.addCommand({ name: "save", @@ -435,9 +435,9 @@ exports.launch = function(env) { mac: "Command-P", sender: function(env, sender, hashId, keyString) { if (sender == "editor") { - return true; + return true; } else { - alert("Sorry, can only print from the editor"); + alert("Sorry, can only print from the editor"); } } }, diff --git a/lib/ace/commands/settings.js b/lib/ace/commands/settings.js new file mode 100644 index 00000000..606f1b74 --- /dev/null +++ b/lib/ace/commands/settings.js @@ -0,0 +1,139 @@ +/* ***** 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 canon = require('pilot/canon'); + +var pref = { + metadata: { + description: 'Commands for managing preferences' + }, + + showMetadata: { + description: 'Show preferences', + params: [ + { + name: 'filter', + type: 'string', + description: 'Regex filter to show only some settings', + defaultValue: null + } + ] + }, + show: function(filter) { + var names = canon.getEnvironment().settings.getSettingNames(); + // first sort the settingsList based on the name + names.sort(function(name1, name2) { + return name1.localeCompare(name2); + }); + + if (filter) { + filter = new RegExp(filter); + } + + var html = ''; + names.forEach(function(name) { + if (filter && !filter.test(name)) { + return; + } + + var setting = canon.getEnvironment().settings.getSetting(name); + var url = 'https://wiki.mozilla.org/Labs/Skywriter/Settings#' + + setting.name; + html += '' + + setting.name + + ' = ' + + setting.value + + '
'; + }); + + canon.getRequest().done(html); + }, + + setMetadata: { + description: 'Alter current settings', + params: [ + { + name: 'setting', + type: 'setting', + description: 'The name of the setting to display or alter' + }, + { + name: 'value', + type: 'settingValue', + description: 'The new value for the chosen setting' + } + ] + }, + set: function(setting, value) { + setting.set(value); + canon.getRequest().done('Setting: ' + setting.name + + ' = ' + setting.get()); + }, + + resetMetadata: { + description: 'Reset a preference to it\'s default values', + params: [ + { + name: 'setting', + type: 'setting', + description: 'The name of the setting to return to defaults' + } + ] + }, + reset: function(setting) { + setting.reset(); + request.done('Reset ' + setting.name + ' to default: ' + setting.value); + } +}; + + +exports.startup = function() { + canon.addCommands(pref, 'pref'); +}; + +exports.shutdown = function() { + canon.removeCommands(pref, 'pref'); +}; + + +}); diff --git a/lib/ace/environment.js b/lib/ace/environment.js new file mode 100644 index 00000000..b8563d70 --- /dev/null +++ b/lib/ace/environment.js @@ -0,0 +1,51 @@ +/* ***** 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) { + + +/** + * Create an environment object + */ +function create() { + return { + }; +}; + +exports.create = create; + + +}); diff --git a/lib/ace/defaults.js b/lib/ace/index.js similarity index 80% rename from lib/ace/defaults.js rename to lib/ace/index.js index bd217cd1..34369f0e 100644 --- a/lib/ace/defaults.js +++ b/lib/ace/index.js @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Joe Walker (jwalker@mozilla.com) * Irakli Gozalishvili (http://jeditoolkit.com) * * Alternatively, the contents of this file may be used under the terms of @@ -38,14 +39,18 @@ define(function(require, exports, module) { -var settings = require("ace/settings/default-settings") -exports.startup = function startup(data, reason) { - settings.startup(data, reason) -} +exports.startup = function startup(data) { + require("ace/settings").startup(data); + require("ace/commands/settings").startup(data); + require("ace/settings/default-settings").startup(data); +}; -exports.shutdown = function shutdown(data, reason) { - settings.shutdown(data, reason) -} +exports.shutdown = function shutdown(data) { + require("ace/settings/default-settings").shutdown(data); + require("ace/commands/settings").shutdown(data); + require("ace/settings").shutdown(data); +}; -}) + +}); diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index f968b39b..cda6a351 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -38,7 +38,7 @@ define(function(require, exports, module) { -var keyUtil = require("pilot/keys"); +var keyUtil = require("ace/keys"); function HashHandler(config) { this.setConfig(config); @@ -100,17 +100,17 @@ function HashHandler(config) { if (hashId != 0 || keyCode != 0) { return { command: (this.$config.reverse[hashId] || {})[textOrKey] - } + }; } else { return { command: "inserttext", args: { text: textOrKey } - } + }; } - } -}).call(HashHandler.prototype) + }; +}).call(HashHandler.prototype); exports.HashHandler = HashHandler; }); diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js index 93fc3fa8..28b08ca5 100644 --- a/lib/ace/keyboard/keybinding.js +++ b/lib/ace/keyboard/keybinding.js @@ -39,10 +39,11 @@ define(function(require, exports, module) { var useragent = require("pilot/useragent"); -var keyUtil = require("pilot/keys"); var event = require("pilot/event"); -var settings = require("pilot/settings").settings; var canon = require("pilot/canon"); + +var keyUtil = require("ace/keys"); +var settings = require("ace/settings").settings; require("ace/commands/default_commands"); var KeyBinding = function(editor) { diff --git a/lib/ace/keys.js b/lib/ace/keys.js new file mode 100644 index 00000000..4f8068c2 --- /dev/null +++ b/lib/ace/keys.js @@ -0,0 +1,118 @@ +/*! @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(); +}; + +}); diff --git a/lib/ace/plugin_manager.js b/lib/ace/plugin_manager.js new file mode 100644 index 00000000..e8ff5416 --- /dev/null +++ b/lib/ace/plugin_manager.js @@ -0,0 +1,159 @@ +/* ***** 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(); + +}); diff --git a/lib/ace/promise.js b/lib/ace/promise.js new file mode 100644 index 00000000..37f1832b --- /dev/null +++ b/lib/ace/promise.js @@ -0,0 +1,247 @@ +/* ***** 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"); + +/** + * 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; + +/** + * 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++; + _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 this 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(); + + console.groupEnd(); + return this; + } + + 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; + +}); diff --git a/lib/ace/settings.js b/lib/ace/settings.js new file mode 100644 index 00000000..adc2b528 --- /dev/null +++ b/lib/ace/settings.js @@ -0,0 +1,385 @@ +/* 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 (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) { + + +var console = require('pilot/console'); +var oop = require('pilot/oop'); +var types = require('pilot/types'); +var EventEmitter = require('pilot/event_emitter').EventEmitter; + +var SelectionType = require('pilot/types/basic').SelectionType; +var DeferredType = require('pilot/types/basic').DeferredType; +var Argument = require('pilot/argument').Argument; + + +/** + * 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 key 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. + *

Usage: + *

+ * // Create manually, or require 'settings' from the container.
+ * // This is the manual version:
+ * // 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');
+ * 
+ * @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. + *

Example usage: + *

+     * 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
+     * });
+     * 
+ * @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); + }, + + getSettings: function() { + return Object.keys(this._settings).map(function(name) { + return this._settings[name]; + }, 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.parseString(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; + + +/** + * 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.getSettings(); + }, + noMatch: function() { + lastSetting = null; + } +}); +(function() { + var originalStringify = setting.stringify; + setting.stringify = function(setting) { + lastSetting = setting; + return originalStringify.call(this, setting); + }; + + var originalParse = setting.parse; + setting.parse = function(arg) { + var conversion = originalParse.call(this, arg); + lastSetting = conversion.value; + return conversion; + }; +})(); + +/** + * 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 { + // console.error('Missing lastSetting. Defaulting to blank'); + return types.getType('blank'); + } + } +}); + +var env; + + +/** + * Registration and de-registration. + */ +exports.startup = function(data) { + env = data.env; + types.registerType(setting); + types.registerType(settingValue); +}; + +exports.shutdown = function(data) { + types.unregisterType(setting); + types.unregisterType(settingValue); +}; + + +}); diff --git a/lib/ace/settings/default-settings.js b/lib/ace/settings/default-settings.js index cb1092aa..9008d2c0 100644 --- a/lib/ace/settings/default-settings.js +++ b/lib/ace/settings/default-settings.js @@ -84,13 +84,13 @@ var settings = { } }; -exports.startup = function startup(data, reason) { +exports.startup = function startup(data) { env = data.env; types.registerTypes(settingTypes); data.env.settings.addSettings(settings); }; -exports.shutdown = function shutdown(data, reason) { +exports.shutdown = function shutdown(data) { data.env.settings.removeSettings(settings); }; diff --git a/support/cockpit b/support/cockpit index aa56894e..e20180cc 160000 --- a/support/cockpit +++ b/support/cockpit @@ -1 +1 @@ -Subproject commit aa56894ef1cfa30ab038d77fbc79f72416eb43e6 +Subproject commit e20180cc3311b0d366e2624ddd1b150767bb41e4