description: "Active when text is selected",
display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""),
bases: [this.COMMAND],
- ownsFocus: true,
- passUnknown: false
+ ownsFocus: true
}, {
leave: function (stack, newMode) {
if (newMode.main == modes.CARET) {
});
this.addMode("CARET", {
description: "Active when the caret is visible in the web content",
- bases: [this.COMMAND]
+ bases: [this.NORMAL]
}, {
get pref() prefs.get("accessibility.browsewithcaret"),
char: "t",
description: "Vim-like editing of input elements",
bases: [this.COMMAND],
- input: true,
- ownsFocus: true,
- passUnknown: false
+ ownsFocus: true
+ }, {
+ onKeyPress: function (eventList) {
+ const KILL = false, PASS = true;
+
+ // Hack, really.
+ if (eventList[0].charCode || /^<(?:.-)*(?:BS|Del|C-h|C-w|C-u|C-k)>$/.test(events.toString(eventList[0]))) {
+ dactyl.beep();
+ return KILL;
+ }
+ return PASS;
+ }
});
this.addMode("OUTPUT_MULTILINE", {
description: "Active when the multi-line output buffer is open",
- bases: [this.COMMAND],
+ bases: [this.NORMAL]
});
this.addMode("INPUT", {
}
});
+
+ function makeTree() {
+ let list = modes.all.filter(function (m) m.name !== m.description);
+
+ let tree = {};
+
+ for (let mode in values(list))
+ tree[mode.name] = {};
+
+ for (let mode in values(list))
+ for (let base in values(mode.bases))
+ tree[base.name][mode.name] = tree[mode.name];
+
+ let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
+
+ default xml namespace = NS;
+ function rec(obj) {
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+
+ let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
+ Object.keys(obj).sort().forEach(function (name) {
+ let mode = modes.getMode(name);
+ res.* += <li><em>{mode.displayName}</em>: {mode.description}{
+ rec(obj[name])
+ }</li>;
+ });
+
+ if (res.*.length())
+ return res;
+ return <></>;
+ }
+
+ return rec(roots);
+ }
+
+ util.timeout(function () {
+ // Waits for the add-on to become available, if necessary.
+ config.addon;
+ config.version;
+
+ services["dactyl:"].pages["modes.dtd"] = services["dactyl:"].pages["modes.dtd"]();
+ });
+
+ services["dactyl:"].pages["modes.dtd"] = function () [null,
+ util.makeDTD(iter({ "modes.tree": makeTree() },
+ config.dtd))];
},
cleanup: function cleanup() {
modes.reset();
let val = this._modeMap[this._main].display();
if (val)
- return "-- " + val + " --" + macromode;;
+ return "-- " + val + " --" + macromode;
return macromode;
},
if (!mode.extended)
this._mainModes.push(mode);
- dactyl.triggerObserver("mode-add", mode);
+ dactyl.triggerObserver("modes.add", mode);
},
dumpStack: function dumpStack() {
// show the current mode string in the command line
show: function show() {
+ if (!loaded.modes)
+ return;
+
let msg = null;
- if (options.get("showmode").getKey(this.main.name, true))
+ if (options.get("showmode").getKey(this.main.allBases, false))
msg = this._getModeMessage();
+
if (msg || loaded.commandline)
commandline.widgets.mode = msg || null;
},
prev = stack && stack.pop || this.topOfStack;
if (push)
this._modeStack.push(push);
-
- if (stack && stack.pop)
- for (let { obj, prop, value, test } in values(this.topOfStack.saved))
- if (!test || !test(stack, prev))
- dactyl.trapErrors(function () { obj[prop] = value });
-
- this.show();
});
- delayed.forEach(function ([fn, self]) dactyl.trapErrors(fn, self));
+ if (stack && stack.pop)
+ for (let { obj, prop, value, test } in values(this.topOfStack.saved))
+ if (!test || !test(stack, prev))
+ dactyl.trapErrors(function () { obj[prop] = value });
+
+ this.show();
if (this.topOfStack.params.enter && prev)
dactyl.trapErrors("enter", this.topOfStack.params,
push ? { push: push } : stack || {},
prev);
- dactyl.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended], stack);
+ delayed.forEach(function ([fn, self]) dactyl.trapErrors(fn, self));
+
+ dactyl.triggerObserver("modes.change", [oldMain, oldExtended], [this._main, this._extended], stack);
this.show();
},
while (this._modeStack.length > 1 && this.main != mode) {
let a = this._modeStack.pop();
this.set(this.topOfStack.main, this.topOfStack.extended, this.topOfStack.params,
- update({ pop: a }, args || {}));
+ update({ pop: a },
+ args || {}));
if (mode == null)
return;
}
},
- replace: function replace(mode, oldMode) {
+ replace: function replace(mode, oldMode, args) {
while (oldMode && this._modeStack.length > 1 && this.main != oldMode)
this.pop();
if (this._modeStack.length > 1)
- this.set(mode, null, null, { push: this.topOfStack, pop: this._modeStack.pop() });
+ this.set(mode, null, null,
+ update({ push: this.topOfStack, pop: this._modeStack.pop() },
+ args || {}));
this.push(mode);
},
init: function init(name, options, params) {
if (options.bases)
util.assert(options.bases.every(function (m) m instanceof this, this.constructor),
- "Invalid bases", true);
+ _("mode.invalidBases"), true);
- update(this, {
+ this.update({
id: 1 << Modes.Mode._id++,
+ description: name,
name: name,
params: params || {}
}, options);
},
+ description: Messages.Localized(""),
+
+ displayName: Class.memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
+
isinstance: function isinstance(obj)
- this === obj || this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
+ this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
allBases: Class.memoize(function () {
- let seen = {}, res = [], queue = this.bases;
+ let seen = {}, res = [], queue = [this].concat(this.bases);
for (let mode in array.iterValues(queue))
- if (!set.add(seen, mode)) {
+ if (!Set.add(seen, mode)) {
res.push(mode);
queue.push.apply(queue, mode.bases);
}
get count() !this.insert,
- get description() this._display,
-
_display: Class.memoize(function _display() this.name.replace("_", " ", "g")),
display: function display() this._display,
ownsFocus: Class.memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
- get passUnknown() this.input,
+ passEvent: function passEvent(event) this.input && event.charCode && !(event.ctrlKey || event.altKey || event.metaKey),
+
+ passUnknown: Class.memoize(function () options.get("passunknown").getKey(this.name)),
get mask() this,
mappings: function initMappings() {
mappings.add([modes.BASE, modes.NORMAL],
["<Esc>", "<C-[>"],
- "Return to NORMAL mode",
+ "Return to Normal mode",
function () { modes.reset(); });
mappings.add([modes.INPUT, modes.COMMAND, modes.PASS_THROUGH, modes.QUOTE],
"Return to the previous mode",
function () { modes.pop(); });
+ mappings.add([modes.MENU], ["<C-c>"],
+ "Leave Menu mode",
+ function () { modes.pop(); });
+
mappings.add([modes.MENU], ["<Esc>"],
"Close the current popup",
- function () {
- modes.pop();
- return Events.PASS_THROUGH;
- });
+ function () { return Events.PASS_THROUGH; });
mappings.add([modes.MENU], ["<C-[>"],
"Close the current popup",
function () { events.feedkeys("<Esc>"); });
},
options: function initOptions() {
+ let opts = {
+ completer: function completer(context, extra) {
+ if (extra.value && context.filter[0] == "!")
+ context.advance(1);
+ return completer.superapply(this, arguments);
+ },
+
+ getKey: function getKey(val, default_) {
+ if (isArray(val))
+ return (array.nth(this.value, function (v) val.some(function (m) m.name === v.mode), 0)
+ || { result: default_ }).result;
+
+ return Set.has(this.valueMap, val) ? this.valueMap[val] : default_;
+ },
+
+ setter: function (vals) {
+ modes.all.forEach(function (m) { delete m.passUnknown });
+
+ vals = vals.map(function (v) update(new String(v.toLowerCase()), {
+ mode: v.replace(/^!/, "").toUpperCase(),
+ result: v[0] !== "!"
+ }));
+
+ this.valueMap = values(vals).map(function (v) [v.mode, v.result]).toObject();
+ return vals;
+ },
+
+ validator: function validator(vals) vals.map(function (v) v.replace(/^!/, "")).every(Set.has(this.values)),
+
+ get values() array.toObject([[m.name.toLowerCase(), m.description] for (m in values(modes._modes)) if (!m.hidden)])
+ };
+
+ options.add(["passunknown", "pu"],
+ "Pass through unknown keys in these modes",
+ "stringlist", "!text_edit,base",
+ opts);
+
options.add(["showmode", "smd"],
"Show the current mode in the command line when it matches this expression",
- "regexplist", "!^normal$",
- { regexpFlags: "i" });
+ "stringlist", "caret,output_multiline,!normal,base",
+ opts);
},
prefs: function initPrefs() {
prefs.watch("accessibility.browsewithcaret", function () modes.onCaretChange.apply(modes, arguments));