diff --git a/License.txt b/License.txt index 563f5f2f..f6a7a8b8 100644 --- a/License.txt +++ b/License.txt @@ -1,6 +1,6 @@ Copyright (c) 2006-2009 by Martin Stubenschrott 2007-2009 by Doug Kearns - 2008-2009 by Kris Maglione + 2008-2010 by Kris Maglione For a full list of authors, refer the AUTHORS file. diff --git a/common/asciidoc.conf b/common/asciidoc.conf deleted file mode 100644 index a950e530..00000000 --- a/common/asciidoc.conf +++ /dev/null @@ -1,124 +0,0 @@ -[miscellaneous] - -[glossary] -author=Martin Stubenschrott -email=stubenschrott@vimperator.org - -[header] - - - - - - - - - - {doctitle} - - -
- {doctitle} -
- -[replacements] -\[count\]=[count] -\[!\]=[!] - -[macros] -# heading:Title[tag1,tag2] -(?u)^(?Pheading)::(?P\S*?)(\[(?P.*?)\])$=# -# section:Title[tag1,tag2] -(?u)^(?Psection)::(?P\S*?)(\[(?P.*?)\])$=# -# subsection:Title[tag1,tag2] -(?u)^(?Psubsection)::(?P\S*?)(\[(?P.*?)\])$=# -# logo:[,] # FIXME: this is a bit silly -(?su)(?logo):(?P\S*?)\[(?P.*?)\]=# -# help:helptext[href] -(?su)(?help):(?P\S*?)\[(?P.*?)\]= - -# FIXME: this logo/donation banner is really ugly. --djk -[heading-blockmacro] -{outfile@.*(intro|all).html::
} - - - - - -
<{outfile@.*all.html:h2:h1 style="border\: none"}>{target} -{3? {3}} -{2? {2}} -{1? {1}} -
- -# maximum 3 tags for now -[section-blockmacro] - - - - - -
<{outfile@.*all.html:h3:h2}>{target} -{3? {3}} -{2? {2}} -{1? {1}} -
- -[subsection-blockmacro] - - - - - -
<{outfile@.*all.html:h4:h3}>{target} -{3? {3}} -{2? {2}} -{1? {1}} -
- -[logo-blockmacro] -
- -[help-inlinemacro] -{target} - -[quotes] -||=key -|=tag -[o]=#option -'=option2 -[c]=#command -[m]=#mapping -[a]=#argument -[j]=#jump -# FIXME: this prevents all asciidoc attribute processing -# ^obviously it doesn't? --stepnem -{|}=argument2 -# disable monospaced text as all our text is monospaced and it causes problems for some things -+= -^= - -[tags] -tag=| -key=| -option=| -option2='|' -command=| -mapping=| -jump=| -argument=| -# NOTE: in certain contexts the unescaped '#' is stripped. As we're ditching -# asciidoc I won't bother investigating --djk -argument2={|} -argument3=[|] - -[specialwords] -warningwords=WARNING: Warning: IMPORTANT: Important: -infowords=NOTE: Note: - -[warningwords] -{words} - -[infowords] -{words} diff --git a/common/components/protocols.js b/common/components/protocols.js index a8b2a975..1afa24fc 100644 --- a/common/components/protocols.js +++ b/common/components/protocols.js @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2009 Kris Maglione +// Copyright (c) 2008-2010 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -35,6 +35,7 @@ function makeChannel(url, orig) { url = dataURL.apply(null, url()); let uri = ioService.newURI(url, null, null); let channel = ioService.newChannelFromURI(uri); + channel.contentCharset = "UTF-8"; channel.owner = systemPrincipal; channel.originalURI = orig; return channel; @@ -45,6 +46,27 @@ function redirect(to, orig, time) { return makeChannel(dataURL('text/html', html), ioService.newURI(to, null, null)); } +function AboutHandler() {} +AboutHandler.prototype = { + + classDescription: "About " + Dactyl.prototype.name + " Page", + + classID: Components.ID("81495d80-89ee-4c36-a88d-ea7c4e5ac63f"), + + contractID: "@mozilla.org/network/protocol/about;1?what=" + Dactyl.prototype.appname, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + + newChannel: function (uri) { + let channel = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService) + .newChannel("chrome://dactyl/content/about.xul", null, null); + channel.originalURI = uri; + return channel; + }, + + getURIFlags: function (uri) Ci.nsIAboutModule.ALLOW_SCRIPT, +}; + function ChromeData() {} ChromeData.prototype = { contractID: "@mozilla.org/network/protocol;1?name=chrome-data", @@ -53,10 +75,10 @@ ChromeData.prototype = { QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIProtocolHandler]), _xpcom_factory: { createInstance: function (outer, iid) { - if (!ChromeData.instance) - ChromeData.instance = new ChromeData(); if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; + if (!ChromeData.instance) + ChromeData.instance = new ChromeData(); return ChromeData.instance.QueryInterface(iid); } }, @@ -89,7 +111,6 @@ ChromeData.prototype = { function Dactyl() { this.wrappedJSObject = this; - const self = this; this.HELP_TAGS = {}; this.FILE_MAP = {}; this.OVERLAY_MAP = {}; @@ -101,14 +122,16 @@ Dactyl.prototype = { QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIProtocolHandler]), _xpcom_factory: { createInstance: function (outer, iid) { - if (!Dactyl.instance) - Dactyl.instance = new Dactyl(); if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; + if (!Dactyl.instance) + Dactyl.instance = new Dactyl(); return Dactyl.instance.QueryInterface(iid); } }, + __proto__: Cc["@dactyl.googlecode.com/base/dactyl"].getService().wrappedJSObject, + init: function (obj) { for each (let prop in ["HELP_TAGS", "FILE_MAP", "OVERLAY_MAP"]) { this[prop] = this[prop].constructor(); @@ -125,9 +148,9 @@ Dactyl.prototype = { | nsIProtocolHandler.URI_IS_LOCAL_RESOURCE, newURI: function (spec, charset, baseURI) { - var uri = Components.classes["@mozilla.org/network/standard-url;1"] - .createInstance(Components.interfaces.nsIStandardURL) - .QueryInterface(Components.interfaces.nsIURI); + var uri = Cc["@mozilla.org/network/standard-url;1"] + .createInstance(Ci.nsIStandardURL) + .QueryInterface(Ci.nsIURI); uri.init(uri.URLTYPE_STANDARD, this.defaultPort, spec, charset, baseURI); return uri; }, @@ -138,16 +161,16 @@ Dactyl.prototype = { return redirect(uri.spec, uri, 1); switch(uri.host) { - case "help": - let url = this.FILE_MAP[uri.path.replace(/^\/|#.*/g, "")]; - return makeChannel(url, uri); - case "help-overlay": - url = this.OVERLAY_MAP[uri.path.replace(/^\/|#.*/g, "")]; - return makeChannel(url, uri); - case "help-tag": - let tag = uri.path.substr(1); - if (tag in this.HELP_TAGS) - return redirect("dactyl://help/" + this.HELP_TAGS[tag] + "#" + tag, uri); + case "help": + let url = this.FILE_MAP[decodeURIComponent(uri.path.replace(/^\/|#.*/g, ""))]; + return makeChannel(url, uri); + case "help-overlay": + url = this.OVERLAY_MAP[decodeURIComponent(uri.path.replace(/^\/|#.*/g, ""))]; + return makeChannel(url, uri); + case "help-tag": + let tag = decodeURIComponent(uri.path.substr(1)); + if (tag in this.HELP_TAGS) + return redirect("dactyl://help/" + this.HELP_TAGS[tag] + "#" + tag, uri); } } catch (e) {} @@ -155,9 +178,19 @@ Dactyl.prototype = { } }; +// A hack to get infermation about interfaces. +// Doesn't belong here. +function Shim() {} +Shim.prototype = { + contractID: "@dactyl.googlecode.com/base/xpc-interface-shim", + classID: Components.ID("{f4506a17-5b4d-4cd9-92d4-2eb4630dc388}"), + classDescription: "XPCOM empty interface shim", + QueryInterface: function () this +}; + if (XPCOMUtils.generateNSGetFactory) - const NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeData, Dactyl]); + const NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutHandler, ChromeData, Dactyl, Shim]); else - const NSGetModule = XPCOMUtils.generateNSGetModule([ChromeData, Dactyl]); + const NSGetModule = XPCOMUtils.generateNSGetModule([AboutHandler, ChromeData, Dactyl, Shim]); // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/content/autocommands.js b/common/content/autocommands.js index 8acf6c36..a9675f42 100644 --- a/common/content/autocommands.js +++ b/common/content/autocommands.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns -// Copyright (c) 2008-2009 by Kris Maglione +// 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. @@ -18,7 +18,7 @@ const AutoCommands = Module("autocommands", { this._store = []; }, - __iterator__: function () util.Array.itervalues(this._store), + __iterator__: function () array.itervalues(this._store), /** * Adds a new autocommand. cmd will be executed when one of the @@ -94,7 +94,7 @@ const AutoCommands = Module("autocommands", { + template.map(items, function (item) -  {item.pattern.source} +  {item.pattern.source} {item.command} )) } diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js index ca83d81e..45c3ae8c 100644 --- a/common/content/bookmarks.js +++ b/common/content/bookmarks.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns -// Copyright (c) 2008-2009 by Kris Maglione +// 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. @@ -11,13 +11,11 @@ const DEFAULT_FAVICON = "chrome://mozapps/skin/places/defaultFavicon.png"; // also includes methods for dealing with keywords and search engines const Bookmarks = Module("bookmarks", { init: function () { - let bookmarkObserver = function (key, event, arg) { + storage.addObserver("bookmark-cache", function (key, event, arg) { if (event == "add") autocommands.trigger("BookmarkAdd", arg); statusline.updateUrl(); - }; - - storage.addObserver("bookmark-cache", bookmarkObserver, window); + }, window); }, get format() ({ @@ -36,7 +34,8 @@ const Bookmarks = Module("bookmarks", { add: function add(starOnly, title, url, keyword, tags, force) { try { let uri = util.createURI(url); - if (!force) { + if (!force && bookmarks.isBookmarked(uri.spec)) { + // WTF? This seems wrong... --Kris for (let bmark in bookmarkcache) { if (bmark[0] == uri.spec) { var id = bmark[5]; @@ -74,21 +73,22 @@ const Bookmarks = Module("bookmarks", { let count = this.remove(url); if (count > 0) - commandline.echo("Removed bookmark: " + url, commandline.HL_NORMAL, commandline.FORCE_SINGLELINE); + dactyl.echomsg({ domains: [util.getHost(url)], message: "Removed bookmark: " + url }); else { let title = buffer.title || url; let extra = ""; if (title != url) extra = " (" + title + ")"; this.add(true, title, url); - commandline.echo("Added bookmark: " + url + extra, commandline.HL_NORMAL, commandline.FORCE_SINGLELINE); + dactyl.echomsg({ domains: [util.getHost(url)], message: "Added bookmark: " + url + extra }); } }, isBookmarked: function isBookmarked(url) { try { - return services.get("bookmarks").getBookmarkIdsForURI(makeURI(url), {}) - .some(bookmarkcache.isRegularBookmark); + return services.get("bookmarks") + .getBookmarkIdsForURI(makeURI(url), {}) + .some(bookmarkcache.closure.isRegularBookmark); } catch (e) { return false; @@ -99,13 +99,14 @@ const Bookmarks = Module("bookmarks", { remove: function remove(url) { try { let uri = util.newURI(url); - let bmarks = services.get("bookmarks").getBookmarkIdsForURI(uri, {}) - .filter(bookmarkcache.isRegularBookmark); + let bmarks = services.get("bookmarks") + .getBookmarkIdsForURI(uri, {}) + .filter(bookmarkcache.closure.isRegularBookmark); bmarks.forEach(services.get("bookmarks").removeItem); return bmarks.length; } catch (e) { - dactyl.log(e, 0); + dactyl.reportError(e); return 0; } }, @@ -287,7 +288,7 @@ const Bookmarks = Module("bookmarks", { args.completeFilter = have.pop(); let prefix = filter.substr(0, filter.length - args.completeFilter.length); - let tags = array.uniq(util.Array.flatten([b.tags for ([k, b] in Iterator(bookmarkcache.bookmarks))])); + let tags = array.uniq(array.flatten([b.tags for ([k, b] in Iterator(bookmarkcache.bookmarks))])); return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag) < 0)]; }, @@ -330,7 +331,8 @@ const Bookmarks = Module("bookmarks", { if (bookmarks.add(false, title, url, keyword, tags, args.bang)) { let extra = (title == url) ? "" : " (" + title + ")"; - dactyl.echomsg("Added bookmark: " + url + extra, 1, commandline.FORCE_SINGLELINE); + dactyl.echomsg({ domains: [util.getHost(url)], message: "Added bookmark: " + url + extra }, + 1, commandline.FORCE_SINGLELINE); } else dactyl.echoerr("Exxx: Could not add bookmark " + title.quote(), commandline.FORCE_SINGLELINE); @@ -385,7 +387,8 @@ const Bookmarks = Module("bookmarks", { let url = args.string || buffer.URL; let deletedCount = bookmarks.remove(url); - dactyl.echomsg(deletedCount + " bookmark(s) with url " + url.quote() + " deleted", 1, commandline.FORCE_SINGLELINE); + dactyl.echomsg({ domains: [util.getHost(url)], message: deletedCount + " bookmark(s) with url " + url.quote() + " deleted" }, + 1, commandline.FORCE_SINGLELINE); } }, @@ -493,13 +496,12 @@ const Bookmarks = Module("bookmarks", { return history.get({ uri: window.makeURI(begin), uriIsPrefix: true }).map(function (item) { let rest = item.url.length - end.length; let query = item.url.substring(begin.length, rest); - if (item.url.substr(rest) == end && query.indexOf("&") == -1) { + if (item.url.substr(rest) == end && query.indexOf("&") == -1) try { - item.url = decodeURIComponent(query.replace(/#.*/, "")); + item.url = decodeURIComponent(query.replace(/#.*/, "").replace(/\+/g, " ")); return item; } catch (e) {} - } return null; }).filter(util.identity); }; diff --git a/common/content/browser.js b/common/content/browser.js index 4334ffc6..e709cca7 100644 --- a/common/content/browser.js +++ b/common/content/browser.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns -// Copyright (c) 2008-2009 by Kris Maglione +// 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. diff --git a/common/content/buffer.js b/common/content/buffer.js index 4cc84adb..3000d059 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns -// Copyright (c) 2008-2009 by Kris Maglione +// 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. @@ -84,7 +84,7 @@ const Buffer = Module("buffer", { const ACCESS_READ = Ci.nsICache.ACCESS_READ; let cacheKey = doc.location.toString().replace(/#.*$/, ""); - for (let proto in util.Array.itervalues(["HTTP", "FTP"])) { + for (let proto in array.itervalues(["HTTP", "FTP"])) { try { var cacheEntryDescriptor = services.get("cache").createSession(proto, 0, true) .openCacheEntry(cacheKey, ACCESS_READ, false); @@ -174,9 +174,11 @@ const Buffer = Module("buffer", { // event listener which is is called on each page load, even if the // page is loaded in a background tab onPageLoad: function onPageLoad(event) { - if (!dactyl.helpInitialized && event.originalTarget instanceof Document) - if (/^dactyl:/.test(event.originalTarget.location.href)) + if (event.originalTarget instanceof Document) + if (/^dactyl:/.test(event.originalTarget.location.href)) { dactyl.initHelp(); + config.styleHelp(); + } if (event.originalTarget instanceof HTMLDocument) { let doc = event.originalTarget; @@ -197,7 +199,7 @@ const Buffer = Module("buffer", { doc.pageIsFullyLoaded = 1; if (doc != config.browser.contentDocument) - dactyl.echomsg("Background tab loaded: " + doc.title || doc.location.href, 3); + dactyl.echomsg({ domains: [util.getHost(doc.location.href)], message: "Background tab loaded: " + doc.title || doc.location.href }, 3); this._triggerLoadAutocmd("PageLoad", doc); } @@ -206,7 +208,11 @@ const Buffer = Module("buffer", { /** * @property {Object} The document loading progress listener. */ - progressListener: update({ __proto__: window.XULBrowserWindow }, { + progressListener: update(Object.create(window.XULBrowserWindow), { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIWebProgressListener]), + + loadCount: 0, + // XXX: function may later be needed to detect a canceled synchronous openURL() onStateChange: function onStateChange(webProgress, request, flags, status) { onStateChange.superapply(this, arguments); @@ -223,7 +229,7 @@ const Buffer = Module("buffer", { // don't reset mode if a frame of the frameset gets reloaded which // is not the focused frame - if (document.commandDispatcher.focusedWindow == webProgress.DOMWindow) { + if (document.commandDispatcher.focusedWindow == webProgress.DOMWindow && this.loadCount++) { util.timeout(function () { modes.reset(false); }, dactyl.mode == modes.HINTS ? 500 : 0); } @@ -1012,7 +1018,7 @@ const Buffer = Module("buffer", { if (win.scrollMaxX > 0 || win.scrollMaxY > 0) return win; - for (let frame in util.Array.itervalues(win.frames)) + for (let frame in array.itervalues(win.frames)) if (frame.scrollMaxX > 0 || frame.scrollMaxY > 0) return frame; @@ -1314,39 +1320,39 @@ const Buffer = Module("buffer", { group[1].push([i, tab.linkedBrowser]); }); - let orig = context; - for (let [id, [name, browsers]] in Iterator(tabGroups)) { - context = orig.fork(id, 0); - context.anchored = false; - context.title = [name || "Buffers"]; - context.keys = { text: "text", description: "url", icon: "icon" }; - context.compare = CompletionContext.Sort.number; - let process = context.process[0]; - context.process = [function (item, text) - <> - {item.item.indicator} - { process.call(this, item, text) } - ]; + context.pushProcessor(0, function (item, text, next) <> + {item.item.indicator} + { next.call(this, item, text) } + ); + context.process[1] = function (item, text) template.highlightURL(text); - context.completions = util.map(util.Array.itervalues(browsers), function ([i, browser]) { - let indicator = " "; - if (i == tabs.index()) - indicator = "%" - else if (i == tabs.index(tabs.alternate)) - indicator = "#"; + context.anchored = false; + context.keys = { text: "text", description: "url", icon: "icon" }; + context.compare = CompletionContext.Sort.number; - let tab = tabs.getTab(i); - let url = browser.contentDocument.location.href; - i = i + 1; + for (let [id, vals] in Iterator(tabGroups)) + context.fork(id, 0, this, function (context, [name, browsers]) { + context.title = [name || "Buffers"]; + context.generate = function () + util.map(array.itervalues(browsers), function ([i, browser]) { + let indicator = " "; + if (i == tabs.index()) + indicator = "%" + else if (i == tabs.index(tabs.alternate)) + indicator = "#"; - return { - text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url], - url: template.highlightURL(url), - indicator: indicator, - icon: tab.image || DEFAULT_FAVICON - }; - }); - } + let tab = tabs.getTab(i); + let url = browser.contentDocument.location.href; + i = i + 1; + + return { + text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url], + url: url, + indicator: indicator, + icon: tab.image || DEFAULT_FAVICON + }; + }); + }, vals); }; }, events: function () { @@ -1354,6 +1360,15 @@ const Buffer = Module("buffer", { config.browser.removeProgressListener(window.XULBrowserWindow); } catch (e) {} // Why? --djk + + // I hate this whole hack. --Kris + let obj = window.XULBrowserWindow, getter; + for (let p in properties(obj)) + if ((getter = obj.__lookupGetter__(p)) && !obj.__lookupSetter__(p)) { + this.progressListener.__defineGetter__(p, getter); + delete obj[p]; + } + config.browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_ALL); window.XULBrowserWindow = this.progressListener; window.QueryInterface(Ci.nsIInterfaceRequestor) @@ -1503,7 +1518,8 @@ const Buffer = Module("buffer", { if (elem.readOnly || elem instanceof HTMLInputElement && !set.has(Events.editableInputs, elem.type)) return false; let computedStyle = util.computedStyle(elem); - return computedStyle.visibility != "hidden" && computedStyle.display != "none"; + return computedStyle.visibility != "hidden" && computedStyle.display != "none" && + computedStyle.MozUserFocus != "ignore"; }); dactyl.assert(elements.length > 0); @@ -1617,13 +1633,13 @@ const Buffer = Module("buffer", { function () { buffer.showPageInfo(true); }); }, options: function () { - options.add(["nextpattern"], // \u00BB is » (>> in a single char) + options.add(["nextpattern"], "Patterns to use when guessing the 'next' page in a document sequence", - "stringlist", "\\bnext\\b,^>$,^(>>|\u00BB)$,^(>|\u00BB),(>|\u00BB)$,\\bmore\\b"); + "stringlist", UTF8("\\bnext\\b,^>$,^(>>|»)$,^(>|»),(>|»)$,\\bmore\\b")); - options.add(["previouspattern"], // \u00AB is « (<< in a single char) + options.add(["previouspattern"], "Patterns to use when guessing the 'previous' page in a document sequence", - "stringlist", "\\bprev|previous\\b,^<$,^(<<|\u00AB)$,^(<|\u00AB),(<|\u00AB)$"); + "stringlist", UTF8("\\bprev|previous\\b,^<$,^(<<|«)$,^(<|«),(<|«)$")); options.add(["pageinfo", "pa"], "Desired info in the :pageinfo output", diff --git a/common/content/commandline.js b/common/content/commandline.js index c7552aaf..9039b7b1 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2009 by Doug Kearns -// Copyright (c) 2008-2009 by Kris Maglione +// 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. @@ -103,31 +103,32 @@ const CommandLine = Module("commandline", { ////////////////////// VARIABLES /////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this._completionList = ItemList("dactyl-completions"); + memoize(this, "_completionList", function () ItemList("dactyl-completions")); this._completions = null; this._history = null; this._startHints = false; // whether we're waiting to start hints mode this._lastSubstring = ""; - this.widgets = { - commandline: document.getElementById("dactyl-commandline"), - prompt: document.getElementById("dactyl-commandline-prompt"), - command: document.getElementById("dactyl-commandline-command"), + memoize(this, "widgets", function () { + let widgets = { + commandline: document.getElementById("dactyl-commandline"), + prompt: document.getElementById("dactyl-commandline-prompt"), + command: document.getElementById("dactyl-commandline-command"), - message: document.getElementById("dactyl-message"), + message: document.getElementById("dactyl-message"), - multilineOutput: document.getElementById("dactyl-multiline-output"), - multilineInput: document.getElementById("dactyl-multiline-input"), - }; + multilineOutput: document.getElementById("dactyl-multiline-output"), + multilineInput: document.getElementById("dactyl-multiline-input"), + }; - this.widgets.command.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); - this.widgets.message.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); + widgets.command.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); + widgets.message.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); + widgets.mowContainer = widgets.multilineOutput.parentNode; - // the widget used for multiline output - this._outputContainer = this.widgets.multilineOutput.parentNode; - - this.widgets.multilineOutput.contentDocument.body.id = "dactyl-multiline-output-content"; + widgets.multilineOutput.contentDocument.body.id = "dactyl-multiline-output-content"; + return widgets; + }); // we need to save the mode which were in before opening the command line // this is then used if we focus the command line again without the "official" @@ -181,12 +182,14 @@ const CommandLine = Module("commandline", { } }, + /** * Highlight the messageBox according to group. */ - _setHighlightGroup: function (group) { - this.widgets.message.setAttributeNS(NS.uri, "highlight", group); + set highlightGroup(group) { + highlight.highlightNode(this.widgets.message, group); }, + get highlightGroup() this.widgets.message.getAttributeNS(NS.uri, "highlight"), /** * Determines whether the command line should be visible. @@ -206,7 +209,7 @@ const CommandLine = Module("commandline", { this.widgets.prompt.value = val; this.widgets.prompt.size = val.length; this.widgets.prompt.collapsed = (val == ""); - this.widgets.prompt.setAttributeNS(NS.uri, "highlight", highlightGroup || commandline.HL_NORMAL); + highlight.highlightNode(this.widgets.prompt, highlightGroup || commandline.HL_NORMAL); }, /** @@ -229,8 +232,8 @@ const CommandLine = Module("commandline", { * @param {boolean} forceSingle If provided, don't let over-long * messages move to the MOW. */ - _echoLine: function (str, highlightGroup, forceSingle) { - this._setHighlightGroup(highlightGroup); + _echoLine: function echoLine(str, highlightGroup, forceSingle) { + this.highlightGroup = highlightGroup; this.widgets.message.value = str; dactyl.triggerObserver("echoLine", str, highlightGroup, forceSingle); @@ -250,7 +253,7 @@ const CommandLine = Module("commandline", { * @param {string} highlightGroup */ // TODO: resize upon a window resize - _echoMultiline: function (str, highlightGroup) { + _echoMultiline: function echoMultiline(str, highlightGroup) { let doc = this.widgets.multilineOutput.contentDocument; let win = this.widgets.multilineOutput.contentWindow; @@ -260,14 +263,15 @@ const CommandLine = Module("commandline", { // Otherwise, white space is significant. // The problem elsewhere is that E4X tends to insert new lines // after interpolated data. - XML.ignoreWhitespace = typeof str != "xml"; - this._lastMowOutput =
{template.maybeXML(str)}
; + XML.ignoreWhitespace = false; + XML.prettyPrinting = false; + let style = typeof str === "string" ? "pre" : "nowrap"; + this._lastMowOutput =
{str}
; let output = util.xmlToDom(this._lastMowOutput, doc); - XML.ignoreWhitespace = true; // FIXME: need to make sure an open MOW is closed when commands // that don't generate output are executed - if (this._outputContainer.collapsed) + if (this.widgets.mowContainer.collapsed) doc.body.innerHTML = ""; doc.body.appendChild(output); @@ -417,6 +421,8 @@ const CommandLine = Module("commandline", { this._currentExtendedMode = null; commandline.triggerCallback("cancel", mode); + if (this._completions) + this._completions.previewClear(); if (this._history) this._history.save(); @@ -431,11 +437,11 @@ const CommandLine = Module("commandline", { this._completionList.hide(); if (!this._keepCommand || this._silent || this._quiet) { - this._outputContainer.collapsed = true; + this.widgets.mowContainer.collapsed = true; commandline.updateMorePrompt(); this.hide(); } - if (!this._outputContainer.collapsed) { + if (!this.widgets.mowContainer.collapsed) { modes.set(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE); commandline.updateMorePrompt(); } @@ -492,39 +498,42 @@ const CommandLine = Module("commandline", { if (flags & this.APPEND_TO_MESSAGES) { let message = isobject(str) ? str : { message: str }; - this._messageHistory.add(update({ highlight: highlightGroup }, str)); + this._messageHistory.add(update({ highlight: highlightGroup }, message)); str = message.message; } + if ((flags & this.ACTIVE_WINDOW) && window != services.get("windowWatcher").activeWindow && services.get("windowWatcher").activeWindow.dactyl) return; - if ((flags & this.DISALLOW_MULTILINE) && !this._outputContainer.collapsed) + if ((flags & this.DISALLOW_MULTILINE) && !this.widgets.mowContainer.collapsed) return; let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE); let action = this._echoLine; - // TODO: this is all a bit convoluted - clean up. - // assume that FORCE_MULTILINE output is fully styled - if (!(flags & this.FORCE_MULTILINE) && !single && (!this._outputContainer.collapsed || this.widgets.message.value == this._lastEcho)) { - highlightGroup += " Message"; - action = this._echoMultiline; - } - - if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || typeof str == "xml") && !(flags & this.FORCE_SINGLELINE)) - action = this._echoMultiline; - if (single) this._lastEcho = null; else { if (this.widgets.message.value == this._lastEcho) this._echoMultiline({this._lastEcho}, - this.widgets.message.getAttributeNS(NS.uri, "highlight")); + this.highlightGroup); this._lastEcho = (action == this._echoLine) && str; } + // TODO: this is all a bit convoluted - clean up. + // assume that FORCE_MULTILINE output is fully styled + if (!(flags & this.FORCE_MULTILINE) && !single + && (!this.widgets.mowContainer.collapsed || this.widgets.message.value == this._lastEcho)) { + + highlightGroup += " Message"; + action = this._echoMultiline; + } + + if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || typeof str == "xml") && !(flags & this.FORCE_SINGLELINE)) + action = this._echoMultiline; + if (action) action.call(this, str, highlightGroup, single); }), @@ -937,7 +946,7 @@ const CommandLine = Module("commandline", { * and what they do. */ updateMorePrompt: function updateMorePrompt(force, showHelp) { - if (this._outputContainer.collapsed) { + if (this.widgets.mowContainer.collapsed) { this._echoLine("", this.HL_NORMAL); return; } @@ -960,19 +969,25 @@ const CommandLine = Module("commandline", { * @param {boolean} open If true, the widget will be opened if it's not * already so. */ - updateOutputHeight: function updateOutputHeight(open) { - if (!open && this._outputContainer.collapsed) + updateOutputHeight: function updateOutputHeight(open, extra) { + if (!open && this.widgets.mowContainer.collapsed) return; let doc = this.widgets.multilineOutput.contentDocument; let availableHeight = config.outputHeight; - if (!this._outputContainer.collapsed) - availableHeight += parseFloat(this._outputContainer.height); + if (!this.widgets.mowContainer.collapsed) + availableHeight += parseFloat(this.widgets.mowContainer.height); + availableHeight -= extra || 0; + doc.body.style.minWidth = this.widgets.commandline.scrollWidth + "px"; - this._outputContainer.height = Math.min(doc.height, availableHeight) + "px"; + this.widgets.mowContainer.height = Math.min(doc.height, availableHeight) + "px"; + this.timeout(function () + this.widgets.mowContainer.height = Math.min(doc.height, availableHeight) + "px", + 0); + doc.body.style.minWidth = ""; - this._outputContainer.collapsed = false; + this.widgets.mowContainer.collapsed = false; }, resetCompletions: function resetCompletions() { @@ -1015,7 +1030,12 @@ const CommandLine = Module("commandline", { if (/^\s*$/.test(str)) return; this.store.mutate("filter", function (line) (line.value || line) != str); - this.store.push({ value: str, timestamp: Date.now()*1000, privateData: this.checkPrivate(str) }); + try { + this.store.push({ value: str, timestamp: Date.now()*1000, privateData: this.checkPrivate(str) }); + } + catch (e) { + dactyl.reportError(e); + } this.store.truncate(options["history"], true); }, /** @@ -1142,6 +1162,8 @@ const CommandLine = Module("commandline", { get wildtype() this.wildtypes[this.wildIndex] || "", + get wildtypes() this.wildmode.values, + complete: function complete(show, tabPressed) { this.context.reset(); this.context.tabPressed = tabPressed; @@ -1162,7 +1184,7 @@ const CommandLine = Module("commandline", { let substring = ""; switch (this.wildtype.replace(/.*:/, "")) { case "": - substring = this.items[0].text; + substring = this.items[0].result; break; case "longest": if (this.items.length > 1) { @@ -1173,7 +1195,7 @@ const CommandLine = Module("commandline", { case "full": let item = this.items[this.selected != null ? this.selected + 1 : 0]; if (item) - substring = item.text; + substring = item.result; break; } @@ -1227,14 +1249,14 @@ const CommandLine = Module("commandline", { this.wildIndex = 0; } - this.wildtypes = this.wildmode.values; this.preview(); }, _reset: function _reset() { - this.prefix = this.context.value.substring(0, this.start); - this.value = this.context.value.substring(this.start, this.caret); - this.suffix = this.context.value.substring(this.caret); + let value = this.editor.selection.focusNode.textContent; + this.prefix = value.substring(0, this.start); + this.value = value.substring(this.start, this.caret); + this.suffix = value.substring(this.caret); this.itemList.reset(); this.itemList.selectItem(this.selected); @@ -1301,7 +1323,7 @@ const CommandLine = Module("commandline", { return; this.selected = idx; - this.completion = this.items[idx].text; + this.completion = this.items[idx].result; } this.itemList.selectItem(idx); @@ -1320,6 +1342,8 @@ const CommandLine = Module("commandline", { return; while (this.tabs.length) { + this.wildIndex = Math.min(this.wildIndex, this.wildtypes.length - 1); + reverse = this.tabs.shift(); switch (this.wildtype.replace(/.*:/, "")) { case "": @@ -1340,7 +1364,7 @@ const CommandLine = Module("commandline", { if (this.haveType("list")) this.itemList.show(); - this.wildIndex = Math.constrain(this.wildIndex + 1, 0, this.wildtypes.length - 1); + this.wildIndex++; this.preview(); commandline._statusTimer.tell(); @@ -1373,7 +1397,7 @@ const CommandLine = Module("commandline", { if (typeof arg === "object") arg = util.objectToString(arg, useColor); - else if (typeof arg == "string" && /\n/.test(arg)) + else if (typeof arg === "string" && /\n/.test(arg)) arg = {arg}; else arg = String(arg); @@ -1533,7 +1557,7 @@ const CommandLine = Module("commandline", { styles: function () { let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize; styles.registerSheet("chrome://dactyl/skin/dactyl.css"); - let error = styles.addSheet(true, "font-size", "chrome://dactyl/content/buffer.xhtml", + styles.addSheet(true, "font-size", "chrome://dactyl/content/buffer.xhtml", "body { font-size: " + fontSize + "; }"); } }); @@ -1542,7 +1566,7 @@ const CommandLine = Module("commandline", { * The list which is used for the completion box (and QuickFix window in * future). * - * @param {string} id The id of the