X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fcontent%2Fdactyl.js;h=2933dcd95d2a35e65ef0a1794d507e6b37188c3c;hb=5ebd29f56d17f62011cdd596b1d351947ee534ff;hp=6296b9ac6d26a5db35ccee446d4ecd3c606de94d;hpb=eeed0be1a8abf7e3c97f43b63c1d595e940fef21;p=dactyl.git diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 6296b9a..2933dcd 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -4,7 +4,7 @@ // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. -"use strict"; +/* use strict */ /** @scope modules */ @@ -12,9 +12,6 @@ default xml namespace = XHTML; XML.ignoreWhitespace = false; XML.prettyPrinting = false; -var userContext = { __proto__: modules }; -var _userContext = newContext(userContext); - var EVAL_ERROR = "__dactyl_eval_error"; var EVAL_RESULT = "__dactyl_eval_result"; var EVAL_STRING = "__dactyl_eval_string"; @@ -32,46 +29,77 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { this._observers = {}; util.addObserver(this); - this.commands["dactyl.help"] = function (event) { - let elem = event.originalTarget; - dactyl.help(elem.getAttribute("tag") || elem.textContent); - }; this.commands["dactyl.restart"] = function (event) { dactyl.restart(); }; styles.registerSheet("resource://dactyl-skin/dactyl.css"); + + this.cleanups = []; + this.cleanups.push(overlay.overlayObject(window, { + focusAndSelectUrlBar: function focusAndSelectUrlBar() { + switch (options.get("strictfocus").getKey(document.documentURIObject || util.newURI(document.documentURI), "moderate")) { + case "laissez-faire": + if (!Events.isHidden(window.gURLBar, true)) + return focusAndSelectUrlBar.superapply(this, arguments); + default: + // Evil. Ignore. + } + } + })); }, cleanup: function () { + for (let cleanup in values(this.cleanups)) + cleanup.call(this); + delete window.dactyl; delete window.liberator; + // Prevents box ordering bugs after our stylesheet is removed. + styles.system.add("cleanup-sheet", config.styleableChrome, ); styles.unregisterSheet("resource://dactyl-skin/dactyl.css"); + DOM('#TabsToolbar tab', document).style.display; }, destroy: function () { + this.observe.unregister(); autocommands.trigger("LeavePre", {}); dactyl.triggerObserver("shutdown", null); util.dump("All dactyl modules destroyed\n"); autocommands.trigger("Leave", {}); }, + // initially hide all GUI elements, they are later restored unless the user + // has :set go= or something similar in his config + hideGUI: function () { + let guioptions = config.guioptions; + for (let option in guioptions) { + guioptions[option].forEach(function (elem) { + try { + document.getElementById(elem).collapsed = true; + } + catch (e) {} + }); + } + }, + + observers: { - "dactyl-cleanup": function dactyl_cleanup() { + "dactyl-cleanup": function dactyl_cleanup(subject, reason) { let modules = dactyl.modules; for (let mod in values(modules.moduleList.reverse())) { mod.stale = true; if ("cleanup" in mod) - this.trapErrors("cleanup", mod); + this.trapErrors("cleanup", mod, reason); if ("destroy" in mod) - this.trapErrors("destroy", mod); + this.trapErrors("destroy", mod, reason); } - for (let mod in values(modules.ownPropertyValues.reverse())) - if (mod instanceof Class && "INIT" in mod && "cleanup" in mod.INIT) - this.trapErrors(mod.cleanup, mod, dactyl, modules, window); + modules.moduleManager.initDependencies("cleanup"); for (let name in values(Object.getOwnPropertyNames(modules).reverse())) try { @@ -82,23 +110,17 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, - /** @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"; - }), + signals: { + "io.source": function ioSource(context, file, modTime) { + if (context.INFO) + help.flush("help/plugins.xml", modTime); + } + }, + + profileName: deprecated("config.profileName", { get: function profileName() config.profileName }), /** - * @property {number} The current main mode. + * @property {Modes.Mode} The current main mode. * @see modes#mainModes */ mode: deprecated("modes.main", { @@ -106,7 +128,37 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { set: function mode(val) modes.main = val }), - get menuItems() Dactyl.getMenuItems(), + getMenuItems: function getMenuItems(targetPath) { + function addChildren(node, parent) { + DOM(node).createContents(); + + if (~["menu", "menupopup"].indexOf(node.localName) && node.children.length) + DOM(node).popupshowing({ bubbles: false }); + + for (let [, item] in Iterator(node.childNodes)) { + if (item.childNodes.length == 0 && item.localName == "menuitem" + && !item.hidden + && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME + item.dactylPath = parent + item.getAttribute("label"); + if (!targetPath || targetPath.indexOf(item.dactylPath) == 0) + items.push(item); + } + else { + let path = parent; + if (item.localName == "menu") + path += item.getAttribute("label") + "."; + if (!targetPath || targetPath.indexOf(path) == 0) + addChildren(item, path); + } + } + } + + let items = []; + addChildren(document.getElementById(config.guioptions["m"][1]), ""); + return items; + }, + + get menuItems() this.getMenuItems(), // Global constants CURRENT_TAB: "here", @@ -114,8 +166,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { NEW_BACKGROUND_TAB: "background-tab", NEW_WINDOW: "window", - forceNewTab: false, - forceNewWindow: false, + forceBackground: null, + forceTarget: null, + + get forceOpen() ({ background: this.forceBackground, + target: this.forceTarget }), + set forceOpen(val) { + for (let [k, v] in Iterator({ background: "forceBackground", target: "forceTarget" })) + if (k in val) + this[v] = val[k]; + }, version: deprecated("config.version", { get: function version() config.version }), @@ -144,7 +204,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { registerObserver: function registerObserver(type, callback, weak) { if (!(type in this._observers)) this._observers[type] = []; - this._observers[type].push(weak ? Cu.getWeakReference(callback) : { get: function () callback }); + this._observers[type].push(weak ? util.weakReference(callback) : { get: function () callback }); }, registerObservers: function registerObservers(obj, prop) { @@ -157,7 +217,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { this._observers[type] = this._observers[type].filter(function (c) c.get() != callback); }, - // TODO: "zoom": if the zoom value of the current buffer changed applyTriggerObserver: function triggerObserver(type, args) { if (type in this._observers) this._observers[type] = this._observers[type].filter(function (callback) { @@ -184,14 +243,17 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, addUsageCommand: function (params) { + function keys(item) (item.names || [item.name]).concat(item.description, item.columns || []); + let name = commands.add(params.name, params.description, function (args) { let results = array(params.iterate(args)) .sort(function (a, b) String.localeCompare(a.name, b.name)); - let filters = args.map(function (arg) RegExp("\\b" + util.regexp.escape(arg) + "\\b", "i")); + let filters = args.map(function (arg) let (re = util.regexp.escape(arg)) + util.regexp("\\b" + re + "\\b|(?:^|[()\\s])" + re + "(?:$|[()\\s])", "i")); if (filters.length) - results = results.filter(function (item) filters.every(function (re) re.test(item.name + " " + item.description))); + results = results.filter(function (item) filters.every(function (re) keys(item).some(re.closure.test))); commandline.commandOutput( template.usage(results, params.format)); @@ -200,11 +262,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { argCount: "*", completer: function (context, args) { context.keys.text = util.identity; - context.keys.description = function () seen[this.text] + " matching items"; + context.keys.description = function () seen[this.text] + /*L*/" matching items"; + context.ignoreCase = true; let seen = {}; - context.completions = array(item.description.toLowerCase().split(/[()\s]+/) + context.completions = array(keys(item).join(" ").toLowerCase().split(/[()\s]+/) for (item in params.iterate(args))) - .flatten().filter(function (w) /^\w[\w-_']+$/.test(w)) + .flatten() .map(function (k) { seen[k] = (seen[k] || 0) + 1; return k; @@ -218,10 +281,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { let results = array((params.iterateIndex || params.iterate).call(params, commands.get(name).newArgs())) .array.sort(function (a, b) String.localeCompare(a.name, b.name)); - let tags = services["dactyl:"].HELP_TAGS; + let haveTag = Set.has(help.tags); for (let obj in values(results)) { let res = dactyl.generateHelp(obj, null, null, true); - if (!set.has(tags, obj.helpTag)) + if (!haveTag(obj.helpTag)) res[1].@tag = obj.helpTag; yield res; @@ -243,7 +306,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }; XML.ignoreWhitespace = true; if (!elems.bell) - util.overlayWindow(window, { + overlay.overlayWindow(window, { objects: elems, prepend: <> @@ -275,16 +338,20 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * This is same as Firefox's readFromClipboard function, but is needed for * apps like Thunderbird which do not provide it. * + * @param {string} which Which clipboard to write to. Either + * "global" or "selection". If not provided, both clipboards are + * updated. + * @optional * @returns {string} */ - clipboardRead: function clipboardRead(getClipboard) { + clipboardRead: function clipboardRead(which) { try { - const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); - const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable); + const { clipboard } = services; + let transferable = services.Transferable(); transferable.addDataFlavor("text/unicode"); - let source = clipboard[getClipboard || !clipboard.supportsSelectionClipboard() ? + let source = clipboard[which == "global" || !clipboard.supportsSelectionClipboard() ? "kGlobalClipboard" : "kSelectionClipboard"]; clipboard.getData(transferable, source); @@ -303,15 +370,22 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * Copies a string to the system clipboard. If *verbose* is specified the * copied string is also echoed to the command line. * - * @param {string} str - * @param {boolean} verbose + * @param {string} str The string to write. + * @param {boolean} verbose If true, the user is notified of the copied data. + * @param {string} which Which clipboard to write to. Either + * "global" or "selection". If not provided, both clipboards are + * updated. + * @optional */ - clipboardWrite: function clipboardWrite(str, verbose) { - const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboardHelper.copyString(str); + clipboardWrite: function clipboardWrite(str, verbose, which) { + if (which == null || which == "selection" && !services.clipboard.supportsSelectionClipboard()) + services.clipboardHelper.copyString(str); + else + services.clipboardHelper.copyStringToClipboard(str, + services.clipboard["k" + util.capitalize(which) + "Clipboard"]); if (verbose) { - let message = { message: "Yanked " + str }; + let message = { message: _("dactyl.yank", str) }; try { message.domains = [util.newURI(str).host]; } @@ -346,8 +420,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { echoerr: function echoerr(str, flags) { flags |= commandline.APPEND_TO_MESSAGES; - if (isinstance(str, ["Error", "Exception"])) + if (isinstance(str, ["DOMException", "Error", "Exception", ErrorBase]) + || isinstance(str, ["XPCWrappedNative_NoHelper"]) && /^\[Exception/.test(str)) dactyl.reportError(str); + if (isObject(str) && "echoerr" in str) str = str.echoerr; else if (isinstance(str, ["Error", FailedAssertion]) && str.fileName) @@ -405,43 +481,50 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { userEval: function (str, context, fileName, lineNumber) { let ctxt; - if (jsmodules.__proto__ != window) + if (jsmodules.__proto__ != window && jsmodules.__proto__ != XPCNativeWrapper(window) && + jsmodules.isPrototypeOf(context)) str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }"; let info = contexts.context; if (fileName == null) - if (info && info.file[0] !== "[") + if (info) ({ file: fileName, line: lineNumber, context: ctxt }) = info; - if (!context && fileName && fileName[0] !== "[") - context = _userContext || ctxt; + if (fileName && fileName[0] == "[") + fileName = "dactyl://command-line/"; + else if (!context) + context = ctxt || _userContext; if (isinstance(context, ["Sandbox"])) return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber); - else - try { - if (!context) - context = userContext || ctxt; - - context[EVAL_ERROR] = null; - context[EVAL_STRING] = str; - context[EVAL_RESULT] = null; - this.loadScript("resource://dactyl-content/eval.js", context); - if (context[EVAL_ERROR]) { - try { - context[EVAL_ERROR].fileName = info.file; - context[EVAL_ERROR].lineNumber += info.line; - } - catch (e) {} - throw context[EVAL_ERROR]; + + if (!context) + context = userContext || ctxt; + + if (services.has("dactyl") && services.dactyl.evalInContext) + return services.dactyl.evalInContext(str, context, fileName, lineNumber); + + try { + context[EVAL_ERROR] = null; + context[EVAL_STRING] = str; + context[EVAL_RESULT] = null; + + this.loadScript("resource://dactyl-content/eval.js", context); + if (context[EVAL_ERROR]) { + try { + context[EVAL_ERROR].fileName = info.file; + context[EVAL_ERROR].lineNumber += info.line; } - return context[EVAL_RESULT]; - } - finally { - delete context[EVAL_ERROR]; - delete context[EVAL_RESULT]; - delete context[EVAL_STRING]; + catch (e) {} + throw context[EVAL_ERROR]; } + return context[EVAL_RESULT]; + } + finally { + delete context[EVAL_ERROR]; + delete context[EVAL_RESULT]; + delete context[EVAL_STRING]; + } }, /** @@ -483,19 +566,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, focus: function focus(elem, flags) { - flags = flags || services.focus.FLAG_BYMOUSE; - try { - if (elem instanceof Document) - elem = elem.defaultView; - if (elem instanceof Element) - services.focus.setFocus(elem, flags); - else if (elem instanceof Window) - services.focus.focusedWindow = elem; - } - catch (e) { - util.dump(elem); - util.reportError(e); - } + DOM(elem).focus(flags); }, /** @@ -559,34 +630,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @param {string} feature The feature name. * @returns {boolean} */ - has: function (feature) set.has(config.features, feature), - - /** - * Returns the URL of the specified help *topic* if it exists. - * - * @param {string} topic The help topic to look up. - * @param {boolean} consolidated Whether to search the consolidated help page. - * @returns {string} - */ - findHelp: function (topic, consolidated) { - if (!consolidated && topic in services["dactyl:"].FILE_MAP) - return topic; - let items = completion._runCompleter("help", topic, null, !!consolidated).items; - let partialMatch = null; - - function format(item) item.description + "#" + encodeURIComponent(item.text); - - for (let [i, item] in Iterator(items)) { - if (item.text == topic) - return format(item); - else if (!partialMatch && topic) - partialMatch = item; - } - - if (partialMatch) - return format(partialMatch); - return null; - }, + has: function (feature) Set.has(config.features, feature), /** * @private @@ -603,221 +647,18 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, + help: deprecated("help.help", { get: function help() modules.help.closure.help }), + findHelp: deprecated("help.findHelp", { get: function findHelp() help.closure.findHelp }), + /** * @private * Initialize the help system. */ - initHelp: function (force) { - if (force || !this.helpInitialized) { - if ("noscriptOverlay" in window) { - noscriptOverlay.safeAllow("chrome-data:", true, false); - noscriptOverlay.safeAllow("dactyl:", true, false); - } - - // Find help and overlay files with the given name. - let findHelpFile = function findHelpFile(file) { - let result = []; - for (let [, namespace] in Iterator(namespaces)) { - let url = ["dactyl://", namespace, "/", file, ".xml"].join(""); - let res = util.httpGet(url); - if (res) { - if (res.responseXML.documentElement.localName == "document") - fileMap[file] = url; - if (res.responseXML.documentElement.localName == "overlay") - overlayMap[file] = url; - result.push(res.responseXML); - } - } - return result; - }; - // Find the tags in the document. - let addTags = function addTags(file, doc) { - for (let elem in util.evaluateXPath("//@tag|//dactyl:tags/text()|//dactyl:tag/text()", doc)) - for (let tag in values((elem.value || elem.textContent).split(/\s+/))) - tagMap[tag] = file; - }; - - let namespaces = ["locale-local", "locale"]; - services["dactyl:"].init({}); - - let tagMap = services["dactyl:"].HELP_TAGS; - let fileMap = services["dactyl:"].FILE_MAP; - let overlayMap = services["dactyl:"].OVERLAY_MAP; - - // Scrape the list of help files from all.xml - // Manually process main and overlay files, since XSLTProcessor and - // XMLHttpRequest don't allow access to chrome documents. - tagMap["all"] = tagMap["all.xml"] = "all"; - tagMap["versions"] = tagMap["versions.xml"] = "versions"; - let files = findHelpFile("all").map(function (doc) - [f.value for (f in util.evaluateXPath("//dactyl:include/@href", doc))]); - - // Scrape the tags from the rest of the help files. - array.flatten(files).forEach(function (file) { - tagMap[file + ".xml"] = file; - findHelpFile(file).forEach(function (doc) { - addTags(file, doc); - }); - }); - - // Process plugin help entries. - XML.ignoreWhiteSpace = XML.prettyPrinting = false; - - let body = XML(); - for (let [, context] in Iterator(plugins.contexts)) - if (context && context.INFO instanceof XML) { - let info = context.INFO; - if (info.*.@lang.length()) { - let lang = config.bestLocale(String(a) for each (a in info.*.@lang)); - - info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang); - - for each (let elem in info.NS::info) - for each (let attr in ["@name", "@summary", "@href"]) - if (elem[attr].length()) - info[attr] = elem[attr]; - } - body +=

