// 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";
try {
-Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("util", {
exports: ["DOM", "$", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
require: ["dom", "services"]
-}, this);
+});
+
+lazyRequire("overlay", ["overlay"]);
+lazyRequire("storage", ["File", "storage"]);
+lazyRequire("template", ["template"]);
+
+var Magic = Class("Magic", {
+ init: function init(str) {
+ this.str = str;
+ },
+
+ get message() this.str,
-this.lazyRequire("overlay", ["overlay"]);
+ toString: function () this.str
+});
var FailedAssertion = Class("FailedAssertion", ErrorBase, {
init: function init(message, level, noTrace) {
}
var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), {
- init: function () {
+ Magic: Magic,
+
+ init: function init() {
this.Array = array;
this.addObserver(this);
parseForm: deprecated("DOM#formData", function parseForm(elem) values(DOM(elem).formData).toArray()),
scrollIntoView: deprecated("DOM#scrollIntoView", function scrollIntoView(elem, alignWithTop) DOM(elem).scrollIntoView(alignWithTop)),
validateMatcher: deprecated("DOM.validateMatcher", { get: function validateMatcher() DOM.validateMatcher }),
+ xmlToDom: deprecated("DOM.fromJSON", function xmlToDom() DOM.fromXML.apply(DOM, arguments)),
map: deprecated("iter.map", function map(obj, fn, self) iter(obj).map(fn, self).toArray()),
writeToClipboard: deprecated("dactyl.clipboardWrite", function writeToClipboard(str, verbose) util.dactyl.clipboardWrite(str, verbose)),
var global = Class.objectGlobal(obj);
return {
- __noSuchMethod__: function (meth, args) {
+ __noSuchMethod__: function __noSuchMethod__(meth, args) {
let win = overlay.activeWindow;
var dactyl = global && global.dactyl || win && win.dactyl;
}
};
}, {
- __noSuchMethod__: function () this().__noSuchMethod__.apply(null, arguments)
+ __noSuchMethod__: function __noSuchMethod__() this().__noSuchMethod__.apply(null, arguments)
}),
/**
* @param {string} message The message to present to the
* user on failure.
*/
- assert: function (condition, message, quiet) {
+ assert: function assert(condition, message, quiet) {
if (!condition)
throw FailedAssertion(message, 1, quiet === undefined ? true : quiet);
return condition;
{
elements: [],
seen: {},
- valid: function (obj) this.elements.every(function (e) !e.test || e.test(obj))
+ valid: function valid(obj) this.elements.every(function (e) !e.test || e.test(obj))
});
let end = 0;
stack.top.elements.push(update(
function (obj) obj[char] != null ? quote(obj, char) : "",
- { test: function (obj) obj[char] != null }));
+ { test: function test(obj) obj[char] != null }));
for (let elem in array.iterValues(stack))
elem.seen[char] = true;
{
elements: [],
seen: {},
- valid: function (obj) this.elements.every(function (e) !e.test || e.test(obj))
+ valid: function valid(obj) this.elements.every(function (e) !e.test || e.test(obj))
});
let defaults = { lt: "<", gt: ">" };
- let re = util.regexp(<![CDATA[
+ let re = util.regexp(literal(/*
([^]*?) // 1
(?:
(<\{) | // 2
(< ((?:[a-z]-)?[a-z-]+?) (?:\[([0-9]+)\])? >) | // 3 4 5
(\}>) // 6
)
- ]]>, "gixy");
+ */), "gixy");
macro = String(macro);
let end = 0;
for (let match in re.iterate(macro)) {
}
else if (close) {
stack.pop();
- util.assert(stack.length, /*L*/"Unmatched %] in macro");
+ util.assert(stack.length, /*L*/"Unmatched }> in macro");
}
else {
let [, flags, name] = /^((?:[a-z]-)*)(.*)/.exec(macro);
function (obj) obj[name] != null && idx in obj[name] ? quote(obj[name][idx])
: Set.has(obj, name) ? "" : unknown(full),
{
- test: function (obj) obj[name] != null && idx in obj[name]
- && obj[name][idx] !== false
- && (!flags.e || obj[name][idx] != "")
+ test: function test(obj) obj[name] != null && idx in obj[name]
+ && obj[name][idx] !== false
+ && (!flags.e || obj[name][idx] != "")
}));
}
else {
function (obj) obj[name] != null ? quote(obj[name])
: Set.has(obj, name) ? "" : unknown(full),
{
- test: function (obj) obj[name] != null
- && obj[name] !== false
- && (!flags.e || obj[name] != "")
+ test: function test(obj) obj[name] != null
+ && obj[name] !== false
+ && (!flags.e || obj[name] != "")
}));
}
}
},
+ /**
+ * Briefly delay the execution of the passed function.
+ *
+ * @param {function} callback The function to delay.
+ */
+ delay: function delay(callback) {
+ let { mainThread } = services.threading;
+ mainThread.dispatch(callback,
+ mainThread.DISPATCH_NORMAL);
+ },
+
/**
* Removes certain backslash-quoted characters while leaving other
* backslash-quoting sequences untouched.
* @param {string} stack The stack trace from an Error.
* @returns {[string]} The stack frames.
*/
- stackLines: function (stack) {
+ stackLines: function stackLines(stack) {
let lines = [];
let match, re = /([^]*?)@([^@\n]*)(?:\n|$)/g;
while (match = re.exec(stack))
* @param {string} url
* @returns {string|null}
*/
- getHost: function (url) {
+ getHost: function getHost(url) {
try {
return util.createURI(url).host;
}
* @param {Object} r2
* @returns {Object}
*/
- intersection: function (r1, r2) ({
+ intersection: function intersection(r1, r2) ({
get width() this.right - this.left,
get height() this.bottom - this.top,
left: Math.max(r1.left, r2.left),
},
// ripped from Firefox; modified
- unsafeURI: Class.Memoize(function () util.regexp(String.replace(<![CDATA[
+ unsafeURI: Class.Memoize(function () util.regexp(String.replace(literal(/*
[
\s
// Invisible characters (bug 452979)
// Bidi formatting characters. (RFC 3987 sections 3.2 and 4.1 paragraph 6)
U200E U200F U202A U202B U202C U202D U202E
]
- ]]>, /U/g, "\\u"),
+ */), /U/g, "\\u"),
"gx")),
losslessDecodeURI: function losslessDecodeURI(url) {
return url.split("%25").map(function (url) {
* for *obj*.
*/
makeDTD: let (map = { "'": "'", '"': """, "%": "%", "&": "&", "<": "<", ">": ">" })
- function makeDTD(obj) iter(obj)
- .map(function ([k, v]) ["<!ENTITY ", k, " '", String.replace(v == null ? "null" : typeof v == "xml" ? v.toXMLString() : v,
- typeof v == "xml" ? /['%]/g : /['"%&<>]/g,
- function (m) map[m]),
- "'>"].join(""))
- .join("\n"),
+ function makeDTD(obj) {
+ function escape(val) {
+ let isDOM = DOM.isJSONXML(val);
+ return String.replace(val == null ? "null" :
+ isDOM ? DOM.toXML(val)
+ : val,
+ isDOM ? /['%]/g
+ : /['"%&<>]/g,
+ function (m) map[m]);
+ }
+
+ return iter(obj).map(function ([k, v])
+ ["<!ENTITY ", k, " '", escape(v), "'>"].join(""))
+ .join("\n");
+ },
/**
* Converts a URI string into a URI object.
* @returns {string}
*/
objectToString: function objectToString(object, color) {
- // Use E4X literals so html is automatically quoted
- // only when it's asked for. No one wants to see <
- // on their console or :map :foo in their buffer
- // when they expect :map <C-f> :foo.
- XML.prettyPrinting = false;
- XML.ignoreWhitespace = false;
-
if (object == null)
return object + "\n";
catch (e) {
obj = Object.prototype.toString.call(obj);
}
- obj = template.highlightFilter(util.clip(obj, 150), "\n", !color ? function () "^J" : function () <span highlight="NonText">^J</span>);
- let string = <><span highlight="Title Object">{obj}</span>::
</>;
+
+ if (color) {
+ obj = template.highlightFilter(util.clip(obj, 150), "\n",
+ function () ["span", { highlight: "NonText" }, "^J"]);
+ var head = ["span", { highlight: "Title Object" }, obj, "::\n"];
+ }
+ else
+ head = util.clip(obj, 150).replace(/\n/g, "^J") + "::\n";
let keys = [];
keyIter = keys(object)
for (let i in keyIter) {
- let value = <![CDATA[<no value>]]>;
+ let value = Magic("<no value>");
try {
value = object[i];
}
}
}
- value = template.highlight(value, true, 150, !color);
- let key = <span highlight="Key">{i}</span>;
+ let key = i;
if (!isNaN(i))
i = parseInt(i);
else if (/^[A-Z_]+$/.test(i))
i = "";
- keys.push([i, <>{noVal ? value : <>{key}: {value}</>}
</>]);
+
+ if (color)
+ value = template.highlight(value, true, 150, !color);
+ else if (value instanceof Magic)
+ value = String(value);
+ else
+ value = util.clip(String(value).replace(/\n/g, "^J"), 150);
+
+ if (noVal)
+ var val = value;
+ else if (color)
+ val = [["span", { highlight: "Key" }, key], ": ", value];
+ else
+ val = key + ": " + value;
+
+ keys.push([i, val]);
}
}
catch (e) {
return a[0] - b[0];
return String.localeCompare(a[0], b[0]);
}
- string += template.map(keys.sort(compare), function (f) f[1]);
- return color ? <div style="white-space: pre-wrap;">{string}</div> : [s for each (s in string)].join("");
+
+ let vals = template.map(keys.sort(compare), function (f) f[1], "\n");
+ if (color) {
+ return ["div", { style: "white-space: pre-wrap" }, head, vals];
+ }
+ return head + vals.join("");
+ },
+
+ prettifyJSON: function prettifyJSON(data, indent, invalidOK) {
+ const INDENT = indent || " ";
+
+ function rec(data, level, seen) {
+ if (isObject(data)) {
+ if (~seen.indexOf(data))
+ throw Error("Recursive object passed");
+ seen = seen.concat([data]);
+ }
+
+ let prefix = level + INDENT;
+
+ if (data === undefined)
+ data = null;
+
+ if (~["boolean", "number"].indexOf(typeof data) || data === null)
+ res.push(String(data));
+ else if (isinstance(data, ["String", _]))
+ res.push(JSON.stringify(String(data)));
+ else if (isArray(data)) {
+ if (data.length == 0)
+ res.push("[]");
+ else {
+ res.push("[\n")
+ for (let [i, val] in Iterator(data)) {
+ if (i)
+ res.push(",\n");
+ res.push(prefix)
+ rec(val, prefix, seen);
+ }
+ res.push("\n", level, "]");
+ }
+ }
+ else if (isObject(data)) {
+ res.push("{\n")
+
+ let i = 0;
+ for (let [key, val] in Iterator(data)) {
+ if (i++)
+ res.push(",\n");
+ res.push(prefix, JSON.stringify(key), ": ")
+ rec(val, prefix, seen);
+ }
+ if (i > 0)
+ res.push("\n", level, "}")
+ else
+ res[res.length - 1] = "{}";
+ }
+ else if (invalidOK)
+ res.push({}.toString.call(data));
+ else
+ throw Error("Invalid JSON object");
+ }
+
+ let res = [];
+ rec(data, "", []);
+ return res.join("");
},
observers: {
- "dactyl-cleanup-modules": function (subject, reason) {
+ "dactyl-cleanup-modules": function cleanupModules(subject, reason) {
defineModule.loadLog.push("dactyl: util: observe: dactyl-cleanup-modules " + reason);
for (let module in values(defineModule.modules))
if (!this.rehashing)
services.observer.addObserver(this, "dactyl-rehash", true);
},
- "dactyl-rehash": function () {
+ "dactyl-rehash": function dactylRehash() {
services.observer.removeObserver(this, "dactyl-rehash");
defineModule.loadLog.push("dactyl: util: observe: dactyl-rehash");
module.init();
}
},
- "dactyl-purge": function () {
+ "dactyl-purge": function dactylPurge() {
this.rehashing = 1;
},
},
// Replace replacement <tokens>.
if (tokens)
- expr = String.replace(expr, /(\(?P)?<(\w+)>/g, function (m, n1, n2) !n1 && Set.has(tokens, n2) ? tokens[n2].dactylSource || tokens[n2].source || tokens[n2] : m);
+ expr = String.replace(expr, /(\(?P)?<(\w+)>/g,
+ function (m, n1, n2) !n1 && Set.has(tokens, n2) ? tokens[n2].dactylSource
+ || tokens[n2].source
+ || tokens[n2]
+ : m);
// Strip comments and white space.
if (/x/.test(flags))
let res = update(RegExp(expr, flags.replace("x", "")), {
closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
dactylPropertyNames: ["exec", "match", "test", "toSource", "toString", "global", "ignoreCase", "lastIndex", "multiLine", "source", "sticky"],
- iterate: function (str, idx) util.regexp.iterate(this, str, idx)
+ iterate: function iterate(str, idx) util.regexp.iterate(this, str, idx)
});
// Return a struct with properties for named parameters if we
* Reloads dactyl in entirety by disabling the add-on and
* re-enabling it.
*/
- rehash: function (args) {
- storage.session.commandlineArgs = args;
+ rehash: function rehash(args) {
+ storage.storeForSession("commandlineArgs", args);
this.timeout(function () {
this.flushCache();
this.rehashing = true;
let obj = update({}, error, {
toString: function () String(error),
- stack: <>{util.stackLines(String(error.stack || Error().stack)).join("\n").replace(/^/mg, "\t")}</>
+ stack: Magic(util.stackLines(String(error.stack || Error().stack)).join("\n").replace(/^/mg, "\t"))
});
services.console.logStringMessage(obj.stack);
* @param {Window} window
* @returns {nsISelectionController}
*/
- selectionController: function (win)
+ selectionController: function selectionController(win)
win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay)
.QueryInterface(Ci.nsISelectionController),
*
* @param {number} delay The time period for which to sleep in milliseconds.
*/
- sleep: function (delay) {
+ sleep: function sleep(delay) {
let mainThread = services.threading.mainThread;
let end = Date.now() + delay;
* @param {number} limit The maximum number of elements to return.
* @returns {[string]}
*/
- split: function (str, re, limit) {
+ split: function split(str, re, limit) {
re.lastIndex = 0;
if (!re.global)
re = RegExp(re.source || re, "g");
* interrupted by pressing <C-c>, in which case,
* Error("Interrupted") will be thrown.
*/
- threadYield: function (flush, interruptable) {
+ threadYield: function threadYield(flush, interruptable) {
this.yielders++;
try {
let mainThread = services.threading.mainThread;
* @param {nsIDOMWindow} win The window for which to find domains.
* @returns {[string]} The visible domains.
*/
- visibleHosts: function (win) {
+ visibleHosts: function visibleHosts(win) {
let res = [], seen = {};
(function rec(frame) {
try {
* @param {nsIDOMWindow} win The window for which to find URIs.
* @returns {[nsIURI]} The visible URIs.
*/
- visibleURIs: function (win) {
+ visibleURIs: function visibleURIs(win) {
let res = [], seen = {};
(function rec(frame) {
try {
catch (e) {
throw e.stack ? e : Error(e);
}
- },
-
- xmlToDom: function () DOM.fromXML.apply(DOM, arguments)
+ }
}, {
Array: array
});
* @singleton
*/
var GlobalMath = Math;
-var Math = update(Object.create(GlobalMath), {
+this.Math = update(Object.create(GlobalMath), {
/**
* Returns the specified *value* constrained to the range *min* - *max*.
*