diff --git a/common/content/hints.js b/common/content/hints.js index bcf431ce..5f2b4c30 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -38,12 +38,13 @@ var HintSession = Class("HintSession", CommandMode, { this.open(); this.top = opts.window || content; - this.top.addEventListener("resize", hints.resizeTimer.closure.tell, true); - this.top.addEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, false, true); + this.top.addEventListener("resize", this.closure._onResize, true); + this.top.addEventListener("dactyl-commandupdate", this.closure._onResize, false, true); this.generate(); this.show(); + this.magic = true; if (this.validHints.length == 0) { dactyl.beep(); @@ -76,7 +77,6 @@ var HintSession = Class("HintSession", CommandMode, { this.span.style.display = (val ? "" : "none"); if (this.imgSpan) this.imgSpan.style.display = (val ? "" : "none"); - this.active = this.active; } }, @@ -92,8 +92,8 @@ var HintSession = Class("HintSession", CommandMode, { if (hints.hintSession == this) hints.hintSession = null; if (this.top) { - this.top.removeEventListener("resize", hints.resizeTimer.closure.tell, true); - this.top.removeEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, true); + this.top.removeEventListener("resize", this.closure._onResize, true); + this.top.removeEventListener("dactyl-commandupdate", this.closure._onResize, true); } this.removeHints(0); @@ -255,6 +255,11 @@ var HintSession = Class("HintSession", CommandMode, { let doc = win.document; + memoize(doc, "dactylLabels", function () + iter([l.getAttribute("for"), l] + for (l in array.iterValues(doc.querySelectorAll("label[for]")))) + .toObject()); + let [offsetX, offsetY] = this.getContainerOffsets(doc); offsets = offsets || { left: 0, right: 0, top: 0, bottom: 0 }; @@ -281,17 +286,24 @@ var HintSession = Class("HintSession", CommandMode, { util.computedStyle(fragment).height; // Force application of binding. let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment; - let baseNodeAbsolute = util.xmlToDom(, doc); + let baseNodeAbsolute = util.xmlToDom(, doc); let mode = this.hintMode; let res = mode.matcher(doc); let start = this.pageHints.length; - for (let elem in res) { - let hint = { elem: elem, showText: false, __proto__: this.Hint }; + let _hints = []; + for (let elem in res) + if (isVisible(elem) && (!mode.filter || mode.filter(elem))) + _hints.push({ + elem: elem, + rect: elem.getClientRects()[0] || elem.getBoundingClientRect(), + showText: false, + __proto__: this.Hint + }); - if (!isVisible(elem) || mode.filter && !mode.filter(elem)) - continue; + for (let hint in values(_hints)) { + let { elem, rect } = hint; if (elem.hasAttributeNS(NS, "hint")) [hint.text, hint.showText] = [elem.getAttributeNS(NS, "hint"), true]; @@ -302,17 +314,15 @@ var HintSession = Class("HintSession", CommandMode, { else hint.text = elem.textContent.toLowerCase(); - hint.span = baseNodeAbsolute.cloneNode(true); + hint.span = baseNodeAbsolute.cloneNode(false); - let rect = elem.getClientRects()[0] || elem.getBoundingClientRect(); let leftPos = Math.max((rect.left + offsetX), offsetX); let topPos = Math.max((rect.top + offsetY), offsetY); if (elem instanceof HTMLAreaElement) [leftPos, topPos] = this.getAreaOffset(elem, leftPos, topPos); - hint.span.style.left = leftPos + "px"; - hint.span.style.top = topPos + "px"; + hint.span.setAttribute("style", ["display: none; left:", leftPos, "px; top:", topPos, "px"].join("")); container.appendChild(hint.span); this.pageHints.push(hint); @@ -399,12 +409,17 @@ var HintSession = Class("HintSession", CommandMode, { return PASS; }, - onResize: function () { + onResize: function onResize() { this.removeHints(0); this.generate(this.top); this.show(); }, + _onResize: function _onResize() { + if (this.magic) + hints.resizeTimer.tell(); + }, + /** * Finish hinting. * @@ -491,6 +506,7 @@ var HintSession = Class("HintSession", CommandMode, { */ removeHints: function _removeHints(timeout) { for (let { doc, start, end } in values(this.docs)) { + delete doc.dactylLabels; for (let elem in util.evaluateXPath("//*[@dactyl:highlight='hints']", doc)) elem.parentNode.removeChild(elem); for (let i in util.range(start, end + 1)) @@ -776,15 +792,14 @@ var Hints = Module("hints", { return [elem.alt.toLowerCase(), true]; } else if (elem.value && type != "password") { - // radio's and checkboxes often use internal ids as values - maybe make this an option too... + // radios and checkboxes often use internal ids as values - maybe make this an option too... if (! ((type == "radio" || type == "checkbox") && !isNaN(elem.value))) return [elem.value.toLowerCase(), (type == "radio" || type == "checkbox")]; } } else if (option == "label") { if (elem.id) { - // TODO: (possibly) do some guess work for label-like objects - let label = util.evaluateXPath(["label[@for=" + elem.id.quote() + "]"], doc).snapshotItem(0); + let label = elem.ownerDocument.dactylLabels[elem.id]; if (label) return [label.textContent.toLowerCase(), true]; } diff --git a/common/modules/highlight.jsm b/common/modules/highlight.jsm index 35288942..713f2989 100644 --- a/common/modules/highlight.jsm +++ b/common/modules/highlight.jsm @@ -100,7 +100,7 @@ var Highlights = Module("Highlight", { __iterator__: function () values(this.highlight).sort(function (a, b) String.localeCompare(a.class, b.class)) .iterValues(), - _create: function (agent, args) { + _create: function _create(agent, args) { let obj = Highlight.apply(Highlight, args); if (!isArray(obj.sites)) @@ -143,9 +143,9 @@ var Highlights = Module("Highlight", { return obj; }, - get: function (k) this.highlight[k], + get: function get(k) this.highlight[k], - set: function (key, newStyle, force, append, extend) { + set: function set(key, newStyle, force, append, extend) { let [, class_, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/); let highlight = this.highlight[key] || this._create(false, [key]); @@ -181,7 +181,7 @@ var Highlights = Module("Highlight", { * Clears all highlighting rules. Rules with default values are * reset. */ - clear: function () { + clear: function clear() { for (let [k, v] in Iterator(this.highlight)) this.set(k, null, true); }, @@ -193,7 +193,7 @@ var Highlights = Module("Highlight", { * @param {Node} node * @param {string} group */ - highlightNode: function (node, group, applyBindings) { + highlightNode: function highlightNode(node, group, applyBindings) { node.setAttributeNS(NS.uri, "highlight", group); let groups = group.split(" "); @@ -214,7 +214,7 @@ var Highlights = Module("Highlight", { * * @param {string} class */ - selector: function (class_) + selector: function selector(class_) let (self = this) class_.replace(/(^|[>\s])([A-Z][\w-]+)\b/g, function (m, n1, hl) n1 + @@ -275,7 +275,7 @@ var Highlights = Module("Highlight", { * @param {string} css The rules to load. See {@link Highlights#css}. * @param {boolean} eager When true, load all provided rules immediately. */ - loadCSS: function (css, eager) { + loadCSS: function loadCSS(css, eager) { String.replace(css, this.groupRegexp, function (m, m1, m2) m1 + " " + m2.replace(/\n\s*/g, " ")) .split("\n").filter(function (s) /\S/.test(s) && !/^\s*\/\//.test(s)) .forEach(function (highlight) { @@ -291,7 +291,7 @@ var Highlights = Module("Highlight", { } }, { }, { - commands: function (dactyl, modules) { + commands: function initCommands(dactyl, modules) { const { autocommands, commands, completion, CommandOption, config, io } = modules; let lastScheme; @@ -403,8 +403,9 @@ var Highlights = Module("Highlight", { ] }); }, - completion: function (dactyl, modules) { + completion: function initCompletion(dactyl, modules) { const { completion, config, io } = modules; + completion.colorScheme = function colorScheme(context) { let extRe = RegExp("\\." + config.fileExtension + "$"); @@ -422,7 +423,7 @@ var Highlights = Module("Highlight", { context.completions = [[v.class, v.value] for (v in highlight)]; }; }, - javascript: function (dactyl, modules, window) { + javascript: function initJavascript(dactyl, modules, window) { modules.JavaScript.setCompleter(["get", "set"].map(function (m) highlight[m]), [ function (context, obj, args) Iterator(highlight.highlight) ]); modules.JavaScript.setCompleter(["highlightNode"].map(function (m) highlight[m]), diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm index 0f78615c..b3201e0b 100644 --- a/common/modules/javascript.jsm +++ b/common/modules/javascript.jsm @@ -836,8 +836,6 @@ var JavaScript = Module("javascript", { commands.add(["javas[cript]", "js"], "Evaluate a JavaScript string", function (args) { - modules.commandline; - if (args[0] && !args.bang) dactyl.userEval(args[0]); else {