mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-02-04 22:15:45 +01:00
Merge groups branch.
This commit is contained in:
@@ -32,7 +32,7 @@ CHROME = $(MANGLE)/
|
||||
JAR = $(CHROME)$(NAME).jar
|
||||
|
||||
XPI_BASES = $(JAR_BASES) $(TOP)/..
|
||||
XPI_FILES = bootstrap.js install.rdf TODO AUTHORS Donors NEWS LICENSE.txt
|
||||
XPI_FILES = bootstrap.js TODO AUTHORS Donors NEWS LICENSE.txt
|
||||
XPI_DIRS = components $(MANGLE) defaults
|
||||
XPI_TEXTS = js jsm $(JAR_TEXTS)
|
||||
XPI_BINS = $(JAR_BINS)
|
||||
@@ -49,6 +49,9 @@ 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:
|
||||
|
||||
@@ -174,11 +177,19 @@ test: xpi
|
||||
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"
|
||||
|
||||
"$(TOP)/chrome.manifest" >"$(XPI_PATH)/chrome.manifest"
|
||||
|
||||
version="$(VERSION)"; \
|
||||
hg root >/dev/null 2>&1 && \
|
||||
case "$$version" in \
|
||||
*pre) version="$$version-hg$$(hg log -r . --template '{rev}')-$$(hg branch)";; \
|
||||
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)"
|
||||
|
||||
@@ -241,7 +241,7 @@ var Abbreviations = Module("abbreviations", {
|
||||
abbreviations.list(modes, lhs || "");
|
||||
else {
|
||||
if (args["-javascript"])
|
||||
rhs = Command.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
|
||||
rhs = contexts.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
|
||||
abbreviations.add(modes, lhs, rhs);
|
||||
}
|
||||
}, {
|
||||
|
||||
@@ -8,13 +8,18 @@
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
var AutoCommand = Struct("event", "patterns", "command");
|
||||
var AutoCommand = Struct("event", "filter", "command");
|
||||
update(AutoCommand.prototype, {
|
||||
eventName: Class.memoize(function () this.event.toLowerCase()),
|
||||
|
||||
/**
|
||||
* @instance autocommands
|
||||
*/
|
||||
var AutoCommands = Module("autocommands", {
|
||||
init: function () {
|
||||
match: function (event, pattern) {
|
||||
return (!event || this.eventName == event.toLowerCase()) && (!pattern || String(this.filter) === pattern);
|
||||
}
|
||||
});
|
||||
|
||||
var AutoCmdHive = Class("AutoCmdHive", Contexts.Hive, {
|
||||
init: function init(group) {
|
||||
init.supercall(this, group);
|
||||
this._store = [];
|
||||
},
|
||||
|
||||
@@ -26,24 +31,26 @@ var AutoCommands = Module("autocommands", {
|
||||
*
|
||||
* @param {Array} events The array of event names for which this
|
||||
* autocommand should be executed.
|
||||
* @param {string} regexp The URL pattern to match against the buffer URL.
|
||||
* @param {string} pattern The URL pattern to match against the buffer URL.
|
||||
* @param {string} cmd The Ex command to run.
|
||||
*/
|
||||
add: function (events, regexp, cmd) {
|
||||
events.forEach(function (event) {
|
||||
this._store.push(AutoCommand(event, Option.parse.regexplist(regexp.source || regexp), cmd));
|
||||
}, this);
|
||||
add: function (events, pattern, cmd) {
|
||||
if (!callable(pattern))
|
||||
pattern = Group.compileFilter(pattern);
|
||||
|
||||
for (let event in values(events))
|
||||
this._store.push(AutoCommand(event, pattern, cmd));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all autocommands with a matching *event* and *regexp*.
|
||||
*
|
||||
* @param {string} event The event name filter.
|
||||
* @param {string} regexp The URL pattern filter.
|
||||
* @param {string} pattern The URL pattern filter.
|
||||
* @returns {AutoCommand[]}
|
||||
*/
|
||||
get: function (event, regexp) {
|
||||
return this._store.filter(function (autoCmd) AutoCommands.matchAutoCmd(autoCmd, event, regexp));
|
||||
get: function (event, pattern) {
|
||||
return this._store.filter(function (autoCmd) autoCmd.match(event, regexp));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -53,8 +60,27 @@ var AutoCommands = Module("autocommands", {
|
||||
* @param {string} regexp The URL pattern filter.
|
||||
*/
|
||||
remove: function (event, regexp) {
|
||||
this._store = this._store.filter(function (autoCmd) !AutoCommands.matchAutoCmd(autoCmd, event, regexp));
|
||||
this._store = this._store.filter(function (autoCmd) !autoCmd.match(event, regexp));
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @instance autocommands
|
||||
*/
|
||||
var AutoCommands = Module("autocommands", {
|
||||
init: function () {
|
||||
update(this, {
|
||||
hives: contexts.Hives("autocmd", AutoCmdHive),
|
||||
user: contexts.hives.autocmd.user
|
||||
});
|
||||
},
|
||||
|
||||
get activeHives() contexts.initializedGroups("autocmd")
|
||||
.filter(function (h) h._store.length),
|
||||
|
||||
add: deprecated("autocommand.user.add", { get: function add() autocommands.user.closure.add }),
|
||||
get: deprecated("autocommand.user.get", { get: function get() autocommands.user.closure.get }),
|
||||
remove: deprecated("autocommand.user.remove", { get: function remove() autocommands.user.closure.remove }),
|
||||
|
||||
/**
|
||||
* Lists all autocommands with a matching *event* and *regexp*.
|
||||
@@ -63,32 +89,39 @@ var AutoCommands = Module("autocommands", {
|
||||
* @param {string} regexp The URL pattern filter.
|
||||
*/
|
||||
list: function (event, regexp) {
|
||||
let cmds = {};
|
||||
|
||||
// XXX
|
||||
this._store.forEach(function (autoCmd) {
|
||||
if (AutoCommands.matchAutoCmd(autoCmd, event, regexp)) {
|
||||
cmds[autoCmd.event] = cmds[autoCmd.event] || [];
|
||||
cmds[autoCmd.event].push(autoCmd);
|
||||
}
|
||||
});
|
||||
function cmds(hive) {
|
||||
let cmds = {};
|
||||
hive._store.forEach(function (autoCmd) {
|
||||
if (autoCmd.match(event, regexp)) {
|
||||
cmds[autoCmd.event] = cmds[autoCmd.event] || [];
|
||||
cmds[autoCmd.event].push(autoCmd);
|
||||
}
|
||||
});
|
||||
return cmds;
|
||||
}
|
||||
|
||||
commandline.commandOutput(
|
||||
<table>
|
||||
<tr highlight="Title">
|
||||
<td colspan="2">----- Auto Commands -----</td>
|
||||
<td colspan="3">----- Auto Commands -----</td>
|
||||
</tr>
|
||||
{
|
||||
template.map(cmds, function ([event, items])
|
||||
template.map(this.activeHives, function (hive)
|
||||
<tr highlight="Title">
|
||||
<td colspan="2">{event}</td>
|
||||
</tr>
|
||||
+
|
||||
template.map(items, function (item)
|
||||
<tr>
|
||||
<td> {item.patterns}</td>
|
||||
<td>{item.command}</td>
|
||||
</tr>))
|
||||
<td colspan="3">{hive.name}</td>
|
||||
</tr> +
|
||||
<tr style="height: .5ex;"/> +
|
||||
template.map(cmds(hive), function ([event, items])
|
||||
<tr style="height: .5ex;"/> +
|
||||
template.map(items, function (item, i)
|
||||
<tr>
|
||||
<td highlight="Title" style="padding-right: 1em;">{i == 0 ? event : ""}</td>
|
||||
<td>{item.filter.toXML ? item.filter.toXML() : item.filter}</td>
|
||||
<td>{item.command}</td>
|
||||
</tr>) +
|
||||
<tr style="height: .5ex;"/>) +
|
||||
<tr style="height: .5ex;"/>)
|
||||
}
|
||||
</table>);
|
||||
},
|
||||
@@ -104,29 +137,31 @@ var AutoCommands = Module("autocommands", {
|
||||
if (options.get("eventignore").has(event))
|
||||
return;
|
||||
|
||||
let autoCmds = this._store.filter(function (autoCmd) autoCmd.event == event);
|
||||
|
||||
dactyl.echomsg('Executing ' + event + ' Auto commands for "*"', 8);
|
||||
|
||||
let lastPattern = null;
|
||||
let url = args.url || "";
|
||||
let uri = args.url ? util.newURI(args.url) : buffer.uri;
|
||||
|
||||
for (let [, autoCmd] in Iterator(autoCmds)) {
|
||||
if (autoCmd.patterns.some(function (re) re.test(url) ^ !re.result)) {
|
||||
if (!lastPattern || String(lastPattern) != String(autoCmd.patterns))
|
||||
dactyl.echomsg("Executing " + event + " Auto commands for " + autoCmd.patterns, 8);
|
||||
event = event.toLowerCase();
|
||||
for (let hive in this.hives.iterValues()) {
|
||||
let args = update({},
|
||||
hive.argsExtra(arguments[1]),
|
||||
arguments[1]);
|
||||
|
||||
lastPattern = autoCmd.patterns;
|
||||
dactyl.echomsg("autocommand " + autoCmd.command, 9);
|
||||
for (let autoCmd in values(hive._store))
|
||||
if (autoCmd.eventName === event && autoCmd.filter(uri)) {
|
||||
if (!lastPattern || lastPattern !== String(autoCmd.filter))
|
||||
dactyl.echomsg("Executing " + event + " Auto commands for " + autoCmd.filter, 8);
|
||||
|
||||
dactyl.trapErrors(autoCmd.command, autoCmd, args);
|
||||
}
|
||||
lastPattern = String(autoCmd.filter);
|
||||
dactyl.echomsg("autocommand " + autoCmd.command, 9);
|
||||
|
||||
dactyl.trapErrors(autoCmd.command, autoCmd, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
matchAutoCmd: function (autoCmd, event, regexp) {
|
||||
return (!event || autoCmd.event == event) && (!regexp || String(autoCmd.patterns) == regexp);
|
||||
}
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["au[tocmd]"],
|
||||
@@ -135,29 +170,21 @@ var AutoCommands = Module("autocommands", {
|
||||
let [event, regexp, cmd] = args;
|
||||
let events = [];
|
||||
|
||||
try {
|
||||
if (args.length > 1)
|
||||
Option.parse.regexplist(regexp);
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.assert(false, "E475: Invalid argument: " + regexp);
|
||||
}
|
||||
|
||||
if (event) {
|
||||
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
|
||||
let validEvents = Object.keys(config.autocommands);
|
||||
let validEvents = Object.keys(config.autocommands).map(String.toLowerCase);
|
||||
validEvents.push("*");
|
||||
|
||||
events = Option.parse.stringlist(event);
|
||||
dactyl.assert(events.every(function (event) validEvents.indexOf(event) >= 0),
|
||||
"E216: No such group or event: " + event);
|
||||
dactyl.assert(events.every(function (event) validEvents.indexOf(event.toLowerCase()) >= 0),
|
||||
"E216: No such group or event: " + event);
|
||||
}
|
||||
|
||||
if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern
|
||||
if (args.bang)
|
||||
autocommands.remove(event, regexp);
|
||||
cmd = Command.bindMacro(args, "-ex", function (params) params);
|
||||
autocommands.add(events, regexp, cmd);
|
||||
args["-group"].remove(event, regexp);
|
||||
cmd = contexts.bindMacro(args, "-ex", function (params) params);
|
||||
args["-group"].add(events, regexp, cmd);
|
||||
}
|
||||
else {
|
||||
if (event == "*")
|
||||
@@ -166,7 +193,7 @@ var AutoCommands = Module("autocommands", {
|
||||
if (args.bang) {
|
||||
// TODO: "*" only appears to work in Vim when there is a {group} specified
|
||||
if (args[0] != "*" || args.length > 1)
|
||||
autocommands.remove(event, regexp); // remove all
|
||||
args["-group"].remove(event, regexp); // remove all
|
||||
}
|
||||
else
|
||||
autocommands.list(event, regexp); // list all
|
||||
@@ -183,6 +210,7 @@ var AutoCommands = Module("autocommands", {
|
||||
keepQuotes: true,
|
||||
literal: 2,
|
||||
options: [
|
||||
contexts.GroupFlag("autocmd"),
|
||||
{
|
||||
names: ["-javascript", "-js"],
|
||||
description: "Interpret the action as JavaScript code rather than an Ex command"
|
||||
@@ -245,7 +273,7 @@ var AutoCommands = Module("autocommands", {
|
||||
};
|
||||
},
|
||||
javascript: function () {
|
||||
JavaScript.setCompleter(autocommands.get, [function () Iterator(config.autocommands)]);
|
||||
JavaScript.setCompleter(autocommands.user.get, [function () Iterator(config.autocommands)]);
|
||||
},
|
||||
options: function () {
|
||||
options.add(["eventignore", "ei"],
|
||||
|
||||
@@ -11,63 +11,206 @@
|
||||
/**
|
||||
* @instance browser
|
||||
*/
|
||||
var Browser = Module("browser", {
|
||||
}, {
|
||||
climbUrlPath: function (count) {
|
||||
let url = buffer.documentURI.clone();
|
||||
dactyl.assert(url instanceof Ci.nsIURL);
|
||||
|
||||
while (count-- && url.path != "/")
|
||||
url.path = url.path.replace(/[^\/]+\/*$/, "");
|
||||
|
||||
dactyl.assert(!url.equals(buffer.documentURI));
|
||||
dactyl.open(url.spec);
|
||||
var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
init: function init() {
|
||||
this.cleanupProgressListener = util.overlayObject(window.XULBrowserWindow,
|
||||
this.progressListener);
|
||||
util.addObserver(this);
|
||||
},
|
||||
|
||||
incrementURL: function (count) {
|
||||
let matches = buffer.uri.spec.match(/(.*?)(\d+)(\D*)$/);
|
||||
dactyl.assert(matches);
|
||||
let oldNum = matches[2];
|
||||
cleanup: function () {
|
||||
this.cleanupProgressListener();
|
||||
this.observe.unregister();
|
||||
},
|
||||
|
||||
// disallow negative numbers as trailing numbers are often proceeded by hyphens
|
||||
let newNum = String(Math.max(parseInt(oldNum, 10) + count, 0));
|
||||
if (/^0/.test(oldNum))
|
||||
while (newNum.length < oldNum.length)
|
||||
newNum = "0" + newNum;
|
||||
observers: {
|
||||
"chrome-document-global-created": function (win, uri) { this.observe(win, "content-document-global-created", uri); },
|
||||
"content-document-global-created": function (win, uri) {
|
||||
let top = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
|
||||
|
||||
matches[2] = newNum;
|
||||
dactyl.open(matches.slice(1).join(""));
|
||||
if (top == window)
|
||||
this._triggerLoadAutocmd("PageLoadPre", win.document, win.location.href != "null" ? window.location.href : uri);
|
||||
}
|
||||
},
|
||||
|
||||
_triggerLoadAutocmd: function _triggerLoadAutocmd(name, doc, uri) {
|
||||
if (!(uri || doc.location))
|
||||
return;
|
||||
|
||||
uri = isObject(uri) ? uri : util.newURI(uri || doc.location.href);
|
||||
let args = {
|
||||
url: { toString: function () uri.spec, valueOf: function () uri },
|
||||
title: doc.title
|
||||
};
|
||||
|
||||
if (dactyl.has("tabs")) {
|
||||
args.tab = tabs.getContentIndex(doc) + 1;
|
||||
args.doc = {
|
||||
valueOf: function () doc,
|
||||
toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentDocument"
|
||||
};
|
||||
}
|
||||
|
||||
autocommands.trigger(name, args);
|
||||
},
|
||||
|
||||
events: {
|
||||
DOMContentLoaded: function onDOMContentLoaded(event) {
|
||||
let doc = event.originalTarget;
|
||||
if (doc instanceof HTMLDocument)
|
||||
this._triggerLoadAutocmd("DOMLoad", doc);
|
||||
},
|
||||
|
||||
// TODO: see what can be moved to onDOMContentLoaded()
|
||||
// event listener which is is called on each page load, even if the
|
||||
// page is loaded in a background tab
|
||||
load: function onLoad(event) {
|
||||
let doc = event.originalTarget;
|
||||
if (doc instanceof Document)
|
||||
dactyl.initDocument(doc);
|
||||
|
||||
if (doc instanceof HTMLDocument) {
|
||||
if (doc.defaultView.frameElement) {
|
||||
// document is part of a frameset
|
||||
|
||||
// hacky way to get rid of "Transferring data from ..." on sites with frames
|
||||
// when you click on a link inside a frameset, because asyncUpdateUI
|
||||
// is not triggered there (Gecko bug?)
|
||||
this.timeout(function () { statusline.updateUrl(); }, 10);
|
||||
}
|
||||
else {
|
||||
// code which should happen for all (also background) newly loaded tabs goes here:
|
||||
if (doc != config.browser.contentDocument)
|
||||
dactyl.echomsg({ domains: [util.getHost(doc.location)], message: "Background tab loaded: " + (doc.title || doc.location.href) }, 3);
|
||||
|
||||
this._triggerLoadAutocmd("PageLoad", doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @property {Object} The document loading progress listener.
|
||||
*/
|
||||
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)) {
|
||||
// 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) {
|
||||
statusline.progress = 0;
|
||||
while (document.commandDispatcher.focusedWindow == webProgress.DOMWindow
|
||||
&& modes.have(modes.INPUT))
|
||||
modes.pop();
|
||||
|
||||
}
|
||||
else if (flags & Ci.nsIWebProgressListener.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();
|
||||
statusline.updateUrl();
|
||||
}
|
||||
}
|
||||
}),
|
||||
// for notifying the user about secure web pages
|
||||
onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
|
||||
onSecurityChange.superapply(this, arguments);
|
||||
if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
|
||||
statusline.security = "broken";
|
||||
else if (state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
|
||||
statusline.security = "extended";
|
||||
else if (state & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
|
||||
statusline.security = "secure";
|
||||
else // if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
|
||||
statusline.security = "insecure";
|
||||
if (webProgress && webProgress.DOMWindow)
|
||||
webProgress.DOMWindow.document.dactylSecurity = statusline.security;
|
||||
}),
|
||||
onStatusChange: util.wrapCallback(function onStatusChange(webProgress, request, status, message) {
|
||||
onStatusChange.superapply(this, arguments);
|
||||
statusline.updateUrl(message);
|
||||
}),
|
||||
onProgressChange: util.wrapCallback(function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
|
||||
onProgressChange.superapply(this, arguments);
|
||||
if (webProgress && webProgress.DOMWindow)
|
||||
webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
|
||||
statusline.progress = curTotalProgress / maxTotalProgress;
|
||||
}),
|
||||
// happens when the users switches tabs
|
||||
onLocationChange: util.wrapCallback(function onLocationChange(webProgress, request, uri) {
|
||||
onLocationChange.superapply(this, arguments);
|
||||
|
||||
delete contexts.groups;
|
||||
|
||||
statusline.updateUrl();
|
||||
statusline.progress = "";
|
||||
|
||||
let win = webProgress.DOMWindow;
|
||||
if (win && uri) {
|
||||
statusline.progress = win.dactylProgress;
|
||||
|
||||
let oldURI = win.document.dactylURI;
|
||||
if (win.document.dactylLoadIdx === webProgress.loadedTransIndex
|
||||
|| !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, ""))
|
||||
for (let frame in values(buffer.allFrames(win)))
|
||||
frame.document.dactylFocusAllowed = false;
|
||||
win.document.dactylURI = uri.spec;
|
||||
win.document.dactylLoadIdx = webProgress.loadedTransIndex;
|
||||
}
|
||||
|
||||
// Workaround for bugs 591425 and 606877, dactyl bug #81
|
||||
let collapse = uri && uri.scheme === "dactyl" && webProgress.isLoadingDocument;
|
||||
if (collapse)
|
||||
dactyl.focus(document.documentElement);
|
||||
config.browser.mCurrentBrowser.collapsed = collapse;
|
||||
|
||||
util.timeout(function () {
|
||||
browser._triggerLoadAutocmd("LocationChange",
|
||||
(win || content).document,
|
||||
uri);
|
||||
});
|
||||
|
||||
// if this is not delayed we get the position of the old buffer
|
||||
util.timeout(function () {
|
||||
statusline.updateBufferPosition();
|
||||
statusline.updateZoomLevel();
|
||||
if (loaded.commandline)
|
||||
commandline.clear();
|
||||
}, 500);
|
||||
}),
|
||||
// called at the very end of a page load
|
||||
asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
|
||||
asyncUpdateUI.superapply(this, arguments);
|
||||
util.timeout(function () { statusline.updateUrl(); }, 100);
|
||||
}),
|
||||
setOverLink: util.wrapCallback(function setOverLink(link, b) {
|
||||
setOverLink.superapply(this, arguments);
|
||||
switch (options["showstatuslinks"]) {
|
||||
case "status":
|
||||
statusline.updateUrl(link ? "Link: " + link : null);
|
||||
break;
|
||||
case "command":
|
||||
if (link)
|
||||
dactyl.echo("Link: " + link, commandline.DISALLOW_MULTILINE);
|
||||
else
|
||||
commandline.clear();
|
||||
break;
|
||||
}
|
||||
}),
|
||||
}
|
||||
}, {
|
||||
options: function () {
|
||||
options.add(["encoding", "enc"],
|
||||
"The current buffer's character encoding",
|
||||
"string", "UTF-8",
|
||||
{
|
||||
scope: Option.SCOPE_LOCAL,
|
||||
getter: function () config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset,
|
||||
setter: function (val) {
|
||||
if (options["encoding"] == val)
|
||||
return val;
|
||||
|
||||
// Stolen from browser.jar/content/browser/browser.js, more or less.
|
||||
try {
|
||||
config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
|
||||
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
|
||||
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
}
|
||||
catch (e) { dactyl.reportError(e); }
|
||||
return null;
|
||||
},
|
||||
completer: function (context) completion.charset(context)
|
||||
});
|
||||
}, {
|
||||
events: function () {
|
||||
events.listen(config.browser, browser, "events", true);
|
||||
},
|
||||
|
||||
mappings: function () {
|
||||
mappings.add([modes.NORMAL],
|
||||
["y", "<yank-location>"], "Yank current location to the clipboard",
|
||||
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
|
||||
|
||||
// opening websites
|
||||
mappings.add([modes.NORMAL],
|
||||
["o"], "Open one or more URLs",
|
||||
@@ -93,16 +236,6 @@ var Browser = Module("browser", {
|
||||
"Open one or more URLs in a new window, based on current location",
|
||||
function () { CommandExMode().open("winopen " + buffer.uri.spec); });
|
||||
|
||||
mappings.add([modes.NORMAL],
|
||||
["<C-a>"], "Increment last number in URL",
|
||||
function (args) { Browser.incrementURL(Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL],
|
||||
["<C-x>"], "Decrement last number in URL",
|
||||
function (args) { Browser.incrementURL(-Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL], ["~"],
|
||||
"Open home directory",
|
||||
function () { dactyl.open("~"); });
|
||||
@@ -118,29 +251,12 @@ var Browser = Module("browser", {
|
||||
dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
|
||||
});
|
||||
|
||||
mappings.add([modes.NORMAL], ["gu"],
|
||||
"Go to parent directory",
|
||||
function (args) { Browser.climbUrlPath(Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL], ["gU"],
|
||||
"Go to the root of the website",
|
||||
function () { Browser.climbUrlPath(-1); });
|
||||
|
||||
mappings.add(modes.all, ["<C-l>"],
|
||||
"Redraw the screen",
|
||||
function () { ex.redraw(); });
|
||||
},
|
||||
|
||||
commands: function () {
|
||||
commands.add(["old-downl[oads]", "old-dl"],
|
||||
"Show progress of current downloads",
|
||||
function () {
|
||||
dactyl.open("chrome://mozapps/content/downloads/downloads.xul",
|
||||
{ from: "downloads"});
|
||||
},
|
||||
{ argCount: "0" });
|
||||
|
||||
commands.add(["o[pen]"],
|
||||
"Open one or more URLs in the current tab",
|
||||
function (args) { dactyl.open(args[0] || "about:blank"); },
|
||||
|
||||
@@ -145,75 +145,6 @@ var Buffer = Module("buffer", {
|
||||
let elem = event.originalTarget;
|
||||
buffer.viewSource([elem.getAttribute("href"), Number(elem.getAttribute("line"))]);
|
||||
};
|
||||
|
||||
this.cleanupProgressListener = util.overlayObject(window.XULBrowserWindow,
|
||||
this.progressListener);
|
||||
|
||||
if (dactyl.has("tabs"))
|
||||
for (let tab in values(tabs.allTabs))
|
||||
if (tab.linkedBrowser.contentDocument.readyState === "complete")
|
||||
dactyl.initDocument(tab.linkedBrowser.contentDocument);
|
||||
},
|
||||
|
||||
cleanup: function () {
|
||||
this.cleanupProgressListener();
|
||||
},
|
||||
|
||||
getDefaultNames: function getDefaultNames(node) {
|
||||
let url = node.href || node.src || node.documentURI;
|
||||
let currExt = url.replace(/^.*?(?:\.([a-z0-9]+))?$/i, "$1").toLowerCase();
|
||||
|
||||
if (isinstance(node, [Document, HTMLImageElement])) {
|
||||
let type = node.contentType || node.QueryInterface(Ci.nsIImageLoadingContent)
|
||||
.getRequest(0).mimeType;
|
||||
|
||||
if (type === "text/plain")
|
||||
var ext = "." + (currExt || "txt");
|
||||
else
|
||||
ext = "." + services.mime.getPrimaryExtension(type, currExt);
|
||||
}
|
||||
else if (currExt)
|
||||
ext = "." + currExt;
|
||||
else
|
||||
ext = "";
|
||||
let re = ext ? RegExp("(\\." + currExt + ")?$", "i") : /$/;
|
||||
|
||||
var names = [];
|
||||
if (node.title)
|
||||
names.push([node.title, "Page Name"]);
|
||||
|
||||
if (node.alt)
|
||||
names.push([node.alt, "Alternate Text"]);
|
||||
|
||||
if (!isinstance(node, Document) && node.textContent)
|
||||
names.push([node.textContent, "Link Text"]);
|
||||
|
||||
names.push([decodeURIComponent(url.replace(/.*?([^\/]*)\/*$/, "$1")), "File Name"]);
|
||||
|
||||
return names.filter(function ([leaf, title]) leaf)
|
||||
.map(function ([leaf, title]) [leaf.replace(util.OS.illegalCharacters, encodeURIComponent)
|
||||
.replace(re, ext), title]);
|
||||
},
|
||||
|
||||
_triggerLoadAutocmd: function _triggerLoadAutocmd(name, doc, uri) {
|
||||
if (!(uri || doc.location))
|
||||
return;
|
||||
|
||||
uri = uri || util.newURI(doc.location.href);
|
||||
let args = {
|
||||
url: { toString: function () uri.spec, valueOf: function () uri },
|
||||
title: doc.title
|
||||
};
|
||||
|
||||
if (dactyl.has("tabs")) {
|
||||
args.tab = tabs.getContentIndex(doc) + 1;
|
||||
args.doc = {
|
||||
valueOf: function () doc,
|
||||
toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentDocument"
|
||||
};
|
||||
}
|
||||
|
||||
autocommands.trigger(name, args);
|
||||
},
|
||||
|
||||
// called when the active document is scrolled
|
||||
@@ -222,158 +153,6 @@ var Buffer = Module("buffer", {
|
||||
commandline.clear();
|
||||
},
|
||||
|
||||
onDOMContentLoaded: function onDOMContentLoaded(event) {
|
||||
let doc = event.originalTarget;
|
||||
if (doc instanceof HTMLDocument)
|
||||
this._triggerLoadAutocmd("DOMLoad", doc);
|
||||
},
|
||||
|
||||
// TODO: see what can be moved to onDOMContentLoaded()
|
||||
// event listener which is is called on each page load, even if the
|
||||
// page is loaded in a background tab
|
||||
onPageLoad: function onPageLoad(event) {
|
||||
let doc = event.originalTarget;
|
||||
if (doc instanceof Document)
|
||||
dactyl.initDocument(doc);
|
||||
|
||||
if (doc instanceof HTMLDocument) {
|
||||
if (doc.defaultView.frameElement) {
|
||||
// document is part of a frameset
|
||||
|
||||
// hacky way to get rid of "Transferring data from ..." on sites with frames
|
||||
// when you click on a link inside a frameset, because asyncUpdateUI
|
||||
// is not triggered there (Gecko bug?)
|
||||
this.timeout(function () { statusline.updateUrl(); }, 10);
|
||||
}
|
||||
else {
|
||||
// code which should happen for all (also background) newly loaded tabs goes here:
|
||||
if (doc != config.browser.contentDocument)
|
||||
dactyl.echomsg({ domains: [util.getHost(doc.location)], message: "Background tab loaded: " + (doc.title || doc.location.href) }, 3);
|
||||
|
||||
this._triggerLoadAutocmd("PageLoad", doc);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @property {Object} The document loading progress listener.
|
||||
*/
|
||||
progressListener: {
|
||||
dactylLoadCount: 0,
|
||||
|
||||
// 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)) {
|
||||
// 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) {
|
||||
statusline.progress = 0;
|
||||
|
||||
buffer._triggerLoadAutocmd("PageLoadPre", webProgress.DOMWindow.document);
|
||||
|
||||
if (document.commandDispatcher.focusedWindow == webProgress.DOMWindow && this.dactylLoadCount++)
|
||||
util.timeout(function () { modes.reset(false); },
|
||||
modes.main == modes.HINTS ? 500 : 0);
|
||||
}
|
||||
else if (flags & Ci.nsIWebProgressListener.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();
|
||||
statusline.updateUrl();
|
||||
}
|
||||
}
|
||||
}),
|
||||
// for notifying the user about secure web pages
|
||||
onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
|
||||
onSecurityChange.superapply(this, arguments);
|
||||
if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
|
||||
statusline.security = "broken";
|
||||
else if (state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
|
||||
statusline.security = "extended";
|
||||
else if (state & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
|
||||
statusline.security = "secure";
|
||||
else // if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
|
||||
statusline.security = "insecure";
|
||||
if (webProgress && webProgress.DOMWindow)
|
||||
webProgress.DOMWindow.document.dactylSecurity = statusline.security;
|
||||
}),
|
||||
onStatusChange: util.wrapCallback(function onStatusChange(webProgress, request, status, message) {
|
||||
onStatusChange.superapply(this, arguments);
|
||||
statusline.updateUrl(message);
|
||||
}),
|
||||
onProgressChange: util.wrapCallback(function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
|
||||
onProgressChange.superapply(this, arguments);
|
||||
if (webProgress && webProgress.DOMWindow)
|
||||
webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
|
||||
statusline.progress = curTotalProgress / maxTotalProgress;
|
||||
}),
|
||||
// happens when the users switches tabs
|
||||
onLocationChange: util.wrapCallback(function onLocationChange(webProgress, request, uri) {
|
||||
onLocationChange.superapply(this, arguments);
|
||||
|
||||
delete mappings.hives;
|
||||
|
||||
statusline.updateUrl();
|
||||
statusline.progress = "";
|
||||
|
||||
let win = webProgress.DOMWindow;
|
||||
if (win && uri) {
|
||||
statusline.progress = win.dactylProgress;
|
||||
|
||||
let oldURI = win.document.dactylURI;
|
||||
if (win.document.dactylLoadIdx === webProgress.loadedTransIndex
|
||||
|| !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, ""))
|
||||
for (let frame in values(buffer.allFrames(win)))
|
||||
frame.document.dactylFocusAllowed = false;
|
||||
win.document.dactylURI = uri.spec;
|
||||
win.document.dactylLoadIdx = webProgress.loadedTransIndex;
|
||||
}
|
||||
|
||||
// Workaround for bugs 591425 and 606877, dactyl bug #81
|
||||
let collapse = uri && uri.scheme === "dactyl" && webProgress.isLoadingDocument;
|
||||
if (collapse)
|
||||
dactyl.focus(document.documentElement);
|
||||
config.browser.mCurrentBrowser.collapsed = collapse;
|
||||
|
||||
util.timeout(function () {
|
||||
buffer._triggerLoadAutocmd("LocationChange",
|
||||
(win || content).document,
|
||||
uri);
|
||||
});
|
||||
|
||||
// if this is not delayed we get the position of the old buffer
|
||||
util.timeout(function () {
|
||||
statusline.updateBufferPosition();
|
||||
statusline.updateZoomLevel();
|
||||
if (loaded.commandline)
|
||||
commandline.clear();
|
||||
}, 500);
|
||||
}),
|
||||
// called at the very end of a page load
|
||||
asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
|
||||
asyncUpdateUI.superapply(this, arguments);
|
||||
util.timeout(function () { statusline.updateUrl(); }, 100);
|
||||
}),
|
||||
setOverLink: util.wrapCallback(function setOverLink(link, b) {
|
||||
setOverLink.superapply(this, arguments);
|
||||
switch (options["showstatuslinks"]) {
|
||||
case "status":
|
||||
statusline.updateUrl(link ? "Link: " + link : null);
|
||||
break;
|
||||
case "command":
|
||||
if (link)
|
||||
dactyl.echo("Link: " + link, commandline.DISALLOW_MULTILINE);
|
||||
else
|
||||
commandline.clear();
|
||||
break;
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* @property {Array} The alternative style sheets for the current
|
||||
* buffer. Only returns style sheets for the 'screen' media type.
|
||||
@@ -386,6 +165,32 @@ var Buffer = Module("buffer", {
|
||||
);
|
||||
},
|
||||
|
||||
climbUrlPath: function (count) {
|
||||
let url = buffer.documentURI.clone();
|
||||
dactyl.assert(url instanceof Ci.nsIURL);
|
||||
|
||||
while (count-- && url.path != "/")
|
||||
url.path = url.path.replace(/[^\/]+\/*$/, "");
|
||||
|
||||
dactyl.assert(!url.equals(buffer.documentURI));
|
||||
dactyl.open(url.spec);
|
||||
},
|
||||
|
||||
incrementURL: function (count) {
|
||||
let matches = buffer.uri.spec.match(/(.*?)(\d+)(\D*)$/);
|
||||
dactyl.assert(matches);
|
||||
let oldNum = matches[2];
|
||||
|
||||
// disallow negative numbers as trailing numbers are often proceeded by hyphens
|
||||
let newNum = String(Math.max(parseInt(oldNum, 10) + count, 0));
|
||||
if (/^0/.test(oldNum))
|
||||
while (newNum.length < oldNum.length)
|
||||
newNum = "0" + newNum;
|
||||
|
||||
matches[2] = newNum;
|
||||
dactyl.open(matches.slice(1).join(""));
|
||||
},
|
||||
|
||||
/**
|
||||
* @property {Object} A map of page info sections to their
|
||||
* content generating functions.
|
||||
@@ -534,11 +339,8 @@ var Buffer = Module("buffer", {
|
||||
let range = selection.getRangeAt(0).cloneRange();
|
||||
if (range.collapsed) {
|
||||
let re = options.get("iskeyword").regexp;
|
||||
util.dump(String.quote(range));
|
||||
Editor.extendRange(range, true, re, true);
|
||||
util.dump(String.quote(range));
|
||||
Editor.extendRange(range, false, re, true);
|
||||
util.dump(String.quote(range) + "\n\n\n");
|
||||
}
|
||||
return util.domToString(range);
|
||||
},
|
||||
@@ -772,7 +574,7 @@ var Buffer = Module("buffer", {
|
||||
onSubmit: function (path) {
|
||||
let file = io.File(path);
|
||||
if (file.exists() && file.isDirectory())
|
||||
file.append(buffer.getDefaultNames(elem)[0][0]);
|
||||
file.append(Buffer.getDefaultNames(elem)[0][0]);
|
||||
|
||||
try {
|
||||
if (!file.exists())
|
||||
@@ -1242,6 +1044,42 @@ var Buffer = Module("buffer", {
|
||||
setZoom: deprecated("buffer.setZoom", function setZoom() buffer.setZoom.apply(buffer, arguments)),
|
||||
bumpZoomLevel: deprecated("buffer.bumpZoomLevel", function bumpZoomLevel() buffer.bumpZoomLevel.apply(buffer, arguments)),
|
||||
|
||||
getDefaultNames: function getDefaultNames(node) {
|
||||
let url = node.href || node.src || node.documentURI;
|
||||
let currExt = url.replace(/^.*?(?:\.([a-z0-9]+))?$/i, "$1").toLowerCase();
|
||||
|
||||
if (isinstance(node, [Document, HTMLImageElement])) {
|
||||
let type = node.contentType || node.QueryInterface(Ci.nsIImageLoadingContent)
|
||||
.getRequest(0).mimeType;
|
||||
|
||||
if (type === "text/plain")
|
||||
var ext = "." + (currExt || "txt");
|
||||
else
|
||||
ext = "." + services.mime.getPrimaryExtension(type, currExt);
|
||||
}
|
||||
else if (currExt)
|
||||
ext = "." + currExt;
|
||||
else
|
||||
ext = "";
|
||||
let re = ext ? RegExp("(\\." + currExt + ")?$", "i") : /$/;
|
||||
|
||||
var names = [];
|
||||
if (node.title)
|
||||
names.push([node.title, "Page Name"]);
|
||||
|
||||
if (node.alt)
|
||||
names.push([node.alt, "Alternate Text"]);
|
||||
|
||||
if (!isinstance(node, Document) && node.textContent)
|
||||
names.push([node.textContent, "Link Text"]);
|
||||
|
||||
names.push([decodeURIComponent(url.replace(/.*?([^\/]*)\/*$/, "$1")), "File Name"]);
|
||||
|
||||
return names.filter(function ([leaf, title]) leaf)
|
||||
.map(function ([leaf, title]) [leaf.replace(util.OS.illegalCharacters, encodeURIComponent)
|
||||
.replace(re, ext), title]);
|
||||
},
|
||||
|
||||
findScrollableWindow: deprecated("buffer.findScrollableWindow", function findScrollableWindow() buffer.findScrollableWindow.apply(buffer, arguments)),
|
||||
findScrollable: deprecated("buffer.findScrollable", function findScrollable() buffer.findScrollable.apply(buffer, arguments)),
|
||||
|
||||
@@ -1495,7 +1333,7 @@ var Buffer = Module("buffer", {
|
||||
let file = io.File(filename.replace(RegExp(File.PATH_SEP + "*$"), ""));
|
||||
|
||||
if (filename.substr(-1) === File.PATH_SEP || file.exists() && file.isDirectory())
|
||||
file.append(buffer.getDefaultNames(doc)[0][0]);
|
||||
file.append(Buffer.getDefaultNames(doc)[0][0]);
|
||||
|
||||
dactyl.assert(args.bang || !file.exists(), "E13: File exists (add ! to override)");
|
||||
|
||||
@@ -1647,19 +1485,38 @@ var Buffer = Module("buffer", {
|
||||
completion.savePage = function savePage(context, node) {
|
||||
context.fork("generated", context.filter.replace(/[^/]*$/, "").length,
|
||||
this, function (context) {
|
||||
context.completions = buffer.getDefaultNames(node);
|
||||
context.completions = Buffer.getDefaultNames(node);
|
||||
});
|
||||
};
|
||||
},
|
||||
events: function () {
|
||||
events.addSessionListener(config.browser, "DOMContentLoaded", buffer.closure.onDOMContentLoaded, true);
|
||||
events.addSessionListener(config.browser, "load", buffer.closure.onPageLoad, true);
|
||||
events.addSessionListener(config.browser, "scroll", buffer.closure._updateBufferPosition, false);
|
||||
events.listen(config.browser, "scroll", buffer.closure._updateBufferPosition, false);
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.browserModes;
|
||||
mappings.add([modes.NORMAL],
|
||||
["y", "<yank-location>"], "Yank current location to the clipboard",
|
||||
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
|
||||
|
||||
mappings.add(myModes, [".", "<repeat-key>"],
|
||||
mappings.add([modes.NORMAL],
|
||||
["<C-a>"], "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",
|
||||
function (args) { buffer.incrementURL(-Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL], ["gu"],
|
||||
"Go to parent directory",
|
||||
function (args) { buffer.climbUrlPath(Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL], ["gU"],
|
||||
"Go to the root of the website",
|
||||
function () { buffer.climbUrlPath(-1); });
|
||||
|
||||
mappings.add(modes.COMMAND, [".", "<repeat-key>"],
|
||||
"Repeat the last key event",
|
||||
function (args) {
|
||||
if (mappings.repeat) {
|
||||
@@ -1669,54 +1526,54 @@ var Buffer = Module("buffer", {
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["i", "<Insert>"],
|
||||
mappings.add(modes.COMMAND, ["i", "<Insert>"],
|
||||
"Start caret mode",
|
||||
function () { modes.push(modes.CARET); });
|
||||
|
||||
mappings.add(myModes, ["<C-c>"],
|
||||
mappings.add(modes.COMMAND, ["<C-c>"],
|
||||
"Stop loading the current web page",
|
||||
function () { ex.stop(); });
|
||||
|
||||
// scrolling
|
||||
mappings.add(myModes, ["j", "<Down>", "<C-e>", "<scroll-down-line>"],
|
||||
mappings.add(modes.COMMAND, ["j", "<Down>", "<C-e>", "<scroll-down-line>"],
|
||||
"Scroll document down",
|
||||
function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["k", "<Up>", "<C-y>", "<scroll-up-line>"],
|
||||
mappings.add(modes.COMMAND, ["k", "<Up>", "<C-y>", "<scroll-up-line>"],
|
||||
"Scroll document up",
|
||||
function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, dactyl.has("mail") ? ["h", "<scroll-left-column>"] : ["h", "<Left>", "<scroll-left-column>"],
|
||||
mappings.add(modes.COMMAND, dactyl.has("mail") ? ["h", "<scroll-left-column>"] : ["h", "<Left>", "<scroll-left-column>"],
|
||||
"Scroll document to the left",
|
||||
function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"],
|
||||
mappings.add(modes.COMMAND, dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"],
|
||||
"Scroll document to the right",
|
||||
function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["0", "^", "<scroll-begin>"],
|
||||
mappings.add(modes.COMMAND, ["0", "^", "<scroll-begin>"],
|
||||
"Scroll to the absolute left of the document",
|
||||
function () { buffer.scrollToPercent(0, null); });
|
||||
|
||||
mappings.add(myModes, ["$", "<scroll-end>"],
|
||||
mappings.add(modes.COMMAND, ["$", "<scroll-end>"],
|
||||
"Scroll to the absolute right of the document",
|
||||
function () { buffer.scrollToPercent(100, null); });
|
||||
|
||||
mappings.add(myModes, ["gg", "<Home>"],
|
||||
mappings.add(modes.COMMAND, ["gg", "<Home>"],
|
||||
"Go to the top of the document",
|
||||
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["G", "<End>"],
|
||||
mappings.add(modes.COMMAND, ["G", "<End>"],
|
||||
"Go to the end of the document",
|
||||
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["%", "<scroll-percent>"],
|
||||
mappings.add(modes.COMMAND, ["%", "<scroll-percent>"],
|
||||
"Scroll to {count} percent of the document",
|
||||
function (args) {
|
||||
dactyl.assert(args.count > 0 && args.count <= 100);
|
||||
@@ -1724,59 +1581,59 @@ var Buffer = Module("buffer", {
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-d>", "<scroll-down>"],
|
||||
mappings.add(modes.COMMAND, ["<C-d>", "<scroll-down>"],
|
||||
"Scroll window downwards in the buffer",
|
||||
function (args) { buffer._scrollByScrollSize(args.count, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-u>", "<scroll-up>"],
|
||||
mappings.add(modes.COMMAND, ["<C-u>", "<scroll-up>"],
|
||||
"Scroll window upwards in the buffer",
|
||||
function (args) { buffer._scrollByScrollSize(args.count, false); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-page-up>"],
|
||||
mappings.add(modes.COMMAND, ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-page-up>"],
|
||||
"Scroll up a full page",
|
||||
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-f>", "<PageDown>", "<Space>", "<scroll-page-down>"],
|
||||
mappings.add(modes.COMMAND, ["<C-f>", "<PageDown>", "<Space>", "<scroll-page-down>"],
|
||||
"Scroll down a full page",
|
||||
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["]f", "<previous-frame>"],
|
||||
mappings.add(modes.COMMAND, ["]f", "<previous-frame>"],
|
||||
"Focus next frame",
|
||||
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["[f", "<next-frame>"],
|
||||
mappings.add(modes.COMMAND, ["[f", "<next-frame>"],
|
||||
"Focus previous frame",
|
||||
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["]]", "<next-page>"],
|
||||
mappings.add(modes.COMMAND, ["]]", "<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(myModes, ["[[", "<previous-page>"],
|
||||
mappings.add(modes.COMMAND, ["[[", "<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(myModes, ["gf", "<view-source>"],
|
||||
mappings.add(modes.COMMAND, ["gf", "<view-source>"],
|
||||
"Toggle between rendered and source view",
|
||||
function () { buffer.viewSource(null, false); });
|
||||
|
||||
mappings.add(myModes, ["gF", "<view-source-externally>"],
|
||||
mappings.add(modes.COMMAND, ["gF", "<view-source-externally>"],
|
||||
"View source with an external editor",
|
||||
function () { buffer.viewSource(null, true); });
|
||||
|
||||
mappings.add(myModes, ["gi", "<focus-input>"],
|
||||
mappings.add(modes.COMMAND, ["gi", "<focus-input>"],
|
||||
"Focus last used input field",
|
||||
function (args) {
|
||||
let elem = buffer.lastInputField;
|
||||
@@ -1808,7 +1665,7 @@ var Buffer = Module("buffer", {
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["gP"],
|
||||
mappings.add(modes.COMMAND, ["gP"],
|
||||
"Open (put) a URL based on the current clipboard contents in a new buffer",
|
||||
function () {
|
||||
let url = dactyl.clipboardRead();
|
||||
@@ -1816,7 +1673,7 @@ var Buffer = Module("buffer", {
|
||||
dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["p", "<MiddleMouse>", "<open-clipboard-url>"],
|
||||
mappings.add(modes.COMMAND, ["p", "<MiddleMouse>", "<open-clipboard-url>"],
|
||||
"Open (put) a URL based on the current clipboard contents in the current buffer",
|
||||
function () {
|
||||
let url = dactyl.clipboardRead();
|
||||
@@ -1824,7 +1681,7 @@ var Buffer = Module("buffer", {
|
||||
dactyl.open(url);
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["P", "<tab-open-clipboard-url>"],
|
||||
mappings.add(modes.COMMAND, ["P", "<tab-open-clipboard-url>"],
|
||||
"Open (put) a URL based on the current clipboard contents in a new buffer",
|
||||
function () {
|
||||
let url = dactyl.clipboardRead();
|
||||
@@ -1833,16 +1690,16 @@ var Buffer = Module("buffer", {
|
||||
});
|
||||
|
||||
// reloading
|
||||
mappings.add(myModes, ["r", "<reload>"],
|
||||
mappings.add(modes.COMMAND, ["r", "<reload>"],
|
||||
"Reload the current web page",
|
||||
function () { tabs.reload(tabs.getTab(), false); });
|
||||
|
||||
mappings.add(myModes, ["R", "<full-reload>"],
|
||||
mappings.add(modes.COMMAND, ["R", "<full-reload>"],
|
||||
"Reload while skipping the cache",
|
||||
function () { tabs.reload(tabs.getTab(), true); });
|
||||
|
||||
// yanking
|
||||
mappings.add(myModes, ["Y", "<yank-word>"],
|
||||
mappings.add(modes.COMMAND, ["Y", "<yank-word>"],
|
||||
"Copy selected text or current word",
|
||||
function () {
|
||||
let sel = buffer.currentWord;
|
||||
@@ -1851,66 +1708,88 @@ var Buffer = Module("buffer", {
|
||||
});
|
||||
|
||||
// zooming
|
||||
mappings.add(myModes, ["zi", "+", "<text-zoom-in>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["zm", "<text-zoom-more>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["zo", "-", "<text-zoom-out>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["zr", "<text-zoom-reduce>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["zz", "<text-zoom>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["ZI", "zI", "<full-zoom-in>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["ZM", "zM", "<full-zoom-more>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["ZO", "zO", "<full-zoom-out>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["ZR", "zR", "<full-zoom-reduce>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["zZ", "<full-zoom>"],
|
||||
mappings.add(modes.COMMAND, ["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(myModes, ["<C-g>", "<page-info>"],
|
||||
mappings.add(modes.COMMAND, ["<C-g>", "<page-info>"],
|
||||
"Print the current file name",
|
||||
function () { buffer.showPageInfo(false); });
|
||||
|
||||
mappings.add(myModes, ["g<C-g>", "<more-page-info>"],
|
||||
mappings.add(modes.COMMAND, ["g<C-g>", "<more-page-info>"],
|
||||
"Print file information",
|
||||
function () { buffer.showPageInfo(true); });
|
||||
},
|
||||
options: function () {
|
||||
options.add(["encoding", "enc"],
|
||||
"The current buffer's character encoding",
|
||||
"string", "UTF-8",
|
||||
{
|
||||
scope: Option.SCOPE_LOCAL,
|
||||
getter: function () config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset,
|
||||
setter: function (val) {
|
||||
if (options["encoding"] == val)
|
||||
return val;
|
||||
|
||||
// Stolen from browser.jar/content/browser/browser.js, more or less.
|
||||
try {
|
||||
config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
|
||||
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
|
||||
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
}
|
||||
catch (e) { dactyl.reportError(e); }
|
||||
return null;
|
||||
},
|
||||
completer: function (context) completion.charset(context)
|
||||
});
|
||||
|
||||
options.add(["iskeyword", "isk"],
|
||||
"Regular expression defining which characters constitute word characters",
|
||||
"string", '[^\\s.,!?:;/"\'^$%&?()[\\]{}<>#*+|=~_-]',
|
||||
|
||||
@@ -424,11 +424,13 @@ var CommandExMode = Class("CommandExMode", CommandMode, {
|
||||
},
|
||||
|
||||
onSubmit: function onSubmit(command) {
|
||||
io.withSavedValues(["readHeredoc", "sourcing"], function () {
|
||||
this.sourcing = { file: "[Command Line]", line: 1 };
|
||||
this.readHeredoc = commandline.readHeredoc;
|
||||
commands.repeat = command;
|
||||
dactyl.execute(command);
|
||||
contexts.withContext({ file: "[Command Line]", line: 1 },
|
||||
function () {
|
||||
io.withSavedValues(["readHeredoc"], function () {
|
||||
this.readHeredoc = commandline.readHeredoc;
|
||||
commands.repeat = command;
|
||||
dactyl.execute(command);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -985,7 +987,9 @@ var CommandLine = Module("commandline", {
|
||||
dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true);
|
||||
|
||||
this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
|
||||
if (!events.feedingKeys && options["autocomplete"].length) {
|
||||
if (events.feedingKeys)
|
||||
this.ignoredCount++;
|
||||
if (options["autocomplete"].length) {
|
||||
this.complete(true, false);
|
||||
this.itemList.visible = true;
|
||||
}
|
||||
@@ -1003,8 +1007,11 @@ var CommandLine = Module("commandline", {
|
||||
this.itemList.visible = false;
|
||||
},
|
||||
|
||||
ignoredCount: 0,
|
||||
onDoneFeeding: function onDoneFeeding() {
|
||||
this.autocompleteTimer.flush(true);
|
||||
if (this.ignoredCount)
|
||||
this.autocompleteTimer.flush(true);
|
||||
this.ignoredCount = 0;
|
||||
},
|
||||
|
||||
UP: {},
|
||||
|
||||
@@ -397,9 +397,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
if (jsmodules.__proto__ != window)
|
||||
str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }";
|
||||
|
||||
let info = contexts.context;
|
||||
if (fileName == null)
|
||||
if (io.sourcing && io.sourcing.file[0] !== "[")
|
||||
({ file: fileName, line: lineNumber, context: ctxt }) = io.sourcing;
|
||||
if (info && info.file[0] !== "[")
|
||||
({ file: fileName, line: lineNumber, context: ctxt }) = info;
|
||||
else try {
|
||||
if (!context)
|
||||
context = userContext || ctxt;
|
||||
@@ -410,8 +411,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
this.loadScript("resource://dactyl-content/eval.js", context);
|
||||
if (context[EVAL_ERROR]) {
|
||||
try {
|
||||
context[EVAL_ERROR].fileName = io.sourcing.file;
|
||||
context[EVAL_ERROR].lineNumber += io.sourcing.line;
|
||||
context[EVAL_ERROR].fileName = info.file;
|
||||
context[EVAL_ERROR].lineNumber += info.line;
|
||||
}
|
||||
catch (e) {}
|
||||
throw context[EVAL_ERROR];
|
||||
@@ -677,7 +678,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
fileMap["plugins"] = function () ['text/xml;charset=UTF-8', help];
|
||||
|
||||
fileMap["versions"] = function () {
|
||||
let NEWS = util.httpGet(config.addon.getResourceURI("NEWS").spec).responseText;
|
||||
let NEWS = util.httpGet(config.addon.getResourceURI("NEWS").spec,
|
||||
{ mimeType: "text/plain;charset=UTF-8" })
|
||||
.responseText;
|
||||
|
||||
let re = util.regexp(<![CDATA[
|
||||
^ (?P<space> \s*)
|
||||
@@ -725,8 +728,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
}
|
||||
|
||||
list = null;
|
||||
if (level == 0 && /^.*:\n$/.test())
|
||||
var elem = <h2>{template.linkifyHelp(par.slice(0, -1), true)}</h2>;
|
||||
if (level == 0 && /^.*:\n$/.test(match.par))
|
||||
res += <h2>{template.linkifyHelp(par.slice(0, -1), true)}</h2>;
|
||||
else {
|
||||
let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(par);
|
||||
res += <p highlight={group + " HelpNews"}>{
|
||||
@@ -756,7 +759,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
unescape(encodeURI( // UTF-8 handling hack.
|
||||
<document xmlns={NS} xmlns:dactyl={NS}
|
||||
name="versions" title={config.appName + " Versions"}>
|
||||
<h1 tag="versions news">{config.appName} Versions</h1>
|
||||
<h1 tag="versions news NEWS">{config.appName} Versions</h1>
|
||||
<toc start="2"/>
|
||||
|
||||
{rec(NEWS, 0)}
|
||||
@@ -1030,7 +1033,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
get: function globalVariables() this._globalVariables
|
||||
}),
|
||||
|
||||
loadPlugins: function (args) {
|
||||
loadPlugins: function (args, force) {
|
||||
function sourceDirectory(dir) {
|
||||
dactyl.assert(dir.isReadable(), "E484: Can't open file " + dir.path);
|
||||
|
||||
@@ -1041,9 +1044,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
loadplugins = { __proto__: loadplugins, value: args.map(Option.parseRegexp) }
|
||||
|
||||
dir.readDirectory(true).forEach(function (file) {
|
||||
if (file.isFile() && loadplugins.getKey(file.path) && !(file.path in dactyl.pluginFiles)) {
|
||||
if (file.isFile() && loadplugins.getKey(file.path) && !(!force && file.path in dactyl.pluginFiles)) {
|
||||
try {
|
||||
io.source(file.path, false);
|
||||
io.source(file.path);
|
||||
dactyl.pluginFiles[file.path] = true;
|
||||
}
|
||||
catch (e) {
|
||||
@@ -1356,8 +1359,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
*/
|
||||
reportError: function reportError(error, echo) {
|
||||
if (error instanceof FailedAssertion || error.message === "Interrupted") {
|
||||
|
||||
let prefix = io.sourcing ? io.sourcing.file + ":" + io.sourcing.line + ": " : "";
|
||||
let context = contexts.context;
|
||||
let prefix = context ? context.file + ":" + context.line + ": " : "";
|
||||
if (error.message && error.message.indexOf(prefix) !== 0)
|
||||
error.message = prefix + error.message;
|
||||
|
||||
@@ -1456,8 +1459,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
}
|
||||
}, {
|
||||
events: function () {
|
||||
events.addSessionListener(window, "click", dactyl.closure.onClick, true);
|
||||
events.addSessionListener(window, "dactyl.execute", dactyl.closure.onExecute, true);
|
||||
events.listen(window, "click", dactyl.closure.onClick, true);
|
||||
events.listen(window, "dactyl.execute", dactyl.closure.onExecute, true);
|
||||
},
|
||||
// Only general options are added here, which are valid for all Dactyl extensions
|
||||
options: function () {
|
||||
@@ -1753,10 +1756,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
commands.add(["loadplugins", "lpl"],
|
||||
"Load all plugins immediately",
|
||||
function (args) {
|
||||
dactyl.loadPlugins(args.length ? args : null);
|
||||
dactyl.loadPlugins(args.length ? args : null, args.bang);
|
||||
},
|
||||
{
|
||||
argCount: "*",
|
||||
bang: true,
|
||||
keepQuotes: true,
|
||||
serialGroup: 10,
|
||||
serialize: function () [
|
||||
@@ -1792,7 +1796,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
|
||||
commands.add(["reh[ash]"],
|
||||
"Reload the " + config.appName + " add-on",
|
||||
function (args) { util.rehash(args); },
|
||||
function (args) {
|
||||
if (args.trailing)
|
||||
JSMLoader.rehashCmd = args.trailing; // Hack.
|
||||
args.break = true;
|
||||
util.rehash(args);
|
||||
},
|
||||
{
|
||||
argCount: "0",
|
||||
options: [
|
||||
@@ -1825,8 +1834,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
function () { dactyl.restart(); });
|
||||
|
||||
function findToolbar(name) util.evaluateXPath(
|
||||
"./*[@toolbarname=" + util.escapeString(name, "'") + "]",
|
||||
toolbox).snapshotItem(0);
|
||||
"//*[@toolbarname=" + util.escapeString(name, "'") + "]",
|
||||
document).snapshotItem(0);
|
||||
|
||||
var toolbox = document.getElementById("navigator-toolbox");
|
||||
if (toolbox) {
|
||||
@@ -2010,7 +2019,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
completion.toolbar = function toolbar(context) {
|
||||
context.title = ["Toolbar"];
|
||||
context.keys = { text: function (item) item.getAttribute("toolbarname"), description: function () "" };
|
||||
context.completions = util.evaluateXPath("./*[@toolbarname]", toolbox);
|
||||
context.completions = util.evaluateXPath("//*[@toolbarname]", document);
|
||||
};
|
||||
|
||||
completion.window = function window(context) {
|
||||
@@ -2076,14 +2085,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
if (dactyl.commandLineOptions.rcFile) {
|
||||
let filename = dactyl.commandLineOptions.rcFile;
|
||||
if (!/^(NONE|NORC)$/.test(filename))
|
||||
io.source(io.File(filename).path, false); // let io.source handle any read failure like Vim
|
||||
io.source(io.File(filename).path, { group: contexts.user });
|
||||
}
|
||||
else {
|
||||
if (init)
|
||||
dactyl.execute(init);
|
||||
else {
|
||||
if (rcFile) {
|
||||
io.source(rcFile.path, false);
|
||||
io.source(rcFile.path, { group: contexts.user });
|
||||
services.environment.set("MY_" + config.idName + "RC", rcFile.path);
|
||||
}
|
||||
else
|
||||
@@ -2093,7 +2102,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
if (options["exrc"] && !dactyl.commandLineOptions.rcFile) {
|
||||
let localRCFile = io.getRCFile(io.cwd);
|
||||
if (localRCFile && !localRCFile.equals(rcFile))
|
||||
io.source(localRCFile.path, false);
|
||||
io.source(localRCFile.path, { group: contexts.user });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2118,6 +2127,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
||||
dactyl.execute(cmd);
|
||||
});
|
||||
|
||||
if (JSMLoader.rehashCmd)
|
||||
dactyl.execute(JSMLoader.rehashCmd);
|
||||
JSMLoader.rehashCmd = null;
|
||||
|
||||
dactyl.fullyInitialized = true;
|
||||
dactyl.triggerObserver("enter", null);
|
||||
autocommands.trigger("Enter", {});
|
||||
|
||||
@@ -36,6 +36,8 @@ var Editor = Module("editor", {
|
||||
let text = dactyl.clipboardRead(clipboard);
|
||||
if (!text)
|
||||
return;
|
||||
if (isinstance(elem, [HTMLInputElement, XULTextBoxElement]))
|
||||
text = text.replace(/\n+/g, "");
|
||||
|
||||
// This is a hacky fix - but it works.
|
||||
// <s-insert> in the bottom of a long textarea bounces up
|
||||
@@ -46,7 +48,9 @@ var Editor = Module("editor", {
|
||||
let end = elem.selectionEnd;
|
||||
let value = elem.value.substring(0, start) + text + elem.value.substring(end);
|
||||
elem.value = value;
|
||||
|
||||
Editor.getEditor(elem).rootElement.firstChild.textContent = value;
|
||||
|
||||
elem.selectionStart = Math.min(start + (toStart ? 0 : text.length), elem.value.length);
|
||||
elem.selectionEnd = elem.selectionStart;
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
},
|
||||
|
||||
execute: function execute(result, force) {
|
||||
function dbg() {}
|
||||
|
||||
if (force && this.actions.length)
|
||||
this.processors.length = 0;
|
||||
|
||||
@@ -59,7 +57,7 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
|
||||
for (var res = this.actions[0]; callable(res);) {
|
||||
res = dactyl.trapErrors(res);
|
||||
dbg("ACTION RES: " + res);
|
||||
events.dbg("ACTION RES: " + res);
|
||||
}
|
||||
result = res === Events.PASS ? Events.PASS : Events.KILL;
|
||||
}
|
||||
@@ -73,17 +71,19 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
else if (result === undefined)
|
||||
result = Events.PASS;
|
||||
|
||||
dbg("RESULT: " + (result === Events.KILL ? "KILL" :
|
||||
result === Events.PASS ? "PASS" :
|
||||
result === Events.ABORT ? "ABORT" : result));
|
||||
events.dbg("RESULT: " + (result === Events.KILL ? "KILL" :
|
||||
result === Events.PASS ? "PASS" :
|
||||
result === Events.ABORT ? "ABORT" : result));
|
||||
|
||||
if (result !== Events.PASS)
|
||||
Events.kill(this.events[this.events.length - 1]);
|
||||
|
||||
if (result === Events.PASS || result === Events.ABORT) {
|
||||
dbg("REFEED: " + this.events.filter(function (e) e.getPreventDefault()).map(events.closure.toString).join(""));
|
||||
this.events.filter(function (e) e.getPreventDefault())
|
||||
.forEach(function (event, i) {
|
||||
let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
|
||||
if (list.length)
|
||||
events.dbg("REFEED: " + list.map(events.closure.toString).join(""));
|
||||
|
||||
list.forEach(function (event, i) {
|
||||
let elem = event.originalTarget;
|
||||
if (event.originalTarget) {
|
||||
let doc = elem.ownerDocument || elem.document || elem;
|
||||
@@ -102,8 +102,6 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
},
|
||||
|
||||
process: function process(event) {
|
||||
function dbg() {}
|
||||
|
||||
if (this.timer)
|
||||
this.timer.cancel();
|
||||
|
||||
@@ -115,15 +113,15 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
let actions = [];
|
||||
let processors = [];
|
||||
|
||||
dbg("\n\n");
|
||||
dbg("KEY: " + key + " skipmap: " + event.skipmap + " macro: " + event.isMacro);
|
||||
events.dbg("\n\n");
|
||||
events.dbg("KEY: " + key + " skipmap: " + event.skipmap + " macro: " + event.isMacro);
|
||||
|
||||
for (let [i, input] in Iterator(this.processors)) {
|
||||
let res = input.process(event);
|
||||
if (res !== Events.ABORT)
|
||||
var result = res;
|
||||
|
||||
dbg("RES: " + input + " " + (callable(res) ? {}.toString.call(res) : res));
|
||||
events.dbg("RES: " + input + " " + (callable(res) ? {}.toString.call(res) : res));
|
||||
|
||||
if (res === Events.KILL)
|
||||
break;
|
||||
@@ -139,9 +137,9 @@ var ProcessorStack = Class("ProcessorStack", {
|
||||
processors.push(input);
|
||||
}
|
||||
|
||||
dbg("RESULT: " + (callable(result) ? {}.toString.call(result) : result) + " " + event.getPreventDefault());
|
||||
dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
||||
dbg("PROCESSORS:", processors);
|
||||
events.dbg("RESULT: " + (callable(result) ? {}.toString.call(result) : result) + " " + event.getPreventDefault());
|
||||
events.dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
||||
events.dbg("PROCESSORS:", processors);
|
||||
|
||||
this._actions = actions;
|
||||
this.actions = actions.concat(this.actions);
|
||||
@@ -257,13 +255,80 @@ var KeyArgProcessor = Class("KeyArgProcessor", KeyProcessor, {
|
||||
}
|
||||
});
|
||||
|
||||
var EventHive = Class("EventHive", Contexts.Hive, {
|
||||
init: function init(group) {
|
||||
init.supercall(this, group);
|
||||
this.sessionListeners = [];
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
this.unlisten(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
listen: function (target, event, callback, capture) {
|
||||
if (isObject(event))
|
||||
var [self, events] = [event, event[callback]];
|
||||
else
|
||||
[self, events] = [null, array.toObject([[event, callback]])];
|
||||
|
||||
for (let [event, callback] in Iterator(events)) {
|
||||
let args = [Cu.getWeakReference(target),
|
||||
event,
|
||||
this.wrapListener(callback, self),
|
||||
capture];
|
||||
|
||||
target.addEventListener.apply(target, args.slice(1));
|
||||
this.sessionListeners.push(args);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an event listener.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
unlisten: function (target, event, callback, capture) {
|
||||
this.sessionListeners = this.sessionListeners.filter(function (args) {
|
||||
if (target == null || args[0].get() == target && args[1] == event && args[2] == callback && args[3] == capture) {
|
||||
args[0].get().removeEventListener.apply(args[0].get(), args.slice(1));
|
||||
return true;
|
||||
}
|
||||
return !args[0].get();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @instance events
|
||||
*/
|
||||
var Events = Module("events", {
|
||||
dbg: function () {},
|
||||
|
||||
init: function () {
|
||||
const self = this;
|
||||
|
||||
update(this, {
|
||||
hives: contexts.Hives("events", EventHive),
|
||||
user: contexts.hives.events.user,
|
||||
builtin: contexts.hives.events.builtin
|
||||
});
|
||||
|
||||
EventHive.prototype.wrapListener = this.closure.wrapListener;
|
||||
|
||||
XML.ignoreWhitespace = true;
|
||||
util.overlayWindow(window, {
|
||||
append: <e4x xmlns={XUL}>
|
||||
@@ -285,8 +350,6 @@ var Events = Module("events", {
|
||||
this._macroKeys = [];
|
||||
this._lastMacro = "";
|
||||
|
||||
this.sessionListeners = [];
|
||||
|
||||
this._macros = storage.newMap("macros", { privateData: true, store: true });
|
||||
for (let [k, m] in this._macros)
|
||||
if (isString(m))
|
||||
@@ -345,21 +408,13 @@ var Events = Module("events", {
|
||||
}
|
||||
|
||||
this._activeMenubar = false;
|
||||
for (let [event, callback] in Iterator(this.events))
|
||||
this.addSessionListener(window, event, callback, true);
|
||||
this.listen(window, this, "events", true);
|
||||
|
||||
dactyl.registerObserver("modeChange", function () {
|
||||
delete self.processor;
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
util.dump("Removing all event listeners");
|
||||
for (let args in values(this.sessionListeners))
|
||||
if (args[0].get())
|
||||
args[0].get().removeEventListener.apply(args[0].get(), args.slice(1));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an event listener for this session and removes it on
|
||||
* dactyl shutdown.
|
||||
@@ -370,13 +425,8 @@ var Events = Module("events", {
|
||||
* @param {boolean} capture When true, listen during the capture
|
||||
* phase, otherwise during the bubbling phase.
|
||||
*/
|
||||
addSessionListener: function (target, event, callback, capture) {
|
||||
let args = Array.slice(arguments, 0);
|
||||
args[2] = this.wrapListener(callback);
|
||||
args[0].addEventListener.apply(args[0], args.slice(1));
|
||||
args[0] = Cu.getWeakReference(args[0]);
|
||||
this.sessionListeners.push(args);
|
||||
},
|
||||
get addSessionListener() this.builtin.closure.listen,
|
||||
get listen() this.builtin.closure.listen,
|
||||
|
||||
/**
|
||||
* Wraps an event listener to ensure that errors are reported.
|
||||
@@ -1053,7 +1103,7 @@ var Events = Module("events", {
|
||||
|
||||
// Hack to deal with <BS> and so forth not dispatching input
|
||||
// events
|
||||
if (event.originalTarget instanceof HTMLInputElement && !modes.main.passthrough) {
|
||||
if (key && event.originalTarget instanceof HTMLInputElement && !modes.main.passthrough) {
|
||||
let elem = event.originalTarget;
|
||||
elem.dactylKeyPress = elem.value;
|
||||
util.timeout(function () {
|
||||
|
||||
@@ -677,7 +677,7 @@ var Hints = Module("hints", {
|
||||
|
||||
let appContent = document.getElementById("appcontent");
|
||||
if (appContent)
|
||||
events.addSessionListener(appContent, "scroll", this.resizeTimer.closure.tell, false);
|
||||
events.listen(appContent, "scroll", this.resizeTimer.closure.tell, false);
|
||||
|
||||
const Mode = Hints.Mode;
|
||||
Mode.defaultValue("tags", function () function () options["hinttags"]);
|
||||
@@ -1159,22 +1159,22 @@ var Hints = Module("hints", {
|
||||
function ({ self }) { self.escapeNumbers = !self.escapeNumbers; });
|
||||
},
|
||||
options: function () {
|
||||
const DEFAULT_HINTTAGS =
|
||||
util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select",
|
||||
"*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @tabindex or @role='link' or @role='button']"]);
|
||||
|
||||
function xpath(arg) Option.quote(util.makeXPath(arg));
|
||||
function xpath(arg) util.makeXPath(arg);
|
||||
options.add(["extendedhinttags", "eht"],
|
||||
"XPath strings of hintable elements for extended hint modes",
|
||||
"regexpmap", "[iI]:" + xpath(["img"]) +
|
||||
",[asOTivVWy]:" + xpath(["{a,area}[@href]", "{img,iframe}[@src]"]) +
|
||||
",[F]:" + xpath(["body", "code", "div", "html", "p", "pre", "span"]) +
|
||||
",[S]:" + xpath(["input[not(@type='hidden')]", "textarea", "button", "select"]),
|
||||
"regexpmap", {
|
||||
"[iI]": xpath(["img"]),
|
||||
"[asOTivVWy]": xpath(["{a,area}[@href]", "{img,iframe}[@src]"]),
|
||||
"[F]": xpath(["body", "code", "div", "html", "p", "pre", "span"]),
|
||||
"[S]": xpath(["input[not(@type='hidden')]", "textarea", "button", "select"])
|
||||
},
|
||||
{ validator: Option.validateXPath });
|
||||
|
||||
options.add(["hinttags", "ht"],
|
||||
"XPath string of hintable elements activated by 'f' and 'F'",
|
||||
"string", DEFAULT_HINTTAGS,
|
||||
"string", xpath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select",
|
||||
"*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or " +
|
||||
"@tabindex or @role='link' or @role='button']"]),
|
||||
{ validator: Option.validateXPath });
|
||||
|
||||
options.add(["hintkeys", "hk"],
|
||||
|
||||
@@ -107,6 +107,10 @@ var Map = Class("Map", {
|
||||
.map(function ([i, prop]) [prop, this[i]], arguments)
|
||||
.toObject();
|
||||
|
||||
args = update({ context: contexts.context },
|
||||
this.hive.argsExtra(args),
|
||||
args);
|
||||
|
||||
let self = this;
|
||||
function repeat() self.action(args)
|
||||
if (this.names[0] != ".") // FIXME: Kludge.
|
||||
@@ -134,18 +138,12 @@ var Map = Class("Map", {
|
||||
id: 0
|
||||
});
|
||||
|
||||
var MapHive = Class("MapHive", {
|
||||
init: function init(name, description, filter) {
|
||||
this.name = name;
|
||||
var MapHive = Class("MapHive", Contexts.Hive, {
|
||||
init: function init(group) {
|
||||
init.supercall(this, group);
|
||||
this.stacks = {};
|
||||
this.description = description;
|
||||
this.filter = filter || function (uri) true;
|
||||
},
|
||||
|
||||
get toStringParams() [this.name],
|
||||
|
||||
get builtin() mappings.builtinHives.indexOf(this) >= 0,
|
||||
|
||||
/**
|
||||
* Iterates over all mappings present in all of the given *modes*.
|
||||
*
|
||||
@@ -172,7 +170,7 @@ var MapHive = Class("MapHive", {
|
||||
extra = extra || {};
|
||||
|
||||
let map = Map(modes, keys, description, action, extra);
|
||||
map.definedAt = Commands.getCaller(Components.stack.caller);
|
||||
map.definedAt = contexts.getCaller(Components.stack.caller);
|
||||
map.hive = this;
|
||||
|
||||
if (this.name !== "builtin")
|
||||
@@ -299,16 +297,11 @@ var MapHive = Class("MapHive", {
|
||||
*/
|
||||
var Mappings = Module("mappings", {
|
||||
init: function () {
|
||||
this.user = MapHive("user", "User-defined mappings");
|
||||
this.builtin = MapHive("builtin", "Builtin mappings");
|
||||
|
||||
this.builtinHives = array([this.user, this.builtin]);
|
||||
this.allHives = [this.user, this.builtin];
|
||||
},
|
||||
|
||||
repeat: Modes.boundProperty(),
|
||||
|
||||
hives: Class.memoize(function () array(this.allHives.filter(function (h) h.filter(buffer.uri)))),
|
||||
get allHives() contexts.allGroups.mappings,
|
||||
|
||||
get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
|
||||
|
||||
@@ -344,7 +337,7 @@ var Mappings = Module("mappings", {
|
||||
*/
|
||||
add: function () {
|
||||
let map = this.builtin.add.apply(this.builtin, arguments);
|
||||
map.definedAt = Commands.getCaller(Components.stack.caller);
|
||||
map.definedAt = contexts.getCaller(Components.stack.caller);
|
||||
return map;
|
||||
},
|
||||
|
||||
@@ -361,33 +354,10 @@ var Mappings = Module("mappings", {
|
||||
*/
|
||||
addUserMap: function () {
|
||||
let map = this.user.add.apply(this.user, arguments);
|
||||
map.definedAt = Commands.getCaller(Components.stack.caller);
|
||||
map.definedAt = contexts.getCaller(Components.stack.caller);
|
||||
return map;
|
||||
},
|
||||
|
||||
addHive: function addHive(name, filter, description) {
|
||||
this.removeHive(name);
|
||||
|
||||
let hive = MapHive(name, description, filter);
|
||||
this.allHives.unshift(hive);
|
||||
return hive;
|
||||
},
|
||||
|
||||
removeHive: function removeHive(name, filter) {
|
||||
let hive = this.getHive(name);
|
||||
dactyl.assert(!hive || !hive.builtin, "Cannot remove builtin group");
|
||||
if (hive)
|
||||
this.allHives.splice(this.allHives.indexOf(hive), 1);
|
||||
|
||||
if (io.sourcing && io.sourcing.mapHive == hive)
|
||||
io.sourcing.mapHive = null;
|
||||
|
||||
delete this.hives;
|
||||
return hive;
|
||||
},
|
||||
|
||||
getHive: function getHive(name) array.nth(this.allHives, function (h) h.name == name, 0) || null,
|
||||
|
||||
/**
|
||||
* Returns the map from *mode* named *cmd*.
|
||||
*
|
||||
@@ -417,7 +387,7 @@ var Mappings = Module("mappings", {
|
||||
* @param {string} filter The filter string to match.
|
||||
*/
|
||||
list: function (modes, filter, hives) {
|
||||
hives = hives || mappings.userHives;
|
||||
hives = (hives || mappings.userHives).filter(function (h) modes.some(function (m) h.getStack(m).length));
|
||||
|
||||
let modeSign = "";
|
||||
modes.filter(function (m) m.char).forEach(function (m) { modeSign += m.char; });
|
||||
@@ -462,7 +432,14 @@ var Mappings = Module("mappings", {
|
||||
}
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
contexts: function initContexts(dactyl, modules, window) {
|
||||
update(Mappings.prototype, {
|
||||
hives: contexts.Hives("mappings", MapHive),
|
||||
user: contexts.hives.mappings.user,
|
||||
builtin: contexts.hives.mappings.builtin
|
||||
});
|
||||
},
|
||||
commands: function initCommands(dactyl, modules, window) {
|
||||
function addMapCommands(ch, mapmodes, modeDescription) {
|
||||
function map(args, noremap) {
|
||||
let mapmodes = array.uniq(args["-modes"].map(findMode));
|
||||
@@ -480,9 +457,12 @@ var Mappings = Module("mappings", {
|
||||
if (!rhs) // list the mapping
|
||||
mappings.list(mapmodes, mappings.expandLeader(lhs), hives);
|
||||
else {
|
||||
util.assert(args["-group"].modifiable,
|
||||
"Cannot change mappings in the builtin group");
|
||||
|
||||
args["-group"].add(mapmodes, [lhs],
|
||||
args["-description"],
|
||||
Command.bindMacro(args, "-keys", function (params) params),
|
||||
contexts.bindMacro(args, "-keys", function (params) params),
|
||||
{
|
||||
arg: args["-arg"],
|
||||
count: args["-count"],
|
||||
@@ -531,7 +511,7 @@ var Mappings = Module("mappings", {
|
||||
names: ["-ex", "-e"],
|
||||
description: "Execute this mapping as an Ex command rather than keys"
|
||||
},
|
||||
groupFlag,
|
||||
contexts.GroupFlag("mappings"),
|
||||
{
|
||||
names: ["-javascript", "-js", "-j"],
|
||||
description: "Execute this mapping as JavaScript rather than keys"
|
||||
@@ -554,20 +534,16 @@ var Mappings = Module("mappings", {
|
||||
serialize: function () {
|
||||
return this.name != "map" ? [] :
|
||||
array(mappings.userHives)
|
||||
.filter(function (h) !h.noPersist)
|
||||
.filter(function (h) h.persist)
|
||||
.map(function (hive) [
|
||||
{
|
||||
command: "mapgroup",
|
||||
bang: true,
|
||||
arguments: [hive.name, String(hive.filter)].slice(0, hive.name == "user" ? 1 : 2)
|
||||
}
|
||||
].concat([
|
||||
{
|
||||
command: "map",
|
||||
options: array([
|
||||
hive !== mappings.user && ["-group", hive.name],
|
||||
["-modes", uniqueModes(map.modes)],
|
||||
["-description", map.description],
|
||||
map.silent && ["-silent"]])
|
||||
|
||||
.filter(util.identity)
|
||||
.toObject(),
|
||||
arguments: [map.names[0]],
|
||||
@@ -576,7 +552,7 @@ var Mappings = Module("mappings", {
|
||||
}
|
||||
for (map in userMappings(hive))
|
||||
if (map.persist)
|
||||
]))
|
||||
])
|
||||
.flatten().array;
|
||||
}
|
||||
};
|
||||
@@ -602,6 +578,9 @@ var Mappings = Module("mappings", {
|
||||
commands.add([ch + "mapc[lear]"],
|
||||
"Remove all mappings" + modeDescription,
|
||||
function (args) {
|
||||
util.assert(args["-group"].modifiable,
|
||||
"Cannot change mappings in the builtin group");
|
||||
|
||||
let mapmodes = array.uniq(args["-modes"].map(findMode));
|
||||
mapmodes.forEach(function (mode) {
|
||||
args["-group"].clear(mode);
|
||||
@@ -610,7 +589,7 @@ var Mappings = Module("mappings", {
|
||||
{
|
||||
argCount: "0",
|
||||
options: [
|
||||
groupFlag,
|
||||
contexts.GroupFlag("mappings"),
|
||||
update({}, modeFlag, {
|
||||
names: ["-modes", "-mode", "-m"],
|
||||
type: CommandOption.LIST,
|
||||
@@ -623,6 +602,9 @@ var Mappings = Module("mappings", {
|
||||
commands.add([ch + "unm[ap]"],
|
||||
"Remove a mapping" + modeDescription,
|
||||
function (args) {
|
||||
util.assert(args["-group"].modifiable,
|
||||
"Cannot change mappings in the builtin group");
|
||||
|
||||
let mapmodes = array.uniq(args["-modes"].map(findMode));
|
||||
|
||||
let found = false;
|
||||
@@ -639,7 +621,7 @@ var Mappings = Module("mappings", {
|
||||
argCount: "1",
|
||||
completer: opts.completer,
|
||||
options: [
|
||||
groupFlag,
|
||||
contexts.GroupFlag("mappings"),
|
||||
update({}, modeFlag, {
|
||||
names: ["-modes", "-mode", "-m"],
|
||||
type: CommandOption.LIST,
|
||||
@@ -650,97 +632,6 @@ var Mappings = Module("mappings", {
|
||||
});
|
||||
}
|
||||
|
||||
commands.add(["mapg[roup]"],
|
||||
"Create or select a mapping group",
|
||||
function (args) {
|
||||
dactyl.assert(args.length <= 2, "Trailing characters");
|
||||
|
||||
if (args.length == 0)
|
||||
return void completion.listCompleter("mapGroup", "");
|
||||
|
||||
let name = Option.dequote(args[0]);
|
||||
let hive = mappings.getHive(name);
|
||||
|
||||
if (args.length == 2) {
|
||||
dactyl.assert(!hive || args.bang, "Group exists");
|
||||
|
||||
let filter = function siteFilter(uri)
|
||||
siteFilter.filters.every(function (f) f(uri) == f.result);
|
||||
|
||||
update(filter, {
|
||||
toString: function () this.filters.join(","),
|
||||
filters: Option.splitList(args[1], true).map(function (pattern) {
|
||||
let [, res, filter] = /^(!?)(.*)/.exec(pattern);
|
||||
|
||||
return update(Styles.matchFilter(Option.dequote(filter)), {
|
||||
result: !res,
|
||||
toString: function () pattern
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
hive = mappings.addHive(name, filter, args["-description"]);
|
||||
if (args["-nopersist"])
|
||||
hive.noPersist = true;
|
||||
}
|
||||
|
||||
dactyl.assert(hive, "No mapping group: " + name);
|
||||
dactyl.assert(hive.name != "builtin", "Can't map to builtin hive");
|
||||
if (io.sourcing)
|
||||
io.sourcing.mapHive = hive;
|
||||
},
|
||||
{
|
||||
argCount: "*",
|
||||
bang: true,
|
||||
completer: function (context, args) {
|
||||
if (args.length == 1)
|
||||
completion.mapGroup(context);
|
||||
else {
|
||||
Option.splitList(context.filter);
|
||||
context.advance(Option._splitAt);
|
||||
|
||||
context.compare = CompletionContext.Sort.unsorted;
|
||||
context.completions = [
|
||||
[buffer.uri.host, "Current Host"],
|
||||
[buffer.uri.spec, "Current Page"]
|
||||
];
|
||||
}
|
||||
},
|
||||
keepQuotes: true,
|
||||
options: [
|
||||
{
|
||||
names: ["-description", "-desc", "-d"],
|
||||
description: "A description of this mapping group",
|
||||
type: CommandOption.STRING
|
||||
},
|
||||
{
|
||||
names: ["-nopersist", "-n"],
|
||||
description: "Do not save this mapping group to an auto-generated RC file"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
commands.add(["delmapg[roup]"],
|
||||
"Delete a mapping group",
|
||||
function (args) {
|
||||
dactyl.assert(mappings.getHive(args[0]), "No mapping group: " + args[0]);
|
||||
mappings.removeHive(args[0]);
|
||||
},
|
||||
{
|
||||
argCount: "1",
|
||||
completer: function (context, args) {
|
||||
completion.mapGroup(context);
|
||||
context.filters.push(function ({ item }) !item.builtin);
|
||||
}
|
||||
});
|
||||
|
||||
let groupFlag = {
|
||||
names: ["-group", "-g"],
|
||||
description: "Mapping group to which to add this mapping",
|
||||
type: ArgType("map-group", function (group) isString(group) ? mappings.getHive(group) : group),
|
||||
get default() io.sourcing && io.sourcing.mapHive || mappings.user,
|
||||
completer: function (context) completion.mapGroup(context)
|
||||
};
|
||||
let modeFlag = {
|
||||
names: ["-mode", "-m"],
|
||||
type: CommandOption.STRING,
|
||||
@@ -780,11 +671,18 @@ var Mappings = Module("mappings", {
|
||||
|
||||
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
|
||||
|
||||
for (let mode in modes.mainModes)
|
||||
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()]);
|
||||
|
||||
let args = {
|
||||
getMode: function (args) findMode(args["-mode"]),
|
||||
iterate: function (args) {
|
||||
let mainMode = this.getMode(args);
|
||||
let seen = {};
|
||||
// Bloody hell. --Kris
|
||||
for (let mode in values([mainMode].concat(mainMode.bases)))
|
||||
for (let hive in mappings.hives.iterValues())
|
||||
for (let map in array.iterValues(hive.getStack(mode)))
|
||||
@@ -794,7 +692,7 @@ var Mappings = Module("mappings", {
|
||||
name: name,
|
||||
columns: [
|
||||
mode == mainMode ? "" : <span highlight="Object" style="padding-right: 1em;">{mode.name}</span>,
|
||||
hive.name == "builtin" ? "" : <span highlight="Object" style="padding-right: 1em;">{hive.name}</span>
|
||||
hive == mappings.builtin ? "" : <span highlight="Object" style="padding-right: 1em;">{hive.name}</span>
|
||||
],
|
||||
__proto__: map
|
||||
};
|
||||
@@ -840,19 +738,8 @@ var Mappings = Module("mappings", {
|
||||
options: []
|
||||
});
|
||||
});
|
||||
|
||||
for (let mode in modes.mainModes)
|
||||
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()]);
|
||||
},
|
||||
completion: function () {
|
||||
completion.mapGroup = function mapGroup(context, modes) {
|
||||
context.title = ["Map group"];
|
||||
context.keys = { text: "name", description: function (h) h.description || h.filter };
|
||||
context.completions = mappings.userHives;
|
||||
};
|
||||
completion: function initCompletion(dactyl, modules, window) {
|
||||
completion.userMapping = function userMapping(context, modes, hive) {
|
||||
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
|
||||
hive = hive || mappings.user;
|
||||
@@ -861,20 +748,14 @@ var Mappings = Module("mappings", {
|
||||
context.completions = hive.iterate(modes);
|
||||
};
|
||||
},
|
||||
javascript: function () {
|
||||
JavaScript.setCompleter(mappings.get,
|
||||
javascript: function initJavascript(dactyl, modules, window) {
|
||||
JavaScript.setCompleter([mappings.get, mappings.builtin.get],
|
||||
[
|
||||
null,
|
||||
function (context, obj, args) {
|
||||
let mode = args[0];
|
||||
return array.flatten([
|
||||
[[name, map.description] for ([i, name] in Iterator(map.names))]
|
||||
for (map in mappings.iterate(mode))
|
||||
]);
|
||||
}
|
||||
function (context, obj, args) [[m.names, m.description] for (m in this.iterate(args[0]))]
|
||||
]);
|
||||
},
|
||||
options: function () {
|
||||
options: function initOptions(dactyl, modules, window) {
|
||||
options.add(["mapleader", "ml"],
|
||||
"Define the replacement keys for the <Leader> pseudo-key",
|
||||
"string", "\\", {
|
||||
|
||||
@@ -208,7 +208,7 @@ var Marks = Module("marks", {
|
||||
events: function () {
|
||||
let appContent = document.getElementById("appcontent");
|
||||
if (appContent)
|
||||
events.addSessionListener(appContent, "load", marks.closure._onPageLoad, true);
|
||||
events.listen(appContent, "load", marks.closure._onPageLoad, true);
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.browserModes;
|
||||
|
||||
@@ -47,8 +47,7 @@ var Modes = Module("modes", {
|
||||
count: false
|
||||
});
|
||||
this.addMode("COMMAND", {
|
||||
description: "The base mode for most modes which accept commands rather than input",
|
||||
hidden: true
|
||||
description: "The base mode for most modes which accept commands rather than input"
|
||||
});
|
||||
|
||||
this.addMode("NORMAL", {
|
||||
@@ -246,6 +245,8 @@ var Modes = Module("modes", {
|
||||
|
||||
getCharModes: function (chr) (this.modeChars[chr] || []).slice(),
|
||||
|
||||
have: function have(mode) this._modeStack.some(function (m) isinstance(m.main, mode)),
|
||||
|
||||
matchModes: function (obj)
|
||||
this._modes.filter(function (mode) Object.keys(obj)
|
||||
.every(function (k) obj[k] == (mode[k] || false))),
|
||||
@@ -398,7 +399,7 @@ var Modes = Module("modes", {
|
||||
},
|
||||
|
||||
isinstance: function (obj)
|
||||
this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
|
||||
this === obj || this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
|
||||
|
||||
allBases: Class.memoize(function () {
|
||||
let seen = {}, res = [], queue = this.bases;
|
||||
|
||||
@@ -9,6 +9,33 @@
|
||||
var MOW = Module("mow", {
|
||||
init: function () {
|
||||
|
||||
this._resize = Timer(20, 400, function () {
|
||||
if (this.visible)
|
||||
this.resize(false);
|
||||
|
||||
if (this.visible && isinstance(modes.main, modes.OUTPUT_MULTILINE))
|
||||
this.updateMorePrompt();
|
||||
}, this);
|
||||
|
||||
this._timer = Timer(20, 400, function () {
|
||||
if (modes.have(modes.OUTPUT_MULTILINE)) {
|
||||
this.resize(true);
|
||||
|
||||
if (options["more"] && this.isScrollable(1)) {
|
||||
// start the last executed command's output at the top of the screen
|
||||
let elements = this.document.getElementsByClassName("ex-command-output");
|
||||
elements[elements.length - 1].scrollIntoView(true);
|
||||
}
|
||||
else
|
||||
this.body.scrollTop = this.body.scrollHeight;
|
||||
|
||||
dactyl.focus(this.window);
|
||||
this.updateMorePrompt();
|
||||
}
|
||||
}, this);
|
||||
|
||||
events.listen(window, this, "windowEvents");
|
||||
|
||||
let fontSize = util.computedStyle(document.documentElement).fontSize;
|
||||
styles.system.add("font-size", "dactyl://content/buffer.xhtml",
|
||||
"body { font-size: " + fontSize + "; } \
|
||||
@@ -56,8 +83,8 @@ var MOW = Module("mow", {
|
||||
widgets: Class.memoize(function () commandline.widgets),
|
||||
|
||||
body: Class.memoize(function () this.widget.contentDocument.documentElement),
|
||||
document: Class.memoize(function () this.widget.contentDocument),
|
||||
window: Class.memoize(function () this.widget.contentWindow),
|
||||
get document() this.widget.contentDocument,
|
||||
get window() this.widget.contentWindow,
|
||||
|
||||
/**
|
||||
* Display a multi-line message.
|
||||
@@ -110,10 +137,9 @@ var MOW = Module("mow", {
|
||||
|
||||
// FIXME: need to make sure an open MOW is closed when commands
|
||||
// that don't generate output are executed
|
||||
if (this.widgets.mowContainer.collapsed) {
|
||||
if (!this.visible) {
|
||||
this.body.scrollTop = 0;
|
||||
while (body.firstChild)
|
||||
body.removeChild(body.firstChild);
|
||||
body.textContent = "";
|
||||
}
|
||||
|
||||
body.appendChild(output);
|
||||
@@ -122,18 +148,9 @@ var MOW = Module("mow", {
|
||||
if (!silent)
|
||||
dactyl.triggerObserver("echoMultiline", data, highlightGroup, output);
|
||||
|
||||
this.resize(true);
|
||||
|
||||
if (options["more"] && this.isScrollable(1)) {
|
||||
// start the last executed command's output at the top of the screen
|
||||
let elements = this.document.getElementsByClassName("ex-command-output");
|
||||
elements[elements.length - 1].scrollIntoView(true);
|
||||
}
|
||||
else
|
||||
this.body.scrollTop = this.body.scrollHeight;
|
||||
|
||||
dactyl.focus(this.window);
|
||||
this.updateMorePrompt();
|
||||
this._timer.tell();
|
||||
if (!this.visible)
|
||||
this._timer.flush();
|
||||
},
|
||||
|
||||
events: {
|
||||
@@ -171,6 +188,12 @@ var MOW = Module("mow", {
|
||||
}
|
||||
},
|
||||
|
||||
windowEvents: {
|
||||
resize: function onResize(event) {
|
||||
this._resize.tell();
|
||||
}
|
||||
},
|
||||
|
||||
contextEvents: {
|
||||
popupshowing: function (event) {
|
||||
let menu = commandline.widgets.contextMenu;
|
||||
@@ -207,14 +230,14 @@ var MOW = Module("mow", {
|
||||
* already so.
|
||||
*/
|
||||
resize: function updateOutputHeight(open, extra) {
|
||||
if (!open && this.widgets.mowContainer.collapsed)
|
||||
if (!(open || this.visible))
|
||||
return;
|
||||
|
||||
let doc = this.widget.contentDocument;
|
||||
|
||||
let availableHeight = config.outputHeight;
|
||||
if (!this.widgets.mowContainer.collapsed)
|
||||
availableHeight += parseFloat(this.widgets.mowContainer.height);
|
||||
if (this.visible)
|
||||
availableHeight += parseFloat(this.widgets.mowContainer.height || 0);
|
||||
availableHeight -= extra || 0;
|
||||
|
||||
doc.body.style.minWidth = this.widgets.commandbar.commandline.scrollWidth + "px";
|
||||
@@ -224,6 +247,7 @@ var MOW = Module("mow", {
|
||||
0);
|
||||
|
||||
doc.body.style.minWidth = "";
|
||||
|
||||
this.visible = true;
|
||||
},
|
||||
|
||||
@@ -242,7 +266,7 @@ var MOW = Module("mow", {
|
||||
* and what they do.
|
||||
*/
|
||||
updateMorePrompt: function updateMorePrompt(force, showHelp) {
|
||||
if (this.widgets.mowContainer.collapsed)
|
||||
if (!this.visible)
|
||||
return this.widgets.message = null;
|
||||
let elem = this.widget.contentDocument.documentElement;
|
||||
|
||||
@@ -255,6 +279,7 @@ var MOW = Module("mow", {
|
||||
},
|
||||
|
||||
visible: Modes.boundProperty({
|
||||
get: function get_mowVisible() !this.widgets.mowContainer.collapsed,
|
||||
set: function set_mowVisible(value) {
|
||||
this.widgets.mowContainer.collapsed = !value;
|
||||
|
||||
|
||||
@@ -161,24 +161,25 @@ var StatusLine = Module("statusline", {
|
||||
// when session information is available, add [+] when we can go
|
||||
// backwards, [-] when we can go forwards
|
||||
let modified = "";
|
||||
if (window.getWebNavigation) {
|
||||
let sh = window.getWebNavigation().sessionHistory;
|
||||
if (sh && sh.index > 0)
|
||||
modified += "+";
|
||||
if (sh && sh.index < sh.count - 1)
|
||||
modified += "-";
|
||||
if (url === buffer.uri.spec) {
|
||||
if (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(url))
|
||||
modified += UTF8("❤");
|
||||
//modified += UTF8("♥");
|
||||
}
|
||||
if (modules.quickmarks)
|
||||
modified += quickmarks.find(url.replace(/#.*/, "")).join("");
|
||||
}
|
||||
if (modules.bookmarkcache) {
|
||||
if (bookmarkcache.isBookmarked(buffer.uri))
|
||||
modified += UTF8("❤");
|
||||
//modified += UTF8("♥");
|
||||
}
|
||||
if (modules.quickmarks)
|
||||
modified += quickmarks.find(url.replace(/#.*/, "")).join("");
|
||||
|
||||
url = losslessDecodeURI(url);
|
||||
|
||||
// make it even more Vim-like
|
||||
if (url == "about:blank") {
|
||||
if (!buffer.title)
|
||||
url = "[No Name]";
|
||||
|
||||
@@ -39,11 +39,17 @@ var Tabs = Module("tabs", {
|
||||
xul|tab { -moz-binding: url(chrome://dactyl/content/bindings.xml#tab) !important; }
|
||||
]]></>, /tab-./g, function (m) util.OS.isMacOSX ? "tab-mac" : m),
|
||||
false, true);
|
||||
|
||||
this.timeout(function () {
|
||||
for (let { linkedBrowser: { contentDocument } } in values(this.allTabs))
|
||||
if (contentDocument.readyState === "complete")
|
||||
dactyl.initDocument(contentDocument);
|
||||
});
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
for (let [i, tab] in Iterator(this.allTabs)) {
|
||||
let node = function node(clas) document.getAnonymousElementByAttribute(tab, "class", clas);
|
||||
let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_);
|
||||
for (let elem in values(["dactyl-tab-icon-number", "dactyl-tab-number"].map(node)))
|
||||
if (elem)
|
||||
elem.parentNode.parentNode.removeChild(elem.parentNode);
|
||||
@@ -53,7 +59,7 @@ var Tabs = Module("tabs", {
|
||||
updateTabCount: function () {
|
||||
for (let [i, tab] in Iterator(this.visibleTabs)) {
|
||||
if (dactyl.has("Gecko2")) {
|
||||
let node = function node(clas) document.getAnonymousElementByAttribute(tab, "class", clas);
|
||||
let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_);
|
||||
if (!node("dactyl-tab-number")) {
|
||||
let img = node("tab-icon-image");
|
||||
if (img) {
|
||||
@@ -880,8 +886,8 @@ var Tabs = Module("tabs", {
|
||||
tabs.timeout(function () { this.updateTabCount(); });
|
||||
}
|
||||
for (let event in values(["TabMove", "TabOpen", "TabClose"]))
|
||||
events.addSessionListener(tabContainer, event, callback, false);
|
||||
events.addSessionListener(tabContainer, "TabSelect", tabs.closure._onTabSelect, false);
|
||||
events.listen(tabContainer, event, callback, false);
|
||||
events.listen(tabContainer, "TabSelect", tabs.closure._onTabSelect, false);
|
||||
},
|
||||
mappings: function () {
|
||||
mappings.add([modes.NORMAL], ["g0", "g^"],
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
interpreted as an Ex command.
|
||||
</p>
|
||||
|
||||
<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>pat</oa>.
|
||||
</p>
|
||||
|
||||
<note>
|
||||
This behavior differs from Vim's implementation in that
|
||||
<oa>pat</oa> is a regular expression rather than a glob.
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
<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>-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>
|
||||
@@ -198,7 +199,10 @@
|
||||
<spec>:tm<oa>ap</oa></spec>
|
||||
<spec>:cm<oa>ap</oa></spec>
|
||||
<description>
|
||||
<p>List all mappings for the applicable mode(s).</p>
|
||||
<p>
|
||||
List all mappings for the applicable mode(s). Mappings are
|
||||
partitioned into <t>groups</t>.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
@@ -561,9 +565,12 @@
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spec>:com<oa>mand</oa> <a>cmd</a></spec>
|
||||
<spec>:com<oa>mand</oa> <oa>cmd</oa></spec>
|
||||
<description>
|
||||
<p>List all user-defined commands that start with <a>cmd</a>.</p>
|
||||
<p>
|
||||
List all user-defined commands that start with <oa>cmd</oa>. Commands
|
||||
are partitioned into <t>groups</t>.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
@@ -598,11 +605,18 @@
|
||||
options when the command is defined.
|
||||
</p>
|
||||
|
||||
<h3 tag=":command-group">Grouping</h3>
|
||||
|
||||
<p>
|
||||
The <em>-group</em> flag (short name: <em>-g</em>) can be used to
|
||||
assign this command to a specific <t>group</t>.
|
||||
</p>
|
||||
|
||||
<h3 tag="E175 E176 :command-nargs">Argument handling</h3>
|
||||
|
||||
<p>
|
||||
By default, user commands accept no arguments. This can be changed by specifying
|
||||
the -nargs option.
|
||||
the <tt>-nargs</tt> option.
|
||||
</p>
|
||||
|
||||
<p>The valid values are:</p>
|
||||
|
||||
@@ -133,6 +133,99 @@
|
||||
</item>
|
||||
|
||||
|
||||
<h2 tag="group groups">Groups</h2>
|
||||
|
||||
<p>
|
||||
In order to facilitate script writing, especially scripts which only
|
||||
apply to certain web sites, many types of commands and mappings can
|
||||
be assigned to a named group. In addition to helping identify the
|
||||
source of such mappings in listings, and aiding in the cleanup of
|
||||
scripts, these groups can be configured to apply only to certain web
|
||||
sites.
|
||||
</p>
|
||||
|
||||
<item>
|
||||
<tags>:gr :group</tags>
|
||||
<spec>:group<oa>!</oa> <a>group</a> …</spec>
|
||||
<description>
|
||||
<p>List all active <t>groups</t>.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spec>:group<oa>!</oa> <a>group</a> …</spec>
|
||||
<description>
|
||||
<p>
|
||||
Select, create, or modify a <t>group</t>. After invocation,
|
||||
<a>group</a> becomes the default group for all further commands
|
||||
issued in the current script. If <oa>!</oa> is given the group is
|
||||
cleared of all mappings, commands, and any other entries bound to
|
||||
it.
|
||||
</p>
|
||||
|
||||
<p>The following <a>group</a> names have special meanings:</p>
|
||||
|
||||
<dl>
|
||||
<dt>builtin</dt> <dd>The default group for builtin items. Can not be modified in any way by scripts.</dd>
|
||||
<dt>default</dt> <dd>The default group for this script.</dd>
|
||||
<dt>user</dt> <dd>The default group for the command line and <t>&dactyl.name;rc</t>.</dd>
|
||||
</dl>
|
||||
|
||||
<p>The following arguments are available:</p>
|
||||
|
||||
<dl>
|
||||
<dt>-args=<a>javascript</a></dt> <dd>JavaScript Object which augments the arguments passed to commands, mappings, and autocommands (short name: <em>-a</em>)</dd>
|
||||
<dt>-description</dt> <dd>A description of this group (short names: <em>-desc</em>, <em>-d</em>)</dd>
|
||||
<dt>-locations=<a>filters</a></dt> <dd>The URLs for which this group should be active. See <t>site-filters</t> (short names: <em>-locs</em>, <em>-loc</em>, <em>-l</em>)</dd>
|
||||
<dt>-nopersist</dt> <dd>Do not save this group to an auto-generated RC file (short name: <em>-n</em>)</dd>
|
||||
</dl>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<h2 tag="site-filter site-filters">Site Filters</h2>
|
||||
|
||||
<p>
|
||||
Many &dactyl.appName; commands accept filters so that they may be applied
|
||||
only to specific sites. Most of these commands accept filters in any of the
|
||||
following formats:
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dd>domain</dd>
|
||||
<dt>
|
||||
Any filter which is a valid domain name will match any site on that
|
||||
domain or any sub-domain thereof. These filters may contain any letter
|
||||
of the Roman alphabet, Arabic numerals, hyphens, and full stops.
|
||||
Non-Latin domain names must be punycode encoded.
|
||||
</dt>
|
||||
|
||||
<dd>URL prefix</dd>
|
||||
<dt>
|
||||
Any URL beginning with a valid protocol name and ending with a
|
||||
<tt>*</tt> is treated as a URL prefix. It will match any URL which
|
||||
begins with the given filter sans the trailing asterisk.
|
||||
</dt>
|
||||
|
||||
<dd>Full URL</dd>
|
||||
<dt>
|
||||
Any URL beginning with a valid protocol name and not ending with an
|
||||
asterisk is treated as a full URL match. It will match any page which
|
||||
has a URL identical to the filter.
|
||||
</dt>
|
||||
|
||||
<dd>Regular expression</dd>
|
||||
<dt>
|
||||
Any filter which does not fall into one of the above categories is
|
||||
treated as a case-sensitive regular expression.
|
||||
</dt>
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
In most cases, any of the above may be prefixed with a <tt>!</tt> character
|
||||
to invert the sense of the match.
|
||||
</p>
|
||||
|
||||
<h2 tag="using-scripts">Using scripts</h2>
|
||||
|
||||
<item>
|
||||
@@ -157,6 +250,32 @@
|
||||
for more information.
|
||||
</p>
|
||||
|
||||
<h3 tag=":source-contexts">Script Contexts</h3>
|
||||
|
||||
<p>
|
||||
Each script executes in its own JavaScript context. This means that
|
||||
any global variable or function, including those defined by
|
||||
<ex>:javascript</ex> and the <tt>-javascript</tt> flag of
|
||||
<ex>:map</ex>, <ex>:command</ex>, and <ex>:autocmd</ex>,
|
||||
is directly available only within the current script. Outside of the
|
||||
current script, they can only be accessed as properties of the
|
||||
script's global object, which is stored in the <tt>plugins</tt>
|
||||
global under the script's full path.
|
||||
</p>
|
||||
|
||||
<h3 tag=":source-groups">Script Groups</h3>
|
||||
|
||||
<p>
|
||||
In addition to its own JavaScript context, each script is executed
|
||||
with its own default <link topic="groups">group</link> into which
|
||||
its styles, mappings, commands, and autocommands are placed. This
|
||||
means that commands such as <ex>:comclear!</ex> can be issued
|
||||
without fear of trampling other user-defined mappings. The command
|
||||
<ex>:group! default</ex> can be issued to clear all such items at
|
||||
once, and should be placed at the head of most scripts to prevent
|
||||
the accumulation of stale items when the script is re-sourced.
|
||||
</p>
|
||||
|
||||
<h3 tag=":source-css">Cascading Stylesheets</h3>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
Windows only. If this file exists, its contents
|
||||
are executed and <tt>$MY_&dactyl.idName;RC</tt> set to its path.
|
||||
</li>
|
||||
<li>
|
||||
<li tag="&dactyl.name;rc ">
|
||||
<em>~/.&dactyl.name;rc</em>
|
||||
<strut/>
|
||||
If this file exists, its contents are executed.
|
||||
|
||||
@@ -149,10 +149,11 @@
|
||||
<spec>:sty<oa>le</oa><oa>!</oa> <oa>-name=<a>name</a></oa> <oa>-append</oa> <a>filter</a> <oa>css</oa></spec>
|
||||
<description>
|
||||
<p>
|
||||
Add CSS styles to the browser or to web pages. <a>filter</a> is a comma-separated
|
||||
list of URLs to match. URLs ending with <em>*</em> are matched as prefixes, URLs not
|
||||
containing any <em>:</em> or <em>/</em> characters are matched as domains. <oa>css</oa> is a full
|
||||
CSS rule set (e.g., <tt>body { color: blue; }</tt>).
|
||||
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>).
|
||||
</p>
|
||||
|
||||
<p>The following options are available:</p>
|
||||
@@ -166,6 +167,11 @@
|
||||
applies to contents user interface widgets as well as normal
|
||||
elements. (short name <em>-A</em>)</dd>
|
||||
|
||||
<dt>-group=<a>group</a></dt>
|
||||
<dd>The <t>group</t> to which to add this style. Please note that
|
||||
this grouping is for semantic and cleanup purposes only. No
|
||||
additional site filtering is applied.</dd>
|
||||
|
||||
<dt>-name=<a>name</a></dt>
|
||||
<dd>If provided, any existing style with the same name is
|
||||
overridden, and the style may later be deleted using
|
||||
|
||||
@@ -437,17 +437,16 @@ var Addons = Module("addons", {
|
||||
context.completions = types.map(function (t) [t, util.capitalize(t)]);
|
||||
}
|
||||
|
||||
if (AddonManager.getAllAddons)
|
||||
context.incomplete = true;
|
||||
|
||||
context.generate = function generate() {
|
||||
update(base);
|
||||
if (AddonManager.getAllAddons)
|
||||
if (AddonManager.getAllAddons) {
|
||||
context.incomplete = true;
|
||||
AddonManager.getAllAddons(function (addons) {
|
||||
context.incomplete = false;
|
||||
update(array.uniq(base.concat(addons.map(function (a) a.type)),
|
||||
true));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ if (!Object.defineProperties)
|
||||
for (let [k, v] in Iterator(props))
|
||||
Object.defineProperty(obj, k, v);
|
||||
}
|
||||
if (!Object.freeze)
|
||||
Object.freeze = function freeze(obj) {};
|
||||
if (!Object.getOwnPropertyDescriptor)
|
||||
Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
|
||||
if (!hasOwnProperty.call(obj, prop))
|
||||
@@ -101,13 +103,15 @@ if (!Object.keys)
|
||||
Object.keys = function keys(obj)
|
||||
Object.getOwnPropertyNames(obj).filter(function (k) objproto.propertyIsEnumerable.call(obj, k));
|
||||
|
||||
let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
|
||||
|
||||
let use = {};
|
||||
let loaded = {};
|
||||
let currentModule;
|
||||
let global = this;
|
||||
function defineModule(name, params, module) {
|
||||
if (!module)
|
||||
module = Cu.getGlobalForObject ? Cu.getGlobalForObject(params) : params.__parent__;
|
||||
module = getGlobalForObject(params);
|
||||
|
||||
module.NAME = name;
|
||||
module.EXPORTED_SYMBOLS = params.exports || [];
|
||||
@@ -436,25 +440,24 @@ function isinstance(object, interfaces) {
|
||||
if (object == null)
|
||||
return false;
|
||||
|
||||
interfaces = Array.concat(interfaces);
|
||||
for (var i = 0; i < interfaces.length; i++) {
|
||||
if (typeof interfaces[i] === "string") {
|
||||
if (objproto.toString.call(object) === "[object " + interfaces[i] + "]")
|
||||
return Array.concat(interfaces).some(function isinstance_some(iface) {
|
||||
if (typeof iface === "string") {
|
||||
if (objproto.toString.call(object) === "[object " + iface + "]")
|
||||
return true;
|
||||
}
|
||||
else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
|
||||
if (object.isinstance(interfaces[i]))
|
||||
if (object.isinstance(iface))
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (object instanceof interfaces[i])
|
||||
if (object instanceof iface)
|
||||
return true;
|
||||
var type = isinstance_types[typeof object];
|
||||
if (type && isSubclass(interfaces[i], type))
|
||||
if (type && isSubclass(iface, type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -978,7 +981,8 @@ let StructBase = Class("StructBase", Array, {
|
||||
}
|
||||
}, {
|
||||
fromArray: function (ary) {
|
||||
ary.__proto__ = this.prototype;
|
||||
if (!(ary instanceof this))
|
||||
ary.__proto__ = this.prototype;
|
||||
return ary;
|
||||
},
|
||||
|
||||
@@ -1013,7 +1017,7 @@ var Timer = Class("Timer", {
|
||||
|
||||
notify: function (timer, force) {
|
||||
try {
|
||||
if (loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
|
||||
if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
|
||||
return;
|
||||
|
||||
this._timer.cancel();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,11 @@ var CompletionContext = Class("CompletionContext", {
|
||||
let parent = editor;
|
||||
name = parent.name + "/" + name;
|
||||
|
||||
this.autoComplete = this.options.get("autocomplete").getKey(name);
|
||||
this.sortResults = this.options.get("wildsort").getKey(name);
|
||||
this.wildcase = this.options.get("wildcase").getKey(name);
|
||||
if (this.options) {
|
||||
this.autoComplete = this.options.get("autocomplete").getKey(name);
|
||||
this.sortResults = this.options.get("wildsort").getKey(name);
|
||||
this.wildcase = this.options.get("wildcase").getKey(name);
|
||||
}
|
||||
|
||||
this.contexts = parent.contexts;
|
||||
if (name in this.contexts)
|
||||
@@ -448,7 +450,7 @@ var CompletionContext = Class("CompletionContext", {
|
||||
let self = this;
|
||||
delete this._substrings;
|
||||
|
||||
if (!this.forceAnchored)
|
||||
if (!this.forceAnchored && this.options)
|
||||
this.anchored = this.options.get("wildanchor").getKey(this.name, this.anchored);
|
||||
|
||||
// Item matchers
|
||||
|
||||
@@ -31,7 +31,7 @@ var ConfigBase = Class("ConfigBase", {
|
||||
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, /'/g, "'"), "'>"].join(""))
|
||||
.map(function ([k, v]) ["<!ENTITY ", k, " '", String.replace(v || "null", /'/g, "'"), "'>"].join(""))
|
||||
.join("\n")]
|
||||
});
|
||||
},
|
||||
@@ -115,6 +115,23 @@ var ConfigBase = Class("ConfigBase", {
|
||||
.nth(function (l) set.has(langs, l), 0);
|
||||
},
|
||||
|
||||
haveHg: 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 ["hg", "-R", uri.file.parent.path];
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
|
||||
branch: Class.memoize(function () {
|
||||
if (this.haveHg)
|
||||
return io.system(this.haveHg.concat(["branch"])).output;
|
||||
return (/pre-hg\d+-(.*)$/.exec(this.version) || [])[1];
|
||||
}),
|
||||
|
||||
/** @property {string} The Dactyl version string. */
|
||||
version: Class.memoize(function () {
|
||||
if (/pre$/.test(this.addon.version)) {
|
||||
@@ -124,11 +141,11 @@ var ConfigBase = Class("ConfigBase", {
|
||||
io.pathSearch("hg")) {
|
||||
return io.system(["hg", "-R", uri.file.parent.path,
|
||||
"log", "-r.",
|
||||
"--template=hg{rev} ({date|isodate})"]).output;
|
||||
"--template=hg{rev}-" + this.branch + " ({date|isodate})"]).output;
|
||||
}
|
||||
}
|
||||
let version = this.addon.version;
|
||||
if ("@DATE" !== "@" + "DATE@")
|
||||
if ("@DATE@" !== "@" + "DATE@")
|
||||
version += " (created: @DATE@)";
|
||||
return version;
|
||||
}),
|
||||
@@ -723,6 +740,15 @@ config.INIT = update(Object.create(config.INIT), config.INIT, {
|
||||
{"}"}</>);
|
||||
img = null;
|
||||
};
|
||||
},
|
||||
|
||||
load: function load(dactyl, modules, window) {
|
||||
load.superapply(this, arguments);
|
||||
|
||||
if (this.branch && this.branch !== "default" &&
|
||||
modules.yes_i_know_i_should_not_report_errors_in_these_branches_thanks.indexOf(this.branch) === -1)
|
||||
dactyl.warn("You are running " + config.appName + " from a testing branch: " + this.branch + ". " +
|
||||
"Please do not report errors which do not also occur in the default branch.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
620
common/modules/contexts.jsm
Normal file
620
common/modules/contexts.jsm
Normal file
@@ -0,0 +1,620 @@
|
||||
// Copyright (c) 2010-2011 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
try {
|
||||
|
||||
Components.utils.import("resource://dactyl/bootstrap.jsm");
|
||||
defineModule("contexts", {
|
||||
exports: ["Contexts", "Group", "contexts"],
|
||||
use: ["commands", "options", "services", "storage", "styles", "template", "util"]
|
||||
}, this);
|
||||
|
||||
var Group = Class("Group", {
|
||||
init: function init(name, description, filter, persist) {
|
||||
const self = this;
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.filter = filter || this.constructor.defaultFilter;
|
||||
this.persist = persist || false;
|
||||
this.hives = [];
|
||||
},
|
||||
|
||||
modifiable: true,
|
||||
|
||||
cleanup: function cleanup() {
|
||||
for (let hive in values(this.hives))
|
||||
util.trapErrors("cleanup", hive);
|
||||
|
||||
this.hives = [];
|
||||
for (let hive in keys(this.hiveMap))
|
||||
delete this[hive];
|
||||
},
|
||||
destroy: function destroy() {
|
||||
for (let hive in values(this.hives))
|
||||
util.trapErrors("destroy", hive);
|
||||
},
|
||||
|
||||
argsExtra: function argsExtra() ({}),
|
||||
|
||||
get toStringParams() [this.name],
|
||||
|
||||
get builtin() this.modules.contexts.builtinGroups.indexOf(this) >= 0,
|
||||
|
||||
}, {
|
||||
compileFilter: function (patterns) {
|
||||
|
||||
function siteFilter(uri) siteFilter.filters.every(function (f) f(uri) == f.result);
|
||||
|
||||
if (!isArray(patterns))
|
||||
patterns = Option.splitList(patterns, true);
|
||||
|
||||
return update(siteFilter, {
|
||||
toString: function () this.filters.join(","),
|
||||
|
||||
toXML: function (modules) let (uri = modules && modules.buffer.uri)
|
||||
template.map(this.filters,
|
||||
function (f) <span highlight={uri && f(uri) ? "Filter" : ""}>{f}</span>,
|
||||
<>,</>),
|
||||
|
||||
filters: patterns.map(function (pattern) {
|
||||
let [, res, filter] = /^(!?)(.*)/.exec(pattern);
|
||||
|
||||
return update(Styles.matchFilter(Option.dequote(filter)), {
|
||||
result: !res,
|
||||
toString: function () pattern
|
||||
});
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
defaultFilter: Class.memoize(function () this.compileFilter(["*"]))
|
||||
});
|
||||
|
||||
var Contexts = Module("contexts", {
|
||||
Local: function Local(dactyl, modules, window) ({
|
||||
init: function () {
|
||||
const contexts = this;
|
||||
this.modules = modules;
|
||||
|
||||
modules.plugins.contexts = {};
|
||||
|
||||
this.groupList = [];
|
||||
this.groupMap = {};
|
||||
this.groupsProto = {};
|
||||
this.hives = {};
|
||||
this.hiveProto = {};
|
||||
|
||||
this.builtin = this.addGroup("builtin", "Builtin items");
|
||||
this.user = this.addGroup("user", "User-defined items", null, true);
|
||||
this.builtinGroups = [this.builtin, this.user];
|
||||
this.builtin.modifiable = false;
|
||||
|
||||
this.GroupFlag = Class("GroupFlag", CommandOption, {
|
||||
init: function (name) {
|
||||
this.name = name;
|
||||
|
||||
this.type = ArgType("group", function (group) {
|
||||
return isString(group) ? contexts.getGroup(group, name)
|
||||
: group[name];
|
||||
});
|
||||
},
|
||||
|
||||
get toStringParams() [this.name],
|
||||
|
||||
names: ["-group", "-g"],
|
||||
|
||||
description: "Group to which to add",
|
||||
|
||||
get default() (contexts.context && contexts.context.group || contexts.user)[this.name],
|
||||
|
||||
completer: function (context) modules.completion.group(context)
|
||||
});
|
||||
},
|
||||
|
||||
cleanup: function () {
|
||||
for (let hive in values(this.groupList))
|
||||
util.trapErrors("cleanup", hive);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
for (let hive in values(this.groupList))
|
||||
util.trapErrors("destroy", hive);
|
||||
|
||||
for (let [name, plugin] in iter(this.modules.plugins.contexts))
|
||||
if (plugin && "onUnload" in plugin)
|
||||
util.trapErrors("onUnload", plugin);
|
||||
},
|
||||
|
||||
Group: Class("Group", Group, { modules: modules, get hiveMap() modules.contexts.hives }),
|
||||
|
||||
Hives: Class("Hives", Class.Property, {
|
||||
init: function init(name, constructor) {
|
||||
const { contexts } = modules;
|
||||
const self = this;
|
||||
|
||||
if (this.Hive)
|
||||
return {
|
||||
enumerable: true,
|
||||
|
||||
get: function () array(contexts.groups[self.name])
|
||||
};
|
||||
|
||||
|
||||
this.Hive = constructor;
|
||||
this.name = name;
|
||||
memoize(contexts.Group.prototype, name, function () {
|
||||
let group = constructor(this);
|
||||
this.hives.push(group);
|
||||
delete contexts.groups;
|
||||
return group;
|
||||
});
|
||||
|
||||
memoize(contexts.hives, name,
|
||||
function () Object.create(Object.create(contexts.hiveProto,
|
||||
{ _hive: { value: name } })));
|
||||
|
||||
memoize(contexts.groupsProto, name,
|
||||
function () [group[name] for (group in values(this.groups)) if (set.has(group, name))]);
|
||||
},
|
||||
|
||||
get toStringParams() [this.name, this.Hive]
|
||||
})
|
||||
}),
|
||||
|
||||
Context: function Context(file, group, args) {
|
||||
const { contexts, io, newContext, plugins, userContext } = this.modules;
|
||||
|
||||
function Const(val) Class.Property({ enumerable: true, value: val });
|
||||
|
||||
let isPlugin = array.nth(io.getRuntimeDirectories("plugins"),
|
||||
function (dir) dir.contains(file, true),
|
||||
0);
|
||||
let isRuntime = array.nth(io.getRuntimeDirectories(""),
|
||||
function (dir) dir.contains(file, true),
|
||||
0);
|
||||
|
||||
let self = set.has(plugins, file.path) && plugins[file.path];
|
||||
if (self) {
|
||||
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]), {
|
||||
NAME: Const(name.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase())),
|
||||
|
||||
PATH: Const(file.path),
|
||||
|
||||
CONTEXT: Const(self),
|
||||
|
||||
unload: Const(function unload() {
|
||||
if (plugins[this.NAME] === this || plugins[this.PATH] === this)
|
||||
if (this.onUnload)
|
||||
this.onUnload();
|
||||
|
||||
if (plugins[this.NAME] === this)
|
||||
delete plugins[this.NAME];
|
||||
|
||||
if (plugins[this.PATH] === this)
|
||||
delete plugins[this.PATH];
|
||||
|
||||
if (!this.GROUP.builtin)
|
||||
contexts.removeGroup(this.GROUP);
|
||||
})
|
||||
});
|
||||
Class.replaceProperty(plugins, file.path, self);
|
||||
|
||||
// This belongs elsewhere
|
||||
if (isPlugin && args)
|
||||
Object.defineProperty(plugins, self.NAME, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: self
|
||||
});
|
||||
}
|
||||
|
||||
let path = isRuntime ? file.getRelativeDescriptor(isRuntime) : file.path;
|
||||
let name = isRuntime ? path.replace(/^(plugin|color)s([\\\/])/, "$1$2") : "script-" + path;
|
||||
|
||||
if (!group)
|
||||
group = this.addGroup(commands.nameRegexp
|
||||
.iterate(name.replace(/\.[^.]*$/, ""))
|
||||
.join("-"),
|
||||
"Script group for " + file.path,
|
||||
null, false);
|
||||
|
||||
Class.replaceProperty(self, "GROUP", group);
|
||||
Class.replaceProperty(self, "group", group);
|
||||
|
||||
return plugins.contexts[file.path] = self;
|
||||
},
|
||||
|
||||
Script: function Script(file, group) {
|
||||
return this.Context(file, group, [this.modules.plugins, true]);
|
||||
},
|
||||
|
||||
context: null,
|
||||
|
||||
/**
|
||||
* Returns a frame object describing the currently executing
|
||||
* command, if applicable, otherwise returns the passed frame.
|
||||
*
|
||||
* @param {nsIStackFrame} frame
|
||||
*/
|
||||
getCaller: function getCaller(frame) {
|
||||
if (this.context && this.context.file)
|
||||
return {
|
||||
__proto__: frame,
|
||||
filename: this.context.file[0] == "[" ? this.context.file
|
||||
: services.io.newFileURI(File(this.context.file)).spec,
|
||||
lineNumber: this.context.line
|
||||
};
|
||||
return frame;
|
||||
},
|
||||
|
||||
groups: Class.memoize(function () Object.create(this.groupsProto, {
|
||||
groups: { value: this.activeGroups() },
|
||||
})),
|
||||
|
||||
allGroups: Class.memoize(function () Object.create(this.groupsProto, {
|
||||
groups: { value: this.initializedGroups() }
|
||||
})),
|
||||
|
||||
activeGroups: function (hive) this.initializedGroups().filter(function (g) g.filter(this), this.modules.buffer.uri),
|
||||
|
||||
initializedGroups: function (hive)
|
||||
let (need = hive ? [hive] : Object.keys(this.hives))
|
||||
this.groupList.filter(function (group) need.some(function (name) set.has(group, name))),
|
||||
|
||||
addGroup: function addGroup(name, description, filter, persist, replace) {
|
||||
let group = this.getGroup(name);
|
||||
if (group)
|
||||
name = group.name;
|
||||
|
||||
if (!group) {
|
||||
group = this.Group(name, description, filter, persist);
|
||||
this.groupList.unshift(group);
|
||||
this.groupMap[name] = group;
|
||||
this.hiveProto.__defineGetter__(name, function () group[this._hive]);
|
||||
}
|
||||
|
||||
if (replace) {
|
||||
util.trapErrors("cleanup", group);
|
||||
if (description)
|
||||
group.description = description;
|
||||
if (filter)
|
||||
group.filter = filter
|
||||
group.persist = persist;
|
||||
}
|
||||
|
||||
delete this.groups;
|
||||
return group;
|
||||
},
|
||||
|
||||
removeGroup: function removeGroup(name, filter) {
|
||||
if (isObject(name)) {
|
||||
if (this.groupList.indexOf(name) === -1)
|
||||
return;
|
||||
name = name.name;
|
||||
}
|
||||
|
||||
let group = this.getGroup(name);
|
||||
|
||||
util.assert(!group || !group.builtin, "Cannot remove builtin group");
|
||||
|
||||
if (group) {
|
||||
name = group.name;
|
||||
this.groupList.splice(this.groupList.indexOf(group), 1);
|
||||
util.trapErrors("destroy", group);
|
||||
}
|
||||
|
||||
if (this.context && this.context.group === group)
|
||||
this.context.group = null;
|
||||
|
||||
delete this.groupMap[name];
|
||||
delete this.hiveProto[name];
|
||||
delete this.groups;
|
||||
return group;
|
||||
},
|
||||
|
||||
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))
|
||||
group = this.groupMap[name];
|
||||
|
||||
if (group && hive)
|
||||
return group[hive];
|
||||
return group;
|
||||
},
|
||||
|
||||
bindMacro: function (args, default_, params) {
|
||||
const { dactyl, events } = this.modules;
|
||||
|
||||
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))));
|
||||
else if (params)
|
||||
makeParams = function makeParams(self, args)
|
||||
iter.toObject([name, process(args[i])]
|
||||
for ([i, name] in Iterator(params)));
|
||||
|
||||
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;
|
||||
/* fallthrough */
|
||||
case "-keys":
|
||||
let silent = args["-silent"];
|
||||
rhs = events.canonicalKeys(rhs, 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() this.modules.commands
|
||||
.execute(action.macro, makeParams(this, arguments),
|
||||
false, null, action.context);
|
||||
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 + "} })");
|
||||
else
|
||||
action = dactyl.userFunc.apply(dactyl, params.concat(args.literalArg).array);
|
||||
process = function (param) isObject(param) && param.valueOf ? param.valueOf() : param;
|
||||
action.makeParams = makeParams;
|
||||
break;
|
||||
}
|
||||
action.toString = function toString() (type === default_ ? "" : type + " ") + rhs;
|
||||
args = null;
|
||||
return action;
|
||||
},
|
||||
|
||||
withContext: function withContext(defaults, callback, self)
|
||||
this.withSavedValues(["context"], function () {
|
||||
this.context = defaults && update({}, defaults);
|
||||
return callback.call(self, this.context);
|
||||
})
|
||||
}, {
|
||||
Hive: Class("Hive", {
|
||||
init: function init(group) {
|
||||
this.group = group;
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {},
|
||||
destroy: function destroy() {},
|
||||
|
||||
get modifiable() this.group.modifiable,
|
||||
|
||||
get argsExtra() this.group.argsExtra,
|
||||
get builtin() this.group.builtin,
|
||||
|
||||
get name() this.group.name,
|
||||
set name(val) this.group.name = val,
|
||||
|
||||
get description() this.group.description,
|
||||
set description(val) this.group.description = val,
|
||||
|
||||
get filter() this.group.filter,
|
||||
set filter(val) this.group.filter = val,
|
||||
|
||||
get persist() this.group.persist,
|
||||
set persist(val) this.group.persist = val,
|
||||
|
||||
get toStringParams() [this.name]
|
||||
})
|
||||
}, {
|
||||
commands: function initCommands(dactyl, modules, window) {
|
||||
const { commands, contexts } = modules;
|
||||
|
||||
commands.add(["gr[oup]"],
|
||||
"Create or select a group",
|
||||
function (args) {
|
||||
if (args.length > 0) {
|
||||
var name = Option.dequote(args[0]);
|
||||
util.assert(name !== "builtin", "Cannot modify builtin group");
|
||||
util.assert(commands.validName.test(name), "Invalid group name");
|
||||
|
||||
var group = contexts.getGroup(name);
|
||||
}
|
||||
else if (args.bang)
|
||||
var group = args.context && args.context.group;
|
||||
else
|
||||
return void modules.completion.listCompleter("group", "", null, null);
|
||||
|
||||
util.assert(group || name, "No current group");
|
||||
|
||||
let filter = Group.compileFilter(args["-locations"]);
|
||||
if (!group || args.bang)
|
||||
group = contexts.addGroup(name, args["-description"], filter, !args["-nopersist"], args.bang);
|
||||
else if (!group.builtin) {
|
||||
if (args.has("-locations"))
|
||||
group.filter = filter;
|
||||
if (args.has("-description"))
|
||||
group.description = args["-description"]
|
||||
if (args.has("-nopersist"))
|
||||
group.persist = !args["-nopersist"]
|
||||
}
|
||||
|
||||
if (!group.builtin && args.has("-args")) {
|
||||
group.argsExtra = contexts.bindMacro({ literalArg: "return " + args["-args"] },
|
||||
"-javascript", util.identity);
|
||||
group.args = args["-args"];
|
||||
}
|
||||
|
||||
if (args.context)
|
||||
args.context.group = group;
|
||||
},
|
||||
{
|
||||
argCount: "?",
|
||||
bang: true,
|
||||
completer: function (context, args) {
|
||||
if (args.length == 1)
|
||||
modules.completion.group(context);
|
||||
},
|
||||
keepQuotes: true,
|
||||
options: [
|
||||
{
|
||||
names: ["-args", "-a"],
|
||||
description: "JavaScript Object which augments the arguments passed to commands, mappings, and autocommands",
|
||||
type: CommandOption.STRING
|
||||
},
|
||||
{
|
||||
names: ["-description", "-desc", "-d"],
|
||||
description: "A description of this group",
|
||||
default: ["User-defined group"],
|
||||
type: CommandOption.STRING
|
||||
},
|
||||
{
|
||||
names: ["-locations", "-locs", "-loc", "-l"],
|
||||
description: ["The URLs for which this group should be active"],
|
||||
default: ["*"],
|
||||
type: CommandOption.LIST
|
||||
},
|
||||
{
|
||||
names: ["-nopersist", "-n"],
|
||||
description: "Do not save this group to an auto-generated RC file"
|
||||
}
|
||||
],
|
||||
serialGroup: 20,
|
||||
serialize: function () [
|
||||
{
|
||||
command: this.name,
|
||||
bang: true,
|
||||
options: iter([v, typeof group[k] == "boolean" ? null : group[k]]
|
||||
// FIXME: this map is expressed multiple times
|
||||
for ([k, v] in Iterator({
|
||||
args: "-args",
|
||||
description: "-description",
|
||||
filter: "-locations"
|
||||
}))
|
||||
if (group[k])).toObject(),
|
||||
arguments: [group.name],
|
||||
ignoreDefaults: true
|
||||
}
|
||||
for (group in values(contexts.initializedGroups()))
|
||||
if (!group.builtin && group.persist)
|
||||
].concat([{ command: this.name, arguments: ["user"] }])
|
||||
});
|
||||
|
||||
commands.add(["delg[roup]"],
|
||||
"Delete a group",
|
||||
function (args) {
|
||||
util.assert(contexts.getGroup(args[0]), "No such group: " + args[0]);
|
||||
contexts.removeGroup(args[0]);
|
||||
},
|
||||
{
|
||||
argCount: "1",
|
||||
completer: function (context, args) {
|
||||
modules.completion.group(context);
|
||||
context.filters.push(function ({ item }) !item.builtin);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
commands.add(["fini[sh]"],
|
||||
"Stop sourcing a script file",
|
||||
function (args) {
|
||||
util.assert(args.context, "E168: :finish used outside of a sourced file");
|
||||
args.context.finished = true;
|
||||
},
|
||||
{ argCount: "0" });
|
||||
|
||||
|
||||
function checkStack(cmd) {
|
||||
util.assert(contexts.context && contexts.context.stack &&
|
||||
contexts.context.stack[cmd] && contexts.context.stack[cmd].length,
|
||||
"Invalid use of conditional");
|
||||
}
|
||||
function pop(cmd) {
|
||||
checkStack(cmd);
|
||||
return contexts.context.stack[cmd].pop();
|
||||
}
|
||||
function push(cmd, value) {
|
||||
util.assert(contexts.context, "Invalid use of conditional");
|
||||
if (arguments.length < 2)
|
||||
value = contexts.context.noExecute;
|
||||
contexts.context.stack = contexts.context.stack || {};
|
||||
contexts.context.stack[cmd] = (contexts.context.stack[cmd] || []).concat([value]);
|
||||
}
|
||||
|
||||
commands.add(["if"],
|
||||
"Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
|
||||
function (args) { args.context.noExecute = !dactyl.userEval(args[0]); },
|
||||
{
|
||||
always: function (args) { push("if"); },
|
||||
argCount: "1",
|
||||
literal: 0
|
||||
});
|
||||
commands.add(["elsei[f]", "elif"],
|
||||
"Execute commands until the next :elseif, :else, or :endif only if the argument returns true",
|
||||
function (args) {},
|
||||
{
|
||||
always: function (args) {
|
||||
checkStack("if");
|
||||
args.context.noExecute = args.context.stack.if.slice(-1)[0] ||
|
||||
!args.context.noExecute || !dactyl.userEval(args[0]);
|
||||
},
|
||||
argCount: "1",
|
||||
literal: 0
|
||||
});
|
||||
commands.add(["el[se]"],
|
||||
"Execute commands until the next :endif only if the previous conditionals were not executed",
|
||||
function (args) {},
|
||||
{
|
||||
always: function (args) {
|
||||
checkStack("if");
|
||||
args.context.noExecute = args.context.stack.if.slice(-1)[0] ||
|
||||
!args.context.noExecute;
|
||||
},
|
||||
argCount: "0"
|
||||
});
|
||||
commands.add(["en[dif]", "fi"],
|
||||
"End a string of :if/:elseif/:else conditionals",
|
||||
function (args) {},
|
||||
{
|
||||
always: function (args) { args.context.noExecute = pop("if"); },
|
||||
argCount: "0"
|
||||
});
|
||||
},
|
||||
completion: function initCompletion(dactyl, modules, window) {
|
||||
const { completion, contexts } = modules;
|
||||
|
||||
completion.group = function group(context, active) {
|
||||
context.title = ["Group"];
|
||||
let uri = modules.buffer.uri;
|
||||
context.keys = {
|
||||
active: function (group) group.filter(uri),
|
||||
text: "name",
|
||||
description: function (g) <>{g.filter.toXML ? g.filter.toXML(modules) + <> </> : ""}{g.description || ""}</>
|
||||
};
|
||||
context.completions = (active === undefined ? contexts.groupList : contexts.initializedGroups(active))
|
||||
.slice(0, -1);
|
||||
|
||||
iter({ Active: true, Inactive: false }).forEach(function ([name, active]) {
|
||||
context.split(name, null, function (context) {
|
||||
context.title[0] = name + " Groups";
|
||||
context.filters.push(function (item) item.active == active);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
endModule();
|
||||
|
||||
} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||
@@ -106,9 +106,13 @@ var Download = Class("Download", {
|
||||
|
||||
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]) ",
|
||||
this.list.modules.commandline.input("This will launch an executable download. Continue? (yes/[no]/always) ",
|
||||
function (resp) {
|
||||
if (resp && resp.match(/^y(es)?$/i))
|
||||
if (/^a(lways)$/i.test(resp)) {
|
||||
prefs.set("browser.download.manager.alertOnEXEOpen", false);
|
||||
resp = "yes";
|
||||
}
|
||||
if (/^y(es)?$/i.test(resp))
|
||||
action.call(self);
|
||||
});
|
||||
else
|
||||
@@ -144,10 +148,7 @@ var Download = Class("Download", {
|
||||
|
||||
updateStatus: function updateStatus() {
|
||||
|
||||
if (this.alive)
|
||||
this.nodes.row.setAttribute("active", "true");
|
||||
else
|
||||
this.nodes.row.removeAttribute("active");
|
||||
this.nodes.row[this.alive ? "setAttribute" : "removeAttribute"]("active", "true");
|
||||
|
||||
this.nodes.row.setAttribute("status", this.status);
|
||||
this.nodes.state.textContent = util.capitalize(this.status);
|
||||
|
||||
@@ -196,7 +196,7 @@ var RangeFinder = Module("rangefinder", {
|
||||
|
||||
get prompt() this.mode === modules.modes.FIND_BACKWARD ? "?" : "/",
|
||||
|
||||
get onCancel() modules.rangefinder.closure.onCancel,
|
||||
get onCancel() rangefinder.closure.onCancel,
|
||||
get onChange() modules.rangefinder.closure.onChange,
|
||||
get onSubmit() modules.rangefinder.closure.onSubmit
|
||||
});
|
||||
|
||||
@@ -23,8 +23,7 @@ Highlight.liveProperty = function (name, prop) {
|
||||
val = Array.slice(val);
|
||||
else
|
||||
val = update({}, val);
|
||||
if (Object.freeze)
|
||||
Object.freeze(val);
|
||||
Object.freeze(val);
|
||||
}
|
||||
this.set(name, val);
|
||||
|
||||
@@ -82,7 +81,7 @@ update(Highlight.prototype, {
|
||||
get cssText() this.inheritedCSS + this.value,
|
||||
|
||||
toString: function () "Highlight(" + this.class + ")\n\t" +
|
||||
[k + ": " + String.quote(v) for ([k, v] in this)] .join("\n\t")
|
||||
[k + ": " + String(v).quote() for ([k, v] in this)] .join("\n\t")
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -291,16 +290,21 @@ var Highlights = Module("Highlight", {
|
||||
}, {
|
||||
commands: function (dactyl, modules) {
|
||||
const { autocommands, commands, completion, CommandOption, config, io } = modules;
|
||||
|
||||
let lastScheme;
|
||||
commands.add(["colo[rscheme]"],
|
||||
"Load a color scheme",
|
||||
function (args) {
|
||||
let scheme = args[0];
|
||||
if (lastScheme)
|
||||
lastScheme.unload();
|
||||
|
||||
if (scheme == "default")
|
||||
highlight.clear();
|
||||
else
|
||||
dactyl.assert(modules.io.sourceFromRuntimePath(["colors/" + scheme + "." + config.fileExtension]),
|
||||
"E185: Cannot find color scheme " + scheme);
|
||||
else {
|
||||
lastScheme = modules.io.sourceFromRuntimePath(["colors/" + scheme + "." + config.fileExtension]);
|
||||
dactyl.assert(lastScheme, "E185: Cannot find color scheme " + scheme);
|
||||
}
|
||||
autocommands.trigger("ColorScheme", { name: scheme });
|
||||
},
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ var IO = Module("io", {
|
||||
this.config = config;
|
||||
},
|
||||
|
||||
Local: function (dactyl, modules, window) let ({ Script, io, plugins } = modules) ({
|
||||
Local: function (dactyl, modules, window) let ({ io, plugins } = modules) ({
|
||||
|
||||
init: function init() {
|
||||
this.config = modules.config;
|
||||
@@ -84,9 +84,6 @@ var IO = Module("io", {
|
||||
|
||||
destroy: function destroy() {
|
||||
services.downloadManager.removeListener(this.downloadListener);
|
||||
for (let [, plugin] in Iterator(plugins.contexts))
|
||||
if (plugin.onUnload)
|
||||
plugin.onUnload();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -113,7 +110,7 @@ var IO = Module("io", {
|
||||
*/
|
||||
sourceFromRuntimePath: function sourceFromRuntimePath(paths, all) {
|
||||
let dirs = modules.options.get("runtimepath").files;
|
||||
let found = false;
|
||||
let found = null;
|
||||
|
||||
dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + modules.options.get("runtimepath").stringValue, 2);
|
||||
|
||||
@@ -125,8 +122,7 @@ var IO = Module("io", {
|
||||
dactyl.echomsg("Searching for " + file.path.quote(), 3);
|
||||
|
||||
if (file.exists() && file.isFile() && file.isReadable()) {
|
||||
io.source(file.path, false);
|
||||
found = true;
|
||||
found = io.source(file.path, false) || true;
|
||||
|
||||
if (!all)
|
||||
break outer;
|
||||
@@ -144,18 +140,22 @@ var IO = Module("io", {
|
||||
* Reads Ex commands, JavaScript or CSS from *filename*.
|
||||
*
|
||||
* @param {string} filename The name of the file to source.
|
||||
* @param {boolean} silent Whether errors should be reported.
|
||||
* @param {object} params Extra parameters:
|
||||
* group: The group in which to execute commands.
|
||||
* silent: Whether errors should not be reported.
|
||||
*/
|
||||
source: function source(filename, silent) {
|
||||
source: function source(filename, params) {
|
||||
const { contexts } = modules;
|
||||
defineModule.loadLog.push("sourcing " + filename);
|
||||
params = params || {};
|
||||
|
||||
let time = Date.now();
|
||||
this.withSavedValues(["sourcing"], function _source() {
|
||||
this.sourcing = null;
|
||||
return contexts.withContext(null, function () {
|
||||
try {
|
||||
var file = util.getFile(filename) || io.File(filename);
|
||||
|
||||
if (!file.exists() || !file.isReadable() || file.isDirectory()) {
|
||||
if (!silent)
|
||||
if (!params.silent)
|
||||
dactyl.echoerr("E484: Can't open file " + filename.quote());
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +167,8 @@ var IO = Module("io", {
|
||||
// handle pure JavaScript files specially
|
||||
if (/\.js$/.test(filename)) {
|
||||
try {
|
||||
dactyl.loadScript(uri.spec, Script(file));
|
||||
var context = contexts.Script(file, params.group);
|
||||
dactyl.loadScript(uri.spec, context);
|
||||
dactyl.helpInitialized = false;
|
||||
}
|
||||
catch (e) {
|
||||
@@ -185,10 +186,14 @@ var IO = Module("io", {
|
||||
else if (/\.css$/.test(filename))
|
||||
styles.registerSheet(uri.spec, false, true);
|
||||
else {
|
||||
if (!(file.path in plugins))
|
||||
plugins[file.path] = modules.newContext(modules.userContext);
|
||||
modules.commands.execute(file.read(), null, silent || "loud", null,
|
||||
{ file: file.path, line: 1, context: plugins[file.path] });
|
||||
context = contexts.Context(file, params.group);
|
||||
modules.commands.execute(file.read(), null, params.silent,
|
||||
null, {
|
||||
context: context,
|
||||
file: file.path,
|
||||
group: context.GROUP,
|
||||
line: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (this._scriptNames.indexOf(file.path) == -1)
|
||||
@@ -197,18 +202,19 @@ var IO = Module("io", {
|
||||
dactyl.echomsg("finished sourcing " + filename.quote(), 2);
|
||||
|
||||
dactyl.log("Sourced: " + filename, 3);
|
||||
return context;
|
||||
}
|
||||
catch (e) {
|
||||
if (!(e instanceof FailedAssertion))
|
||||
dactyl.reportError(e);
|
||||
let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
|
||||
if (!silent)
|
||||
if (!params.silent)
|
||||
dactyl.echoerr(message);
|
||||
}
|
||||
finally {
|
||||
defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms");
|
||||
}
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -547,35 +553,6 @@ var IO = Module("io", {
|
||||
*/
|
||||
PATH_SEP: deprecated("File.PATH_SEP", { get: function PATH_SEP() File.PATH_SEP })
|
||||
}, {
|
||||
init: function init(dactyl, modules, window) {
|
||||
modules.plugins.contexts = {};
|
||||
modules.Script = function Script(file) {
|
||||
const { io, plugins } = modules;
|
||||
|
||||
let self = set.has(plugins, file.path) && plugins[file.path];
|
||||
if (self) {
|
||||
if (set.has(self, "onUnload"))
|
||||
self.onUnload();
|
||||
}
|
||||
else {
|
||||
self = update(modules.newContext(plugins, true), {
|
||||
NAME: file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase()),
|
||||
PATH: file.path,
|
||||
CONTEXT: self
|
||||
});
|
||||
Class.replaceProperty(plugins, file.path, self);
|
||||
|
||||
// This belongs elsewhere
|
||||
if (io.getRuntimeDirectories("plugins").some(
|
||||
function (dir) dir.contains(file, false)))
|
||||
Class.replaceProperty(plugins, self.NAME, self);
|
||||
}
|
||||
plugins.contexts[file.path] = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
init.superapply(this, arguments);
|
||||
},
|
||||
commands: function (dactyl, modules, window) {
|
||||
const { commands, completion, io } = modules;
|
||||
|
||||
@@ -617,15 +594,6 @@ var IO = Module("io", {
|
||||
literal: 0
|
||||
});
|
||||
|
||||
// NOTE: this command is only used in :source
|
||||
commands.add(["fini[sh]"],
|
||||
"Stop sourcing a script file",
|
||||
function () {
|
||||
dactyl.assert(io.sourcing, "E168: :finish used outside of a sourced file");
|
||||
io.sourcing.finished = true;
|
||||
},
|
||||
{ argCount: "0" });
|
||||
|
||||
commands.add(["pw[d]"],
|
||||
"Print the current directory name",
|
||||
function () { dactyl.echomsg(io.cwd.path); },
|
||||
@@ -642,7 +610,7 @@ var IO = Module("io", {
|
||||
"E189: " + file.path.quote() + " exists (add ! to override)");
|
||||
|
||||
// TODO: Use a set/specifiable list here:
|
||||
let lines = [cmd.serialize().map(commands.commandToString, cmd) for (cmd in commands.iterator()) if (cmd.serialize)];
|
||||
let lines = [cmd.serialize().map(commands.commandToString, cmd) for (cmd in commands.iterator(true)) if (cmd.serialize)];
|
||||
lines = array.flatten(lines);
|
||||
|
||||
lines.unshift('"' + config.version + "\n");
|
||||
@@ -780,16 +748,17 @@ unlet s:cpo_save
|
||||
return lines.map(function (l) l.join("")).join("\n").replace(/\s+\n/gm, "\n");
|
||||
}
|
||||
|
||||
const { commands, options } = modules;
|
||||
file.write(template({
|
||||
name: config.name,
|
||||
autocommands: wrap("syn keyword " + config.name + "AutoEvent ",
|
||||
keys(config.autocommands)),
|
||||
commands: wrap("syn keyword " + config.name + "Command ",
|
||||
array(c.specs for (c in commands)).flatten()),
|
||||
array(c.specs for (c in commands.iterator())).flatten()),
|
||||
options: wrap("syn keyword " + config.name + "Option ",
|
||||
array(o.names for (o in modules.options) if (o.type != "boolean")).flatten()),
|
||||
array(o.names for (o in options) if (o.type != "boolean")).flatten()),
|
||||
toggleoptions: wrap("let s:toggleOptions = [",
|
||||
array(o.realNames for (o in modules.options) if (o.type == "boolean"))
|
||||
array(o.realNames for (o in options) if (o.type == "boolean"))
|
||||
.flatten().map(String.quote),
|
||||
", ") + "]"
|
||||
}));
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
try {
|
||||
|
||||
Components.utils.import("resource://dactyl/bootstrap.jsm");
|
||||
defineModule("options", {
|
||||
exports: ["Option", "Options", "ValueError", "options"],
|
||||
require: ["storage"],
|
||||
use: ["commands", "completion", "prefs", "services", "template", "util"]
|
||||
}, this);
|
||||
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
let ValueError = Class("ValueError", ErrorBase);
|
||||
@@ -62,6 +72,13 @@ var Option = Class("Option", {
|
||||
if (arguments.length > 3) {
|
||||
if (this.type == "string")
|
||||
defaultValue = Commands.quote(defaultValue);
|
||||
|
||||
if (isObject(defaultValue))
|
||||
defaultValue = iter(defaultValue).map(function (val) val.map(Option.quote).join(":")).join(",");
|
||||
|
||||
if (isArray(defaultValue))
|
||||
defaultValue = defaultValue.map(Option.quote).join(",");
|
||||
|
||||
this.defaultValue = this.parse(defaultValue);
|
||||
}
|
||||
|
||||
@@ -76,7 +93,7 @@ var Option = Class("Option", {
|
||||
get helpTag() "'" + this.name + "'",
|
||||
|
||||
initValue: function () {
|
||||
dactyl.trapErrors(function () this.value = this.value, this);
|
||||
util.trapErrors(function () this.value = this.value, this);
|
||||
},
|
||||
|
||||
get isDefault() this.stringValue === this.stringDefaultValue,
|
||||
@@ -120,13 +137,15 @@ var Option = Class("Option", {
|
||||
|
||||
let values;
|
||||
|
||||
if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
/*
|
||||
if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
values = tabs.options[this.name];
|
||||
*/
|
||||
if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
|
||||
values = this.globalValue;
|
||||
|
||||
if (this.getter)
|
||||
return dactyl.trapErrors(this.getter, this, values);
|
||||
return util.trapErrors(this.getter, this, values);
|
||||
|
||||
return values;
|
||||
},
|
||||
@@ -144,19 +163,21 @@ var Option = Class("Option", {
|
||||
return;
|
||||
|
||||
if (this.setter)
|
||||
newValues = dactyl.trapErrors(this.setter, this, newValues);
|
||||
newValues = this.modules.dactyl.trapErrors(this.setter, this, newValues);
|
||||
if (newValues === undefined)
|
||||
return;
|
||||
|
||||
if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
/*
|
||||
if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
tabs.options[this.name] = newValues;
|
||||
*/
|
||||
if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
|
||||
this.globalValue = newValues;
|
||||
|
||||
this.hasChanged = true;
|
||||
this.setFrom = null;
|
||||
|
||||
dactyl.triggerObserver("options." + this.name, newValues);
|
||||
// dactyl.triggerObserver("options." + this.name, newValues);
|
||||
},
|
||||
|
||||
getValues: deprecated("Option#get", "get"),
|
||||
@@ -232,7 +253,7 @@ var Option = Class("Option", {
|
||||
}
|
||||
catch (e) {
|
||||
if (!(e instanceof ValueError))
|
||||
dactyl.reportError(e);
|
||||
util.reportError(e);
|
||||
return this.invalidArgument(str || this.stringify(values), operator) + ": " + e.message;
|
||||
}
|
||||
|
||||
@@ -473,6 +494,7 @@ var Option = Class("Option", {
|
||||
Option._splitAt = 0;
|
||||
return arg;
|
||||
},
|
||||
|
||||
splitList: function (value, keepQuotes) {
|
||||
let res = [];
|
||||
Option._splitAt = 0;
|
||||
@@ -488,6 +510,7 @@ var Option = Class("Option", {
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
quote: function quote(str, re)
|
||||
Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str)
|
||||
? (/[\b\f\n\r\t]/.test(str) ? '"' : "'")
|
||||
@@ -507,8 +530,8 @@ var Option = Class("Option", {
|
||||
values = values[(values.indexOf(String(this.value)) + 1) % values.length];
|
||||
|
||||
let value = parseInt(values);
|
||||
dactyl.assert(Number(values) % 1 == 0,
|
||||
"E521: Number required after =: " + this.name + "=" + values);
|
||||
util.assert(Number(values) % 1 == 0,
|
||||
"E521: Number required after =: " + this.name + "=" + values);
|
||||
|
||||
switch (operator) {
|
||||
case "+":
|
||||
@@ -620,7 +643,7 @@ var Option = Class("Option", {
|
||||
},
|
||||
|
||||
validateXPath: function (values) {
|
||||
let evaluator = XPathEvaluator();
|
||||
let evaluator = services.XPathEvaluator();
|
||||
return this.testValues(values,
|
||||
function (value) evaluator.createExpression(value, util.evaluateXPath.resolver));
|
||||
}
|
||||
@@ -630,73 +653,124 @@ var Option = Class("Option", {
|
||||
* @instance options
|
||||
*/
|
||||
var Options = Module("options", {
|
||||
init: function () {
|
||||
this.needInit = [];
|
||||
this._options = [];
|
||||
this._optionMap = {};
|
||||
Local: function (dactyl, modules, window) let ({ contexts } = modules) ({
|
||||
init: function init() {
|
||||
const self = this;
|
||||
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) {
|
||||
// Trigger any setters.
|
||||
let opt = options.get(option);
|
||||
if (event == "change" && opt)
|
||||
opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
|
||||
}, window);
|
||||
},
|
||||
storage.newMap("options", { store: false });
|
||||
storage.addObserver("options", function optionObserver(key, event, option) {
|
||||
// Trigger any setters.
|
||||
let opt = self.get(option);
|
||||
if (event == "change" && opt)
|
||||
opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
|
||||
}, window);
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
for (let opt in this)
|
||||
if (opt.cleanupValue != null)
|
||||
opt.value = opt.parse(opt.cleanupValue);
|
||||
},
|
||||
dactyl: dactyl,
|
||||
|
||||
/**
|
||||
* Lists all options in *scope* or only those with changed values if
|
||||
* *onlyNonDefault* is specified.
|
||||
*
|
||||
* @param {function(Option)} filter Limit the list
|
||||
* @param {number} scope Only list options in this scope (see
|
||||
* {@link Option#scope}).
|
||||
*/
|
||||
list: function (filter, scope) {
|
||||
if (!scope)
|
||||
scope = Option.SCOPE_BOTH;
|
||||
|
||||
function opts(opt) {
|
||||
for (let opt in Iterator(options)) {
|
||||
let option = {
|
||||
__proto__: opt,
|
||||
isDefault: opt.isDefault,
|
||||
default: opt.stringDefaultValue,
|
||||
pre: "\u00a0\u00a0", // Unicode nonbreaking space.
|
||||
value: <></>
|
||||
};
|
||||
|
||||
if (filter && !filter(opt))
|
||||
continue;
|
||||
if (!(opt.scope & scope))
|
||||
continue;
|
||||
|
||||
if (opt.type == "boolean") {
|
||||
if (!opt.value)
|
||||
option.pre = "no";
|
||||
option.default = (opt.defaultValue ? "" : "no") + opt.name;
|
||||
}
|
||||
else if (isArray(opt.value))
|
||||
option.value = <>={template.map(opt.value, function (v) template.highlight(String(v)), <>,<span style="width: 0; display: inline-block"> </span></>)}</>;
|
||||
else
|
||||
option.value = <>={template.highlight(opt.stringValue)}</>;
|
||||
yield option;
|
||||
}
|
||||
};
|
||||
|
||||
modules.commandline.commandOutput(template.options("Options", opts(), options["verbose"] > 0));
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
for (let opt in this)
|
||||
if (opt.cleanupValue != null)
|
||||
opt.value = opt.parse(opt.cleanupValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new 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.
|
||||
* @param {Object} extra An optional extra configuration hash (see
|
||||
* {@link Map#extraInfo}).
|
||||
* @optional
|
||||
*/
|
||||
add: function (names, description, type, defaultValue, extraInfo) {
|
||||
const self = this;
|
||||
|
||||
if (!extraInfo)
|
||||
extraInfo = {};
|
||||
|
||||
extraInfo.definedAt = contexts.getCaller(Components.stack.caller);
|
||||
|
||||
let name = names[0];
|
||||
if (name in this._optionMap) {
|
||||
this.dactyl.log("Warning: " + name.quote() + " already exists: replacing existing option.", 1);
|
||||
this.remove(name);
|
||||
}
|
||||
|
||||
let closure = function () self._optionMap[name];
|
||||
|
||||
memoize(this._optionMap, name, function () self.Option(names, description, type, defaultValue, extraInfo));
|
||||
for (let alias in values(names.slice(1)))
|
||||
memoize(this._optionMap, alias, closure);
|
||||
|
||||
if (extraInfo.setter && (!extraInfo.scope || extraInfo.scope & Option.SCOPE_GLOBAL))
|
||||
if (this.dactyl.initialized)
|
||||
closure().initValue();
|
||||
else
|
||||
memoize(this.needInit, this.needInit.length, closure);
|
||||
|
||||
this._floptions = (this._floptions || []).concat(name);
|
||||
memoize(this._options, this._options.length, closure);
|
||||
|
||||
// quickly access options with options["wildmode"]:
|
||||
this.__defineGetter__(name, function () this._optionMap[name].value);
|
||||
this.__defineSetter__(name, function (value) { this._optionMap[name].value = value; });
|
||||
}
|
||||
}),
|
||||
|
||||
/** @property {Iterator(Option)} @private */
|
||||
__iterator__: function ()
|
||||
values(this._options.sort(function (a, b) String.localeCompare(a.name, b.name))),
|
||||
|
||||
/**
|
||||
* Adds a new 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.
|
||||
* @param {Object} extra An optional extra configuration hash (see
|
||||
* {@link Map#extraInfo}).
|
||||
* @optional
|
||||
*/
|
||||
add: function (names, description, type, defaultValue, extraInfo) {
|
||||
if (!extraInfo)
|
||||
extraInfo = {};
|
||||
|
||||
extraInfo.definedAt = Commands.getCaller(Components.stack.caller);
|
||||
|
||||
let name = names[0];
|
||||
if (name in this._optionMap) {
|
||||
dactyl.log("Warning: " + name.quote() + " already exists: replacing existing option.", 1);
|
||||
this.remove(name);
|
||||
}
|
||||
|
||||
let closure = function () options._optionMap[name];
|
||||
|
||||
memoize(this._optionMap, name, function () Option(names, description, type, defaultValue, extraInfo));
|
||||
for (let alias in values(names.slice(1)))
|
||||
memoize(this._optionMap, alias, closure);
|
||||
|
||||
if (extraInfo.setter && (!extraInfo.scope || extraInfo.scope & Option.SCOPE_GLOBAL))
|
||||
if (dactyl.initialized)
|
||||
closure().initValue();
|
||||
else
|
||||
memoize(this.needInit, this.needInit.length, closure);
|
||||
|
||||
this._floptions = (this._floptions || []).concat(name);
|
||||
memoize(this._options, this._options.length, closure);
|
||||
|
||||
// quickly access options with options["wildmode"]:
|
||||
this.__defineGetter__(name, function () this._optionMap[name].value);
|
||||
this.__defineSetter__(name, function (value) { this._optionMap[name].value = value; });
|
||||
},
|
||||
|
||||
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)),
|
||||
@@ -727,49 +801,6 @@ var Options = Module("options", {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Lists all options in *scope* or only those with changed values if
|
||||
* *onlyNonDefault* is specified.
|
||||
*
|
||||
* @param {function(Option)} filter Limit the list
|
||||
* @param {number} scope Only list options in this scope (see
|
||||
* {@link Option#scope}).
|
||||
*/
|
||||
list: function (filter, scope) {
|
||||
if (!scope)
|
||||
scope = Option.SCOPE_BOTH;
|
||||
|
||||
function opts(opt) {
|
||||
for (let opt in Iterator(options)) {
|
||||
let option = {
|
||||
__proto__: opt,
|
||||
isDefault: opt.isDefault,
|
||||
default: opt.stringDefaultValue,
|
||||
pre: "\u00a0\u00a0", // Unicode nonbreaking space.
|
||||
value: <></>
|
||||
};
|
||||
|
||||
if (filter && !filter(opt))
|
||||
continue;
|
||||
if (!(opt.scope & scope))
|
||||
continue;
|
||||
|
||||
if (opt.type == "boolean") {
|
||||
if (!opt.value)
|
||||
option.pre = "no";
|
||||
option.default = (opt.defaultValue ? "" : "no") + opt.name;
|
||||
}
|
||||
else if (isArray(opt.value))
|
||||
option.value = <>={template.map(opt.value, function (v) template.highlight(String(v)), <>,<span style="width: 0; display: inline-block"> </span></>)}</>;
|
||||
else
|
||||
option.value = <>={template.highlight(opt.stringValue)}</>;
|
||||
yield option;
|
||||
}
|
||||
};
|
||||
|
||||
commandline.commandOutput(template.options("Options", opts(), options["verbose"] > 0));
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses a :set command's argument string.
|
||||
*
|
||||
@@ -794,7 +825,7 @@ var Options = Module("options", {
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
ret.option = options.get(ret.name, ret.scope);
|
||||
ret.option = this.get(ret.name, ret.scope);
|
||||
if (!ret.option && (ret.option = options.get(prefix + ret.name, ret.scope))) {
|
||||
ret.name = prefix + ret.name;
|
||||
prefix = "";
|
||||
@@ -842,7 +873,9 @@ var Options = Module("options", {
|
||||
get store() storage.options
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
commands: function initCommands(dactyl, modules, window) {
|
||||
const { commands, contexts, options } = modules;
|
||||
|
||||
let args = {
|
||||
getMode: function (args) findMode(args["-mode"]),
|
||||
iterate: function (args) {
|
||||
@@ -909,7 +942,7 @@ var Options = Module("options", {
|
||||
}
|
||||
|
||||
if (name == "all" && reset)
|
||||
commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
|
||||
modules.commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
|
||||
function (resp) {
|
||||
if (resp == "yes")
|
||||
for (let pref in values(prefs.getNames()))
|
||||
@@ -939,22 +972,21 @@ var Options = Module("options", {
|
||||
prefs.set(name, value);
|
||||
}
|
||||
else
|
||||
commandline.commandOutput(prefs.list(onlyNonDefault, name));
|
||||
modules.commandline.commandOutput(prefs.list(onlyNonDefault, name));
|
||||
return;
|
||||
}
|
||||
|
||||
let opt = options.parseOpt(arg, modifiers);
|
||||
dactyl.assert(opt, "Error parsing :set command: " + arg);
|
||||
let opt = modules.options.parseOpt(arg, modifiers);
|
||||
util.assert(opt, "Error parsing :set command: " + arg);
|
||||
|
||||
let option = opt.option;
|
||||
dactyl.assert(option != null || opt.all,
|
||||
"E518: Unknown option: " + opt.name);
|
||||
util.assert(option != null || opt.all, "E518: Unknown option: " + opt.name);
|
||||
|
||||
// reset a variable to its default value
|
||||
if (opt.reset) {
|
||||
flushList();
|
||||
if (opt.all) {
|
||||
for (let option in options)
|
||||
for (let option in modules.options)
|
||||
option.reset();
|
||||
}
|
||||
else {
|
||||
@@ -968,7 +1000,7 @@ var Options = Module("options", {
|
||||
else {
|
||||
flushList();
|
||||
if (opt.option.type === "boolean") {
|
||||
dactyl.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
|
||||
util.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
|
||||
opt.values = !opt.unsetBoolean;
|
||||
}
|
||||
else if (/^(string|number)$/.test(opt.option.type) && opt.invert)
|
||||
@@ -982,7 +1014,7 @@ var Options = Module("options", {
|
||||
}
|
||||
if (res)
|
||||
dactyl.echoerr(res);
|
||||
option.setFrom = Commands.getCaller(null);
|
||||
option.setFrom = contexts.getCaller(null);
|
||||
}
|
||||
}
|
||||
flushList();
|
||||
@@ -1007,7 +1039,7 @@ var Options = Module("options", {
|
||||
return completion.preference(context);
|
||||
}
|
||||
|
||||
let opt = options.parseOpt(filter, modifiers);
|
||||
let opt = modules.options.parseOpt(filter, modifiers);
|
||||
let prefix = opt.prefix;
|
||||
|
||||
context.highlight();
|
||||
@@ -1048,7 +1080,7 @@ var Options = Module("options", {
|
||||
}
|
||||
|
||||
let optcontext = context.fork("values");
|
||||
completion.optionValue(optcontext, opt.name, opt.operator);
|
||||
modules.completion.optionValue(optcontext, opt.name, opt.operator);
|
||||
|
||||
// Fill in the current values if we're removing
|
||||
if (opt.operator == "-" && isArray(opt.values)) {
|
||||
@@ -1058,7 +1090,7 @@ var Options = Module("options", {
|
||||
context.maxItems = optcontext.maxItems;
|
||||
|
||||
context.filters.push(function (i) !set.has(have, i.text));
|
||||
completion.optionValue(context, opt.name, opt.operator, null,
|
||||
modules.completion.optionValue(context, opt.name, opt.operator, null,
|
||||
function (context) {
|
||||
context.generate = function () option.value.map(function (o) [o, ""]);
|
||||
});
|
||||
@@ -1101,10 +1133,10 @@ var Options = Module("options", {
|
||||
let [, scope, name, op, expr] = matches;
|
||||
let fullName = (scope || "") + name;
|
||||
|
||||
dactyl.assert(scope == "g:" || scope == null,
|
||||
"E461: Illegal variable name: " + scope + name);
|
||||
dactyl.assert(set.has(globalVariables, name) || (expr && !op),
|
||||
"E121: Undefined variable: " + fullName);
|
||||
util.assert(scope == "g:" || scope == null,
|
||||
"E461: Illegal variable name: " + scope + name);
|
||||
util.assert(set.has(globalVariables, name) || (expr && !op),
|
||||
"E121: Undefined variable: " + fullName);
|
||||
|
||||
if (!expr)
|
||||
dactyl.echo(fullName + "\t\t" + fmt(globalVariables[name]));
|
||||
@@ -1113,7 +1145,7 @@ var Options = Module("options", {
|
||||
var newValue = dactyl.userEval(expr);
|
||||
}
|
||||
catch (e) {}
|
||||
dactyl.assert(newValue !== undefined,
|
||||
util.assert(newValue !== undefined,
|
||||
"E15: Invalid expression: " + expr);
|
||||
|
||||
let value = newValue;
|
||||
@@ -1160,7 +1192,7 @@ var Options = Module("options", {
|
||||
literalArg: [opt.type == "boolean" ? (opt.value ? "" : "no") + opt.name
|
||||
: opt.name + "=" + opt.stringValue]
|
||||
}
|
||||
for (opt in options)
|
||||
for (opt in modules.options)
|
||||
if (!opt.getter && !opt.isDefault && (opt.scope & Option.SCOPE_GLOBAL))
|
||||
]
|
||||
}
|
||||
@@ -1175,18 +1207,18 @@ var Options = Module("options", {
|
||||
completer: setCompleter,
|
||||
domains: function (args) array.flatten(args.map(function (spec) {
|
||||
try {
|
||||
let opt = options.parseOpt(spec);
|
||||
let opt = modules.options.parseOpt(spec);
|
||||
if (opt.option && opt.option.domains)
|
||||
return opt.option.domains(opt.values);
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.reportError(e);
|
||||
util.reportError(e);
|
||||
}
|
||||
return [];
|
||||
})),
|
||||
keepQuotes: true,
|
||||
privateData: function (args) args.some(function (spec) {
|
||||
let opt = options.parseOpt(spec);
|
||||
let opt = modules.options.parseOpt(spec);
|
||||
return opt.option && opt.option.privateData &&
|
||||
(!callable(opt.option.privateData) ||
|
||||
opt.option.privateData(opt.values));
|
||||
@@ -1216,12 +1248,14 @@ var Options = Module("options", {
|
||||
deprecated: "the options system"
|
||||
});
|
||||
},
|
||||
completion: function () {
|
||||
completion: function initCompletion(dactyl, modules, window) {
|
||||
const { completion } = modules;
|
||||
|
||||
completion.option = function option(context, scope, prefix) {
|
||||
context.title = ["Option"];
|
||||
context.keys = { text: "names", description: "description" };
|
||||
context.anchored = false;
|
||||
context.completions = options;
|
||||
context.completions = modules.options;
|
||||
if (prefix == "inv")
|
||||
context.keys.text = function (opt)
|
||||
opt.type == "boolean" || isArray(opt.value) ? opt.names.map(function (n) "inv" + n)
|
||||
@@ -1231,7 +1265,7 @@ var Options = Module("options", {
|
||||
};
|
||||
|
||||
completion.optionValue = function (context, name, op, curValue, completer) {
|
||||
let opt = options.get(name);
|
||||
let opt = modules.options.get(name);
|
||||
completer = completer || opt.completer;
|
||||
if (!completer || !opt)
|
||||
return;
|
||||
@@ -1294,15 +1328,18 @@ var Options = Module("options", {
|
||||
context.completions = res;
|
||||
};
|
||||
},
|
||||
javascript: function () {
|
||||
javascript: function initJavascript(dactyl, modules, window) {
|
||||
const { options, JavaScript } = modules;
|
||||
JavaScript.setCompleter(options.get, [function () ([o.name, o.description] for (o in options))]);
|
||||
},
|
||||
sanitizer: function () {
|
||||
sanitizer: function initSanitizer(dactyl, modules, window) {
|
||||
const { sanitizer } = modules;
|
||||
|
||||
sanitizer.addItem("options", {
|
||||
description: "Options containing hostname data",
|
||||
action: function (timespan, host) {
|
||||
if (host)
|
||||
for (let opt in values(options._options))
|
||||
for (let opt in values(modules.options._options))
|
||||
if (timespan.contains(opt.lastSet * 1000) && opt.domains)
|
||||
try {
|
||||
opt.value = opt.filterDomain(host, opt.value);
|
||||
@@ -1312,12 +1349,12 @@ var Options = Module("options", {
|
||||
}
|
||||
},
|
||||
privateEnter: function () {
|
||||
for (let opt in values(options._options))
|
||||
for (let opt in values(modules.options._options))
|
||||
if (opt.privateData && (!callable(opt.privateData) || opt.privateData(opt.value)))
|
||||
opt.oldValue = opt.value;
|
||||
},
|
||||
privateLeave: function () {
|
||||
for (let opt in values(options._options))
|
||||
for (let opt in values(modules.options._options))
|
||||
if (opt.oldValue != null) {
|
||||
opt.value = opt.oldValue;
|
||||
opt.oldValue = null;
|
||||
@@ -1327,4 +1364,8 @@ var Options = Module("options", {
|
||||
}
|
||||
});
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
endModule();
|
||||
|
||||
} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||
@@ -102,6 +102,7 @@ var Overlay = Module("Overlay", {
|
||||
|
||||
const jsmodules = { NAME: "jsmodules" };
|
||||
const modules = update(create(jsmodules), {
|
||||
yes_i_know_i_should_not_report_errors_in_these_branches_thanks: [],
|
||||
|
||||
jsmodules: jsmodules,
|
||||
|
||||
@@ -153,13 +154,16 @@ var Overlay = Module("Overlay", {
|
||||
defineModule.time("load", null, function _load() {
|
||||
["addons",
|
||||
"base",
|
||||
"commands",
|
||||
"completion",
|
||||
"config",
|
||||
"contexts",
|
||||
"downloads",
|
||||
"finder",
|
||||
"highlight",
|
||||
"io",
|
||||
"javascript",
|
||||
"options",
|
||||
"overlay",
|
||||
"prefs",
|
||||
"services",
|
||||
@@ -171,18 +175,16 @@ var Overlay = Module("Overlay", {
|
||||
|
||||
["dactyl",
|
||||
"modes",
|
||||
"commandline",
|
||||
"abbreviations",
|
||||
"autocommands",
|
||||
"buffer",
|
||||
"commandline",
|
||||
"commands",
|
||||
"editor",
|
||||
"events",
|
||||
"hints",
|
||||
"mappings",
|
||||
"marks",
|
||||
"mow",
|
||||
"options",
|
||||
"statusline"
|
||||
].forEach(function (name) defineModule.time("load", name, modules.load, modules, name));
|
||||
|
||||
@@ -294,8 +296,10 @@ var Overlay = Module("Overlay", {
|
||||
|
||||
frob("init");
|
||||
defineModule.modules.forEach(function ({ lazyInit, constructor: { className } }) {
|
||||
if (!lazyInit)
|
||||
if (!lazyInit) {
|
||||
frob(className);
|
||||
modules[className] = modules[className];
|
||||
}
|
||||
else
|
||||
modules.__defineGetter__(className, function () {
|
||||
delete modules[className];
|
||||
@@ -323,6 +327,8 @@ var Overlay = Module("Overlay", {
|
||||
}
|
||||
});
|
||||
|
||||
endModule();
|
||||
|
||||
} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||
|
||||
@@ -604,12 +604,12 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
|
||||
context.completions = this.values;
|
||||
},
|
||||
values: [
|
||||
["all", "Everything"],
|
||||
["session", "The current session"],
|
||||
["10m", "Last ten minutes"],
|
||||
["1h", "Past hour"],
|
||||
["1d", "Past day"],
|
||||
["1w", "Past week"]
|
||||
["all", "Everything"],
|
||||
["session", "The current session"],
|
||||
["10m", "Last ten minutes"],
|
||||
["1h", "Past hour"],
|
||||
["1d", "Past day"],
|
||||
["1w", "Past week"]
|
||||
],
|
||||
validator: function (value) /^(a(ll)?|s(ession)|\d+[mhdw])$/.test(value)
|
||||
});
|
||||
|
||||
@@ -84,6 +84,7 @@ var Services = Module("Services", {
|
||||
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback");
|
||||
this.addClass("StreamCopier", "@mozilla.org/network/async-stream-copier;1",Ci.nsIAsyncStreamCopier, "init");
|
||||
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest);
|
||||
this.addClass("XPathEvaluator", "@mozilla.org/dom/xpath-evaluator;1", Ci.nsIDOMXPathEvaluator);
|
||||
this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", Ci.nsIZipReader, "open");
|
||||
this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
|
||||
},
|
||||
|
||||
@@ -8,13 +8,13 @@ Components.utils.import("resource://dactyl/bootstrap.jsm");
|
||||
defineModule("styles", {
|
||||
exports: ["Style", "Styles", "styles"],
|
||||
require: ["services", "util"],
|
||||
use: ["template"]
|
||||
use: ["contexts", "template"]
|
||||
}, this);
|
||||
|
||||
function cssUri(css) "chrome-data:text/css," + encodeURI(css);
|
||||
var namespace = "@namespace html " + XHTML.uri.quote() + ";\n" +
|
||||
"@namespace xul " + XUL.uri.quote() + ";\n" +
|
||||
"@namespace dactyl " + NS.uri.quote() + ";\n";
|
||||
"@namespace xul " + XUL.uri.quote() + ";\n" +
|
||||
"@namespace dactyl " + NS.uri.quote() + ";\n";
|
||||
|
||||
var Sheet = Struct("name", "id", "sites", "css", "hive", "agent");
|
||||
Sheet.liveProperty = function (name) {
|
||||
@@ -23,7 +23,7 @@ Sheet.liveProperty = function (name) {
|
||||
this.prototype.__defineSetter__(name, function (val) {
|
||||
if (isArray(val))
|
||||
val = Array.slice(val);
|
||||
if (isArray(val) && Object.freeze)
|
||||
if (isArray(val))
|
||||
Object.freeze(val);
|
||||
this[i] = val;
|
||||
this.enabled = this.enabled;
|
||||
@@ -87,6 +87,19 @@ var Hive = Class("Hive", {
|
||||
this.name = name;
|
||||
this.sheets = [];
|
||||
this.names = {};
|
||||
this.refs = [];
|
||||
},
|
||||
|
||||
addRef: function (obj) {
|
||||
this.refs.push(Cu.getWeakReference(obj));
|
||||
this.dropRef(null);
|
||||
},
|
||||
dropRef: function (obj) {
|
||||
this.refs = this.refs.filter(function (ref) ref.get() && ref.get() !== obj);
|
||||
if (!this.refs.length) {
|
||||
this.cleanup();
|
||||
styles.hives = styles.hives.filter(function (h) h !== this, this);
|
||||
}
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
@@ -219,7 +232,6 @@ var Hive = Class("Hive", {
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
/**
|
||||
* Manages named and unnamed user style sheets, which apply to both
|
||||
* chrome and content pages.
|
||||
@@ -229,8 +241,7 @@ try {
|
||||
var Styles = Module("Styles", {
|
||||
init: function () {
|
||||
this._id = 0;
|
||||
this.user = Hive("user");
|
||||
this.system = Hive("system");
|
||||
this.cleanup();
|
||||
this.allSheets = {};
|
||||
|
||||
services["dactyl:"].providers["style"] = function styleProvider(uri) {
|
||||
@@ -242,8 +253,22 @@ var Styles = Module("Styles", {
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
for each (let hive in [this.user, this.system])
|
||||
hive.cleanup();
|
||||
for each (let hive in this.hives)
|
||||
util.trapErrors("cleanup", hive);
|
||||
this.hives = [];
|
||||
this.user = this.addHive("user", this);
|
||||
this.system = this.addHive("system", this);
|
||||
},
|
||||
|
||||
addHive: function addHive(name, ref) {
|
||||
let hive = array.nth(this.hives, function (h) h.name === name, 0);
|
||||
if (!hive) {
|
||||
hive = Hive(name);
|
||||
this.hives.push(hive);
|
||||
}
|
||||
if (ref)
|
||||
hive.addRef(ref);
|
||||
return hive;
|
||||
},
|
||||
|
||||
__iterator__: function () Iterator(this.user.sheets.concat(this.system.sheets)),
|
||||
@@ -324,7 +349,7 @@ var Styles = Module("Styles", {
|
||||
matchFilter: function (filter) {
|
||||
if (filter === "*")
|
||||
var test = function test(uri) true;
|
||||
else if (filter[0] === "^") {
|
||||
else if (!/^(?:[a-z-]+:|[a-z-.]+$)/.test(filter)) {
|
||||
let re = RegExp(filter);
|
||||
test = function test(uri) re.test(uri.spec);
|
||||
}
|
||||
@@ -425,7 +450,7 @@ var Styles = Module("Styles", {
|
||||
})
|
||||
}, {
|
||||
commands: function (dactyl, modules, window) {
|
||||
const commands = modules.commands;
|
||||
const { commands, contexts } = modules;
|
||||
|
||||
commands.add(["sty[le]"],
|
||||
"Add or list user styles",
|
||||
@@ -434,17 +459,17 @@ var Styles = Module("Styles", {
|
||||
|
||||
if (css) {
|
||||
if ("-append" in args) {
|
||||
let sheet = styles.user.get(args["-name"]);
|
||||
let sheet = args["-group"].get(args["-name"]);
|
||||
if (sheet) {
|
||||
filter = sheet.sites.concat(filter).join(",");
|
||||
css = sheet.css + " " + css;
|
||||
|
||||
}
|
||||
}
|
||||
styles.user.add(args["-name"], filter, css, args["-agent"]);
|
||||
args["-group"].add(args["-name"], filter, css, args["-agent"]);
|
||||
}
|
||||
else {
|
||||
let list = styles.user.sheets.slice()
|
||||
let list = args["-group"].sheets.slice()
|
||||
.sort(function (a, b) a.name && b.name ? String.localeCompare(a.name, b.name)
|
||||
: !!b.name - !!a.name || a.id - b.id);
|
||||
let uris = util.visibleURIs(window.content);
|
||||
@@ -455,7 +480,7 @@ var Styles = Module("Styles", {
|
||||
"padding: 0 1em 0 1ex; vertical-align: top;",
|
||||
"padding: 0 1em 0 0; vertical-align: top;"],
|
||||
([sheet.enabled ? "" : UTF8("×"),
|
||||
sheet.name || styles.user.sheets.indexOf(sheet),
|
||||
sheet.name || args["-group"].sheets.indexOf(sheet),
|
||||
sheet.formatSites(uris),
|
||||
sheet.css]
|
||||
for (sheet in values(list))
|
||||
@@ -466,7 +491,7 @@ var Styles = Module("Styles", {
|
||||
bang: true,
|
||||
completer: function (context, args) {
|
||||
let compl = [];
|
||||
let sheet = styles.user.get(args["-name"]);
|
||||
let sheet = args["-group"].get(args["-name"]);
|
||||
if (args.completeArg == 0) {
|
||||
if (sheet)
|
||||
context.completions = [[sheet.sites.join(","), "Current Value"]];
|
||||
@@ -483,10 +508,11 @@ var Styles = Module("Styles", {
|
||||
options: [
|
||||
{ names: ["-agent", "-A"], description: "Apply style as an Agent sheet" },
|
||||
{ names: ["-append", "-a"], description: "Append site filter and css to an existing, matching sheet" },
|
||||
contexts.GroupFlag("styles"),
|
||||
{
|
||||
names: ["-name", "-n"],
|
||||
description: "The name of this stylesheet",
|
||||
completer: function () [[k, v.css] for ([k, v] in Iterator(styles.user.names))],
|
||||
completer: function (context, args) [[k, v.css] for ([k, v] in Iterator(args["-group"].hive.names))],
|
||||
type: modules.CommandOption.STRING
|
||||
}
|
||||
],
|
||||
@@ -587,6 +613,28 @@ var Styles = Module("Styles", {
|
||||
});
|
||||
});
|
||||
},
|
||||
contexts: function (dactyl, modules, window) {
|
||||
modules.contexts.Hives("styles",
|
||||
Class("LocalHive", Contexts.Hive, {
|
||||
init: function init(group) {
|
||||
init.superapply(this, arguments);
|
||||
|
||||
this.hive = styles.addHive(group.name);
|
||||
this.hive.addRef(this);
|
||||
},
|
||||
|
||||
get names() this.hive.names,
|
||||
get sheets() this.hive.sheets,
|
||||
|
||||
__noSuchMethod__: function __noSuchMethod__(meth, args) {
|
||||
return this.hive[meth].apply(this.hive, args);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.hive.dropRef(this);
|
||||
}
|
||||
}));
|
||||
},
|
||||
completion: function (dactyl, modules, window) {
|
||||
const names = Array.slice(util.computedStyle(window.document.createElement("div")));
|
||||
modules.completion.css = function (context) {
|
||||
@@ -642,6 +690,6 @@ var Styles = Module("Styles", {
|
||||
|
||||
endModule();
|
||||
|
||||
} catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
|
||||
// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
Components.utils.import("resource://dactyl/bootstrap.jsm");
|
||||
defineModule("template", {
|
||||
exports: ["Template", "template"],
|
||||
exports: ["Binding", "Template", "template"],
|
||||
require: ["util"],
|
||||
use: ["services"]
|
||||
}, this);
|
||||
@@ -14,8 +14,9 @@ defineModule("template", {
|
||||
default xml namespace = XHTML;
|
||||
|
||||
var Binding = Class("Binding", {
|
||||
init: function (node) {
|
||||
init: function (node, nodes) {
|
||||
this.node = node;
|
||||
this.nodes = nodes;
|
||||
node.dactylBinding = this;
|
||||
|
||||
Object.defineProperties(node, this.constructor.properties);
|
||||
@@ -32,9 +33,13 @@ var Binding = Class("Binding", {
|
||||
},
|
||||
get collapsed() !!this.getAttribute("collapsed"),
|
||||
|
||||
__noSuchMethod__: function __noSuchMethod__(meth, args) {
|
||||
return this.node[meth].apply(this.node, args);
|
||||
}
|
||||
__noSuchMethod__: Class.Property({
|
||||
configurable: true,
|
||||
writeable: true,
|
||||
value: function __noSuchMethod__(meth, args) {
|
||||
return this.node[meth].apply(this.node, args);
|
||||
}
|
||||
})
|
||||
}, {
|
||||
get bindings() {
|
||||
let bindingProto = Object.getPrototypeOf(Binding.prototype);
|
||||
@@ -66,10 +71,12 @@ var Binding = Class("Binding", {
|
||||
for (let obj in this.bindings)
|
||||
for (let prop in properties(obj)) {
|
||||
let desc = Object.getOwnPropertyDescriptor(obj, prop);
|
||||
for (let k in values(["get", "set", "value"]))
|
||||
if (typeof desc[k] === "function")
|
||||
desc[k] = this.bind(desc[k]);
|
||||
res[prop] = desc;
|
||||
if (desc.enumerable) {
|
||||
for (let k in values(["get", "set", "value"]))
|
||||
if (typeof desc[k] === "function")
|
||||
desc[k] = this.bind(desc[k]);
|
||||
res[prop] = desc;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
})
|
||||
@@ -86,10 +93,10 @@ var Template = Module("Template", {
|
||||
let ret = <></>;
|
||||
let n = 0;
|
||||
for each (let i in Iterator(iter)) {
|
||||
let val = func(i);
|
||||
let val = func(i, n);
|
||||
if (val == undefined)
|
||||
continue;
|
||||
if (sep && n++)
|
||||
if (n++ && sep)
|
||||
ret += sep;
|
||||
if (interruptable && n % interruptable == 0)
|
||||
util.threadYield(true, true);
|
||||
|
||||
@@ -13,7 +13,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm");
|
||||
defineModule("util", {
|
||||
exports: ["frag", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
|
||||
require: ["services"],
|
||||
use: ["config", "highlight", "storage", "template"]
|
||||
use: ["commands", "config", "highlight", "storage", "template"]
|
||||
}, this);
|
||||
|
||||
var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
|
||||
@@ -22,13 +22,6 @@ var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is
|
||||
var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
|
||||
default xml namespace = XHTML;
|
||||
|
||||
memoize(this, "Commands", function () {
|
||||
// FIXME
|
||||
let obj = { Module: Class };
|
||||
JSMLoader.loadSubScript("resource://dactyl-content/commands.js", obj);
|
||||
return obj.Commands;
|
||||
});
|
||||
|
||||
var FailedAssertion = Class("FailedAssertion", ErrorBase);
|
||||
var Point = Struct("x", "y");
|
||||
|
||||
@@ -132,7 +125,10 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
obj.observers[target].call(obj, subject, data);
|
||||
}
|
||||
catch (e) {
|
||||
util.reportError(e);
|
||||
if (typeof util === "undefined")
|
||||
dump("dactyl: error: " + e + "\n" + (e.stack || Error().stack).replace(/^/gm, "dactyl: "));
|
||||
else
|
||||
util.reportError(e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -770,14 +766,18 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
try {
|
||||
let xmlhttp = services.Xmlhttp();
|
||||
xmlhttp.mozBackgroundRequest = true;
|
||||
if (params.callback) {
|
||||
xmlhttp.onload = function handler(event) { util.trapErrors(params.callback, params, xmlhttp, event) };
|
||||
xmlhttp.onerror = xmlhttp.onload;
|
||||
|
||||
let async = params.callback || params.onload || params.onerror;
|
||||
if (async) {
|
||||
xmlhttp.onload = function handler(event) { util.trapErrors(params.onload || params.callback, params, xmlhttp, event) };
|
||||
xmlhttp.onerror = function handler(event) { util.trapErrors(params.onerror || params.callback, params, xmlhttp, event) };
|
||||
}
|
||||
if (params.mimeType)
|
||||
xmlhttp.overrideMimeType(params.mimeType);
|
||||
|
||||
xmlhttp.open(params.method || "GET", url, !!params.callback, params.user, params.pass);
|
||||
xmlhttp.open(params.method || "GET", url, async,
|
||||
params.user, params.pass);
|
||||
|
||||
xmlhttp.send(null);
|
||||
return xmlhttp;
|
||||
}
|
||||
@@ -1042,7 +1042,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
JSMLoader.cleanup();
|
||||
|
||||
services.observer.addObserver(this, "dactyl-rehash", true);
|
||||
if (!this.rehashing)
|
||||
services.observer.addObserver(this, "dactyl-rehash", true);
|
||||
},
|
||||
"dactyl-rehash": function () {
|
||||
services.observer.removeObserver(this, "dactyl-rehash");
|
||||
@@ -1083,6 +1084,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
this._loadOverlay(window, obj(window));
|
||||
}
|
||||
},
|
||||
|
||||
_loadOverlay: function _loadOverlay(window, obj) {
|
||||
let doc = window.document;
|
||||
if (!doc.dactylOverlayElements) {
|
||||
@@ -1353,7 +1355,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
* @param {string} string The string to search.
|
||||
* @param {number} lastIndex The index at which to begin searching. @optional
|
||||
*/
|
||||
iterate: function iterate(regexp, string, lastIndex) {
|
||||
iterate: function iterate(regexp, string, lastIndex) iter(function () {
|
||||
regexp.lastIndex = lastIndex = lastIndex || 0;
|
||||
let match;
|
||||
while (match = regexp.exec(string)) {
|
||||
@@ -1363,7 +1365,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
if (match[0].length == 0 || !regexp.global)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}())
|
||||
}),
|
||||
|
||||
rehash: function (args) {
|
||||
@@ -1407,6 +1409,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
}
|
||||
catch (e) { dump(e + "\n"); }
|
||||
}
|
||||
|
||||
// ctypes.open("libc.so.6").declare("kill", ctypes.default_abi, ctypes.void_t, ctypes.int, ctypes.int)(
|
||||
// ctypes.open("libc.so.6").declare("getpid", ctypes.default_abi, ctypes.int)(), 2)
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1598,6 +1603,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
*/
|
||||
trapErrors: function trapErrors(func, self) {
|
||||
try {
|
||||
if (isString(func))
|
||||
func = self[func];
|
||||
return func.apply(self || this, Array.slice(arguments, 2));
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -64,7 +64,7 @@ function Controller(controller) {
|
||||
}
|
||||
this.errors = [];
|
||||
this._countError = function countError(message, highlight) {
|
||||
if (/\bErrorMsg\b/.test(highlight))
|
||||
if (/\b(Error|Warning)Msg\b/.test(highlight))
|
||||
self.errors.push(String(message));
|
||||
}
|
||||
this.modules.dactyl.registerObserver("beep", this._countBeep);
|
||||
|
||||
@@ -122,7 +122,17 @@ var tests = {
|
||||
singleOutput: ["", "foobar"],
|
||||
noOutput: ["foo bar", "-js bar baz"],
|
||||
multiOutput: [""],
|
||||
error: ["foo bar", "-js bar baz"]
|
||||
error: [
|
||||
"foo bar",
|
||||
"-js bar baz",
|
||||
"-group=builtin baz quux",
|
||||
"! -group=builtin baz quux",
|
||||
],
|
||||
completions: [
|
||||
["", hasItems],
|
||||
["-group=", hasItems],
|
||||
["-group=user ", hasItems]
|
||||
]
|
||||
},
|
||||
comclear: {
|
||||
noOutput: [""]
|
||||
@@ -140,6 +150,11 @@ var tests = {
|
||||
delcommand: [
|
||||
{
|
||||
init: ["comclear", "command foo bar"],
|
||||
completions: [
|
||||
["", hasItems],
|
||||
["-group=", hasItems],
|
||||
["-group=user ", hasItems]
|
||||
],
|
||||
noOutput: ["foo"]
|
||||
},
|
||||
{
|
||||
@@ -152,7 +167,6 @@ var tests = {
|
||||
noOutput: ["x"],
|
||||
completions: ["", "x"]
|
||||
},
|
||||
delmapgroup: {}, // Skip for now
|
||||
get delmarks() this.delmacros,
|
||||
get delqmarks() this.delmacros,
|
||||
delstyle: {
|
||||
@@ -233,6 +247,25 @@ var tests = {
|
||||
finish: { noOutput: [""] },
|
||||
forward: { noOutput: [""] },
|
||||
frameonly: { noOutput: [""] },
|
||||
delgroup: {
|
||||
error: ["builtin"],
|
||||
completions: [""]
|
||||
},
|
||||
group: {
|
||||
multiOutput: [""],
|
||||
noOutput: [
|
||||
"foo -d='foo group' -nopersist -l 'bar.com','http://bar/*','http://bar','^http:'",
|
||||
"! foo -d='foo group' -nopersist -l 'bar.com','http://bar/*','http://bar','^http:'",
|
||||
"foo",
|
||||
"user"
|
||||
],
|
||||
error: ["builtin"],
|
||||
completions: [
|
||||
"",
|
||||
"foo "
|
||||
],
|
||||
cleanup: ["delmapgroup foo"]
|
||||
},
|
||||
hardcopy: {}, // Skip for now
|
||||
help: {
|
||||
noOutput: ["", "intro"],
|
||||
@@ -279,7 +312,21 @@ var tests = {
|
||||
["window", hasItems],
|
||||
["window.", hasItems],
|
||||
["window['", hasItems],
|
||||
["commands.get('", hasItems]
|
||||
["File('", hasItems],
|
||||
["File.expandPath('", hasItems],
|
||||
"autocommands.user.get('",
|
||||
["commands.get('", hasItems],
|
||||
["commands.builtin.get('", hasItems],
|
||||
["highlight.get('", hasItems],
|
||||
["highlight.highlightNode(null, '", hasItems],
|
||||
["mappings.get(modes.NORMAL, '", hasItems],
|
||||
// ["mappings.builtin.get(modes.NORMAL, '", hasItems],
|
||||
["options.get('", hasItems],
|
||||
["prefs.get('", hasItems],
|
||||
["prefs.defaults.get('", hasItems],
|
||||
["localPrefs.get('", hasItems],
|
||||
["localPrefs.defaults.get('", hasItems],
|
||||
["styles.system.get('", hasItems],
|
||||
]
|
||||
},
|
||||
jumps: {
|
||||
@@ -318,7 +365,8 @@ var tests = {
|
||||
],
|
||||
error: [
|
||||
"-mode=some-nonexistent-mode <C-a> <C-a>",
|
||||
"-gtroup=some-nonexistent-group <C-a> <C-a>"
|
||||
"-group=some-nonexistent-group <C-a> <C-a>",
|
||||
"-group=builtin <C-a> <C-a>"
|
||||
],
|
||||
completions: [
|
||||
["", hasItems],
|
||||
@@ -333,25 +381,13 @@ var tests = {
|
||||
},
|
||||
mapclear: {
|
||||
noOutput: [""],
|
||||
completions: [""]
|
||||
},
|
||||
mapgroup: {
|
||||
multiOutput: [""],
|
||||
noOutput: [
|
||||
"foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'",
|
||||
"! foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'",
|
||||
"foo",
|
||||
"user"
|
||||
],
|
||||
error: [
|
||||
"some-nonexistent-group",
|
||||
"foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'"
|
||||
"-group=builtin"
|
||||
],
|
||||
completions: [
|
||||
"",
|
||||
"foo "
|
||||
],
|
||||
cleanup: ["delmapgroup foo"]
|
||||
"-group="
|
||||
]
|
||||
},
|
||||
mark: {
|
||||
error: ["", "#", "xy"],
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
- Only visible tabs are considered in tab numbering,
|
||||
gt/gn/gN, etc. [b1]
|
||||
* Improved startup time by a factor of 7. [b1]
|
||||
* Further improved startup time. [b6]
|
||||
* Added site-local and script-local groups: [b6]
|
||||
- Added the :group command to define and select groups.
|
||||
- Added the -group flag to :autocmd, :command, :map, :style,
|
||||
and friends.
|
||||
- Mappings and commands can now be bound to groups which
|
||||
execute only for certain websites.
|
||||
- Autocommands, commands, mappings, and styles are now
|
||||
automatically added to per-script groups so that most traces
|
||||
of a script can be easily purged.
|
||||
* Significant completion speed improvements, especially for
|
||||
JavaScript. [b1]
|
||||
* Greatly improved private mode support and :sanitize command.
|
||||
@@ -76,8 +86,6 @@
|
||||
and linking to source code locations). [b4]
|
||||
- :downloads now opens a download list in the multi-line output
|
||||
buffer. [b6]
|
||||
- Added :mapgroup command and -group flag to :map, :unmap, and
|
||||
:mapclear. [b6]
|
||||
- Added -arg flag to :map. [b6]
|
||||
- :extensions has been replaced with a more powerful :addons.
|
||||
- Added -literal flag to :command. [b6]
|
||||
|
||||
Reference in New Issue
Block a user