+++ /dev/null
-repo: 373f1649c80dea9be7b5bc9c57e8395f94f93ab1
-node: b83bb8e6d273f71b1278c4ee03376594ad6dd039
-branch: pentadactyl-1.0b6
-tag: pentadactyl-1.0b6
https://developer.mozilla.org/en/New_in_JavaScript_1.7#Block_scope_with_let
* Reuse common local variable names E.g. "elem" is generally used for element,
- "win" for windows, "func" for functions, "ret" for return values etc.
+ "win" for windows, "func" for functions, "res" for return values etc.
* Prefer // over /* */ comments (exceptions for big comments are usually OK)
Right: if (HACK) // TODO: remove hack
Functional tests are implemented using the Mozmill automated testing framework
-- https://developer.mozilla.org/en/Mozmill_Tests.
-A fresh profile is created for the duration of the test run, however, passing
-arguments to the host application won't be supported until Mozmill 1.5.2, the
-next release, so any user RC and plugin files should be temporarily disabled.
-This can be done by adding the following to the head of the RC file:
-set loadplugins=
-finish
+A fresh profile is created for the duration of the test run.
The host application binary tested can be overridden via the HOSTAPP_PATH
makefile variable. E.g.,
#### configuration
+
+AWK ?= awk
+B64ENCODE ?= base64
+CURL ?= curl
+SED := $(shell if [ "xoo" = x$$(echo foo | sed -E 's/f(o)/\1/' 2>/dev/null) ]; \
+ then echo sed -E; else echo sed -r; \
+ fi)
+
TOP = $(shell pwd)
OS = $(shell uname -s)
BUILD_DATE = $(shell date "+%Y/%m/%d %H:%M:%S")
BASE = $(TOP)/../common
GOOGLE_PROJ = dactyl
GOOGLE = https://$(GOOGLE_PROJ).googlecode.com/files
-VERSION ?= $(shell sed -n 's/.*em:version\(>\|="\)\(.*\)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
-UUID := $(shell sed -n 's/.*em:id\(>\|="\)\(.*\)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
+VERSION ?= $(shell $(SED) -n 's/.*em:version(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
+UUID := $(shell $(SED) -n 's/.*em:id(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
MANGLE := $(shell date '+%s' | awk '{ printf "%x", $$1 }')
MOZMILL = mozmill
HOSTAPP_PATH = $(shell which $(HOSTAPP))
BUILD_DIR = build.$(VERSION).$(OS)
-AWK ?= awk
-B64ENCODE ?= base64
-CURL ?= curl
-SED := $(shell if [ "xoo" = x$$(echo foo | sed -E 's/f(o)/\1/' 2>/dev/null) ]; \
- then echo sed -E; else echo sed -r; \
- fi)
-
.SILENT:
#### rules
done; \
\
profile=$$(sed 's/^$$/\#/' "$$dir/profiles.ini" | \
- awk -v"profile=$(PROFILE)" \
+ awk -v "profile=$(PROFILE)" \
'BEGIN { RS="#" } \
index($$0, "\nName=" profile "\n") { print; exit } \
!profile && /\nName=default\n/ { args["name=default"] = $$0 } \
xpi: $(CHROME)
@echo "Building XPI..."
mkdir -p "$(XPI_PATH)"
-
+
$(AWK) -v 'name=$(NAME)' -v 'suffix=$(MANGLE)' \
-f $(BASE)/process_manifest.awk \
"$(TOP)/chrome.manifest" >"$(XPI_PATH)/chrome.manifest"
-
+
version="$(VERSION)"; \
hg root >/dev/null 2>&1 && \
case "$$version" in \
esac; \
$(SED) -e 's/(em:version(>|="))([^"<]+)/\1'"$$version/" \
<"$(TOP)/install.rdf" >"$(XPI_PATH)/install.rdf"
-
+
$(MAKE_JAR) "$(XPI)" "$(XPI_BASES)" "$(XPI_DIRS)" "$(XPI_TEXTS)" "$(XPI_BINS)" "$(XPI_FILES)"
rm -r -- $(CHROME)
@echo "Built XPI: $(XPI)"
const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
const manager = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+const BOOTSTRAP_JSM = "resource://dactyl/bootstrap.jsm";
+
const BOOTSTRAP_CONTRACT = "@dactyl.googlecode.com/base/bootstrap";
JSMLoader = JSMLoader || BOOTSTRAP_CONTRACT in Cc && Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader;
.getService(Components.interfaces.extIApplication)
.storage.get("dactyl.JSMLoader", null);
-
function reportError(e) {
dump("\ndactyl: bootstrap: " + e + "\n" + (e.stack || Error().stack) + "\n");
Cu.reportError(e);
let chars = "0123456789abcdefghijklmnopqrstuv";
for (let n = Date.now(); n; n = Math.round(n / chars.length))
suffix += chars[n % chars.length];
- suffix = "";
for each (let line in manifest.split("\n")) {
let fields = line.split(/\s+/);
break;
case "resource":
+ var hardSuffix = /^[^\/]*/.exec(fields[2])[0];
+
resources.push(fields[1], fields[1] + suffix);
resourceProto.setSubstitution(fields[1], getURI(fields[2]));
resourceProto.setSubstitution(fields[1] + suffix, getURI(fields[2]));
}
}
+ // Flush the cache if necessary, just to be paranoid
+ let pref = "extensions.dactyl.cacheFlushCheck";
+ let val = addon.version + "-" + hardSuffix;
+ if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) {
+ Services.obs.notifyObservers(null, "startupcache-invalidate", "");
+ Services.prefs.setCharPref(pref, val);
+ }
+
try {
module("resource://dactyl-content/disable-acr.jsm").init(addon.id);
}
reportError(e);
}
- if (JSMLoader && JSMLoader.bump !== 4) // Temporary hack
- Services.scriptloader.loadSubScript("resource://dactyl" + suffix + "/bootstrap.jsm",
- Cu.import("resource://dactyl/bootstrap.jsm", global));
+ if (JSMLoader) {
+ if (Cu.unload) {
+ Cu.unload(BOOTSTRAP_JSM);
+ for (let [name] in Iterator(JSMLoader.globals))
+ Cu.unload(~name.indexOf(":") ? name : "resource://dactyl" + JSMLoader.suffix + "/" + name);
+ }
+ else if (JSMLoader.bump != 5) // Temporary hack
+ Services.scriptloader.loadSubScript("resource://dactyl" + suffix + "/bootstrap.jsm",
+ Cu.import(BOOTSTRAP_JSM, global));
+ }
- if (!JSMLoader || JSMLoader.bump !== 4)
- Cu.import("resource://dactyl/bootstrap.jsm", global);
+ if (!JSMLoader || JSMLoader.bump !== 5 || Cu.unload)
+ Cu.import(BOOTSTRAP_JSM, global);
JSMLoader.bootstrap = this;
- JSMLoader.load("resource://dactyl/bootstrap.jsm", global);
+ JSMLoader.load(BOOTSTRAP_JSM, global);
JSMLoader.init(suffix);
JSMLoader.load("base.jsm", global);
wrappedJSObject: {}
},
createInstance: function () this.instance
- })
+ });
- Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader = JSMLoader;
+ Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader = !Cu.unload && JSMLoader;
for each (let component in components)
component.register();
reportError(e);
}
- if ([ADDON_UPGRADE, ADDON_DOWNGRADE, ADDON_UNINSTALL].indexOf(reason) >= 0)
+ if (~[ADDON_UPGRADE, ADDON_DOWNGRADE, ADDON_UNINSTALL].indexOf(reason))
Services.obs.notifyObservers(null, "dactyl-purge", null);
- Services.obs.notifyObservers(null, "dactyl-cleanup", null);
- Services.obs.notifyObservers(null, "dactyl-cleanup-modules", null);
+ Services.obs.notifyObservers(null, "dactyl-cleanup", reasonToString(reason));
+ Services.obs.notifyObservers(null, "dactyl-cleanup-modules", reasonToString(reason));
JSMLoader.purge();
for each (let [category, entry] in categories)
function StringChannel(data, contentType, uri) {
let channel = services.StreamChannel(uri);
- channel.contentStream = services.StringStream(data);
+ channel.contentStream = services.CharsetConv("UTF-8").convertToInputStream(data);
if (contentType)
channel.contentType = contentType;
channel.contentCharset = "UTF-8";
if (doctype) {
this.writes.push(doctype + "[\n");
try {
- this.writes.push(services.io.newChannel(url, null, null).open())
+ this.writes.push(services.io.newChannel(url, null, null).open());
}
catch (e) {}
if (!open)
this.writes.push("\n]");
- this.writes.push(post)
+ this.writes.push(post);
}
this.writes.push(channelStream);
/** @scope modules */
+/**
+ * A user-defined input mode binding of a typed string to an automatically
+ * inserted expansion string.
+ *
+ * Abbreviations have a left-hand side (LHS) whose text is replaced by that of
+ * the right-hand side (RHS) when triggered by an Input mode expansion key.
+ * E.g. an abbreviation with a LHS of "gop" and RHS of "Grand Old Party" will
+ * replace the former with the latter.
+ *
+ * @param {[Mode]} modes The modes in which this abbreviation is active.
+ * @param {string} lhs The left hand side of the abbreviation; the text to
+ * be replaced.
+ * @param {string|function(nsIEditor):string} rhs The right hand side of
+ * the abbreviation; the replacement text. This may either be a string
+ * literal or a function that will be passed the appropriate nsIEditor.
+ * @private
+ */
var Abbreviation = Class("Abbreviation", {
init: function (modes, lhs, rhs) {
this.modes = modes.sort();
this.rhs = rhs;
},
+ /**
+ * Returns true if this abbreviation's LHS and RHS are equal to those in
+ * *other*.
+ *
+ * @param {Abbreviation} other The abbreviation to test.
+ * @returns {boolean} The result of the comparison.
+ */
equals: function (other) this.lhs == other.lhs && this.rhs == other.rhs,
+ /**
+ * Returns the abbreviation's expansion text.
+ *
+ * @param {nsIEditor} editor The editor in which abbreviation expansion is
+ * occurring.
+ * @returns {string}
+ */
expand: function (editor) String(callable(this.rhs) ? this.rhs(editor) : this.rhs),
+ /**
+ * Returns true if this abbreviation is defined for all *modes*.
+ *
+ * @param {[Mode]} modes The modes to test.
+ * @returns {boolean} The result of the comparison.
+ */
modesEqual: function (modes) array.equals(this.modes, modes),
+ /**
+ * Returns true if this abbreviation is defined for *mode*.
+ *
+ * @param {Mode} mode The mode to test.
+ * @returns {boolean} The result of the comparison.
+ */
inMode: function (mode) this.modes.some(function (_mode) _mode == mode),
+ /**
+ * Returns true if this abbreviation is defined in any of *modes*.
+ *
+ * @param {[Modes]} modes The modes to test.
+ * @returns {boolean} The result of the comparison.
+ */
inModes: function (modes) modes.some(function (mode) this.inMode(mode), this),
+ /**
+ * Remove *mode* from the list of supported modes for this abbreviation.
+ *
+ * @param {Mode} mode The mode to remove.
+ */
removeMode: function (mode) {
this.modes = this.modes.filter(function (m) m != mode).sort();
},
+ /**
+ * @property {string} The mode display characters associated with the
+ * supported mode combination.
+ */
get modeChar() Abbreviation.modeChar(this.modes)
}, {
modeChar: function (_modes) {
this._store = {};
},
+ /** @property {boolean} True if there are no abbreviations. */
get empty() !values(this._store).nth(util.identity, 0),
/**
*
* @param {Mode} mode The mode of the abbreviation.
* @param {string} lhs The LHS of the abbreviation.
+ * @returns {Abbreviation} The matching abbreviation.
*/
get: function (mode, lhs) {
let abbrevs = this._store[mode];
- return abbrevs && set.has(abbrevs, lhs) ? abbrevs[lhs] : null;
+ return abbrevs && Set.has(abbrevs, lhs) ? abbrevs[lhs] : null;
},
/**
- * @property {Abbreviation[]} The list of the abbreviations merged from
+ * @property {[Abbreviation]} The list of the abbreviations merged from
* each mode.
*/
get merged() {
},
/**
- * Lists all abbreviations matching *modes* and *lhs*.
+ * Lists all abbreviations matching *modes*, *lhs* and optionally *hives*.
*
* @param {Array} modes List of modes.
* @param {string} lhs The LHS of the abbreviation.
+ * @param {[Hive]} hives List of hives.
+ * @optional
*/
- list: function (modes, lhs) {
- let hives = contexts.allGroups.abbrevs.filter(function (h) !h.empty);
+ list: function (modes, lhs, hives) {
+ let hives = hives || contexts.allGroups.abbrevs.filter(function (h) !h.empty);
function abbrevs(hive)
hive.merged.filter(function (abbr) (abbr.inModes(modes) && abbr.lhs.indexOf(lhs) == 0));
let list = <table>
<tr highlight="Title">
<td/>
- <td style="padding-right: 1em;">Mode</td>
- <td style="padding-right: 1em;">Abbrev</td>
- <td style="padding-right: 1em;">Replacement</td>
+ <td style="padding-right: 1em;">{_("title.Mode")}</td>
+ <td style="padding-right: 1em;">{_("title.Abbrev")}</td>
+ <td style="padding-right: 1em;">{_("title.Replacement")}</td>
</tr>
<col style="min-width: 6em; padding-right: 1em;"/>
{
// TODO: Move this to an ItemList to show this automatically
if (list.*.length() === list.text().length() + 2)
- dactyl.echomsg(_("abbrev.none"));
+ dactyl.echomsg(_("abbreviation.none"));
else
commandline.commandOutput(list);
}
context.completions = group.merged.filter(fn);
};
},
-
commands: function () {
function addAbbreviationCommands(modes, ch, modeDescription) {
modes.sort();
dactyl.assert(!args.length || abbreviations._check.test(lhs),
_("error.invalidArgument"));
- if (!rhs)
- abbreviations.list(modes, lhs || "");
+ if (!rhs) {
+ let hives = args.explicitOpts["-group"] ? [args["-group"]] : null;
+ abbreviations.list(modes, lhs || "", hives);
+ }
else {
if (args["-javascript"])
rhs = contexts.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
args["-group"].add(modes, lhs, rhs);
}
}, {
+ identifier: "abbreviate",
completer: function (context, args) {
if (args.length == 1)
return completion.abbreviation(context, modes, args["-group"]);
if (args.bang)
args["-group"].clear(modes);
else if (!args["-group"].remove(modes, args[0]))
- return dactyl.echoerr(_("abbrev.noSuch"));
+ return dactyl.echoerr(_("abbreviation.noSuch"));
}, {
argCount: "?",
bang: true,
}
addAbbreviationCommands([modes.INSERT, modes.COMMAND_LINE], "", "");
- addAbbreviationCommands([modes.INSERT], "i", "insert");
- addAbbreviationCommands([modes.COMMAND_LINE], "c", "command line");
+ [modes.INSERT, modes.COMMAND_LINE].forEach(function (mode) {
+ addAbbreviationCommands([mode], mode.char, mode.displayName);
+ });
}
});
*
* @param {string} event The event name filter.
* @param {string} pattern The URL pattern filter.
- * @returns {AutoCommand[]}
+ * @returns {[AutoCommand]}
*/
get: function (event, pattern) {
return this._store.filter(function (autoCmd) autoCmd.match(event, regexp));
remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.closure.remove }),
/**
- * Lists all autocommands with a matching *event* and *regexp*.
+ * Lists all autocommands with a matching *event*, *regexp* and optionally
+ * *hives*.
*
* @param {string} event The event name filter.
* @param {string} regexp The URL pattern filter.
+ * @param {[Hive]} hives List of hives.
+ * @optional
*/
- list: function (event, regexp) {
+ list: function (event, regexp, hives) {
+
+ let hives = hives || this.activeHives;
function cmds(hive) {
let cmds = {};
<td colspan="3">----- Auto Commands -----</td>
</tr>
{
- template.map(this.activeHives, function (hive)
+ template.map(hives, function (hive)
<tr highlight="Title">
<td colspan="3">{hive.name}</td>
</tr> +
let lastPattern = null;
var { url, doc } = args;
if (url)
- uri = util.newURI(url);
+ uri = util.createURI(url);
else
var { uri, doc } = buffer;
args["-group"].remove(event, regexp); // remove all
}
else
- autocommands.list(event, regexp); // list all
+ autocommands.list(event, regexp, args.explicitOpts["-group"] ? [args["-group"]] : null); // list all
}
}, {
bang: true,
// also includes methods for dealing with keywords and search engines
var Bookmarks = Module("bookmarks", {
init: function () {
+ this.timer = Timer(0, 100, function () {
+ this.checkBookmarked(buffer.uri);
+ }, this);
+
storage.addObserver("bookmark-cache", function (key, event, arg) {
if (["add", "change", "remove"].indexOf(event) >= 0)
autocommands.trigger("Bookmark" + event[0].toUpperCase() + event.substr(1),
valueOf: function () arg
}
}, arg));
- statusline.updateStatus();
+ bookmarks.timer.tell();
}, window);
},
+ signals: {
+ "browser.locationChange": function (webProgress, request, uri) {
+ statusline.bookmarked = false;
+ this.checkBookmarked(uri);
+ }
+ },
+
get format() ({
anchored: false,
title: ["URL", "Info"],
*
* @param {Element} elem A form element for which to add a keyword.
*/
- addSearchKeyword: function (elem) {
+ addSearchKeyword: function addSearchKeyword(elem) {
if (elem instanceof HTMLFormElement || elem.form)
var [url, post, charset] = util.parseForm(elem);
else
commands.commandToString({ command: "bmark", options: options, arguments: [url] }) + " -keyword ");
},
+ checkBookmarked: function checkBookmarked(uri) {
+ if (PlacesUtils.asyncGetBookmarkIds)
+ PlacesUtils.asyncGetBookmarkIds(uri, function (ids) {
+ statusline.bookmarked = ids.length;
+ });
+ else
+ this.timeout(function () {
+ statusline.bookmarked = bookmarkcache.isBookmarked(uri);
+ });
+ },
+
/**
* Toggles the bookmarked state of the given URL. If the URL is
* bookmarked, all bookmarks for said URL are removed.
if (!alias)
alias = "search"; // for search engines which we can't find a suitable alias
- if (set.has(aliases, alias))
+ if (Set.has(aliases, alias))
alias += ++aliases[alias];
else
aliases[alias] = 0;
getSuggestions: function getSuggestions(engineName, query, callback) {
const responseType = "application/x-suggestions+json";
- let engine = set.has(this.searchEngines, engineName) && this.searchEngines[engineName];
+ let engine = Set.has(this.searchEngines, engineName) && this.searchEngines[engineName];
if (engine && engine.supportsResponseType(responseType))
var queryURI = engine.getSubmission(query, responseType).uri.spec;
if (!queryURI)
param = query.substr(offset + 1);
}
- var engine = set.has(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword];
+ var engine = Set.has(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword];
if (engine) {
if (engine.searchForm && !param)
return engine.searchForm;
completer: function title(context, args) {
let frames = buffer.allFrames();
if (!args.bang)
- return [
- [win.document.title, frames.length == 1 ? "Current Location" : "Frame: " + win.location.href]
+ return [
+ [win.document.title, frames.length == 1 ? /*L*/"Current Location" : /*L*/"Frame: " + win.location.href]
for ([, win] in Iterator(frames))];
context.keys.text = "title";
context.keys.description = "url";
context.title = ["Page URL"];
let frames = buffer.allFrames();
context.completions = [
- [win.document.documentURI, frames.length == 1 ? "Current Location" : "Frame: " + win.document.title]
+ [win.document.documentURI, frames.length == 1 ? /*L*/"Current Location" : /*L*/"Frame: " + win.document.title]
for ([, win] in Iterator(frames))];
return;
}
"Delete a bookmark",
function (args) {
if (args.bang)
- commandline.input("This will delete all bookmarks. Would you like to continue? (yes/[no]) ",
+ commandline.input(_("bookmark.prompt.deleteAll") + " ",
function (resp) {
if (resp && resp.match(/^y(es)?$/i)) {
bookmarks.remove(Object.keys(bookmarkcache.bookmarks));
- dactyl.echomsg(_("bookmark.allGone"));
+ dactyl.echomsg(_("bookmark.allDeleted"));
}
});
else {
let context = CompletionContext(args.join(" "));
context.fork("bookmark", 0, completion, "bookmark",
args["-tags"], { keyword: args["-keyword"], title: args["-title"] });
- var deletedCount = bookmarks.remove(context.allItems.items.map(function (item) item.item.id));
+ deletedCount = bookmarks.remove(context.allItems.items.map(function (item) item.item.id));
}
dactyl.echomsg({ message: _("bookmark.deleted", deletedCount) });
if (item && item.url.indexOf("%s") > -1)
context.fork("keyword/" + keyword, keyword.length + space.length, null, function (context) {
context.format = history.format;
- context.title = [keyword + " Quick Search"];
+ context.title = [/*L*/keyword + " Quick Search"];
// context.background = true;
context.compare = CompletionContext.Sort.unsorted;
context.generate = function () {
return;
let ctxt = context.fork(name, 0);
- ctxt.title = [engine.description + " Suggestions"];
+ ctxt.title = [/*L*/engine.description + " Suggestions"];
ctxt.keys = { text: util.identity, description: function () "" };
ctxt.compare = CompletionContext.Sort.unsorted;
ctxt.filterFunc = null;
"content-document-global-created": function (win, uri) {
let top = util.topWindow(win);
- if (top == window)
- this._triggerLoadAutocmd("PageLoadPre", win.document, win.location.href != "null" ? window.location.href : uri);
+ if (uri == "null")
+ uri = null;
+
+ if (top == window && (win.location.href || uri))
+ this._triggerLoadAutocmd("PageLoadPre", win.document, win.location.href || uri);
}
},
title: doc.title
};
- if (dactyl.has("tabs")) {
+ if (!dactyl.has("tabs"))
+ update(args, { doc: doc, win: doc.defaultView });
+ else {
args.tab = tabs.getContentIndex(doc) + 1;
args.doc = {
valueOf: function () doc,
toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentDocument"
};
+ args.win = {
+ valueOf: function () doc.defaultView,
+ toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentWindow"
+ };
}
autocommands.trigger(name, args);
progressListener: {
// XXX: function may later be needed to detect a canceled synchronous openURL()
onStateChange: util.wrapCallback(function onStateChange(webProgress, request, flags, status) {
- onStateChange.superapply(this, arguments);
- // STATE_IS_DOCUMENT | STATE_IS_WINDOW is important, because we also
- // receive statechange events for loading images and other parts of the web page
- if (flags & (Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
+ const L = Ci.nsIWebProgressListener;
+
+ if (request)
dactyl.applyTriggerObserver("browser.stateChange", arguments);
+
+ if (flags & (L.STATE_IS_DOCUMENT | L.STATE_IS_WINDOW)) {
// This fires when the load event is initiated
// only thrown for the current tab, not when another tab changes
- if (flags & Ci.nsIWebProgressListener.STATE_START) {
+ if (flags & L.STATE_START) {
while (document.commandDispatcher.focusedWindow == webProgress.DOMWindow
&& modes.have(modes.INPUT))
modes.pop();
}
- else if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
+ else if (flags & L.STATE_STOP) {
// Workaround for bugs 591425 and 606877, dactyl bug #81
config.browser.mCurrentBrowser.collapsed = false;
if (!dactyl.focusedElement || dactyl.focusedElement === document.documentElement)
dactyl.focusContent();
}
}
+
+ onStateChange.superapply(this, arguments);
}),
onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
onSecurityChange.superapply(this, arguments);
["o"], "Open one or more URLs",
function () { CommandExMode().open("open "); });
+ function decode(uri) util.losslessDecodeURI(uri)
+ .replace(/%20(?!(?:%20)*$)/g, " ")
+ .replace(RegExp(options["urlseparator"], "g"), encodeURIComponent);
+
mappings.add([modes.NORMAL], ["O"],
"Open one or more URLs, based on current location",
- function () { CommandExMode().open("open " + buffer.uri.spec); });
+ function () { CommandExMode().open("open " + decode(buffer.uri.spec)); });
mappings.add([modes.NORMAL], ["t"],
"Open one or more URLs in a new tab",
mappings.add([modes.NORMAL], ["T"],
"Open one or more URLs in a new tab, based on current location",
- function () { CommandExMode().open("tabopen " + buffer.uri.spec); });
+ function () { CommandExMode().open("tabopen " + decode(buffer.uri.spec)); });
mappings.add([modes.NORMAL], ["w"],
"Open one or more URLs in a new window",
mappings.add([modes.NORMAL], ["W"],
"Open one or more URLs in a new window, based on current location",
- function () { CommandExMode().open("winopen " + buffer.uri.spec); });
+ function () { CommandExMode().open("winopen " + decode(buffer.uri.spec)); });
- mappings.add([modes.NORMAL], ["~"],
+ mappings.add([modes.NORMAL], ["<open-home-directory>", "~"],
"Open home directory",
function () { dactyl.open("~"); });
- mappings.add([modes.NORMAL], ["gh"],
+ mappings.add([modes.NORMAL], ["<open-homepage>", "gh"],
"Open homepage",
function () { BrowserHome(); });
- mappings.add([modes.NORMAL], ["gH"],
+ mappings.add([modes.NORMAL], ["<tab-open-homepage>", "gH"],
"Open homepage in a new tab",
function () {
let homepages = gHomeButton.getHomePage();
dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
});
- mappings.add([modes.MAIN], ["<C-l>"],
+ mappings.add([modes.MAIN], ["<redraw-screen>", "<C-l>"],
"Redraw the screen",
function () { ex.redraw(); });
}
this.evaluateXPath = util.evaluateXPath;
this.pageInfo = {};
+ this.addPageInfoSection("e", "Search Engines", function (verbose) {
+
+ let n = 1;
+ let nEngines = 0;
+ for (let { document: doc } in values(buffer.allFrames())) {
+ let engines = util.evaluateXPath(["link[@href and @rel='search' and @type='application/opensearchdescription+xml']"], doc);
+ nEngines += engines.snapshotLength;
+
+ if (verbose)
+ for (let link in engines)
+ yield [link.title || /*L*/ "Engine " + n++,
+ <a xmlns={XHTML} href={link.href} onclick="if (event.button == 0) { window.external.AddSearchProvider(this.href); return false; }" highlight="URL">{link.href}</a>];
+ }
+
+ if (!verbose && nEngines)
+ yield nEngines + /*L*/" engine" + (nEngines > 1 ? "s" : "");
+ });
+
this.addPageInfoSection("f", "Feeds", function (verbose) {
const feedTypes = {
"application/rss+xml": "RSS",
}
if (!verbose && nFeed)
- yield nFeed + " feed" + (nFeed > 1 ? "s" : "");
+ yield nFeed + /*L*/" feed" + (nFeed > 1 ? "s" : "");
});
this.addPageInfoSection("g", "General Info", function (verbose) {
if (!verbose) {
if (pageSize[0])
- yield (pageSize[1] || pageSize[0]) + " bytes";
+ yield (pageSize[1] || pageSize[0]) + /*L*/" bytes";
yield lastMod;
return;
}
});
this.addPageInfoSection("m", "Meta Tags", function (verbose) {
+ if (!verbose)
+ return [];
+
// get meta tag data, sort and put into pageMeta[]
let metaNodes = buffer.focusedFrame.document.getElementsByTagName("meta");
.sort(function (a, b) util.compareIgnoreCase(a[0], b[0]));
});
+ let identity = window.gIdentityHandler;
+ this.addPageInfoSection("s", "Security", function (verbose) {
+ if (!verbose || !identity)
+ return; // For now
+
+ // Modified from Firefox
+ function location(data) array.compact([
+ data.city, data.state, data.country
+ ]).join(", ");
+
+ switch (statusline.security) {
+ case "secure":
+ case "extended":
+ var data = identity.getIdentityData();
+
+ yield ["Host", identity.getEffectiveHost()];
+
+ if (statusline.security === "extended")
+ yield ["Owner", data.subjectOrg];
+ else
+ yield ["Owner", _("pageinfo.s.ownerUnverified", data.subjectOrg)];
+
+ if (location(data).length)
+ yield ["Location", location(data)];
+
+ yield ["Verified by", data.caOrg];
+
+ if (identity._overrideService.hasMatchingOverride(identity._lastLocation.hostname,
+ (identity._lastLocation.port || 443),
+ data.cert, {}, {}))
+ yield ["User exception", /*L*/"true"];
+ break;
+ }
+ });
+
dactyl.commands["buffer.viewSource"] = function (event) {
let elem = event.originalTarget;
- buffer.viewSource([elem.getAttribute("href"), Number(elem.getAttribute("line"))]);
+ let obj = { url: elem.getAttribute("href"), line: Number(elem.getAttribute("line")) };
+ if (elem.hasAttribute("column"))
+ obj.column = elem.getAttribute("column");
+
+ buffer.viewSource(obj);
};
},
allFrames: function allFrames(win, focusedFirst) {
let frames = [];
(function rec(frame) {
- if (frame.document.body instanceof HTMLBodyElement)
+ if (true || frame.document.body instanceof HTMLBodyElement)
frames.push(frame);
Array.forEach(frame.frames, rec);
})(win || content);
* @returns {string}
*/
get currentWord() Buffer.currentWord(this.focusedFrame),
- getCurrentWord: deprecated("buffer.currentWord", function getCurrentWord() this.currentWord),
+ getCurrentWord: deprecated("buffer.currentWord", function getCurrentWord() Buffer.currentWord(this.focusedFrame, true)),
/**
* Returns true if a scripts are allowed to focus the given input
focusAllowed: function focusAllowed(elem) {
if (elem instanceof Window && !Editor.getEditor(elem))
return true;
+
let doc = elem.ownerDocument || elem.document || elem;
- return !options["strictfocus"] || doc.dactylFocusAllowed;
+ switch (options.get("strictfocus").getKey(doc.documentURIObject || util.newURI(doc.documentURI), "moderate")) {
+ case "despotic":
+ return elem.dactylFocusAllowed || elem.frameElement && elem.frameElement.dactylFocusAllowed;
+ case "moderate":
+ return doc.dactylFocusAllowed || elem.frameElement && elem.frameElement.ownerDocument.dactylFocusAllowed;
+ default:
+ return true;
+ }
},
/**
*/
focusElement: function focusElement(elem) {
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
+ elem.dactylFocusAllowed = true;
win.document.dactylFocusAllowed = true;
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
},
/**
- * Find the counth last link on a page matching one of the given
+ * Find the *count*th last link on a page matching one of the given
* regular expressions, or with a @rel or @rev attribute matching
* the given relation. Each frame is searched beginning with the
* last link and progressing to the first, once checking for
*/
followLink: function followLink(elem, where) {
let doc = elem.ownerDocument;
- let view = doc.defaultView;
+ let win = doc.defaultView;
let { left: offsetX, top: offsetY } = elem.getBoundingClientRect();
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey
}));
});
+ let sel = util.selectionController(win);
+ sel.getSelection(sel.SELECTION_FOCUS_REGION).collapseToStart();
});
},
* @property {nsISelectionController} The current document's selection
* controller.
*/
- get selectionController() config.browser.docShell
- .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay)
- .QueryInterface(Ci.nsISelectionController),
+ get selectionController() util.selectionController(this.focusedFrame),
/**
* Opens the appropriate context menu for *elem*.
try {
window.urlSecurityCheck(uri.spec, doc.nodePrincipal);
- io.CommandFileMode("Save link: ", {
+ io.CommandFileMode(_("buffer.prompt.saveLink") + " ", {
onSubmit: function (path) {
let file = io.File(path);
if (file.exists() && file.isDirectory())
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
let downloadListener = new window.DownloadListener(window,
- services.Transfer(uri, services.io.newFileURI(file), "",
+ services.Transfer(uri, File(file).URI, "",
null, null, null, persist));
persist.progressListener = update(Object.create(downloadListener), {
- onStateChange: function onStateChange(progress, request, flag, status) {
- if (callback && (flag & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
- dactyl.trapErrors(callback, self, uri, file, progress, request, flag, status);
+ onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
+ if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
+ dactyl.trapErrors(callback, self, uri, file, progress, request, flags, status);
return onStateChange.superapply(this, arguments);
- }
+ })
});
persist.saveURI(uri, null, null, null, null, file);
*/
findScrollable: function findScrollable(dir, horizontal) {
function find(elem) {
- while (!(elem instanceof Element) && elem.parentNode)
+ while (elem && !(elem instanceof Element) && elem.parentNode)
elem = elem.parentNode;
for (; elem && elem.parentNode instanceof Element; elem = elem.parentNode)
if (Buffer.isScrollable(elem, dir, horizontal))
doc.documentElement);
}
let doc = this.focusedFrame.document;
- return elem || doc.body || doc.documentElement;
+ return dactyl.assert(elem || doc.body || doc.documentElement);
},
/**
return win;
},
+ /**
+ * Finds the next visible element for the node path in 'jumptags'
+ * for *arg*.
+ *
+ * @param {string} arg The element in 'jumptags' to use for the search.
+ * @param {number} count The number of elements to jump.
+ * @optional
+ * @param {boolean} reverse If true, search backwards. @optional
+ */
+ findJump: function findJump(arg, count, reverse) {
+ const FUDGE = 10;
+
+ let path = options["jumptags"][arg];
+ dactyl.assert(path, _("error.invalidArgument", arg));
+
+ let distance = reverse ? function (rect) -rect.top : function (rect) rect.top;
+ let elems = [[e, distance(e.getBoundingClientRect())] for (e in path.matcher(this.focusedFrame.document))]
+ .filter(function (e) e[1] > FUDGE)
+ .sort(function (a, b) a[1] - b[1])
+
+ let idx = Math.min((count || 1) - 1, elems.length);
+ dactyl.assert(idx in elems);
+
+ let elem = elems[idx][0];
+ elem.scrollIntoView(true);
+
+ let sel = elem.ownerDocument.defaultView.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(RangeFind.endpoint(RangeFind.nodeRange(elem), true));
+ },
+
// TODO: allow callback for filtering out unwanted frames? User defined?
/**
* Shifts the focus to another frame within the buffer. Each buffer
* contains at least one frame.
*
- * @param {number} count The number of frames to skip through. A negative
+ * @param {number} count The number of frames to skip through. A negative
* count skips backwards.
*/
shiftFrameFocus: function shiftFrameFocus(count) {
* @param {Node} elem The element to query.
*/
showElementInfo: function showElementInfo(elem) {
- dactyl.echo(<>Element:<br/>{util.objectToString(elem, true)}</>, commandline.FORCE_MULTILINE);
+ dactyl.echo(<><!--L-->Element:<br/>{util.objectToString(elem, true)}</>, commandline.FORCE_MULTILINE);
},
/**
showPageInfo: function showPageInfo(verbose, sections) {
// Ctrl-g single line output
if (!verbose) {
- let file = content.location.pathname.split("/").pop() || "[No Name]";
- let title = content.document.title || "[No Title]";
+ let file = content.location.pathname.split("/").pop() || _("buffer.noName");
+ let title = content.document.title || _("buffer.noTitle");
- let info = template.map("gf",
+ let info = template.map(sections || options["pageinfo"],
function (opt) template.map(buffer.pageInfo[opt].action(), util.identity, ", "),
", ");
if (bookmarkcache.isBookmarked(this.URL))
- info += ", bookmarked";
+ info += ", " + _("buffer.bookmarked");
let pageInfoText = <>{file.quote()} [{info}] {title}</>;
dactyl.echo(pageInfoText, commandline.FORCE_SINGLELINE);
* specified *url*. Either the default viewer or the configured external
* editor is used.
*
- * @param {string} url The URL of the source.
- * @default The current buffer.
+ * @param {string|object|null} loc If a string, the URL of the source,
+ * otherwise an object with some or all of the following properties:
+ *
+ * url: The URL to view.
+ * doc: The document to view.
+ * line: The line to select.
+ * column: The column to select.
+ *
+ * If no URL is provided, the current document is used.
+ * @default The current buffer.
* @param {boolean} useExternalEditor View the source in the external editor.
*/
- viewSource: function viewSource(url, useExternalEditor) {
+ viewSource: function viewSource(loc, useExternalEditor) {
let doc = this.focusedFrame.document;
- if (isArray(url)) {
- if (options.get("editor").has("line"))
- this.viewSourceExternally(url[0] || doc, url[1]);
+ if (isObject(loc)) {
+ if (options.get("editor").has("line") || !loc.url)
+ this.viewSourceExternally(loc.doc || loc.url || doc, loc);
else
window.openDialog("chrome://global/content/viewSource.xul",
"_blank", "all,dialog=no",
- url[0], null, null, url[1]);
+ loc.url, null, null, loc.line);
}
else {
if (useExternalEditor)
- this.viewSourceExternally(url || doc);
+ this.viewSourceExternally(loc || doc);
else {
- url = url || doc.location.href;
+ let url = loc || doc.location.href;
const PREFIX = "view-source:";
if (url.indexOf(PREFIX) == 0)
url = url.substr(PREFIX.length);
* immediately.
*
* @param {Document} doc The document to view.
+ * @param {function|object} callback If a function, the callback to be
+ * called with two arguments: the nsIFile of the file, and temp, a
+ * boolean which is true if the file is temporary. Otherwise, an object
+ * with line and column properties used to determine where to open the
+ * source.
+ * @optional
*/
viewSourceExternally: Class("viewSourceExternally",
XPCOM([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), {
init: function init(doc, callback) {
this.callback = callable(callback) ? callback :
function (file, temp) {
- editor.editFileExternally({ file: file.path, line: callback },
+ editor.editFileExternally(update({ file: file.path }, callback || {}),
function () { temp && file.remove(false); });
return true;
};
return null;
},
- onStateChange: function onStateChange(progress, request, flag, status) {
- if ((flag & this.STATE_STOP) && status == 0) {
+ onStateChange: function onStateChange(progress, request, flags, status) {
+ if ((flags & this.STATE_STOP) && status == 0) {
try {
var ok = this.callback(this.file, true);
}
* Adjusts the page zoom of the current buffer relative to the
* current zoom level.
*
- * @param {number} steps The integral number of natural fractions by
- * which to adjust the current page zoom. If positive, the zoom
- * level is increased, if negative it is decreased.
+ * @param {number} steps The integral number of natural fractions by which
+ * to adjust the current page zoom. If positive, the zoom level is
+ * increased, if negative it is decreased.
* @param {boolean} fullZoom If true, zoom all content of the page,
- * including raster images. If false, zoom only text. If omitted,
- * use the current zoom function. @optional
- * @throws {FailedAssertion} if the buffer's zoom level is already
- * at its extreme in the given direction.
+ * including raster images. If false, zoom only text. If omitted, use
+ * the current zoom function. @optional
+ * @throws {FailedAssertion} if the buffer's zoom level is already at its
+ * extreme in the given direction.
*/
bumpZoomLevel: function bumpZoomLevel(steps, fullZoom) {
if (fullZoom === undefined)
this.setZoom(Math.round(values[i] * 100), fullZoom);
},
- getAllFrames: deprecated("buffer.allFrames", function getAllFrames() buffer.getAllFrames.apply(buffer, arguments)),
+ getAllFrames: deprecated("buffer.allFrames", "allFrames"),
scrollTop: deprecated("buffer.scrollToPercent", function scrollTop() buffer.scrollToPercent(null, 0)),
scrollBottom: deprecated("buffer.scrollToPercent", function scrollBottom() buffer.scrollToPercent(null, 100)),
scrollStart: deprecated("buffer.scrollToPercent", function scrollStart() buffer.scrollToPercent(0, null)),
scrollColumns: deprecated("buffer.scrollHorizontal", function scrollColumns(cols) buffer.scrollHorizontal("columns", cols)),
scrollPages: deprecated("buffer.scrollHorizontal", function scrollPages(pages) buffer.scrollVertical("pages", pages)),
scrollTo: deprecated("Buffer.scrollTo", function scrollTo(x, y) content.scrollTo(x, y)),
- textZoom: deprecated("buffer.zoomValue and buffer.fullZoom", function textZoom() config.browser.markupDocumentViewer.textZoom * 100)
+ textZoom: deprecated("buffer.zoomValue/buffer.fullZoom", function textZoom() config.browser.markupDocumentViewer.textZoom * 100)
}, {
PageInfo: Struct("PageInfo", "name", "title", "action")
.localize("title"),
*
* @returns {string}
*/
- currentWord: function currentWord(win) {
+ currentWord: function currentWord(win, select) {
let selection = win.getSelection();
if (selection.rangeCount == 0)
return "";
let range = selection.getRangeAt(0).cloneRange();
- if (range.collapsed) {
+ if (range.collapsed && range.startContainer instanceof Text) {
let re = options.get("iskeyword").regexp;
Editor.extendRange(range, true, re, true);
Editor.extendRange(range, false, re, true);
}
+ if (select) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
return util.domToString(range);
},
var names = [];
if (node.title)
- names.push([node.title, "Page Name"]);
+ names.push([node.title, /*L*/"Page Name"]);
if (node.alt)
- names.push([node.alt, "Alternate Text"]);
+ names.push([node.alt, /*L*/"Alternate Text"]);
if (!isinstance(node, Document) && node.textContent)
- names.push([node.textContent, "Link Text"]);
+ names.push([node.textContent, /*L*/"Link Text"]);
names.push([decodeURIComponent(url.replace(/.*?([^\/]*)\/*$/, "$1")), "File Name"]);
elem.scrollLeft = left;
if (top != null)
elem.scrollTop = top;
+
+ if (util.haveGecko("2.0") && !util.haveGecko("7.*"))
+ elem.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
+ .redraw();
},
/**
else
throw Error();
+ dactyl.assert(number < 0 ? elem.scrollLeft > 0 : elem.scrollLeft < elem.scrollWidth - elem.clientWidth);
+
let left = elem.dactylScrollDestX !== undefined ? elem.dactylScrollDestX : elem.scrollLeft;
elem.dactylScrollDestX = undefined;
- dactyl.assert(number < 0 ? left > 0 : left < elem.scrollWidth - elem.clientWidth);
Buffer.scrollTo(elem, left + number * increment, null);
},
else
throw Error();
+ dactyl.assert(number < 0 ? elem.scrollTop > 0 : elem.scrollTop < elem.scrollHeight - elem.clientHeight);
+
let top = elem.dactylScrollDestY !== undefined ? elem.dactylScrollDestY : elem.scrollTop;
elem.dactylScrollDestY = undefined;
- dactyl.assert(number < 0 ? top > 0 : top < elem.scrollHeight - elem.clientHeight);
Buffer.scrollTo(elem, null, top + number * increment);
},
},
openUploadPrompt: function openUploadPrompt(elem) {
- io.CommandFileMode("Upload file: ", {
+ io.CommandFileMode(_("buffer.prompt.uploadFile") + " ", {
onSubmit: function onSubmit(path) {
let file = io.File(path);
dactyl.assert(file.exists());
// FIXME: arg handling is a bit of a mess, check for filename
dactyl.assert(!arg || arg[0] == ">" && !util.OS.isWindows,
- _("error.trailing"));
+ _("error.trailingCharacters"));
+
+ const PRINTER = "PostScript/default";
+ const BRANCH = "print.printer_" + PRINTER + ".";
prefs.withContext(function () {
if (arg) {
- prefs.set("print.print_to_file", "true");
- prefs.set("print.print_to_filename", io.File(arg.substr(1)).path);
+ prefs.set("print.print_printer", PRINTER);
+
+ prefs.set( "print.print_to_file", true);
+ prefs.set(BRANCH + "print_to_file", true);
+
+ prefs.set( "print.print_to_filename", io.File(arg.substr(1)).path);
+ prefs.set(BRANCH + "print_to_filename", io.File(arg.substr(1)).path);
+
dactyl.echomsg(_("print.toFile", arg.substr(1)));
}
else
level = Math.constrain(level, Buffer.ZOOM_MIN, Buffer.ZOOM_MAX);
}
else
- dactyl.assert(false, _("error.trailing"));
+ dactyl.assert(false, _("error.trailingCharacters"));
buffer.setZoom(level, args.bang);
},
let styles = iter([s.title, []] for (s in values(buffer.alternateStyleSheets))).toObject();
buffer.alternateStyleSheets.forEach(function (style) {
- styles[style.title].push(style.href || "inline");
+ styles[style.title].push(style.href || _("style.inline"));
});
context.completions = [[title, href.join(", ")] for ([title, href] in Iterator(styles))];
};
- completion.buffer = function buffer(context) {
+ completion.buffer = function buffer(context, visible) {
let filter = context.filter.toLowerCase();
+
let defItem = { parent: { getTitle: function () "" } };
+
let tabGroups = {};
tabs.getGroups();
- tabs.allTabs.forEach(function (tab, i) {
+ tabs[visible ? "visibleTabs" : "allTabs"].forEach(function (tab, i) {
let group = (tab.tabItem || tab._tabViewTabItem || defItem).parent || defItem.parent;
- if (!set.has(tabGroups, group.id))
+ if (!Set.has(tabGroups, group.id))
tabGroups[group.id] = [group.getTitle(), []];
+
group = tabGroups[group.id];
group[1].push([i, tab.linkedBrowser]);
});
command: function () "tabs.select"
};
context.compare = CompletionContext.Sort.number;
- context.filters = [CompletionContext.Filter.textDescription];
+ context.filters[0] = CompletionContext.Filter.textDescription;
for (let [id, vals] in Iterator(tabGroups))
context.fork(id, 0, this, function (context, [name, browsers]) {
else if (i == tabs.index(tabs.alternate))
indicator = "#";
- let tab = tabs.getTab(i);
+ let tab = tabs.getTab(i, visible);
let url = browser.contentDocument.location.href;
i = i + 1;
return {
- text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
+ text: [i + ": " + (tab.label || /*L*/"(Untitled)"), i + ": " + url],
tab: tab,
id: i - 1,
url: url,
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
mappings.add([modes.NORMAL],
- ["<C-a>"], "Increment last number in URL",
+ ["<C-a>", "<increment-url-path>"], "Increment last number in URL",
function (args) { buffer.incrementURL(Math.max(args.count, 1)); },
{ count: true });
mappings.add([modes.NORMAL],
- ["<C-x>"], "Decrement last number in URL",
+ ["<C-x>", "<decrement-url-path>"], "Decrement last number in URL",
function (args) { buffer.incrementURL(-Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.NORMAL], ["gu"],
+ mappings.add([modes.NORMAL], ["gu", "<open-parent-path>"],
"Go to parent directory",
function (args) { buffer.climbUrlPath(Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.NORMAL], ["gU"],
+ mappings.add([modes.NORMAL], ["gU", "<open-root-path>"],
"Go to the root of the website",
function () { buffer.climbUrlPath(-1); });
},
{ count: true });
- mappings.add([modes.COMMAND], ["i", "<Insert>"],
- "Start caret mode",
+ mappings.add([modes.NORMAL], ["i", "<Insert>"],
+ "Start Caret mode",
function () { modes.push(modes.CARET); });
- mappings.add([modes.COMMAND], ["<C-c>"],
+ mappings.add([modes.NORMAL], ["<C-c>", "<stop-load>"],
"Stop loading the current web page",
function () { ex.stop(); });
"Scroll to the absolute right of the document",
function () { buffer.scrollToPercent(100, null); });
- mappings.add([modes.COMMAND], ["gg", "<Home>"],
+ mappings.add([modes.COMMAND], ["gg", "<Home>", "<scroll-top>"],
"Go to the top of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); },
{ count: true });
- mappings.add([modes.COMMAND], ["G", "<End>"],
+ mappings.add([modes.COMMAND], ["G", "<End>", "<scroll-bottom>"],
"Go to the end of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
{ count: true });
function (args) { buffer._scrollByScrollSize(args.count, false); },
{ count: true });
- mappings.add([modes.COMMAND], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-page-up>"],
+ mappings.add([modes.COMMAND], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-up-page>"],
"Scroll up a full page",
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["<C-f>", "<PageDown>", "<Space>", "<scroll-page-down>"],
+ mappings.add([modes.COMMAND], ["<Space>"],
+ "Scroll down a full page",
+ function (args) {
+ if (isinstance(content.document.activeElement, [HTMLInputElement, HTMLButtonElement]))
+ return Events.PASS;
+ buffer.scrollVertical("pages", Math.max(args.count, 1));
+ },
+ { count: true });
+
+ mappings.add([modes.COMMAND], ["<C-f>", "<PageDown>", "<scroll-down-page>"],
"Scroll down a full page",
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["]f", "<previous-frame>"],
+ mappings.add([modes.NORMAL], ["]f", "<previous-frame>"],
"Focus next frame",
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["[f", "<next-frame>"],
+ mappings.add([modes.NORMAL], ["[f", "<next-frame>"],
"Focus previous frame",
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["]]", "<next-page>"],
+ mappings.add([modes.NORMAL], ["["],
+ "Jump to the previous element as defined by 'jumptags'",
+ function (args) { buffer.findJump(args.arg, args.count, true); },
+ { arg: true, count: true });
+
+ mappings.add([modes.NORMAL], ["]"],
+ "Jump to the next element as defined by 'jumptags'",
+ function (args) { buffer.findJump(args.arg, args.count, false); },
+ { arg: true, count: true });
+
+ mappings.add([modes.NORMAL], ["{"],
+ "Jump to the previous paragraph",
+ function (args) { buffer.findJump("p", args.count, true); },
+ { count: true });
+
+ mappings.add([modes.NORMAL], ["}"],
+ "Jump to the next paragraph",
+ function (args) { buffer.findJump("p", args.count, false); },
+ { count: true });
+
+ mappings.add([modes.NORMAL], ["]]", "<next-page>"],
"Follow the link labeled 'next' or '>' if it exists",
function (args) {
buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true);
},
{ count: true });
- mappings.add([modes.COMMAND], ["[[", "<previous-page>"],
+ mappings.add([modes.NORMAL], ["[[", "<previous-page>"],
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
function (args) {
buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
},
{ count: true });
- mappings.add([modes.COMMAND], ["gf", "<view-source>"],
+ mappings.add([modes.NORMAL], ["gf", "<view-source>"],
"Toggle between rendered and source view",
function () { buffer.viewSource(null, false); });
- mappings.add([modes.COMMAND], ["gF", "<view-source-externally>"],
+ mappings.add([modes.NORMAL], ["gF", "<view-source-externally>"],
"View source with an external editor",
function () { buffer.viewSource(null, true); });
- mappings.add([modes.COMMAND], ["gi", "<focus-input>"],
+ mappings.add([modes.NORMAL], ["gi", "<focus-input>"],
"Focus last used input field",
function (args) {
let elem = buffer.lastInputField;
if (args.count >= 1 || !elem || !events.isContentNode(elem)) {
- let xpath = ["frame", "iframe", "input", "textarea[not(@disabled) and not(@readonly)]"];
+ let xpath = ["frame", "iframe", "input", "xul:textbox", "textarea[not(@disabled) and not(@readonly)]"];
let frames = buffer.allFrames(null, true);
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
return Editor.getEditor(elem.contentWindow);
- if (elem.readOnly || elem instanceof HTMLInputElement && !set.has(util.editableInputs, elem.type))
+ if (elem.readOnly || elem instanceof HTMLInputElement && !Set.has(util.editableInputs, elem.type))
return false;
let computedStyle = util.computedStyle(elem);
let rect = elem.getBoundingClientRect();
return computedStyle.visibility != "hidden" && computedStyle.display != "none" &&
- computedStyle.MozUserFocus != "ignore" && rect.width && rect.height;
+ (elem instanceof Ci.nsIDOMXULTextBoxElement || computedStyle.MozUserFocus != "ignore") &&
+ rect.width && rect.height;
});
dactyl.assert(elements.length > 0);
},
{ count: true });
- mappings.add([modes.COMMAND], ["gP"],
- "Open (]put) a URL based on the current clipboard contents in a new buffer",
+ function url() {
+ let url = dactyl.clipboardRead();
+ dactyl.assert(url, _("error.clipboardEmpty"));
+
+ let proto = /^([-\w]+):/.exec(url);
+ if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc && !RegExp(options["urlseparator"]).test(url))
+ return url.replace(/\s+/g, "");
+ return url;
+ }
+
+ mappings.add([modes.NORMAL], ["gP"],
+ "Open (put) a URL based on the current clipboard contents in a new background buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
+ dactyl.open(url(), { from: "paste", where: dactyl.NEW_TAB, background: true });
});
- mappings.add([modes.COMMAND], ["p", "<MiddleMouse>", "<open-clipboard-url>"],
+ mappings.add([modes.NORMAL], ["p", "<MiddleMouse>", "<open-clipboard-url>"],
"Open (put) a URL based on the current clipboard contents in the current buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url);
+ dactyl.open(url());
});
- mappings.add([modes.COMMAND], ["P", "<tab-open-clipboard-url>"],
+ mappings.add([modes.NORMAL], ["P", "<tab-open-clipboard-url>"],
"Open (put) a URL based on the current clipboard contents in a new buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB });
+ dactyl.open(url(), { from: "paste", where: dactyl.NEW_TAB });
});
// reloading
- mappings.add([modes.COMMAND], ["r", "<reload>"],
+ mappings.add([modes.NORMAL], ["r", "<reload>"],
"Reload the current web page",
function () { tabs.reload(tabs.getTab(), false); });
- mappings.add([modes.COMMAND], ["R", "<full-reload>"],
+ mappings.add([modes.NORMAL], ["R", "<full-reload>"],
"Reload while skipping the cache",
function () { tabs.reload(tabs.getTab(), true); });
});
// zooming
- mappings.add([modes.COMMAND], ["zi", "+", "<text-zoom-in>"],
+ mappings.add([modes.NORMAL], ["zi", "+", "<text-zoom-in>"],
"Enlarge text zoom of current web page",
function (args) { buffer.zoomIn(Math.max(args.count, 1), false); },
{ count: true });
- mappings.add([modes.COMMAND], ["zm", "<text-zoom-more>"],
+ mappings.add([modes.NORMAL], ["zm", "<text-zoom-more>"],
"Enlarge text zoom of current web page by a larger amount",
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); },
{ count: true });
- mappings.add([modes.COMMAND], ["zo", "-", "<text-zoom-out>"],
+ mappings.add([modes.NORMAL], ["zo", "-", "<text-zoom-out>"],
"Reduce text zoom of current web page",
function (args) { buffer.zoomOut(Math.max(args.count, 1), false); },
{ count: true });
- mappings.add([modes.COMMAND], ["zr", "<text-zoom-reduce>"],
+ mappings.add([modes.NORMAL], ["zr", "<text-zoom-reduce>"],
"Reduce text zoom of current web page by a larger amount",
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); },
{ count: true });
- mappings.add([modes.COMMAND], ["zz", "<text-zoom>"],
+ mappings.add([modes.NORMAL], ["zz", "<text-zoom>"],
"Set text zoom value of current web page",
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZI", "zI", "<full-zoom-in>"],
+ mappings.add([modes.NORMAL], ["ZI", "zI", "<full-zoom-in>"],
"Enlarge full zoom of current web page",
function (args) { buffer.zoomIn(Math.max(args.count, 1), true); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZM", "zM", "<full-zoom-more>"],
+ mappings.add([modes.NORMAL], ["ZM", "zM", "<full-zoom-more>"],
"Enlarge full zoom of current web page by a larger amount",
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZO", "zO", "<full-zoom-out>"],
+ mappings.add([modes.NORMAL], ["ZO", "zO", "<full-zoom-out>"],
"Reduce full zoom of current web page",
function (args) { buffer.zoomOut(Math.max(args.count, 1), true); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZR", "zR", "<full-zoom-reduce>"],
+ mappings.add([modes.NORMAL], ["ZR", "zR", "<full-zoom-reduce>"],
"Reduce full zoom of current web page by a larger amount",
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); },
{ count: true });
- mappings.add([modes.COMMAND], ["zZ", "<full-zoom>"],
+ mappings.add([modes.NORMAL], ["zZ", "<full-zoom>"],
"Set full zoom value of current web page",
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); },
{ count: true });
// page info
- mappings.add([modes.COMMAND], ["<C-g>", "<page-info>"],
+ mappings.add([modes.NORMAL], ["<C-g>", "<page-info>"],
"Print the current file name",
function () { buffer.showPageInfo(false); });
- mappings.add([modes.COMMAND], ["g<C-g>", "<more-page-info>"],
+ mappings.add([modes.NORMAL], ["g<C-g>", "<more-page-info>"],
"Print file information",
function () { buffer.showPageInfo(true); });
},
validator: function (value) RegExp(value)
});
+ options.add(["jumptags", "jt"],
+ "XPath or CSS selector strings of jumpable elements for extended hint modes",
+ "stringmap", {
+ "p": "p,table,ul,ol,blockquote",
+ "h": "h1,h2,h3,h4,h5,h6"
+ },
+ {
+ keepQuotes: true,
+ setter: function (vals) {
+ for (let [k, v] in Iterator(vals))
+ vals[k] = update(new String(v), { matcher: util.compileMatcher(Option.splitList(v)) });
+ return vals;
+ },
+ validator: function (value) util.validateMatcher.call(this, value)
+ && Object.keys(value).every(function (v) v.length == 1)
+ });
+
options.add(["nextpattern"],
"Patterns to use when guessing the next page in a document sequence",
"regexplist", UTF8("'\\bnext\\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\\bmore\\b'"),
options.add(["pageinfo", "pa"],
"Define which sections are shown by the :pageinfo command",
- "charlist", "gfm",
+ "charlist", "gesfm",
{ get values() values(buffer.pageInfo).toObject() });
options.add(["scroll", "scr"],
return this.commandbar;
}
});
+ this.updateVisibility();
},
addElement: function addElement(obj) {
const self = this;
if (obj.value != null)
return [obj.value[0],
obj.get ? obj.get.call(this, elem) : elem.value]
- .concat(obj.value.slice(2))
+ .concat(obj.value.slice(2));
return null;
},
});
var CommandMode = Class("CommandMode", {
- init: function init() {
+ init: function CM_init() {
this.keepCommand = userContext.hidden_option_command_afterimage;
},
+ get autocomplete() options["autocomplete"].length,
+
get command() this.widgets.command[1],
set command(val) this.widgets.command = val,
get prompt() this.widgets.prompt,
set prompt(val) this.widgets.prompt = val,
- open: function (command) {
+ open: function CM_open(command) {
dactyl.assert(isinstance(this.mode, modes.COMMAND_LINE),
- "Not opening command line in non-command-line mode.");
+ /*L*/"Not opening command line in non-command-line mode.");
this.messageCount = commandline.messageCount;
modes.push(this.mode, this.extendedMode, this.closure);
get widgets() commandline.widgets,
- enter: function (stack) {
+ enter: function CM_enter(stack) {
commandline.commandSession = this;
if (stack.pop && commandline.command) {
this.onChange(commandline.command);
}
},
- leave: function (stack) {
+ leave: function CM_leave(stack) {
if (!stack.push) {
commandline.commandSession = null;
this.input.dactylKeyPress = undefined;
},
events: {
- input: function onInput(event) {
+ input: function CM_onInput(event) {
if (this.completions) {
this.resetCompletions();
}
this.onChange(commandline.command);
},
- keyup: function onKeyUp(event) {
+ keyup: function CM_onKeyUp(event) {
let key = events.toString(event);
if (/-?Tab>$/.test(key) && this.completions)
this.completions.tabTimer.flush();
keepCommand: false,
- onKeyPress: function onKeyPress(events) {
+ onKeyPress: function CM_onKeyPress(events) {
if (this.completions)
this.completions.previewClear();
return true; /* Pass event */
},
- onCancel: function (value) {
- },
+ onCancel: function (value) {},
- onChange: function (value) {
- },
+ onChange: function (value) {},
- onSubmit: function (value) {
- },
+ onHistory: function (value) {},
+
+ onSubmit: function (value) {},
- resetCompletions: function resetCompletions() {
+ resetCompletions: function CM_resetCompletions() {
if (this.completions) {
this.completions.context.cancelAll();
this.completions.wildIndex = -1;
prompt: ["Normal", ":"],
- complete: function complete(context) {
+ complete: function CEM_complete(context) {
context.fork("ex", 0, completion, "ex");
},
- onSubmit: function onSubmit(command) {
- contexts.withContext({ file: "[Command Line]", line: 1 },
+ onSubmit: function CEM_onSubmit(command) {
+ contexts.withContext({ file: /*L*/"[Command Line]", line: 1 },
function _onSubmit() {
io.withSavedValues(["readHeredoc"], function _onSubmit() {
this.readHeredoc = commandline.readHeredoc;
init.supercall(this);
},
- complete: function (context) {
+ complete: function CPM_complete(context) {
if (this.completer)
context.forkapply("prompt", 0, this, "completer", Array.slice(arguments, 1));
},
get completionList() {
let node = this.widgets.active.commandline;
+ if (this.commandSession && this.commandSession.completionList)
+ node = document.getElementById(this.commandSession.completionList);
+
if (!node.completionList) {
let elem = document.getElementById("dactyl-completions-" + node.id);
util.waitFor(bind(this.widgets._ready, null, elem));
* @param {XML} xml The output as an E4X XML object.
*/
commandOutput: function commandOutput(xml) {
- XML.ignoreWhitespace = false;
- XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
if (this.command)
- this.echo(<>:{this.command}</>, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
- this.echo(xml, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
+ this.echo(<><div xmlns={XHTML}>:{this.command}</div>
{xml}</>, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
+ else
+ this.echo(xml, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
this.command = null;
},
if (flags & this.APPEND_TO_MESSAGES) {
let message = isObject(data) ? data : { message: data };
+
+ // Make sure the memoized message property is an instance property.
+ message.message;
this._messageHistory.add(update({ highlight: highlightGroup }, message));
data = message.message;
}
let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
let action = this._echoLine;
- if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isString(data)) && !(flags & this.FORCE_SINGLELINE))
+ if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isinstance(data, [_, "String"])) && !(flags & this.FORCE_SINGLELINE))
action = mow.closure.echo;
if (single)
// FIXME: Buggy, especially when pasting.
inputMultiline: function inputMultiline(end, callback) {
let cmd = this.command;
+ let self = {
+ end: "\n" + end + "\n",
+ callback: callback
+ };
+
modes.push(modes.INPUT_MULTILINE, null, {
- mappingSelf: {
- end: "\n" + end + "\n",
- callback: callback
- }
+ holdFocus: true,
+ leave: function leave() {
+ if (!self.done)
+ self.callback(null);
+ },
+ mappingSelf: self
});
+
if (cmd != false)
this._echoLine(cmd, this.HL_NORMAL);
event.target.blur();
dactyl.beep();
}
- },
+ }
}
),
if (this.completions)
this.completions.previewClear();
this.input.value = val;
+ this.session.onHistory(val);
},
/**
dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true);
this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
- if (events.feedingKeys)
+ if (events.feedingKeys && !tabPressed)
this.ignoredCount++;
- if (options["autocomplete"].length) {
+ else if (this.session.autocomplete) {
this.itemList.visible = true;
this.complete(true, false);
}
get wildtype() this.wildtypes[this.wildIndex] || "",
complete: function complete(show, tabPressed) {
+ this.session.ignoredCount = 0;
this.context.reset();
this.context.tabPressed = tabPressed;
this.session.complete(this.context);
for (let [, context] in Iterator(list)) {
let done = function done() !(idx >= n + context.items.length || idx == -2 && !context.items.length);
- util.waitFor(function () !context.incomplete || done())
+ util.waitFor(function () !context.incomplete || done());
if (done())
break;
tab: function tab(reverse, wildmode) {
this.autocompleteTimer.flush();
+ this.ignoredCount = 0;
if (this._caret != this.caret)
this.reset();
if (this.selected == null)
statusline.progress = "";
else
- statusline.progress = "match " + (this.selected + 1) + " of " + this.items.length;
+ statusline.progress = _("completion.matchIndex", this.selected + 1, this.items.length);
}
if (this.items.length == 0)
mappings: function init_mappings() {
mappings.add([modes.COMMAND],
- [":"], "Enter command-line mode",
+ [":"], "Enter Command Line mode",
function () { CommandExMode().open(""); });
mappings.add([modes.INPUT_MULTILINE],
let index = text.indexOf(self.end);
if (index >= 0) {
+ self.done = true;
text = text.substring(1, index);
modes.pop();
_init: function _init() {
this._div = this._dom(
<div class="ex-command-output" highlight="Normal" style="white-space: nowrap">
- <div highlight="Completions" key="noCompletions"><span highlight="Title">No Completions</span></div>
+ <div highlight="Completions" key="noCompletions"><span highlight="Title">{_("completion.noCompletions")}</span></div>
<div key="completions"/>
<div highlight="Completions">
{
onKeyPress: function onKeyPress(event) false
}, {
- WAITING_MESSAGE: "Generating results..."
+ WAITING_MESSAGE: _("completion.generating")
});
// vim: set fdm=marker sw=4 ts=4 et:
XML.ignoreWhitespace = false;
XML.prettyPrinting = false;
-var userContext = { __proto__: modules };
-var _userContext = newContext(userContext);
-
var EVAL_ERROR = "__dactyl_eval_error";
var EVAL_RESULT = "__dactyl_eval_result";
var EVAL_STRING = "__dactyl_eval_string";
};
styles.registerSheet("resource://dactyl-skin/dactyl.css");
+
+ this.cleanups = [];
+ this.cleanups.push(util.overlayObject(window, {
+ focusAndSelectUrlBar: function focusAndSelectUrlBar() {
+ switch (options.get("strictfocus").getKey(document.documentURIObject || util.newURI(document.documentURI), "moderate")) {
+ case "laissez-faire":
+ if (!Events.isHidden(window.gURLBar, true))
+ return focusAndSelectUrlBar.superapply(this, arguments);
+ default:
+ // Evil. Ignore.
+ }
+ }
+ }));
},
cleanup: function () {
+ for (let cleanup in values(this.cleanups))
+ cleanup.call(this);
+
delete window.dactyl;
delete window.liberator;
autocommands.trigger("Leave", {});
},
+ // initially hide all GUI elements, they are later restored unless the user
+ // has :set go= or something similar in his config
+ hideGUI: function () {
+ let guioptions = config.guioptions;
+ for (let option in guioptions) {
+ guioptions[option].forEach(function (elem) {
+ try {
+ document.getElementById(elem).collapsed = true;
+ }
+ catch (e) {}
+ });
+ }
+ },
+
+
observers: {
- "dactyl-cleanup": function dactyl_cleanup() {
+ "dactyl-cleanup": function dactyl_cleanup(subject, reason) {
let modules = dactyl.modules;
for (let mod in values(modules.moduleList.reverse())) {
mod.stale = true;
if ("cleanup" in mod)
- this.trapErrors("cleanup", mod);
+ this.trapErrors("cleanup", mod, reason);
if ("destroy" in mod)
- this.trapErrors("destroy", mod);
+ this.trapErrors("destroy", mod, reason);
}
for (let mod in values(modules.ownPropertyValues.reverse()))
if (mod instanceof Class && "INIT" in mod && "cleanup" in mod.INIT)
- this.trapErrors(mod.cleanup, mod, dactyl, modules, window);
+ this.trapErrors(mod.cleanup, mod, dactyl, modules, window, reason);
for (let name in values(Object.getOwnPropertyNames(modules).reverse()))
try {
}),
/**
- * @property {number} The current main mode.
+ * @property {Modes.Mode} The current main mode.
* @see modes#mainModes
*/
mode: deprecated("modes.main", {
set: function mode(val) modes.main = val
}),
- get menuItems() Dactyl.getMenuItems(),
+ get menuItems() {
+ function dispatch(node, name) {
+ let event = node.ownerDocument.createEvent("Events");
+ event.initEvent(name, false, false);
+ node.dispatchEvent(event);
+ }
+
+ function addChildren(node, parent) {
+ if (~["menu", "menupopup"].indexOf(node.localName) && node.children.length)
+ dispatch(node, "popupshowing");
+
+ for (let [, item] in Iterator(node.childNodes)) {
+ if (item.childNodes.length == 0 && item.localName == "menuitem"
+ && !item.hidden
+ && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME
+ item.dactylPath = parent + item.getAttribute("label");
+ items.push(item);
+ }
+ else {
+ let path = parent;
+ if (item.localName == "menu")
+ path += item.getAttribute("label") + ".";
+ addChildren(item, path);
+ }
+ }
+ }
+
+ let items = [];
+ addChildren(document.getElementById(config.guioptions["m"][1]), "");
+ return items;
+ },
// Global constants
CURRENT_TAB: "here",
},
addUsageCommand: function (params) {
+ function keys(item) (item.names || [item.name]).concat(item.description, item.columns || []);
+
let name = commands.add(params.name, params.description,
function (args) {
let results = array(params.iterate(args))
.sort(function (a, b) String.localeCompare(a.name, b.name));
- let filters = args.map(function (arg) RegExp("\\b" + util.regexp.escape(arg) + "\\b", "i"));
+ let filters = args.map(function (arg) util.regexp("\\b" + util.regexp.escape(arg) + "\\b", "i"));
if (filters.length)
- results = results.filter(function (item) filters.every(function (re) re.test(item.name + " " + item.description)));
+ results = results.filter(function (item) filters.every(function (re) keys(item).some(re.closure.test)));
commandline.commandOutput(
template.usage(results, params.format));
argCount: "*",
completer: function (context, args) {
context.keys.text = util.identity;
- context.keys.description = function () seen[this.text] + " matching items";
+ context.keys.description = function () seen[this.text] + /*L*/" matching items";
let seen = {};
- context.completions = array(item.description.toLowerCase().split(/[()\s]+/)
+ context.completions = array(keys(item).join(" ").toLowerCase().split(/[()\s]+/)
for (item in params.iterate(args)))
.flatten().filter(function (w) /^\w[\w-_']+$/.test(w))
.map(function (k) {
let tags = services["dactyl:"].HELP_TAGS;
for (let obj in values(results)) {
let res = dactyl.generateHelp(obj, null, null, true);
- if (!set.has(tags, obj.helpTag))
+ if (!Set.has(tags, obj.helpTag))
res[1].@tag = obj.helpTag;
yield res;
clipboardHelper.copyString(str);
if (verbose) {
- let message = { message: "Yanked " + str };
+ let message = { message: _("dactyl.yank", str) };
try {
message.domains = [util.newURI(str).host];
}
echoerr: function echoerr(str, flags) {
flags |= commandline.APPEND_TO_MESSAGES;
- if (isinstance(str, ["Error", "Exception"]))
+ if (isinstance(str, ["DOMException", "Error", "Exception"]) || isinstance(str, ["XPCWrappedNative_NoHelper"]) && /^\[Exception/.test(str))
dactyl.reportError(str);
if (isObject(str) && "echoerr" in str)
str = str.echoerr;
({ file: fileName, line: lineNumber, context: ctxt }) = info;
if (!context && fileName && fileName[0] !== "[")
- context = _userContext || ctxt;
+ context = ctxt || _userContext;
if (isinstance(context, ["Sandbox"]))
return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
* @param {string} feature The feature name.
* @returns {boolean}
*/
- has: function (feature) set.has(config.features, feature),
+ has: function (feature) Set.has(config.features, feature),
/**
* Returns the URL of the specified help *topic* if it exists.
* Initialize the help system.
*/
initHelp: function (force) {
+ // Waits for the add-on to become available, if necessary.
+ config.addon;
+ config.version;
+
if (force || !this.helpInitialized) {
if ("noscriptOverlay" in window) {
noscriptOverlay.safeAllow("chrome-data:", true, false);
let body = XML();
for (let [, context] in Iterator(plugins.contexts))
- if (context && context.INFO instanceof XML) {
- let info = context.INFO;
- if (info.*.@lang.length()) {
- let lang = config.bestLocale(String(a) for each (a in info.*.@lang));
+ try {
+ let info = contexts.getDocs(context);
+ if (info instanceof XML) {
+ if (info.*.@lang.length()) {
+ let lang = config.bestLocale(String(a) for each (a in info.*.@lang));
- info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang);
+ info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang);
- for each (let elem in info.NS::info)
- for each (let attr in ["@name", "@summary", "@href"])
- if (elem[attr].length())
- info[attr] = elem[attr];
+ for each (let elem in info.NS::info)
+ for each (let attr in ["@name", "@summary", "@href"])
+ if (elem[attr].length())
+ info[attr] = elem[attr];
+ }
+ body += <h2 xmlns={NS.uri} tag={info.@name + '-plugin'}>{info.@summary}</h2> +
+ info;
}
- body += <h2 xmlns={NS.uri} tag={context.INFO.@name + '-plugin'}>{context.INFO.@summary}</h2> +
- context.INFO;
+ }
+ catch (e) {
+ util.reportError(e);
}
let help =
'<?xml version="1.0"?>\n' +
'<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
'<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
- unescape(encodeURI( // UTF-8 handling hack.
<document xmlns={NS}
name="plugins" title={config.appName + " Plugins"}>
- <h1 tag="using-plugins">Using Plugins</h1>
+ <h1 tag="using-plugins">{_("help.title.Using Plugins")}</h1>
<toc start="2"/>
{body}
- </document>.toXMLString()));
+ </document>.toXMLString();
fileMap["plugins"] = function () ['text/xml;charset=UTF-8', help];
fileMap["versions"] = function () {
default xml namespace = NS;
function rec(text, level, li) {
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+
let res = <></>;
let list, space, i = 0;
if (!list)
res += list = <ul/>;
let li = <li/>;
- li.* += rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1, li)
+ li.* += rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1, li);
list.* += li;
}
else if (match.par) {
}
list = null;
- if (level == 0 && /^.*:\n$/.test(match.par))
- res += <h2>{template.linkifyHelp(par.slice(0, -1), true)}</h2>;
+ if (level == 0 && /^.*:\n$/.test(match.par)) {
+ let text = par.slice(0, -1);
+ res += <h2 tag={"news-" + text}>{template.linkifyHelp(text, true)}</h2>;
+ }
else {
let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(par);
res += <p highlight={group + " HelpNews"}>{
return res;
}
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
let body = rec(NEWS, 0);
for each (let li in body..li) {
let list = li..li.(@NS::highlight == "HelpNewsOld");
}
}
- XML.prettyPrinting = XML.ignoreWhitespace = false;
return ["application/xml",
'<?xml version="1.0"?>\n' +
'<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
'<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
- unescape(encodeURI( // UTF-8 handling hack.
<document xmlns={NS} xmlns:dactyl={NS}
name="versions" title={config.appName + " Versions"}>
<h1 tag="versions news NEWS">{config.appName} Versions</h1>
<toc start="2"/>
{body}
- </document>.toXMLString()))
+ </document>.toXMLString()
];
}
addTags("versions", util.httpGet("dactyl://help/versions").responseXML);
overlayMap["index"] = ['text/xml;charset=UTF-8',
'<?xml version="1.0"?>\n' +
- '<overlay xmlns="' + NS + '">\n' +
- unescape(encodeURI( // UTF-8 handling hack.
+ <overlay xmlns={NS}>{
template.map(dactyl.indices, function ([name, iter])
<dl insertafter={name + "-index"}>{
template.map(iter(), util.identity)
- }</dl>, <>{"\n\n"}</>))) +
- '\n</overlay>'];
-
+ }</dl>, <>{"\n\n"}</>)
+ }</overlay>];
addTags("index", util.httpGet("dactyl://help-overlay/index").responseXML);
+ overlayMap["gui"] = ['text/xml;charset=UTF-8',
+ '<?xml version="1.0"?>\n' +
+ <overlay xmlns={NS}>
+ <dl insertafter="dialog-list">{
+ template.map(config.dialogs, function ([name, val])
+ (!val[2] || val[2]())
+ ? <><dt>{name}</dt><dd>{val[0]}</dd></>
+ : undefined,
+ <>{"\n"}</>)
+ }</dl>
+ </overlay>];
+
+
this.helpInitialized = true;
}
},
addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data));
}
- let empty = set("area base basefont br col frame hr img input isindex link meta param"
+ let empty = Set("area base basefont br col frame hr img input isindex link meta param"
.split(" "));
function fix(node) {
switch(node.nodeType) {
- case Node.ELEMENT_NODE:
- if (isinstance(node, [HTMLBaseElement]))
- return;
-
- data.push("<"); data.push(node.localName);
- if (node instanceof HTMLHtmlElement)
- data.push(" xmlns=" + XHTML.uri.quote());
-
- for (let { name, value } in array.iterValues(node.attributes)) {
- if (name == "dactyl:highlight") {
- set.add(styles, value);
- name = "class";
- value = "hl-" + value;
- }
- if (name == "href") {
- value = node.href;
- if (value.indexOf("dactyl://help-tag/") == 0) {
- let uri = services.io.newChannel(value, null, null).originalURI;
- value = uri.spec == value ? "javascript:;" : uri.path.substr(1);
- }
- if (!/^#|[\/](#|$)|^[a-z]+:/.test(value))
- value = value.replace(/(#|$)/, ".xhtml$1");
- }
- if (name == "src" && value.indexOf(":") > 0) {
- chromeFiles[value] = value.replace(/.*\//, "");
- value = value.replace(/.*\//, "");
+ case Node.ELEMENT_NODE:
+ if (isinstance(node, [HTMLBaseElement]))
+ return;
+
+ data.push("<"); data.push(node.localName);
+ if (node instanceof HTMLHtmlElement)
+ data.push(" xmlns=" + XHTML.uri.quote(),
+ " xmlns:dactyl=" + NS.uri.quote());
+
+ for (let { name, value } in array.iterValues(node.attributes)) {
+ if (name == "dactyl:highlight") {
+ Set.add(styles, value);
+ name = "class";
+ value = "hl-" + value;
+ }
+ if (name == "href") {
+ value = node.href || value;
+ if (value.indexOf("dactyl://help-tag/") == 0) {
+ let uri = services.io.newChannel(value, null, null).originalURI;
+ value = uri.spec == value ? "javascript:;" : uri.path.substr(1);
}
- data.push(" ");
- data.push(name);
- data.push('="');
- data.push(<>{value}</>.toXMLString());
- data.push('"');
+ if (!/^#|[\/](#|$)|^[a-z]+:/.test(value))
+ value = value.replace(/(#|$)/, ".xhtml$1");
}
- 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, fix);
- data.push("</"); data.push(node.localName); data.push(">");
+ if (name == "src" && value.indexOf(":") > 0) {
+ chromeFiles[value] = value.replace(/.*\//, "");
+ value = value.replace(/.*\//, "");
}
- break;
- case Node.TEXT_NODE:
- data.push(<>{node.textContent}</>.toXMLString());
+
+ data.push(" ", name, '="',
+ <>{value}</>.toXMLString().replace(/"/g, """),
+ '"');
+ }
+ 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, fix);
+ data.push("</", node.localName, ">");
+ }
+ break;
+ case Node.TEXT_NODE:
+ data.push(<>{node.textContent}</>.toXMLString());
}
}
let chromeFiles = {};
let styles = {};
for (let [file, ] in Iterator(services["dactyl:"].FILE_MAP)) {
- dactyl.open("dactyl://help/" + file);
- dactyl.modules.events.waitForPageLoad();
- let data = [
+ let url = "dactyl://help/" + file;
+ dactyl.open(url);
+ util.waitFor(function () content.location.href == url && buffer.loaded
+ && content.document.documentElement instanceof HTMLHtmlElement,
+ 15000);
+ events.waitForPageLoad();
+ var 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'
addDataEntry(file + ".xhtml", data.join(""));
}
- let data = [h for (h in highlight) if (set.has(styles, h.class) || /^Help/.test(h.class))]
+ let data = [h for (h in highlight) if (Set.has(styles, h.class) || /^Help/.test(h.class))]
.map(function (h) h.selector
.replace(/^\[.*?=(.*?)\]/, ".hl-$1")
.replace(/html\|/g, "") + "\t" + "{" + h.cssText + "}")
if (obj instanceof Command) {
link = function (cmd) <ex>{cmd}</ex>;
args = obj.parseArgs("", CompletionContext(str || ""));
- spec = function (cmd) cmd + (obj.bang ? <oa>!</oa> : <></>);
+ spec = function (cmd) <>{
+ obj.count ? <oa>count</oa> : <></>
+ }{
+ cmd
+ }{
+ obj.bang ? <oa>!</oa> : <></>
+ }</>;
}
else if (obj instanceof Map) {
spec = function (map) obj.count ? <><oa>count</oa>{map}</> : <>{map}</>;
};
}
else if (obj instanceof Option) {
+ tag = spec = function (name) <>'{name}'</>;
link = function (opt, name) <o>{name}</o>;
+ args = { value: "", values: [] };
}
XML.prettyPrinting = false;
<description>{
obj.description ? br + <p>{template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)}</p> : "" }{
extraHelp ? br + extraHelp : "" }{
- !(extraHelp || obj.description) ? br + <p>Sorry, no help available.</p> : "" }
+ !(extraHelp || obj.description) ? br + <p><!--L-->Sorry, no help available.</p> : "" }
</description>
</item></>;
}
if (obj.completer)
- add(completion._runCompleter(obj.completer, "", null, args).items
+ add(completion._runCompleter(obj.closure.completer, "", null, args).items
.map(function (i) [i.text, i.description]));
if (obj.options && obj.options.some(function (o) o.description))
* These are set and accessed with the "g:" prefix.
*/
_globalVariables: {},
- globalVariables: deprecated("the options system", {
+ globalVariables: deprecated(_("deprecated.for.theOptionsSystem"), {
get: function globalVariables() this._globalVariables
}),
let loadplugins = options.get("loadplugins");
if (args)
- loadplugins = { __proto__: loadplugins, value: args.map(Option.parseRegexp) }
+ loadplugins = { __proto__: loadplugins, value: args.map(Option.parseRegexp) };
dir.readDirectory(true).forEach(function (file) {
- if (file.isFile() && loadplugins.getKey(file.path) && !(!force && file.path in dactyl.pluginFiles)) {
+ if (file.isFile() && loadplugins.getKey(file.path)
+ && !(!force && file.path in dactyl.pluginFiles && dactyl.pluginFiles[file.path] >= file.lastModifiedTime)) {
try {
io.source(file.path);
- dactyl.pluginFiles[file.path] = true;
+ dactyl.pluginFiles[file.path] = file.lastModifiedTime;
}
catch (e) {
dactyl.reportError(e);
onExecute: function onExecute(event) {
let cmd = event.originalTarget.getAttribute("dactyl-execute");
commands.execute(cmd, null, false, null,
- { file: "[Command Line]", line: 1 });
+ { file: /*L*/"[Command Line]", line: 1 });
},
/**
urls = dactyl.parseURLs(urls);
if (urls.length > prefs.get("browser.tabs.maxOpenBeforeWarn", 20) && !force)
- return commandline.input("This will open " + urls.length + " new tabs. Would you like to continue? (yes/[no]) ",
+ return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ",
function (resp) {
if (resp && resp.match(/^y(es)?$/i))
dactyl.open(urls, params, true);
* ['www.google.com/search?q=bla', 'www.osnews.com']
*
* @param {string} str
- * @returns {string[]}
+ * @returns {[string]}
*/
parseURLs: function parseURLs(str) {
let urls;
return urls.map(function (url) {
url = url.trim();
- if (/^(\.{0,2}|~)(\/|$)/.test(url)) {
+ if (/^(\.{0,2}|~)(\/|$)/.test(url) || util.OS.isWindows && /^[a-z]:/i.test(url)) {
try {
// Try to find a matching file.
let file = io.File(url);
// If it starts with a valid protocol, pass it through.
let proto = /^([-\w]+):/.exec(url);
if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc)
- return url.replace(/\s+/g, "");
+ return url;
// Check for a matching search keyword.
let searchURL = this.has("bookmarks") && bookmarks.getSearchURL(url, false);
},
/**
- * @property {Window[]} Returns an array of all the host application's
+ * @property {[Window]} Returns an array of all the host application's
* open windows.
*/
- get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser")))],
+ get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser"))) if (win.dactyl)],
}, {
- // initially hide all GUI elements, they are later restored unless the user
- // has :set go= or something similar in his config
- hideGUI: function () {
- let guioptions = config.guioptions;
- for (let option in guioptions) {
- guioptions[option].forEach(function (elem) {
- try {
- document.getElementById(elem).collapsed = true;
- }
- catch (e) {}
- });
- }
- },
-
- // TODO: move this
- getMenuItems: function () {
- function addChildren(node, parent) {
- for (let [, item] in Iterator(node.childNodes)) {
- if (item.childNodes.length == 0 && item.localName == "menuitem"
- && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME
- item.fullMenuPath = parent + item.getAttribute("label");
- items.push(item);
- }
- else {
- let path = parent;
- if (item.localName == "menu")
- path += item.getAttribute("label") + ".";
- addChildren(item, path);
- }
- }
- }
-
- let items = [];
- addChildren(document.getElementById(config.guioptions["m"][1]), "");
- return items;
- }
+ toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true"
}, {
events: function () {
events.listen(window, "click", dactyl.closure.onClick, true);
M: ["Always show messages outside of the status line"]
},
setter: function (opts) {
- if (loaded.commandline)
+ if (loaded.commandline || ~opts.indexOf("c"))
commandline.widgets.updateVisibility();
}
},
true);
prefs.safeSet("layout.scrollbar.side", opts.indexOf("l") >= 0 ? 3 : 2,
- "See 'guioptions' scrollbar flags.");
+ _("option.guioptions.safeSet"));
},
validator: function (opts) Option.validIf(!(opts.indexOf("l") >= 0 && opts.indexOf("r") >= 0),
UTF8("Only one of ‘l’ or ‘r’ allowed"))
// FIXME: cleanup
cleanupValue: config.cleanups.guioptions ||
"r" + [k for ([k, v] in iter(groups[1].opts))
- if (!document.getElementById(v[1][0]).collapsed)].join(""),
+ if (!Dactyl.toolbarHidden(document.getElementById(v[1][0])))].join(""),
values: array(groups).map(function (g) [[k, v[0]] for ([k, v] in Iterator(g.opts))]).flatten(),
options.add(["urlseparator", "urlsep", "us"],
"The regular expression used to separate multiple URLs in :open and friends",
- "string", "\\|",
+ "string", " \\| ",
{ validator: function (value) RegExp(value) });
options.add(["verbose", "vbs"],
{
setter: function (value) {
prefs.safeSet("accessibility.typeaheadfind.enablesound", !value,
- "See 'visualbell' option");
+ _("option.visualbell.safeSet"));
return value;
}
});
},
mappings: function () {
- mappings.add([modes.MAIN], ["<F1>"],
+ mappings.add([modes.MAIN], ["<open-help>", "<F1>"],
"Open the introductory help page",
function () { dactyl.help(); });
- mappings.add([modes.MAIN], ["<A-F1>"],
+ mappings.add([modes.MAIN], ["<open-single-help>", "<A-F1>"],
"Open the single, consolidated help page",
function () { ex.helpall(); });
}
}, {
argCount: "1",
- bang: true,
completer: function (context) {
context.ignoreCase = true;
completion.dialog(context);
"Execute the specified menu item from the command line",
function (args) {
let arg = args[0] || "";
- let items = Dactyl.getMenuItems();
+ let items = dactyl.menuItems;
- dactyl.assert(items.some(function (i) i.fullMenuPath == arg),
+ dactyl.assert(items.some(function (i) i.dactylPath == arg),
_("emenu.notFound", arg));
for (let [, item] in Iterator(items)) {
- if (item.fullMenuPath == arg)
+ if (item.dactylPath == arg) {
+ dactyl.assert(!item.disabled, _("error.disabled", item.dactylPath));
item.doCommand();
+ }
}
}, {
argCount: "1",
});
commands.add(["loadplugins", "lpl"],
- "Load all plugins immediately",
+ "Load all or matching plugins",
function (args) {
dactyl.loadPlugins(args.length ? args : null, args.bang);
},
bang: true,
keepQuotes: true,
serialGroup: 10,
- serialize: function () [
+ serialize: function () [
{
command: this.name,
literalArg: options["loadplugins"].join(" ")
literal: 0
});
+ commands.add(["exit", "x"],
+ "Quit " + config.appName,
+ function (args) {
+ dactyl.quit(false, args.bang);
+ }, {
+ argCount: "0",
+ bang: true
+ });
+
commands.add(["q[uit]"],
dactyl.has("tabs") ? "Quit current tab" : "Quit application",
function (args) {
"Reload the " + config.appName + " add-on",
function (args) {
if (args.trailing)
- JSMLoader.rehashCmd = args.trailing; // Hack.
+ storage.session.rehashCmd = args.trailing; // Hack.
args.break = true;
util.rehash(args);
},
{
- argCount: "0",
+ argCount: "0", // FIXME
options: [
{
names: ["+u"],
commands.add(["res[tart]"],
"Force " + config.appName + " to restart",
- function () { dactyl.restart(); });
+ function () { dactyl.restart(); },
+ { argCount: "0" });
function findToolbar(name) util.evaluateXPath(
- "//*[@toolbarname=" + util.escapeString(name, "'") + "]",
+ "//*[@toolbarname=" + util.escapeString(name, "'") + " or " +
+ "@toolbarname=" + util.escapeString(name.trim(), "'") + "]",
document).snapshotItem(0);
var toolbox = document.getElementById("navigator-toolbox");
if (toolbox) {
- let hidden = function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true";
-
let toolbarCommand = function (names, desc, action, filter) {
commands.add(names, desc,
function (args) {
action(toolbar);
events.checkFocus();
}, {
- argcount: "1",
+ argCount: "1",
completer: function (context) {
completion.toolbar(context);
if (filter)
toolbarCommand(["toolbars[how]", "tbs[how]"], "Show the named toolbar",
function (toolbar) dactyl.setNodeVisible(toolbar, true),
- function ({ item }) hidden(item));
+ function ({ item }) Dactyl.toolbarHidden(item));
toolbarCommand(["toolbarh[ide]", "tbh[ide]"], "Hide the named toolbar",
function (toolbar) dactyl.setNodeVisible(toolbar, false),
- function ({ item }) !hidden(item));
+ function ({ item }) !Dactyl.toolbarHidden(item));
toolbarCommand(["toolbart[oggle]", "tbt[oggle]"], "Toggle the named toolbar",
- function (toolbar) dactyl.setNodeVisible(toolbar, hidden(toolbar)));
+ function (toolbar) dactyl.setNodeVisible(toolbar, Dactyl.toolbarHidden(toolbar)));
}
commands.add(["time"],
args = args[0] || "";
if (args[0] == ":")
- var method = function () commands.execute(args, null, true);
+ var func = function () commands.execute(args, null, false);
else
- method = dactyl.userFunc(args);
+ func = dactyl.userFunc(args);
try {
if (count > 1) {
for (let i in util.interruptibleRange(0, count, 500)) {
let now = Date.now();
- method();
+ func();
total += Date.now() - now;
}
commandline.commandOutput(
<table>
<tr highlight="Title" align="left">
- <th colspan="3">Code execution summary</th>
+ <th colspan="3">{_("title.Code execution summary")}</th>
</tr>
- <tr><td>  Executed:</td><td align="right"><span class="times-executed">{count}</span></td><td>times</td></tr>
- <tr><td>  Average time:</td><td align="right"><span class="time-average">{each.toFixed(2)}</span></td><td>{eachUnits}</td></tr>
- <tr><td>  Total time:</td><td align="right"><span class="time-total">{total.toFixed(2)}</span></td><td>{totalUnits}</td></tr>
+ <tr><td>  {_("title.Executed")}:</td><td align="right"><span class="times-executed">{count}</span></td><td><!--L-->times</td></tr>
+ <tr><td>  {_("title.Average time")}:</td><td align="right"><span class="time-average">{each.toFixed(2)}</span></td><td>{eachUnits}</td></tr>
+ <tr><td>  {_("title.Total time")}:</td><td align="right"><span class="time-total">{total.toFixed(2)}</span></td><td>{totalUnits}</td></tr>
</table>);
}
else {
let beforeTime = Date.now();
- method();
+ func();
if (special)
return;
dactyl.echoerr(e);
}
}, {
- argCount: "+",
+ argCount: "1",
bang: true,
completer: function (context) {
if (/^:/.test(context.filter))
vbs.setFrom = setFrom;
}
}, {
- argCount: "+",
+ argCount: "1",
completer: function (context) completion.ex(context),
count: true,
literal: 0,
completion.menuItem = function menuItem(context) {
context.title = ["Menu Path", "Label"];
context.anchored = false;
- context.keys = { text: "fullMenuPath", description: function (item) item.getAttribute("label") };
- context.completions = dactyl.menuItems;
+ context.keys = {
+ text: "dactylPath",
+ description: function (item) item.getAttribute("label"),
+ highlight: function (item) item.disabled ? "Disabled" : ""
+ };
+ context.generate = function () dactyl.menuItems;
};
var toolbox = document.getElementById("navigator-toolbox");
dactyl.timeout(function () {
try {
- var args = JSMLoader.commandlineArgs || services.commandLineHandler.optionValue;
+ var args = storage.session.commandlineArgs || services.commandLineHandler.optionValue;
if (isString(args))
args = dactyl.parseCommandLine(args);
}
// TODO: we should have some class where all this guioptions stuff fits well
- // Dactyl.hideGUI();
+ // dactyl.hideGUI();
if (dactyl.userEval("typeof document", null, "test.js") === "undefined")
jsmodules.__proto__ = XPCSafeJSObjectWrapper(window);
dactyl.execute(cmd);
});
- if (JSMLoader.rehashCmd)
- dactyl.execute(JSMLoader.rehashCmd);
- JSMLoader.rehashCmd = null;
+ if (storage.session.rehashCmd)
+ dactyl.execute(storage.session.rehashCmd);
+ storage.session.rehashCmd = null;
dactyl.fullyInitialized = true;
dactyl.triggerObserver("enter", null);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const TOPIC = "chrome-document-global-created";
+
function observe(window, topic, url) {
- if (topic === "chrome-document-global-created")
+ if (topic === TOPIC)
checkDocument(window.document);
}
function init(id) {
if (id)
ADDON_ID = id;
- Services.obs[id ? "addObserver" : "removeObserver"](observe, "chrome-document-global-created", false);
- for (let doc in chromeDocuments)
+ Services.obs[id ? "addObserver" : "removeObserver"](observe, TOPIC, false);
+ for (let doc in chromeDocuments())
checkDocument(doc, !id);
}
function cleanup() { init(null); }
}
function chromeDocuments() {
- let windows = services.windowMediator.getXULWindowEnumerator(null);
+ let windows = Services.wm.getXULWindowEnumerator(null);
while (windows.hasMoreElements()) {
let window = windows.getNext().QueryInterface(Ci.nsIXULWindow);
for each (let type in ["typeChrome", "typeContent"]) {
+// Copyright (c) 2008-2011 Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
selectedText: function () String(Editor.getEditor(null).selection),
pasteClipboard: function (clipboard, toStart) {
- // TODO: I don't think this is needed anymore? --djk
- if (util.OS.isWindows) {
- this.executeCommand("cmd_paste");
- return;
- }
-
let elem = dactyl.focusedElement;
if (elem.inputField)
elem = elem.inputField;
}
},
- // returns the position of char
- findCharForward: function (ch, count) {
- if (!Editor.getEditor())
+ findChar: function (key, count, backward) {
+
+ let editor = Editor.getEditor();
+ if (!editor)
return -1;
- let text = Editor.getEditor().value;
// XXX
if (count == null)
count = 1;
- for (let i = Editor.getEditor().selectionEnd + 1; i < text.length; i++) {
- if (text[i] == "\n")
- break;
- if (text[i] == ch)
- count--;
- if (count == 0)
- return i + 1; // always position the cursor after the char
- }
+ let code = events.fromString(key)[0].charCode;
+ util.assert(code);
+ let char = String.fromCharCode(code);
- dactyl.beep();
- return -1;
- },
-
- // returns the position of char
- findCharBackward: function (ch, count) {
- if (!Editor.getEditor())
- return -1;
-
- let text = Editor.getEditor().value;
- // XXX
- if (count == null)
- count = 1;
+ let text = editor.value;
+ let caret = editor.selectionEnd;
+ if (backward) {
+ let end = text.lastIndexOf("\n", caret);
+ while (caret > end && caret >= 0 && count--)
+ caret = text.lastIndexOf(char, caret - 1);
+ }
+ else {
+ let end = text.indexOf("\n", caret);
+ if (end == -1)
+ end = text.length;
- for (let i = Editor.getEditor().selectionStart - 1; i >= 0; i--) {
- if (text[i] == "\n")
- break;
- if (text[i] == ch)
- count--;
- if (count == 0)
- return i;
+ while (caret < end && caret >= 0 && count--)
+ caret = text.indexOf(char, caret + 1);
}
- dactyl.beep();
- return -1;
+ if (count > 0)
+ caret = -1;
+ if (caret == -1)
+ dactyl.beep();
+ return caret;
},
/**
* Edits the given file in the external editor as specified by the
* 'editor' option.
*
- * @param {object|File|string} args An object specifying the file,
- * line, and column to edit. If a non-object is specified, it is
- * treated as the file parameter of the object.
+ * @param {object|File|string} args An object specifying the file, line,
+ * and column to edit. If a non-object is specified, it is treated as
+ * the file parameter of the object.
* @param {boolean} blocking If true, this function does not return
- * until the editor exits.
+ * until the editor exits.
*/
editFileExternally: function (args, blocking) {
if (!isObject(args) || args instanceof File)
let args = options.get("editor").format(args);
- dactyl.assert(args.length >= 1, _("editor.noEditor"));
+ dactyl.assert(args.length >= 1, _("option.notSet", "editor"));
io.run(args.shift(), args, blocking);
},
let line, column;
if (!forceEditing && textBox && textBox.type == "password") {
- commandline.input("Editing a password field externally will reveal the password. Would you like to continue? (yes/[no]): ",
+ commandline.input(_("editor.prompt.editPassword") + " ",
function (resp) {
if (resp && resp.match(/^y(es)?$/i))
editor.editFieldExternally(true);
column = 1 + pre.replace(/[^]*\n/, "").length;
}
else {
- var editor = window.GetCurrentEditor ? GetCurrentEditor()
- : Editor.getEditor(document.commandDispatcher.focusedWindow);
- dactyl.assert(editor);
- text = Array.map(editor.rootElement.childNodes, function (e) util.domToString(e, true)).join("");
+ var editor_ = window.GetCurrentEditor ? GetCurrentEditor()
+ : Editor.getEditor(document.commandDispatcher.focusedWindow);
+ dactyl.assert(editor_);
+ text = Array.map(editor_.rootElement.childNodes, function (e) util.domToString(e, true)).join("");
}
let origGroup = textBox && textBox.getAttributeNS(NS, "highlight") || "";
lastUpdate = Date.now();
let val = tmpfile.read();
- if (textBox)
+ if (textBox) {
textBox.value = val;
+
+ textBox.setAttributeNS(NS, "modifiable", true);
+ util.computedStyle(textBox).MozUserInput;
+ events.dispatch(textBox, events.create(textBox.ownerDocument, "input", {}));
+ textBox.removeAttributeNS(NS, "modifiable");
+ }
else {
- while (editor.rootElement.firstChild)
- editor.rootElement.removeChild(editor.rootElement.firstChild);
- editor.rootElement.innerHTML = val;
+ while (editor_.rootElement.firstChild)
+ editor_.rootElement.removeChild(editor_.rootElement.firstChild);
+ editor_.rootElement.innerHTML = val;
}
}
try {
var tmpfile = io.createTempFile();
if (!tmpfile)
- throw Error("Couldn't create temporary file");
+ throw Error(_("io.cantCreateTempFile"));
if (textBox) {
highlight.highlightNode(textBox, origGroup + " EditorEditing");
}
if (!tmpfile.write(text))
- throw Error("Input contains characters not valid in the current " +
- "file encoding");
+ throw Error(_("io.cantEncode"));
var lastUpdate = Date.now();
elem = dactyl.focusedElement || document.commandDispatcher.focusedWindow;
dactyl.assert(elem);
- if (elem instanceof Element)
- return elem.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
try {
+ if (elem instanceof Element)
+ return elem.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
return elem.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIEditingSession)
.getEditorForWindow(elem);
mappings.add([modes.INPUT],
["<C-t>"], "Edit text field in Vi mode",
function () {
+ dactyl.assert(dactyl.focusedElement);
dactyl.assert(!editor.isTextEdit);
modes.push(modes.TEXT_EDIT);
});
+ // Ugh.
+ mappings.add([modes.INPUT, modes.CARET],
+ ["<*-CR>", "<*-BS>", "<*-Del>", "<*-Left>", "<*-Right>", "<*-Up>", "<*-Down>",
+ "<*-Home>", "<*-End>", "<*-PageUp>", "<*-PageDown>",
+ "<M-c>", "<M-v>", "<*-Tab>"],
+ "Handled by " + config.host,
+ function () Events.PASS_THROUGH);
+
mappings.add([modes.INSERT],
- ["<Space>", "<Return>"], "Expand insert mode abbreviation",
+ ["<Space>", "<Return>"], "Expand Insert mode abbreviation",
function () {
editor.expandAbbreviation(modes.INSERT);
- return Events.PASS;
+ return Events.PASS_THROUGH;
});
mappings.add([modes.INSERT],
- ["<C-]>", "<C-5>"], "Expand insert mode abbreviation",
+ ["<C-]>", "<C-5>"], "Expand Insert mode abbreviation",
function () { editor.expandAbbreviation(modes.INSERT); });
// text edit mode
// visual mode
mappings.add([modes.CARET, modes.TEXT_EDIT],
- ["v"], "Start visual mode",
+ ["v"], "Start Visual mode",
function () { modes.push(modes.VISUAL); });
mappings.add([modes.VISUAL],
- ["v", "V"], "End visual mode",
+ ["v", "V"], "End Visual mode",
function () { modes.pop(); });
mappings.add([modes.TEXT_EDIT],
- ["V"], "Start visual line mode",
+ ["V"], "Start Visual Line mode",
function () {
modes.push(modes.VISUAL, modes.LINE);
editor.executeCommand("cmd_beginLine", 1);
mappings.add([modes.TEXT_EDIT, modes.VISUAL],
["f"], "Move to a character on the current line after the cursor",
function ({ arg, count }) {
- let pos = editor.findCharForward(arg, Math.max(count, 1));
+ let pos = editor.findChar(arg, Math.max(count, 1));
if (pos >= 0)
editor.moveToPosition(pos, true, modes.main == modes.VISUAL);
},
mappings.add([modes.TEXT_EDIT, modes.VISUAL],
["F"], "Move to a character on the current line before the cursor",
function ({ arg, count }) {
- let pos = editor.findCharBackward(arg, Math.max(count, 1));
+ let pos = editor.findChar(arg, Math.max(count, 1), true);
if (pos >= 0)
editor.moveToPosition(pos, false, modes.main == modes.VISUAL);
},
mappings.add([modes.TEXT_EDIT, modes.VISUAL],
["t"], "Move before a character on the current line",
function ({ arg, count }) {
- let pos = editor.findCharForward(arg, Math.max(count, 1));
+ let pos = editor.findChar(arg, Math.max(count, 1));
if (pos >= 0)
editor.moveToPosition(pos - 1, true, modes.main == modes.VISUAL);
},
mappings.add([modes.TEXT_EDIT, modes.VISUAL],
["T"], "Move before a character on the current line, backwards",
function ({ arg, count }) {
- let pos = editor.findCharBackward(arg, Math.max(count, 1));
+ let pos = editor.findChar(arg, Math.max(count, 1), true);
if (pos >= 0)
editor.moveToPosition(pos + 1, false, modes.main == modes.VISUAL);
},
function bind() mappings.add.apply(mappings,
[[modes.AUTOCOMPLETE]].concat(Array.slice(arguments)))
- bind(["<Esc>"], "Return to INSERT mode",
+ bind(["<Esc>"], "Return to Insert mode",
function () Events.PASS_THROUGH);
- bind(["<C-[>"], "Return to INSERT mode",
+ bind(["<C-[>"], "Return to Insert mode",
function () { events.feedkeys("<Esc>", { skipmap: true }); });
bind(["<Up>"], "Select the previous autocomplete result",
options: function () {
options.add(["editor"],
"The external text editor",
- "string", "gvim -f +<line> <file>", {
+ "string", 'gvim -f +<line> +"sil! call cursor(0, <column>)" <file>', {
format: function (obj, value) {
let args = commands.parseArgs(value || this.value, { argCount: "*", allowUnknownOptions: true })
.map(util.compileMacro).filter(function (fmt) fmt.valid(obj))
args.push(obj["file"]);
return args;
},
- has: function (key) set.has(util.compileMacro(this.value).seen, key),
+ has: function (key) Set.has(util.compileMacro(this.value).seen, key),
validator: function (value) {
this.format({}, value);
return Object.keys(util.compileMacro(value).seen).every(function (k) ["column", "file", "line"].indexOf(k) >= 0);
this.buffer = "";
this.events = [];
+ events.dbg("STACK " + mode);
+
let main = { __proto__: mode.main, params: mode.params };
- let keyModes = array([mode.params.keyModes, main, mode.main.allBases]).flatten().compact();
+ this.modes = array([mode.params.keyModes, main, mode.main.allBases.slice(1)]).flatten().compact();
if (builtin)
hives = hives.filter(function (h) h.name === "builtin");
- this.processors = keyModes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
- .flatten().array;
+ this.processors = this.modes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
+ .flatten().array;
this.ownsBuffer = !this.processors.some(function (p) p.main.ownsBuffer);
for (let [i, input] in Iterator(this.processors)) {
let params = input.main.params;
+
if (params.preExecute)
input.preExecute = params.preExecute;
+
if (params.postExecute)
input.postExecute = params.postExecute;
+
if (params.onKeyPress && input.hive === mappings.builtin)
input.fallthrough = function fallthrough(events) {
return params.onKeyPress(events) === false ? Events.KILL : Events.PASS;
}
let hive = options.get("passkeys")[this.main.input ? "inputHive" : "commandHive"];
- if (!builtin && hive.active
- && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)))
+ if (!builtin && hive.active && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)))
this.processors.unshift(KeyProcessor(modes.BASE, hive));
},
+ passUnknown: Class.memoize(function () options.get("passunknown").getKey(this.modes)),
+
notify: function () {
+ events.dbg("NOTIFY()");
events.keyEvents = [];
events.processor = null;
- if (!this.execute(Events.KILL, true)) {
+ if (!this.execute(undefined, true)) {
events.processor = this;
events.keyEvents = this.keyEvents;
}
callable(result) ? result.toSource().substr(0, 50) : result),
execute: function execute(result, force) {
+ events.dbg("EXECUTE(" + this._result(result) + ", " + force + ") events:" + this.events.length
+ + " processors:" + this.processors.length + " actions:" + this.actions.length);
+
+ let processors = this.processors;
+ let length = 1;
- if (force && this.actions.length)
- this.processors.length = 0;
+ if (force)
+ this.processors = [];
if (this.ownsBuffer)
statusline.inputBuffer = this.processors.length ? this.buffer : "";
if (!this.processors.some(function (p) !p.extended) && this.actions.length) {
- if (this._actions.length == 0) {
- dactyl.beep();
- events.feedingKeys = false;
- }
+ // We have matching actions and no processors other than
+ // those waiting on further arguments. Execute actions as
+ // long as they continue to return PASS.
for (var action in values(this.actions)) {
while (callable(action)) {
+ length = action.eventLength;
action = dactyl.trapErrors(action);
- events.dbg("ACTION RES: " + this._result(action));
+ events.dbg("ACTION RES: " + length + " " + this._result(action));
}
if (action !== Events.PASS)
break;
}
+ // Result is the result of the last action. Unless it's
+ // PASS, kill any remaining argument processors.
result = action !== undefined ? action : Events.KILL;
if (action !== Events.PASS)
this.processors.length = 0;
}
else if (this.processors.length) {
+ // We're still waiting on the longest matching processor.
+ // Kill the event, set a timeout to give up waiting if applicable.
+
result = Events.KILL;
- if (this.actions.length && options["timeout"])
+ if (options["timeout"] && (this.actions.length || events.hasNativeKey(this.events[0], this.main, this.passUnknown)))
this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
}
else if (result !== Events.KILL && !this.actions.length &&
- (this.events.length > 1 ||
- this.processors.some(function (p) !p.main.passUnknown))) {
- result = Events.KILL;
+ !(this.events[0].isReplay || this.passUnknown
+ || this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
+ // No patching processors, this isn't a fake, pass-through
+ // event, we're not in pass-through mode, and we're not
+ // choosing to pass unknown keys. Kill the event and beep.
+
+ result = Events.ABORT;
if (!Events.isEscape(this.events.slice(-1)[0]))
dactyl.beep();
events.feedingKeys = false;
}
else if (result === undefined)
+ // No matching processors, we're willing to pass this event,
+ // and we don't have a default action from a processor. Just
+ // pass the event.
result = Events.PASS;
- events.dbg("RESULT: " + this._result(result));
-
- if (result === Events.PASS || result === Events.PASS_THROUGH)
- if (this.events[0].originalTarget)
- this.events[0].originalTarget.dactylKeyPress = undefined;
+ events.dbg("RESULT: " + length + " " + this._result(result) + "\n\n");
if (result !== Events.PASS || this.events.length > 1)
- Events.kill(this.events[this.events.length - 1]);
+ if (result !== Events.ABORT || !this.events[0].isReplay)
+ Events.kill(this.events[this.events.length - 1]);
+
+ if (result === Events.PASS_THROUGH || result === Events.PASS && this.passUnknown)
+ events.passing = true;
+
+ if (result === Events.PASS_THROUGH && this.keyEvents.length)
+ events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, events.toString(e)]).join("\n\t"));
if (result === Events.PASS_THROUGH)
events.feedevents(null, this.keyEvents, { skipmap: true, isMacro: true, isReplay: true });
- else if (result === Events.PASS || result === Events.ABORT) {
+ else {
let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
- if (list.length)
- events.dbg("REFEED: " + list.map(events.closure.toString).join(""));
- events.feedevents(null, list, { skipmap: true, isMacro: true, isReplay: true });
+
+ if (result === Events.PASS)
+ events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(events.closure.toString));
+ if (list.length > length)
+ events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(events.closure.toString));
+
+ if (result === Events.PASS)
+ events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
+ if (list.length > length && this.processors.length === 0)
+ events.feedevents(null, list.slice(length));
}
return this.processors.length === 0;
let actions = [];
let processors = [];
- events.dbg("KEY: " + key + " skipmap: " + event.skipmap + " macro: " + event.isMacro + " replay: " + event.isReplay);
+ events.dbg("PROCESS(" + key + ") skipmap: " + event.skipmap + " macro: " + event.isMacro + " replay: " + event.isReplay);
for (let [i, input] in Iterator(this.processors)) {
let res = input.process(event);
if (res === Events.KILL)
break;
- buffer = buffer || input.inputBuffer;
-
if (callable(res))
actions.push(res);
events.dbg("RESULT: " + event.getPreventDefault() + " " + this._result(result));
events.dbg("ACTIONS: " + actions.length + " " + this.actions.length);
- events.dbg("PROCESSORS:", processors);
+ events.dbg("PROCESSORS:", processors, "\n");
this._actions = actions;
this.actions = actions.concat(this.actions);
+ for (let action in values(actions))
+ if (!("eventLength" in action))
+ action.eventLength = this.events.length;
+
if (result === Events.KILL)
this.actions = [];
else if (!this.actions.length && !processors.length)
function execute() {
if (self.preExecute)
self.preExecute.apply(self, args);
- let res = map.execute.call(map, update({ self: self.main.params.mappingSelf || self.main.mappingSelf || map },
- args));
+
+ args.self = self.main.params.mappingSelf || self.main.mappingSelf || map;
+ let res = map.execute.call(map, args);
+
if (self.postExecute)
self.postExecute.apply(self, args);
return res;
}
});
+/**
+ * A hive used mainly for tracking event listeners and cleaning them up when a
+ * group is destroyed.
+ */
var EventHive = Class("EventHive", Contexts.Hive, {
init: function init(group) {
init.supercall(this, group);
var [self, events] = [null, array.toObject([[event, callback]])];
else {
[self, events] = [event, event[callback || "events"]];
- [,, capture, allowUntrusted] = arguments;
+ [, , capture, allowUntrusted] = arguments;
}
+ if (Set.has(events, "input") && !Set.has(events, "dactyl-input"))
+ events["dactyl-input"] = events.input;
+
for (let [event, callback] in Iterator(events)) {
let args = [Cu.getWeakReference(target),
event,
dbg: function () {},
init: function () {
- const self = this;
this.keyEvents = [];
update(this, {
util.overlayWindow(window, {
append: <e4x xmlns={XUL}>
<window id={document.documentElement.id}>
- <!--this notifies us also of focus events in the XUL
- from: http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands !-->
- <!-- I don't think we really need this. ––Kris -->
+ <!-- http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands -->
<commandset id="dactyl-onfocus" commandupdater="true" events="focus"
oncommandupdate="dactyl.modules.events.onFocusChange(event);"/>
<commandset id="dactyl-onselect" commandupdater="true" events="select"
subtract: ["Minus", "Subtract"]
};
- this._pseudoKeys = set(["count", "leader", "nop", "pass"]);
+ this._pseudoKeys = Set(["count", "leader", "nop", "pass"]);
this._key_key = {};
this._code_key = {};
this._key_code = {};
+ this._code_nativeKey = {};
for (let list in values(this._keyTable))
for (let v in values(list)) {
}
for (let [k, v] in Iterator(KeyEvent)) {
+ this._code_nativeKey[v] = k.substr(4);
+
k = k.substr(7).toLowerCase();
let names = [k.replace(/(^|_)(.)/g, function (m, n1, n2) n2.toUpperCase())
.replace(/^NUMPAD/, "k")];
}
this._activeMenubar = false;
- this.listen(window, this, "events", true);
-
- dactyl.registerObserver("modeChange", function () {
- delete self.processor;
- });
+ this.listen(window, this, "events");
},
signals: {
"browser.locationChange": function (webProgress, request, uri) {
options.get("passkeys").flush();
+ },
+ "modes.change": function (oldMode, newMode) {
+ delete this.processor;
}
},
- /**
- * Adds an event listener for this session and removes it on
- * dactyl shutdown.
- *
- * @param {Element} target The element on which to listen.
- * @param {string} event The event to listen for.
- * @param {function} callback The function to call when the event is received.
- * @param {boolean} capture When true, listen during the capture
- * phase, otherwise during the bubbling phase.
- */
get listen() this.builtin.closure.listen,
addSessionListener: deprecated("events.listen", { get: function addSessionListener() this.listen }),
timeRecorded: Date.now()
});
- dactyl.log("Recorded " + this.recording + ": " + this._macroKeys.join(""), 9);
+ dactyl.log(_("macro.recorded", this.recording, this._macroKeys.join("")), 9);
dactyl.echomsg(_("macro.recorded", this.recording));
}
this._recording = macro || null;
if (quiet)
commandline.quiet = quiet;
+ keys = mappings.expandLeader(keys);
+
for (let [, evt_obj] in Iterator(events.fromString(keys))) {
let now = Date.now();
- for (let type in values(["keydown", "keyup", "keypress"])) {
+ let key = events.toString(evt_obj);
+ for (let type in values(["keydown", "keypress", "keyup"])) {
let evt = update({}, evt_obj, { type: type });
+ if (type !== "keypress" && !evt.keyCode)
+ evt.keyCode = evt._keyCode || 0;
if (isObject(noremap))
update(evt, noremap);
evt.dactylSavedEvents = savedEvents;
this.feedingEvent = evt;
- let event = events.create(document.commandDispatcher.focusedWindow.document, type, evt);
- if (!evt_obj.dactylString && !evt_obj.dactylShift && !mode)
- events.dispatch(dactyl.focusedElement || buffer.focusedFrame, event, evt);
+ let doc = document.commandDispatcher.focusedWindow.document;
+ let event = events.create(doc, type, evt);
+ let target = dactyl.focusedElement
+ || ["complete", "interactive"].indexOf(doc.readyState) >= 0 && doc.documentElement
+ || doc.defaultView;
+
+ if (target instanceof Element && !Events.isInputElement(target) &&
+ ["<Return>", "<Space>"].indexOf(key) == -1)
+ target = target.ownerDocument.documentElement;
+
+ if (!evt_obj.dactylString && !mode)
+ events.dispatch(target, event, evt);
else if (type === "keypress")
events.events.keypress.call(events, event);
}
* @param {Object} opts The pseudo-event. @optional
*/
create: function (doc, type, opts) {
- opts = opts || {};
- var DEFAULTS = {
+ const DEFAULTS = {
HTML: {
type: type, bubbles: true, cancelable: false
},
relatedTarget: null
}
};
- const TYPES = {
- change: "", input: "", submit: "",
- click: "Mouse", mousedown: "Mouse", mouseup: "Mouse",
- mouseover: "Mouse", mouseout: "Mouse",
- keypress: "Key", keyup: "Key", keydown: "Key"
- };
- var t = TYPES[type];
+
+ opts = opts || {};
+
+ var t = this._create_types[type];
var evt = doc.createEvent((t || "HTML") + "Events");
let defaults = DEFAULTS[t || "HTML"];
- evt["init" + t + "Event"].apply(evt, Object.keys(defaults)
- .map(function (k) k in opts ? opts[k]
- : defaults[k]));
+
+ let args = Object.keys(defaults)
+ .map(function (k) k in opts ? opts[k] : defaults[k]);
+
+ evt["init" + t + "Event"].apply(evt, args);
return evt;
},
+ _create_types: Class.memoize(function () iter(
+ {
+ Mouse: "click mousedown mouseout mouseover mouseup",
+ Key: "keydown keypress keyup",
+ "": "change dactyl-input input submit"
+ }
+ ).map(function ([k, v]) v.split(" ").map(function (v) [v, k]))
+ .flatten()
+ .toObject()),
+
/**
* Converts a user-input string of keys into a canonical
* representation.
return events.fromString(keys, unknownOk).map(events.closure.toString).join("");
},
- iterKeys: function (keys) {
+ iterKeys: function (keys) iter(function () {
let match, re = /<.*?>?>|[^<]/g;
while (match = re.exec(keys))
yield match[0];
- },
+ }()),
/**
* Dispatches an event to an element as if it were a native event.
let out = [];
for (let match in util.regexp.iterate(/<.*?>?>|[^<]|<(?!.*>)/g, input)) {
let evt_str = match[0];
+
let evt_obj = { ctrlKey: false, shiftKey: false, altKey: false, metaKey: false,
keyCode: 0, charCode: 0, type: "keypress" };
- if (evt_str.length > 1) { // <.*?>
- let [match, modifier, keyname] = evt_str.match(/^<((?:[CSMA]-)*)(.+?)>$/i) || [false, '', ''];
- modifier = modifier.toUpperCase();
+ if (evt_str.length == 1) {
+ evt_obj.charCode = evt_str.charCodeAt(0);
+ evt_obj._keyCode = this._key_code[evt_str[0].toLowerCase()];
+ evt_obj.shiftKey = evt_str !== evt_str.toLowerCase();
+ }
+ else {
+ let [match, modifier, keyname] = evt_str.match(/^<((?:[*12CASM⌘]-)*)(.+?)>$/i) || [false, '', ''];
+ modifier = Set(modifier.toUpperCase());
keyname = keyname.toLowerCase();
evt_obj.dactylKeyname = keyname;
if (/^u[0-9a-f]+$/.test(keyname))
keyname = String.fromCharCode(parseInt(keyname.substr(1), 16));
if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) ||
- this._key_code[keyname] || set.has(this._pseudoKeys, keyname))) {
- evt_obj.ctrlKey = /C-/.test(modifier);
- evt_obj.altKey = /A-/.test(modifier);
- evt_obj.shiftKey = /S-/.test(modifier);
- evt_obj.metaKey = /M-/.test(modifier);
+ this._key_code[keyname] || Set.has(this._pseudoKeys, keyname))) {
+ evt_obj.globKey ="*" in modifier;
+ evt_obj.ctrlKey ="C" in modifier;
+ evt_obj.altKey ="A" in modifier;
+ evt_obj.shiftKey ="S" in modifier;
+ evt_obj.metaKey ="M" in modifier || "⌘" in modifier;
+ evt_obj.dactylShift = evt_obj.shiftKey;
if (keyname.length == 1) { // normal characters
- if (evt_obj.shiftKey) {
+ if (evt_obj.shiftKey)
keyname = keyname.toUpperCase();
- if (keyname == keyname.toLowerCase())
- evt_obj.dactylShift = true;
- }
evt_obj.charCode = keyname.charCodeAt(0);
+ evt_obj._keyCode = this._key_code[keyname.toLowerCase()];
}
- else if (set.has(this._pseudoKeys, keyname)) {
+ else if (Set.has(this._pseudoKeys, keyname)) {
evt_obj.dactylString = "<" + this._key_key[keyname] + ">";
}
else if (/mouse$/.test(keyname)) { // mouse events
continue;
}
}
- else // a simple key (no <...>)
- evt_obj.charCode = evt_str.charCodeAt(0);
// TODO: make a list of characters that need keyCode and charCode somewhere
if (evt_obj.keyCode == 32 || evt_obj.charCode == 32)
if (evt_obj.keyCode == 60 || evt_obj.charCode == 60)
evt_obj.charCode = evt_obj.keyCode = 60; // <lt>
- evt_obj.modifiers = (evt_obj.ctrlKey && Ci.nsIDOMNSEvent.CONTROL_MASK)
- | (evt_obj.altKey && Ci.nsIDOMNSEvent.ALT_MASK)
+ evt_obj.modifiers = (evt_obj.ctrlKey && Ci.nsIDOMNSEvent.CONTROL_MASK)
+ | (evt_obj.altKey && Ci.nsIDOMNSEvent.ALT_MASK)
| (evt_obj.shiftKey && Ci.nsIDOMNSEvent.SHIFT_MASK)
- | (evt_obj.metaKey && Ci.nsIDOMNSEvent.META_MASK);
+ | (evt_obj.metaKey && Ci.nsIDOMNSEvent.META_MASK);
out.push(evt_obj);
}
let key = null;
let modifier = "";
+ if (event.globKey)
+ modifier += "*-";
if (event.ctrlKey)
modifier += "C-";
if (event.altKey)
key = key.toUpperCase();
else
key = key.toLowerCase();
+
if (!modifier && /^[a-z0-9]$/i.test(key))
return key;
}
else {
// a shift modifier is only allowed if the key is alphabetical and used in a C-A-M- mapping in the uppercase,
// or if the shift has been forced for a non-alphabetical character by the user while :map-ping
- if (key != key.toLowerCase() && (event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
+ if (key !== key.toLowerCase() && (event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
modifier += "S-";
if (/^\s$/.test(key))
key = let (s = charCode.toString(16)) "U" + "0000".substr(4 - s.length) + s;
return key;
}
}
- if (key == null)
+ if (key == null) {
+ if (event.shiftKey)
+ modifier += "S-";
key = this._key_key[event.dactylKeyname] || event.dactylKeyname;
+ }
if (key == null)
return null;
}
},
/**
- * Whether *key* is a key code defined to accept/execute input on the
- * command line.
+ * Returns true if there's a known native key handler for the given
+ * event in the given mode.
+ *
+ * @param {Event} event A keypress event.
+ * @param {Modes.Mode} mode The main mode.
+ * @param {boolean} passUnknown Whether unknown keys should be passed.
+ */
+ hasNativeKey: function hasNativeKey(event, mode, passUnknown) {
+ if (mode.input && event.charCode && !(event.ctrlKey || event.metaKey))
+ return true;
+
+ if (!passUnknown)
+ return false;
+
+ var elements = document.getElementsByTagNameNS(XUL, "key");
+ var filters = [];
+
+ if (event.keyCode)
+ filters.push(["keycode", this._code_nativeKey[event.keyCode]]);
+ if (event.charCode) {
+ let key = String.fromCharCode(event.charCode);
+ filters.push(["key", key.toUpperCase()],
+ ["key", key.toLowerCase()]);
+ }
+
+ let accel = util.OS.isMacOSX ? "metaKey" : "ctrlKey";
+
+ let access = iter({ 1: "shiftKey", 2: "ctrlKey", 4: "altKey", 8: "metaKey" })
+ .filter(function ([k, v]) this & k, prefs.get("ui.key.chromeAccess"))
+ .map(function ([k, v]) [v, true])
+ .toObject();
+
+ outer:
+ for (let [, key] in iter(elements))
+ if (filters.some(function ([k, v]) key.getAttribute(k) == v)) {
+ let keys = { ctrlKey: false, altKey: false, shiftKey: false, metaKey: false };
+ let needed = { ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey, metaKey: event.metaKey };
+
+ let modifiers = (key.getAttribute("modifiers") || "").trim().split(/[\s,]+/);
+ for (let modifier in values(modifiers))
+ switch (modifier) {
+ case "access": update(keys, access); break;
+ case "accel": keys[accel] = true; break;
+ default: keys[modifier + "Key"] = true; break;
+ case "any":
+ if (!iter.some(keys, function ([k, v]) v && needed[k]))
+ continue outer;
+ for (let [k, v] in iter(keys)) {
+ if (v)
+ needed[k] = false;
+ keys[k] = false;
+ }
+ break;
+ }
+
+ if (iter(needed).every(function ([k, v]) v == keys[k]))
+ return key;
+ }
+
+ return false;
+ },
+
+ /**
+ * Returns true if *key* is a key code defined to accept/execute input on
+ * the command line.
*
* @param {string} key The key code to test.
* @returns {boolean}
isAcceptKey: function (key) key == "<Return>" || key == "<C-j>" || key == "<C-m>",
/**
- * Whether *key* is a key code defined to reject/cancel input on the
- * command line.
+ * Returns true if *key* is a key code defined to reject/cancel input on
+ * the command line.
*
* @param {string} key The key code to test.
* @returns {boolean}
*/
isCancelKey: function (key) key == "<Esc>" || key == "<C-[>" || key == "<C-c>",
+ /**
+ * Returns true if *node* belongs to the current content document or any
+ * sub-frame thereof.
+ *
+ * @param {Node|Document|Window} node The node to test.
+ * @returns {boolean}
+ */
isContentNode: function isContentNode(node) {
let win = (node.ownerDocument || node).defaultView || node;
return XPCNativeWrapper(win).top == content;
if (buffer.loaded)
return true;
- dactyl.echo(_("macro.loadWaiting"), commandline.DISALLOW_MULTILINE);
+ dactyl.echo(_("macro.loadWaiting"), commandline.FORCE_SINGLELINE);
const maxWaitTime = (time || 25);
- util.waitFor(function () !events.feedingKeys || buffer.loaded, this, maxWaitTime * 1000, true);
+ util.waitFor(function () buffer.loaded, this, maxWaitTime * 1000, true);
+ dactyl.echo("", commandline.FORCE_SINGLELINE);
if (!buffer.loaded)
dactyl.echoerr(_("macro.loadFailed", maxWaitTime));
let win = (elem.ownerDocument || elem).defaultView || elem;
- if (events.isContentNode(elem) && !buffer.focusAllowed(elem)
- && !(services.focus.getLastFocusMethod(win) & 0x7000)
+ if (!(services.focus.getLastFocusMethod(win) & 0x7000)
+ && events.isContentNode(elem)
+ && !buffer.focusAllowed(elem)
&& isinstance(elem, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, Window])) {
+
if (elem.frameElement)
dactyl.focusContent(true);
else if (!(elem instanceof Window) || Editor.getEditor(elem))
dactyl.focus(window);
}
+
+ if (elem instanceof Element)
+ elem.dactylFocusAllowed = undefined;
},
/*
elem.dactylKeyPress = elem.value;
util.timeout(function () {
if (elem.dactylKeyPress !== undefined && elem.value !== elem.dactylKeyPress)
- events.dispatch(elem, events.create(elem.ownerDocument, "input"));
+ events.dispatch(elem, events.create(elem.ownerDocument, "dactyl-input"));
elem.dactylKeyPress = undefined;
});
}
let ignore = false;
- if (modes.main == modes.PASS_THROUGH)
+ if (mode.main == modes.PASS_THROUGH)
ignore = !Events.isEscape(key) && key != "<C-v>";
- else if (modes.main == modes.QUOTE) {
+ else if (mode.main == modes.QUOTE) {
if (modes.getStack(1).main == modes.PASS_THROUGH) {
- mode.params.mainMode = modes.getStack(2).main;
+ mode = Modes.StackElement(modes.getStack(2).main);
ignore = Events.isEscape(key);
}
else if (events.shouldPass(event))
- mode.params.mainMode = modes.getStack(1).main;
+ mode = Modes.StackElement(modes.getStack(1).main);
else
ignore = true;
- if (ignore && !Events.isEscape(key))
- modes.pop();
+ modes.pop();
}
else if (!event.isMacro && !event.noremap && events.shouldPass(event))
ignore = true;
},
keyup: function onKeyUp(event) {
- this.keyEvents.push(event);
+ if (event.type == "keydown")
+ this.keyEvents.push(event);
+ else if (!this.processor)
+ this.keyEvents = [];
- let pass = this.feedingEvent && this.feedingEvent.isReplay ||
+ let pass = this.passing && !event.isMacro ||
+ this.feedingEvent && this.feedingEvent.isReplay ||
event.isReplay ||
modes.main == modes.PASS_THROUGH ||
modes.main == modes.QUOTE
&& modes.getStack(1).main !== modes.PASS_THROUGH
&& !this.shouldPass(event) ||
- !modes.passThrough && this.shouldPass(event);
+ !modes.passThrough && this.shouldPass(event) ||
+ !this.processor && event.type === "keydown"
+ && options.get("passunknown").getKey(modes.main.allBases)
+ && let (key = events.toString(event))
+ !modes.main.allBases.some(
+ function (mode) mappings.hives.some(
+ function (hive) hive.get(mode, key) || hive.getCandidates(mode, key)));
+
+ if (event.type === "keydown")
+ this.passing = pass;
- events.dbg("ON " + event.type.toUpperCase() + " " + this.toString(event) + " pass: " + pass);
+ events.dbg("ON " + event.type.toUpperCase() + " " + this.toString(event) + " pass: " + pass + " replay: " + event.isReplay + " macro: " + event.isMacro);
// Prevents certain sites from transferring focus to an input box
// before we get a chance to process our key bindings on the
// "keypress" event.
- if (!pass && !Events.isInputElement(dactyl.focusedElement))
+ if (!pass)
event.stopPropagation();
},
keydown: function onKeyDown(event) {
+ if (!event.isMacro)
+ this.passing = false;
this.events.keyup.call(this, event);
},
let elem = event.target;
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
- for (; win; win = win != win.parent && win.parent)
+ for (; win; win = win != win.parent && win.parent) {
+ for (; elem instanceof Element; elem = elem.parentNode)
+ elem.dactylFocusAllowed = true;
win.document.dactylFocusAllowed = true;
+ }
},
popupshown: function onPopupShown(event) {
modes.push(modes.MENU);
},
- popuphidden: function onPopupHidden() {
- // gContextMenu is set to NULL, when a context menu is closed
+ popuphidden: function onPopupHidden(event) {
if (window.gContextMenu == null && !this._activeMenubar)
modes.remove(modes.MENU, true);
modes.remove(modes.AUTOCOMPLETE);
if (elem == null && urlbar && urlbar.inputField == this._lastFocus)
util.threadYield(true); // Why? --Kris
- while (modes.main.ownsFocus && !modes.topOfStack.params.holdFocus)
+ while (modes.main.ownsFocus && modes.topOfStack.params.ownsFocus != elem
+ && !modes.topOfStack.params.holdFocus)
modes.pop(null, { fromFocus: true });
}
finally {
this._lastFocus = elem;
+
+ if (modes.main.ownsFocus)
+ modes.topOfStack.params.ownsFocus = elem;
}
},
key === "<Esc>" || key === "<C-[>",
isHidden: function isHidden(elem, aggressive) {
- for (let e = elem; e instanceof Element; e = e.parentNode) {
- if (util.computedStyle(e).visibility !== "visible" ||
- aggressive && e.boxObject && e.boxObject.height === 0)
- return true;
- }
+ if (util.computedStyle(elem).visibility !== "visible")
+ return true;
+
+ if (aggressive)
+ for (let e = elem; e instanceof Element; e = e.parentNode) {
+ if (!/set$/.test(e.localName) && e.boxObject && e.boxObject.height === 0)
+ return true;
+ else if (e.namespaceURI == XUL && e.localName === "panel")
+ break;
+ }
return false;
},
isInputElement: function isInputElement(elem) {
- return elem instanceof HTMLInputElement && set.has(util.editableInputs, elem.type) ||
- isinstance(elem, [HTMLIsIndexElement, HTMLEmbedElement,
+ return elem instanceof HTMLInputElement && Set.has(util.editableInputs, elem.type) ||
+ isinstance(elem, [HTMLEmbedElement,
HTMLObjectElement, HTMLSelectElement,
HTMLTextAreaElement,
- Ci.nsIDOMXULTreeElement, Ci.nsIDOMXULTextBoxElement]) ||
+ Ci.nsIDOMXULTextBoxElement]) ||
elem instanceof Window && Editor.getEditor(elem);
},
else
dactyl.echoerr(_("error.argumentRequired"));
}, {
+ argCount: "?",
bang: true,
completer: function (context) completion.macro(context),
literal: 0
});
- commands.add(["macros"],
+ commands.add(["mac[ros]"],
"List all macros",
function (args) { completion.listCompleter("macro", args[0]); }, {
argCount: "?",
mappings: function () {
mappings.add([modes.MAIN],
- ["<A-b>"], "Process the next key as a builtin mapping",
+ ["<A-b>", "<pass-next-key-builtin>"], "Process the next key as a builtin mapping",
function () {
events.processor = ProcessorStack(modes.getStack(0), mappings.hives.array, true);
events.processor.keyEvents = events.keyEvents;
["<C-z>", "<pass-all-keys>"], "Temporarily ignore all " + config.appName + " key bindings",
function () { modes.push(modes.PASS_THROUGH); });
- mappings.add([modes.MAIN],
+ mappings.add([modes.MAIN, modes.PASS_THROUGH, modes.QUOTE],
["<C-v>", "<pass-next-key>"], "Pass through next key",
function () {
if (modes.main == modes.QUOTE)
modes.push(modes.QUOTE);
});
+ mappings.add([modes.BASE],
+ ["<CapsLock>"], "Do Nothing",
+ function () {});
+
mappings.add([modes.BASE],
["<Nop>"], "Do nothing",
function () {});
"sitemap", "", {
flush: function flush() {
memoize(this, "filters", function () this.value.filter(function (f) f(buffer.documentURI)));
- memoize(this, "pass", function () set(array.flatten(this.filters.map(function (f) f.keys))));
+ memoize(this, "pass", function () Set(array.flatten(this.filters.map(function (f) f.keys))));
memoize(this, "commandHive", function hive() Hive(this.filters, "command"));
memoize(this, "inputHive", function hive() Hive(this.filters, "input"));
},
- has: function (key) set.has(this.pass, key) || set.has(this.commandHive.stack.mappings, key),
+ has: function (key) Set.has(this.pass, key) || Set.has(this.commandHive.stack.mappings, key),
get pass() (this.flush(), this.pass),
filter.keys = events.fromString(vals[0]).map(events.closure.toString);
filter.commandKeys = vals.slice(1).map(events.closure.canonicalKeys);
- filter.inputKeys = filter.commandKeys.filter(/^<[ACM]-/);
+ filter.inputKeys = filter.commandKeys.filter(bind("test", /^<[ACM]-/));
});
this.flush();
return values;
options.add(["strictfocus", "sf"],
"Prevent scripts from focusing input elements without user intervention",
- "boolean", true);
+ "sitemap", "'chrome:*':laissez-faire,*:moderate",
+ {
+ values: {
+ despotic: "Only allow focus changes when explicitly requested by the user",
+ moderate: "Allow focus changes after user-initiated focus change",
+ "laissez-faire": "Always allow focus changes"
+ }
+ });
options.add(["timeout", "tmo"],
"Whether to execute a shorter key command after a timeout when a longer command exists",
<xsl:if test="//dactyl:toc[1 and self::*]">
<div dactyl:highlight="HelpTOC">
- <h2>Contents</h2>
+ <h2><!--L-->Contents</h2>
<xsl:if test="@start">
<xsl:call-template name="toc">
<xsl:with-param name="level" select="number(@start)"/>
</xsl:when>
<xsl:when test="contains($type, 'list') or contains($type, 'map')">
<span dactyl:highlight="HelpString" delim=""><xsl:apply-templates mode="help-1"/></span>
- <xsl:if test=". = ''">(empty)</xsl:if>
+ <xsl:if test=". = ''"><!--L-->(empty)</xsl:if>
</xsl:when>
<xsl:otherwise>
<span>
<a>
<xsl:choose>
<xsl:when test="not(@topic)"/>
- <xsl:when test="regexp:match(@topic, '^([a-zA-Z]*:|[^/]*#|/)', '')">
+ <xsl:when test="regexp:match(@topic, '^([a-zA-Z]+:|[^/]*#|/)', '')">
<xsl:attribute name="href"><xsl:value-of select="@topic"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="href"><xsl:value-of select="concat('dactyl://help-tag/', @topic)"/></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
- <xsl:if test="regexp:match(@topic, '^[a-zA-Z]*:', '')
+ <xsl:if test="regexp:match(@topic, '^[a-zA-Z]+:', '')
and not(starts-with(@topic, 'mailto:') or
starts-with(@topic, 'chrome:') or
starts-with(@topic, 'resource:') or
<xsl:template match="dactyl:deprecated" mode="help-2">
<p style="clear: both;">
<xsl:apply-templates select="@*" mode="help-1"/>
- <span dactyl:highlight="HelpWarning">Deprecated:</span>
+ <span dactyl:highlight="HelpWarning"><!--L-->Deprecated:</span>
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()" mode="help-1"/>
</p>
<p style="clear: both;">
<xsl:apply-templates select="@*" mode="help-1"/>
<div style="clear: both;"/>
- <span dactyl:highlight="HelpNote">Note:</span>
+ <span dactyl:highlight="HelpNote"><!--L-->Note:</span>
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()" mode="help-1"/>
</p>
<p style="clear: both;">
<xsl:apply-templates select="@*" mode="help-1"/>
<div style="clear: both;"/>
- <span dactyl:highlight="HelpWarning">Warning:</span>
+ <span dactyl:highlight="HelpWarning"><!--L-->Warning:</span>
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()" mode="help-1"/>
</p>
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</div>
</xsl:template>
- <xsl:template match="dactyl:str | dactyl:type" mode="help-2">
- <span>
- <xsl:if test="self::dactyl:str"><xsl:attribute name="dactyl:highlight">HelpString</xsl:attribute></xsl:if>
- <xsl:if test="self::dactyl:type"><xsl:attribute name="dactyl:highlight">HelpType</xsl:attribute></xsl:if>
+ <xsl:template match="dactyl:type" mode="help-2">
+ <a dactyl:highlight="HelpType">
+ <xsl:choose>
+ <xsl:when test="contains(ancestor::*/@document-tags, concat(' ', ., ' '))">
+ <xsl:attribute name="href">#<xsl:value-of select="."/></xsl:attribute>
+ </xsl:when>
+ <xsl:when test="not(. = 'boolean' or . = 'number' or . = 'string')">
+ <xsl:attribute name="href">dactyl://help-tag/<xsl:value-of select="."/></xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:apply-templates select="@*|node()" mode="help-1"/>
+ </a>
+ </xsl:template>
+ <xsl:template match="dactyl:str" mode="help-2">
+ <span dactyl:highlight="HelpString">
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</span>
</xsl:template>
this.open();
this.top = opts.window || content;
- this.top.addEventListener("resize", hints.resizeTimer.closure.tell, true);
- this.top.addEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, false, true);
+ this.top.addEventListener("resize", this.closure._onResize, true);
+ this.top.addEventListener("dactyl-commandupdate", this.closure._onResize, false, true);
this.generate();
this.show();
+ this.magic = true;
if (this.validHints.length == 0) {
dactyl.beep();
}
else if (this.validHints.length == 1 && !this.continue)
this.process(false);
- else // Ticket #185
+ else
this.checkUnique();
},
hints.setClass(this.imgSpan, this.valid ? val : null);
},
+ get ambiguous() this.span.hasAttribute("ambiguous"),
+ set ambiguous(val) {
+ let meth = val ? "setAttribute" : "removeAttribute";
+ this.elem[meth]("ambiguous", "true");
+ this.span[meth]("ambiguous", "true");
+ if (this.imgSpan)
+ this.imgSpan[meth]("ambiguous", "true");
+ },
+
get valid() this._valid,
set valid(val) {
this._valid = val,
this.span.style.display = (val ? "" : "none");
if (this.imgSpan)
this.imgSpan.style.display = (val ? "" : "none");
-
this.active = this.active;
}
},
if (hints.hintSession == this)
hints.hintSession = null;
if (this.top) {
- this.top.removeEventListener("resize", hints.resizeTimer.closure.tell, true);
- this.top.removeEventListener("dactyl-commandupdate", hints.resizeTimer.closure.tell, true);
+ this.top.removeEventListener("resize", this.closure._onResize, true);
+ this.top.removeEventListener("dactyl-commandupdate", this.closure._onResize, true);
}
this.removeHints(0);
return res.reverse().join("");
},
+ /**
+ * The reverse of {@link #getHintString}. Given a hint string,
+ * returns its index.
+ *
+ * @param {string} str The hint's string.
+ * @returns {number} The hint's index.
+ */
+ getHintNumber: function getHintNumber(str) {
+ let base = this.hintKeys.length;
+ let res = 0;
+ for (let char in values(str))
+ res = res * base + this.hintKeys.indexOf(char);
+ return res;
+ },
+
/**
* Returns true if the given key string represents a
* pseudo-hint-number.
let doc = win.document;
+ memoize(doc, "dactylLabels", function ()
+ iter([l.getAttribute("for"), l]
+ for (l in array.iterValues(doc.querySelectorAll("label[for]"))))
+ .toObject());
+
let [offsetX, offsetY] = this.getContainerOffsets(doc);
offsets = offsets || { left: 0, right: 0, top: 0, bottom: 0 };
function isVisible(elem) {
let rect = elem.getBoundingClientRect();
- if (!rect || !rect.width || !rect.height ||
+ if (!rect ||
rect.top > offsets.bottom || rect.bottom < offsets.top ||
rect.left > offsets.right || rect.right < offsets.left)
return false;
+ if (!rect.width || !rect.height)
+ if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && util.computedStyle(elem).float != "none" && isVisible(elem)))
+ return false;
+
let computedStyle = doc.defaultView.getComputedStyle(elem, null);
if (computedStyle.visibility != "visible" || computedStyle.display == "none")
return false;
util.computedStyle(fragment).height; // Force application of binding.
let container = doc.getAnonymousElementByAttribute(fragment, "anonid", "hints") || fragment;
- let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none"/>, doc);
+ let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none;"/>, doc);
let mode = this.hintMode;
let res = mode.matcher(doc);
let start = this.pageHints.length;
- for (let elem in res) {
- let hint = { elem: elem, showText: false, __proto__: this.Hint };
-
- if (!isVisible(elem) || mode.filter && !mode.filter(elem))
- continue;
+ let _hints = [];
+ for (let elem in res)
+ if (isVisible(elem) && (!mode.filter || mode.filter(elem)))
+ _hints.push({
+ elem: elem,
+ rect: elem.getClientRects()[0] || elem.getBoundingClientRect(),
+ showText: false,
+ __proto__: this.Hint
+ });
+
+ for (let hint in values(_hints)) {
+ let { elem, rect } = hint;
if (elem.hasAttributeNS(NS, "hint"))
[hint.text, hint.showText] = [elem.getAttributeNS(NS, "hint"), true];
else
hint.text = elem.textContent.toLowerCase();
- hint.span = baseNodeAbsolute.cloneNode(true);
+ hint.span = baseNodeAbsolute.cloneNode(false);
- let rect = elem.getClientRects()[0] || elem.getBoundingClientRect();
let leftPos = Math.max((rect.left + offsetX), offsetX);
let topPos = Math.max((rect.top + offsetY), offsetY);
if (elem instanceof HTMLAreaElement)
[leftPos, topPos] = this.getAreaOffset(elem, leftPos, topPos);
- hint.span.style.left = leftPos + "px";
- hint.span.style.top = topPos + "px";
+ hint.span.setAttribute("style", ["display: none; left:", leftPos, "px; top:", topPos, "px"].join(""));
container.appendChild(hint.span);
this.pageHints.push(hint);
},
/**
- * Handle a hint mode event.
+ * Handle a hints mode event.
*
* @param {Event} event The event to handle.
*/
return PASS;
},
- onResize: function () {
+ onResize: function onResize() {
this.removeHints(0);
this.generate(this.top);
this.show();
},
+ _onResize: function _onResize() {
+ if (this.magic)
+ hints.resizeTimer.tell();
+ },
+
/**
* Finish hinting.
*
// This "followhints" option is *too* confusing. For me, and
// presumably for users, too. --Kris
- if (options["followhints"] > 0) {
- if (!followFirst)
- return; // no return hit; don't examine uniqueness
-
- // OK. return hit. But there's more than one hint, and
- // there's no tab-selected current link. Do not follow in mode 2
- dactyl.assert(options["followhints"] != 2 || this.validHints.length == 1 || this.hintNumber);
- }
+ if (options["followhints"] > 0 && !followFirst)
+ return; // no return hit; don't examine uniqueness
if (!followFirst) {
let firstHref = this.validHints[0].elem.getAttribute("href") || null;
else if (n)
hints.setClass(elem, n % 2);
else
- hints.setClass(elem, this.validHints[Math.max(0, this.hintNumber-1)].elem === elem);
+ hints.setClass(elem, this.validHints[Math.max(0, this.hintNumber - 1)].elem === elem);
if (n--)
this.timeout(next, 50);
modes.push(modes.IGNORE, modes.HINTS);
}
+ dactyl.trapErrors("action", this.hintMode,
+ elem, elem.href || elem.src || "",
+ this.extendedhintCount, top);
+
this.timeout(function () {
- if ((modes.extended & modes.HINTS) && !this.continue)
+ if (modes.main == modes.IGNORE && !this.continue)
modes.pop();
commandline.lastEcho = null; // Hack.
- dactyl.trapErrors("action", this.hintMode,
- elem, elem.href || elem.src || "",
- this.extendedhintCount, top);
if (this.continue && this.top)
this.show();
}, timeout);
*/
removeHints: function _removeHints(timeout) {
for (let { doc, start, end } in values(this.docs)) {
+ // Goddamn stupid fucking Gecko 1.x security manager bullshit.
+ try { delete doc.dactylLabels; } catch (e) { doc.dactylLabels = undefined; }
+
for (let elem in util.evaluateXPath("//*[@dactyl:highlight='hints']", doc))
elem.parentNode.removeChild(elem);
- for (let i in util.range(start, end + 1))
+ for (let i in util.range(start, end + 1)) {
+ this.pageHints[i].ambiguous = false;
this.pageHints[i].valid = false;
+ }
}
styles.system.remove("hint-positions");
text.push(UTF8(hint.elem.checked ? "⊙" : "○"));
else if (hint.elem.type === "checkbox")
text.push(UTF8(hint.elem.checked ? "☑" : "☐"));
- if (hint.showText)
+ if (hint.showText && !/^\s*$/.test(hint.text))
text.push(hint.text.substr(0, 50));
hint.span.setAttribute("text", str + (text.length ? ": " + text.join(" ") : ""));
hint.span.setAttribute("number", str);
if (hint.imgSpan)
hint.imgSpan.setAttribute("number", str);
+
hint.active = activeHint == hintnum;
+
this.validHints.push(hint);
hintnum++;
}
}
+ let base = this.hintKeys.length;
+ for (let [i, hint] in Iterator(this.validHints))
+ hint.ambiguous = (i + 1) * base <= this.validHints.length;
+
if (options["usermode"]) {
let css = [];
for (let hint in values(this.pageHints)) {
events.listen(appContent, "scroll", this.resizeTimer.closure.tell, false);
const Mode = Hints.Mode;
- Mode.defaultValue("tags", function () function () options.get("hinttags").matcher);
Mode.prototype.__defineGetter__("matcher", function ()
- options.get("extendedhinttags").getKey(this.name, this.tags()));
+ options.get("extendedhinttags").getKey(this.name, options.get("hinttags").matcher));
this.modes = {};
this.addMode(";", "Focus hint", buffer.closure.focusElement);
this.addMode("?", "Show information for hint", function (elem) buffer.showElementInfo(elem));
this.addMode("s", "Save hint", function (elem) buffer.saveLink(elem, false));
this.addMode("f", "Focus frame", function (elem) dactyl.focus(elem.ownerDocument.defaultView));
- this.addMode("F", "Focus frame or pseudo-frame", buffer.closure.focusElement, null, isScrollable);
+ this.addMode("F", "Focus frame or pseudo-frame", buffer.closure.focusElement, isScrollable);
this.addMode("o", "Follow hint", function (elem) buffer.followLink(elem, dactyl.CURRENT_TAB));
this.addMode("t", "Follow hint in a new tab", function (elem) buffer.followLink(elem, dactyl.NEW_TAB));
this.addMode("b", "Follow hint in a background tab", function (elem) buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB));
hintSession: Modes.boundProperty(),
/**
- * Creates a new hint mode.
+ * Creates a new hints mode.
*
* @param {string} mode The letter that identifies this mode.
* @param {string} prompt The description to display to the user
* about this mode.
* @param {function(Node)} action The function to be called with the
* element that matches.
- * @param {function():string} tags The function that returns an
- * XPath expression to decide which elements can be hinted (the
- * default returns options["hinttags"]).
- * @optional
+ * @param {function(Node):boolean} filter A function used to filter
+ * the returned node set.
+ * @param {[string]} tags A value to add to the default
+ * 'extendedhinttags' value for this mode.
+ * @optional
*/
- addMode: function (mode, prompt, action, tags) {
- arguments[1] = UTF8(prompt);
- this.modes[mode] = Hints.Mode.apply(Hints.Mode, arguments);
+ addMode: function (mode, prompt, action, filter, tags) {
+ function toString(regexp) RegExp.prototype.toString.call(regexp);
+
+ if (tags != null) {
+ let eht = options.get("extendedhinttags");
+ let update = eht.isDefault;
+
+ let value = eht.parse(Option.quote(util.regexp.escape(mode)) + ":" + tags.map(Option.quote))[0];
+ eht.defaultValue = eht.defaultValue.filter(function (re) toString(re) != toString(value))
+ .concat(value);
+
+ if (update)
+ eht.reset();
+ }
+
+ this.modes[mode] = Hints.Mode(mode, UTF8(prompt), action, filter);
},
/**
let type = elem.type;
- if (elem instanceof HTMLInputElement && set.has(util.editableInputs, elem.type))
+ if (elem instanceof HTMLInputElement && Set.has(util.editableInputs, elem.type))
return [elem.value, false];
else {
for (let [, option] in Iterator(options["hintinputs"])) {
return [elem.alt.toLowerCase(), true];
}
else if (elem.value && type != "password") {
- // radio's and checkboxes often use internal ids as values - maybe make this an option too...
+ // radios and checkboxes often use internal ids as values - maybe make this an option too...
if (! ((type == "radio" || type == "checkbox") && !isNaN(elem.value)))
return [elem.value.toLowerCase(), (type == "radio" || type == "checkbox")];
}
}
else if (option == "label") {
if (elem.id) {
- // TODO: (possibly) do some guess work for label-like objects
- let label = util.evaluateXPath(["label[@for=" + elem.id.quote() + "]"], doc).snapshotItem(0);
+ let label = elem.ownerDocument.dactylLabels[elem.id];
if (label)
return [label.textContent.toLowerCase(), true];
}
* returns true if each set of characters typed can be found, in any
* order, in the link.
*
- * @param {string} hintString The string typed by the user.
+ * @param {string} hintString The string typed by the user.
* @returns {function(String):boolean} A function that takes the text
* of a hint and returns true if all the (space-delimited) sets of
* characters typed by the user can be found in it.
open: function open(mode, opts) {
this._extendedhintCount = opts.count;
commandline.input(["Normal", mode], "", {
+ autocomplete: false,
completer: function (context) {
context.compare = function () 0;
context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))];
if (arg)
hints.show(arg, opts);
},
- onChange: function () {
+ onChange: function (arg) {
+ if (Object.keys(hints.modes).some(function (m) m != arg && m.indexOf(arg) == 0))
+ return;
+
this.accepted = true;
modes.pop();
- },
+ }
});
},
return -1;
},
- Mode: Struct("HintMode", "name", "prompt", "action", "tags", "filter")
+ Mode: Struct("HintMode", "name", "prompt", "action", "filter")
.localize("prompt")
}, {
modes: function initModes() {
initModes.require("commandline");
modes.addMode("HINTS", {
extended: true,
- description: "Active when selecting elements in QuickHint or ExtendedHint mode",
+ description: "Active when selecting elements with hints",
bases: [modes.COMMAND_LINE],
input: true,
ownsBuffer: true
mappings: function () {
var myModes = config.browserModes.concat(modes.OUTPUT_MULTILINE);
mappings.add(myModes, ["f"],
- "Start QuickHint mode",
+ "Start Hints mode",
function () { hints.show("o"); });
mappings.add(myModes, ["F"],
- "Start QuickHint mode, but open link in a new tab",
+ "Start Hints mode, but open link in a new tab",
function () { hints.show(options.get("activate").has("links") ? "t" : "b"); });
mappings.add(myModes, [";"],
- "Start an extended hint mode",
+ "Start an extended hints mode",
function ({ count }) { hints.open(";", { count: count }); },
{ count: true });
mappings.add(myModes, ["g;"],
- "Start an extended hint mode and stay there until <Esc> is pressed",
+ "Start an extended hints mode and stay there until <Esc> is pressed",
function ({ count }) { hints.open("g;", { continue: true, count: count }); },
{ count: true });
"XPath or CSS selector strings of hintable elements for extended hint modes",
"regexpmap", {
"[iI]": "img",
- "[asOTivVWy]": ["a[href]", "area[href]", "img[src]", "iframe[src]"],
+ "[asOTvVWy]": ["a[href]", "area[href]", "img[src]", "iframe[src]"],
"[f]": "body",
"[F]": ["body", "code", "div", "html", "p", "pre", "span"],
"[S]": ["input:not([type=hidden])", "textarea", "button", "select"]
{
keepQuotes: true,
getKey: function (val, default_)
- let (res = array.nth(this.value, function (re) re.test(val), 0))
+ let (res = array.nth(this.value, function (re) let (match = re.exec(val)) match && match[0] == val, 0))
res ? res.matcher : default_,
setter: function (vals) {
for (let value in values(vals))
});
options.add(["hinttags", "ht"],
- "XPath string of hintable elements activated by 'f' and 'F'",
- "stringlist", "input:not([type=hidden]),a,area,iframe,textarea,button,select," +
+ "XPath or CSS selector strings of hintable elements for Hints mode",
+ "stringlist", "input:not([type=hidden]),a[href],area,iframe,textarea,button,select," +
"[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," +
- "[tabindex],[role=link],[role=button]",
+ "[tabindex],[role=link],[role=button],[contenteditable=true]",
{
setter: function (values) {
this.matcher = util.compileMatcher(values);
},
validator: function (value) {
let values = events.fromString(value).map(events.closure.toString);
- return Option.validIf(array.uniq(values).length === values.length,
- "Duplicate keys not allowed");
+ return Option.validIf(array.uniq(values).length === values.length && values.length > 1,
+ _("option.hintkeys.duplicate"));
}
});
{ validator: function (value) value >= 0 });
options.add(["followhints", "fh"],
- // FIXME: this description isn't very clear but I can't think of a
- // better one right now.
- "Change the behavior of <Return> in hint mode",
+ "Define the conditions under which selected hints are followed",
"number", 0,
{
values: {
"0": "Follow the first hint as soon as typed text uniquely identifies it. Follow the selected hint on <Return>.",
"1": "Follow the selected hint on <Return>.",
- "2": "Follow the selected hint on <Return> only it's been <Tab>-selected."
}
});
return {
url: node.uri,
title: node.title,
- icon: node.icon ? node.icon.spec : DEFAULT_FAVICON
+ icon: node.icon ? node.icon : DEFAULT_FAVICON
};
}).toArray();
root.containerOpen = false; // close a container after using it!
"uri",
"visitcount"
].map(function (order) [
- ["+" + order.replace(" ", ""), "Sort by " + order + " ascending"],
- ["-" + order.replace(" ", ""), "Sort by " + order + " descending"]
+ ["+" + order.replace(" ", ""), /*L*/"Sort by " + order + " ascending"],
+ ["-" + order.replace(" ", ""), /*L*/"Sort by " + order + " descending"]
]));
}
}
* A class representing key mappings. Instances are created by the
* {@link Mappings} class.
*
- * @param {number[]} modes The modes in which this mapping is active.
- * @param {string[]} keys The key sequences which are bound to
+ * @param {[Modes.Mode]} modes The modes in which this mapping is active.
+ * @param {[string]} keys The key sequences which are bound to
* *action*.
* @param {string} description A short one line description of the key mapping.
* @param {function} action The action invoked by each key sequence.
*/
var Map = Class("Map", {
init: function (modes, keys, description, action, extraInfo) {
- modes = Array.concat(modes);
- if (!modes.every(util.identity))
- throw TypeError("Invalid modes: " + modes);
-
this.id = ++Map.id;
this.modes = modes;
this._keys = keys;
this.action = action;
this.description = description;
- if (Object.freeze)
- Object.freeze(this.modes);
+ Object.freeze(this.modes);
if (extraInfo)
- update(this, extraInfo);
+ this.update(extraInfo);
},
name: Class.memoize(function () this.names[0]),
- /** @property {string[]} All of this mapping's names (key sequences). */
+ /** @property {[string]} All of this mapping's names (key sequences). */
names: Class.memoize(function () this._keys.map(function (k) events.canonicalKeys(k))),
get toStringParams() [this.modes.map(function (m) m.name), this.names.map(String.quote)],
/** @property {number} A unique ID for this mapping. */
id: null,
- /** @property {number[]} All of the modes for which this mapping applies. */
+ /** @property {[Modes.Mode]} All of the modes for which this mapping applies. */
modes: null,
/** @property {function (number)} The function called to execute this mapping. */
action: null,
*/
hasName: function (name) this.keys.indexOf(name) >= 0,
- get keys() this.names.map(mappings.expandLeader),
+ get keys() array.flatten(this.names.map(mappings.closure.expand)),
/**
* Execute the action for this mapping.
mappings.repeat = repeat;
if (this.executing)
- util.dumpStack("Attempt to execute mapping recursively: " + args.command);
+ util.dumpStack(_("map.recursive", args.command));
dactyl.assert(!this.executing, _("map.recursive", args.command));
try {
},
/**
- * Adds a new default key mapping.
+ * Adds a new key mapping.
*
- * @param {number[]} modes The modes that this mapping applies to.
- * @param {string[]} keys The key sequences which are bound to *action*.
+ * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+ * @param {[string]} keys The key sequences which are bound to *action*.
* @param {string} description A description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extra An optional extra configuration hash.
add: function (modes, keys, description, action, extra) {
extra = extra || {};
+ modes = Array.concat(modes);
+ if (!modes.every(util.identity))
+ throw TypeError(/*L*/"Invalid modes: " + modes);
+
let map = Map(modes, keys, description, action, extra);
map.definedAt = contexts.getCaller(Components.stack.caller);
map.hive = this;
get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
- expandLeader: function (keyString) keyString.replace(/<Leader>/i, options["mapleader"]),
+ expandLeader: function expandLeader(keyString) keyString.replace(/<Leader>/i, function () options["mapleader"]),
+
+ prefixes: Class.memoize(function () {
+ let list = Array.map("CASM", function (s) s + "-");
+
+ return iter(util.range(0, 1 << list.length)).map(function (mask)
+ list.filter(function (p, i) mask & (1 << i)).join("")).toArray().concat("*-");
+ }),
+
+ expand: function expand(keys) {
+ keys = keys.replace(/<leader>/i, options["mapleader"]);
+ if (!/<\*-/.test(keys))
+ return keys;
+
+ return util.debrace(events.iterKeys(keys).map(function (key) {
+ if (/^<\*-/.test(key))
+ return ["<", this.prefixes, key.slice(3)];
+ return key;
+ }, this).flatten().array).map(function (k) events.canonicalKeys(k));
+ },
iterate: function (mode) {
let seen = {};
for (let hive in this.hives.iterValues())
for (let map in array(hive.getStack(mode)).iterValues())
- if (!set.add(seen, map.name))
+ if (!Set.add(seen, map.name))
yield map;
},
/**
* Adds a new default key mapping.
*
- * @param {number[]} modes The modes that this mapping applies to.
- * @param {string[]} keys The key sequences which are bound to *action*.
+ * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+ * @param {[string]} keys The key sequences which are bound to *action*.
* @param {string} description A description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extra An optional extra configuration hash.
/**
* Adds a new user-defined key mapping.
*
- * @param {number[]} modes The modes that this mapping applies to.
- * @param {string[]} keys The key sequences which are bound to *action*.
+ * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+ * @param {[string]} keys The key sequences which are bound to *action*.
* @param {string} description A description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extra An optional extra configuration hash (see
/**
* Returns the map from *mode* named *cmd*.
*
- * @param {number} mode The mode to search.
+ * @param {Modes.Mode} mode The mode to search.
* @param {string} cmd The map name to match.
* @returns {Map}
*/
* Returns an array of maps with names starting with but not equal to
* *prefix*.
*
- * @param {number} mode The mode to search.
+ * @param {Modes.Mode} mode The mode to search.
* @param {string} prefix The map prefix string to match.
- * @returns {Map[]}
+ * @returns {[Map]}
*/
getCandidates: function (mode, prefix)
this.hives.map(function (h) h.getCandidates(mode, prefix))
/**
* Lists all user-defined mappings matching *filter* for the specified
- * *modes*.
+ * *modes* in the specified *hives*.
*
- * @param {number[]} modes An array of modes to search.
- * @param {string} filter The filter string to match.
+ * @param {[Modes.Mode]} modes An array of modes to search.
+ * @param {string} filter The filter string to match. @optional
+ * @param {[MapHive]} hives The map hives to list. @optional
*/
list: function (modes, filter, hives) {
- let modeSign = "";
- modes.filter(function (m) m.char).forEach(function (m) { modeSign += m.char; });
- modes.filter(function (m) !m.char).forEach(function (m) { modeSign += " " + m.name; });
+ let modeSign = modes.map(function (m) m.char || "").join("")
+ + modes.map(function (m) !m.char ? " " + m.name : "").join("");
modeSign = modeSign.replace(/^ /, "");
hives = (hives || mappings.userHives).map(function (h) [h, maps(h)])
let list = <table>
<tr highlight="Title">
<td/>
- <td style="padding-right: 1em;">Mode</td>
- <td style="padding-right: 1em;">Command</td>
- <td style="padding-right: 1em;">Action</td>
+ <td style="padding-right: 1em;">{_("title.Mode")}</td>
+ <td style="padding-right: 1em;">{_("title.Command")}</td>
+ <td style="padding-right: 1em;">{_("title.Action")}</td>
</tr>
<col style="min-width: 6em; padding-right: 1em;"/>
{
return;
}
+ if (args[1] && !/^<nop>$/i.test(args[1])
+ && !args["-count"] && !args["-ex"] && !args["-javascript"]
+ && mapmodes.every(function (m) m.count))
+ args[1] = "<count>" + args[1];
+
let [lhs, rhs] = args;
if (noremap)
args["-builtin"] = true;
contexts.bindMacro(args, "-keys", function (params) params),
{
arg: args["-arg"],
- count: args["-count"],
+ count: args["-count"] || !(args["-ex"] || args["-javascript"]),
noremap: args["-builtin"],
persist: !args["-nopersist"],
get rhs() String(this.action),
}
const opts = {
- completer: function (context, args) {
- let mapmodes = array.uniq(args["-modes"].map(findMode));
- if (args.length == 1)
- return completion.userMapping(context, mapmodes, args["-group"]);
- if (args.length == 2) {
- if (args["-javascript"])
- return completion.javascript(context);
- if (args["-ex"])
- return completion.ex(context);
- }
+ identifier: "map",
+ completer: function (context, args) {
+ let mapmodes = array.uniq(args["-modes"].map(findMode));
+ if (args.length == 1)
+ return completion.userMapping(context, mapmodes, args["-group"]);
+ if (args.length == 2) {
+ if (args["-javascript"])
+ return completion.javascript(context);
+ if (args["-ex"])
+ return completion.ex(context);
+ }
+ },
+ hereDoc: true,
+ literal: 1,
+ options: [
+ {
+ names: ["-arg", "-a"],
+ description: "Accept an argument after the requisite key press",
},
- hereDoc: true,
- literal: 1,
- options: [
- {
- names: ["-arg", "-a"],
- description: "Accept an argument after the requisite key press",
- },
- {
- names: ["-builtin", "-b"],
- description: "Execute this mapping as if there were no user-defined mappings"
- },
- {
- names: ["-count", "-c"],
- description: "Accept a count before the requisite key press"
- },
- {
- names: ["-description", "-desc", "-d"],
- description: "A description of this mapping",
- default: "User-defined mapping",
- type: CommandOption.STRING
- },
- {
- names: ["-ex", "-e"],
- description: "Execute this mapping as an Ex command rather than keys"
- },
- contexts.GroupFlag("mappings"),
- {
- names: ["-javascript", "-js", "-j"],
- description: "Execute this mapping as JavaScript rather than keys"
- },
- update({}, modeFlag, {
- names: ["-modes", "-mode", "-m"],
- type: CommandOption.LIST,
- description: "Create this mapping in the given modes",
- default: mapmodes || ["n", "v"]
- }),
- {
- names: ["-nopersist", "-n"],
- description: "Do not save this mapping to an auto-generated RC file"
- },
- {
- names: ["-silent", "-s", "<silent>", "<Silent>"],
- description: "Do not echo any generated keys to the command line"
- }
- ],
- serialize: function () {
- return this.name != "map" ? [] :
- array(mappings.userHives)
- .filter(function (h) h.persist)
- .map(function (hive) [
- {
- command: "map",
- options: array([
- hive.name !== "user" && ["-group", hive.name],
- ["-modes", uniqueModes(map.modes)],
- ["-description", map.description],
- map.silent && ["-silent"]])
-
- .filter(util.identity)
- .toObject(),
- arguments: [map.names[0]],
- literalArg: map.rhs,
- ignoreDefaults: true
- }
- for (map in userMappings(hive))
- if (map.persist)
- ])
- .flatten().array;
+ {
+ names: ["-builtin", "-b"],
+ description: "Execute this mapping as if there were no user-defined mappings"
+ },
+ {
+ names: ["-count", "-c"],
+ description: "Accept a count before the requisite key press"
+ },
+ {
+ names: ["-description", "-desc", "-d"],
+ description: "A description of this mapping",
+ default: /*L*/"User-defined mapping",
+ type: CommandOption.STRING
+ },
+ {
+ names: ["-ex", "-e"],
+ description: "Execute this mapping as an Ex command rather than keys"
+ },
+ contexts.GroupFlag("mappings"),
+ {
+ names: ["-javascript", "-js", "-j"],
+ description: "Execute this mapping as JavaScript rather than keys"
+ },
+ update({}, modeFlag, {
+ names: ["-modes", "-mode", "-m"],
+ type: CommandOption.LIST,
+ description: "Create this mapping in the given modes",
+ default: mapmodes || ["n", "v"]
+ }),
+ {
+ names: ["-nopersist", "-n"],
+ description: "Do not save this mapping to an auto-generated RC file"
+ },
+ {
+ names: ["-silent", "-s", "<silent>", "<Silent>"],
+ description: "Do not echo any generated keys to the command line"
}
+ ],
+ serialize: function () {
+ return this.name != "map" ? [] :
+ array(mappings.userHives)
+ .filter(function (h) h.persist)
+ .map(function (hive) [
+ {
+ command: "map",
+ options: array([
+ hive.name !== "user" && ["-group", hive.name],
+ ["-modes", uniqueModes(map.modes)],
+ ["-description", map.description],
+ map.silent && ["-silent"]])
+
+ .filter(util.identity)
+ .toObject(),
+ arguments: [map.names[0]],
+ literalArg: map.rhs,
+ ignoreDefaults: true
+ }
+ for (map in userMappings(hive))
+ if (map.persist)
+ ])
+ .flatten().array;
+ }
};
function userMappings(hive) {
let seen = {};
for (let stack in values(hive.stacks))
for (let map in array.iterValues(stack))
- if (!set.add(seen, map.id))
+ if (!Set.add(seen, map.id))
yield map;
}
dactyl.echoerr(_("map.noSuch", args[0]));
},
{
+ identifier: "unmap",
argCount: "?",
bang: true,
completer: opts.completer,
validator: function (value) Array.concat(value).every(findMode),
completer: function () [[array.compact([mode.name.toLowerCase().replace(/_/g, "-"), mode.char]), mode.description]
for (mode in values(modes.all))
- if (!mode.hidden)],
+ if (!mode.hidden)]
};
function findMode(name) {
function uniqueModes(modes) {
let chars = [k for ([k, v] in Iterator(modules.modes.modeChars))
if (v.every(function (mode) modes.indexOf(mode) >= 0))];
- return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0).concat(chars));
+ return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0)
+ .map(function (m) m.name.toLowerCase())
+ .concat(chars));
}
commands.add(["feedkeys", "fk"],
if (mode.char && !commands.get(mode.char + "map", true))
addMapCommands(mode.char,
[m.mask for (m in modes.mainModes) if (m.char == mode.char)],
- [mode.name.toLowerCase()]);
+ mode.displayName);
let args = {
getMode: function (args) findMode(args["-mode"]),
iterate: function (args, mainOnly) {
let modes = [this.getMode(args)];
if (!mainOnly)
- modes = modes.concat(modes[0].bases);
+ modes = modes[0].allBases;
let seen = {};
// Bloody hell. --Kris
for (let hive in mappings.hives.iterValues())
for (let map in array.iterValues(hive.getStack(mode)))
for (let name in values(map.names))
- if (!set.add(seen, name)) {
+ if (!Set.add(seen, name)) {
yield {
name: name,
columns: [
tags = services["dactyl:"].HELP_TAGS)
({ helpTag: prefix + map.name, __proto__: map }
for (map in self.iterate(args, true))
- if (map.hive === mappings.builtin || set.has(tags, prefix + map.name))),
- description: "List all " + mode.name + " mode mappings along with their short descriptions",
+ if (map.hive === mappings.builtin || Set.has(tags, prefix + map.name))),
+ description: "List all " + mode.displayName + " mode mappings along with their short descriptions",
index: mode.char + "-map",
getMode: function (args) mode,
options: []
if (Marks.isURLMark(mark)) {
let res = this._urlMarks.set(mark, { location: doc.documentURI, position: position, tab: Cu.getWeakReference(tabs.getTab()), timestamp: Date.now()*1000 });
if (!silent)
- dactyl.log("Adding URL mark: " + Marks.markToString(mark, res), 5);
+ dactyl.log(_("mark.addURL", Marks.markToString(mark, res)), 5);
}
else if (Marks.isLocalMark(mark)) {
let marks = this._localMarks.get(doc.documentURI, {});
marks[mark] = { location: doc.documentURI, position: position, timestamp: Date.now()*1000 };
this._localMarks.changed();
if (!silent)
- dactyl.log("Adding local mark: " + Marks.markToString(mark, marks[mark]), 5);
+ dactyl.log(_("mark.addLocal", Marks.markToString(mark, marks[mark])), 5);
}
},
tabs.select(tab);
let doc = tab.linkedBrowser.contentDocument;
if (doc.documentURI == mark.location) {
- dactyl.log("Jumping to URL mark: " + Marks.markToString(char, mark), 5);
+ dactyl.log(_("mark.jumpingToURL", Marks.markToString(char, mark)), 5);
buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100);
}
else {
let mark = (this._localMarks.get(this.localURI) || {})[char];
dactyl.assert(mark, _("mark.unset", char));
- dactyl.log("Jumping to local mark: " + Marks.markToString(char, mark), 5);
+ dactyl.log(_("mark.jumpingToLocal", Marks.markToString(char, mark)), 5);
buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100);
}
else
"Mark current location within the web page",
function (args) {
let mark = args[0] || "";
- dactyl.assert(mark.length <= 1, _("error.trailing"));
+ dactyl.assert(mark.length <= 1, _("error.trailingCharacters"));
dactyl.assert(/[a-zA-Z]/.test(mark), _("mark.invalid"));
marks.add(mark);
completion.mark = function mark(context) {
function percent(i) Math.round(i * 100);
- // FIXME: Line/Column doesn't make sense with %
context.title = ["Mark", "HPos VPos File"];
context.keys.description = function ([, m]) percent(m.position.x) + "% " + percent(m.position.y) + "% " + m.location;
context.completions = marks.all;
description: "Active when text is selected",
display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""),
bases: [this.COMMAND],
- ownsFocus: true,
- passUnknown: false
+ ownsFocus: true
}, {
leave: function (stack, newMode) {
if (newMode.main == modes.CARET) {
});
this.addMode("CARET", {
description: "Active when the caret is visible in the web content",
- bases: [this.COMMAND]
+ bases: [this.NORMAL]
}, {
get pref() prefs.get("accessibility.browsewithcaret"),
char: "t",
description: "Vim-like editing of input elements",
bases: [this.COMMAND],
- input: true,
- ownsFocus: true,
- passUnknown: false
+ ownsFocus: true
+ }, {
+ onKeyPress: function (eventList) {
+ const KILL = false, PASS = true;
+
+ // Hack, really.
+ if (eventList[0].charCode || /^<(?:.-)*(?:BS|Del|C-h|C-w|C-u|C-k)>$/.test(events.toString(eventList[0]))) {
+ dactyl.beep();
+ return KILL;
+ }
+ return PASS;
+ }
});
this.addMode("OUTPUT_MULTILINE", {
description: "Active when the multi-line output buffer is open",
- bases: [this.COMMAND],
+ bases: [this.NORMAL]
});
this.addMode("INPUT", {
}
});
+
+ function makeTree() {
+ let list = modes.all.filter(function (m) m.name !== m.description);
+
+ let tree = {};
+
+ for (let mode in values(list))
+ tree[mode.name] = {};
+
+ for (let mode in values(list))
+ for (let base in values(mode.bases))
+ tree[base.name][mode.name] = tree[mode.name];
+
+ let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
+
+ default xml namespace = NS;
+ function rec(obj) {
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+
+ let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
+ Object.keys(obj).sort().forEach(function (name) {
+ let mode = modes.getMode(name);
+ res.* += <li><em>{mode.displayName}</em>: {mode.description}{
+ rec(obj[name])
+ }</li>;
+ });
+
+ if (res.*.length())
+ return res;
+ return <></>;
+ }
+
+ return rec(roots);
+ }
+
+ util.timeout(function () {
+ // Waits for the add-on to become available, if necessary.
+ config.addon;
+ config.version;
+
+ services["dactyl:"].pages["modes.dtd"] = services["dactyl:"].pages["modes.dtd"]();
+ });
+
+ services["dactyl:"].pages["modes.dtd"] = function () [null,
+ util.makeDTD(iter({ "modes.tree": makeTree() },
+ config.dtd))];
},
cleanup: function cleanup() {
modes.reset();
let val = this._modeMap[this._main].display();
if (val)
- return "-- " + val + " --" + macromode;;
+ return "-- " + val + " --" + macromode;
return macromode;
},
if (!mode.extended)
this._mainModes.push(mode);
- dactyl.triggerObserver("mode-add", mode);
+ dactyl.triggerObserver("modes.add", mode);
},
dumpStack: function dumpStack() {
// show the current mode string in the command line
show: function show() {
+ if (!loaded.modes)
+ return;
+
let msg = null;
- if (options.get("showmode").getKey(this.main.name, true))
+ if (options.get("showmode").getKey(this.main.allBases, false))
msg = this._getModeMessage();
+
if (msg || loaded.commandline)
commandline.widgets.mode = msg || null;
},
prev = stack && stack.pop || this.topOfStack;
if (push)
this._modeStack.push(push);
-
- if (stack && stack.pop)
- for (let { obj, prop, value, test } in values(this.topOfStack.saved))
- if (!test || !test(stack, prev))
- dactyl.trapErrors(function () { obj[prop] = value });
-
- this.show();
});
- delayed.forEach(function ([fn, self]) dactyl.trapErrors(fn, self));
+ if (stack && stack.pop)
+ for (let { obj, prop, value, test } in values(this.topOfStack.saved))
+ if (!test || !test(stack, prev))
+ dactyl.trapErrors(function () { obj[prop] = value });
+
+ this.show();
if (this.topOfStack.params.enter && prev)
dactyl.trapErrors("enter", this.topOfStack.params,
push ? { push: push } : stack || {},
prev);
- dactyl.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended], stack);
+ delayed.forEach(function ([fn, self]) dactyl.trapErrors(fn, self));
+
+ dactyl.triggerObserver("modes.change", [oldMain, oldExtended], [this._main, this._extended], stack);
this.show();
},
while (this._modeStack.length > 1 && this.main != mode) {
let a = this._modeStack.pop();
this.set(this.topOfStack.main, this.topOfStack.extended, this.topOfStack.params,
- update({ pop: a }, args || {}));
+ update({ pop: a },
+ args || {}));
if (mode == null)
return;
}
},
- replace: function replace(mode, oldMode) {
+ replace: function replace(mode, oldMode, args) {
while (oldMode && this._modeStack.length > 1 && this.main != oldMode)
this.pop();
if (this._modeStack.length > 1)
- this.set(mode, null, null, { push: this.topOfStack, pop: this._modeStack.pop() });
+ this.set(mode, null, null,
+ update({ push: this.topOfStack, pop: this._modeStack.pop() },
+ args || {}));
this.push(mode);
},
init: function init(name, options, params) {
if (options.bases)
util.assert(options.bases.every(function (m) m instanceof this, this.constructor),
- "Invalid bases", true);
+ _("mode.invalidBases"), true);
- update(this, {
+ this.update({
id: 1 << Modes.Mode._id++,
+ description: name,
name: name,
params: params || {}
}, options);
},
+ description: Messages.Localized(""),
+
+ displayName: Class.memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
+
isinstance: function isinstance(obj)
- this === obj || this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
+ this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
allBases: Class.memoize(function () {
- let seen = {}, res = [], queue = this.bases;
+ let seen = {}, res = [], queue = [this].concat(this.bases);
for (let mode in array.iterValues(queue))
- if (!set.add(seen, mode)) {
+ if (!Set.add(seen, mode)) {
res.push(mode);
queue.push.apply(queue, mode.bases);
}
get count() !this.insert,
- get description() this._display,
-
_display: Class.memoize(function _display() this.name.replace("_", " ", "g")),
display: function display() this._display,
ownsFocus: Class.memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
- get passUnknown() this.input,
+ passEvent: function passEvent(event) this.input && event.charCode && !(event.ctrlKey || event.altKey || event.metaKey),
+
+ passUnknown: Class.memoize(function () options.get("passunknown").getKey(this.name)),
get mask() this,
mappings: function initMappings() {
mappings.add([modes.BASE, modes.NORMAL],
["<Esc>", "<C-[>"],
- "Return to NORMAL mode",
+ "Return to Normal mode",
function () { modes.reset(); });
mappings.add([modes.INPUT, modes.COMMAND, modes.PASS_THROUGH, modes.QUOTE],
"Return to the previous mode",
function () { modes.pop(); });
+ mappings.add([modes.MENU], ["<C-c>"],
+ "Leave Menu mode",
+ function () { modes.pop(); });
+
mappings.add([modes.MENU], ["<Esc>"],
"Close the current popup",
- function () {
- modes.pop();
- return Events.PASS_THROUGH;
- });
+ function () { return Events.PASS_THROUGH; });
mappings.add([modes.MENU], ["<C-[>"],
"Close the current popup",
function () { events.feedkeys("<Esc>"); });
},
options: function initOptions() {
+ let opts = {
+ completer: function completer(context, extra) {
+ if (extra.value && context.filter[0] == "!")
+ context.advance(1);
+ return completer.superapply(this, arguments);
+ },
+
+ getKey: function getKey(val, default_) {
+ if (isArray(val))
+ return (array.nth(this.value, function (v) val.some(function (m) m.name === v.mode), 0)
+ || { result: default_ }).result;
+
+ return Set.has(this.valueMap, val) ? this.valueMap[val] : default_;
+ },
+
+ setter: function (vals) {
+ modes.all.forEach(function (m) { delete m.passUnknown });
+
+ vals = vals.map(function (v) update(new String(v.toLowerCase()), {
+ mode: v.replace(/^!/, "").toUpperCase(),
+ result: v[0] !== "!"
+ }));
+
+ this.valueMap = values(vals).map(function (v) [v.mode, v.result]).toObject();
+ return vals;
+ },
+
+ validator: function validator(vals) vals.map(function (v) v.replace(/^!/, "")).every(Set.has(this.values)),
+
+ get values() array.toObject([[m.name.toLowerCase(), m.description] for (m in values(modes._modes)) if (!m.hidden)])
+ };
+
+ options.add(["passunknown", "pu"],
+ "Pass through unknown keys in these modes",
+ "stringlist", "!text_edit,base",
+ opts);
+
options.add(["showmode", "smd"],
"Show the current mode in the command line when it matches this expression",
- "regexplist", "!^normal$",
- { regexpFlags: "i" });
+ "stringlist", "caret,output_multiline,!normal,base",
+ opts);
},
prefs: function initPrefs() {
prefs.watch("accessibility.browsewithcaret", function () modes.onCaretChange.apply(modes, arguments));
<popupset>
<menupopup id="dactyl-contextmenu" highlight="Events" events="contextEvents">
<menuitem id="dactyl-context-copylink"
- label="Copy Link Location" dactyl:group="link"
+ label={_("mow.contextMenu.copyLink")} dactyl:group="link"
oncommand="goDoCommand('cmd_copyLink');"/>
<menuitem id="dactyl-context-copypath"
- label="Copy File Path" dactyl:group="link path"
+ label={_("mow.contextMenu.copyPath")} dactyl:group="link path"
oncommand="dactyl.clipboardWrite(document.popupNode.getAttribute('path'));"/>
<menuitem id="dactyl-context-copy"
- label="Copy" dactyl:group="selection"
+ label={_("mow.contextMenu.copy")} dactyl:group="selection"
command="cmd_copy"/>
<menuitem id="dactyl-context-selectall"
- label="Select All"
+ label={_("mow.contextMenu.selectAll")}
command="cmd_selectAll"/>
</menupopup>
</popupset>
catch (e) {
util.reportError(e);
util.dump(data);
- this.messages.push(data);
}
+ this.messages.push(data);
}
else {
let style = isString(data) ? "pre" : "nowrap";
let elem = this.widget.contentDocument.documentElement;
if (showHelp)
- this.widgets.message = ["MoreMsg", "-- More -- SPACE/<C-f>/j: screen/page/line down, <C-b>/<C-u>/k: up, q: quit"];
+ this.widgets.message = ["MoreMsg", _("mow.moreHelp")];
else if (force || (options["more"] && Buffer.isScrollable(elem, 1)))
- this.widgets.message = ["MoreMsg", "-- More --"];
+ this.widgets.message = ["MoreMsg", _("mow.more")];
else
- this.widgets.message = ["Question", "Press ENTER or type command to continue"];
+ this.widgets.message = ["Question", _("mow.continue")];
},
visible: Modes.boundProperty({
mappings.add([modes.COMMAND],
["g<lt>"], "Redisplay the last command output",
function () {
- dactyl.assert(commandline.lastOutput, _("mow.noPreviousOutput"));
+ dactyl.assert(mow.lastOutput, _("mow.noPreviousOutput"));
mow.echo(mow.lastOutput, "Normal");
});
"boolean", true);
}
});
+
+// vim: set fdm=marker sw=4 ts=4 et:
* Returns a list of QuickMarks associates with the given URL.
*
* @param {string} url The url to find QuickMarks for.
- * @return {[string]}
+ * @returns {[string]}
*/
find: function find(url) {
let res = [];
context.fork("current", 0, this, function (context) {
context.title = ["Extra Completions"];
context.completions = [
- [quickmarks.get(args[0]), "Current Value"]
+ [quickmarks.get(args[0]), _("option.currentValue")]
].filter(function ([k, v]) k);
});
context.fork("url", 0, completion, "url");
util.overlayWindow(window, { append: <><statusbar id="status-bar" ordinal="0"/></> });
highlight.loadCSS(util.compileMacro(<![CDATA[
- !AddonBar;#addon-bar {
+ !AddonBar;#addon-bar {
+ /* The Add-on Bar */
padding-left: 0 !important;
min-height: 18px !important;
-moz-appearance: none !important;
<padding>
}
- !AddonButton;#addon-bar xul|toolbarbutton {
+ !AddonButton;#addon-bar xul|toolbarbutton {
+ /* An Add-on Bar button */
-moz-appearance: none !important;
padding: 0 !important;
border-width: 0px !important;
min-width: 0 !important;
color: inherit !important;
}
- AddonButton:not(:hover) background: transparent !important;
+ AddonButton:not(:hover) background: transparent;
]]>)({ padding: util.OS.isMacOSX ? "padding-right: 10px !important;" : "" }));
if (document.getElementById("appmenu-button"))
highlight.loadCSS(<![CDATA[
- AppmenuButton min-width: 0 !important; padding: 0 .5em !important;
+ AppmenuButton /* The app-menu button */ \
+ min-width: 0 !important; padding: 0 .5em !important;
]]>);
}
<hbox highlight="CmdLine StatusCmdLine" class="dactyl-container">
<label key="mode" crop="end" class="plain" collapsed="true"/>
<stack id="dactyl-statusline-stack" flex="1" highlight="CmdLine StatusCmdLine" class="dactyl-container">
- <textbox key="url" crop="end" flex="1" class="plain dactyl-status-field-url" readonly="true"/>
+ <textbox key="url" crop="end" flex="1" style="background: transparent;" class="plain dactyl-status-field-url" readonly="true"/>
<textbox key="message" crop="end" flex="1" highlight="Normal StatusNormal" class="plain" readonly="true"/>
</stack>
</hbox>
signals: {
"browser.locationChange": function (webProgress, request, uri) {
let win = webProgress.DOMWindow;
- this.status = buffer.uri;
+ this.status = uri;
this.progress = uri && win && win.dactylProgress || "";
// if this is not delayed we get the position of the old buffer
this.progress = 0;
if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
this.progress = "";
- this.status = buffer.uri;
+ this.updateStatus();
}
},
"browser.statusChange": function onStatusChange(webProgress, request, status, message) {
- this.status = message || buffer.uri;
- },
+ this.timeout(function () {
+ this.status = message || buffer.uri;
+ });
+ }
},
/**
// update all fields of the statusline
update: function update() {
- this.status = buffer.uri;
+ this.updateStatus();
this.inputBuffer = "";
this.progress = "";
this.updateTabCount();
this.updateZoomLevel();
},
- // ripped from Firefox; modified
- unsafeURI: util.regexp(String.replace(<![CDATA[
- [
- \s
- // Invisible characters (bug 452979)
- U001C U001D U001E U001F // file/group/record/unit separator
- U00AD // Soft hyphen
- UFEFF // BOM
- U2060 // Word joiner
- U2062 U2063 // Invisible times/separator
- U200B UFFFC // Zero-width space/no-break space
-
- // Bidi formatting characters. (RFC 3987 sections 3.2 and 4.1 paragraph 6)
- U200E U200F U202A U202B U202C U202D U202E
- ]
- ]]>, /U/g, "\\u"),
- "gx"),
- losslessDecodeURI: function losslessDecodeURI(url) {
- return url.split("%25").map(function (url) {
- // Non-UTF-8 compliant URLs cause "malformed URI sequence" errors.
- try {
- return decodeURI(url).replace(this.unsafeURI, encodeURIComponent);
- }
- catch (e) {
- return url;
- }
- }, this).join("%25");
- },
+ unsafeURI: deprecated("util.unsafeURI", { get: function unsafeURI() util.unsafeURI }),
+ losslessDecodeURI: deprecated("util.losslessDecodeURI", function losslessDecodeURI() util.losslessDecodeURI.apply(util, arguments)),
/**
* Update the URL displayed in the status line. Also displays status
if (uri.equals(buffer.uri) && window.getWebNavigation) {
let sh = window.getWebNavigation().sessionHistory;
if (sh && sh.index > 0)
- modified += "+";
- if (sh && sh.index < sh.count - 1)
modified += "-";
- }
-
- if (modules.bookmarkcache) {
- if (bookmarkcache.isBookmarked(uri))
+ if (sh && sh.index < sh.count - 1)
+ modified += "+";
+ if (this.bookmarked)
modified += UTF8("❤");
}
if (modules.quickmarks)
modified += quickmarks.find(uri.spec.replace(/#.*/, "")).join("");
- url = this.losslessDecodeURI(uri.spec);
+ url = util.losslessDecodeURI(uri.spec);
}
if (url == "about:blank") {
if (!buffer.title)
- url = "[No Name]";
+ url = _("buffer.noName");
}
else {
- url = url.replace(RegExp("^dactyl://help/(\\S+)#(.*)"), function (m, n1, n2) n1 + " " + decodeURIComponent(n2) + " [Help]")
- .replace(RegExp("^dactyl://help/(\\S+)"), "$1 [Help]");
+ url = url.replace(RegExp("^dactyl://help/(\\S+)#(.*)"), function (m, n1, n2) n1 + " " + decodeURIComponent(n2) + " " + _("buffer.help"))
+ .replace(RegExp("^dactyl://help/(\\S+)"), "$1 " + _("buffer.help"));
}
if (modified)
this.widgets.url.value = url;
this._status = uri;
+ },
+ get bookmarked() this._bookmarked,
+ set bookmarked(val) {
+ this._bookmarked = val;
+ if (this.status)
+ this.status = this.status;
},
- updateStatus: function updateStatus() { this.status = buffer.uri; },
+ updateStatus: function updateStatus() {
+ this.timeout(function () {
+ this.status = buffer.uri;
+ });
+ },
updateUrl: deprecated("statusline.status", function updateUrl(url) { this.status = url || buffer.uri }),
*/
progress: Modes.boundProperty({
get: function progress() this._progress,
- set: function progress(progress) {
+ set: function progress(progress) {
this._progress = progress || "";
- if (typeof progress == "string")
+ if (isinstance(progress, ["String", _]))
this.widgets.progress.value = this._progress;
else if (typeof progress == "number") {
let progressStr = "";
if (this._progress <= 0)
- progressStr = "[ Loading... ]";
+ progressStr = /*L*/"[ Loading... ]";
else if (this._progress < 1) {
let progress = Math.round(this._progress * 20);
progressStr = "["
this._lastBufferSwitchArgs = "";
this._lastBufferSwitchSpecial = true;
+ this.xulTabs = document.getElementById("tabbrowser-tabs");
+
// hide tabs initially to prevent flickering when 'stal' would hide them
// on startup
if (config.hasTabbrowser)
for (let { linkedBrowser: { contentDocument } } in values(this.allTabs))
if (contentDocument.readyState === "complete")
dactyl.initDocument(contentDocument);
- });
+ }, 1000);
+
+ if (window.TabsInTitlebar)
+ window.TabsInTitlebar.allowedBy("dactyl", false);
+ },
+
+ signals: {
+ enter: function enter() {
+ if (window.TabsInTitlebar)
+ window.TabsInTitlebar.allowedBy("dactyl", true);
+ }
},
_alternates: Class.memoize(function () [config.tabbrowser.mCurrentTab, null]),
}
},
- updateTabCount: function () {
+ updateTabCount: function updateTabCount() {
for (let [i, tab] in Iterator(this.visibleTabs)) {
if (dactyl.has("Gecko2")) {
let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_);
statusline.updateTabCount(true);
},
- _onTabSelect: function () {
+ _onTabSelect: function _onTabSelect() {
// TODO: is all of that necessary?
// I vote no. --Kris
modes.reset();
get browsers() {
let browsers = config.tabbrowser.browsers;
for (let i = 0; i < browsers.length; i++)
- yield [i, browsers[i]];
+ if (browsers[i] !== undefined) // Bug in Google's Page Speed add-on.
+ yield [i, browsers[i]];
},
/**
// property doesn't. And the property is so oft-used that it's
// convenient. To the former question, because I think this is mainly
// useful for autocommands, and they get index arguments. --Kris
- getLocalStore: function (tabIndex) {
+ getLocalStore: function getLocalStore(tabIndex) {
let tab = this.getTab(tabIndex);
if (!tab.dactylStore)
tab.dactylStore = {};
get localStore() this.getLocalStore(),
/**
- * @property {Object[]} The array of closed tabs for the current
+ * @property {[Object]} The array of closed tabs for the current
* session.
*/
- get closedTabs() services.json.decode(services.sessionStore.getClosedTabData(window)),
+ get closedTabs() JSON.parse(services.sessionStore.getClosedTabData(window)),
/**
* Clones the specified *tab* and append it to the tab list.
* @param {Object} tab The tab to clone.
* @param {boolean} activate Whether to select the newly cloned tab.
*/
- cloneTab: function (tab, activate) {
- let newTab = config.tabbrowser.addTab();
+ cloneTab: function cloneTab(tab, activate) {
+ let newTab = config.tabbrowser.addTab("about:blank", { ownerTab: tab });
Tabs.copyTab(newTab, tab);
if (activate)
*
* @param {Object} tab The tab to detach.
*/
- detachTab: function (tab) {
+ detachTab: function detachTab(tab) {
if (!tab)
tab = config.tabbrowser.mTabContainer.selectedItem;
* document.
*/
// FIXME: Only called once...necessary?
- getContentIndex: function (content) {
+ getContentIndex: function getContentIndex(content) {
for (let [i, browser] in this.browsers) {
if (browser.contentWindow == content || browser.contentDocument == content)
return i;
*
* @returns {Window}
*/
- getGroups: function () {
+ getGroups: function getGroups() {
if ("_groups" in this)
return this._groups;
* if *index* is not specified. This is a 0-based index.
*
* @param {number|Node} index The index of the tab required or the tab itself
+ * @param {boolean} visible If true, consider only visible tabs rather than
+ * all tabs.
* @returns {Object}
*/
- getTab: function (index) {
+ getTab: function getTab(index, visible) {
if (index instanceof Node)
return index;
if (index != null)
- return config.tabbrowser.mTabs[index];
+ return this[visible ? "visibleTabs" : "allTabs"][index];
return config.tabbrowser.mCurrentTab;
},
* @param {boolean} visible Whether to consider only visible tabs.
* @returns {number}
*/
- index: function (tab, visible) {
+ index: function index(tab, visible) {
let tabs = this[visible ? "visibleTabs" : "allTabs"];
return tabs.indexOf(tab || config.tabbrowser.mCurrentTab);
},
* - "-3" for the tab, which is 3 positions left of the current
* - "$" for the last tab
*/
- indexFromSpec: function (spec, wrap) {
+ indexFromSpec: function indexFromSpec(spec, wrap, offset) {
if (spec instanceof Node)
return this.allTabs.indexOf(spec);
let tabs = this.visibleTabs;
let position = this.index(null, true);
- if (spec == null || spec === "")
+ if (spec == null)
+ return -1;
+
+ if (spec === "")
return position;
- if (typeof spec === "number")
- position = spec;
+ if (/^\d+$/.test(spec))
+ position = parseInt(spec, 10) + (offset || 0);
else if (spec === "$")
position = tabs.length - 1;
else if (/^[+-]\d+$/.test(spec))
position += parseInt(spec, 10);
- else if (/^\d+$/.test(spec))
- position = parseInt(spec, 10);
else
return -1;
*
* @param {Object} tab The tab to keep.
*/
- keepOnly: function (tab) {
+ keepOnly: function keepOnly(tab) {
config.tabbrowser.removeAllTabsBut(tab);
},
* @param {string} filter A filter matching a substring of the tab's
* document title or URL.
*/
- list: function (filter) {
+ list: function list(filter) {
completion.listCompleter("buffer", filter);
},
* @param {boolean} wrap Whether an out of bounds *spec* causes the
* destination position to wrap around the start/end of the tab list.
*/
- move: function (tab, spec, wrap) {
- let index = tabs.indexFromSpec(spec, wrap);
+ move: function move(tab, spec, wrap) {
+ let index = tabs.indexFromSpec(spec, wrap, -1);
config.tabbrowser.moveTabTo(tab, index);
},
* @param {number} count How many tabs to remove.
* @param {boolean} focusLeftTab Focus the tab to the left of the removed tab.
*/
- remove: function (tab, count, focusLeftTab) {
+ remove: function remove(tab, count, focusLeftTab) {
count = count || 1;
let res = this.count > count;
* @param {boolean} bypassCache Whether to bypass the cache when
* reloading.
*/
- reload: function (tab, bypassCache) {
+ reload: function reload(tab, bypassCache) {
try {
if (bypassCache) {
const flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
* @param {boolean} bypassCache Whether to bypass the cache when
* reloading.
*/
- reloadAll: function (bypassCache) {
- if (bypassCache) {
- for (let i = 0; i < config.tabbrowser.mTabs.length; i++) {
- try {
- this.reload(config.tabbrowser.mTabs[i], bypassCache);
- }
- catch (e) {
- // FIXME: can we do anything useful here without stopping the
- // other tabs from reloading?
- }
+ reloadAll: function reloadAll(bypassCache) {
+ this.visibleTabs.forEach(function (tab) {
+ try {
+ tabs.reload(tab, bypassCache);
}
- }
- else
- config.tabbrowser.reloadAllTabs();
+ catch (e) {
+ dactyl.reportError(e, true);
+ }
+ });
},
/**
* @param {boolean} wrap Whether an out of bounds *spec* causes the
* selection position to wrap around the start/end of the tab list.
*/
- select: function (spec, wrap) {
+ select: function select(spec, wrap) {
let index = tabs.indexFromSpec(spec, wrap);
if (index == -1)
dactyl.beep();
/**
* Selects the alternate tab.
*/
- selectAlternateTab: function () {
+ selectAlternateTab: function selectAlternateTab() {
dactyl.assert(tabs.alternate != null && tabs.getTab() != tabs.alternate,
_("buffer.noAlternate"));
tabs.select(tabs.alternate);
*
* @param {Object} tab The tab to stop loading.
*/
- stop: function (tab) {
+ stop: function stop(tab) {
if (config.stop)
config.stop(tab);
else
/**
* Stops loading all tabs.
*/
- stopAll: function () {
+ stopAll: function stopAll() {
for (let [, browser] in this.browsers)
browser.stop();
},
*
*/
// FIXME: help!
- switchTo: function (buffer, allowNonUnique, count, reverse) {
+ switchTo: function switchTo(buffer, allowNonUnique, count, reverse) {
if (buffer != null) {
// store this command, so it can be repeated with "B"
this._lastBufferSwitchArgs = buffer;
* @param {Array(Object)} tabs The current and alternate tab.
* @see tabs#alternate
*/
- updateSelectionHistory: function (tabs) {
+ updateSelectionHistory: function updateSelectionHistory(tabs) {
if (!tabs) {
if (this.getTab() == this._alternates[0]
|| this.alternate && this.allTabs.indexOf(this._alternates[0]) == -1
commands.add(["bd[elete]", "bw[ipeout]", "bun[load]", "tabc[lose]"],
"Delete current buffer",
function (args) {
- let special = args.bang;
- let count = args.count;
- let arg = args[0] || "";
-
- if (arg) {
- let removed = 0;
- let matches = arg.match(/^(\d+):?/);
-
- if (matches) {
- config.removeTab(tabs.getTab(parseInt(matches[1], 10) - 1));
- removed = 1;
- }
- else {
- let str = arg.toLowerCase();
- let browsers = config.tabbrowser.browsers;
-
- for (let i = browsers.length - 1; i >= 0; i--) {
- let host, title, uri = browsers[i].currentURI.spec;
- if (browsers[i].currentURI.schemeIs("about")) {
- host = "";
- title = "(Untitled)";
- }
- else {
- host = browsers[i].currentURI.host;
- title = browsers[i].contentTitle;
- }
-
- [host, title, uri] = [host, title, uri].map(String.toLowerCase);
-
- if (host.indexOf(str) >= 0 || uri == str ||
- (special && (title.indexOf(str) >= 0 || uri.indexOf(str) >= 0))) {
- config.removeTab(tabs.getTab(i));
- removed++;
- }
- }
- }
+ let removed = 0;
+ for (let tab in matchTabs(args, args.bang, true)) {
+ config.removeTab(tab);
+ removed++;
+ }
+ if (args[0])
if (removed > 0)
- dactyl.echomsg(_("buffer.fewer", removed, removed == 1 ? "" : "s"), 9);
+ dactyl.echomsg(_("buffer.fewerTab" + (removed == 1 ? "" : "s"), removed), 9);
else
dactyl.echoerr(_("buffer.noMatching", arg));
- }
- else // just remove the current tab
- tabs.remove(tabs.getTab(), Math.max(count, 1), special);
}, {
argCount: "?",
bang: true,
privateData: true
});
+ function matchTabs(args, substr, all) {
+ let filter = args[0];
+
+ if (!filter && args.count == null)
+ yield tabs.getTab();
+ else if (!filter)
+ yield dactyl.assert(tabs.getTab(args.count - 1));
+ else {
+ let matches = /^(\d+)(?:$|:)/.exec(filter);
+ if (matches)
+ yield dactyl.assert(args.count == null &&
+ tabs.getTab(parseInt(matches[1], 10) - 1, !all));
+ else {
+ let str = filter.toLowerCase();
+ for (let tab in values(tabs[all ? "allTabs" : "visibleTabs"])) {
+ let host, title;
+ let browser = tab.linkedBrowser;
+ let uri = browser.currentURI.spec;
+ if (browser.currentURI.schemeIs("about")) {
+ host = "";
+ title = "(Untitled)";
+ }
+ else {
+ host = browser.currentURI.host;
+ title = browser.contentTitle;
+ }
+
+ [host, title, uri] = [host, title, uri].map(String.toLowerCase);
+
+ if (host.indexOf(str) >= 0 || uri == str ||
+ (substr && (title.indexOf(str) >= 0 || uri.indexOf(str) >= 0)))
+ if (args.count == null || --args.count == 0)
+ yield tab;
+ }
+ }
+ }
+ }
+
+ commands.add(["pin[tab]"],
+ "Pin tab as an application tab",
+ function (args) {
+ for (let tab in matchTabs(args))
+ config.browser[!args.bang || !tab.pinned ? "pinTab" : "unpinTab"](tab);
+ },
+ {
+ argCount: "?",
+ bang: true,
+ count: true,
+ completer: function (context, args) {
+ if (!args.bang)
+ context.filters.push(function ({ item }) !item.tab.pinned);
+ completion.buffer(context);
+ }
+ });
+
+ commands.add(["unpin[tab]"],
+ "Unpin tab as an application tab",
+ function (args) {
+ for (let tab in matchTabs(args))
+ config.browser.unpinTab(tab);
+ },
+ {
+ argCount: "?",
+ count: true,
+ completer: function (context, args) {
+ context.filters.push(function ({ item }) item.tab.pinned);
+ completion.buffer(context);
+ }
+ });
+
commands.add(["keepa[lt]"],
"Execute a command without changing the current alternate buffer",
function (args) {
- let alternate = tabs.alternate;
-
try {
- commands.execute(args[0] || "", null, true);
+ dactyl.execute(args[0], null, true);
}
finally {
- tabs.updateSelectionHistory([tabs.getTab(), alternate]);
+ tabs.updateSelectionHistory([tabs.getTab(), tabs.alternate]);
}
}, {
- argCount: "+",
+ argCount: "1",
completer: function (context) completion.ex(context),
literal: 0,
subCommand: 0
function (args) {
dactyl.withSavedValues(["forceNewTab"], function () {
this.forceNewTab = true;
- commands.execute(args[0] || "", null, true);
+ dactyl.execute(args[0], null, true);
});
}, {
- argCount: "+",
+ argCount: "1",
completer: function (context) completion.ex(context),
literal: 0,
subCommand: 0
if (/^\d+$/.test(arg))
tabs.select("-" + arg, true);
else
- dactyl.echoerr(_("error.trailing"));
+ dactyl.echoerr(_("error.trailingCharacters"));
}
else if (count > 0)
tabs.select("-" + count, true);
// count is ignored if an arg is specified, as per Vim
if (arg) {
- dactyl.assert(/^\d+$/.test(arg), _("error.trailing"));
+ dactyl.assert(/^\d+$/.test(arg), _("error.trailingCharacters"));
index = arg - 1;
}
else
});
commands.add(["quita[ll]", "qa[ll]"],
- "Quit " + config.appName,
- function (args) { dactyl.quit(false, args.bang); }, {
- argCount: "0",
- bang: true
- });
+ "Quit this " + config.appName + " window",
+ function (args) { window.close(); },
+ { argCount: "0" });
commands.add(["reloada[ll]"],
"Reload all tab pages",
function () { tabs.stopAll(); },
{ argCount: "0" });
- // TODO: add count support
+ // TODO: add count and bang multimatch support - unify with :buffer nonsense
commands.add(["tabm[ove]"],
- "Move the current tab after tab N",
+ "Move the current tab to the position of tab N",
function (args) {
let arg = args[0];
- // FIXME: tabmove! N should probably produce an error
- dactyl.assert(!arg || /^([+-]?\d+)$/.test(arg),
- _("error.trailing"));
-
- // if not specified, move to after the last tab
- tabs.move(config.tabbrowser.mCurrentTab, arg || "$", args.bang);
+ if (tabs.indexFromSpec(arg) == -1) {
+ let tabs = [tab for (tab in matchTabs(args, true))];
+ dactyl.assert(tabs.length, _("error.invalidArgument", arg));
+ dactyl.assert(tabs.length == 1, _("buffer.multipleMatching", arg));
+ arg = tabs[0];
+ }
+ tabs.move(tabs.getTab(), arg, args.bang);
}, {
- argCount: "?",
- bang: true
+ argCount: "1",
+ bang: true,
+ completer: function (context, args) completion.buffer(context, true),
+ literal: 0
});
commands.add(["tabo[nly]"],
commands.add(["taba[ttach]"],
"Attach the current tab to another window",
function (args) {
- dactyl.assert(args.length <= 2 && !args.some(function (i) !/^\d+$/.test(i)),
- _("error.trailing"));
+ dactyl.assert(args.length <= 2 && !args.some(function (i) !/^\d+(?:$|:)/.test(i)),
+ _("error.trailingCharacters"));
- let [winIndex, tabIndex] = args.map(parseInt);
+ let [winIndex, tabIndex] = args.map(function (arg) parseInt(arg));
let win = dactyl.windows[winIndex - 1];
dactyl.assert(win, _("window.noIndex", winIndex));
dactyl.assert(win != window, _("window.cantAttachSame"));
let browser = win.getBrowser();
+
+ if (args[1]) {
+ let tabList = browser.visibleTabs || browser.mTabs;
+ let target = dactyl.assert(tabList[tabIndex]);
+ tabIndex = Array.indexOf(browser.mTabs, target) - 1;
+ }
+
let dummy = browser.addTab("about:blank");
browser.stop();
// XXX: the implementation of DnD in tabbrowser.xml suggests
let last = browser.mTabs.length - 1;
- browser.moveTabTo(dummy, Math.constrain(tabIndex || last, 0, last));
+ if (args[1])
+ browser.moveTabTo(dummy, tabIndex);
browser.selectedTab = dummy; // required
browser.swapBrowsersAndCloseOther(dummy, config.tabbrowser.mCurrentTab);
}, {
argCount: "+",
+ literal: 1,
completer: function (context, args) {
- if (args.completeArg == 0) {
+ switch (args.completeArg) {
+ case 0:
context.filters.push(function ({ item }) item != window);
completion.window(context);
+ break;
+ case 1:
+ let win = dactyl.windows[Number(args[0]) - 1];
+ if (!win || !win.dactyl)
+ context.message = _("Error", _("window.noIndex", winIndex));
+ else
+ win.dactyl.modules.commands.get("tabmove").completer(context);
+ break;
}
}
});
tabs.tabStyle.enabled = true;
else {
prefs.safeSet("browser.tabs.autoHide", value === "multitab",
- "See 'showtabline' option.");
+ _("option.showtabline.safeSet"));
tabs.tabStyle.enabled = false;
}
+
if (value !== "multitab" || !dactyl.has("Gecko2"))
- config.tabStrip.collapsed = false;
+ if (tabs.xulTabs)
+ tabs.xulTabs.visible = value !== "never";
+ else
+ config.tabStrip.collapsed = false;
+
if (config.tabbrowser.tabContainer._positionPinnedTabs)
config.tabbrowser.tabContainer._positionPinnedTabs();
return value;
values: activateGroups,
has: Option.has.toggleAll,
setter: function (newValues) {
- let valueSet = set(newValues);
+ let valueSet = Set(newValues);
for (let group in values(activateGroups))
if (group[2])
prefs.safeSet("browser.tabs." + group[2],
!(valueSet["all"] ^ valueSet[group[0]]),
- "See the 'activate' option");
+ _("option.activate.safeSet"));
return newValues;
}
});
{
values: {
"all": "All commands",
- "addons": ":addo[ns] command",
- "downloads": ":downl[oads] command",
"extoptions": ":exto[ptions] command",
"help": ":h[elp] command",
- "javascript": ":javascript! or :js! command",
"prefs": ":pref[erences]! or :prefs! command"
},
has: Option.has.toggleAll
}
prefs.safeSet("browser.link.open_newwindow", open,
- "See 'popups' option.");
+ _("option.popups.safeSet"));
prefs.safeSet("browser.link.open_newwindow.restriction", restriction,
- "See 'popups' option.");
+ _("option.popups.safeSet"));
return values;
},
values: {
--- /dev/null
+" Vim syntax file
+" Language: JavaScript
+" Maintainer: Yi Zhao (ZHAOYI) <zzlinux AT hotmail DOT com>
+" Last Change: May 17, 2007
+" Version: 0.7.5
+" Changes: 1, Get the vimdiff problem fixed finally.
+" Matthew Gallant reported the problem and test the fix. ;)
+" 2, Follow the suggestioin from Ingo Karkat.
+" The 'foldtext' and 'foldlevel' settings should only be
+" changed if the file being edited is pure JavaScript,
+" not if JavaScript syntax is embedded inside other syntaxes.
+" 3, Remove function FT_JavaScriptDoc().
+" Since VIM do the better than me.
+"
+" TODO:
+" - Add the HTML syntax inside the JSDoc
+
+if !exists("main_syntax")
+ if version < 600
+ syntax clear
+ elseif exists("b:current_syntax")
+ finish
+ endif
+ let main_syntax = 'javascript'
+endif
+
+"" Drop fold if it set but VIM doesn't support it.
+let b:javascript_fold='true'
+if version < 600 " Don't support the old version
+ unlet! b:javascript_fold
+endif
+
+syn include @xmlTop syntax/xml.vim
+unlet b:current_syntax
+
+syn include @cssTop syntax/css.vim
+unlet b:current_syntax
+
+"" dollar sigh is permittd anywhere in an identifier
+setlocal iskeyword+=$
+
+syntax sync fromstart
+syntax sync maxlines=200
+
+"" JavaScript comments
+syntax keyword javaScriptCommentTodo TODO FIXME XXX TBD contained
+syntax region javaScriptLineComment start=+\/\/+ end="\v$|(\</?(css|e4x)\>)@=" keepend contains=javaScriptCommentTodo,@Spell
+syntax region javaScriptLineComment start=+^\s*\/\/+ skip=+\n\s*\/\/+ end="\v$|(\</?(css|e4x)\>)@=" keepend contains=javaScriptCommentTodo,@Spell fold
+syntax region javaScriptCvsTag start="\$\cid:" end="\$" oneline contained
+syntax region javaScriptComment start="/\*" end="\v\*/|(\</?(css|e4x)\>)@=" contains=javaScriptCommentTodo,javaScriptCvsTag,@Spell fold
+
+"" JSDoc support start
+if !exists("javascript_ignore_javaScriptdoc")
+ syntax case ignore
+
+ "" syntax coloring for javadoc comments (HTML)
+ "syntax include @javaHtml <sfile>:p:h/html.vim
+ "unlet b:current_syntax
+
+ syntax region javaScriptDocComment matchgroup=javaScriptComment start="/\*\*\s*$" end="\*/" contains=javaScriptDocTags,javaScriptCommentTodo,javaScriptCvsTag,@javaScriptHtml,@Spell fold
+ syntax match javaScriptDocTags contained "@\(param\|argument\|requires\|exception\|throws\|type\|class\|extends\|see\|link\|member\|module\|method\|title\|namespace\|optional\|default\|base\|file\)\>" nextgroup=javaScriptDocParam,javaScriptDocSeeTag skipwhite
+ syntax match javaScriptDocTags contained "@\(beta\|deprecated\|description\|fileoverview\|author\|license\|version\|returns\=\|constructor\|private\|protected\|final\|ignore\|addon\|exec\)\>"
+ syntax match javaScriptDocParam contained "\%(#\|\w\|\.\|:\|\/\)\+"
+ syntax region javaScriptDocSeeTag contained matchgroup=javaScriptDocSeeTag start="{" end="}" contains=javaScriptDocTags
+
+ syntax case match
+endif "" JSDoc end
+
+syntax case match
+
+"" Syntax in the JavaScript code
+syntax match javaScriptSpecial "\\\d\d\d\|\\x\x\{2\}\|\\u\x\{4\}\|\\."
+syntax region javaScriptStringD start=+"+ skip=+\\\\\|\\$"+ end=+"+ contains=javaScriptSpecial,@htmlPreproc
+syntax region javaScriptStringS start=+'+ skip=+\\\\\|\\$'+ end=+'+ contains=javaScriptSpecial,@htmlPreproc
+syntax region javaScriptRegexpString start=+/\(\*\|/\)\@!+ skip=+\\\\\|\\/+ end=+/[gim]\{-,3}+ contains=javaScriptSpecial,@htmlPreproc oneline
+syntax match javaScriptNumber /\<-\=\d\+L\=\>\|\<0[xX]\x\+\>/
+syntax match javaScriptFloat /\<-\=\%(\d\+\.\d\+\|\d\+\.\|\.\d\+\)\%([eE][+-]\=\d\+\)\=\>/
+syntax match javaScriptLabel /\(?\s*\)\@<!\<\w\+\(\s*:\)\@=/
+
+"" JavaScript Prototype
+syntax keyword javaScriptPrototype prototype
+
+"" Programm Keywords
+syntax keyword javaScriptSource import export
+syntax keyword javaScriptType const this var void yield arguments
+syntax keyword javaScriptOperator delete new in instanceof let typeof
+syntax keyword javaScriptBoolean true false
+syntax keyword javaScriptNull null
+
+"" Statement Keywords
+syntax keyword javaScriptConditional if else
+syntax keyword javaScriptRepeat do while for
+syntax keyword javaScriptBranch break continue switch case default return
+syntax keyword javaScriptStatement try catch throw with finally
+
+syntax keyword javaScriptGlobalObjects Array Boolean Date Function Infinity JavaArray JavaClass JavaObject JavaPackage Math Number NaN Object Packages RegExp String Undefined java netscape sun
+
+syntax keyword javaScriptExceptions Error EvalError RangeError ReferenceError SyntaxError TypeError URIError
+
+syntax keyword javaScriptFutureKeys abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws goto private transient debugger implements protected volatile double import public
+
+"" DOM/HTML/CSS specified things
+
+ " DOM2 Objects
+ syntax keyword javaScriptGlobalObjects DOMImplementation DocumentFragment Document Node NodeList NamedNodeMap CharacterData Attr Element Text Comment CDATASection DocumentType Notation Entity EntityReference ProcessingInstruction
+ syntax keyword javaScriptExceptions DOMException
+
+ " DOM2 CONSTANT
+ syntax keyword javaScriptDomErrNo INDEX_SIZE_ERR DOMSTRING_SIZE_ERR HIERARCHY_REQUEST_ERR WRONG_DOCUMENT_ERR INVALID_CHARACTER_ERR NO_DATA_ALLOWED_ERR NO_MODIFICATION_ALLOWED_ERR NOT_FOUND_ERR NOT_SUPPORTED_ERR INUSE_ATTRIBUTE_ERR INVALID_STATE_ERR SYNTAX_ERR INVALID_MODIFICATION_ERR NAMESPACE_ERR INVALID_ACCESS_ERR
+ syntax keyword javaScriptDomNodeConsts ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE CDATA_SECTION_NODE ENTITY_REFERENCE_NODE ENTITY_NODE PROCESSING_INSTRUCTION_NODE COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE NOTATION_NODE
+
+ " HTML events and internal variables
+ syntax case ignore
+ syntax keyword javaScriptHtmlEvents onblur onclick oncontextmenu ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup onresize
+ syntax case match
+
+" Follow stuff should be highligh within a special context
+" While it can't be handled with context depended with Regex based highlight
+" So, turn it off by default
+if exists("javascript_enable_domhtmlcss")
+
+ " DOM2 things
+ syntax match javaScriptDomElemAttrs contained /\%(nodeName\|nodeValue\|nodeType\|parentNode\|childNodes\|firstChild\|lastChild\|previousSibling\|nextSibling\|attributes\|ownerDocument\|namespaceURI\|prefix\|localName\|tagName\)\>/
+ syntax match javaScriptDomElemFuncs contained /\%(insertBefore\|replaceChild\|removeChild\|appendChild\|hasChildNodes\|cloneNode\|normalize\|isSupported\|hasAttributes\|getAttribute\|setAttribute\|removeAttribute\|getAttributeNode\|setAttributeNode\|removeAttributeNode\|getElementsByTagName\|getAttributeNS\|setAttributeNS\|removeAttributeNS\|getAttributeNodeNS\|setAttributeNodeNS\|getElementsByTagNameNS\|hasAttribute\|hasAttributeNS\)\>/ nextgroup=javaScriptParen skipwhite
+ " HTML things
+ syntax match javaScriptHtmlElemAttrs contained /\%(className\|clientHeight\|clientLeft\|clientTop\|clientWidth\|dir\|id\|innerHTML\|lang\|length\|offsetHeight\|offsetLeft\|offsetParent\|offsetTop\|offsetWidth\|scrollHeight\|scrollLeft\|scrollTop\|scrollWidth\|style\|tabIndex\|title\)\>/
+ syntax match javaScriptHtmlElemFuncs contained /\%(blur\|click\|focus\|scrollIntoView\|addEventListener\|dispatchEvent\|removeEventListener\|item\)\>/ nextgroup=javaScriptParen skipwhite
+
+ " CSS Styles in JavaScript
+ syntax keyword javaScriptCssStyles contained color font fontFamily fontSize fontSizeAdjust fontStretch fontStyle fontVariant fontWeight letterSpacing lineBreak lineHeight quotes rubyAlign rubyOverhang rubyPosition
+ syntax keyword javaScriptCssStyles contained textAlign textAlignLast textAutospace textDecoration textIndent textJustify textJustifyTrim textKashidaSpace textOverflowW6 textShadow textTransform textUnderlinePosition
+ syntax keyword javaScriptCssStyles contained unicodeBidi whiteSpace wordBreak wordSpacing wordWrap writingMode
+ syntax keyword javaScriptCssStyles contained bottom height left position right top width zIndex
+ syntax keyword javaScriptCssStyles contained border borderBottom borderLeft borderRight borderTop borderBottomColor borderLeftColor borderTopColor borderBottomStyle borderLeftStyle borderRightStyle borderTopStyle borderBottomWidth borderLeftWidth borderRightWidth borderTopWidth borderColor borderStyle borderWidth borderCollapse borderSpacing captionSide emptyCells tableLayout
+ syntax keyword javaScriptCssStyles contained margin marginBottom marginLeft marginRight marginTop outline outlineColor outlineStyle outlineWidth padding paddingBottom paddingLeft paddingRight paddingTop
+ syntax keyword javaScriptCssStyles contained listStyle listStyleImage listStylePosition listStyleType
+ syntax keyword javaScriptCssStyles contained background backgroundAttachment backgroundColor backgroundImage gackgroundPosition backgroundPositionX backgroundPositionY backgroundRepeat
+ syntax keyword javaScriptCssStyles contained clear clip clipBottom clipLeft clipRight clipTop content counterIncrement counterReset cssFloat cursor direction display filter layoutGrid layoutGridChar layoutGridLine layoutGridMode layoutGridType
+ syntax keyword javaScriptCssStyles contained marks maxHeight maxWidth minHeight minWidth opacity MozOpacity overflow overflowX overflowY verticalAlign visibility zoom cssText
+ syntax keyword javaScriptCssStyles contained scrollbar3dLightColor scrollbarArrowColor scrollbarBaseColor scrollbarDarkShadowColor scrollbarFaceColor scrollbarHighlightColor scrollbarShadowColor scrollbarTrackColor
+
+ " Highlight ways
+ syntax match javaScriptDotNotation "\." nextgroup=javaScriptPrototype,javaScriptDomElemAttrs,javaScriptDomElemFuncs,javaScriptHtmlElemAttrs,javaScriptHtmlElemFuncs
+ syntax match javaScriptDotNotation "\.style\." nextgroup=javaScriptCssStyles
+
+endif "DOM/HTML/CSS
+
+"" end DOM/HTML/CSS specified things
+
+
+"" Code blocks
+syntax cluster javaScriptAll contains=javaScriptComment,javaScriptLineComment,javaScriptDocComment,javaScriptStringD,javaScriptStringS,javaScriptRegexpString,javaScriptNumber,javaScriptFloat,javaScriptLabel,javaScriptSource,javaScriptType,javaScriptOperator,javaScriptBoolean,javaScriptNull,javaScriptFunction,javaScriptConditional,javaScriptRepeat,javaScriptBranch,javaScriptStatement,javaScriptGlobalObjects,javaScriptExceptions,javaScriptFutureKeys,javaScriptDomErrNo,javaScriptDomNodeConsts,javaScriptHtmlEvents,javaScriptDotNotation,javascriptE4X,javascriptCSS,javascriptCDATA
+syntax region javaScriptBracket matchgroup=javaScriptBracket transparent start="\[" end="\]" contains=@javaScriptAll,javaScriptParensErrB,javaScriptParensErrC,javaScriptBracket,javaScriptParen,javaScriptBlock,@htmlPreproc
+syntax region javaScriptParen matchgroup=javaScriptParen transparent start="(" end=")" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrC,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
+syntax region javaScriptBlock matchgroup=javaScriptBlock transparent start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
+
+syntax region javascriptCDATA matchgroup=javascriptCDATA start="<\!\[CDATA\[" end="\]\]>" keepend contains=javascriptCSS
+syntax region javascriptCSS matchgroup=javascriptCSSDelimiter start="<css>" end="</css>" contains=@cssTop
+syntax region javascriptE4X matchgroup=javascriptE4XDelimiter start="<e4x>" end="</e4x>" contains=@xmlTop
+syntax region javascriptE4X matchgroup=javascriptE4XDelimiter start="<>" end="</>" contains=@xmlTop oneline
+
+"" catch errors caused by wrong parenthesis
+syntax match javaScriptParensError ")\|}\|\]"
+syntax match javaScriptParensErrA contained "\]"
+syntax match javaScriptParensErrB contained ")"
+syntax match javaScriptParensErrC contained "}"
+
+if main_syntax == "javascript"
+ syntax sync ccomment javaScriptComment
+endif
+
+"" Fold control
+if exists("b:javascript_fold")
+ syntax match javaScriptFunction /\<function\>/ nextgroup=javaScriptFuncName skipwhite
+ syntax match javaScriptOpAssign /=\@<!=/ nextgroup=javaScriptFuncBlock skipwhite skipempty
+ syntax region javaScriptFuncName contained matchgroup=javaScriptFuncName start=/\%(\$\|\w\)*\s*(/ end=/)/ contains=javaScriptLineComment,javaScriptComment nextgroup=javaScriptFuncBlock skipwhite skipempty
+ syntax region javaScriptFuncBlock contained matchgroup=javaScriptFuncBlock start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock fold
+
+ if &l:filetype=='javascript' && !&diff
+ " Fold setting
+ " Redefine the foldtext (to show a JS function outline) and foldlevel
+ " only if the entire buffer is JavaScript, but not if JavaScript syntax
+ " is embedded in another syntax (e.g. HTML).
+ setlocal foldmethod=syntax
+ setlocal foldlevel=4
+ endif
+else
+ syntax keyword javaScriptFunction function
+ setlocal foldmethod<
+ setlocal foldlevel<
+endif
+
+" Define the default highlighting.
+" For version 5.7 and earlier: only when not done already
+" For version 5.8 and later: only when an item doesn't have highlighting yet
+if version >= 508 || !exists("did_javascript_syn_inits")
+ if version < 508
+ let did_javascript_syn_inits = 1
+ command -nargs=+ HiLink hi link <args>
+ else
+ command -nargs=+ HiLink hi def link <args>
+ endif
+ HiLink javascriptCDATA String
+ HiLink javaScriptComment Comment
+ HiLink javaScriptLineComment Comment
+ HiLink javaScriptDocComment Comment
+ HiLink javaScriptCommentTodo Todo
+ HiLink javaScriptCvsTag Function
+ HiLink javaScriptDocTags Special
+ HiLink javaScriptDocSeeTag Function
+ HiLink javaScriptDocParam Function
+ HiLink javaScriptStringS String
+ HiLink javaScriptStringD String
+ HiLink javaScriptRegexpString String
+ HiLink javaScriptCharacter Character
+ HiLink javaScriptPrototype Type
+ HiLink javaScriptConditional Conditional
+ HiLink javaScriptBranch Conditional
+ HiLink javaScriptRepeat Repeat
+ HiLink javaScriptStatement Statement
+ HiLink javaScriptFunction Function
+ HiLink javaScriptError Error
+ HiLink javaScriptParensError Error
+ HiLink javaScriptParensErrA Error
+ HiLink javaScriptParensErrB Error
+ HiLink javaScriptParensErrC Error
+ HiLink javaScriptOperator Operator
+ HiLink javaScriptType Type
+ HiLink javaScriptNull Type
+ HiLink javaScriptNumber Number
+ HiLink javaScriptFloat Number
+ HiLink javaScriptBoolean Boolean
+ HiLink javaScriptLabel Label
+ HiLink javaScriptSpecial Special
+ HiLink javaScriptSource Special
+ HiLink javaScriptGlobalObjects Special
+ HiLink javaScriptExceptions Special
+
+ HiLink javaScriptDomErrNo Constant
+ HiLink javaScriptDomNodeConsts Constant
+ HiLink javaScriptDomElemAttrs Label
+ HiLink javaScriptDomElemFuncs PreProc
+
+ HiLink javaScriptHtmlEvents Special
+ HiLink javaScriptHtmlElemAttrs Label
+ HiLink javaScriptHtmlElemFuncs PreProc
+
+ HiLink javaScriptCssStyles Label
+
+ delcommand HiLink
+endif
+
+" Define the htmlJavaScript for HTML syntax html.vim
+"syntax clear htmlJavaScript
+"syntax clear javaScriptExpression
+syntax cluster htmlJavaScript contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError
+syntax cluster javaScriptExpression contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError,@htmlPreproc
+
+let b:current_syntax = "javascript"
+if main_syntax == 'javascript'
+ unlet main_syntax
+endif
+
+
+if exists('b:did_indent')
+ finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetJsIndent()
+setlocal indentkeys=0{,0},0),:,!^F,O,e,=*/
+" Clean CR when the file is in Unix format
+if &fileformat == "unix"
+ silent! %s/\r$//g
+endif
+" Only define the functions once per Vim session.
+"if exists("*GetJsIndent")
+" finish
+"endif
+"function! GetJsIndent()
+" let pnum = prevnonblank(v:lnum - 1)
+" if pnum == 0
+" return 0
+" endif
+" let line = getline(v:lnum)
+" let pline = getline(pnum)
+" let ind = indent(pnum)
+"
+" if pline =~ '{\s*$\|[\s*$\|(\s*$'
+" let ind = ind + &sw
+" endif
+"
+" if pline =~ ';\s*$' && line =~ '^\s*}'
+" let ind = ind - &sw
+" endif
+"
+" if pline =~ '\s*]\s*$' && line =~ '^\s*),\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if pline =~ '\s*]\s*$' && line =~ '^\s*}\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if line =~ '^\s*});\s*$\|^\s*);\s*$' && pline !~ ';\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if line =~ '^\s*})' && pline =~ '\s*,\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if line =~ '^\s*}();\s*$' && pline =~ '^\s*}\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if line =~ '^\s*}),\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if pline =~ '^\s*}\s*$' && line =~ '),\s*$'
+" let ind = ind - &sw
+" endif
+"
+" if pline =~ '^\s*for\s*' && line =~ ')\s*$'
+" let ind = ind + &sw
+" endif
+"
+" if line =~ '^\s*}\s*$\|^\s*]\s*$\|\s*},\|\s*]);\s*\|\s*}]\s*$\|\s*};\s*$\|\s*})$\|\s*}).el$' && pline !~ '\s*;\s*$\|\s*]\s*$' && line !~ '^\s*{' && line !~ '\s*{\s*}\s*'
+" let ind = ind - &sw
+" endif
+"
+" if pline =~ '^\s*/\*'
+" let ind = ind + 1
+" endif
+"
+" if pline =~ '\*/$'
+" let ind = ind - 1
+" endif
+" return ind
+"endfunction
+
+" vim: ts=4
<p>
If the <em>-group</em>=<a>group</a> flag is given, add this autocmd
to the named <t>group</t>. Any filters for <a>group</a> apply in
- addition to <oa>filter</oa>.
+ addition to <oa>filter</oa>. When listing commands this limits the
+ output to the specified group.
</p>
<p>Available <oa>events</oa>:</p>
</item>
<h2 tag="autocmd-examples">Examples</h2>
-<p>Enable <em>passthrough</em> mode on all Google sites:</p>
+<p>Enable <em>Pass Through</em> mode on all Google sites:</p>
<code><ex>:autocmd LocationChange</ex> <str delim="">google.com</str> <ex>:normal!</ex> <k name="C-z"/></code>
-<p>Enable <em>passthrough</em> mode on <em>some</em> Google sites:</p>
+<p>Enable <em>Pass Through</em> mode on <em>some</em> Google sites:</p>
<code><ex>:autocmd LocationChange</ex> <str delim="'">^https?://(www|mail)\.google\.com/</str> <ex>:normal!</ex> <k name="C-z"/></code>
&dactyl.appName; overrides nearly all &dactyl.host; keys in order to
make browsing more pleasant for Vim users. On the occasions when you
want to bypass &dactyl.appName;'s key handling and pass keys directly to
-&dactyl.host; or to a web page, you have two options:
+&dactyl.host; or to a web page, you have several options:
<item>
- <tags><![CDATA[<A-b>]]></tags>
+ <tags><![CDATA[<pass-next-key-builtin> <A-b>]]></tags>
<spec><![CDATA[<A-b>]]></spec>
<description>
<p>
</description>
</item>
+<p>
+ See also <o>passkeys</o> and <o>passunknown</o> for ways to permanently pass
+ all or particular keys under certain conditions.
+</p>
+
<h2 tag="opening">Opening web pages</h2>
<item>
<example><ex>:open Linus Torvalds</ex></example>
</li>
<li>
- Any other value is passed directly &dactyl.host; and
+ Any other value is passed directly to &dactyl.host; and
must be a valid URL or hostname.
</li>
</ol>
</item>
<item>
- <tags><![CDATA[<C-x>]]></tags>
+ <tags><![CDATA[<decrement-url-path> <C-x>]]></tags>
<strut/>
<spec><oa>count</oa><C-x></spec>
<description>
</item>
<item>
- <tags><![CDATA[<C-a>]]></tags>
+ <tags><![CDATA[<increment-url-path> <C-a>]]></tags>
<strut/>
<spec><oa>count</oa><C-a></spec>
<description>
</item>
<item>
- <tags>~</tags>
+ <tags><![CDATA[<open-home-directory> ~]]></tags>
<spec>~</spec>
<description short="true">
<p>Open home directory. Equivalent to <ex>:open ~/</ex></p>
<p>
Current history position is marked with <em>></em>.
- Jump numbers may be used as counts for with
+ Jump numbers may be used as counts for
<ex>:back</ex> or <ex>:forward</ex>.
</p>
</description>
</item>
<item>
- <tags>gh</tags>
+ <tags><![CDATA[<open-homepage> gh]]></tags>
<spec>gh</spec>
<description short="true">
<p>Go home. Opens the homepage in the current tab.</p>
</item>
<item>
- <tags>gH</tags>
+ <tags><![CDATA[<tab-open-homepage> gH]]></tags>
<strut/>
<spec>gH</spec>
<description>
</item>
<item>
- <tags>gu</tags>
+ <tags><![CDATA[<open-parent-path> gu]]></tags>
<spec><oa>count</oa>gu</spec>
<description short="true">
<p>Go to <oa>count</oa>th parent directory.</p>
</item>
<item>
- <tags>gU</tags>
+ <tags><![CDATA[<open-root-path> gU]]></tags>
<spec>gU</spec>
<description short="true">
<p>Go to the root of the web site.</p>
</description>
</item>
-<item>
- <tags>:reh :rehash</tags>
- <spec>:reh<oa>ash</oa> <oa>arg</oa> …</spec>
- <description>
- <p>
- Reload the &dactyl.appName; add-on, including all code, plugins,
- and configuration. For users running directly from the development
- repository, this is a good way to update to the latest version or
- to test your changes.
- </p>
- <p>
- Any arguments supplied are parsed as command-line arguments as
- specified in <t>startup-options</t>.
- </p>
- <warning>
- Not all plugins are designed to cleanly un-apply during a rehash.
- While official plugins are safe, beware of possible instability
- if you rehash while running third-party plugins.
- </warning>
- </description>
-</item>
-
<item>
<tags>:re :reload</tags>
<spec>:re<oa>load</oa><oa>!</oa></spec>
<h2 tag="stopping">Stopping</h2>
<item>
- <tags><![CDATA[<C-c> :st :stop]]></tags>
+ <tags><![CDATA[<stop-load> <C-c> :st :stop]]></tags>
<spec><C-c></spec>
<strut/>
<spec>:st<oa>op</oa></spec>
<h2 tag="quitting save-session">Quitting</h2>
+<item>
+ <tags>ZQ :x :exit</tags>
+ <strut/>
+ <spec>:exit<oa>!</oa></spec>
+ <description>
+ <p>
+ Quit &dactyl.appName;, no matter how many tabs/windows are open.
+ The session is not stored. Use <oa>!</oa> to forcibly quit.
+ </p>
+ </description>
+</item>
+
<item>
<tags>:q :quit</tags>
<strut/>
<spec>:quita<oa>ll</oa></spec>
<description>
<p>
- Quit &dactyl.appName;, no matter how many tabs/windows
- are open. The session is not stored.
+ Close the current &dactyl.appName; window, no matter how
+ many tabs are open.
</p>
</description>
</item>
</item>
<item>
- <tags>:xa :xall :wq :wqa :wqall</tags>
+ <tags>ZZ :xa :xall :wq :wqa :wqall</tags>
<spec>:wqa<oa>ll</oa></spec>
<strut/>
<spec>:xa<oa>ll</oa></spec>
</description>
</item>
-<item>
- <tags>ZQ</tags>
- <spec>ZQ</spec>
- <description short="true">
- <p>Quit and don't save the session. Works like <ex>:qall</ex>.</p>
- </description>
-</item>
-
-<item>
- <tags>ZZ</tags>
- <spec>ZZ</spec>
- <description short="true">
- <p>
- Quit &dactyl.appName; and save the session. Works like
- <ex>:xall</ex>.
- </p>
- </description>
-</item>
-
<h2 tag="current-directory">The current directory</h2>
<item>
</item>
<item>
- <tags>gf</tags>
+ <tags><![CDATA[<view-source> gf]]></tags>
<strut/>
<spec>gf</spec>
<description>
</item>
<item>
- <tags>gF</tags>
+ <tags><![CDATA[<view-source-externally> gF]]></tags>
<strut/>
<spec>gF</spec>
<description>
</item>
<item>
- <tags><![CDATA[<Home> gg]]></tags>
+ <tags><![CDATA[<scroll-top> <Home> gg]]></tags>
<strut/>
<spec><oa>count</oa>gg</spec>
<description>
</item>
<item>
- <tags><![CDATA[<End> G]]></tags>
+ <tags><![CDATA[<scroll-bottom> <End> G]]></tags>
<strut/>
<spec><oa>count</oa>G</spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-column-left> <Left> h]]></tags>
+ <tags><![CDATA[<scroll-left-column> <Left> h]]></tags>
<strut/>
<spec><oa>count</oa>h</spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-line-down> <C-e> <Down> j]]></tags>
+ <tags><![CDATA[<scroll-down-line> <C-e> <Down> j]]></tags>
<strut/>
<spec><oa>count</oa>j</spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-line-up> <C-y> <Up> k]]></tags>
+ <tags><![CDATA[<scroll-up-line> <C-y> <Up> k]]></tags>
<strut/>
<spec><oa>count</oa>k</spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-column-right> <Right> l]]></tags>
+ <tags><![CDATA[<scroll-right-column> <Right> l]]></tags>
<strut/>
<spec><oa>count</oa>l</spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-page-up> <S-Space> <PageUp> <C-b>]]></tags>
+ <tags><![CDATA[<scroll-up-page> <S-Space> <PageUp> <C-b>]]></tags>
<strut/>
<spec><oa>count</oa><C-b></spec>
<description>
</item>
<item>
- <tags><![CDATA[<scroll-page-down> <Space> <PageDown> <C-f>]]></tags>
+ <tags><![CDATA[<scroll-down-page> <Space> <PageDown> <C-f>]]></tags>
<strut/>
<spec><oa>count</oa><C-f></spec>
<description>
</description>
</item>
+<item>
+ <tags>[h [p [</tags>
+ <spec><oa>count</oa>[<a>arg</a></spec>
+ <description short="true">
+ <p>Jump to the previous element as defined by <o>jumptags</o>.</p>
+ </description>
+</item>
+
+<item>
+ <tags>]h ]p ]</tags>
+ <spec><oa>count</oa>]<a>arg</a></spec>
+ <description short="true">
+ <p>Jump to the next element as defined by <o>jumptags</o>.</p>
+ </description>
+</item>
+
+<item>
+ <tags>{</tags>
+ <spec><oa>count</oa>{</spec>
+ <description short="true">
+ <p>Jump to the previous paragraph. Identical to <k>[p</k>.</p>
+ </description>
+</item>
+
+<item>
+ <tags>}</tags>
+ <spec><oa>count</oa>}</spec>
+ <description short="true">
+ <p>Jump to the next paragraph. Identical to <k>]p</k>.</p>
+ </description>
+</item>
+
+
<h2 tag="zooming zoom">Zooming</h2>
<p>
xmlns="&xmlns.dactyl;"
xmlns:html="&xmlns.html;">
-<h1 tag="command-line-mode command-line mode-cmdline">Command-line mode</h1>
+<h1 tag="command-line-mode command-line mode-cmdline">Command Line mode</h1>
<toc start="2"/>
<p>
- &dactyl.appName;'s command-line mode is perhaps its most
+ &dactyl.appName;'s Command Line mode is perhaps its most
powerful interface. In this mode, the command input bar at the
bottom of the window is given the keyboard focus for any of a
variety of required inputs. In addition to access to almost
<p>
Included among the several command-line modes are Ex command
- mode (the standard mode for entering commands), find mode (for
- searching the current page), prompt mode (for selecting files,
- confirming actions), and hint mode (for selecting links and
+ mode (the standard mode for entering commands), Find mode (for
+ searching the current page), Prompt mode (for selecting files,
+ confirming actions), and Hints mode (for selecting links and
other items on a page).
</p>
<tags><![CDATA[c_<C-c>]]></tags>
<spec><C-c></spec>
<description short="true">
- <p>Quit Command-line mode without executing.</p>
+ <p>Quit Command Line mode without executing.</p>
</description>
</item>
<h3 tag="faq-statussymbols">What do the "[-+♥]" symbols in the status bar mean?</h3>
<p>
These indicate that you can move backward through history,
- that you can move forward through history, and that the page
- is bookmarked, respectively. See also <ex>:help</ex>
+ that you can move forward through history, and that the page is
+ bookmarked, respectively. See also <ex>:help</ex>
<t>status-line</t>.
</p>
<ex>:help</ex> <t>pattern</t>.
</p>
- <h3 tag="faq-autocomplete">How can I prevent the command line completion list showing until I press <k name="Tab"/>?</h3>
+ <h3 tag="faq-autocomplete"><strut/>How can I prevent the command line completion list showing until I press <k name="Tab"/>?</h3>
<p>
- You can disable it entirely with <ex>:set autocomplete=</ex>
- or for specific types of command completion by
- choosing more restrictive values. See <ex>:help</ex>
- <o>autocomplete</o> and <o>wildmode</o>.
+ You can disable it entirely with <se opt="autocomplete"/> or for
+ specific types of command completion by choosing more
+ restrictive values. See <ex>:help</ex> <o>autocomplete</o> and
+ <o>wildmode</o>.
</p>
<h3 tag="faq-editor-fork">Why doesn't external input field editing work with my <o>editor</o> setting?</h3>
<p>
- Unfortunately, external editors which return immediately,
- before editing is complete, are not supported. This means that
- gvim, for instance, must be run with the <em>-f</em> flag, and
- editors run from a terminal must not connect to a remote
- process. In the case of Rxvt-unicode, this means that the
- urxvtc program is not an option, and Gnome Terminal is very
- likely not useable under any circumstances.
+ Unfortunately, external editors which return immediately, before
+ editing is complete, are not supported. This means that
+ <tt>gvim</tt>, for instance, must be run with the <em>-f</em>
+ flag, and editors run from a terminal must not connect to a
+ remote process. In the case of Rxvt-unicode, this means that the
+ <tt>urxvtc</tt> program is not an option, and Gnome Terminal is
+ very likely not useable under any circumstances.
</p>
<note>
- If you are using a version of Firefox newer than 4.0
- beta 7 and a version of Pentadactyl less than 1.0
- βeta 4, you'll need to upgrade the latter.
+ If you are using a version of Firefox newer than 4.0 beta 7 and
+ a version of Pentadactyl less than 1.0 βeta 4, you'll need to
+ upgrade the latter.
</note>
<h3 tag="faq-symlinks">Why can't I build/install from the Mercurial repository on Windows®?</h3>
<p>
We use symbolic links in our repository to deal with certain
- files which are common across projects. Mercurial for
- Windows®, unfortunately, doesn't deal with these very well.
- However, adding the following lines to the <tt>.hg\hgrc</tt>
- file in your repository should make things work:
+ files which are common across projects. Mercurial for Windows®,
+ unfortunately, doesn't deal with these very well. However,
+ adding the following lines to the <tt>.hg\hgrc</tt> file in your
+ repository should make things work:
</p>
<code><hl key="Key">[hooks]</hl>
update = <str delim="">python:common/contrib/fix_symlinks.py:fix_symlinks</str>
engines are hidden after an update. This can be remedied by
invoking
- <code style="position: relative"><ex>:js</ex> services.get(<str>browserSearch</str>).getEngines().forEach(<em>function</em> (e) e.hidden = <hl key="Boolean">false</hl>)</code>
+ <code style="position: relative"><ex>:js</ex> services.browserSearch.getEngines().forEach(<em>function</em> (e) e.hidden = <hl key="Boolean">false</hl>)</code>
</p>
<h2 tag="faq-keys">Key bindings</h2>
<p>
See the <o>passkeys</o> option to automatically pass specific
keys on sites of your choosing, or <t>autocmd-examples</t> to
- automatically enter <em>PASS THROUGH</em> mode for certain websites.
+ automatically enter <em>Pass Through</em> mode for certain websites.
</p>
<h3 tag="faq-passkeys-autocmd">Why doesn't my modes.passAllKeys autocmd work anymore?</h3>
<h3 tag="faq-hintkeys">How can I use keys other than numbers for hinting?</h3>
<p>Use the <o>hintkeys</o> option.</p>
- <h3 tag="faq-hintkeys-uppercase">How can I display my hints in upper case but type them in lower case?</h3>
+ <h3 tag="faq-hintkeys-uppercase"><strut/>How can I display my hints in upper case but type them in lower case?</h3>
<p>
If you use alphabetic characters for your <o>hintkeys</o> and
would like to be able to type them in lower case but still have
Show progress of current downloads. Here, downloads can
be paused, resumed, and canceled.
</p>
+
+ <p>Available options include:</p>
+
+ <dl>
+ <dt>-sort</dt> <dd>Sort order (see <o>downloadsort</o>) (short name: <em>-s</em>)</dd>
+ </dl>
+ </description>
+</item>
+
+<item>
+ <tags>:dlc :dlclear</tags>
+ <strut/>
+ <spec>:dlc<oa>lear</oa></spec>
+ <description>
+ <p>Clear completed downloads.</p>
</description>
</item>
<h2 tag="extensions add-ons">Add-ons</h2>
+<p>
+ The following commands manipulate the currently installed
+ add-ons. With the exception of <ex>:extadd</ex>, they all except
+ the following arguments:
+</p>
+
+<dl>
+ <dt>-types</dt> <dd>The types of add-ons to operate on, the most
+ common types being <tt>extension</tt>, <tt>theme</tt>, and <tt>plugin</tt> (short names <em>-type</em>,
+ <em>-t</em>)</dd>
+</dl>
+
<item>
<tags>:exta :extadd</tags>
<spec>:exta<oa>dd</oa> <a>file|url</a></spec>
</li>
<li>
<em>History and bookmark status</em> (<tt>[+-❤⋯]</tt>): The position
- of the current page in the tab's session history; <tt>+</tt> and
- <tt>-</tt> indicate that it is possible to move backwards and forwards
+ of the current page in the tab's session history; <tt>-</tt> and
+ <tt>+</tt> indicate that it is possible to move backwards and forwards
through the history respectively. ❤ indicates that the current page is
bookmarked. Any other character indicates a QuickMark matching the
current page.
xmlns="&xmlns.dactyl;"
xmlns:html="&xmlns.html;">
-<h1 tag="quick-hints hints">Hints</h1>
+<h1 tag="hints">Hints</h1>
<toc start="2"/>
<p>
Hints are an easy way to interact with web pages without using
- your mouse. In hint mode, &dactyl.appName; highlights and
+ your mouse. In Hints mode, &dactyl.appName; highlights and
numbers all clickable elements. The elements can be selected
either by typing their numbers, or typing parts of their text to
narrow them down. While the default action is to click the
</p>
<item>
- <tags>quick-hints</tags>
- <tags>f QuickHint</tags>
+ <tags>quick-hints hint-mode</tags>
+ <tags>f</tags>
<strut/>
<spec>f<a>hint</a></spec>
<description>
<p>
- Start <t>QuickHint</t> mode. In this mode, every clickable
+ Start <t>hint-mode</t>. In this mode, every clickable
element (as defined by the <o>hinttags</o> option) is
highlighted and numbered. Elements can be selected
either by typing their number, or by typing part of
their text to narrow down the result. When an element
has been selected, it is automatically clicked and hint
mode ends. Additionally, the following keys have
- special meanings in QuickHint mode:
+ special meanings in Hints mode:
</p>
<dl>
<dt><k name="CR"/></dt>
value of <o>hintkeys</o>) as ordinary text</dd>
<dt><k name="Esc"/></dt>
- <dd>Exits hint mode without selecting an element</dd>
+ <dd>Exits Hints mode without selecting an element</dd>
</dl>
</description>
</item>
<spec>F<a>hint</a></spec>
<description>
<p>
- Start <t>QuickHint</t> mode, but the selected elements
+ Start <t>hint-mode</t>, but the selected elements
are clicked with the <k name="Shift" link="false"/> key pressed,
which has the normal effect of opening it in a new tab
(depending on the value of the
<item>
<tags>extended-hints</tags>
- <tags>; ExtendedHint</tags>
+ <tags>;</tags>
<strut/>
<spec>;<a>mode</a><a>hint</a></spec>
<description>
<p>
- Start an extended hint mode. <t>ExtendedHint</t> mode is exactly
- like <t>QuickHint</t> mode, except that each sub-mode highlights a
+ Start an extended hints mode. Extended hints are exactly like
+ <t>quick-hints</t>, except that each sub-mode highlights a
more specialized set of elements, and performs a unique action on
the selected link. Because of the panoply of extended hint modes
available, after pressing <k>;</k>, pressing <k name="Tab"/> brings
- up the completion list with each hint mode and its description.
+ up the completion list with each hints mode and its description.
</p>
<p><a>mode</a> may be one of:</p>
<spec>g;<a>mode</a><a>hint</a></spec>
<description>
<p>
- Start an extended hint mode and stay there until
+ Start an extended hints mode and stay there until
<k name="Esc"/> is pressed. Like <k>;</k>, except that
after a hint is selected, hints remain visible so that
another one can be selected with the same action as the
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>
-<!DOCTYPE document SYSTEM "dactyl://content/dtd">
+<!DOCTYPE document SYSTEM "dactyl://content/modes.dtd">
<document
name="map"
<p>
Key mappings are the most basic means &dactyl.appName; provides
for altering the actions of key presses. Each key mapping is
- associated with a mode, such as <link topic="insert-mode">insert</link>,
- <link>normal</link>, or
- <link topic="command-line-mode">command-line</link>, and only
+ associated with a mode, such as <link topic="insert-mode">Insert</link>,
+ <link>Normal</link>, or
+ <link topic="command-line-mode">Command Line</link>, and only
has effect when that mode is active. Although each mode has a
full suite of internal mappings, they may be easily augmented,
altered, or removed with the <ex>:map</ex> command and its
<dt>n</dt> <dd>Normal mode: When browsing normally</dd>
<dt>v</dt> <dd>Visual mode: When selecting text with the cursor keys</dd>
<dt>i</dt> <dd>Insert mode: When interacting with text fields on a website</dd>
- <dt>t</dt> <dd>TextEdit mode: When editing text fields in Vim-like NORMAL mode</dd>
- <dt>c</dt> <dd>Command-line mode: When typing into the &dactyl.appName; command line</dd>
+ <dt>t</dt> <dd>Text Edit mode: When editing text fields in Vim-like Normal mode</dd>
+ <dt>c</dt> <dd>Command Line mode: When typing into the &dactyl.appName; command line</dd>
</dl>
<p>
The ordinary <ex>:map</ex> and <ex>:noremap</ex> commands
- add mappings for normal and visual mode. In order to map key
+ add mappings for Normal and Visual mode. In order to map key
bindings in a different mode, any of the mapping commands may be
prefixed with one of the above letters. For instance,
- <ex>:imap</ex> creates a new key mapping in insert mode, while
- <ex>:cunmap</ex> removes a key mapping from command-line mode.
+ <ex>:imap</ex> creates a new key mapping in Insert mode, while
+ <ex>:cunmap</ex> removes a key mapping from Command Line mode.
Other modes can be specified using the -modes option described below.
</p>
saved via the <ex>:mk&dactyl.name;rc</ex> command.
</warning>
+<p tag="modes">
+ The following tree represents all of the modes understood by
+ dactyl. Mappings for a mode also apply to its children and
+ descendants. So a mapping in the Base mode, for instance, is
+ also active in Normal and Ex mode.
+</p>
+
+&modes.tree;
+
<h3 tag=":map-commands">Map commands</h3>
<item>
</description>
</item>
+<p tag=":map-overview">
+ Below is an overview of which modes each map command applies to:
+</p>
+
+<dl dt="width: 20em;">
+ <dt>:map :noremap :unmap</dt> <dd>Normal and Visual modes</dd>
+ <dt>:nmap :nnoremap :nunmap</dt> <dd>Normal mode</dd>
+ <dt>:vmap :vnoremap :vunmap</dt> <dd>Visual mode</dd>
+ <dt>:imap :inoremap :iunmap</dt> <dd>Insert mode</dd>
+ <dt>:tmap :tnoremap :tunmap</dt> <dd>Text Edit mode</dd>
+ <dt>:cmap :cnoremap :cunmap</dt> <dd>Command Line mode</dd>
+</dl>
+
+<note>
+ The <em>-modes</em> option, described below, provides a more flexible way
+ to specify the applicable modes.
+</note>
+
<h3 tag=":map-options">Map options</h3>
<p>
Any of the map commands may be given the following options:
<dt>-count</dt> <dd>Accept a count before the requisite key press. Sets the <tt>count</tt> parameter to the result. (short name <em>-c</em>)</dd>
<dt>-description</dt> <dd>A description of this mapping (short name <em>-d</em>)</dd>
<dt>-ex</dt> <dd>Execute <a>rhs</a> as an Ex command rather than keys (short name <em>-e</em>)</dd>
- <dt>-group=<a>group</a></dt> <dd>Add this command to the given <t>group</t> (short name <em>-g</em>)</dd>
+ <dt>-group=<a>group</a></dt> <dd>Add this command to the given <t>group</t> (short name <em>-g</em>). When listing commands this limits the output to the specified group.</dd>
<dt>-javascript</dt> <dd>Execute <a>rhs</a> as JavaScript rather than keys (short names <em>-js</em>, <em>-j</em>)</dd>
<dt>-literal=<a>n</a></dt> <dd>Parse the <a>n</a>th argument without specially processing any quote or meta characters. (short name <em>-l</em>)</dd>
- <dt>-modes</dt> <dd>Create this mapping in the given modes (short names <em>-mode</em>, <em>-m</em>)</dd>
+ <dt>-modes=<a>modes</a></dt> <dd>Create this mapping in the given modes (short names <em>-mode</em>, <em>-m</em>)</dd>
<dt>-nopersist</dt> <dd>Do not save this mapping to an auto-generated rc file (short name <em>-n</em>)</dd>
<dt>-silent</dt> <dd>Do not echo any generated keys to the command line (short name <em>-s</em>, also <em><silent></em> for Vim compatibility)</dd>
</dl>
<item>
<tags>:no :noremap</tags>
<spec>:no<oa>remap</oa> <a>lhs</a> <a>rhs</a></spec>
- <tags>:nn :nnoremap</tags>
- <spec>:nn<oa>oremap</oa> <a>lhs</a> <a>rhs</a></spec>
- <tags>:vn :vnoremap</tags>
- <spec>:vn<oa>oremap</oa> <a>lhs</a> <a>rhs</a></spec>
+ <tags>:nno :nnoremap</tags>
+ <spec>:nno<oa>remap</oa> <a>lhs</a> <a>rhs</a></spec>
+ <tags>:vno :vnoremap</tags>
+ <spec>:vno<oa>remap</oa> <a>lhs</a> <a>rhs</a></spec>
<tags>:ino :inoremap</tags>
<spec>:ino<oa>remap</oa> <a>lhs</a> <a>rhs</a></spec>
<tags>:tno :tnoremap</tags>
documentation of those options for more information.
</p>
-<h3 tag=":map-arguments">Special arguments</h3>
-
-<p>
- Below is an overview of which modes each map command applies to:
-</p>
-
-<!-- TODO: table format -->
-
-<code>
-:map :noremap :unmap :mapclear – both Normal and Visual modes
-:nmap :nnoremap :nunmap :nmapclear – Normal mode
-:vmap :vnoremap :vunmap :vmapclear – Visual mode
-:imap :inoremap :iunmap :imapclear – Insert mode
-:tmap :tnoremap :tunmap :tmapclear – Text Edit mode
-:cmap :cnoremap :cunmap :cmapclear – Command-line mode
-</code>
-
<h3 tag="key-notation key-sequence">Key sequences</h3>
<p>
<li><k name="C-␣" link="false"/>: The control or ctrl key.</li>
<li><k name="A-␣" link="false"/>: The alt key.</li>
<li><k name="M-␣" link="false"/>: The meta key, windows key, or command key.</li>
+ <li><k name="⌘-␣" link="false"/>: Same as <k name="M-␣" link="false"/>.</li>
<li><k name="S-␣" link="false"/>: The shift key.</li>
</ol>
<dl dt="width: 10em;">
<dt><k link="false">xc</k></dt>
- <dd>Type the ‘X’ key followed by the ‘C’ key.</dd>
+ <dd>Press the ‘X’ key followed by the ‘C’ key.</dd>
<dt><k name="C-x" link="false">c</k></dt>
<dd>
- Type the ‘X’ key while holding the ‘Control’ key, followed
+ Press the ‘X’ key while holding the ‘Control’ key, followed
by the ‘C’ key.
</dd>
<dt><k name="C-2" link="false"/></dt>
- <dd>Type the ‘2’ while holding the ‘Control’ key.</dd>
+ <dd>Type ‘2’ while holding the ‘Control’ key.</dd>
<dt><k name="C-@" link="false"/></dt>
- <dd>Type the ‘@’ key while holding the ‘Control’ key.</dd>
+ <dd>Press the ‘@’ key while holding the ‘Control’ key.</dd>
<dt><k name="S-Space" link="false"/></dt>
<dd>Press the space bar while holding the ‘Shift’ key.</dd>
<dt><k name="C-A-j" link="false"/></dt>
- <dd>Type the ‘J’ key while while holding both the ‘Control’ and ‘Alt’ keys.</dd>
+ <dd>Press the ‘J’ key while holding both the ‘Control’ and ‘Alt’ keys.</dd>
<dt><k name="C-A-J" link="false"/></dt>
<dd>Exactly the same as above.</dd>
<dt><k name="C-A-S-j" link="false"/></dt>
- <dd>Type the ‘J’ key while while holding both the ‘Control’, ‘Alt’, and ‘Shift’ keys.</dd>
+ <dd>Press the ‘J’ key while holding all of ‘Control’, ‘Alt’, and ‘Shift’ keys.</dd>
</dl>
<h3 tag=":map-special-chars">Special characters</h3>
<item>
<tags>:ab :abbreviate</tags>
- <spec>:ab<oa>breviate</oa> <oa>-group=<a>group</a></oa> <a>lhs</a> <a>rhs</a></spec>
+ <spec>:ab<oa>breviate</oa> <oa>-group=<a>group</a></oa> <oa>-js</oa> <a>lhs</a> <a>rhs</a></spec>
<spec>:ab<oa>breviate</oa> <oa>-group=<a>group</a></oa> <a>lhs</a></spec>
<spec>:ab<oa>breviate</oa> <oa>-group=<a>group</a></oa></spec>
<description>
<spec>:ca<oa>bbreviate</oa></spec>
<description>
<p>
- Abbreviate a key sequence for Command-line mode. Same as
- <ex>:ab<oa>breviate</oa></ex>, but for
- <t>command-line</t> mode only.
+ Abbreviate a key sequence for Command Line mode. Same as
+ <ex>:ab<oa>breviate</oa></ex>, but for &mode.command-line; mode
+ only.
</p>
</description>
</item>
<description>
<p>
Abbreviate a key sequence for Insert mode. Same as
- <ex>:ab<oa>breviate</oa></ex>, but for insert mode only.
+ <ex>:ab<oa>breviate</oa></ex>, but for Insert mode only.
</p>
</description>
</item>
<spec>:cuna<oa>bbreviate</oa>!</spec>
<description>
<p>
- Remove abbreviation(s) for Command-line mode. Same as
- <ex>:una<oa>bbreviate</oa></ex>, but for
- <t>command-line</t> mode only.
+ Remove abbreviation(s) for Command Line mode. Same as
+ <ex>:una<oa>bbreviate</oa></ex>, but for &mode.command-line; mode
+ only.
</p>
</description>
</item>
<p>
The <em>-group</em> flag (short name: <em>-g</em>) can be used to
- assign this command to a specific <t>group</t>.
+ assign this command to a specific <t>group</t>. When listing
+ commands this limits the output to the specified group.
</p>
<h3 tag="E175 E176 :command-nargs">Argument handling</h3>
<p>
<em>completions</em> is a two-dimensional array of the form:
- <tt>[[arg1, description1], [arg2, description2], …]</tt>
+ <tt>[[val1, description1], [val2, description2], …]</tt>
</p>
<p>
<p>
Example:
<code><ex>:command foo -nargs=? -complete custom,<str delim="'">
- \ function (context) context.completions = [["arg1", "description1"], ["arg2", "description2"]]</str>
+ \ function (context) context.completions = [["val1", "description1"], ["val2", "description2"]]</str>
\ <ex>:echo</ex> <str>Useless </str> + <em><q-args></em></ex>
<ex>:command foo -nargs=?
- \ -complete custom,<str delim="'">[["arg1", "description1"], ["arg2, "description2"]]</str>
+ \ -complete custom,<str delim="'">[["val1", "description1"], ["val2", "description2"]]</str>
\ <ex>:echo</ex> <str>Same as above but simpler </str> + <em><q-args></em></ex></code>
</p>
# TODO: normalize this debacle of Vim legacy messages
-# : are we losing the error code prefixes? --djk
-abbrev.noSuch = No such abbreviation
-abbrev.none = No abbreviations found
+abbreviation.noSuch = No such abbreviation
+abbreviation.none = No abbreviations found
addon.check-1 = Checking updates for addons: %S
addon.cantInstallDir-1 = Cannot install a directory: %S
addon.unavailable = Don't have add-on yet
+addon.unknownCommand = Unknown command
+addon.commandNotAllowed = Command not allowed
+addon.installingUpdates-1 = Installing updates for addons: %S
+addon.noUpdates = No addon updates found
+addon.error-3 = Add-on %S %S: %S
+
+addon.action.On = On
+addon.action.Off = Off
+addon.action.Delete = Del
+addon.action.Update = Upd
+addon.action.Options = Opt
+
+AddonManager.ERROR_NETWORK_FAILURE = A network error occurred
+AddonManager.ERROR_INCORRECT_HASH = The downloaded file did not match the expected hash
+AddonManager.ERROR_CORRUPT_FILE = The file appears to be corrupt
+AddonManager.ERROR_FILE_ACCESS = There was an error accessing the filesystem
autocmd.executing-2 = Executing %S Auto commands for %S
autocmd.autocommand-1 = autocommand %S
bookmark.noMatchingString-1 = No bookmarks matching string %S
bookmark.none = No bookmarks set
bookmark.cantAdd-1 = Could not add bookmark %S
-bookmark.allGone = All bookmarks deleted
+bookmark.allDeleted = All bookmarks deleted
bookmark.removed-1 = Removed bookmark: %S
bookmark.added-1 = Added bookmark: %S
bookmark.deleted-1 = %S bookmark(s) deleted
+bookmark.prompt.deleteAll = This will delete all bookmarks. Would you like to continue? (yes/[no]):
-buffer.fewer-2 = %S fewer tab%S
+# TODO: add plurals support
+buffer.fewerTab-1 = %S fewer tab
+buffer.fewerTabs-1 = %S fewer tabs
buffer.cantDetatchLast = Can't detach the last tab
buffer.noMatching-1 = No matching buffer for %S
buffer.multipleMatching-1 = More than one match for %S
buffer.noAlternate = No alternate page
buffer.backgroundLoaded = Background tab loaded: %S
+# TODO: categorise these
+buffer.noTitle = [No Title]
+buffer.noName = [No Name]
+buffer.help = [Help]
+
+buffer.bookmarked = bookmarked
+
+buffer.prompt.uploadFile = Upload file:
+buffer.prompt.saveLink = Save link:
+
+context.scriptGroup-1 = Script group for %S
+
command.commands = commands
command.cantDelete = Cannot delete non-user commands
command.invalidOptTypeArg-3 = Invalid argument for %S option %S: %S
command.parsing-1 = Error parsing arguments: %S
command.none = No user-defined commands found
-command.unknownCompleter-1 = E117: Unknown function: %S
command.exists = E174: Command already exists: add ! to replace it
command.noPrevious = E30: No previous command line
-command.noRange = E481: No range allowed
+command.noCount = E481: No count allowed
command.noBang = E477: No ! allowed
command.colorscheme.notFound-1 = E185: Cannot find color scheme %S
command.conditional.illegal = Invalid use of conditional
command.finish.illegal = E168: :finish used outside of a sourced file
-command.let.noSuch-1 = E108: No such variable: %S
-command.let.unexpectedChar = E18: Unexpected characters in :let
command.let.illegalVar-1 = E461: Illegal variable name: %S
-command.let.undefinedVar-1 = E121: Undefined variable: %S
command.let.invalidExpression-1 = E15: Invalid expression: %S
-
+command.let.noSuch-1 = E108: No such variable: %S
+command.let.undefinedVar-1 = E121: Undefined variable: %S
+command.let.unexpectedChar = E18: Unexpected characters in :let
+command.run.noPrevious = E34: No previous command
+command.sanitize.privateMode = Cannot sanitize items in private mode
+command.sanitize.allDeleted = All items sanitized
+command.sanitize.noneDeleted = No items sanitized
+command.scriptnames.none = No sourced scripts found
+command.set.errorParsing-1 = Error parsing :set command: %S
+command.set.numberRequired-2 = E521: Number required after =: %S=%S
+command.set.unknownOption-1 = E518: Unknown option: %S
+command.yank.yankedLine-1 = Yanked %S line
+command.yank.yankedLines-1 = Yanked %S lines
+
+completion.additional = (additional)
+completion.generating = Generating results...
+completion.noCompletions = No Completions
+completion.waitingFor-1 = Waiting for %S
+completion.waitingForKeyPress = Waiting for key press
+completion.matchIndex-2 = match %S of %S
+
+dactyl.created-1 = "(created %S)"
dactyl.parsingCommandLine-1 = Parsing command line options: %S
dactyl.notCommand-2 = E492: Not a %S command: %S
dactyl.sourcingPlugins-1 = Sourcing plugin directory: %S...
dactyl.commandlineOpts-1 = Command-line options: %S
dactyl.noRCFile = No user RC file found
dactyl.initialized-1 = %S fully initialized
+dactyl.sourced-1 = Sourced: %S
+dactyl.prompt.openMany-1 = This will open %S new tabs. Would you like to continue? (yes/[no]):
+dactyl.yank-1 = Yank %S
+
+deprecated.for.theOptionsSystem = the options system
dialog.notAvailable-1 = Dialog %S not available
-group.cantChangeBuiltin-1 = Cannot change %S in the builtin group
-group.cantModifyBuiltin = Cannot modify builtin group
-group.cantRemoveBuiltin = Cannot remove builtin group
-group.noSuch-1 = No such group: %S
-group.invalidName-1 = Invalid group name: %S
-group.noCurrent = No current group
+# TODO: merge with addon.*?
+download.unknownCommand = Unknown command
+download.commandNotAllowed = Command not allowed
+download.prompt.launchExecutable = This will launch an executable download. Would you like to continue? (yes/[no]/always):
-editor.noEditor = No editor specified
+download.nActive-1 = %S active
+download.almostDone = ~1 second
+download.unknown = Unknown
+
+download.action.Cancel = Cancel
+download.action.Clear = Clear
+download.action.Delete = Delete
+download.action.Pause = Pause
+download.action.Remove = Remove
+download.action.Resume = Resume
+download.action.Retry = Retry
+
+editor.prompt.editPassword = Editing a password field externally will reveal the password. Would you like to continue? (yes/[no]):
emenu.notFound-1 = Menu not found: %S
event.nothingToPass = No events to pass
finder.notFound-1 = E486: Pattern not found: %S
-finder.atTop = find hit TOP, continuing at BOTTOM
-finder.atBottom = find hit BOTTOM, continuing at TOP
+finder.atTop = Find hit TOP, continuing at BOTTOM
+finder.atBottom = Find hit BOTTOM, continuing at TOP
+
+group.cantChangeBuiltin-1 = Cannot change %S in the builtin group
+group.cantModifyBuiltin = Cannot modify builtin group
+group.cantRemoveBuiltin = Cannot remove builtin group
+group.noSuch-1 = No such group: %S
+group.invalidName-1 = Invalid group name: %S
+group.noCurrent = No current group
help.dontPanic = E478: Don't panic!
+help.Example = Example
help.noFile-1 = Sorry, help file %S not found
help.noTopic-1 = Sorry, no help for %S
+help.title.Using Plugins = Using Plugins
-hints.noMatcher-1 = Invalid hintmatching type: %S
+hints.noMatcher-1 = Invalid 'hintmatching' type: %S
history.noMatching-1 = No history matching %S
history.none = No history set
history.noURL = URL not found in history
-io.noSuchDir-1 = E344: Can't find directory %S
-io.noPrevDir = E186: No previous directory
-io.notReadable-1 = Can't open file %S
-io.notWriteable-1 = Can't open file %S for writing
+io.callingShell-1 = Calling shell to execute: %S
+io.cantCreateTempFile = Couldn't create temporary file
+io.cantEncode = Input contains characters not valid in the current file encoding
+io.commandFailed = E472: Command failed
+io.definedAt = Defined at
+io.downloadFinished-2 = Download of %S to %S finished
+io.eNotDir = Not a directory
io.exists = File exists (add ! to override)
io.exists-1 = File %S exists (add ! to override)
io.noCommand-1 = Command not found: %S
-io.commandFailed = E472: Command failed
-io.oneFileAllowed = E172: Only one file name allowed
-io.callingShell-1 = Calling shell to execute: %S
-io.sourcing-1 = sourcing %S
-io.sourcingEnd-1 = finished sourcing %S
+io.noPrevDir = E186: No previous directory
+io.noSuchDir-1 = E344: Can't find directory %S
+io.noSuchFile = File does not exist
io.notInRTP-1 = not found in 'runtimepath': %S
+io.notReadable-1 = Can't open file %S
+io.notWriteable-1 = Can't open file %S for writing
+io.oneFileAllowed = E172: Only one file name allowed
io.searchingFor-1 = Searching for %S
io.searchingFor-2 = Searching for %S in %S
-io.downloadFinished-2 = Download of %S to %S finished
+io.shellReturn-1 = shell returned %S
+io.sourcing-1 = sourcing %S
+io.sourcingEnd-1 = finished sourcing %S
+io.sourcingError-1 = Sourcing file: %S
macro.canceled-1 = Canceled playback of macro '%S'
macro.recorded-1 = Recorded macro '%S'
+macro.recorded-2 = Recorded macro %S: %S
macro.loadFailed-1 = Page did not load completely in %S seconds
macro.loadWaiting = Waiting for page to load...
macro.noSuch-1 = Macro '%S' not set
mark.invalid = Invalid mark
mark.unset-1 = Mark not set: %S
mark.noMatching-1 = E283: No marks matching %S
+# TODO: relies on mark formatter
+mark.addURL-1 = Adding URL mark: %S
+mark.addLocal-1 = Adding local mark: %S
+mark.jumpingToURL-1 = Jumping to URL mark: %S
+mark.jumpingToLocal-1 = Jumping to local mark: %S
mode.recursiveSet = Not executing modes.set recursively
+mode.invalidBases = Invalid bases
mow.noPreviousOutput = No previous command output
+mow.continue = Press ENTER or type command to continue
+mow.more = -- More --
+mow.moreHelp = -- More -- SPACE/<C-f>/j: screen/page/line down, <C-b>/<C-u>/k: up, q: quit
+
+mow.contextMenu.copyLink = Copy Link Location
+mow.contextMenu.copyPath = Copy File Path
+mow.contextMenu.copy = Copy
+mow.contextMenu.selectAll = Select All
option.noSuch = No such option
option.noSuch-1 = No such option: %S
option.replaceExisting-1 = Warning: %S already exists: replacing existing option
+option.intRequired = Integer value required
+option.operatorNotSupported-2 = Operator %S not supported for option type %S
+option.notSet-1 = E764: Option '%S' is not set
+
+option.currentValue = Current Value
+option.defaultValue = Default Value
+
+option.bufferLocal = buffer local
+
+option.activate.safeSet = See the 'activate' option.
+option.guioptions.safeSet = See 'guioptions' scrollbar flags.
+option.hintkeys.duplicate = Duplicate keys not allowed
+# The string 'passkeys' must appear exactly, including U0027 apostrophes
+option.passkeys.passedBy = passed by 'passkeys'
+option.popups.safeSet = See the 'activate' option.
+option.showtabline.safeSet = See 'showtabline' option.
+option.visualbell.safeSet = See 'visualbell' option.
+
+pageinfo.s.ownerUnverified = %S (unverified)
plugin.searchingFor-1 = Searching for %S
plugin.searchingForIn-2 = Searching for %S in %S
plugin.notReplacingContext-1 = Not replacing plugin context for %S
+pref.hostPreferences-1 = %S Preferences
+pref.prompt.resetAll-1 = Warning: Resetting all preferences may make %S unusable. Would you like to continue (yes/[no]):
+pref.safeSet.warnChanged-1 = Warning: Setting preference %S, but it's changed from its default value.
+
print.toFile-1 = Printing to file: %S
print.sending = Sending to printer...
print.sent = Print job sent
quickmark.invalid = Argument must be an ASCII letter or digit
quickmark.added-2 = Added Quick Mark '%S': %S
+sanitize.prompt.deleteAll = This will sanitize all items. Would you like to continue? (yes/[no]):
+
save.invalidDestination-1 = Invalid destination: %S
+sort.ascending = ascending
+sort.descending = descending
+
status.link-1 = Link: %S
style.none = No style found
+style.styles = styles
+style.inline = inline
time.total-1 = Total time: %S
+title.Abbrev = Abbrev
+title.Action = Action
+title.Args = Args
+title.Average time = Average time
+title.CSS = CSS
+title.Code execution summary = Code execution summary
+title.Command = Command
+title.Complete = Complete
+title.Definition = Definition
+title.Description = Description
+title.Executed = Executed
+title.Filter = Filter
+title.Jump = Jump
+title.Mode = Mode
+title.Name = Name
+title.Progress = Progress
+title.Range = Range
+title.Replacement = Replacement
+title.Source = Source
+title.Speed = Speed
+title.Status = Status
+title.Time remaining = Time remaining
+title.Title = Title
+title.Total time = Total time
+title.Totals = Totals
+title.URI = URI
+title.Version = Version
+
variable.none = No variables found
window.cantAttachSame = Can't reattach to the same window
error.clipboardEmpty = No clipboard data
error.countRequired-1 = Count required for %S
error.cantOpen-2 = Error opening %S: %S
+error.error-1 = Error: %S
+error.disabled = Item is disabled
+error.disabled-1 = '%S' is disabled
error.interrupted = Interrupted
error.invalidSort-1 = Invalid sort order: %S
-error.trailing = Trailing characters
-error.trailing-1 = Trailing characters: %S
+error.missingQuote-1 = E114: Missing quote: %S
+error.trailingCharacters = Trailing characters
+error.trailingCharacters-1 = Trailing characters: %S
error.invalid-1 = Invalid %S
error.invalidArgument = Invalid argument
error.invalidArgument-1 = Invalid argument: %S
error.invalidOperation = Invalid operation
error.monkeyPatchOverlay-1 = Not replacing property with eval-generated overlay by %S
error.nullComputedStyle-1 = Computed style is null: %S
+error.syntaxError = Syntax error
+error.charactersOutsideRange-1 = Character list outside the range %S
+error.invalidCharacterRange = Invalid character range: %S
+error.notWriteable-2 = Could not write to %S: %S
+warn.deprecated-2 = %S is deprecated: Please use %S instead.
warn.notDefaultBranch-2 = You are running %S from a testing branch: %S. Please do not report errors which do not also occur in the default branch.
# vim:se ft=jproperties tw=0:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>
-<!DOCTYPE document SYSTEM "dactyl://content/dtd">
+<!DOCTYPE document SYSTEM "dactyl://content/options.dtd">
<document
name="options"
the form of <tt><e-<a>name</a>></tt>, its value is never shown but may be
used to test whether the given parameter is empty.
</p>
+<p>
+ Array elements, such as in the <tt><args></tt> parameter
+ of <ex>:command</ex> macros, may be accessed by appending
+ <tt>[<a>n</a>]</tt>, where <a>n</a> is the one-based array
+ index, to the macro name. The first argument of a command is
+ therefore accessed with <tt><args[1]></tt>.
+</p>
<p>
Any substring enclosed by <em><tt><{</tt></em> and <em><tt>}></tt></em>
is automatically elided if any of the contained macros aren't currently
</item>
<item>
+ <tags>:set!=</tags>
<spec>:se<oa>t</oa> inv<a>option</a>=<a>value</a> <oa>...</oa></spec>
<spec>:se<oa>t</oa> <a>option</a>!=<a>value</a> <oa>...</oa></spec>
<description>
</item>
<item>
+ <tags>:set-default-all</tags>
<spec>:se<oa>t</oa> all&</spec>
<description>
<p>Set all options to their default value.</p>
<item>
<tags>:set! :set-!</tags>
- <spec>:se<oa>t</oa>! <a>preference</a>=<a>value</a></spec>
- <spec>:se<oa>t</oa>! <a>preference</a>&</spec>
+ <spec>:se<oa>t</oa>! …</spec>
<description>
<p>
- Change any &dactyl.host; <a>preference</a> (those on the about:config
- page). You can also reset/delete these preferences with
- <ex>:set! <a>preference</a>&</ex>.
+ The same as <ex>:set</ex> but operates on &dactyl.host; preferences
+ (those on the about:config page). See <ex>:set</ex> for operator
+ details.
</p>
+ <note>
+ The <em>no</em> and <em>inv</em> prefix operators are not available
+ for setting preferences.
+ </note>
</description>
</item>
<tags>'act' 'activate'</tags>
<strut/>
<spec>'activate' 'act'</spec>
- <type>stringlist</type>
+ <type>&option.activate.type;</type>
<default>addons,bookmarks,diverted,downloads,extoptions,
help,homepage,quickmark,tabopen,paste</default>
<description>
<item>
<tags>'awim' 'altwildmode'</tags>
<spec>'altwildmode' 'awim'</spec>
- <type>stringlist</type>
- <default>list:full</default>
+ <type>&option.altwildmode.type;</type>
+ <default>&option.altwildmode.default;</default>
<description>
<p>
Like <o>wildmode</o>, but when the <k name="A-Tab" mode="c"/> key
<item>
<tags>'au' 'autocomplete'</tags>
<spec>'autocomplete' 'au'</spec>
- <type>regexplist</type>
- <default>.*</default>
+ <type>&option.autocomplete.type;</type>
+ <default>&option.autocomplete.default;</default>
<description>
<p>
Enables automatic completion for completion contexts (see
<item>
<tags>'bh' 'banghist'</tags>
<spec>'banghist' 'bh'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.banghist.type;</type>
+ <default>&option.banghist.default;</default>
<description>
<p>
Replace occurrences of ! with the previous command when
<tags>$CDPATH</tags>
<tags>'cd' 'cdpath'</tags>
<spec>'cdpath' 'cd'</spec>
- <type>stringlist</type>
+ <type>&option.cdpath.type;</type>
<default type="plain">equivalent to <str>.</str> or <str>.,$CDPATH</str></default>
<description>
<p>
<item>
<tags>'ca' 'cookieaccept'</tags>
<spec>'cookieaccept' 'ca'</spec>
- <type>string</type>
- <default>all</default>
+ <type>&option.cookieaccept.type;</type>
+ <default>&option.cookieaccept.default;</default>
<description>
<p>When to accept cookies.</p>
<item>
<tags>'cl' 'cookielifetime'</tags>
<spec>'cookielifetime'</spec>
- <type>string</type>
- <default>default</default>
+ <type>&option.cookielifetime.type;</type>
+ <default>&option.cookielifetime.default;</default>
<description>
<p>
The lifetime for which to accept cookies. The available
<item>
<tags>'ck' 'cookies'</tags>
<spec>'cookies' 'ck'</spec>
- <type>stringlist</type> <default>session</default>
+ <type>&option.cookies.type;</type>
+ <default>&option.cookies.default;</default>
<description>
<p>The default action for the <ex>:cookies</ex> command.</p>
</description>
<item>
<tags>'cpt' 'complete'</tags>
<spec>'complete' 'cpt'</spec>
- <type>charlist</type>
- <default>slf</default>
+ <type>&option.complete.type;</type>
+ <default>&option.complete.default;</default>
<description>
<p>Items which are completed at the <ex>:open</ex> prompts. Available items:</p>
</description>
</item>
+<item>
+ <tags>'dls' 'dlsort' 'downloadsort'</tags>
+ <spec>'downloadsort'</spec>
+ <type>stringlist</type>
+ <default>-active,+filename</default>
+ <description>
+ <p>
+ <ex>:downloads</ex> sort order, in order of precedence.
+ Each element must be preceded by a <tt>+</tt> or
+ <tt>-</tt>, indicating ascending or descending sorting,
+ respectively. Valid sort orders are:
+ </p>
+
+ <dl>
+ <dt>active</dt> <dd>Whether download is active</dd>
+ <dt>complete</dt> <dd>Percent complete</dd>
+ <dt>date</dt> <dd>Date and time the download began</dd>
+ <dt>filename</dt> <dd>Target filename</dd>
+ <dt>size</dt> <dd>File size</dd>
+ <dt>speed</dt> <dd>Download speed</dd>
+ <dt>time</dt> <dd>Time remaining</dd>
+ <dt>url</dt> <dd>Source URL</dd>
+ </dl>
+ </description>
+</item>
+
+
<item>
<tags>'ds' 'defsearch'</tags>
<spec>'defsearch' 'ds'</spec>
- <type>string</type>
- <default>google</default>
+ <type>&option.defsearch.type;</type>
+ <default>&option.defsearch.default;</default>
<description>
<p>
Sets the default search engine. The default search engine is
<item>
<tags>'editor'</tags>
<spec>'editor'</spec>
- <type>string</type>
- <default><![CDATA[gvim -f +<line> <file>]]></default>
+ <type>&option.editor.type;</type>
+ <default>&option.editor.default;</default>
<description>
<p>
Set the external text editor.
<item>
<tags>'enc' 'encoding'</tags>
<spec>'encoding' 'enc'</spec>
- <type>string</type>
- <default>UTF-8</default>
+ <type>&option.encoding.type;</type>
+ <default>&option.encoding.default;</default>
<description>
<p>
Changes the character encoding of the current buffer. Valid only
<tags>'noeb' 'noerrorbells'</tags>
<tags>'eb' 'errorbells'</tags>
<spec>'errorbells' 'eb'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.errorbells.type;</type>
+ <default>&option.errorbells.default;</default>
<description>
<p>
Ring the bell when an error message is displayed. See also
<item>
<tags>'ei' 'eventignore'</tags>
<spec>'eventignore' 'ei'</spec>
- <type>stringlist</type>
- <default></default>
+ <type>&option.eventignore.type;</type>
+ <default>&option.eventignore.default;</default>
<description>
<p>
A list of autocommand event names which should be ignored. If the
<tags>'noex' 'noexrc'</tags>
<tags>'ex' 'exrc'</tags>
<spec>'exrc' 'ex'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.exrc.type;</type>
+ <default>&option.exrc.default;</default>
<description>
<p>
Allow reading of an RC file in the current directory. This file is
<tags>'eht' 'extendedhinttags'</tags>
<spec>'extendedhinttags' 'eht'</spec>
<strut/>
- <type>regexpmap</type>
- <default>[asOTivVWy]:a[href],area[href],img[src],iframe[src],
+ <type>&option.extendedhinttags.type;</type>
+ <default>[asOTvVWy]:a[href],area[href],img[src],iframe[src],
[f]:body,
[F]:body,code,div,html,p,pre,span,
[iI]:img,
<item>
<tags>'fenc' 'fileencoding'</tags>
<spec>'fileencoding' 'fenc'</spec>
- <type>string</type>
- <default>UTF-8</default>
+ <type>&option.fileencoding.type;</type>
+ <default>&option.fileencoding.default;</default>
<description>
<p>
Changes the character encoding that &dactyl.appName; uses to read
<item>
<tags>'fc' 'findcase'</tags>
<spec>'findcase' 'fc'</spec>
- <type>string</type>
- <default>smart</default>
+ <type>&option.findcase.type;</type>
+ <default>&option.findcase.default;</default>
<description>
<p>Find case matching mode.</p>
<item>
<tags>'fh' 'followhints'</tags>
<spec>'followhints' 'fh'</spec>
- <type>number</type>
- <default>0</default>
+ <type>&option.followhints.type;</type>
+ <default>&option.followhints.default;</default>
<description>
- <p>Changes how soon matching hints are followed in Hints mode.</p>
+ <p>
+ Define the conditions under which hints selected by typing the link
+ substring are followed. Hints selected by typing their label (as
+ specified by <o>hintkeys</o>) are always followed immediately.
+ </p>
<p>Possible values:</p>
<dl dt="width: 6em;">
<dt>0</dt> <dd>Follow the first hint as soon as typed text uniquely identifies it.</dd>
<dt>1</dt> <dd>Follow the selected hint on <k name="CR"/>.</dd>
- <dt>2</dt> <dd>Follow the selected hint on <k name="CR"/> only if it's been <k name="Tab" mode="c"/>-selected.</dd>
</dl>
</description>
</item>
<tags>'nofs' 'nofullscreen'</tags>
<tags>'fs' 'fullscreen'</tags>
<spec>'fullscreen' 'fs'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.fullscreen.type;</type>
+ <default>&option.fullscreen.default;</default>
<description>
<p>
Show the current window full-screen. Also hide certain GUI elements, such as
<item>
<tags>'go' 'guioptions'</tags>
<spec>'guioptions' 'go'</spec>
- <type>charlist</type>
- <default>bCrs</default> <!-- TODO: make this config specific -->
+ <type>&option.guioptions.type;</type>
+ <default>&option.guioptions.default;</default>
<description>
<p>Show or hide certain GUI elements.</p>
<item>
<tags>'hf' 'helpfile'</tags>
<spec>'helpfile' 'hf'</spec>
- <type>string</type>
- <default>intro</default>
+ <type>&option.helpfile.type;</type>
+ <default>&option.helpfile.default;</default>
<description>
<p>
Name of the main help file. This is that page shown if the
<item>
<tags>'hin' 'hintinputs'</tags>
<spec>'hintinputs' 'hin'</spec>
- <type>stringlist</type>
- <default>label,value</default>
+ <type>&option.hintinputs.type;</type>
+ <default>&option.hintinputs.default;</default>
<description>
<p>
When generating hints for input elements that do not have an
<item>
<tags>'hk' 'hintkeys'</tags>
<spec>'hintkeys' 'hk'</spec>
- <type>string</type>
- <default>0123456789</default>
+ <type>&option.hintkeys.type;</type>
+ <default>&option.hintkeys.default;</default>
<description>
<p>
The keys used to label and select hints. With its default value,
<item>
<tags>'hm' 'hintmatching'</tags>
<spec>'hintmatching' 'hm'</spec>
- <type>stringlist</type>
- <default>contains</default>
+ <type>&option.hintmatching.type;</type>
+ <default>&option.hintmatching.default;</default>
<description>
<p>Change the hint matching algorithm used in Hints mode.</p>
<tags>'ht' 'hinttags'</tags>
<strut/>
<spec>'hinttags' 'ht'</spec>
- <type>stringlist</type>
+ <type>&option.hinttags.type;</type>
<default>a,area,button,iframe,input:not([type=hidden]),select,textarea,
[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand],
[tabindex],[role=link],[role=button]</default>
<item>
<tags>'hto' 'hinttimeout'</tags>
<spec>'hinttimeout' 'hto'</spec>
- <type>number</type>
- <default>0</default>
+ <type>&option.hinttimeout.type;</type>
+ <default>&option.hinttimeout.default;</default>
<description>
<p>
Timeout in milliseconds before automatically following a non-unique
<item>
<tags>'hi' 'history'</tags>
<spec>'history' 'hi'</spec>
- <type>number</type>
- <default>500</default>
+ <type>&option.history.type;</type>
+ <default>&option.history.default;</default>
<description>
<p>
Maximum number of Ex commands and find patterns to store in the
<tags>'nohlf' 'nohlfind'</tags>
<tags>'hlf' 'hlfind'</tags>
<spec>'hlfind' 'hlf'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.hlfind.type;</type>
+ <default>&option.hlfind.default;</default>
<description>
<p>Highlight previous find pattern matches.</p>
</description>
<tags>'noif' 'noincfind'</tags>
<tags>'if' 'incfind'</tags>
<spec>'incfind' 'if'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.incfind.type;</type>
+ <default>&option.incfind.default;</default>
<description>
<p>Show the first match for a find pattern as it is typed.</p>
</description>
<tags>'noim' 'noinsertmode'</tags>
<tags>'im' 'insertmode'</tags>
<spec>'insertmode' 'im'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.insertmode.type;</type>
+ <default>&option.insertmode.default;</default>
<description>
<p>
Use Insert mode as the default for text areas. This is useful if you
</p>
<p>
- TextEdit mode can be entered with <k name="C-t" mode="I"/> from Insert mode.
+ Text Edit mode can be entered with <k name="C-t" mode="I"/> from Insert mode.
</p>
</description>
</item>
<tags>'nojsd' 'nojsdebugger'</tags>
<tags>'jsd' 'jsdebugger'</tags>
<spec>'jsdebugger' 'jsd'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.jsdebugger.type;</type>
+ <default>&option.jsdebugger.default;</default>
<description>
<p>
Use the JavaScript debugger service for JavaScript completion.
</item>
<item>
- <tags>'nolpl' 'noloadplugins'</tags>
+ <tags>'jt' 'jumptags'</tags>
+ <spec>'jumptags'</spec>
+ <type>&option.jumptags.type;</type>
+ <default>&option.jumptags.default;</default>
+ <description>
+ <p>XPath or CSS selector strings of jumpable elements for extended hint modes.</p>
+ </description>
+</item>
+
+<item>
<tags>'lpl' 'loadplugins'</tags>
<spec>'loadplugins' 'lpl'</spec>
- <type>regexplist</type>
- <default>'\.(js|&dactyl.fileExt;)$'</default>
+ <type>&option.loadplugins.type;</type>
+ <default>&option.loadplugins.default;</default>
<description>
<p>
A regular expression list that defines which plugins are loaded at
<item>
<tags>'ml' 'mapleader'</tags>
<spec>'mapleader' 'ml'</spec>
- <type>string</type>
- <default>\</default>
+ <type>&option.mapleader.type;</type>
+ <default>&option.mapleader.default;</default>
<description>
<p>Defines the replacement keys for the <k name="Leader"/> pseudo-key.</p>
</description>
<item>
<tags>'maxitems'</tags>
<spec>'maxitems'</spec>
- <type>number</type>
- <default>20</default>
+ <type>&option.maxitems.type;</type>
+ <default>&option.maxitems.default;</default>
<description>
<p>Maximum number of items to display at once in a listing.</p>
</description>
<item>
<tags>'msgs' 'messages'</tags>
<spec>'messages' 'msgs'</spec>
- <type>number</type>
- <default>100</default>
+ <type>&option.messages.type;</type>
+ <default>&option.messages.default;</default>
<description>
<p>Maximum number of messages to store in the message history.</p>
</description>
<item>
<tags>'nomore' 'more'</tags>
<spec>'more'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.more.type;</type>
+ <default>&option.more.default;</default>
<description>
<p>
Pause the message list window when more than one screen of
<item>
<tags>'newtab'</tags>
<spec>'newtab'</spec>
- <type>stringlist</type>
- <default></default>
+ <type>&option.newtab.type;</type>
+ <default>&option.newtab.default;</default>
<description>
<p>
Defines which Ex commands open pages in new tabs rather than the
<tags>'nextpattern'</tags>
<strut/>
<spec>'nextpattern'</spec>
- <type>stringlist</type>
- <default>'\bnext',^>$,'^(>>|»)$','^(>|»)','(>|»)$','\bmore\b'</default>
+ <type>&option.nextpattern.type;</type>
+ <default>&option.nextpattern.default;</default>
<description>
<p>
Patterns to use when guessing the next page in a document
<item>
<tags>'noonline' 'online'</tags>
<spec>'online'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.online.type;</type>
+ <default>&option.online.default;</default>
<description>
<p>
Enables or disables ‘offline’ mode, where network access is
<item>
<tags>'pa' 'pageinfo'</tags>
<spec>'pageinfo' 'pa'</spec>
- <type>charlist</type>
- <default>gfm</default>
+ <type>&option.pageinfo.type;</type>
+ <default>&option.pageinfo.default;</default>
<description>
<p>Info shown in the <ex>:pageinfo</ex> output.</p>
<dl dt="width: 6em;">
<dt>g</dt> <dd>General info</dd>
+ <dt>e</dt> <dd>Search Engines</dd>
<dt>f</dt> <dd>Feeds</dd>
<dt>m</dt> <dd>Meta tags</dd>
+ <dt>s</dt> <dd>Security information</dd>
</dl>
<p>
<item>
<tags>'pk' 'passkeys'</tags>
<spec>'passkeys' 'pk'</spec>
- <type>sitemap</type>
- <default/>
+ <type>&option.passkeys.type;</type>
+ <default>&option.passkeys.default;</default>
<description>
<p>
Pass certain keys through directly for the given URLs.
</description>
</item>
+<item>
+ <tags>'pu' 'passunknown'</tags>
+ <spec>'passunknown' 'pu'</spec>
+ <type>&option.showmode.type;</type>
+ <default>&option.showmode.default;</default>
+ <description>
+ <p>
+ Pass unknown keys through to &dactyl.host; in these
+ <t>modes</t>. The first element matching a currently
+ active mode is the one that takes effect. Modes may be
+ negated by prefixing them with a <tt>!</tt>.
+ </p>
+ </description>
+</item>
+
<item>
<tags>'pps' 'popups'</tags>
<spec>'popups' 'pps'</spec>
- <type>stringlist</type>
- <default>tab</default>
+ <type>&option.popups.type;</type>
+ <default>&option.popups.default;</default>
<description>
<p>
Defines where to show requested pop-up windows. Applies only to
<tags>'previouspattern'</tags>
<strut/>
<spec>'previouspattern'</spec>
- <type>stringlist</type>
- <default><![CDATA['\bprev|previous\b',^<$,'^(<<|«)$','^(<|«)','(<|«)$']]></default>
+ <type>&option.previouspattern.type;</type>
+ <default>&option.previouspattern.default;</default>
<description>
<p>
Patterns to use when guessing the previous page in a document
<item>
<tags>'noprivate' 'private'</tags>
<spec>'private'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.private.type;</type>
+ <default>&option.private.default;</default>
<description>
<p>
Set the <str>private browsing</str> option. In private browsing mode
<tags>$&dactyl.idName;_RUNTIME</tags>
<tags>'rtp' 'runtimepath'</tags>
<spec>'runtimepath' 'rtp'</spec>
- <type>stringlist</type>
+ <type>&option.runtimepath.type;</type>
<default type="plain"><str>$&dactyl.idName;_RUNTIME</str> or
Unix, Mac: <str>~/.&dactyl.name;</str>
Windows: <str>~/&dactyl.name;</str></default>
<tags>'si' 'sanitizeitems'</tags>
<spec>'sanitizeitems' 'si'</spec>
<strut/>
- <type>stringlist</type>
- <default>all</default>
+ <type>&option.sanitizeitems.type;</type>
+ <default>&option.sanitizeitems.default;</default>
<description>
<p>
The default list of private items to sanitize. See
<item>
<tags>'ss' 'sanitizeshutdown'</tags>
<spec>'sanitizeshutdown' 'ss'</spec>
- <type>stringlist</type>
- <default/>
+ <type>&option.sanitizeshutdown.type;</type>
+ <default>&option.sanitizeshutdown.default;</default>
<description>
<p>The items to sanitize automatically at shutdown.</p>
</description>
<tags>'sts' 'sanitizetimespan'</tags>
<spec>'sanitizetimespan' 'sts'</spec>
<strut/>
- <type>number</type>
- <default>all</default>
+ <type>&option.sanitizetimespan.type;</type>
+ <default>&option.sanitizetimespan.default;</default>
<description>
<p>
The default sanitizer time span. Only items created within this timespan are
<item>
<tags>'scr' 'scroll'</tags>
<spec>'scroll' 'scr'</spec>
- <type>number</type>
- <default>0</default>
+ <type>&option.scroll.type;</type>
+ <default>&option.scroll.default;</default>
<description>
<p>
Number of lines to scroll with <k name="C-u"/> and <k name="C-d"/>
<item>
<tags>'sh' 'shell'</tags>
<spec>'shell' 'sh'</spec>
- <type>string</type>
+ <type>&option.shell.type;</type>
<default type="plain"><em>$SHELL</em> or <str>sh</str>, Windows: <str>cmd.exe</str></default>
<description>
<p>Shell to use for executing <ex>:!</ex> and <ex>:run</ex> commands.</p>
<tags>'shcf' 'shellcmdflag'</tags>
<spec>'shellcmdflag' 'shcf'</spec>
<strut/>
- <type>string</type>
+ <type>&option.shellcmdflag.type;</type>
<default type="plain"><str>-c</str>, Windows: <str>/c</str></default>
<description>
<p>Flag passed to shell when executing <ex>:!</ex> and <ex>:run</ex> commands.</p>
</item>
<item>
- <tags>'nosmd' 'noshowmode'</tags>
<tags>'smd' 'showmode'</tags>
<spec>'showmode' 'smd'</spec>
- <type>regexplist</type>
- <default>!^normal$</default>
+ <type>&option.showmode.type;</type>
+ <default>&option.showmode.default;</default>
<description>
- <p>Show the current mode in the command line if it matches this expression.</p>
+ <p>
+ Show the current mode in the command line if it or any
+ of its parent <t>modes</t> is included in the list.
+ Modes may be negated by prefixing them with a
+ <tt>!</tt>.
+ </p>
</description>
</item>
<tags>'ssli' 'showstatuslinks'</tags>
<spec>'showstatuslinks' 'ssli'</spec>
<strut/>
- <type>string</type>
- <default>status</default>
+ <type>&option.showstatuslinks.type;</type>
+ <default>&option.showstatuslinks.default;</default>
<description>
<p>
When the mouse hovers over a link, or a link is otherwise focused,
<item>
<tags>'stal' 'showtabline'</tags>
<spec>'showtabline' 'stal'</spec>
- <type>string</type>
- <default>always</default>
+ <type>&option.showtabline.type;</type>
+ <default>&option.showtabline.default;</default>
<description>
<p>Define when the tab bar is visible.</p>
</item>
<item>
- <tags>'nosf' 'nostrictfocus'</tags>
<tags>'sf' 'strictfocus'</tags>
<spec>'strictfocus' 'sf'</spec>
- <type>boolean</type>
- <default>on</default>
+ <type>&option.strictfocus.type;</type>
+ <default>&option.strictfocus.default;</default>
<description>
<p>
Prevent scripts from focusing input elements without user intervention.
</p>
+
+ <p>Possible values:</p>
+
+ <dl>
+ <dt>despotic</dt> <dd>Only allow focus changes when explicitly requested by the user</dd>
+ <dt>laissez-faire</dt> <dd>Always allow focus changes</dd>
+ <dt>moderate</dt> <dd>Allow focus changes after user-initiated focus change</dd>
+ </dl>
</description>
</item>
<item>
<tags>'suggestengines'</tags>
<spec>'suggestengines'</spec>
- <type>stringlist</type>
- <default>google</default>
+ <type>&option.suggestengines.type;</type>
+ <default>&option.suggestengines.default;</default>
<description>
<p>
Set the search engines which can be used for completion
<tags>'notmo' 'notimeout'</tags>
<tags>'tmo' 'timeout'</tags>
<spec>'timeout' 'tmo'</spec>
- <type>boolean</type>
- <default>true</default>
+ <type>&option.timeout.type;</type>
+ <default>&option.timeout.default;</default>
<description>
<p>
When this option is set and a key sequence interpretable both as a
<item>
<tags>'tmol' 'timeoutlen'</tags>
<spec>'timeoutlen' 'tmol'</spec>
- <type>number</type>
- <default>1000</default>
+ <type>&option.timeoutlen.type;</type>
+ <default>&option.timeoutlen.default;</default>
<description>
<p>
Maximum number of milliseconds to wait for a longer key command
<item>
<tags>'titlestring'</tags>
<spec>'titlestring'</spec>
- <type>string</type>
- <default>&dactyl.appName;</default>
+ <type>&option.titlestring.type;</type>
+ <default>&option.titlestring.default;</default>
<description>
<p>
Set the application name shown after the current page title in
<item>
<tags>'us' 'urlsep' 'urlseparator'</tags>
<spec>'urlseparator' 'urlsep' 'us'</spec>
- <type>string</type>
- <default>\|</default>
+ <type>&option.urlseparator.type;</type>
+ <default>&option.urlseparator.default;</default>
<description>
<p>
The regular expression used to split URL lists in commands
<tags>'noum' 'nousermode'</tags>
<tags>'um' 'usermode'</tags>
<spec>'usermode' 'um'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.usermode.type;</type>
+ <default>&option.usermode.default;</default>
<description>
<p>Show current website with minimal styling.</p>
</description>
<item>
<tags>'vbs' 'verbose'</tags>
<spec>'verbose' 'vbs'</spec>
- <type>number</type>
- <default>1</default>
+ <type>&option.verbose.type;</type>
+ <default>&option.verbose.default;</default>
<description>
<p>
Define which info messages are displayed. As the value increases,
<tags>'novb' 'novisualbell'</tags>
<tags>'vb' 'visualbell'</tags>
<spec>'visualbell' 'vb'</spec>
- <type>boolean</type>
- <default>off</default>
+ <type>&option.visualbell.type;</type>
+ <default>&option.visualbell.default;</default>
<description>
<p>
Use visual bell instead of beeping on errors. The visual bell
<tags>'wia' 'wildanchor'</tags>
<strut/>
<spec>'wildanchor' 'wia'</spec>
- <type>regexplist</type>
- <default>!'/ex/(back|buffer|ext|forward|help|undo)'</default>
+ <type>&option.wildanchor.type;</type>
+ <default>&option.wildanchor.default;</default>
<description>
<p>
Regular expression list defining which completion groups show only
<item>
<tags>'wic' 'wildcase'</tags>
<spec>'wildcase' 'wic'</spec>
- <type>regexpmap</type>
- <default>.?:smart</default>
+ <type>&option.wildcase.type;</type>
+ <default>&option.wildcase.default;</default>
<description>
<p>
Defines how completions are matched with regard to character case.
<item>
<tags>'wig' 'wildignore'</tags>
<spec>'wildignore' 'wig'</spec>
- <type>regexplist</type>
- <default></default>
+ <type>&option.wildignore.type;</type>
+ <default>&option.wildignore.default;</default>
<description>
<p>
- List of file patterns to ignore when completing files. For example,
- the following will ignore object files and Vim swap files:
+ List of path name patterns to ignore when completing files and
+ directories. For example, the following will ignore object files
+ and Vim swap files:
</p>
<set opt="wildignore"><str delim="'">\.o$</str>,<str delim="'">^\..*\.s[a-z]{2}$</str></set>
<item>
<tags>'wim' 'wildmode'</tags>
<spec>'wildmode' 'wim'</spec>
- <type>stringlist</type>
- <default>list:full</default>
+ <type>&option.wildmode.type;</type>
+ <default>&option.wildmode.default;</default>
<description>
<p>
Defines how command-line completion works. It is a comma-separated
<item>
<tags>'wis' 'wildsort'</tags>
<spec>'wildsort' 'wis'</spec>
- <type>regexplist</type>
- <default>.*</default>
+ <type>&option.wildsort.type;</type>
+ <default>&option.wildsort.default;</default>
<description>
<p>
A list of regular expressions defining which completion contexts
<tags>'wsp' 'wordseparators'</tags>
<spec>'wordseparators' 'wsp'</spec>
<strut/>
- <type>string</type>
- <default><![CDATA[[.,!?:;\\/"^$%&?()[\]{}<>#*+|=~ _-]]]></default>
+ <type>&option.wordseparators.type;</type>
+ <default>&option.wordseparators.default;</default>
<description>
<p>
A regular expression which defines how words are split for
</p>
<item>
- <tags>/</tags>
- <spec>/<a>pattern</a><oa>/</oa><k name="CR"/></spec>
+ <tags><![CDATA[<find-forward> /]]></tags>
+ <spec>/<a>pattern</a><k name="CR"/></spec>
<description>
<p>Find <a>pattern</a> starting at the current caret position.</p>
</item>
<item>
- <tags>?</tags>
- <spec>?<a>pattern</a><oa>?</oa><k name="CR"/></spec>
+ <tags><![CDATA[<find-forward> ?]]></tags>
+ <spec>?<a>pattern</a><k name="CR"/></spec>
<description>
<p>
Find a pattern backward of the current caret position in exactly the
</item>
<item>
- <tags>n</tags>
+ <tags><![CDATA[<find-next> n]]></tags>
<spec>n</spec>
<description short="true">
<p>Find next. Repeat the last find.</p>
</item>
<item>
- <tags>N</tags>
+ <tags><![CDATA[<find-previous> N]]></tags>
<spec>N</spec>
<description short="true">
<p>Find previous. Repeat the last find in the opposite direction.</p>
</item>
<item>
- <tags>*</tags>
+ <tags><![CDATA[<find-word-next> *]]></tags>
<spec>*</spec>
<description short="true">
<p>Search forward for the next occurrence of the word under cursor.</p>
</item>
<item>
- <tags>#</tags>
+ <tags><![CDATA[<find-word-previous> #]]></tags>
<spec>#</spec>
<description short="true">
<p>Search backward for the previous occurrence of the word under cursor.</p>
sensitive data.
</p>
-<h2 tag="private-mode porn-mode">Private mode browsing</h2>
+<h2 tag="private-mode porn-mode">Private browsing</h2>
<p>
&dactyl.appName; fully supports &dactyl.host;'s private browsing mode.
<note>
The following items are always cleared entirely, regardless of
- <a>timeframe</a>: <em>cache</em>, <em>host</em>, <em>offlineapps</em>,
+ <a>timespan</a>: <em>cache</em>, <em>host</em>, <em>offlineapps</em>,
<em>passwords</em>, <em>sessions</em>, <em>sitesettings</em>.
Conversely, <em>host</em> and <em>options</em> are never cleared
unless a host is specified.
</description>
</item>
+<item>
+ <tags>:delgr :delgroup</tags>
+ <spec>:delgr<oa>oup</oa> <a>group</a></spec>
+ <spec>:delgr<oa>oup</oa>!</spec>
+ <description>
+ <p>
+ Delete the specified <a>group</a>. With <oa>!</oa> delete all
+ user groups.
+ </p>
+ </description>
+</item>
<h2 tag="site-filter site-filters">Site Filters</h2>
</item>
<item>
- <tags>:ru :runtime</tags>
+ <tags>:runt :runtime</tags>
<spec>:runt<oa>ime</oa><oa>!</oa> <a>file</a> …</spec>
<description>
<p>
<p tag="comments">
Lines may be commented out by prefixing them with a <em>"</em>
- character.
+ character. Comments and commands cannot both occur in a single command
+ line.
</p>
<code> <hl style="color: #444">" This is a comment</hl>
- foo bar <hl style="color: #444">" This is a comment</hl>
+ foo bar " This is a syntax error
<str> This is not a comment</str>
foo bar <str> This is not a comment</str>
</code>
</li>
<li>
<p>
- If <o>exrc</o> is set and the +u command-line option was not
+ If <o>exrc</o> is set and the <t>+u</t> command-line option was not
specified, then any RC file in the current directory is also
sourced.
</p>
</p>
<ul>
- <li><o>noloadplugins</o> is set,</li>
+ <li><o>loadplugins</o> is unset,</li>
<li>the <t>++noplugin</t> command-line option was specified, or</li>
<li>the <tt><t>+u</t>=NONE</tt> command-line option was specified.</li>
</ul>
<h2 tag="restarting">Restarting</h2>
+<item>
+ <tags>:reh :rehash</tags>
+ <spec>:reh<oa>ash</oa> <oa>arg</oa> …</spec>
+ <description>
+ <p>
+ Reload the &dactyl.appName; add-on, including all code, plugins,
+ and configuration. For users running directly from the development
+ repository, this is a good way to update to the latest version or
+ to test your changes.
+ </p>
+ <p>
+ Any arguments supplied are parsed as command-line arguments as
+ specified in <t>startup-options</t>.
+ </p>
+ <warning>
+ Not all plugins are designed to cleanly un-apply during a rehash.
+ While official plugins are safe, beware of possible instability
+ if you rehash while running third-party plugins.
+ </warning>
+ </description>
+</item>
+
<item>
<tags>:res :restart</tags>
<spec>:res<oa>tart</oa></spec>
<p>
Load a color scheme. <a>name</a> is found by searching the <o>runtimepath</o> for the
first file matching <tt>colors/<a>name</a>.&dactyl.fileExt;</tt>.
+ The special scheme <em>default</em> can be used to reload the
+ default highlight settings.
</p>
<p>
<p>Valid groups include:</p>
- <dl>
- <dt>Bell</dt> <dd>&dactyl.appName;'s visual bell</dd>
- <dt>Boolean</dt> <dd>A JavaScript Boolean object</dd>
- <dt>CmdLine</dt> <dd>The command line</dd>
- <dt>CmdOutput</dt> <dd>The output of commands executed by <ex>:run</ex></dd>
- <dt>CompDesc</dt> <dd>The description column of the completion list</dd>
- <dt>CompGroup</dt> <dd>The top-level container for a group of completion results</dd>
- <dt>CompIcon</dt> <dd>The favicon of a completion row</dd>
- <dt>CompItem</dt> <dd>A row of completion list</dd>
- <dt>CompItem[selected]</dt><dd>A selected row of completion list</dd>
- <dt>CompLess::after</dt> <dd>The character of indicator shown when completions may be scrolled up</dd>
- <dt>CompLess</dt> <dd>The indicator shown when completions may be scrolled up</dd>
- <dt>CompMore::after</dt> <dd>The character of indicator shown when completions may be scrolled down</dd>
- <dt>CompMore</dt> <dd>The indicator shown when completions may be scrolled down</dd>
- <dt>CompMsg</dt> <dd>The message which may appear at the top of a group of completion results</dd>
- <dt>CompResult</dt> <dd>The result column of the completion list</dd>
- <dt>CompTitle</dt> <dd>Completion row titles</dd>
- <dt>CompTitleSep</dt> <dd>The element which separates the completion title from its results</dd>
- <dt>Disabled</dt> <dd>Text indicating disabled status, such as of an extension or style group</dd>
- <dt>Enabled</dt> <dd>Text indicating enabled status, such as of an extension or style group</dd>
- <dt>ErrorMsg</dt> <dd>Error messages</dd>
- <dt>Filter</dt> <dd>The matching text in a completion list</dd>
- <dt>FrameIndicator</dt> <dd>The indicator shown when a new frame is selected</dd>
- <dt>Function</dt> <dd>A JavaScript Function object</dd>
- <dt>Hint</dt> <dd>A hint indicator. See <ex>:help hints</ex></dd>
- <dt>HintActive</dt> <dd>The hint element of link which will be followed by <k name="CR"/></dd>
- <dt>HintElem</dt> <dd>The hintable element</dd>
- <dt>HintImage</dt> <dd>The indicator which floats above hinted images</dd>
- <dt>Indicator</dt> <dd>The <em>#</em> and <em>%</em> in the <ex>:buffers</ex> list</dd>
- <dt>InfoMsg</dt> <dd>Information messages</dd>
- <dt>Key</dt> <dd>Generally a keyword used in syntax highlighting.</dd>
- <dt>Keyword</dt> <dd>A bookmark keyword for a URL</dd>
- <dt>LineNr</dt> <dd>The line number of an error</dd>
- <dt>Message</dt> <dd>A message as displayed in <ex>:messages</ex></dd>
- <dt>ModeMsg</dt> <dd>The mode indicator in the command line</dd>
- <dt>MoreMsg</dt> <dd>The indicator that there is more text to view</dd>
- <dt>NonText</dt> <dd>The <em>~</em> indicators which mark blank lines in the completion list</dd>
- <dt>Normal</dt> <dd>Normal text in the command line</dd>
- <dt>Null</dt> <dd>A JavaScript Null object</dd>
- <dt>Number</dt> <dd>A JavaScript Number object</dd>
- <dt>Object</dt> <dd>A JavaScript Object</dd>
- <dt>Preview</dt> <dd>The completion preview displayed in the &tag.command-line;</dd>
- <dt>Question</dt> <dd>A prompt for a decision</dd>
- <dt>StatusLine</dt> <dd>The status bar</dd>
- <dt>StatusLineNormal</dt> <dd>The status bar for an ordinary web page</dd>
- <dt>StatusLineBroken</dt> <dd>The status bar for a broken web page</dd>
- <dt>StatusLineExtended</dt><dd>The status bar for a secure web page with an Extended Validation (EV) certificate</dd>
- <dt>StatusLineSecure</dt> <dd>The status bar for a secure web page</dd>
- <dt>String</dt> <dd>A JavaScript String object</dd>
- <dt>TabClose</dt> <dd>The close button of a browser tab</dd>
- <dt>TabIcon</dt> <dd>The icon of a browser tab</dd>
- <dt>TabIconNumber</dt> <dd>The number of a browser tab, over its icon</dd>
- <dt>TabNumber</dt> <dd>The number of a browser tab, next to its icon</dd>
- <dt>TabText</dt> <dd>The text of a browser tab</dd>
- <dt>Tag</dt> <dd>A bookmark tag for a URL</dd>
- <dt>Title</dt> <dd>The title of a listing, including <ex>:pageinfo</ex>, <ex>:jumps</ex></dd>
- <dt>URL</dt> <dd>A URL</dd>
- <dt>WarningMsg</dt> <dd>A warning message</dd>
+ <dl dt="width: 12em;">
+ <dt>Addon</dt> <dd>An add-on in the <ex>:addons</ex> manager</dd>
+ <dt>AddonBar</dt> <dd></dd>
+ <dt>AddonButton</dt> <dd></dd>
+ <dt>AddonButtons</dt> <dd></dd>
+ <dt>AddonCell</dt> <dd>A cell in tell <ex>:addons</ex> manager</dd>
+ <dt>AddonDescription</dt> <dd></dd>
+ <dt>AddonHead</dt> <dd>A heading in the <ex>:addons</ex> manager</dd>
+ <dt>AddonName</dt> <dd></dd>
+ <dt>AddonStatus</dt> <dd></dd>
+ <dt>AddonVersion</dt> <dd></dd>
+ <dt>Addons</dt> <dd>The <ex>:addons</ex> manager</dd>
+ <dt>AppmenuButton</dt> <dd>The app-menu button</dd>
+ <dt>Bell</dt> <dd>&dactyl.appName;'s visual bell</dd>
+ <dt>Boolean</dt> <dd>JavaScript booleans</dd>
+ <dt>Button</dt> <dd>A button widget</dd>
+ <dt>Buttons</dt> <dd>A group of buttons</dd>
+ <dt>CmdCmdLine</dt> <dd></dd>
+ <dt>CmdErrorMsg</dt> <dd></dd>
+ <dt>CmdInfoMsg</dt> <dd></dd>
+ <dt>CmdInput</dt> <dd></dd>
+ <dt>CmdLine</dt> <dd>The command line</dd>
+ <dt>CmdModeMsg</dt> <dd></dd>
+ <dt>CmdMoreMsg</dt> <dd></dd>
+ <dt>CmdNormal</dt> <dd></dd>
+ <dt>CmdOutput</dt> <dd>The output of commands executed by <ex>:run</ex></dd>
+ <dt>CmdPrompt</dt> <dd></dd>
+ <dt>CmdQuestion</dt> <dd></dd>
+ <dt>CmdWarningMsg</dt> <dd></dd>
+ <dt>Comment</dt> <dd>JavaScriptor CSS comments</dd>
+ <dt>CompDesc</dt> <dd>The description column of the completion list</dd>
+ <dt>CompGroup</dt> <dd>Item group in completion output</dd>
+ <dt>CompIcon</dt> <dd>The favicon of a completion row</dd>
+ <dt>CompIcon>img</dt> <dd></dd>
+ <dt>CompItem</dt> <dd>A single row of output in the completion list</dd>
+ <dt>CompItem[selected]</dt> <dd>A selected row of completion list</dd>
+ <dt>CompLess::after</dt> <dd>The character of indicator shown when completions may be scrolled up</dd>
+ <dt>CompLess</dt> <dd>The indicator shown when completions may be scrolled up</dd>
+ <dt>CompMore::after</dt> <dd>The character of indicator shown when completions may be scrolled down</dd>
+ <dt>CompMore</dt> <dd>The indicator shown when completions may be scrolled down</dd>
+ <dt>CompMsg</dt> <dd>The message which may appear at the top of a group of completion results</dd>
+ <dt>CompResult</dt> <dd>The result column of the completion list</dd>
+ <dt>CompTitle</dt> <dd>Completion row titles</dd>
+ <dt>CompTitleSep</dt> <dd>The element which separates the completion title from its results</dd>
+ <dt>Dense</dt> <dd>Arbitrary elements which should be packed densely together</dd>
+ <dt>Disabled</dt> <dd>Disabled item indicator text</dd>
+ <dt>Download[active]</dt> <dd>A currently active download</dd>
+ <dt>Download</dt> <dd>A download in the <ex>:downloads</ex> manager</dd>
+ <dt>DownloadButtons</dt> <dd>A button group in the <ex>:downloads</ex> manager</dd>
+ <dt>DownloadCell</dt> <dd>A table cell in the <ex>:downloads</ex> manager</dd>
+ <dt>DownloadHead</dt> <dd>A heading in the <ex>:downloads</ex> manager</dd>
+ <dt>DownloadPercent</dt> <dd>The percentage column for a download</dd>
+ <dt>DownloadProgress</dt> <dd>The progress column for a download</dd>
+ <dt>DownloadProgressHave</dt> <dd>The completed portion of the progress column</dd>
+ <dt>DownloadProgressTotal</dt> <dd>The remaining portion of the progress column</dd>
+ <dt>DownloadSource</dt> <dd>The download source column for a download</dd>
+ <dt>DownloadState</dt> <dd>The download state column for a download</dd>
+ <dt>DownloadTime</dt> <dd>The time remaining column for a download</dd>
+ <dt>DownloadTitle</dt> <dd>The title column for a download</dd>
+ <dt>Downloads</dt> <dd>The <ex>:downloads</ex> manager</dd>
+ <dt>EditorBlink1</dt> <dd>Text fields briefly after successfully running the external editor, alternated with EditorBlink2</dd>
+ <dt>EditorBlink2</dt> <dd>Text fields briefly after successfully running the external editor, alternated with EditorBlink1</dd>
+ <dt>EditorEditing</dt> <dd>Text fields for which an external editor is open</dd>
+ <dt>EditorError</dt> <dd>Text fields briefly after an error has occurred running the external editor</dd>
+ <dt>Enabled</dt> <dd>Enabled item indicator text</dd>
+ <dt>ErrorMsg</dt> <dd>Error messages</dd>
+ <dt>Filter</dt> <dd>The matching text in a completion list</dd>
+ <dt>FontCode</dt> <dd>The font used for code listings</dd>
+ <dt>FontFixed</dt> <dd>The font used for fixed-width text</dd>
+ <dt>FontProportional</dt> <dd>The font used for proportionally spaced text</dd>
+ <dt>Find</dt> <dd>Text find highlighting. Only background and foreground colors apply.</dd>
+ <dt>FrameIndicator</dt> <dd>The styling applied to briefly indicate the active frame</dd>
+ <dt>Function</dt> <dd>JavaScript functions</dd>
+ <dt>Help</dt> <dd>A help page</dd>
+ <dt>HelpArg</dt> <dd>A required command argument indicator</dd>
+ <dt>HelpBody</dt> <dd>The body of a help page</dd>
+ <dt>HelpBorder</dt> <dd>The styling of bordered elements</dd>
+ <dt>HelpCode</dt> <dd>Code listings</dd>
+ <dt>HelpDefault</dt> <dd>The default value of a help item</dd>
+ <dt>HelpDescription</dt> <dd>The description of a help item</dd>
+ <dt>HelpDescription[short]</dt> <dd></dd>
+ <dt>HelpEm</dt> <dd>Emphasized text</dd>
+ <dt>HelpEx</dt> <dd>An Ex command</dd>
+ <dt>HelpExample</dt> <dd>An example</dd>
+ <dt>HelpHead1</dt> <dd>Any help heading</dd>
+ <dt>HelpHead2</dt> <dd>A first-level help heading</dd>
+ <dt>HelpHead3</dt> <dd>A second-level help heading</dd>
+ <dt>HelpHead4</dt> <dd>A third-level help heading</dd>
+ <dt>HelpHead</dt> <dd>A fourth-level help heading</dd>
+ <dt>HelpInclude</dt> <dd>A help page included in the consolidated help listing</dd>
+ <dt>HelpInfo</dt> <dd>Arbitrary information about a help item</dd>
+ <dt>HelpInfoLabel</dt> <dd>The label for a HelpInfo item</dd>
+ <dt>HelpInfoValue</dt> <dd>The details for a HelpInfo item</dd>
+ <dt>HelpItem</dt> <dd>A help item</dd>
+ <dt>HelpKey</dt> <dd>A keyboard key specification</dd>
+ <dt>HelpKeyword</dt> <dd>A keyword</dd>
+ <dt>HelpLink</dt> <dd>A hyperlink</dd>
+ <dt>HelpLink[rel=external]</dt> <dd>A hyperlink to an external resource</dd>
+ <dt>HelpList</dt> <dd>An unordered list</dd>
+ <dt>HelpListItem</dt> <dd>A list item, ordered or unordered</dd>
+ <dt>HelpNews</dt> <dd>A news item</dd>
+ <dt>HelpNewsNew</dt> <dd>A new news item</dd>
+ <dt>HelpNewsOld</dt> <dd>An old news item</dd>
+ <dt>HelpNewsTag</dt> <dd>The version tag for a news item</dd>
+ <dt>HelpNote</dt> <dd>The indicator for a note</dd>
+ <dt>HelpOpt</dt> <dd>An option name</dd>
+ <dt>HelpOptInfo</dt> <dd>Information about the type and default values for an option entry</dd>
+ <dt>HelpOptionalArg</dt> <dd>An optional command argument indicator</dd>
+ <dt>HelpOrderedList1</dt> <dd>A first-level ordered list</dd>
+ <dt>HelpOrderedList2</dt> <dd>A second-level ordered list</dd>
+ <dt>HelpOrderedList3</dt> <dd>A third-level ordered list</dd>
+ <dt>HelpOrderedList4</dt> <dd>A fourth-level ordered list</dd>
+ <dt>HelpOrderedList</dt> <dd>Any ordered list</dd>
+ <dt>HelpParagraph</dt> <dd>An ordinary paragraph</dd>
+ <dt>HelpSpec</dt> <dd>The specification for a help entry</dd>
+ <dt>HelpString</dt> <dd>A quoted string</dd>
+ <dt>HelpTOC</dt> <dd>The Table of Contents for a help page</dd>
+ <dt>HelpTOC>ol</dt> <dd></dd>
+ <dt>HelpTT</dt> <dd>Teletype text</dd>
+ <dt>HelpTab</dt> <dd></dd>
+ <dt>HelpTabColumn</dt> <dd></dd>
+ <dt>HelpTabDescription</dt> <dd>The description column of description tables</dd>
+ <dt>HelpTabRow</dt> <dd>Entire rows in description tables</dd>
+ <dt>HelpTabTitle</dt> <dd>The title column of description tables</dd>
+ <dt>HelpTag</dt> <dd>A help tag</dd>
+ <dt>HelpTags</dt> <dd>A group of help tags</dd>
+ <dt>HelpTopic</dt> <dd>A link to a help topic</dd>
+ <dt>HelpType</dt> <dd>An option type</dd>
+ <dt>HelpWarning</dt> <dd>The indicator for a warning</dd>
+ <dt>HelpXML</dt> <dd>Highlighted XML</dd>
+ <dt>HelpXMLAttribute</dt> <dd></dd>
+ <dt>HelpXMLBlock</dt> <dd></dd>
+ <dt>HelpXMLComment</dt> <dd></dd>
+ <dt>HelpXMLNamespace</dt> <dd></dd>
+ <dt>HelpXMLProcessing</dt> <dd></dd>
+ <dt>HelpXMLString</dt> <dd></dd>
+ <dt>HelpXMLTagEnd</dt> <dd></dd>
+ <dt>HelpXMLTagStart</dt> <dd></dd>
+ <dt>HelpXMLText</dt> <dd></dd>
+ <dt>Hint</dt> <dd></dd>
+ <dt>HintActive</dt> <dd>The hint element of link which will be followed by <k name="CR"/></dd>
+ <dt>HintElem</dt> <dd>The hintable element</dd>
+ <dt>HintImage</dt> <dd>The indicator which floats above hinted images</dd>
+ <dt>Hint[active]</dt> <dd></dd>
+ <dt>Indicator</dt> <dd>The <em>#</em> and <em>%</em> in the <ex>:buffers</ex> list</dd>
+ <dt>InfoMsg</dt> <dd>Information messages</dd>
+ <dt>InlineHelpLink</dt> <dd>A help link shown in the command line or multi-line output area</dd>
+ <dt>Key</dt> <dd>Keywords</dd>
+ <dt>Keyword</dt> <dd>A bookmark keyword for a URL</dd>
+ <dt>LineNr</dt> <dd>The line number of an error</dd>
+ <dt>Link</dt> <dd>A link with additional information shown on hover</dd>
+ <dt>LinkInfo</dt> <dd>Information shown when hovering over a link</dd>
+ <dt>Message</dt> <dd></dd>
+ <dt>Message</dt> <dd>A message as displayed in <ex>:messages</ex></dd>
+ <dt>ModeMsg</dt> <dd>The mode indicator</dd>
+ <dt>MoreMsg</dt> <dd>The indicator that there is more text to view</dd>
+ <dt>NonText</dt> <dd>The <em>~</em> indicators which mark blank lines in the completion list</dd>
+ <dt>Normal</dt> <dd>Normal text</dd>
+ <dt>Null</dt> <dd>JavaScript null values</dd>
+ <dt>Number</dt> <dd>JavaScript numbers</dd>
+ <dt>Object</dt> <dd>JavaScript objects</dd>
+ <dt>Preview</dt> <dd>The completion preview displayed in the &tag.command-line;</dd>
+ <dt>Question</dt> <dd>A prompt for a decision</dd>
+ <dt>REPL-E</dt> <dd>Evaled input in REPL mode</dd>
+ <dt>REPL-P</dt> <dd>Evaled output in REPL mode</dd>
+ <dt>REPL-R</dt> <dd>Prompts in REPL mode</dd>
+ <dt>REPL</dt> <dd>Read-Eval-Print-Loop output</dd>
+ <dt>StatusInfoMsg</dt> <dd>Information messages in the status line</dd>
+ <dt>StatusLine</dt> <dd>The status bar</dd>
+ <dt>StatusLineBroken</dt> <dd>The status bar for a broken web page</dd>
+ <dt>StatusLineExtended</dt> <dd>The status bar for a secure web page with an Extended Validation (EV) certificate</dd>
+ <dt>StatusLineNormal</dt> <dd>The status bar for an ordinary web page</dd>
+ <dt>StatusLineSecure</dt> <dd>The status bar for a secure web page</dd>
+ <dt>StatusModeMsg</dt> <dd>The mode indicator in the status line</dd>
+ <dt>StatusMoreMsg</dt> <dd></dd>
+ <dt>StatusNormal</dt> <dd>Normal text in the status line</dd>
+ <dt>StatusQuestion</dt> <dd>A prompt for a decision in the status line</dd>
+ <dt>StatusWarningMsg</dt> <dd>A warning message in the status line</dd>
+ <dt>String</dt> <dd>String values</dd>
+ <dt>TabClose</dt> <dd>The close button of a browser tab</dd>
+ <dt>TabIcon</dt> <dd>The icon of a browser tab</dd>
+ <dt>TabIconNumber</dt> <dd>The number of a browser tab, over its icon</dd>
+ <dt>TabNumber</dt> <dd>The number of a browser tab, next to its icon</dd>
+ <dt>TabText</dt> <dd>The text of a browser tab</dd>
+ <dt>Tag</dt> <dd>A bookmark tag for a URL</dd>
+ <dt>Title</dt> <dd>The title of a listing, including <ex>:pageinfo</ex>, <ex>:jumps</ex></dd>
+ <dt>URL:hover</dt> <dd></dd>
+ <dt>URL</dt> <dd>A URL</dd>
+ <dt>URLExtra</dt> <dd>Extra information about a URL</dd>
+ <dt>Usage</dt> <dd>Output from the :*usage commands</dd>
+ <dt>UsageBody</dt> <dd>The body of listings in output from the :*usage commands</dd>
+ <dt>UsageHead</dt> <dd>Headings in output from the :*usage commands</dd>
+ <dt>UsageItem</dt> <dd>Individual items in output from the :*usage commands</dd>
+ <dt>WarningMsg</dt> <dd>A warning message</dd>
</dl>
<p>
<item>
<tags>:sty :style</tags>
- <spec>:sty<oa>le</oa> <oa>-name=<a>name</a></oa> <oa>-append</oa> <a>filter</a> <oa>css</oa></spec>
+ <strut/>
+ <spec>:sty<oa>le</oa> <oa>-name=<a>name</a></oa> <oa>-group=<a>name</a></oa> <oa>-agent</oa> <a>filter</a> <a>css</a></spec>
+ <spec>:sty<oa>le</oa> -name=<a>name</a> -append <a>filter</a> <a>css</a></spec>
+ <spec>:sty<oa>le</oa> <oa>-name=<a>name</a></oa> <oa>-group=<a>name</a></oa> <oa>filter</oa></spec>
+ <strut/>
<description>
<p>
Add CSS styles to the browser or to web pages. <a>filter</a> is a
comma-separated list of <t>site-filters</t> for which the style will
- apply. Regular expression filters may not be used and the <tt>!</tt>
- character may not be used to invert the sense of the match.
- <oa>css</oa> is a full CSS rule set (e.g., <tt>body { color: blue; }</tt>).
+ apply. The <tt>!</tt> character may not be used to invert the sense
+ of the match. <oa>css</oa> is a full CSS rule set (e.g., <tt>body {
+ color: blue; }</tt>).
</p>
<p>The following options are available:</p>
<dl>
<dt>-append</dt>
- <dd>If provided along with <em>-name</em>, <oa>css</oa> and
- <a>filter</a> are appended to its current value. (short name <em>-a</em>)</dd>
+ <dd>If provided along with <em>-name</em>, <a>css</a> and
+ <a>filter</a> are appended to its current value. (short name
+ <em>-a</em>)</dd>
<dt>-agent</dt>
<dd>If provided, the style is installed as an Agent sheet, which
</description>
</item>
-<item>
- <tags>:tabdu :tabduplicate</tags>
- <spec>:<oa>count</oa>tabdu<oa>plicate</oa></spec>
- <description>
- <p>
- Duplicate the current tab and focus the duplicate. If
- <oa>count</oa> is given, duplicate the tab <oa>count</oa> times.
- </p>
- </description>
-</item>
-
<!-- TODO: should the tab commands be moved back here? -->
<p>
See <t>opening</t> for other ways to open new tabs.
<item>
<tags>b :b :buffer</tags>
<spec>:<oa>count</oa>b<oa>uffer</oa><oa>!</oa> <oa>url|index</oa></spec>
+ <spec>:<oa>count</oa>b<oa>uffer</oa><oa>!</oa> <a>match</a></spec>
<spec><oa>count</oa>b</spec>
<description>
<p>
<p>
If argument is neither a full URL nor an index but uniquely identifies a
- buffer, it is selected. With <oa>!</oa> the next buffer matching the argument is
- selected, even if it cannot be identified uniquely. Use <k>b</k> as a
+ buffer, by a partial match with the URL or title, it is selected.
+ With <oa>!</oa> the next buffer matching the argument is selected,
+ even if it cannot be identified uniquely. Use <k>b</k> as a
shortcut to open this prompt.
</p>
<item>
<tags>:tabm :tabmove</tags>
- <spec>:tabm<oa>ove</oa> <oa>N</oa></spec>
- <spec>:tabm<oa>ove</oa><oa>!</oa> <oa>+N</oa> | <oa>-N</oa></spec>
+ <spec>:tabm<oa>ove</oa> <a>N</a></spec>
+ <spec>:tabm<oa>ove</oa> <a>match</a></spec>
+ <spec>:tabm<oa>ove</oa><oa>!</oa> <a>+N|-N</a></spec>
<description>
<p>
- Move the current tab to a position after tab <oa>N</oa>. When <oa>N</oa> is 0, the
- current tab is made the first one. Without <oa>N</oa> the current tab is made the
- last one. <oa>N</oa> can also be prefixed with ‘+’ or ‘-’ to indicate a relative
- movement. If <oa>!</oa> is specified the movement wraps around the start or end of the
- tab list.
+ Move the current tab to the position of tab <a>N</a>. When <a>N</a>
+ is <em>$</em>, the current tab is made the last one. <a>N</a> can
+ also be prefixed with <em>+</em> or <em>-</em> to indicate a
+ relative movement. If <oa>!</oa> is specified the movement wraps
+ around the start or end of the tab list.
+ </p>
+ <p>
+ The tab index may also be selected by a general <a>match</a> string
+ like <ex>:buffer</ex>.
</p>
</description>
</item>
</description>
</item>
+<h2 tag="app-tabs application-tabs pinned-tabs">Application Tabs</h2>
+
+<item>
+ <tags>:pin :pintab</tags>
+ <spec>:<oa>count</oa>pin<oa>tab</oa><oa>!</oa> <oa>arg</oa></spec>
+ <description>
+ <p>
+ Pin tab as an application tab. If <oa>!</oa> is given,
+ the tab's pinned state is toggled. Arguments and count
+ are the same as for <ex>:bdelete</ex> and <ex>:buffer</ex>.
+ </p>
+ </description>
+</item>
+
+<item>
+ <tags>:unpin :unpintab</tags>
+ <spec>:<oa>count</oa>unpin<oa>tab</oa> <oa>arg</oa></spec>
+ <description>
+ <p>
+ Unpin tab as an application tab. Arguments and count
+ are the same as for <ex>:pintab</ex>.
+ </p>
+ </description>
+</item>
+
+
</document>
<!-- vim:se sts=4 sw=4 et: -->
</item>
<item>
- <tags><![CDATA[<C-l> CTRL-L :redr :redraw]]></tags>
+ <tags><![CDATA[<redraw-screen> <C-l> CTRL-L :redr :redraw]]></tags>
<strut/>
<spec>:redr<oa>aw</oa></spec>
<description>
<h2 tag="online-help">Online help</h2>
<item>
- <tags><![CDATA[<F1> :help :h help]]></tags>
+ <tags><![CDATA[<open-help> <F1> :help :h help]]></tags>
<spec>:h<oa>elp</oa> <oa>subject</oa></spec>
<spec><F1></spec>
<description>
</item>
<item>
- <tags><![CDATA[<A-F1> :helpall :helpa help-all]]></tags>
+ <tags><![CDATA[<open-single-help> <A-F1> :helpall :helpa help-all]]></tags>
<spec>:helpa<oa>ll</oa> <oa>subject</oa></spec>
<spec><A-F1></spec>
<description>
<p>
Start Caret mode. This mode resembles the Vim's Normal mode where
the text cursor is visible on the web page. The <k link="false">v</k> key
- enters visual mode, where text is selected as the cursor moves.
+ enters Visual mode, where text is selected as the cursor moves.
</p>
</description>
</item>
var listener = function listener(action, event)
function addonListener(install) {
this.dactyl[install.error ? "echoerr" : "echomsg"](
- "Add-on " + action + " " + event + ": " + (install.name || install.sourceURI.spec) +
- (install.error ? ": " + addonErrors[install.error] : ""));
+ _("addon.error", action, event, (install.name || install.sourceURI.spec) +
+ (install.error ? ": " + addons.errors[install.error] : "")));
}
var AddonListener = Class("AddonListener", {
},
onUpdateAvailable: function (addon, install) {
+ util.dump("onUpdateAvailable");
this.upgrade.push(addon);
install.addListener(this);
install.install();
},
onUpdateFinished: function (addon, error) {
- this.remaining = this.remaining.filter(function (a) a != addon);
+ this.remaining = this.remaining.filter(function (a) a.type != addon.type || a.id != addon.id);
if (!this.remaining.length)
this.dactyl.echomsg(
this.upgrade.length
- ? "Installing updates for addons: " + this.upgrade.map(function (i) i.name).join(", ")
- : "No addon updates found");
+ ? _("addon.installingUpdates", this.upgrade.map(function (i) i.name).join(", "))
+ : _("addon.noUpdates"));
}
});
name: "extr[ehash]",
description: "Reload an extension",
action: function (addon) {
- util.assert(util.haveGecko("2b"), _("error.notUseful", config.host));
+ util.assert(util.haveGecko("2b"), _("command.notUseful", config.host));
util.timeout(function () {
addon.userDisabled = true;
addon.userDisabled = false;
});
},
get filter() {
- let ids = set(keys(JSON.parse(prefs.get("extensions.bootstrappedAddons", "{}"))));
- return function ({ item }) !item.userDisabled && set.has(ids, item.id);
+ let ids = Set(keys(JSON.parse(prefs.get("extensions.bootstrappedAddons", "{}"))));
+ return function ({ item }) !item.userDisabled && Set.has(ids, item.id);
},
perm: "disable"
},
<td highlight="AddonVersion" key="version"/>
<td highlight="AddonStatus" key="status"/>
<td highlight="AddonButtons Buttons">
- <a highlight="Button" key="enable">On </a>
- <a highlight="Button" key="disable">Off</a>
- <a highlight="Button" key="delete">Del</a>
- <a highlight="Button" key="update">Upd</a>
- <a highlight="Button" key="options">Opt</a>
+ <a highlight="Button" href="javascript:0" key="enable">{_("addon.action.On")}</a>
+ <a highlight="Button" href="javascript:0" key="disable">{_("addon.action.Off")}</a>
+ <a highlight="Button" href="javascript:0" key="delete">{_("addon.action.Delete")}</a>
+ <a highlight="Button" href="javascript:0" key="update">{_("addon.action.Update")}</a>
+ <a highlight="Button" href="javascript:0" key="options">{_("addon.action.Options")}</a>
</td>
<td highlight="AddonDescription" key="description"/>
</tr>,
},
commandAllowed: function commandAllowed(cmd) {
- util.assert(set.has(actions, cmd), "Unknown command");
+ util.assert(Set.has(actions, cmd), _("addon.unknownCommand"));
let action = actions[cmd];
if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()]))
},
command: function command(cmd) {
- util.assert(this.commandAllowed(cmd), "Command not allowed");
+ util.assert(this.commandAllowed(cmd), _("addon.commandNotAllowed"));
let action = actions[cmd];
if (action.action)
XML.ignoreWhitespace = true;
util.xmlToDom(<table highlight="Addons" key="list" xmlns={XHTML}>
<tr highlight="AddonHead">
- <td>Name</td>
- <td>Version</td>
- <td>Status</td>
+ <td>{_("title.Name")}</td>
+ <td>{_("title.Version")}</td>
+ <td>{_("title.Status")}</td>
<td/>
- <td>Description</td>
+ <td>{_("title.Description")}</td>
</tr>
</table>, this.document, this.nodes);
});
var Addons = Module("addons", {
+ errors: Class.memoize(function ()
+ array(["ERROR_NETWORK_FAILURE", "ERROR_INCORRECT_HASH",
+ "ERROR_CORRUPT_FILE", "ERROR_FILE_ACCESS"])
+ .map(function (e) [AddonManager[e], _("AddonManager." + e)])
+ .toObject())
}, {
}, {
commands: function (dactyl, modules, window) {
else if (file.isDirectory())
dactyl.echoerr(_("addon.cantInstallDir", file.path.quote()));
else
- dactyl.echoerr(_("io.notReadable-1", file.path));
+ dactyl.echoerr(_("io.notReadable", file.path));
}, {
argCount: "1",
completer: function (context) {
function (args) {
let name = args[0];
if (args.bang && !command.bang)
- dactyl.assert(!name, _("error.trailing"));
+ dactyl.assert(!name, _("error.trailingCharacters"));
else
dactyl.assert(name, _("error.argumentRequired"));
- AddonManager.getAddonsByTypes(["extension"], dactyl.wrapCallback(function (list) {
+ AddonManager.getAddonsByTypes(args["-types"], dactyl.wrapCallback(function (list) {
if (!args.bang || command.bang) {
list = list.filter(function (extension) extension.name == name);
if (list.length == 0)
}, {
argCount: "?", // FIXME: should be "1"
bang: true,
- completer: function (context) {
- completion.extension(context);
+ completer: function (context, args) {
+ completion.extension(context, args["-types"]);
context.filters.push(function ({ item }) ok(item));
if (command.filter)
context.filters.push(command.filter);
},
- literal: 0
+ literal: 0,
+ options: [
+ {
+ names: ["-types", "-type", "-t"],
+ description: "The add-on types to operate on",
+ default: ["extension"],
+ completer: function (context, args) completion.addonType(context),
+ type: CommandOption.LIST
+ }
+ ]
});
});
},
true));
});
}
- }
- }
+ };
+ };
completion.extension = function extension(context, types) {
context.title = ["Extension"];
addon = this.wrapAddon(addon);
return callback(addon);
},
+
wrapAddon: function wrapAddon(addon) {
addon = Object.create(addon.QueryInterface(Ci.nsIUpdateItem));
- function getRdfProperty(item, property) {
- let resource = services.rdf.GetResource("urn:mozilla:item:" + item.id);
- let value = "";
-
- if (resource) {
- let target = services.extensionManager.datasource.GetTarget(resource,
- services.rdf.GetResource("http://www.mozilla.org/2004/em-rdf#" + property), true);
- if (target && target instanceof Ci.nsIRDFLiteral)
- value = target.Value;
- }
-
- return value;
- }
-
["aboutURL", "creator", "description", "developers",
"homepageURL", "installDate", "optionsURL",
"releaseNotesURI", "updateDate"].forEach(function (item) {
- memoize(addon, item, function (item) getRdfProperty(this, item));
+ memoize(addon, item, function (item) this.getProperty(item));
});
update(addon, {
appDisabled: false,
+ getProperty: function getProperty(property) {
+ let resource = services.rdf.GetResource("urn:mozilla:item:" + this.id);
+
+ if (resource) {
+ let target = services.extensionManager.datasource.GetTarget(resource,
+ services.rdf.GetResource("http://www.mozilla.org/2004/em-rdf#" + property), true);
+
+ if (target && target instanceof Ci.nsIRDFLiteral)
+ return target.Value;
+ }
+
+ return "";
+ },
+
installLocation: Class.memoize(function () services.extensionManager.getInstallLocation(this.id)),
getResourceURI: function getResourceURI(path) {
let file = this.installLocation.getItemFile(this.id, path);
return services.io.newFileURI(file);
},
- isActive: getRdfProperty(addon, "isDisabled") != "true",
+ get isActive() this.getProperty("isDisabled") != "true",
uninstall: function uninstall() {
services.extensionManager.uninstallItem(this.id);
},
- get userDisabled() getRdfProperty(addon, "userDisabled") === "true",
+ get userDisabled() this.getProperty("userDisabled") === "true",
set userDisabled(val) {
services.extensionManager[val ? "disableItem" : "enableItem"](this.id);
}
return addon;
},
+
getAddonsByTypes: function (types, callback) {
let res = [];
for (let [, type] in Iterator(types))
util.timeout(function () { callback(res); });
return res;
},
+
getInstallForFile: function (file, callback, mimetype) {
callback({
addListener: function () {},
}
});
},
+
getInstallForURL: function (url, callback, mimetype) {
util.assert(false, _("error.unavailable", config.host, services.runtime.version));
},
+
observers: [],
addAddonListener: function (listener) {
observer.listener = listener;
}
};
-var addonErrors = array.toObject([
- [AddonManager.ERROR_NETWORK_FAILURE, "A network error occurred"],
- [AddonManager.ERROR_INCORRECT_HASH, "The downloaded file did not match the expected hash"],
- [AddonManager.ERROR_CORRUPT_FILE, "The file appears to be corrupt"],
- [AddonManager.ERROR_FILE_ACCESS, "There was an error accessing the filesystem"]]);
-
endModule();
} catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
};
if (!Object.defineProperty)
Object.defineProperty = function defineProperty(obj, prop, desc) {
- let value = desc.value;
- if ("value" in desc)
- if (desc.writable && !__lookupGetter__.call(obj, prop)
- && !__lookupSetter__.call(obj, prop))
- try {
- obj[prop] = value;
+ try {
+ let value = desc.value;
+ if ("value" in desc)
+ if (desc.writable && !__lookupGetter__.call(obj, prop)
+ && !__lookupSetter__.call(obj, prop))
+ try {
+ obj[prop] = value;
+ }
+ catch (e if e instanceof TypeError) {}
+ else {
+ objproto.__defineGetter__.call(obj, prop, function () value);
+ if (desc.writable)
+ objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
}
- catch (e if e instanceof TypeError) {}
- else {
- objproto.__defineGetter__.call(obj, prop, function () value);
- if (desc.writable)
- objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
- }
- if ("get" in desc)
- objproto.__defineGetter__.call(obj, prop, desc.get);
- if ("set" in desc)
- objproto.__defineSetter__.call(obj, prop, desc.set);
+ if ("get" in desc)
+ objproto.__defineGetter__.call(obj, prop, desc.get);
+ if ("set" in desc)
+ objproto.__defineSetter__.call(obj, prop, desc.set);
+ }
+ catch (e) {
+ throw e.stack ? e : Error(e);
+ }
};
if (!Object.defineProperties)
Object.defineProperties = function defineProperties(obj, props) {
for (let [k, v] in Iterator(props))
Object.defineProperty(obj, k, v);
- }
+ };
if (!Object.freeze)
Object.freeze = function freeze(obj) {};
+if (!Object.getPropertyDescriptor)
+ Object.getPropertyDescriptor = function getPropertyDescriptor(obj, prop) {
+ try {
+ let desc = {
+ configurable: true,
+ enumerable: propertyIsEnumerable.call(obj, prop)
+ };
+ var get = __lookupGetter__.call(obj, prop),
+ set = __lookupSetter__.call(obj, prop);
+ if (!get && !set) {
+ desc.value = obj[prop];
+ desc.writable = true;
+ }
+ if (get)
+ desc.get = get;
+ if (set)
+ desc.set = set;
+ return desc;
+ }
+ catch (e) {
+ throw e.stack ? e : Error(e);
+ }
+ };
if (!Object.getOwnPropertyDescriptor)
Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
- if (!hasOwnProperty.call(obj, prop))
- return undefined;
- let desc = {
- configurable: true,
- enumerable: propertyIsEnumerable.call(obj, prop)
- };
- var get = __lookupGetter__.call(obj, prop),
- set = __lookupSetter__.call(obj, prop);
- if (!get && !set) {
- desc.value = obj[prop];
- desc.writable = true;
- }
- if (get)
- desc.get = get;
- if (set)
- desc.set = set;
- return desc;
+ if (hasOwnProperty.call(obj, prop))
+ return Object.getPropertyDescriptor(obj, prop);
};
if (!Object.getOwnPropertyNames)
Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
- // This is an ugly and unfortunately necessary hack.
- if (hasOwnProperty.call(obj, "__iterator__")) {
- var oldIter = obj.__iterator__;
- delete obj.__iterator__;
+ try {
+ // This is an ugly and unfortunately necessary hack.
+ if (hasOwnProperty.call(obj, "__iterator__")) {
+ var oldIter = obj.__iterator__;
+ delete obj.__iterator__;
+ }
+ let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
+ if (oldIter !== undefined) {
+ obj.__iterator__ = oldIter;
+ res.push("__iterator__");
+ }
+ return res;
}
- let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
- if (oldIter !== undefined) {
- obj.__iterator__ = oldIter;
- res.push("__iterator__");
+ catch (e) {
+ throw e.stack ? e : Error(e);
}
- return res;
};
if (!Object.getPrototypeOf)
Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
use[mod].push(module);
}
currentModule = module;
+ module.startTime = Date.now();
}
defineModule.loadLog = [];
.replace(/^./gm, name + ": $&"));
}
defineModule.modules = [];
-defineModule.times = { all: 0 };
defineModule.time = function time(major, minor, func, self) {
let time = Date.now();
if (typeof func !== "function")
loaded.util && util.reportError(e);
}
- let delta = Date.now() - time;
- defineModule.times.all += delta;
- defineModule.times[major] = (defineModule.times[major] || 0) + delta;
- if (minor) {
- defineModule.times[":" + minor] = (defineModule.times[":" + minor] || 0) + delta;
- defineModule.times[major + ":" + minor] = (defineModule.times[major + ":" + minor] || 0) + delta;
- }
+ JSMLoader.times.add(major, minor, Date.now() - time);
return res;
}
if (arguments.length === 1)
[obj, name] = [{}, obj];
+ let caller = Components.stack.caller;
+
if (!loaded[name])
- defineModule.loadLog.push((from || "require") + ": loading " + name + (obj.NAME ? " into " + obj.NAME : ""));
+ defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber));
JSMLoader.load(name + ".jsm", obj);
return obj;
if (loaded.util)
util.reportError(e);
else
- defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e +"\n");
+ defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
}
}
// sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
exports: [
"ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable",
- "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper",
+ "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper",
"array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule",
"deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject",
- "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties","keys", "memoize", "octal",
+ "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties", "keys", "memoize", "octal",
"properties", "require", "set", "update", "values", "withCallerGlobal"
],
use: ["config", "services", "util"]
try {
if ("dactylPropertyNames" in obj && !prototypes)
for (let key in values(obj.dactylPropertyNames))
- if (key in obj && !set.add(seen, key))
+ if (key in obj && !Set.add(seen, key))
yield key;
}
catch (e) {}
iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
for (let key in iter)
- if (!prototypes || !set.add(seen, key) && obj != orig)
+ if (!prototypes || !Set.add(seen, key) && obj != orig)
yield key;
}
}
let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
function deprecatedMethod() {
- let obj = this.className ? this.className + "#" :
+ let obj = !this ? "" :
+ this.className ? this.className + "#" :
this.constructor.className ? this.constructor.className + "#" :
"";
}
deprecated.warn = function warn(func, name, alternative, frame) {
if (!func.seenCaller)
- func.seenCaller = set([
+ func.seenCaller = Set([
"resource://dactyl" + JSMLoader.suffix + "/javascript.jsm",
"resource://dactyl" + JSMLoader.suffix + "/util.jsm"
]);
frame = frame || Components.stack.caller.caller;
let filename = util.fixURI(frame.filename || "unknown");
- if (!set.add(func.seenCaller, filename))
- util.dactyl(func).warn(
- util.urlPath(filename) + ":" + frame.lineNumber + ": " +
- name + " is deprecated: Please use " + alternative + " instead");
+ if (!Set.add(func.seenCaller, filename))
+ util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
+ + require("messages")._("warn.deprecated", name, alternative));
}
/**
* @param {[string]} ary @optional
* @returns {object}
*/
-function set(ary) {
+function Set(ary) {
let obj = {};
if (ary)
for (let val in values(ary))
* @param {string} key The key to add.
* @returns boolean
*/
-set.add = curry(function set_add(set, key) {
+Set.add = curry(function set_add(set, key) {
let res = this.has(set, key);
set[key] = true;
return res;
* @param {string} key The key to check.
* @returns {boolean}
*/
-set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
+Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
propertyIsEnumerable.call(set, key));
/**
* Returns a new set containing the members of the first argument which
* @param {object} set The set.
* @returns {object}
*/
-set.subtract = function set_subtract(set) {
+Set.subtract = function set_subtract(set) {
set = update({}, set);
for (let i = 1; i < arguments.length; i++)
for (let k in keys(arguments[i]))
* @param {string} key The key to remove.
* @returns boolean
*/
-set.remove = curry(function set_remove(set, key) {
+Set.remove = curry(function set_remove(set, key) {
let res = set.has(set, key);
delete set[key];
return res;
});
+function set() {
+ deprecated.warn(set, "set", "Set");
+ return Set.apply(this, arguments);
+}
+Object.keys(Set).forEach(function (meth) {
+ set[meth] = function proxy() {
+ deprecated.warn(proxy, "set." + meth, "Set." + meth);
+ return Set[meth].apply(Set, arguments);
+ };
+});
+
/**
* Curries a function to the given number of arguments. Each
* call of the resulting function returns a new function. When
}
if (curry.bind)
- var bind = function bind(func) func.bind.apply(func, Array.slice(arguments, bind.length));
+ var bind = function bind(meth, self) let (func = callable(meth) ? meth : self[meth])
+ func.bind.apply(func, Array.slice(arguments, 1));
else
var bind = function bind(func, self) {
+ if (!callable(func))
+ func = self[func];
+
let args = Array.slice(arguments, bind.length);
return function bound() func.apply(self, args.concat(Array.slice(arguments)));
};
*/
function memoize(obj, key, getter) {
if (arguments.length == 1) {
- obj = update({}, obj);
+ obj = update({ __proto__: obj.__proto__ }, obj);
for (let prop in Object.getOwnPropertyNames(obj)) {
let get = __lookupGetter__.call(obj, prop);
if (get)
return obj;
}
- Object.defineProperty(obj, key, {
- configurable: true,
- enumerable: true,
+ try {
+ Object.defineProperty(obj, key, {
+ configurable: true,
+ enumerable: true,
- get: function g_replaceProperty() (
- Class.replaceProperty(this.instance || this, key, null),
- Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
+ get: function g_replaceProperty() (
+ Class.replaceProperty(this.instance || this, key, null),
+ Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
- set: function s_replaceProperty(val)
- Class.replaceProperty(this.instance || this, key, val)
- });
+ set: function s_replaceProperty(val)
+ Class.replaceProperty(this.instance || this, key, val)
+ });
+ }
+ catch (e) {
+ obj[key] = getter.call(obj, key);
+ }
}
let sandbox = Cu.Sandbox(this);
if (typeof desc.value === "function" && target.__proto__) {
let func = desc.value.wrapped || desc.value;
- func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
- func.superapply = function superapply(self, args)
- let (meth = Object.getPrototypeOf(target)[k])
- meth && meth.apply(self, args);
- func.supercall = function supercall(self)
- func.superapply(self, Array.slice(arguments, 1));
+ if (!func.superapply) {
+ func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
+ func.superapply = function superapply(self, args)
+ let (meth = Object.getPrototypeOf(target)[k])
+ meth && meth.apply(self, args);
+ func.supercall = function supercall(self)
+ func.superapply(self, Array.slice(arguments, 1));
+ }
}
try {
Object.defineProperty(target, k, desc);
if (callable(args[0]))
superclass = args.shift();
- var Constructor = eval(String.replace(<![CDATA[
- (function constructor(PARAMS) {
+ if (loaded.util && util.haveGecko("6.0a1")) // Bug 657418.
+ var Constructor = function Constructor() {
var self = Object.create(Constructor.prototype, {
constructor: { value: Constructor },
});
self.instance = self;
var res = self.init.apply(self, arguments);
return res !== undefined ? res : self;
- })]]>,
- "constructor", (name || superclass.className).replace(/\W/g, "_"))
- .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
- .replace(/\b(self|res|Constructor)\b/g, "$1_")));
+ };
+ else
+ var Constructor = eval(String.replace(<![CDATA[
+ (function constructor(PARAMS) {
+ var self = Object.create(Constructor.prototype, {
+ constructor: { value: Constructor },
+ });
+ self.instance = self;
+ var res = self.init.apply(self, arguments);
+ return res !== undefined ? res : self;
+ })]]>,
+ "constructor", (name || superclass.className).replace(/\W/g, "_"))
+ .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
+ .replace(/\b(self|res|Constructor)\b/g, "$1_")));
Constructor.className = name || superclass.className || superclass.name;
}
if (Cu.getGlobalForObject)
- Class.objectGlobal = function (caller) {
+ Class.objectGlobal = function (object) {
try {
- return Cu.getGlobalForObject(caller);
+ return Cu.getGlobalForObject(object);
}
catch (e) {
return null;
}
};
else
- Class.objectGlobal = function (caller) {
- while (caller.__parent__)
- caller = caller.__parent__;
- return caller;
+ Class.objectGlobal = function (object) {
+ while (object.__parent__)
+ object = object.__parent__;
+ return object;
};
/**
*
* @param {function(string)} getter The function which returns the
* property's value.
- * @return {Class.Property}
+ * @returns {Class.Property}
*/
Class.memoize = function memoize(getter, wait)
Class.Property({
util.trapErrors(callback, self);
}
return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
- }
+ },
+
+ /**
+ * Updates this instance with the properties of the given objects.
+ * Like the update function, but with special semantics for
+ * localized properties.
+ */
+ update: function update() {
+ let self = this;
+ // XXX: Duplication.
+
+ for (let i = 0; i < arguments.length; i++) {
+ let src = arguments[i];
+ Object.getOwnPropertyNames(src || {}).forEach(function (k) {
+ let desc = Object.getOwnPropertyDescriptor(src, k);
+ if (desc.value instanceof Class.Property)
+ desc = desc.value.init(k, this) || desc.value;
+
+ if (typeof desc.value === "function") {
+ let func = desc.value.wrapped || desc.value;
+ if (!func.superapply) {
+ func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]);
+ func.superapply = function superapply(self, args)
+ let (meth = Object.getPrototypeOf(self)[k])
+ meth && meth.apply(self, args);
+ func.supercall = function supercall(self)
+ func.superapply(self, Array.slice(arguments, 1));
+ }
+ }
+
+ try {
+ if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
+ this[k] = desc.value;
+ else
+ Object.defineProperty(this, k, desc);
+ }
+ catch (e) {}
+ }, this);
+ }
+ },
+
+ magicalProperties: {}
};
Class.makeClosure = function makeClosure() {
const self = this;
level: 2,
init: function EB_init(message, level) {
level = level || 0;
- update(this, Error(message))
+ let error = Error(message);
+ update(this, error)
+ this.stack = error.stack;
this.message = message;
let frame = Components.stack;
}
this.fileName = frame.filename;
this.lineNumber = frame.lineNumber;
- }
+ },
+ toString: function () String(this.message)
});
/**
uniq: function uniq(iter) {
let seen = {};
for (let item in iter)
- if (!set.add(seen, item))
+ if (!Set.add(seen, item))
yield item;
},
* as such:
* [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
*
- * @param {Array[]} assoc
+ * @param {[Array]} assoc
* @... {string} 0 - Key
* @... 1 - Value
*/
})
Bookmark.setter = function (key, func) this.prototype.__defineSetter__(key, func);
Bookmark.setter("url", function (val) {
+ if (isString(val))
+ val = util.newURI(val);
let tags = this.tags;
this.tags = null;
services.bookmarks.changeBookmarkURI(this.id, val);
return this.rootFolders.indexOf(root) >= 0;
},
- // Should be made thread safe.
load: function load() {
let bookmarks = {};
try {
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
var EXPORTED_SYMBOLS = ["JSMLoader"];
var BOOTSTRAP_CONTRACT = "@dactyl.googlecode.com/base/bootstrap";
.getService(Components.interfaces.extIApplication)
.storage.get("dactyl.JSMLoader", null);
-if (JSMLoader && JSMLoader.bump === 4)
+if (JSMLoader && JSMLoader.bump === 5)
JSMLoader.global = this;
else
JSMLoader = {
- bump: 4,
- builtin: Components.utils.Sandbox(this),
+ bump: 5,
+
+ builtin: Cu.Sandbox(this),
+
canonical: {},
+
factories: [],
+
global: this,
+
globals: JSMLoader ? JSMLoader.globals : {},
- io: Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService),
- loader: Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader),
- manager: Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar),
+
+ io: Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService),
+
+ loader: Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader),
+
+ manager: Components.manager.QueryInterface(Ci.nsIComponentRegistrar),
+
+ modules: JSMLoader && JSMLoader.modules || {},
+
stale: JSMLoader ? JSMLoader.stale : {},
+
suffix: "",
+
+ times: {
+ all: 0,
+ add: function add(major, minor, delta) {
+ this.all += delta;
+
+ this[major] = (this[major] || 0) + delta;
+ if (minor) {
+ minor = ":" + minor;
+ this[minor] = (this[minor] || 0) + delta;
+ this[major + minor] = (this[major + minor] || 0) + delta;
+ }
+ },
+ clear: function clear() {
+ for (let key in this)
+ if (typeof this[key] !== "number")
+ delete this[key];
+ }
+ },
+
init: function init(suffix) {
this.initialized = true;
this.suffix = suffix || "";
this.global.JSMLoader = this;
base.JSMLoader = this;
},
+
getTarget: function getTarget(url) {
if (url.indexOf(":") === -1)
url = "resource://dactyl" + this.suffix + "/" + url;
let chan = this.io.newChannel(url, null, null);
- chan.cancel(Components.results.NS_BINDING_ABORTED);
+ chan.cancel(Cr.NS_BINDING_ABORTED);
return chan.name;
},
+
load: function load(name, target) {
let url = name;
if (url.indexOf(":") === -1)
}
try {
- let global = Components.utils.import(url, target);
+ let now = Date.now();
+ this.modules[url] = true;
+ let global = Cu.import(url, target);
+
+ if (!(name in this.globals))
+ this.times.add("require", name, Date.now() - now);
+
return this.globals[name] = global;
}
catch (e) {
throw e;
}
},
- loadSubScript: function loadSubScript() this.loader.loadSubScript.apply(this.loader, arguments),
+
+ loadSubScript: function loadSubScript(script) {
+ let now = Date.now();
+ this.loader.loadSubScript.apply(this.loader, arguments);
+ this.times.add("loadSubScript", script, Date.now() - now);
+ },
+
cleanup: function unregister() {
for each (let factory in this.factories.splice(0))
this.manager.unregisterFactory(factory.classID, factory);
},
+
purge: function purge() {
dump("dactyl: JSMLoader: purge\n");
- for (let [url, global] in Iterator(this.globals)) {
- if (url === "bootstrap.jsm" || url === "resource://dactyl/bootstrap.jsm")
- continue;
-
- let target = this.getTarget(url);
- this.stale[url] = target;
- this.stale[target] = target;
-
- for each (let prop in Object.getOwnPropertyNames(global))
+ if (Cu.unload) {
+ Object.keys(this.modules).reverse().forEach(function (url) {
try {
- if (!(prop in this.builtin) &&
- ["JSMLoader", "set", "EXPORTED_SYMBOLS"].indexOf(prop) < 0 &&
- !global.__lookupGetter__(prop))
- global[prop] = undefined;
+ Cu.unload(url);
}
catch (e) {
- dump("Deleting property " + prop + " on " + url + ":\n " + e + "\n");
- Components.utils.reportError(e);
+ Cu.reportError(e);
}
+ });
+ }
+ else {
+ for (let [url, global] in Iterator(this.globals)) {
+ if (url === "bootstrap.jsm" || url === "resource://dactyl/bootstrap.jsm")
+ continue;
+
+ let target = this.getTarget(url);
+ this.stale[url] = target;
+ this.stale[target] = target;
+
+ for each (let prop in Object.getOwnPropertyNames(global))
+ try {
+ if (!(prop in this.builtin) &&
+ ["JSMLoader", "Set", "set", "EXPORTED_SYMBOLS"].indexOf(prop) < 0 &&
+ !global.__lookupGetter__(prop))
+ global[prop] = undefined;
+ }
+ catch (e) {
+ dump("Deleting property " + prop + " on " + url + ":\n " + e + "\n");
+ Cu.reportError(e);
+ }
+ }
}
},
* A class representing Ex commands. Instances are created by
* the {@link Commands} class.
*
- * @param {string[]} specs The names by which this command can be invoked.
+ * @param {[string]} specs The names by which this command can be invoked.
* These are specified in the form "com[mand]" where "com" is a unique
* command name prefix.
* @param {string} description A short one line description of the command.
this.action = action;
if (extraInfo)
- update(this, extraInfo);
+ this.update(extraInfo);
if (this.options)
this.options = this.options.map(CommandOption.fromArray, CommandOption);
for each (let option in this.options)
get helpTag() ":" + this.name,
- get lastCommand() this._lastCommand || commandline.command,
+ get lastCommand() this._lastCommand || this.modules.commandline.command,
set lastCommand(val) { this._lastCommand = val; },
/**
const { dactyl } = this.modules;
let context = args.context;
- if (this.deprecated && !set.add(this.complained, context ? context.file : "[Command Line]")) {
- let loc = contexts.context ? context.file + ":" + context.line + ": " : "";
- dactyl.echoerr(loc + ":" + this.name + " is deprecated" +
- (isString(this.deprecated) ? ": " + this.deprecated : ""));
- }
+ if (this.deprecated)
+ this.warn(context, "deprecated", _("warn.deprecated", ":" + this.name, this.deprecated));
modifiers = modifiers || {};
if (args.count != null && !this.count)
- throw FailedAssertion(_("command.noRange"));
+ throw FailedAssertion(_("command.noCount"));
if (args.bang && !this.bang)
throw FailedAssertion(_("command.noBang"));
complained: Class.memoize(function () ({})),
/**
- * @property {string[]} All of this command's name specs. e.g., "com[mand]"
+ * @property {[string]} All of this command's name specs. e.g., "com[mand]"
*/
specs: null,
- /** @property {string[]} All of this command's short names, e.g., "com" */
+ /** @property {[string]} All of this command's short names, e.g., "com" */
shortNames: null,
/**
- * @property {string[]} All of this command's long names, e.g., "command"
+ * @property {[string]} All of this command's long names, e.g., "command"
*/
longNames: null,
/** @property {string} The command's canonical name. */
name: null,
- /** @property {string[]} All of this command's long and short names. */
+ /** @property {[string]} All of this command's long and short names. */
names: null,
/** @property {string} This command's description, as shown in :listcommands */
description: Messages.Localized(""),
+
+ /** @property {string|null} If set, the deprecation message for this command. */
+ deprecated: Messages.Localized(null),
+
/**
* @property {function (Args)} The function called to execute this command.
*/
explicitOpts: Class.memoize(function () ({})),
- has: function AP_has(opt) set.has(this.explicitOpts, opt) || typeof opt === "number" && set.has(this, opt),
+ has: function AP_has(opt) Set.has(this.explicitOpts, opt) || typeof opt === "number" && Set.has(this, opt),
get literalArg() this.command.literal != null && this[this.command.literal] || "",
util.assert((this.length == 0 || this.command.argCount !== "0") &&
(this.length <= 1 || !/^[01?]$/.test(this.command.argCount)),
- _("error.trailing"));
+ _("error.trailingCharacters"));
}
}
});
* @property {string} For commands defined via :command, contains the Ex
* command line to be executed upon invocation.
*/
- replacementText: null
+ replacementText: null,
+
+ /**
+ * Warns of a misuse of this command once per warning type per file.
+ *
+ * @param {object} context The calling context.
+ * @param {string} type The type of warning.
+ * @param {string} warning The warning message.
+ */
+ warn: function warn(context, type, message) {
+ let loc = !context ? "" : [context.file, context.line, " "].join(":");
+
+ if (!Set.add(this.complained, type + ":" + (context ? context.file : "[Command Line]")))
+ this.modules.dactyl.warn(loc + message);
+ }
}, {
// TODO: do we really need more than longNames as a convenience anyway?
/**
else {
let opt = cmd.optionMap["-" + k];
let val = opt.type && opt.type.parse(v);
+
util.assert(val != null && (typeof val !== "number" || !isNaN(val)),
_("option.noSuch", k));
- Class.replaceProperty(args, opt.names[0], val);
- args.explicitOpts[opt.names[0]] = val;
+
+ Class.replaceProperty(res, opt.names[0], val);
+ res.explicitOpts[opt.names[0]] = val;
}
for (let [i, val] in array.iterItems(args))
res[i] = String(val);
* Adds a new command to the builtin hive. Accessible only to core
* dactyl code. Plugins should use group.commands.add instead.
*
- * @param {string[]} names The names by which this command can be
+ * @param {[string]} specs The names by which this command can be
* invoked. The first name specified is the command's canonical
* name.
* @param {string} description A description of the command.
* @param {Object} extra An optional extra configuration hash.
* @optional
*/
- add: function add(names, description, action, extra, replace) {
+ add: function add(specs, description, action, extra, replace) {
const { commands, contexts } = this.modules;
extra = extra || {};
extra.definedAt = contexts.getCaller(Components.stack.caller);
extra.hive = this;
- extra.parsedSpecs = Command.parseSpecs(names);
+ extra.parsedSpecs = Command.parseSpecs(specs);
let names = array.flatten(extra.parsedSpecs);
let name = names[0];
let self = this;
let closure = function () self._map[name];
- memoize(this._map, name, function () commands.Command(names, description, action, extra));
+ memoize(this._map, name, function () commands.Command(specs, description, action, extra));
memoize(this._list, this._list.length, closure);
for (let alias in values(names.slice(1)))
memoize(this._map, alias, closure);
},
/**
- * Displays a list of user-defined commands.
+ * Lists all user-defined commands matching *filter* and optionally
+ * *hives*.
+ *
+ * @param {string} filter Limits the list to those commands with a name
+ * matching this anchored substring.
+ * @param {[Hive]} hives List of hives.
+ * @optional
*/
- list: function list() {
+ list: function list(filter, hives) {
const { commandline, completion } = this.modules;
function completerToString(completer) {
if (completer)
return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.closure[v])][0] || "custom";
return "";
}
+ // TODO: allow matching of aliases?
+ function cmds(hive) hive._list.filter(function (cmd) cmd.name.indexOf(filter || "") == 0)
+
+ let hives = (hives || this.userHives).map(function (h) [h, cmds(h)]).filter(function ([h, c]) c.length);
+
+ let list = <table>
+ <tr highlight="Title">
+ <td/>
+ <td style="padding-right: 1em;"></td>
+ <td style="padding-right: 1ex;">{_("title.Name")}</td>
+ <td style="padding-right: 1ex;">{_("title.Args")}</td>
+ <td style="padding-right: 1ex;">{_("title.Range")}</td>
+ <td style="padding-right: 1ex;">{_("title.Complete")}</td>
+ <td style="padding-right: 1ex;">{_("title.Definition")}</td>
+ </tr>
+ <col style="min-width: 6em; padding-right: 1em;"/>
+ {
+ template.map(hives, function ([hive, cmds]) let (i = 0)
+ <tr style="height: .5ex;"/> +
+ template.map(cmds, function (cmd)
+ <tr>
+ <td highlight="Title">{!i++ ? hive.name : ""}</td>
+ <td>{cmd.bang ? "!" : " "}</td>
+ <td>{cmd.name}</td>
+ <td>{cmd.argCount}</td>
+ <td>{cmd.count ? "0c" : ""}</td>
+ <td>{completerToString(cmd.completer)}</td>
+ <td>{cmd.replacementText || "function () { ... }"}</td>
+ </tr>) +
+ <tr style="height: .5ex;"/>)
+ }
+ </table>;
- if (!this.userHives.some(function (h) h._list.length))
+ if (list.*.length() === list.text().length() + 2)
dactyl.echomsg(_("command.none"));
else
- commandline.commandOutput(
- <table>
- <tr highlight="Title">
- <td/>
- <td style="padding-right: 1em;"></td>
- <td style="padding-right: 1ex;">Name</td>
- <td style="padding-right: 1ex;">Args</td>
- <td style="padding-right: 1ex;">Range</td>
- <td style="padding-right: 1ex;">Complete</td>
- <td style="padding-right: 1ex;">Definition</td>
- </tr>
- <col style="min-width: 6em; padding-right: 1em;"/>
- {
- template.map(this.userHives, function (hive) let (i = 0)
- <tr style="height: .5ex;"/> +
- template.map(hive, function (cmd)
- template.map(cmd.names, function (name)
- <tr>
- <td highlight="Title">{!i++ ? hive.name : ""}</td>
- <td>{cmd.bang ? "!" : " "}</td>
- <td>{cmd.name}</td>
- <td>{cmd.argCount}</td>
- <td>{cmd.count ? "0c" : ""}</td>
- <td>{completerToString(cmd.completer)}</td>
- <td>{cmd.replacementText || "function () { ... }"}</td>
- </tr>)) +
- <tr style="height: .5ex;"/>)
- }
- </table>);
+ commandline.commandOutput(list);
}
}),
let [count, arg, quote] = Commands.parseArg(str, null, _keepQuotes);
if (quote == "\\" && !complete)
- return [, , , "Trailing \\"];
+ return [, , , _("error.trailingCharacters", "\\")];
if (quote && !complete)
- return [, , , "E114: Missing quote: " + quote];
+ return [, , , _("error.missingQuote", quote)];
return [count, arg, quote];
}
let matchOpts = function matchOpts(arg) {
// Push possible option matches into completions
if (complete && !onlyArgumentsRemaining)
- completeOpts = options.filter(function (opt) opt.multiple || !set.has(args, opt.names[0]));
+ completeOpts = options.filter(function (opt) opt.multiple || !Set.has(args, opt.names[0]));
};
let resetCompletions = function resetCompletions() {
completeOpts = null;
util.assert(!error, error);
// if we add the argument to an option after a space, it MUST not be empty
- if (sep != "=" && !quote && arg.length == 0)
+ if (sep != "=" && !quote && arg.length == 0 && !complete)
arg = null;
count++; // to compensate the "=" character
// dynamically get completions as specified with the command's completer function
context.highlight();
if (!command) {
- context.message = "No such command: " + match.cmd;
+ context.message = _("command.noSuch", match.cmd);
context.highlight(0, match.cmd.length, "SPELLCHECK");
return;
}
}
catch (e) {
util.reportError(e);
+ cmdContext.message = _("error.error", e);
}
};
commands: function initCommands(dactyl, modules, window) {
const { commands, contexts } = modules;
- // TODO: Vim allows commands to be defined without {rep} if there are {attr}s
- // specified - useful?
commands.add(["com[mand]"],
"List or define commands",
function (args) {
util.assert(!cmd || cmd.split(",").every(commands.validName.closure.test),
_("command.invalidName", cmd));
- if (!args.literalArg)
- commands.list();
+ if (args.length <= 1)
+ commands.list(cmd, args.explicitOpts["-group"] ? [args["-group"]] : null);
else {
util.assert(args["-group"].modifiable,
_("group.cantChangeBuiltin", _("command.commands")));
- let completer = args["-complete"];
+ let completer = args["-complete"];
let completerFunc = null; // default to no completion for user commands
if (completer) {
if (/^custom,/.test(completer)) {
completer = completer.substr(7);
- let context = update({}, contexts.context || {});
+ if (contexts.context)
+ var ctxt = update({}, contexts.context || {});
completerFunc = function (context) {
- try {
- var result = contextswithSavedValues(["context"], function () {
- contexts.context = context;
- return dactyl.userEval(completer);
- });
- }
- catch (e) {
- dactyl.echo(":" + this.name + " ...");
- dactyl.echoerr(_("command.unknownCompleter", completer));
- dactyl.log(e);
- return undefined;
- }
+ var result = contexts.withSavedValues(["context"], function () {
+ contexts.context = ctxt;
+ return dactyl.userEval(completer);
+ });
if (callable(result))
return result.apply(this, Array.slice(arguments));
else
},
{
names: ["-literal", "-l"],
- description: "Process the nth ignoring any quoting or meta characters",
+ description: "Process the specified argument ignoring any quoting or meta characters",
type: CommandOption.INT
},
{
]
})),
iterateIndex: function (args) let (tags = services["dactyl:"].HELP_TAGS)
- this.iterate(args).filter(function (cmd) cmd.hive === commands.builtin || set.has(cmd.helpTag)),
+ this.iterate(args).filter(function (cmd) cmd.hive === commands.builtin || Set.has(tags, cmd.helpTag)),
format: {
headings: ["Command", "Group", "Description"],
description: function (cmd) template.linkifyHelp(cmd.description + (cmd.replacementText ? ": " + cmd.action : "")),
dactyl.clipboardWrite(res);
let lines = res.split("\n").length;
- dactyl.echomsg("Yanked " + lines + " line" + (lines == 1 ? "" : "s"));
+ dactyl.echomsg(_("command.yank.yankedLine" + (lines == 1 ? "" : "s"), lines));
},
{
+ argCount: "1",
completer: function (context) modules.completion[/^:/.test(context.filter) ? "ex" : "javascript"](context),
literal: 0
});
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("completion", {
exports: ["CompletionContext", "Completion", "completion"],
- use: ["config", "template", "util"]
+ use: ["config", "messages", "template", "util"]
}, this);
/**
* The message displayed at the head of the completions for the
* current context.
*/
- get message() this._message || (this.waitingForTab && this.hasItems !== false ? "Waiting for <Tab>" : null),
+ get message() this._message || (this.waitingForTab && this.hasItems !== false ? _("completion.waitingFor", "<Tab>") : null),
set message(val) this._message = val,
/**
* The prototype object for items returned by {@link items}.
*/
get itemPrototype() {
- let res = {};
+ let res = { highlight: "" };
function result(quote) {
- yield ["result", quote ? function () quote[0] + quote[1](this.text) + quote[2]
+ yield ["result", quote ? function () quote[0] + util.trapErrors(1, quote, this.text) + quote[2]
: function () this.text];
};
for (let i in iter(this.keys, result(this.quote))) {
}
catch (e) {
util.reportError(e);
- this.message = "Error: " + e;
+ this.message = _("error.error", e);
}
}
// XXX
return this.cache.filtered = filtered;
}
catch (e) {
- this.message = "Error: " + e;
+ this.message = _("error.error", e);
util.reportError(e);
return [];
}
// depending on the 'complete' option
// if the 'complete' argument is passed like "h", it temporarily overrides the complete option
url: function url(context, complete) {
+ if (/^jar:[^!]*$/.test(context.filter)) {
+ context.advance(4);
+
+ context.quote = context.quote || ["", util.identity, ""];
+ let quote = context.quote[1];
+ context.quote[1] = function (str) quote(str.replace(/!/g, escape));
+ }
if (this.options["urlseparator"])
var skip = util.regexp("^.*" + this.options["urlseparator"] + "\\s*")
context.title = ["URL", "Title"];
context.fork("additional", 0, this, function (context) {
- context.title[0] += " (additional)";
+ context.title[0] += " " + _("completion.additional");
context.filter = context.parent.filter; // FIXME
context.completions = context.parent.completions;
// For items whose URL doesn't exactly match the filter,
};
options.add(["altwildmode", "awim"],
- "Define the behavior of the <A-Tab> key in command-line completion",
+ "Define the behavior of the c_<A-Tab> key in command-line completion",
"stringlist", "list:full",
wildmode);
});
options.add(["wildmode", "wim"],
- "Define the behavior of the <Tab> key in command-line completion",
+ "Define the behavior of the c_<Tab> key in command-line completion",
"stringlist", "list:full",
wildmode);
defineModule("config", {
exports: ["ConfigBase", "Config", "config"],
require: ["services", "storage", "util", "template"],
- use: ["io", "prefs"]
+ use: ["io", "messages", "prefs", "styles"]
}, this);
var ConfigBase = Class("ConfigBase", {
* initialization code. Must call superclass's init function.
*/
init: function init() {
- this.features.push = deprecated("set.add", function push(feature) set.add(this, feature));
+ this.features.push = deprecated("Set.add", function push(feature) Set.add(this, feature));
if (util.haveGecko("2b"))
- set.add(this.features, "Gecko2");
+ Set.add(this.features, "Gecko2");
this.timeout(function () {
- services["dactyl:"].pages.dtd = function () [null,
- iter(config.dtdExtra,
- (["dactyl." + k, v] for ([k, v] in iter(config.dtd))),
- (["dactyl." + s, config[s]] for each (s in config.dtdStrings)))
- .map(function ([k, v]) ["<!ENTITY ", k, " '", String.replace(v || "null", /'/g, "'"), "'>"].join(""))
- .join("\n")]
+ services["dactyl:"].pages.dtd = function () [null, util.makeDTD(config.dtd)];
});
},
- loadStyles: function loadStyles() {
+ loadStyles: function loadStyles(force) {
const { highlight } = require("highlight");
+ const { _ } = require("messages");
+
highlight.styleableChrome = this.styleableChrome;
- highlight.loadCSS(this.CSS);
- highlight.loadCSS(this.helpCSS);
+
+ highlight.loadCSS(this.CSS.replace(/__MSG_(.*?)__/g, function (m0, m1) _(m1)));
+ highlight.loadCSS(this.helpCSS.replace(/__MSG_(.*?)__/g, function (m0, m1) _(m1)));
+
if (!util.haveGecko("2b"))
highlight.loadCSS(<![CDATA[
!TabNumber font-weight: bold; margin: 0px; padding-right: .8ex;
text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px;
}
]]>);
+
+ let hl = highlight.set("Find", "");
+ hl.onChange = function () {
+ function hex(val) ("#" + util.regexp.iterate(/\d+/g, val)
+ .map(function (num) ("0" + Number(num).toString(16)).slice(-2))
+ .join("")
+ ).slice(0, 7);
+
+ let elem = services.appShell.hiddenDOMWindow.document.createElement("div");
+ elem.style.cssText = this.cssText;
+ let style = util.computedStyle(elem);
+
+ let keys = iter(Styles.propertyIter(this.cssText)).map(function (p) p.name).toArray();
+ let bg = keys.some(function (k) /^background/.test(k));
+ let fg = keys.indexOf("color") >= 0;
+
+ prefs[bg ? "safeSet" : "safeReset"]("ui.textHighlightBackground", hex(style.backgroundColor));
+ prefs[fg ? "safeSet" : "safeReset"]("ui.textHighlightForeground", hex(style.color));
+ };
},
get addonID() this.name + "@dactyl.googlecode.com",
* @returns {string}
*/
bestLocale: function (list) {
- let langs = set(list);
+ let langs = Set(list);
return values([this.appLocale, this.appLocale.replace(/-.*/, ""),
"en", "en-US", iter(langs).next()])
- .nth(function (l) set.has(langs, l), 0);
+ .nth(function (l) Set.has(langs, l), 0);
},
- haveHg: Class.memoize(function () {
+ /**
+ * @property {string} The pathname of the VCS repository clone's root
+ * directory if the application is running from one via an extension
+ * proxy file.
+ */
+ VCSPath: Class.memoize(function () {
if (/pre$/.test(this.addon.version)) {
- let uri = this.addon.getResourceURI("../.hg");
+ let uri = util.newURI(this.addon.getResourceURI("").spec + "../.hg");
if (uri instanceof Ci.nsIFileURL &&
- uri.QueryInterface(Ci.nsIFileURL).file.exists() &&
+ uri.file.exists() &&
io.pathSearch("hg"))
- return ["hg", "-R", uri.file.parent.path];
+ return uri.file.parent.path;
}
return null;
}),
+ /**
+ * @property {string} The name of the VCS branch that the application is
+ * running from if using an extension proxy file or was built from if
+ * installed as an XPI.
+ */
branch: Class.memoize(function () {
- if (this.haveHg)
- return io.system(this.haveHg.concat(["branch"])).output;
+ if (this.VCSPath)
+ return io.system(["hg", "-R", this.VCSPath, "branch"]).output;
return (/pre-hg\d+-(\S*)/.exec(this.version) || [])[1];
}),
/** @property {string} The Dactyl version string. */
version: Class.memoize(function () {
- if (/pre$/.test(this.addon.version)) {
- let uri = this.addon.getResourceURI("../.hg");
- if (uri instanceof Ci.nsIFileURL &&
- uri.QueryInterface(Ci.nsIFileURL).file.exists() &&
- io.pathSearch("hg")) {
- return io.system(["hg", "-R", uri.file.parent.path,
- "log", "-r.",
- "--template=hg{rev}-" + this.branch + " ({date|isodate})"]).output;
- }
- }
+ if (this.VCSPath)
+ return io.system(["hg", "-R", this.VCSPath, "log", "-r.",
+ "--template=hg{rev}-" + this.branch + " ({date|isodate})"]).output;
let version = this.addon.version;
if ("@DATE@" !== "@" + "DATE@")
- version += " (created: @DATE@)";
+ version += " " + _("dactyl.created", "@DATE@");
return version;
}),
- get fileExt() this.name.slice(0, -5),
+ get fileExt() this.name.slice(0, -6),
- dtd: memoize({
+ dtd: Class.memoize(function ()
+ iter(this.dtdExtra,
+ (["dactyl." + k, v] for ([k, v] in iter(config.dtdDactyl))),
+ (["dactyl." + s, config[s]] for each (s in config.dtdStrings)))
+ .toObject()),
+
+ dtdDactyl: memoize({
get name() config.name,
get home() "http://dactyl.sourceforge.net/",
get apphome() this.home + this.name,
"xmlns.html": "http://www.w3.org/1999/xhtml",
"xmlns.xul": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
- "tag.command-line": '<link topic="command-line">command line</link>',
- "tag.status-line": '<link topic="status-line">status line</link>',
+ "tag.command-line": <link topic="command-line">command line</link>,
+ "tag.status-line": <link topic="status-line">status line</link>,
+ "mode.command-line": <link topic="command-line-mode">Command Line</link>,
},
dtdStrings: [
"version"
],
- helpStyles: /^(Help|StatusLine|REPL)|^(Boolean|Indicator|MoreMsg|Number|Object|Logo|Key(word)?|String)$/,
+ helpStyles: /^(Help|StatusLine|REPL)|^(Boolean|Dense|Indicator|MoreMsg|Number|Object|Logo|Key(word)?|String)$/,
styleHelp: function styleHelp() {
if (!this.helpStyled) {
const { highlight } = require("highlight");
</e4x>;
for each (let [id, [name, key, uri]] in Iterator(this.sidebars)) {
append.XUL::menupopup[0].* +=
- <menuitem observes={"pentadactyl-" + id + "Sidebar"} label={name} accesskey={key} xmlns={XUL}/>
+ <menuitem observes={"pentadactyl-" + id + "Sidebar"} label={name} accesskey={key} xmlns={XUL}/>;
append.XUL::broadcasterset[0].* +=
<broadcaster id={"pentadactyl-" + id + "Sidebar"}
autoCheck="false" type="checkbox" group="sidebar"
sidebartitle={name} sidebarurl={uri}
- oncommand="toggleSidebar(this.id || this.observes);" xmlns={XUL}/>
+ oncommand="toggleSidebar(this.id || this.observes);" xmlns={XUL}/>;
}
util.overlayWindow(window, { append: append.elements() });
/**
* @property {Object} A map of dialogs available via the
* :dialog command. Property names map dialog names to an array
- * as follows:
+ * with the following elements:
* [0] description - A description of the dialog, used in
* command completion results for :dialog.
* [1] action - The function executed by :dialog.
+ * [2] test - Function which returns true if the dialog is available in
+ * the current window. @optional
*/
dialogs: {},
*/
CSS: UTF8(String.replace(<><![CDATA[
// <css>
- Boolean color: red;
- Function color: navy;
- Null color: blue;
- Number color: blue;
- Object color: maroon;
- String color: green; white-space: pre;
-
- Key font-weight: bold;
-
- Enabled color: blue;
- Disabled color: red;
-
- FontFixed font-family: monospace !important;
- FontCode font-size: 9pt; font-family: -mox-fixed, monospace !important;
- FontProportional font-size: 10pt; font-family: "Droid Sans", "Helvetica LT Std", Helvetica, "DejaVu Sans", Verdana, sans-serif !important;
+ Boolean /* JavaScript booleans */ color: red;
+ Function /* JavaScript functions */ color: navy;
+ Null /* JavaScript null values */ color: blue;
+ Number /* JavaScript numbers */ color: blue;
+ Object /* JavaScript objects */ color: maroon;
+ String /* String values */ color: green; white-space: pre;
+ Comment /* JavaScriptor CSS comments */ color: gray;
+
+ Key /* Keywords */ font-weight: bold;
+
+ Enabled /* Enabled item indicator text */ color: blue;
+ Disabled /* Disabled item indicator text */ color: red;
+
+ FontFixed /* The font used for fixed-width text */ \
+ font-family: monospace !important;
+ FontCode /* The font used for code listings */ \
+ font-size: 9pt; font-family: monospace !important;
+ FontProportional /* The font used for proportionally spaced text */ \
+ font-size: 10pt; font-family: "Droid Sans", "Helvetica LT Std", Helvetica, "DejaVu Sans", Verdana, sans-serif !important;
// Hack to give these groups slightly higher precedence
// than their unadorned variants.
CmdQuestion;[dactyl|highlight] 
 StatusQuestion;[dactyl|highlight]
CmdWarningMsg;[dactyl|highlight] 
 StatusWarningMsg;[dactyl|highlight]
- Normal color: black !important; background: white !important; font-weight: normal !important;
- StatusNormal color: inherit !important; background: transparent !important;
- ErrorMsg color: white !important; background: red !important; font-weight: bold !important;
- InfoMsg color: black !important; background: white !important;
- StatusInfoMsg color: inherit !important; background: transparent !important;
- LineNr color: orange !important; background: white !important;
- ModeMsg color: black !important; background: white !important;
- StatusModeMsg color: inherit !important; background: transparent !important; padding-right: 1em;
- MoreMsg color: green !important; background: white !important;
+ Normal /* Normal text */ \
+ color: black !important; background: white !important; font-weight: normal !important;
+ StatusNormal /* Normal text in the status line */ \
+ color: inherit !important; background: transparent !important;
+ ErrorMsg /* Error messages */ \
+ color: white !important; background: red !important; font-weight: bold !important;
+ InfoMsg /* Information messages */ \
+ color: black !important; background: white !important;
+ StatusInfoMsg /* Information messages in the status line */ \
+ color: inherit !important; background: transparent !important;
+ LineNr /* The line number of an error */ \
+ color: orange !important; background: white !important;
+ ModeMsg /* The mode indicator */ \
+ color: black !important; background: white !important;
+ StatusModeMsg /* The mode indicator in the status line */ \
+ color: inherit !important; background: transparent !important; padding-right: 1em;
+ MoreMsg /* The indicator that there is more text to view */ \
+ color: green !important; background: white !important;
StatusMoreMsg background: transparent !important;
- Message white-space: pre-wrap !important; min-width: 100%; width: 100%; padding-left: 4em; text-indent: -4em; display: block;
- Message String white-space: pre-wrap;
- NonText color: blue; background: transparent !important;
- *Preview color: gray;
- Question color: green !important; background: white !important; font-weight: bold !important;
- StatusQuestion color: green !important; background: transparent !important;
- WarningMsg color: red !important; background: white !important;
- StatusWarningMsg color: red !important; background: transparent !important;
-
- CmdLine;>*;;FontFixed padding: 1px !important;
- CmdPrompt;.dactyl-commandline-prompt
+ Message /* A message as displayed in <ex>:messages</ex> */ \
+ white-space: pre-wrap !important; min-width: 100%; width: 100%; padding-left: 4em; text-indent: -4em; display: block;
+ Message String /* A message as displayed in <ex>:messages</ex> */ \
+ white-space: pre-wrap;
+ NonText /* The <em>~</em> indicators which mark blank lines in the completion list */ \
+ color: blue; background: transparent !important;
+ *Preview /* The completion preview displayed in the &tag.command-line; */ \
+ color: gray;
+ Question /* A prompt for a decision */ \
+ color: green !important; background: white !important; font-weight: bold !important;
+ StatusQuestion /* A prompt for a decision in the status line */ \
+ color: green !important; background: transparent !important;
+ WarningMsg /* A warning message */ \
+ color: red !important; background: white !important;
+ StatusWarningMsg /* A warning message in the status line */ \
+ color: red !important; background: transparent !important;
+ Disabled /* Disabled items */ \
+ color: gray !important;
+
+ CmdLine;>*;;FontFixed /* The command line */ \
+ padding: 1px !important;
+ CmdPrompt;.dactyl-commandline-prompt /* The default styling form the command prompt */
CmdInput;.dactyl-commandline-command
- CmdOutput white-space: pre;
-
+ CmdOutput /* The output of commands executed by <ex>:run</ex> */ \
+ white-space: pre;
- CompGroup
+ CompGroup /* Item group in completion output */
CompGroup:not(:first-of-type) margin-top: .5em;
CompGroup:last-of-type padding-bottom: 1.5ex;
- CompTitle color: magenta; background: white; font-weight: bold;
+ CompTitle /* Completion row titles */ \
+ color: magenta; background: white; font-weight: bold;
CompTitle>* padding: 0 .5ex;
- CompTitleSep height: 1px; background: magenta; background: -moz-linear-gradient(60deg, magenta, white);
+ CompTitleSep /* The element which separates the completion title from its results */ \
+ height: 1px; background: magenta; background: -moz-linear-gradient(60deg, magenta, white);
- CompMsg font-style: italic; margin-left: 16px;
+ CompMsg /* The message which may appear at the top of a group of completion results */ \
+ font-style: italic; margin-left: 16px;
- CompItem
+ CompItem /* A single row of output in the completion list */
CompItem:nth-child(2n+1) background: rgba(0, 0, 0, .04);
- CompItem[selected] background: yellow;
+ CompItem[selected] /* A selected row of completion list */ \
+ background: yellow;
CompItem>* padding: 0 .5ex;
- CompIcon width: 16px; min-width: 16px; display: inline-block; margin-right: .5ex;
+ CompIcon /* The favicon of a completion row */ \
+ width: 16px; min-width: 16px; display: inline-block; margin-right: .5ex;
CompIcon>img max-width: 16px; max-height: 16px; vertical-align: middle;
- CompResult width: 36%; padding-right: 1%; overflow: hidden;
- CompDesc color: gray; width: 62%; padding-left: 1em;
-
- CompLess text-align: center; height: 0; line-height: .5ex; padding-top: 1ex;
- CompLess::after content: "⌃";
-
- CompMore text-align: center; height: .5ex; line-height: .5ex; margin-bottom: -.5ex;
- CompMore::after content: "⌄";
-
-
- EditorEditing;;* background: #bbb !important; -moz-user-input: none !important; -moz-user-modify: read-only !important;
- EditorError;;* background: red !important;
- EditorBlink1;;* background: yellow !important;
- EditorBlink2;;*
-
- REPL overflow: auto; max-height: 40em;
- REPL-R;;;Question
- REPL-E white-space: pre-wrap;
- REPL-P white-space: pre-wrap; margin-bottom: 1em;
-
- Usage width: 100%;
- UsageBody
- UsageHead
- UsageItem
+ CompResult /* The result column of the completion list */ \
+ width: 36%; padding-right: 1%; overflow: hidden;
+ CompDesc /* The description column of the completion list */ \
+ color: gray; width: 62%; padding-left: 1em;
+
+ CompLess /* The indicator shown when completions may be scrolled up */ \
+ text-align: center; height: 0; line-height: .5ex; padding-top: 1ex;
+ CompLess::after /* The character of indicator shown when completions may be scrolled up */ \
+ content: "⌃";
+
+ CompMore /* The indicator shown when completions may be scrolled down */ \
+ text-align: center; height: .5ex; line-height: .5ex; margin-bottom: -.5ex;
+ CompMore::after /* The character of indicator shown when completions may be scrolled down */ \
+ content: "⌄";
+
+ Dense /* Arbitrary elements which should be packed densely together */\
+ margin-top: 0; margin-bottom: 0;
+
+ EditorEditing;;* /* Text fields for which an external editor is open */ \
+ background-color: #bbb !important; -moz-user-input: none !important; -moz-user-modify: read-only !important;
+ EditorError;;* /* Text fields briefly after an error has occurred running the external editor */ \
+ background: red !important;
+ EditorBlink1;;* /* Text fields briefly after successfully running the external editor, alternated with EditorBlink2 */ \
+ background: yellow !important;
+ EditorBlink2;;* /* Text fields briefly after successfully running the external editor, alternated with EditorBlink1 */
+
+ REPL /* Read-Eval-Print-Loop output */ \
+ overflow: auto; max-height: 40em;
+ REPL-R;;;Question /* Prompts in REPL mode */
+ REPL-E /* Evaled input in REPL mode */ \
+ white-space: pre-wrap;
+ REPL-P /* Evaled output in REPL mode */ \
+ white-space: pre-wrap; margin-bottom: 1em;
+
+ Usage /* Output from the :*usage commands */ \
+ width: 100%;
+ UsageHead /* Headings in output from the :*usage commands */
+ UsageBody /* The body of listings in output from the :*usage commands */
+ UsageItem /* Individual items in output from the :*usage commands */
UsageItem:nth-of-type(2n) background: rgba(0, 0, 0, .04);
- Indicator color: blue; width: 1.5em; text-align: center;
- Filter font-weight: bold;
+ Indicator /* The <em>#</em> and <em>%</em> in the <ex>:buffers</ex> list */ \
+ color: blue; width: 1.5em; text-align: center;
+ Filter /* The matching text in a completion list */ \
+ font-weight: bold;
- Keyword color: red;
- Tag color: blue;
+ Keyword /* A bookmark keyword for a URL */ \
+ color: red;
+ Tag /* A bookmark tag for a URL */ \
+ color: blue;
- Link position: relative; padding-right: 2em;
+ Link /* A link with additional information shown on hover */ \
+ position: relative; padding-right: 2em;
Link:not(:hover)>LinkInfo opacity: 0; left: 0; width: 1px; height: 1px; overflow: hidden;
LinkInfo {
+ /* Information shown when hovering over a link */
color: black;
position: absolute;
left: 100%;
}
StatusLine;;;FontFixed {
+ /* The status bar */
-moz-appearance: none !important;
font-weight: bold;
background: transparent !important;
min-height: 18px !important;
text-shadow: none !important;
}
- StatusLineNormal;[dactyl|highlight] color: white !important; background: black !important;
- StatusLineBroken;[dactyl|highlight] color: black !important; background: #FFa0a0 !important; /* light-red */
- StatusLineSecure;[dactyl|highlight] color: black !important; background: #a0a0FF !important; /* light-blue */
- StatusLineExtended;[dactyl|highlight] color: black !important; background: #a0FFa0 !important; /* light-green */
-
- TabClose;.tab-close-button
- TabIcon;.tab-icon min-width: 16px;
- TabText;.tab-text
- TabNumber font-weight: bold; margin: 0px; padding-right: .8ex; cursor: default;
- TabIconNumber {
+ StatusLineNormal;[dactyl|highlight] /* The status bar for an ordinary web page */ \
+ color: white !important; background: black !important;
+ StatusLineBroken;[dactyl|highlight] /* The status bar for a broken web page */ \
+ color: black !important; background: #FFa0a0 !important; /* light-red */
+ StatusLineSecure;[dactyl|highlight] /* The status bar for a secure web page */ \
+ color: black !important; background: #a0a0FF !important; /* light-blue */
+ StatusLineExtended;[dactyl|highlight] /* The status bar for a secure web page with an Extended Validation (EV) certificate */ \
+ color: black !important; background: #a0FFa0 !important; /* light-green */
+
+ !TabClose;.tab-close-button /* The close button of a browser tab */ \
+ /* The close button of a browser tab */
+ !TabIcon;.tab-icon,.tab-icon-image /* The icon of a browser tab */ \
+ /* The icon of a browser tab */
+ !TabText;.tab-text /* The text of a browser tab */
+ TabNumber /* The number of a browser tab, next to its icon */ \
+ font-weight: bold; margin: 0px; padding-right: .8ex; cursor: default;
+ TabIconNumber {
+ /* The number of a browser tab, over its icon */
cursor: default;
width: 16px;
margin: 0 2px 0 -18px !important;
text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px;
}
- Title color: magenta; font-weight: bold;
- URL text-decoration: none; color: green; background: inherit;
+ Title /* The title of a listing, including <ex>:pageinfo</ex>, <ex>:jumps</ex> */ \
+ color: magenta; font-weight: bold;
+ URL /* A URL */ \
+ text-decoration: none; color: green; background: inherit;
URL:hover text-decoration: underline; cursor: pointer;
- URLExtra color: gray;
+ URLExtra /* Extra information about a URL */ \
+ color: gray;
FrameIndicator;;* {
+ /* The styling applied to briefly indicate the active frame */
background-color: red;
opacity: 0.5;
z-index: 999999;
right: 0;
}
- Bell background-color: black !important;
+ Bell /* &dactyl.appName;’s visual bell */ \
+ background-color: black !important;
Hint;;* {
+ /* A hint indicator. See <ex>:help hints</ex> */
font: bold 10px "Droid Sans Mono", monospace !important;
margin: -.2ex;
padding: 0 0 0 1px;
}
Hint[active];;* background: rgba(255, 253, 208, .8);
Hint::after;;* content: attr(text) !important;
- HintElem;;* background-color: yellow !important; color: black !important;
- HintActive;;* background-color: #88FF00 !important; color: black !important;
- HintImage;;* opacity: .5 !important;
-
- Button display: inline-block; font-weight: bold; cursor: pointer; color: black; text-decoration: none;
+ HintElem;;* /* The hintable element */ \
+ background-color: yellow !important; color: black !important;
+ HintActive;;* /* The hint element of link which will be followed by <k name="CR"/> */ \
+ background-color: #88FF00 !important; color: black !important;
+ HintImage;;* /* The indicator which floats above hinted images */ \
+ opacity: .5 !important;
+
+ Button /* A button widget */ \
+ display: inline-block; font-weight: bold; cursor: pointer; color: black; text-decoration: none;
Button:hover text-decoration: underline;
Button[collapsed] visibility: collapse; width: 0;
Button::before content: "["; color: gray; text-decoration: none !important;
Button::after content: "]"; color: gray; text-decoration: none !important;
Button:not([collapsed]) ~ Button:not([collapsed])::before content: "/[";
- Buttons
+ Buttons /* A group of buttons */
- DownloadCell display: table-cell; padding: 0 1ex;
+ DownloadCell /* A table cell in the :downloads manager */ \
+ display: table-cell; padding: 0 1ex;
- Downloads display: table; margin: 0; padding: 0;
- DownloadHead;;;CompTitle display: table-row;
+ Downloads /* The :downloads manager */ \
+ display: table; margin: 0; padding: 0;
+ DownloadHead;;;CompTitle /* A heading in the :downloads manager */ \
+ display: table-row;
DownloadHead>*;;;DownloadCell
- Download display: table-row;
+ Download /* A download in the :downloads manager */ \
+ display: table-row;
Download:not([active]) color: gray;
+ Download:nth-child(2n+1) background: rgba(0, 0, 0, .04);
Download>*;;;DownloadCell
- DownloadButtons
- DownloadPercent
- DownloadProgress
- DownloadProgressHave
- DownloadProgressTotal
- DownloadSource
- DownloadState
- DownloadTime
- DownloadTitle
+ DownloadButtons /* A button group in the :downloads manager */
+ DownloadPercent /* The percentage column for a download */
+ DownloadProgress /* The progress column for a download */
+ DownloadProgressHave /* The completed portion of the progress column */
+ DownloadProgressTotal /* The remaining portion of the progress column */
+ DownloadSource /* The download source column for a download */
+ DownloadState /* The download state column for a download */
+ DownloadTime /* The time remaining column for a download */
+ DownloadTitle /* The title column for a download */
DownloadTitle>Link>a max-width: 48ex; overflow: hidden; display: inline-block;
- AddonCell display: table-cell; padding: 0 1ex;
+ AddonCell /* A cell in tell :addons manager */ \
+ display: table-cell; padding: 0 1ex;
- Addons display: table; margin: 0; padding: 0;
- AddonHead;;;CompTitle display: table-row;
+ Addons /* The :addons manager */ \
+ display: table; margin: 0; padding: 0;
+ AddonHead;;;CompTitle /* A heading in the :addons manager */ \
+ display: table-row;
AddonHead>*;;;AddonCell
- Addon display: table-row;
+ Addon /* An add-on in the :addons manager */ \
+ display: table-row;
+ Addon:nth-child(2n+1) background: rgba(0, 0, 0, .04);
Addon>*;;;AddonCell
AddonButtons
helpCSS: UTF8(<><![CDATA[
// <css>
- InlineHelpLink font-size: inherit !important; font-family: inherit !important;
+ InlineHelpLink /* A help link shown in the command line or multi-line output area */ \
+ font-size: inherit !important; font-family: inherit !important;
- Help;;;FontProportional line-height: 1.4em;
+ Help;;;FontProportional /* A help page */ \
+ line-height: 1.4em;
- HelpInclude margin: 2em 0;
+ HelpInclude /* A help page included in the consolidated help listing */ \
+ margin: 2em 0;
- HelpArg;;;FontCode color: #6A97D4;
- HelpOptionalArg;;;FontCode color: #6A97D4;
+ HelpArg;;;FontCode /* A required command argument indicator */ \
+ color: #6A97D4;
+ HelpOptionalArg;;;FontCode /* An optional command argument indicator */ \
+ color: #6A97D4;
- HelpBody display: block; margin: 1em auto; max-width: 100ex; padding-bottom: 1em; margin-bottom: 4em; border-bottom-width: 1px;
- HelpBorder;*;dactyl://help/* border-color: silver; border-width: 0px; border-style: solid;
- HelpCode;;;FontCode display: block; white-space: pre; margin-left: 2em;
- HelpTT;html|tt;dactyl://help/*;FontCode
+ HelpBody /* The body of a help page */ \
+ display: block; margin: 1em auto; max-width: 100ex; padding-bottom: 1em; margin-bottom: 4em; border-bottom-width: 1px;
+ HelpBorder;*;dactyl://help/* /* The styling of bordered elements */ \
+ border-color: silver; border-width: 0px; border-style: solid;
+ HelpCode;;;FontCode /* Code listings */ \
+ display: block; white-space: pre; margin-left: 2em;
+ HelpTT;html|tt;dactyl://help/*;FontCode /* Teletype text */
- HelpDefault;;;FontCode display: inline-block; margin: -1px 1ex 0 0; white-space: pre; vertical-align: text-top;
+ HelpDefault;;;FontCode /* The default value of a help item */ \
+ display: inline-block; margin: -1px 1ex 0 0; white-space: pre; vertical-align: text-top;
- HelpDescription display: block; clear: right;
+ HelpDescription /* The description of a help item */ \
+ display: block; clear: right;
HelpDescription[short] clear: none;
- HelpEm;html|em;dactyl://help/* font-weight: bold; font-style: normal;
+ HelpEm;html|em;dactyl://help/* /* Emphasized text */ \
+ font-weight: bold; font-style: normal;
- HelpEx;;;FontCode display: inline-block; color: #527BBD;
+ HelpEx;;;FontCode /* An Ex command */ \
+ display: inline-block; color: #527BBD;
- HelpExample display: block; margin: 1em 0;
- HelpExample::before content: "Example: "; font-weight: bold;
+ HelpExample /* An example */ \
+ display: block; margin: 1em 0;
+ HelpExample::before content: "__MSG_help.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;
+ HelpInfo /* Arbitrary information about a help item */ \
+ display: block; width: 20em; margin-left: auto;
+ HelpInfoLabel /* The label for a HelpInfo item */ \
+ display: inline-block; width: 6em; color: magenta; font-weight: bold; vertical-align: text-top;
+ HelpInfoValue /* The details for a HelpInfo item */ \
+ display: inline-block; width: 14em; text-decoration: none; vertical-align: text-top;
- HelpItem display: block; margin: 1em 1em 1em 10em; clear: both;
+ HelpItem /* A help item */ \
+ display: block; margin: 1em 1em 1em 10em; clear: both;
- HelpKey;;;FontCode color: #102663;
- HelpKeyword font-weight: bold; color: navy;
+ HelpKey;;;FontCode /* A keyboard key specification */ \
+ color: #102663;
+ HelpKeyword /* A keyword */ \
+ font-weight: bold; color: navy;
- HelpLink;html|a;dactyl://help/* text-decoration: none !important;
+ HelpLink;html|a;dactyl://help/* /* A hyperlink */ \
+ text-decoration: none !important;
HelpLink[href]:hover text-decoration: underline !important;
HelpLink[href^="mailto:"]::after content: "✉"; padding-left: .2em;
HelpLink[rel=external] {
+ /* A hyperlink to an external resource */
/* Thanks, Wikipedia */
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAFVBMVEVmmcwzmcyZzP8AZswAZv////////9E6giVAAAAB3RSTlP///////8AGksDRgAAADhJREFUGFcly0ESAEAEA0Ei6/9P3sEcVB8kmrwFyni0bOeyyDpy9JTLEaOhQq7Ongf5FeMhHS/4AVnsAZubxDVmAAAAAElFTkSuQmCC) no-repeat scroll right center;
padding-right: 13px;
}
-
- HelpTOC
- HelpTOC>ol ol margin-left: -1em;
-
- HelpOrderedList;ol;dactyl://help/* margin: 1em 0;
- HelpOrderedList1;ol[level="1"],ol;dactyl://help/* list-style: outside decimal; display: block;
- HelpOrderedList2;ol[level="2"],ol ol;dactyl://help/* list-style: outside upper-alpha;
- HelpOrderedList3;ol[level="3"],ol ol ol;dactyl://help/* list-style: outside lower-roman;
- HelpOrderedList4;ol[level="4"],ol ol ol ol;dactyl://help/* list-style: outside decimal;
-
- HelpList;html|ul;dactyl://help/* display: block; list-style-position: outside; margin: 1em 0;
- HelpListItem;html|li;dactyl://help/* display: list-item;
-
-
- HelpNote color: red; font-weight: bold;
-
- HelpOpt;;;FontCode color: #106326;
- HelpOptInfo;;;FontCode display: block; margin-bottom: 1ex; padding-left: 4em;
-
- HelpParagraph;html|p;dactyl://help/* display: block; margin: 1em 0em;
+ ErrorMsg HelpEx color: inherit; background: inherit; text-decoration: underline;
+ ErrorMsg HelpKey color: inherit; background: inherit; text-decoration: underline;
+ ErrorMsg HelpOption color: inherit; background: inherit; text-decoration: underline;
+ ErrorMsg HelpTopic color: inherit; background: inherit; text-decoration: underline;
+
+ HelpTOC /* The Table of Contents for a help page */
+ HelpTOC>ol ol margin-left: -1em;
+
+ HelpOrderedList;ol;dactyl://help/* /* Any ordered list */ \
+ margin: 1em 0;
+ HelpOrderedList1;ol[level="1"],ol;dactyl://help/* /* A first-level ordered list */ \
+ list-style: outside decimal; display: block;
+ HelpOrderedList2;ol[level="2"],ol ol;dactyl://help/* /* A second-level ordered list */ \
+ list-style: outside upper-alpha;
+ HelpOrderedList3;ol[level="3"],ol ol ol;dactyl://help/* /* A third-level ordered list */ \
+ list-style: outside lower-roman;
+ HelpOrderedList4;ol[level="4"],ol ol ol ol;dactyl://help/* /* A fourth-level ordered list */ \
+ list-style: outside decimal;
+
+ HelpList;html|ul;dactyl://help/* /* An unordered list */ \
+ display: block; list-style-position: outside; margin: 1em 0;
+ HelpListItem;html|li;dactyl://help/* /* A list item, ordered or unordered */ \
+ display: list-item;
+
+ HelpNote /* The indicator for a note */ \
+ color: red; font-weight: bold;
+
+ HelpOpt;;;FontCode /* An option name */ \
+ color: #106326;
+ HelpOptInfo;;;FontCode /* Information about the type and default values for an option entry */ \
+ display: block; margin-bottom: 1ex; padding-left: 4em;
+
+ HelpParagraph;html|p;dactyl://help/* /* An ordinary paragraph */ \
+ display: block; margin: 1em 0em;
HelpParagraph:first-child margin-top: 0;
HelpParagraph:last-child margin-bottom: 0;
- HelpSpec;;;FontCode display: block; margin-left: -10em; float: left; clear: left; color: #527BBD; margin-right: 1em;
+ HelpSpec;;;FontCode /* The specification for a help entry */ \
+ display: block; margin-left: -10em; float: left; clear: left; color: #527BBD; margin-right: 1em;
- HelpString;;;FontCode color: green; font-weight: normal;
+ HelpString;;;FontCode /* A quoted string */ \
+ color: green; font-weight: normal;
HelpString::before content: '"';
HelpString::after content: '"';
HelpString[delim]::before content: attr(delim);
HelpString[delim]::after content: attr(delim);
- HelpNews position: relative;
- HelpNewsOld opacity: .7;
- HelpNewsNew font-style: italic;
- HelpNewsTag font-style: normal; position: absolute; left: 100%; padding-left: 1em; color: #527BBD; opacity: .6; white-space: pre;
+ HelpNews /* A news item */ position: relative;
+ HelpNewsOld /* An old news item */ opacity: .7;
+ HelpNewsNew /* A new news item */ font-style: italic;
+ HelpNewsTag /* The version tag for a news item */ \
+ font-style: normal; position: absolute; left: 100%; padding-left: 1em; color: #527BBD; opacity: .6; white-space: pre;
HelpHead;html|h1,html|h2,html|h3,html|h4;dactyl://help/* {
+ /* Any help heading */
font-weight: bold;
color: #527BBD;
clear: both;
}
HelpHead1;html|h1;dactyl://help/* {
+ /* A first-level help heading */
margin: 2em 0 1em;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 2em;
}
HelpHead2;html|h2;dactyl://help/* {
+ /* A second-level help heading */
margin: 2em 0 1em;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 1.2em;
}
HelpHead3;html|h3;dactyl://help/* {
+ /* A third-level help heading */
margin: 1em 0;
padding-bottom: .2ex;
font-size: 1.1em;
}
HelpHead4;html|h4;dactyl://help/* {
+ /* A fourth-level help heading */
}
-
HelpTab;html|dl;dactyl://help/* {
+ /* A description table */
display: table;
width: 100%;
margin: 1em 0;
}
HelpTabColumn;html|column;dactyl://help/* display: table-column;
HelpTabColumn:first-child width: 25%;
- HelpTabTitle;html|dt;dactyl://help/*;FontCode display: table-cell; padding: .1ex 1ex; font-weight: bold;
- HelpTabDescription;html|dd;dactyl://help/* display: table-cell; padding: .3ex 1em; text-indent: -1em; border-width: 0px;
+ HelpTabTitle;html|dt;dactyl://help/*;FontCode /* The title column of description tables */ \
+ display: table-cell; padding: .1ex 1ex; font-weight: bold;
+ HelpTabDescription;html|dd;dactyl://help/* /* The description column of description tables */ \
+ display: table-cell; padding: .3ex 1em; text-indent: -1em; border-width: 0px;
HelpTabDescription>*;;dactyl://help/* text-indent: 0;
- HelpTabRow;html|dl>html|tr;dactyl://help/* display: table-row;
-
- HelpTag;;;FontCode display: inline-block; color: #527BBD; margin-left: 1ex; font-weight: normal;
- HelpTags display: block; float: right; clear: right;
- HelpTopic;;;FontCode color: #102663;
- HelpType;;;FontCode margin-right: 2ex;
-
- HelpWarning color: red; font-weight: bold;
-
- HelpXML;;;FontCode color: #C5F779; background-color: #444444; font-family: Terminus, Fixed, monospace;
+ HelpTabRow;html|dl>html|tr;dactyl://help/* /* Entire rows in description tables */ \
+ display: table-row;
+
+ HelpTag;;;FontCode /* A help tag */ \
+ display: inline-block; color: #527BBD; margin-left: 1ex; font-weight: normal;
+ HelpTags /* A group of help tags */ \
+ display: block; float: right; clear: right;
+ HelpTopic;;;FontCode /* A link to a help topic */ \
+ color: #102663;
+ HelpType;;;FontCode /* An option type */ \
+ color: #102663 !important; margin-right: 2ex;
+
+ HelpWarning /* The indicator for a warning */ \
+ color: red; font-weight: bold;
+
+ HelpXML;;;FontCode /* Highlighted XML */ \
+ color: #C5F779; background-color: #444444; font-family: Terminus, Fixed, monospace;
HelpXMLBlock { white-space: pre; color: #C5F779; background-color: #444444;
border: 1px dashed #aaaaaa;
display: block;
completer: function (context) modules.completion.group(context)
});
+
+ memoize(modules, "userContext", function () contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules, true]));
+ memoize(modules, "_userContext", function () contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules.userContext]));
},
cleanup: function () {
util.trapErrors("destroy", hive);
for (let [name, plugin] in iter(this.modules.plugins.contexts))
- if (plugin && "onUnload" in plugin)
+ if (plugin && "onUnload" in plugin && callable(plugin.onUnload))
util.trapErrors("onUnload", plugin);
},
{ _hive: { value: name } })));
memoize(contexts.groupsProto, name,
- function () [group[name] for (group in values(this.groups)) if (set.has(group, name))]);
+ function () [group[name] for (group in values(this.groups)) if (Set.has(group, name))]);
},
get toStringParams() [this.name, this.Hive]
0);
let contextPath = file.path;
- let self = set.has(plugins, contextPath) && plugins.contexts[contextPath];
+ let self = Set.has(plugins, contextPath) && plugins.contexts[contextPath];
if (self) {
- if (set.has(self, "onUnload"))
+ if (Set.has(self, "onUnload"))
self.onUnload();
}
else {
let name = isPlugin ? file.getRelativeDescriptor(isPlugin).replace(File.PATH_SEP, "-")
: file.leafName;
- self = update(newContext.apply(null, args || [userContext]), {
+ self = args && !isArray(args) ? args : newContext.apply(null, args || [userContext]);
+ update(self, {
NAME: Const(name.replace(/\.[^.]*$/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase())),
PATH: Const(file.path),
contexts.removeGroup(this.GROUP);
})
});
- Class.replaceProperty(plugins, file.path, self);
+
+ if (group !== this.user)
+ Class.replaceProperty(plugins, file.path, self);
// This belongs elsewhere
- if (isPlugin && args)
+ if (isPlugin)
Object.defineProperty(plugins, self.NAME, {
configurable: true,
enumerable: true,
group = this.addGroup(commands.nameRegexp
.iterate(name.replace(/\.[^.]*$/, ""))
.join("-").replace(/--+/g, "-"),
- "Script group for " + file.path,
+ _("context.scriptGroup", file.path),
null, false);
Class.replaceProperty(self, "GROUP", group);
})),
matchingGroups: function (uri) Object.create(this.groupsProto, {
- groups: { value: this.activeGroups(uri) },
+ groups: { value: this.activeGroups(uri) }
}),
activeGroups: function (uri, doc) {
initializedGroups: function (hive)
let (need = hive ? [hive] : Object.keys(this.hives))
- this.groupList.filter(function (group) need.some(set.has(group))),
+ this.groupList.filter(function (group) need.some(Set.has(group))),
addGroup: function addGroup(name, description, filter, persist, replace) {
let group = this.getGroup(name);
if (description)
group.description = description;
if (filter)
- group.filter = filter
+ group.filter = filter;
group.persist = persist;
}
getGroup: function getGroup(name, hive) {
if (name === "default")
var group = this.context && this.context.context && this.context.context.GROUP;
- else if (set.has(this.groupMap, name))
+ else if (Set.has(this.groupMap, name))
group = this.groupMap[name];
if (group && hive)
return group;
},
+ getDocs: function getDocs(context) {
+ try {
+ if (isinstance(context, ["Sandbox"])) {
+ let info = "INFO" in context && Cu.evalInSandbox("this.INFO instanceof XML && INFO.toXMLString()", context);
+ return info && XML(info);
+ }
+ if (typeof context.INFO == "xml")
+ return context.INFO;
+ }
+ catch (e) {}
+ return null;
+ },
+
bindMacro: function (args, default_, params) {
const { dactyl, events, modules } = this.modules;
+ function Proxy(obj, key) Class.Property({
+ configurable: true,
+ enumerable: true,
+ get: function Proxy_get() process(obj[key]),
+ set: function Proxy_set(val) obj[key] = val
+ })
+
let process = util.identity;
if (callable(params))
var makeParams = function makeParams(self, args)
- iter.toObject([k, process(v)]
- for ([k, v] in iter(params.apply(self, args))));
+ let (obj = params.apply(self, args))
+ iter.toObject([k, Proxy(obj, k)] for (k in properties(obj)));
else if (params)
makeParams = function makeParams(self, args)
iter.toObject([name, process(args[i])]
let rhs = args.literalArg;
let type = ["-builtin", "-ex", "-javascript", "-keys"].reduce(function (a, b) args[b] ? b : a, default_);
+
switch (type) {
case "-builtin":
let noremap = true;
var action = function action() {
events.feedkeys(action.macro(makeParams(this, arguments)),
noremap, silent);
- }
+ };
action.macro = util.compileMacro(rhs, true);
break;
+
case "-ex":
action = function action() modules.commands
.execute(action.macro, makeParams(this, arguments),
action.macro = util.compileMacro(rhs, true);
action.context = this.context && update({}, this.context);
break;
+
case "-javascript":
if (callable(params))
action = dactyl.userEval("(function action() { with (action.makeParams(this, arguments)) {" + args.literalArg + "} })");
action.makeParams = makeParams;
break;
}
+
action.toString = function toString() (type === default_ ? "" : type + " ") + rhs;
args = null;
return action;
if (args.has("-locations"))
group.filter = filter;
if (args.has("-description"))
- group.description = args["-description"]
+ group.description = args["-description"];
if (args.has("-nopersist"))
- group.persist = !args["-nopersist"]
+ group.persist = !args["-nopersist"];
}
if (!group.builtin && args.has("-args")) {
util.assert(!group.builtin ||
!["-description", "-locations", "-nopersist"]
- .some(set.has(args.explicitOpts)),
+ .some(Set.has(args.explicitOpts)),
_("group.cantModifyBuiltin"));
},
{
commands.add(["delg[roup]"],
"Delete a group",
function (args) {
- util.assert(contexts.getGroup(args[0]), _("group.noSuch", args[0]));
- contexts.removeGroup(args[0]);
+ util.assert(args.bang ^ !!args[0], _("error.argumentOrBang"));
+
+ if (args.bang)
+ contexts.groupList = contexts.groupList.filter(function (g) g.builtin);
+ else {
+ util.assert(contexts.getGroup(args[0]), _("group.noSuch", args[0]));
+ contexts.removeGroup(args[0]);
+ }
},
{
- argCount: "1",
+ argCount: "?",
+ bang: true,
completer: function (context, args) {
- modules.completion.group(context);
+ if (args.bang)
+ return;
context.filters.push(function ({ item }) !item.builtin);
+ modules.completion.group(context);
}
});
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("downloads", {
exports: ["Download", "Downloads", "downloads"],
- use: ["io", "prefs", "services", "util"]
+ use: ["io", "messages", "prefs", "services", "util"]
}, this);
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
<tr highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
<td highlight="DownloadTitle">
<span highlight="Link">
- <a key="launch" dactyl:command="download.command"
+ <a key="launch"
href={self.target.spec} path={self.targetFile.path}>{self.displayName}</a>
<span highlight="LinkInfo">{self.targetFile.path}</span>
</span>
</td>
<td highlight="DownloadState" key="state"/>
<td highlight="DownloadButtons Buttons">
- <a highlight="Button" key="pause">Pause</a>
- <a highlight="Button" key="remove">Remove</a>
- <a highlight="Button" key="resume">Resume</a>
- <a highlight="Button" key="retry">Retry</a>
- <a highlight="Button" key="cancel">Cancel</a>
- <a highlight="Button" key="delete">Delete</a>
+ <a highlight="Button" href="javascript:0" key="pause">{_("download.action.Pause")}</a>
+ <a highlight="Button" href="javascript:0" key="remove">{_("download.action.Remove")}</a>
+ <a highlight="Button" href="javascript:0" key="resume">{_("download.action.Resume")}</a>
+ <a highlight="Button" href="javascript:0" key="retry">{_("download.action.Retry")}</a>
+ <a highlight="Button" href="javascript:0" key="cancel">{_("download.action.Cancel")}</a>
+ <a highlight="Button" href="javascript:0" key="delete">{_("download.action.Delete")}</a>
</td>
<td highlight="DownloadProgress" key="progress">
<span highlight="DownloadProgressHave" key="progressHave"
/>/<span highlight="DownloadProgressTotal" key="progressTotal"/>
</td>
<td highlight="DownloadPercent" key="percent"/>
+ <td highlight="DownloadSpeed" key="speed"/>
<td highlight="DownloadTime" key="time"/>
<td><a highlight="DownloadSource" key="source" href={self.source.spec}>{self.source.spec}</a></td>
</tr>,
this.list.document, this.nodes);
+ this.nodes.launch.addEventListener("click", function (event) {
+ if (event.button == 0) {
+ event.preventDefault();
+ self.command("launch");
+ }
+ }, false);
+
self.updateStatus();
return self;
},
})),
command: function command(name) {
- util.assert(set.has(this.allowedCommands, name), "Unknown command");
- util.assert(this.allowedCommands[name], "Command not allowed");
+ util.assert(Set.has(this.allowedCommands, name), _("download.unknownCommand"));
+ util.assert(this.allowedCommands[name], _("download.commandNotAllowed"));
- services.downloadManager[name + "Download"](this.id);
+ if (Set.has(this.commands, name))
+ this.commands[name].call(this);
+ else
+ services.downloadManager[name + "Download"](this.id);
},
commands: {
function action() {
try {
if (this.MIMEInfo && this.MIMEInfo.preferredAction == this.MIMEInfo.useHelperApp)
- this.MIMEInfo.launchWithFile(file)
+ this.MIMEInfo.launchWithFile(file);
else
file.launch();
}
let file = io.File(this.targetFile);
if (file.isExecutable() && prefs.get("browser.download.manager.alertOnEXEOpen", true))
- this.list.modules.commandline.input("This will launch an executable download. Continue? (yes/[no]/always) ",
+ this.list.modules.commandline.input(_("download.prompt.launchExecutable") + " ",
function (resp) {
if (/^a(lways)$/i.test(resp)) {
prefs.set("browser.download.manager.alertOnEXEOpen", false);
}
},
- compare: function compare(other) String.localeCompare(this.displayName, other.displayName),
+ _compare: {
+ active: function (a, b) a.alive - b.alive,
+ complete: function (a, b) a.percentComplete - b.percentComplete,
+ date: function (a, b) a.startTime - b.startTime,
+ filename: function (a, b) String.localeCompare(a.targetFile.leafName, b.targetFile.leafName),
+ size: function (a, b) a.size - b.size,
+ speed: function (a, b) a.speed - b.speed,
+ time: function (a, b) a.timeRemaining - b.timeRemaining,
+ url: function (a, b) String.localeCompare(a.source.spec, b.source.spec)
+ },
+
+ compare: function compare(other) values(this.list.sortOrder).map(function (order) {
+ let val = this._compare[order.substr(1)](this, other);
+
+ return (order[0] == "-") ? -val : val;
+ }, this).nth(util.identity, 0) || 0,
timeRemaining: Infinity,
updateProgress: function updateProgress() {
let self = this.__proto__;
- if (this.amountTransferred === this.size)
+ if (this.amountTransferred === this.size) {
+ this.nodes.speed.textContent = "";
this.nodes.time.textContent = "";
- else if (this.speed == 0 || this.size == 0)
- this.nodes.time.textContent = "Unknown";
+ }
else {
- let seconds = (this.size - this.amountTransferred) / this.speed;
- [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
- if (this.timeRemaining)
- this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
- else
- this.nodes.time.textContent = "~1 second";
+ this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s";
+
+ if (this.speed == 0 || this.size == 0)
+ this.nodes.time.textContent = _("download.unknown");
+ else {
+ let seconds = (this.size - this.amountTransferred) / this.speed;
+ [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
+ if (this.timeRemaining)
+ this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
+ else
+ this.nodes.time.textContent = _("download.almostDone");
+ }
}
- let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : "Unknown";
+
+ let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : _("download.unknown");
let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, "");
XPCOM([Ci.nsIDownloadProgressListener,
Ci.nsIObserver,
Ci.nsISupportsWeakReference]), {
- init: function init(modules, filter) {
+ init: function init(modules, filter, sort) {
+ this.sortOrder = sort;
this.modules = modules;
this.filter = filter && filter.toLowerCase();
this.nodes = {
};
this.downloads = {};
},
+
cleanup: function cleanup() {
this.observe.unregister();
services.downloadManager.removeListener(this);
util.xmlToDom(<table highlight="Downloads" key="list" xmlns={XHTML}>
<tr highlight="DownloadHead">
- <span>Title</span>
- <span>Status</span>
+ <span>{_("title.Title")}</span>
+ <span>{_("title.Status")}</span>
<span/>
- <span>Progress</span>
+ <span>{_("title.Progress")}</span>
<span/>
- <span>Time remaining</span>
- <span>Source</span>
+ <span>{_("title.Speed")}</span>
+ <span>{_("title.Time remaining")}</span>
+ <span>{_("title.Source")}</span>
</tr>
<tr highlight="Download"><span><div style="min-height: 1ex; /* FIXME */"/></span></tr>
<tr highlight="Download" key="totals" active="true">
- <td><span highlight="Title">Totals:</span> <span key="total"/></td>
+ <td><span highlight="Title">{_("title.Totals")}:</span> <span key="total"/></td>
<td/>
<td highlight="DownloadButtons">
- <a highlight="Button" key="clear">Clear</a>
+ <a highlight="Button" href="javascript:0" key="clear">{_("download.action.Clear")}</a>
</td>
<td highlight="DownloadProgress" key="progress">
<span highlight="DownloadProgressHave" key="progressHave"
/>/<span highlight="DownloadProgressTotal" key="progressTotal"/>
</td>
<td highlight="DownloadPercent" key="percent"/>
+ <td highlight="DownloadSpeed" key="speed"/>
<td highlight="DownloadTime" key="time"/>
<td/>
</tr>
}
},
+ sort: function sort() {
+ let list = values(this.downloads).sort(function (a, b) a.compare(b));
+
+ for (let [i, download] in iter(list))
+ if (this.nodes.list.childNodes[i + 1] != download.nodes.row)
+ this.nodes.list.insertBefore(download.nodes.row,
+ this.nodes.list.childNodes[i + 1]);
+ },
+
+ shouldSort: function shouldSort() Array.some(arguments, function (val) this.sortOrder.some(function (v) v.substr(1) == val), this),
+
update: function update() {
for (let node in values(this.nodes))
if (node.update && node.update != update)
let active = downloads.filter(function (dl) dl.alive).length;
if (active)
- this.nodes.total.textContent = active + " active";
- else for (let key in values(["total", "percent", "time"]))
+ this.nodes.total.textContent = _("download.nActive", active);
+ else for (let key in values(["total", "percent", "speed", "time"]))
this.nodes[key].textContent = "";
+
+ if (this.shouldSort("complete", "size", "speed", "time"))
+ this.sort();
},
observers: {
this.nodes.list.scrollIntoView(false);
}
this.update();
+
+ if (this.shouldSort("active"))
+ this.sort();
}
catch (e) {
util.reportError(e);
}
},
+
onProgressChange: function (webProgress, request,
curProgress, maxProgress,
curTotalProgress, maxTotalProgress,
var Downloads = Module("downloads", {
}, {
}, {
- commands: function (dactyl, modules, window) {
- const { commands } = modules;
+ commands: function initCommands(dactyl, modules, window) {
+ const { commands, CommandOption } = modules;
commands.add(["downl[oads]", "dl"],
"Display the downloads list",
function (args) {
- let downloads = DownloadList(modules, args[0]);
+ let downloads = DownloadList(modules, args[0], args["-sort"]);
modules.commandline.echo(downloads);
},
{
- argCount: "?"
+ argCount: "?",
+ options: [
+ {
+ names: ["-sort", "-s"],
+ description: "Sort order (see 'downloadsort')",
+ type: CommandOption.LIST,
+ get default() modules.options["downloadsort"],
+ completer: function (context, args) modules.options.get("downloadsort").completer(context, { values: args["-sort"] }),
+ validator: function (value) modules.options.get("downloadsort").validator(value)
+ }
+ ]
+ });
+
+ commands.add(["dlc[lear]"],
+ "Clear completed downloads",
+ function (args) { services.downloadManager.cleanUp(); });
+ },
+ options: function initOptions(dactyl, modules, window) {
+ const { options } = modules;
+
+ if (false)
+ options.add(["downloadcolumns", "dlc"],
+ "The columns to show in the download manager",
+ "stringlist", "filename,state,buttons,progress,percent,time,url",
+ {
+ values: {
+ buttons: "Control buttons",
+ filename: "Target filename",
+ percent: "Percent complete",
+ size: "File size",
+ speed: "Download speed",
+ state: "The download's state",
+ time: "Time remaining",
+ url: "Source URL"
+ }
+ });
+
+ options.add(["downloadsort", "dlsort", "dls"],
+ ":downloads sort order",
+ "stringlist", "-active,+filename",
+ {
+ values: {
+ active: "Whether download is active",
+ complete: "Percent complete",
+ date: "Date and time the download began",
+ filename: "Target filename",
+ size: "File size",
+ speed: "Download speed",
+ time: "Time remaining",
+ url: "Source URL"
+ },
+
+ completer: function (context, extra) {
+ let seen = Set.has(Set(extra.values.map(function (val) val.substr(1))));
+
+ context.completions = iter(this.values).filter(function ([k, v]) !seen(k))
+ .map(function ([k, v]) [["+" + k, [v, " (", _("sort.ascending"), ")"].join("")],
+ ["-" + k, [v, " (", _("sort.descending"), ")"].join("")]])
+ .flatten().array;
+ },
+
+ has: function () Array.some(arguments, function (val) this.value.some(function (v) v.substr(1) == val)),
+
+ validator: function (value) {
+ let seen = {};
+ return value.every(function (val) /^[+-]/.test(val) && Set.has(this.values, val.substr(1))
+ && !Set.add(seen, val.substr(1)),
+ this) && value.length;
+ }
});
}
});
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("finder", {
exports: ["RangeFind", "RangeFinder", "rangefinder"],
+ require: ["prefs"],
use: ["messages", "services", "util"]
}, this);
function equals(a, b) XPCNativeWrapper(a) == XPCNativeWrapper(b);
+try {
+
/** @instance rangefinder */
var RangeFinder = Module("rangefinder", {
Local: function (dactyl, modules, window) ({
set rangeFind(val) modules.buffer.localStore.rangeFind = val
}),
+ init: function init() {
+ prefs.safeSet("accessibility.typeaheadfind.autostart", false);
+ // The above should be sufficient, but: http://dactyl.sf.net/bmo/348187
+ prefs.safeSet("accessibility.typeaheadfind", false);
+ },
+
get commandline() this.modules.commandline,
get modes() this.modules.modes,
get options() this.modules.options,
if (this.rangeFind && equals(this.rangeFind.window.get(), this.window))
this.rangeFind.reset();
- this.find("", mode === this.modes.FIND_BACKWARD);
+ this.find("", mode == this.modes.FIND_BACKWARD);
},
bootstrap: function (str, backward) {
+ if (arguments.length < 2 && this.rangeFind)
+ backward = this.rangeFind.reverse;
+
let highlighted = this.rangeFind && this.rangeFind.highlighted;
let selections = this.rangeFind && this.rangeFind.selections;
let linksOnly = false;
}
},
+ onHistory: function () {
+ this.rangeFind.found = false;
+ },
+
onSubmit: function (command) {
if (!this.options["incfind"] || !this.rangeFind || !this.rangeFind.found) {
this.clear();
modes.addMode("FIND", {
description: "Find mode, active when typing search input",
- bases: [modes.COMMAND_LINE],
+ bases: [modes.COMMAND_LINE]
});
modes.addMode("FIND_FORWARD", {
description: "Forward Find mode, active when typing search input",
get onCancel() modules.rangefinder.closure.onCancel,
get onChange() modules.rangefinder.closure.onChange,
+ get onHistory() modules.rangefinder.closure.onHistory,
get onSubmit() modules.rangefinder.closure.onSubmit
});
},
mappings: function (dactyl, modules, window) {
- const { buffer, config, mappings, modes, rangefinder } = modules;
+ const { Buffer, buffer, config, mappings, modes, rangefinder } = modules;
var myModes = config.browserModes.concat([modes.CARET]);
mappings.add(myModes,
- ["/"], "Find a pattern starting at the current caret position",
+ ["/", "<find-forward>"], "Find a pattern starting at the current caret position",
function () { rangefinder.openPrompt(modes.FIND_FORWARD); });
mappings.add(myModes,
- ["?"], "Find a pattern backward of the current caret position",
+ ["?", "<find-backward>"], "Find a pattern backward of the current caret position",
function () { rangefinder.openPrompt(modes.FIND_BACKWARD); });
mappings.add(myModes,
- ["n"], "Find next",
+ ["n", "<find-next>"], "Find next",
function () { rangefinder.findAgain(false); });
mappings.add(myModes,
- ["N"], "Find previous",
+ ["N", "<find-previous>"], "Find previous",
function () { rangefinder.findAgain(true); });
- mappings.add(myModes.concat([modes.CARET, modes.TEXT_EDIT]), ["*"],
+ mappings.add(myModes.concat([modes.CARET, modes.TEXT_EDIT]), ["*", "<find-word-forward>"],
"Find word under cursor",
function () {
- rangefinder.find(buffer.getCurrentWord(), false);
+ rangefinder.find(Buffer.currentWord(buffer.focusedFrame, true), false);
rangefinder.findAgain();
});
- mappings.add(myModes.concat([modes.CARET, modes.TEXT_EDIT]), ["#"],
+ mappings.add(myModes.concat([modes.CARET, modes.TEXT_EDIT]), ["#", "<find-word-backward>"],
"Find word under cursor backwards",
function () {
- rangefinder.find(buffer.getCurrentWord(), true);
+ rangefinder.find(Buffer.currentWord(buffer.focusedFrame, true), true);
rangefinder.findAgain();
});
const { options, rangefinder } = modules;
const { prefs } = require("prefs");
- // prefs.safeSet("accessibility.typeaheadfind.autostart", false);
- // The above should be sufficient, but: https://bugzilla.mozilla.org/show_bug.cgi?id=348187
- prefs.safeSet("accessibility.typeaheadfind", false);
-
options.add(["hlfind", "hlf"],
"Highlight all /find pattern matches on the current page after submission",
"boolean", false, {
});
options.add(["incfind", "if"],
- "Find a pattern incrementally as it is typed rather than awaiting <Return>",
+ "Find a pattern incrementally as it is typed rather than awaiting c_<Return>",
"boolean", true);
}
});
let doc = range.startContainer.ownerDocument;
let win = doc.defaultView;
let ranges = this.ranges.filter(function (r)
- r.window === win && RangeFind.contains(r.range, range));
+ r.window === win && RangeFind.sameDocument(r.range, range) && RangeFind.contains(r.range, range));
if (this.backward)
return ranges[ranges.length - 1];
var node = util.evaluateXPath(RangeFind.selectNodePath,
this.lastRange.commonAncestorContainer).snapshotItem(0);
if (node) {
- node.focus()
+ node.focus();
// Re-highlight collapsed selection
this.selectedRange = this.lastRange;
}
frames.push(r);
}
- let range = start.startContainer.ownerDocument.createRange();
+ let doc = start.startContainer.ownerDocument;
+
+ let range = doc.createRange();
range.setStart(start.startContainer, start.startOffset);
range.setEnd(end.startContainer, end.startOffset);
}
function rec(win) {
let doc = win.document;
- let pageRange = RangeFind.nodeRange(doc.body || doc.documentElement.lastChild);
+ let pageRange = RangeFind[doc.body ? "nodeRange" : "nodeContents"](doc.body || doc.documentElement);
backup = backup || pageRange;
let pageStart = RangeFind.endpoint(pageRange, true);
let pageEnd = RangeFind.endpoint(pageRange, false);
}
}
pushRange(pageStart, pageEnd);
+
+ let anonNodes = doc.getAnonymousNodes(doc.documentElement);
+ if (anonNodes) {
+ for (let [, elem] in iter(anonNodes)) {
+ let range = RangeFind.nodeContents(elem);
+ pushRange(RangeFind.endpoint(range, true), RangeFind.endpoint(range, false));
+ }
+ }
}
rec(win);
if (frames.length == 0)
return false;
}
},
+ nodeContents: function (node) {
+ let range = node.ownerDocument.createRange();
+ try {
+ range.selectNodeContents(node);
+ }
+ catch (e) {}
+ return range;
+ },
nodeRange: function (node) {
let range = node.ownerDocument.createRange();
try {
catch (e) {}
return range;
},
- sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
+ sameDocument: function (r1, r2) {
+ if (!(r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument))
+ return false;
+ try {
+ r1.compareBoundaryPoints(r1.START_TO_START, r2);
+ }
+ catch (e if e.result == 0x80530004 /* NS_ERROR_DOM_WRONG_DOCUMENT_ERR */) {
+ return false;
+ }
+ return true;
+ },
selectNodePath: ["a", "xhtml:a", "*[@onclick]"].map(function (p) "ancestor-or-self::" + p).join(" | ")
});
+} catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
+
endModule();
+
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
h.style.css = h.css;
this.style[prop || name] = this[prop || name];
+ if (this.onChange)
+ this.onChange();
});
}
Highlight.liveProperty("agent");
__iterator__: function () values(this.highlight).sort(function (a, b) String.localeCompare(a.class, b.class))
.iterValues(),
- _create: function (agent, args) {
+ _create: function _create(agent, args) {
let obj = Highlight.apply(Highlight, args);
if (!isArray(obj.sites))
return obj;
},
- get: function (k) this.highlight[k],
+ get: function get(k) this.highlight[k],
- set: function (key, newStyle, force, append, extend) {
+ set: function set(key, newStyle, force, append, extend) {
let [, class_, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
let highlight = this.highlight[key] || this._create(false, [key]);
* Clears all highlighting rules. Rules with default values are
* reset.
*/
- clear: function () {
+ clear: function clear() {
for (let [k, v] in Iterator(this.highlight))
this.set(k, null, true);
},
* @param {Node} node
* @param {string} group
*/
- highlightNode: function (node, group, applyBindings) {
+ highlightNode: function highlightNode(node, group, applyBindings) {
node.setAttributeNS(NS.uri, "highlight", group);
let groups = group.split(" ");
*
* @param {string} class
*/
- selector: function (class_)
+ selector: function selector(class_)
let (self = this)
class_.replace(/(^|[>\s])([A-Z][\w-]+)\b/g,
function (m, n1, hl) n1 +
* @param {string} css The rules to load. See {@link Highlights#css}.
* @param {boolean} eager When true, load all provided rules immediately.
*/
- loadCSS: function (css, eager) {
- String.replace(css, this.groupRegexp, function (m, m1, m2) m1 + " " + m2.replace(/\n\s*/g, " "))
+ loadCSS: function loadCSS(css, eager) {
+ String.replace(css, /\\\n/g, "")
+ .replace(this.groupRegexp, function (m, m1, m2) m1 + " " + m2.replace(/\n\s*/g, " "))
.split("\n").filter(function (s) /\S/.test(s) && !/^\s*\/\//.test(s))
.forEach(function (highlight) {
}
}, {
}, {
- commands: function (dactyl, modules) {
+ commands: function initCommands(dactyl, modules) {
const { autocommands, commands, completion, CommandOption, config, io } = modules;
let lastScheme;
if (!modify && /&$/.test(key))
[clear, modify, key] = [true, true, key.replace(/&$/, "")];
- dactyl.assert(!(clear && css), _("error.trailing"));
+ dactyl.assert(!(clear && css), _("error.trailingCharacters"));
if (!modify)
modules.commandline.commandOutput(
([h.class,
<span style={"text-align: center; line-height: 1em;" + h.value + style}>XXX</span>,
template.map(h.extends, template.highlight),
- template.highlightRegexp(h.value, /\b[-\w]+(?=:)/g)]
- for (h in highlight)
- if (!key || h.class.indexOf(key) > -1))));
+ template.highlightRegexp(h.value, /\b[-\w]+(?=:)|\/\*.*?\*\//g,
+ function (match) <span highlight={match[0] == "/" ? "Comment" : "Key"}>{match}</span>)
+ ]
+ for (h in highlight)
+ if (!key || h.class.indexOf(key) > -1))));
else if (!key && clear)
highlight.clear();
else if (key)
else if (args.completeArg == 1) {
let hl = highlight.get(args[0]);
if (hl)
- context.completions = [[hl.value, "Current Value"], [hl.defaultValue || "", "Default Value"]];
+ context.completions = [
+ [hl.value, _("option.currentValue")],
+ [hl.defaultValue || "", _("option.defaultValue")]
+ ];
context.fork("css", 0, completion, "css");
}
},
let group = args[0] && highlight.get(args[0]);
if (group)
context.fork("extra", 0, this, function (context) [
- [String(group.extends), "Current Value"],
- [String(group.defaultExtends) || "", "Default Value"]
+ [String(group.extends), _("option.currentValue")],
+ [String(group.defaultExtends) || "", _("option.defaultValue")]
]);
context.fork("groups", 0, completion, "highlightGroup");
}
]
});
},
- completion: function (dactyl, modules) {
+ completion: function initCompletion(dactyl, modules) {
const { completion, config, io } = modules;
+
completion.colorScheme = function colorScheme(context) {
let extRe = RegExp("\\." + config.fileExtension + "$");
context.title = ["Color Scheme", "Runtime Path"];
context.keys = { text: function (f) f.leafName.replace(extRe, ""), description: ".parent.path" };
- context.completions = array.flatten(
- io.getRuntimeDirectories("colors").map(
- function (dir) dir.readDirectory().filter(
- function (file) extRe.test(file.leafName))));
+ context.completions =
+ array.flatten(
+ io.getRuntimeDirectories("colors").map(
+ function (dir) dir.readDirectory().filter(
+ function (file) extRe.test(file.leafName))))
+ .concat([
+ { leafName: "default", parent: { path: /*L*/"Revert to builtin colorscheme" } }
+ ]);
};
context.completions = [[v.class, v.value] for (v in highlight)];
};
},
- javascript: function (dactyl, modules, window) {
+ javascript: function initJavascript(dactyl, modules, window) {
modules.JavaScript.setCompleter(["get", "set"].map(function (m) highlight[m]),
[ function (context, obj, args) Iterator(highlight.highlight) ]);
modules.JavaScript.setCompleter(["highlightNode"].map(function (m) highlight[m]),
* found it is sourced. Only the first file found (per specified path) is
* sourced unless *all* is specified, then all found files are sourced.
*
- * @param {string[]} paths An array of relative paths to source.
+ * @param {[string]} paths An array of relative paths to source.
* @param {boolean} all Whether all found files should be sourced.
*/
sourceFromRuntimePath: function sourceFromRuntimePath(paths, all) {
outer:
for (let dir in values(dirs)) {
- for (let [,path] in Iterator(paths)) {
+ for (let [, path] in Iterator(paths)) {
let file = dir.child(path);
dactyl.echomsg(_("io.searchingFor", file.path.quote()), 3);
this._scriptNames.push(file.path);
dactyl.echomsg(_("io.sourcingEnd", filename.quote()), 2);
+ dactyl.log(_("dactyl.sourced", filename), 3);
- dactyl.log("Sourced: " + filename, 3);
return context;
}
catch (e) {
dactyl.reportError(e);
- let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
+ let message = _("io.sourcingError", e.echoerr || (file ? file.path : filename) + ": " + e);
if (!params.silent)
dactyl.echoerr(message);
}
defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms");
}
}, this);
- },
+ }
}),
// TODO: there seems to be no way, short of a new component, to change
*/
listJar: function listJar(file, path) {
file = util.getFile(file);
- if (file) {
+ if (file && file.exists() && file.isFile() && file.isReadable()) {
// let jar = services.zipReader.getZip(file); Crashes.
let jar = services.ZipReader(file);
try {
yield entry;
}
finally {
- jar.close();
+ if (jar)
+ jar.close();
}
}
},
* Runs an external program.
*
* @param {File|string} program The program to run.
- * @param {string[]} args An array of arguments to pass to *program*.
+ * @param {[string]} args An array of arguments to pass to *program*.
*/
run: function (program, args, blocking) {
args = args || [];
// go directly to an absolute path or look for a relative path
// match in 'cdpath'
- // TODO: handle ../ and ./ paths
if (File.isAbsolutePath(arg)) {
io.cwd = arg;
dactyl.echomsg(io.cwd.path);
dactyl.assert(!file.exists() || args.bang, _("io.exists", file.path.quote()));
// TODO: Use a set/specifiable list here:
- let lines = [cmd.serialize().map(commands.commandToString, cmd) for (cmd in commands.iterator(true)) if (cmd.serialize)];
+ let lines = [cmd.serialize().map(commands.commandToString, cmd) for (cmd in commands.iterator()) if (cmd.serialize)];
lines = array.flatten(lines);
lines.unshift('"' + config.version + "\n");
file.write(lines.join("\n"));
}
catch (e) {
- dactyl.echoerr(_("io.notWriteable"), file.path.quote());
- dactyl.log("Could not write to " + file.path + ": " + e.message); // XXX
+ dactyl.echoerr(_("io.notWriteable", file.path.quote()));
+ dactyl.log(_("error.notWriteable", file.path, e.message)); // XXX
}
}, {
argCount: "*", // FIXME: should be "?" but kludged for proper error message
if (file.exists() && file.isDirectory() || args[0] && /\/$/.test(args[0]))
file.append(config.name + ".vim");
- dactyl.assert(!file.exists() || args.bang, "File exists");
+ dactyl.assert(!file.exists() || args.bang, _("io.exists"));
let template = util.compileMacro(<![CDATA[
" Vim syntax file
syn match <name>Notation "<[0-9A-Za-z-]\+>"
-syn match <name>Comment +".*$+ contains=<name>Todo,@Spell
syn keyword <name>Todo FIXME NOTE TODO XXX contained
syn region <name>String start="\z(["']\)" end="\z1" skip="\\\\\|\\\z1" oneline
-syn match <name>LineComment +^\s*".*$+ contains=<name>Todo,@Spell
+syn match <name>Comment +^\s*".*$+ contains=<name>Todo,@Spell
" NOTE: match vim.vim highlighting group names
hi def link <name>AutoCmd <name>Command
hi def link <name>AutoEvent Type
hi def link <name>Command Statement
-hi def link <name>Comment Comment
hi def link <name>JavaScriptDelimiter Delimiter
hi def link <name>CssDelimiter Delimiter
hi def link <name>Notation Special
-hi def link <name>LineComment Comment
+hi def link <name>Comment Comment
hi def link <name>Option PreProc
hi def link <name>SetMod <name>Option
hi def link <name>String String
commands.add(["scrip[tnames]"],
"List all sourced script names",
function () {
- modules.commandline.commandOutput(
- template.tabular(["<SNR>", "Filename"], ["text-align: right; padding-right: 1em;"],
- ([i + 1, file] for ([i, file] in Iterator(io._scriptNames))))); // TODO: add colon and remove column titles for pedantic Vim compatibility?
+ if (!io._scriptNames.length)
+ dactyl.echomsg(_("command.scriptnames.none"));
+ else
+ modules.commandline.commandOutput(
+ template.tabular(["<SNR>", "Filename"], ["text-align: right; padding-right: 1em;"],
+ ([i + 1, file] for ([i, file] in Iterator(io._scriptNames)))));
+
},
{ argCount: "0" });
commands.add(["so[urce]"],
- "Read Ex commands from a file",
+ "Read Ex commands, JavaScript or CSS from a file",
function (args) {
if (args.length > 1)
dactyl.echoerr(_("io.oneFileAllowed"));
if (modules.options["banghist"]) {
// replaceable bang and no previous command?
dactyl.assert(!/((^|[^\\])(\\\\)*)!/.test(arg) || io._lastRunCommand,
- "E34: No previous command");
+ _("command.run.noPrevious"));
arg = arg.replace(/(\\)*!/g,
function (m) /^\\(\\\\)*!$/.test(m) ? m.replace("\\!", "!") : m.replace("!", io._lastRunCommand)
let result = io.system(arg);
if (result.returnValue != 0)
- result.output += "\nshell returned " + result.returnValue;
+ result.output += "\n" + _("io.shellReturn", result.returnValue);
- modules.commandline.command = "!" + arg;
+ modules.commandline.command = args.commandName.replace("run", "$& ") + arg;
modules.commandline.commandOutput(<span highlight="CmdOutput">{result.output}</span>);
modules.autocommands.trigger("ShellCmdPost", {});
}, {
- argCount: "?", // TODO: "1" - probably not worth supporting weird Vim edge cases. The dream is dead. --djk
+ argCount: "?",
bang: true,
// This is abominably slow.
// completer: function (context) completion.shellCommand(context),
};
completion.file = function file(context, full, dir) {
+ if (/^jar:[^!]*$/.test(context.filter))
+ context.advance(4);
+
// dir == "" is expanded inside readDirectory to the current dir
function getDir(str) str.match(/^(?:.*[\/\\])?/)[0];
dir = getDir(dir || context.filter);
};
context.compare = function (a, b) b.isdir - a.isdir || String.localeCompare(a.text, b.text);
- if (modules.options["wildignore"]) {
- let wig = modules.options.get("wildignore");
- context.filters.push(function (item) item.isdir || !wig.getKey(this.name));
- }
+ if (modules.options["wildignore"])
+ context.filters.push(function (item) !modules.options.get("wildignore").getKey(item.path));
// context.background = true;
context.key = dir;
context.key = match.prefix;
context.advance(match.prefix.length + 1);
context.generate = function () iter({
- content: "Chrome content",
- locale: "Locale-specific content",
- skin: "Theme-specific content"
+ content: /*L*/"Chrome content",
+ locale: /*L*/"Locale-specific content",
+ skin: /*L*/"Theme-specific content"
});
}
}
if (!match || match.scheme === "resource" && match.path)
- if (/^(\.{0,2}|~)\/|^file:/.test(context.filter) || util.getFile(context.filter) || io.isJarURL(context.filter))
+ if (/^(\.{0,2}|~)\/|^file:/.test(context.filter)
+ || util.OS.isWindows && /^[a-z]:/i.test(context.filter)
+ || util.getFile(context.filter)
+ || io.isJarURL(context.filter))
completion.file(context, full);
});
},
options["shellcmdflag"];
options.add(["wildignore", "wig"],
- "List of file patterns to ignore when completing file names",
+ "List of path name patterns to ignore when completing files and directories",
"regexplist", "");
}
});
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("javascript", {
exports: ["JavaScript", "javascript"],
- use: ["services", "template", "util"]
+ use: ["messages", "services", "template", "util"]
}, this);
let isPrototypeOf = Object.prototype.isPrototypeOf;
this.window = window;
init.supercall(this);
- },
+ }
}),
globals: Class.memoize(function () [
- [this.modules.userContext, "Global Variables"],
+ [this.modules.userContext, /*L*/"Global Variables"],
[this.modules, "modules"],
[this.window, "window"]
]),
lazyInit: true,
- newContext: function () this.modules.newContext(this.modules.userContext),
+ newContext: function () this.modules.newContext(this.modules.userContext, true),
get completers() JavaScript.completers, // For backward compatibility
if (obj == null)
return;
- let seen = isinstance(obj, ["Sandbox"]) ? set(JavaScript.magicalNames) : {};
+ let seen = isinstance(obj, ["Sandbox"]) ? Set(JavaScript.magicalNames) : {};
let globals = values(toplevel && this.window === obj ? this.globalNames : []);
if (toplevel && isObject(obj) && "wrappedJSObject" in obj)
- if (!set.add(seen, "wrappedJSObject"))
+ if (!Set.add(seen, "wrappedJSObject"))
yield "wrappedJSObject";
for (let key in iter(globals, properties(obj, !toplevel, true)))
- if (!set.add(seen, key))
+ if (!Set.add(seen, key))
yield key;
// Properties aren't visible in an XPCNativeWrapper until
// they're accessed.
for (let key in properties(this.getKey(obj, "wrappedJSObject"), !toplevel, true))
try {
- if (key in obj && !set.has(seen, key))
+ if (key in obj && !Set.has(seen, key))
yield key;
}
catch (e) {}
return cache[key];
context[JavaScript.EVAL_TMP] = tmp;
- context[JavaScript.EVAL_EXPORT] = function export_(obj) cache[key] = obj;
try {
- if (tmp != null) // Temporary hack until bug 609949 is fixed.
- this.modules.dactyl.userEval(JavaScript.EVAL_EXPORT + "(" + arg + ")", context, "[Command Line Completion]", 1);
- else
- cache[key] = this.modules.dactyl.userEval(arg, context, "[Command Line Completion]", 1);
+ cache[key] = this.modules.dactyl.userEval(arg, context, /*L*/"[Command Line Completion]", 1);
return cache[key];
}
catch (e) {
util.reportError(e);
- this.context.message = "Error: " + e;
+ this.context.message = _("error.error", e);
return null;
}
finally {
if (this._top.char != arg) {
this.context.highlight(this._top.offset, this._i - this._top.offset, "SPELLCHECK");
- throw Error("Invalid JS");
+ throw Error(/*L*/"Invalid JS");
}
// The closing character of this stack frame will have pushed a new
if (this._checkFunction(prev, dot, cacheKey))
return [];
if (prev != statement && obj == null) {
- this.context.message = "Error: " + cacheKey.quote() + " is " + String(obj);
+ this.context.message = /*L*/"Error: " + cacheKey.quote() + " is " + String(obj);
return [];
}
let end = (frame == -1 ? this._lastIdx : this._get(frame + 1).offset);
this._cacheKey = null;
- let obj = [[this.cache.evalContext, "Local Variables"]].concat(this.globals);
+ let obj = [[this.cache.evalContext, /*L*/"Local Variables"]].concat(this.globals);
// Is this an object dereference?
if (dot < statement) // No.
dot = statement - 1;
const self = this;
if (!getOwnPropertyNames && !services.debugger.isOn && !this.context.message)
- this.context.message = "For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
+ this.context.message = /*L*/"For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
let base = this.context.fork("js", this._top.offset);
base.forceAnchored = true;
objects.forEach(function (obj) {
obj.ctxt_p.split(obj[1] + "/anchored", this, function (context) {
context.anchored = true;
- context.title[0] += " (prototypes)";
+ context.title[0] += /*L*/" (prototypes)";
});
});
objects.forEach(function (obj) {
obj.ctxt_t.split(obj[1] + "/unanchored", this, function (context) {
context.anchored = false;
- context.title[0] += " (substrings)";
+ context.title[0] += /*L*/" (substrings)";
context.filters.push(unanchored);
});
});
objects.forEach(function (obj) {
obj.ctxt_p.split(obj[1] + "/unanchored", this, function (context) {
context.anchored = false;
- context.title[0] += " (prototype substrings)";
+ context.title[0] += /*L*/" (prototype substrings)";
context.filters.push(unanchored);
});
});
// Wait for a keypress before completing when there's no key
if (!this.context.tabPressed && key == "" && obj.length > 1) {
this.context.waitingForTab = true;
- this.context.message = "Waiting for key press";
+ this.context.message = _("completion.waitingForKeyPress");
return null;
}
"Math", "NaN", "Namespace", "Number", "Object", "Proxy", "QName",
"ROCSSPrimitiveValue", "RangeError", "ReferenceError", "RegExp",
"StopIteration", "String", "SyntaxError", "TypeError", "URIError",
- "Uint16Array", "Uint32Array", "Uint8Array", "XML",
- "XMLHttpProgressEvent", "XMLList", "XMLSerializer", "XPCNativeWrapper",
- "XPCSafeJSWrapper", "XULControllers", "constructor", "decodeURI",
- "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape",
- "eval", "isFinite", "isNaN", "isXMLName", "parseFloat", "parseInt",
- "undefined", "unescape", "uneval"
+ "Uint16Array", "Uint32Array", "Uint8Array", "XML", "XMLHttpProgressEvent",
+ "XMLList", "XMLSerializer", "XPCNativeWrapper", "XPCSafeJSWrapper",
+ "XULControllers", "constructor", "decodeURI", "decodeURIComponent",
+ "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN",
+ "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval"
].concat([k.substr(6) for (k in keys(Ci)) if (/^nsIDOM/.test(k))])
.concat([k.substr(3) for (k in keys(Ci)) if (/^nsI/.test(k))])
.concat(this.magicalNames)
}, {
EVAL_TMP: "__dactyl_eval_tmp",
- EVAL_EXPORT: "__dactyl_eval_export",
/**
* A map of argument completion functions for named methods. The
* time they are accessed, so they should be accessed
* judiciously.
*
- * @param {function|function[]} funcs The functions for which to
+ * @param {function|[function]} funcs The functions for which to
* install the completers.
- * @param {function[]} completers An array of completer
+ * @param {[function]} completers An array of completer
* functions.
*/
setCompleter: function (funcs, completers) {
modes.addMode("REPL", {
description: "JavaScript Read Eval Print Loop",
- bases: [modes.COMMAND_LINE]
+ bases: [modes.COMMAND_LINE],
+ displayName: Class.memoize(function () this.name)
});
},
commandline: function initCommandLine(dactyl, modules, window) {
this.js.newContext = function newContext() modules.newContext(self.context, !sandbox);
this.js.globals = [
- [this.context, "REPL Variables"],
- [context, "REPL Global"]
+ [this.context, /*L*/"REPL Variables"],
+ [context, /*L*/"REPL Global"]
].concat(this.js.globals.filter(function ([global]) isPrototypeOf.call(global, context)));
if (!isPrototypeOf.call(modules.jsmodules, context))
this.repl = REPL(this.context);
},
+
open: function open(context) {
- this.updatePrompt();
modules.mow.echo(this.repl);
this.widgets.message = null;
open.superapply(this, arguments);
+ this.updatePrompt();
},
complete: function complete(context) {
mode: modes.REPL,
+ get completionList() this.widgets.statusbar.commandline.id,
+
accept: function accept() {
- dactyl.trapErrors(function () { this.repl.addOutput(this.command) }, this);
+ dactyl.trapErrors(function () { this.repl.addOutput(this.command); }, this);
this.completions.cleanup();
this.history.save();
commands.add(["javas[cript]", "js"],
"Evaluate a JavaScript string",
function (args) {
- modules.commandline;
-
if (args[0] && !args.bang)
dactyl.userEval(args[0]);
else {
.open();
}
}, {
+ argCount: "?",
bang: true,
completer: function (context) modules.completion.javascript(context),
hereDoc: true,
let seen = {};
for (let { key } in this.iterate()) {
- if (!set.add(seen, key))
+ if (!Set.add(seen, key))
this._[key] = this[key] = {
__noSuchMethod__: function __(prop, args) self._.apply(self, [prop].concat(args))
};
catch (e) {}
// Report error so tests fail, but don't throw
- if (arguments.length < 2)
+ if (arguments.length < 2) // Do *not* localize these strings
util.reportError(Error("Invalid locale string: " + value));
return arguments.length > 1 ? default_ : value;
},
catch (e) {}
// Report error so tests fail, but don't throw
- if (arguments.length < 3)
+ if (arguments.length < 3) // Do *not* localize these strings
util.reportError(Error("Invalid locale string: " + value));
return arguments.length > 2 ? default_ : value;
}
}, {
Localized: Class("Localized", Class.Property, {
init: function init(prop, obj) {
- let _prop = "localized_" + prop;
+ let _prop = "unlocalized_" + prop;
if (this.initialized) {
/*
if (config.locale === "en-US")
- return { configurable: true, enumerable: true, value: null, writable: true };
+ return { configurable: true, enumerable: true, value: this.default, writable: true };
*/
+ if (!Set.has(obj, "localizedProperties"))
+ obj.localizedProperties = { __proto__: obj.localizedProperties };
+ obj.localizedProperties[prop] = true;
+
obj[_prop] = this.default;
return {
get: function get() {
function getter(key, default_) function getter() messages.get([name, key].join("."), default_);
- let name = [this.constructor.className.toLowerCase(), this.identifier || this.name, prop].join(".");
- if (!isObject(value))
- value = messages.get(name, value)
- else if (isArray(value))
- // Deprecated
- iter(value).forEach(function ([k, v]) {
- if (isArray(v))
- memoize(v, 1, getter(v[0], v[1]));
- else
- memoize(value, k, getter(k, v));
- });
- else
- iter(value).forEach(function ([k, v]) {
- memoize(value, k, function () messages.get([name, k].join("."), v));
- });
+ if (value != null) {
+ var name = [this.constructor.className.toLowerCase(), this.identifier || this.name, prop].join(".");
+ if (!isObject(value))
+ value = messages.get(name, value);
+ else if (isArray(value))
+ // Deprecated
+ iter(value).forEach(function ([k, v]) {
+ if (isArray(v))
+ memoize(v, 1, getter(v[0], v[1]));
+ else
+ memoize(value, k, getter(k, v));
+ });
+ else
+ iter(value).forEach(function ([k, v]) {
+ memoize(value, k, function () messages.get([name, k].join("."), v));
+ });
+ }
return Class.replaceProperty(this, prop, value);
},
+
set: function set(val) this[_prop] = val
- }
+ };
}
this.default = prop;
this.initialized = true;
defineModule("options", {
exports: ["Option", "Options", "ValueError", "options"],
require: ["messages", "storage"],
- use: ["commands", "completion", "prefs", "services", "styles", "template", "util"]
+ use: ["commands", "completion", "config", "prefs", "services", "styles", "template", "util"]
}, this);
/** @scope modules */
* A class representing configuration options. Instances are created by the
* {@link Options} class.
*
- * @param {string[]} names The names by which this option is identified.
+ * @param {[string]} names The names by which this option is identified.
* @param {string} description A short one line description of the option.
* @param {string} type The option's value data type (see {@link Option#type}).
* @param {string} defaultValue The default value for this option.
* @private
*/
var Option = Class("Option", {
- init: function init(names, description, type, defaultValue, extraInfo) {
+ init: function init(modules, names, description, defaultValue, extraInfo) {
+ this.modules = modules;
this.name = names[0];
this.names = names;
this.realNames = names;
- this.type = type;
this.description = description;
- if (this.type in Option.getKey)
- this.getKey = Option.getKey[this.type];
-
- if (this.type in Option.parse)
- this.parse = Option.parse[this.type];
-
- if (this.type in Option.stringify)
- this.stringify = Option.stringify[this.type];
-
- if (this.type in Option.domains)
- this.domains = Option.domains[this.type];
-
- if (this.type in Option.testValues)
- this.testValues = Option.testValues[this.type];
-
- this._op = Option.ops[this.type];
-
- // Need to trigger setter
- if (extraInfo && "values" in extraInfo && !extraInfo.__lookupGetter__("values")) {
- this.values = extraInfo.values;
- delete extraInfo.values;
- }
-
if (extraInfo)
- update(this, extraInfo);
+ this.update(extraInfo);
- if (set.has(this.modules.config.defaults, this.name))
+ if (Set.has(this.modules.config.defaults, this.name))
defaultValue = this.modules.config.defaults[this.name];
if (defaultValue !== undefined) {
- if (this.type == "string")
+ if (this.type === "string")
defaultValue = Commands.quote(defaultValue);
if (isObject(defaultValue))
this.globalValue = this.defaultValue;
},
+ magicalProperties: Set(["cleanupValue"]),
+
/**
* @property {string} This option's description, as shown in :listoptions.
*/
get isDefault() this.stringValue === this.stringDefaultValue,
+ /** @property {value} The value to reset this option to at cleanup time. */
+ get cleanupValue() options.cleanupPrefs.get(this.name),
+ set cleanupValue(value) {
+ if (options.cleanupPrefs.get(this.name) == null)
+ options.cleanupPrefs.set(this.name, value);
+ },
+
/** @property {value} The option's global value. @see #scope */
get globalValue() { try { return options.store.get(this.name, {}).value; } catch (e) { util.reportError(e); throw e; } },
set globalValue(val) { options.store.set(this.name, { value: val, time: Date.now() }); },
* "charlist" or "stringlist" or else unchanged.
*
* @param {value} value The option value.
- * @returns {value|string[]}
+ * @returns {value|[string]}
*/
parse: function parse(value) Option.dequote(value),
/**
* Returns *values* packed in the appropriate format for the option type.
*
- * @param {value|string[]} values The option value.
+ * @param {value|[string]} values The option value.
* @returns {value}
*/
stringify: function stringify(vals) Commands.quote(vals),
*
* @param {number} scope The scope to return these values from (see
* {@link Option#scope}).
- * @returns {value|string[]}
+ * @returns {value|[string]}
*/
get: function get(scope) {
if (scope) {
set stringValue(value) this.value = this.parse(value),
get stringDefaultValue() this.stringify(this.defaultValue),
+ set stringDefaultValue(val) this.defaultValue = this.parse(val),
getKey: function getKey(key) undefined,
* Sets the option's value using the specified set *operator*.
*
* @param {string} operator The set operator.
- * @param {value|string[]} values The value (or values) to apply.
+ * @param {value|[string]} values The value (or values) to apply.
* @param {number} scope The scope to apply this value to (see
* {@link #scope}).
* @param {boolean} invert Whether this is an invert boolean operation.
try {
var newValues = this._op(operator, values, scope, invert);
if (newValues == null)
- return "Operator " + operator + " not supported for option type " + this.type;
+ return _("option.operatorNotSupported", operator, this.type);
if (!this.isValidValue(newValues))
return this.invalidArgument(str || this.stringify(values), operator);
/** @property {string} The option's canonical name. */
name: null,
- /** @property {string[]} All names by which this option is identified. */
+ /** @property {[string]} All names by which this option is identified. */
names: null,
/**
*/
scope: 1, // Option.SCOPE_GLOBAL // XXX set to BOTH by default someday? - kstep
- cleanupValue: null,
-
/**
* @property {function(CompletionContext, Args)} This option's completer.
* @see CompletionContext
*/
- completer: function completer(context) {
+ completer: function completer(context, extra) {
+ if (/map$/.test(this.type) && extra.value == null)
+ return;
+
if (this.values)
context.completions = this.values;
},
let [, bang, filter] = /^(!?)(.*)/.exec(pattern);
filter = Option.dequote(filter);
+ let quote = this.keepQuotes ? util.identity : Option.quote;
+
return update(Styles.matchFilter(filter), {
bang: bang,
filter: filter,
result: result !== undefined ? result : !bang,
- toString: function toString() this.bang + Option.quote(this.filter) +
- (typeof this.result === "boolean" ? "" : ":" + Option.quote(this.result)),
+ toString: function toString() this.bang + Option.quote(this.filter, /:/) +
+ (typeof this.result === "boolean" ? "" : ":" + quote(this.result)),
});
},
regexplist: function regexplist(k, default_) {
for (let re in values(this.value))
- if (re(k))
+ if ((re.test || re).call(re, k))
return re.result;
return arguments.length > 1 ? default_ : null;
},
parse: {
number: function (value) let (val = Option.dequote(value))
- Option.validIf(Number(val) % 1 == 0, "Integer value required") && parseInt(val),
+ Option.validIf(Number(val) % 1 == 0, _("option.intRequired")) && parseInt(val),
boolean: function boolean(value) Option.dequote(value) == "true" || value == true ? true : false,
return [];
if (!isArray(value))
value = Option.splitList(value, true);
- return value.map(Option.parseSite);
+ return value.map(Option.parseSite, this);
},
stringmap: function stringmap(value) array.toObject(
if (v.length > count)
return prev = parse.call(this, filter, val);
else {
- util.assert(prev, "Syntax error", false);
+ util.assert(prev, _("error.syntaxError"), false);
prev.result += "," + v;
}
}, this))
let value = parseInt(values);
util.assert(Number(values) % 1 == 0,
- "E521: Number required after =: " + this.name + "=" + values);
+ _("command.set.numberRequired", this.name, values));
switch (operator) {
case "+":
stringlist: function stringlist(operator, values, scope, invert) {
values = Array.concat(values);
+ function uniq(ary) {
+ let seen = {};
+ return ary.filter(function (elem) !Set.add(seen, elem));
+ }
+
switch (operator) {
case "+":
- return array.uniq(Array.concat(this.value, values), true);
+ return uniq(Array.concat(this.value, values), true);
case "^":
// NOTE: Vim doesn't prepend if there's a match in the current value
- return array.uniq(Array.concat(values, this.value), true);
+ return uniq(Array.concat(values, this.value), true);
case "-":
- return this.value.filter(function (item) values.indexOf(item) == -1);
+ return this.value.filter(function (item) !Set.has(this, item), Set(values));
case "=":
if (invert) {
- let keepValues = this.value.filter(function (item) values.indexOf(item) == -1);
- let addValues = values.filter(function (item) this.value.indexOf(item) == -1, this);
+ let keepValues = this.value.filter(function (item) !Set.has(this, item), Set(values));
+ let addValues = values.filter(function (item) !Set.has(this, item), Set(this.value));
return addValues.concat(keepValues);
}
return values;
* Validates the specified *values* against values generated by the
* option's completer function.
*
- * @param {value|string[]} values The value or array of values to validate.
+ * @param {value|[string]} values The value or array of values to validate.
* @returns {boolean}
*/
validateCompleter: function validateCompleter(values) {
}
if (isArray(acceptable))
- acceptable = set(acceptable.map(function ([k]) k));
+ acceptable = Set(acceptable.map(function ([k]) k));
if (this.type === "regexpmap" || this.type === "sitemap")
- return Array.concat(values).every(function (re) set.has(acceptable, re.result));
+ return Array.concat(values).every(function (re) Set.has(acceptable, re.result));
- return Array.concat(values).every(set.has(acceptable));
- }
+ return Array.concat(values).every(Set.has(acceptable));
+ },
+
+ types: {}
});
+["Boolean",
+ "Charlist",
+ "Number",
+ "RegexpList",
+ "RegexpMap",
+ "SiteList",
+ "SiteMap",
+ "String",
+ "StringList",
+ "StringMap"].forEach(function (name) {
+ let type = name.toLowerCase();
+ let class_ = Class(name + "Option", Option, {
+ type: type,
+
+ _op: Option.ops[type]
+ });
+
+ if (type in Option.getKey)
+ class_.prototype.getKey = Option.getKey[type];
+
+ if (type in Option.parse)
+ class_.prototype.parse = Option.parse[type];
+
+ if (type in Option.stringify)
+ class_.prototype.stringify = Option.stringify[type];
+
+ if (type in Option.domains)
+ class_.prototype.domains = Option.domains[type];
+
+ if (type in Option.testValues)
+ class_.prototype.testValues = Option.testValues[type];
+
+ Option.types[type] = class_;
+ this[class_.className] = class_;
+ EXPORTED_SYMBOLS.push(class_.className);
+}, this);
+
/**
* @instance options
*/
this.needInit = [];
this._options = [];
this._optionMap = {};
- this.Option = Class("Option", Option, { modules: modules });
storage.newMap("options", { store: false });
storage.addObserver("options", function optionObserver(key, event, option) {
if (event == "change" && opt)
opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
}, window);
+
+ services["dactyl:"].pages["options.dtd"] = function () [null,
+ util.makeDTD(
+ iter(([["option", o.name, "default"].join("."),
+ o.type === "string" ? o.defaultValue.replace(/'/g, "''") :
+ o.value === true ? "on" :
+ o.value === false ? "off" : o.stringDefaultValue]
+ for (o in self)),
+
+ ([["option", o.name, "type"].join("."), o.type] for (o in self)),
+
+ config.dtd))];
},
dactyl: dactyl,
* @param {number} scope Only list options in this scope (see
* {@link Option#scope}).
*/
- list: function (filter, scope) {
+ list: function list(filter, scope) {
if (!scope)
scope = Option.SCOPE_BOTH;
cleanup: function cleanup() {
for (let opt in this)
if (opt.cleanupValue != null)
- opt.value = opt.parse(opt.cleanupValue);
+ opt.stringValue = opt.cleanupValue;
},
/**
* Adds a new option.
*
- * @param {string[]} names All names for the option.
+ * @param {[string]} names All names for the option.
* @param {string} description A description of the option.
* @param {string} type The option type (see {@link Option#type}).
* @param {value} defaultValue The option's default value.
* {@link Map#extraInfo}).
* @optional
*/
- add: function (names, description, type, defaultValue, extraInfo) {
+ add: function add(names, description, type, defaultValue, extraInfo) {
const self = this;
if (!extraInfo)
let closure = function () self._optionMap[name];
- memoize(this._optionMap, name, function () self.Option(names, description, type, defaultValue, extraInfo));
+ memoize(this._optionMap, name, function () Option.types[type](modules, names, description, defaultValue, extraInfo));
for (let alias in values(names.slice(1)))
memoize(this._optionMap, alias, closure);
allPrefs: deprecated("prefs.getNames", function allPrefs() prefs.getNames.apply(prefs, arguments)),
getPref: deprecated("prefs.get", function getPref() prefs.get.apply(prefs, arguments)),
invertPref: deprecated("prefs.invert", function invertPref() prefs.invert.apply(prefs, arguments)),
- listPrefs: deprecated("prefs.list", function listPrefs() { commandline.commandOutput(prefs.list.apply(prefs, arguments)); }),
+ listPrefs: deprecated("prefs.list", function listPrefs() { this.modules.commandline.commandOutput(prefs.list.apply(prefs, arguments)); }),
observePref: deprecated("prefs.observe", function observePref() prefs.observe.apply(prefs, arguments)),
popContext: deprecated("prefs.popContext", function popContext() prefs.popContext.apply(prefs, arguments)),
pushContext: deprecated("prefs.pushContext", function pushContext() prefs.pushContext.apply(prefs, arguments)),
setPref: deprecated("prefs.set", function setPref() prefs.set.apply(prefs, arguments)),
withContext: deprecated("prefs.withContext", function withContext() prefs.withContext.apply(prefs, arguments)),
+ cleanupPrefs: Class.memoize(function () localPrefs.Branch("cleanup.option.")),
+
+ cleanup: function cleanup(reason) {
+ if (~["disable", "uninstall"].indexOf(reason))
+ this.cleanupPrefs.resetBranch();
+ },
+
/**
* Returns the option with *name* in the specified *scope*.
*
}
if (matches) {
- res.option = this.get(res.name, res.scope);
- if (!res.option && (res.option = this.get(prefix + res.name, res.scope))) {
+ if (res.option = this.get(res.name, res.scope)) {
+ if (prefix === "no" && res.option.type !== "boolean")
+ res.option = null;
+ }
+ else if (res.option = this.get(prefix + res.name, res.scope)) {
res.name = prefix + res.name;
prefix = "";
}
format: {
description: function (map) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <>
{options.get("passkeys").has(map.name)
- ? <span highlight="URLExtra">(passed by {template.helpLink("'passkeys'")})</span>
+ ? <span highlight="URLExtra">({
+ tempate.linkifyHelp(_("option.passkeys.passedBy"))
+ })</span>
: <></>}
{template.linkifyHelp(map.description)}
</>)
format: {
description: function (opt) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <>
{opt.scope == Option.SCOPE_LOCAL
- ? <span highlight="URLExtra">(buffer local)</span> : ""}
+ ? <span highlight="URLExtra">({_("option.bufferLocal")})</span> : ""}
{template.linkifyHelp(opt.description)}
</>),
help: function (opt) "'" + opt.name + "'"
let list = [];
function flushList() {
- let names = set(list.map(function (opt) opt.option ? opt.option.name : ""));
+ let names = Set(list.map(function (opt) opt.option ? opt.option.name : ""));
if (list.length)
if (list.some(function (opt) opt.all))
- options.list(function (opt) !(list[0].onlyNonDefault && opt.isDefault) , list[0].scope);
+ options.list(function (opt) !(list[0].onlyNonDefault && opt.isDefault), list[0].scope);
else
- options.list(function (opt) set.has(names, opt.name), list[0].scope);
+ options.list(function (opt) Set.has(names, opt.name), list[0].scope);
list = [];
}
}
if (name == "all" && reset)
- modules.commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
+ modules.commandline.input(_("pref.prompt.resetAll", config.host) + " ",
function (resp) {
if (resp == "yes")
for (let pref in values(prefs.getNames()))
},
{ promptHighlight: "WarningMsg" });
else if (name == "all")
- commandline.commandOutput(prefs.list(onlyNonDefault, ""));
+ modules.commandline.commandOutput(prefs.list(onlyNonDefault, ""));
else if (reset)
prefs.reset(name);
else if (invertBoolean)
}
let opt = modules.options.parseOpt(arg, modifiers);
- util.assert(opt, "Error parsing :set command: " + arg);
+ util.assert(opt, _("command.set.errorParsing", arg));
+ util.assert(!opt.error, _("command.set.errorParsing", opt.error));
let option = opt.option;
- util.assert(option != null || opt.all, "E518: Unknown option: " + opt.name);
+ util.assert(option != null || opt.all, _("command.set.unknownOption", opt.name));
// reset a variable to its default value
if (opt.reset) {
context.pushProcessor(0, function (item, text, next) next(item, text.substr(0, 100)));
context.completions = [
- [prefs.get(filter), "Current Value"],
- [prefs.defaults.get(filter), "Default Value"]
+ [prefs.get(filter), _("option.currentValue")],
+ [prefs.defaults.get(filter), _("option.defaultValue")]
].filter(function (k) k[0] != null);
return null;
}
if (context.filter.indexOf("=") == -1) {
if (false && prefix)
context.filters.push(function ({ item }) item.type == "boolean" || prefix == "inv" && isArray(item.values));
- return completion.option(context, opt.scope, prefix);
+ return completion.option(context, opt.scope, opt.name == "inv" ? opt.name : prefix);
}
function error(length, message) {
let option = opt.option;
if (!option)
- return error(opt.name.length, "No such option: " + opt.name);
+ return error(opt.name.length, _("option.noSuch", opt.name));
context.advance(context.filter.indexOf("="));
if (option.type == "boolean")
- return error(context.filter.length, "Trailing characters");
+ return error(context.filter.length, _("error.trailingCharacters"));
context.advance(1);
if (opt.error)
context.title = ["Extra Completions"];
context.pushProcessor(0, function (item, text, next) next(item, text.substr(0, 100)));
context.completions = [
- [option.stringValue, "Current value"],
- [option.stringDefaultValue, "Default value"]
+ [option.stringValue, _("option.currentValue")],
+ [option.stringDefaultValue, _("option.defaultValue")]
].filter(function (f) f[0] !== "");
context.quote = ["", util.identity, ""];
});
// Fill in the current values if we're removing
if (opt.operator == "-" && isArray(opt.values)) {
- let have = set([i.text for (i in values(context.allItems.items))]);
+ let have = Set([i.text for (i in values(context.allItems.items))]);
context = context.fork("current-values", 0);
context.anchored = optcontext.anchored;
context.maxItems = optcontext.maxItems;
- context.filters.push(function (i) !set.has(have, i.text));
+ context.filters.push(function (i) !Set.has(have, i.text));
modules.completion.optionValue(context, opt.name, opt.operator, null,
function (context) {
context.generate = function () option.value.map(function (o) [o, ""]);
if (str.text().length() == str.*.length())
dactyl.echomsg(_("variable.none"));
else
- dactyl.echo(str, commandline.FORCE_MULTILINE);
+ dactyl.echo(str, modules.commandline.FORCE_MULTILINE);
return;
}
util.assert(scope == "g:" || scope == null,
_("command.let.illegalVar", scope + name));
- util.assert(set.has(globalVariables, name) || (expr && !op),
+ util.assert(Set.has(globalVariables, name) || (expr && !op),
_("command.let.undefinedVar", fullName));
if (!expr)
function (args) {
for (let [, name] in args) {
name = name.replace(/^g:/, ""); // throw away the scope prefix
- if (!set.has(dactyl._globalVariables, name)) {
+ if (!Set.has(dactyl._globalVariables, name)) {
if (!args.bang)
dactyl.echoerr(_("command.let.noSuch", name));
return;
var newValues = opt.parse(context.filter);
}
catch (e) {
- context.message = "Error: " + e;
+ context.message = _("error.error", e);
context.completions = [];
return;
}
context.filters.push(function (i) curValues.indexOf(i.text) == -1);
if (op == "-")
context.filters.push(function (i) curValues.indexOf(i.text) > -1);
+
+ memoize(extra, "values", function () {
+ if (op == "+")
+ return curValues.concat(newValues);
+ if (op == "-")
+ return curValues.filter(function (v) newValues.indexOf(val) == -1);
+ return newValues;
+ });
}
let res = completer.call(opt, context, extra);
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("overlay", {
exports: ["ModuleBase"],
- require: ["config", "services", "util"]
+ require: ["config", "io", "services", "util"]
}, this);
/**
const start = Date.now();
const deferredInit = { load: {} };
- const seen = set();
- const loaded = set();
+ const seen = Set();
+ const loaded = Set();
modules.loaded = loaded;
function load(module, prereq, frame) {
return;
if (module.className in seen)
throw Error("Module dependency loop.");
- set.add(seen, module.className);
+ Set.add(seen, module.className);
for (let dep in values(module.requires))
load(Module.constructors[dep], module.className);
});
}
defineModule.modules.forEach(function defModule(mod) {
- let names = set(Object.keys(mod.INIT));
+ let names = Set(Object.keys(mod.INIT));
if ("init" in mod.INIT)
- set.add(names, "init");
+ Set.add(names, "init");
keys(names).forEach(function (name) { deferInit(name, mod.INIT, mod); });
});
}, this);
var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), {
- SAVED: "extensions.dactyl.saved.",
+ ORIGINAL: "extensions.dactyl.original.",
RESTORE: "extensions.dactyl.restore.",
+ SAVED: "extensions.dactyl.saved.",
INIT: {},
- init: function (branch, defaults) {
+ init: function init(branch, defaults) {
this._prefContexts = [];
this.branch = services.pref[defaults ? "getDefaultBranch" : "getBranch"](branch || "");
- if (this.branch instanceof Ci.nsIPrefBranch2)
- this.branch.QueryInterface(Ci.nsIPrefBranch2);
+ this.branch instanceof Ci.nsIPrefBranch2;
this.defaults = defaults ? this : this.constructor(branch, true);
+ this.branches = memoize({
+ __proto__: this,
+ get original() this.constructor(this.ORIGINAL + this.root),
+ get restore() this.constructor(this.RESTORE + this.root),
+ get saved() this.constructor(this.SAVED + this.root),
+ });
+
if (!defaults)
this.restore();
this._observers = {};
},
- cleanup: function cleanup() {
+ cleanup: function cleanup(reason) {
if (this.defaults != this)
this.defaults.cleanup();
+
this._observers = {};
if (this.observe) {
this.branch.removeObserver("", this);
this.observe.unregister();
delete this.observe;
}
+
+ if (this == prefs) {
+ if (~["uninstall", "disable"].indexOf(reason)) {
+ for (let name in values(this.branches.saved.getNames()))
+ this.safeReset(name, null, true);
+
+ this.branches.original.resetBranch();
+ this.branches.saved.resetBranch();
+ }
+
+ if (reason == "uninstall" && this == prefs)
+ localPrefs.resetBranch();
+ }
},
+ /**
+ * Returns the full name of this object's preference branch.
+ */
+ get root() this.branch.root,
+
+ /**
+ * Returns a new Prefs instance for the sub-branch *branch* of this
+ * branch.
+ *
+ * @param {string} branch The branch to branch to.
+ * @returns {Prefs}
+ */
+ Branch: function Branch(branch) Prefs(this.root + branch),
+
observe: null,
observers: {
"nsPref:changed": function (subject, data) {
* @param {function(object)} callback The callback, called with the
* new value of the preference whenever it changes.
*/
- watch: function (pref, callback, strong) {
+ watch: function watch(pref, callback, strong) {
if (!this.observe) {
util.addObserver(this);
this.branch.addObserver("", this, false);
}
};
- return template.options(config.host + " Preferences", prefs.call(this));
+ return template.options(_("pref.hostPreferences", config.host), prefs.call(this));
},
/**
* @param {string} branch The branch in which to search preferences.
* @default ""
*/
- getNames: function (branch) this.branch.getChildList(branch || "", { value: 0 }),
+ getNames: function getNames(branch) this.branch.getChildList(branch || "", { value: 0 }),
- _checkSafe: function (name, message, value) {
+ _checkSafe: function _checkSafe(name, message, value) {
let curval = this.get(name, null);
+
+ if (this.branches.original.get(name) == null)
+ this.branches.original.set(name, curval, true);
+
if (arguments.length > 2 && curval === value)
return;
+
let defval = this.defaults.get(name, null);
- let saved = this.get(this.SAVED + name);
+ let saved = this.branches.saved.get(name);
if (saved == null && curval != defval || saved != null && curval != saved) {
- let msg = "Warning: setting preference " + name + ", but it's changed from its default value.";
+ let msg = _("pref.safeSet.warnChanged", name);
if (message)
msg = template.linkifyHelp(msg + " " + message);
util.dactyl.warn(msg);
*
* @param {string} name The preference name.
* @param {value} value The new preference value.
+ * @param {boolean} silent Ignore errors.
*/
- safeReset: function (name, message) {
+ safeReset: function safeReset(name, message, silent) {
this._checkSafe(name, message);
- this.reset(name);
- this.reset(this.SAVED + name);
+ this.set(name, this.branches.original.get(name), silent);
+ this.branches.original.reset(name);
+ this.branches.saved.reset(name);
},
/**
* @param {string} name The preference name.
* @param {value} value The new preference value.
*/
- safeSet: function (name, value, message, skipSave) {
+ safeSet: function safeSet(name, value, message, skipSave) {
this._checkSafe(name, message, value);
this.set(name, value);
- this[skipSave ? "reset" : "set"](this.SAVED + name, value);
+ this.branches.saved[skipSave ? "reset" : "set"](name, value);
},
/**
*
* @param {string} name The preference name.
* @param {value} value The new preference value.
+ * @param {boolean} silent Ignore errors.
*/
- set: function (name, value) {
- if (this._prefContexts.length) {
- let val = this.get(name, null);
- if (val != null)
- this._prefContexts[this._prefContexts.length - 1][name] = val;
- }
+ set: function set(name, value, silent) {
+ if (this._prefContexts.length)
+ this._prefContexts[this._prefContexts.length - 1][name] = this.get(name, null);
function assertType(needType)
util.assert(type === Ci.nsIPrefBranch.PREF_INVALID || type === needType,
type === Ci.nsIPrefBranch.PREF_INT
- ? "E521: Number required after =: " + name + "=" + value
- : "E474: Invalid argument: " + name + "=" + value);
+ ? /*L*/"E521: Number required after =: " + name + "=" + value
+ : /*L*/"E474: Invalid argument: " + name + "=" + value);
let type = this.branch.getPrefType(name);
- switch (typeof value) {
- case "string":
- assertType(Ci.nsIPrefBranch.PREF_STRING);
-
- this.branch.setComplexValue(name, Ci.nsISupportsString, services.String(value));
- break;
- case "number":
- assertType(Ci.nsIPrefBranch.PREF_INT);
-
- this.branch.setIntPref(name, value);
- break;
- case "boolean":
- assertType(Ci.nsIPrefBranch.PREF_BOOL);
-
- this.branch.setBoolPref(name, value);
- break;
- default:
- throw FailedAssertion("Unknown preference type: " + typeof value + " (" + name + "=" + value + ")");
+ try {
+ switch (typeof value) {
+ case "string":
+ assertType(Ci.nsIPrefBranch.PREF_STRING);
+
+ this.branch.setComplexValue(name, Ci.nsISupportsString, services.String(value));
+ break;
+ case "number":
+ assertType(Ci.nsIPrefBranch.PREF_INT);
+
+ this.branch.setIntPref(name, value);
+ break;
+ case "boolean":
+ assertType(Ci.nsIPrefBranch.PREF_BOOL);
+
+ this.branch.setBoolPref(name, value);
+ break;
+ default:
+ if (value == null && this != this.defaults)
+ this.reset(name);
+ else
+ throw FailedAssertion("Unknown preference type: " + typeof value + " (" + name + "=" + value + ")");
+ }
}
+ catch (e if silent) {}
+ return value;
},
/**
*
* @param {string} name The preference to save.
*/
- save: function (name) {
+ save: function save(name) {
let val = this.get(name);
this.set(this.RESTORE + name, val);
this.safeSet(name, val);
* @param {string} branch The branch from which to restore
* preferences. @optional
*/
- restore: function (branch) {
+ restore: function restore(branch) {
this.getNames(this.RESTORE + (branch || "")).forEach(function (pref) {
this.safeSet(pref.substr(this.RESTORE.length), this.get(pref), null, true);
this.reset(pref);
*
* @param {string} name The preference name.
*/
- reset: function (name) {
+ reset: function reset(name) {
try {
this.branch.clearUserPref(name);
}
catch (e) {} // ignore - thrown if not a user set value
},
+ /**
+ * Resets the preference branch *branch* to its default value.
+ *
+ * @param {string} branch The preference name. @optional
+ */
+ resetBranch: function resetBranch(branch) {
+ this.getNames(branch).forEach(this.closure.reset);
+ },
+
/**
* Toggles the value of the boolean preference *name*.
*
* @param {string} name The preference name.
*/
- toggle: function (name) {
+ toggle: function toggle(name) {
util.assert(this.branch.getPrefType(name) === Ci.nsIPrefBranch.PREF_BOOL,
- _("error.trailing", name + "!"));
+ _("error.trailingCharacters", name + "!"));
this.set(name, !this.get(name));
},
*
* @see #withContext
*/
- pushContext: function () {
+ pushContext: function pushContext() {
this._prefContexts.push({});
},
*
* @see #withContext
*/
- popContext: function () {
+ popContext: function popContext() {
for (let [k, v] in Iterator(this._prefContexts.pop()))
this.set(k, v);
},
* @see #pushContext
* @see #popContext
*/
- withContext: function (func, self) {
+ withContext: function withContext(func, self) {
try {
this.pushContext();
return func.call(self);
}
}, {
}, {
- completion: function (dactyl, modules) {
+ completion: function init_completion(dactyl, modules) {
modules.completion.preference = function preference(context) {
context.anchored = false;
context.title = [config.host + " Preference", "Value"];
context.completions = prefs.getNames();
};
},
- javascript: function (dactyl, modules) {
+ javascript: function init_javascript(dactyl, modules) {
modules.JavaScript.setCompleter([this.get, this.safeSet, this.set, this.reset, this.toggle],
[function (context) (context.anchored=false, this.getNames().map(function (pref) [pref, ""]))]);
}
defineModule("sanitizer", {
exports: ["Range", "Sanitizer", "sanitizer"],
use: ["config"],
- require: ["messages", "prefs", "services", "storage", "template", "util"],
+ require: ["messages", "prefs", "services", "storage", "template", "util"]
}, this);
let tmp = {};
get isEternity() this.max == null && this.min == null,
get isSession() this.max == null && this.min == sanitizer.sessionStart,
- get native() this.isEternity ? null : [range.min || 0, range.max == null ? Number.MAX_VALUE : range.max]
+ get native() this.isEternity ? null : [this.min || 0, this.max == null ? Number.MAX_VALUE : this.max]
});
var Item = Class("SanitizeItem", {
util.addObserver(this);
- services.add("contentprefs", "@mozilla.org/content-pref/service;1", Ci.nsIContentPrefService);
+ services.add("contentPrefs", "@mozilla.org/content-pref/service;1", Ci.nsIContentPrefService);
services.add("cookies", "@mozilla.org/cookiemanager;1", [Ci.nsICookieManager, Ci.nsICookieManager2,
Ci.nsICookieService]);
- services.add("loginmanager", "@mozilla.org/login-manager;1", Ci.nsILoginManager);
+ services.add("loginManager", "@mozilla.org/login-manager;1", Ci.nsILoginManager);
services.add("permissions", "@mozilla.org/permissionmanager;1", Ci.nsIPermissionManager);
this.itemMap = {};
action: function (range, host) {
if (host)
services.history.removePagesFromHost(host, true);
- else
- services.history.removeVisitsByTimeframe(this.range.min, this.range.max);
-
- if (!host)
+ else {
+ if (range.isEternity)
+ services.history.removeAllPages();
+ else
+ services.history.removeVisitsByTimeframe(range.native[0], Math.min(Date.now() * 1000, range.native[1])); // XXX
services.observer.notifyObservers(null, "browser:purge-session-history", "");
+ }
if (!host || util.isDomainURL(prefs.get("general.open_location.last_url"), host))
prefs.reset("general.open_location.last_url");
services.permissions.remove(util.createURI(p.host), p.type);
services.permissions.add(util.createURI(p.host), p.type, 0);
}
- for (let p in iter(services.contentprefs.getPrefs(util.createURI(host))))
- services.contentprefs.removePref(util.createURI(host), p.QueryInterface(Ci.nsIProperty).name);
+ for (let p in iter(services.contentPrefs.getPrefs(util.createURI(host))))
+ services.contentPrefs.removePref(util.createURI(host), p.QueryInterface(Ci.nsIProperty).name);
}
else {
// "Allow this site to open popups" ...
services.permissions.removeAll();
// Zoom level, ...
- services.contentprefs.removeGroupedPrefs();
+ services.contentPrefs.removeGroupedPrefs();
}
// "Never remember passwords" ...
- for each (let domain in services.loginmanager.getAllDisabledHosts())
+ for each (let domain in services.loginManager.getAllDisabledHosts())
if (!host || util.isSubdomain(domain, host))
- services.loginmanager.setLoginSavingEnabled(host, true);
+ services.loginManager.setLoginSavingEnabled(host, true);
},
override: true
});
append: {
SanitizeDialogPane:
<groupbox orient="horizontal" xmlns={XUL}>
- <caption label={config.appName + " (see :help privacy)"}/>
+ <caption label={config.appName + /*L*/" (see :help privacy)"}/>
<grid flex="1">
<columns><column flex="1"/><column flex="1"/></columns>
<rows>{
function (win) prefOverlay(branch, false, {
append: {
itemList: <>
- <listitem xmlns={XUL} label="See :help privacy for the following:" disabled="true" style="font-style: italic; font-weight: bold;"/>
+ <listitem xmlns={XUL} label={/*L*/"See :help privacy for the following:"} disabled="true" style="font-style: italic; font-weight: bold;"/>
{
template.map(ourItems(), function ([item, desc])
<listitem xmlns={XUL} type="checkbox"
}
},
+ firstRun: 0,
+
addItem: function addItem(name, params) {
let item = this.itemMap[name] || Item(name, params);
this.itemMap[name] = item;
if (!("value" in prop) || !callable(prop.value) && !(k in item))
Object.defineProperty(item, k, prop);
- let names = set([name].concat(params.contains || []).map(function (e) "clear-" + e));
+ let names = Set([name].concat(params.contains || []).map(function (e) "clear-" + e));
if (params.action)
storage.addObserver("sanitizer",
function (key, event, arg) {
deny: 2,
session: 8
},
+
UNPERMS: Class.memoize(function () iter(this.PERMS).map(Array.reverse).toObject()),
+
COMMANDS: {
- unset: "Unset",
- allow: "Allowed",
- deny: "Denied",
- session: "Allowed for the current session",
- list: "List all cookies for domain",
- clear: "Clear all cookies for domain",
- "clear-persistent": "Clear all persistent cookies for domain",
- "clear-session": "Clear all session cookies for domain"
+ unset: /*L*/"Unset",
+ allow: /*L*/"Allowed",
+ deny: /*L*/"Denied",
+ session: /*L*/"Allowed for the current session",
+ list: /*L*/"List all cookies for domain",
+ clear: /*L*/"Clear all cookies for domain",
+ "clear-persistent": /*L*/"Clear all persistent cookies for domain",
+ "clear-session": /*L*/"Clear all session cookies for domain"
},
argPrefMap: {
}
}, {
load: function (dactyl, modules, window) {
- if (sanitizer.runAtShutdown && !sanitizer.ranAtShutdown)
+ if (!sanitizer.firstRun++ && sanitizer.runAtShutdown && !sanitizer.ranAtShutdown)
sanitizer.sanitizeItems(null, Range(), null, "shutdown");
sanitizer.ranAtShutdown = false;
},
commands.add(["sa[nitize]"],
"Clear private data",
function (args) {
- dactyl.assert(!modules.options['private'], "Cannot sanitize items in private mode");
+ dactyl.assert(!modules.options['private'], _("command.sanitize.privateMode"));
let timespan = args["-timespan"] || modules.options["sanitizetimespan"];
args[0] = "all";
if (args.bang) {
- dactyl.assert(args.length == 0, _("error.trailing"));
+ dactyl.assert(args.length == 0, _("error.trailingCharacters"));
items = Object.keys(sanitizer.itemMap).filter(
function (k) modules.options.get("sanitizeitems").has(k));
}
else
- dactyl.assert(modules.options.get("sanitizeitems").validator(items), "Valid items required");
+ dactyl.assert(modules.options.get("sanitizeitems").validator(items), _("error.invalidArgument"));
+
+ function sanitize(items) {
+ sanitizer.range = range.native;
+ sanitizer.ignoreTimespan = range.min == null;
+ sanitizer.sanitizing = true;
+ if (args["-host"]) {
+ args["-host"].forEach(function (host) {
+ sanitizer.sanitizing = true;
+ sanitizer.sanitizeItems(items, range, host);
+ });
+ }
+ else
+ sanitizer.sanitize(items, range);
+ }
if (items.indexOf("all") >= 0)
- items = Object.keys(sanitizer.itemMap).filter(function (k) items.indexOf(k) === -1);
-
- sanitizer.range = range.native;
- sanitizer.ignoreTimespan = range.min == null;
- sanitizer.sanitizing = true;
- if (args["-host"]) {
- args["-host"].forEach(function (host) {
- sanitizer.sanitizing = true;
- sanitizer.sanitizeItems(items, range, host)
- });
- }
+ modules.commandline.input(_("sanitize.prompt.deleteAll") + " ",
+ function (resp) {
+ if (resp.match(/^y(es)?$/i)) {
+ items = Object.keys(sanitizer.itemMap).filter(function (k) items.indexOf(k) === -1);
+ sanitize(items);
+ dactyl.echo(_("command.sanitize.allDeleted"));
+ }
+ else
+ dactyl.echo(_("command.sanitize.noneDeleted"));
+ });
else
- sanitizer.sanitize(items, range);
+ sanitize(items);
+
},
{
argCount: "*", // FIXME: should be + and 0
!args["-host"].some(function (host) util.isSubdomain(item.text, host)));
modules.completion.domain(context);
},
- type: modules.CommandOption.LIST,
+ type: modules.CommandOption.LIST
}, {
names: ["-older", "-o"],
description: "Sanitize items older than timespan",
get values() values(sanitizer.itemMap).toArray(),
has: modules.Option.has.toggleAll,
validator: function (values) values.length &&
- values.every(function (val) val === "all" || set.has(sanitizer.itemMap, val))
+ values.every(function (val) val === "all" || Set.has(sanitizer.itemMap, val))
});
options.add(["sanitizeshutdown", "ss"],
sanitizer.runAtShutdown = false;
else {
sanitizer.runAtShutdown = true;
- let have = set(value);
+ let have = Set(value);
for (let item in values(sanitizer.itemMap))
prefs.set(item.shutdownPref,
- Boolean(set.has(have, item.name) ^ set.has(have, "all")));
+ Boolean(Set.has(have, item.name) ^ Set.has(have, "all")));
}
return value;
}
},
initialValue: true,
persist: false,
- validator: function (val) parseInt(val) == val || modules.Option.validateCompleter.call(this, val)
+ validator: function validator(val) parseInt(val) == val || validator.superapply(this, arguments)
});
}
});
this.add("appStartup", "@mozilla.org/toolkit/app-startup;1", "nsIAppStartup");
this.add("autoCompleteSearch", "@mozilla.org/autocomplete/search;1?name=history", "nsIAutoCompleteSearch");
this.add("bookmarks", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService");
+ this.add("bootstrap", "@dactyl.googlecode.com/base/bootstrap");
this.add("browserSearch", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService");
this.add("cache", "@mozilla.org/network/cache-service;1", "nsICacheService");
this.add("charset", "@mozilla.org/charset-converter-manager;1", "nsICharsetConverterManager");
this.add("extensionManager", "@mozilla.org/extensions/manager;1", "nsIExtensionManager");
this.add("externalProtocol", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService");
this.add("favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
+ this.add("file:", "@mozilla.org/network/protocol;1?name=file", "nsIFileProtocolHandler");
this.add("focus", "@mozilla.org/focus-manager;1", "nsIFocusManager");
this.add("history", "@mozilla.org/browser/global-history;2",
["nsIBrowserHistory", "nsIGlobalHistory3", "nsINavHistoryService", "nsPIPlacesDatabase"]);
this.add("io", "@mozilla.org/network/io-service;1", "nsIIOService");
this.add("json", "@mozilla.org/dom/json;1", "nsIJSON", "createInstance");
+ this.add("listeners", "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
this.add("livemark", "@mozilla.org/browser/livemark-service;2", "nsILivemarkService");
this.add("mime", "@mozilla.org/mime;1", "nsIMIMEService");
this.add("observer", "@mozilla.org/observer-service;1", "nsIObserverService");
this.add("stylesheet", "@mozilla.org/content/style-sheet-service;1", "nsIStyleSheetService");
this.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader");
this.add("tagging", "@mozilla.org/browser/tagging-service;1", "nsITaggingService");
+ this.add("tld", "@mozilla.org/network/effective-tld-service;1", "nsIEffectiveTLDService");
this.add("threading", "@mozilla.org/thread-manager;1", "nsIThreadManager");
this.add("urifixup", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
this.add("versionCompare", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator");
this.addClass("CharsetConv", "@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter", "charset");
this.addClass("File", "@mozilla.org/file/local;1", "nsILocalFile");
- this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", "nsIFileProtocolHandler");
this.addClass("Find", "@mozilla.org/embedcomp/rangefind;1", "nsIFind");
this.addClass("HtmlConverter","@mozilla.org/widget/htmlformatconverter;1", "nsIFormatConverter");
this.addClass("HtmlEncoder", "@mozilla.org/layout/htmlCopyEncoder;1", "nsIDocumentEncoder");
this.addClass("Timer", "@mozilla.org/timer;1", "nsITimer", "initWithCallback");
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
this.addClass("XPathEvaluator", "@mozilla.org/dom/xpath-evaluator;1", "nsIDOMXPathEvaluator");
+ this.addClass("XMLDocument", "@mozilla.org/xml/xml-document;1", ["nsIDOMXMLDocument", "nsIDOMNodeSelector"]);
this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open");
this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", "nsIZipWriter");
},
*
* @param {string} name The service's cache key.
* @param {string} class The class's contract ID.
- * @param {string|string[]} ifaces The interface or array of
+ * @param {string|[string]} ifaces The interface or array of
* interfaces implemented by this service.
* @param {string} meth The name of the function used to instantiate
* the service.
*
* @param {string} name The class's cache key.
* @param {string} class_ The class's contract ID.
- * @param {nsISupports|nsISupports[]} ifaces The interface or array of
+ * @param {nsISupports|[nsISupports]} ifaces The interface or array of
* interfaces implemented by this class.
* @param {string} init Name of a property or method used to initialise the
* class. See {@link #_create}.
*
* @param {string} name The service's cache key.
*/
- has: function (name) set.has(this.services, name) && this.services[name].class in Cc &&
+ has: function (name) Set.has(this.services, name) && this.services[name].class in Cc &&
this.services[name].interfaces.every(function (iface) iface in Ci)
}, {
}, {
init: function () {
this.cleanup();
+
+ if (services.bootstrap && !services.bootstrap.session)
+ services.bootstrap.session = {};
+ this.session = services.bootstrap ? services.bootstrap.session : {};
},
cleanup: function () {
let file = services.File();
if (path instanceof Ci.nsIFile)
- file = path.QueryInterface(Ci.nsIFile).clone();
+ file = path.clone();
else if (/file:\/\//.test(path))
- file = services["file:"]().getFileFromURLSpec(path);
+ file = services["file:"].getFileFromURLSpec(path);
else {
try {
let expandedPath = File.expandPath(path);
return self;
},
+ /**
+ * @property {nsIFileURL} Returns the nsIFileURL object for this file.
+ */
+ get URI() services.io.newFileURI(this),
+
/**
* Iterates over the objects in this directory.
*/
iterDirectory: function () {
if (!this.exists())
- throw Error("File does not exist");
+ throw Error(_("io.noSuchFile"));
if (!this.isDirectory())
- throw Error("Not a directory");
+ throw Error(_("io.eNotDir"));
for (let file in iter(this.directoryEntries))
yield File(file);
},
*
* @param {boolean} sort Whether to sort the returned directory
* entries.
- * @returns {nsIFile[]}
+ * @returns {[nsIFile]}
*/
readDirectory: function (sort) {
if (!this.isDirectory())
- throw Error("Not a directory");
+ throw Error(_("io.eNotDir"));
let array = [e for (e in this.iterDirectory())];
if (sort)
ofstream.init(this, mode, perms, 0);
try {
- if (callable(buf))
- buf(ofstream.QueryInterface(Ci.nsIOutputStream));
- else {
- var ocstream = getStream(0);
- ocstream.writeString(buf);
- }
+ var ocstream = getStream(0);
+ ocstream.writeString(buf);
}
- catch (e if callable(buf) && e.result == Cr.NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
+ catch (e if e.result == Cr.NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
+ ocstream.close();
ocstream = getStream("?".charCodeAt(0));
ocstream.writeString(buf);
return false;
get fullCSS() {
let filter = this.sites;
let css = this.css;
+
+ let preamble = "/* " + this.uri + (this.agent ? " (agent)" : "") + " */\n\n" + namespace + "\n";
if (filter[0] == "*")
- return namespace + css;
+ return preamble + css;
let selectors = filter.map(function (part)
- (/[*]$/.test(part) ? "url-prefix" :
- /[\/:]/.test(part) ? "url"
- : "domain")
- + '("' + part.replace(/"/g, "%22").replace(/\*$/, "") + '")')
+ !/^(?:[a-z-]+[:*]|[a-z-.]+$)/i.test(part) ? "regexp(" + (".*(?:" + part + ").*").quote() + ")" :
+ (/[*]$/.test(part) ? "url-prefix" :
+ /[\/:]/.test(part) ? "url"
+ : "domain")
+ + '("' + part.replace(/"/g, "%22").replace(/\*$/, "") + '")')
.join(",\n ");
- return "/* " + this.uri + (this.agent ? " (agent)" : "") + " */\n\n"
- + namespace + "\n@-moz-document " + selectors + " {\n\n" + css + "\n\n}\n";
+
+ return preamble + "@-moz-document " + selectors + " {\n\n" + css + "\n\n}\n";
}
});
services["dactyl:"].providers["style"] = function styleProvider(uri) {
let id = /^\/(\d*)/.exec(uri.path)[1];
- if (set.has(styles.allSheets, id))
- return ["text/css", unescape(encodeURI(styles.allSheets[id].fullCSS))];
+ if (Set.has(styles.allSheets, id))
+ return ["text/css", styles.allSheets[id].fullCSS];
return null;
};
},
systemNames: Class.Property({ get: deprecated("Styles#system.names", function systemNames() this.system.names) }),
sites: Class.Property({ get: deprecated("Styles#user.sites", function sites() this.user.sites) }),
- list: function list(content, filter, name, hives) {
+ list: function list(content, sites, name, hives) {
const { commandline, dactyl } = this.modules;
hives = hives || styles.hives.filter(function (h) h.modifiable && h.sheets.length);
function sheets(group)
group.sheets.slice()
+ .filter(function (sheet) (!name || sheet.name === name) &&
+ (!sites || sites.every(function (s) sheet.sites.indexOf(s) >= 0)))
.sort(function (a, b) a.name && b.name ? String.localeCompare(a.name, b.name)
: !!b.name - !!a.name || a.id - b.id);
<tr highlight="Title">
<td/>
<td/>
- <td style="padding-right: 1em;">Name</td>
- <td style="padding-right: 1em;">Filter</td>
- <td style="padding-right: 1em;">CSS</td>
+ <td style="padding-right: 1em;">{_("title.Name")}</td>
+ <td style="padding-right: 1em;">{_("title.Filter")}</td>
+ <td style="padding-right: 1em;">{_("title.CSS")}</td>
</tr>
<col style="min-width: 4em; padding-right: 1em;"/>
<col style="min-width: 1em; text-align: center; color: red; font-weight: bold;"/>
for (let prop in Styles.propertyIter(str))
props[prop.name] = prop.value;
- return Object.keys(props)[sort ? "sort" : "slice"]()
- .map(function (prop) prop + ": " + props[prop] + ";")
- .join(" ");
+ let val = Object.keys(props)[sort ? "sort" : "slice"]()
+ .map(function (prop) prop + ": " + props[prop] + ";")
+ .join(" ");
+
+ if (/^\s*(\/\*.*?\*\/)/.exec(src))
+ val = RegExp.$1 + " " + val;
+ return val;
},
completeSite: function (context, content, group) {
context.fork("current", 0, this, function (context) {
context.title = ["Current Site"];
context.completions = [
- [content.location.host, "Current Host"],
- [content.location.href, "Current URL"]
+ [content.location.host, /*L*/"Current Host"],
+ [content.location.href, /*L*/"Current URL"]
];
});
}
context.generate = function () values(group.sites);
context.keys.text = util.identity;
- context.keys.description = function (site) this.sheets.length + " sheet" + (this.sheets.length == 1 ? "" : "s") + ": " +
+ context.keys.description = function (site) this.sheets.length + /*L*/" sheet" + (this.sheets.length == 1 ? "" : "s") + ": " +
array.compact(this.sheets.map(function (s) s.name)).join(", ");
context.keys.sheets = function (site) group.sheets.filter(function (s) s.sites.indexOf(site) >= 0);
context.keys.active = function (site) uris.some(Styles.matchFilter(site));
for (let item in Iterator({ Active: true, Inactive: false })) {
let [name, active] = item;
context.split(name, null, function (context) {
- context.title[0] = name + " " + (title || "Sheets");
+ context.title[0] = /*L*/name + " " + (title || "Sheets");
context.filters.push(function (item) !!item.active == active);
});
}
let [filter, css] = args;
if (!css)
- styles.list(window.content, filter, args["-name"], args.explicitOpts["-group"] ? [args["-group"]] : null);
+ styles.list(window.content, filter ? filter.split(",") : null, args["-name"], args.explicitOpts["-group"] ? [args["-group"]] : null);
else {
util.assert(args["-group"].modifiable && args["-group"].hive.modifiable,
- "Cannot modify styles in the builtin group");
+ _("group.cantChangeBuiltin", _("style.styles")));
if (args["-append"]) {
let sheet = args["-group"].get(args["-name"]);
if (sheet) {
- filter = sheet.sites.concat(filter).join(",");
+ filter = array(sheet.sites).concat(filter).uniq().join(",");
css = sheet.css + " " + css;
-
}
}
let style = args["-group"].add(args["-name"], filter, css, args["-agent"]);
}
},
{
- bang: true,
completer: function (context, args) {
let compl = [];
let sheet = args["-group"].get(args["-name"]);
}
else if (args.completeArg == 1) {
if (sheet)
- context.completions = [[sheet.css, "Current Value"]];
+ context.completions = [
+ [sheet.css, _("option.currentValue")]
+ ];
context.fork("css", 0, modules.completion, "css");
}
},
command: "style",
arguments: [style.sites.join(",")],
literalArg: style.css,
- options: update({
- "-group": hive.name,
- },
+ options: update(
+ hive.name == "user" ? {} : { "-group": hive.name },
style.name ? { "-name": style.name } : {})
})))
.flatten().array
commands.add(cmd.name, cmd.desc,
function (args) {
dactyl.assert(args.bang ^ !!(args[0] || args[1] || args["-name"] || args["-index"]),
- "Argument or ! required");
+ _("error.argumentOrBang"));
args["-group"].find(args["-name"], args[0], args.literalArg, args["-index"])
.forEach(cmd.action);
"click": function onClick(event) {
event.preventDefault();
if (this.commandAllowed) {
- if (set.has(this.target.commands || {}, this.command))
+ if (Set.has(this.target.commands || {}, this.command))
this.target.commands[this.command].call(this.target);
else
this.target.command(this.command);
},
get commandAllowed() {
- if (set.has(this.target.allowedCommands || {}, this.command))
+ if (Set.has(this.target.allowedCommands || {}, this.command))
return this.target.allowedCommands[this.command];
if ("commandAllowed" in this.target)
return this.target.commandAllowed(this.command);
- from pushing the baseline down and enlarging
- the row.
-->
- <li highlight="CompResult">{text} </li>
+ <li highlight={"CompResult " + item.highlight}>{text} </li>
<li highlight="CompDesc">{desc} </li>
</div>;
// </e4x>
else if (/^n_/.test(topic))
topic = topic.slice(2);
- if (services["dactyl:"].initialized && !set.has(services["dactyl:"].HELP_TAGS, topic))
+ if (services["dactyl:"].initialized && !Set.has(services["dactyl:"].HELP_TAGS, topic))
return <span highlight={type || ""}>{text || token}</span>;
XML.ignoreWhitespace = false; XML.prettyPrinting = false;
else if (/^n_/.test(topic))
topic = topic.slice(2);
- if (services["dactyl:"].initialized && !set.has(services["dactyl:"].HELP_TAGS, topic))
+ if (services["dactyl:"].initialized && !Set.has(services["dactyl:"].HELP_TAGS, topic))
return <>{token}</>;
XML.ignoreWhitespace = false; XML.prettyPrinting = false;
linkifyHelp: function linkifyHelp(str, help) {
let re = util.regexp(<![CDATA[
(?P<pre> [/\s]|^)
- (?P<tag> '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | [a-zA-Z]_\w+ | \[[\w-]+\] | E\d{3} )
+ (?P<tag> '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | \b[a-zA-Z]_(?:\w+|.) | \[[\w-]+\] | E\d{3} )
(?= [[\)!,:;./\s]|$)
]]>, "gx");
return this.highlightSubstrings(str, (function () {
highlightURL: function highlightURL(str, force) {
if (force || /^[a-zA-Z]+:\/\//.test(str))
- return <a highlight="URL" href={str}>{str}</a>;
+ return <a highlight="URL" href={str}>{util.losslessDecodeURI(str)}</a>;
else
return str;
},
// <e4x>
return <table>
<tr style="text-align: left;" highlight="Title">
- <th colspan="2">jump</th><th>title</th><th>URI</th>
+ <th colspan="2">{_("title.Jump")}</th>
+ <th>{_("title.Title")}</th>
+ <th>{_("title.URI")}</th>
</tr>
{
this.map(Iterator(elems), function ([idx, val])
<td class="indicator">{idx == index ? ">" : ""}</td>
<td>{Math.abs(idx - index)}</td>
<td style="width: 250px; max-width: 500px; overflow: hidden;">{val.title}</td>
- <td><a href={val.URI.spec} highlight="URL jump-list">{val.URI.spec}</a></td>
+ <td><a href={val.URI.spec} highlight="URL jump-list">{util.losslessDecodeURI(val.URI.spec)}</a></td>
</tr>)
}
</table>;
let (name = item.name || item.names[0], frame = item.definedAt)
!frame ? name :
template.helpLink(help(item), name, "Title") +
- <span highlight="LinkInfo" xmlns:dactyl={NS}>Defined at {sourceLink(frame)}</span>
+ <span highlight="LinkInfo" xmlns:dactyl={NS}>{_("io.definedAt")} {sourceLink(frame)}</span>
}</span>
</td>
{ item.columns ? template.map(item.columns, function (c) <td>{c}</td>) : "" }
defineModule("util", {
exports: ["frag", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
require: ["services"],
- use: ["commands", "config", "highlight", "storage", "template"]
+ use: ["commands", "config", "highlight", "messages", "storage", "template"]
}, this);
var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
*
* @param {object} obj
*/
- addObserver: function (obj) {
+ addObserver: update(function addObserver(obj) {
if (!obj.observers)
obj.observers = obj.observe;
function register(meth) {
- for (let target in set(["dactyl-cleanup-modules", "quit-application"].concat(Object.keys(obj.observers))))
+ for (let target in Set(["dactyl-cleanup-modules", "quit-application"].concat(Object.keys(obj.observers))))
try {
services.observer[meth](obj, target, true);
}
}
catch (e) {
if (typeof util === "undefined")
- dump("dactyl: error: " + e + "\n" + (e.stack || Error().stack).replace(/^/gm, "dactyl: "));
+ addObserver.dump("dactyl: error: " + e + "\n" + (e.stack || addObserver.Error().stack).replace(/^/gm, "dactyl: "));
else
util.reportError(e);
}
obj.observe.unregister = function () register("removeObserver");
register("addObserver");
- },
+ }, { dump: dump, Error: Error }),
/*
* Tests a condition and throws a FailedAssertion error on
assert: function (condition, message, quiet) {
if (!condition)
throw FailedAssertion(message, 1, quiet === undefined ? true : quiet);
+ return condition;
},
/**
* @param {string} str The string to capitalize
* @returns {string}
*/
- capitalize: function capitalize(str) str && str[0].toUpperCase() + str.slice(1),
+ capitalize: function capitalize(str) str && str[0].toUpperCase() + str.slice(1).toLowerCase(),
/**
* Returns a RegExp object that matches characters specified in the range
// check for chars not in the accepted range
this.assert(RegExp("^[" + accepted + "-]+$").test(list),
- "Character list outside the range " + accepted.quote());
+ _("error.charactersOutsideRange", accepted.quote()));
// check for illegal ranges
for (let [match] in this.regexp.iterate(/.-./g, list))
this.assert(match.charCodeAt(0) <= match.charCodeAt(2),
- "Invalid character range: " + list.slice(list.indexOf(match)))
+ _("error.invalidCharacterRange", list.slice(list.indexOf(match))));
return RegExp("[" + util.regexp.escape(list) + "]");
},
}
else if (char === "]") {
stack.pop();
- util.assert(stack.length, "Unmatched %] in format");
+ util.assert(stack.length, /*L*/"Unmatched %] in format");
}
else {
let quote = function quote(obj, char) obj[char];
if (end < format.length)
stack.top.elements.push(format.substr(end));
- util.assert(stack.length === 1, "Unmatched %[ in format");
+ util.assert(stack.length === 1, /*L*/"Unmatched %[ in format");
return stack.top;
},
+ /**
+ * Compiles a macro string into a function which generates a string
+ * result based on the input *macro* and its parameters. The
+ * definitive documentation for macro strings resides in :help
+ * macro-string.
+ *
+ * Macro parameters may have any of the following flags:
+ * e: The parameter is only tested for existence. Its
+ * interpolation is always empty.
+ * q: The result is quoted such that it is parsed as a single
+ * argument by the Ex argument parser.
+ *
+ * The returned function has the following additional properties:
+ *
+ * seen {set}: The set of parameters used in this macro.
+ *
+ * valid {function(object)}: Returns true if every parameter of
+ * this macro is provided by the passed object.
+ *
+ * @param {string} macro The macro string to compile.
+ * @param {boolean} keepUnknown If true, unknown macro parameters
+ * are left untouched. Otherwise, they are replaced with the null
+ * string.
+ * @returns {function}
+ */
compileMacro: function compileMacro(macro, keepUnknown) {
let stack = [frame()];
stack.__defineGetter__("top", function () this[this.length - 1]);
([^]*?) // 1
(?:
(<\{) | // 2
- (< ((?:[a-z]-)?[a-z-]+?) >) | // 3 4
- (\}>) // 5
+ (< ((?:[a-z]-)?[a-z-]+?) (?:\[([0-9]+)\])? >) | // 3 4 5
+ (\}>) // 6
)
]]>, "gixy");
macro = String(macro);
let end = 0;
for (let match in re.iterate(macro)) {
- let [, prefix, open, full, macro, close] = match;
+ let [, prefix, open, full, macro, idx, close] = match;
end += match[0].length;
if (prefix)
}
else if (close) {
stack.pop();
- util.assert(stack.length, "Unmatched %] in macro");
+ util.assert(stack.length, /*L*/"Unmatched %] in macro");
}
else {
let [, flags, name] = /^((?:[a-z]-)*)(.*)/.exec(macro);
- flags = set(flags);
+ flags = Set(flags);
let quote = util.identity;
if (flags.q)
if (flags.e)
quote = function quote(obj) "";
- if (set.has(defaults, name))
+ if (Set.has(defaults, name))
stack.top.elements.push(quote(defaults[name]));
else {
- stack.top.elements.push(update(
- function (obj) obj[name] != null ? quote(obj[name]) : set.has(obj, name) ? "" : unknown(full),
- { test: function (obj) obj[name] != null && obj[name] !== false && (!flags.e || obj[name] != "") }));
+ if (idx) {
+ idx = Number(idx) - 1;
+ stack.top.elements.push(update(
+ function (obj) obj[name] != null && idx in obj[name] ? quote(obj[name][idx]) : Set.has(obj, name) ? "" : unknown(full),
+ { test: function (obj) obj[name] != null && idx in obj[name] && obj[name][idx] !== false && (!flags.e || obj[name][idx] != "") }));
+ }
+ else {
+ stack.top.elements.push(update(
+ function (obj) obj[name] != null ? quote(obj[name]) : Set.has(obj, name) ? "" : unknown(full),
+ { test: function (obj) obj[name] != null && obj[name] !== false && (!flags.e || obj[name] != "") }));
+ }
for (let elem in array.iterValues(stack))
elem.seen[name] = true;
if (end < macro.length)
stack.top.elements.push(macro.substr(end));
- util.assert(stack.length === 1, "Unmatched <{ in macro");
+ util.assert(stack.length === 1, /*L*/"Unmatched <{ in macro");
return stack.top;
},
+ /**
+ * Compiles a CSS spec and XPath pattern matcher based on the given
+ * list. List elements prefixed with "xpath:" are parsed as XPath
+ * patterns, while other elements are parsed as CSS specs. The
+ * returned function will, given a node, return an iterator of all
+ * descendants of that node which match the given specs.
+ *
+ * @param {[string]} list The list of patterns to match.
+ * @returns {function(Node)}
+ */
compileMatcher: function compileMatcher(list) {
let xpath = [], css = [];
for (let elem in values(list))
});
},
- validateMatcher: function validateMatcher(values) {
+ /**
+ * Validates a list as input for {@link #compileMatcher}. Returns
+ * true if and only if every element of the list is a valid XPath or
+ * CSS selector.
+ *
+ * @param {[string]} list The list of patterns to test
+ * @returns {boolean} True when the patterns are all valid.
+ */
+ validateMatcher: function validateMatcher(list) {
let evaluator = services.XPathEvaluator();
- let node = util.xmlToDom(<div/>, document);
- return this.testValues(values, function (value) {
+ let node = services.XMLDocument();
+ return this.testValues(list, function (value) {
if (/^xpath:/.test(value))
evaluator.createExpression(value.substr(6), util.evaluateXPath.resolver);
else
* Example:
* "a{b,c}d" => ["abd", "acd"]
*
- * @param {string} pattern The pattern to deglob.
+ * @param {string|[string|Array]} pattern The pattern to deglob.
* @returns [string] The resulting strings.
*/
debrace: function debrace(pattern) {
+ if (isArray(pattern)) {
+ let res = [];
+ let rec = function rec(acc) {
+ let vals;
+
+ while (isString(vals = pattern[acc.length]))
+ acc.push(vals);
+
+ if (acc.length == pattern.length)
+ res.push(acc.join(""))
+ else
+ for (let val in values(vals))
+ rec(acc.concat(val));
+ }
+ rec([]);
+ return res;
+ }
+
if (pattern.indexOf("{") == -1)
return [pattern];
res.push(pattern.substr(end));
return res.map(function (s) util.dequote(s, dequote));
}
- let patterns = [], res = [];
+ let patterns = [];
let substrings = split(pattern, /((?:[^\\{]|\\.)*)\{((?:[^\\}]|\\.)*)\}/gy,
function (match) {
patterns.push(split(match[2], /((?:[^\\,]|\\.)*),/gy,
null, ",{}"));
}, "{}");
+
+ let res = [];
function rec(acc) {
if (acc.length == patterns.length)
res.push(array(substrings).zip(acc).flatten().join(""));
dequote: function dequote(pattern, chars)
pattern.replace(/\\(.)/, function (m0, m1) chars.indexOf(m1) >= 0 ? m1 : m0),
+ /**
+ * Converts a given DOM Node, Range, or Selection to a string. If
+ * *html* is true, the output is HTML, otherwise it is presentation
+ * text.
+ *
+ * @param {nsIDOMNode | nsIDOMRange | nsISelection} node The node to
+ * stringify.
+ * @param {boolean} html Whether the output should be HTML rather
+ * than presentation text.
+ */
domToString: function (node, html) {
if (node instanceof Ci.nsISelection && node.isCollapsed)
return "";
range.selectNode(node);
node = range;
}
- let doc = (node.getRangeAt ? node.getRangeAt(0) : node).startContainer.ownerDocument;
+ let doc = (node.getRangeAt ? node.getRangeAt(0) : node).startContainer;
+ doc = doc.ownerDocument || doc;
let encoder = services.HtmlEncoder();
encoder.init(doc, "text/unicode", encoder.OutputRaw|encoder.OutputPreformatted);
*/
dump: defineModule.dump,
+ /**
+ * Returns a list of reformatted stack frames from
+ * {@see Error#stack}.
+ *
+ * @param {string} stack The stack trace from an Error.
+ * @returns {[string]} The stack frames.
+ */
stackLines: function (stack) {
let lines = [];
let match, re = /([^]*?)@([^@\n]*)(?:\n|$)/g;
* The set of input element type attribute values that mark the element as
* an editable field.
*/
- editableInputs: set(["date", "datetime", "datetime-local", "email", "file",
+ editableInputs: Set(["date", "datetime", "datetime-local", "email", "file",
"month", "number", "password", "range", "search",
"tel", "text", "time", "url", "week"]),
[hours, minutes] = div(minutes, 60);
[days, hours] = div(hours, 24);
if (days)
- return days + " days " + hours + " hours"
+ return /*L*/days + " days " + hours + " hours"
if (hours)
- return hours + "h " + minutes + "m";
+ return /*L*/hours + "h " + minutes + "m";
if (minutes)
- return minutes + ":" + pad(2, seconds);
- return seconds + "s";
+ return /*L*/minutes + ":" + pad(2, seconds);
+ return /*L*/seconds + "s";
},
/**
uri = util.newURI(util.fixURI(uri));
if (uri instanceof Ci.nsIFileURL)
- return File(uri.QueryInterface(Ci.nsIFileURL).file);
+ return File(uri.file);
let channel = services.io.newChannelFromURI(uri);
channel.cancel(Cr.NS_BINDING_ABORTED);
if (channel instanceof Ci.nsIFileChannel)
- return File(channel.QueryInterface(Ci.nsIFileChannel).file);
+ return File(channel.file);
}
catch (e) {}
return null;
return xmlhttp;
}
catch (e) {
- util.dactyl.log("Error opening " + String.quote(url) + ": " + e, 1);
+ util.dactyl.log(_("error.cantOpen", String.quote(url), e), 1);
return null;
}
},
}
},
+ // ripped from Firefox; modified
+ unsafeURI: Class.memoize(function () util.regexp(String.replace(<![CDATA[
+ [
+ \s
+ // Invisible characters (bug 452979)
+ U001C U001D U001E U001F // file/group/record/unit separator
+ U00AD // Soft hyphen
+ UFEFF // BOM
+ U2060 // Word joiner
+ U2062 U2063 // Invisible times/separator
+ U200B UFFFC // Zero-width space/no-break space
+
+ // Bidi formatting characters. (RFC 3987 sections 3.2 and 4.1 paragraph 6)
+ U200E U200F U202A U202B U202C U202D U202E
+ ]
+ ]]>, /U/g, "\\u"),
+ "gx")),
+ losslessDecodeURI: function losslessDecodeURI(url) {
+ return url.split("%25").map(function (url) {
+ // Non-UTF-8 compliant URLs cause "malformed URI sequence" errors.
+ try {
+ return decodeURI(url).replace(this.unsafeURI, encodeURIComponent);
+ }
+ catch (e) {
+ return url;
+ }
+ }, this).join("%25");
+ },
+
/**
* Returns an XPath union expression constructed from the specified node
* tests. An expression is built with node tests for both the null and
*/
makeXPath: function makeXPath(nodes) {
return array(nodes).map(util.debrace).flatten()
- .map(function (node) [node, "xhtml:" + node]).flatten()
+ .map(function (node) /^[a-z]+:/.test(node) ? node : [node, "xhtml:" + node]).flatten()
.map(function (node) "//" + node).join(" | ");
},
+ /**
+ * Creates a DTD fragment from the given object. Each property of
+ * the object is converted to an ENTITY declaration. SGML special
+ * characters other than ' and % are left intact.
+ *
+ * @param {object} obj The object to convert.
+ * @returns {string} The DTD fragment containing entity declaration
+ * for *obj*.
+ */
+ makeDTD: let (map = { "'": "'", '"': """, "%": "%", "&": "&", "<": "<", ">": ">" })
+ function makeDTD(obj) iter(obj)
+ .map(function ([k, v]) ["<!ENTITY ", k, " '", String.replace(v == null ? "null" : typeof v == "xml" ? v.toXMLString() : v,
+ typeof v == "xml" ? /['%]/g : /['"%&<>]/g,
+ function (m) map[m]),
+ "'>"].join(""))
+ .join("\n"),
+
map: deprecated("iter.map", function map(obj, fn, self) iter(obj).map(fn, self).toArray()),
writeToClipboard: deprecated("dactyl.clipboardWrite", function writeToClipboard(str, verbose) util.dactyl.clipboardWrite(str, verbose)),
readFromClipboard: deprecated("dactyl.clipboardRead", function readFromClipboard() util.dactyl.clipboardRead(false)),
* @returns {nsIURI}
*/
// FIXME: createURI needed too?
- newURI: function (uri, charset, base) services.io.newURI(uri, charset, base),
+ newURI: function newURI(uri, charset, base) this.withProperErrors("newURI", services.io, uri, charset, base),
/**
* Removes leading garbage prepended to URIs by the subscript
}</span>;
let tag = "<" + [namespaced(elem)].concat(
- [namespaced(a) + "=" + template.highlight(a.value, true)
+ [namespaced(a) + "=" + template.highlight(a.value, true)
for ([i, a] in array.iterItems(elem.attributes))]).join(" ");
return tag + (!hasChildren ? "/>" : ">...</" + namespaced(elem) + ">");
}
},
observers: {
- "dactyl-cleanup-modules": function () {
- defineModule.loadLog.push("dactyl: util: observe: dactyl-cleanup-modules");
+ "dactyl-cleanup-modules": function (subject, reason) {
+ defineModule.loadLog.push("dactyl: util: observe: dactyl-cleanup-modules " + reason);
for (let module in values(defineModule.modules))
if (module.cleanup) {
util.dump("cleanup: " + module.constructor.className);
- util.trapErrors(module.cleanup, module);
+ util.trapErrors(module.cleanup, module, reason);
}
JSMLoader.cleanup();
"dactyl-purge": function () {
this.rehashing = 1;
},
+
"toplevel-window-ready": function (window, data) {
window.addEventListener("DOMContentLoaded", wrapCallback(function listener(event) {
if (event.originalTarget === window.document) {
}), true);
},
+ /**
+ * Overlays an object with the given property overrides. Each
+ * property in *overrides* is added to *object*, replacing any
+ * original value. Functions in *overrides* are augmented with the
+ * new properties *super*, *supercall*, and *superapply*, in the
+ * same manner as class methods, so that they man call their
+ * overridden counterparts.
+ *
+ * @param {object} object The object to overlay.
+ * @param {object} overrides An object containing properties to
+ * override.
+ * @returns {function} A function which, when called, will remove
+ * the overlay.
+ */
overlayObject: function (object, overrides) {
let original = Object.create(object);
overrides = update(Object.create(original), overrides);
if (desc.value instanceof Class.Property)
desc = desc.value.init(k) || desc.value;
- for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj))
- if (orig = Object.getOwnPropertyDescriptor(obj, k))
- Object.defineProperty(original, k, orig);
+ if (k in object) {
+ for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj))
+ if (orig = Object.getOwnPropertyDescriptor(obj, k))
+ Object.defineProperty(original, k, orig);
+
+ if (!orig)
+ if (orig = Object.getPropertyDescriptor(object, k))
+ Object.defineProperty(original, k, orig);
+ }
// Guard against horrible add-ons that use eval-based monkey
// patching.
+ let value = desc.value;
if (callable(desc.value)) {
- let value = desc.value;
-
- let sentinel = "(function DactylOverlay() {}())"
- value.toString = function toString() toString.toString.call(this).replace(/\}?$/, sentinel + "; $&");
- value.toSource = function toSource() toString.toSource.call(this).replace(/\}?$/, sentinel + "; $&");
delete desc.value;
delete desc.writable;
desc.get = function get() value;
desc.set = function set(val) {
- if (String(val).indexOf(sentinel) < 0)
+ if (!callable(val) || Function.prototype.toString(val).indexOf(sentinel) < 0)
Class.replaceProperty(this, k, val);
else {
let package_ = util.newURI(util.fixURI(Components.stack.caller.filename)).host;
};
}
- Object.defineProperty(object, k, desc);
+ try {
+ Object.defineProperty(object, k, desc);
+
+ if (callable(value)) {
+ let sentinel = "(function DactylOverlay() {}())"
+ value.toString = function toString() toString.toString.call(this).replace(/\}?$/, sentinel + "; $&");
+ value.toSource = function toSource() toSource.toSource.call(this).replace(/\}?$/, sentinel + "; $&");
+ }
+ }
+ catch (e) {
+ try {
+ if (value) {
+ object[k] = value;
+ return;
+ }
+ }
+ catch (f) {}
+ util.reportError(e);
+ }
}, this);
return function unwrap() {
for each (let k in Object.getOwnPropertyNames(original))
- Object.defineProperty(object, k, Object.getOwnPropertyDescriptor(original, k));
+ if (Object.getOwnPropertyDescriptor(object, k).configurable)
+ Object.defineProperty(object, k, Object.getOwnPropertyDescriptor(original, k));
+ else {
+ try {
+ object[k] = original[k];
+ }
+ catch (e) {}
+ }
};
},
if (field instanceof Ci.nsIDOMHTMLInputElement && field.type == "submit")
elems.push(encode(field.name, field.value));
- for (let [, elem] in iter(form.elements)) {
- if (set.has(util.editableInputs, elem.type)
- || /^(?:hidden|textarea)$/.test(elem.type)
- || elem.checked && /^(?:checkbox|radio)$/.test(elem.type))
- elems.push(encode(elem.name, elem.value, elem === field));
- else if (elem instanceof Ci.nsIDOMHTMLSelectElement) {
- for (let [, opt] in Iterator(elem.options))
- if (opt.selected)
- elems.push(encode(elem.name, opt.value));
+ for (let [, elem] in iter(form.elements))
+ if (elem.name && !elem.disabled) {
+ if (Set.has(util.editableInputs, elem.type)
+ || /^(?:hidden|textarea)$/.test(elem.type)
+ || elem.type == "submit" && elem == field
+ || elem.checked && /^(?:checkbox|radio)$/.test(elem.type))
+ elems.push(encode(elem.name, elem.value, elem === field));
+ else if (elem instanceof Ci.nsIDOMHTMLSelectElement) {
+ for (let [, opt] in Iterator(elem.options))
+ if (opt.selected)
+ elems.push(encode(elem.name, opt.value));
+ }
}
- }
+
if (post)
return [url, elems.join('&'), charset, elems];
return [url + "?" + elems.join('&'), null, charset, elems];
// Replace replacement <tokens>.
if (tokens)
- expr = String.replace(expr, /(\(?P)?<(\w+)>/g, function (m, n1, n2) !n1 && set.has(tokens, n2) ? tokens[n2].dactylSource || tokens[n2].source || tokens[n2] : m);
+ expr = String.replace(expr, /(\(?P)?<(\w+)>/g, function (m, n1, n2) !n1 && Set.has(tokens, n2) ? tokens[n2].dactylSource || tokens[n2].source || tokens[n2] : m);
// Strip comments and white space.
if (/x/.test(flags))
}())
}),
+ /**
+ * Reloads dactyl in entirety by disabling the add-on and
+ * re-enabling it.
+ */
rehash: function (args) {
- JSMLoader.commandlineArgs = args;
+ storage.session.commandlineArgs = args;
this.timeout(function () {
+ services.observer.notifyObservers(null, "startupcache-invalidate", "");
this.rehashing = true;
let addon = config.addon;
addon.userDisabled = true;
errorCount: 0,
errors: Class.memoize(function () []),
maxErrors: 15,
+ /**
+ * Reports an error to the Error Console and the standard output,
+ * along with a stack trace and other relevant information. The
+ * error is appended to {@see #errors}.
+ */
reportError: function (error) {
if (error.noTrace)
return;
catch (e) {}
let ary = host.split(".");
- ary = [ary.slice(i).join(".") for (i in util.range(ary.length - 1, 0, -1))];
+ ary = [ary.slice(i).join(".") for (i in util.range(ary.length, 0, -1))];
return ary.filter(function (h) h.length >= base.length);
},
return true;
},
- highlightFilter: function highlightFilter(str, filter, highlight) {
- return this.highlightSubstrings(str, (function () {
- if (filter.length == 0)
- return;
- let lcstr = String.toLowerCase(str);
- let lcfilter = filter.toLowerCase();
- let start = 0;
- while ((start = lcstr.indexOf(lcfilter, start)) > -1) {
- yield [start, filter.length];
- start += filter.length;
- }
- })(), highlight || template.filter);
- },
-
/**
* Behaves like String.split, except that when *limit* is reached,
* the trailing element contains the entire trailing portion of the
},
yielders: 0,
+ /**
+ * Yields execution to the next event in the current thread's event
+ * queue. This is a potentially dangerous operation, since any
+ * yielders higher in the event stack will prevent execution from
+ * returning to the caller until they have finished their wait. The
+ * potential for deadlock is high.
+ *
+ * @param {boolean} flush If true, flush all events in the event
+ * queue before returning. Otherwise, wait for an event to
+ * process before proceeding.
+ * @param {boolean} interruptable If true, this yield may be
+ * interrupted by pressing <C-c>, in which case,
+ * Error("Interrupted") will be thrown.
+ */
threadYield: function (flush, interruptable) {
this.yielders++;
try {
do {
mainThread.processNextEvent(!flush);
if (util.interrupted)
- throw new Error("Interrupted");
+ throw Error("Interrupted");
}
while (flush === true && mainThread.hasPendingEvents());
}
}
},
+ /**
+ * Waits for the function *test* to return true, or *timeout*
+ * milliseconds to expire.
+ *
+ * @param {function} test The predicate on which to wait.
+ * @param {object} self The 'this' object for *test*.
+ * @param {Number} timeout The maximum number of milliseconds to
+ * wait.
+ * @optional
+ * @param {boolean} interruptable If true, may be interrupted by
+ * pressing <C-c>, in which case, Error("Interrupted") will be
+ * thrown.
+ */
waitFor: function waitFor(test, self, timeout, interruptable) {
let end = timeout && Date.now() + timeout, result;
return result;
},
+ /**
+ * Makes the passed function yieldable. Each time the function calls
+ * yield, execution is suspended for the yielded number of
+ * milliseconds.
+ *
+ * Example:
+ * let func = yieldable(function () {
+ * util.dump(Date.now()); // 0
+ * yield 1500;
+ * util.dump(Date.now()); // 1500
+ * });
+ * func();
+ *
+ * @param {function} func The function to mangle.
+ * @returns {function} A new function which may not execute
+ * synchronously.
+ */
yieldable: function yieldable(func)
function magic() {
let gen = func.apply(this, arguments);
})();
},
+ /**
+ * Wraps a callback function such that its errors are not lost. This
+ * is useful for DOM event listeners, which ordinarily eat errors.
+ * The passed function has the property *wrapper* set to the new
+ * wrapper function, while the wrapper has the property *wrapped*
+ * set to the original callback.
+ *
+ * @param {function} callback The callback to wrap.
+ * @returns {function}
+ */
wrapCallback: wrapCallback,
/**
*/
trapErrors: function trapErrors(func, self) {
try {
- if (isString(func))
+ if (!callable(func))
func = self[func];
return func.apply(self || this, Array.slice(arguments, 2));
}
}
},
+ /**
+ * Returns the file path of a given *url*, for debugging purposes.
+ * If *url* points to a file (even if indirectly), the native
+ * filesystem path is returned. Otherwise, the URL itself is
+ * returned.
+ *
+ * @param {string} url The URL to mangle.
+ * @returns {string} The path to the file.
+ */
urlPath: function urlPath(url) {
try {
return util.getFile(url).path;
}
},
+ /**
+ * Returns a list of all domains and subdomains of documents in the
+ * given window and all of its descendant frames.
+ *
+ * @param {nsIDOMWindow} win The window for which to find domains.
+ * @returns {[string]} The visible domains.
+ */
visibleHosts: function (win) {
let res = [], seen = {};
(function rec(frame) {
try {
- res = res.concat(util.subdomains(frame.location.host));
+ if (frame.location.hostname)
+ res = res.concat(util.subdomains(frame.location.hostname));
}
catch (e) {}
Array.forEach(frame.frames, rec);
})(win);
- return res.filter(function (h) !set.add(seen, h));
+ return res.filter(function (h) !Set.add(seen, h));
},
+ /**
+ * Returns a list of URIs of documents in the given window and all
+ * of its descendant frames.
+ *
+ * @param {nsIDOMWindow} win The window for which to find URIs.
+ * @returns {[nsIURI]} The visible URIs.
+ */
visibleURIs: function (win) {
let res = [], seen = {};
(function rec(frame) {
catch (e) {}
Array.forEach(frame.frames, rec);
})(win);
- return res.filter(function (h) !set.add(seen, h.spec));
+ return res.filter(function (h) !Set.add(seen, h.spec));
+ },
+
+ /**
+ * Wraps native exceptions thrown by the called function so that a
+ * proper stack trace may be retrieved from them.
+ *
+ * @param {function|string} meth The method to call.
+ * @param {object} self The 'this' object of the method.
+ * @param ... Arguments to pass to *meth*.
+ */
+ withProperErrors: function withProperErrors(meth, self) {
+ try {
+ return (callable(meth) ? meth : self[meth]).apply(self, Array.slice(arguments, withProperErrors.length));
+ }
+ catch (e) {
+ throw e.stack ? e : Error(e);
+ }
},
/**
-moz-binding: url(resource://dactyl-content/bindings.xml#frame) !important;
}
+[dactyl|modifiable][dactyl|modifiable] {
+ -moz-user-input: enabled !important;
+}
+
[dactyl|highlight~=hints] {
-moz-binding: url(resource://dactyl-content/bindings.xml#hints) !important;
+ position: static !important;
}
[dactyl|highlight~=HintImage],
}),
/**
- * Returns dactyl to normal mode.
+ * Returns dactyl to Normal mode.
*/
setNormalMode: wrapAssertNoErrors(function () {
// XXX: Normal mode test
utils.assert("dactyl.setNormalMode", this.modules.modes.stack.length == 1,
"Failed to return to Normal mode");
- this.assertMessageWindowOpen(false, "Returning to normal mode: Multi-line output not closed");
- this.assertMessageLine(function (msg) !msg, "Returning to normal mode: Message not cleared");
- }, "Returning to normal mode"),
+ this.assertMessageWindowOpen(false, "Returning to Normal mode: Multi-line output not closed");
+ this.assertMessageLine(function (msg) !msg, "Returning to Normal mode: Message not cleared");
+ }, "Returning to Normal mode"),
/**
* Returns dactyl to Ex mode.
"!": {
multiOutput: ["echo foo"]
},
+ get Clistkeys() this.listcommands,
+ Cmap: {},
+ Cnoremap: {},
+ Cunmap: {},
+ get Ilistkeys() this.listcommands,
+ Imap: {},
+ Inoremap: {},
+ Iunmap: {},
abbreviate: {
+ error: ["!"],
someOutput: ["", "abc"],
noOutput: ["abc def", "-js abc def"],
completions: ["", "abc ", "-js abc "]
},
addons: {
+ error: ["!"],
multiOutput: ["", "dactyl", "-type=extension", "-type=extension dactyl"],
completions: [
"",
anyOutput: ["about:pentadactyl"],
completions: [["", hasItems]]
},
+ get blistkeys() this.listcommands,
+ bmap: {},
bmark: {
singleOutput: ["", "-tags=foo -title=bar -keyword=baz -charset=UTF-8 -post=quux about:pentadactyl"],
error: ["-tags=foo -title=bar -keyword=baz -charset=nonExistentCharset -post=quux about:pentadactyl"],
"-max=1 -keyword=foo -tags=bar -title=baz about:"
]
},
+ bnoremap: {},
buffer: {
anyOutput: ["", "1"],
noOutput: ["!", "! 1"],
]
},
buffers: {
+ error: ["!"],
multiOutput: ["", "1"],
completions: ["", "1"]
},
+ bunmap: {},
cd: {
+ error: ["!"],
singleOutput: ["", "~/"],
completions: ["", "~/"]
},
+ get clistkeys() this.listcommands,
+ cmap: {},
+ cnoremap: {},
colorscheme: {
- error: ["", "some-nonexistent-scheme"]
+ error: ["!", "", "some-nonexistent-scheme"]
},
command: {
init: ["delc!"],
["-group=user ", hasItems]
]
},
- contexts: {}, // Not testable in this manner
+ completions: {
+ error: ["!", ""]
+ },
+ contexts: { // Not testable in this manner
+ error: ["!"]
+ },
cookies: {
anyOutput: ["dactyl.sf.net", "dactyl.sf.net list"],
- error: [""],
+ error: ["!", ""],
completions: [
"",
["dactyl.sf.net ", hasItems]
]
},
+ cunabbreviate: {},
+ cunmap: {},
delbmarks: { anyOutput: ["", "about:pentadactyl"] },
delcommand: [
{
error: ["foo"]
}
],
+ delgroup: {
+ error: ["", "! foo", "builtin"],
+ completions: [""]
+ },
delmacros: {
- error: [""],
+ error: ["", "! foo"],
noOutput: ["x"],
completions: ["", "x"]
},
completions: ["", "-name=", "-name=foo ", "-index=", "-index="]
},
dialog: {
+ error: ["!", ""],
// Skip implementation for now
completions: [
["", hasntNullItems]
]
},
- doautoall: {}, // Skip for now
- doautocmd: {}, // Skip for now
+ dlclear: {
+ error: ["!"]
+ },
+ doautoall: {
+ error: ["!"]
+ },
+ doautocmd: {
+ error: ["!"]
+ },
downloads: {
+ error: ["!"],
multiOutput: ["", "dactyl", "dactyl"]
},
echo: {
+ error: ["!"],
singleOutput: [
["' - '", " - "]
],
__proto__: this.echo,
}),
get echomsg() this.echo,
- else: {}, // Skip for now
- elseif: {}, // Skip for now
+ else: {
+ error: ["!", "foo"]
+ },
+ elseif: {
+ error: ["!", ""]
+ },
emenu: {
noOutput: ["View.Zoom.Zoom In", "View.Zoom.Zoom Out"],
- error: [""],
+ error: ["!", ""],
completions: [
["", hasItems],
["View.", hasItems]
]
},
- endif: {}, // Skip for now
+ endif: {
+ error: ["!", "foo"]
+ },
execute: {
+ error: ["!"],
noOutput: ["", "'js " + "".quote() + "'"],
someOutput: ["'ls'"],
completions: [["", hasItems]]
},
+ exit: {
+ error: ["foo"]
+ },
extadd: {
completions: [["", hasItems]],
- error: [""]
+ error: ["!", ""]
},
extdelete: {
completions: [["", hasItems]],
noOutput: ["<Esc>"],
error: [""]
},
- finish: { noOutput: [""] },
+ finish: {
+ error: ["!", "foo"],
+ noOutput: [""]
+ },
forward: { noOutput: [""] },
- frameonly: { noOutput: [""] },
- delgroup: {
- error: ["builtin"],
- completions: [""]
+ frameonly: {
+ error: ["!", "foo"],
+ noOutput: [""]
},
group: {
multiOutput: [""],
},
hardcopy: {}, // Skip for now
help: {
+ error: ["!"],
noOutput: ["", "intro"],
cleanup: ["tabdelete", "tabdelete"],
completions: [
},
get helpall() this.help,
highlight: {
+ error: ["!"],
multiOutput: ["", "Help"],
noOutput: [
"Help foo: bar;",
"-sort=+date about:"
]
},
- if: {}, // Skip for now
+ if: {
+ error: ["!", ""],
+ },
+ iabbreviate: {},
+ get ilistkeys() this.listcommands,
+ imap: {},
+ inoremap: {},
+ iunabbreviate: {},
+ iunmap: {},
javascript: {
- noOutput: ["''", "'\\n'", "<pre>foo bar</pre>", "window"],
+ noOutput: ["''", "'\\n'", "<pre>foo bar</pre>", "window", "<<EOF\n''\nEOF"],
completions: [
["", hasItems],
["window", hasItems],
]
},
jumps: {
+ error: ["!", "foo"],
multiOutput: [""]
},
keepalt: {
- error: [""],
+ error: ["!", "", "some-nonexistent-command"],
noOutput: ["js ''"],
- anyOutput: ["echo 'foo'"]
+ anyOutput: ["echo 'foo'"],
+ completions: [["", hasItems]]
+ },
+ let: {
+ error: ["!"]
},
- let: {}, // Deprecated. Fuck it.
listcommands: {
+ error: ["!"],
anyOutput: ["", "in"],
completions: [
["", hasItems],
get listoptions() this.listcommands,
loadplugins: {},
macros: {
+ error: ["!"],
multiOutput: [""],
completions: [""]
},
map: {
+ error: ["!"],
init: ["unmap!"],
anyOutput: [""],
singleOutput: ["i"],
]
},
mark: {
- error: ["", "#", "xy"],
+ error: ["!", "", "#", "xy"],
noOutput: ["y"],
completions: [""]
},
marks: {
init: ["delmarks q"],
multiOutput: ["", "y"],
- error: ["q", "#"],
+ error: ["!", "q", "#"],
completions: [""]
},
messages: {
+ error: ["!", "foo"],
anyOutput: ["messages"]
},
messclear: {
- error: ["q"],
+ error: ["!", "foo"],
noOutput: [""]
},
mkpentadactylrc: {
],
cleanup: ["silent !rm -r some-nonexistent-pentadactyl-dir/"]
},
+ get mlistkeys() this.listcommands,
+ mmap: {},
+ mnoremap: {},
+ munmap: {},
+ get nlistkeys() this.listcommands,
+ nmap: {},
+ nnoremap: {},
+ nohlfind: {
+ error: ["!", "foo"]
+ },
+ noremap: {},
normal: {
+ error: [""],
noOutput: ["<Nop>"],
singleOutput: ["<C-g>"],
multiOutput: ["g<C-g>"]
},
+ nunmap: {},
open: {
+ error: ["!"],
noOutput: ["about:blank | about:home"],
completions: [
["", hasItems],
pageinfo: {
multiOutput: ["", "fgm"],
completions: [["", hasItems]],
- error: ["abcdefghijklmnopqrstuvwxyz", "f g m"]
+ error: ["!", "abcdefghijklmnopqrstuvwxyz", "f g m"]
},
pagestyle: {
+ error: ["!"],
completions: [""]
},
- preferences: {}, // Skip for now
+ pintab: {},
+ preferences: {
+ error: ["foo"]
+ },
pwd: {
+ error: ["!", "foo"],
singleOutput: [""]
},
qmark: {
"m",
"m foo bar"
],
- error: ["", "#"],
+ error: ["!", "", "#"],
completions: [
["", hasItems],
["m ", hasItems]
]
},
qmarks: [
+ {
+ error: ["!"]
+ },
{
init: ["delqmarks a-zA-Z0-9"],
error: ["", "x"],
completions: [["", hasItems]]
}
],
- quit: {}, // Skip for now
- quitall: {}, // Skip for now
+ quit: {
+ error: ["foo"]
+ },
+ quitall: {
+ error: ["!", "foo"]
+ },
redraw: {
+ error: ["!", "foo"],
noOutput: [""]
},
- rehash: {}, // Skip for now
+ rehash: {
+ error: ["!"]
+ },
reload: {
+ error: ["foo"],
noOutput: [""]
},
reloadall: {
+ error: ["foo"],
noOutput: [""]
},
- restart: {}, // Skip
+ restart: {
+ error: ["!", "foo"]
+ },
runtime: {
init: [
"js File('~/.pentadactyl/some-nonexistent/good.css').write('')",
"some-nonexistent/good.penta"
],
error: [
+ "",
"some-nonexistent/bad.js",
"some-nonexistent/bad.penta"
],
},
saveas: {},
sbclose: {
+ error: ["!", "foo"],
noOutput: [""]
},
- scriptnames: {},
+ scriptnames: {
+ error: ["!", "foo"]
+ },
set: {
multiOutput: [
"vb?", "cpt?", "messages?", "titlestring?", "au?", "eht?",
- "cpt", "messages", "titlestring", "au", "eht"
+ "cpt", "messages", "titlestring", "au", "eht", "! "
],
noOutput: ["vb", "novb"],
completions: [
]
},
silent: {
+ error: ["!"],
noOutput: [
"echo 'foo'",
"echo " + "foo\nbar".quote(),
".pentadactyl/some-nonexistent/good.penta"
],
error: [
+ "",
".pentadactyl/some-nonexistent/really-nonexistent.js",
"~/.pentadactyl/some-nonexistent/bad.js",
"~/.pentadactyl/some-nonexistent/bad.penta",
["resource://dactyl/", hasItems]
]
}),
- stop: { noOutput: [""] },
- stopall: { noOutput: [""] },
+ stop: {
+ error: ["!", "foo"],
+ noOutput: [""]
+ },
+ stopall: {
+ error: ["!", "foo"],
+ noOutput: [""]
+ },
style: {
+ error: ["!"],
cleanup: ["delstyle -n foo"],
noOutput: [
"-name=foo http://does.not.exist/* div { display: inline; }",
["-index=", hasNItems(2)]
]
},
- tab: {},
- tabattach: {},
- tabdetach: {},
- tabdo: {},
- tabduplicate: {},
- tablast: {},
- tabmove: {},
- tabnext: {},
- tabonly: {},
+ tab: {
+ error: ["!", "", "some-nonexistent-command"],
+ noOutput: ["js ''"],
+ anyOutput: ["echo 'foo'"],
+ completions: [["", hasItems]]
+ },
+ tabattach: {
+ error: ["!", ""]
+ },
+ tabdetach: {
+ error: ["!", "foo"]
+ },
+ tabdo: {
+ error: ["!", "", "some-nonexistent-command"],
+ noOutput: ["js ''"],
+ anyOutput: ["echo 'foo'"],
+ completions: [["", hasItems]]
+ },
+ tabduplicate: {
+ error: ["foo"]
+ },
+ tablast: {
+ error: ["!", "foo"]
+ },
+ tabmove: {
+ error: [""],
+ noOutput: ["1", "$", "999", "-1", "+1", "! +1", "! -1", "-999", "+999", "! +999", "! -999"],
+ completions: [
+ ["", hasItems],
+ ["1", hasItems]
+ ]
+ },
+ tabnext: {
+ error: ["!", "foo"]
+ },
+ tabonly: {
+ error: ["!", "foo"]
+ },
tabopen: {},
- tabprevious: {},
- tabrewind: {},
- time: {},
+ tabprevious: {
+ error: ["!", "foo"]
+ },
+ tabrewind: {
+ error: ["!", "foo"]
+ },
+ time: {
+ error: ["", ":some-nonexistent-command"/*, "some_nonexistent_reference"*/], // FIXME
+ singleOutput: [":js null", "null"]
+
+ },
+ get tlistkeys() this.listcommands,
+ tmap: {},
+ tnoremap: {},
toolbarhide: {
init: [
["tbs Navigation Toolbar", toolbarState("#nav-bar", true)],
["Navigation Toolbar", toolbarState("#nav-bar", false)],
["Bookmarks Toolbar", toolbarState("#PersonalToolbar", false)]
],
- error: ["", "foo"]
+ error: ["!", "", "foo"]
},
toolbarshow: {
completions: [["", hasItems]],
["Navigation Toolbar", toolbarState("#nav-bar", true)],
["Bookmarks Toolbar", toolbarState("#PersonalToolbar", true)]
],
- error: ["", "foo"]
+ error: ["!", "", "foo"]
},
toolbartoggle: {
completions: [["", hasItems]],
["Navigation Toolbar", toolbarState("#nav-bar", false)],
["Bookmarks Toolbar", toolbarState("#PersonalToolbar", false)]
],
- error: ["", "foo"]
+ error: ["!", "", "foo"]
},
+ tunmap: {},
unabbreviate: {
noOutput: ["abc", "! "],
error: [""]
},
- undo: {},
- undoall: {},
- unlet: {},
+ undo: {
+ error: ["!"]
+ },
+ undoall: {
+ error: ["!", "foo"]
+ },
+ unpintab: {
+ error: ["!"]
+ },
+ unlet: {
+ error: [""],
+ },
unmap: {
noOutput: [
"i",
"-group="
]
},
- verbose: {},
+ verbose: {
+ error: ["!", ""]
+ },
version: {
+ error: ["foo"],
multiOutput: [
["", function (msg) {
var res = /(\w+dactyl) (\S+) \(([\^)]+)\) running on:\nMozilla/;
]
},
viewsource: {},
- winclose: {},
- window: {},
- winonly: {},
- winopen: {},
- wqall: {},
+ get vlistkeys() this.listcommands,
+ vmap: {},
+ vnoremap: {},
+ vunmap: {},
+ winclose: {
+ error: ["!", "foo"]
+ },
+ window: {
+ error: ["!", ""]
+ },
+ winonly: {
+ error: ["!", "foo"]
+ },
+ winopen: {
+ error: ["!"]
+ },
+ wqall: {
+ error: ["!", "foo"]
+ },
yank: {
multiOutput: [
["foo".quote(), /foo/],
[":addons", /Pentadactyl/]
],
error: [
+ "!", "",
":echoerr " + "foo".quote()
],
completions: [
function testCompleters() {
for (var option in dactyl.modules.options)
- for (var [,value] in Iterator([""].concat(options[option.name] || []))) {
+ for (var [, value] in Iterator([""].concat(options[option.name] || []))) {
dump("OPT COMP " + option.name + " " + value + "\n");
dactyl.testCompleter(option, "completer", value, "Option '" + option.name + "' completer failed");
}
},
/*** optional options, there are checked for existence and a fallback provided ***/
- features: set(["bookmarks", "hints", "marks", "history", "quickmarks", "session", "tabs", "player"]),
+ features: Set(["bookmarks", "hints", "marks", "history", "quickmarks", "session", "tabs", "player"]),
defaults: {
guioptions: "bCmprs",
/**
* Returns an array of all the artist names in the main library.
*
- * @returns {string[]}
+ * @returns {[string]}
*/
getArtists: function getArtists() this._toJSArray(this.MAIN_LIBRARY.getDistinctValuesForProperty(SBProperties.artistName)),
* library.
*
* @param {string} artist The artist's name.
- * @returns {string[]}
+ * @returns {[string]}
*/
getAlbums: function getAlbums(artist) {
let albums = this._toJSArray(this.MAIN_LIBRARY.getItemsByProperty(SBProperties.artistName, artist))
*
* @param {string} artist The artist's name.
* @param {string} album The album's name.
- * @returns {string[]}
+ * @returns {[string]}
*/
getTracks: function getTracks(artist, album) {
let properties = services.MutablePropertyArray();
onMediacoreEvent: function (event) {
switch (event.type) {
case Ci.sbIMediacoreEvent.BEFORE_TRACK_CHANGE:
- dactyl.log("Before track changed: " + event.data);
+ dactyl.log(_("player.preTrackChange", event.data));
autocommands.trigger("TrackChangePre", { track: event.data });
break;
case Ci.sbIMediacoreEvent.TRACK_CHANGE:
+ dactyl.log(_("player.trackChanged", event.data));
autocommands.trigger("TrackChange", { track: event.data });
break;
case Ci.sbIMediacoreEvent.BEFORE_VIEW_CHANGE:
- dactyl.log("Before view changed: " + event.data);
+ dactyl.log(_("player.preViewChange", event.data));
autocommands.trigger("ViewChangePre", { view: event.data });
break;
case Ci.sbIMediacoreEvent.VIEW_CHANGE:
- dactyl.log("View changed: " + event.data);
+ dactyl.log(_("player.viewChange", event.data));
autocommands.trigger("ViewChange", { view: event.data });
break;
case Ci.sbIMediacoreEvent.STREAM_START:
- dactyl.log("Track started: " + gMM.sequencer.currentItem);
+ dactyl.log(_("player.trackStart", gMM.sequencer.currentItem));
autocommands.trigger("StreamStart", { track: gMM.sequencer.currentItem });
break;
case Ci.sbIMediacoreEvent.STREAM_PAUSE:
- dactyl.log("Track paused: " + gMM.sequencer.currentItem);
+ dactyl.log(_("player.trackPause", gMM.sequencer.currentItem));
autocommands.trigger("StreamPause", { track: gMM.sequencer.currentItem });
break;
case Ci.sbIMediacoreEvent.STREAM_END:
- dactyl.log("Track ended: " + gMM.sequencer.currentItem);
+ dactyl.log(_("player.trackEnd", gMM.sequencer.currentItem));
autocommands.trigger("StreamEnd", { track: gMM.sequencer.currentItem });
break;
case Ci.sbIMediacoreEvent.STREAM_STOP:
- dactyl.log("Track stopped: " + gMM.sequencer.currentItem);
+ dactyl.log(_("player.trackStop", gMM.sequencer.currentItem));
autocommands.trigger("StreamStop", { track: gMM.sequencer.currentItem });
break;
}
/**
* Returns an array of all available playlists.
*
- * @returns {sbIMediaList[]}
+ * @returns {[sbIMediaList]}
*/
getPlaylists: function getPlaylists() {
let mainLibrary = LibraryUtils.mainLibrary;
/**
* Returns an array of all available media pages.
*
- * @returns {sbIMediaPageInfo[]}
+ * @returns {[sbIMediaPageInfo]}
*/
getMediaPages: function getMediaPages() {
let list = SBGetBrowser().currentMediaPage.mediaListView.mediaList;
},
/**
- * Loads the the specified media page into *view* with the given *list* of
+ * Loads the specified media page into *view* with the given *list* of
* media items.
*
* @param {sbIMediaPage} page
let arg = args[0];
dactyl.assert(arg, _("error.argumentRequired"));
- dactyl.assert(/^[+-]?\d+$/.test(arg), _("error.trailing"));
+ dactyl.assert(/^[+-]?\d+$/.test(arg), _("error.trailingCharacters"));
let level = parseInt(arg, 10) / 100;
xmlns="&xmlns.dactyl;"
xmlns:html="&xmlns.html;">
-<dl tag="dialog-list" replace="dialog-list">
- <dt>about</dt> <dd>About &dactyl.host;</dd>
- <dt>addons</dt> <dd>Manage Add-ons</dd>
- <dt>checkupdates</dt> <dd>Check for updates</dd>
- <dt>cleardata</dt> <dd>Clear private data</dd>
- <dt>cookies</dt> <dd>List your cookies</dd>
- <dt>console</dt> <dd>JavaScript console</dd>
- <dt>dominspector</dt> <dd>DOM Inspector</dd>
- <dt>downloads</dt> <dd>Manage Downloads</dd>
- <dt>jumpto</dt> <dd>Jump to a media item</dd>
- <dt>newsmartplaylist</dt><dd>Create a new smart playlist</dd>
- <dt>openfile</dt> <dd>Open the file selector dialog</dd>
- <dt>pagesource</dt> <dd>View page source</dd>
- <dt>places</dt> <dd>Places Organizer: Manage your bookmarks and history</dd>
- <dt>preferences</dt> <dd>Show &dactyl.host; preferences dialog</dd>
- <dt>printsetup</dt> <dd>Setup the page size and orientation before printing</dd>
- <dt>print</dt> <dd>Show print dialog</dd>
- <dt>saveframe</dt> <dd>Save frame to disk</dd>
- <dt>savepage</dt> <dd>Save page to disk</dd>
- <dt>searchengines</dt> <dd>Manage installed search engines</dd>
- <dt>selectionsource</dt> <dd>View selection source</dd>
- <dt>subscribe</dt> <dd>Add a new subscription</dd>
-</dl>
-
<item replace=":sbcl">
<tags>:dpcl :dpclose</tags>
<spec>:dpcl<oa>ose</oa> <a>pane</a></spec>
etc.)
</li>
<li>
- <link topic="cmdline">Command-line mode</link>:
+ <link topic="cmdline">Command Line mode</link>:
Command-line editing.
</li>
<li>
--- /dev/null
+player.preTrackChange-1 = Before track changed: %S
+player.postTrackChange-1 = Track changed: %S
+player.preViewChange-1 = Before view changed: %S
+player.postViewChange-1 = View changed: %S
+player.trackStart-1 = Track started: %S
+player.trackPause-1 = Track paused: %S
+player.trackEnd-1 = Track ended: %S
+player.trackStop-1 = Track stopped: %S
+
+# vim:se ft=jproperties tw=0:
Developers:
* Kris Maglione
* Doug Kearns <dougkearns@gmail.com>
- * anekos <anekos@snca.net>
- * teramako <teramako@gmail.com>
+
+Contributers:
* Štěpán Němec
Inactive/former developers:
* janus_wel <janus.wel.3@gmail.com>
* Martin Stubenschrott <stubenschrott@vimperator.org>
* Conrad Irwin
+ * anekos <anekos@snca.net>
+ * teramako <teramako@gmail.com>
Patches (in no special order):
* Ruud Grosmann ('followhints' option)
FIREFOX ?= firefox
HOSTAPP ?= $(FIREFOX)
PROFILEPATHS ?= "$$HOME/.mozilla/firefox" \
- "$$HOME/Library/Mozilla/Firefox" \
- "$$APPDATA/Mozilla/Firefox" \
- "$$AppData/Mozilla/Firefox"
+ "$$HOME/Library/Application Support/Firefox" \
+ "$$APPDATA/Mozilla/Firefox/Profiles" \
+ "$$AppData/Mozilla/Firefox/Profiles"
include ../common/Makefile
-1.0b6:
+1.0b7pre:
• Extensive Firefox 4 support, including:
- Fully restartless. Can now be installed, uninstalled,
enabled, disabled, and upgraded without restarting Firefox.
quotes, i.e., 'fo\o''bar' ⇒ fo\o'bar [b1]
• The command line is now hidden by default. Added c, C, and M to
'guioptions'. [b4]
- • Hint mode improvements, including:
- - Added g; continued extended hint mode, which allows
+ • Hints mode improvements, including:
+ - Added g; continued extended hints mode, which allows
selecting multiple hints. Removed ;F. [b1]
- Hints are now updated after scrolling and window resizing. [b3]
- ;s now prompts for a filename on the command-line rather
• The external editor can now be configured to open to a given
line number and column, used for opening source links and
editing input fields with i_<C-i>. See :h 'editor'. [b4]
+ • Improved [macro-string] support, including automatic elision
+ of optional elements, and array subscripts. [b4][b7]
• Mapping changes:
- It's now possible to map keys in many more modes, including
Hint, Multi-line Output, and Menu. [b4]
- Added site-specific mapping groups and related command
changes. [b6]
- Added 'timeout' and 'timeoutlen' options. [b6]
+ - Added n_{, n_}, n_[, and n_] mappings. [b7]
- Added <A-b> to execute a builtin mapping. [b6]
- Added <A-m>l and <A-m>s to aid in the construction of
macros. [b6]
listing keys for modes other than Normal, filtering the output,
and linking to source code locations). [b4]
- :downloads now opens a download list in the multi-line output
- buffer. [b6]
+ buffer. Added -sort flag. [b6][b7]
+ - :style now supports regexp site-filters on Firefox 6+. [b7]
+ - :qa closes only the current window, per Vim. [b7]
+ - Added :exit command. [b7]
+ - Added :dlclear command. [b7]
- :extensions has been replaced with a more powerful :addons. [b6]
- :javascript! now opens a Read Eval Print Loop. [b6]
- Added -arg flag to :map. [b6]
- Added several new options, including -javascript, to :abbreviate and
:map. [b2]
- Added :mksyntax command to auto-generate Vim syntax files. [b4]
+ - Added 's' and 'e' flags to :pageinfo command. [b7]
+ - Added :pintab and :unpintab commands. [b7]
+ - Extension manipulation commands, including :extenable, :extdisable,
+ and :exttoggle, now accept a -types argument. [b7]
+ - :tabmove now moves to the position of tab N, not after it.
+ Without arguments it no longer moves the tab to the end of
+ the list. [b7]
- :open now only opens files beginning with /, ./, ../, or ~/ [b1]
- :saveas now provides completions for default file names, and
automatically chooses a filename when the save target is a
- CSS property name completion is now available. [b4]
- :delstyle, :styleenable, :styledisable and :styletoggle accept a !
to operate on all styles. [b6]
+ - Added Find highlighting group. [b7]
• IMPORTANT option changes:
- Option value quoting has changed. List options will
no longer be split at quoted commas and the option name,
operators, and = sign may no longer be quoted. This will
break certain automatically-generated configuration files.
- See :help
- [stringlist]. [b2]
+ See :help [stringlist]. [b2]
- Boolean options no longer accept an argument. [b4]
- 'cdpath' and 'runtimepath' no longer treat ",,"
specially. Use "." instead. [b2]
- 'extendedhinttags' is now a [regexpmap] rather than a
string. [b2]
- - 'guioptions' default value has changed. [b4]
+ - 'guioptions' default value has changed. [b4][b7]
- 'hinttags' and 'extendedhinttags' now treat their values as
CSS selectors by default. [b6]
- 'incsearch', 'hlsearch', 'ignorecase', and 'smartcase' have
'guioptions'. [b4]
- 'linksearch' has been removed. The \l search modifier can
still be used for this purpose. [b4]
- - 'loadplugins' is now a regexplist option rather than
+ - 'loadplugins' is now a [regexplist] option rather than
a boolean. [b2]
- 'mapleader' is now an option rather than a :let
variable. [b4]
- - 'passkeys' is now a sitemap with key chain support rather
- than a regexpmap. [b6]
- - 'showmode' is now a regexplist. [b6]
- - 'showstatuslinks' and 'showtabline' are now string options. [b4]
+ - 'passkeys' is now a [sitemap] with key chain support rather
+ than a [regexpmap]. [b6]
+ - 'showmode' is now a [regexplist]. [b6]
+ - 'showstatuslinks' and 'showtabline' are now [string] options. [b4]
• IMPORTANT: Command script files now use the *.penta file extension. [b2]
• IMPORTANT: Plugins are now loaded from the 'plugins/'
directory in 'runtimepath' rather than 'plugin/'. [b1]
- Added 'autocomplete' option for specifying which completion
groups should be auto-completed. [b2]
- Added 'banghist' option. [b1]
+ - Added 'cookies', 'cookieaccept', and 'cookielifetime' options. [b3]
+ - Added 'downloadsort' option. [b7]
- Replaced 'focuscontent' with 'strictfocus'. [b1]
+ - Made 'strictfocus' a [sitemap]. [b7]
- 'complete' now defaults to "slf" but file completion only
triggers when the URL begins as above. [b1]
+ - Added 'jumptags' option. [b7]
+ - Added 's' flag to 'pageinfo' and changed default value. [b7]
- Added 'passkeys' option. [b3]
+ - Added 'passunknown' option. [b7]
- Changed 'urlseparator' default value to "|". [b3]
- Added "passwords" and "venkman" dialogs to :dialog. [b2]
+ - Make 'showmode' a [stringlist] option. [b7]
- Added 'wildanchor' option. [b2]
- - Added 'cookies', 'cookieaccept', and 'cookielifetime' options. [b3]
• Added BookmarkChange, BookmarkRemove autocommands. [b2]
• Removed the :source line at the end of files generated by
:mkpentadactylrc. [b2]
1-9 as in Vim (9 = required for next release, 5 = would be nice, 1 = probably not)
ARCHITECTURE:
-- decide on how to document Arrays in our jsdoc(ish) documentation. Host docs
- on the website - is there even a documentation tool that can parse our source
- sensibly?
+- Host docs on the website - is there even a documentation tool that
+ can parse our source sensibly?
- unify the disgusting hodge podge of contract specification styles
-REFACTORING:
-- remove unnecessary usage of "self"
-
BUGS:
-- insert abbreviations broken on <space>
-- :sidebar improvements (:sidebar! Downloads while downloads is open should refocus the sidebar)
- ;s saves the page rather than the image
- RC file is sourced once per window
FEATURES:
+9 <C-o>/<C-i> should work as in Vim (i.e., save page positions as well as
+ locations in the history list).
+9 clean up error message codes and document
+9 option groups, including buffer-local and site-specific
+9 proper motion maps
+8 wherever possible: get rid of dialogs and ask console-like dialog questions
+ or write error prompts directly on the webpage or with :echo()
+8 add search capability to MOW
8 registers
8 Document Caret and Visual modes.
8 replace global variables with plugin scoped user options
-8 fix local options
8 adaptive timeout for auto-completions, :set completions can be updated more often than
:open foo
-8 use the storage module for autocommands
8 add support for filename special characters such as %
8 :redir and 'verbosefile'
8 middleclick in content == p, and if command line is open, paste there the clipboard buffer
8 all search commands should start searching from the top of the visible viewport
-8 <C-o>/<C-i> should work as in Vim (i.e., save page positions as well as
- locations in the history list).
-8 jump to the next heading with ]h, next image ]i, previous textbox [t and so on
-7 clean up error message codes and document
-7 add search capability to MOW
+8 Add information to dactyl/HACKING file about testing and optimization
7 describe-key command (prompt for a key and display its binding with documentation)
7 Specify all INSERT mode mappings rather than falling through to the host apps
mystery meat mappings.
7 use ctrl-n/p in insert mode for word completion
7 implement QuickFix window based on ItemList
-7 wherever possible: get rid of dialogs and ask console-like dialog questions
- or write error prompts directly on the webpage or with :echo()
7 [d could go to the last domain in the history stack. So if I browse from
google to another page and click 10 links there, [d would take me back to the google page
opera's fast forward does something like this
7 make an option to disable session saving by default when you close Firefox
-7 The output of the pageinfo-command should contain the security-information of ssl-encrypted sites
7 :grep support (needs location list)
-7 map <A-n/p> in command-line mode to something useful (such as Down/Up) and
- possibly <C-n/p> to Vim-like behaviour
6 :mksession
6 add [count] support to :b* and :tab* commands where missing
6 check/correct spellings in insert mode with some mappings
6 add more autocommands (TabClose, TabOpen, TabChanged etc)
6 Use ctrl-w+j/k/w to switch between sidebar, content, preview window
-6 Command :tags for getting a list of used tags
6 ;?<hint> should show more information
-6 Add information to dactyl/HACKING file about testing and optimization
5 when looking at a zoomed out image (because it's large), zi should zoom in
maybe with this? : http://mxr.mozilla.org/seamonkey/source/content/html/document/public/nsIImageDocument.idl
-5 make a command to search within google search results
- (http://gadelkareem.com/2007/01/28/using-google-ajax-api-as-an-array/)
- maybe impossible, needs a per-site key from google
-4 } { should jump to the next paragraph of the page
3 A format for 'guitablabel' and 'statusline'
3 add a command-line window (:help cmdline-window in Vim).
3 Splitting Windows with [:sp :vsp ctrl-w,s ctrl-w,v] and closing with [ctrl-w,q], moving with [ctrl-w,w or tab]
have a look into the split browser extension
-1 Reformat dactyl/HACKING so that git diff can find sections and report changes @ somewhere
LocationChange: "Triggered when changing tabs or when navigation to a new location",
PageLoadPre: "Triggered after a page load is initiated",
PageLoad: "Triggered when a page gets (re)loaded/opened",
- PrivateMode: "Triggered when private mode is activated or deactivated",
+ PrivateMode: "Triggered when private browsing mode is activated or deactivated",
Sanitize: "Triggered when a sanitizeable item is cleared",
ShellCmdPost: "Triggered after executing a shell command with :!cmd",
Enter: "Triggered after Firefox starts",
titlestring: "Pentadactyl"
},
- features: set([
+ features: Set([
"bookmarks", "hints", "history", "marks", "quickmarks", "sanitizer",
"session", "tabs", "tabs_undo", "windows"
]),
let title = document.getElementById("sidebar-title");
dactyl.assert(args.length || title.value || args.bang && config.lastSidebar,
- "Argument required");
+ _("error.argumentRequired"));
if (!args.length)
return window.toggleSidebar(title.value ? null : config.lastSidebar);
return;
}
- return dactyl.echoerr("No sidebar " + args[0] + " found");
+ return dactyl.echoerr(_("error.invalidArgument", args[0]));
},
{
argCount: "?",
});
},
{
- argCount: "+",
+ argCount: "1",
completer: function (context) completion.ex(context),
literal: 0,
subCommand: 0
em:id="pentadactyl@dactyl.googlecode.com"
em:type="2"
em:name="Pentadactyl"
- em:version="1.0b6"
+ em:version="1.0b7.1"
em:description="Firefox for Vim and Links addicts"
em:homepageURL="http://dactyl.sourceforge.net/pentadactyl"
em:iconURL="resource://dactyl-local-skin/icon.png"
<em:creator>Kris Maglione, Doug Kearns</em:creator>
<em:developer>Kris Maglione</em:developer>
<em:developer>Doug Kearns</em:developer>
- <em:developer>Štěpán Němec</em:developer>
<em:contributor>anekos</em:contributor>
<em:contributor>teramako</em:contributor>
<em:contributor>janus_wel</em:contributor>
<em:contributor>Martin Stubenschrott</em:contributor>
<em:contributor>Conrad Irwin</em:contributor>
+ <em:contributor>Štěpán Němec</em:contributor>
<em:targetApplication>
<Description
em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
- em:minVersion="3.5"
- em:maxVersion="4.0.*"/>
+ em:minVersion="3.6"
+ em:maxVersion="8.*"/>
</em:targetApplication>
</Description>
</RDF>
<dt>LocationChange</dt> <dd>Triggered when changing tabs or when navigating to a new location</dd>
<dt>PageLoadPre</dt> <dd>Triggered after a page load is initiated</dd>
<dt>PageLoad</dt> <dd>Triggered when a page gets (re)loaded/opened</dd>
- <dt>PrivateMode</dt> <dd>Triggered when private mode is activated or deactivated</dd>
+ <dt>PrivateMode</dt> <dd>Triggered when private browsing mode is activated or deactivated</dd>
<dt>Sanitize</dt> <dd>Triggered when private data are sanitized</dd>
<dt>ShellCmdPost</dt> <dd>Triggered after executing a shell command with <ex>:!</ex><a>cmd</a></dd>
<dt>Enter</dt> <dd>Triggered after &dactyl.host; starts</dd>
<dt><tags></dt> <dd>The tags applied to <em><url></em>. Only for <em>Bookmark*</em>.</dd>
<dt><title></dt> <dd>The page, bookmark or download title.</dd>
<dt><url></dt> <dd>The URL against which the event was selected.</dd>
+ <dt><win></dt> <dd>The window for which the event occurred. Only for <em>DOMLoad</em>, <em>PageLoad</em> and <em>PageLoadPre</em>.</dd>
</dl>
</overlay>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>
-
-<!DOCTYPE overlay SYSTEM "dactyl://content/dtd">
-
-<overlay
- xmlns="&xmlns.dactyl;"
- xmlns:html="&xmlns.html;">
-
-<dl tag="dialog-list" replace="dialog-list">
- <dt>about</dt> <dd>About Mozilla &dactyl.host;</dd>
- <dt>addbookmark</dt> <dd>Add bookmark for the current page</dd>
- <dt>addons</dt> <dd>Manage Add-ons</dd>
- <dt>bookmarks</dt> <dd>List your bookmarks</dd>
- <dt>checkupdates</dt> <dd>Check for updates</dd>
- <dt>cookies</dt> <dd>List your cookies</dd>
- <dt>console</dt> <dd>JavaScript console</dd>
- <dt>customizetoolbar</dt><dd>Customize the Toolbar</dd>
- <dt>dominspector</dt> <dd>DOM Inspector</dd>
- <dt>downloads</dt> <dd>Manage Downloads</dd>
- <dt>history</dt> <dd>List your history</dd>
- <dt>import</dt> <dd>Import Preferences, Bookmarks, History, etc. from other browsers</dd>
- <dt>openfile</dt> <dd>Open the file selector dialog</dd>
- <dt>pageinfo</dt> <dd>Show information about the current page</dd>
- <dt>pagesource</dt> <dd>View page source</dd>
- <dt>passwords</dt> <dd>Passwords dialog</dd>
- <dt>places</dt> <dd>Places Organizer: Manage your bookmarks and history</dd>
- <dt>preferences</dt> <dd>Show &dactyl.host; preferences dialog</dd>
- <dt>printpreview</dt> <dd>Preview the page before printing</dd>
- <dt>printsetup</dt> <dd>Setup the page size and orientation before printing</dd>
- <dt>print</dt> <dd>Show print dialog</dd>
- <dt>saveframe</dt> <dd>Save frame to disk</dd>
- <dt>savepage</dt> <dd>Save page to disk</dd>
- <dt>searchengines</dt> <dd>Manage installed search engines</dd>
- <dt>selectionsource</dt> <dd>View selection source</dd>
- <dt>venkman</dt> <dd>The JavaScript debugger</dd>
-</dl>
-
-</overlay>
-
-<!-- vim:se sts=4 sw=4 et: -->
etc.)
</li>
<li>
- <link topic="cmdline">Command-line mode</link>:
+ <link topic="cmdline">Command Line mode</link>:
Command-line editing.
</li>
<li>
<li>Quick and powerful <link topic="hints">keyboard navigation</link> for links, input fields, etc.</li>
<li>Vim-like <link topic="status-line">status line</link></li>
<li>Keyboard <t>macros</t>, along with custom key mappings and commands</li>
- <li>Minimal GUI, along with commands to <link href=":toolbarhide">hide</link> and <link href=":toolbartoggle">toggle</link> toolbars and menus</li>
+ <li>Minimal GUI, along with commands to <link topic=":toolbarhide">hide</link> and <link topic=":toolbartoggle">toggle</link> toolbars and menus</li>
<li>Ability to <ex>:source</ex> JavaScript, CSS, and &dactyl.appName; command files</li>
<li>Ability to <link topic="marks">mark</link> the current page position and return to it</li>
<li>Count support for many commands (<tt>3</tt><k name="C-o"/> will go back <tt>3</tt> pages)</li>
--- /dev/null
+
+# vim:se ft=jproperties tw=0:
&dactyl.appName;'s power, like Vim's, comes from its modal interface. Keys have
different meanings depending on which mode the browser is in. &dactyl.appName; has
several modes, but the 2 most important are <em>Normal</em> mode and
- <em>Command-line</em> mode.
+ <em>Command Line</em> mode.
</p>
<p>
</p>
<p>
- The other core mode of &dactyl.appName;, Command-line mode, can be entered from
+ The other core mode of &dactyl.appName;, Command Line mode, can be entered from
Normal mode by typing a <k>:</k> (colon). You will frequently see &dactyl.appName;
commands start with a <k>:</k>, indicating that what follows is a command.
</p>
<p>
- To return to Normal mode from Command-line mode, type <k name="Esc"/>. Pressing
+ To return to Normal mode from Command Line mode, type <k name="Esc"/>. Pressing
<k name="Esc"/> will also return you to Normal mode from most other modes in
&dactyl.appName;.
</p>
</p>
<p>
- The most common hint mode is called <t>quick-hints</t>.
- To activate QuickHint mode, press either <k>f</k> or <k>F</k>. The lower-case
- <k>f</k> will open the resulting link in the current tab, while the upper-case
- <k>F</k> will open it in a new tab.
+ To activate Hints mode, press either <k>f</k> or <k>F</k>. The lower-case
+ <k>f</k> will open the resulting link in the current tab, while the
+ upper-case <k>F</k> will open it in a new tab.
</p>
<p>
To test it, try this link: <link topic="&dactyl.apphome;">&dactyl.appName; Homepage</link>.
- Activate QuickHint mode with <k>f</k> or <k>F</k> to highlight all currently
+ Activate Hints mode with <k>f</k> or <k>F</k> to highlight all currently
visible links. Then start typing the text of the link. The link should be
- uniquely identified soon, and &dactyl.appName; will open it. Once you're done,
- remember to use <k name="C-o"/> (<em>History Back</em>) or <k>d</k> (<em>Delete Buffer</em>)
- to return here, depending on which key you used to activate QuickHint mode.
+ uniquely identified soon, and &dactyl.appName; will open it. Once you're
+ done, remember to use <k name="C-o"/> (<em>History Back</em>) or <k>d</k>
+ (<em>Delete Buffer</em>) to return here, depending on which key you used to
+ activate Hints mode.
</p>
<h2 tag="common-issues">Common issues</h2>
--- /dev/null
+../common/bootstrap.js
\ No newline at end of file
// given in the LICENSE.txt file included with this file.
"use strict";
-const Addressbook = Module("addressbook", {
+var Addressbook = Module("addressbook", {
init: function () {
},
if (addresses.length < 1) {
if (!filter)
- dactyl.echoerr("Exxx: No contacts", commandline.FORCE_SINGLELINE);
+ dactyl.echoerr(_("addressbook.noContacts"), commandline.FORCE_SINGLELINE);
else
- dactyl.echoerr("Exxx: No contacts matching string '" + filter + "'", commandline.FORCE_SINGLELINE);
+ dactyl.echoerr(_("addressbook.noMatchingContacts"), filter, commandline.FORCE_SINGLELINE);
return false;
}
displayName = this.generateDisplayName(firstName, lastName);
if (addressbook.add(mailAddr, firstName, lastName, displayName))
- dactyl.echomsg("Added address: " + displayName + " <" + mailAddr + ">", 1, commandline.FORCE_SINGLELINE);
+ dactyl.echomsg(_("addressbook.added", displayName, mailAddr), 1, commandline.FORCE_SINGLELINE);
else
- dactyl.echoerr("Exxx: Could not add contact `" + mailAddr + "'", commandline.FORCE_SINGLELINE);
+ dactyl.echoerr(_("addressbook.cantAdd", mailAddr), commandline.FORCE_SINGLELINE);
},
{
// given in the LICENSE.txt file included with this file.
"use strict";
-const Config = Module("config", ConfigBase, {
+var Config = Module("config", ConfigBase, {
name: "teledactyl",
appName: "Teledactyl",
idName: "TELEDACTYL",
init: function init() {
init.superapply(this, arguments);
- modules.__defineGetter__("content", function () window.content);
+ if (!("content" in modules))
+ modules.__defineGetter__("content", function () window.content);
util.overlayWindow(window, { append: <><hbox id="statusTextBox" flex=""/></> });
},
- get browser() window.getBrowser(),
+ get browser()
+ let (tabmail = document.getElementById('tabmail'))
+ tabmail && tabmail.tabInfo.length ? tabmail.getBrowserForSelectedTab()
+ : document.getElementById("messagepane"),
get commandContainer() document.documentElement.id,
get mStrip() this.tabStrip,
get browsers() [browser for (browser in Iterator(this.mTabs))],
+ removeTab: function removeTab(tab) this.closeTab(tab),
+
loadOneTab: function loadOneTab(uri) {
return this.openTab("contentTab", { contentPage: uri });
},
},
/*** optional options, there are checked for existence and a fallback provided ***/
- features: Class.memoize(function () set(
+ features: Class.memoize(function () Set(
this.isComposeWindow ? ["addressbook"]
: ["hints", "mail", "marks", "addressbook", "tabs"])),
// given in the LICENSE.txt file included with this file.
"use strict";
-const Mail = Module("mail", {
+var Mail = Module("mail", {
init: function init() {
// used for asynchronously selecting messages after wrapping folders
this._selectMessageKeys = [];
_moveOrCopy: function (copy, destinationFolder, operateOnThread) {
let folders = mail.getFolders(destinationFolder);
if (folders.length == 0)
- return void dactyl.echoerr("Exxx: No matching folder for " + destinationFolder);
+ return void dactyl.echoerr(_("addressbook.noMatchingFolder", destinationFolder));
else if (folders.length > 1)
- return dactyl.echoerr("Exxx: More than one match for " + destinationFolder);
+ return dactyl.echoerr(_("addressbook.multipleFolderMatches", destinationFolder));
let count = gDBView.selection.count;
if (!count)
get currentFolder() gFolderTreeView.getSelectedFolders()[0],
- /** @property {nsISmtpServer[]} The list of configured SMTP servers. */
+ /** @property {[nsISmtpServer]} The list of configured SMTP servers. */
get smtpServers() {
let servers = services.smtp.smtpServers;
let res = [];
let url = args.attachments.pop();
let file = io.getFile(url);
if (!file.exists())
- return void dactyl.echoerr("Exxx: Could not attach file `" + url + "'", commandline.FORCE_SINGLELINE);
+ return void dactyl.echoerr(_("mail.cantAttachFile", url), commandline.FORCE_SINGLELINE);
attachment = Cc["@mozilla.org/messengercompose/attachment;1"].createInstance(Ci.nsIMsgAttachment);
attachment.url = "file://" + file.path;
let folder = mail.getFolders(arg, true, true)[count];
if (!folder)
- dactyl.echoerr("Exxx: Folder \"" + arg + "\" does not exist");
+ dactyl.echoerr(_("command.goto.folderNotExist", arg));
else if (dactyl.forceNewTab)
MsgOpenNewTabForFolder(folder.URI);
else
// TODO: is there a better way to check for validity?
if (addresses.some(function (recipient) !(/\S@\S+\.\S/.test(recipient))))
- return void dactyl.echoerr("Exxx: Invalid e-mail address");
+ return void dactyl.echoerr(_("command.mail.invalidEmailAddress"));
mail.composeNewMail(mailargs);
},
"Copy selected messages",
function (args) { mail._moveOrCopy(true, args.literalArg); },
{
- argCount: 1,
+ argCount: "1",
completer: function (context) completion.mailFolder(context),
literal: 0
});
"Move selected messages",
function (args) { mail._moveOrCopy(false, args.literalArg); },
{
- argCount: 1,
+ argCount: "1",
completer: function (context) completion.mailFolder(context),
literal: 0
});
em:id="teledactyl@dactyl.googlecode.com"
em:type="2"
em:name="Teledactyl"
- em:version="0.5b1pre"
+ em:version="0.6"
em:description="Thunderbird for Mutt and Vim addicts"
em:creator="Kris Maglione"
em:homepageURL="http://dactyl.sf.net/Teledactyl"
em:iconURL="chrome://teledactyl/skin/icon.png"
- em:optionsURL="chrome://dactyl/content/preferences.xul">
+ em:bootstrap="true">
<em:targetApplication>
<Description>
<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
- <em:minVersion>3.0b3</em:minVersion>
- <em:maxVersion>3.2</em:maxVersion>
+ <em:minVersion>5.0</em:minVersion>
+ <em:maxVersion>8.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>
-
-<!DOCTYPE overlay SYSTEM "dactyl://content/dtd">
-
-<overlay
- xmlns="&xmlns.dactyl;"
- xmlns:html="&xmlns.html;">
-
-<dl tag="dialog-list" replace="dialog-list">
- <dt>about</dt> <dd>About &dactyl.host;</dd>
- <dt>addons</dt> <dd>Manage Add-ons</dd>
- <dt>addressbook</dt> <dd>Address book</dd>
- <dt>checkupdates</dt> <dd>Check for updates</dd>
- <dt>console</dt> <dd>JavaScript console</dd>
- <dt>dominspector</dt> <dd>DOM Inspector</dd>
- <dt>downloads</dt> <dd>Manage Downloads</dd>
- <dt>openfile</dt> <dd>Open the file selector dialog</dd>
- <dt>pageinfo</dt> <dd>Show information about the current page</dd>
- <dt>pagesource</dt> <dd>View page source</dd>
- <dt>preferences</dt> <dd>Show &dactyl.host; preferences dialog</dd>
- <dt>printsetup</dt> <dd>Setup the page size and orientation before printing</dd>
- <dt>print</dt> <dd>Show print dialog</dd>
- <dt>saveframe</dt> <dd>Save frame to disk</dd>
- <dt>savepage</dt> <dd>Save page to disk</dd>
-</dl>
-
-</overlay>
-
-<!-- vim:se sts=4 sw=4 et: -->
etc.)
</li>
<li>
- <link topic="cmdline">Command-line mode</link>:
+ <link topic="cmdline">Command Line mode</link>:
Command-line editing.
</li>
<li>
--- /dev/null
+addressbook.noContacts = No contacts
+addressbook.noMatchingContacts-1 = No contacts matching '%S'
+addressbook.added-2 = Added address: %S <%S>
+addressbook.cantAdd-1 = Could not add contact '%S'
+
+command.goto.folderNotExist-1 = Folder '%S' does not exist
+command.mail.invalidEmailAddress = Invalid e-mail address
+
+mail.noMatchingFolder-1 = No matching folder for %S
+mail.multipleFolderMatches-1 = More than one match for %S
+mail.cantAttachFile = Could not attach file '%S'
+
+# vim:se ft=jproperties tw=0:
+