// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2012 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 */
+"use strict";
/** @scope modules */
-default xml namespace = XHTML;
-XML.ignoreWhitespace = false;
-XML.prettyPrinting = false;
-
var EVAL_ERROR = "__dactyl_eval_error";
var EVAL_RESULT = "__dactyl_eval_result";
var EVAL_STRING = "__dactyl_eval_string";
init: function () {
window.dactyl = this;
// cheap attempt at compatibility
- let prop = { get: deprecated("dactyl", function liberator() dactyl) };
+ let prop = { get: deprecated("dactyl", function liberator() dactyl),
+ configurable: true };
Object.defineProperty(window, "liberator", prop);
Object.defineProperty(modules, "liberator", prop);
this.commands = {};
delete window.liberator;
// Prevents box ordering bugs after our stylesheet is removed.
- styles.system.add("cleanup-sheet", config.styleableChrome, <![CDATA[
+ styles.system.add("cleanup-sheet", config.styleableChrome, literal(/*
#TabsToolbar tab { display: none; }
- ]]>);
+ */));
styles.unregisterSheet("resource://dactyl-skin/dactyl.css");
DOM('#TabsToolbar tab', document).style.display;
},
signals: {
"io.source": function ioSource(context, file, modTime) {
- if (context.INFO)
+ if (contexts.getDocs(context))
help.flush("help/plugins.xml", modTime);
}
},
NEW_WINDOW: "window",
forceBackground: null,
+ forcePrivate: null,
forceTarget: null,
get forceOpen() ({ background: this.forceBackground,
for (let obj in values(results)) {
let res = dactyl.generateHelp(obj, null, null, true);
if (!haveTag(obj.helpTag))
- res[1].@tag = obj.helpTag;
+ res[0][1].tag = obj.helpTag;
yield res;
}
bell: document.getElementById("dactyl-bell"),
strut: document.getElementById("dactyl-bell-strut")
};
- XML.ignoreWhitespace = true;
if (!elems.bell)
overlay.overlayWindow(window, {
objects: elems,
- prepend: <>
- <window id={document.documentElement.id} xmlns={XUL}>
- <hbox style="display: none" highlight="Bell" id="dactyl-bell" key="bell"/>
- </window>
- </>,
- append: <>
- <window id={document.documentElement.id} xmlns={XUL}>
- <hbox style="display: none" highlight="Bell" id="dactyl-bell-strut" key="strut"/>
- </window>
- </>
+ prepend: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ ["hbox", { style: "display: none", highlight: "Bell", id: "dactyl-bell", key: "bell" }]]],
+ append: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ ["hbox", { style: "display: none", highlight: "Bell", id: "dactyl-bell-strut", key: "strut" }]]]
}, elems);
elems.bell.style.height = window.innerHeight + "px";
if (isObject(str) && "echoerr" in str)
str = str.echoerr;
else if (isinstance(str, ["Error", FailedAssertion]) && str.fileName)
- str = <>{str.fileName.replace(/^.* -> /, "")}: {str.lineNumber}: {str}</>;
+ str = [str.fileName.replace(/^.* -> /, ""), ": ", str.lineNumber, ": ", str].join("");
if (options["errorbells"])
dactyl.beep();
* @param {Object} context The context object into which the script
* should be loaded.
*/
- loadScript: function (uri, context) {
+ loadScript: function loadScript(uri, context) {
JSMLoader.loadSubScript(uri, context, File.defaultEncoding);
},
- userEval: function (str, context, fileName, lineNumber) {
+ userEval: function userEval(str, context, fileName, lineNumber) {
let ctxt;
if (jsmodules.__proto__ != window && jsmodules.__proto__ != XPCNativeWrapper(window) &&
jsmodules.isPrototypeOf(context))
* Acts like the Function builtin, but the code executes in the
* userContext global.
*/
- userFunc: function () {
+ userFunc: function userFunc() {
return this.userEval(
"(function userFunction(" + Array.slice(arguments, 0, -1).join(", ") + ")" +
" { " + arguments[arguments.length - 1] + " })");
* @param {boolean} silent Whether the command should be echoed on the
* command line.
*/
- execute: function (str, modifiers, silent) {
+ execute: function execute(str, modifiers, silent) {
// skip comments and blank lines
if (/^\s*("|$)/.test(str))
return;
* @param {string} feature The feature name.
* @returns {boolean}
*/
- has: function (feature) Set.has(config.features, feature),
+ has: function has(feature) Set.has(config.features, feature),
/**
* @private
*/
initHelp: function initHelp() {
if ("noscriptOverlay" in window)
- noscriptOverlay.safeAllow("dactyl:", true, false);
+ window.noscriptOverlay.safeAllow("dactyl:", true, false);
help.initialize();
},
- stringifyXML: function (xml) {
- XML.prettyPrinting = false;
- XML.ignoreWhitespace = false;
- return UTF8(xml.toXMLString());
- },
-
/**
* Generates a help entry and returns it as a string.
*
* @returns {string}
*/
generateHelp: function generateHelp(obj, extraHelp, str, specOnly) {
- default xml namespace = "";
-
let link, tag, spec;
link = tag = spec = util.identity;
let args = null;
if (obj instanceof Command) {
- link = function (cmd) <ex>{cmd}</ex>;
+ link = function (cmd) ["ex", {}, cmd];
args = obj.parseArgs("", CompletionContext(str || ""));
- tag = function (cmd) <>:{cmd}</>;
- spec = function (cmd) <>{
- obj.count ? <oa>count</oa> : <></>
- }{
- cmd
- }{
- obj.bang ? <oa>!</oa> : <></>
- }</>;
+ tag = function (cmd) DOM.DOMString(":" + cmd);
+ spec = function (cmd) [
+ obj.count ? ["oa", {}, "count"] : [],
+ cmd,
+ obj.bang ? ["oa", {}, "!"] : []
+ ];
}
else if (obj instanceof Map) {
- spec = function (map) obj.count ? <><oa>count</oa>{map}</> : <>{map}</>;
- tag = function (map) <>{
- let (c = obj.modes[0].char) c ? c + "_" : ""
- }{ map }</>;
+ spec = function (map) obj.count ? [["oa", {}, "count"], map] : DOM.DOMString(map);
+ tag = function (map) [
+ let (c = obj.modes[0].char) c ? c + "_" : "",
+ map
+ ]
link = function (map) {
let [, mode, name, extra] = /^(?:(.)_)?(?:<([^>]+)>)?(.*)$/.exec(map);
- let k = <k>{extra}</k>;
+ let k = ["k", {}, extra];
if (name)
- k.@name = name;
+ k[1].name = name;
if (mode)
- k.@mode = mode;
+ k[1].mode = mode;
return k;
};
}
else if (obj instanceof Option) {
spec = function () template.map(obj.names, tag, " ");
- tag = function (name) <>'{name}'</>;
- link = function (opt, name) <o>{name}</o>;
+ tag = function (name) DOM.DOMString("'" + name + "'");
+ link = function (opt, name) ["o", {}, name];
args = { value: "", values: [] };
}
- XML.prettyPrinting = false;
- XML.ignoreWhitespace = false;
- default xml namespace = NS;
-
- // E4X has its warts.
- let br = <>
- </>;
-
- let res = <res>
- <dt>{link(obj.helpTag || tag(obj.name), obj.name)}</dt> <dd>{
- template.linkifyHelp(obj.description ? obj.description.replace(/\.$/, "") : "", true)
- }</dd></res>;
+ let res = [
+ ["dt", {}, link(obj.helpTag || tag(obj.name), obj.name)],
+ ["dd", {},
+ template.linkifyHelp(obj.description ? obj.description.replace(/\.$/, "") : "", true)]];
if (specOnly)
- return res.elements();
+ return res;
- res.* += <>
- <item>
- <tags>{template.map(obj.names.slice().reverse(), tag, " ")}</tags>
- <spec>{let (name = (obj.specs || obj.names)[0])
+ let description = ["description", {},
+ obj.description ? ["p", {}, template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)] : "",
+ extraHelp ? extraHelp : "",
+ !(extraHelp || obj.description) ? ["p", {}, /*L*/ "Sorry, no help available."] : ""]
+
+ res.push(
+ ["item", {},
+ ["tags", {}, template.map(obj.names.slice().reverse(),
+ tag,
+ " ").join("")],
+ ["spec", {},
+ let (name = (obj.specs || obj.names)[0])
spec(template.highlightRegexp(tag(name),
/\[(.*?)\]/g,
- function (m, n0) <oa>{n0}</oa>),
- name)
- }</spec>{
- !obj.type ? "" : <>
- <type>{obj.type}</type>
- <default>{obj.stringDefaultValue}</default></>}
- <description>{
- obj.description ? br + <p>{template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)}</p> : "" }{
- extraHelp ? br + extraHelp : "" }{
- !(extraHelp || obj.description) ? br + <p><!--L-->Sorry, no help available.</p> : "" }
- </description>
- </item></>;
+ function (m, n0) ["oa", {}, n0]),
+ name)],
+ !obj.type ? "" : [
+ ["type", {}, obj.type],
+ ["default", {}, obj.stringDefaultValue]],
+ description]);
function add(ary) {
- res.item.description.* += br +
- let (br = br + <> </>)
- <><dl>{ br + template.map(ary, function ([a, b]) <><dt>{a}</dt> <dd>{b}</dd></>, br) }
- </dl>
- </>;
+ description.push(
+ ["dl", {}, template.map(ary,
+ function ([a, b]) [["dt", {}, a], " ",
+ ["dd", {}, b]])]);
}
- if (obj.completer)
+ if (obj.completer && false)
add(completion._runCompleter(obj.closure.completer, "", null, args).items
.map(function (i) [i.text, i.description]));
- if (obj.options && obj.options.some(function (o) o.description))
+ if (obj.options && obj.options.some(function (o) o.description) && false)
add(obj.options.filter(function (o) o.description)
.map(function (o) [
o.names[0],
- <>{o.description}{
- o.names.length == 1 ? "" :
- <> (short name: {
- template.map(o.names.slice(1), function (n) <em>{n}</em>, <>, </>)
- })</>
- }</>
+ [o.description,
+ o.names.length == 1 ? "" :
+ ["", " (short name: ",
+ template.map(o.names.slice(1), function (n) ["em", {}, n], ", "),
+ ")"]]
]));
- return res.*.toXMLString()
- .replace(' xmlns="' + NS + '"', "", "g")
- .replace(/^ {12}|[ \t]+$/gm, "")
- .replace(/^\s*\n|\n\s*$/g, "") + "\n";
+
+ return DOM.toPrettyXML(res, true, null, { "": String(NS) });
},
/**
get: function globalVariables() this._globalVariables
}),
- loadPlugins: function (args, force) {
+ loadPlugins: function loadPlugins(args, force) {
function sourceDirectory(dir) {
dactyl.assert(dir.isReadable(), _("io.notReadable", dir.path));
* @param {string|Object} msg The message to print.
* @param {number} level The logging level 0 - 15.
*/
- log: function (msg, level) {
+ log: function log(msg, level) {
let verbose = config.prefs.get("loglevel", 0);
if (!level || level <= verbose) {
* tabs.
* @returns {boolean}
*/
- open: function (urls, params, force) {
+ open: function open(urls, params, force) {
if (typeof urls == "string")
urls = dactyl.parseURLs(urls);
});
case dactyl.NEW_WINDOW:
- let win = window.openDialog(document.documentURI, "_blank", "chrome,all,dialog=no");
+ let options = ["chrome", "all", "dialog=no"];
+ if (dactyl.forcePrivate)
+ options.push("private");
+
+ let win = window.openDialog(document.documentURI, "_blank", options.join(","));
util.waitFor(function () win.document.readyState === "complete");
browser = win.dactyl && win.dactyl.modules.config.tabbrowser || win.getBrowser();
// FALLTHROUGH
// Try to find a matching file.
let file = io.File(url);
if (file.exists() && file.isReadable())
- return services.io.newFileURI(file).spec;
+ return file.URI.spec;
}
catch (e) {}
}
}, this);
},
stringToURLArray: deprecated("dactyl.parseURLs", "parseURLs"),
- urlish: Class.Memoize(function () util.regexp(<![CDATA[
+ urlish: Class.Memoize(function () util.regexp(literal(/*
^ (
<domain>+ (:\d+)? (/ .*) |
<domain>+ (:\d+) |
<domain>+ \. [a-z0-9]+ |
localhost
) $
- ]]>, "ix", {
- domain: util.regexp(String.replace(<![CDATA[
+ */), "ix", {
+ domain: util.regexp(String.replace(literal(/*
[^
U0000-U002c // U002d-U002e --.
U002f // /
U005b-U0060 // U0061-U007a A-Z
U007b-U007f
]
- ]]>, /U/g, "\\u"), "x")
+ */), /U/g, "\\u"), "x")
})),
pluginFiles: {},
* @param {boolean} force Forcibly quit irrespective of whether all
* windows could be closed individually.
*/
- quit: function (saveSession, force) {
+ quit: function quit(saveSession, force) {
if (!force && !this.confirmQuit())
return;
/**
* Restart the host application.
*/
- restart: function (args) {
+ restart: function restart(args) {
if (!this.confirmQuit())
return;
* @returns {Object}
* @see Commands#parseArgs
*/
- parseCommandLine: function (cmdline) {
+ parseCommandLine: function parseCommandLine(cmdline) {
try {
return commands.get("rehash").parseArgs(cmdline);
}
return [];
}
},
- wrapCallback: function (callback, self) {
+ wrapCallback: function wrapCallback(callback, self) {
self = self || this;
let save = ["forceOpen"];
let saved = save.map(function (p) dactyl[p]);
}, {
toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true"
}, {
- cache: function () {
+ cache: function initCache() {
cache.register("help/plugins.xml", function () {
// Process plugin help entries.
- XML.ignoreWhiteSpace = XML.prettyPrinting = false;
- let body = XML();
+ let body = [];
for (let [, context] in Iterator(plugins.contexts))
try {
let info = contexts.getDocs(context);
- if (info instanceof XML) {
- if (info.*.@lang.length()) {
- let lang = config.bestLocale(String(a) for each (a in info.*.@lang));
-
- info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang);
-
- for each (let elem in info.NS::info)
- for (let attr in values(["@name", "@summary", "@href"]))
- if (elem[attr].length())
- info[attr] = elem[attr];
+ if (DOM.isJSONXML(info)) {
+ let langs = info.slice(2).filter(function (e) isArray(e) && isObject(e[1]) && e[1].lang);
+ if (langs) {
+ let lang = config.bestLocale(l[1].lang for each (l in langs));
+
+ info = info.slice(0, 2).concat(
+ info.slice(2).filter(function (e) !isArray(e) || !isObject(e[1])
+ || e[1].lang == lang));
+
+ for each (let elem in info.slice(2).filter(function (e) isArray(e) && e[0] == "info" && isObject(e[1])))
+ for (let attr in values(["name", "summary", "href"]))
+ if (attr in elem[1])
+ info[attr] = elem[1][attr];
}
- body += <h2 xmlns={NS.uri} tag={info.@name + '-plugin'}>{info.@summary}</h2> +
- info;
+ body.push(["h2", { xmlns: "dactyl", tag: info[1].name + '-plugin' },
+ String(info[1].summary)]);
+ body.push(info);
}
}
catch (e) {
return '<?xml version="1.0"?>\n' +
'<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
- '<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
- <document xmlns={NS}
- name="plugins" title={config.appName + " Plugins"}>
- <h1 tag="using-plugins">{_("help.title.Using Plugins")}</h1>
- <toc start="2"/>
-
- {body}
- </document>.toXMLString();
+ DOM.toXML(
+ ["document", { xmlns: "dactyl", name: "plugins",
+ title: config.appName + ", Plugins" },
+ ["h1", { tag: "using-plugins" }, _("help.title.Using Plugins")],
+ ["toc", { start: "2" }],
+
+ body]);
});
cache.register("help/index.xml", function () {
- default xml namespace = NS;
-
return '<?xml version="1.0"?>\n' +
- <overlay xmlns={NS}>{
- template.map(dactyl.indices, function ([name, iter])
- <dl insertafter={name + "-index"}>{
- template.map(iter(), util.identity)
- }</dl>, <>{"\n\n"}</>)
- }</overlay>;
+ DOM.toXML(["overlay", { xmlns: "dactyl" },
+ template.map(dactyl.indices, function ([name, iter])
+ ["dl", { insertafter: name + "-index" },
+ template.map(iter(), util.identity)],
+ "\n\n")]);
});
cache.register("help/gui.xml", function () {
- default xml namespace = NS;
-
return '<?xml version="1.0"?>\n' +
- <overlay xmlns={NS}>
- <dl insertafter="dialog-list">{
- template.map(config.dialogs, function ([name, val])
- (!val[2] || val[2]())
- ? <><dt>{name}</dt><dd>{val[0]}</dd></>
- : undefined,
- <>{"\n"}</>)
- }</dl>
- </overlay>;
+ DOM.toXML(["overlay", { xmlns: "dactyl" },
+ ["dl", { insertafter: "dialog-list" },
+ template.map(config.dialogs, function ([name, val])
+ (!val[2] || val[2]())
+ ? [["dt", {}, name],
+ ["dd", {}, val[0]]]
+ : undefined,
+ "\n")]]);
});
cache.register("help/privacy.xml", function () {
- default xml namespace = NS;
-
return '<?xml version="1.0"?>\n' +
- <overlay xmlns={NS}>
- <dl insertafter="sanitize-items">{
- template.map(options.get("sanitizeitems").values
- .sort(function (a, b) String.localeCompare(a.name, b.name)),
- function ({ name, description })
- <><dt>{name}</dt><dd>{template.linkifyHelp(description, true)}</dd></>,
- <>{"\n"}</>)
- }</dl>
- </overlay>;
+ DOM.toXML(["overlay", { xmlns: "dactyl" },
+ ["dl", { insertafter: "sanitize-items" },
+ template.map(options.get("sanitizeitems").values
+ .sort(function (a, b) String.localeCompare(a.name,
+ b.name)),
+ function ({ name, description })
+ [["dt", {}, name],
+ ["dd", {}, template.linkifyHelp(description, true)]],
+ "\n")]]);
});
},
- events: function () {
+ events: function initEvents() {
events.listen(window, dactyl, "events", true);
},
// Only general options are added here, which are valid for all Dactyl extensions
- options: function () {
+ options: function initOptions() {
options.add(["errorbells", "eb"],
"Ring the bell when an error message is displayed",
"boolean", false);
document.title = document.title.replace(RegExp("(.*)" + util.regexp.escape(old)), "$1" + current);
}
- if (services.has("privateBrowsing")) {
+ if (win.hasAttribute("titlemodifier_privatebrowsing")) {
let oldValue = win.getAttribute("titlemodifier_normal");
let suffix = win.getAttribute("titlemodifier_privatebrowsing").substr(oldValue.length);
win.setAttribute("titlemodifier_normal", value);
win.setAttribute("titlemodifier_privatebrowsing", value + suffix);
- if (services.privateBrowsing.privateBrowsingEnabled) {
+ if (storage.privateMode) {
updateTitle(oldValue + suffix, value + suffix);
+ win.setAttribute("titlemodifier", value + suffix);
return value;
}
}
});
},
- mappings: function () {
+ mappings: function initMappings() {
if (dactyl.has("session"))
mappings.add([modes.NORMAL], ["ZQ"],
"Quit and don't save the session",
function () { dactyl.quit(true); });
},
- commands: function () {
+ commands: function initCommands() {
commands.add(["dia[log]"],
"Open a " + config.appName + " dialog",
function (args) {
literal: 0
});
+ commands.add(["pr[ivate]", "pr0n", "porn"],
+ "Enable privacy features of a command, when applicable, and do not save the invocation in command history",
+ function (args) {
+ dactyl.withSavedValues(["forcePrivate"], function () {
+ this.forcePrivate = true;
+ dactyl.execute(args[0], null, true);
+ });
+ }, {
+ argCount: "1",
+ completer: function (context) completion.ex(context),
+ literal: 0,
+ privateData: "never-save",
+ subCommand: 0
+ });
+
commands.add(["exit", "x"],
"Quit " + config.appName,
function (args) {
"Reload the " + config.appName + " add-on",
function (args) {
if (args.trailing)
- storage.session.rehashCmd = args.trailing; // Hack.
+ storage.storeForSession("rehashCmd", args.trailing); // Hack.
args.break = true;
if (args["+purgecaches"])
cache.flush();
- util.rehash(args);
+ util.delay(function () { util.rehash(args) });
},
{
argCount: "0", // FIXME
totalUnits = "msec";
commandline.commandOutput(
- <table>
- <tr highlight="Title" align="left">
- <th colspan="3">{_("title.Code execution summary")}</th>
- </tr>
- <tr><td>  {_("title.Executed")}:</td><td align="right"><span class="times-executed">{count}</span></td><td><!--L-->times</td></tr>
- <tr><td>  {_("title.Average time")}:</td><td align="right"><span class="time-average">{each.toFixed(2)}</span></td><td>{eachUnits}</td></tr>
- <tr><td>  {_("title.Total time")}:</td><td align="right"><span class="time-total">{total.toFixed(2)}</span></td><td>{totalUnits}</td></tr>
- </table>);
+ ["table", {}
+ ["tr", { highlight: "Title", align: "left" },
+ ["th", { colspan: "3" }, _("title.Code execution summary")]],
+ ["tr", {},
+ ["td", {}, _("title.Executed"), ":"],
+ ["td", { align: "right" },
+ ["span", { class: "times-executed" }, count]],
+ ["td", {}, /*L*/"times"]],
+ ["tr", {},
+ ["td", {}, _("title.Average time"), ":"],
+ ["td", { align: "right" },
+ ["span", { class: "time-average" }, each.toFixed(2)]],
+ ["td", {}, eachUnits]],
+ ["tr", {},
+ ["td", {}, _("title.Total time"), ":"],
+ ["td", { align: "right" },
+ ["span", { class: "time-total" }, total.toFixed(2)]],
+ ["td", {}, totalUnits]]]);
}
else {
let beforeTime = Date.now();
let date = config.buildDate;
date = date ? " (" + date + ")" : "";
- commandline.commandOutput(
- <div>{config.appName} {config.version}{date} running on: </div> +
- <div>{navigator.userAgent}</div>)
+ commandline.commandOutput([
+ ["div", {}, [config.appName, " ", config.version, date, " running on: "].join("")],
+ ["div", {}, [window.navigator.userAgent].join("")]
+ ])
}
}, {
argCount: "0",
},
- completion: function () {
+ completion: function initCompletion() {
completion.dialog = function dialog(context) {
context.title = ["Dialog"];
context.filters.push(function ({ item }) !item[2] || item[2]());
context.completions = dactyl.windows;
};
},
- load: function () {
+ load: function initLoad() {
dactyl.triggerObserver("load");
dactyl.log(_("dactyl.modulesLoaded"), 3);
userContext.DOM = Class("DOM", DOM, { init: function DOM_(sel, ctxt) DOM(sel, ctxt || buffer.focusedFrame.document) });
userContext.$ = modules.userContext.DOM;
+ // Hack: disable disabling of Personas in private windows.
+ let root = document.documentElement;
+
+ if (PrivateBrowsingUtils && PrivateBrowsingUtils.isWindowPrivate(window)
+ && root._lightweightTheme
+ && root._lightweightTheme._lastScreenWidth == null) {
+
+ dactyl.withSavedValues.call(PrivateBrowsingUtils,
+ ["isWindowPrivate"], function () {
+ PrivateBrowsingUtils.isWindowPrivate = function () false;
+
+ Cu.import("resource://gre/modules/LightweightThemeConsumer.jsm", {})
+ .LightweightThemeConsumer.call(root._lightweightTheme, document);
+ });
+ }
+
dactyl.timeout(function () {
try {
var args = config.prefs.get("commandline-args")