diff --git a/content/bookmarks.js b/content/bookmarks.js
index 83f42643..88dba54e 100644
--- a/content/bookmarks.js
+++ b/content/bookmarks.js
@@ -175,13 +175,14 @@ liberator.Bookmarks = function () //{{{
bookmarksService.addObserver(observer, false);
}
- var cache = liberator.storage.newObject("bookmark-cache", Cache, false);
- liberator.storage.addObserver("bookmark-cache", function (key, event, arg)
+ let bookmarkObserver = function (key, event, arg)
{
if (event == "add")
liberator.autocommands.trigger("BookmarkAdd", "");
liberator.statusline.updateUrl();
- });
+ }
+ var cache = liberator.storage.newObject("bookmark-cache", Cache, false);
+ liberator.storage.addObserver("bookmark-cache", bookmarkObserver);
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS /////////////////////////////////////////////////
@@ -494,6 +495,7 @@ liberator.Bookmarks = function () //{{{
destroy: function ()
{
+ liberator.storage.removeObserver("bookmark-cache", bookmarkObserver);
}
};
//}}}
diff --git a/content/buffer.js b/content/buffer.js
index 18059183..64825420 100644
--- a/content/buffer.js
+++ b/content/buffer.js
@@ -32,6 +32,13 @@ liberator.Buffer = function () //{{{
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
+ function arrayIter(ary)
+ {
+ let length = ary.length;
+ for (let i = 0; i < length; i++)
+ yield ary[i];
+ }
+
var zoomLevels = [ 1, 10, 25, 50, 75, 90, 100,
120, 150, 200, 300, 500, 1000, 2000 ];
@@ -138,6 +145,13 @@ liberator.Buffer = function () //{{{
win.scrollTo(h, v);
}
+ // Holds option: [function, title] to generate :pageinfo sections
+ var pageInfo = {};
+ function addPageInfoSection(option, title, fn)
+ {
+ pageInfo[option] = [fn, title];
+ }
+
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
@@ -169,13 +183,8 @@ liberator.Buffer = function () //{{{
{
completer: function (filter)
{
- return [
- ["g", "General info"],
- ["f", "Feeds"],
- ["m", "Meta tags"]
- ];
+ return [[k, v[1]] for ([k, v] in Iterator(pageInfo))]
},
- validator: function (value) !(/[^gfm]/.test(value) || value.length > 3 || value.length < 1)
});
liberator.options.add(["scroll", "scr"],
@@ -601,6 +610,161 @@ liberator.Buffer = function () //{{{
liberator.buffer.textZoom = level;
});
+ /////////////////////////////////////////////////////////////////////////////}}}
+ ////////////////////// PAGE INFO ///////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////{{{
+
+ addPageInfoSection("f", "Feeds", function (verbose)
+ {
+ var doc = window.content.document;
+
+ const feedTypes = {
+ "application/rss+xml": "RSS",
+ "application/atom+xml": "Atom",
+ "text/xml": "XML",
+ "application/xml": "XML",
+ "application/rdf+xml": "XML"
+ };
+
+ function isValidFeed(data, principal, isFeed)
+ {
+ if (!data || !principal)
+ return false;
+
+ if (!isFeed)
+ {
+ var type = data.type && data.type.toLowerCase();
+ type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
+
+ isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
+ if (!isFeed)
+ {
+ // really slimy: general XML types with magic letters in the title
+ const titleRegex = /(^|\s)rss($|\s)/i;
+ isFeed = ((type == "text/xml" || type == "application/rdf+xml" || type == "application/xml")
+ && titleRegex.test(data.title));
+ }
+ }
+
+ if (isFeed)
+ {
+ try
+ {
+ urlSecurityCheck(data.href, principal,
+ Components.interfaces.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+ }
+ catch (e)
+ {
+ isFeed = false;
+ }
+ }
+
+ if (type)
+ data.type = type;
+
+ return isFeed;
+ }
+
+ // put feeds rss into pageFeeds[]
+ let nFeed = 0;
+ var linkNodes = doc.getElementsByTagName("link");
+ for (link in arrayIter(linkNodes)) {
+ if (!link.href)
+ return;
+
+ var rel = link.rel && link.rel.toLowerCase();
+
+ if (rel == "feed" || (link.type && rel == "alternate"))
+ {
+ var feed = { title: link.title, href: link.href, type: link.type || "" };
+ if (isValidFeed(feed, doc.nodePrincipal, rel == "feed"))
+ {
+ nFeed++;
+ var type = feedTypes[feed.type] || feedTypes["application/rss+xml"];
+ if (verbose)
+ yield [feed.title, liberator.util.highlightURL(feed.href, true) + ({type})];
+ }
+ }
+ }
+
+ if (!verbose && nFeed)
+ yield nFeed + " feed" + (nFeed > 1 ? "s" : "");
+ });
+
+ addPageInfoSection("g", "General Info", function (verbose)
+ {
+ let doc = window.content.document;
+
+ // get file size
+ const nsICacheService = Components.interfaces.nsICacheService;
+ const ACCESS_READ = Components.interfaces.nsICache.ACCESS_READ;
+ const cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
+ .getService(nsICacheService);
+ let cacheKey = doc.location.toString().replace(/#.*$/, "");
+
+ for (let proto in arrayIter(["HTTP", "FTP"]))
+ {
+ try
+ {
+ var cacheEntryDescriptor = cacheService.createSession(proto, 0, true)
+ .openCacheEntry(cacheKey, ACCESS_READ, false);
+ break;
+ }
+ catch (e) {}
+ }
+
+ var pageSize = []; // [0] bytes; [1] kbytes
+ if (cacheEntryDescriptor)
+ {
+ pageSize[0] = liberator.util.formatBytes(cacheEntryDescriptor.dataSize, 0, false);
+ pageSize[1] = liberator.util.formatBytes(cacheEntryDescriptor.dataSize, 2, true);
+ if (pageSize[1] == pageSize[0])
+ pageSize.length = 1; // don't output "xx Bytes" twice
+ }
+
+ var lastModVerbose = new Date(doc.lastModified).toLocaleString();
+ var lastMod = new Date(doc.lastModified).toLocaleFormat("%x %X");
+ // FIXME: probably not portable across different language versions
+ if (lastModVerbose == "Invalid Date" || new Date(doc.lastModified).getFullYear() == 1970)
+ lastModVerbose = lastMod = null;
+
+ if (!verbose)
+ {
+ if (pageSize[0])
+ yield (pageSize[1] || pageSize[0]) + " bytes";
+ yield lastMod;
+ return;
+ }
+
+ yield ["Title", doc.title];
+ yield ["URL", liberator.util.highlightURL(doc.location.toString(), true)];
+
+ var ref = "referrer" in doc && doc.referrer;
+ if (ref)
+ yield ["Referrer", liberator.util.highlightURL(ref, true)];
+
+ if (pageSize[0])
+ yield ["File Size", pageSize[1] ? pageSize[1] + " (" + pageSize[0] + ")"
+ : pageSize[0]];
+
+ yield ["Mime-Type", doc.contentType];
+ yield ["Encoding", doc.characterSet];
+ yield ["Compatibility", doc.compatMode == "BackCompat" ? "Quirks Mode" : "Full/Almost Standards Mode"];
+ if (lastModVerbose)
+ yield ["Last Modified", lastModVerbose];
+ });
+
+ addPageInfoSection("m", "Meta Tags", function (verbose)
+ {
+ // get meta tag data, sort and put into pageMeta[]
+ var metaNodes = window.content.document.getElementsByTagName("meta");
+
+ let nodes = Array.map(metaNodes, function (node) [(node.name || node.httpEquiv), node.content])
+ .sort(function (a, b) String.localeCompare(a[0].toLowerCase(), b[0].toLowerCase()));
+ return ([node[0], liberator.util.highlightURL(node[1], false)]
+ for each (node in arrayIter(nodes)));
+ });
+
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
@@ -618,6 +782,8 @@ liberator.Buffer = function () //{{{
return stylesheets;
},
+ get pageInfo() pageInfo,
+
// 0 if loading, 1 if loaded or 2 if load failed
get loaded()
{
@@ -677,6 +843,8 @@ liberator.Buffer = function () //{{{
return window.content.document.title;
},
+ addPageInfoSection: addPageInfoSection,
+
// returns an XPathResult object
evaluateXPath: function (expression, doc, elem, asIterator)
{
@@ -1099,202 +1267,34 @@ liberator.Buffer = function () //{{{
showPageInfo: function (verbose)
{
- var doc = window.content.document;
-
- const feedTypes = {
- "application/rss+xml": "RSS",
- "application/atom+xml": "Atom",
- "text/xml": "XML",
- "application/xml": "XML",
- "application/rdf+xml": "XML"
- };
-
- function isValidFeed(data, principal, isFeed)
- {
- if (!data || !principal)
- return false;
-
- if (!isFeed)
- {
- var type = data.type && data.type.toLowerCase();
- type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
-
- isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
- if (!isFeed)
- {
- // really slimy: general XML types with magic letters in the title
- const titleRegex = /(^|\s)rss($|\s)/i;
- isFeed = ((type == "text/xml" || type == "application/rdf+xml" ||
- type == "application/xml") && titleRegex.test(data.title));
- }
- }
-
- if (isFeed)
- {
- try
- {
- urlSecurityCheck(data.href, principal,
- Components.interfaces.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
- }
- catch (e)
- {
- isFeed = false;
- }
- }
-
- if (type)
- data.type = type;
-
- return isFeed;
- }
-
- var pageGeneral = [];
- var pageFeeds = [];
- var pageMeta = [];
-
- // get file size
- const nsICacheService = Components.interfaces.nsICacheService;
- const ACCESS_READ = Components.interfaces.nsICache.ACCESS_READ;
- const cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
- .getService(nsICacheService);
- var httpCacheSession = cacheService.createSession("HTTP", 0, true);
- var ftpCacheSession = cacheService.createSession("FTP", 0, true);
- httpCacheSession.doomEntriesIfExpired = false;
- ftpCacheSession.doomEntriesIfExpired = false;
- var cacheKey = doc.location.toString().replace(/#.*$/, "");
- try
- {
- var cacheEntryDescriptor = httpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
- }
- catch (e)
- {
- try
- {
- cacheEntryDescriptor = ftpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
- }
- catch (e) {}
- }
-
- var pageSize = []; // [0] bytes; [1] kbytes
- if (cacheEntryDescriptor)
- {
- pageSize[0] = liberator.util.formatBytes(cacheEntryDescriptor.dataSize, 0, false);
- pageSize[1] = liberator.util.formatBytes(cacheEntryDescriptor.dataSize, 2, true);
- if (pageSize[1] == pageSize[0])
- pageSize[1] = null; // don't output "xx Bytes" twice
- }
-
- // put feeds rss into pageFeeds[]
- var linkNodes = doc.getElementsByTagName("link");
- Array.forEach(linkNodes, function (link) {
- if (!link.href)
- return;
-
- /* Ok... I don't know what this insanity was trying
- * to do, but, as far as I can tell, it was:
- */
- var rel = link.rel && link.rel.toLowerCase();
-
- if (rel == "feed" || (link.type && rel == "alternate"))
- {
- var feed = { title: link.title, href: link.href, type: link.type || "" };
- if (isValidFeed(feed, doc.nodePrincipal, rel == "feed"))
- {
- var type = feedTypes[feed.type] || feedTypes["application/rss+xml"];
- pageFeeds.push([feed.title, liberator.util.highlightURL(feed.href, true) + ({type})]);
- }
- }
- });
-
- var lastModVerbose = new Date(doc.lastModified).toLocaleString();
- var lastMod = new Date(doc.lastModified).toLocaleFormat("%x %X");
- // FIXME: probably not portable across different language versions
- if (lastModVerbose == "Invalid Date" || new Date(doc.lastModified).getFullYear() == 1970)
- lastModVerbose = lastMod = null;
-
// Ctrl-g single line output
if (!verbose)
{
- var info = []; // tmp array for joining later
- var file = doc.location.pathname.split("/").pop() || "[No Name]";
- var title = doc.title || "[No Title]";
+ let file = content.document.location.pathname.split("/").pop() || "[No Name]";
+ let title = content.document.title || "[No Title]";
- if (pageSize[0])
- info.push(pageSize[1] || pageSize[0]);
-
- if (lastMod)
- info.push(lastMod);
-
- var countFeeds = "";
- if (pageFeeds.length)
- countFeeds = pageFeeds.length + (pageFeeds.length == 1 ? " feed" : " feeds");
-
- if (countFeeds)
- info.push(countFeeds);
+ let info = liberator.template.map("gf", function (opt)
+ liberator.template.map(pageInfo[opt][0](), function (val) val, ", "),
+ ", ");
if (liberator.bookmarks.isBookmarked(this.URL))
- info.push("bookmarked");
+ info += ", bookmarked";
- var pageInfoText = '"' + file + '" [' + info.join(", ") + "] " + title;
+ var pageInfoText = <>"{file}" [{info}] {title}>;
liberator.echo(pageInfoText, liberator.commandline.FORCE_SINGLELINE);
return;
}
- // get general infos
- pageGeneral.push(["Title", doc.title]);
- pageGeneral.push(["URL", liberator.util.highlightURL(doc.location.toString(), true)]);
-
- var ref = "referrer" in doc && doc.referrer;
- if (ref)
- pageGeneral.push(["Referrer", liberator.util.highlightURL(ref, true)]);
-
- if (pageSize[0])
+ let option = liberator.options["pageinfo"];
+ let list = liberator.template.map(option, function (option)
{
- if (pageSize[1])
- pageGeneral.push(["File Size", pageSize[1] + " (" + pageSize[0] + ")"]);
- else
- pageGeneral.push(["File Size", pageSize[0]]);
- }
-
- pageGeneral.push(["Mime-Type", doc.contentType]);
- pageGeneral.push(["Encoding", doc.characterSet]);
- pageGeneral.push(["Compatibility", doc.compatMode == "BackCompat" ? "Quirks Mode" : "Full/Almost Standards Mode"]);
- if (lastModVerbose)
- pageGeneral.push(["Last Modified", lastModVerbose]);
-
- // get meta tag data, sort and put into pageMeta[]
- var metaNodes = doc.getElementsByTagName("meta");
- if (metaNodes.length)
- {
- let nodes = [];
- let i = 0;
-
- nodes = Array.map(metaNodes, function (node) [node.name || node.httpEquiv, node.content]);
- nodes.sort(function (a, b) String.localeCompare(a[0].toLowerCase(), b[0].toLowerCase()));
-
- pageMeta = [[node[0], liberator.util.highlightURL(node[1], false)]
- for each (node in nodes)];
- }
-
- var pageInfoText = "";
- var option = liberator.options["pageinfo"];
- var br = "";
-
- let options = {
- g: [pageGeneral, "General Info"],
- f: [pageFeeds, "Feeds"],
- m: [pageMeta, "Meta Tags"],
- };
- Array.forEach(option, function (option)
- {
- let opt = options[option];
- if (opt && opt[0].length > 0)
- pageInfoText += br + liberator.template.table(opt[1], opt[0]);
- if (!br)
- br = "
";
- }
- );
- liberator.echo(pageInfoText, liberator.commandline.FORCE_MULTILINE);
+ let opt = pageInfo[option];
+ if (opt)
+ return liberator.template.table(opt[1], opt[0](true));
+ else alert(option);
+ },
);
+ XML.prettyPrinting = false;
+ liberator.echo(list, liberator.commandline.FORCE_MULTILINE);
},
viewSelectionSource: function ()
@@ -1715,21 +1715,18 @@ liberator.template = {
map: function (iter, fn, sep)
{
+ if (iter.length) /* Kludge? */
+ iter = liberator.util.arrayIter(iter);
let ret = <>>;
- if (sep == undefined)
+ let n = 0;
+ for each (let i in iter)
{
- for each (let i in iter)
- ret += fn(i);
- }
- else
- {
- let n = 0;
- for each (let i in iter)
- {
- if (n++)
- ret += sep;
- ret += fn(i);
- }
+ let val = fn(i);
+ if (val == undefined)
+ continue;
+ if (sep && n++)
+ ret += sep;
+ ret += val;
}
return ret;
},
@@ -1846,9 +1843,7 @@ liberator.template = {
table: function (title, data)
{
- let self = this;
-
- return this.generic(
+ let table =
| {title} |
@@ -1857,10 +1852,12 @@ liberator.template = {
this.map(data, function (datum)
| {datum[0]} |
- {self.maybeXML(datum[1])} |
+ {liberator.template.maybeXML(datum[1])} |
)
}
-
);
+ ;
+ if (table.tr.length() > 1)
+ return table;
}
};
diff --git a/content/completion.js b/content/completion.js
index fbedc7e3..038ab5ea 100644
--- a/content/completion.js
+++ b/content/completion.js
@@ -234,7 +234,7 @@ liberator.Completion = function () //{{{
try
{
- files = liberator.io.readDirectory(dir);
+ files = liberator.io.readDirectory(dir, true);
if (liberator.options["wildignore"])
{
@@ -244,8 +244,7 @@ liberator.Completion = function () //{{{
}
mapped = files.map(function (file) [tail ? file.leafName : (dir + file.leafName),
- file.isDirectory() ? "Directory" : "File"])
- .sort(function (a, b) a[1].localeCompare(b[1]) || a[0].localeCompare(b[0]));
+ file.isDirectory() ? "Directory" : "File"]);
}
catch (e)
{
diff --git a/content/io.js b/content/io.js
index 1c286661..c26a7af3 100644
--- a/content/io.js
+++ b/content/io.js
@@ -142,7 +142,7 @@ liberator.IO = function () //{{{
// go directly to an absolute path or look for a relative path
// match in 'cdpath'
- if (/^(~|\/|[a-z]:|\.\/|\.\.\/)/i.test(args))
+ if (/^(~|\/|\.\/|\.\.\/)/.test(args))
{
// TODO: apparently we don't handle ../ or ./ paths yet
if (liberator.io.setCurrentDirectory(args))
@@ -323,12 +323,12 @@ liberator.IO = function () //{{{
XML.prettyPrinting = false;
var list =
- {
- liberator.template.map2(scriptNames, function (i, name)
+ {[
| {i+1} |
{name} |
-
)
+
+ for ([i, name] in Iterator(striptNames))].reduce(liberator.buffer.template.add, <>>)
}
.toXMLString();
liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
@@ -551,7 +551,7 @@ liberator.IO = function () //{{{
},
// file is either a full pathname or an instance of file instanceof nsILocalFile
- readDirectory: function (file)
+ readDirectory: function (file, sort)
{
if (typeof file == "string")
file = ioManager.getFile(file);
@@ -568,6 +568,8 @@ liberator.IO = function () //{{{
entry.QueryInterface(Components.interfaces.nsIFile);
array.push(entry);
}
+ if (sort)
+ return array.sort(function (a, b) b.isDirectory() - a.isDirectory() || String.localeCompare(a.path, b.path));
return array;
}
else
diff --git a/content/liberator.js b/content/liberator.js
index be27fd67..efdd331d 100644
--- a/content/liberator.js
+++ b/content/liberator.js
@@ -37,9 +37,16 @@ const liberator = (function () //{{{
function loadModule(name, func)
{
var message = "Loading module " + name + "...";
- liberator.log(message, 0);
- liberator.dump(message + "\n");
- liberator[name] = func();
+ try
+ {
+ liberator.log(message, 0);
+ liberator.dump(message + "\n");
+ liberator[name] = func();
+ }
+ catch(e)
+ {
+ liberator.dump(e + "\n");
+ }
}
// Only general options are added here, which are valid for all vimperator like extensions
@@ -1111,9 +1118,9 @@ const liberator = (function () //{{{
liberator.log("Sourcing plugin directory: " + dir.path + "...", 3);
- let files = liberator.io.readDirectory(dir.path);
+ let files = liberator.io.readDirectory(dir.path, true);
- files.sort(function (a, b) String.localeCompare(a.path, b.path)).forEach(function (file) {
+ files.forEach(function (file) {
if (!file.isDirectory() && /\.(js|vimp)$/i.test(file.path))
liberator.io.source(file.path, false);
});
diff --git a/content/options.js b/content/options.js
index 4da2eb27..aa076702 100644
--- a/content/options.js
+++ b/content/options.js
@@ -847,7 +847,7 @@ liberator.Options = function () //{{{
isDefault: opt.value == opt.defaultValue,
name: opt.name,
default: opt.defaultValue,
- pre: <> >,
+ pre: " ", /* Unicode nonbreaking space. */
value: <>>,
};
@@ -897,7 +897,7 @@ liberator.Options = function () //{{{
default: loadPreference(pref, null, true),
value: <>={liberator.util.colorize(value, false)}>,
name: pref,
- pre: <> >,
+ pre: " ", /* Unicode nonbreaking space. */
};
yield option;
diff --git a/content/util.js b/content/util.js
index c26d7f73..42198ca9 100644
--- a/content/util.js
+++ b/content/util.js
@@ -28,6 +28,13 @@ the terms of any one of the MPL, the GPL or the LGPL.
liberator.util = { //{{{
+ arrayIter: function (ary)
+ {
+ let length = ary.length;
+ for (let i = 0; i < length; i++)
+ yield ary[i];
+ },
+
clip: function (str, length)
{
return str.length <= length ? str : str.substr(0, length - 3) + "...";