diff --git a/common/components/protocols.js b/common/components/protocols.js
index 4cb99a95..3719858c 100644
--- a/common/components/protocols.js
+++ b/common/components/protocols.js
@@ -24,6 +24,7 @@ var global = this;
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
+var DNE = "chrome://dactyl/content/does/not/exist";
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -34,21 +35,23 @@ function makeChannel(url, orig) {
try {
if (url == null)
return fakeChannel(orig);
+
if (typeof url === "function")
return let ([type, data] = url()) StringChannel(data, type, orig);
- let channel = ioService.newChannel(url, null, null);
- channel.contentCharset = "UTF-8";
- channel.owner = systemPrincipal;
- channel.originalURI = orig;
- return channel;
+ let uri = ioService.newURI(url, null, null);
+ return (new XMLChannel(uri)).channel;
}
catch (e) {
util.reportError(e);
throw e;
}
}
-function fakeChannel(orig) makeChannel("chrome://dactyl/content/does/not/exist", orig);
+function fakeChannel(orig) {
+ let channel = ioService.newChannel(DNE, null, null);
+ channel.originalURI = orig;
+ return channel;
+}
function redirect(to, orig, time) {
let html =
.toXMLString();
return StringChannel(html, "text/html", ioService.newURI(to, null, null));
@@ -148,13 +151,13 @@ Dactyl.prototype = {
newChannel: function newChannel(uri) {
try {
- if (uri.host != "content" && !("all" in this.FILE_MAP))
+ if (/^help/.test(uri.host) && !("all" in this.FILE_MAP))
return redirect(uri.spec, uri, 1);
let path = decodeURIComponent(uri.path.replace(/^\/|#.*/g, ""));
switch(uri.host) {
case "content":
- return makeChannel(this.pages[path], uri);
+ return makeChannel(this.pages[path] || "chrome://dactyl/content/" + path, uri);
case "help":
return makeChannel(this.FILE_MAP[path], uri);
case "help-overlay":
@@ -165,6 +168,10 @@ Dactyl.prototype = {
return redirect("dactyl://help/" + tag, uri);
if (tag in this.HELP_TAGS)
return redirect("dactyl://help/" + this.HELP_TAGS[tag] + "#" + tag, uri);
+ case "locale":
+ return makeChannel("chrome://dactyl/locale/" + path, uri);
+ case "locale-local":
+ return makeChannel("chrome://" + config.name + "/locale/" + path, uri);
}
}
catch (e) {}
@@ -188,7 +195,8 @@ Dactyl.prototype = {
function StringChannel(data, contentType, uri) {
let channel = services.StreamChannel(uri);
channel.contentStream = services.StringStream(data);
- channel.contentType = contentType;
+ if (contentType)
+ channel.contentType = contentType;
channel.contentCharset = "UTF-8";
channel.owner = systemPrincipal;
if (uri)
@@ -197,17 +205,76 @@ function StringChannel(data, contentType, uri) {
}
function XMLChannel(uri, contentType) {
+ let channel = services.io.newChannelFromURI(uri);
+ try {
+ var channelStream = channel.open();
+ }
+ catch (e) {
+ this.channel = fakeChannel(uri);
+ return;
+ }
+
+ this.uri = uri;
this.sourceChannel = services.io.newChannelFromURI(uri);
this.pipe = services.Pipe(true, true, 0, 0, null);
+ this.writes = [];
this.channel = services.StreamChannel(uri);
- this.channel.contentStream = this.pipe.outputStream;
- this.channel.contentType = contentType;
+ this.channel.contentStream = this.pipe.inputStream;
+ this.channel.contentType = contentType || channel.contentType;
this.channel.contentCharset = "UTF-8";
this.channel.owner = systemPrincipal;
+
+ let stream = services.InputStream(channelStream);
+ let [, pre, doctype, url, open, post] = util.regexp().exec(stream.read(4096));
+ this.writes.push(pre);
+ if (doctype) {
+ this.writes.push(doctype + "[\n");
+ try {
+ this.writes.push(services.io.newChannel(url, null, null).open())
+ }
+ catch (e) {}
+ if (!open)
+ this.writes.push("\n]");
+ this.writes.push(post)
+ }
+ this.writes.push(channelStream);
+
+ this.writeNext();
}
XMLChannel.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver]),
+ writeNext: function () {
+ try {
+ if (!this.writes.length)
+ this.pipe.outputStream.close();
+ else {
+ let stream = this.writes.shift();
+ if (isString(stream))
+ stream = services.StringStream(stream);
+
+ services.StreamCopier(stream, this.pipe.outputStream, null,
+ false, true, 4096, true, false)
+ .asyncCopy(this, null);
+ }
+ }
+ catch (e) {
+ util.reportError(e);
+ }
+ },
+
+ onStartRequest: function (request, context) {},
+ onStopRequest: function (request, context, statusCode) {
+ this.writeNext();
+ }
};
function AboutHandler() {}
diff --git a/common/content/base.dtd b/common/content/base.dtd
deleted file mode 100644
index aed97f89..00000000
--- a/common/content/base.dtd
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-%dactylBranding;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index b0723506..7f6905b9 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -92,9 +92,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
* @property {number} The current main mode.
* @see modes#mainModes
*/
- mode: Class.Property({
- get: deprecated("Please use modes.main instead", function mode() modes.main),
- set: deprecated("Please use modes.main instead", function mode(val) modes.main = val),
+ mode: deprecated("Please use modes.main instead", {
+ get: function mode() modes.main,
+ set: function mode(val) modes.main = val
}),
get menuItems() Dactyl.getMenuItems(),
@@ -108,23 +108,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
forceNewTab: false,
forceNewWindow: false,
- /** @property {string} The Dactyl version string. */
- version: Class.memoize(function () {
- if (/pre$/.test(util.addon.version)) {
- let uri = util.addon.getResourceURI("../.hg");
- if (uri instanceof Ci.nsIFileURL &&
- uri.QueryInterface(Ci.nsIFileURL).file.exists() &&
- io.pathSearch("hg")) {
- return io.system(["hg", "-R", uri.file.parent.path,
- "log", "-r.",
- "--template=hg{rev} ({date|isodate})"]);
- }
- }
- let version = util.addon.version;
- if ("@DATE" !== "@" + "DATE@")
- version += " (created: @DATE@)";
- return version;
- }),
+ version: deprecated("Please use config.version instead", { get: function version() config.version }),
/**
* @property {Object} The map of command-line options. These are
@@ -304,9 +288,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
},
dump: deprecated("Use util.dump instead",
- function dump() util.dump.apply(util, arguments)),
+ { get: function dump() util.closure.dump }),
dumpStack: deprecated("Use util.dumpStack instead",
- function dumpStack() util.dumpStack.apply(util, arguments)),
+ { get: function dumpStack() util.closure.dumpStack }),
/**
* Outputs a plain message to the command line.
@@ -546,14 +530,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
* @private
* Initialize the help system.
*/
- initHelp: function () {
- if (!this.helpInitialized) {
+ initHelp: function (force) {
+ if (!force && !this.helpInitialized) {
if ("noscriptOverlay" in window) {
noscriptOverlay.safeAllow("chrome-data:", true, false);
noscriptOverlay.safeAllow("dactyl:", true, false);
}
- let namespaces = [config.name, "dactyl"];
+ let namespaces = ["locale-local", "locale"];
services["dactyl:"].init({});
let tagMap = services["dactyl:"].HELP_TAGS;
@@ -564,7 +548,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
function findHelpFile(file) {
let result = [];
for (let [, namespace] in Iterator(namespaces)) {
- let url = ["chrome://", namespace, "/locale/", file, ".xml"].join("");
+ let url = ["dactyl://", namespace, "/", file, ".xml"].join("");
let res = util.httpGet(url);
if (res) {
if (res.responseXML.documentElement.localName == "document")
@@ -894,9 +878,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
* These are set and accessed with the "g:" prefix.
*/
_globalVariables: {},
- globalVariables: Class.Property({
- get: deprecated("Please use the options system instead",
- function globalVariables() this._globalVariables)
+ globalVariables: deprecated("Please use the options system instead", {
+ get: function globalVariables() this._globalVariables
}),
loadPlugins: function (args) {
@@ -2051,7 +2034,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
dactyl.open("about:");
else
commandline.commandOutput(<>
- {config.appName} {dactyl.version} running on:
{navigator.userAgent}
+ {config.appName} {config.version} running on:
{navigator.userAgent}
>);
}, {
argCount: "0",
diff --git a/common/content/help.dtd b/common/content/help.dtd
deleted file mode 100644
index 4bfcf5b9..00000000
--- a/common/content/help.dtd
+++ /dev/null
@@ -1,6 +0,0 @@
-
-%dactylMain;
-
-command line'>
-status line'>
-
diff --git a/common/content/help.xsl b/common/content/help.xsl
index 143a815b..288bef12 100644
--- a/common/content/help.xsl
+++ b/common/content/help.xsl
@@ -1,5 +1,5 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
]>
diff --git a/common/locale/en-US/eval.xml b/common/locale/en-US/eval.xml
index 66918643..507ee5a7 100644
--- a/common/locale/en-US/eval.xml
+++ b/common/locale/en-US/eval.xml
@@ -1,7 +1,7 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
"].join(""))
+ .join("\n")]
+ });
},
get addonID() this.name + "@dactyl.googlecode.com",
+ addon: Class.memoize(function () {
+ let addon = services.fuel.storage.get("dactyl.bootstrap", {}).addon;
+ if (!addon)
+ addon = AddonManager.getAddonByID(this.addonID);
+ return addon;
+ }),
+
+ /** @property {string} The Dactyl version string. */
+ version: Class.memoize(function () {
+ if (/pre$/.test(this.addon.version)) {
+ let uri = this.addon.getResourceURI("../.hg");
+ if (uri instanceof Ci.nsIFileURL &&
+ uri.QueryInterface(Ci.nsIFileURL).file.exists() &&
+ io.pathSearch("hg")) {
+ return io.system(["hg", "-R", uri.file.parent.path,
+ "log", "-r.",
+ "--template=hg{rev} ({date|isodate})"]);
+ }
+ }
+ let version = this.addon.version;
+ if ("@DATE" !== "@" + "DATE@")
+ version += " (created: @DATE@)";
+ return version;
+ }),
+
+ // TODO: DTD properties. Cleanup.
+ get home() "http://dactyl.sourceforge.net/",
+ get apphome() this.home + this.name,
+ code: "http://code.google.com/p/dactyl/",
+ get issues() this.home + "bug/" + this.name,
+ get plugins() "http://dactyl.sf.net/" + this.name + "/plugins",
+ get faq() this.home + this.name + "/faq",
+ "list.mailto": Class.memoize(function () config.name + "@googlegroups.com"),
+ "list.href": Class.memoize(function () "http://groups.google.com/group/" + config.name),
+
+ dtdExtra: {
+ "xmlns.dactyl": "http://vimperator.org/namespaces/liberator",
+ "xmlns.html": "http://www.w3.org/1999/xhtml",
+ "xmlns.xul": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+
+ "tag.command-line": 'command line',
+ "tag.status-line": 'status line',
+ },
+
+ dtdStrings: [
+ "appName",
+ "apphome",
+ "code",
+ "faq",
+ "fileExt",
+ "home",
+ "host",
+ "hostbin",
+ "idName",
+ "issues",
+ "list.href",
+ "list.mailto",
+ "name",
+ "plugins",
+ "version",
+ ],
styleHelp: function styleHelp() {
if (!this.helpStyled)
@@ -519,15 +588,6 @@ config.INIT = update(Object.create(config.INIT), config.INIT, {
init: function init(dactyl, modules, window) {
init.superapply(this, arguments);
- // Hmm...
- let config1 = Object.create(config);
- let config2 = Object.create(config1);
- config2.instance = config2;
- update(config1, config.Local.superapply(config2, arguments));
- update(config2, config.Local.apply(config2, arguments));
- modules.config = config2;
- modules.config.init();
-
let img = window.Image();
img.src = this.logo || "chrome://" + this.name + "/content/logo.png";
img.onload = function () {
diff --git a/common/content/io.js b/common/modules/io.jsm
similarity index 74%
rename from common/content/io.js
rename to common/modules/io.jsm
index 096b5f65..c657fe9f 100644
--- a/common/content/io.js
+++ b/common/modules/io.jsm
@@ -7,31 +7,14 @@
// given in the LICENSE.txt file included with this file.
"use strict";
-/** @scope modules */
+try {
-plugins.contexts = {};
-function Script(file) {
- let self = set.has(plugins, file.path) && plugins[file.path];
- if (self) {
- if (set.has(self, "onUnload"))
- self.onUnload();
- }
- else {
- self = update({ __proto__: plugins }, {
- 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;
-}
+Components.utils.import("resource://dactyl/base.jsm");
+defineModule("io", {
+ exports: ["IO", "io"],
+ require: ["services"],
+ use: ["config", "storage", "template", "util"]
+});
// TODO: why are we passing around strings rather than file objects?
/**
@@ -43,37 +26,168 @@ var IO = Module("io", {
this._processDir = services.directory.get("CurWorkD", Ci.nsIFile);
this._cwd = this._processDir.path;
this._oldcwd = null;
+ },
- this._lastRunCommand = ""; // updated whenever the users runs a command with :!
- this._scriptNames = [];
+ Local: function (dactyl, modules, window) let ({ Script, plugins } = modules) ({
- this.downloadListener = {
- onDownloadStateChange: function (state, download) {
- if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
- let url = download.source.spec;
- let title = download.displayName;
- let file = download.targetFile.path;
- let size = download.size;
+ init: function init() {
+ this._processDir = services.directory.get("CurWorkD", Ci.nsIFile);
+ this._cwd = this._processDir.path;
+ this._oldcwd = null;
- dactyl.echomsg({ domains: [util.getHost(url)], message: "Download of " + title + " to " + file + " finished" },
- 1, commandline.ACTIVE_WINDOW);
- autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
+ this._lastRunCommand = ""; // updated whenever the users runs a command with :!
+ this._scriptNames = [];
+
+ this.downloadListener = {
+ onDownloadStateChange: function (state, download) {
+ if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
+ let url = download.source.spec;
+ let title = download.displayName;
+ let file = download.targetFile.path;
+ let size = download.size;
+
+ dactyl.echomsg({ domains: [util.getHost(url)], message: "Download of " + title + " to " + file + " finished" },
+ 1, modules.commandline.ACTIVE_WINDOW);
+ modules.autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
+ }
+ },
+ onStateChange: function () {},
+ onProgressChange: function () {},
+ onSecurityChange: function () {}
+ };
+
+ services.downloadManager.addListener(this.downloadListener);
+ },
+
+ destroy: function destroy() {
+ services.downloadManager.removeListener(this.downloadListener);
+ for (let [, plugin] in Iterator(plugins.contexts))
+ if (plugin.onUnload)
+ plugin.onUnload();
+ },
+
+ /**
+ * Returns all directories named *name* in 'runtimepath'.
+ *
+ * @param {string} name
+ * @returns {nsIFile[])
+ */
+ getRuntimeDirectories: function getRuntimeDirectories(name) {
+ let dirs = modules.options["runtimepath"];
+
+ dirs = dirs.map(function (dir) File.joinPaths(dir, name, this.cwd), this)
+ .filter(function (dir) dir.exists() && dir.isDirectory() && dir.isReadable());
+ return dirs;
+ },
+
+ // FIXME: multiple paths?
+ /**
+ * Sources files found in 'runtimepath'. For each relative path in *paths*
+ * each directory in 'runtimepath' is searched and if a matching file is
+ * found it is sourced. Only the first file found (per specified path) is
+ * sourced unless *all* is specified, then all found files are sourced.
+ *
+ * @param {string[]} paths An array of relative paths to source.
+ * @param {boolean} all Whether all found files should be sourced.
+ */
+ sourceFromRuntimePath: function sourceFromRuntimePath(paths, all) {
+ let dirs = options["runtimepath"];
+ let found = false;
+
+ dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + options.get("runtimepath").stringValue, 2);
+
+ outer:
+ for (let [, dir] in Iterator(dirs)) {
+ for (let [, path] in Iterator(paths)) {
+ let file = File.joinPaths(dir, path, this.cwd);
+
+ dactyl.echomsg("Searching for " + file.path.quote(), 3);
+
+ if (file.exists() && file.isFile() && file.isReadable()) {
+ io.source(file.path, false);
+ found = true;
+
+ if (!all)
+ break outer;
+ }
}
- },
- onStateChange: function () {},
- onProgressChange: function () {},
- onSecurityChange: function () {}
- };
+ }
- services.downloadManager.addListener(this.downloadListener);
- },
+ if (!found)
+ dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1);
- destroy: function () {
- services.downloadManager.removeListener(this.downloadListener);
- for (let [, plugin] in Iterator(plugins.contexts))
- if (plugin.onUnload)
- plugin.onUnload();
- },
+ return found;
+ },
+
+ /**
+ * 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.
+ */
+ source: function source(filename, silent) {
+ defineModule.loadLog.push("sourcing " + filename);
+ let time = Date.now();
+ this.withSavedValues(["sourcing"], function _source() {
+ this.sourcing = null;
+ try {
+ var file = util.getFile(filename) || io.File(filename);
+
+ if (!file.exists() || !file.isReadable() || file.isDirectory()) {
+ if (!silent)
+ dactyl.echoerr("E484: Can't open file " + filename.quote());
+ return;
+ }
+
+ dactyl.echomsg("sourcing " + filename.quote(), 2);
+
+ let uri = services.io.newFileURI(file);
+
+ // handle pure JavaScript files specially
+ if (/\.js$/.test(filename)) {
+ try {
+ dactyl.loadScript(uri.spec, Script(file));
+ dactyl.helpInitialized = false;
+ }
+ catch (e) {
+ if (e.fileName)
+ try {
+ e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, "");
+ if (e.fileName == uri.spec)
+ e.fileName = filename;
+ e.echoerr = <>{e.fileName}:{e.lineNumber}: {e}>;
+ }
+ catch (e) {}
+ throw e;
+ }
+ }
+ else if (/\.css$/.test(filename))
+ styles.registerSheet(uri.spec, false, true);
+ else {
+ modules.commands.execute(file.read(), null, silent || "loud", null,
+ { file: file.path, line: 1 });
+ }
+
+ if (this._scriptNames.indexOf(file.path) == -1)
+ this._scriptNames.push(file.path);
+
+ dactyl.echomsg("finished sourcing " + filename.quote(), 2);
+
+ dactyl.log("Sourced: " + filename, 3);
+ }
+ catch (e) {
+ if (!(e instanceof FailedAssertion))
+ dactyl.reportError(e);
+ let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
+ if (!silent)
+ dactyl.echoerr(message);
+ }
+ finally {
+ defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms");
+ }
+ });
+ },
+ }),
// TODO: there seems to be no way, short of a new component, to change
// the process's CWD - see https://bugzilla.mozilla.org/show_bug.cgi?id=280953
@@ -123,10 +237,11 @@ var IO = Module("io", {
* @property {function} File class.
* @final
*/
- File: Class("File", File, {
- init: function init(path, checkCWD)
- init.supercall(this, path, (arguments.length < 2 || checkCWD) && io.cwd)
- }),
+ File: Class.memoize(function () let (io = this)
+ Class("File", File, {
+ init: function init(path, checkCWD)
+ init.supercall(this, path, (arguments.length < 2 || checkCWD) && io.cwd)
+ })),
/**
* @property {Object} The current file sourcing context. As a file is
@@ -135,36 +250,7 @@ var IO = Module("io", {
*/
sourcing: null,
- /**
- * Expands "~" and environment variables in *path*.
- *
- * "~" is expanded to to the value of $HOME. On Windows if this is not
- * set then the following are tried in order:
- * $USERPROFILE
- * ${HOMDRIVE}$HOMEPATH
- *
- * The variable notation is $VAR (terminated by a non-word character)
- * or ${VAR}. %VAR% is also supported on Windows.
- *
- * @param {string} path The unexpanded path string.
- * @param {boolean} relative Whether the path is relative or absolute.
- * @returns {string}
- */
- expandPath: File.expandPath,
-
- /**
- * Returns all directories named *name* in 'runtimepath'.
- *
- * @param {string} name
- * @returns {nsIFile[])
- */
- getRuntimeDirectories: function (name) {
- let dirs = options["runtimepath"];
-
- dirs = dirs.map(function (dir) File.joinPaths(dir, name, this.cwd), this)
- .filter(function (dir) dir.exists() && dir.isDirectory() && dir.isReadable());
- return dirs;
- },
+ expandPath: deprecated("Please use File.expandPath instead", function expandPath() File.expandPath.apply(File, arguments)),
/**
* Returns the first user RC file found in *dir*.
@@ -207,7 +293,7 @@ var IO = Module("io", {
Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
.getService(Ci.nsPIExternalAppLauncher).deleteTemporaryFileOnExit(file);
- return io.File(file);
+ return File(file);
},
isJarURL: function (url) {
@@ -266,9 +352,9 @@ var IO = Module("io", {
let file;
if (File.isAbsolutePath(program))
- file = io.File(program, true);
+ file = this.File(program, true);
else
- file = io.pathSearch(program);
+ file = this.pathSearch(program);
if (!file || !file.exists()) {
dactyl.echoerr("Command not found: " + program);
@@ -283,7 +369,7 @@ var IO = Module("io", {
function () {
if (!process.isRunning) {
timer.cancel();
- dactyl.trapErrors(blocking);
+ util.trapErrors(blocking);
}
},
100, services.Timer.TYPE_REPEATING_SLACK);
@@ -299,114 +385,6 @@ var IO = Module("io", {
return process.exitValue;
},
- // FIXME: multiple paths?
- /**
- * Sources files found in 'runtimepath'. For each relative path in *paths*
- * each directory in 'runtimepath' is searched and if a matching file is
- * found it is sourced. Only the first file found (per specified path) is
- * sourced unless *all* is specified, then all found files are sourced.
- *
- * @param {string[]} paths An array of relative paths to source.
- * @param {boolean} all Whether all found files should be sourced.
- */
- sourceFromRuntimePath: function (paths, all) {
- let dirs = options["runtimepath"];
- let found = false;
-
- dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + options.get("runtimepath").stringValue, 2);
-
- outer:
- for (let [, dir] in Iterator(dirs)) {
- for (let [, path] in Iterator(paths)) {
- let file = File.joinPaths(dir, path, this.cwd);
-
- dactyl.echomsg("Searching for " + file.path.quote(), 3);
-
- if (file.exists() && file.isFile() && file.isReadable()) {
- io.source(file.path, false);
- found = true;
-
- if (!all)
- break outer;
- }
- }
- }
-
- if (!found)
- dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1);
-
- return found;
- },
-
- /**
- * 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.
- */
- source: function (filename, silent) {
- defineModule.loadLog.push("sourcing " + filename);
- let time = Date.now();
- this.withSavedValues(["sourcing"], function () {
- this.sourcing = null;
- try {
- var file = util.getFile(filename) || io.File(filename);
-
- if (!file.exists() || !file.isReadable() || file.isDirectory()) {
- if (!silent)
- dactyl.echoerr("E484: Can't open file " + filename.quote());
- return;
- }
-
- dactyl.echomsg("sourcing " + filename.quote(), 2);
-
- let uri = services.io.newFileURI(file);
-
- // handle pure JavaScript files specially
- if (/\.js$/.test(filename)) {
- try {
- dactyl.loadScript(uri.spec, Script(file));
- dactyl.helpInitialized = false;
- }
- catch (e) {
- if (e.fileName)
- try {
- e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, "");
- if (e.fileName == uri.spec)
- e.fileName = filename;
- e.echoerr = <>{e.fileName}:{e.lineNumber}: {e}>;
- }
- catch (e) {}
- throw e;
- }
- }
- else if (/\.css$/.test(filename))
- styles.registerSheet(uri.spec, false, true);
- else {
- commands.execute(file.read(), null, silent || "loud", null,
- { file: file.path, line: 1 });
- }
-
- if (this._scriptNames.indexOf(file.path) == -1)
- this._scriptNames.push(file.path);
-
- dactyl.echomsg("finished sourcing " + filename.quote(), 2);
-
- dactyl.log("Sourced: " + filename, 3);
- }
- catch (e) {
- if (!(e instanceof FailedAssertion))
- dactyl.reportError(e);
- let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
- if (!silent)
- dactyl.echoerr(message);
- }
- finally {
- defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms");
- }
- });
- },
-
// TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is
// fixed use that instead of a tmpfile
/**
@@ -418,7 +396,7 @@ var IO = Module("io", {
* @returns {string}
*/
system: function (command, input) {
- dactyl.echomsg("Calling shell to execute: " + command, 4);
+ util.dactyl.echomsg("Calling shell to execute: " + command, 4);
function escape(str) '"' + str.replace(/[\\"$]/g, "\\$&") + '"';
@@ -428,7 +406,8 @@ var IO = Module("io", {
else if (input)
stdin.write(input);
- let shell = File.expandPath(options["shell"]);
+ let shell = File.expandPath(storage["options"].get("shell").value);
+ let shcf = storage["options"].get("shellcmdflag").value;
if (isArray(command))
command = command.map(escape).join(" ");
@@ -436,12 +415,12 @@ var IO = Module("io", {
// TODO: implement 'shellredir'
if (util.OS.isWindows && !/sh/.test(options["shell"])) {
command = "cd /D " + this.cwd + " && " + command + " > " + stdout.path + " 2>&1" + " < " + stdin.path;
- var res = this.run(shell, options["shellcmdflag"].split(/\s+/).concat(command), true);
+ var res = this.run(shell, shcf.split(/\s+/).concat(command), true);
}
else {
cmd.write("cd " + escape(this.cwd) + "\n" +
["exec", ">" + escape(stdout.path), "2>&1", "<" + escape(stdin.path),
- escape(shell), options["shellcmdflag"], escape(command)].join(" "));
+ escape(shell), shcf, escape(command)].join(" "));
res = this.run("/bin/sh", ["-e", cmd.path], true);
}
@@ -496,9 +475,40 @@ var IO = Module("io", {
/**
* @property {string} The current platform's path separator.
*/
- PATH_SEP: File.PATH_SEP
+ PATH_SEP: deprecated("Please use File.PATH_SEP", { get: function PATH_SEP() File.PATH_SEP })
}, {
- commands: function () {
+ 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;
+
commands.add(["cd", "chd[ir]"],
"Change the current directory",
function (args) {
@@ -517,7 +527,7 @@ var IO = Module("io", {
dactyl.echomsg(io.cwd);
}
else {
- let dirs = options["cdpath"];
+ let dirs = modules.options["cdpath"];
for (let [, dir] in Iterator(dirs)) {
dir = File.joinPaths(dir, arg, io.cwd);
@@ -708,9 +718,9 @@ unlet s:cpo_save
commands: wrap("syn keyword " + config.name + "Command ",
array(c.specs for (c in commands)).flatten()),
options: wrap("syn keyword " + config.name + "Option ",
- array(o.names for (o in options) if (o.type != "boolean")).flatten()),
+ array(o.names for (o in modules.options) if (o.type != "boolean")).flatten()),
toggleoptions: wrap("let s:toggleOptions = [",
- array(o.realNames for (o in options) if (o.type == "boolean"))
+ array(o.realNames for (o in modules.options) if (o.type == "boolean"))
.flatten().map(String.quote),
", ") + "]"
}));
@@ -734,7 +744,7 @@ unlet s:cpo_save
commands.add(["scrip[tnames]"],
"List all sourced script names",
function () {
- commandline.commandOutput(
+ modules.commandline.commandOutput(
template.tabular(["", "Filename"], ["text-align: right; padding-right: 1em;"],
([i + 1, file] for ([i, file] in Iterator(io._scriptNames))))); // TODO: add colon and remove column titles for pedantic Vim compatibility?
},
@@ -768,7 +778,7 @@ unlet s:cpo_save
// This is an asinine and irritating feature when we have searchable
// command-line history. --Kris
- if (options["banghist"]) {
+ if (modules.options["banghist"]) {
// replaceable bang and no previous command?
dactyl.assert(!/((^|[^\\])(\\\\)*)!/.test(arg) || io._lastRunCommand,
"E34: No previous command");
@@ -782,10 +792,10 @@ unlet s:cpo_save
let output = io.system(arg);
- commandline.command = "!" + arg;
- commandline.commandOutput({output});
+ modules.commandline.command = "!" + arg;
+ modules.commandline.commandOutput({output});
- autocommands.trigger("ShellCmdPost", {});
+ modules.autocommands.trigger("ShellCmdPost", {});
}, {
argCount: "?", // TODO: "1" - probably not worth supporting weird Vim edge cases. The dream is dead. --djk
bang: true,
@@ -794,7 +804,9 @@ unlet s:cpo_save
literal: 0
});
},
- completion: function () {
+ completion: function (dactyl, modules, window) {
+ const { completion, io } = modules;
+
completion.charset = function (context) {
context.anchored = false;
context.keys = {
@@ -840,8 +852,8 @@ unlet s:cpo_save
};
context.compare = function (a, b) b.isdir - a.isdir || String.localeCompare(a.text, b.text);
- if (options["wildignore"]) {
- let wig = options.get("wildignore");
+ if (modules.options["wildignore"]) {
+ let wig = modules.options.get("wildignore");
context.filters.push(function (item) item.isdir || !wig.getKey(this.name));
}
@@ -881,7 +893,7 @@ unlet s:cpo_save
};
completion.runtime = function (context) {
- for (let [, dir] in Iterator(options["runtimepath"]))
+ for (let [, dir] in Iterator(modules.options["runtimepath"]))
context.fork(dir, 0, this, function (context) {
dir = dir.replace("/+$", "") + "/";
completion.file(context, true, dir + context.filter);
@@ -938,15 +950,17 @@ unlet s:cpo_save
completion.file(context, full);
});
},
- javascript: function () {
- JavaScript.setCompleter([File, File.expandPath],
+ javascript: function (dactyl, modules, window) {
+ modules.JavaScript.setCompleter([File, File.expandPath],
[function (context, obj, args) {
context.quote[2] = "";
completion.file(context, true);
}]);
},
- options: function () {
+ options: function (dactyl, modules, window) {
+ const { options } = modules;
+
var shell, shellcmdflag;
if (util.OS.isWindows) {
shell = "cmd.exe";
@@ -991,6 +1005,8 @@ unlet s:cpo_save
return /sh/.test(options["shell"]) ? "-c" : "/c";
}
});
+ options["shell"]; // Make sure it's loaded into global storage.
+ options["shellcmdflag"];
options.add(["wildignore", "wig"],
"List of file patterns to ignore when completing file names",
@@ -998,4 +1014,8 @@ unlet s:cpo_save
}
});
-// vim: set fdm=marker sw=4 ts=4 et:
+endModule();
+
+} catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
+
+// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm
index 9ce2bb93..3056438e 100644
--- a/common/modules/overlay.jsm
+++ b/common/modules/overlay.jsm
@@ -129,7 +129,9 @@ var Overlay = Module("Overlay", {
}
},
- newContext: function newContext(proto) {
+ newContext: function newContext(proto, normal) {
+ if (normal)
+ return create(proto);
let sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, wantXrays: false });
// Hack:
sandbox.Object = jsmodules.Object;
diff --git a/common/modules/services.jsm b/common/modules/services.jsm
index 343b89ea..7d4138f4 100644
--- a/common/modules/services.jsm
+++ b/common/modules/services.jsm
@@ -69,11 +69,12 @@ var Services = Module("Services", {
this.addClass("Find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
this.addClass("HtmlConverter","@mozilla.org/widget/htmlformatconverter;1", Ci.nsIFormatConverter);
this.addClass("HtmlEncoder", "@mozilla.org/layout/htmlCopyEncoder;1", Ci.nsIDocumentEncoder);
- this.addClass("StreamChannel","@mozilla.org/network/input-stream-channel;1",
- [Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI");
+ this.addClass("InputStream", "@mozilla.org/scriptableinputstream;1", Ci.nsIScriptableInputStream, "init");
this.addClass("Persist", "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", Ci.nsIWebBrowserPersist);
this.addClass("Pipe", "@mozilla.org/pipe;1", Ci.nsIPipe, "init");
this.addClass("Process", "@mozilla.org/process/util;1", Ci.nsIProcess, "init");
+ this.addClass("StreamChannel","@mozilla.org/network/input-stream-channel;1",
+ [Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI");
this.addClass("String", "@mozilla.org/supports-string;1", Ci.nsISupportsString, "data");
this.addClass("StringStream", "@mozilla.org/io/string-input-stream;1", Ci.nsIStringInputStream, "data");
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback");
diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm
index 7748d644..e9c3cf12 100644
--- a/common/modules/storage.jsm
+++ b/common/modules/storage.jsm
@@ -490,6 +490,21 @@ var File = Class("File", {
defaultEncoding: "UTF-8",
+ /**
+ * Expands "~" and environment variables in *path*.
+ *
+ * "~" is expanded to to the value of $HOME. On Windows if this is not
+ * set then the following are tried in order:
+ * $USERPROFILE
+ * ${HOMDRIVE}$HOMEPATH
+ *
+ * The variable notation is $VAR (terminated by a non-word character)
+ * or ${VAR}. %VAR% is also supported on Windows.
+ *
+ * @param {string} path The unexpanded path string.
+ * @param {boolean} relative Whether the path is relative or absolute.
+ * @returns {string}
+ */
expandPath: function (path, relative) {
function getenv(name) services.environment.get(name);
diff --git a/common/modules/util.jsm b/common/modules/util.jsm
index df758275..2c361142 100644
--- a/common/modules/util.jsm
+++ b/common/modules/util.jsm
@@ -59,13 +59,6 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
}
},
- addon: Class.memoize(function () {
- let addon = services.fuel.storage.get("dactyl.bootstrap", {}).addon;
- if (!addon)
- addon = AddonManager.getAddonByID(config.addonID);
- return addon;
- }),
-
// FIXME: Only works for Pentadactyl
get activeWindow() services.windowMediator.getMostRecentWindow("navigator:browser"),
dactyl: update(function dactyl(obj) {
@@ -1203,8 +1196,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
services.fuel.storage.set("dactyl.commandlineArgs", args);
this.timeout(function () {
this.rehashing = true;
- this.addon.userDisabled = true;
- this.addon.userDisabled = false;
+ config.addon.userDisabled = true;
+ config.addon.userDisabled = false;
});
},
@@ -1508,6 +1501,6 @@ var Math = update(Object.create(GlobalMath), {
endModule();
-} catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
+} catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
diff --git a/melodactyl/content/dactyl.dtd b/melodactyl/content/dactyl.dtd
deleted file mode 100644
index 23ba95cb..00000000
--- a/melodactyl/content/dactyl.dtd
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-%dactylBase;
-
diff --git a/melodactyl/locale/en-US/all.xml b/melodactyl/locale/en-US/all.xml
index 6af00427..7d838b98 100644
--- a/melodactyl/locale/en-US/all.xml
+++ b/melodactyl/locale/en-US/all.xml
@@ -1,7 +1,7 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-%dactylBase;
-
diff --git a/pentadactyl/locale/en-US/all.xml b/pentadactyl/locale/en-US/all.xml
index b0335c24..859eef45 100644
--- a/pentadactyl/locale/en-US/all.xml
+++ b/pentadactyl/locale/en-US/all.xml
@@ -1,7 +1,7 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
diff --git a/teledactyl/content/dactyl.dtd b/teledactyl/content/dactyl.dtd
deleted file mode 100644
index 0fc1a9e2..00000000
--- a/teledactyl/content/dactyl.dtd
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-%dactylBase;
-
diff --git a/teledactyl/locale/en-US/all.xml b/teledactyl/locale/en-US/all.xml
index cd195af1..59d250a5 100644
--- a/teledactyl/locale/en-US/all.xml
+++ b/teledactyl/locale/en-US/all.xml
@@ -1,7 +1,7 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+