{context.INFO.@summary}

+ - context.INFO; - } + initHelp: function initHelp() { + if ("noscriptOverlay" in window) + noscriptOverlay.safeAllow("dactyl:", true, false); - let help = - '\n' + - '\n' + - '\n' + - unescape(encodeURI( // UTF-8 handling hack. - -

Using Plugins

- - - {body} -
.toXMLString())); - fileMap["plugins"] = function () ['text/xml;charset=UTF-8', help]; - - fileMap["versions"] = function () { - let NEWS = util.httpGet(config.addon.getResourceURI("NEWS").spec, - { mimeType: "text/plain;charset=UTF-8" }) - .responseText; - - let re = util.regexp( \s* # .*\n) - - | ^ (?P \s*) - (?P [-•*+]) \ // - (?P .*\n - (?: \2\ \ .*\n | \s*\n)* ) - - | (?P - (?: ^ [^\S\n]* - (?:[^-•*+\s] | [-•*+]\S) - .*\n - )+ - ) - - | (?: ^ [^\S\n]* \n) + - ]]>, "gmxy"); - - let betas = util.regexp(/\[(b\d)\]/, "gx"); - - let beta = array(betas.iterate(NEWS)) - .map(function (m) m[1]).uniq().slice(-1)[0]; - - default xml namespace = NS; - function rec(text, level, li) { - let res = <>; - let list, space, i = 0; - - for (let match in re.iterate(text)) { - if (match.comment) - continue; - else if (match.char) { - if (!list) - res += list =
    ; - let li =
  • ; - li.* += rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1, li) - list.* += li; - } - else if (match.par) { - let [, par, tags] = /([^]*?)\s*((?:\[[^\]]+\])*)\n*$/.exec(match.par); - let t = tags; - tags = array(betas.iterate(tags)).map(function (m) m[1]); - - let group = !tags.length ? "" : - !tags.some(function (t) t == beta) ? "HelpNewsOld" : "HelpNewsNew"; - if (i === 0 && li) { - li.@highlight = group; - group = ""; - } - - list = null; - if (level == 0 && /^.*:\n$/.test(match.par)) - res +=

    {template.linkifyHelp(par.slice(0, -1), true)}

    ; - else { - let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(par); - res +=

    { - !tags.length ? "" : - {tags.join(" ")} - }{ - a ? {a} : "" - }{ - template.linkifyHelp(b, true) - }

    ; - } - } - i++; - } - for each (let attr in res..@highlight) { - attr.parent().@NS::highlight = attr; - delete attr.parent().@highlight; - } - return res; - } - - let body = rec(NEWS, 0); - for each (let li in body..li) { - let list = li..li.(@NS::highlight == "HelpNewsOld"); - if (list.length() && list.length() == li..li.(@NS::highlight != "").length()) { - for each (let li in list) - li.@NS::highlight = ""; - li.@NS::highlight = "HelpNewsOld"; - } - } - - XML.prettyPrinting = XML.ignoreWhitespace = false; - return ["application/xml", - '\n' + - '\n' + - '\n' + - unescape(encodeURI( // UTF-8 handling hack. - -

    {config.appName} Versions

    - - - {body} -
    .toXMLString())) - ]; - } - addTags("versions", util.httpGet("dactyl://help/versions").responseXML); - addTags("plugins", util.httpGet("dactyl://help/plugins").responseXML); - - default xml namespace = NS; - - overlayMap["index"] = ['text/xml;charset=UTF-8', - '\n' + - '\n' + - unescape(encodeURI( // UTF-8 handling hack. - template.map(dactyl.indices, function ([name, iter]) -
    { - template.map(iter(), util.identity) - }
    , <>{"\n\n"}))) + - '\n
    ']; - - addTags("index", util.httpGet("dactyl://help-overlay/index").responseXML); - - this.helpInitialized = true; - } + help.initialize(); }, stringifyXML: function (xml) { @@ -826,116 +667,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return UTF8(xml.toXMLString()); }, - exportHelp: JavaScript.setCompleter(function (path) { - const FILE = io.File(path); - const PATH = FILE.leafName.replace(/\..*/, "") + "/"; - const TIME = Date.now(); - - if (!FILE.exists() && (/\/$/.test(path) && !/\./.test(FILE.leafName))) - FILE.create(FILE.DIRECTORY_TYPE, octal(755)); - - dactyl.initHelp(); - if (FILE.isDirectory()) { - var addDataEntry = function addDataEntry(file, data) FILE.child(file).write(data); - var addURIEntry = function addURIEntry(file, uri) addDataEntry(file, util.httpGet(uri).responseText); - } - else { - var zip = services.ZipWriter(); - zip.open(FILE, File.MODE_CREATE | File.MODE_WRONLY | File.MODE_TRUNCATE); - - addURIEntry = function addURIEntry(file, uri) - zip.addEntryChannel(PATH + file, TIME, 9, - services.io.newChannel(uri, null, null), false); - addDataEntry = function addDataEntry(file, data) // Unideal to an extreme. - addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data)); - } - - let empty = set("area base basefont br col frame hr img input isindex link meta param" - .split(" ")); - function fix(node) { - switch(node.nodeType) { - case Node.ELEMENT_NODE: - if (isinstance(node, [HTMLBaseElement])) - return; - - data.push("<"); data.push(node.localName); - if (node instanceof HTMLHtmlElement) - data.push(" xmlns=" + XHTML.uri.quote()); - - for (let { name, value } in array.iterValues(node.attributes)) { - if (name == "dactyl:highlight") { - set.add(styles, value); - name = "class"; - value = "hl-" + value; - } - if (name == "href") { - value = node.href; - if (value.indexOf("dactyl://help-tag/") == 0) { - let uri = services.io.newChannel(value, null, null).originalURI; - value = uri.spec == value ? "javascript:;" : uri.path.substr(1); - } - if (!/^#|[\/](#|$)|^[a-z]+:/.test(value)) - value = value.replace(/(#|$)/, ".xhtml$1"); - } - if (name == "src" && value.indexOf(":") > 0) { - chromeFiles[value] = value.replace(/.*\//, ""); - value = value.replace(/.*\//, ""); - } - data.push(" "); - data.push(name); - data.push('="'); - data.push(<>{value}.toXMLString()); - data.push('"'); - } - if (node.localName in empty) - data.push(" />"); - else { - data.push(">"); - if (node instanceof HTMLHeadElement) - data.push(.toXMLString()); - Array.map(node.childNodes, fix); - data.push(""); - } - break; - case Node.TEXT_NODE: - data.push(<>{node.textContent}.toXMLString()); - } - } - - let chromeFiles = {}; - let styles = {}; - for (let [file, ] in Iterator(services["dactyl:"].FILE_MAP)) { - dactyl.open("dactyl://help/" + file); - dactyl.modules.events.waitForPageLoad(); - let data = [ - '\n', - '\n' - ]; - fix(content.document.documentElement); - addDataEntry(file + ".xhtml", data.join("")); - } - - let data = [h for (h in highlight) if (set.has(styles, h.class) || /^Help/.test(h.class))] - .map(function (h) h.selector - .replace(/^\[.*?=(.*?)\]/, ".hl-$1") - .replace(/html\|/g, "") + "\t" + "{" + h.cssText + "}") - .join("\n"); - addDataEntry("help.css", data.replace(/chrome:[^ ")]+\//g, "")); - - addDataEntry("tag-map.json", JSON.stringify(services["dactyl:"].HELP_TAGS)); - - let m, re = /(chrome:[^ ");]+\/)([^ ");]+)/g; - while ((m = re.exec(data))) - chromeFiles[m[0]] = m[2]; - - for (let [uri, leaf] in Iterator(chromeFiles)) - addURIEntry(leaf, uri); - - if (zip) - zip.close(); - }, [function (context, args) completion.file(context)]), - /** * Generates a help entry and returns it as a string. * @@ -954,10 +685,20 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (obj instanceof Command) { link = function (cmd) {cmd}; args = obj.parseArgs("", CompletionContext(str || "")); - spec = function (cmd) cmd + (obj.bang ? ! : <>); + tag = function (cmd) <>:{cmd}; + spec = function (cmd) <>{ + obj.count ? count : <> + }{ + cmd + }{ + obj.bang ? ! : <> + }; } else if (obj instanceof Map) { spec = function (map) obj.count ? <>count{map} : <>{map}; + tag = function (map) <>{ + let (c = obj.modes[0].char) c ? c + "_" : "" + }{ map }; link = function (map) { let [, mode, name, extra] = /^(?:(.)_)?(?:<([^>]+)>)?(.*)$/.exec(map); let k = {extra}; @@ -969,7 +710,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }; } else if (obj instanceof Option) { + spec = function () template.map(obj.names, tag, " "); + tag = function (name) <>'{name}'; link = function (opt, name) {name}; + args = { value: "", values: [] }; } XML.prettyPrinting = false; @@ -981,7 +725,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { ; let res = -
    {link(obj.helpTag || obj.name, obj.name)}
    { +
    {link(obj.helpTag || tag(obj.name), obj.name)}
    { template.linkifyHelp(obj.description ? obj.description.replace(/\.$/, "") : "", true) }
    ; if (specOnly) @@ -990,10 +734,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { res.* += <> {template.map(obj.names.slice().reverse(), tag, " ")} - { - spec(template.highlightRegexp((obj.specs || obj.names)[0], - /\[(.*?)\]/g, - function (m, n0) {n0})) + {let (name = (obj.specs || obj.names)[0]) + spec(template.highlightRegexp(tag(name), + /\[(.*?)\]/g, + function (m, n0) {n0}), + name) }{ !obj.type ? "" : <> {obj.type} @@ -1001,7 +746,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { { obj.description ? br +

    {template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)}

    : "" }{ extraHelp ? br + extraHelp : "" }{ - !(extraHelp || obj.description) ? br +

    Sorry, no help available.

    : "" } + !(extraHelp || obj.description) ? br +

    Sorry, no help available.

    : "" }
    ; @@ -1014,7 +759,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } if (obj.completer) - add(completion._runCompleter(obj.completer, "", null, args).items + add(completion._runCompleter(obj.closure.completer, "", null, args).items .map(function (i) [i.text, i.description])); if (obj.options && obj.options.some(function (o) o.description)) @@ -1034,37 +779,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { .replace(/^\s*\n|\n\s*$/g, "") + "\n"; }, - /** - * Opens the help page containing the specified *topic* if it exists. - * - * @param {string} topic The help topic to open. - * @param {boolean} consolidated Whether to use the consolidated help page. - */ - help: function (topic, consolidated) { - dactyl.initHelp(); - if (!topic) { - let helpFile = consolidated ? "all" : options["helpfile"]; - - if (helpFile in services["dactyl:"].FILE_MAP) - dactyl.open("dactyl://help/" + helpFile, { from: "help" }); - else - dactyl.echomsg(_("help.noFile", helpFile.quote())); - return; - } - - let page = this.findHelp(topic, consolidated); - dactyl.assert(page != null, _("help.noTopic", topic)); - - dactyl.open("dactyl://help/" + page, { from: "help" }); - }, - /** * The map of global variables. * * These are set and accessed with the "g:" prefix. */ _globalVariables: {}, - globalVariables: deprecated("the options system", { + globalVariables: deprecated(_("deprecated.for.theOptionsSystem"), { get: function globalVariables() this._globalVariables }), @@ -1076,13 +797,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { let loadplugins = options.get("loadplugins"); if (args) - loadplugins = { __proto__: loadplugins, value: args.map(Option.parseRegexp) } + loadplugins = { __proto__: loadplugins, value: args.map(Option.parseRegexp) }; dir.readDirectory(true).forEach(function (file) { - if (file.isFile() && loadplugins.getKey(file.path) && !(!force && file.path in dactyl.pluginFiles)) { + if (file.leafName[0] == ".") + ; + else if (file.isFile() && loadplugins.getKey(file.path) + && !(!force && file.path in dactyl.pluginFiles && dactyl.pluginFiles[file.path] >= file.lastModifiedTime)) { try { io.source(file.path); - dactyl.pluginFiles[file.path] = true; + dactyl.pluginFiles[file.path] = file.lastModifiedTime; } catch (e) { dactyl.reportError(e); @@ -1123,7 +847,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @param {number} level The logging level 0 - 15. */ log: function (msg, level) { - let verbose = localPrefs.get("loglevel", 0); + let verbose = config.prefs.get("loglevel", 0); if (!level || level <= verbose) { if (isObject(msg) && !isinstance(msg, _)) @@ -1133,35 +857,45 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, - onClick: function onClick(event) { - if (event.originalTarget instanceof Element) { - let command = event.originalTarget.getAttributeNS(NS, "command"); - if (command && event.button == 0) { - event.preventDefault(); + events: { + click: function onClick(event) { + let elem = event.originalTarget; - if (dactyl.commands[command]) - dactyl.withSavedValues(["forceNewTab"], function () { - dactyl.forceNewTab = event.ctrlKey || event.shiftKey || event.button == 1; - dactyl.commands[command](event); - }); + if (elem instanceof Element && services.security.isSystemPrincipal(elem.nodePrincipal)) { + let command = elem.getAttributeNS(NS, "command"); + if (command && event.button == 0) { + event.preventDefault(); + + if (dactyl.commands[command]) + dactyl.withSavedValues(["forceTarget"], function () { + if (event.ctrlKey || event.shiftKey || event.button == 1) + dactyl.forceTarget = dactyl.NEW_TAB; + dactyl.commands[command](event); + }); + } } - } - }, + }, - onExecute: function onExecute(event) { - let cmd = event.originalTarget.getAttribute("dactyl-execute"); - commands.execute(cmd, null, false, null, - { file: "[Command Line]", line: 1 }); + "dactyl.execute": function onExecute(event) { + let cmd = event.originalTarget.getAttribute("dactyl-execute"); + commands.execute(cmd, null, false, null, + { file: /*L*/"[Command Line]", line: 1 }); + } }, /** * Opens one or more URLs. Returns true when load was initiated, or * false on error. * - * @param {string|Array} urls A representation of the URLs to open. May be - * either a string, which will be passed to - * {@see Dactyl#parseURLs}, or an array in the same format as - * would be returned by the same. + * @param {string|Array} urls A representation of the URLs to open. + * A string will be passed to {@link Dactyl#parseURLs}. An array may + * contain elements of the following forms: + * + * • {string} A URL to open. + * • {[string, {string|Array}]} Pair of a URL and POST data. + * • {object} Object compatible with those returned + * by {@link DOM#formData}. + * * @param {object} params A set of parameters specifying how to open the * URLs. The following properties are recognized: * @@ -1187,7 +921,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { urls = dactyl.parseURLs(urls); if (urls.length > prefs.get("browser.tabs.maxOpenBeforeWarn", 20) && !force) - return commandline.input("This will open " + urls.length + " new tabs. Would you like to continue? (yes/[no]) ", + return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ", function (resp) { if (resp && resp.match(/^y(es)?$/i)) dactyl.open(urls, params, true); @@ -1202,8 +936,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { flags |= params[opt] && Ci.nsIWebNavigation["LOAD_FLAGS_" + flag]; let where = params.where || dactyl.CURRENT_TAB; - let background = ("background" in params) ? params.background - : params.where == dactyl.NEW_BACKGROUND_TAB; + let background = dactyl.forceBackground != null ? dactyl.forceBackground : + ("background" in params) ? params.background + : params.where == dactyl.NEW_BACKGROUND_TAB; if (params.from && dactyl.has("tabs")) { if (!params.where && options.get("newtab").has(params.from)) @@ -1215,21 +950,35 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return; let browser = config.tabbrowser; - function open(urls, where) { + function open(loc, where) { try { - let url = Array.concat(urls)[0]; - let postdata = Array.concat(urls)[1]; + if (isArray(loc)) + loc = { url: loc[0], postData: loc[1] }; + else if (isString(loc)) + loc = { url: loc }; + else + loc = Object.create(loc); + + if (isString(loc.postData)) + loc.postData = ["application/x-www-form-urlencoded", loc.postData]; + + if (isArray(loc.postData)) { + let stream = services.MIMEStream(services.StringStream(loc.postData[1])); + stream.addHeader("Content-Type", loc.postData[0]); + stream.addContentLength = true; + loc.postData = stream; + } // decide where to load the first url switch (where) { case dactyl.NEW_TAB: if (!dactyl.has("tabs")) - return open(urls, dactyl.NEW_WINDOW); + return open(loc, dactyl.NEW_WINDOW); return prefs.withContext(function () { prefs.set("browser.tabs.loadInBackground", true); - return browser.loadOneTab(url, null, null, postdata, background).linkedBrowser.contentDocument; + return browser.loadOneTab(loc.url, null, null, loc.postData, background).linkedBrowser.contentDocument; }); case dactyl.NEW_WINDOW: @@ -1238,7 +987,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { browser = win.dactyl && win.dactyl.modules.config.tabbrowser || win.getBrowser(); // FALLTHROUGH case dactyl.CURRENT_TAB: - browser.loadURIWithFlags(url, flags, null, null, postdata); + browser.loadURIWithFlags(loc.url, flags, null, null, loc.postData); return browser.contentWindow; } } @@ -1248,10 +997,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { // any genuine errors go unreported. } - if (dactyl.forceNewTab) - where = dactyl.NEW_TAB; - else if (dactyl.forceNewWindow) - where = dactyl.NEW_WINDOW; + if (dactyl.forceTarget) + where = dactyl.forceTarget; else if (!where) where = dactyl.CURRENT_TAB; @@ -1270,7 +1017,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * ['www.google.com/search?q=bla', 'www.osnews.com'] * * @param {string} str - * @returns {string[]} + * @returns {[string]} */ parseURLs: function parseURLs(str) { let urls; @@ -1283,7 +1030,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return urls.map(function (url) { url = url.trim(); - if (/^(\.{0,2}|~)(\/|$)/.test(url)) { + if (/^(\.{0,2}|~)(\/|$)/.test(url) || config.OS.isWindows && /^[a-z]:/i.test(url)) { try { // Try to find a matching file. let file = io.File(url); @@ -1295,8 +1042,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { // If it starts with a valid protocol, pass it through. let proto = /^([-\w]+):/.exec(url); - if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc) - return url.replace(/\s+/g, ""); + if (proto && services.PROTOCOL + proto[1] in Cc) + return url; // Check for a matching search keyword. let searchURL = this.has("bookmarks") && bookmarks.getSearchURL(url, false); @@ -1313,7 +1060,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, this); }, stringToURLArray: deprecated("dactyl.parseURLs", "parseURLs"), - urlish: Class.memoize(function () util.regexp(+ (:\d+)? (/ .*) | + (:\d+) | @@ -1375,10 +1122,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { /** * Restart the host application. */ - restart: function () { + restart: function (args) { if (!this.confirmQuit()) return; + config.prefs.set("commandline-args", args); + services.appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); }, @@ -1397,7 +1146,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return func.apply(self || this, Array.slice(arguments, 2)); } catch (e) { - dactyl.reportError(e, true); + try { + dactyl.reportError(e, true); + } + catch (e) { + util.reportError(e); + } return e; } }, @@ -1412,7 +1166,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (error instanceof FailedAssertion && error.noTrace || error.message === "Interrupted") { let context = contexts.context; let prefix = context ? context.file + ":" + context.line + ": " : ""; - if (error.message && error.message.indexOf(prefix) !== 0) + if (error.message && error.message.indexOf(prefix) !== 0 && + prefix != "[Command Line]:1: ") error.message = prefix + error.message; if (error.message) @@ -1424,8 +1179,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { util.reportError(error); return; } + if (error.result == Cr.NS_BINDING_ABORTED) return; + if (echo) dactyl.echoerr(error, commandline.FORCE_SINGLELINE); else @@ -1450,10 +1207,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return []; } }, - wrapCallback: function (callback, self) { self = self || this; - let save = ["forceNewTab", "forceNewWindow"]; + let save = ["forceOpen"]; let saved = save.map(function (p) dactyl[p]); return function wrappedCallback() { let args = arguments; @@ -1470,52 +1226,98 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, /** - * @property {Window[]} Returns an array of all the host application's + * @property {[Window]} Returns an array of all the host application's * open windows. */ - get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser")))], + get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser"))) if (win.dactyl)], }, { - // initially hide all GUI elements, they are later restored unless the user - // has :set go= or something similar in his config - hideGUI: function () { - let guioptions = config.guioptions; - for (let option in guioptions) { - guioptions[option].forEach(function (elem) { + toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true" +}, { + cache: function () { + cache.register("help/plugins.xml", function () { + // Process plugin help entries. + XML.ignoreWhiteSpace = XML.prettyPrinting = false; + + let body = XML(); + for (let [, context] in Iterator(plugins.contexts)) try { - document.getElementById(elem).collapsed = true; - } - catch (e) {} - }); - } - }, + let info = contexts.getDocs(context); + if (info instanceof XML) { + if (info.*.@lang.length()) { + let lang = config.bestLocale(String(a) for each (a in info.*.@lang)); - // TODO: move this - getMenuItems: function () { - function addChildren(node, parent) { - for (let [, item] in Iterator(node.childNodes)) { - if (item.childNodes.length == 0 && item.localName == "menuitem" - && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME - item.fullMenuPath = parent + item.getAttribute("label"); - items.push(item); + info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang); + + for each (let elem in info.NS::info) + for (let attr in values(["@name", "@summary", "@href"])) + if (elem[attr].length()) + info[attr] = elem[attr]; + } + body +=

    {info.@summary}

    + + info; + } } - else { - let path = parent; - if (item.localName == "menu") - path += item.getAttribute("label") + "."; - addChildren(item, path); + catch (e) { + util.reportError(e); } - } - } - let items = []; - addChildren(document.getElementById(config.guioptions["m"][1]), ""); - return items; - } -}, { + return '\n' + + '\n' + + '\n' + + +

    {_("help.title.Using Plugins")}

    + + + {body} +
    .toXMLString(); + }); + + cache.register("help/index.xml", function () { + default xml namespace = NS; + + return '\n' + + { + template.map(dactyl.indices, function ([name, iter]) +
    { + template.map(iter(), util.identity) + }
    , <>{"\n\n"}) + }
    ; + }); + + cache.register("help/gui.xml", function () { + default xml namespace = NS; + + return '\n' + + +
    { + template.map(config.dialogs, function ([name, val]) + (!val[2] || val[2]()) + ? <>
    {name}
    {val[0]}
    + : undefined, + <>{"\n"}) + }
    +
    ; + }); + + cache.register("help/privacy.xml", function () { + default xml namespace = NS; + + return '\n' + + +
    { + template.map(options.get("sanitizeitems").values + .sort(function (a, b) String.localeCompare(a.name, b.name)), + function ({ name, description }) + <>
    {name}
    {template.linkifyHelp(description, true)}
    , + <>{"\n"}) + }
    +
    ; + }); + }, events: function () { - events.listen(window, "click", dactyl.closure.onClick, true); - events.listen(window, "dactyl.execute", dactyl.closure.onExecute, true); + events.listen(window, dactyl, "events", true); }, // Only general options are added here, which are valid for all Dactyl extensions options: function () { @@ -1542,7 +1344,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { M: ["Always show messages outside of the status line"] }, setter: function (opts) { - if (loaded.commandline) + if (loaded.commandline || ~opts.indexOf("c")) commandline.widgets.updateVisibility(); } }, @@ -1578,7 +1380,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { true); prefs.safeSet("layout.scrollbar.side", opts.indexOf("l") >= 0 ? 3 : 2, - "See 'guioptions' scrollbar flags."); + _("option.guioptions.safeSet")); }, validator: function (opts) Option.validIf(!(opts.indexOf("l") >= 0 && opts.indexOf("r") >= 0), UTF8("Only one of ‘l’ or ‘r’ allowed")) @@ -1611,12 +1413,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { options.add(["guioptions", "go"], "Show or hide certain GUI elements like the menu or toolbar", - "charlist", config.defaults.guioptions || "", { + "charlist", "", { // FIXME: cleanup cleanupValue: config.cleanups.guioptions || - "r" + [k for ([k, v] in iter(groups[1].opts)) - if (!document.getElementById(v[1][0]).collapsed)].join(""), + "rb" + [k for ([k, v] in iter(groups[1].opts)) + if (!Dactyl.toolbarHidden(document.getElementById(v[1][0])))].join(""), values: array(groups).map(function (g) [[k, v[0]] for ([k, v] in Iterator(g.opts))]).flatten(), @@ -1640,12 +1442,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { options.add(["titlestring"], "The string shown at the end of the window title", - "string", config.defaults.titlestring || config.host, + "string", config.host, { setter: function (value) { let win = document.documentElement; function updateTitle(old, current) { - document.title = document.title.replace(RegExp("(.*)" + util.regexp.escape(old)), "$1" + current); + if (config.browser.updateTitlebar) + config.browser.updateTitlebar(); + else + document.title = document.title.replace(RegExp("(.*)" + util.regexp.escape(old)), "$1" + current); } if (services.has("privateBrowsing")) { @@ -1670,7 +1475,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { options.add(["urlseparator", "urlsep", "us"], "The regular expression used to separate multiple URLs in :open and friends", - "string", "\\|", + "string", " \\| ", { validator: function (value) RegExp(value) }); options.add(["verbose", "vbs"], @@ -1684,21 +1489,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { { setter: function (value) { prefs.safeSet("accessibility.typeaheadfind.enablesound", !value, - "See 'visualbell' option"); + _("option.safeSet", "visualbell")); return value; } }); }, mappings: function () { - mappings.add([modes.MAIN], [""], - "Open the introductory help page", - function () { dactyl.help(); }); - - mappings.add([modes.MAIN], [""], - "Open the single, consolidated help page", - function () { ex.helpall(); }); - if (dactyl.has("session")) mappings.add([modes.NORMAL], ["ZQ"], "Quit and don't save the session", @@ -1727,7 +1524,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, { argCount: "1", - bang: true, completer: function (context) { context.ignoreCase = true; completion.dialog(context); @@ -1738,14 +1534,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { "Execute the specified menu item from the command line", function (args) { let arg = args[0] || ""; - let items = Dactyl.getMenuItems(); + let items = dactyl.getMenuItems(arg); - dactyl.assert(items.some(function (i) i.fullMenuPath == arg), + dactyl.assert(items.some(function (i) i.dactylPath == arg), _("emenu.notFound", arg)); for (let [, item] in Iterator(items)) { - if (item.fullMenuPath == arg) + if (item.dactylPath == arg) { + dactyl.assert(!item.disabled, _("error.disabled", item.dactylPath)); item.doCommand(); + } } }, { argCount: "1", @@ -1768,32 +1566,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { literal: 0 }); - [ - { - name: "h[elp]", - description: "Open the introductory help page" - }, { - name: "helpa[ll]", - description: "Open the single consolidated help page" - } - ].forEach(function (command) { - let consolidated = command.name == "helpa[ll]"; - - commands.add([command.name], - command.description, - function (args) { - dactyl.assert(!args.bang, _("help.dontPanic")); - dactyl.help(args.literalArg, consolidated); - }, { - argCount: "?", - bang: true, - completer: function (context) completion.help(context, consolidated), - literal: 0 - }); - }); - commands.add(["loadplugins", "lpl"], - "Load all plugins immediately", + "Load all or matching plugins", function (args) { dactyl.loadPlugins(args.length ? args : null, args.bang); }, @@ -1802,7 +1576,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { bang: true, keepQuotes: true, serialGroup: 10, - serialize: function () [ + serialize: function () [ { command: this.name, literalArg: options["loadplugins"].join(" ") @@ -1819,6 +1593,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { literal: 0 }); + commands.add(["exit", "x"], + "Quit " + config.appName, + function (args) { + dactyl.quit(false, args.bang); + }, { + argCount: "0", + bang: true + }); + commands.add(["q[uit]"], dactyl.has("tabs") ? "Quit current tab" : "Quit application", function (args) { @@ -1833,53 +1616,72 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { bang: true }); + let startupOptions = [ + { + names: ["+u"], + description: "The initialization file to execute at startup", + type: CommandOption.STRING + }, + { + names: ["++noplugin"], + description: "Do not automatically load plugins" + }, + { + names: ["++cmd"], + description: "Ex commands to execute prior to initialization", + type: CommandOption.STRING, + multiple: true + }, + { + names: ["+c"], + description: "Ex commands to execute after initialization", + type: CommandOption.STRING, + multiple: true + }, + { + names: ["+purgecaches"], + description: "Purge " + config.appName + " caches at startup", + type: CommandOption.NOARG + } + ]; + commands.add(["reh[ash]"], "Reload the " + config.appName + " add-on", function (args) { if (args.trailing) - JSMLoader.rehashCmd = args.trailing; // Hack. + storage.session.rehashCmd = args.trailing; // Hack. args.break = true; + + if (args["+purgecaches"]) + cache.flush(); + util.rehash(args); }, { - argCount: "0", - options: [ - { - names: ["+u"], - description: "The initialization file to execute at startup", - type: CommandOption.STRING - }, - { - names: ["++noplugin"], - description: "Do not automatically load plugins" - }, - { - names: ["++cmd"], - description: "Ex commands to execute prior to initialization", - type: CommandOption.STRING, - multiple: true - }, - { - names: ["+c"], - description: "Ex commands to execute after initialization", - type: CommandOption.STRING, - multiple: true - } - ] + argCount: "0", // FIXME + options: startupOptions }); commands.add(["res[tart]"], - "Force " + config.appName + " to restart", - function () { dactyl.restart(); }); + "Force " + config.host + " to restart", + function (args) { + if (args["+purgecaches"]) + cache.flush(); - function findToolbar(name) util.evaluateXPath( - "//*[@toolbarname=" + util.escapeString(name, "'") + "]", + dactyl.restart(args.string); + }, + { + argCount: "0", + options: startupOptions + }); + + function findToolbar(name) DOM.XPath( + "//*[@toolbarname=" + util.escapeString(name, "'") + " or " + + "@toolbarname=" + util.escapeString(name.trim(), "'") + "]", document).snapshotItem(0); var toolbox = document.getElementById("navigator-toolbox"); if (toolbox) { - let hidden = function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true"; - let toolbarCommand = function (names, desc, action, filter) { commands.add(names, desc, function (args) { @@ -1888,7 +1690,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { action(toolbar); events.checkFocus(); }, { - argcount: "1", + argCount: "1", completer: function (context) { completion.toolbar(context); if (filter) @@ -1900,12 +1702,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { toolbarCommand(["toolbars[how]", "tbs[how]"], "Show the named toolbar", function (toolbar) dactyl.setNodeVisible(toolbar, true), - function ({ item }) hidden(item)); + function ({ item }) Dactyl.toolbarHidden(item)); toolbarCommand(["toolbarh[ide]", "tbh[ide]"], "Hide the named toolbar", function (toolbar) dactyl.setNodeVisible(toolbar, false), - function ({ item }) !hidden(item)); + function ({ item }) !Dactyl.toolbarHidden(item)); toolbarCommand(["toolbart[oggle]", "tbt[oggle]"], "Toggle the named toolbar", - function (toolbar) dactyl.setNodeVisible(toolbar, hidden(toolbar))); + function (toolbar) dactyl.setNodeVisible(toolbar, Dactyl.toolbarHidden(toolbar))); } commands.add(["time"], @@ -1916,9 +1718,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { args = args[0] || ""; if (args[0] == ":") - var method = function () commands.execute(args, null, true); + var func = function () commands.execute(args, null, false); else - method = dactyl.userFunc(args); + func = dactyl.userFunc(args); try { if (count > 1) { @@ -1927,7 +1729,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { for (let i in util.interruptibleRange(0, count, 500)) { let now = Date.now(); - method(); + func(); total += Date.now() - now; } @@ -1953,16 +1755,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { commandline.commandOutput( - + - - - + + +
    Code execution summary{_("title.Code execution summary")}
      Executed:{count}times
      Average time:{each.toFixed(2)}{eachUnits}
      Total time:{total.toFixed(2)}{totalUnits}
      {_("title.Executed")}:{count}times
      {_("title.Average time")}:{each.toFixed(2)}{eachUnits}
      {_("title.Total time")}:{total.toFixed(2)}{totalUnits}
    ); } else { let beforeTime = Date.now(); - method(); + func(); if (special) return; @@ -1979,7 +1781,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { dactyl.echoerr(e); } }, { - argCount: "+", + argCount: "1", bang: true, completer: function (context) { if (/^:/.test(context.filter)) @@ -2010,7 +1812,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { vbs.setFrom = setFrom; } }, { - argCount: "+", + argCount: "1", completer: function (context) completion.ex(context), count: true, literal: 0, @@ -2022,10 +1824,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { function (args) { if (args.bang) dactyl.open("about:"); - else - commandline.commandOutput(<> - {config.appName} {config.version} running on:
    {navigator.userAgent} - ); + else { + let date = config.buildDate; + date = date ? " (" + date + ")" : ""; + + commandline.commandOutput( +
    {config.appName} {config.version}{date} running on:
    + +
    {navigator.userAgent}
    ) + } }, { argCount: "0", bang: true @@ -2040,27 +1846,22 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { context.completions = [[k, v[0], v[2]] for ([k, v] in Iterator(config.dialogs))]; }; - completion.help = function help(context, consolidated) { - dactyl.initHelp(); - context.title = ["Help"]; - context.anchored = false; - context.completions = services["dactyl:"].HELP_TAGS; - if (consolidated) - context.keys = { text: 0, description: function () "all" }; - }; - completion.menuItem = function menuItem(context) { context.title = ["Menu Path", "Label"]; context.anchored = false; - context.keys = { text: "fullMenuPath", description: function (item) item.getAttribute("label") }; - context.completions = dactyl.menuItems; + context.keys = { + text: "dactylPath", + description: function (item) item.getAttribute("label"), + highlight: function (item) item.disabled ? "Disabled" : "" + }; + context.generate = function () dactyl.menuItems; }; var toolbox = document.getElementById("navigator-toolbox"); completion.toolbar = function toolbar(context) { context.title = ["Toolbar"]; context.keys = { text: function (item) item.getAttribute("toolbarname"), description: function () "" }; - context.completions = util.evaluateXPath("//*[@toolbarname]", document); + context.completions = DOM.XPath("//*[@toolbarname]", document); }; completion.window = function window(context) { @@ -2074,9 +1875,17 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { dactyl.log(_("dactyl.modulesLoaded"), 3); + userContext.DOM = Class("DOM", DOM, { init: function DOM_(sel, ctxt) DOM(sel, ctxt || buffer.focusedFrame.document) }); + userContext.$ = modules.userContext.DOM; + dactyl.timeout(function () { try { - var args = JSMLoader.commandlineArgs || services.commandLineHandler.optionValue; + var args = config.prefs.get("commandline-args") + || storage.session.commandlineArgs + || services.commandLineHandler.optionValue; + + config.prefs.reset("commandline-args"); + if (isString(args)) args = dactyl.parseCommandLine(args); @@ -2094,20 +1903,17 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { dactyl.log(_("dactyl.commandlineOpts", util.objectToString(dactyl.commandLineOptions)), 3); - // first time intro message - const firstTime = "extensions." + config.name + ".firsttime"; - if (prefs.get(firstTime, true)) { + if (config.prefs.get("first-run", true)) dactyl.timeout(function () { - this.withSavedValues(["forceNewTab"], function () { - this.forceNewTab = true; - this.help(); - prefs.set(firstTime, false); + config.prefs.set("first-run", false); + this.withSavedValues(["forceTarget"], function () { + this.forceTarget = dactyl.NEW_TAB; + help.help(); }); }, 1000); - } // TODO: we should have some class where all this guioptions stuff fits well - // Dactyl.hideGUI(); + // dactyl.hideGUI(); if (dactyl.userEval("typeof document", null, "test.js") === "undefined") jsmodules.__proto__ = XPCSafeJSObjectWrapper(window); @@ -2149,7 +1955,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (dactyl.commandLineOptions.rcFile == "NONE" || dactyl.commandLineOptions.noPlugins) options["loadplugins"] = []; - if (options["loadplugins"]) + if (options["loadplugins"].length) dactyl.loadPlugins(); } catch (e) { @@ -2167,9 +1973,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { dactyl.execute(cmd); }); - if (JSMLoader.rehashCmd) - dactyl.execute(JSMLoader.rehashCmd); - JSMLoader.rehashCmd = null; + if (storage.session.rehashCmd) + dactyl.execute(storage.session.rehashCmd); + storage.session.rehashCmd = null; dactyl.fullyInitialized = true; dactyl.triggerObserver("enter", null);