From f8bd1fd4fbd0940e66779fbbbe50d0e56cdefb31 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Mon, 14 Mar 2011 15:19:54 -0400 Subject: [PATCH] Allow <*-Globbed> modifier keys. --- common/content/events.js | 33 +++++++++++++++++++-------------- common/content/mappings.js | 35 ++++++++++++++++++++++++++--------- common/modules/base.jsm | 3 ++- common/modules/util.jsm | 24 ++++++++++++++++++++++-- 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/common/content/events.js b/common/content/events.js index d3fd363f..3730b4e2 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -772,11 +772,11 @@ var Events = Module("events", { return events.fromString(keys, unknownOk).map(events.closure.toString).join(""); }, - iterKeys: function (keys) { + iterKeys: function (keys) iter(function () { let match, re = /<.*?>?>|[^<]/g; while (match = re.exec(keys)) yield match[0]; - }, + }()), /** * Dispatches an event to an element as if it were a native event. @@ -850,8 +850,8 @@ var Events = Module("events", { keyCode: 0, charCode: 0, type: "keypress" }; if (evt_str.length > 1) { // <.*?> - let [match, modifier, keyname] = evt_str.match(/^<((?:[CSMA]-)*)(.+?)>$/i) || [false, '', '']; - modifier = modifier.toUpperCase(); + 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)) @@ -859,17 +859,16 @@ var Events = Module("events", { if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) || this._key_code[keyname] || set.has(this._pseudoKeys, keyname))) { - evt_obj.ctrlKey = /C-/.test(modifier); - evt_obj.altKey = /A-/.test(modifier); - evt_obj.shiftKey = /S-/.test(modifier); - evt_obj.metaKey = /M-/.test(modifier); + evt_obj.globKey = modifier["*"]; + evt_obj.ctrlKey = modifier["C"]; + evt_obj.altKey = modifier["A"]; + evt_obj.shiftKey = modifier["S"]; + evt_obj.metaKey = modifier["M"]; + evt_obj.dactylShift = evt_obj.shiftKey; if (keyname.length == 1) { // normal characters - if (evt_obj.shiftKey) { + if (evt_obj.shiftKey) keyname = keyname.toUpperCase(); - if (keyname == keyname.toLowerCase()) - evt_obj.dactylShift = true; - } evt_obj.charCode = keyname.charCodeAt(0); evt_obj._keyCode = this._key_code[keyname]; @@ -931,6 +930,8 @@ var Events = Module("events", { let key = null; let modifier = ""; + if (event.globKey) + modifier += "*-"; if (event.ctrlKey) modifier += "C-"; if (event.altKey) @@ -951,6 +952,7 @@ var Events = Module("events", { key = key.toUpperCase(); else key = key.toLowerCase(); + if (!modifier && /^[a-z0-9]$/i.test(key)) return key; } @@ -995,7 +997,7 @@ var Events = Module("events", { 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) + 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; @@ -1003,8 +1005,11 @@ var Events = Module("events", { return key; } } - if (key == null) + if (key == null) { + if (event.shiftKey) + modifier += "S-"; key = this._key_key[event.dactylKeyname] || event.dactylKeyname; + } if (key == null) return null; } diff --git a/common/content/mappings.js b/common/content/mappings.js index f78d6f59..e9d322fc 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -30,18 +30,13 @@ */ var Map = Class("Map", { init: function (modes, keys, description, action, extraInfo) { - modes = Array.concat(modes); - if (!modes.every(util.identity)) - throw TypeError("Invalid modes: " + modes); - this.id = ++Map.id; this.modes = modes; this._keys = keys; this.action = action; this.description = description; - if (Object.freeze) - Object.freeze(this.modes); + Object.freeze(this.modes); if (extraInfo) update(this, extraInfo); @@ -96,7 +91,7 @@ var Map = Class("Map", { */ hasName: function (name) this.keys.indexOf(name) >= 0, - get keys() this.names.map(mappings.expandLeader), + get keys() array.flatten(this.names.map(mappings.closure.expand)), /** * Execute the action for this mapping. @@ -159,7 +154,7 @@ var MapHive = Class("MapHive", Contexts.Hive, { }, /** - * Adds a new default key mapping. + * Adds a new key mapping. * * @param {number[]} modes The modes that this mapping applies to. * @param {string[]} keys The key sequences which are bound to *action*. @@ -171,6 +166,10 @@ var MapHive = Class("MapHive", Contexts.Hive, { add: function (modes, keys, description, action, extra) { extra = extra || {}; + modes = Array.concat(modes); + if (!modes.every(util.identity)) + throw TypeError("Invalid modes: " + modes); + let map = Map(modes, keys, description, action, extra); map.definedAt = contexts.getCaller(Components.stack.caller); map.hive = this; @@ -307,7 +306,25 @@ var Mappings = Module("mappings", { get userHives() this.allHives.filter(function (h) h !== this.builtin, this), - expandLeader: function (keyString) keyString.replace(//i, options["mapleader"]), + expandLeader: deprecated("expand", function expandLeader(keyString) keyString.replace(//i, options["mapleader"])), + + prefixes: Class.memoize(function () { + let list = Array.slice("CASM").map(function (s) s + "-"); + return iter(util.range(0, 1 << list.length)).map(function (mask) + list.filter(function (p, i) mask & (1 << i)).join("")).toArray().concat("*-"); + }), + + expand: function expand(keys) { + keys = keys.replace(//i, options["mapleader"]); + if (!/<\*-/.test(keys)) + return keys; + + return util.debrace(events.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)); + }, iterate: function (mode) { let seen = {}; diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 38f732a9..fca0c9ae 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -307,7 +307,8 @@ function deprecated(alternative, fn) { let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments); function deprecatedMethod() { - let obj = this.className ? this.className + "#" : + let obj = !this ? "" : + this.className ? this.className + "#" : this.constructor.className ? this.constructor.className + "#" : ""; diff --git a/common/modules/util.jsm b/common/modules/util.jsm index a8ffcc26..35476bf5 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -484,10 +484,28 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * Example: * "a{b,c}d" => ["abd", "acd"] * - * @param {string} pattern The pattern to deglob. + * @param {string|[string|Array]} pattern The pattern to deglob. * @returns [string] The resulting strings. */ debrace: function debrace(pattern) { + if (isArray(pattern)) { + let res = []; + let rec = function rec(acc) { + let vals; + + while (isString(vals = pattern[acc.length])) + acc.push(vals); + + if (acc.length == pattern.length) + res.push(acc.join("")) + else + for (let val in values(vals)) + rec(acc.concat(val)); + } + rec([]); + return res; + } + if (pattern.indexOf("{") == -1) return [pattern]; @@ -502,12 +520,14 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), res.push(pattern.substr(end)); return res.map(function (s) util.dequote(s, dequote)); } - let patterns = [], res = []; + let patterns = []; let substrings = split(pattern, /((?:[^\\{]|\\.)*)\{((?:[^\\}]|\\.)*)\}/gy, function (match) { patterns.push(split(match[2], /((?:[^\\,]|\\.)*),/gy, null, ",{}")); }, "{}"); + + let res = []; function rec(acc) { if (acc.length == patterns.length) res.push(array(substrings).zip(acc).flatten().join(""));