1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-23 11:27:58 +01:00

Show the default styles in :highlight, etc.

This commit is contained in:
Kris Maglione
2008-10-31 22:46:34 +00:00
parent d7c5e79cd7
commit f0d258c8fc
7 changed files with 522 additions and 405 deletions

View File

@@ -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. the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/ }}} ***** END LICENSE BLOCK *****/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const Point = new Struct("x", "y"); const Point = new Struct("x", "y");
function Buffer() //{{{ function Buffer() //{{{
@@ -35,241 +33,6 @@ function Buffer() //{{{
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION ///////////////////////////////////////// ////////////////////// 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(
<html><head><link type="text/css" rel="stylesheet" href={uri}/></head></html>, 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. */ /* FIXME: This doesn't belong here. */
let mainWindowID = config.mainWindowID || "main-window"; let mainWindowID = config.mainWindowID || "main-window";
let fontSize = util.computedStyle(document.getElementById(mainWindowID))["font-size"]; let fontSize = util.computedStyle(document.getElementById(mainWindowID))["font-size"];
@@ -813,114 +576,6 @@ function Buffer() //{{{
function () { BrowserStop(); }, function () { BrowserStop(); },
{ argCount: "0" }); { 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]"], commands.add(["vie[wsource]"],
"View source code of current document", "View source code of current document",
function (args, special) { buffer.viewSource(args.arguments[0], special); }, function (args, special) { buffer.viewSource(args.arguments[0], special); },
@@ -1030,7 +685,7 @@ function Buffer() //{{{
// put feeds rss into pageFeeds[] // put feeds rss into pageFeeds[]
let nFeed = 0; let nFeed = 0;
var linkNodes = doc.getElementsByTagName("link"); var linkNodes = doc.getElementsByTagName("link");
for (link in arrayIter(linkNodes)) for (link in util.arrayIter(linkNodes))
{ {
if (!link.href) if (!link.href)
return; return;
@@ -1065,7 +720,7 @@ function Buffer() //{{{
.getService(nsICacheService); .getService(nsICacheService);
let cacheKey = doc.location.toString().replace(/#.*$/, ""); let cacheKey = doc.location.toString().replace(/#.*$/, "");
for (let proto in arrayIter(["HTTP", "FTP"])) for (let proto in util.arrayIter(["HTTP", "FTP"]))
{ {
try try
{ {
@@ -1206,39 +861,6 @@ function Buffer() //{{{
addPageInfoSection: addPageInfoSection, 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 // returns an XPathResult object
evaluateXPath: function (expression, doc, elem, asIterator) evaluateXPath: function (expression, doc, elem, asIterator)
{ {

View File

@@ -284,7 +284,7 @@ function Search() //{{{
if (!aWord) if (!aWord)
{ {
let elems = doc.getElementsByClassName("__liberator-search"); let elems = doc.getElementsByClassName("liberator-search");
for (let i = elems.length; --i >= 0;) for (let i = elems.length; --i >= 0;)
{ {
let elem = elems[i]; let elem = elems[i];
@@ -303,7 +303,7 @@ function Search() //{{{
return; return;
} }
var baseNode = <span class="__liberator-search"/> var baseNode = <span class="liberator-search"/>
baseNode = util.xmlToDom(baseNode, window.content.document); baseNode = util.xmlToDom(baseNode, window.content.document);
var body = doc.body; var body = doc.body;
@@ -551,7 +551,7 @@ function Search() //{{{
highlight: function (text) highlight: function (text)
{ {
// already highlighted? // already highlighted?
if (window.content.document.getElementsByClassName("__liberator-search").length > 0) if (window.content.document.getElementsByClassName("liberator-search").length > 0)
return; return;
if (!text) if (!text)

View File

@@ -25,6 +25,7 @@
["liberator.js", ["liberator.js",
"util.js", "util.js",
"style.js",
"config.js", "config.js",
"buffer.js", "buffer.js",
"commands.js", "commands.js",

View File

@@ -37,6 +37,10 @@ const liberator = (function () //{{{
var callbacks = []; var callbacks = [];
var observers = []; var observers = [];
function registerObserver(type, callback)
{
observers.push([type, callback]);
}
function loadModule(name, func) function loadModule(name, func)
{ {
@@ -46,6 +50,7 @@ const liberator = (function () //{{{
liberator.log(message, 0); liberator.log(message, 0);
liberator.dump(message); liberator.dump(message);
modules[name] = func(); modules[name] = func();
liberator.triggerObserver("load_" + name, name);
} }
catch (e) catch (e)
{ {
@@ -57,7 +62,7 @@ const liberator = (function () //{{{
} }
// Only general options are added here, which are valid for all vimperator like extensions // Only general options are added here, which are valid for all vimperator like extensions
function addOptions() registerObserver("load_options", function ()
{ {
options.add(["errorbells", "eb"], options.add(["errorbells", "eb"],
"Ring the bell when an error message is displayed", "Ring the bell when an error message is displayed",
@@ -140,9 +145,9 @@ const liberator = (function () //{{{
return value; return value;
} }
}); });
} })
function addMappings() registerObserver("load_mappings", function ()
{ {
mappings.add(modes.all, ["<F1>"], mappings.add(modes.all, ["<F1>"],
"Open help window", "Open help window",
@@ -158,9 +163,9 @@ const liberator = (function () //{{{
mappings.add([modes.NORMAL], ["ZZ"], mappings.add([modes.NORMAL], ["ZZ"],
"Quit and save the session", "Quit and save the session",
function () { liberator.quit(true); }); function () { liberator.quit(true); });
} })
function addCommands() registerObserver("load_commands", function ()
{ {
commands.add(["addo[ns]"], commands.add(["addo[ns]"],
"Manage available Extensions and Themes", "Manage available Extensions and Themes",
@@ -529,7 +534,7 @@ const liberator = (function () //{{{
argCount: "0", argCount: "0",
bang: true bang: true
}); });
} })
// initially hide all GUI, it is later restored unless the user has :set go= or something // initially hide all GUI, it is later restored unless the user has :set go= or something
// similar in his config // similar in his config
@@ -603,10 +608,7 @@ const liberator = (function () //{{{
return false; return false;
}, },
registerObserver: function (type, callback) registerObserver: registerObserver,
{
observers.push([type, callback]);
},
triggerObserver: function (type, data) triggerObserver: function (type, data)
{ {
@@ -1145,9 +1147,9 @@ const liberator = (function () //{{{
config.features.push(navigator.platform); config.features.push(navigator.platform);
// commands must always be the first module to be initialized // commands must always be the first module to be initialized
loadModule("commands", Commands); addCommands(); loadModule("commands", Commands);
loadModule("options", Options); addOptions(); loadModule("options", Options);
loadModule("mappings", Mappings); addMappings(); loadModule("mappings", Mappings);
loadModule("buffer", Buffer); loadModule("buffer", Buffer);
loadModule("events", Events); loadModule("events", Events);
loadModule("commandline", CommandLine); loadModule("commandline", CommandLine);

473
content/style.js Normal file
View File

@@ -0,0 +1,473 @@
/***** BEGIN LICENSE BLOCK ***** {{{
©2008 Kris Maglione <maglione.k at Gmail>
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 = <![CDATA[
Boolean color: red;
Function color: navy;
Null color: blue;
Number color: blue;
Object color: maroon;
String color: green;
Normal color: black; background: white;
ErrorMsg color: white; background: red;
InfoMsg color: black; background: white;
ModeMsg color: white; background: white;
MoreMsg color: green; background: white;
WarningMsg color: red; background: white;
Message white-space: normal; min-width: 100%; padding-left: 2em; text-indent: -2em; display: block;
Filter font-weight: bold;
Keyword color: red;
Tag color: blue;
LineNr color: orange; background: white;
Question color: green; background: white;
StatusLine color: white; background: black;
StatusLineBroken color: black; background: #FF6060; /* light-red */
StatusLineSecure color: black; background: #B0FF00; /* light-green */
TabClose
TabIcon
TabText
TabNumber font-weight: bold; margin: 0px; padding-right: .3ex;
TabIconNumber {
font-weight: bold;
color: white;
text-align: center;
text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px;
}
Title color: magenta; background: white; font-weight: bold;
URL text-decoration: none; color: green; background: inherit;
URL:hover text-decoration: underline; cursor: pointer;
Bell,#liberator-visualbell border: none; background-color: black;
Hint,.liberator-hint,* {
z-index: 5000;
font-family: monospace;
font-size: 10px;
font-weight: bold;
color: white;
background-color: red;
border-color: ButtonShadow;
border-width: 0px;
border-style: solid;
padding: 0px 1px 0px 1px;
position: absolute;
}
Search,.liberator-search,* {
display: inline;
font-size: inherit;
padding: 0;
color: black;
background-color: yellow;
padding: 0;
}
]]>.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(
<html><head><link type="text/css" rel="stylesheet" href={uri}/></head></html>, 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)
]
});
});

View File

@@ -214,8 +214,10 @@ function CommandLine() //{{{
.selection.getRangeAt(0) .selection.getRangeAt(0)
.startContainer.parentNode .startContainer.parentNode
.scrollWidth > commandWidget.inputField.scrollWidth) .scrollWidth > commandWidget.inputField.scrollWidth)
// Yeah, the min-width is stupid. Somehow, it doesn't work otherwise. {
setMultiline(<p style={"white-space: normal; min-width: " + commandWidget.inputField.scrollWidth + "px; width: 100%"}>{str}</p>, highlightGroup); setCommand("");
setMultiline(<span class="hl-Message">{str}</span>, highlightGroup);
}
} }
// TODO: extract CSS // TODO: extract CSS
@@ -233,7 +235,7 @@ function CommandLine() //{{{
* after interpolated data. * after interpolated data.
*/ */
XML.ignoreWhitespace = typeof str == "xml"; XML.ignoreWhitespace = typeof str == "xml";
var output = <div class={"ex-command-output " + highlightGroup}>{template.maybeXML(str)}</div>; var output = <div class={"ex-command-output " + highlightGroup} style={"min-width: " + commandlineWidget.scrollWidth + "px"}>{template.maybeXML(str)}</div>;
XML.ignoreWhiteSpace = true; XML.ignoreWhiteSpace = true;
lastMowOutput = output; lastMowOutput = output;
@@ -267,7 +269,7 @@ function CommandLine() //{{{
if (win.scrollY >= win.scrollMaxY) if (win.scrollY >= win.scrollMaxY)
setLine("Press ENTER or type command to continue", commandline.HL_QUESTION, true); setLine("Press ENTER or type command to continue", commandline.HL_QUESTION, true);
else else
setLine("-- More --", commandline.HL_QUESTION, true); setLine("-- More --", commandline.HL_MOREMSG, true);
} }
else else
{ {
@@ -278,7 +280,7 @@ function CommandLine() //{{{
win.focus(); win.focus();
startHints = false; startHints = false;
modes.push(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE); modes.set(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE);
} }
function autosizeMultilineInputWidget() function autosizeMultilineInputWidget()
@@ -532,7 +534,7 @@ function CommandLine() //{{{
let list = <></>; let list = <></>;
for (let [,message] in Iterator(messageHistory.messages)) for (let [,message] in Iterator(messageHistory.messages))
list += <div class={message.highlight}>{message.str}</div>; list += <div class={message.highlight + " hl-Message"}>{message.str}</div>;
liberator.echo(list, commandline.FORCE_MULTILINE); liberator.echo(list, commandline.FORCE_MULTILINE);
} }
@@ -591,7 +593,7 @@ function CommandLine() //{{{
historyIndex = UNINITIALIZED; historyIndex = UNINITIALIZED;
completionIndex = UNINITIALIZED; completionIndex = UNINITIALIZED;
modes.push(modes.COMMAND_LINE, currentExtendedMode); modes.set(modes.COMMAND_LINE, currentExtendedMode);
setHighlightGroup(this.HL_NORMAL); setHighlightGroup(this.HL_NORMAL);
setPrompt(currentPrompt); setPrompt(currentPrompt);
setCommand(currentCommand); setCommand(currentCommand);

View File

@@ -484,10 +484,23 @@ function Struct()
{ {
let self = this instanceof arguments.callee ? this : new arguments.callee(); let self = this instanceof arguments.callee ? this : new arguments.callee();
for (let [k, v] in Iterator(Array.slice(arguments))) for (let [k, v] in Iterator(Array.slice(arguments)))
{
if (v != undefined)
self[k] = v; self[k] = v;
}
return self; return self;
} }
ConStructor.prototype = 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; return self.constructor = ConStructor;
} }
@@ -497,7 +510,11 @@ Struct.prototype = {
return this.constructor.apply(null, this.slice()); return this.constructor.apply(null, this.slice());
}, },
// Iterator over our named members // 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 // Add no-sideeffect array methods. Can't set new Array() as the prototype or