X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fcontent%2Fhints.js;fp=common%2Fcontent%2Fhints.js;h=004f90716208469a8c42eaa10e952bb680ed1f91;hb=9044153cb63835e39b9de8ec4ade237c03e3888a;hp=a30ef295057f33655020e429bf58aacb61b918f0;hpb=70740024f9c028c1fd63e1a1850ab062ff956054;p=dactyl.git diff --git a/common/content/hints.js b/common/content/hints.js index a30ef29..004f907 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -4,7 +4,7 @@ // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. -"use strict"; +/* use strict */ /** @scope modules */ /** @instance hints */ @@ -17,9 +17,8 @@ var HintSession = Class("HintSession", CommandMode, { opts = opts || {}; - // Hack. - if (!opts.window && modes.main == modes.OUTPUT_MULTILINE) - opts.window = commandline.widgets.multilineOutput.contentWindow; + if (!opts.window) + opts.window = modes.getStack(0).params.window; this.hintMode = hints.modes[mode]; dactyl.assert(this.hintMode); @@ -27,7 +26,7 @@ var HintSession = Class("HintSession", CommandMode, { this.activeTimeout = null; // needed for hinttimeout > 0 this.continue = Boolean(opts.continue); this.docs = []; - this.hintKeys = events.fromString(options["hintkeys"]).map(events.closure.toString); + this.hintKeys = DOM.Event.parse(options["hintkeys"]).map(DOM.Event.closure.stringify); this.hintNumber = 0; this.hintString = opts.filter || ""; this.pageHints = []; @@ -35,6 +34,7 @@ var HintSession = Class("HintSession", CommandMode, { this.usedTabKey = false; this.validHints = []; // store the indices of the "hints" array with valid elements + mappings.pushCommand(); this.open(); this.top = opts.window || content; @@ -98,6 +98,8 @@ var HintSession = Class("HintSession", CommandMode, { leave.superapply(this, arguments); if (!stack.push) { + mappings.popCommand(); + if (hints.hintSession == this) hints.hintSession = null; if (this.top) { @@ -255,7 +257,7 @@ var HintSession = Class("HintSession", CommandMode, { getContainerOffsets: function _getContainerOffsets(doc) { let body = doc.body || doc.documentElement; // TODO: getComputedStyle returns null for Facebook channel_iframe doc - probable Gecko bug. - let style = util.computedStyle(body); + let style = DOM(body).style; if (style && /^(absolute|fixed|relative)$/.test(style.position)) { let rect = body.getClientRects()[0]; @@ -298,7 +300,7 @@ var HintSession = Class("HintSession", CommandMode, { return false; if (!rect.width || !rect.height) - if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && util.computedStyle(elem).float != "none" && isVisible(elem))) + if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && DOM(elem).style.float != "none" && isVisible(elem))) return false; let computedStyle = doc.defaultView.getComputedStyle(elem, null); @@ -309,12 +311,11 @@ var HintSession = Class("HintSession", CommandMode, { let body = doc.body || doc.querySelector("body"); if (body) { - let fragment = util.xmlToDom(
, doc); - body.appendChild(fragment); - util.computedStyle(fragment).height; // Force application of binding. - let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment; + let fragment = DOM(
, doc).appendTo(body); + fragment.style.height; // Force application of binding. + let container = doc.getAnonymousElementByAttribute(fragment[0], "anonid", "hints") || fragment[0]; - let baseNodeAbsolute = util.xmlToDom(, doc); + let baseNode = DOM(, doc)[0]; let mode = this.hintMode; let res = mode.matcher(doc); @@ -342,7 +343,7 @@ var HintSession = Class("HintSession", CommandMode, { else hint.text = elem.textContent.toLowerCase(); - hint.span = baseNodeAbsolute.cloneNode(false); + hint.span = baseNode.cloneNode(false); let leftPos = Math.max((rect.left + offsetX), offsetX); let topPos = Math.max((rect.top + offsetY), offsetY); @@ -402,7 +403,7 @@ var HintSession = Class("HintSession", CommandMode, { */ onKeyPress: function onKeyPress(eventList) { const KILL = false, PASS = true; - let key = events.toString(eventList[0]); + let key = DOM.Event.stringify(eventList[0]); this.clearTimeout(); @@ -500,6 +501,7 @@ var HintSession = Class("HintSession", CommandMode, { this.timeout(next, 50); }).call(this); + mappings.pushCommand(); if (!this.continue) { modes.pop(); if (timeout) @@ -509,6 +511,7 @@ var HintSession = Class("HintSession", CommandMode, { dactyl.trapErrors("action", this.hintMode, elem, elem.href || elem.src || "", this.extendedhintCount, top); + mappings.popCommand(); this.timeout(function () { if (modes.main == modes.IGNORE && !this.continue) @@ -532,7 +535,7 @@ var HintSession = Class("HintSession", CommandMode, { // Goddamn stupid fucking Gecko 1.x security manager bullshit. try { delete doc.dactylLabels; } catch (e) { doc.dactylLabels = undefined; } - for (let elem in util.evaluateXPath("//*[@dactyl:highlight='hints']", doc)) + for (let elem in DOM.XPath("//*[@dactyl:highlight='hints']", doc)) elem.parentNode.removeChild(elem); for (let i in util.range(start, end + 1)) { this.pageHints[i].ambiguous = false; @@ -754,9 +757,9 @@ var Hints = Module("hints", { 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) dactyl.clipboardWrite(loc, true)); - this.addMode("Y", "Yank hint description", function (elem) dactyl.clipboardWrite(elem.textContent || "", true)); - this.addMode("c", "Open context menu", function (elem) buffer.openContextMenu(elem)); + 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("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)); @@ -824,7 +827,7 @@ var Hints = Module("hints", { let type = elem.type; - if (elem instanceof HTMLInputElement && Set.has(util.editableInputs, elem.type)) + if (DOM(elem).isInput) return [elem.value, false]; else { for (let [, option] in Iterator(options["hintinputs"])) { @@ -1031,15 +1034,21 @@ var Hints = Module("hints", { open: function open(mode, opts) { this._extendedhintCount = opts.count; - commandline.input(["Normal", mode], "", { + + opts = opts || {}; + + mappings.pushCommand(); + commandline.input(["Normal", mode], null, { autocomplete: false, completer: function (context) { context.compare = function () 0; context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))]; }, + onCancel: mappings.closure.popCommand, onSubmit: function (arg) { if (arg) hints.show(arg, opts); + mappings.popCommand(); }, onChange: function (arg) { if (Object.keys(hints.modes).some(function (m) m != arg && m.indexOf(arg) == 0)) @@ -1077,7 +1086,24 @@ var Hints = Module("hints", { this.hintSession = HintSession(mode, opts); } }, { - translitTable: Class.memoize(function () { + 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))) + return false; + + let win = elem.ownerDocument.defaultView; + if (offScreen && (rect.top + win.scrollY < 0 || rect.left + win.scrollX < 0 || + rect.bottom + win.scrollY > win.scrolMaxY + win.innerHeight || + rect.right + win.scrollX > win.scrolMaxX + win.innerWidth)) + return false; + + if (!DOM(elem).isVisible) + return false; + return true; + }, + + translitTable: Class.Memoize(function () { const table = {}; [ [0x00c0, 0x00c6, ["A"]], [0x00c7, 0x00c7, ["C"]], @@ -1191,53 +1217,58 @@ var Hints = Module("hints", { }); }, mappings: function () { - var myModes = config.browserModes.concat(modes.OUTPUT_MULTILINE); - mappings.add(myModes, ["f"], + let bind = function bind(names, description, action, params) + mappings.add(config.browserModes, names, description, + action, params); + + bind(["f"], "Start Hints mode", function () { hints.show("o"); }); - mappings.add(myModes, ["F"], + bind(["F"], "Start Hints mode, but open link in a new tab", function () { hints.show(options.get("activate").has("links") ? "t" : "b"); }); - mappings.add(myModes, [";"], + bind([";"], "Start an extended hints mode", function ({ count }) { hints.open(";", { count: count }); }, { count: true }); - mappings.add(myModes, ["g;"], + bind(["g;"], "Start an extended hints mode and stay there until is pressed", function ({ count }) { hints.open("g;", { continue: true, count: count }); }, { count: true }); - mappings.add(modes.HINTS, [""], + let bind = function bind(names, description, action, params) + mappings.add([modes.HINTS], names, description, + action, params); + + bind([""], "Follow the selected hint", function ({ self }) { self.update(true); }); - mappings.add(modes.HINTS, [""], + bind([""], "Focus the next matching hint", function ({ self }) { self.tab(false); }); - mappings.add(modes.HINTS, [""], + bind([""], "Focus the previous matching hint", function ({ self }) { self.tab(true); }); - mappings.add(modes.HINTS, ["", ""], + bind(["", ""], "Delete the previous character", function ({ self }) self.backspace()); - mappings.add(modes.HINTS, [""], + bind([""], "Toggle hint filtering", function ({ self }) { self.escapeNumbers = !self.escapeNumbers; }); }, options: function () { - function xpath(arg) util.makeXPath(arg); - options.add(["extendedhinttags", "eht"], "XPath or CSS selector strings of hintable elements for extended hint modes", "regexpmap", { "[iI]": "img", - "[asOTvVWy]": ["a[href]", "area[href]", "img[src]", "iframe[src]"], + "[asOTvVWy]": [":-moz-any-link", "area[href]", "img[src]", "iframe[src]"], "[f]": "body", "[F]": ["body", "code", "div", "html", "p", "pre", "span"], "[S]": ["input:not([type=hidden])", "textarea", "button", "select"] @@ -1249,23 +1280,23 @@ var Hints = Module("hints", { res ? res.matcher : default_, setter: function (vals) { for (let value in values(vals)) - value.matcher = util.compileMatcher(Option.splitList(value.result)); + value.matcher = DOM.compileMatcher(Option.splitList(value.result)); return vals; }, - validator: util.validateMatcher + validator: DOM.validateMatcher }); options.add(["hinttags", "ht"], "XPath or CSS selector strings of hintable elements for Hints mode", - "stringlist", "input:not([type=hidden]),a[href],area,iframe,textarea,button,select," + + "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),select,textarea," + "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," + "[tabindex],[role=link],[role=button],[contenteditable=true]", { setter: function (values) { - this.matcher = util.compileMatcher(values); + this.matcher = DOM.compileMatcher(values); return values; }, - validator: util.validateMatcher + validator: DOM.validateMatcher }); options.add(["hintkeys", "hk"], @@ -1277,7 +1308,7 @@ var Hints = Module("hints", { "asdfg;lkjh": "Home Row" }, validator: function (value) { - let values = events.fromString(value).map(events.closure.toString); + let values = DOM.Event.parse(value).map(DOM.Event.closure.stringify); return Option.validIf(array.uniq(values).length === values.length && values.length > 1, _("option.hintkeys.duplicate")); }