//
// 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 */
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);
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 = [];
this.usedTabKey = false;
this.validHints = []; // store the indices of the "hints" array with valid elements
+ mappings.pushCommand();
this.open();
this.top = opts.window || content;
leave.superapply(this, arguments);
if (!stack.push) {
+ mappings.popCommand();
+
if (hints.hintSession == this)
hints.hintSession = null;
if (this.top) {
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];
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);
let body = doc.body || doc.querySelector("body");
if (body) {
- let fragment = util.xmlToDom(<div highlight="hints"/>, doc);
- body.appendChild(fragment);
- util.computedStyle(fragment).height; // Force application of binding.
- let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment;
+ 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 baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none;"/>, doc);
+ let baseNode = DOM(<span highlight="Hint" style="display: none;"/>, doc)[0];
let mode = this.hintMode;
let res = mode.matcher(doc);
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);
*/
onKeyPress: function onKeyPress(eventList) {
const KILL = false, PASS = true;
- let key = events.toString(eventList[0]);
+ let key = DOM.Event.stringify(eventList[0]);
this.clearTimeout();
this.timeout(next, 50);
}).call(this);
+ mappings.pushCommand();
if (!this.continue) {
modes.pop();
if (timeout)
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)
// 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;
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));
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"])) {
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))
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"]],
});
},
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 <Esc> is pressed",
function ({ count }) { hints.open("g;", { continue: true, count: count }); },
{ count: true });
- mappings.add(modes.HINTS, ["<Return>"],
+ let bind = function bind(names, description, action, params)
+ mappings.add([modes.HINTS], names, description,
+ action, params);
+
+ bind(["<Return>"],
"Follow the selected hint",
function ({ self }) { self.update(true); });
- mappings.add(modes.HINTS, ["<Tab>"],
+ bind(["<Tab>"],
"Focus the next matching hint",
function ({ self }) { self.tab(false); });
- mappings.add(modes.HINTS, ["<S-Tab>"],
+ bind(["<S-Tab>"],
"Focus the previous matching hint",
function ({ self }) { self.tab(true); });
- mappings.add(modes.HINTS, ["<BS>", "<C-h>"],
+ bind(["<BS>", "<C-h>"],
"Delete the previous character",
function ({ self }) self.backspace());
- mappings.add(modes.HINTS, ["<Leader>"],
+ bind(["<Leader>"],
"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"]
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"],
"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"));
}