1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 08:27:59 +01:00

Speed up hint generation for large numbers of hints.

This commit is contained in:
Kris Maglione
2011-03-10 17:47:44 -05:00
parent 60ad623ddc
commit e77c97b6cb
3 changed files with 44 additions and 30 deletions

View File

@@ -38,12 +38,13 @@ var HintSession = Class("HintSession", CommandMode, {
this.open(); this.open();
this.top = opts.window || content; this.top = opts.window || content;
this.top.addEventListener("resize", hints.resizeTimer.closure.tell, true); this.top.addEventListener("resize", this.closure._onResize, true);
this.top.addEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, false, true); this.top.addEventListener("dactyl-commandupdate", this.closure._onResize, false, true);
this.generate(); this.generate();
this.show(); this.show();
this.magic = true;
if (this.validHints.length == 0) { if (this.validHints.length == 0) {
dactyl.beep(); dactyl.beep();
@@ -76,7 +77,6 @@ var HintSession = Class("HintSession", CommandMode, {
this.span.style.display = (val ? "" : "none"); this.span.style.display = (val ? "" : "none");
if (this.imgSpan) if (this.imgSpan)
this.imgSpan.style.display = (val ? "" : "none"); this.imgSpan.style.display = (val ? "" : "none");
this.active = this.active; this.active = this.active;
} }
}, },
@@ -92,8 +92,8 @@ var HintSession = Class("HintSession", CommandMode, {
if (hints.hintSession == this) if (hints.hintSession == this)
hints.hintSession = null; hints.hintSession = null;
if (this.top) { if (this.top) {
this.top.removeEventListener("resize", hints.resizeTimer.closure.tell, true); this.top.removeEventListener("resize", this.closure._onResize, true);
this.top.removeEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, true); this.top.removeEventListener("dactyl-commandupdate", this.closure._onResize, true);
} }
this.removeHints(0); this.removeHints(0);
@@ -255,6 +255,11 @@ var HintSession = Class("HintSession", CommandMode, {
let doc = win.document; 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); let [offsetX, offsetY] = this.getContainerOffsets(doc);
offsets = offsets || { left: 0, right: 0, top: 0, bottom: 0 }; 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. util.computedStyle(fragment).height; // Force application of binding.
let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment; let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment;
let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none"/>, doc); let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none;"/>, doc);
let mode = this.hintMode; let mode = this.hintMode;
let res = mode.matcher(doc); let res = mode.matcher(doc);
let start = this.pageHints.length; let start = this.pageHints.length;
for (let elem in res) { let _hints = [];
let hint = { elem: elem, showText: false, __proto__: this.Hint }; 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)) for (let hint in values(_hints)) {
continue; let { elem, rect } = hint;
if (elem.hasAttributeNS(NS, "hint")) if (elem.hasAttributeNS(NS, "hint"))
[hint.text, hint.showText] = [elem.getAttributeNS(NS, "hint"), true]; [hint.text, hint.showText] = [elem.getAttributeNS(NS, "hint"), true];
@@ -302,17 +314,15 @@ var HintSession = Class("HintSession", CommandMode, {
else else
hint.text = elem.textContent.toLowerCase(); 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 leftPos = Math.max((rect.left + offsetX), offsetX);
let topPos = Math.max((rect.top + offsetY), offsetY); let topPos = Math.max((rect.top + offsetY), offsetY);
if (elem instanceof HTMLAreaElement) if (elem instanceof HTMLAreaElement)
[leftPos, topPos] = this.getAreaOffset(elem, leftPos, topPos); [leftPos, topPos] = this.getAreaOffset(elem, leftPos, topPos);
hint.span.style.left = leftPos + "px"; hint.span.setAttribute("style", ["display: none; left:", leftPos, "px; top:", topPos, "px"].join(""));
hint.span.style.top = topPos + "px";
container.appendChild(hint.span); container.appendChild(hint.span);
this.pageHints.push(hint); this.pageHints.push(hint);
@@ -399,12 +409,17 @@ var HintSession = Class("HintSession", CommandMode, {
return PASS; return PASS;
}, },
onResize: function () { onResize: function onResize() {
this.removeHints(0); this.removeHints(0);
this.generate(this.top); this.generate(this.top);
this.show(); this.show();
}, },
_onResize: function _onResize() {
if (this.magic)
hints.resizeTimer.tell();
},
/** /**
* Finish hinting. * Finish hinting.
* *
@@ -491,6 +506,7 @@ var HintSession = Class("HintSession", CommandMode, {
*/ */
removeHints: function _removeHints(timeout) { removeHints: function _removeHints(timeout) {
for (let { doc, start, end } in values(this.docs)) { for (let { doc, start, end } in values(this.docs)) {
delete doc.dactylLabels;
for (let elem in util.evaluateXPath("//*[@dactyl:highlight='hints']", doc)) for (let elem in util.evaluateXPath("//*[@dactyl:highlight='hints']", doc))
elem.parentNode.removeChild(elem); elem.parentNode.removeChild(elem);
for (let i in util.range(start, end + 1)) for (let i in util.range(start, end + 1))
@@ -776,15 +792,14 @@ var Hints = Module("hints", {
return [elem.alt.toLowerCase(), true]; return [elem.alt.toLowerCase(), true];
} }
else if (elem.value && type != "password") { 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))) if (! ((type == "radio" || type == "checkbox") && !isNaN(elem.value)))
return [elem.value.toLowerCase(), (type == "radio" || type == "checkbox")]; return [elem.value.toLowerCase(), (type == "radio" || type == "checkbox")];
} }
} }
else if (option == "label") { else if (option == "label") {
if (elem.id) { if (elem.id) {
// TODO: (possibly) do some guess work for label-like objects let label = elem.ownerDocument.dactylLabels[elem.id];
let label = util.evaluateXPath(["label[@for=" + elem.id.quote() + "]"], doc).snapshotItem(0);
if (label) if (label)
return [label.textContent.toLowerCase(), true]; return [label.textContent.toLowerCase(), true];
} }

View File

@@ -100,7 +100,7 @@ var Highlights = Module("Highlight", {
__iterator__: function () values(this.highlight).sort(function (a, b) String.localeCompare(a.class, b.class)) __iterator__: function () values(this.highlight).sort(function (a, b) String.localeCompare(a.class, b.class))
.iterValues(), .iterValues(),
_create: function (agent, args) { _create: function _create(agent, args) {
let obj = Highlight.apply(Highlight, args); let obj = Highlight.apply(Highlight, args);
if (!isArray(obj.sites)) if (!isArray(obj.sites))
@@ -143,9 +143,9 @@ var Highlights = Module("Highlight", {
return obj; 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 [, class_, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
let highlight = this.highlight[key] || this._create(false, [key]); 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 * Clears all highlighting rules. Rules with default values are
* reset. * reset.
*/ */
clear: function () { clear: function clear() {
for (let [k, v] in Iterator(this.highlight)) for (let [k, v] in Iterator(this.highlight))
this.set(k, null, true); this.set(k, null, true);
}, },
@@ -193,7 +193,7 @@ var Highlights = Module("Highlight", {
* @param {Node} node * @param {Node} node
* @param {string} group * @param {string} group
*/ */
highlightNode: function (node, group, applyBindings) { highlightNode: function highlightNode(node, group, applyBindings) {
node.setAttributeNS(NS.uri, "highlight", group); node.setAttributeNS(NS.uri, "highlight", group);
let groups = group.split(" "); let groups = group.split(" ");
@@ -214,7 +214,7 @@ var Highlights = Module("Highlight", {
* *
* @param {string} class * @param {string} class
*/ */
selector: function (class_) selector: function selector(class_)
let (self = this) let (self = this)
class_.replace(/(^|[>\s])([A-Z][\w-]+)\b/g, class_.replace(/(^|[>\s])([A-Z][\w-]+)\b/g,
function (m, n1, hl) n1 + 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 {string} css The rules to load. See {@link Highlights#css}.
* @param {boolean} eager When true, load all provided rules immediately. * @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, " ")) 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)) .split("\n").filter(function (s) /\S/.test(s) && !/^\s*\/\//.test(s))
.forEach(function (highlight) { .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; const { autocommands, commands, completion, CommandOption, config, io } = modules;
let lastScheme; let lastScheme;
@@ -403,8 +403,9 @@ var Highlights = Module("Highlight", {
] ]
}); });
}, },
completion: function (dactyl, modules) { completion: function initCompletion(dactyl, modules) {
const { completion, config, io } = modules; const { completion, config, io } = modules;
completion.colorScheme = function colorScheme(context) { completion.colorScheme = function colorScheme(context) {
let extRe = RegExp("\\." + config.fileExtension + "$"); let extRe = RegExp("\\." + config.fileExtension + "$");
@@ -422,7 +423,7 @@ var Highlights = Module("Highlight", {
context.completions = [[v.class, v.value] for (v in 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]), modules.JavaScript.setCompleter(["get", "set"].map(function (m) highlight[m]),
[ function (context, obj, args) Iterator(highlight.highlight) ]); [ function (context, obj, args) Iterator(highlight.highlight) ]);
modules.JavaScript.setCompleter(["highlightNode"].map(function (m) highlight[m]), modules.JavaScript.setCompleter(["highlightNode"].map(function (m) highlight[m]),

View File

@@ -836,8 +836,6 @@ var JavaScript = Module("javascript", {
commands.add(["javas[cript]", "js"], commands.add(["javas[cript]", "js"],
"Evaluate a JavaScript string", "Evaluate a JavaScript string",
function (args) { function (args) {
modules.commandline;
if (args[0] && !args.bang) if (args[0] && !args.bang)
dactyl.userEval(args[0]); dactyl.userEval(args[0]);
else { else {