summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKris Maglione <maglione.k@gmail.com>2011-01-26 03:36:34 -0500
committerKris Maglione <maglione.k@gmail.com>2011-01-26 03:36:34 -0500
commit0a7f399bf7658ca8a2bcc55ebb4af646162faa35 (patch)
tree499b2068a6f36a8535b3cf378a07a7936a11120e /common
parent102fb67efff462160837ce733c8d542f91f8467a (diff)
downloadpentadactyl-0a7f399bf7658ca8a2bcc55ebb4af646162faa35.tar.gz
Use table tags in :addons and :downloads to better support :yank.
Diffstat (limited to 'common')
-rw-r--r--common/content/bookmarks.js10
-rw-r--r--common/content/browser.js12
-rw-r--r--common/content/buffer.js58
-rw-r--r--common/content/commandline.js946
-rw-r--r--common/content/dactyl.js6
-rw-r--r--common/content/events.js17
-rw-r--r--common/content/hints.js87
-rw-r--r--common/content/mappings.js2
-rw-r--r--common/content/modes.js46
-rw-r--r--common/content/mow.js352
-rw-r--r--common/content/tabs.js2
-rw-r--r--common/modules/addons.jsm37
-rw-r--r--common/modules/base.jsm11
-rw-r--r--common/modules/downloads.jsm58
-rw-r--r--common/modules/finder.jsm57
-rw-r--r--common/modules/io.jsm31
-rw-r--r--common/modules/overlay.jsm9
-rw-r--r--common/modules/template.jsm4
18 files changed, 891 insertions, 854 deletions
diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js
index 8b741d9a..fb936e3b 100644
--- a/common/content/bookmarks.js
+++ b/common/content/bookmarks.js
@@ -115,9 +115,8 @@ var Bookmarks = Module("bookmarks", {
if (charset != null && charset !== "UTF-8")
options["-charset"] = charset;
- commandline.open(":",
- commands.commandToString({ command: "bmark", options: options, arguments: [url] }) + " -keyword ",
- modes.EX);
+ CommandExMode().open(
+ commands.commandToString({ command: "bmark", options: options, arguments: [url] }) + " -keyword ");
},
/**
@@ -582,9 +581,8 @@ var Bookmarks = Module("bookmarks", {
options["-charset"] = content.document.characterSet;
}
- commandline.open(":",
- commands.commandToString({ command: "bmark", options: options, arguments: [buffer.uri.spec] }),
- modes.EX);
+ CommandExMode().open(
+ commands.commandToString({ command: "bmark", options: options, arguments: [buffer.uri.spec] }));
});
mappings.add(myModes, ["A"],
diff --git a/common/content/browser.js b/common/content/browser.js
index 1be4fcfb..fd00987e 100644
--- a/common/content/browser.js
+++ b/common/content/browser.js
@@ -71,27 +71,27 @@ var Browser = Module("browser", {
// opening websites
mappings.add([modes.NORMAL],
["o"], "Open one or more URLs",
- function () { commandline.open(":", "open ", modes.EX); });
+ function () { CommandExMode().open("open "); });
mappings.add([modes.NORMAL], ["O"],
"Open one or more URLs, based on current location",
- function () { commandline.open(":", "open " + buffer.uri.spec, modes.EX); });
+ function () { CommandExMode().open("open " + buffer.uri.spec); });
mappings.add([modes.NORMAL], ["t"],
"Open one or more URLs in a new tab",
- function () { commandline.open(":", "tabopen ", modes.EX); });
+ function () { CommandExMode().open("tabopen "); });
mappings.add([modes.NORMAL], ["T"],
"Open one or more URLs in a new tab, based on current location",
- function () { commandline.open(":", "tabopen " + buffer.uri.spec, modes.EX); });
+ function () { CommandExMode().open("tabopen " + buffer.uri.spec); });
mappings.add([modes.NORMAL], ["w"],
"Open one or more URLs in a new window",
- function () { commandline.open(":", "winopen ", modes.EX); });
+ function () { CommandExMode().open("winopen "); });
mappings.add([modes.NORMAL], ["W"],
"Open one or more URLs in a new window, based on current location",
- function () { commandline.open(":", "winopen " + buffer.uri.spec, modes.EX); });
+ function () { CommandExMode().open("winopen " + buffer.uri.spec); });
mappings.add([modes.NORMAL],
["<C-a>"], "Increment last number in URL",
diff --git a/common/content/buffer.js b/common/content/buffer.js
index 999e1710..1bbaab99 100644
--- a/common/content/buffer.js
+++ b/common/content/buffer.js
@@ -766,25 +766,25 @@ var Buffer = Module("buffer", {
try {
window.urlSecurityCheck(uri.spec, doc.nodePrincipal);
- commandline.input("Save link: ", function (path) {
- let file = io.File(path);
- if (file.exists() && file.isDirectory())
- file.append(buffer.getDefaultNames(elem)[0][0]);
+ io.CommandFileMode("Save link: ", {
+ onSubmit: function (path) {
+ let file = io.File(path);
+ if (file.exists() && file.isDirectory())
+ file.append(buffer.getDefaultNames(elem)[0][0]);
- try {
- if (!file.exists())
- file.create(File.NORMAL_FILE_TYPE, octal(644));
- }
- catch (e) {
- util.assert(false, "Invalid destination: " + e.name);
- }
+ try {
+ if (!file.exists())
+ file.create(File.NORMAL_FILE_TYPE, octal(644));
+ }
+ catch (e) {
+ util.assert(false, "Invalid destination: " + e.name);
+ }
- buffer.saveURI(uri, file);
- }, {
- autocomplete: true,
- completer: function (context) completion.savePage(context, elem),
- history: "file"
- });
+ buffer.saveURI(uri, file);
+ },
+
+ completer: function (context) completion.savePage(context, elem)
+ }).open();
}
catch (e) {
dactyl.echoerr(e);
@@ -1360,17 +1360,15 @@ var Buffer = Module("buffer", {
},
openUploadPrompt: function openUploadPrompt(elem) {
- commandline.input("Upload file: ", function (path) {
- let file = io.File(path);
- dactyl.assert(file.exists());
-
- elem.value = file.path;
- events.dispatch(elem, events.create(elem.ownerDocument, "change", {}));
- }, {
- completer: function (context) completion.file(context),
- default: elem.value,
- history: "file"
- });
+ io.CommandFileMode("Upload file: ", {
+ onSubmit: function (path) {
+ let file = io.File(path);
+ dactyl.assert(file.exists());
+
+ elem.value = file.path;
+ events.dispatch(elem, events.create(elem.ownerDocument, "change", {}));
+ }
+ }).open(elem.value);
}
}, {
commands: function () {
@@ -1530,7 +1528,8 @@ var Buffer = Module("buffer", {
if (/^>>/.test(context.filter))
context.advance(/^>>\s*/.exec(context.filter)[0].length);
- return completion.savePage(context, content.document);
+ completion.savePage(context, content.document);
+ context.fork("file", 0, completion, "file");
},
literal: 0
});
@@ -1642,7 +1641,6 @@ var Buffer = Module("buffer", {
this, function (context) {
context.completions = buffer.getDefaultNames(node);
});
- return context.fork("files", 0, completion, "file");
};
},
events: function () {
diff --git a/common/content/commandline.js b/common/content/commandline.js
index 8b3bae08..77a11e4d 100644
--- a/common/content/commandline.js
+++ b/common/content/commandline.js
@@ -20,39 +20,12 @@ var CommandWidgets = Class("CommandWidgets", {
eventTarget: commandline
},
append: <e4x xmlns={XUL} xmlns:dactyl={NS}>
- <window id={document.documentElement.id}>
- <popupset>
- <menupopup id="dactyl-contextmenu"
- onpopupshowing="return (event.target != this || dactyl.modules.commandline.onContext(event));">
- <menuitem id="dactyl-context-copylink"
- label="Copy Link Location" dactyl:group="link"
- oncommand="goDoCommand('cmd_copyLink');"/>
- <menuitem id="dactyl-context-copypath"
- label="Copy File Path" dactyl:group="link path"
- oncommand="dactyl.clipboardWrite(document.popupNode.getAttribute('path'));"/>
- <menuitem id="dactyl-context-copy"
- label="Copy" dactyl:group="selection"
- command="cmd_copy"/>
- <menuitem id="dactyl-context-selectall"
- label="Select All"
- command="cmd_selectAll"/>
- </menupopup>
- </popupset>
- </window>
-
<vbox id={config.commandContainer}>
-
- <vbox class="dactyl-container" id="dactyl-multiline-output-container" hidden="false" collapsed="true">
- <iframe id="dactyl-multiline-output" src="dactyl://content/buffer.xhtml"
- flex="1" hidden="false" collapsed="false" contextmenu="dactyl-contextmenu"
- highlight="Events" events="multilineOutputEvents" />
- </vbox>
-
<vbox class="dactyl-container" hidden="false" collapsed="true">
<iframe class="dactyl-completions" id="dactyl-completions-dactyl-commandline" src="dactyl://content/buffer.xhtml"
contextmenu="dactyl-contextmenu"
flex="1" hidden="false" collapsed="false"
- highlight="Events" events="multilineOutputEvents" />
+ highlight="Events" events="mowEvents" />
</vbox>
<stack orient="horizontal" align="stretch" class="dactyl-container" id="dactyl-container" highlight="CmdLine CmdCmdLine">
@@ -69,7 +42,7 @@ var CommandWidgets = Class("CommandWidgets", {
<vbox class="dactyl-container" hidden="false" collapsed="false" highlight="CmdLine">
<textbox id="dactyl-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true"
- highlight="Normal Events" events="multilineInputEvents" />
+ highlight="Normal Events" events="mowEvents" />
</vbox>
</vbox>
@@ -87,28 +60,32 @@ var CommandWidgets = Class("CommandWidgets", {
<vbox id={"dactyl-completions-" + s + "commandline-container"} class="dactyl-container" hidden="false" collapsed="true">
<iframe class="dactyl-completions" id={"dactyl-completions-" + s + "commandline"} src="dactyl://content/buffer.xhtml"
contextmenu="dactyl-contextmenu" flex="1" hidden="false" collapsed="false"
- highlight="Events" events="multilineOutputEvents" />
+ highlight="Events" events="mowEvents" />
</vbox>
</toolbar>
</e4x>.elements()
});
this.elements = {};
+
this.addElement({
name: "container",
noValue: true
});
+
this.addElement({
name: "commandline",
getGroup: function () options.get("guioptions").has("C") ? this.commandbar : this.statusbar,
getValue: function () this.command
});
+
this.addElement({
name: "strut",
defaultGroup: "Normal",
getGroup: function () this.commandbar,
getValue: function () options.get("guioptions").has("c")
});
+
this.addElement({
name: "command",
id: "commandline-command",
@@ -125,24 +102,26 @@ var CommandWidgets = Class("CommandWidgets", {
getElement: CommandWidgets.getEditor,
getGroup: function (value) this.activeGroup.commandline,
onChange: function (elem) {
- if (elem.inputField != dactyl.focusedElement) {
+ if (elem.inputField != dactyl.focusedElement)
try {
elem.selectionStart = elem.value.length;
elem.selectionEnd = elem.value.length;
}
catch (e) {}
- }
+
if (!elem.collapsed)
dactyl.focus(elem);
},
onVisibility: function (elem, visible) { visible && dactyl.focus(elem); }
});
+
this.addElement({
name: "prompt",
id: "commandline-prompt",
defaultGroup: "CmdPrompt",
getGroup: function () this.activeGroup.commandline
});
+
this.addElement({
name: "message",
defaultGroup: "Normal",
@@ -157,20 +136,20 @@ var CommandWidgets = Class("CommandWidgets", {
return this.activeGroup.mode;
}
});
+
this.addElement({
name: "mode",
defaultGroup: "ModeMsg",
getGroup: function (value) {
if (!options.get("guioptions").has("M"))
if (this.commandbar.container.clientHeight == 0 ||
- value && !this.commandbar.commandline.collapsed)
+ value && !this.commandbar.commandline.collapsed)
return this.statusbar;
return this.commandbar;
}
});
let fontSize = util.computedStyle(document.documentElement).fontSize;
- styles.registerSheet("resource://dactyl-skin/dactyl.css");
styles.system.add("font-size", "dactyl://content/buffer.xhtml",
"body { font-size: " + fontSize + "; } \
html|html > xul|scrollbar { visibility: collapse !important; }",
@@ -188,14 +167,17 @@ var CommandWidgets = Class("CommandWidgets", {
memoize(this.statusbar, obj.name, function () get("dactyl-statusline-field-", statusline.widgets, (obj.id || obj.name)));
memoize(this.commandbar, obj.name, function () get("dactyl-", {}, (obj.id || obj.name)));
- if (!(obj.noValue || obj.getValue))
+ if (!(obj.noValue || obj.getValue)) {
Object.defineProperty(this, obj.name, Modes.boundProperty({
+
get: function get_widgetValue() {
let elem = self.getGroup(obj.name, obj.value)[obj.name];
if (obj.value != null)
- return [obj.value[0], obj.get ? obj.get.call(this, elem) : elem.value];
+ return [obj.value[0],
+ obj.get ? obj.get.call(this, elem) : elem.value];
return null;
},
+
set: function set_widgetValue(val) {
if (val != null && !isArray(val))
val = [obj.defaultGroup || "", val];
@@ -219,19 +201,23 @@ var CommandWidgets = Class("CommandWidgets", {
return val;
}
}).init(obj.name));
- else if (obj.defaultGroup)
+ }
+ else if (obj.defaultGroup) {
[this.commandbar, this.statusbar].forEach(function (nodeSet) {
let elem = nodeSet[obj.name];
if (elem)
highlight.highlightNode(elem, obj.defaultGroup.split(/\s/)
.map(function (g) g + " " + nodeSet.group + g).join(" "));
});
+ }
},
+
getGroup: function getgroup(name, value) {
if (!statusline.visible)
return this.commandbar;
return this.elements[name].getGroup.call(this, arguments.length > 1 ? value : this[name]);
},
+
updateVisibility: function updateVisibility() {
for (let elem in values(this.elements))
if (elem.getGroup) {
@@ -249,7 +235,10 @@ var CommandWidgets = Class("CommandWidgets", {
}
}
}
- // Hack.
+
+ // Hack. Collapse hidden elements in the stack.
+ // Might possibly be better to use a deck and programmatically
+ // choose which element to select.
function check(node) {
if (util.computedStyle(node).display === "-moz-stack") {
let nodes = Array.filter(node.children, function (n) !n.collapsed && n.boxObject.height);
@@ -306,6 +295,159 @@ var CommandWidgets = Class("CommandWidgets", {
}
});
+
+var CommandMode = Class("CommandMode", {
+ init: function init() {
+ this.keepCommand = userContext.hidden_option_command_afterimage;
+
+ if (this.historyKey)
+ this.history = CommandLine.History(commandline.widgets.active.command.inputField, this.historyKey);
+
+ if (this.complete)
+ this.completions = CommandLine.Completions(commandline.widgets.active.command.inputField);
+
+ this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
+ if (!events.feedingKeys && this.completions && options["autocomplete"].length) {
+ this.completions.complete(true, false);
+ if (this.completions)
+ this.completions.itemList.visible = true;
+ }
+ }, this);
+ },
+
+ open: function (command) {
+ this.command = command;
+
+ dactyl.assert(isinstance(this.mode, modes.COMMAND_LINE),
+ "Not opening command line in non-command-line mode.");
+ modes.push(this.mode, null, this.closure);
+
+ this.widgets.active.commandline.collapsed = false;
+ this.widgets.prompt = this.prompt;
+ this.widgets.command = command || "";
+ },
+
+ get mappingSelf() this,
+
+ get widgets() commandline.widgets,
+
+ enter: function (stack) {
+ commandline.commandSession = this;
+ if (this.command || stack.pop && commandline.command) {
+ this.onChange(commandline.command);
+ this.autocompleteTimer.flush(true);
+ }
+ },
+
+ leave: function (stack) {
+ this.autocompleteTimer.reset();
+
+ if (this.completions) {
+ this.completions.previewClear();
+ this.completions.tabTimer.reset();
+ }
+
+ if (this.history)
+ this.history.save();
+
+ commandline.hideCompletions();
+ this.resetCompletions();
+
+ if (!stack.push) {
+ modes.delay(function () {
+ if (!this.keepCommand || commandline.silent || commandline.quiet)
+ commandline.hide();
+ this[this.accepted ? "onSubmit" : "onCancel"](commandline.command);
+ }, this);
+ commandline.commandSession = null;
+ }
+ },
+
+ events: {
+ input: function onInput(event) {
+ if (this.completions) {
+ this.resetCompletions();
+
+ this.autocompleteTimer.tell(false);
+ if (!this.completions.itemList.visible)
+ this.autocompleteTimer.flush();
+ }
+ this.onChange(commandline.command);
+ },
+ keyup: function onKeyUp(event) {
+ let key = events.toString(event);
+ if (/-?Tab>$/.test(key) && this.completions)
+ this.completions.tabTimer.flush();
+ }
+ },
+
+ keepCommand: false,
+
+ onKeyPress: function onKeyPress(event) {
+ let key = events.toString(event);
+ if (this.completions)
+ this.completions.previewClear();
+
+ return true; /* Pass event */
+ },
+
+ onCancel: function (value) {
+ },
+
+ onChange: function (value) {
+ },
+
+ onSubmit: function (value) {
+ },
+
+ resetCompletions: function resetCompletions() {
+ if (this.completions) {
+ this.completions.context.cancelAll();
+ this.completions.wildIndex = -1;
+ this.completions.previewClear();
+ }
+ if (this.history)
+ this.history.reset();
+ },
+});
+
+var CommandExMode = Class("CommandExMode", CommandMode, {
+
+ get mode() modes.EX,
+
+ historyKey: "command",
+
+ prompt: ["Normal", ":"],
+
+ complete: function complete(context) {
+ context.fork("ex", 0, completion, "ex");
+ },
+
+ onSubmit: function onSubmit(command) {
+ io.withSavedValues(["readHeredoc", "sourcing"], function () {
+ this.sourcing = { file: "[Command Line]", line: 1 };
+ this.readHeredoc = commandline.readHeredoc;
+ commands.repeat = command;
+ dactyl.execute(command);
+ });
+ }
+});
+
+var CommandPromptMode = Class("CommandPromptMode", CommandMode, {
+ init: function init(prompt, params) {
+ this.prompt = prompt;
+ update(this, params);
+ init.supercall(this);
+ },
+
+ complete: function () {
+ if (this.completer)
+ return this.completer.apply(this, arguments);
+ },
+
+ get mode() modes.PROMPT
+});
+
/**
* This class is used for prompting of user input and echoing of messages.
*
@@ -364,117 +506,10 @@ var CommandLine = Module("commandline", {
}
}; //}}}
- this._lastMowOutput = null;
-
this._silent = false;
this._quiet = false;
- this._keepCommand = false;
this._lastEcho = null;
- /////////////////////////////////////////////////////////////////////////////}}}
- ////////////////////// TIMERS //////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
-
- this._autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
- dactyl.trapErrors(function _autocompleteTell() {
- if (!events.feedingKeys && self._completions && options["autocomplete"].length) {
- self._completions.complete(true, false);
- if (self._completions)
- self._completions.itemList.show();
- }
- });
- });
-
- this._statusTimer = Timer(5, 100, function statusTell() {
- if (self._completions == null || self._completions.selected == null)
- statusline.progress = "";
- else
- statusline.progress = "match " + (self._completions.selected + 1) + " of " + self._completions.items.length;
- });
-
- // This timer just prevents <Tab>s from queueing up when the
- // system is under load (and, thus, giving us several minutes of
- // the completion list scrolling). Multiple <Tab> presses are
- // still processed normally, as the timer is flushed on "keyup".
- this._tabTimer = Timer(0, 0, function tabTell(event) {
- dactyl.trapErrors(function () {
- if (self._completions)
- self._completions.tab(event.shiftKey, event.altKey && options["altwildmode"]);
- });
- });
-
- this._timers = [this._autocompleteTimer, this._statusTimer, this._tabTimer];
-
- /////////////////////////////////////////////////////////////////////////////}}}
- ////////////////////// VARIABLES ///////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
-
- this.__defineGetter__("_completionList", function () {
- let node = this.widgets.active.commandline;
- if (!node._completionList)
- this.widgets._whenReady.call(node, "_completionList", "dactyl-completions-" + node.id,
- function (node) ItemList(node.id));
- return node._completionList;
- });
- this._completions = null;
- this._history = null;
-
- this._startHints = false; // whether we're waiting to start hints mode
- this._lastSubstring = "";
-
- // we need to save the mode which were in before opening the command line
- // this is then used if we focus the command line again without the "official"
- // way of calling "open"
- this.currentExtendedMode = null; // the extended mode which we last opened the command line for
-
- this._input = {};
-
- this.registerCallback("submit", modes.EX, function (command) {
- io.withSavedValues(["readHeredoc", "sourcing"], function () {
- this.sourcing = { file: "[Command Line]", line: 1 };
- this.readHeredoc = commandline.readHeredoc;
- commands.repeat = command;
- dactyl.execute(command);
- });
- });
-
- this.registerCallback("complete", modes.EX, function (context) {
- context.fork("ex", 0, completion, "ex");
- });
- this.registerCallback("change", modes.EX, function (command, from) {
- if (from !== "history")
- self._autocompleteTimer.tell(false);
- });
-
- this.registerCallback("cancel", modes.PROMPT, cancelPrompt);
- this.registerCallback("submit", modes.PROMPT, closePrompt);
- this.registerCallback("change", modes.PROMPT, function (str) {
- if (self._input.complete)
- self._autocompleteTimer.tell(false);
- if (self._input.change)
- self._input.change.call(commandline, str);
- });
- this.registerCallback("complete", modes.PROMPT, function (context) {
- if (self._input.complete)
- context.fork("input", 0, commandline, self._input.complete);
- });
-
- function cancelPrompt(value) {
- let callback = self._input.cancel;
- self._input = {};
- if (callback)
- dactyl.trapErrors(callback, self, value != null ? value : commandline.command);
- }
-
- function closePrompt(value) {
- let callback = self._input.submit;
- self._input = {};
- if (callback)
- dactyl.trapErrors(callback, self, value != null ? value : commandline.command);
- }
- },
- cleanup: function cleanup() {
- styles.unregisterSheet("resource://dactyl-skin/dactyl.css");
},
/**
@@ -482,8 +517,7 @@ var CommandLine = Module("commandline", {
*
* @returns {boolean}
*/
- get commandVisible() modes.main == modes.COMMAND_LINE &&
- !(modes.extended & modes.INPUT_MULTILINE),
+ get commandVisible() !!this.commandSession,
/**
* Ensure that the multiline input widget is the correct size.
@@ -513,13 +547,12 @@ var CommandLine = Module("commandline", {
get completionContext() this._completions.context,
- get mode() (modes.extended == modes.EX) ? "cmd" : "search",
-
get silent() this._silent,
set silent(val) {
this._silent = val;
- this._quiet = this._quiet;
+ this.quiet = this.quiet;
},
+
get quiet() this._quiet,
set quiet(val) {
this._quiet = val;
@@ -532,133 +565,36 @@ var CommandLine = Module("commandline", {
widgets: Class.memoize(function () CommandWidgets()),
- // @param type can be:
- // "submit": when the user pressed enter in the command line
- // "change"
- // "cancel"
- // "complete"
- registerCallback: function registerCallback(type, mode, func) {
- if (!(type in this._callbacks))
- this._callbacks[type] = {};
- this._callbacks[type][mode] = func;
- },
-
- triggerCallback: function triggerCallback(type, mode) {
- if (this._callbacks[type] && this._callbacks[type][mode])
- try {
- this._callbacks[type][mode].apply(this, Array.slice(arguments, 2));
- }
- catch (e) {
- dactyl.reportError(e, true);
- }
- },
-
runSilently: function runSilently(func, self) {
- this.withSavedValues(["_silent"], function () {
- this._silent = true;
+ this.withSavedValues(["silent"], function () {
+ this.silent = true;
func.call(self);
});
},
+ get completionList() {
+ let node = this.widgets.active.commandline;
+ if (!node.completionList)
+ this.widgets._whenReady.call(node, "completionList", "dactyl-completions-" + node.id,
+ function (node) ItemList(node.id));
+ return node.completionList;
+ },
+
hideCompletions: function hideCompletions() {
for (let nodeSet in values([this.widgets.statusbar, this.widgets.commandbar]))
- if (nodeSet.commandline._completionList)
- nodeSet.commandline._completionList.hide();
+ if (nodeSet.commandline.completionList)
+ nodeSet.commandline.completionList.visible = false;
},
_multilineEnd: Modes.boundProperty(),
_multilineCallback: Modes.boundProperty(),
- currentExtendedMode: Modes.boundProperty(),
- _completions: Modes.boundProperty(),
- _history: Modes.boundProperty(),
_lastClearable: Modes.boundProperty(),
- _keepCommand: Modes.boundProperty(),
messages: Modes.boundProperty(),
multilineInputVisible: Modes.boundProperty({
set: function set_miwVisible(value) { this.widgets.multilineInput.collapsed = !value; }
}),
- multilineOutputVisible: Modes.boundProperty({
- set: function set_mowVisible(value) {
- this.widgets.mowContainer.collapsed = !value;
- let elem = this.widgets.multilineOutput;
- if (!value && elem && elem.contentWindow == document.commandDispatcher.focusedWindow)
- document.commandDispatcher.focusedWindow = content;
- }
- }),
-
- /**
- * Open the command line. The main mode is set to COMMAND_LINE, the
- * extended mode to *extendedMode*. Further, callbacks defined for
- * *extendedMode* are triggered as appropriate
- * (see {@link #registerCallback}).
- *
- * @param {string} prompt
- * @param {string} cmd
- * @param {number} extendedMode
- */
- open: function open(prompt, cmd, extendedMode) {
- this.widgets.message = null;
-
- this.currentExtendedMode = extendedMode || null;
- modes.push(modes.COMMAND_LINE, this.currentExtendedMode, {
- autocomplete: cmd.length,
- onKeyPress: this.closure.onKeyPress,
- history: (extendedMode || {}).params.history,
- leave: function (params) {
- if (params.pop)
- commandline.leave();
- },
- keyModes: [this.currentExtendedMode]
- });
-
- this._keepCommand = false;
-
- this.widgets.active.commandline.collapsed = false;
- this.widgets.prompt = prompt;
- this.widgets.command = cmd || "";
-
- this.enter();
- },
-
- enter: function enter() {
- let params = modes.getStack(0).params;
-
- if (params.history)
- this._history = CommandLine.History(this.widgets.active.command.inputField, params.history);
- this._completions = CommandLine.Completions(this.widgets.active.command.inputField);
-
- if (params.autocomplete) {
- commandline.triggerCallback("change", this.currentExtendedMode, commandline.command);
- this._autocompleteTimer.flush();
- }
- },
-
- /**
- * Called when leaving a command-line mode.
- */
- leave: function leave() {
- commandline.triggerCallback("cancel", this.currentExtendedMode);
-
- this._timers.forEach(function (timer) timer.reset());
- if (this._completions)
- this._completions.previewClear();
- if (this._history)
- this._history.save();
- this.resetCompletions(); // cancels any asynchronous completion still going on, must be before we set completions = null
- this.hideCompletions();
- this._completions = null;
- this._history = null;
- this._statusTimer.tell();
-
- if (!this._keepCommand || this._silent || this._quiet) {
- modes.delay(function () {
- this.updateMorePrompt();
- this.hide();
- }, this);
- }
- },
get command() {
if (this.commandVisible && this.widgets.command)
@@ -670,18 +606,22 @@ var CommandLine = Module("commandline", {
return this.widgets.command = val;
return this._lastCommand = val;
},
+
get lastCommand() this._lastCommand || this.command,
set lastCommand(val) { this._lastCommand = val },
clear: function clear() {
if (this.widgets.message && this.widgets.message[1] === this._lastClearable)
this.widgets.message = null;
+
if (modes.main != modes.COMMAND_LINE)
this.widgets.command = null;
+
if (modes.main == modes.OUTPUT_MULTILINE && !mow.isScrollable(1))
modes.pop();
+
if (modes.main != modes.OUTPUT_MULTILINE)
- this.multilineOutputVisible = false;
+ mow.visible = false;
},
/**
@@ -726,92 +666,11 @@ var CommandLine = Module("commandline", {
let field = this.widgets.active.message.inputField;
if (field.value && !forceSingle && field.editor.rootElement.scrollWidth > field.scrollWidth) {
this.widgets.message = null;
- this._echoMultiline(<span highlight="Message">{str}</span>, highlightGroup, true);
+ mow.echo(<span highlight="Message">{str}</span>, highlightGroup, true);
}
},
/**
- * Display a multi-line message.
- *
- * @param {string} data
- * @param {string} highlightGroup
- */
- _echoMultiline: function echoMultiline(data, highlightGroup, silent) {
- let doc = this.widgets.multilineOutput.contentDocument;
- let win = this.widgets.multilineOutput.contentWindow;
- let elem = doc.documentElement;
- let body = doc.body;
-
- this.widgets.message = null;
- if (!this.commandVisible)
- this.hide();
-
- this._startHints = false;
- if (modes.main != modes.OUTPUT_MULTILINE) {
- modes.push(modes.OUTPUT_MULTILINE, null, {
- onKeyPress: this.closure.onMOWKeyPress,
- leave: this.closure(function leave(stack) {
- if (stack.pop)
- for (let message in values(this.messages))
- if (message.leave)
- message.leave(stack);
- })
- });
- this.messages = [];
- }
-
- // If it's already XML, assume it knows what it's doing.
- // Otherwise, white space is significant.
- // The problem elsewhere is that E4X tends to insert new lines
- // after interpolated data.
- XML.ignoreWhitespace = XML.prettyPrinting = false;
-
- if (isObject(data)) {
- this._lastMowOutput = null;
-
- var output = util.xmlToDom(<div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}/>, doc);
- data.document = doc;
- output.appendChild(data.message);
-
- this.messages.push(data);
- }
- else {
- let style = isString(data) ? "pre" : "nowrap";
- this._lastMowOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{data}</div>;
-
- var output = util.xmlToDom(this._lastMowOutput, doc);
- }
-
- // FIXME: need to make sure an open MOW is closed when commands
- // that don't generate output are executed
- if (this.widgets.mowContainer.collapsed) {
- elem.scrollTop = 0;
- while (body.firstChild)
- body.removeChild(body.firstChild);
- }
-
- body.appendChild(output);
-
- let str = typeof data !== "xml" && data.message || data;
- if (!silent)
- dactyl.triggerObserver("echoMultiline", data, highlightGroup, output);
-
- commandline.updateOutputHeight(true);
-
- if (options["more"] && Buffer.isScrollable(elem, 1)) {
- // start the last executed command's output at the top of the screen
- let elements = doc.getElementsByClassName("ex-command-output");
- elements[elements.length - 1].scrollIntoView(true);
- }
- else
- elem.scrollTop = elem.scrollHeight;
-
- dactyl.focus(win);
-
- commandline.updateMorePrompt();
- },
-
- /**
* Output the given string onto the command line. With no flags, the
* message will be shown in the status line if it's short enough to
* fit, and contains no new lines, and isn't XML. Otherwise, it will be
@@ -857,19 +716,19 @@ var CommandLine = Module("commandline", {
let action = this._echoLine;
if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isString(data)) && !(flags & this.FORCE_SINGLELINE))
- action = this._echoMultiline;
+ action = mow.closure.echo;
if (single)
this._lastEcho = null;
else {
if (this.widgets.message && this.widgets.message[1] == this._lastEcho)
- this._echoMultiline(<span highlight="Message">{this._lastEcho}</span>,
- this.widgets.message[0], true);
+ mow.echo(<span highlight="Message">{this._lastEcho}</span>,
+ this.widgets.message[0], true);
if (action === this._echoLine && !(flags & this.FORCE_MULTILINE)
&& !(dactyl.fullyInitialized && this.widgets.mowContainer.collapsed)) {
highlightGroup += " Message";
- action = this._echoMultiline;
+ action = mow.closure.echo;
}
this._lastEcho = (action == this._echoLine) && data;
}
@@ -899,29 +758,7 @@ var CommandLine = Module("commandline", {
input: function _input(prompt, callback, extra) {
extra = extra || {};
- this._input = {
- submit: callback || extra.onAccept,
- change: extra.onChange,
- complete: extra.completer,
- cancel: extra.onCancel
- };
-
- modes.push(modes.COMMAND_LINE, modes.PROMPT | extra.extended,
- update(Object.create(extra), {
- onKeyPress: extra.onKeyPress || this.closure.onKeyPress,
- leave: function leave(stack) {
- commandline.leave(stack);
- leave.supercall(extra, stack);
- },
- keyModes: [extra.extended, modes.PROMPT]
- }));
- this.currentExtendedMode = modes.PROMPT;
-
- this.widgets.prompt = !prompt ? null : [extra.promptHighlight || "Question", prompt];
- this.widgets.command = extra.default || "";
- this.widgets.active.commandline.collapsed = false;
-
- this.enter();
+ CommandPromptMode(prompt, extra).open();
},
readHeredoc: function readHeredoc(end) {
@@ -959,56 +796,32 @@ var CommandLine = Module("commandline", {
this.timeout(function () { dactyl.focus(this.widgets.multilineInput); }, 10);
},
- onContext: function onContext(event) {
- try {
- let enabled = {
- link: window.document.popupNode instanceof HTMLAnchorElement,
- path: window.document.popupNode.hasAttribute("path"),
- selection: !window.document.commandDispatcher.focusedWindow.getSelection().isCollapsed
- };
-
- for (let node in array.iterValues(event.target.children)) {
- let group = node.getAttributeNS(NS, "group");
- node.hidden = group && !group.split(/\s+/).every(function (g) enabled[g]);
- }
- }
- catch (e) {
- util.reportError(e);
+ events: update(
+ iter(CommandMode.prototype.events).map(
+ function ([event, handler]) [
+ event, function (event) {
+ if (this.commandSession)
+ handler.call(this.commandSession, event);
+ }
+ ]).toObject(),
+ {
+ blur: function onBlur(event) {
+ this.timeout(function () {
+ if (this.commandSession && event.originalTarget === this.widgets.active.command.inputField)
+ dactyl.focus(this.widgets.active.command.inputField);
+ });
+ },
+ focus: function onFocus(event) {
+ if (!this.commandSession
+ && event.originalTarget === this.widgets.active.command.inputField) {
+ event.target.blur();
+ dactyl.beep();
+ }
+ },
}
- return true;
- },
-
- onKeyPress: function onKeyPress(event) {
- let key = events.toString(event);
- if (this._completions)
- this._completions.previewClear();
+ ),
- return true; /* Pass event */
- },
-
- events: {
- blur: function onBlur(event) {
- this.timeout(function () {
- if (this.commandVisible && event.originalTarget == this.widgets.active.command.inputField)
- dactyl.focus(this.widgets.active.command.inputField);
- });
- },
- focus: function onFocus(event) {
- if (!this.commandVisible && event.target == this.widgets.active.command.inputField) {
- event.target.blur();
- dactyl.beep();
- }
- },
- input: function onInput(event) {
- this.resetCompletions();
- this.triggerCallback("change", this.currentExtendedMode, this.command);
- },
- keyup: function onKeyUp(event) {
- let key = events.toString(event);
- if (/-?Tab>$/.test(key))
- this._tabTimer.flush();
- }
- },
+ get mowEvents() mow.events,
/**
* Multiline input events, they will come straight from
@@ -1028,117 +841,7 @@ var CommandLine = Module("commandline", {
}
},
- multilineOutputEvents: {
- click: function onClick(event) {
- if (event.getPreventDefault())
- return;
-
- const openLink = function openLink(where) {
- event.preventDefault();
- dactyl.open(event.target.href, where);
- }
-
- if (event.target instanceof HTMLAnchorElement)
- switch (events.toString(event)) {
- case "<LeftMouse>":
- openLink(dactyl.CURRENT_TAB);
- break;
- case "<MiddleMouse>":
- case "<C-LeftMouse>":
- case "<C-M-LeftMouse>":
- openLink({ where: dactyl.NEW_TAB, background: true });
- break;
- case "<S-MiddleMouse>":
- case "<C-S-LeftMouse>":
- case "<C-M-S-LeftMouse>":
- openLink({ where: dactyl.NEW_TAB, background: false });
- break;
- case "<S-LeftMouse>":
- openLink(dactyl.NEW_WINDOW);
- break;
- }
- },
- unload: function onUnload(event) {
- event.preventDefault();
- }
- },
-
- onMOWKeyPress: function onMOWKeyPress(event) {
- const KILL = false, PASS = true;
-
- if (options["more"] && mow.isScrollable(1))
- commandline.updateMorePrompt(false, true);
- else {
- modes.pop();
- events.feedkeys(events.toString(event));
- return KILL;
- }
- return PASS;
- },
-
- getSpaceNeeded: function getSpaceNeeded() {
- let rect = this.widgets.commandbar.commandline.getBoundingClientRect();
- let offset = rect.bottom - window.innerHeight;
- return Math.max(0, offset);
- },
-
- /**
- * Update or remove the multi-line output widget's "MORE" prompt.
- *
- * @param {boolean} force If true, "-- More --" is shown even if we're
- * at the end of the output.
- * @param {boolean} showHelp When true, show the valid key sequences
- * and what they do.
- */
- updateMorePrompt: function updateMorePrompt(force, showHelp) {
- if (this.widgets.mowContainer.collapsed)
- return this.widgets.message = null;
- let elem = this.widgets.multilineOutput.contentDocument.documentElement;
-
- if (showHelp)
- this.widgets.message = ["MoreMsg", "-- More -- SPACE/<C-f>/j: screen/page/line down, <C-b>/<C-u>/k: up, q: quit"];
- else if (force || (options["more"] && Buffer.isScrollable(elem, 1)))
- this.widgets.message = ["MoreMsg", "-- More --"];
- else
- this.widgets.message = ["Question", "Press ENTER or type command to continue"];
- },
-
- /**
- * Changes the height of the message window to fit in the available space.
- *
- * @param {boolean} open If true, the widget will be opened if it's not
- * already so.
- */
- updateOutputHeight: function updateOutputHeight(open, extra) {
- if (!open && this.widgets.mowContainer.collapsed)
- return;
-
- let doc = this.widgets.multilineOutput.contentDocument;
-
- let availableHeight = config.outputHeight;
- if (!this.widgets.mowContainer.collapsed)
- availableHeight += parseFloat(this.widgets.mowContainer.height);
- availableHeight -= extra || 0;
-
- doc.body.style.minWidth = this.widgets.commandbar.commandline.scrollWidth + "px";
- this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px";
- this.timeout(function ()
- this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px",
- 0);
-
- doc.body.style.minWidth = "";
- this.multilineOutputVisible = true;
- },
-
- resetCompletions: function resetCompletions() {
- if (this._completions) {
- this._completions.context.cancelAll();
- this._completions.wildIndex = -1;
- this._completions.previewClear();
- }
- if (this._history)
- this._history.reset();
- },
+ updateOutputHeight: deprecated("mow.resize", function updateOutputHeight(open, extra) mow.resize(open, extra)),
withOutputToString: function withOutputToString(fn, self) {
dactyl.registerObserver("echoLine", observe, true);
@@ -1213,7 +916,7 @@ var CommandLine = Module("commandline", {
replace: function replace(val) {
delete this.input.dactylKeyPress;
this.input.value = val;
- commandline.triggerCallback("change", commandline.currentExtendedMode, val, "history");
+ commandline.commandSession.onChange(val, "history");
},
/**
@@ -1281,8 +984,12 @@ var CommandLine = Module("commandline", {
this.selected = null;
this.wildmode = options.get("wildmode");
this.wildtypes = this.wildmode.value;
- this.itemList = commandline._completionList;
+ this.itemList = commandline.completionList;
this.itemList.setItems(this.context);
+
+ this.tabTimer = Timer(0, 0, function tabTell(event) {
+ this.tab(event.shiftKey, event.altKey && options["altwildmode"]);
+ }, this);
},
UP: {},
@@ -1291,6 +998,8 @@ var CommandLine = Module("commandline", {
PAGE_DOWN: {},
RESET: null,
+ lastSubstring: "",
+
get completion() {
let str = commandline.command;
return str.substring(this.prefix.length, str.length - this.suffix.length);
@@ -1329,7 +1038,7 @@ var CommandLine = Module("commandline", {
complete: function complete(show, tabPressed) {
this.context.reset();
this.context.tabPressed = tabPressed;
- commandline.triggerCallback("complete", commandline.currentExtendedMode, this.context);
+ commandline.commandSession.complete(this.context);
this.context.updateAsync = true;
this.reset(show, tabPressed);
this.wildIndex = 0;
@@ -1362,10 +1071,10 @@ var CommandLine = Module("commandline", {
}
// Don't show 1-character substrings unless we've just hit backspace
- if (substring.length < 2 && (!this._lastSubstring || this._lastSubstring.indexOf(substring) != 0))
+ if (substring.length < 2 && this.lastSubstring.indexOf(substring) !== 0)
return;
- this._lastSubstring = substring;
+ this.lastSubstring = substring;
let value = this.completion;
if (util.compareIgnoreCase(value, substring.substr(0, value.length)))
@@ -1495,7 +1204,8 @@ var CommandLine = Module("commandline", {
tabs: [],
tab: function tab(reverse, wildmode) {
- commandline._autocompleteTimer.flush();
+ commandline.commandSession.autocompleteTimer.flush();
+
if (this._caret != this.caret)
this.reset();
this._caret = this.caret;
@@ -1529,12 +1239,15 @@ var CommandLine = Module("commandline", {
}
if (this.haveType("list"))
- this.itemList.show();
+ this.itemList.visible = true;
this.wildIndex++;
this.preview();
- commandline._statusTimer.tell();
+ if (this.selected == null)
+ statusline.progress = "";
+ else
+ statusline.progress = "match " + (this.selected + 1) + " of " + this.items.length;
}
if (this.items.length == 0)
@@ -1623,19 +1336,30 @@ var CommandLine = Module("commandline", {
subCommand: 0
});
},
+ modes: function () {
+ modes.addMode("COMMAND_LINE", {
+ char: "c",
+ description: "Active when the command line is focused",
+ input: true,
+ get mappingSelf() commandline.commandSession
+ });
+ // this._extended modes, can include multiple modes, and even main modes
+ modes.addMode("EX", {
+ description: "Ex command mode, active when the command line is open for Ex commands",
+ bases: [modes.COMMAND_LINE],
+ input: true
+ });
+ modes.addMode("PROMPT", {
+ description: "Active when a prompt is open in the command line",
+ bases: [modes.COMMAND_LINE],
+ input: true
+ });
+ },
mappings: function init_mappings() {
mappings.add([modes.COMMAND],
[":"], "Enter command-line mode",
- function () { commandline.open(":", "", modes.EX); });
-
- mappings.add([modes.COMMAND],
- ["g<lt>"], "Redisplay the last command output",
- function () {
- dactyl.assert(commandline._lastMowOutput, "No previous command output");
- commandline._echoMultiline(commandline._lastMowOutput, commandline.HL_NORMAL);
- });
-
+ function () { CommandExMode().open(""); });
mappings.add([modes.INPUT_MULTILINE],
["<Return>", "<C-j>", "<C-m>"], "Begin a new line",
@@ -1666,23 +1390,18 @@ var CommandLine = Module("commandline", {
// be two lists of the same characters (one here and a regexp in
// mappings.js)
bind(["<Space>", '"', "'"], "Expand command line abbreviation",
- function () {
- commandline.resetCompletions();
+ function ({ self }) {
+ self.resetCompletions();
editor.expandAbbreviation(modes.COMMAND_LINE);
return Events.PASS;
});
bind(["<Return>", "<C-j>", "<C-m>"], "Accept the current input",
- function (args) {
-
- commandline._keepCommand = userContext.hidden_option_command_afterimage;
-
- let mode = commandline.currentExtendedMode;
+ function ({ self }) {
let command = commandline.command;
- commandline.currentExtendedMode = null; // Don't let modes.pop trigger "cancel"
- modes.pop();
- return function () commandline.triggerCallback("submit", mode, command);
+ self.accepted = true;
+ modes.pop();
});
[
@@ -1692,17 +1411,23 @@ var CommandLine = Module("commandline", {
[["<S-Down>", "<C-n>", "<PageDown>"], "next", false, false]
].forEach(function ([keys, desc, up, search]) {
bind(keys, "Recall the " + desc + " command line from the history list",
- function (args) {
- dactyl.assert(commandline._history);
- commandline._history.select(up, search);
+ function ({ self }) {
+ dactyl.assert(self.history);
+ self.history.select(up, search);
});
});
bind(["<A-Tab>", "<Tab>"], "Select the next matching completion item",
- function ({ events }) { commandline._tabTimer.tell(events[0]); });
+ function ({ events, self }) {
+ dactyl.assert(self.completions);
+ self.completions.tabTimer.tell(events[0]);
+ });
bind(["<A-S-Tab>", "<S-Tab>"], "Select the previous matching completion item",
- function ({ events }) { commandline._tabTimer.tell(events[0]); });
+ function ({ events, self }) {
+ dactyl.assert(self.completions);
+ self.completions.tabTimer.tell(events[0]);
+ });
bind(["<BS>", "<C-h>"], "Delete the previous character",
function () {
@@ -1714,86 +1439,6 @@ var CommandLine = Module("commandline", {
bind(["<C-]>", "<C-5>"], "Expand command line abbreviation",
function () { editor.expandAbbreviation(modes.COMMAND_LINE); });
-
- let mow = modules.mow = {
- __noSuchMethod__: function (meth, args) Buffer[meth].apply(Buffer, [this.body].concat(args))
- };
- memoize(mow, "body", function () commandline.widgets.multilineOutput.contentDocument.documentElement);
- memoize(mow, "window", function () commandline.widgets.multilineOutput.contentWindow);
-
- const PASS = true;
- const DROP = false;
- const BEEP = {};
-
- bind = function bind(keys, description, action, test, default_) {
- mappings.add([modes.OUTPUT_MULTILINE],
- keys, description,
- function (command) {
- if (!options["more"])
- var res = PASS;
- else if (test && !test(command))
- res = default_;
- else
- res = action.call(command);
-
- if (res === PASS || res === DROP)
- modes.pop();
- else
- commandline.updateMorePrompt();
- if (res === BEEP)
- dactyl.beep();
- else if (res === PASS)
- events.feedkeys(command);
- });
- }
-
- bind(["j", "<C-e>", "<Down>"], "Scroll down one line",
- function () { mow.scrollVertical("lines", 1); },
- function () mow.isScrollable(1), BEEP);
-
- bind(["k", "<C-y>", "<Up>"], "Scroll up one line",
- function () { mow.scrollVertical("lines", -1); },
- function () mow.isScrollable(-1), BEEP);
-
- bind(["<C-j>", "<C-m>", "<Return>"], "Scroll down one line, exit on last line",
- function () { mow.scrollVertical("lines", 1); },
- function () mow.isScrollable(1), DROP);
-
- // half page down
- bind(["<C-d>"], "Scroll down half a page",
- function () { mow.scrollVertical("pages", .5); },
- function () mow.isScrollable(1), BEEP);
-
- bind(["<C-f>", "<PageDown>"], "Scroll down one page",
- function () { mow.scrollVertical("pages", 1); },
- function () mow.isScrollable(1), BEEP);
-
- bind(["<Space>"], "Scroll down one page",
- function () { mow.scrollVertical("pages", 1); },
- function () mow.isScrollable(1), DROP);
-
- bind(["<C-u>"], "Scroll up half a page",
- function () { mow.scrollVertical("pages", -.5); },
- function () mow.isScrollable(-1), BEEP);
-
- bind(["<C-b>", "<PageUp>"], "Scroll up half a page",
- function () { mow.scrollVertical("pages", -1); },
- function () mow.isScrollable(-1), BEEP);
-
- bind(["gg"], "Scroll to the beginning of output",
- function () { mow.scrollToPercent(null, 0); })
-
- bind(["G"], "Scroll to the end of output",
- function () { mow.body.scrollTop = mow.body.scrollHeight; })
-
- // copy text to clipboard
- bind(["<C-y>"], "Yank selection to clipboard",
- function () { dactyl.clipboardWrite(buffer.getCurrentWord(mow.window)); });
-
- // close the window
- bind(["q"], "Close the output window",
- function () {},
- function () false, DROP);
},
options: function init_options() {
options.add(["history", "hi"],
@@ -1811,10 +1456,6 @@ var CommandLine = Module("commandline", {
"number", 100,
{ validator: function (value) value >= 0 });
- options.add(["more"],
- "Pause the message list window when the full output will not fit on one page",
- "boolean", true);
-
options.add(["showmode", "smd"],
"Show the current mode in the command line",
"boolean", true);
@@ -1903,12 +1544,14 @@ var ItemList = Class("ItemList", {
this._div.style.minWidth = "";
// FIXME: Belongs elsewhere.
- commandline.updateOutputHeight(false, Math.max(0, this._minHeight - this._container.height));
+ mow.resize(false, Math.max(0, this._minHeight - this._container.height));
this._container.height = this._minHeight;
- this._container.height -= commandline.getSpaceNeeded();
- commandline.updateOutputHeight(false);
- this.timeout(function () { this._container.height -= commandline.getSpaceNeeded(); }, 0);
+ this._container.height -= mow.spaceNeeded;
+ mow.resize(false);
+ this.timeout(function () {
+ this._container.height -= mow.spaceNeeded;
+ });
},
_getCompletion: function _getCompletion(index) this._completionElements.snapshotItem(index - this._startIndex),
@@ -2037,9 +1680,8 @@ var ItemList = Class("ItemList", {
},
clear: function clear() { this.setItems(); this._doc.body.innerHTML = ""; },
- hide: function hide() { this._container.collapsed = true; },
- show: function show() { this._container.collapsed = false; },
- visible: function visible() !this._container.collapsed,
+ get visible() !this._container.collapsed,
+ set visible(val) this._container.collapsed = !val,
reset: function reset(brief) {
this._startIndex = this._endIndex = this._selIndex = -1;
@@ -2061,7 +1703,7 @@ var ItemList = Class("ItemList", {
this.reset(true);
if (typeof selectedItem == "number") {
this.selectItem(selectedItem);
- this.show();
+ this.visible = true;
}
},
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 95b7c6f6..bb5aa362 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -40,11 +40,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
this.commands["dactyl.restart"] = function (event) {
dactyl.restart();
};
+
+ styles.registerSheet("resource://dactyl-skin/dactyl.css");
},
cleanup: function () {
delete window.dactyl;
delete window.liberator;
+
+ styles.unregisterSheet("resource://dactyl-skin/dactyl.css");
},
destroy: function () {
@@ -1285,6 +1289,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
*/
trapErrors: function trapErrors(func, self) {
try {
+ if (isString(func))
+ func = self[func];
return func.apply(self || this, Array.slice(arguments, 2));
}
catch (e) {
diff --git a/common/content/events.js b/common/content/events.js
index c2d7ab91..b0cfecee 100644
--- a/common/content/events.js
+++ b/common/content/events.js
@@ -19,7 +19,7 @@ var ProcessorStack = Class("ProcessorStack", {
.flatten().array;
for (let [i, input] in Iterator(this.processors)) {
- let params = input.main == mode.main ? mode.params : input.main.params;
+ let params = input.main.params;
if (params.preExecute)
input.preExecute = params.preExecute;
if (params.postExecute)
@@ -101,7 +101,8 @@ var ProcessorStack = Class("ProcessorStack", {
result = res === Events.PASS ? Events.PASS : Events.KILL;
}
}
- else if (result !== Events.KILL && processors.some(function (p) !p.main.passUnknown)) {
+ else if (result !== Events.KILL && !this.actions.length &&
+ processors.some(function (p) !p.main.passUnknown)) {
result = Events.KILL;
dactyl.beep();
}
@@ -157,12 +158,13 @@ var KeyProcessor = Class("KeyProcessor", {
return this.onKeyPress(event);
},
- execute: function execute(map)
- let (self = this, args = arguments)
+ execute: function execute(map, args)
+ let (self = this)
function execute() {
if (self.preExecute)
self.preExecute.apply(self, args);
- let res = map.execute.apply(map, Array.slice(args, 1));
+ let res = map.execute.call(map, update({ self: self.main.params.mappingSelf || self.main.mappingSelf || map },
+ args))
if (self.postExecute)
self.postExecute.apply(self, args);
return res;
@@ -1092,7 +1094,8 @@ var Events = Module("events", {
let hives = mappings.hives.slice(event.noremap ? -1 : 0);
- let keyModes = array([mode.params.keyModes, mode.main, mode.main.allBases]).flatten().compact();
+ let main = { __proto__: mode.main, params: mode.params };
+ let keyModes = array([mode.params.keyModes, main, mode.main.allBases]).flatten().compact();
this.processor = ProcessorStack(mode, hives, keyModes);
}
@@ -1175,7 +1178,7 @@ var Events = Module("events", {
// Huh? --djk
onFocusChange: function onFocusChange(event) {
// command line has its own focus change handler
- if (modes.main == modes.COMMAND_LINE)
+ if (modes.main.input)
return;
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
diff --git a/common/content/hints.js b/common/content/hints.js
index 9381de8d..f9bcae15 100644
--- a/common/content/hints.js
+++ b/common/content/hints.js
@@ -9,16 +9,18 @@
/** @scope modules */
/** @instance hints */
-var HintSession = Class("HintSession", {
+var HintSession = Class("HintSession", CommandMode, {
init: function init(mode, opts) {
+ init.supercall(this);
+
opts = opts || {};
// Hack.
if (!opts.window && modes.main == modes.OUTPUT_MULTILINE)
opts.window = commandline.widgets.multilineOutput.contentWindow;
- this.mode = hints.modes[mode];
- dactyl.assert(this.mode);
+ this.hintMode = hints.modes[mode];
+ dactyl.assert(this.hintMode);
this.activeTimeout = null; // needed for hinttimeout > 0
this.continue = Boolean(opts.continue);
@@ -31,8 +33,7 @@ var HintSession = Class("HintSession", {
this.usedTabKey = false;
this.validHints = []; // store the indices of the "hints" array with valid elements
- commandline.input(UTF8(this.mode.prompt) + ": ", null, this.closure);
- modes.extended = modes.HINTS;
+ this.open();
this.top = opts.window || content;
this.top.addEventListener("resize", hints.resizeTimer.closure.tell, true);
@@ -51,7 +52,22 @@ var HintSession = Class("HintSession", {
this.checkUnique();
},
- get extended() modes.HINTS,
+ get mode() modes.HINTS,
+
+ get prompt() ["Question", UTF8(this.hintMode.prompt) + ": "],
+
+ leave: function leave(stack) {
+ leave.superapply(this, arguments);
+
+ if (!stack.push) {
+ if (hints.hintSession == this)
+ hints.hintSession = null;
+ if (this.top)
+ this.top.removeEventListener("resize", hints.resizeTimer.closure.tell, true);
+
+ this.removeHints(0);
+ }
+ },
checkUnique: function _checkUnique() {
if (this.hintNumber == 0)
@@ -236,7 +252,7 @@ var HintSession = Class("HintSession", {
let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none"/>, doc);
- let mode = this.mode;
+ let mode = this.hintMode;
let res = util.evaluateXPath(mode.xpath, doc, true);
let start = this.pageHints.length;
@@ -289,18 +305,6 @@ var HintSession = Class("HintSession", {
return true;
},
- leave: function leave(stack) {
- if (!stack.push) {
- if (hints.hintSession == this)
- hints.hintSession = null;
- this.continue = false;
- if (this.top)
- this.top.removeEventListener("resize", hints.resizeTimer.closure.tell, true);
-
- this.removeHints(0);
- }
- },
-
/**
* Handle user input.
*
@@ -429,7 +433,7 @@ var HintSession = Class("HintSession", {
if ((modes.extended & modes.HINTS) && !this.continue)
modes.pop();
commandline.lastEcho = null; // Hack.
- dactyl.trapErrors(this.mode.action, this.mode,
+ dactyl.trapErrors("action", this.hintMode,
elem, elem.href || elem.src || "",
this.extendedhintCount, top);
if (this.continue && this.top)
@@ -662,6 +666,7 @@ var Hints = Module("hints", {
if (modes.extended & modes.HINTS)
modes.getStack(0).params.onResize();
});
+
let appContent = document.getElementById("appcontent");
if (appContent)
events.addSessionListener(appContent, "scroll", this.resizeTimer.closure.tell, false);
@@ -681,9 +686,9 @@ var Hints = Module("hints", {
this.addMode("t", "Follow hint in a new tab", function (elem) buffer.followLink(elem, dactyl.NEW_TAB));
this.addMode("b", "Follow hint in a background tab", function (elem) buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB));
this.addMode("w", "Follow hint in a new window", function (elem) buffer.followLink(elem, dactyl.NEW_WINDOW));
- this.addMode("O", "Generate an ‘:open URL’ prompt", function (elem, loc) commandline.open(":", "open " + loc, modes.EX));
- this.addMode("T", "Generate a ‘:tabopen URL’ prompt", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX));
- this.addMode("W", "Generate a ‘:winopen URL’ prompt", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX));
+ this.addMode("O", "Generate an ‘:open URL’ prompt", function (elem, loc) CommandExMode.open("open " + loc));
+ this.addMode("T", "Generate a ‘:tabopen URL’ prompt", function (elem, loc) CommandExMode.open("tabopen " + loc));
+ this.addMode("W", "Generate a ‘:winopen URL’ prompt", function (elem, loc) CommandExMode.open("winopen " + loc));
this.addMode("a", "Add a bookmark", function (elem) bookmarks.addSearchKeyword(elem));
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));
@@ -952,20 +957,19 @@ var Hints = Module("hints", {
open: function open(mode, opts) {
this._extendedhintCount = opts.count;
- commandline.input(mode, null, {
+ commandline.input(["Normal", mode], "", {
completer: function (context) {
context.compare = function () 0;
context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))];
},
- onAccept: function (arg) {
+ onSubmit: function (arg) {
if (arg)
- util.timeout(function () {
- dactyl.trapErrors(hints.show, hints, arg, opts);
- });
+ hints.show(arg, opts);
+ },
+ onChange: function () {
+ this.accepted = true;
+ modes.pop();
},
- get onCancel() this.onAccept,
- onChange: function () { modes.pop(); },
- promptHighlight: "Normal"
});
},
@@ -1075,6 +1079,15 @@ var Hints = Module("hints", {
Mode: Struct("name", "prompt", "action", "tags", "filter")
}, {
+ modes: function () {
+ modes.addMode("HINTS", {
+ extended: true,
+ description: "Active when selecting elements in QuickHint or ExtendedHint mode",
+ bases: [modes.COMMAND_LINE],
+ input: true,
+ ownsBuffer: true
+ });
+ },
mappings: function () {
var myModes = config.browserModes.concat(modes.OUTPUT_MULTILINE);
mappings.add(myModes, ["f"],
@@ -1097,25 +1110,23 @@ var Hints = Module("hints", {
mappings.add(modes.HINTS, ["<Return>"],
"Follow the selected hint",
- function () { hints.hintSession.update(true); });
+ function ({ self }) { self.update(true); });
mappings.add(modes.HINTS, ["<Tab>"],
"Focus the next matching hint",
- function () { hints.hintSession.tab(false); });
+ function ({ self }) { self.tab(false); });
mappings.add(modes.HINTS, ["<S-Tab>"],
"Focus the previous matching hint",
- function () { hints.hintSession.tab(true); });
+ function ({ self }) { self.tab(true); });
mappings.add(modes.HINTS, ["<BS>", "<C-h>"],
"Delete the previous character",
- function () hints.hintSession.backspace());
+ function ({ self }) self.backspace());
mappings.add(modes.HINTS, ["<Leader>"],
"Toggle hint filtering",
- function () {
- hints.hintSession.escapeNumbers = !hints.hintSession.escapeNumbers;
- });
+ function ({ self }) { self.escapeNumbers = !self.escapeNumbers; });
},
options: function () {
const DEFAULT_HINTTAGS =
diff --git a/common/content/mappings.js b/common/content/mappings.js
index bb83522e..b5255d77 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -295,6 +295,8 @@ var Mappings = Module("mappings", {
this.allHives = [this.user, this.builtin];
},
+ repeat: Modes.boundProperty(),
+
hives: Class.memoize(function () array(this.allHives.filter(function (h) h.filter(buffer.uri)))),
get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
diff --git a/common/content/modes.js b/common/content/modes.js
index 8904aeec..fe9b0275 100644
--- a/common/content/modes.js
+++ b/common/content/modes.js
@@ -119,12 +119,6 @@ var Modes = Module("modes", {
input: true,
ownsFocus: true
});
- this.addMode("COMMAND_LINE", {
- char: "c",
- description: "Active when the command line is focused",
- input: true
- });
-
this.addMode("EMBED", {
input: true,
@@ -160,18 +154,6 @@ var Modes = Module("modes", {
input: true
});
- // this._extended modes, can include multiple modes, and even main modes
- this.addMode("EX", {
- extended: true,
- description: "Ex command mode, active when the command line is open for Ex commands",
- input: true
- }, { history: "command" });
- this.addMode("HINTS", {
- extended: true,
- description: "Active when selecting elements in QuickHint or ExtendedHint mode",
- count: false,
- ownsBuffer: true
- });
this.addMode("INPUT_MULTILINE", {
extended: true,
hidden: true,
@@ -180,11 +162,6 @@ var Modes = Module("modes", {
this.addMode("LINE", {
extended: true, hidden: true
});
- this.addMode("PROMPT", {
- extended: true,
- description: "Active when a prompt is open in the command line",
- input: true
- });
this.push(this.NORMAL, 0, {
enter: function (stack, prev) {
@@ -214,9 +191,9 @@ var Modes = Module("modes", {
_getModeMessage: function () {
// when recording a macro
let macromode = "";
- if (modes.recording)
+ if (this.recording)
macromode = "recording";
- else if (modes.replaying)
+ else if (this.replaying)
macromode = "replaying";
let val = this._modeMap[this._main].display();
@@ -317,13 +294,15 @@ var Modes = Module("modes", {
}
if (stack && stack.pop && stack.pop.params.leave)
- stack.pop.params.leave(stack, this.topOfStack);
+ dactyl.trapErrors("leave", stack.pop.params,
+ stack, this.topOfStack);
let push = mainMode != null && !(stack && stack.pop) &&
Modes.StackElement(this._main, this._extended, params, {});
if (push && this.topOfStack) {
if (this.topOfStack.params.leave)
- this.topOfStack.params.leave({ push: push }, push);
+ dactyl.trapErrors("leave", this.topOfStack.params,
+ { push: push }, push);
for (let [id, { obj, prop }] in Iterator(this.boundProperties)) {
if (!obj.get())
delete this.boundProperties[id];
@@ -332,7 +311,7 @@ var Modes = Module("modes", {
}
}
- this.delayed.forEach(function ([fn, self]) fn.call(self));
+ let delayed = this.delayed;
this.delayed = [];
let prev = stack && stack.pop || this.topOfStack;
@@ -343,9 +322,14 @@ var Modes = Module("modes", {
for (let { obj, prop, value } in values(this.topOfStack.saved))
obj[prop] = value;
+ this.show();
+
+ delayed.forEach(function ([fn, self]) dactyl.trapErrors(fn, self));
+
if (this.topOfStack.params.enter && prev)
- this.topOfStack.params.enter(push ? { push: push } : stack || {},
- prev);
+ dactyl.trapErrors("enter", this.topOfStack.params,
+ push ? { push: push } : stack || {},
+ prev);
dactyl.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended], stack);
this.show();
@@ -439,7 +423,7 @@ var Modes = Module("modes", {
input: false,
- passUnknown: false,
+ get passUnknown() this.input,
get mask() this,
diff --git a/common/content/mow.js b/common/content/mow.js
new file mode 100644
index 00000000..bccff26c
--- /dev/null
+++ b/common/content/mow.js
@@ -0,0 +1,352 @@
+// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
+// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
+// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+//
+// This work is licensed for reuse under an MIT license. Details are
+// given in the LICENSE.txt file included with this file.
+
+var MOW = Module("mow", {
+ init: function () {
+
+ XML.ignoreWhitespace = true;
+ util.overlayWindow(window, {
+ objects: {
+ eventTarget: this
+ },
+ append: <e4x xmlns={XUL} xmlns:dactyl={NS}>
+ <window id={document.documentElement.id}>
+ <popupset>
+ <menupopup id="dactyl-contextmenu" highlight="Events" events="contextEvents">
+ <menuitem id="dactyl-context-copylink"
+ label="Copy Link Location" dactyl:group="link"
+ oncommand="goDoCommand('cmd_copyLink');"/>
+ <menuitem id="dactyl-context-copypath"
+ label="Copy File Path" dactyl:group="link path"
+ oncommand="dactyl.clipboardWrite(document.popupNode.getAttribute('path'));"/>
+ <menuitem id="dactyl-context-copy"
+ label="Copy" dactyl:group="selection"
+ command="cmd_copy"/>
+ <menuitem id="dactyl-context-selectall"
+ label="Select All"
+ command="cmd_selectAll"/>
+ </menupopup>
+ </popupset>
+ </window>
+ <vbox id={config.commandContainer}>
+ <vbox class="dactyl-container" id="dactyl-multiline-output-container" hidden="false" collapsed="true">
+ <iframe id="dactyl-multiline-output" src="dactyl://content/buffer.xhtml"
+ flex="1" hidden="false" collapsed="false" contextmenu="dactyl-contextmenu"
+ highlight="Events" />
+ </vbox>
+ </vbox>
+ </e4x>
+ });
+ },
+
+ __noSuchMethod__: function (meth, args) Buffer[meth].apply(Buffer, [this.body].concat(args)),
+
+ get widget() this.widgets.multilineOutput,
+ widgets: Class.memoize(function () commandline.widgets),
+
+ body: Class.memoize(function () this.widget.contentDocument.documentElement),
+ document: Class.memoize(function () this.widget.contentDocument),
+ window: Class.memoize(function () this.widget.contentWindow),
+
+ /**
+ * Display a multi-line message.
+ *
+ * @param {string} data
+ * @param {string} highlightGroup
+ */
+ echo: function echo(data, highlightGroup, silent) {
+ let body = this.document.body;
+
+ this.widgets.message = null;
+ if (!commandline.commandVisible)
+ commandline.hide();
+
+ this._startHints = false;
+ if (modes.main != modes.OUTPUT_MULTILINE) {
+ modes.push(modes.OUTPUT_MULTILINE, null, {
+ onKeyPress: this.closure.onKeyPress,
+ leave: this.closure(function leave(stack) {
+ if (stack.pop)
+ for (let message in values(this.messages))
+ if (message.leave)
+ message.leave(stack);
+ })
+ });
+ this.messages = [];
+ }
+
+ // If it's already XML, assume it knows what it's doing.
+ // Otherwise, white space is significant.
+ // The problem elsewhere is that E4X tends to insert new lines
+ // after interpolated data.
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+
+ if (isObject(data)) {
+ this.lastOutput = null;
+
+ var output = util.xmlToDom(<div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}/>,
+ this.document);
+ data.document = this.document;
+ output.appendChild(data.message);
+
+ this.messages.push(data);
+ }
+ else {
+ let style = isString(data) ? "pre" : "nowrap";
+ this.lastOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{data}</div>;
+
+ var output = util.xmlToDom(this.lastOutput, this.document);
+ }
+
+ // FIXME: need to make sure an open MOW is closed when commands
+ // that don't generate output are executed
+ if (this.widgets.mowContainer.collapsed) {
+ this.body.scrollTop = 0;
+ while (body.firstChild)
+ body.removeChild(body.firstChild);
+ }
+
+ body.appendChild(output);
+
+ let str = typeof data !== "xml" && data.message || data;
+ if (!silent)
+ dactyl.triggerObserver("echoMultiline", data, highlightGroup, output);
+
+ this.resize(true);
+
+ if (options["more"] && this.isScrollable(1)) {
+ // start the last executed command's output at the top of the screen
+ let elements = this.document.getElementsByClassName("ex-command-output");
+ elements[elements.length - 1].scrollIntoView(true);
+ }
+ else
+ this.body.scrollTop = this.body.scrollHeight;
+
+ dactyl.focus(this.window);
+ this.updateMorePrompt();
+ },
+
+ events: {
+ click: function onClick(event) {
+ if (event.getPreventDefault())
+ return;
+
+ const openLink = function openLink(where) {
+ event.preventDefault();
+ dactyl.open(event.target.href, where);
+ }
+
+ if (event.target instanceof HTMLAnchorElement)
+ switch (events.toString(event)) {
+ case "<LeftMouse>":
+ openLink(dactyl.CURRENT_TAB);
+ break;
+ case "<MiddleMouse>":
+ case "<C-LeftMouse>":
+ case "<C-M-LeftMouse>":
+ openLink({ where: dactyl.NEW_TAB, background: true });
+ break;
+ case "<S-MiddleMouse>":
+ case "<C-S-LeftMouse>":
+ case "<C-M-S-LeftMouse>":
+ openLink({ where: dactyl.NEW_TAB, background: false });
+ break;
+ case "<S-LeftMouse>":
+ openLink(dactyl.NEW_WINDOW);
+ break;
+ }
+ },
+ unload: function onUnload(event) {
+ event.preventDefault();
+ }
+ },
+ contextEvents: {
+ popupshowing: function (event) {
+ let enabled = {
+ link: window.document.popupNode instanceof HTMLAnchorElement,
+ path: window.document.popupNode.hasAttribute("path"),
+ selection: !window.document.commandDispatcher.focusedWindow.getSelection().isCollapsed
+ };
+
+ for (let node in array.iterValues(event.target.children)) {
+ let group = node.getAttributeNS(NS, "group");
+ node.hidden = group && !group.split(/\s+/).every(function (g) enabled[g]);
+ }
+ }
+ },
+
+ onContext: function onContext(event) {
+ return true;
+ },
+
+ onKeyPress: function onKeyPress(event) {
+ const KILL = false, PASS = true;
+
+ if (options["more"] && mow.isScrollable(1))
+ commandline.updateMorePrompt(false, true);
+ else {
+ modes.pop();
+ events.feedkeys(events.toString(event));
+ return KILL;
+ }
+ return PASS;
+ },
+
+ /**
+ * Changes the height of the message window to fit in the available space.
+ *
+ * @param {boolean} open If true, the widget will be opened if it's not
+ * already so.
+ */
+ resize: function updateOutputHeight(open, extra) {
+ if (!open && this.widgets.mowContainer.collapsed)
+ return;
+
+ let doc = this.widget.contentDocument;
+
+ let availableHeight = config.outputHeight;
+ if (!this.widgets.mowContainer.collapsed)
+ availableHeight += parseFloat(this.widgets.mowContainer.height);
+ availableHeight -= extra || 0;
+
+ doc.body.style.minWidth = this.widgets.commandbar.commandline.scrollWidth + "px";
+ this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px";
+ this.timeout(function ()
+ this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px",
+ 0);
+
+ doc.body.style.minWidth = "";
+ this.visible = true;
+ },
+
+ get spaceNeeded() {
+ let rect = this.widgets.commandbar.commandline.getBoundingClientRect();
+ let offset = rect.bottom - window.innerHeight;
+ return Math.max(0, offset);
+ },
+
+ /**
+ * Update or remove the multi-line output widget's "MORE" prompt.
+ *
+ * @param {boolean} force If true, "-- More --" is shown even if we're
+ * at the end of the output.
+ * @param {boolean} showHelp When true, show the valid key sequences
+ * and what they do.
+ */
+ updateMorePrompt: function updateMorePrompt(force, showHelp) {
+ if (this.widgets.mowContainer.collapsed)
+ return this.widgets.message = null;
+ let elem = this.widget.contentDocument.documentElement;
+
+ if (showHelp)
+ this.widgets.message = ["MoreMsg", "-- More -- SPACE/<C-f>/j: screen/page/line down, <C-b>/<C-u>/k: up, q: quit"];
+ else if (force || (options["more"] && Buffer.isScrollable(elem, 1)))
+ this.widgets.message = ["MoreMsg", "-- More --"];
+ else
+ this.widgets.message = ["Question", "Press ENTER or type command to continue"];
+ },
+
+ visible: Modes.boundProperty({
+ set: function set_mowVisible(value) {
+ this.widgets.mowContainer.collapsed = !value;
+
+ let elem = this.widget;
+ if (!value && elem && elem.contentWindow == document.commandDispatcher.focusedWindow)
+ document.commandDispatcher.focusedWindow = content;
+ }
+ }),
+
+}, {
+}, {
+ mappings: function () {
+ const PASS = true;
+ const DROP = false;
+ const BEEP = {};
+
+ mappings.add([modes.COMMAND],
+ ["g<lt>"], "Redisplay the last command output",
+ function () {
+ dactyl.assert(commandline.lastOutput, "No previous command output");
+ mow.echo(mow.lastOutput, "Normal");
+ });
+
+ bind = function bind(keys, description, action, test, default_) {
+ mappings.add([modes.OUTPUT_MULTILINE],
+ keys, description,
+ function (command) {
+ if (!options["more"])
+ var res = PASS;
+ else if (test && !test(command))
+ res = default_;
+ else
+ res = action.call(command);
+
+ if (res === PASS || res === DROP)
+ modes.pop();
+ else
+ mow.updateMorePrompt();
+ if (res === BEEP)
+ dactyl.beep();
+ else if (res === PASS)
+ events.feedkeys(command);
+ });
+ }
+
+ bind(["j", "<C-e>", "<Down>"], "Scroll down one line",
+ function () { mow.scrollVertical("lines", 1); },
+ function () mow.isScrollable(1), BEEP);
+
+ bind(["k", "<C-y>", "<Up>"], "Scroll up one line",
+ function () { mow.scrollVertical("lines", -1); },
+ function () mow.isScrollable(-1), BEEP);
+
+ bind(["<C-j>", "<C-m>", "<Return>"], "Scroll down one line, exit on last line",
+ function () { mow.scrollVertical("lines", 1); },
+ function () mow.isScrollable(1), DROP);
+
+ // half page down
+ bind(["<C-d>"], "Scroll down half a page",
+ function () { mow.scrollVertical("pages", .5); },
+ function () mow.isScrollable(1), BEEP);
+
+ bind(["<C-f>", "<PageDown>"], "Scroll down one page",
+ function () { mow.scrollVertical("pages", 1); },
+ function () mow.isScrollable(1), BEEP);
+
+ bind(["<Space>"], "Scroll down one page",
+ function () { mow.scrollVertical("pages", 1); },
+ function () mow.isScrollable(1), DROP);
+
+ bind(["<C-u>"], "Scroll up half a page",
+ function () { mow.scrollVertical("pages", -.5); },
+ function () mow.isScrollable(-1), BEEP);
+
+ bind(["<C-b>", "<PageUp>"], "Scroll up half a page",
+ function () { mow.scrollVertical("pages", -1); },
+ function () mow.isScrollable(-1), BEEP);
+
+ bind(["gg"], "Scroll to the beginning of output",
+ function () { mow.scrollToPercent(null, 0); })
+
+ bind(["G"], "Scroll to the end of output",
+ function () { mow.body.scrollTop = mow.body.scrollHeight; })
+
+ // copy text to clipboard
+ bind(["<C-y>"], "Yank selection to clipboard",
+ function () { dactyl.clipboardWrite(buffer.getCurrentWord(mow.window)); });
+
+ // close the window
+ bind(["q"], "Close the output window",
+ function () {},
+ function () false, DROP);
+ },
+ options: function () {
+ options.add(["more"],
+ "Pause the message list window when the full output will not fit on one page",
+ "boolean", true);
+ }
+});
diff --git a/common/content/tabs.js b/common/content/tabs.js
index a6ecaeae..87a91e7d 100644
--- a/common/content/tabs.js
+++ b/common/content/tabs.js
@@ -908,7 +908,7 @@ var Tabs = Module("tabs", {
if (count != null)
tabs.switchTo(String(count));
else
- commandline.open(":", "buffer! ", modes.EX);
+ CommandExMode.open("buffer! ");
},
{ count: true });
diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm
index 00ae81de..8fbbba32 100644
--- a/common/modules/addons.jsm
+++ b/common/modules/addons.jsm
@@ -147,20 +147,19 @@ var Addon = Class("Addon", {
};
XML.ignoreWhitespace = true;
util.xmlToDom(
- <li highlight="Addon" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
- <span highlight="AddonName" key="name">
- </span>
- <span highlight="AddonVersion" key="version"/>
- <span highlight="AddonStatus" key="status"/>
- <span highlight="AddonButtons Buttons">
+ <tr highlight="Addon" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
+ <td highlight="AddonName" key="name"/>
+ <td highlight="AddonVersion" key="version"/>
+ <td highlight="AddonStatus" key="status"/>
+ <td highlight="AddonButtons Buttons">
<a highlight="Button" key="enable">On&#xa0;</a>
<a highlight="Button" key="disable">Off</a>
<a highlight="Button" key="delete">Del</a>
<a highlight="Button" key="update">Upd</a>
<a highlight="Button" key="options">Opt</a>
- </span>
- <span highlight="AddonDescription" key="description"/>
- </li>,
+ </td>
+ <td highlight="AddonDescription" key="description"/>
+ </tr>,
this.list.document, this.nodes);
this.update();
@@ -266,15 +265,15 @@ var AddonList = Class("AddonList", {
message: Class.memoize(function () {
XML.ignoreWhitespace = true;
- util.xmlToDom(<ul highlight="Addons" key="list" xmlns={XHTML}>
- <li highlight="AddonHead">
- <span>Name</span>
- <span>Version</span>
- <span>Status</span>
- <span/>
- <span>Description</span>
- </li>
- </ul>, this.document, this.nodes);
+ util.xmlToDom(<table highlight="Addons" key="list" xmlns={XHTML}>
+ <tr highlight="AddonHead">
+ <td>Name</td>
+ <td>Version</td>
+ <td>Status</td>
+ <td/>
+ <td>Description</td>
+ </tr>
+ </table>, this.document, this.nodes);
return this.nodes.list;
}),
@@ -314,7 +313,7 @@ var AddonList = Class("AddonList", {
if (addon && addon.id in this.addons)
this.addons[addon.id].update();
if (this.ready)
- this.modules.commandline.updateOutputHeight(false);
+ this.modules.mow.resize(false);
},
onDisabled: function (addon) { this.update(addon); },
diff --git a/common/modules/base.jsm b/common/modules/base.jsm
index 1a5e5b7a..1de0923f 100644
--- a/common/modules/base.jsm
+++ b/common/modules/base.jsm
@@ -818,7 +818,7 @@ memoize(Class.prototype, "closure", function () {
iter(properties(this), properties(this, true)).forEach(function (k) {
if (!this.__lookupGetter__(k) && callable(this[k]))
closure[k] = closure(this[k]);
- else if (!(k in closure || k in Object.prototype))
+ else if (!(k in closure))
Object.defineProperty(closure, k, {
get: function get_proxy() self[k],
set: function set_proxy(val) self[k] = val,
@@ -985,9 +985,10 @@ let StructBase = Class("StructBase", Array, {
});
var Timer = Class("Timer", {
- init: function (minInterval, maxInterval, callback) {
+ init: function (minInterval, maxInterval, callback, self) {
this._timer = services.Timer();
this.callback = callback;
+ this.self = self || this;
this.minInterval = minInterval;
this.maxInterval = maxInterval;
this.doneAt = 0;
@@ -1004,7 +1005,7 @@ var Timer = Class("Timer", {
// minInterval is the time between the completion of the command and the next firing
this.doneAt = Date.now() + this.minInterval;
- this.callback(this.arg);
+ this.callback.call(this.self, this.arg);
}
catch (e) {
if (typeof util === "undefined")
@@ -1042,8 +1043,8 @@ var Timer = Class("Timer", {
this.doneAt = 0;
},
- flush: function () {
- if (this.doneAt == -1)
+ flush: function (force) {
+ if (this.doneAt == -1 || force)
this.notify();
}
});
diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm
index a7ee8ed8..5dbbc897 100644
--- a/common/modules/downloads.jsm
+++ b/common/modules/downloads.jsm
@@ -29,31 +29,31 @@ var Download = Class("Download", {
commandTarget: self
};
util.xmlToDom(
- <li highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
- <span highlight="DownloadTitle">
+ <tr highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
+ <td highlight="DownloadTitle">
<span highlight="Link">
<a key="launch" dactyl:command="download.command"
href={self.target.spec} path={self.targetFile.path}>{self.displayName}</a>
<span highlight="LinkInfo">{self.targetFile.path}</span>
</span>
- </span>
- <span highlight="DownloadState" key="state"/>
- <span highlight="DownloadButtons Buttons">
+ </td>
+ <td highlight="DownloadState" key="state"/>
+ <td highlight="DownloadButtons Buttons">
<a highlight="Button" key="pause">Pause</a>
<a highlight="Button" key="remove">Remove</a>
<a highlight="Button" key="resume">Resume</a>
<a highlight="Button" key="retry">Retry</a>
<a highlight="Button" key="cancel">Cancel</a>
<a highlight="Button" key="delete">Delete</a>
- </span>
- <span highlight="DownloadProgress" key="progress">
+ </td>
+ <td highlight="DownloadProgress" key="progress">
<span highlight="DownloadProgressHave" key="progressHave"
/>/<span highlight="DownloadProgressTotal" key="progressTotal"/>
- </span>
- <span highlight="DownloadPercent" key="percent"/>
- <span highlight="DownloadTime" key="time"/>
- <a highlight="DownloadSource" key="source" href={self.source.spec}>{self.source.spec}</a>
- </li>,
+ </td>
+ <td highlight="DownloadPercent" key="percent"/>
+ <td highlight="DownloadTime" key="time"/>
+ <td><a highlight="DownloadSource" key="source" href={self.source.spec}>{self.source.spec}</a></td>
+ </tr>,
this.list.document, this.nodes);
self.updateStatus();
@@ -179,8 +179,8 @@ var DownloadList = Class("DownloadList",
message: Class.memoize(function () {
- util.xmlToDom(<ul highlight="Downloads" key="list" xmlns={XHTML}>
- <li highlight="DownloadHead">
+ util.xmlToDom(<table highlight="Downloads" key="list" xmlns={XHTML}>
+ <tr highlight="DownloadHead">
<span>Title</span>
<span>Status</span>
<span/>
@@ -188,23 +188,23 @@ var DownloadList = Class("DownloadList",
<span/>
<span>Time remaining</span>
<span>Source</span>
- </li>
- <li highlight="Download"><span><div style="min-height: 1ex; /* FIXME */"/></span></li>
- <li highlight="Download" key="totals" active="true">
- <span><span highlight="Title">Totals:</span>&#xa0;<span key="total"/></span>
- <span/>
- <span highlight="DownloadButtons">
+ </tr>
+ <tr highlight="Download"><span><div style="min-height: 1ex; /* FIXME */"/></span></tr>
+ <tr highlight="Download" key="totals" active="true">
+ <td><span highlight="Title">Totals:</span>&#xa0;<span key="total"/></td>
+ <td/>
+ <td highlight="DownloadButtons">
<a highlight="Button" key="clear">Clear</a>
- </span>
- <span highlight="DownloadProgress" key="progress">
+ </td>
+ <td highlight="DownloadProgress" key="progress">
<span highlight="DownloadProgressHave" key="progressHave"
/>/<span highlight="DownloadProgressTotal" key="progressTotal"/>
- </span>
- <span highlight="DownloadPercent" key="percent"/>
- <span highlight="DownloadTime" key="time"/>
- <span/>
- </li>
- </ul>, this.document, this.nodes);
+ </td>
+ <td highlight="DownloadPercent" key="percent"/>
+ <td highlight="DownloadTime" key="time"/>
+ <td/>
+ </tr>
+ </table>, this.document, this.nodes);
for (let row in iter(services.downloadManager.DBConnection
.createStatement("SELECT id FROM moz_downloads")))
@@ -296,7 +296,7 @@ var DownloadList = Class("DownloadList",
else {
this.addDownload(download.id);
- this.modules.commandline.updateOutputHeight(false);
+ this.modules.mow.resize(false);
this.nodes.list.scrollIntoView(false);
}
this.update();
diff --git a/common/modules/finder.jsm b/common/modules/finder.jsm
index 5694e4df..80525f53 100644
--- a/common/modules/finder.jsm
+++ b/common/modules/finder.jsm
@@ -15,24 +15,25 @@ var RangeFinder = Module("rangefinder", {
Local: function (dactyl, modules, window) ({
init: function () {
this.dactyl = dactyl;
- this.commandline = modules.commandline;
- this.modes = modules.modes;
+ this.modules = modules;
this.window = window;
- this.options = modules.options;
this.lastFindPattern = "";
},
+ get commandline() this.modules.commandline,
+ get modes() this.modules.modes,
+ get options() this.modules.options,
+
get rangeFind() modules.buffer.localStore.rangeFind,
set rangeFind(val) modules.buffer.localStore.rangeFind = val
}),
openPrompt: function (mode) {
- let backwards = mode == this.modes.FIND_BACKWARD;
- this.commandline.open(backwards ? "?" : "/", "", mode);
+ this.CommandMode(mode).open();
if (this.rangeFind && this.rangeFind.window.get() === this.window)
this.rangeFind.reset();
- this.find("", backwards);
+ this.find("", mode === this.modes.FIND_BACKWARD);
},
bootstrap: function (str, backward) {
@@ -119,8 +120,12 @@ var RangeFinder = Module("rangefinder", {
this.rangeFind.focus();
},
- // Called when the user types a key in the find dialog. Triggers a find attempt if 'incfind' is set
- onKeyPress: function (command) {
+ onCancel: function () {
+ if (this.rangeFind)
+ this.rangeFind.cancel();
+ },
+
+ onChange: function (command) {
if (this.options["incfind"]) {
command = this.bootstrap(command);
this.rangeFind.find(command);
@@ -138,13 +143,6 @@ var RangeFinder = Module("rangefinder", {
this.rangeFind.focus();
},
- // Called when the find is canceled - for example if someone presses
- // escape while typing a find
- onCancel: function () {
- if (this.rangeFind)
- this.rangeFind.cancel();
- },
-
/**
* Highlights all occurrences of the last sought for string in the
* current buffer.
@@ -163,12 +161,12 @@ var RangeFinder = Module("rangefinder", {
}
}, {
}, {
- /* Must come before commandline. */
modes: function (dactyl, modules, window) {
- const { modes } = modules;
+ const { commandline, modes } = modules;
modes.addMode("FIND", {
extended: true,
description: "Find mode, active when typing search input",
+ bases: [modes.COMMAND_LINE],
input: true
});
modes.addMode("FIND_FORWARD", {
@@ -184,15 +182,6 @@ var RangeFinder = Module("rangefinder", {
input: true
}, { history: "search" });
},
- commandline: function (dactyl, modules, window) {
- const { commandline, modes, rangefinder } = modules;
- commandline.registerCallback("change", modes.FIND_FORWARD, rangefinder.closure.onKeyPress);
- commandline.registerCallback("submit", modes.FIND_FORWARD, rangefinder.closure.onSubmit);
- commandline.registerCallback("cancel", modes.FIND_FORWARD, rangefinder.closure.onCancel);
- commandline.registerCallback("change", modes.FIND_BACKWARD, rangefinder.closure.onKeyPress);
- commandline.registerCallback("submit", modes.FIND_BACKWARD, rangefinder.closure.onSubmit);
- commandline.registerCallback("cancel", modes.FIND_BACKWARD, rangefinder.closure.onCancel);
- },
commands: function (dactyl, modules, window) {
const { commands, rangefinder } = modules;
commands.add(["noh[lfind]"],
@@ -200,6 +189,22 @@ var RangeFinder = Module("rangefinder", {
function () { rangefinder.clear(); },
{ argCount: "0" });
},
+ commandline: function (dactyl, modules, window) {
+ this.CommandMode = Class("CommandFindMode", modules.CommandMode, {
+ init: function init(mode) {
+ this.mode = mode;
+ init.supercall(this);
+ },
+
+ historyKey: "find",
+
+ get prompt() this.mode === modules.modes.FIND_BACKWARD ? "?" : "/",
+
+ onCancel: this.closure.onCancel,
+ onChange: this.closure.onChange,
+ onSubmit: this.closure.onSubmit
+ });
+ },
mappings: function (dactyl, modules, window) {
const { buffer, config, mappings, modes, rangefinder } = modules;
var myModes = config.browserModes.concat([modes.CARET]);
diff --git a/common/modules/io.jsm b/common/modules/io.jsm
index f98c363f..d49f2349 100644
--- a/common/modules/io.jsm
+++ b/common/modules/io.jsm
@@ -61,6 +61,27 @@ var IO = Module("io", {
services.downloadManager.addListener(this.downloadListener);
},
+ CommandFileMode: Class("CommandFileMode", modules.CommandMode, {
+ init: function init(prompt, params) {
+ init.supercall(this);
+ this.prompt = isArray(prompt) ? prompt : ["Question", prompt];
+ update(this, params);
+ },
+
+ historyKey: "file",
+
+ get mode() modules.modes.FILE_INPUT,
+
+ complete: function (context) {
+ if (this.completer)
+ this.completer(context);
+
+ context = context.fork("files", 0);
+ modules.completion.file(context);
+ context.filters = context.filters.concat(this.filters || []);
+ }
+ }),
+
destroy: function destroy() {
services.downloadManager.removeListener(this.downloadListener);
for (let [, plugin] in Iterator(plugins.contexts))
@@ -1002,6 +1023,16 @@ unlet s:cpo_save
}]);
},
+ modes: function (dactyl, modules, window) {
+ const { commandline, modes } = modules;
+
+ modes.addMode("FILE_INPUT", {
+ extended: true,
+ description: "Active when selecting a file",
+ bases: [modes.COMMAND_LINE],
+ input: true
+ });
+ },
options: function (dactyl, modules, window) {
const { options } = modules;
diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm
index dc1e7431..c456b8a8 100644
--- a/common/modules/overlay.jsm
+++ b/common/modules/overlay.jsm
@@ -176,6 +176,7 @@ var Overlay = Module("Overlay", {
"io",
"mappings",
"marks",
+ "mow",
"options",
"statusline",
"styles",
@@ -203,12 +204,14 @@ var Overlay = Module("Overlay", {
modules.loaded = loaded;
function init(module) {
+ let name = module.constructor.className;
+
function init(func, mod)
function () defineModule.time(module.className || module.constructor.className, mod,
- func, module,
+ func, modules[name],
modules.dactyl, modules, window);
- set.add(loaded, module.constructor.className);
+ set.add(loaded, name);
for (let [mod, func] in Iterator(module.INIT)) {
if (mod in loaded)
init(func, mod)();
@@ -240,7 +243,7 @@ var Overlay = Module("Overlay", {
defineModule.loadLog.push("Load" + (isString(prereq) ? " " + prereq + " dependency: " : ": ") + module.className);
if (frame && frame.filename)
- defineModule.loadLog.push(" from: " + frame.filename + ":" + frame.lineNumber);
+ defineModule.loadLog.push(" from: " + frame.filename.replace(/.* -> /, "") + ":" + frame.lineNumber);
delete modules[module.className];
modules[module.className] = defineModule.time(module.className, "init", module);
diff --git a/common/modules/template.jsm b/common/modules/template.jsm
index 902d6f04..e7ddcab7 100644
--- a/common/modules/template.jsm
+++ b/common/modules/template.jsm
@@ -138,7 +138,9 @@ var Template = Module("Template", {
init.supercall(this, node);
let obj = params.eventTarget;
- for (let [event, handler] in Iterator(obj[this.getAttribute("events") || "events"]))
+ let events = obj[this.getAttribute("events") || "events"];
+
+ for (let [event, handler] in Iterator(events))
node.addEventListener(event, obj.closure(handler), false);
}
})