summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/content/about.xul2
-rw-r--r--common/content/commandline.js2
-rw-r--r--common/content/editor.js2
-rw-r--r--common/content/events.js349
-rw-r--r--common/content/hints.js6
-rw-r--r--common/content/key-processors.js10
-rw-r--r--common/content/mappings.js8
-rw-r--r--common/content/modes.js2
-rw-r--r--common/content/mow.js2
-rw-r--r--common/modules/base.jsm1
-rw-r--r--common/modules/contexts.jsm2
-rw-r--r--common/modules/dom.jsm336
-rw-r--r--teledactyl/content/compose/dactyl.xul98
13 files changed, 374 insertions, 446 deletions
diff --git a/common/content/about.xul b/common/content/about.xul
index 433402c8..aba640b3 100644
--- a/common/content/about.xul
+++ b/common/content/about.xul
@@ -6,7 +6,7 @@
<page id="about-&dactyl.name;" orient="vertical" title="About &dactyl.appName;"
xmlns="&xmlns.xul;" xmlns:html="&xmlns.html;">
- <html:link rel="icon" href="chrome://&dactyl.name;/skin/icon.png"
+ <html:link rel="icon" href="resource://dactyl-local-skin/icon.png"
type="image/png" style="display: none;"/>
<spring flex="1"/>
diff --git a/common/content/commandline.js b/common/content/commandline.js
index 46d22cbb..0a556dbd 100644
--- a/common/content/commandline.js
+++ b/common/content/commandline.js
@@ -393,7 +393,7 @@ var CommandMode = Class("CommandMode", {
this.onChange(commandline.command);
},
keyup: function CM_onKeyUp(event) {
- let key = events.toString(event);
+ let key = DOM.Event.stringify(event);
if (/-?Tab>$/.test(key) && this.completions)
this.completions.tabTimer.flush();
}
diff --git a/common/content/editor.js b/common/content/editor.js
index cc60c721..560bd9d6 100644
--- a/common/content/editor.js
+++ b/common/content/editor.js
@@ -132,7 +132,7 @@ var Editor = Module("editor", {
if (count == null)
count = 1;
- let code = events.fromString(key)[0].charCode;
+ let code = DOM.Event.parse(key)[0].charCode;
util.assert(code);
let char = String.fromCharCode(code);
diff --git a/common/content/events.js b/common/content/events.js
index 2693b9a9..b2a2ab6d 100644
--- a/common/content/events.js
+++ b/common/content/events.js
@@ -115,67 +115,6 @@ var Events = Module("events", {
if (isString(m))
m = { keys: m, timeRecorded: Date.now() };
- // NOTE: the order of ["Esc", "Escape"] or ["Escape", "Esc"]
- // matters, so use that string as the first item, that you
- // want to refer to within dactyl's source code for
- // comparisons like if (key == "<Esc>") { ... }
- this._keyTable = {
- add: ["Plus", "Add"],
- back_space: ["BS"],
- count: ["count"],
- delete: ["Del"],
- escape: ["Esc", "Escape"],
- insert: ["Insert", "Ins"],
- leader: ["Leader"],
- left_shift: ["LT", "<"],
- nop: ["Nop"],
- pass: ["Pass"],
- return: ["Return", "CR", "Enter"],
- right_shift: [">"],
- space: ["Space", " "],
- subtract: ["Minus", "Subtract"]
- };
-
- this._pseudoKeys = Set(["count", "leader", "nop", "pass"]);
-
- this._key_key = {};
- this._code_key = {};
- this._key_code = {};
- this._code_nativeKey = {};
-
- for (let list in values(this._keyTable))
- for (let v in values(list)) {
- if (v.length == 1)
- v = v.toLowerCase();
- this._key_key[v.toLowerCase()] = v;
- }
-
- for (let [k, v] in Iterator(KeyEvent)) {
- this._code_nativeKey[v] = k.substr(4);
-
- k = k.substr(7).toLowerCase();
- let names = [k.replace(/(^|_)(.)/g, function (m, n1, n2) n2.toUpperCase())
- .replace(/^NUMPAD/, "k")];
-
- if (names[0].length == 1)
- names[0] = names[0].toLowerCase();
-
- if (k in this._keyTable)
- names = this._keyTable[k];
- this._code_key[v] = names[0];
- for (let [, name] in Iterator(names)) {
- this._key_key[name.toLowerCase()] = name;
- this._key_code[name.toLowerCase()] = v;
- }
- }
-
- // HACK: as Gecko does not include an event for <, we must add this in manually.
- if (!("<" in this._key_code)) {
- this._key_code["<"] = 60;
- this._key_code["lt"] = 60;
- this._code_key[60] = "lt";
- }
-
this.popups = {
active: [],
@@ -298,8 +237,8 @@ var Events = Module("events", {
if (/[A-Z]/.test(macro)) { // uppercase (append)
macro = macro.toLowerCase();
- this._macroKeys = events.fromString((this._macros.get(macro) || { keys: "" }).keys, true)
- .map(events.closure.toString);
+ this._macroKeys = DOM.Event.stringify((this._macros.get(macro) || { keys: "" }).keys, true)
+ .map(DOM.Event.closure.stringify);
}
else if (macro) {
this._macroKeys = [];
@@ -421,9 +360,9 @@ var Events = Module("events", {
keys = mappings.expandLeader(keys);
- for (let [, evt_obj] in Iterator(events.fromString(keys))) {
+ for (let [, evt_obj] in Iterator(DOM.Event.parse(keys))) {
let now = Date.now();
- let key = events.toString(evt_obj);
+ let key = DOM.Event.stringify(evt_obj);
for (let type in values(["keydown", "keypress", "keyup"])) {
let evt = update({}, evt_obj, { type: type });
if (type !== "keypress" && !evt.keyCode)
@@ -472,268 +411,22 @@ var Events = Module("events", {
return true;
},
- create: deprecated("DOM.Event", function create() DOM.Event.apply(null, arguments)),
- dispatch: deprecated("DOM.Event.dispatch", function dispatch() DOM.Event.dispatch.apply(DOM.Event, arguments)),
+ canonicalKeys: deprecated("DOM.Event.canonicalKeys", { get: function canonicalKeys() DOM.Event.closure.canonicalKeys }),
+ create: deprecated("DOM.Event", function create() DOM.Event.apply(null, arguments)),
+ dispatch: deprecated("DOM.Event.dispatch", function dispatch() DOM.Event.dispatch.apply(DOM.Event, arguments)),
+ fromString: deprecated("DOM.Event.parse", { get: function fromString() DOM.Event.closure.parse }),
+ iterKeys: deprecated("DOM.Event.iterKeys", { get: function iterKeys() DOM.Event.closure.iterKeys }),
- /**
- * Converts a user-input string of keys into a canonical
- * representation.
- *
- * <C-A> maps to <C-a>, <C-S-a> maps to <C-S-A>
- * <C- > maps to <C-Space>, <S-a> maps to A
- * << maps to <lt><lt>
- *
- * <S-@> is preserved, as in Vim, to allow untypeable key-combinations
- * in macros.
- *
- * canonicalKeys(canonicalKeys(x)) == canonicalKeys(x) for all values
- * of x.
- *
- * @param {string} keys Messy form.
- * @param {boolean} unknownOk Whether unknown keys are passed
- * through rather than being converted to <lt>keyname>.
- * @default false
- * @returns {string} Canonical form.
- */
- canonicalKeys: function (keys, unknownOk) {
- if (arguments.length === 1)
- unknownOk = true;
- return events.fromString(keys, unknownOk).map(events.closure.toString).join("");
+ toString: function toString() {
+ if (!arguments.length)
+ return toString.supercall(this);
+ deprecated.warn(toString, "toString", "DOM.Event.stringify");
+ return DOM.Event.stringify.apply(DOM.Event, arguments);
},
- iterKeys: function (keys) iter(function () {
- let match, re = /<.*?>?>|[^<]/g;
- while (match = re.exec(keys))
- yield match[0];
- }()),
-
get defaultTarget() dactyl.focusedElement || content.document.body || document.documentElement,
/**
- * Converts an event string into an array of pseudo-event objects.
- *
- * These objects can be used as arguments to events.toString or
- * events.create, though they are unlikely to be much use for other
- * purposes. They have many of the properties you'd expect to find on a
- * real event, but none of the methods.
- *
- * Also may contain two "special" parameters, .dactylString and
- * .dactylShift these are set for characters that can never by
- * typed, but may appear in mappings, for example <Nop> is passed as
- * dactylString, and dactylShift is set when a user specifies
- * <S-@> where @ is a non-case-changeable, non-space character.
- *
- * @param {string} keys The string to parse.
- * @param {boolean} unknownOk Whether unknown keys are passed
- * through rather than being converted to <lt>keyname>.
- * @default false
- * @returns {Array[Object]}
- */
- fromString: function (input, unknownOk) {
-
- if (arguments.length === 1)
- unknownOk = true;
-
- let out = [];
- for (let match in util.regexp.iterate(/<.*?>?>|[^<]|<(?!.*>)/g, input)) {
- let evt_str = match[0];
-
- let evt_obj = { ctrlKey: false, shiftKey: false, altKey: false, metaKey: false,
- keyCode: 0, charCode: 0, type: "keypress" };
-
- if (evt_str.length == 1) {
- evt_obj.charCode = evt_str.charCodeAt(0);
- evt_obj._keyCode = this._key_code[evt_str[0].toLowerCase()];
- evt_obj.shiftKey = evt_str !== evt_str.toLowerCase();
- }
- else {
- let [match, modifier, keyname] = evt_str.match(/^<((?:[*12CASM⌘]-)*)(.+?)>$/i) || [false, '', ''];
- modifier = Set(modifier.toUpperCase());
- keyname = keyname.toLowerCase();
- evt_obj.dactylKeyname = keyname;
- if (/^u[0-9a-f]+$/.test(keyname))
- keyname = String.fromCharCode(parseInt(keyname.substr(1), 16));
-
- if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) ||
- this._key_code[keyname] || Set.has(this._pseudoKeys, keyname))) {
- evt_obj.globKey ="*" in modifier;
- evt_obj.ctrlKey ="C" in modifier;
- evt_obj.altKey ="A" in modifier;
- evt_obj.shiftKey ="S" in modifier;
- evt_obj.metaKey ="M" in modifier || "⌘" in modifier;
- evt_obj.dactylShift = evt_obj.shiftKey;
-
- if (keyname.length == 1) { // normal characters
- if (evt_obj.shiftKey)
- keyname = keyname.toUpperCase();
-
- evt_obj.dactylShift = evt_obj.shiftKey && keyname.toUpperCase() == keyname.toLowerCase();
- evt_obj.charCode = keyname.charCodeAt(0);
- evt_obj._keyCode = this._key_code[keyname.toLowerCase()];
- }
- else if (Set.has(this._pseudoKeys, keyname)) {
- evt_obj.dactylString = "<" + this._key_key[keyname] + ">";
- }
- else if (/mouse$/.test(keyname)) { // mouse events
- evt_obj.type = (/2-/.test(modifier) ? "dblclick" : "click");
- evt_obj.button = ["leftmouse", "middlemouse", "rightmouse"].indexOf(keyname);
- delete evt_obj.keyCode;
- delete evt_obj.charCode;
- }
- else { // spaces, control characters, and <
- evt_obj.keyCode = this._key_code[keyname];
- evt_obj.charCode = 0;
- }
- }
- else { // an invalid sequence starting with <, treat as a literal
- out = out.concat(events.fromString("<lt>" + evt_str.substr(1)));
- continue;
- }
- }
-
- // TODO: make a list of characters that need keyCode and charCode somewhere
- if (evt_obj.keyCode == 32 || evt_obj.charCode == 32)
- evt_obj.charCode = evt_obj.keyCode = 32; // <Space>
- if (evt_obj.keyCode == 60 || evt_obj.charCode == 60)
- evt_obj.charCode = evt_obj.keyCode = 60; // <lt>
-
- evt_obj.modifiers = (evt_obj.ctrlKey && Ci.nsIDOMNSEvent.CONTROL_MASK)
- | (evt_obj.altKey && Ci.nsIDOMNSEvent.ALT_MASK)
- | (evt_obj.shiftKey && Ci.nsIDOMNSEvent.SHIFT_MASK)
- | (evt_obj.metaKey && Ci.nsIDOMNSEvent.META_MASK);
-
- out.push(evt_obj);
- }
- return out;
- },
-
- /**
- * Converts the specified event to a string in dactyl key-code
- * notation. Returns null for an unknown event.
- *
- * @param {Event} event
- * @returns {string}
- */
- toString: function toString(event) {
- if (!event)
- return toString.supercall(this);
-
- if (event.dactylString)
- return event.dactylString;
-
- let key = null;
- let modifier = "";
-
- if (event.globKey)
- modifier += "*-";
- if (event.ctrlKey)
- modifier += "C-";
- if (event.altKey)
- modifier += "A-";
- if (event.metaKey)
- modifier += "M-";
-
- if (/^key/.test(event.type)) {
- let charCode = event.type == "keyup" ? 0 : event.charCode; // Why? --Kris
- if (charCode == 0) {
- if (event.keyCode in this._code_key) {
- key = this._code_key[event.keyCode];
-
- if (event.shiftKey && (key.length > 1 || event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
- modifier += "S-";
- else if (!modifier && key.length === 1)
- if (event.shiftKey)
- key = key.toUpperCase();
- else
- key = key.toLowerCase();
-
- if (!modifier && /^[a-z0-9]$/i.test(key))
- return key;
- }
- }
- // [Ctrl-Bug] special handling of mysterious <C-[>, <C-\\>, <C-]>, <C-^>, <C-_> bugs (OS/X)
- // (i.e., cntrl codes 27--31)
- // ---
- // For more information, see:
- // [*] Referenced mailing list msg: http://www.mozdev.org/pipermail/pentadactyl/2008-May/001548.html
- // [*] Mozilla bug 416227: event.charCode in keypress handler has unexpected values on Mac for Ctrl with chars in "[ ] _ \"
- // https://bugzilla.mozilla.org/show_bug.cgi?id=416227
- // [*] Mozilla bug 432951: Ctrl+'foo' doesn't seem same charCode as Meta+'foo' on Cocoa
- // https://bugzilla.mozilla.org/show_bug.cgi?id=432951
- // ---
- //
- // The following fixes are only activated if config.OS.isMacOSX.
- // Technically, they prevent mappings from <C-Esc> (and
- // <C-C-]> if your fancy keyboard permits such things<?>), but
- // these <C-control> mappings are probably pathological (<C-Esc>
- // certainly is on Windows), and so it is probably
- // harmless to remove the config.OS.isMacOSX if desired.
- //
- else if (config.OS.isMacOSX && event.ctrlKey && charCode >= 27 && charCode <= 31) {
- if (charCode == 27) { // [Ctrl-Bug 1/5] the <C-[> bug
- key = "Esc";
- modifier = modifier.replace("C-", "");
- }
- else // [Ctrl-Bug 2,3,4,5/5] the <C-\\>, <C-]>, <C-^>, <C-_> bugs
- key = String.fromCharCode(charCode + 64);
- }
- // a normal key like a, b, c, 0, etc.
- else if (charCode > 0) {
- key = String.fromCharCode(charCode);
-
- if (!/^[a-z0-9]$/i.test(key) && key in this._key_code) {
- // a named charCode key (<Space> and <lt>) space can be shifted, <lt> must be forced
- if ((key.match(/^\s$/) && event.shiftKey) || event.dactylShift)
- modifier += "S-";
-
- key = this._code_key[this._key_code[key]];
- }
- else {
- // a shift modifier is only allowed if the key is alphabetical and used in a C-A-M- mapping in the uppercase,
- // or if the shift has been forced for a non-alphabetical character by the user while :map-ping
- if (key !== key.toLowerCase() && (event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
- modifier += "S-";
- if (/^\s$/.test(key))
- key = let (s = charCode.toString(16)) "U" + "0000".substr(4 - s.length) + s;
- else if (modifier.length == 0)
- return key;
- }
- }
- if (key == null) {
- if (event.shiftKey)
- modifier += "S-";
- key = this._key_key[event.dactylKeyname] || event.dactylKeyname;
- }
- if (key == null)
- return null;
- }
- else if (event.type == "click" || event.type == "dblclick") {
- if (event.shiftKey)
- modifier += "S-";
- if (event.type == "dblclick")
- modifier += "2-";
- // TODO: triple and quadruple click
-
- switch (event.button) {
- case 0:
- key = "LeftMouse";
- break;
- case 1:
- key = "MiddleMouse";
- break;
- case 2:
- key = "RightMouse";
- break;
- }
- }
-
- if (key == null)
- return null;
-
- return "<" + modifier + key + ">";
- },
-
- /**
* Returns true if there's a known native key handler for the given
* event in the given mode.
*
@@ -957,7 +650,7 @@ var Events = Module("events", {
event[k] = v;
DOM.Event.feedingEvent = null;
- let key = events.toString(event);
+ let key = DOM.Event.stringify(event);
// Hack to deal with <BS> and so forth not dispatching input
// events
@@ -1079,7 +772,7 @@ var Events = Module("events", {
!modes.passThrough && this.shouldPass(event) ||
!this.processor && event.type === "keydown"
&& options.get("passunknown").getKey(modes.main.allBases)
- && let (key = events.toString(event))
+ && let (key = DOM.Event.stringify(event))
!modes.main.allBases.some(
function (mode) mappings.hives.some(
function (hive) hive.get(mode, key) || hive.getCandidates(mode, key)));
@@ -1087,7 +780,7 @@ var Events = Module("events", {
if (event.type === "keydown")
this.passing = pass;
- events.dbg("ON " + event.type.toUpperCase() + " " + this.toString(event) + " pass: " + pass + " replay: " + event.isReplay + " macro: " + event.isMacro);
+ events.dbg("ON " + event.type.toUpperCase() + " " + DOM.Event.stringify(event) + " pass: " + pass + " replay: " + event.isReplay + " macro: " + event.isMacro);
// Prevents certain sites from transferring focus to an input box
// before we get a chance to process our key bindings on the
@@ -1220,7 +913,7 @@ var Events = Module("events", {
shouldPass: function shouldPass(event)
!event.noremap && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)) &&
- options.get("passkeys").has(events.toString(event))
+ options.get("passkeys").has(DOM.Event.stringify(event))
}, {
ABORT: {},
KILL: true,
@@ -1229,7 +922,7 @@ var Events = Module("events", {
WAIT: null,
isEscape: function isEscape(event)
- let (key = isString(event) ? event : events.toString(event))
+ let (key = isString(event) ? event : DOM.Event.stringify(event))
key === "<Esc>" || key === "<C-[>",
isHidden: function isHidden(elem, aggressive) {
@@ -1405,9 +1098,9 @@ var Events = Module("events", {
let value = parse.superapply(this, arguments);
value.forEach(function (filter) {
let vals = Option.splitList(filter.result);
- filter.keys = events.fromString(vals[0]).map(events.closure.toString);
+ filter.keys = DOM.Event.parse(vals[0]).map(DOM.Event.closure.stringify);
- filter.commandKeys = vals.slice(1).map(events.closure.canonicalKeys);
+ filter.commandKeys = vals.slice(1).map(DOM.Event.closure.canonicalKeys);
filter.inputKeys = filter.commandKeys.filter(bind("test", /^<[ACM]-/));
});
return value;
diff --git a/common/content/hints.js b/common/content/hints.js
index 2bdd7552..4bd50348 100644
--- a/common/content/hints.js
+++ b/common/content/hints.js
@@ -29,7 +29,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 = [];
@@ -403,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();
@@ -1301,7 +1301,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"));
}
diff --git a/common/content/key-processors.js b/common/content/key-processors.js
index 966f300a..c90d1528 100644
--- a/common/content/key-processors.js
+++ b/common/content/key-processors.js
@@ -134,7 +134,7 @@ var ProcessorStack = Class("ProcessorStack", {
events.passing = true;
if (result === Events.PASS_THROUGH && this.keyEvents.length)
- events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, events.toString(e)]).join("\n\t"));
+ events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, DOM.Event.stringify(e)]).join("\n\t"));
if (result === Events.PASS_THROUGH)
events.feedevents(null, this.keyEvents, { skipmap: true, isMacro: true, isReplay: true });
@@ -142,9 +142,9 @@ var ProcessorStack = Class("ProcessorStack", {
let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
if (result === Events.PASS)
- events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(events.closure.toString));
+ events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(DOM.Event.closure.stringify));
if (list.length > length)
- events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(events.closure.toString));
+ events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(DOM.Event.closure.stringify));
if (result === Events.PASS)
events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
@@ -159,7 +159,7 @@ var ProcessorStack = Class("ProcessorStack", {
if (this.timer)
this.timer.cancel();
- let key = events.toString(event);
+ let key = DOM.Event.stringify(event);
this.events.push(event);
if (this.keyEvents)
this.keyEvents.push(event);
@@ -233,7 +233,7 @@ var KeyProcessor = Class("KeyProcessor", {
append: function append(event) {
this.events.push(event);
- let key = events.toString(event);
+ let key = DOM.Event.stringify(event);
if (this.wantCount && !this.command &&
(this.countStr ? /^[0-9]$/ : /^[1-9]$/).test(key))
diff --git a/common/content/mappings.js b/common/content/mappings.js
index 1275d40c..548d7b0a 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -48,7 +48,7 @@ var Map = Class("Map", {
name: Class.Memoize(function () this.names[0]),
/** @property {[string]} All of this mapping's names (key sequences). */
- names: Class.Memoize(function () this._keys.map(function (k) events.canonicalKeys(k))),
+ names: Class.Memoize(function () this._keys.map(function (k) DOM.Event.canonicalKeys(k))),
get toStringParams() [this.modes.map(function (m) m.name), this.names.map(String.quote)],
@@ -303,7 +303,7 @@ var MapHive = Class("MapHive", Contexts.Hive, {
for (let name in values(map.keys)) {
states.mappings[name] = map;
let state = "";
- for (let key in events.iterKeys(name)) {
+ for (let key in DOM.Event.iterKeys(name)) {
state += key;
if (state !== name)
states.candidates[state] = (states.candidates[state] || 0) + 1;
@@ -341,11 +341,11 @@ var Mappings = Module("mappings", {
if (!/<\*-/.test(keys))
return keys;
- return util.debrace(events.iterKeys(keys).map(function (key) {
+ return util.debrace(DOM.Event.iterKeys(keys).map(function (key) {
if (/^<\*-/.test(key))
return ["<", this.prefixes, key.slice(3)];
return key;
- }, this).flatten().array).map(function (k) events.canonicalKeys(k));
+ }, this).flatten().array).map(function (k) DOM.Event.canonicalKeys(k));
},
iterate: function (mode) {
diff --git a/common/content/modes.js b/common/content/modes.js
index c5ddb74c..a29b36b1 100644
--- a/common/content/modes.js
+++ b/common/content/modes.js
@@ -109,7 +109,7 @@ var Modes = Module("modes", {
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]))) {
+ if (eventList[0].charCode || /^<(?:.-)*(?:BS|Del|C-h|C-w|C-u|C-k)>$/.test(DOM.Event.stringify(eventList[0]))) {
dactyl.beep();
return KILL;
}
diff --git a/common/content/mow.js b/common/content/mow.js
index d2c769be..80359e65 100644
--- a/common/content/mow.js
+++ b/common/content/mow.js
@@ -170,7 +170,7 @@ var MOW = Module("mow", {
};
if (event.target instanceof HTMLAnchorElement)
- switch (events.toString(event)) {
+ switch (DOM.Event.stringify(event)) {
case "<LeftMouse>":
openLink(dactyl.CURRENT_TAB);
break;
diff --git a/common/modules/base.jsm b/common/modules/base.jsm
index 09bd47ac..9622aa8d 100644
--- a/common/modules/base.jsm
+++ b/common/modules/base.jsm
@@ -768,6 +768,7 @@ function Class() {
}
Class.extend(Constructor, superclass, args[0]);
+ memoize(Constructor, "closure", Class.makeClosure);
update(Constructor, args[1]);
Constructor.__proto__ = superclass;
diff --git a/common/modules/contexts.jsm b/common/modules/contexts.jsm
index 85525269..6f8eb7e4 100644
--- a/common/modules/contexts.jsm
+++ b/common/modules/contexts.jsm
@@ -421,7 +421,7 @@ var Contexts = Module("contexts", {
/* fallthrough */
case "-keys":
let silent = args["-silent"];
- rhs = events.canonicalKeys(rhs, true);
+ rhs = DOM.Event.canonicalKeys(rhs, true);
var action = function action() {
events.feedkeys(action.macro(makeParams(this, arguments)),
noremap, silent);
diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm
index 70f0048e..ee1cc1a6 100644
--- a/common/modules/dom.jsm
+++ b/common/modules/dom.jsm
@@ -787,8 +787,9 @@ var DOM = Class("DOM", {
/**
* Creates an actual event from a pseudo-event object.
*
- * The pseudo-event object (such as may be retrieved from events.fromString)
- * should have any properties you want the event to have.
+ * The pseudo-event object (such as may be retrieved from
+ * DOM.Event.parse) should have any properties you want the event to
+ * have.
*
* @param {Document} doc The DOM document to associate this event with
* @param {Type} type The type of event (keypress, click, etc.)
@@ -834,6 +835,337 @@ var DOM = Class("DOM", {
return evt;
}
}, {
+ init: function init() {
+ // NOTE: the order of ["Esc", "Escape"] or ["Escape", "Esc"]
+ // matters, so use that string as the first item, that you
+ // want to refer to within dactyl's source code for
+ // comparisons like if (key == "<Esc>") { ... }
+ this.keyTable = {
+ add: ["Plus", "Add"],
+ back_space: ["BS"],
+ count: ["count"],
+ delete: ["Del"],
+ escape: ["Esc", "Escape"],
+ insert: ["Insert", "Ins"],
+ leader: ["Leader"],
+ left_shift: ["LT", "<"],
+ nop: ["Nop"],
+ pass: ["Pass"],
+ return: ["Return", "CR", "Enter"],
+ right_shift: [">"],
+ space: ["Space", " "],
+ subtract: ["Minus", "Subtract"]
+ };
+
+ this.key_key = {};
+ this.code_key = {};
+ this.key_code = {};
+ this.code_nativeKey = {};
+
+ for (let list in values(this.keyTable))
+ for (let v in values(list)) {
+ if (v.length == 1)
+ v = v.toLowerCase();
+ this.key_key[v.toLowerCase()] = v;
+ }
+
+ for (let [k, v] in Iterator(Ci.nsIDOMKeyEvent)) {
+ this.code_nativeKey[v] = k.substr(4);
+
+ k = k.substr(7).toLowerCase();
+ let names = [k.replace(/(^|_)(.)/g, function (m, n1, n2) n2.toUpperCase())
+ .replace(/^NUMPAD/, "k")];
+
+ if (names[0].length == 1)
+ names[0] = names[0].toLowerCase();
+
+ if (k in this.keyTable)
+ names = this.keyTable[k];
+
+ this.code_key[v] = names[0];
+ for (let [, name] in Iterator(names)) {
+ this.key_key[name.toLowerCase()] = name;
+ this.key_code[name.toLowerCase()] = v;
+ }
+ }
+
+ // HACK: as Gecko does not include an event for <, we must add this in manually.
+ if (!("<" in this.key_code)) {
+ this.key_code["<"] = 60;
+ this.key_code["lt"] = 60;
+ this.code_key[60] = "lt";
+ }
+
+ return this;
+ },
+
+
+ code_key: Class.Memoize(function (prop) this.init()[prop]),
+ code_nativeKey: Class.Memoize(function (prop) this.init()[prop]),
+ keyTable: Class.Memoize(function (prop) this.init()[prop]),
+ key_code: Class.Memoize(function (prop) this.init()[prop]),
+ key_key: Class.Memoize(function (prop) this.init()[prop]),
+ pseudoKeys: Set(["count", "leader", "nop", "pass"]),
+
+ /**
+ * Converts a user-input string of keys into a canonical
+ * representation.
+ *
+ * <C-A> maps to <C-a>, <C-S-a> maps to <C-S-A>
+ * <C- > maps to <C-Space>, <S-a> maps to A
+ * << maps to <lt><lt>
+ *
+ * <S-@> is preserved, as in Vim, to allow untypeable key-combinations
+ * in macros.
+ *
+ * canonicalKeys(canonicalKeys(x)) == canonicalKeys(x) for all values
+ * of x.
+ *
+ * @param {string} keys Messy form.
+ * @param {boolean} unknownOk Whether unknown keys are passed
+ * through rather than being converted to <lt>keyname>.
+ * @default false
+ * @returns {string} Canonical form.
+ */
+ canonicalKeys: function canonicalKeys(keys, unknownOk) {
+ if (arguments.length === 1)
+ unknownOk = true;
+ return this.parse(keys, unknownOk).map(this.closure.stringify).join("");
+ },
+
+ iterKeys: function iterKeys(keys) iter(function () {
+ let match, re = /<.*?>?>|[^<]/g;
+ while (match = re.exec(keys))
+ yield match[0];
+ }()),
+
+ /**
+ * Converts an event string into an array of pseudo-event objects.
+ *
+ * These objects can be used as arguments to {@link #stringify} or
+ * {@link DOM.Event}, though they are unlikely to be much use for other
+ * purposes. They have many of the properties you'd expect to find on a
+ * real event, but none of the methods.
+ *
+ * Also may contain two "special" parameters, .dactylString and
+ * .dactylShift these are set for characters that can never by
+ * typed, but may appear in mappings, for example <Nop> is passed as
+ * dactylString, and dactylShift is set when a user specifies
+ * <S-@> where @ is a non-case-changeable, non-space character.
+ *
+ * @param {string} keys The string to parse.
+ * @param {boolean} unknownOk Whether unknown keys are passed
+ * through rather than being converted to <lt>keyname>.
+ * @default false
+ * @returns {Array[Object]}
+ */
+ parse: function parse(input, unknownOk) {
+ if (isArray(input))
+ return array.flatten(input.map(function (k) this.parse(k, unknownOk), this));
+
+ if (arguments.length === 1)
+ unknownOk = true;
+
+ let out = [];
+ for (let match in util.regexp.iterate(/<.*?>?>|[^<]|<(?!.*>)/g, input)) {
+ let evt_str = match[0];
+
+ let evt_obj = { ctrlKey: false, shiftKey: false, altKey: false, metaKey: false,
+ keyCode: 0, charCode: 0, type: "keypress" };
+
+ if (evt_str.length == 1) {
+ evt_obj.charCode = evt_str.charCodeAt(0);
+ evt_obj._keyCode = this.key_code[evt_str[0].toLowerCase()];
+ evt_obj.shiftKey = evt_str !== evt_str.toLowerCase();
+ }
+ else {
+ let [match, modifier, keyname] = evt_str.match(/^<((?:[*12CASM⌘]-)*)(.+?)>$/i) || [false, '', ''];
+ modifier = Set(modifier.toUpperCase());
+ keyname = keyname.toLowerCase();
+ evt_obj.dactylKeyname = keyname;
+ if (/^u[0-9a-f]+$/.test(keyname))
+ keyname = String.fromCharCode(parseInt(keyname.substr(1), 16));
+
+ if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) ||
+ this.key_code[keyname] || Set.has(this.pseudoKeys, keyname))) {
+ evt_obj.globKey ="*" in modifier;
+ evt_obj.ctrlKey ="C" in modifier;
+ evt_obj.altKey ="A" in modifier;
+ evt_obj.shiftKey ="S" in modifier;
+ evt_obj.metaKey ="M" in modifier || "⌘" in modifier;
+ evt_obj.dactylShift = evt_obj.shiftKey;
+
+ if (keyname.length == 1) { // normal characters
+ if (evt_obj.shiftKey)
+ keyname = keyname.toUpperCase();
+
+ evt_obj.dactylShift = evt_obj.shiftKey && keyname.toUpperCase() == keyname.toLowerCase();
+ evt_obj.charCode = keyname.charCodeAt(0);
+ evt_obj.keyCode = this.key_code[keyname.toLowerCase()];
+ }
+ else if (Set.has(this.pseudoKeys, keyname)) {
+ evt_obj.dactylString = "<" + this.key_key[keyname] + ">";
+ }
+ else if (/mouse$/.test(keyname)) { // mouse events
+ evt_obj.type = (/2-/.test(modifier) ? "dblclick" : "click");
+ evt_obj.button = ["leftmouse", "middlemouse", "rightmouse"].indexOf(keyname);
+ delete evt_obj.keyCode;
+ delete evt_obj.charCode;
+ }
+ else { // spaces, control characters, and <
+ evt_obj.keyCode = this.key_code[keyname];
+ evt_obj.charCode = 0;
+ }
+ }
+ else { // an invalid sequence starting with <, treat as a literal
+ out = out.concat(this.parse("<lt>" + evt_str.substr(1)));
+ continue;
+ }
+ }
+
+ // TODO: make a list of characters that need keyCode and charCode somewhere
+ if (evt_obj.keyCode == 32 || evt_obj.charCode == 32)
+ evt_obj.charCode = evt_obj.keyCode = 32; // <Space>
+ if (evt_obj.keyCode == 60 || evt_obj.charCode == 60)
+ evt_obj.charCode = evt_obj.keyCode = 60; // <lt>
+
+ evt_obj.modifiers = (evt_obj.ctrlKey && Ci.nsIDOMNSEvent.CONTROL_MASK)
+ | (evt_obj.altKey && Ci.nsIDOMNSEvent.ALT_MASK)
+ | (evt_obj.shiftKey && Ci.nsIDOMNSEvent.SHIFT_MASK)
+ | (evt_obj.metaKey && Ci.nsIDOMNSEvent.META_MASK);
+
+ out.push(evt_obj);
+ }
+ return out;
+ },
+
+ /**
+ * Converts the specified event to a string in dactyl key-code
+ * notation. Returns null for an unknown event.
+ *
+ * @param {Event} event
+ * @returns {string}
+ */
+ stringify: function stringify(event) {
+ if (isArray(event))
+ return event.map(function (e) this.stringify(e), this).join("");
+
+ if (event.dactylString)
+ return event.dactylString;
+
+ let key = null;
+ let modifier = "";
+
+ if (event.globKey)
+ modifier += "*-";
+ if (event.ctrlKey)
+ modifier += "C-";
+ if (event.altKey)
+ modifier += "A-";
+ if (event.metaKey)
+ modifier += "M-";
+
+ if (/^key/.test(event.type)) {
+ let charCode = event.type == "keyup" ? 0 : event.charCode; // Why? --Kris
+ if (charCode == 0) {
+ if (event.keyCode in this.code_key) {
+ key = this.code_key[event.keyCode];
+
+ if (event.shiftKey && (key.length > 1 || event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
+ modifier += "S-";
+ else if (!modifier && key.length === 1)
+ if (event.shiftKey)
+ key = key.toUpperCase();
+ else
+ key = key.toLowerCase();
+
+ if (!modifier && /^[a-z0-9]$/i.test(key))
+ return key;
+ }
+ }
+ // [Ctrl-Bug] special handling of mysterious <C-[>, <C-\\>, <C-]>, <C-^>, <C-_> bugs (OS/X)
+ // (i.e., cntrl codes 27--31)
+ // ---
+ // For more information, see:
+ // [*] Referenced mailing list msg: http://www.mozdev.org/pipermail/pentadactyl/2008-May/001548.html
+ // [*] Mozilla bug 416227: event.charCode in keypress handler has unexpected values on Mac for Ctrl with chars in "[ ] _ \"
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=416227
+ // [*] Mozilla bug 432951: Ctrl+'foo' doesn't seem same charCode as Meta+'foo' on Cocoa
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=432951
+ // ---
+ //
+ // The following fixes are only activated if config.OS.isMacOSX.
+ // Technically, they prevent mappings from <C-Esc> (and
+ // <C-C-]> if your fancy keyboard permits such things<?>), but
+ // these <C-control> mappings are probably pathological (<C-Esc>
+ // certainly is on Windows), and so it is probably
+ // harmless to remove the config.OS.isMacOSX if desired.
+ //
+ else if (config.OS.isMacOSX && event.ctrlKey && charCode >= 27 && charCode <= 31) {
+ if (charCode == 27) { // [Ctrl-Bug 1/5] the <C-[> bug
+ key = "Esc";
+ modifier = modifier.replace("C-", "");
+ }
+ else // [Ctrl-Bug 2,3,4,5/5] the <C-\\>, <C-]>, <C-^>, <C-_> bugs
+ key = String.fromCharCode(charCode + 64);
+ }
+ // a normal key like a, b, c, 0, etc.
+ else if (charCode > 0) {
+ key = String.fromCharCode(charCode);
+
+ if (!/^[a-z0-9]$/i.test(key) && key in this.key_code) {
+ // a named charCode key (<Space> and <lt>) space can be shifted, <lt> must be forced
+ if ((key.match(/^\s$/) && event.shiftKey) || event.dactylShift)
+ modifier += "S-";
+
+ key = this.code_key[this.key_code[key]];
+ }
+ else {
+ // a shift modifier is only allowed if the key is alphabetical and used in a C-A-M- mapping in the uppercase,
+ // or if the shift has been forced for a non-alphabetical character by the user while :map-ping
+ if (key !== key.toLowerCase() && (event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
+ modifier += "S-";
+ if (/^\s$/.test(key))
+ key = let (s = charCode.toString(16)) "U" + "0000".substr(4 - s.length) + s;
+ else if (modifier.length == 0)
+ return key;
+ }
+ }
+ if (key == null) {
+ if (event.shiftKey)
+ modifier += "S-";
+ key = this.key_key[event.dactylKeyname] || event.dactylKeyname;
+ }
+ if (key == null)
+ return null;
+ }
+ else if (event.type == "click" || event.type == "dblclick") {
+ if (event.shiftKey)
+ modifier += "S-";
+ if (event.type == "dblclick")
+ modifier += "2-";
+ // TODO: triple and quadruple click
+
+ switch (event.button) {
+ case 0:
+ key = "LeftMouse";
+ break;
+ case 1:
+ key = "MiddleMouse";
+ break;
+ case 2:
+ key = "RightMouse";
+ break;
+ }
+ }
+
+ if (key == null)
+ return null;
+
+ return "<" + modifier + key + ">";
+ },
+
+
defaults: {
load: { bubbles: false },
submit: { cancelable: true }
diff --git a/teledactyl/content/compose/dactyl.xul b/teledactyl/content/compose/dactyl.xul
deleted file mode 100644
index bcdb613a..00000000
--- a/teledactyl/content/compose/dactyl.xul
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ***** BEGIN LICENSE BLOCK ***** {{{
-// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
-//
-// This work is licensed for reuse under an MIT license. Details are
-// given in the LICENSE.txt file included with this file.
-}}} ***** END LICENSE BLOCK ***** -->
-
-<?xml-stylesheet href="chrome://dactyl/skin/dactyl.css" type="text/css"?>
-<!DOCTYPE overlay SYSTEM "dactyl.dtd" [
- <!ENTITY dactyl.content "chrome://dactyl/content/">
-]>
-
-<overlay id="dactyl"
- xmlns:dactyl="http://vimperator.org/namespaces/liberator"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:nc="http://home.netscape.com/NC-rdf#"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <script type="application/x-javascript;version=1.8" src="&dactyl.content;dactyl-overlay.js"/>
-
- <window id="&dactyl.mainWindow;">
-
- <keyset id="mainKeyset">
- <key id="key_open_vimbar" key=":" oncommand="dactyl.modules.commandline.open(':', '', dactyl.modules.modes.EX);" modifiers=""/>
- <key id="key_stop" keycode="VK_ESCAPE" oncommand="dactyl.modules.events.onEscape();"/>
- <!-- other keys are handled inside the event loop in events.js -->
- </keyset>
-
- <popupset>
- <panel id="dactyl-visualbell" dactyl:highlight="Bell"/>
- </popupset>
-
- <!--this notifies us also of focus events in the XUL
- from: http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands !-->
- <commandset id="onPentadactylFocus"
- commandupdater="true"
- events="focus"
- oncommandupdate="if (dactyl.modules.events != undefined) dactyl.modules.events.onFocusChange(event);"/>
- <commandset id="onPentadactylSelect"
- commandupdater="true"
- events="select"
- oncommandupdate="if (dactyl.modules.events != undefined) dactyl.modules.events.onSelectionChange(event);"/>
-
- <!-- As of Firefox 3.1pre, <iframe>.height changes do not seem to have immediate effect,
- therefore we need to put them into a <vbox> for which that works just fine -->
- <vbox class="dactyl-container" hidden="false" collapsed="true">
- <iframe id="dactyl-multiline-output" src="chrome://dactyl/content/buffer.xhtml"
- flex="1" hidden="false" collapsed="false"
- onclick="dactyl.modules.commandline.onMultilineOutputEvent(event)"/>
- </vbox>
-
- <vbox class="dactyl-container" hidden="false" collapsed="true">
- <iframe id="dactyl-completions" src="chrome://dactyl/content/buffer.xhtml"
- flex="1" hidden="false" collapsed="false"
- onclick="dactyl.modules.commandline.onMultilineOutputEvent(event)"/>
- </vbox>
-
- <stack orient="horizontal" align="stretch" class="dactyl-container" dactyl:highlight="CmdLine">
- <textbox class="plain" id="dactyl-message" flex="1" readonly="true" dactyl:highlight="Normal"/>
- <hbox id="dactyl-commandline" hidden="false" collapsed="true" class="dactyl-container" dactyl:highlight="Normal">
- <label class="plain" id="dactyl-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
- <textbox class="plain" id="dactyl-commandline-command" flex="1" type="timed" timeout="100"
- oninput="dactyl.modules.commandline.onEvent(event);"
- onkeyup="dactyl.modules.commandline.onEvent(event);"
- onfocus="dactyl.modules.commandline.onEvent(event);"
- onblur="dactyl.modules.commandline.onEvent(event);"/>
- </hbox>
- </stack>
-
- <vbox class="dactyl-container" hidden="false" collapsed="false">
- <textbox id="dactyl-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true"
- onkeypress="dactyl.modules.commandline.onMultilineInputEvent(event);"
- oninput="dactyl.modules.commandline.onMultilineInputEvent(event);"
- onblur="dactyl.modules.commandline.onMultilineInputEvent(event);"/>
- </vbox>
-
- </window>
-
- <statusbar id="status-bar" dactyl:highlight="StatusLine">
- <hbox insertbefore="&dactyl.statusBefore;" insertafter="&dactyl.statusAfter;"
- id="dactyl-statusline" flex="1" hidden="false" align="center">
- <textbox class="plain" id="dactyl-statusline-field-url" readonly="false" flex="1" crop="end"/>
- <label class="plain" id="dactyl-statusline-field-inputbuffer" flex="0"/>
- <label class="plain" id="dactyl-statusline-field-progress" flex="0"/>
- <label class="plain" id="dactyl-statusline-field-tabcount" flex="0"/>
- <label class="plain" id="dactyl-statusline-field-bufferposition" flex="0"/>
- </hbox>
- <!-- just hide them since other elements expect them -->
- <statusbarpanel id="statusbar-display" hidden="true"/>
- <statusbarpanel id="statusbar-progresspanel" hidden="true"/>
- </statusbar>
-
-</overlay>
-
-<!-- vim: set fdm=marker sw=4 ts=4 et: -->