// 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"; function deprecated(reason, fn) { let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments); function deprecatedMethod() { let frame = Components.stack.caller; let obj = this.className || this.constructor.className; if (!set.add(deprecatedMethod.seen, frame.filename)) dactyl.echoerr( (frame.filename || "unknown").replace(/^.*? -> /, "") + ":" + frame.lineNumber + ": " + (obj ? obj + "." : "") + (fn.name || name) + " is deprecated: " + reason); return func.apply(this, arguments); } deprecatedMethod.seen = { "chrome://dactyl/content/javascript.js": true }; return callable(fn) ? deprecatedMethod : Class.Property({ get: function () deprecatedMethod, init: function (prop) { name = prop; } }); } 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.modules = modules; this.observers = {}; this.commands["dactyl.help"] = function (event) { let elem = event.originalTarget; dactyl.help(elem.getAttribute("tag") || elem.textContent); }; }, /** @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"; }), destroy: function () { autocommands.trigger("LeavePre", {}); storage.saveAll(); dactyl.triggerObserver("shutdown", null); util.dump("All dactyl modules destroyed\n"); autocommands.trigger("Leave", {}); }, /** * @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 || [] }); }, /** * 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(