diff --git a/common/content/browser.js b/common/content/browser.js
index fd00987e..ac647fee 100644
--- a/common/content/browser.js
+++ b/common/content/browser.js
@@ -65,7 +65,7 @@ var Browser = Module("browser", {
mappings: function () {
mappings.add([modes.NORMAL],
- ["y"], "Yank current location to the clipboard",
+ ["y", ""], "Yank current location to the clipboard",
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
// opening websites
diff --git a/common/content/buffer.js b/common/content/buffer.js
index 1bbaab99..05223fe4 100644
--- a/common/content/buffer.js
+++ b/common/content/buffer.js
@@ -1651,7 +1651,7 @@ var Buffer = Module("buffer", {
mappings: function () {
var myModes = config.browserModes;
- mappings.add(myModes, ["."],
+ mappings.add(myModes, [".", ""],
"Repeat the last key event",
function (args) {
if (mappings.repeat) {
@@ -1670,31 +1670,31 @@ var Buffer = Module("buffer", {
function () { ex.stop(); });
// scrolling
- mappings.add(myModes, ["j", "", ""],
+ mappings.add(myModes, ["j", "", "", ""],
"Scroll document down",
function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["k", "", ""],
+ mappings.add(myModes, ["k", "", "", ""],
"Scroll document up",
function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, dactyl.has("mail") ? ["h"] : ["h", ""],
+ mappings.add(myModes, dactyl.has("mail") ? ["h", ""] : ["h", "", ""],
"Scroll document to the left",
function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, dactyl.has("mail") ? ["l"] : ["l", ""],
+ mappings.add(myModes, dactyl.has("mail") ? ["l", ""] : ["l", "", ""],
"Scroll document to the right",
function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["0", "^"],
+ mappings.add(myModes, ["0", "^", ""],
"Scroll to the absolute left of the document",
function () { buffer.scrollToPercent(0, null); });
- mappings.add(myModes, ["$"],
+ mappings.add(myModes, ["$", ""],
"Scroll to the absolute right of the document",
function () { buffer.scrollToPercent(100, null); });
@@ -1708,7 +1708,7 @@ var Buffer = Module("buffer", {
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
{ count: true });
- mappings.add(myModes, ["%"],
+ mappings.add(myModes, ["%", ""],
"Scroll to {count} percent of the document",
function (args) {
dactyl.assert(args.count > 0 && args.count <= 100);
@@ -1716,59 +1716,59 @@ var Buffer = Module("buffer", {
},
{ count: true });
- mappings.add(myModes, [""],
+ mappings.add(myModes, ["", ""],
"Scroll window downwards in the buffer",
function (args) { buffer._scrollByScrollSize(args.count, true); },
{ count: true });
- mappings.add(myModes, [""],
+ mappings.add(myModes, ["", ""],
"Scroll window upwards in the buffer",
function (args) { buffer._scrollByScrollSize(args.count, false); },
{ count: true });
- mappings.add(myModes, ["", "", ""],
+ mappings.add(myModes, ["", "", "", ""],
"Scroll up a full page",
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["", "", ""],
+ mappings.add(myModes, ["", "", "", ""],
"Scroll down a full page",
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["]f"],
+ mappings.add(myModes, ["]f", ""],
"Focus next frame",
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["[f"],
+ mappings.add(myModes, ["[f", ""],
"Focus previous frame",
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
{ count: true });
- mappings.add(myModes, ["]]"],
+ mappings.add(myModes, ["]]", ""],
"Follow the link labeled 'next' or '>' if it exists",
function (args) {
buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true);
},
{ count: true });
- mappings.add(myModes, ["[["],
+ mappings.add(myModes, ["[[", ""],
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
function (args) {
buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
},
{ count: true });
- mappings.add(myModes, ["gf"],
+ mappings.add(myModes, ["gf", ""],
"Toggle between rendered and source view",
function () { buffer.viewSource(null, false); });
- mappings.add(myModes, ["gF"],
+ mappings.add(myModes, ["gF", ""],
"View source with an external editor",
function () { buffer.viewSource(null, true); });
- mappings.add(myModes, ["gi"],
+ mappings.add(myModes, ["gi", ""],
"Focus last used input field",
function (args) {
let elem = buffer.lastInputField;
@@ -1808,7 +1808,7 @@ var Buffer = Module("buffer", {
dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
});
- mappings.add(myModes, ["p", ""],
+ mappings.add(myModes, ["p", "", ""],
"Open (put) a URL based on the current clipboard contents in the current buffer",
function () {
let url = dactyl.clipboardRead();
@@ -1816,7 +1816,7 @@ var Buffer = Module("buffer", {
dactyl.open(url);
});
- mappings.add(myModes, ["P"],
+ mappings.add(myModes, ["P", ""],
"Open (put) a URL based on the current clipboard contents in a new buffer",
function () {
let url = dactyl.clipboardRead();
@@ -1825,16 +1825,16 @@ var Buffer = Module("buffer", {
});
// reloading
- mappings.add(myModes, ["r"],
+ mappings.add(myModes, ["r", ""],
"Reload the current web page",
function () { tabs.reload(tabs.getTab(), false); });
- mappings.add(myModes, ["R"],
+ mappings.add(myModes, ["R", ""],
"Reload while skipping the cache",
function () { tabs.reload(tabs.getTab(), true); });
// yanking
- mappings.add(myModes, ["Y"],
+ mappings.add(myModes, ["Y", ""],
"Copy selected text or current word",
function () {
let sel = buffer.getCurrentWord();
@@ -1843,62 +1843,62 @@ var Buffer = Module("buffer", {
});
// zooming
- mappings.add(myModes, ["zi", "+"],
+ mappings.add(myModes, ["zi", "+", ""],
"Enlarge text zoom of current web page",
function (args) { buffer.zoomIn(Math.max(args.count, 1), false); },
{ count: true });
- mappings.add(myModes, ["zm"],
+ mappings.add(myModes, ["zm", ""],
"Enlarge text zoom of current web page by a larger amount",
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); },
{ count: true });
- mappings.add(myModes, ["zo", "-"],
+ mappings.add(myModes, ["zo", "-", ""],
"Reduce text zoom of current web page",
function (args) { buffer.zoomOut(Math.max(args.count, 1), false); },
{ count: true });
- mappings.add(myModes, ["zr"],
+ mappings.add(myModes, ["zr", ""],
"Reduce text zoom of current web page by a larger amount",
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); },
{ count: true });
- mappings.add(myModes, ["zz"],
+ mappings.add(myModes, ["zz", ""],
"Set text zoom value of current web page",
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); },
{ count: true });
- mappings.add(myModes, ["ZI", "zI"],
+ mappings.add(myModes, ["ZI", "zI", ""],
"Enlarge full zoom of current web page",
function (args) { buffer.zoomIn(Math.max(args.count, 1), true); },
{ count: true });
- mappings.add(myModes, ["ZM", "zM"],
+ mappings.add(myModes, ["ZM", "zM", ""],
"Enlarge full zoom of current web page by a larger amount",
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); },
{ count: true });
- mappings.add(myModes, ["ZO", "zO"],
+ mappings.add(myModes, ["ZO", "zO", ""],
"Reduce full zoom of current web page",
function (args) { buffer.zoomOut(Math.max(args.count, 1), true); },
{ count: true });
- mappings.add(myModes, ["ZR", "zR"],
+ mappings.add(myModes, ["ZR", "zR", ""],
"Reduce full zoom of current web page by a larger amount",
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); },
{ count: true });
- mappings.add(myModes, ["zZ"],
+ mappings.add(myModes, ["zZ", ""],
"Set full zoom value of current web page",
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); },
{ count: true });
// page info
- mappings.add(myModes, [""],
+ mappings.add(myModes, ["", ""],
"Print the current file name",
function () { buffer.showPageInfo(false); });
- mappings.add(myModes, ["g"],
+ mappings.add(myModes, ["g", ""],
"Print file information",
function () { buffer.showPageInfo(true); });
},
diff --git a/common/content/commandline.js b/common/content/commandline.js
index 0e98e956..159a1de9 100644
--- a/common/content/commandline.js
+++ b/common/content/commandline.js
@@ -372,7 +372,6 @@ var CommandMode = Class("CommandMode", {
if (!this.completions.itemList.visible)
this.autocompleteTimer.flush();
}
- util.dump("input", commandline.command, this.widgets.command[1]);
this.onChange(commandline.command);
},
keyup: function onKeyUp(event) {
diff --git a/common/content/events.js b/common/content/events.js
index b0cfecee..e6c3400a 100644
--- a/common/content/events.js
+++ b/common/content/events.js
@@ -33,6 +33,7 @@ var ProcessorStack = Class("ProcessorStack", {
process: function process(event) {
function dbg() {}
+ let dbg = util.closure.dump;
let key = events.toString(event);
this.events.push(event);
@@ -90,21 +91,20 @@ var ProcessorStack = Class("ProcessorStack", {
if (processors.length)
result = Events.KILL;
else if (this.actions.length) {
- if (actions.length == 0)
+ if (actions.length == 0) {
dactyl.beep();
-
- if (modes.replaying && !events.waitForPageLoad())
- result = Events.KILL;
- else {
- for (var res = this.actions[0]; callable(res);)
- res = res();
- result = res === Events.PASS ? Events.PASS : Events.KILL;
+ events.feedingKeys = false;
}
+
+ for (var res = this.actions[0]; callable(res);)
+ res = res();
+ result = res === Events.PASS ? Events.PASS : Events.KILL;
}
else if (result !== Events.KILL && !this.actions.length &&
processors.some(function (p) !p.main.passUnknown)) {
result = Events.KILL;
dactyl.beep();
+ events.feedingKeys = false;
}
else if (result === undefined)
result = Events.PASS;
@@ -115,9 +115,11 @@ var ProcessorStack = Class("ProcessorStack", {
if (result === Events.PASS || result === Events.ABORT)
this.events.filter(function (e) e.getPreventDefault())
.forEach(function (event, i) {
+ let elem = event.originalTarget;
if (event.originalTarget) {
- let evt = events.create(event.originalTarget.ownerDocument, event.type, event);
- events.dispatch(event.originalTarget, evt, { skipmap: true, isMacro: true });
+ let doc = elem.ownerDocument || elem.document || elem;
+ let evt = events.create(doc, event.type, event);
+ events.dispatch(elem, evt, { skipmap: true, isMacro: true, isReplay: true });
}
else if (i > 0)
events.events.keypress.call(events, event);
@@ -491,6 +493,8 @@ var Events = Module("events", {
util.threadYield(1, true);
for (let [, evt_obj] in Iterator(events.fromString(keys))) {
+ let now = Date.now();
+ util.dump("+feed", events.toString(evt_obj));
for (let type in values(["keydown", "keyup", "keypress"])) {
let evt = update({}, evt_obj, { type: type });
@@ -508,13 +512,10 @@ var Events = Module("events", {
else
events.events.keypress.call(events, event);
}
+ util.dump("-feed", events.toString(evt_obj), this.feedingKeys);
if (!this.feedingKeys)
break;
-
- // Stop feeding keys if page loading failed.
- if (modes.replaying && !this.waitForPageLoad())
- break;
}
}
catch (e) {
@@ -594,9 +595,14 @@ var Events = Module("events", {
* of x.
*
* @param {string} keys Messy form.
+ * @param {boolean} unknownOk Whether unknown keys are passed
+ * through rather than being converted to 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("");
},
@@ -1035,7 +1041,7 @@ var Events = Module("events", {
if (!key)
return null;
- if (modes.recording && (!this._input || !mappings.user.hasMap(modes.main, this._input.buffer + key)))
+ if (modes.recording && !event.isReplay)
events._macroKeys.push(key);
// feedingKeys needs to be separate from interrupted so
@@ -1043,8 +1049,6 @@ var Events = Module("events", {
// interrupting whatever it's started and a real
// interrupting our playback.
if (events.feedingKeys && !event.isMacro) {
- if (!event.originalTarget)
- util.dumpStack();
if (key == "") {
events.feedingKeys = false;
if (modes.replaying) {
@@ -1323,11 +1327,11 @@ var Events = Module("events", {
},
mappings: function () {
mappings.add(modes.all,
- [""], "Temporarily ignore all " + config.appName + " key bindings",
+ ["", ""], "Temporarily ignore all " + config.appName + " key bindings",
function () { modes.push(modes.PASS_THROUGH); });
mappings.add(modes.all,
- [""], "Pass through next key",
+ ["", ""], "Pass through next key",
function () {
if (modes.main == modes.QUOTE)
return Events.PASS;
@@ -1340,7 +1344,7 @@ var Events = Module("events", {
// macros
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
- ["q"], "Record a key sequence into a macro",
+ ["q", ""], "Record a key sequence into a macro",
function ({ arg }) {
events._macroKeys.pop();
events[modes.recording ? "finishRecording" : "startRecording"](arg);
@@ -1348,13 +1352,34 @@ var Events = Module("events", {
{ get arg() !modes.recording });
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
- ["@"], "Play a macro",
+ ["@", ""], "Play a macro",
function ({ arg, count }) {
count = Math.max(count, 1);
while (count-- && events.playMacro(arg))
;
},
{ arg: true, count: true });
+
+ mappings.add([modes.COMMAND],
+ ["s", ""], "Sleep for {count} milliseconds before continuing macro playback",
+ function ({ command, count }) {
+ let now = Date.now();
+ util.dump("+", count);
+ dactyl.assert(count, "Count required for " + command);
+ if (events.feedingKeys)
+ util.sleep(count);
+ util.dump("-", count, now - Date.now());
+ },
+ { count: true });
+
+ mappings.add([modes.MAIN],
+ ["l", ""], "Wait for the current page to finish loading before continuing macro playback",
+ function () {
+ if (!events.waitForPageLoad()) {
+ util.interrupted = true;
+ throw Error("Interrupted");
+ }
+ });
},
options: function () {
options.add(["passkeys", "pk"],
diff --git a/common/content/mappings.js b/common/content/mappings.js
index b5255d77..1beae7bb 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -113,9 +113,17 @@ var Map = Class("Map", {
if (this.executing)
util.dumpStack("Attempt to execute mapping recursively: " + args.command);
dactyl.assert(!this.executing, "Attempt to execute mapping recursively: " + args.command);
- this.executing = true;
- let res = dactyl.trapErrors(repeat);
- this.executing = false;
+
+ try {
+ this.executing = true;
+ var res = repeat();
+ }
+ catch (e) {
+ events.feedingKeys = false;
+ }
+ finally {
+ this.executing = false;
+ }
return res;
}
diff --git a/common/locale/en-US/browsing.xml b/common/locale/en-US/browsing.xml
index fb48cf46..03011a37 100644
--- a/common/locale/en-US/browsing.xml
+++ b/common/locale/en-US/browsing.xml
@@ -20,7 +20,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
&dactyl.host; or to a web page, you have two options:
-
- CTRL-Z]]>
+ CTRL-Z]]>
<C-z>
@@ -33,7 +33,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
-
- CTRL-V]]>
+ CTRL-V]]>
<C-v>
@@ -171,7 +171,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
-
- p]]>
+ p]]>
p
@@ -185,7 +185,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
-
- P
+ <tab-open-clipboard-url> P
P
@@ -343,7 +343,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
Reloading
-
- r
+ <reload> r
r
Reload the current web page.
@@ -351,7 +351,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
-
- R
+ <full-reload> R
R
Reload the current web page without using the cache.
diff --git a/common/locale/en-US/buffer.xml b/common/locale/en-US/buffer.xml
index 5dbcfae5..66d0d978 100644
--- a/common/locale/en-US/buffer.xml
+++ b/common/locale/en-US/buffer.xml
@@ -22,7 +22,7 @@
Buffer information
-
- ]]>
+ ]]>
<C-g>
@@ -35,7 +35,7 @@
-
- ]]>
+ g]]>
g<C-g>
Print file information. Same as :pageinfo.
@@ -95,7 +95,7 @@
Motion commands
-
- ^ 0
+ <scroll-begin> ^ 0
0
@@ -107,7 +107,7 @@
-
- $
+ <scroll-end> $
$
Scroll to the absolute right of the document
@@ -140,7 +140,7 @@
-
- N%
+ <scroll-percent> N%
count%
Scroll to count percent of the document.
@@ -148,7 +148,7 @@
-
- h]]>
+ h]]>
counth
@@ -160,7 +160,7 @@
-
- j]]>
+ j]]>
countj
@@ -172,7 +172,7 @@
-
- k]]>
+ k]]>
countk
@@ -184,7 +184,7 @@
-
- l]]>
+ l]]>
countl
@@ -196,7 +196,7 @@
-
- ]]>
+ ]]>
count<C-d>
@@ -209,7 +209,7 @@
-
- ]]>
+ ]]>
count<C-u>
@@ -222,7 +222,7 @@
-
- ]]>
+ ]]>
count<C-b>
@@ -234,7 +234,7 @@
-
- ]]>
+ ]]>
count<C-f>
@@ -264,7 +264,7 @@
-
- gi
+ <focus-input> gi
countgi
@@ -277,7 +277,7 @@
-
- ]f
+ <next-frame> ]f
count]f
@@ -290,7 +290,7 @@
-
- [f
+ <previous-frame> [f
count[f
@@ -303,7 +303,7 @@
-
- ]]
+ <next-page> ]]
count]]
@@ -316,7 +316,7 @@
-
- [[
+ <previous-page> [[
count[[
@@ -361,7 +361,7 @@
-
- + zi
+ + zi]]>
countzi
Enlarge text zoom of current web page. Mnemonic: zoom in.
@@ -369,7 +369,7 @@
-
- zm
+ zm]]>
countzm
@@ -378,7 +378,7 @@
-
- - zo
+ - zo]]>
countzo
Reduce text zoom of current web page. Mnemonic: zoom out.
@@ -386,7 +386,7 @@
-
- zr
+ zr]]>
countzr
Reduce text zoom of current web page by a larger amount. Mnemonic: zoom reduce.
@@ -394,7 +394,7 @@
-
- zz
+ zz]]>
countzz
@@ -407,7 +407,7 @@
-
- ZI zI
+ ZI zI]]>
countZI
Enlarge full zoom of current web page. Mnemonic: zoom in.
@@ -415,7 +415,7 @@
-
- ZM zM
+ ZM zM]]>
countZM
@@ -424,7 +424,7 @@
-
- ZO zO
+ ZO zO]]>
countZO
Reduce full zoom of current web page. Mnemonic: zoom out.
@@ -432,7 +432,7 @@
-
- ZR zR
+ ZR zR]]>
countZR
Reduce full zoom of current web page by a larger amount. Mnemonic: zoom reduce.
@@ -440,7 +440,7 @@
-
- zZ
+ zZ]]>
countzZ
@@ -489,7 +489,7 @@
-
- y
+ <yank-location> y
y
Yank current location to the clipboard.
@@ -497,7 +497,7 @@
-
- Y
+ <yank-word> Y
Y
Copy currently selected text to the system clipboard.
diff --git a/common/locale/en-US/repeat.xml b/common/locale/en-US/repeat.xml
index 753bc48f..8070ce21 100644
--- a/common/locale/en-US/repeat.xml
+++ b/common/locale/en-US/repeat.xml
@@ -21,7 +21,7 @@
Single repeats
-
- .
+ <repeat-key> .
count.
@@ -45,7 +45,7 @@
Macros
-
- q
+ <record-macro> q
q0-9a-zA-Z
@@ -82,7 +82,7 @@
-
- @
+ <play-macro> @
count@a-z0-9
@@ -100,6 +100,38 @@
+Macro utilities
+
+
+ The following key bindings facilitate the recording of efficient
+ macros. They have no effect when typed normally, but are
+ recorded and take effect during macro playback.
+
+
+-
+ s
+
+ count<m>s
+
+
+ Sleep for count milliseconds before resuming playback.
+
+
+
+
+-
+ l]]>
+
+ l]]>
+
+
+ Wait for the current page to finish loading before resuming
+ playback.
+
+
+
+
+
Using scripts
-
diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS
index 88787654..189ac3bf 100644
--- a/pentadactyl/NEWS
+++ b/pentadactyl/NEWS
@@ -66,6 +66,8 @@
and linking to source code locations).
- :downloads now opens a download list in the multi-line output
buffer.
+ - Added :mapgroup command and -group flag to :map, :unmap, and
+ :mapclear.
- :extensions has been replaced with a more powerful :addons.
- Added :cookies command.
- :extadd now supports remote URLs as well as local files on Firefox 4.
@@ -91,6 +93,14 @@
- :command now accepts comma-separated alternative command names.
- :command -complete custom now also accepts a completions array, see
:h :command-completion-custom.
+ * Mapping changes:
+ - Added site-specific mapping groups and related command
+ changes.
+ - Added named aliases for many mappings
+ - Added l and s to aid in the construction of macros.
+ - Removed the implicit page load delays during macro playback.
+ - Added the base modes Base, Main, and Command which other
+ modes inherit key bindings from.
* Improvements to :style and :highlight:
- Added -agent flag to :style.
- The -append flag now updates existing properties rather than