1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 03:47:58 +01:00

Prepare for merge with testing branch.

This commit is contained in:
Kris Maglione
2010-08-28 15:47:45 -04:00
parent 0c6a7b178f
commit 5632e14721
25 changed files with 903 additions and 801 deletions

View File

@@ -83,7 +83,7 @@ const AutoCommands = Module("autocommands", {
} }
}); });
let list = template.commandOutput( let list = template.commandOutput(commandline.command,
<table> <table>
<tr highlight="Title"> <tr highlight="Title">
<td colspan="2">----- Auto Commands -----</td> <td colspan="2">----- Auto Commands -----</td>
@@ -257,8 +257,6 @@ const AutoCommands = Module("autocommands", {
}); });
}, },
completion: function () { completion: function () {
JavaScript.setCompleter(this.get, [function () Iterator(config.autocommands)]);
completion.autocmdEvent = function autocmdEvent(context) { completion.autocmdEvent = function autocmdEvent(context) {
context.completions = Iterator(config.autocommands); context.completions = Iterator(config.autocommands);
}; };
@@ -268,6 +266,9 @@ const AutoCommands = Module("autocommands", {
context.completions = [item for (item in events.getMacros())]; context.completions = [item for (item in events.getMacros())];
}; };
}, },
javascript: function () {
JavaScript.setCompleter(this.get, [function () Iterator(config.autocommands)]);
},
options: function () { options: function () {
options.add(["eventignore", "ei"], options.add(["eventignore", "ei"],
"List of autocommand event names which should be ignored", "List of autocommand event names which should be ignored",

View File

@@ -9,14 +9,6 @@ const Ci = Components.interfaces;
const Cr = Components.results; const Cr = Components.results;
const Cu = Components.utils; const Cu = Components.utils;
function array(obj) {
if (isgenerator(obj))
obj = [k for (k in obj)];
else if (obj.length)
obj = Array.slice(obj);
return util.Array(obj);
}
function allkeys(obj) { function allkeys(obj) {
let ret = {}; let ret = {};
try { try {
@@ -45,7 +37,7 @@ function allkeys(obj) {
} }
function debuggerProperties(obj) { function debuggerProperties(obj) {
if (modules.services && options["jsdebugger"]) { if (modules.services && services.get("debugger").isOn) {
let ret = {}; let ret = {};
services.get("debugger").wrapValue(obj).getProperties(ret, {}); services.get("debugger").wrapValue(obj).getProperties(ret, {});
return ret.value; return ret.value;
@@ -125,9 +117,9 @@ function iter(obj) {
} }
catch (e) {} catch (e) {}
})(); })();
if (isinstance(obj, [HTMLCollection, NodeList])) if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
return util.Array.iteritems(obj); return array.iteritems(obj);
if (obj instanceof NamedNodeMap) if (obj instanceof Ci.nsIDOMNamedNodeMap)
return (function () { return (function () {
for (let i = 0; i < obj.length; i++) for (let i = 0; i < obj.length; i++)
yield [obj.name, obj]; yield [obj.name, obj];
@@ -208,6 +200,13 @@ function call(fn) {
return fn; return fn;
} }
function memoize(obj, key, getter) {
obj.__defineGetter__(key, function () {
delete obj[key];
return obj[key] = getter(obj, key);
});
}
/** /**
* Curries a function to the given number of arguments. Each * Curries a function to the given number of arguments. Each
* call of the resulting function returns a new function. When * call of the resulting function returns a new function. When
@@ -312,13 +311,8 @@ function update(target) {
* @param {Object} overrides @optional * @param {Object} overrides @optional
*/ */
function extend(subclass, superclass, overrides) { function extend(subclass, superclass, overrides) {
subclass.prototype = {}; subclass.prototype = { __proto__: superclass.prototype };
update(subclass.prototype, overrides); update(subclass.prototype, overrides);
// This is unduly expensive. Unfortunately necessary since
// we apparently can't rely on the presence of the
// debugger to enumerate properties when we have
// __iterators__ attached to prototypes.
subclass.prototype.__proto__ = superclass.prototype;
subclass.superclass = superclass.prototype; subclass.superclass = superclass.prototype;
subclass.prototype.constructor = subclass; subclass.prototype.constructor = subclass;
@@ -421,8 +415,10 @@ Class.prototype = {
*/ */
setTimeout: function (callback, timeout) { setTimeout: function (callback, timeout) {
const self = this; const self = this;
function target() callback.call(self); let notify = { notify: function notify(timer) { callback.call(self) } };
return window.setTimeout(target, timeout); let timer = services.create("timer");
timer.initWithCallback(notify, timeout, timer.TYPE_ONE_SHOT);
return timer;
} }
}; };
@@ -493,4 +489,136 @@ for (let k in values(["concat", "every", "filter", "forEach", "indexOf", "join",
"map", "reduce", "reduceRight", "reverse", "slice", "some", "sort"])) "map", "reduce", "reduceRight", "reverse", "slice", "some", "sort"]))
StructBase.prototype[k] = Array.prototype[k]; StructBase.prototype[k] = Array.prototype[k];
/**
* Array utility methods.
*/
const array = Class("util.Array", Array, {
init: function (ary) {
if (isgenerator(ary))
ary = [k for (k in ary)];
else if (ary.length)
ary = Array.slice(ary);
return {
__proto__: ary,
__iterator__: function () this.iteritems(),
__noSuchMethod__: function (meth, args) {
var res = array[meth].apply(null, [this.__proto__].concat(args));
if (array.isinstance(res))
return array(res);
return res;
},
toString: function () this.__proto__.toString(),
concat: function () this.__proto__.concat.apply(this.__proto__, arguments),
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
};
}
}, {
isinstance: function isinstance(obj) {
return Object.prototype.toString.call(obj) == "[object Array]";
},
/**
* Converts an array to an object. As in lisp, an assoc is an
* array of key-value pairs, which maps directly to an object,
* as such:
* [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
*
* @param {Array[]} assoc
* @... {string} 0 - Key
* @... 1 - Value
*/
toObject: function toObject(assoc) {
let obj = {};
assoc.forEach(function ([k, v]) { obj[k] = v; });
return obj;
},
/**
* Compacts an array, removing all elements that are null or undefined:
* ["foo", null, "bar", undefined] -> ["foo", "bar"]
*
* @param {Array} ary
* @returns {Array}
*/
compact: function compact(ary) ary.filter(function (item) item != null),
/**
* Flattens an array, such that all elements of the array are
* joined into a single array:
* [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
*
* @param {Array} ary
* @returns {Array}
*/
flatten: function flatten(ary) ary.length ? Array.concat.apply([], ary) : [],
/**
* Returns an Iterator for an array's values.
*
* @param {Array} ary
* @returns {Iterator(Object)}
*/
itervalues: function itervalues(ary) {
let length = ary.length;
for (let i = 0; i < length; i++)
yield ary[i];
},
/**
* Returns an Iterator for an array's indices and values.
*
* @param {Array} ary
* @returns {Iterator([{number}, {Object}])}
*/
iteritems: function iteritems(ary) {
let length = ary.length;
for (let i = 0; i < length; i++)
yield [i, ary[i]];
},
/**
* Filters out all duplicates from an array. If
* <b>unsorted</b> is false, the array is sorted before
* duplicates are removed.
*
* @param {Array} ary
* @param {boolean} unsorted
* @returns {Array}
*/
uniq: function uniq(ary, unsorted) {
let ret = [];
if (unsorted) {
for (let [, item] in Iterator(ary))
if (ret.indexOf(item) == -1)
ret.push(item);
}
else {
for (let [, item] in Iterator(ary.sort())) {
if (item != last || !ret.length)
ret.push(item);
var last = item;
}
}
return ret;
},
/**
* Zips the contents of two arrays. The resulting array is twice the
* length of ary1, with any shortcomings of ary2 replaced with null
* strings.
*
* @param {Array} ary1
* @param {Array} ary2
* @returns {Array}
*/
zip: function zip(ary1, ary2) {
let res = []
for(let [i, item] in Iterator(ary1))
res.push(item, i in ary2 ? ary2[i] : "");
return res;
}
});
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -464,7 +464,7 @@ const Bookmarks = Module("bookmarks", {
args.completeFilter = have.pop(); args.completeFilter = have.pop();
let prefix = filter.substr(0, filter.length - args.completeFilter.length); let prefix = filter.substr(0, filter.length - args.completeFilter.length);
let tags = util.Array.uniq(util.Array.flatten([b.tags for ([k, b] in Iterator(this._cache.bookmarks))])); let tags = array.uniq(util.Array.flatten([b.tags for ([k, b] in Iterator(this._cache.bookmarks))]));
return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag) < 0)]; return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag) < 0)];
} }

View File

@@ -1003,7 +1003,7 @@ const Buffer = Module("buffer", {
let values = ZoomManager.zoomValues; let values = ZoomManager.zoomValues;
let cur = values.indexOf(ZoomManager.snap(ZoomManager.zoom)); let cur = values.indexOf(ZoomManager.snap(ZoomManager.zoom));
let i = util.Math.constrain(cur + steps, 0, values.length - 1); let i = Math.constrain(cur + steps, 0, values.length - 1);
if (i == cur && fullZoom == ZoomManager.useFullZoom) if (i == cur && fullZoom == ZoomManager.useFullZoom)
dactyl.beep(); dactyl.beep();
@@ -1286,7 +1286,7 @@ const Buffer = Module("buffer", {
level = buffer.textZoom + parseInt(arg, 10); level = buffer.textZoom + parseInt(arg, 10);
// relative args shouldn't take us out of range // relative args shouldn't take us out of range
level = util.Math.constrain(level, Buffer.ZOOM_MIN, Buffer.ZOOM_MAX); level = Math.constrain(level, Buffer.ZOOM_MIN, Buffer.ZOOM_MAX);
} }
else else
dactyl.assert(false, "E488: Trailing characters"); dactyl.assert(false, "E488: Trailing characters");
@@ -1519,14 +1519,14 @@ const Buffer = Module("buffer", {
let xpath = ["input", "textarea[not(@disabled) and not(@readonly)]"]; let xpath = ["input", "textarea[not(@disabled) and not(@readonly)]"];
let elements = [m for (m in util.evaluateXPath(xpath))].filter(function (elem) { let elements = [m for (m in util.evaluateXPath(xpath))].filter(function (elem) {
if (elem.readOnly || elem instanceof HTMLInputElement && set.has(Events.editableInputs, elem.type)) if (elem.readOnly || elem instanceof HTMLInputElement && !set.has(Events.editableInputs, elem.type))
return false; return false;
let computedStyle = util.computedStyle(elem); let computedStyle = util.computedStyle(elem);
return computedStyle.visibility != "hidden" && computedStyle.display != "none"; return computedStyle.visibility != "hidden" && computedStyle.display != "none";
}); });
dactyl.assert(elements.length > 0); dactyl.assert(elements.length > 0);
let elem = elements[util.Math.constrain(count, 1, elements.length) - 1]; let elem = elements[Math.constrain(count, 1, elements.length) - 1];
buffer.focusElement(elem); buffer.focusElement(elem);
util.scrollIntoView(elem); util.scrollIntoView(elem);
} }

View File

@@ -131,26 +131,24 @@ const CommandLine = Module("commandline", {
this._startHints = false; // whether we're waiting to start hints mode this._startHints = false; // whether we're waiting to start hints mode
this._lastSubstring = ""; this._lastSubstring = "";
// the containing box for the this._promptWidget and this._commandWidget this.widgets = {
this._commandlineWidget = document.getElementById("dactyl-commandline"); commandline: document.getElementById("dactyl-commandline"),
// the prompt for the current command, for example : or /. Can be blank prompt: document.getElementById("dactyl-commandline-prompt"),
this._promptWidget = document.getElementById("dactyl-commandline-prompt"); command: document.getElementById("dactyl-commandline-command"),
// the command bar which contains the current command
this._commandWidget = document.getElementById("dactyl-commandline-command");
this._messageBox = document.getElementById("dactyl-message"); message: document.getElementById("dactyl-message"),
this._commandWidget.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); multilineOutput: document.getElementById("dactyl-multiline-output"),
this._messageBox.inputField.QueryInterface(Ci.nsIDOMNSEditableElement); multilineInput: document.getElementById("dactyl-multiline-input"),
};
this.widgets.command.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
this.widgets.message.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
// the widget used for multiline output // the widget used for multiline output
this._multilineOutputWidget = document.getElementById("dactyl-multiline-output"); this._outputContainer = this.widgets.multilineOutput.parentNode;
this._outputContainer = this._multilineOutputWidget.parentNode;
this._multilineOutputWidget.contentDocument.body.id = "dactyl-multiline-output-content"; this.widgets.multilineOutput.contentDocument.body.id = "dactyl-multiline-output-content";
// the widget used for multiline intput
this._multilineInputWidget = document.getElementById("dactyl-multiline-input");
// we need to save the mode which were in before opening the command line // 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" // this is then used if we focus the command line again without the "official"
@@ -208,7 +206,7 @@ const CommandLine = Module("commandline", {
* Highlight the messageBox according to <b>group</b>. * Highlight the messageBox according to <b>group</b>.
*/ */
_setHighlightGroup: function (group) { _setHighlightGroup: function (group) {
this._messageBox.setAttributeNS(NS.uri, "highlight", group); this.widgets.message.setAttributeNS(NS.uri, "highlight", group);
}, },
/** /**
@@ -226,10 +224,10 @@ const CommandLine = Module("commandline", {
* @param {string} highlightGroup * @param {string} highlightGroup
*/ */
_setPrompt: function (val, highlightGroup) { _setPrompt: function (val, highlightGroup) {
this._promptWidget.value = val; this.widgets.prompt.value = val;
this._promptWidget.size = val.length; this.widgets.prompt.size = val.length;
this._promptWidget.collapsed = (val == ""); this.widgets.prompt.collapsed = (val == "");
this._promptWidget.setAttributeNS(NS.uri, "highlight", highlightGroup || commandline.HL_NORMAL); this.widgets.prompt.setAttributeNS(NS.uri, "highlight", highlightGroup || commandline.HL_NORMAL);
}, },
/** /**
@@ -239,9 +237,9 @@ const CommandLine = Module("commandline", {
* @param {string} cmd * @param {string} cmd
*/ */
_setCommand: function (cmd) { _setCommand: function (cmd) {
this._commandWidget.value = cmd; this.widgets.command.value = cmd;
this._commandWidget.selectionStart = cmd.length; this.widgets.command.selectionStart = cmd.length;
this._commandWidget.selectionEnd = cmd.length; this.widgets.command.selectionEnd = cmd.length;
}, },
/** /**
@@ -254,14 +252,14 @@ const CommandLine = Module("commandline", {
*/ */
_echoLine: function (str, highlightGroup, forceSingle) { _echoLine: function (str, highlightGroup, forceSingle) {
this._setHighlightGroup(highlightGroup); this._setHighlightGroup(highlightGroup);
this._messageBox.value = str; this.widgets.message.value = str;
dactyl.triggerObserver("echoLine", str, highlightGroup, forceSingle); dactyl.triggerObserver("echoLine", str, highlightGroup, forceSingle);
if (!this._commandShown()) if (!this._commandShown())
commandline.hide(); commandline.hide();
let field = this._messageBox.inputField; let field = this.widgets.message.inputField;
if (!forceSingle && field.editor.rootElement.scrollWidth > field.scrollWidth) if (!forceSingle && field.editor.rootElement.scrollWidth > field.scrollWidth)
this._echoMultiline(<span highlight="Message">{str}</span>, highlightGroup); this._echoMultiline(<span highlight="Message">{str}</span>, highlightGroup);
}, },
@@ -274,8 +272,8 @@ const CommandLine = Module("commandline", {
*/ */
// TODO: resize upon a window resize // TODO: resize upon a window resize
_echoMultiline: function (str, highlightGroup) { _echoMultiline: function (str, highlightGroup) {
let doc = this._multilineOutputWidget.contentDocument; let doc = this.widgets.multilineOutput.contentDocument;
let win = this._multilineOutputWidget.contentWindow; let win = this.widgets.multilineOutput.contentWindow;
dactyl.triggerObserver("echoMultiline", str, highlightGroup); dactyl.triggerObserver("echoMultiline", str, highlightGroup);
@@ -316,9 +314,9 @@ const CommandLine = Module("commandline", {
* Ensure that the multiline input widget is the correct size. * Ensure that the multiline input widget is the correct size.
*/ */
_autosizeMultilineInputWidget: function () { _autosizeMultilineInputWidget: function () {
let lines = this._multilineInputWidget.value.split("\n").length - 1; let lines = this.widgets.multilineInput.value.split("\n").length - 1;
this._multilineInputWidget.setAttribute("rows", Math.max(lines, 1)); this.widgets.multilineInput.setAttribute("rows", Math.max(lines, 1));
}, },
HL_NORMAL: "Normal", HL_NORMAL: "Normal",
@@ -386,15 +384,15 @@ const CommandLine = Module("commandline", {
try { try {
// The long path is because of complications with the // The long path is because of complications with the
// completion preview. // completion preview.
return this._commandWidget.inputField.editor.rootElement.firstChild.textContent; return this.widgets.command.inputField.editor.rootElement.firstChild.textContent;
} }
catch (e) { catch (e) {
return this._commandWidget.value; return this.widgets.command.value;
} }
}, },
set command(cmd) this._commandWidget.value = cmd, set command(cmd) this.widgets.command.value = cmd,
get message() this._messageBox.value, get message() this.widgets.message.value,
/** /**
* Open the command line. The main mode is set to * Open the command line. The main mode is set to
@@ -416,14 +414,14 @@ const CommandLine = Module("commandline", {
this._setPrompt(this._currentPrompt); this._setPrompt(this._currentPrompt);
this._setCommand(this._currentCommand); this._setCommand(this._currentCommand);
this._commandlineWidget.collapsed = false; this.widgets.commandline.collapsed = false;
modes.set(modes.COMMAND_LINE, this._currentExtendedMode); modes.set(modes.COMMAND_LINE, this._currentExtendedMode);
this._commandWidget.focus(); this.widgets.command.focus();
this._history = CommandLine.History(this._commandWidget.inputField, (modes.extended == modes.EX) ? "command" : "search"); this._history = CommandLine.History(this.widgets.command.inputField, (modes.extended == modes.EX) ? "command" : "search");
this._completions = CommandLine.Completions(this._commandWidget.inputField); this._completions = CommandLine.Completions(this.widgets.command.inputField);
// open the completion list automatically if wanted // open the completion list automatically if wanted
if (cmd.length) if (cmd.length)
@@ -450,7 +448,7 @@ const CommandLine = Module("commandline", {
statusline.updateProgress(""); // we may have a "match x of y" visible statusline.updateProgress(""); // we may have a "match x of y" visible
dactyl.focusContent(false); dactyl.focusContent(false);
this._multilineInputWidget.collapsed = true; this.widgets.multilineInput.collapsed = true;
this._completionList.hide(); this._completionList.hide();
if (!this._keepCommand || this._silent || this._quiet) { if (!this._keepCommand || this._silent || this._quiet) {
@@ -470,7 +468,7 @@ const CommandLine = Module("commandline", {
* are under it. * are under it.
*/ */
hide: function hide() { hide: function hide() {
this._commandlineWidget.collapsed = true; this.widgets.commandline.collapsed = true;
}, },
/** /**
@@ -509,7 +507,7 @@ const CommandLine = Module("commandline", {
return; return;
// The DOM isn't threadsafe. It must only be accessed from the main thread. // The DOM isn't threadsafe. It must only be accessed from the main thread.
dactyl.callInMainThread(function () { util.callInMainThread(function () {
if ((flags & this.DISALLOW_MULTILINE) && !this._outputContainer.collapsed) if ((flags & this.DISALLOW_MULTILINE) && !this._outputContainer.collapsed)
return; return;
@@ -518,7 +516,7 @@ const CommandLine = Module("commandline", {
// TODO: this is all a bit convoluted - clean up. // TODO: this is all a bit convoluted - clean up.
// assume that FORCE_MULTILINE output is fully styled // assume that FORCE_MULTILINE output is fully styled
if (!(flags & this.FORCE_MULTILINE) && !single && (!this._outputContainer.collapsed || this._messageBox.value == this._lastEcho)) { if (!(flags & this.FORCE_MULTILINE) && !single && (!this._outputContainer.collapsed || this.widgets.message.value == this._lastEcho)) {
highlightGroup += " Message"; highlightGroup += " Message";
action = this._echoMultiline; action = this._echoMultiline;
} }
@@ -529,9 +527,9 @@ const CommandLine = Module("commandline", {
if (single) if (single)
this._lastEcho = null; this._lastEcho = null;
else { else {
if (this._messageBox.value == this._lastEcho) if (this.widgets.message.value == this._lastEcho)
this._echoMultiline(<span highlight="Message">{this._lastEcho}</span>, this._echoMultiline(<span highlight="Message">{this._lastEcho}</span>,
this._messageBox.getAttributeNS(NS.uri, "highlight")); this.widgets.message.getAttributeNS(NS.uri, "highlight"));
this._lastEcho = (action == this._echoLine) && str; this._lastEcho = (action == this._echoLine) && str;
} }
@@ -571,10 +569,10 @@ const CommandLine = Module("commandline", {
this._setPrompt(prompt, extra.promptHighlight || this.HL_QUESTION); this._setPrompt(prompt, extra.promptHighlight || this.HL_QUESTION);
this._setCommand(extra.default || ""); this._setCommand(extra.default || "");
this._commandlineWidget.collapsed = false; this.widgets.commandline.collapsed = false;
this._commandWidget.focus(); this.widgets.command.focus();
this._completions = CommandLine.Completions(this._commandWidget.inputField); this._completions = CommandLine.Completions(this.widgets.command.inputField);
}, },
/** /**
@@ -588,7 +586,7 @@ const CommandLine = Module("commandline", {
// FIXME: Buggy, especially when pasting. Shouldn't use a RegExp. // FIXME: Buggy, especially when pasting. Shouldn't use a RegExp.
inputMultiline: function inputMultiline(untilRegexp, callbackFunc) { inputMultiline: function inputMultiline(untilRegexp, callbackFunc) {
// Kludge. // Kludge.
let cmd = !this._commandWidget.collapsed && this.command; let cmd = !this.widgets.command.collapsed && this.command;
modes.push(modes.COMMAND_LINE, modes.INPUT_MULTILINE); modes.push(modes.COMMAND_LINE, modes.INPUT_MULTILINE);
if (cmd != false) if (cmd != false)
this._echoLine(cmd, this.HL_NORMAL); this._echoLine(cmd, this.HL_NORMAL);
@@ -597,11 +595,11 @@ const CommandLine = Module("commandline", {
this._multilineRegexp = untilRegexp; this._multilineRegexp = untilRegexp;
this._multilineCallback = callbackFunc; this._multilineCallback = callbackFunc;
this._multilineInputWidget.collapsed = false; this.widgets.multilineInput.collapsed = false;
this._multilineInputWidget.value = ""; this.widgets.multilineInput.value = "";
this._autosizeMultilineInputWidget(); this._autosizeMultilineInputWidget();
this.setTimeout(function () { this._multilineInputWidget.focus(); }, 10); this.setTimeout(function () { this.widgets.multilineInput.focus(); }, 10);
}, },
/** /**
@@ -619,12 +617,12 @@ const CommandLine = Module("commandline", {
if (event.type == "blur") { if (event.type == "blur") {
// prevent losing focus, there should be a better way, but it just didn't work otherwise // prevent losing focus, there should be a better way, but it just didn't work otherwise
this.setTimeout(function () { this.setTimeout(function () {
if (this._commandShown() && event.originalTarget == this._commandWidget.inputField) if (this._commandShown() && event.originalTarget == this.widgets.command.inputField)
this._commandWidget.inputField.focus(); this.widgets.command.inputField.focus();
}, 0); }, 0);
} }
else if (event.type == "focus") { else if (event.type == "focus") {
if (!this._commandShown() && event.target == this._commandWidget.inputField) { if (!this._commandShown() && event.target == this.widgets.command.inputField) {
event.target.blur(); event.target.blur();
dactyl.beep(); dactyl.beep();
} }
@@ -703,22 +701,22 @@ const CommandLine = Module("commandline", {
if (event.type == "keypress") { if (event.type == "keypress") {
let key = events.toString(event); let key = events.toString(event);
if (events.isAcceptKey(key)) { if (events.isAcceptKey(key)) {
let text = this._multilineInputWidget.value.substr(0, this._multilineInputWidget.selectionStart); let text = this.widgets.multilineInput.value.substr(0, this.widgets.multilineInput.selectionStart);
if (text.match(this._multilineRegexp)) { if (text.match(this._multilineRegexp)) {
text = text.replace(this._multilineRegexp, ""); text = text.replace(this._multilineRegexp, "");
modes.pop(); modes.pop();
this._multilineInputWidget.collapsed = true; this.widgets.multilineInput.collapsed = true;
this._multilineCallback.call(this, text); this._multilineCallback.call(this, text);
} }
} }
else if (events.isCancelKey(key)) { else if (events.isCancelKey(key)) {
modes.pop(); modes.pop();
this._multilineInputWidget.collapsed = true; this.widgets.multilineInput.collapsed = true;
} }
} }
else if (event.type == "blur") { else if (event.type == "blur") {
if (modes.extended & modes.INPUT_MULTILINE) if (modes.extended & modes.INPUT_MULTILINE)
this.setTimeout(function () { this._multilineInputWidget.inputField.focus(); }, 0); this.setTimeout(function () { this.widgets.multilineInput.inputField.focus(); }, 0);
} }
else if (event.type == "input") else if (event.type == "input")
this._autosizeMultilineInputWidget(); this._autosizeMultilineInputWidget();
@@ -735,7 +733,7 @@ const CommandLine = Module("commandline", {
// FIXME: if 'more' is set and the MOW is not scrollable we should still // FIXME: if 'more' is set and the MOW is not scrollable we should still
// allow a down motion after an up rather than closing // allow a down motion after an up rather than closing
onMultilineOutputEvent: function onMultilineOutputEvent(event) { onMultilineOutputEvent: function onMultilineOutputEvent(event) {
let win = this._multilineOutputWidget.contentWindow; let win = this.widgets.multilineOutput.contentWindow;
let showMoreHelpPrompt = false; let showMoreHelpPrompt = false;
let showMorePrompt = false; let showMorePrompt = false;
@@ -934,7 +932,7 @@ const CommandLine = Module("commandline", {
}, },
getSpaceNeeded: function getSpaceNeeded() { getSpaceNeeded: function getSpaceNeeded() {
let rect = this._commandlineWidget.getBoundingClientRect(); let rect = this.widgets.commandline.getBoundingClientRect();
let offset = rect.bottom - window.innerHeight; let offset = rect.bottom - window.innerHeight;
return Math.max(0, offset); return Math.max(0, offset);
}, },
@@ -953,7 +951,7 @@ const CommandLine = Module("commandline", {
return; return;
} }
let win = this._multilineOutputWidget.contentWindow; let win = this.widgets.multilineOutput.contentWindow;
function isScrollable() !win.scrollMaxY == 0; function isScrollable() !win.scrollMaxY == 0;
function atEnd() win.scrollY / win.scrollMaxY >= 1; function atEnd() win.scrollY / win.scrollMaxY >= 1;
@@ -975,12 +973,12 @@ const CommandLine = Module("commandline", {
if (!open && this._outputContainer.collapsed) if (!open && this._outputContainer.collapsed)
return; return;
let doc = this._multilineOutputWidget.contentDocument; let doc = this.widgets.multilineOutput.contentDocument;
let availableHeight = config.outputHeight; let availableHeight = config.outputHeight;
if (!this._outputContainer.collapsed) if (!this._outputContainer.collapsed)
availableHeight += parseFloat(this._outputContainer.height); availableHeight += parseFloat(this._outputContainer.height);
doc.body.style.minWidth = this._commandlineWidget.scrollWidth + "px"; doc.body.style.minWidth = this.widgets.commandline.scrollWidth + "px";
this._outputContainer.height = Math.min(doc.height, availableHeight) + "px"; this._outputContainer.height = Math.min(doc.height, availableHeight) + "px";
doc.body.style.minWidth = ""; doc.body.style.minWidth = "";
this._outputContainer.collapsed = false; this._outputContainer.collapsed = false;
@@ -1086,7 +1084,7 @@ const CommandLine = Module("commandline", {
while (true) { while (true) {
this.index += diff; this.index += diff;
if (this.index < 0 || this.index > this.store.length) { if (this.index < 0 || this.index > this.store.length) {
this.index = util.Math.constrain(this.index, 0, this.store.length); this.index = Math.constrain(this.index, 0, this.store.length);
dactyl.beep(); dactyl.beep();
// I don't know why this kludge is needed. It // I don't know why this kludge is needed. It
// prevents the caret from moving to the end of // prevents the caret from moving to the end of
@@ -1146,8 +1144,8 @@ const CommandLine = Module("commandline", {
// Change the completion text. // Change the completion text.
// The second line is a hack to deal with some substring // The second line is a hack to deal with some substring
// preview corner cases. // preview corner cases.
commandline._commandWidget.value = this.prefix + completion + this.suffix; commandline.widgets.command.value = this.prefix + completion + this.suffix;
this.editor.selection.focusNode.textContent = commandline._commandWidget.value; this.editor.selection.focusNode.textContent = commandline.widgets.command.value;
// Reset the caret to one position after the completion. // Reset the caret to one position after the completion.
this.caret = this.prefix.length + completion.length; this.caret = this.prefix.length + completion.length;
@@ -1155,8 +1153,8 @@ const CommandLine = Module("commandline", {
get caret() this.editor.selection.focusOffset, get caret() this.editor.selection.focusOffset,
set caret(offset) { set caret(offset) {
commandline._commandWidget.selectionStart = offset; commandline.widgets.command.selectionStart = offset;
commandline._commandWidget.selectionEnd = offset; commandline.widgets.command.selectionEnd = offset;
}, },
get start() this.context.allItems.start, get start() this.context.allItems.start,
@@ -1236,9 +1234,9 @@ const CommandLine = Module("commandline", {
} }
else if (this.removeSubstring) { else if (this.removeSubstring) {
let str = this.removeSubstring; let str = this.removeSubstring;
let cmd = commandline._commandWidget.value; let cmd = commandline.widgets.command.value;
if (cmd.substr(cmd.length - str.length) == str) if (cmd.substr(cmd.length - str.length) == str)
commandline._commandWidget.value = cmd.substr(0, cmd.length - str.length); commandline.widgets.command.value = cmd.substr(0, cmd.length - str.length);
} }
delete this.removeSubstring; delete this.removeSubstring;
}, },
@@ -1289,7 +1287,7 @@ const CommandLine = Module("commandline", {
idx = null; idx = null;
break; break;
default: default:
idx = util.Math.constrain(idx, 0, this.items.length - 1); idx = Math.constrain(idx, 0, this.items.length - 1);
break; break;
} }
@@ -1310,7 +1308,7 @@ const CommandLine = Module("commandline", {
for (let [, context] in Iterator(list)) { for (let [, context] in Iterator(list)) {
function done() !(idx >= n + context.items.length || idx == -2 && !context.items.length); function done() !(idx >= n + context.items.length || idx == -2 && !context.items.length);
while (context.incomplete && !done()) while (context.incomplete && !done())
dactyl.threadYield(false, true); util.threadYield(false, true);
if (done()) if (done())
break; break;
@@ -1369,7 +1367,7 @@ const CommandLine = Module("commandline", {
if (this.type.list) if (this.type.list)
this.itemList.show(); this.itemList.show();
this.wildIndex = util.Math.constrain(this.wildIndex + 1, 0, this.wildtypes.length - 1); this.wildIndex = Math.constrain(this.wildIndex + 1, 0, this.wildtypes.length - 1);
this.preview(); this.preview();
commandline._statusTimer.tell(); commandline._statusTimer.tell();
@@ -1651,7 +1649,7 @@ const ItemList = Class("ItemList", {
let off = 0; let off = 0;
let end = this._startIndex + options["maxitems"]; let end = this._startIndex + options["maxitems"];
function getRows(context) { function getRows(context) {
function fix(n) util.Math.constrain(n, 0, len); function fix(n) Math.constrain(n, 0, len);
let len = context.items.length; let len = context.items.length;
let start = off; let start = off;
end -= !!context.message + context.incomplete; end -= !!context.message + context.incomplete;

View File

@@ -858,23 +858,7 @@ const Commands = Module("commands", {
return [len - str.length, arg, quote]; return [len - str.length, arg, quote];
} }
}, { }, {
mappings: function () {
mappings.add(config.browserModes,
["@:"], "Repeat the last Ex command",
function (count) {
if (commands.repeat) {
for (let i in util.interruptibleRange(0, Math.max(count, 1), 100))
dactyl.execute(commands.repeat);
}
else
dactyl.echoerr("E30: No previous command line");
},
{ count: true });
},
completion: function () { completion: function () {
JavaScript.setCompleter(this.get, [function () ([c.name, c.description] for (c in commands))]);
completion.command = function command(context) { completion.command = function command(context) {
context.title = ["Command"]; context.title = ["Command"];
context.keys = { text: "longNames", description: "description" }; context.keys = { text: "longNames", description: "description" };
@@ -1105,6 +1089,22 @@ const Commands = Module("commands", {
argCount: "1", argCount: "1",
completer: function (context) completion.userCommand(context) completer: function (context) completion.userCommand(context)
}); });
},
javascript: function () {
JavaScript.setCompleter(this.get, [function () ([c.name, c.description] for (c in commands))]);
},
mappings: function () {
mappings.add(config.browserModes,
["@:"], "Repeat the last Ex command",
function (count) {
if (commands.repeat) {
for (let i in util.interruptibleRange(0, Math.max(count, 1), 100))
dactyl.execute(commands.repeat);
}
else
dactyl.echoerr("E30: No previous command line");
},
{ count: true });
} }
}); });

View File

@@ -260,7 +260,7 @@ const CompletionContext = Class("CompletionContext", {
this._completions = items; this._completions = items;
let self = this; let self = this;
if (this.updateAsync && !this.noUpdate) if (this.updateAsync && !this.noUpdate)
dactyl.callInMainThread(function () { self.onUpdate.call(self); }); util.callInMainThread(function () { self.onUpdate.call(self); });
}, },
get createRow() this._createRow || template.completionRow, // XXX get createRow() this._createRow || template.completionRow, // XXX
@@ -326,7 +326,7 @@ const CompletionContext = Class("CompletionContext", {
this.cache.backgroundLock = lock; this.cache.backgroundLock = lock;
this.incomplete = true; this.incomplete = true;
let thread = this.getCache("backgroundThread", dactyl.newThread); let thread = this.getCache("backgroundThread", dactyl.newThread);
dactyl.callAsync(thread, this, function () { util.callAsync(thread, this, function () {
if (this.cache.backgroundLock != lock) if (this.cache.backgroundLock != lock)
return; return;
let items = this.generate(); let items = this.generate();
@@ -609,7 +609,7 @@ const CompletionContext = Class("CompletionContext", {
wait: function wait(interruptable, timeout) { wait: function wait(interruptable, timeout) {
let end = Date.now() + timeout; let end = Date.now() + timeout;
while (this.incomplete && (!timeout || Date.now() > end)) while (this.incomplete && (!timeout || Date.now() > end))
dactyl.threadYield(false, interruptable); util.threadYield(false, interruptable);
return this.incomplete; return this.incomplete;
} }
}, { }, {
@@ -668,7 +668,7 @@ const Completion = Module("completion", {
context = context.contexts["/list"]; context = context.contexts["/list"];
context.wait(); context.wait();
let list = template.commandOutput( let list = template.commandOutput(commandline.command,
<div highlight="Completions"> <div highlight="Completions">
{ template.map(context.contextList.filter(function (c) c.hasItems), { template.map(context.contextList.filter(function (c) c.hasItems),
function (context) function (context)
@@ -765,7 +765,7 @@ const Completion = Module("completion", {
commands.add(["contexts"], commands.add(["contexts"],
"List the completion contexts used during completion of an ex command", "List the completion contexts used during completion of an ex command",
function (args) { function (args) {
commandline.echo(template.commandOutput( commandline.echo(template.commandOutput(commandline.command,
<div highlight="Completions"> <div highlight="Completions">
{ template.completionRow(["Context", "Title"], "CompTitle") } { template.completionRow(["Context", "Title"], "CompTitle") }
{ template.map(completion.contextList || [], function (item) template.completionRow(item, "CompItem")) } { template.map(completion.contextList || [], function (item) template.completionRow(item, "CompItem")) }

View File

@@ -7,6 +7,15 @@
"use strict"; "use strict";
const ConfigBase = Class(ModuleBase, { const ConfigBase = Class(ModuleBase, {
/**
* Called on dactyl startup to allow for any arbitrary application-specific
* initialization code. Must call superclass's init function.
*/
init: function () {
highlight.styleableChrome = this.styleableChrome;
highlight.loadCSS(this.CSS);
},
/** /**
* @property {[["string", "string"]]} A sequence of names and descriptions * @property {[["string", "string"]]} A sequence of names and descriptions
* of the autocommands available in this application. Primarily used * of the autocommands available in this application. Primarily used
@@ -14,8 +23,8 @@ const ConfigBase = Class(ModuleBase, {
*/ */
autocommands: [], autocommands: [],
browser: window.gBrowser, get browser() window.gBrowser,
tabbrowser: window.gBrowser, get tabbrowser() window.gBrowser,
get browserModes() [modes.NORMAL], get browserModes() [modes.NORMAL],
@@ -55,12 +64,6 @@ const ConfigBase = Class(ModuleBase, {
*/ */
hostApplication: null, hostApplication: null,
/**
* @property {function} Called on dactyl startup to allow for any
* arbitrary application-specific initialization code.
*/
init: function () {},
/** /**
* @property {Object} A map between key names for key events should be ignored, * @property {Object} A map between key names for key events should be ignored,
* and a mask of the modes in which they should be ignored. * and a mask of the modes in which they should be ignored.
@@ -102,8 +105,229 @@ const ConfigBase = Class(ModuleBase, {
* @property {string} The leaf name of any temp files created by * @property {string} The leaf name of any temp files created by
* {@link io.createTempFile}. * {@link io.createTempFile}.
*/ */
get tempFile() this.name.toLowerCase() + ".tmp" get tempFile() this.name.toLowerCase() + ".tmp",
/**
* @constant
* @property {string} The default highlighting rules. They have the
* form:
* rule ::= selector space space+ css
* selector ::= group
* | group "," css-selector
* | group "," css-selector "," scope
* group ::= groupname
* | groupname css-selector
*/
// <css>
CSS: <![CDATA[
Boolean color: red;
Function color: navy;
Null color: blue;
Number color: blue;
Object color: maroon;
String color: green;
Key font-weight: bold;
Enabled color: blue;
Disabled color: red;
Normal color: black; background: white;
ErrorMsg color: white; background: red; font-weight: bold;
InfoMsg color: black; background: white;
ModeMsg color: black; background: white;
MoreMsg color: green; background: white;
WarningMsg color: red; background: white;
Message white-space: normal; min-width: 100%; padding-left: 2em; text-indent: -2em; display: block;
NonText color: blue; min-height: 16px; padding-left: 2px;
Preview color: gray;
CmdLine,>* font-family: monospace; padding: 1px;
CmdOutput white-space: pre;
CompGroup
CompGroup:not(:first-of-type) margin-top: .5em;
CompTitle color: magenta; background: white; font-weight: bold;
CompTitle>* padding: 0 .5ex;
CompMsg font-style: italic; margin-left: 16px;
CompItem
CompItem[selected] background: yellow;
CompItem>* padding: 0 .5ex;
CompIcon width: 16px; min-width: 16px; display: inline-block; margin-right: .5ex;
CompIcon>img max-width: 16px; max-height: 16px; vertical-align: middle;
CompResult width: 45%; overflow: hidden;
CompDesc color: gray; width: 50%;
CompLess text-align: center; height: 0; line-height: .5ex; padding-top: 1ex;
CompLess::after content: "\2303" /* Unicode up arrowhead */
CompMore text-align: center; height: .5ex; line-height: .5ex; margin-bottom: -.5ex;
CompMore::after content: "\2304" /* Unicode down arrowhead */
Gradient height: 1px; margin-bottom: -1px; margin-top: -1px;
GradientLeft background-color: magenta;
GradientRight background-color: white;
Indicator color: blue;
Filter font-weight: bold;
Keyword color: red;
Tag color: blue;
LineNr color: orange; background: white;
Question color: green; background: white; font-weight: bold;
StatusLine color: white; background: black;
StatusLineBroken color: black; background: #FFa0a0 /* light-red */
StatusLineSecure color: black; background: #a0a0FF /* light-blue */
StatusLineExtended color: black; background: #a0FFa0 /* light-green */
TabClose,.tab-close-button
TabIcon,.tab-icon
TabText,.tab-text
TabNumber font-weight: bold; margin: 0px; padding-right: .3ex;
TabIconNumber {
font-weight: bold;
color: white;
text-align: center;
text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px;
}
Title color: magenta; background: white; font-weight: bold;
URL text-decoration: none; color: green; background: inherit;
URL:hover text-decoration: underline; cursor: pointer;
FrameIndicator,,* {
background-color: red;
opacity: 0.5;
z-index: 999;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
Bell border: none; background-color: black;
Hint,,* {
font-family: monospace;
font-size: 10px;
font-weight: bold;
color: white;
background-color: red;
border-color: ButtonShadow;
border-width: 0px;
border-style: solid;
padding: 0px 1px 0px 1px;
}
Hint::after,,* content: attr(number);
HintElem,,* background-color: yellow; color: black;
HintActive,,* background-color: #88FF00; color: black;
HintImage,,* opacity: .5;
Help font-size: 8pt; line-height: 1.4em; font-family: -moz-fixed;
HelpArg color: #6A97D4;
HelpOptionalArg color: #6A97D4;
HelpBody display: block; margin: 1em auto; max-width: 100ex;
HelpBorder,*,dactyl://help/* border-color: silver; border-width: 0px; border-style: solid;
HelpCode display: block; white-space: pre; margin-left: 2em; font-family: Terminus, Fixed, monospace;
HelpDefault margin-right: 1ex; white-space: pre;
HelpDescription display: block;
HelpEm,html|em,dactyl://help/* font-weight: bold; font-style: normal;
HelpEx display: inline-block; color: #527BBD; font-weight: bold;
HelpExample display: block; margin: 1em 0;
HelpExample::before content: "Example: "; font-weight: bold;
HelpInfo display: block; width: 20em; margin-left: auto;
HelpInfoLabel display: inline-block; width: 6em; color: magenta; font-weight: bold; vertical-align: text-top;
HelpInfoValue display: inline-block; width: 14em; text-decoration: none; vertical-align: text-top;
HelpItem display: block; margin: 1em 1em 1em 10em; clear: both;
HelpKey color: #102663;
HelpLink,html|a,dactyl://help/* text-decoration: none;
HelpLink[href]:hover text-decoration: underline;
HelpList,html|ul,dactyl://help/* display: block; list-style: outside disc;
HelpOrderedList,html|ol,dactyl://help/* display: block; list-style: outside decimal;
HelpListItem,html|li,dactyl://help/* display: list-item;
HelpNote color: red; font-weight: bold;
HelpOpt color: #106326;
HelpOptInfo display: inline-block; margin-bottom: 1ex;
HelpParagraph,html|p,dactyl://help/* display: block; margin: 1em 0em;
HelpParagraph:first-child margin-top: 0;
HelpSpec display: block; margin-left: -10em; float: left; clear: left; color: #527BBD;
HelpString display: inline-block; color: green; font-weight: normal; vertical-align: text-top;
HelpString::before content: '"';
HelpString::after content: '"';
HelpString[delim]::before content: attr(delim);
HelpString[delim]::after content: attr(delim);
HelpHead,html|h1,dactyl://help/* {
display: block;
margin: 1em 0;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 2em;
font-weight: bold;
color: #527BBD;
clear: both;
}
HelpSubhead,html|h2,dactyl://help/* {
display: block;
margin: 1em 0;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 1.2em;
font-weight: bold;
color: #527BBD;
clear: both;
}
HelpSubsubhead,html|h3,dactyl://help/* {
display: block;
margin: 1em 0;
padding-bottom: .2ex;
font-size: 1.1em;
font-weight: bold;
color: #527BBD;
clear: both;
}
HelpTOC
HelpTOC>ol ol margin-left: -1em;
HelpTab,html|dl,dactyl://help/* display: table; width: 100%; margin: 1em 0; border-bottom-width: 1px; border-top-width: 1px; padding: .5ex 0; table-layout: fixed;
HelpTabColumn,html|column,dactyl://help/* display: table-column;
HelpTabColumn:first-child width: 25%;
HelpTabTitle,html|dt,dactyl://help/* display: table-cell; padding: .1ex 1ex; font-weight: bold;
HelpTabDescription,html|dd,dactyl://help/* display: table-cell; padding: .1ex 1ex; border-width: 0px;
HelpTabRow,html|dl>html|tr,dactyl://help/* display: table-row;
HelpTag display: inline-block; color: #527BBD; margin-left: 1ex; font-size: 8pt; font-weight: bold;
HelpTags display: block; float: right; clear: right;
HelpTopic color: #102663;
HelpType margin-right: 2ex;
HelpWarning color: red; font-weight: bold;
Logo
Search,,* {
font-size: inherit;
padding: 0;
color: black;
background-color: yellow;
}
]]>.toString()
}); });
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -38,13 +38,6 @@ const Storage = Module("storage", {
} }
}); });
function Runnable(self, func, args) {
return {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRunnable]),
run: function () { func.apply(self, args || []); }
};
}
const FailedAssertion = Class("FailedAssertion", Error, { const FailedAssertion = Class("FailedAssertion", Error, {
init: function (message) { init: function (message) {
this.message = message; this.message = message;
@@ -92,40 +85,6 @@ const Dactyl = Module("dactyl", {
/** @property {Element} The currently focused element. */ /** @property {Element} The currently focused element. */
get focus() document.commandDispatcher.focusedElement, get focus() document.commandDispatcher.focusedElement,
get extensions() {
const rdf = services.get("rdf");
const extensionManager = services.get("extensionManager");
let extensions = extensionManager.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
function getRdfProperty(item, property) {
let resource = rdf.GetResource("urn:mozilla:item:" + item.id);
let value = "";
if (resource) {
let target = extensionManager.datasource.GetTarget(resource,
rdf.GetResource("http://www.mozilla.org/2004/em-rdf#" + property), true);
if (target && target instanceof Ci.nsIRDFLiteral)
value = target.Value;
}
return value;
}
//const Extension = Struct("id", "name", "description", "icon", "enabled", "version");
return extensions.map(function (e) ({
id: e.id,
name: e.name,
description: getRdfProperty(e, "description"),
enabled: getRdfProperty(e, "isDisabled") != "true",
icon: e.iconURL,
options: getRdfProperty(e, "optionsURL"),
version: e.version
}));
},
getExtension: function (name) this.extensions.filter(function (e) e.name == name)[0],
// Global constants // Global constants
CURRENT_TAB: [], CURRENT_TAB: [],
NEW_TAB: [], NEW_TAB: [],
@@ -183,7 +142,7 @@ const Dactyl = Module("dactyl", {
beep: function () { beep: function () {
// FIXME: popups clear the command line // FIXME: popups clear the command line
if (options["visualbell"]) { if (options["visualbell"]) {
dactyl.callInMainThread(function () { util.callInMainThread(function () {
// flash the visual bell // flash the visual bell
let popup = document.getElementById("dactyl-visualbell"); let popup = document.getElementById("dactyl-visualbell");
let win = config.visualbellWindow; let win = config.visualbellWindow;
@@ -204,45 +163,6 @@ const Dactyl = Module("dactyl", {
return false; // so you can do: if (...) return dactyl.beep(); return false; // so you can do: if (...) return dactyl.beep();
}, },
/**
* Creates a new thread.
*/
newThread: function () services.get("threadManager").newThread(0),
/**
* Calls a function asynchronously on a new thread.
*
* @param {nsIThread} thread The thread to call the function on. If no
* thread is specified a new one is created.
* @optional
* @param {Object} self The 'this' object used when executing the
* function.
* @param {function} func The function to execute.
*
*/
callAsync: function (thread, self, func) {
thread = thread || services.get("threadManager").newThread(0);
thread.dispatch(Runnable(self, func, Array.slice(arguments, 3)), thread.DISPATCH_NORMAL);
},
/**
* Calls a function synchronously on a new thread.
*
* NOTE: Be sure to call GUI related methods like alert() or dump()
* ONLY in the main thread.
*
* @param {nsIThread} thread The thread to call the function on. If no
* thread is specified a new one is created.
* @optional
* @param {function} func The function to execute.
*/
callFunctionInThread: function (thread, func) {
thread = thread || services.get("threadManager").newThread(0);
// DISPATCH_SYNC is necessary, otherwise strange things will happen
thread.dispatch(Runnable(null, func, Array.slice(arguments, 2)), thread.DISPATCH_SYNC);
},
/** /**
* Prints a message to the console. If <b>msg</b> is an object it is * Prints a message to the console. If <b>msg</b> is an object it is
* pretty printed. * pretty printed.
@@ -488,18 +408,6 @@ const Dactyl = Module("dactyl", {
*/ */
has: function (feature) config.features.indexOf(feature) >= 0, has: function (feature) config.features.indexOf(feature) >= 0,
/**
* Returns whether the host application has the specified extension
* installed.
*
* @param {string} name The extension name.
* @returns {boolean}
*/
hasExtension: function (name) {
let extensions = services.get("extensionManager").getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
return extensions.some(function (e) e.name == name);
},
/** /**
* Returns the URL of the specified help <b>topic</b> if it exists. * Returns the URL of the specified help <b>topic</b> if it exists.
* *
@@ -622,6 +530,99 @@ const Dactyl = Module("dactyl", {
} }
}, },
exportHelp: function (path) {
const FILE = io.File(path);
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
const TIME = Date.now();
dactyl.initHelp();
let zip = services.create("zipWriter");
zip.open(FILE, File.MODE_CREATE | File.MODE_WRONLY | File.MODE_TRUNCATE);
function addURIEntry(file, uri)
zip.addEntryChannel(PATH + file, TIME, 9,
services.get("io").newChannel(uri, null, null), false);
function addDataEntry(file, data) // Inideal to an extreme.
addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data));
let empty = util.Array.toObject(
"area base basefont br col frame hr img input isindex link meta param"
.split(" ").map(Array.concat));
let chrome = {};
for (let [file,] in Iterator(services.get("dactyl:").FILE_MAP)) {
dactyl.open("dactyl://help/" + file);
dactyl.modules.events.waitForPageLoad();
let data = [
'<?xml version="1.0" encoding="UTF-8"?>\n',
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n',
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
];
function fix(node) {
switch(node.nodeType) {
case Node.ELEMENT_NODE:
if (node instanceof HTMLScriptElement)
return;
data.push("<"); data.push(node.localName);
if (node instanceof HTMLHtmlElement)
data.push(" xmlns=" + XHTML.uri.quote());
for (let { name: name, value: value } in util.Array.itervalues(node.attributes)) {
if (name == "dactyl:highlight") {
name = "class";
value = "hl-" + value;
}
if (name == "href") {
if (value.indexOf("dactyl://help-tag/") == 0)
value = services.get("io").newChannel(value, null, null).originalURI.path.substr(1);
if (!/[#\/]/.test(value))
value += ".xhtml";
}
if (name == "src" && value.indexOf(":") > 0) {
chrome[value] = value.replace(/.*\//, "");;
value = value.replace(/.*\//, "");
}
data.push(" ");
data.push(name);
data.push('="');
data.push(<>{value}</>.toXMLString());
data.push('"')
}
if (node.localName in empty)
data.push(" />");
else {
data.push(">");
if (node instanceof HTMLHeadElement)
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
Array.map(node.childNodes, arguments.callee);
data.push("</"); data.push(node.localName); data.push(">");
}
break;
case Node.TEXT_NODE:
data.push(<>{node.textContent}</>.toXMLString());
}
}
fix(content.document.documentElement);
addDataEntry(file + ".xhtml", data.join(""));
}
let data = [h.selector.replace(/^\[.*?=(.*?)\]/, ".hl-$1").replace(/html\|/, "") +
"\t{" + h.value + "}"
for (h in highlight) if (/^Help|^Logo/.test(h.class))];
data = data.join("\n");
addDataEntry("help.css", data.replace(/chrome:[^ ")]+\//g, ""));
let re = /(chrome:[^ ");]+\/)([^ ");]+)/g;
while ((m = re.exec(data)))
chrome[m[0]] = m[2];
for (let [uri, leaf] in Iterator(chrome))
addURIEntry(leaf, uri);
zip.close();
},
/** /**
* Opens the help page containing the specified <b>topic</b> if it * Opens the help page containing the specified <b>topic</b> if it
* exists. * exists.
@@ -1016,34 +1017,6 @@ const Dactyl = Module("dactyl", {
return commands.parseArgs(cmdline, options, "*"); return commands.parseArgs(cmdline, options, "*");
}, },
sleep: function (delay) {
let mainThread = services.get("threadManager").mainThread;
let end = Date.now() + delay;
while (Date.now() < end)
mainThread.processNextEvent(true);
return true;
},
callInMainThread: function (callback, self) {
let mainThread = services.get("threadManager").mainThread;
if (!services.get("threadManager").isMainThread)
mainThread.dispatch(Runnable(self, callback), mainThread.DISPATCH_NORMAL);
else
callback.call(self);
},
threadYield: function (flush, interruptable) {
let mainThread = services.get("threadManager").mainThread;
dactyl.interrupted = false;
do {
mainThread.processNextEvent(!flush);
if (dactyl.interrupted)
throw new Error("Interrupted");
}
while (flush === true && mainThread.hasPendingEvents());
},
variableReference: function (string) { variableReference: function (string) {
if (!string) if (!string)
return [null, null, null]; return [null, null, null];
@@ -1097,7 +1070,7 @@ const Dactyl = Module("dactyl", {
// return the platform normalized to Vim values // return the platform normalized to Vim values
getPlatformFeature: function () { getPlatformFeature: function () {
let platform = navigator.platform; let platform = services.get("runtime").OS;
return /^Mac/.test(platform) ? "MacUnix" : platform == "Win32" ? "Win32" : "Unix"; return /^Mac/.test(platform) ? "MacUnix" : platform == "Win32" ? "Win32" : "Unix";
}, },
@@ -1521,7 +1494,6 @@ const Dactyl = Module("dactyl", {
literal: 0 literal: 0
}); });
// TODO: maybe indicate pending status too?
commands.add(["extens[ions]", "exts"], commands.add(["extens[ions]", "exts"],
"List available extensions", "List available extensions",
function (args) { function (args) {
@@ -1558,8 +1530,6 @@ const Dactyl = Module("dactyl", {
}, },
{ argCount: "?" }); { argCount: "?" });
///////////////////////////////////////////////////////////////////////////
commands.add(["exu[sage]"], commands.add(["exu[sage]"],
"List all Ex commands with a short description", "List all Ex commands with a short description",
function (args) { Dactyl.showHelpIndex("ex-cmd-index", commands, args.bang); }, { function (args) { Dactyl.showHelpIndex("ex-cmd-index", commands, args.bang); }, {
@@ -1726,7 +1696,7 @@ const Dactyl = Module("dactyl", {
else else
totalUnits = "msec"; totalUnits = "msec";
let str = template.commandOutput( let str = template.commandOutput(commandline.command,
<table> <table>
<tr highlight="Title" align="left"> <tr highlight="Title" align="left">
<th colspan="3">Code execution summary</th> <th colspan="3">Code execution summary</th>
@@ -1797,7 +1767,8 @@ const Dactyl = Module("dactyl", {
if (args.bang) if (args.bang)
dactyl.open("about:"); dactyl.open("about:");
else else
dactyl.echo(template.commandOutput(<>{config.name} {dactyl.version} running on:<br/>{navigator.userAgent}</>)); dactyl.echo(template.commandOutput(commandline.command,
<>{config.name} {dactyl.version} running on:<br/>{navigator.userAgent}</>));
}, { }, {
argCount: "0", argCount: "0",
bang: true bang: true

View File

@@ -333,7 +333,7 @@ const Editor = Module("editor", {
dactyl.assert(args.length >= 1, "No editor specified"); dactyl.assert(args.length >= 1, "No editor specified");
args.push(path); args.push(path);
dactyl.callFunctionInThread(null, io.run, io.expandPath(args.shift()), args, true); util.callInThread(null, io.run, io.expandPath(args.shift()), args, true);
}, },
// TODO: clean up with 2 functions for textboxes and currentEditor? // TODO: clean up with 2 functions for textboxes and currentEditor?

View File

@@ -1,8 +1,6 @@
try { __dactyl_eval_result = eval(__dactyl_eval_string); try { __dactyl_eval_result = eval(__dactyl_eval_string); }
} catch (e) { __dactyl_eval_error = e; }
catch (e) {
__dactyl_eval_error = e;
}
// IMPORTANT: The eval statement *must* remain on the first line // IMPORTANT: The eval statement *must* remain on the first line
// in order for line numbering in any errors to remain correct. // in order for line numbering in any errors to remain correct.

View File

@@ -288,7 +288,7 @@ const Events = Module("events", {
commandline.quiet = quiet; commandline.quiet = quiet;
try { try {
dactyl.threadYield(1, true); util.threadYield(1, true);
for (let [, evt_obj] in Iterator(events.fromString(keys))) { for (let [, evt_obj] in Iterator(events.fromString(keys))) {
let elem = dactyl.focus || window.content; let elem = dactyl.focus || window.content;
@@ -614,7 +614,7 @@ const Events = Module("events", {
*/ */
waitForPageLoad: function () { waitForPageLoad: function () {
//dactyl.dump("start waiting in loaded state: " + buffer.loaded); //dactyl.dump("start waiting in loaded state: " + buffer.loaded);
dactyl.threadYield(true); // clear queue util.threadYield(true); // clear queue
if (buffer.loaded == 1) if (buffer.loaded == 1)
return true; return true;
@@ -624,7 +624,7 @@ const Events = Module("events", {
let end = start + (maxWaitTime * 1000); // maximum time to wait - TODO: add option let end = start + (maxWaitTime * 1000); // maximum time to wait - TODO: add option
let now; let now;
while (now = Date.now(), now < end) { while (now = Date.now(), now < end) {
dactyl.threadYield(); util.threadYield();
//if ((now - start) % 1000 < 10) //if ((now - start) % 1000 < 10)
// dactyl.dump("waited: " + (now - start) + " ms"); // dactyl.dump("waited: " + (now - start) + " ms");
@@ -632,7 +632,7 @@ const Events = Module("events", {
return false; return false;
if (buffer.loaded > 0) { if (buffer.loaded > 0) {
dactyl.sleep(250); util.sleep(250);
break; break;
} }
else else
@@ -799,7 +799,7 @@ const Events = Module("events", {
let urlbar = document.getElementById("urlbar"); let urlbar = document.getElementById("urlbar");
if (elem == null && urlbar && urlbar.inputField == this._lastFocus) if (elem == null && urlbar && urlbar.inputField == this._lastFocus)
dactyl.threadYield(true); util.threadYield(true);
if (dactyl.mode & (modes.EMBED | modes.INSERT | modes.TEXTAREA | modes.VISUAL)) if (dactyl.mode & (modes.EMBED | modes.INSERT | modes.TEXTAREA | modes.VISUAL))
modes.reset(); modes.reset();
@@ -837,7 +837,7 @@ const Events = Module("events", {
} }
if (key == "<C-c>") if (key == "<C-c>")
dactyl.interrupted = true; util.interrupted = true;
// feedingKeys needs to be separate from interrupted so // feedingKeys needs to be separate from interrupted so
// we can differentiate between a recorded <C-c> // we can differentiate between a recorded <C-c>

View File

@@ -77,7 +77,7 @@ const Hints = Module("hints", {
hints.escNumbers = false; hints.escNumbers = false;
if (this._activeTimeout) if (this._activeTimeout)
clearTimeout(this._activeTimeout); this._activeTimeout.cancel();
this._activeTimeout = null; this._activeTimeout = null;
}, },
@@ -504,7 +504,7 @@ const Hints = Module("hints", {
// clear any timeout which might be active after pressing a number // clear any timeout which might be active after pressing a number
if (this._activeTimeout) { if (this._activeTimeout) {
clearTimeout(this._activeTimeout); this._activeTimeout.cancel();
this._activeTimeout = null; this._activeTimeout = null;
} }
@@ -728,7 +728,7 @@ const Hints = Module("hints", {
this._generate(win); this._generate(win);
// get all keys from the input queue // get all keys from the input queue
dactyl.threadYield(true); util.threadYield(true);
this._canUpdate = true; this._canUpdate = true;
this._showHints(); this._showHints();
@@ -761,7 +761,7 @@ const Hints = Module("hints", {
// clear any timeout which might be active after pressing a number // clear any timeout which might be active after pressing a number
if (this._activeTimeout) { if (this._activeTimeout) {
clearTimeout(this._activeTimeout); this._activeTimeout.cancel();
this._activeTimeout = null; this._activeTimeout = null;
} }
@@ -864,123 +864,88 @@ const Hints = Module("hints", {
//}}} //}}}
}, { }, {
indexOf: (function () { get translitTable() function () {
const table = [ const table = {};
[0x00c0, 0x00c6, ["A"]], [
[0x00c7, 0x00c7, ["C"]], [0x00c0, 0x00c6, ["A"]], [0x00c7, 0x00c7, ["C"]],
[0x00c8, 0x00cb, ["E"]], [0x00c8, 0x00cb, ["E"]], [0x00cc, 0x00cf, ["I"]],
[0x00cc, 0x00cf, ["I"]], [0x00d1, 0x00d1, ["N"]], [0x00d2, 0x00d6, ["O"]],
[0x00d1, 0x00d1, ["N"]], [0x00d8, 0x00d8, ["O"]], [0x00d9, 0x00dc, ["U"]],
[0x00d2, 0x00d6, ["O"]], [0x00dd, 0x00dd, ["Y"]], [0x00e0, 0x00e6, ["a"]],
[0x00d8, 0x00d8, ["O"]], [0x00e7, 0x00e7, ["c"]], [0x00e8, 0x00eb, ["e"]],
[0x00d9, 0x00dc, ["U"]], [0x00ec, 0x00ef, ["i"]], [0x00f1, 0x00f1, ["n"]],
[0x00dd, 0x00dd, ["Y"]], [0x00f2, 0x00f6, ["o"]], [0x00f8, 0x00f8, ["o"]],
[0x00e0, 0x00e6, ["a"]], [0x00f9, 0x00fc, ["u"]], [0x00fd, 0x00fd, ["y"]],
[0x00e7, 0x00e7, ["c"]], [0x00ff, 0x00ff, ["y"]], [0x0100, 0x0105, ["A", "a"]],
[0x00e8, 0x00eb, ["e"]], [0x0106, 0x010d, ["C", "c"]], [0x010e, 0x0111, ["D", "d"]],
[0x00ec, 0x00ef, ["i"]], [0x0112, 0x011b, ["E", "e"]], [0x011c, 0x0123, ["G", "g"]],
[0x00f1, 0x00f1, ["n"]], [0x0124, 0x0127, ["H", "h"]], [0x0128, 0x0130, ["I", "i"]],
[0x00f2, 0x00f6, ["o"]], [0x0132, 0x0133, ["IJ", "ij"]], [0x0134, 0x0135, ["J", "j"]],
[0x00f8, 0x00f8, ["o"]], [0x0136, 0x0136, ["K", "k"]], [0x0139, 0x0142, ["L", "l"]],
[0x00f9, 0x00fc, ["u"]], [0x0143, 0x0148, ["N", "n"]], [0x0149, 0x0149, ["n"]],
[0x00fd, 0x00fd, ["y"]], [0x014c, 0x0151, ["O", "o"]], [0x0152, 0x0153, ["OE", "oe"]],
[0x00ff, 0x00ff, ["y"]], [0x0154, 0x0159, ["R", "r"]], [0x015a, 0x0161, ["S", "s"]],
[0x0100, 0x0105, ["A", "a"]], [0x0162, 0x0167, ["T", "t"]], [0x0168, 0x0173, ["U", "u"]],
[0x0106, 0x010d, ["C", "c"]], [0x0174, 0x0175, ["W", "w"]], [0x0176, 0x0178, ["Y", "y", "Y"]],
[0x010e, 0x0111, ["D", "d"]], [0x0179, 0x017e, ["Z", "z"]], [0x0180, 0x0183, ["b", "B", "B", "b"]],
[0x0112, 0x011b, ["E", "e"]], [0x0187, 0x0188, ["C", "c"]], [0x0189, 0x0189, ["D"]],
[0x011c, 0x0123, ["G", "g"]],
[0x0124, 0x0127, ["H", "h"]],
[0x0128, 0x0130, ["I", "i"]],
[0x0132, 0x0133, ["IJ", "ij"]],
[0x0134, 0x0135, ["J", "j"]],
[0x0136, 0x0136, ["K", "k"]],
[0x0139, 0x0142, ["L", "l"]],
[0x0143, 0x0148, ["N", "n"]],
[0x0149, 0x0149, ["n"]],
[0x014c, 0x0151, ["O", "o"]],
[0x0152, 0x0153, ["OE", "oe"]],
[0x0154, 0x0159, ["R", "r"]],
[0x015a, 0x0161, ["S", "s"]],
[0x0162, 0x0167, ["T", "t"]],
[0x0168, 0x0173, ["U", "u"]],
[0x0174, 0x0175, ["W", "w"]],
[0x0176, 0x0178, ["Y", "y", "Y"]],
[0x0179, 0x017e, ["Z", "z"]],
[0x0180, 0x0183, ["b", "B", "B", "b"]],
[0x0187, 0x0188, ["C", "c"]],
[0x0189, 0x0189, ["D"]],
[0x018a, 0x0192, ["D", "D", "d", "F", "f"]], [0x018a, 0x0192, ["D", "D", "d", "F", "f"]],
[0x0193, 0x0194, ["G"]], [0x0193, 0x0194, ["G"]],
[0x0197, 0x019b, ["I", "K", "k", "l", "l"]], [0x0197, 0x019b, ["I", "K", "k", "l", "l"]],
[0x019d, 0x01a1, ["N", "n", "O", "O", "o"]], [0x019d, 0x01a1, ["N", "n", "O", "O", "o"]],
[0x01a4, 0x01a5, ["P", "p"]], [0x01a4, 0x01a5, ["P", "p"]], [0x01ab, 0x01ab, ["t"]],
[0x01ab, 0x01ab, ["t"]],
[0x01ac, 0x01b0, ["T", "t", "T", "U", "u"]], [0x01ac, 0x01b0, ["T", "t", "T", "U", "u"]],
[0x01b2, 0x01d2, ["V", "Y", "y", "Z", "z", "D", "L", "N", "A", "a", "I", "i", "O", "o"]], [0x01b2, 0x01d2, ["V", "Y", "y", "Z", "z", "D", "L", "N", "A", "a",
[0x01d3, 0x01dc, ["U", "u"]], "I", "i", "O", "o"]],
[0x01de, 0x01e1, ["A", "a"]], [0x01d3, 0x01dc, ["U", "u"]], [0x01de, 0x01e1, ["A", "a"]],
[0x01e2, 0x01e3, ["AE", "ae"]], [0x01e2, 0x01e3, ["AE", "ae"]],
[0x01e4, 0x01ed, ["G", "g", "G", "g", "K", "k", "O", "o", "O", "o"]], [0x01e4, 0x01ed, ["G", "g", "G", "g", "K", "k", "O", "o", "O", "o"]],
[0x01f0, 0x01f5, ["j", "D", "G", "g"]], [0x01f0, 0x01f5, ["j", "D", "G", "g"]],
[0x01fa, 0x01fb, ["A", "a"]], [0x01fa, 0x01fb, ["A", "a"]], [0x01fc, 0x01fd, ["AE", "ae"]],
[0x01fc, 0x01fd, ["AE", "ae"]], [0x01fe, 0x0217, ["O", "o", "A", "a", "A", "a", "E", "e", "E", "e",
[0x01fe, 0x0217, ["O", "o", "A", "a", "A", "a", "E", "e", "E", "e", "I", "i", "I", "i", "O", "o", "O", "o", "R", "r", "R", "r", "U", "u", "U", "u"]], "I", "i", "I", "i", "O", "o", "O", "o", "R", "r", "R", "r", "U",
"u", "U", "u"]],
[0x0253, 0x0257, ["b", "c", "d", "d"]], [0x0253, 0x0257, ["b", "c", "d", "d"]],
[0x0260, 0x0269, ["g", "h", "h", "i", "i"]], [0x0260, 0x0269, ["g", "h", "h", "i", "i"]],
[0x026b, 0x0273, ["l", "l", "l", "l", "m", "n", "n"]], [0x026b, 0x0273, ["l", "l", "l", "l", "m", "n", "n"]],
[0x027c, 0x028b, ["r", "r", "r", "r", "s", "t", "u", "u", "v"]], [0x027c, 0x028b, ["r", "r", "r", "r", "s", "t", "u", "u", "v"]],
[0x0290, 0x0291, ["z"]], [0x0290, 0x0291, ["z"]], [0x029d, 0x02a0, ["j", "q"]],
[0x029d, 0x02a0, ["j", "q"]],
[0x1e00, 0x1e09, ["A", "a", "B", "b", "B", "b", "B", "b", "C", "c"]], [0x1e00, 0x1e09, ["A", "a", "B", "b", "B", "b", "B", "b", "C", "c"]],
[0x1e0a, 0x1e13, ["D", "d"]], [0x1e0a, 0x1e13, ["D", "d"]], [0x1e14, 0x1e1d, ["E", "e"]],
[0x1e14, 0x1e1d, ["E", "e"]], [0x1e1e, 0x1e21, ["F", "f", "G", "g"]], [0x1e22, 0x1e2b, ["H", "h"]],
[0x1e1e, 0x1e21, ["F", "f", "G", "g"]], [0x1e2c, 0x1e8f, ["I", "i", "I", "i", "K", "k", "K", "k", "K", "k",
[0x1e22, 0x1e2b, ["H", "h"]], "L", "l", "L", "l", "L", "l", "L", "l", "M", "m", "M", "m", "M",
[0x1e2c, 0x1e8f, ["I", "i", "I", "i", "K", "k", "K", "k", "K", "k", "L", "l", "L", "l", "L", "l", "L", "l", "M", "m", "M", "m", "M", "m", "N", "n", "N", "n", "N", "n", "N", "n", "O", "o", "O", "o", "O", "o", "O", "o", "P", "p", "P", "p", "R", "r", "R", "r", "R", "r", "R", "r", "S", "s", "S", "s", "S", "s", "S", "s", "S", "s", "T", "t", "T", "t", "T", "t", "T", "t", "U", "u", "U", "u", "U", "u", "U", "u", "U", "u", "V", "v", "V", "v", "W", "w", "W", "w", "W", "w", "W", "w", "W", "w", "X", "x", "X", "x", "Y", "y"]], "m", "N", "n", "N", "n", "N", "n", "N", "n", "O", "o", "O", "o",
"O", "o", "O", "o", "P", "p", "P", "p", "R", "r", "R", "r", "R",
"r", "R", "r", "S", "s", "S", "s", "S", "s", "S", "s", "S", "s",
"T", "t", "T", "t", "T", "t", "T", "t", "U", "u", "U", "u", "U",
"u", "U", "u", "U", "u", "V", "v", "V", "v", "W", "w", "W", "w",
"W", "w", "W", "w", "W", "w", "X", "x", "X", "x", "Y", "y"]],
[0x1e90, 0x1e9a, ["Z", "z", "Z", "z", "Z", "z", "h", "t", "w", "y", "a"]], [0x1e90, 0x1e9a, ["Z", "z", "Z", "z", "Z", "z", "h", "t", "w", "y", "a"]],
[0x1ea0, 0x1eb7, ["A", "a"]], [0x1ea0, 0x1eb7, ["A", "a"]], [0x1eb8, 0x1ec7, ["E", "e"]],
[0x1eb8, 0x1ec7, ["E", "e"]], [0x1ec8, 0x1ecb, ["I", "i"]], [0x1ecc, 0x1ee3, ["O", "o"]],
[0x1ec8, 0x1ecb, ["I", "i"]], [0x1ee4, 0x1ef1, ["U", "u"]], [0x1ef2, 0x1ef9, ["Y", "y"]],
[0x1ecc, 0x1ee3, ["O", "o"]], [0x2071, 0x2071, ["i"]], [0x207f, 0x207f, ["n"]],
[0x1ee4, 0x1ef1, ["U", "u"]], [0x249c, 0x24b5, "a"], [0x24b6, 0x24cf, "A"],
[0x1ef2, 0x1ef9, ["Y", "y"]],
[0x2071, 0x2071, ["i"]],
[0x207f, 0x207f, ["n"]],
[0x249c, 0x24b5, "a"],
[0x24b6, 0x24cf, "A"],
[0x24d0, 0x24e9, "a"], [0x24d0, 0x24e9, "a"],
[0xfb00, 0xfb06, ["ff", "fi", "fl", "ffi", "ffl", "st", "st"]], [0xfb00, 0xfb06, ["ff", "fi", "fl", "ffi", "ffl", "st", "st"]],
[0xff21, 0xff3a, "A"], [0xff21, 0xff3a, "A"], [0xff41, 0xff5a, "a"],
[0xff41, 0xff5a, "a"], ].forEach(function (start, stop, val) {
].map(function (a) { if (typeof a[2] != "string")
if (typeof a[2] == "string") for (i=start; i <= stop; i++)
a[3] = function (chr) String.fromCharCode(this[2].charCodeAt(0) + chr - this[0]); table[String.fromCharCode(i)] = val[(i - start) % val.length];
else else {
a[3] = function (chr) this[2][(chr - this[0]) % this[2].length]; let n = val.charCodeAt(0);
return a; for (i=start; i <= stop; i++)
table[String.fromCharCode(i)] = String.fromCharCode(n + i - start);
}
}); });
function translate(chr) { delete this.translitTable;
var m, c = chr.charCodeAt(0); return this.translitTable = table;
var n = table.length; },
var i = 0; indexOf: function indexOf(dest, src) {
while (n) { let table = this.translitTable;
m = Math.floor(n / 2);
var t = table[i + m];
if (c >= t[0] && c <= t[1])
return t[3](c);
if (c < t[0] || m == 0)
n = m;
else {
i += m;
n = n - m;
}
}
return chr;
}
return function indexOf(dest, src) {
var end = dest.length - src.length; var end = dest.length - src.length;
if (src.length == 0) if (src.length == 0)
return 0; return 0;
@@ -988,7 +953,8 @@ const Hints = Module("hints", {
for (var i = 0; i < end; i++) { for (var i = 0; i < end; i++) {
var j = i; var j = i;
for (var k = 0; k < src.length;) { for (var k = 0; k < src.length;) {
var s = translate(dest[j++]); var s = dest[j++];
s = table[s] || s;
for (var l = 0; l < s.length; l++, k++) { for (var l = 0; l < s.length; l++, k++) {
if (s[l] != src[k]) if (s[l] != src[k])
continue outer; continue outer;
@@ -998,8 +964,7 @@ const Hints = Module("hints", {
} }
} }
return -1; return -1;
} },
})(),
Mode: Struct("prompt", "action", "tags") Mode: Struct("prompt", "action", "tags")
}, { }, {

View File

@@ -65,7 +65,7 @@ const History = Module("history", {
if (current == start && steps < 0 || current == end && steps > 0) if (current == start && steps < 0 || current == end && steps > 0)
dactyl.beep(); dactyl.beep();
else { else {
let index = util.Math.constrain(current + steps, start, end); let index = Math.constrain(current + steps, start, end);
window.getWebNavigation().gotoIndex(index); window.getWebNavigation().gotoIndex(index);
} }
}, },

View File

@@ -612,7 +612,6 @@ lookup:
dactyl.echomsg("Cannot source a directory: " + filename.quote(), 0); dactyl.echomsg("Cannot source a directory: " + filename.quote(), 0);
else else
dactyl.echomsg("could not source: " + filename.quote(), 1); dactyl.echomsg("could not source: " + filename.quote(), 1);
dactyl.echoerr("E484: Can't open file " + filename); dactyl.echoerr("E484: Can't open file " + filename);
} }
@@ -621,7 +620,6 @@ lookup:
dactyl.echomsg("sourcing " + filename.quote(), 2); dactyl.echomsg("sourcing " + filename.quote(), 2);
let str = file.read();
let uri = services.get("io").newFileURI(file); let uri = services.get("io").newFileURI(file);
// handle pure JavaScript files specially // handle pure JavaScript files specially
@@ -643,6 +641,7 @@ lookup:
else { else {
let heredoc = ""; let heredoc = "";
let heredocEnd = null; // the string which ends the heredoc let heredocEnd = null; // the string which ends the heredoc
let str = file.read();
let lines = str.split(/\r\n|[\r\n]/); let lines = str.split(/\r\n|[\r\n]/);
function execute(args) { command.execute(args, special, count, { setFrom: file }); } function execute(args) { command.execute(args, special, count, { setFrom: file }); }
@@ -968,7 +967,8 @@ lookup:
let output = io.system(arg); let output = io.system(arg);
commandline.command = "!" + arg; commandline.command = "!" + arg;
commandline.echo(template.commandOutput(<span highlight="CmdOutput">{output}</span>)); commandline.echo(template.commandOutput(commandline.command,
<span highlight="CmdOutput">{output}</span>));
autocommands.trigger("ShellCmdPost", {}); autocommands.trigger("ShellCmdPost", {});
}, { }, {
@@ -979,12 +979,6 @@ lookup:
}); });
}, },
completion: function () { completion: function () {
JavaScript.setCompleter([this.File, File.expandPath],
[function (context, obj, args) {
context.quote[2] = "";
completion.file(context, true);
}]);
completion.charset = function (context) { completion.charset = function (context) {
context.anchored = false; context.anchored = false;
context.generate = function () { context.generate = function () {
@@ -1070,6 +1064,14 @@ lookup:
completion.file(context, full); completion.file(context, full);
}); });
}, },
javascript: function () {
JavaScript.setCompleter([this.File, File.expandPath],
[function (context, obj, args) {
context.quote[2] = "";
completion.file(context, true);
}]);
},
options: function () { options: function () {
var shell, shellcmdflag; var shell, shellcmdflag;
if (dactyl.has("Win32")) { if (dactyl.has("Win32")) {

View File

@@ -127,6 +127,13 @@ const Mappings = Module("mappings", {
init: function () { init: function () {
this._main = []; // default mappings this._main = []; // default mappings
this._user = []; // user created mappings this._user = []; // user created mappings
dactyl.registerObserver("mode-add", function (mode) {
if (!(mode.mask in this._user || mode.mask in this._main)) {
this._main[mode.mask] = [];
this._user[mode.mask] = [];
}
});
}, },
_addMap: function (map) { _addMap: function (map) {
@@ -189,13 +196,6 @@ const Mappings = Module("mappings", {
*/ */
getUserIterator: function (mode) this._mappingsIterator(mode, this._user), getUserIterator: function (mode) this._mappingsIterator(mode, this._user),
addMode: function (mode) {
if (!(mode in this._user || mode in this._main)) {
this._main[mode] = [];
this._user[mode] = [];
}
},
/** /**
* Adds a new default key mapping. * Adds a new default key mapping.
* *
@@ -477,6 +477,17 @@ const Mappings = Module("mappings", {
[mode.disp.toLowerCase()]); [mode.disp.toLowerCase()]);
}, },
completion: function () { completion: function () {
completion.userMapping = function userMapping(context, args, modes) {
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
modes = modes || [modules.modes.NORMAL];
if (args.completeArg == 0) {
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
context.completions = maps;
}
};
},
javascript: function () {
JavaScript.setCompleter(this.get, JavaScript.setCompleter(this.get,
[ [
null, null,
@@ -489,16 +500,6 @@ const Mappings = Module("mappings", {
]); ]);
} }
]); ]);
completion.userMapping = function userMapping(context, args, modes) {
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
modes = modes || [modules.modes.NORMAL];
if (args.completeArg == 0) {
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
context.completions = maps;
}
};
}, },
modes: function () { modes: function () {
for (let mode in modes) { for (let mode in modes) {

View File

@@ -139,7 +139,7 @@ const Modes = Module("modes", {
options = extended; options = extended;
extended = false; extended = false;
} }
this._modeMap[name] = this._modeMap[this[name]] = util.extend({ let mode = util.extend({
extended: extended, extended: extended,
count: true, count: true,
input: false, input: false,
@@ -147,11 +147,12 @@ const Modes = Module("modes", {
name: name, name: name,
disp: disp disp: disp
}, options); }, options);
this._modeMap[name].display = this._modeMap[name].display || function () disp; mode.display = mode.display || function () disp;
this._modeMap[name] = mode;
this._modeMap[this[name]] = mode;
if (!extended) if (!extended)
this._mainModes.push(this[name]); this._mainModes.push(this[name]);
if ("mappings" in modules) dactyl.triggerObserver("mode-add", mode);
mappings.addMode(this[name]);
}, },
getMode: function (name) this._modeMap[name], getMode: function (name) this._modeMap[name],

View File

@@ -85,7 +85,7 @@ window.addEventListener("load", function () {
set.add(seen, module.name); set.add(seen, module.name);
for (let dep in values(module.requires)) for (let dep in values(module.requires))
load(Module.constructors[dep], module.name); load(Module.constructors[dep], module.name, dep);
dump("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.name); dump("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.name);
modules[module.name] = module(); modules[module.name] = module();

View File

@@ -1269,10 +1269,6 @@ const Options = Module("options", {
}); });
}, },
completion: function () { completion: function () {
JavaScript.setCompleter(this.get, [function () ([o.name, o.description] for (o in options))]);
JavaScript.setCompleter([this.getPref, this.safeSetPref, this.setPref, this.resetPref, this.invertPref],
[function () options.allPrefs().map(function (pref) [pref, ""])]);
completion.option = function option(context, scope) { completion.option = function option(context, scope) {
context.title = ["Option"]; context.title = ["Option"];
context.keys = { text: "names", description: "description" }; context.keys = { text: "names", description: "description" };
@@ -1350,6 +1346,11 @@ const Options = Module("options", {
context.keys = { text: function (item) item, description: function (item) options.getPref(item) }; context.keys = { text: function (item) item, description: function (item) options.getPref(item) };
context.completions = options.allPrefs(); context.completions = options.allPrefs();
}; };
},
javascript: function () {
JavaScript.setCompleter(this.get, [function () ([o.name, o.description] for (o in options))]);
JavaScript.setCompleter([this.getPref, this.safeSetPref, this.setPref, this.resetPref, this.invertPref],
[function () options.allPrefs().map(function (pref) [pref, ""])]);
} }
}); });

View File

@@ -29,16 +29,19 @@ const Services = Module("services", {
this.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment); this.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment);
this.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager); this.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
this.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService); this.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService);
this.add("history", "@mozilla.org/browser/global-history;2", [Ci.nsIGlobalHistory3, Ci.nsINavHistoryService, Ci.nsIBrowserHistory]); this.add("history", "@mozilla.org/browser/global-history;2", [Ci.nsIBrowserHistory, Ci.nsIGlobalHistory3, Ci.nsINavHistoryService]);
this.add("io", "@mozilla.org/network/io-service;1", Ci.nsIIOService); this.add("io", "@mozilla.org/network/io-service;1", Ci.nsIIOService);
this.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance"); this.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance");
this.add("livemark", "@mozilla.org/browser/livemark-service;2", Ci.nsILivemarkService); this.add("livemark", "@mozilla.org/browser/livemark-service;2", Ci.nsILivemarkService);
this.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService); this.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService);
this.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefService, Ci.nsIPrefBranch, Ci.nsIPrefBranch2]); this.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefBranch, Ci.nsIPrefBranch2, Ci.nsIPrefService]);
this.add("profile", "@mozilla.org/toolkit/profile-service;1", Ci.nsIToolkitProfileService); this.add("profile", "@mozilla.org/toolkit/profile-service;1", Ci.nsIToolkitProfileService);
this.add("runtime", "@mozilla.org/xre/runtime;1", [Ci.nsIXULAppInfo, Ci.nsIXULRuntime]);
this.add("rdf", "@mozilla.org/rdf/rdf-service;1", Ci.nsIRDFService); this.add("rdf", "@mozilla.org/rdf/rdf-service;1", Ci.nsIRDFService);
this.add("sessionStore", "@mozilla.org/browser/sessionstore;1", Ci.nsISessionStore); this.add("sessionStore", "@mozilla.org/browser/sessionstore;1", Ci.nsISessionStore);
this.add("stylesheet", "@mozilla.org/content/style-sheet-service;1", Ci.nsIStyleSheetService);
this.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader); this.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader);
this.add("tagging", "@mozilla.org/browser/tagging-service;1", Ci.nsITaggingService);
this.add("threadManager", "@mozilla.org/thread-manager;1", Ci.nsIThreadManager); this.add("threadManager", "@mozilla.org/thread-manager;1", Ci.nsIThreadManager);
this.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator); this.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator);
this.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher); this.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher);
@@ -48,6 +51,8 @@ const Services = Module("services", {
this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler); this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler);
this.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind); this.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
this.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess); this.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
this.addClass("timer", "@mozilla.org/timer;1", Ci.nsITimer);
this.addClass("xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest);
this.addClass("zipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter); this.addClass("zipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
if (!this.get("extensionManager")) if (!this.get("extensionManager"))
@@ -81,7 +86,11 @@ const Services = Module("services", {
* the service. * the service.
*/ */
add: function (name, class_, ifaces, meth) { add: function (name, class_, ifaces, meth) {
return this.services[name] = this._create(class_, ifaces, meth); const self = this;
this.services.__defineGetter__(name, function () {
delete this[name];
return this[name] = self._create(class_, ifaces, meth);
});
}, },
/** /**
@@ -111,11 +120,9 @@ const Services = Module("services", {
*/ */
create: function (name) this.classes[name]() create: function (name) this.classes[name]()
}, { }, {
}, { javascript: function (dactyl, modules) {
completion: function () {
JavaScript.setCompleter(this.get, [function () services.services]); JavaScript.setCompleter(this.get, [function () services.services]);
JavaScript.setCompleter(this.create, [function () [[c, ""] for (c in services.classes)]]); JavaScript.setCompleter(this.create, [function () [[c, ""] for (c in services.classes)]]);
} }
}); });

View File

@@ -819,7 +819,7 @@ const Tabs = Module("tabs", {
let last = browser.mTabs.length - 1; let last = browser.mTabs.length - 1;
browser.moveTabTo(dummy, util.Math.constrain(tabIndex || last, 0, last)); browser.moveTabTo(dummy, Math.constrain(tabIndex || last, 0, last));
browser.selectedTab = dummy; // required browser.selectedTab = dummy; // required
browser.swapBrowsersAndCloseOther(dummy, config.tabbrowser.mCurrentTab); browser.swapBrowsersAndCloseOther(dummy, config.tabbrowser.mCurrentTab);
}, { }, {

View File

@@ -12,7 +12,7 @@ const Template = Module("template", {
map: function map(iter, func, sep, interruptable) { map: function map(iter, func, sep, interruptable) {
if (iter.length) // FIXME: Kludge? if (iter.length) // FIXME: Kludge?
iter = util.Array.itervalues(iter); iter = array.itervalues(iter);
let ret = <></>; let ret = <></>;
let n = 0; let n = 0;
for each (let i in Iterator(iter)) { for each (let i in Iterator(iter)) {
@@ -22,7 +22,7 @@ const Template = Module("template", {
if (sep && n++) if (sep && n++)
ret += sep; ret += sep;
if (interruptable && n % interruptable == 0) if (interruptable && n % interruptable == 0)
dactyl.threadYield(true, true); util.threadYield(true, true);
ret += val; ret += val;
} }
return ret; return ret;
@@ -93,7 +93,8 @@ const Template = Module("template", {
<table width="100%" style="height: 100%"> <table width="100%" style="height: 100%">
<tr> <tr>
{ template.map(util.range(0, 100), function (i) { template.map(util.range(0, 100), function (i)
<td highlight={left} style={"opacity: " + (1 - i / 100)}/>) } <td highlight={left} style={"opacity: " + (1 - i / 100)}
/>) }
</tr> </tr>
</table> </table>
</div>, </div>,
@@ -118,6 +119,7 @@ const Template = Module("template", {
// Using /foo*(:?)/ instead. // Using /foo*(:?)/ instead.
if (processStrings) if (processStrings)
return <span highlight="Function">{str.replace(/\{(.|\n)*(?:)/g, "{ ... }")}</span>; return <span highlight="Function">{str.replace(/\{(.|\n)*(?:)/g, "{ ... }")}</span>;
<>}</>; /* Vim */
return <>{arg}</>; return <>{arg}</>;
case "undefined": case "undefined":
return <span highlight="Null">{arg}</span>; return <span highlight="Null">{arg}</span>;
@@ -190,8 +192,8 @@ const Template = Module("template", {
return str; return str;
}, },
commandOutput: function generic(xml) { commandOutput: function generic(command, xml) {
return <>:{commandline.command}<br/>{xml}</>; return <>:{command}<br/>{xml}</>;
}, },
genericTable: function genericTable(items, format) { genericTable: function genericTable(items, format) {

View File

@@ -15,7 +15,16 @@ default xml namespace = XHTML;
const Util = Module("util", { const Util = Module("util", {
init: function () { init: function () {
this.Array = Util.Array; this.Array = array;
},
get activeWindow() services.get("windowWatcher").activeWindow,
callInMainThread: function (callback, self) {
let mainThread = services.get("threadManager").mainThread;
if (!services.get("threadManager").isMainThread)
mainThread.dispatch({ run: callback.call(self) }, mainThread.DISPATCH_NORMAL);
else
callback.call(self);
}, },
/** /**
@@ -25,7 +34,7 @@ const Util = Module("util", {
* @returns {Object} * @returns {Object}
*/ */
cloneObject: function cloneObject(obj) { cloneObject: function cloneObject(obj) {
if (obj instanceof Array) if (isarray(obj))
return obj.slice(); return obj.slice();
let newObj = {}; let newObj = {};
for (let [k, v] in Iterator(obj)) for (let [k, v] in Iterator(obj))
@@ -62,7 +71,7 @@ const Util = Module("util", {
* @returns {Object} * @returns {Object}
*/ */
computedStyle: function computedStyle(node) { computedStyle: function computedStyle(node) {
while (node instanceof Text && node.parentNode) while (node instanceof Ci.nsIDOMText && node.parentNode)
node = node.parentNode; node = node.parentNode;
return node.ownerDocument.defaultView.getComputedStyle(node, null); return node.ownerDocument.defaultView.getComputedStyle(node, null);
}, },
@@ -151,13 +160,7 @@ const Util = Module("util", {
* @returns {string} * @returns {string}
*/ */
escapeHTML: function escapeHTML(str) { escapeHTML: function escapeHTML(str) {
// XXX: the following code is _much_ slower than a simple .replace() return str.replace(/&/g, "&amp;").replace(/</g, "&lt;");
// :history display went down from 2 to 1 second after changing
//
// var e = window.content.document.createElement("div");
// e.appendChild(window.content.document.createTextNode(str));
// return e.innerHTML;
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}, },
/** /**
@@ -185,6 +188,48 @@ const Util = Module("util", {
return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter; return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter;
}, },
/**
* Evaluates an XPath expression in the current or provided
* document. It provides the xhtml, xhtml2 and dactyl XML
* namespaces. The result may be used as an iterator.
*
* @param {string} expression The XPath expression to evaluate.
* @param {Document} doc The document to evaluate the expression in.
* @default The current document.
* @param {Node} elem The context element.
* @default <b>doc</b>
* @param {boolean} asIterator Whether to return the results as an
* XPath iterator.
*/
evaluateXPath: function (expression, doc, elem, asIterator) {
if (!doc)
doc = content.document;
if (!elem)
elem = doc;
if (isarray(expression))
expression = util.makeXPath(expression);
let result = doc.evaluate(expression, elem,
function lookupNamespaceURI(prefix) {
return {
xul: XUL.uri,
xhtml: XHTML.uri,
xhtml2: "http://www.w3.org/2002/06/xhtml2",
dactyl: NS.uri
}[prefix] || null;
},
asIterator ? Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE : Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
return {
__proto__: result,
__iterator__: asIterator
? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
: function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
}
},
extend: function extend(dest) { extend: function extend(dest) {
Array.slice(arguments, 1).filter(util.identity).forEach(function (src) { Array.slice(arguments, 1).filter(util.identity).forEach(function (src) {
for (let [k, v] in Iterator(src)) { for (let [k, v] in Iterator(src)) {
@@ -201,38 +246,6 @@ const Util = Module("util", {
return dest; return dest;
}, },
/**
* Returns an XPath union expression constructed from the specified node
* tests. An expression is built with node tests for both the null and
* XHTML namespaces. See {@link Buffer#evaluateXPath}.
*
* @param nodes {Array(string)}
* @returns {string}
*/
makeXPath: function makeXPath(nodes) {
return util.Array(nodes).map(util.debrace).flatten()
.map(function (node) [node, "xhtml:" + node]).flatten()
.map(function (node) "//" + node).join(" | ");
},
/**
* Memoize the lookup of a property in an object.
*
* @param {object} obj The object to alter.
* @param {string} key The name of the property to memoize.
* @param {function} getter A function of zero to two arguments which
* will return the property's value. <b>obj</b> is
* passed as the first argument, <b>key</b> as the
* second.
*/
memoize: function memoize(obj, key, getter) {
obj.__defineGetter__(key, function () {
delete obj[key];
obj[key] = getter(obj, key);
return obj[key];
});
},
/** /**
* Returns the selection controller for the given window. * Returns the selection controller for the given window.
* *
@@ -247,37 +260,6 @@ const Util = Module("util", {
.getInterface(Ci.nsISelectionDisplay) .getInterface(Ci.nsISelectionDisplay)
.QueryInterface(Ci.nsISelectionController), .QueryInterface(Ci.nsISelectionController),
/**
* Split a string on literal occurrences of a marker.
*
* Specifically this ignores occurrences preceded by a backslash, or
* contained within 'single' or "double" quotes.
*
* It assumes backslash escaping on strings, and will thus not count quotes
* that are preceded by a backslash or within other quotes as starting or
* ending quoted sections of the string.
*
* @param {string} str
* @param {RegExp} marker
*/
splitLiteral: function splitLiteral(str, marker) {
let results = [];
let resep = RegExp(/^(([^\\'"]|\\.|'([^\\']|\\.)*'|"([^\\"]|\\.)*")*?)/.source + marker.source);
let cont = true;
while (cont) {
cont = false;
str = str.replace(resep, function (match, before) {
results.push(before);
cont = true;
return "";
});
}
results.push(str);
return results;
},
/** /**
* Converts <b>bytes</b> to a pretty printed data size string. * Converts <b>bytes</b> to a pretty printed data size string.
* *
@@ -319,99 +301,6 @@ const Util = Module("util", {
return strNum[0] + " " + unitVal[unitIndex]; return strNum[0] + " " + unitVal[unitIndex];
}, },
exportHelp: function (path) {
const FILE = io.File(path);
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
const TIME = Date.now();
dactyl.initHelp();
let zip = services.create("zipWriter");
zip.open(FILE, File.MODE_CREATE | File.MODE_WRONLY | File.MODE_TRUNCATE);
function addURIEntry(file, uri)
zip.addEntryChannel(PATH + file, TIME, 9,
services.get("io").newChannel(uri, null, null), false);
function addDataEntry(file, data) // Inideal to an extreme.
addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data));
let empty = util.Array.toObject(
"area base basefont br col frame hr img input isindex link meta param"
.split(" ").map(Array.concat));
let chrome = {};
for (let [file,] in Iterator(services.get("dactyl:").FILE_MAP)) {
dactyl.open("dactyl://help/" + file);
events.waitForPageLoad();
let data = [
'<?xml version="1.0" encoding="UTF-8"?>\n',
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n',
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
];
function fix(node) {
switch(node.nodeType) {
case Node.ELEMENT_NODE:
if (node instanceof HTMLScriptElement)
return;
data.push("<"); data.push(node.localName);
if (node instanceof HTMLHtmlElement)
data.push(" xmlns=" + XHTML.uri.quote());
for (let { name: name, value: value } in util.Array.itervalues(node.attributes)) {
if (name == "dactyl:highlight") {
name = "class";
value = "hl-" + value;
}
if (name == "href") {
if (value.indexOf("dactyl://help-tag/") == 0)
value = services.get("io").newChannel(value, null, null).originalURI.path.substr(1);
if (!/[#\/]/.test(value))
value += ".xhtml";
}
if (name == "src" && value.indexOf(":") > 0) {
chrome[value] = value.replace(/.*\//, "");;
value = value.replace(/.*\//, "");
}
data.push(" ");
data.push(name);
data.push('="');
data.push(<>{value}</>.toXMLString());
data.push('"')
}
if (node.localName in empty)
data.push(" />");
else {
data.push(">");
if (node instanceof HTMLHeadElement)
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
Array.map(node.childNodes, arguments.callee);
data.push("</"); data.push(node.localName); data.push(">");
}
break;
case Node.TEXT_NODE:
data.push(<>{node.textContent}</>.toXMLString());
}
}
fix(content.document.documentElement);
addDataEntry(file + ".xhtml", data.join(""));
}
let data = [h.selector.replace(/^\[.*?=(.*?)\]/, ".hl-$1").replace(/html\|/, "") +
"\t{" + h.value + "}"
for (h in highlight) if (/^Help|^Logo/.test(h.class))];
data = data.join("\n");
addDataEntry("help.css", data.replace(/chrome:[^ ")]+\//g, ""));
let re = /(chrome:[^ ");]+\/)([^ ");]+)/g;
while ((m = re.exec(data)))
chrome[m[0]] = m[2];
for (let [uri, leaf] in Iterator(chrome))
addURIEntry(leaf, uri);
zip.close();
},
/** /**
* Sends a synchronous or asynchronous HTTP request to <b>url</b> and * Sends a synchronous or asynchronous HTTP request to <b>url</b> and
* returns the XMLHttpRequest object. If <b>callback</b> is specified the * returns the XMLHttpRequest object. If <b>callback</b> is specified the
@@ -424,7 +313,7 @@ const Util = Module("util", {
*/ */
httpGet: function httpGet(url, callback) { httpGet: function httpGet(url, callback) {
try { try {
let xmlhttp = new XMLHttpRequest(); let xmlhttp = services.create("xmlhttp");
xmlhttp.mozBackgroundRequest = true; xmlhttp.mozBackgroundRequest = true;
if (callback) { if (callback) {
xmlhttp.onreadystatechange = function () { xmlhttp.onreadystatechange = function () {
@@ -437,53 +326,11 @@ const Util = Module("util", {
return xmlhttp; return xmlhttp;
} }
catch (e) { catch (e) {
dactyl.log("Error opening " + url + ": " + e, 1); dactyl.log("Error opening " + String.quote(url) + ": " + e, 1);
return null; return null;
} }
}, },
/**
* Evaluates an XPath expression in the current or provided
* document. It provides the xhtml, xhtml2 and dactyl XML
* namespaces. The result may be used as an iterator.
*
* @param {string} expression The XPath expression to evaluate.
* @param {Document} doc The document to evaluate the expression in.
* @default The current document.
* @param {Node} elem The context element.
* @default <b>doc</b>
* @param {boolean} asIterator Whether to return the results as an
* XPath iterator.
*/
evaluateXPath: function (expression, doc, elem, asIterator) {
if (!doc)
doc = window.content.document;
if (!elem)
elem = doc;
if (isarray(expression))
expression = util.makeXPath(expression);
let result = doc.evaluate(expression, elem,
function lookupNamespaceURI(prefix) {
return {
xul: XUL.uri,
xhtml: XHTML.uri,
xhtml2: "http://www.w3.org/2002/06/xhtml2",
dactyl: NS.uri
}[prefix] || null;
},
asIterator ? XPathResult.ORDERED_NODE_ITERATOR_TYPE : XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
return {
__proto__: result,
__iterator__: asIterator
? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
: function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
}
},
/** /**
* The identity function. * The identity function.
* *
@@ -508,6 +355,20 @@ const Util = Module("util", {
bottom: Math.min(r1.bottom, r2.bottom) bottom: Math.min(r1.bottom, r2.bottom)
}), }),
/**
* Returns an XPath union expression constructed from the specified node
* tests. An expression is built with node tests for both the null and
* XHTML namespaces. See {@link Buffer#evaluateXPath}.
*
* @param nodes {Array(string)}
* @returns {string}
*/
makeXPath: function makeXPath(nodes) {
return util.Array(nodes).map(util.debrace).flatten()
.map(function (node) [node, "xhtml:" + node]).flatten()
.map(function (node) "//" + node).join(" | ");
},
/** /**
* Returns the array that results from applying <b>func</b> to each * Returns the array that results from applying <b>func</b> to each
* property of <b>obj</b>. * property of <b>obj</b>.
@@ -524,21 +385,16 @@ const Util = Module("util", {
}, },
/** /**
* Math utility methods. * Memoize the lookup of a property in an object.
* @singleton
*/
Math: {
/**
* Returns the specified <b>value</b> constrained to the range <b>min</b> -
* <b>max</b>.
* *
* @param {number} value The value to constrain. * @param {object} obj The object to alter.
* @param {number} min The minimum constraint. * @param {string} key The name of the property to memoize.
* @param {number} max The maximum constraint. * @param {function} getter A function of zero to two arguments which
* @returns {number} * will return the property's value. <b>obj</b> is
* passed as the first argument, <b>key</b> as the
* second.
*/ */
constrain: function constrain(value, min, max) Math.min(Math.max(min, value), max) memoize: memoize,
},
/** /**
* Converts a URI string into a URI object. * Converts a URI string into a URI object.
@@ -578,7 +434,7 @@ const Util = Module("util", {
[XHTML, 'html'], [XHTML, 'html'],
[XUL, 'xul'] [XUL, 'xul']
]); ]);
if (object instanceof Element) { if (object instanceof Ci.nsIDOMElement) {
let elem = object; let elem = object;
if (elem.nodeType == elem.TEXT_NODE) if (elem.nodeType == elem.TEXT_NODE)
return elem.data; return elem.data;
@@ -616,10 +472,12 @@ const Util = Module("util", {
let keys = []; let keys = [];
try { // window.content often does not want to be queried with "var i in object" try { // window.content often does not want to be queried with "var i in object"
let hasValue = !("__iterator__" in object); let hasValue = !("__iterator__" in object);
/*
if (modules.isPrototypeOf(object)) { if (modules.isPrototypeOf(object)) {
object = Iterator(object); object = Iterator(object);
hasValue = false; hasValue = false;
} }
*/
for (let i in object) { for (let i in object) {
let value = <![CDATA[<no value>]]>; let value = <![CDATA[<no value>]]>;
try { try {
@@ -627,7 +485,7 @@ const Util = Module("util", {
} }
catch (e) {} catch (e) {}
if (!hasValue) { if (!hasValue) {
if (i instanceof Array && i.length == 2) if (isarray(i) && i.length == 2)
[i, value] = i; [i, value] = i;
else else
var noVal = true; var noVal = true;
@@ -689,7 +547,7 @@ const Util = Module("util", {
let endTime = Date.now() + time; let endTime = Date.now() + time;
while (start < end) { while (start < end) {
if (Date.now() > endTime) { if (Date.now() > endTime) {
dactyl.threadYield(true, true); util.threadYield(true, true);
endTime = Date.now() + time; endTime = Date.now() + time;
} }
yield start++; yield start++;
@@ -746,6 +604,58 @@ const Util = Module("util", {
elem.scrollIntoView(); elem.scrollIntoView();
}, },
sleep: function (delay) {
let mainThread = services.get("threadManager").mainThread;
let end = Date.now() + delay;
while (Date.now() < end)
mainThread.processNextEvent(true);
return true;
},
/**
* Split a string on literal occurrences of a marker.
*
* Specifically this ignores occurrences preceded by a backslash, or
* contained within 'single' or "double" quotes.
*
* It assumes backslash escaping on strings, and will thus not count quotes
* that are preceded by a backslash or within other quotes as starting or
* ending quoted sections of the string.
*
* @param {string} str
* @param {RegExp} marker
*/
splitLiteral: function splitLiteral(str, marker) {
let results = [];
let resep = RegExp(/^(([^\\'"]|\\.|'([^\\']|\\.)*'|"([^\\"]|\\.)*")*?)/.source + marker.source);
let cont = true;
while (cont) {
cont = false;
str = str.replace(resep, function (match, before) {
results.push(before);
cont = true;
return "";
});
}
results.push(str);
return results;
},
threadYield: function (flush, interruptable) {
let mainThread = services.get("threadManager").mainThread;
/* FIXME */
util.interrupted = false;
do {
mainThread.processNextEvent(!flush);
if (util.interrupted)
throw new Error("Interrupted");
}
while (flush === true && mainThread.hasPendingEvents());
},
/** /**
* Converts an E4X XML literal to a DOM node. * Converts an E4X XML literal to a DOM node.
* *
@@ -780,133 +690,26 @@ const Util = Module("util", {
} }
} }
}, { }, {
// TODO: Why don't we just push all util.BuiltinType up into modules? --djk Array: array
/**
* Array utility methods.
*/
Array: Class("Array", Array, {
init: function (ary) {
return {
__proto__: ary,
__iterator__: function () this.iteritems(),
__noSuchMethod__: function (meth, args) {
var res = util.Array[meth].apply(null, [this.__proto__].concat(args));
if (util.Array.isinstance(res))
return util.Array(res);
return res;
},
toString: function () this.__proto__.toString(),
concat: function () this.__proto__.concat.apply(this.__proto__, arguments),
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
};
}
}, {
isinstance: function isinstance(obj) {
return Object.prototype.toString.call(obj) == "[object Array]";
},
/**
* Converts an array to an object. As in lisp, an assoc is an
* array of key-value pairs, which maps directly to an object,
* as such:
* [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
*
* @param {Array[]} assoc
* @... {string} 0 - Key
* @... 1 - Value
*/
toObject: function toObject(assoc) {
let obj = {};
assoc.forEach(function ([k, v]) { obj[k] = v; });
return obj;
},
/**
* Compacts an array, removing all elements that are null or undefined:
* ["foo", null, "bar", undefined] -> ["foo", "bar"]
*
* @param {Array} ary
* @returns {Array}
*/
compact: function compact(ary) ary.filter(function (item) item != null),
/**
* Flattens an array, such that all elements of the array are
* joined into a single array:
* [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
*
* @param {Array} ary
* @returns {Array}
*/
flatten: function flatten(ary) ary.length ? Array.concat.apply([], ary) : [],
/**
* Returns an Iterator for an array's values.
*
* @param {Array} ary
* @returns {Iterator(Object)}
*/
itervalues: function itervalues(ary) {
let length = ary.length;
for (let i = 0; i < length; i++)
yield ary[i];
},
/**
* Returns an Iterator for an array's indices and values.
*
* @param {Array} ary
* @returns {Iterator([{number}, {Object}])}
*/
iteritems: function iteritems(ary) {
let length = ary.length;
for (let i = 0; i < length; i++)
yield [i, ary[i]];
},
/**
* Filters out all duplicates from an array. If
* <b>unsorted</b> is false, the array is sorted before
* duplicates are removed.
*
* @param {Array} ary
* @param {boolean} unsorted
* @returns {Array}
*/
uniq: function uniq(ary, unsorted) {
let ret = [];
if (unsorted) {
for (let [, item] in Iterator(ary))
if (ret.indexOf(item) == -1)
ret.push(item);
}
else {
for (let [, item] in Iterator(ary.sort())) {
if (item != last || !ret.length)
ret.push(item);
var last = item;
}
}
return ret;
},
/**
* Zips the contents of two arrays. The resulting array is twice the
* length of ary1, with any shortcomings of ary2 replaced with null
* strings.
*
* @param {Array} ary1
* @param {Array} ary2
* @returns {Array}
*/
zip: function zip(ary1, ary2) {
let res = []
for(let [i, item] in Iterator(ary1))
res.push(item, i in ary2 ? ary2[i] : "");
return res;
}
})
}); });
/**
* Math utility methods.
* @singleton
*/
var Math = {
__proto__: window.Math,
/**
* Returns the specified <b>value</b> constrained to the range <b>min</b> -
* <b>max</b>.
*
* @param {number} value The value to constrain.
* @param {number} min The minimum constraint.
* @param {number} max The maximum constraint.
* @returns {number}
*/
constrain: function constrain(value, min, max) Math.min(Math.max(min, value), max)
};
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et: