X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fcontent%2Fhints.js;h=af09ad4dd499498ef16561971e67bb3a09af3c68;hb=354a049cce8415487552ce405cce167b7071fe1f;hp=96fe44acbbbfa5399fa2a7aed63596d6fa999051;hpb=5ebd29f56d17f62011cdd596b1d351947ee534ff;p=dactyl.git diff --git a/common/content/hints.js b/common/content/hints.js index 96fe44a..af09ad4 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -1,10 +1,10 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2011 by Kris Maglione +// Copyright (c) 2008-2013 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. -/* use strict */ +"use strict"; /** @scope modules */ /** @instance hints */ @@ -12,11 +12,9 @@ var HintSession = Class("HintSession", CommandMode, { get extendedMode() modes.HINTS, - init: function init(mode, opts) { + init: function init(mode, opts = {}) { init.supercall(this); - opts = opts || {}; - if (!opts.window) opts.window = modes.getStack(0).params.window; @@ -281,7 +279,7 @@ var HintSession = Class("HintSession", CommandMode, { let doc = win.document; - memoize(doc, "dactylLabels", function () + memoize(doc, "dactylLabels", () => iter([l.getAttribute("for"), l] for (l in array.iterValues(doc.querySelectorAll("label[for]")))) .toObject()); @@ -300,7 +298,9 @@ var HintSession = Class("HintSession", CommandMode, { return false; if (!rect.width || !rect.height) - if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && DOM(elem).style.float != "none" && isVisible(elem))) + if (!Array.some(elem.childNodes, elem => elem instanceof Element + && DOM(elem).style.float != "none" + && isVisible(elem))) if (elem.textContent || !elem.name) return false; @@ -312,11 +312,11 @@ var HintSession = Class("HintSession", CommandMode, { let body = doc.body || doc.querySelector("body"); if (body) { - let fragment = DOM(
, doc).appendTo(body); + let fragment = DOM(["div", { highlight: "hints" }], doc).appendTo(body); fragment.style.height; // Force application of binding. let container = doc.getAnonymousElementByAttribute(fragment[0], "anonid", "hints") || fragment[0]; - let baseNode = DOM(, doc)[0]; + let baseNode = DOM(["span", { highlight: "Hint", style: "display: none;" }], doc)[0]; let mode = this.hintMode; let res = mode.matcher(doc); @@ -337,9 +337,11 @@ var HintSession = Class("HintSession", CommandMode, { if (elem.hasAttributeNS(NS, "hint")) [hint.text, hint.showText] = [elem.getAttributeNS(NS, "hint"), true]; - else if (isinstance(elem, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement])) + else if (isinstance(elem, [Ci.nsIDOMHTMLInputElement, + Ci.nsIDOMHTMLSelectElement, + Ci.nsIDOMHTMLTextAreaElement])) [hint.text, hint.showText] = hints.getInputHint(elem, doc); - else if (elem.firstElementChild instanceof HTMLImageElement && /^\s*$/.test(elem.textContent)) + else if (elem.firstElementChild instanceof Ci.nsIDOMHTMLImageElement && /^\s*$/.test(elem.textContent)) [hint.text, hint.showText] = [elem.firstElementChild.alt || elem.firstElementChild.title, true]; else hint.text = elem.textContent.toLowerCase(); @@ -349,7 +351,7 @@ var HintSession = Class("HintSession", CommandMode, { let leftPos = Math.max((rect.left + offsetX), offsetX); let topPos = Math.max((rect.top + offsetY), offsetY); - if (elem instanceof HTMLAreaElement) + if (elem instanceof Ci.nsIDOMHTMLAreaElement) [leftPos, topPos] = this.getAreaOffset(elem, leftPos, topPos); hint.span.setAttribute("style", ["display: none; left:", leftPos, "px; top:", topPos, "px"].join("")); @@ -471,7 +473,7 @@ var HintSession = Class("HintSession", CommandMode, { if (!followFirst) { let firstHref = this.validHints[0].elem.getAttribute("href") || null; if (firstHref) { - if (this.validHints.some(function (h) h.elem.getAttribute("href") != firstHref)) + if (this.validHints.some(h => h.elem.getAttribute("href") != firstHref)) return; } else if (this.validHints.length > 1) @@ -490,7 +492,11 @@ var HintSession = Class("HintSession", CommandMode, { let n = 5; (function next() { - let hinted = n || this.validHints.some(function (h) h.elem === elem); + if (Cu.isDeadWrapper && Cu.isDeadWrapper(elem)) + // Hint document has been unloaded. + return; + + let hinted = n || this.validHints.some(h => h.elem === elem); if (!hinted) hints.setClass(elem, null); else if (n) @@ -569,7 +575,9 @@ var HintSession = Class("HintSession", CommandMode, { /** * Display the hints in pageHints that are still valid. */ + showCount: 0, show: function _show() { + let count = ++this.showCount; let hintnum = 1; let validHint = hints.hintMatcher(this.hintString.toLowerCase()); let activeHint = this.hintNumber || 1; @@ -581,19 +589,22 @@ var HintSession = Class("HintSession", CommandMode, { inner: for (let i in (util.interruptibleRange(start, end + 1, 500))) { + if (this.showCount != count) + return; + let hint = this.pageHints[i]; hint.valid = validHint(hint.text); if (!hint.valid) continue inner; - if (hint.text == "" && hint.elem.firstChild && hint.elem.firstChild instanceof HTMLImageElement) { + if (hint.text == "" && hint.elem.firstChild && hint.elem.firstChild instanceof Ci.nsIDOMHTMLImageElement) { if (!hint.imgSpan) { let rect = hint.elem.firstChild.getBoundingClientRect(); if (!rect) continue; - hint.imgSpan = DOM(, doc).css({ + hint.imgSpan = DOM(["span", { highlight: "Hint", "dactyl:hl": "HintImage" }], doc).css({ display: "none", left: (rect.left + offsetX) + "px", top: (rect.top + offsetY) + "px", @@ -605,7 +616,7 @@ var HintSession = Class("HintSession", CommandMode, { let str = this.getHintString(hintnum); let text = []; - if (hint.elem instanceof HTMLInputElement) + if (hint.elem instanceof Ci.nsIDOMHTMLInputElement) if (hint.elem.type === "radio") text.push(UTF8(hint.elem.checked ? "⊙" : "○")); else if (hint.elem.type === "checkbox") @@ -745,33 +756,35 @@ var Hints = Module("hints", { this.modes = {}; this.addMode(";", "Focus hint", buffer.closure.focusElement); - this.addMode("?", "Show information for hint", function (elem) buffer.showElementInfo(elem)); - this.addMode("s", "Save hint", function (elem) buffer.saveLink(elem, false)); - this.addMode("f", "Focus frame", function (elem) dactyl.focus(elem.ownerDocument.defaultView)); + this.addMode("?", "Show information for hint", elem => buffer.showElementInfo(elem)); + // TODO: allow for ! override to overwrite existing paths -- where? --djk + this.addMode("s", "Save hint", elem => buffer.saveLink(elem, false)); + this.addMode("f", "Focus frame", elem => dactyl.focus(elem.ownerDocument.defaultView)); this.addMode("F", "Focus frame or pseudo-frame", buffer.closure.focusElement, isScrollable); - this.addMode("o", "Follow hint", function (elem) buffer.followLink(elem, dactyl.CURRENT_TAB)); - this.addMode("t", "Follow hint in a new tab", function (elem) buffer.followLink(elem, dactyl.NEW_TAB)); - this.addMode("b", "Follow hint in a background tab", function (elem) buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB)); - this.addMode("w", "Follow hint in a new window", function (elem) buffer.followLink(elem, dactyl.NEW_WINDOW)); - this.addMode("O", "Generate an ‘:open URL’ prompt", function (elem, loc) CommandExMode().open("open " + loc)); - this.addMode("T", "Generate a ‘:tabopen URL’ prompt", function (elem, loc) CommandExMode().open("tabopen " + loc)); - this.addMode("W", "Generate a ‘:winopen URL’ prompt", function (elem, loc) CommandExMode().open("winopen " + loc)); - this.addMode("a", "Add a bookmark", function (elem) bookmarks.addSearchKeyword(elem)); - this.addMode("S", "Add a search keyword", function (elem) bookmarks.addSearchKeyword(elem)); - this.addMode("v", "View hint source", function (elem, loc) buffer.viewSource(loc, false)); - this.addMode("V", "View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true)); - this.addMode("y", "Yank hint location", function (elem, loc) editor.setRegister(null, loc, true)); - this.addMode("Y", "Yank hint description", function (elem) editor.setRegister(null, elem.textContent || "", true)); + this.addMode("o", "Follow hint", elem => buffer.followLink(elem, dactyl.CURRENT_TAB)); + this.addMode("t", "Follow hint in a new tab", elem => buffer.followLink(elem, dactyl.NEW_TAB)); + this.addMode("b", "Follow hint in a background tab", elem => buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB)); + this.addMode("w", "Follow hint in a new window", elem => buffer.followLink(elem, dactyl.NEW_WINDOW)); + this.addMode("O", "Generate an ‘:open URL’ prompt", (elem, loc) => CommandExMode().open("open " + loc)); + this.addMode("T", "Generate a ‘:tabopen URL’ prompt", (elem, loc) => CommandExMode().open("tabopen " + loc)); + this.addMode("W", "Generate a ‘:winopen URL’ prompt", (elem, loc) => CommandExMode().open("winopen " + loc)); + this.addMode("a", "Add a bookmark", elem => bookmarks.addSearchKeyword(elem)); + this.addMode("S", "Add a search keyword", elem => bookmarks.addSearchKeyword(elem)); + this.addMode("v", "View hint source", (elem, loc) => buffer.viewSource(loc, false)); + this.addMode("V", "View hint source in external editor", (elem, loc) => buffer.viewSource(loc, true)); + this.addMode("y", "Yank hint location", (elem, loc) => editor.setRegister(null, loc, true)); + this.addMode("Y", "Yank hint description", elem => editor.setRegister(null, elem.textContent || "", true)); this.addMode("A", "Yank hint anchor url", function (elem) { let uri = elem.ownerDocument.documentURIObject.clone(); uri.ref = elem.id || elem.name; dactyl.clipboardWrite(uri.spec, true); }); - this.addMode("c", "Open context menu", function (elem) DOM(elem).contextmenu()); - this.addMode("i", "Show image", function (elem) dactyl.open(elem.src)); - this.addMode("I", "Show image in a new tab", function (elem) dactyl.open(elem.src, dactyl.NEW_TAB)); + this.addMode("c", "Open context menu", elem => DOM(elem).contextmenu()); + this.addMode("i", "Show image", elem => dactyl.open(elem.src)); + this.addMode("I", "Show image in a new tab", elem => dactyl.open(elem.src, dactyl.NEW_TAB)); - function isScrollable(elem) isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]) || + function isScrollable(elem) isinstance(elem, [Ci.nsIDOMHTMLFrameElement, + Ci.nsIDOMHTMLIFrameElement]) || Buffer.isScrollable(elem, 0, true) || Buffer.isScrollable(elem, 0, false); }, @@ -799,7 +812,7 @@ var Hints = Module("hints", { let update = eht.isDefault; let value = eht.parse(Option.quote(util.regexp.escape(mode)) + ":" + tags.map(Option.quote))[0]; - eht.defaultValue = eht.defaultValue.filter(function (re) toString(re) != toString(value)) + eht.defaultValue = eht.defaultValue.filter(re => toString(re) != toString(value)) .concat(value); if (update) @@ -840,7 +853,7 @@ var Hints = Module("hints", { else { for (let [, option] in Iterator(options["hintinputs"])) { if (option == "value") { - if (elem instanceof HTMLSelectElement) { + if (elem instanceof Ci.nsIDOMHTMLSelectElement) { if (elem.selectedIndex >= 0) return [elem.item(elem.selectedIndex).text.toLowerCase(), false]; } @@ -856,7 +869,8 @@ var Hints = Module("hints", { } else if (option == "label") { if (elem.id) { - let label = elem.ownerDocument.dactylLabels[elem.id]; + let label = (elem.ownerDocument.dactylLabels || {})[elem.id]; + // Urgh. if (label) return [label.textContent.toLowerCase(), true]; } @@ -901,7 +915,7 @@ var Hints = Module("hints", { let tokens = tokenize(/\s+/, hintString); return function (linkText) { linkText = linkText.toLowerCase(); - return tokens.every(function (token) indexOf(linkText, token) >= 0); + return tokens.every(token => indexOf(linkText, token) >= 0); }; } //}}} @@ -1040,16 +1054,14 @@ var Hints = Module("hints", { return null; }, //}}} - open: function open(mode, opts) { + open: function open(mode, opts = {}) { this._extendedhintCount = opts.count; - opts = opts || {}; - mappings.pushCommand(); commandline.input(["Normal", mode], null, { autocomplete: false, completer: function (context) { - context.compare = function () 0; + context.compare = () => 0; context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))]; }, onCancel: mappings.closure.popCommand, @@ -1059,7 +1071,7 @@ var Hints = Module("hints", { mappings.popCommand(); }, onChange: function (arg) { - if (Object.keys(hints.modes).some(function (m) m != arg && m.indexOf(arg) == 0)) + if (Object.keys(hints.modes).some(m => m != arg && m.indexOf(arg) == 0)) return; this.accepted = true; @@ -1097,7 +1109,9 @@ var Hints = Module("hints", { isVisible: function isVisible(elem, offScreen) { let rect = elem.getBoundingClientRect(); if (!rect.width || !rect.height) - if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && DOM(elem).style.float != "none" && isVisible(elem))) + if (!Array.some(elem.childNodes, elem => elem instanceof Element + && DOM(elem).style.float != "none" + && isVisible(elem))) return false; let win = elem.ownerDocument.defaultView; @@ -1286,7 +1300,7 @@ var Hints = Module("hints", { { keepQuotes: true, getKey: function (val, default_) - let (res = array.nth(this.value, function (re) let (match = re.exec(val)) match && match[0] == val, 0)) + let (res = array.nth(this.value, re => let (match = re.exec(val)) match && match[0] == val, 0)) res ? res.matcher : default_, parse: function parse(val) { let vals = parse.supercall(this, val); @@ -1294,14 +1308,14 @@ var Hints = Module("hints", { value.matcher = DOM.compileMatcher(Option.splitList(value.result)); return vals; }, - testValues: function testValues(vals, validator) vals.every(function (re) Option.splitList(re).every(validator)), + testValues: function testValues(vals, validator) vals.every(re => Option.splitList(re).every(validator)), validator: DOM.validateMatcher }); options.add(["hinttags", "ht"], "XPath or CSS selector strings of hintable elements for Hints mode", // Make sure to update the docs when you change this. - "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),select,textarea," + + "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),label[for],select,textarea," + "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," + "[tabindex],[role=link],[role=button],[contenteditable=true]", { @@ -1338,7 +1352,7 @@ var Hints = Module("hints", { { values: { "0": "Follow the first hint as soon as typed text uniquely identifies it. Follow the selected hint on .", - "1": "Follow the selected hint on .", + "1": "Follow the selected hint on ." } }); @@ -1354,7 +1368,8 @@ var Hints = Module("hints", { "transliterated": UTF8("When true, special latin characters are translated to their ASCII equivalents (e.g., é ⇒ e)") }, validator: function (values) Option.validateCompleter.call(this, values) && - 1 === values.reduce(function (acc, v) acc + (["contains", "custom", "firstletters", "wordstartswith"].indexOf(v) >= 0), 0) + 1 === values.reduce((acc, v) => acc + (["contains", "custom", "firstletters", "wordstartswith"].indexOf(v) >= 0), + 0) }); options.add(["wordseparators", "wsp"], @@ -1375,4 +1390,4 @@ var Hints = Module("hints", { } }); -// vim: set fdm=marker sw=4 ts=4 et: +// vim: set fdm=marker sw=4 sts=4 ts=8 et: