diff --git a/common/content/commandline.js b/common/content/commandline.js index e217bb58..b1ad96ca 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -893,18 +893,17 @@ var CommandLine = Module("commandline", { */ input: function _input(prompt, callback, extra) { extra = extra || {}; - let closure = extra.closure || extra; this._input = { - submit: callback || closure.onAccept, - change: closure.onChange, - complete: closure.completer, - cancel: closure.onCancel + 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), { - onEvent: closure.onEvent || this.closure.onEvent, + onEvent: extra.onEvent || this.extra.onEvent, leave: function leave(stack) { commandline.leave(stack); leave.supercall(extra, stack); @@ -965,7 +964,6 @@ var CommandLine = Module("commandline", { for (let node in array.iterValues(event.target.children)) { let group = node.getAttributeNS(NS, "group"); - util.dump(node, group, group && !group.split(/\s+/).every(function (g) enabled[g])); node.hidden = group && !group.split(/\s+/).every(function (g) enabled[g]); } } @@ -1672,6 +1670,13 @@ var CommandLine = Module("commandline", { [":"], "Enter command-line mode", function () { commandline.open(":", "", modes.EX); }); + mappings.add([modes.COMMAND], + ["g"], "Redisplay the last command output", + function () { + dactyl.assert(commandline._lastMowOutput, "No previous command output"); + commandline._echoMultiline(commandline._lastMowOutput, commandline.HL_NORMAL); + }); + let bind = function bind() mappings.add.apply(mappings, [[modes.COMMAND_LINE]].concat(Array.slice(arguments))) @@ -1728,12 +1733,6 @@ var CommandLine = Module("commandline", { bind(["", ""], "Expand command line abbreviation", function () { editor.expandAbbreviation(modes.COMMAND_LINE); }); - bind(["g<"], "Redisplay the last command output", - function () { - dactyl.assert(commandline._lastMowOutput, "No previous command output"); - commandline._echoMultiline(commandline._lastMowOutput, commandline.HL_NORMAL); - }); - let mow = modules.mow = { __noSuchMethod__: function (meth, args) Buffer[meth].apply(Buffer, [this.body].concat(args)) }; diff --git a/common/content/editor.js b/common/content/editor.js index ffbe217c..e0e99895 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -51,9 +51,7 @@ var Editor = Module("editor", { elem.scrollTop = top; elem.scrollLeft = left; - let event = elem.ownerDocument.createEvent("Event"); - event.initEvent("input", true, false); - events.dispatch(elem, event); + events.dispatch(elem, events.create(elem.ownerDocument, "input")); } }, diff --git a/common/content/events.js b/common/content/events.js index 241e39f1..95bd8519 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -530,9 +530,10 @@ var Events = Module("events", { * * @param {Document} doc The DOM document to associate this event with * @param {Type} type The type of event (keypress, click, etc.) - * @param {Object} opts The pseudo-event. + * @param {Object} opts The pseudo-event. @optional */ create: function (doc, type, opts) { + opts = opts || {}; var DEFAULTS = { HTML: { type: type, bubbles: true, cancelable: false @@ -557,7 +558,7 @@ var Events = Module("events", { } }; const TYPES = { - change: "", + change: "", input: "", click: "Mouse", mousedown: "Mouse", mouseup: "Mouse", mouseover: "Mouse", mouseout: "Mouse", keypress: "Key", keyup: "Key", keydown: "Key" @@ -566,7 +567,9 @@ var Events = Module("events", { var evt = doc.createEvent((t || "HTML") + "Events"); let defaults = DEFAULTS[t || "HTML"] - evt["init" + t + "Event"].apply(evt, Object.keys(defaults).map(function (k) k in opts ? opts[k] : defaults[k])); + evt["init" + t + "Event"].apply(evt, Object.keys(defaults) + .map(function (k) k in opts ? opts[k] + : defaults[k])); return evt; }, @@ -988,12 +991,28 @@ var Events = Module("events", { }, */ + input: function onInput(event) { + delete event.originalTarget.dactylKeyPress; + }, + // this keypress handler gets always called first, even if e.g. // the command-line has focus // TODO: ...help me...please... keypress: function onKeyPress(event) { event.dactylDefaultPrevented = event.getPreventDefault(); + // Hack to deal with and so forth not dispatching input + // events + if (event.originalTarget instanceof HTMLInputElement) { + let elem = event.originalTarget; + elem.dactylKeyPress = elem.value; + util.timeout(function () { + if ("dactylKeyPress" in elem && elem.value !== elem.dactylKeyPress) + events.dispatch(elem, events.create(elem.ownerDocument, "input")); + delete events.dactylKeyPress; + }); + } + let duringFeed = this.duringFeed || []; this.duringFeed = []; try { diff --git a/common/content/hints.js b/common/content/hints.js index ddb0feb7..b2571170 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -31,7 +31,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); + commandline.input(UTF8(this.mode.prompt) + ": ", null, this.closure); modes.extended = modes.HINTS; this.top = opts.window || content; @@ -80,6 +80,17 @@ var HintSession = Class("HintSession", { this.activeTimeout = null; }, + _escapeNumbers: false, + get escapeNumbers() this._escapeNumbers, + set escapeNumbers(val) { + this.clearTimeout(); + this._escapeNumbers = !!val; + if (val && this.usedTabKey) + this.hintNumber = 0; + + this.updateStatusline(); + }, + /** * Returns the hint string for a given number based on the values of * the 'hintkeys' option. @@ -322,7 +333,7 @@ var HintSession = Class("HintSession", { this.clearTimeout(); - if (!this.escNumbers && this.isHintKey(key)) { + if (!this.escapeNumbers && this.isHintKey(key)) { this.prevInput = "number"; let oldHintNumber = this.hintNumber; @@ -586,11 +597,61 @@ var HintSession = Class("HintSession", { } }, + backspace: function () { + this.clearTimeout(); + if (this.prevInput !== "number") + return Events.PASS; + + if (this.hintNumber > 0 && !this.usedTabKey) { + this.hintNumber = Math.floor(this.hintNumber / this.hintKeys.length); + if (this.hintNumber == 0) + this.prevInput = "text"; + this.update(false); + } + else { + this.usedTabKey = false; + this.hintNumber = 0; + dactyl.beep(); + } + return Events.KILL; + }, + + tab: function tab(previous) { + this.clearTimeout(); + this.usedTabKey = true; + if (this.hintNumber == 0) + this.hintNumber = 1; + + let oldId = this.hintNumber; + if (!previous) { + if (++this.hintNumber > this.validHints.length) + this.hintNumber = 1; + } + else { + if (--this.hintNumber < 1) + this.hintNumber = this.validHints.length; + } + + this.showActiveHint(this.hintNumber, oldId); + this.updateStatusline(); + }, + + update: function update(followFirst) { + this.clearTimeout(); + this.updateStatusline(); + + if (this.docs.length == 0 && this.hintString.length > 0) + this.generate(); + + this.show(); + this.process(followFirst); + }, + /** * Display the current status to the user. */ updateStatusline: function _updateStatusline() { - statusline.updateInputBuffer((hints.escNumbers ? options["mapleader"] : "") + + statusline.updateInputBuffer((this.escapeNumbers ? options["mapleader"] : "") + (this.hintNumber ? this.getHintString(this.hintNumber) : "")); }, }); @@ -1034,79 +1095,26 @@ var Hints = Module("hints", { function ({ count }) { hints.open("g;", { continue: true, count: count }); }, { count: true }); - function update(followFirst) { - hints.clearTimeout(); - hints._updateStatusline(); - - if (hints._docs.length == 0 && hints._hintString.length > 0) - hints._generate(); - - hints._showHints(); - hints._processHints(followFirst); - } - mappings.add(modes.HINTS, [""], "Follow the selected hint", - function () { update(true); }); - - function tab(previous) { - hints.clearTimeout(); - this._usedTabKey = true; - if (this._hintNumber == 0) - this._hintNumber = 1; - - let oldId = this._hintNumber; - if (!previous) { - if (++this._hintNumber > this._validHints.length) - this._hintNumber = 1; - } - else { - if (--this._hintNumber < 1) - this._hintNumber = this._validHints.length; - } - - this._showActiveHint(this._hintNumber, oldId); - this._updateStatusline(); - } + function () { hints.hintSession.update(true); }); mappings.add(modes.HINTS, [""], "Focus the next matching hint", - function () { tab.call(hints, false); }); + function () { hints.hintSession.tab(false); }); mappings.add(modes.HINTS, [""], "Focus the previous matching hint", - function () { tab.call(hints, true); }); + function () { hints.hintSession.tab(true); }); mappings.add(modes.HINTS, ["", ""], "Delete the previous character", - function () { - hints.clearTimeout(); - if (hints.prevInput !== "number") - return Events.PASS; - - if (hints._hintNumber > 0 && !hints._usedTabKey) { - hints._hintNumber = Math.floor(hints._hintNumber / hints.hintKeys.length); - if (hints._hintNumber == 0) - hints.prevInput = "text"; - update(false); - } - else { - hints._usedTabKey = false; - hints._hintNumber = 0; - dactyl.beep(); - } - return Events.KILL; - }); + function () hints.hintSession.backspace()); mappings.add(modes.HINTS, [""], "Toggle hint filtering", function () { - hints.clearTimeout(); - hints.escNumbers = !hints.escNumbers; - if (hints.escNumbers && hints._usedTabKey) - hints._hintNumber = 0; - - hints._updateStatusline(); + hints.hintSession.escapeNumbers = !hints.hintSession.escapeNumbers; }); }, options: function () { diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 8c83a3c2..0d7f92c5 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -803,10 +803,23 @@ Class.prototype = { }; memoize(Class.prototype, "closure", function () { const self = this; - function closure(fn) function () fn.apply(self, arguments); - for (let k in iter(properties(this), properties(this, true))) + function closure(fn) function () { + try { + return fn.apply(self, arguments); + } + catch (e) { + util.reportError(e); + } + } + 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)) + Object.defineProperty(closure, k, { + get: function get_proxy() self[k], + set: function set_proxy(val) self[k] = val, + }); + }, this); return closure; });