// Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns // Copyright (c) 2008-2010 by Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. "use strict"; /** @scope modules */ default xml namespace = XHTML; XML.ignoreWhitespace = false; XML.prettyPrinting = false; const plugins = { __proto__: modules }; const userContext = newContext(modules); const EVAL_ERROR = "__dactyl_eval_error"; const EVAL_RESULT = "__dactyl_eval_result"; const EVAL_STRING = "__dactyl_eval_string"; const Dactyl = Module("dactyl", { init: function () { window.dactyl = this; // cheap attempt at compatibility let prop = { get: deprecated("Please use dactyl instead", function liberator() dactyl) }; Object.defineProperty(window, "liberator", prop); Object.defineProperty(modules, "liberator", prop); this.commands = {}; this.indices = {}; this.modules = modules; this.observers = {}; this.commands["dactyl.help"] = function (event) { let elem = event.originalTarget; dactyl.help(elem.getAttribute("tag") || elem.textContent); }; }, cleanup: function () { delete window.dactyl; delete window.liberator; }, destroy: function () { autocommands.trigger("LeavePre", {}); storage.saveAll(); dactyl.triggerObserver("shutdown", null); util.dump("All dactyl modules destroyed\n"); autocommands.trigger("Leave", {}); }, /** @property {string} The name of the current user profile. */ profileName: Class.memoize(function () { // NOTE: services.profile.selectedProfile.name doesn't return // what you might expect. It returns the last _actively_ selected // profile (i.e. via the Profile Manager or -P option) rather than the // current profile. These will differ if the current process was run // without explicitly selecting a profile. let dir = services.directory.get("ProfD", Ci.nsIFile); for (let prof in iter(services.profile.profiles)) if (prof.QueryInterface(Ci.nsIToolkitProfile).rootDir.path === dir.path) return prof.name; return "unknown"; }), /** * @property {number} The current main mode. * @see modes#mainModes */ get mode() modes.main, set mode(value) modes.main = value, get menuItems() Dactyl.getMenuItems(), // Global constants CURRENT_TAB: [], NEW_TAB: [], NEW_BACKGROUND_TAB: [], NEW_WINDOW: [], forceNewTab: false, forceNewWindow: false, /** @property {string} The Dactyl version string. */ version: null, /** * @property {Object} The map of command-line options. These are * specified in the argument to the host application's -{config.name} * option. E.g. $ firefox -pentadactyl '+u=/tmp/rcfile ++noplugin' * Supported options: * +u=RCFILE Use RCFILE instead of .pentadactylrc. * ++noplugin Don't load plugins. */ commandLineOptions: { /** @property Whether plugin loading should be prevented. */ noPlugins: false, /** @property An RC file to use rather than the default. */ rcFile: null, /** @property An Ex command to run before any initialization is performed. */ preCommands: null, /** @property An Ex command to run after all initialization has been performed. */ postCommands: null }, registerObserver: function (type, callback, weak) { if (!(type in this.observers)) this.observers[type] = []; this.observers[type].push(weak ? Cu.getWeakReference(callback) : { get: function () callback }); }, unregisterObserver: function (type, callback) { if (type in this.observers) this.observers[type] = this.observers[type].filter(function (c) c.get() != callback); }, // TODO: "zoom": if the zoom value of the current buffer changed triggerObserver: function (type) { let args = Array.slice(arguments, 1); if (type in this.observers) this.observers[type] = this.observers[type].filter(function (callback) { if (callback.get()) { callback.get().apply(null, args); return true; } }); }, addUsageCommand: function (params) { commands.add(params.name, params.description, function (args) { let results = array(params.iterate(args)) .sort(function (a, b) String.localeCompare(a.name, b.name)); if (args.length) results = results.filter(function (item) args.map(String.toLowerCase) .every(function (arg) (item.name + item.description).toLowerCase().indexOf(arg) >= 0)); commandline.commandOutput( template.usage(results, params.format)); }, { argCount: "*", completer: function (context, args) { context.keys.text = util.identity; context.keys.description = function () seen[this.text] + " matching items"; let seen = {}; context.completions = array(item.description.toLowerCase().split(/[()\s]+/) for (item in params.iterate(args))) .flatten().filter(function (w) /^\w[\w-_']+$/.test(w)) .map(function (k) { seen[k] = (seen[k] || 0) + 1; return k; }).uniq() }, options: params.options || [] }); if (params.index) this.indices[params.index] = function () { for (let obj in (params.iterateIndex || params.iterate)()) if (obj.helpTag in services["dactyl:"].HELP_TAGS) yield dactyl.generateHelp(obj, null, null, true); } }, /** * Triggers the application bell to notify the user of an error. The * bell may be either audible or visual depending on the value of the * 'visualbell' option. */ beep: function () { if (options["visualbell"]) { let bell = document.getElementById("dactyl-bell"); let strut = document.getElementById("dactyl-bell-strut"); if (!bell) { bell = document.documentElement.insertBefore( util.xmlToDom(