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

Make :pageinfo extensible

This commit is contained in:
Kris Maglione
2008-10-02 19:22:02 +00:00
parent 9dd5f1612b
commit 8100d941f8
7 changed files with 240 additions and 226 deletions

View File

@@ -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);
}
};
//}}}

View File

@@ -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) + <span style="color: gray;"> ({type})</span>];
}
}
}
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) + <span style="color: gray;"> ({type})</span>]);
}
}
});
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 = "<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);
}, <br/>);
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 =
<table>
<tr>
<th class="hl-Title" align="left" colspan="2">{title}</th>
@@ -1857,10 +1852,12 @@ liberator.template = {
this.map(data, function (datum)
<tr>
<td style="font-weight: bold; min-width: 150px">{datum[0]}</td>
<td>{self.maybeXML(datum[1])}</td>
<td>{liberator.template.maybeXML(datum[1])}</td>
</tr>)
}
</table>);
</table>;
if (table.tr.length() > 1)
return table;
}
};

View File

@@ -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)
{

View File

@@ -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 =
<table>
{
liberator.template.map2(scriptNames, function (i, name)
{[
<tr>
<td style="text-align: right">{i+1}</td>
<td>{name}</td>
</tr>)
</tr>
for ([i, name] in Iterator(striptNames))].reduce(liberator.buffer.template.add, <></>)
}
</table>.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

View File

@@ -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);
});

View File

@@ -847,7 +847,7 @@ liberator.Options = function () //{{{
isDefault: opt.value == opt.defaultValue,
name: opt.name,
default: opt.defaultValue,
pre: <>&#160;&#160;</>,
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: <>&#160;&#160;</>,
pre: "  ", /* Unicode nonbreaking space. */
};
yield option;

View File

@@ -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) + "...";