diff --git a/content/buffer.js b/content/buffer.js
index be65cb0d..2e923b76 100644
--- a/content/buffer.js
+++ b/content/buffer.js
@@ -26,8 +26,6 @@ the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
const Point = new Struct("x", "y");
function Buffer() //{{{
@@ -35,241 +33,6 @@ function Buffer() //{{{
////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
-
- const highlightClasses = ["Boolean", "ErrorMsg", "Filter", "Function", "InfoMsg", "Keyword",
- "LineNr", "ModeMsg", "MoreMsg", "Normal", "Null", "Number", "Object", "Question",
- "StatusLine", "StatusLineBroken", "StatusLineSecure", "String", "TabClose", "TabIcon",
- "TabIconNumber", "TabNumber", "TabText", "Tag", "Title", "URL", "WarningMsg",
- ["Hint", ".liberator-hint", "*"],
- ["Search", ".__liberator-search", "*"],
- ["Bell", "#liberator-visualbell"],
- ];
- const highlightDocs = "chrome://liberator/content/buffer.xhtml,chrome://browser/content/browser.xul";
-
- var highlight = storage.newMap("highlight", false);
-
- const util = modules.util;
- const arrayIter = util.Array.iterator;
-
- function Styles(name, store, serial)
- {
- /* Can't reference liberator or Components inside Styles --
- * they're members of the window object, which disappear
- * with this window.
- */
- const sleep = liberator.sleep;
- const storage = modules.storage;
- const consoleService = Components.classes["@mozilla.org/consoleservice;1"]
- .getService(Components.interfaces.nsIConsoleService);
- const ios = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService);
- const sss = Components.classes["@mozilla.org/content/style-sheet-service;1"]
- .getService(Components.interfaces.nsIStyleSheetService);
- const XHTML = "http://www.w3.org/1999/xhtml";
- const namespace = "@namespace html url(" + XHTML + ");\n" +
- "@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);\n";
- const Sheet = new Struct("name", "sites", "css", "ref");
-
- let cssUri = function (css) "chrome-data:text/css," + encodeURI(css);
-
- let userSheets = [];
- let systemSheets = [];
- let userNames = {};
- let systemNames = {};
-
- this.__iterator__ = function () Iterator(userSheets.concat(systemSheets));
- this.__defineGetter__("systemSheets", function () Iterator(systemSheets));
- this.__defineGetter__("userSheets", function () Iterator(userSheets));
- this.__defineGetter__("systemNames", function () Iterator(systemNames));
- this.__defineGetter__("userNames", function () Iterator(userNames));
-
- this.addSheet = function (name, filter, css, system, force)
- {
- let sheets = system ? systemSheets : userSheets;
- let names = system ? systemNames : userNames;
- if (name && name in names)
- this.removeSheet(name, null, null, null, system);
-
- let sheet = sheets.filter(function (s) s.sites.join(",") == filter && s.css == css)[0];
- if (!sheet)
- sheet = new Sheet(name, filter.split(","), css, null);
-
- if (sheet.ref == null) // Not registered yet
- {
- sheet.ref = [];
- try
- {
- this.registerSheet(cssUri(wrapCSS(sheet)), !force);
- }
- catch (e)
- {
- return e.echoerr || e;
- }
- sheets.push(sheet);
- }
- if (name)
- {
- sheet.ref.push(name);
- names[name] = sheet;
- }
- return null;
- }
-
- this.findSheets = function (name, filter, css, index, system)
- {
- let sheets = system ? systemSheets : userSheets;
- let names = system ? systemNames : userNames;
-
- // Grossly inefficient.
- let matches = [k for ([k, v] in sheets)];
- if (index)
- matches = String(index).split(",").filter(function (i) i in sheets);
- if (name)
- matches = matches.filter(function (i) sheets[i] == names[name]);
- if (css)
- matches = matches.filter(function (i) sheets[i].css == css);
- if (filter)
- matches = matches.filter(function (i) sheets[i].sites.indexOf(filter) >= 0);
- return matches;
- },
-
- this.removeSheet = function (name, filter, css, index, system)
- {
- let self = this;
- let sheets = system ? systemSheets : userSheets;
- let names = system ? systemNames : userNames;
-
- if (filter && filter.indexOf(",") > -1)
- return filter.split(",").reduce(
- function (n, f) n + self.removeSheet(name, f, index, system), 0);
-
- if (filter == undefined)
- filter = "";
-
- let matches = this.findSheets(name, filter, css, index, system);
- if (matches.length == 0)
- return;
-
- for (let [,i] in Iterator(matches.reverse()))
- {
- let sheet = sheets[i];
- if (name)
- {
- sheet.ref.splice(sheet.ref.indexOf(name));
- delete names[name];
- }
- if (!sheet.ref.length)
- {
- sheets.splice(i);
- this.unregisterSheet(cssUri(wrapCSS(sheet)));
- }
- // Filter out the given site, and re-add if there are any left
- if (filter)
- {
- let sites = sheet.sites.filter(function (f) f != filter);
- if (sites.length)
- this.addSheet(name, sites.join(","), css, system, true);
- }
- }
- return matches.length;
- }
-
- this.registerSheet = function (uri, doCheckSyntax, reload)
- {
- if (doCheckSyntax)
- checkSyntax(uri);
- if (reload)
- this.unregisterSheet(uri);
- uri = ios.newURI(uri, null, null);
- if (reload || !sss.sheetRegistered(uri, sss.USER_SHEET))
- sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
- }
-
- this.unregisterSheet = function (uri)
- {
- uri = ios.newURI(uri, null, null);
- if (sss.sheetRegistered(uri, sss.USER_SHEET))
- sss.unregisterSheet(uri, sss.USER_SHEET);
- }
-
- function wrapCSS(sheet)
- {
- let filter = sheet.sites;
- let css = sheet.css;
- if (filter[0] == "*")
- return namespace + css;
- let selectors = filter.map(function (part) (/[*]$/.test(part) ? "url-prefix" :
- /[\/:]/.test(part) ? "url"
- : "domain")
- + '("' + part.replace(/"/g, "%22").replace(/[*]$/, "") + '")')
- .join(", ");
- return namespace + "@-moz-document " + selectors + "{\n" + css + "\n}\n";
- }
-
- let queryinterface = XPCOMUtils.generateQI([Components.interfaces.nsIConsoleListener]);
- /* What happens if more than one thread tries to use this? */
- let testDoc = document.implementation.createDocument(XHTML, "doc", null);
- function checkSyntax(uri)
- {
- let errors = [];
- let listener = {
- QueryInterface: queryinterface,
- observe: function (message)
- {
- try
- {
- message = message.QueryInterface(Components.interfaces.nsIScriptError);
- if (message.sourceName == uri)
- errors.push(message);
- }
- catch (e) {}
- }
- };
-
- try
- {
- consoleService.registerListener(listener);
- if (testDoc.documentElement.firstChild)
- testDoc.documentElement.removeChild(testDoc.documentElement.firstChild);
- testDoc.documentElement.appendChild(util.xmlToDom(
-
, testDoc));
-
- while (true)
- {
- try
- {
- // Throws NS_ERROR_DOM_INVALID_ACCESS_ERR if not finished loading
- testDoc.styleSheets[0].cssRules.length;
- break;
- }
- catch (e)
- {
- if (e.name != "NS_ERROR_DOM_INVALID_ACCESS_ERR")
- return [e.toString()];
- sleep(10);
- }
- }
- }
- finally
- {
- consoleService.unregisterListener(listener);
- }
- if (errors.length)
- {
- let err = new Error("", errors[0].sourceName.replace(/^(chrome-data:text\/css,).*/, "$1..."), errors[0].lineNumber);
- err.name = "CSSError"
- err.message = errors.reduce(function (msg, e) msg + "; " + e.lineNumber + ": " + e.errorMessage, errors.shift().errorMessage);
- err.echoerr = err.fileName + ":" + err.lineNumber + ": " + err.message;
- throw err;
- }
- }
- }
- Styles.prototype = {
- get sites() util.Array.uniq(util.Array.flatten([v.sites for ([k, v] in this.userSheets)]))
- };
-
- let styles = storage.newObject("styles", Styles, false);
-
/* FIXME: This doesn't belong here. */
let mainWindowID = config.mainWindowID || "main-window";
let fontSize = util.computedStyle(document.getElementById(mainWindowID))["font-size"];
@@ -813,114 +576,6 @@ function Buffer() //{{{
function () { BrowserStop(); },
{ argCount: "0" });
- commands.add(["sty[le]"],
- "Add or list user styles",
- function (args, special)
- {
- let [filter] = args.arguments;
- let name = args["-name"];
- let css = args.literalArg;
-
- if (!css)
- {
- let list = Array.concat([i for (i in styles.userNames)],
- [i for (i in styles.userSheets) if (!i[1].ref.length)]);
- let str = template.tabular(["", "Filter", "CSS"],
- ["padding: 0 1em 0 1ex; vertical-align: top", "padding: 0 1em 0 0; vertical-align: top"],
- ([k, v[1].join(","), v[2]]
- for ([i, [k, v]] in Iterator(list))
- if ((!filter || v[1].indexOf(filter) >= 0) && (!name || v[0] == name))));
- commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
- }
- else
- {
- let err = styles.addSheet(name, filter, css, false, special);
- if (err)
- liberator.echoerr(err);
- }
- },
- {
- completer: function (filter) {
- let compl = [];
- try
- {
- compl.push([content.location.host, "Current Host"]);
- compl.push([content.location.href, "Current URL"]);
- }
- catch (e) {}
- comp = compl.concat([[s, ""] for each (s in styles.sites)])
- return [0, completion.filter(compl, filter)];
- },
- argCount: 1,
- bang: true,
- hereDoc: true,
- literal: true,
- options: [[["-name", "-n"], commands.OPTION_STRING]],
- serial: function () [
- {
- command: this.name,
- bang: true,
- options: sty.name ? {"-name": sty.name} : {},
- arguments: [sty.sites.join(",")],
- literalArg: sty.css
- } for ([k, sty] in styles.userSheets)
- ]
- });
-
- commands.add(["dels[tyle]"],
- "Remove a user stylesheet",
- function (args) {
- styles.removeSheet(args["-name"], args.arguments[0], args.literalArg, args["-index"], false);
- },
- {
- argCount: 1,
- completer: function (filter) [0, completion.filter(
- [[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}>]
- for ([i, s] in styles.userSheets)
- ]
- .concat([[s, ""] for each (s in styles.sites)])
- , filter)],
- literal: true,
- options: [[["-index", "-i"], commands.OPTION_INT],
- [["-name", "-n"], commands.OPTION_STRING]]
- });
-
- commands.add(["hi[ghlight]"],
- "Set the style of certain display elements",
- function (args, special)
- {
- let key = args.arguments[0];
- let css = args.literalArg;
- if (!css && !(key && special))
- {
- let str = template.tabular(["Key", "CSS"],
- ["padding: 0 1em 0 0; vertical-align: top"],
- (h for (h in highlight) if (!key || h[0].indexOf(key) > -1)));
- commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
- return;
- }
- buffer.highlight(key, css, special);
- },
- {
- // TODO: add this as a standard highlight completion function?
- // I agree. It could (should) be much more sophisticated. --Kris
- completer: function (filter) [0,
- completion.filter([[v instanceof Array ? v[0] : v, ""] for ([k, v] in Iterator(highlightClasses))], filter)
- ],
- argCount: 1,
- bang: true,
- hereDoc: true,
- literal: true,
- serial: function () [
- {
- command: this.name,
- arguments: [k],
- literalArg: v
- }
- for ([k, v] in Iterator(highlight))
- ]
- });
-
commands.add(["vie[wsource]"],
"View source code of current document",
function (args, special) { buffer.viewSource(args.arguments[0], special); },
@@ -1030,7 +685,7 @@ function Buffer() //{{{
// put feeds rss into pageFeeds[]
let nFeed = 0;
var linkNodes = doc.getElementsByTagName("link");
- for (link in arrayIter(linkNodes))
+ for (link in util.arrayIter(linkNodes))
{
if (!link.href)
return;
@@ -1065,7 +720,7 @@ function Buffer() //{{{
.getService(nsICacheService);
let cacheKey = doc.location.toString().replace(/#.*$/, "");
- for (let proto in arrayIter(["HTTP", "FTP"]))
+ for (let proto in util.arrayIter(["HTTP", "FTP"]))
{
try
{
@@ -1206,39 +861,6 @@ function Buffer() //{{{
addPageInfoSection: addPageInfoSection,
- highlight: function (key, style, force)
- {
- let [, class, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
-
- class = highlightClasses.filter(function (i) i == class || i[0] == class)[0];
- if (!class)
- {
- liberator.echoerr("Unknown highlight keyword");
- return;
- }
- if (!(class instanceof Array))
- class = [class];
-
- styles.removeSheet("hl-" + key, null, null, null, true);
- highlight.remove(key);
-
- if (/^\s*$/.test(style))
- return;
-
- let cssClass = class[1] || ".hl-" + class[0];
- let scope = class[2] || highlightDocs;
-
- let css = style.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
- .replace(";!important;", ";", "g") // Seeming Spidermonkey bug
- css = cssClass + selectors + " { " + css + " }";
-
- let error = styles.addSheet("hl-" + key, scope, css, true, force);
- if (error)
- liberator.echoerr(error);
- else
- highlight.set(key, style);
- },
-
// returns an XPathResult object
evaluateXPath: function (expression, doc, elem, asIterator)
{
diff --git a/content/find.js b/content/find.js
index 03edecae..b7acd8a7 100644
--- a/content/find.js
+++ b/content/find.js
@@ -284,7 +284,7 @@ function Search() //{{{
if (!aWord)
{
- let elems = doc.getElementsByClassName("__liberator-search");
+ let elems = doc.getElementsByClassName("liberator-search");
for (let i = elems.length; --i >= 0;)
{
let elem = elems[i];
@@ -303,7 +303,7 @@ function Search() //{{{
return;
}
- var baseNode =
+ var baseNode =
baseNode = util.xmlToDom(baseNode, window.content.document);
var body = doc.body;
@@ -551,7 +551,7 @@ function Search() //{{{
highlight: function (text)
{
// already highlighted?
- if (window.content.document.getElementsByClassName("__liberator-search").length > 0)
+ if (window.content.document.getElementsByClassName("liberator-search").length > 0)
return;
if (!text)
diff --git a/content/liberator-overlay.js b/content/liberator-overlay.js
index 66903973..2ff583aa 100644
--- a/content/liberator-overlay.js
+++ b/content/liberator-overlay.js
@@ -25,6 +25,7 @@
["liberator.js",
"util.js",
+ "style.js",
"config.js",
"buffer.js",
"commands.js",
diff --git a/content/liberator.js b/content/liberator.js
index 7b202381..af9be989 100644
--- a/content/liberator.js
+++ b/content/liberator.js
@@ -37,6 +37,10 @@ const liberator = (function () //{{{
var callbacks = [];
var observers = [];
+ function registerObserver(type, callback)
+ {
+ observers.push([type, callback]);
+ }
function loadModule(name, func)
{
@@ -46,6 +50,7 @@ const liberator = (function () //{{{
liberator.log(message, 0);
liberator.dump(message);
modules[name] = func();
+ liberator.triggerObserver("load_" + name, name);
}
catch (e)
{
@@ -57,7 +62,7 @@ const liberator = (function () //{{{
}
// Only general options are added here, which are valid for all vimperator like extensions
- function addOptions()
+ registerObserver("load_options", function ()
{
options.add(["errorbells", "eb"],
"Ring the bell when an error message is displayed",
@@ -140,9 +145,9 @@ const liberator = (function () //{{{
return value;
}
});
- }
+ })
- function addMappings()
+ registerObserver("load_mappings", function ()
{
mappings.add(modes.all, [""],
"Open help window",
@@ -158,9 +163,9 @@ const liberator = (function () //{{{
mappings.add([modes.NORMAL], ["ZZ"],
"Quit and save the session",
function () { liberator.quit(true); });
- }
+ })
- function addCommands()
+ registerObserver("load_commands", function ()
{
commands.add(["addo[ns]"],
"Manage available Extensions and Themes",
@@ -529,7 +534,7 @@ const liberator = (function () //{{{
argCount: "0",
bang: true
});
- }
+ })
// initially hide all GUI, it is later restored unless the user has :set go= or something
// similar in his config
@@ -603,10 +608,7 @@ const liberator = (function () //{{{
return false;
},
- registerObserver: function (type, callback)
- {
- observers.push([type, callback]);
- },
+ registerObserver: registerObserver,
triggerObserver: function (type, data)
{
@@ -1145,9 +1147,9 @@ const liberator = (function () //{{{
config.features.push(navigator.platform);
// commands must always be the first module to be initialized
- loadModule("commands", Commands); addCommands();
- loadModule("options", Options); addOptions();
- loadModule("mappings", Mappings); addMappings();
+ loadModule("commands", Commands);
+ loadModule("options", Options);
+ loadModule("mappings", Mappings);
loadModule("buffer", Buffer);
loadModule("events", Events);
loadModule("commandline", CommandLine);
diff --git a/content/style.js b/content/style.js
new file mode 100644
index 00000000..0b5cd46d
--- /dev/null
+++ b/content/style.js
@@ -0,0 +1,473 @@
+/***** BEGIN LICENSE BLOCK ***** {{{
+ ©2008 Kris Maglione
+ Distributable under the terms of the MIT license, which allows
+ for sublicensing under any compatible license, including the MPL,
+ GPL, and MPL. Anyone who changes this file is welcome to relicense
+ it under any or all of those licenseses.
+}}} ***** END LICENSE BLOCK *****/
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function Highlights(name, store, serial)
+{
+ const highlightCSS = .toString();
+ var self = this;
+ var highlight = {};
+ var styles = storage.styles;
+
+ const Highlight = Struct("class", "selector", "filter", "default", "value");
+ Highlight.defaultValue("filter", function () "chrome://liberator/content/buffer.xhtml,chrome://browser/content/browser.xul");
+ Highlight.defaultValue("selector", function () ".hl-" + this.class);
+ Highlight.defaultValue("value", function () this.default);
+ Highlight.prototype.toString = function () [k + ": " + util.escapeString(v || "undefined") for ([k, v] in this)].join(", ");
+
+ this.__iterator__ = function () (v for ([k,v] in Iterator(highlight)));
+
+ this.get = function (k) highlight[k];
+ this.set = function (key, newStyle, force)
+ {
+ let [, class, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
+
+ if (!(class in highlight))
+ return "Unknown highlight keyword";
+
+ if (/^\s*$/.test(newStyle))
+ newStyle = null;
+
+ let style = highlight[key] || new Highlight(key);
+ styles.removeSheet(style.selector, null, null, true);
+
+ if (newStyle == null)
+ {
+ if (style.default == null)
+ {
+ delete highlight[style.class];
+ return null;
+ }
+ newStyle = style.default;
+ force = true;
+ }
+
+ let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
+ .replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
+ css = style.selector + " { " + css + " }";
+
+ let error = styles.addSheet(style.selector, style.filter, css, true, force);
+ if (!error)
+ style.value = newStyle;
+ return error;
+ }
+
+ highlightCSS.replace(/\{((?:.|\n)*?)\}/g, function (_, _1) _1.replace(/\n\s*/g, " "))
+ .split("\n").filter(function (s) /\S/.test(s))
+ .forEach(function (style)
+ {
+ let style = Highlight.apply(Highlight, Array.slice(style.match(/^\s*([^,\s]+)(?:,([^,\s]+))?(?:,([^,\s]+))?(?:\s+(.*))?/), 1));
+ highlight[style.class] = style;
+ self.set(style.class);
+ });
+}
+
+function Styles(name, store, serial)
+{
+ /* Can't reference liberator or Components inside Styles --
+ * they're members of the window object, which disappear
+ * with this window.
+ */
+ const util = modules.util;
+ const sleep = liberator.sleep;
+ const storage = modules.storage;
+ const consoleService = Components.classes["@mozilla.org/consoleservice;1"]
+ .getService(Components.interfaces.nsIConsoleService);
+ const ios = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+ const sss = Components.classes["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Components.interfaces.nsIStyleSheetService);
+ const XHTML = "http://www.w3.org/1999/xhtml";
+ const namespace = "@namespace html url(" + XHTML + ");\n" +
+ "@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);\n";
+ const Sheet = new Struct("name", "sites", "css", "ref");
+
+ let cssUri = function (css) "chrome-data:text/css," + encodeURI(css);
+
+ let userSheets = [];
+ let systemSheets = [];
+ let userNames = {};
+ let systemNames = {};
+
+ this.__iterator__ = function () Iterator(userSheets.concat(systemSheets));
+ this.__defineGetter__("systemSheets", function () Iterator(systemSheets));
+ this.__defineGetter__("userSheets", function () Iterator(userSheets));
+ this.__defineGetter__("systemNames", function () Iterator(systemNames));
+ this.__defineGetter__("userNames", function () Iterator(userNames));
+
+ this.addSheet = function (name, filter, css, system, force)
+ {
+ let sheets = system ? systemSheets : userSheets;
+ let names = system ? systemNames : userNames;
+ if (name && name in names)
+ this.removeSheet(name, null, null, null, system);
+
+ let sheet = sheets.filter(function (s) s.sites.join(",") == filter && s.css == css)[0];
+ if (!sheet)
+ sheet = new Sheet(name, filter.split(","), css, null);
+
+ if (sheet.ref == null) // Not registered yet
+ {
+ sheet.ref = [];
+ try
+ {
+ this.registerSheet(cssUri(wrapCSS(sheet)), !force);
+ }
+ catch (e)
+ {
+ return e.echoerr || e;
+ }
+ sheets.push(sheet);
+ }
+ if (name)
+ {
+ sheet.ref.push(name);
+ names[name] = sheet;
+ }
+ return null;
+ }
+
+ this.findSheets = function (name, filter, css, index, system)
+ {
+ let sheets = system ? systemSheets : userSheets;
+ let names = system ? systemNames : userNames;
+
+ // Grossly inefficient.
+ let matches = [k for ([k, v] in sheets)];
+ if (index)
+ matches = String(index).split(",").filter(function (i) i in sheets);
+ if (name)
+ matches = matches.filter(function (i) sheets[i] == names[name]);
+ if (css)
+ matches = matches.filter(function (i) sheets[i].css == css);
+ if (filter)
+ matches = matches.filter(function (i) sheets[i].sites.indexOf(filter) >= 0);
+ return matches;
+ },
+
+ this.removeSheet = function (name, filter, css, index, system)
+ {
+ let self = this;
+ let sheets = system ? systemSheets : userSheets;
+ let names = system ? systemNames : userNames;
+
+ if (filter && filter.indexOf(",") > -1)
+ return filter.split(",").reduce(
+ function (n, f) n + self.removeSheet(name, f, index, system), 0);
+
+ if (filter == undefined)
+ filter = "";
+
+ let matches = this.findSheets(name, filter, css, index, system);
+ if (matches.length == 0)
+ return;
+
+ for (let [,i] in Iterator(matches.reverse()))
+ {
+ let sheet = sheets[i];
+ if (name)
+ {
+ sheet.ref.splice(sheet.ref.indexOf(name));
+ delete names[name];
+ }
+ if (!sheet.ref.length)
+ {
+ sheets.splice(i);
+ this.unregisterSheet(cssUri(wrapCSS(sheet)));
+ }
+ // Filter out the given site, and re-add if there are any left
+ if (filter)
+ {
+ let sites = sheet.sites.filter(function (f) f != filter);
+ if (sites.length)
+ this.addSheet(name, sites.join(","), css, system, true);
+ }
+ }
+ return matches.length;
+ }
+
+ this.registerSheet = function (uri, doCheckSyntax, reload)
+ {
+ if (doCheckSyntax)
+ checkSyntax(uri);
+ if (reload)
+ this.unregisterSheet(uri);
+ uri = ios.newURI(uri, null, null);
+ if (reload || !sss.sheetRegistered(uri, sss.USER_SHEET))
+ sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
+ }
+
+ this.unregisterSheet = function (uri)
+ {
+ uri = ios.newURI(uri, null, null);
+ if (sss.sheetRegistered(uri, sss.USER_SHEET))
+ sss.unregisterSheet(uri, sss.USER_SHEET);
+ }
+
+ function wrapCSS(sheet)
+ {
+ let filter = sheet.sites;
+ let css = sheet.css;
+ if (filter[0] == "*")
+ return namespace + css;
+ let selectors = filter.map(function (part) (/[*]$/.test(part) ? "url-prefix" :
+ /[\/:]/.test(part) ? "url"
+ : "domain")
+ + '("' + part.replace(/"/g, "%22").replace(/[*]$/, "") + '")')
+ .join(", ");
+ return namespace + "@-moz-document " + selectors + "{\n" + css + "\n}\n";
+ }
+
+ let queryinterface = XPCOMUtils.generateQI([Components.interfaces.nsIConsoleListener]);
+ /* What happens if more than one thread tries to use this? */
+ let testDoc = document.implementation.createDocument(XHTML, "doc", null);
+ function checkSyntax(uri)
+ {
+ let errors = [];
+ let listener = {
+ QueryInterface: queryinterface,
+ observe: function (message)
+ {
+ try
+ {
+ message = message.QueryInterface(Components.interfaces.nsIScriptError);
+ if (message.sourceName == uri)
+ errors.push(message);
+ }
+ catch (e) {}
+ }
+ };
+
+ try
+ {
+ consoleService.registerListener(listener);
+ if (testDoc.documentElement.firstChild)
+ testDoc.documentElement.removeChild(testDoc.documentElement.firstChild);
+ testDoc.documentElement.appendChild(util.xmlToDom(
+ , testDoc));
+
+ while (true)
+ {
+ try
+ {
+ // Throws NS_ERROR_DOM_INVALID_ACCESS_ERR if not finished loading
+ testDoc.styleSheets[0].cssRules.length;
+ break;
+ }
+ catch (e)
+ {
+ if (e.name != "NS_ERROR_DOM_INVALID_ACCESS_ERR")
+ return [e.toString()];
+ sleep(10);
+ }
+ }
+ }
+ finally
+ {
+ consoleService.unregisterListener(listener);
+ }
+ if (errors.length)
+ {
+ let err = new Error("", errors[0].sourceName.replace(/^(chrome-data:text\/css,).*/, "$1..."), errors[0].lineNumber);
+ err.name = "CSSError"
+ err.message = errors.reduce(function (msg, e) msg + "; " + e.lineNumber + ": " + e.errorMessage,
+ errors.shift().errorMessage);
+ err.echoerr = err.fileName + ":" + err.lineNumber + ": " + err.message;
+ throw err;
+ }
+ }
+}
+Styles.prototype = {
+ get sites() util.Array.uniq(util.Array.flatten([v.sites for ([k, v] in this.userSheets)]))
+};
+
+const styles = storage.newObject("styles", Styles, false);
+const highlight = storage.newObject("highlight", Highlights, false);
+
+liberator.registerObserver("load_commands", function ()
+{
+ commands.add(["sty[le]"],
+ "Add or list user styles",
+ function (args, special)
+ {
+ let [filter] = args.arguments;
+ let name = args["-name"];
+ let css = args.literalArg;
+
+ if (!css)
+ {
+ let list = Array.concat([i for (i in styles.userNames)],
+ [i for (i in styles.userSheets) if (!i[1].ref.length)]);
+ let str = template.tabular(["", "Filter", "CSS"],
+ ["padding: 0 1em 0 1ex; vertical-align: top", "padding: 0 1em 0 0; vertical-align: top"],
+ ([k, v[1].join(","), v[2]]
+ for ([i, [k, v]] in Iterator(list))
+ if ((!filter || v[1].indexOf(filter) >= 0) && (!name || v[0] == name))));
+ commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
+ }
+ else
+ {
+ let err = styles.addSheet(name, filter, css, false, special);
+ if (err)
+ liberator.echoerr(err);
+ }
+ },
+ {
+ completer: function (filter) {
+ let compl = [];
+ try
+ {
+ compl.push([content.location.host, "Current Host"]);
+ compl.push([content.location.href, "Current URL"]);
+ }
+ catch (e) {}
+ comp = compl.concat([[s, ""] for each (s in styles.sites)])
+ return [0, completion.filter(compl, filter)];
+ },
+ argCount: 1,
+ bang: true,
+ hereDoc: true,
+ literal: true,
+ options: [[["-name", "-n"], commands.OPTION_STRING]],
+ serial: function () [
+ {
+ command: this.name,
+ bang: true,
+ options: sty.name ? {"-name": sty.name} : {},
+ arguments: [sty.sites.join(",")],
+ literalArg: sty.css
+ } for ([k, sty] in styles.userSheets)
+ ]
+ });
+
+ commands.add(["dels[tyle]"],
+ "Remove a user stylesheet",
+ function (args) {
+ styles.removeSheet(args["-name"], args.arguments[0], args.literalArg, args["-index"], false);
+ },
+ {
+ argCount: 1,
+ completer: function (filter) [0, completion.filter(
+ [[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}>]
+ for ([i, s] in styles.userSheets)
+ ]
+ .concat([[s, ""] for each (s in styles.sites)])
+ , filter)],
+ literal: true,
+ options: [[["-index", "-i"], commands.OPTION_INT],
+ [["-name", "-n"], commands.OPTION_STRING]]
+ });
+
+ commands.add(["hi[ghlight]"],
+ "Set the style of certain display elements",
+ function (args, special)
+ {
+ let key = args.arguments[0];
+ let css = args.literalArg;
+ if (!css && !(key && special))
+ {
+ let str = template.tabular(["Key", "CSS"],
+ ["padding: 0 1em 0 0; vertical-align: top"],
+ ([h.class, h.value]
+ for (h in highlight)
+ if (!key || h.class.indexOf(key) > -1)));
+ commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
+ return;
+ }
+ let error = highlight.set(key, css, special);
+ if (error)
+ liberator.echoerr(error);
+ },
+ {
+ // TODO: add this as a standard highlight completion function?
+ // I agree. It could (should) be much more sophisticated. --Kris
+ completer: function (filter) [0,
+ completion.filter([[v.class, ""] for (v in highlight)], filter)
+ ],
+ argCount: 1,
+ bang: true,
+ hereDoc: true,
+ literal: true,
+ serial: function () [
+ {
+ command: this.name,
+ arguments: [k],
+ literalArg: v
+ }
+ for ([k, v] in Iterator(highlight))
+ if (v.value != v.default)
+ ]
+ });
+});
+
diff --git a/content/ui.js b/content/ui.js
index 6f949c3a..6241fbec 100644
--- a/content/ui.js
+++ b/content/ui.js
@@ -214,8 +214,10 @@ function CommandLine() //{{{
.selection.getRangeAt(0)
.startContainer.parentNode
.scrollWidth > commandWidget.inputField.scrollWidth)
- // Yeah, the min-width is stupid. Somehow, it doesn't work otherwise.
- setMultiline({str}
, highlightGroup);
+ {
+ setCommand("");
+ setMultiline({str}, highlightGroup);
+ }
}
// TODO: extract CSS
@@ -233,7 +235,7 @@ function CommandLine() //{{{
* after interpolated data.
*/
XML.ignoreWhitespace = typeof str == "xml";
- var output = {template.maybeXML(str)}
;
+ var output = {template.maybeXML(str)}
;
XML.ignoreWhiteSpace = true;
lastMowOutput = output;
@@ -267,7 +269,7 @@ function CommandLine() //{{{
if (win.scrollY >= win.scrollMaxY)
setLine("Press ENTER or type command to continue", commandline.HL_QUESTION, true);
else
- setLine("-- More --", commandline.HL_QUESTION, true);
+ setLine("-- More --", commandline.HL_MOREMSG, true);
}
else
{
@@ -278,7 +280,7 @@ function CommandLine() //{{{
win.focus();
startHints = false;
- modes.push(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE);
+ modes.set(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE);
}
function autosizeMultilineInputWidget()
@@ -532,7 +534,7 @@ function CommandLine() //{{{
let list = <>>;
for (let [,message] in Iterator(messageHistory.messages))
- list += {message.str}
;
+ list += {message.str}
;
liberator.echo(list, commandline.FORCE_MULTILINE);
}
@@ -591,7 +593,7 @@ function CommandLine() //{{{
historyIndex = UNINITIALIZED;
completionIndex = UNINITIALIZED;
- modes.push(modes.COMMAND_LINE, currentExtendedMode);
+ modes.set(modes.COMMAND_LINE, currentExtendedMode);
setHighlightGroup(this.HL_NORMAL);
setPrompt(currentPrompt);
setCommand(currentCommand);
diff --git a/content/util.js b/content/util.js
index e3a862a1..5410d827 100644
--- a/content/util.js
+++ b/content/util.js
@@ -484,10 +484,23 @@ function Struct()
{
let self = this instanceof arguments.callee ? this : new arguments.callee();
for (let [k, v] in Iterator(Array.slice(arguments)))
- self[k] = v;
+ {
+ if (v != undefined)
+ self[k] = v;
+ }
return self;
}
ConStructor.prototype = self;
+ ConStructor.defaultValue = function (key, val)
+ {
+ let i = args.indexOf(key);
+ let _i = "_" + i;
+ ConStructor.prototype.__defineGetter__(i, val);
+ ConStructor.prototype.__defineSetter__(i, function (val) {
+ this.__defineGetter__(i, function () this[_i]);
+ this[_i] = val;
+ });
+ };
return self.constructor = ConStructor;
}
@@ -497,7 +510,11 @@ Struct.prototype = {
return this.constructor.apply(null, this.slice());
},
// Iterator over our named members
- __iterator__: function () ([v, this[v]] for ([k, v] in this.members))
+ __iterator__: function ()
+ {
+ let self = this;
+ return ([v, self[v]] for ([k, v] in Iterator(self.members)))
+ }
}
// Add no-sideeffect array methods. Can't set new Array() as the prototype or