X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Fbuffer.jsm;h=97398efa920803e5a35375396ddf893eb8fcc43f;hb=refs%2Fheads%2Fupstream;hp=94b1692e762d840e0759af5834058630d003804b;hpb=9044153cb63835e39b9de8ec4ade237c03e3888a;p=dactyl.git diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index 94b1692..97398ef 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -1,21 +1,25 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2011 by Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. -try {"use strict"; +"use strict"; -Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("buffer", { exports: ["Buffer", "buffer"], require: ["prefs", "services", "util"] -}, this); +}); -this.lazyRequire("finder", ["RangeFind"]); -this.lazyRequire("overlay", ["overlay"]); -this.lazyRequire("storage", ["storage"]); -this.lazyRequire("template", ["template"]); +lazyRequire("bookmarkcache", ["bookmarkcache"]); +lazyRequire("contexts", ["Group"]); +lazyRequire("io", ["io"]); +lazyRequire("finder", ["RangeFind"]); +lazyRequire("overlay", ["overlay"]); +lazyRequire("promises", ["Promise", "promises"]); +lazyRequire("sanitizer", ["sanitizer"]); +lazyRequire("storage", ["File", "storage"]); +lazyRequire("template", ["template"]); /** * A class to manage the primary web content buffer. The name comes @@ -30,7 +34,7 @@ var Buffer = Module("Buffer", { let win = services.focus.focusedWindow; if (!win || win == window || util.topWindow(win) != window) - return window.content + return window.content; if (win.top == window) return win; return win.top; @@ -42,7 +46,7 @@ var Buffer = Module("Buffer", { this.win = win; }, - get addPageInfoSection() Buffer.closure.addPageInfoSection, + get addPageInfoSection() Buffer.bound.addPageInfoSection, get pageInfo() Buffer.pageInfo, @@ -58,13 +62,113 @@ var Buffer = Module("Buffer", { */ get alternateStyleSheets() { let stylesheets = array.flatten( - this.allFrames().map(function (w) Array.slice(w.document.styleSheets))); + this.allFrames().map(w => Array.slice(w.document.styleSheets))); return stylesheets.filter( - function (stylesheet) /^(screen|all|)$/i.test(stylesheet.media.mediaText) && !/^\s*$/.test(stylesheet.title) + s => /^(screen|all|)$/i.test(s.media.mediaText) && !/^\s*$/.test(s.title) ); }, + /** + * The load context of the window bound to this buffer. + */ + get loadContext() sanitizer.getContext(this.win), + + /** + * Content preference methods. + */ + prefs: Class.Memoize(function () + let (self = this) ({ + /** + * Returns a promise for the given preference name. + * + * @param {string} pref The name of the preference to return. + * @returns {Promise<*>} + */ + get: promises.withCallbacks(function get([resolve, reject], pref) { + let val = services.contentPrefs.getCachedByDomainAndName( + self.uri.spec, pref, self.loadContext); + + let found = false; + if (val) + resolve(val.value); + else + services.contentPrefs.getByDomainAndName( + self.uri.spec, pref, self.loadContext, + { handleCompletion: () => { + if (!found) + resolve(undefined); + }, + handleResult: (pref) => { + found = true; + resolve(pref.value); + }, + handleError: reject }); + }), + + /** + * Sets a content preference for the given buffer. + * + * @param {string} pref The preference to set. + * @param {string} value The value to store. + */ + set: promises.withCallbacks(function set([resolve, reject], pref, value) { + services.contentPrefs.set( + self.uri.spec, pref, value, self.loadContext, + { handleCompletion: () => {}, + handleResult: resolve, + handleError: reject }); + }), + + /** + * Clear a content preference for the given buffer. + * + * @param {string} pref The preference to clear. + */ + clear: promises.withCallbacks(function clear([resolve, reject], pref) { + services.contentPrefs.removeByDomainAndName( + self.uri.spec, pref, self.loadContext, + { handleCompletion: () => {}, + handleResult: resolve, + handleError: reject }); + }) + })), + + /** + * Gets a content preference for the given buffer. + * + * @param {string} pref The preference to get. + * @param {function(string|number|boolean)} callback The callback to + * call with the preference value. @optional + * @returns {string|number|boolean} The value of the preference, if + * callback is not provided. + */ + getPref: deprecated("prefs.get", function getPref(pref, callback) { + services.contentPrefs.getPref(this.uri, pref, + this.loadContext, callback); + }), + + /** + * Sets a content preference for the given buffer. + * + * @param {string} pref The preference to set. + * @param {string} value The value to store. + */ + setPref: deprecated("prefs.set", function setPref(pref, value) { + services.contentPrefs.setPref( + this.uri, pref, value, this.loadContext); + }), + + /** + * Clear a content preference for the given buffer. + * + * @param {string} pref The preference to clear. + */ + clearPref: deprecated("prefs.clear", function clearPref(pref) { + services.contentPrefs.removePref( + this.uri, pref, this.loadContext); + }), + climbUrlPath: function climbUrlPath(count) { let { dactyl } = this.modules; @@ -72,7 +176,7 @@ var Buffer = Module("Buffer", { dactyl.assert(url instanceof Ci.nsIURL); while (count-- && url.path != "/") - url.path = url.path.replace(/[^\/]+\/*$/, ""); + url.path = url.path.replace(/[^\/]*\/*$/, ""); dactyl.assert(!url.equals(this.documentURI)); dactyl.open(url.spec); @@ -100,8 +204,8 @@ var Buffer = Module("Buffer", { */ get loaded() Math.min.apply(null, this.allFrames() - .map(function (frame) ["loading", "interactive", "complete"] - .indexOf(frame.document.readyState))), + .map(frame => ["loading", "interactive", "complete"] + .indexOf(frame.document.readyState))), /** * @property {Object} The local state store for the currently selected @@ -177,7 +281,7 @@ var Buffer = Module("Buffer", { */ get zoomLevel() { let v = this.contentViewer; - return v[v.textZoom == 1 ? "fullZoom" : "textZoom"] * 100 + return v[v.textZoom == 1 ? "fullZoom" : "textZoom"] * 100; }, set zoomLevel(value) { this.setZoom(value, this.fullZoom); }, @@ -233,8 +337,8 @@ var Buffer = Module("Buffer", { })(win || this.win); if (focusedFirst) - return frames.filter(function (f) f === this.focusedFrame).concat( - frames.filter(function (f) f !== this.focusedFrame)); + return frames.filter(f => f === this.focusedFrame).concat( + frames.filter(f => f !== this.focusedFrame)); return frames; }, @@ -393,8 +497,10 @@ var Buffer = Module("Buffer", { yield elem; function a(regexp, elem) regexp.test(elem.textContent) === regexp.result || - Array.some(elem.childNodes, function (child) regexp.test(child.alt) === regexp.result); - function b(regexp, elem) regexp.test(elem.title); + Array.some(elem.childNodes, + child => (regexp.test(child.alt) === regexp.result)); + + function b(regexp, elem) regexp.test(elem.title) === regexp.result; let res = Array.filter(frame.document.querySelectorAll(selector), Hints.isVisible); for (let test in values([a, b])) @@ -456,10 +562,11 @@ var Buffer = Module("Buffer", { let { dactyl } = this.modules; let ctrlKey = false, shiftKey = false; + let button = 0; switch (dactyl.forceTarget || where) { case dactyl.NEW_TAB: case dactyl.NEW_BACKGROUND_TAB: - ctrlKey = true; + button = 1; shiftKey = dactyl.forceBackground != null ? dactyl.forceBackground : where != dactyl.NEW_BACKGROUND_TAB; break; @@ -475,13 +582,11 @@ var Buffer = Module("Buffer", { prefs.withContext(function () { prefs.set("browser.tabs.loadInBackground", true); let params = { - screenX: offsetX, screenY: offsetY, + button: button, screenX: offsetX, screenY: offsetY, ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey }; DOM(elem).mousedown(params).mouseup(params); - if (!config.haveGecko("2b")) - DOM(elem).click(params); let sel = util.selectionController(win); sel.getSelection(sel.SELECTION_FOCUS_REGION).collapseToStart(); @@ -498,7 +603,7 @@ var Buffer = Module("Buffer", { function getRanges(rect) { let nodes = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils) .nodesFromRect(rect.x, rect.y, 0, rect.width, rect.height, 0, false, false); - return Array.filter(nodes, function (n) n instanceof Ci.nsIDOMText) + return Array.filter(nodes, n => n instanceof Ci.nsIDOMText) .map(RangeFind.nodeContents); } @@ -522,11 +627,11 @@ var Buffer = Module("Buffer", { rect = { x: w / 3, y: 0, width: w / 3, height: win.innerHeight }; } - var reduce = function (a, b) DOM(a).rect.top < DOM(b).rect.top ? a : b; + var reduce = (a, b) => DOM(a).rect.top < DOM(b).rect.top ? a : b; var dir = "forward"; var y = 0; if (reverse) { - reduce = function (a, b) DOM(b).rect.bottom > DOM(a).rect.bottom ? b : a; + reduce = (a, b) => DOM(b).rect.bottom > DOM(a).rect.bottom ? b : a; dir = "backward"; y = win.innerHeight - 1; } @@ -572,6 +677,41 @@ var Buffer = Module("Buffer", { */ get selectionController() util.selectionController(this.focusedFrame), + /** + * @property {string|null} The canonical short URL for the current + * document. + */ + get shortURL() { + let { uri, doc } = this; + + function hashify(url) { + let newURI = util.newURI(url); + + if (uri.hasRef && !newURI.hasRef) + newURI.ref = uri.ref; + + return newURI.spec; + } + + for (let shortener of Buffer.uriShorteners) + try { + let shortened = shortener(uri, doc); + if (shortened) + return hashify(shortened.spec); + } + catch (e) { + util.reportError(e); + } + + let link = DOM("link[href][rev=canonical], \ + link[href][rel=shortlink]", doc) + .attr("href"); + if (link) + return hashify(link); + + return null; + }, + /** * Opens the appropriate context menu for *elem*. * @@ -583,8 +723,9 @@ var Buffer = Module("Buffer", { * Saves a page link to disk. * * @param {HTMLAnchorElement} elem The page link to save. + * @param {boolean} overwrite If true, overwrite any existing file. */ - saveLink: function saveLink(elem) { + saveLink: function saveLink(elem, overwrite) { let { completion, dactyl, io } = this.modules; let self = this; @@ -602,6 +743,8 @@ var Buffer = Module("Buffer", { if (file.exists() && file.isDirectory()) file.append(Buffer.getDefaultNames(elem)[0][0]); + util.assert(!file.exists() || overwrite, _("io.existsNoOverride", file.path)); + try { if (!file.exists()) file.create(File.NORMAL_FILE_TYPE, octal(644)); @@ -610,7 +753,7 @@ var Buffer = Module("Buffer", { util.assert(false, _("save.invalidDestination", e.name)); } - self.saveURI(uri, file); + self.saveURI({ uri: uri, file: file, context: elem }); }, completer: function (context) completion.savePage(context, elem) @@ -627,26 +770,42 @@ var Buffer = Module("Buffer", { * @param {nsIURI} uri The URI to save * @param {nsIFile} file The file into which to write the result. */ - saveURI: function saveURI(uri, file, callback, self) { + saveURI: function saveURI(params) { + if (params instanceof Ci.nsIURI) + // Deprecated? + params = { uri: arguments[0], file: arguments[1], + callback: arguments[2], self: arguments[3] }; + var persist = services.Persist(); persist.persistFlags = persist.PERSIST_FLAGS_FROM_CACHE | persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES; let window = this.topWindow; - let downloadListener = new window.DownloadListener(window, - services.Transfer(uri, File(file).URI, "", - null, null, null, persist)); - - persist.progressListener = update(Object.create(downloadListener), { - onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) { - if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0) - util.trapErrors(callback, self, uri, file, progress, request, flags, status); + let privacy = sanitizer.getContext(params.context || this.win); + let file = File(params.file); + if (!file.exists()) + file.create(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666)); - return onStateChange.superapply(this, arguments); - }) - }); + let downloadListener = new window.DownloadListener(window, + services.Transfer(params.uri, file.URI, "", null, null, null, + persist, privacy && privacy.usePrivateBrowsing)); + + var { callback, self } = params; + if (callback) + persist.progressListener = update(Object.create(downloadListener), { + onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) { + if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0) + util.trapErrors(callback, self, params.uri, file.file, + progress, request, flags, status); + + return onStateChange.superapply(this, arguments); + }) + }); + else + persist.progressListener = downloadListener; - persist.saveURI(uri, null, null, null, null, file); + persist.saveURI(params.uri, null, null, null, null, + file.file, privacy); }, /** @@ -668,8 +827,8 @@ var Buffer = Module("Buffer", { * vertical percentages. See {@link Buffer.scrollToPercent} for * parameters. */ - scrollToPercent: function scrollToPercent(horizontal, vertical) - Buffer.scrollToPercent(this.findScrollable(0, vertical == null), horizontal, vertical), + scrollToPercent: function scrollToPercent(horizontal, vertical, dir) + Buffer.scrollToPercent(this.findScrollable(dir || 0, vertical == null), horizontal, vertical), /** * Scrolls the currently active element to the given horizontal and @@ -695,11 +854,10 @@ var Buffer = Module("Buffer", { * @param {number} count The multiple of 'scroll' lines to scroll. * @optional */ - scrollByScrollSize: function scrollByScrollSize(direction, count) { + scrollByScrollSize: function scrollByScrollSize(direction, count=1) { let { options } = this.modules; direction = direction ? 1 : -1; - count = count || 1; if (options["scroll"] > 0) this.scrollVertical("lines", options["scroll"] * direction); @@ -740,8 +898,26 @@ var Buffer = Module("Buffer", { var sel = this.focusedFrame.getSelection(); } catch (e) {} + if (!elem && sel && sel.rangeCount) elem = sel.getRangeAt(0).startContainer; + + if (!elem) { + let area = -1; + for (let e in DOM(Buffer.SCROLLABLE_SEARCH_SELECTOR, + this.focusedFrame.document)) { + if (Buffer.isScrollable(e, dir, horizontal)) { + let r = DOM(e).rect; + let a = r.width * r.height; + if (a > area) { + area = a; + elem = e; + } + } + } + if (elem) + util.trapErrors("focus", elem); + } if (elem) elem = find(elem); @@ -799,10 +975,13 @@ var Buffer = Module("Buffer", { let path = options["jumptags"][arg]; util.assert(path, _("error.invalidArgument", arg)); - let distance = reverse ? function (rect) -rect.top : function (rect) rect.top; - let elems = [[e, distance(e.getBoundingClientRect())] for (e in path.matcher(this.focusedFrame.document))] - .filter(function (e) e[1] > FUDGE) - .sort(function (a, b) a[1] - b[1]) + let distance = reverse ? rect => -rect.top + : rect => rect.top; + + let elems = [[e, distance(e.getBoundingClientRect())] + for (e in path.matcher(this.focusedFrame.document))] + .filter(e => e[1] > FUDGE) + .sort((a, b) => a[1] - b[1]); if (offScreen && !reverse) elems = elems.filter(function (e) e[1] > this, this.topWindow.innerHeight); @@ -836,8 +1015,8 @@ var Buffer = Module("Buffer", { return; // remove all hidden frames - frames = frames.filter(function (frame) !(frame.document.body instanceof Ci.nsIDOMHTMLFrameSetElement)) - .filter(function (frame) !frame.frameElement || + frames = frames.filter(frame => !(frame.document.body instanceof Ci.nsIDOMHTMLFrameSetElement)) + .filter(frame => !frame.frameElement || let (rect = frame.frameElement.getBoundingClientRect()) rect.width && rect.height); @@ -857,14 +1036,14 @@ var Buffer = Module("Buffer", { // add the frame indicator let doc = frames[next].document; - let indicator = DOM(
, doc) + let indicator = DOM(["div", { highlight: "FrameIndicator" }], doc) .appendTo(doc.body || doc.documentElement || doc); util.timeout(function () { indicator.remove(); }, 500); // Doesn't unattach - //doc.body.setAttributeNS(NS.uri, "activeframe", "true"); - //util.timeout(function () { doc.body.removeAttributeNS(NS.uri, "activeframe"); }, 500); + //doc.body.setAttributeNS(NS, "activeframe", "true"); + //util.timeout(function () { doc.body.removeAttributeNS(NS, "activeframe"); }, 500); }, // similar to pageInfo @@ -877,8 +1056,7 @@ var Buffer = Module("Buffer", { showElementInfo: function showElementInfo(elem) { let { dactyl } = this.modules; - XML.ignoreWhitespace = XML.prettyPrinting = false; - dactyl.echo(<>Element:
{util.objectToString(elem, true)}); + dactyl.echo(["", /*L*/"Element:", ["br"], util.objectToString(elem, true)]); }, /** @@ -891,8 +1069,6 @@ var Buffer = Module("Buffer", { showPageInfo: function showPageInfo(verbose, sections) { let { commandline, dactyl, options } = this.modules; - let self = this; - // Ctrl-g single line output if (!verbose) { let file = this.win.location.pathname.split("/").pop() || _("buffer.noName"); @@ -900,22 +1076,22 @@ var Buffer = Module("Buffer", { let info = template.map( (sections || options["pageinfo"]) - .map(function (opt) Buffer.pageInfo[opt].action.call(self)), - function (res) res && iter(res).join(", ") || undefined, - ", "); + .map((opt) => Buffer.pageInfo[opt].action.call(this)), + res => (res && iter(res).join(", ") || undefined), + ", ").join(""); if (bookmarkcache.isBookmarked(this.URL)) info += ", " + _("buffer.bookmarked"); - let pageInfoText = <>{file.quote()} [{info}] {title}; + let pageInfoText = [file.quote(), " [", info, "] ", title].join(""); dactyl.echo(pageInfoText, commandline.FORCE_SINGLELINE); return; } - let list = template.map(sections || options["pageinfo"], function (option) { + let list = template.map(sections || options["pageinfo"], (option) => { let { action, title } = Buffer.pageInfo[option]; - return template.table(title, action.call(self, true)); - },
); + return template.table(title, action.call(this, true)); + }, ["br"]); commandline.commandOutput(list); }, @@ -989,7 +1165,7 @@ var Buffer = Module("Buffer", { else { let url = loc || doc.location.href; const PREFIX = "view-source:"; - if (url.indexOf(PREFIX) == 0) + if (url.startsWith(PREFIX)) url = url.substr(PREFIX.length); else url = PREFIX + url; @@ -1029,10 +1205,21 @@ var Buffer = Module("Buffer", { return true; }; - let uri = isString(doc) ? util.newURI(doc) : util.newURI(doc.location.href); + if (isString(doc)) { + var privacyContext = null; + var uri = util.newURI(doc); + } + else { + privacyContext = sanitizer.getContext(doc); + uri = util.newURI(doc.location.href); + } + let ext = uri.fileExtension || "txt"; if (doc.contentType) - ext = services.mime.getPrimaryExtension(doc.contentType, ext); + try { + ext = services.mime.getPrimaryExtension(doc.contentType, ext); + } + catch (e) {} if (!isString(doc)) return io.withTempFiles(function (temp) { @@ -1050,7 +1237,8 @@ var Buffer = Module("Buffer", { var persist = services.Persist(); persist.persistFlags = persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES; persist.progressListener = this; - persist.saveURI(uri, null, null, null, null, this.file); + persist.saveURI(uri, null, null, null, null, this.file, + privacyContext); } return null; }, @@ -1102,7 +1290,7 @@ var Buffer = Module("Buffer", { * closed range [Buffer.ZOOM_MIN, Buffer.ZOOM_MAX]. */ setZoom: function setZoom(value, fullZoom) { - let { dactyl, statusline } = this.modules; + let { dactyl, statusline, storage } = this.modules; let { ZoomManager } = this; if (fullZoom === undefined) @@ -1119,12 +1307,16 @@ var Buffer = Module("Buffer", { return dactyl.echoerr(_("zoom.illegal")); } - if (services.has("contentPrefs") && !storage.privateMode - && prefs.get("browser.zoom.siteSpecific")) { - services.contentPrefs[value != 1 ? "setPref" : "removePref"] - (this.uri, "browser.content.full-zoom", value); - services.contentPrefs[value != 1 ? "setPref" : "removePref"] - (this.uri, "dactyl.content.full-zoom", fullZoom); + if (prefs.get("browser.zoom.siteSpecific")) { + var privacy = sanitizer.getContext(this.win); + if (value == 1) { + this.prefs.clear("browser.content.full-zoom"); + this.prefs.clear("dactyl.content.full-zoom"); + } + else { + this.prefs.set("browser.content.full-zoom", value); + this.prefs.set("dactyl.content.full-zoom", fullZoom); + } } statusline.updateZoomLevel(); @@ -1133,16 +1325,16 @@ var Buffer = Module("Buffer", { /** * Updates the zoom level of this buffer from a content preference. */ - updateZoom: util.wrapCallback(function updateZoom() { - let self = this; + updateZoom: promises.task(function updateZoom() { let uri = this.uri; - if (services.has("contentPrefs") && prefs.get("browser.zoom.siteSpecific")) - services.contentPrefs.getPref(uri, "dactyl.content.full-zoom", function (val) { - if (val != null && uri.equals(self.uri) && val != prefs.get("browser.zoom.full")) - [self.contentViewer.textZoom, self.contentViewer.fullZoom] = - [self.contentViewer.fullZoom, self.contentViewer.textZoom]; - }); + if (prefs.get("browser.zoom.siteSpecific")) { + let val = yield this.prefs.get("dactyl.content.full-zoom"); + + if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full")) + [this.contentViewer.textZoom, this.contentViewer.fullZoom] = + [this.contentViewer.fullZoom, this.contentViewer.textZoom]; + } }), /** @@ -1183,6 +1375,12 @@ var Buffer = Module("Buffer", { scrollTo: deprecated("Buffer.scrollTo", function scrollTo(x, y) this.win.scrollTo(x, y)), textZoom: deprecated("buffer.zoomValue/buffer.fullZoom", function textZoom() this.contentViewer.markupDocumentViewer.textZoom * 100) }, { + /** + * The pattern used to search for a scrollable element when we have + * no starting point. + */ + SCROLLABLE_SEARCH_SELECTOR: "html, body, div", + PageInfo: Struct("PageInfo", "name", "title", "action") .localize("title"), @@ -1201,6 +1399,27 @@ var Buffer = Module("Buffer", { this.pageInfo[option] = Buffer.PageInfo(option, title, func); }, + uriShorteners: [], + + /** + * Adds a new URI shortener for documents matching the given filter. + * + * @param {string|function(URI, Document):boolean} filter A site filter + * string or a function which accepts a URI and a document and + * returns true if it can shorten the document's URI. + * @param {function(URI, Document):URI} shortener Returns a shortened + * URL for the given URI and document. + */ + addURIShortener: function addURIShortener(filter, shortener) { + if (isString(filter)) + filter = Group.compileFilter(filter); + + this.uriShorteners.push(function uriShortener(uri, doc) { + if (filter(uri, doc)) + return shortener(uri, doc); + }); + }, + Scrollable: function Scrollable(elem) { if (elem instanceof Ci.nsIDOMElement) return elem; @@ -1216,11 +1435,14 @@ var Buffer = Module("Buffer", { get scrollWidth() this.win.scrollMaxX + this.win.innerWidth, get scrollHeight() this.win.scrollMaxY + this.win.innerHeight, + get scrollLeftMax() this.win.scrollMaxX, + get scrollRightMax() this.win.scrollMaxY, + get scrollLeft() this.win.scrollX, - set scrollLeft(val) { this.win.scrollTo(val, this.win.scrollY) }, + set scrollLeft(val) { this.win.scrollTo(val, this.win.scrollY); }, get scrollTop() this.win.scrollY, - set scrollTop(val) { this.win.scrollTo(this.win.scrollX, val) } + set scrollTop(val) { this.win.scrollTo(this.win.scrollX, val); } }; return elem; }, @@ -1295,9 +1517,9 @@ var Buffer = Module("Buffer", { names.push([decodeURIComponent(url.replace(/.*?([^\/]*)\/*$/, "$1")), _("buffer.save.filename")]); - return names.filter(function ([leaf, title]) leaf) - .map(function ([leaf, title]) [leaf.replace(config.OS.illegalCharacters, encodeURIComponent) - .replace(re, ext), title]); + return names.filter(([leaf, title]) => leaf) + .map(([leaf, title]) => [leaf.replace(config.OS.illegalCharacters, encodeURIComponent) + .replace(re, ext), title]); }, findScrollableWindow: deprecated("buffer.findScrollableWindow", function findScrollableWindow() @@ -1313,23 +1535,35 @@ var Buffer = Module("Buffer", { }, canScroll: function canScroll(elem, dir, horizontal) { - let pos = "scrollTop", size = "clientHeight", max = "scrollHeight", layoutSize = "offsetHeight", + let pos = "scrollTop", size = "clientHeight", end = "scrollHeight", layoutSize = "offsetHeight", overflow = "overflowX", border1 = "borderTopWidth", border2 = "borderBottomWidth"; if (horizontal) - pos = "scrollLeft", size = "clientWidth", max = "scrollWidth", layoutSize = "offsetWidth", + pos = "scrollLeft", size = "clientWidth", end = "scrollWidth", layoutSize = "offsetWidth", overflow = "overflowX", border1 = "borderLeftWidth", border2 = "borderRightWidth"; + if (dir < 0) + return elem[pos] > 0; + + let max = pos + "Max"; + if (max in elem) { + if (elem[pos] < elem[max]) + return true; + if (dir > 0) + return false; + return elem[pos] > 0; + } + let style = DOM(elem).style; let borderSize = Math.round(parseFloat(style[border1]) + parseFloat(style[border2])); let realSize = elem[size]; // Stupid Gecko eccentricities. May fail for quirks mode documents. - if (elem[size] + borderSize == elem[max] || elem[size] == 0) // Stupid, fallible heuristic. + if (elem[size] + borderSize >= elem[end] || elem[size] == 0) // Stupid, fallible heuristic. return false; if (style[overflow] == "hidden") realSize += borderSize; - return dir < 0 && elem[pos] > 0 || dir > 0 && elem[pos] + realSize < elem[max] || !dir && realSize < elem[max]; + return dir > 0 && elem[pos] + realSize < elem[end] || !dir && realSize < elem[end]; }, /** @@ -1366,7 +1600,8 @@ var Buffer = Module("Buffer", { * Like scrollTo, but scrolls more smoothly and does not update * marks. */ - smoothScrollTo: function smoothScrollTo(node, x, y) { + smoothScrollTo: let (timers = WeakMap()) + function smoothScrollTo(node, x, y) { let { options } = overlay.activeModules; let time = options["scrolltime"]; @@ -1374,8 +1609,8 @@ var Buffer = Module("Buffer", { let elem = Buffer.Scrollable(node); - if (node.dactylScrollTimer) - node.dactylScrollTimer.cancel(); + if (timers.has(node)) + timers.get(node).cancel(); if (x == null) x = elem.scrollLeft; @@ -1396,7 +1631,7 @@ var Buffer = Module("Buffer", { else { elem.scrollLeft = startX + (x - startX) / steps * n; elem.scrollTop = startY + (y - startY) / steps * n; - node.dactylScrollTimer = util.timeout(next, time / steps); + timers.set(node, util.timeout(next, time / steps)); } }).call(this); }, @@ -1436,7 +1671,7 @@ var Buffer = Module("Buffer", { /** * Scrolls the given element vertically. * - * @param {Element} elem The element to scroll. + * @param {Node} node The node to scroll. * @param {string} unit The increment by which to scroll. * Possible values are: "lines", "pages" * @param {number} number The possibly fractional number of @@ -1517,12 +1752,12 @@ var Buffer = Module("Buffer", { return { x: elem.scrollLeft && elem.scrollLeft / this._exWidth(node), y: elem.scrollTop / parseFloat(style.lineHeight) - } + }; }, _exWidth: function _exWidth(elem) { try { - let div = DOM(, + let div = DOM(["elem", { style: "width: 1ex !important; position: absolute !important; padding: 0 !important; display: block;" }], elem.ownerDocument).appendTo(elem.body || elem); try { return parseFloat(div.style.width); @@ -1577,41 +1812,45 @@ var Buffer = Module("Buffer", { let arg = args[0]; // FIXME: arg handling is a bit of a mess, check for filename - dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows, + dactyl.assert(!arg || arg[0] == ">", _("error.trailingCharacters")); - const PRINTER = "PostScript/default"; - const BRANCH = "print.printer_" + PRINTER + "."; - - prefs.withContext(function () { - if (arg) { - prefs.set("print.print_printer", PRINTER); - - prefs.set( "print.print_to_file", true); - prefs.set(BRANCH + "print_to_file", true); + const PRINTER = "PostScript/default"; + const BRANCH = "printer_" + PRINTER + "."; + const BRANCHES = ["print.", BRANCH, "print." + BRANCH]; + function set(pref, value) { + BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value); }); + } - prefs.set( "print.print_to_filename", io.File(arg.substr(1)).path); - prefs.set(BRANCH + "print_to_filename", io.File(arg.substr(1)).path); + let settings = services.printSettings.newPrintSettings; + settings.printSilent = args.bang; + if (arg) { + settings.printToFile = true; + settings.toFileName = io.File(arg.substr(1)).path; + settings.outputFormat = settings.kOutputFormatPDF; - dactyl.echomsg(_("print.toFile", arg.substr(1))); - } - else - dactyl.echomsg(_("print.sending")); + dactyl.echomsg(_("print.toFile", arg.substr(1))); + } + else { + dactyl.echomsg(_("print.sending")); - prefs.set("print.always_print_silent", args.bang); - prefs.set("print.show_print_progress", !args.bang); + if (false) + prefs.set("print.show_print_progress", !args.bang); + } - config.browser.contentWindow.print(); - }); + config.browser.contentWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebBrowserPrint).print(settings, null); - if (arg) - dactyl.echomsg(_("print.printed", arg.substr(1))); - else - dactyl.echomsg(_("print.sent")); + dactyl.echomsg(_("print.sent")); }, { argCount: "?", bang: true, + completer: function (context, args) { + if (args.bang && /^>/.test(context.filter)) + context.fork("file", 1, modules.completion, "file"); + }, literal: 0 }); @@ -1638,7 +1877,7 @@ var Buffer = Module("Buffer", { function (args) { let arg = args[0] || ""; - let titles = buffer.alternateStyleSheets.map(function (stylesheet) stylesheet.title); + let titles = buffer.alternateStyleSheets.map(sheet => sheet.title); dactyl.assert(!arg || titles.indexOf(arg) >= 0, _("error.invalidArgument", arg)); @@ -1679,7 +1918,7 @@ var Buffer = Module("Buffer", { function (file) { let output = io.system(filename.substr(1), file); commandline.command = command; - commandline.commandOutput({output}); + commandline.commandOutput(["span", { highlight: "CmdOutput" }, output]); }); if (/^>>/.test(filename)) { @@ -1705,7 +1944,7 @@ var Buffer = Module("Buffer", { dactyl.assert(args.bang || !file.exists(), _("io.exists")); - chosenData = { file: file, uri: util.newURI(doc.location.href) }; + chosenData = { file: file.file, uri: util.newURI(doc.location.href) }; } // if browser.download.useDownloadDir = false then the "Save As" @@ -1723,7 +1962,7 @@ var Buffer = Module("Buffer", { window.internalSave(doc.location.href, doc, null, contentDisposition, doc.contentType, false, null, chosenData, doc.referrer ? window.makeURI(doc.referrer) : null, - true); + doc, true); }, { argCount: "?", @@ -1806,7 +2045,8 @@ var Buffer = Module("Buffer", { context.incomplete = false; try { if (/filename="(.*?)"/.test(xhr.getResponseHeader("Content-Disposition"))) - context.completions.push([decodeURIComponent(RegExp.$1), _("buffer.save.suggested")]); + context.completions.push([decodeURIComponent(RegExp.$1), + _("buffer.save.suggested")]); } finally { context.completions = context.completions.slice(); @@ -1829,7 +2069,7 @@ var Buffer = Module("Buffer", { events: function initEvents(dactyl, modules, window) { let { buffer, config, events } = modules; - events.listen(config.browser, "scroll", buffer.closure._updateBufferPosition, false); + events.listen(config.browser, "scroll", buffer.bound._updateBufferPosition, false); }, mappings: function initMappings(dactyl, modules, window) { let { Editor, Events, buffer, editor, events, ex, mappings, modes, options, tabs } = modules; @@ -1842,24 +2082,23 @@ var Buffer = Module("Buffer", { uri.query = uri.query.replace(/(?:^|&)utm_[^&]+/g, "") .replace(/^&/, ""); - let link = DOM("link[href][rev=canonical], link[href][rel=shortlink]", doc); - let url = link.length && options.get("yankshort").getKey(uri) ? link.attr("href") : uri.spec; + let url = options.get("yankshort").getKey(uri) && buffer.shortURL || uri.spec; dactyl.clipboardWrite(url, true); }); mappings.add([modes.NORMAL], ["", ""], "Increment last number in URL", - function (args) { buffer.incrementURL(Math.max(args.count, 1)); }, + function ({ count }) { buffer.incrementURL(Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["", ""], "Decrement last number in URL", - function (args) { buffer.incrementURL(-Math.max(args.count, 1)); }, + function ({ count }) { buffer.incrementURL(-Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["gu", ""], "Go to parent directory", - function (args) { buffer.climbUrlPath(Math.max(args.count, 1)); }, + function ({ count }) { buffer.climbUrlPath(Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["gU", ""], @@ -1868,9 +2107,9 @@ var Buffer = Module("Buffer", { mappings.add([modes.COMMAND], [".", ""], "Repeat the last key event", - function (args) { + function ({ count }) { if (mappings.repeat) { - for (let i in util.interruptibleRange(0, Math.max(args.count, 1), 100)) + for (let i in util.interruptibleRange(0, Math.max(count, 1), 100)) mappings.repeat(); } }, @@ -1887,22 +2126,22 @@ var Buffer = Module("Buffer", { // scrolling mappings.add([modes.NORMAL], ["j", "", "", ""], "Scroll document down", - function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollVertical("lines", Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["k", "", "", ""], "Scroll document up", - function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollVertical("lines", -Math.max(count, 1)); }, { count: true }); - mappings.add([modes.COMMAND], dactyl.has("mail") ? ["h", ""] : ["h", "", ""], + mappings.add([modes.NORMAL], dactyl.has("mail") ? ["h", ""] : ["h", "", ""], "Scroll document to the left", - function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollHorizontal("columns", -Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], dactyl.has("mail") ? ["l", ""] : ["l", "", ""], "Scroll document to the right", - function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollHorizontal("columns", Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["0", "^", ""], @@ -1915,112 +2154,113 @@ var Buffer = Module("Buffer", { mappings.add([modes.NORMAL], ["gg", "", ""], "Go to the top of the document", - function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); }, + function ({ count }) { buffer.scrollToPercent(null, count != null ? count : 0, + count != null ? 0 : -1); }, { count: true }); mappings.add([modes.NORMAL], ["G", "", ""], "Go to the end of the document", - function (args) { - if (args.count) + function ({ count }) { + if (count) var elem = options.get("linenumbers") .getLine(buffer.focusedFrame.document, - args.count); + count); if (elem) elem.scrollIntoView(true); - else if (args.count) - buffer.scrollToPosition(null, args.count); + else if (count) + buffer.scrollToPosition(null, count); else - buffer.scrollToPercent(null, 100); + buffer.scrollToPercent(null, 100, 1); }, { count: true }); mappings.add([modes.NORMAL], ["%", ""], "Scroll to {count} percent of the document", - function (args) { - dactyl.assert(args.count > 0 && args.count <= 100); - buffer.scrollToPercent(null, args.count); + function ({ count }) { + dactyl.assert(count > 0 && count <= 100); + buffer.scrollToPercent(null, count); }, { count: true }); mappings.add([modes.NORMAL], ["", ""], "Scroll window downwards in the buffer", - function (args) { buffer._scrollByScrollSize(args.count, true); }, + function ({ count }) { buffer._scrollByScrollSize(count, true); }, { count: true }); mappings.add([modes.NORMAL], ["", ""], "Scroll window upwards in the buffer", - function (args) { buffer._scrollByScrollSize(args.count, false); }, + function ({ count }) { buffer._scrollByScrollSize(count, false); }, { count: true }); mappings.add([modes.NORMAL], ["", "", "", ""], "Scroll up a full page", - function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollVertical("pages", -Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], [""], "Scroll down a full page", - function (args) { + function ({ count }) { if (isinstance((services.focus.focusedWindow || buffer.win).document.activeElement, [Ci.nsIDOMHTMLInputElement, Ci.nsIDOMHTMLButtonElement, Ci.nsIDOMXULButtonElement])) return Events.PASS; - buffer.scrollVertical("pages", Math.max(args.count, 1)); + buffer.scrollVertical("pages", Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["", "", ""], "Scroll down a full page", - function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); }, + function ({ count }) { buffer.scrollVertical("pages", Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["]f", ""], "Focus next frame", - function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); }, + function ({ count }) { buffer.shiftFrameFocus(Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["[f", ""], "Focus previous frame", - function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); }, + function ({ count }) { buffer.shiftFrameFocus(-Math.max(count, 1)); }, { count: true }); mappings.add([modes.NORMAL], ["["], "Jump to the previous element as defined by 'jumptags'", - function (args) { buffer.findJump(args.arg, args.count, true); }, + function ({ arg, count }) { buffer.findJump(arg, count, true); }, { arg: true, count: true }); mappings.add([modes.NORMAL], ["g]"], "Jump to the next off-screen element as defined by 'jumptags'", - function (args) { buffer.findJump(args.arg, args.count, false, true); }, + function ({ arg, count }) { buffer.findJump(arg, count, false, true); }, { arg: true, count: true }); mappings.add([modes.NORMAL], ["]"], "Jump to the next element as defined by 'jumptags'", - function (args) { buffer.findJump(args.arg, args.count, false); }, + function ({ arg, count }) { buffer.findJump(arg, count, false); }, { arg: true, count: true }); mappings.add([modes.NORMAL], ["{"], "Jump to the previous paragraph", - function (args) { buffer.findJump("p", args.count, true); }, + function ({ count }) { buffer.findJump("p", count, true); }, { count: true }); mappings.add([modes.NORMAL], ["}"], "Jump to the next paragraph", - function (args) { buffer.findJump("p", args.count, false); }, + function ({ count }) { buffer.findJump("p", count, false); }, { count: true }); mappings.add([modes.NORMAL], ["]]", ""], "Follow the link labeled 'next' or '>' if it exists", - function (args) { - buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true); + function ({ count }) { + buffer.findLink("next", options["nextpattern"], (count || 1) - 1, true); }, { count: true }); mappings.add([modes.NORMAL], ["[[", ""], "Follow the link labeled 'prev', 'previous' or '<' if it exists", - function (args) { - buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true); + function ({ count }) { + buffer.findLink("prev", options["previouspattern"], (count || 1) - 1, true); }, { count: true }); @@ -2034,15 +2274,15 @@ var Buffer = Module("Buffer", { mappings.add([modes.NORMAL], ["gi", ""], "Focus last used input field", - function (args) { + function ({ count }) { let elem = buffer.lastInputField; - if (args.count >= 1 || !elem || !events.isContentNode(elem)) { + if (count >= 1 || !elem || !events.isContentNode(elem)) { let xpath = ["frame", "iframe", "input", "xul:textbox", "textarea[not(@disabled) and not(@readonly)]"]; let frames = buffer.allFrames(null, true); - let elements = array.flatten(frames.map(function (win) [m for (m in DOM.XPath(xpath, win.document))])) + let elements = array.flatten(frames.map(win => [m for (m in DOM.XPath(xpath, win.document))])) .filter(function (elem) { if (isinstance(elem, [Ci.nsIDOMHTMLFrameElement, Ci.nsIDOMHTMLIFrameElement])) @@ -2050,7 +2290,7 @@ var Buffer = Module("Buffer", { elem = DOM(elem); - if (elem[0].readOnly || !DOM(elem).isEditable) + if (elem[0].readOnly || elem[0].disabled || !DOM(elem).isEditable) return false; let style = elem.style; @@ -2061,7 +2301,7 @@ var Buffer = Module("Buffer", { }); dactyl.assert(elements.length > 0); - elem = elements[Math.constrain(args.count, 1, elements.length) - 1]; + elem = elements[Math.constrain(count, 1, elements.length) - 1]; } buffer.focusElement(elem); DOM(elem).scrollIntoView(); @@ -2117,52 +2357,52 @@ var Buffer = Module("Buffer", { // zooming mappings.add([modes.NORMAL], ["zi", "+", ""], "Enlarge text zoom of current web page", - function (args) { buffer.zoomIn(Math.max(args.count, 1), false); }, + function ({ count }) { buffer.zoomIn(Math.max(count, 1), false); }, { count: true }); mappings.add([modes.NORMAL], ["zm", ""], "Enlarge text zoom of current web page by a larger amount", - function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); }, + function ({ count }) { buffer.zoomIn(Math.max(count, 1) * 3, false); }, { count: true }); mappings.add([modes.NORMAL], ["zo", "-", ""], "Reduce text zoom of current web page", - function (args) { buffer.zoomOut(Math.max(args.count, 1), false); }, + function ({ count }) { buffer.zoomOut(Math.max(count, 1), false); }, { count: true }); mappings.add([modes.NORMAL], ["zr", ""], "Reduce text zoom of current web page by a larger amount", - function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); }, + function ({ count }) { buffer.zoomOut(Math.max(count, 1) * 3, false); }, { count: true }); mappings.add([modes.NORMAL], ["zz", ""], "Set text zoom value of current web page", - function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); }, + function ({ count }) { buffer.setZoom(count > 1 ? count : 100, false); }, { count: true }); mappings.add([modes.NORMAL], ["ZI", "zI", ""], "Enlarge full zoom of current web page", - function (args) { buffer.zoomIn(Math.max(args.count, 1), true); }, + function ({ count }) { buffer.zoomIn(Math.max(count, 1), true); }, { count: true }); mappings.add([modes.NORMAL], ["ZM", "zM", ""], "Enlarge full zoom of current web page by a larger amount", - function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); }, + function ({ count }) { buffer.zoomIn(Math.max(count, 1) * 3, true); }, { count: true }); mappings.add([modes.NORMAL], ["ZO", "zO", ""], "Reduce full zoom of current web page", - function (args) { buffer.zoomOut(Math.max(args.count, 1), true); }, + function ({ count }) { buffer.zoomOut(Math.max(count, 1), true); }, { count: true }); mappings.add([modes.NORMAL], ["ZR", "zR", ""], "Reduce full zoom of current web page by a larger amount", - function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); }, + function ({ count }) { buffer.zoomOut(Math.max(count, 1) * 3, true); }, { count: true }); mappings.add([modes.NORMAL], ["zZ", ""], "Set full zoom value of current web page", - function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); }, + function ({ count }) { buffer.setZoom(count > 1 ? count : 100, true); }, { count: true }); // page info @@ -2224,17 +2464,20 @@ var Buffer = Module("Buffer", { return vals; }, validator: function (value) DOM.validateMatcher.call(this, value) - && Object.keys(value).every(function (v) v.length == 1) + && Object.keys(value).every(v => v.length == 1) }); options.add(["linenumbers", "ln"], "Patterns used to determine line numbers used by G", "sitemap", { + // Make sure to update the docs when you change this. + "view-source:*": 'body,[id^=line]', "code.google.com": '#nums [id^="nums_table"] a[href^="#"]', "github.com": '.line_numbers>*', "mxr.mozilla.org": 'a.l', "pastebin.com": '#code_frame>div>ol>li', "addons.mozilla.org": '.gutter>.line>a', + "bugzilla.mozilla.org": ".bz_comment:not(.bz_first_comment):not(.ih_history)", "*": '/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr' }, { @@ -2245,9 +2488,9 @@ var Buffer = Module("Buffer", { if (/^func:/.test(filter.result)) var res = dactyl.userEval("(" + Option.dequote(filter.result.substr(5)) + ")")(doc, line); else - res = iter.nth(filter.matcher(doc), - function (elem) (elem.nodeValue || elem.textContent).trim() == line && DOM(elem).display != "none", - 0) + res = iter.find(filter.matcher(doc), + elem => ((elem.nodeValue || elem.textContent).trim() == line && + DOM(elem).display != "none")) || iter.nth(filter.matcher(doc), util.identity, line - 1); if (res) break; @@ -2277,12 +2520,12 @@ var Buffer = Module("Buffer", { options.add(["nextpattern"], "Patterns to use when guessing the next page in a document sequence", - "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), + "regexplist", UTF8(/'^Next [>»]','^Next »','\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), { regexpFlags: "i" }); options.add(["previouspattern"], "Patterns to use when guessing the previous page in a document sequence", - "regexplist", UTF8(/'\bprev|previous\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source), + "regexplist", UTF8(/'[<«] Prev$','« Prev$','\bprev(ious)?\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source), { regexpFlags: "i" }); options.add(["pageinfo", "pa"], @@ -2354,9 +2597,9 @@ Buffer.addPageInfoSection("e", "Search Engines", function (verbose) { if (verbose) for (let link in engines) yield [link.title || /*L*/ "Engine " + n++, - {link.href}]; + ["a", { href: link.href, highlight: "URL", + onclick: "if (event.button == 0) { window.external.AddSearchProvider(this.href); return false; }" }, + link.href]]; } if (!verbose && nEngines) @@ -2412,7 +2655,8 @@ Buffer.addPageInfoSection("f", "Feeds", function (verbose) { nFeed++; let type = feedTypes[feed.type] || "RSS"; if (verbose) - yield [feed.title, template.highlightURL(feed.href, true) +  ({type})]; + yield [feed.title, [template.highlightURL(feed.href, true), + ["span", { class: "extra-info" }, " (" + type + ")"]]]; } } @@ -2462,6 +2706,10 @@ Buffer.addPageInfoSection("g", "General Info", function (verbose) { yield ["Title", doc.title]; yield ["URL", template.highlightURL(doc.location.href, true)]; + let { shortURL } = this; + if (shortURL) + yield ["Short URL", template.highlightURL(shortURL, true)]; + let ref = "referrer" in doc && doc.referrer; if (ref) yield ["Referrer", template.highlightURL(ref, true)]; @@ -2484,12 +2732,13 @@ Buffer.addPageInfoSection("m", "Meta Tags", function (verbose) { // get meta tag data, sort and put into pageMeta[] let metaNodes = this.focusedFrame.document.getElementsByTagName("meta"); - return Array.map(metaNodes, function (node) [(node.name || node.httpEquiv), template.highlightURL(node.content)]) - .sort(function (a, b) util.compareIgnoreCase(a[0], b[0])); + return Array.map(metaNodes, node => [(node.name || node.httpEquiv), + template.highlightURL(node.content)]) + .sort((a, b) => util.compareIgnoreCase(a[0], b[0])); }); Buffer.addPageInfoSection("s", "Security", function (verbose) { - let { statusline } = this.modules + let { statusline } = this.modules; let identity = this.topWindow.gIdentityHandler; @@ -2518,16 +2767,25 @@ Buffer.addPageInfoSection("s", "Security", function (verbose) { yield ["Verified by", data.caOrg]; - if (identity._overrideService.hasMatchingOverride(identity._lastLocation.hostname, - (identity._lastLocation.port || 443), - data.cert, {}, {})) + let { host, port } = identity._lastUri; + if (port == -1) + port = 443; + + if (identity._overrideService.hasMatchingOverride(host, port, data.cert, {}, {})) yield ["User exception", /*L*/"true"]; break; } }); -} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } +// internal navigation doesn't currently update link[rel='shortlink'] +Buffer.addURIShortener("youtube.com", (uri, doc) => { + let video = array.toObject(uri.query.split("&") + .map(p => p.split("="))).v; + return video ? util.newURI("http://youtu.be/" + video) : null; +}); + +// catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } endModule(); -// vim: set fdm=marker sw=4 ts=4 et ft=javascript: +// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: