mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-22 11:47:58 +01:00
Recfactoring:
* Standard module format. All modules are explicitly declared as modules, they're created via a constructor and instantiated automatically. They're dependency aware. They stringify properly. * Classes are declared the same way (rather like Structs already were). They also stringify properly. Plus, each instance has a rather nifty closure member that closes all of its methods around 'this', so you can pass them to map, forEach, setTimeout, etc. Modules are themselves classes, with a special metaclass, as it were. * Doug Crockford is dead, metaphorically speaking. Closure-based classes just don't fit into any of the common JavaScript frameworks, and they're inefficient and confusing. Now, all class and module members are accessed explicitly via 'this', which makes it very clear that they're class members and not (e.g.) local variables, without anything nasty like Hungarian notation. * Strictly one module per file. Classes that belong to a module live in the same file. * For the moment, there are quite a few utility functions sitting in base.c, because my class implementation used them, and I haven't had the time or inclination to sort them out. I plan to reconcile them with the current mess that is the util namespace. * Changed bracing style.
This commit is contained in:
76
HACKING
76
HACKING
@@ -18,16 +18,15 @@ important, please ask.
|
|||||||
== Coding Style ==
|
== Coding Style ==
|
||||||
|
|
||||||
In general: Just look at the existing source code!
|
In general: Just look at the existing source code!
|
||||||
We try to be quite consistent, but of course, that's not always possible.
|
|
||||||
|
|
||||||
Also we try to target experienced JavaScript developers which do not
|
We try to target experienced JavaScript developers who do not
|
||||||
necessarily need to have a good understanding of Vimperator's source code, nor
|
necessarily need to have a good understanding of Vimperator's source
|
||||||
do they probably know in-depth concepts of other languages like Lisp or Python.
|
code, nor necessarily understand in-depth concepts of other
|
||||||
Therefore, the coding style should feel natural to any JavaScript developer
|
languages like Lisp or Python. Therefore, the coding style should
|
||||||
so it is easy to read and understand. Of course, this does not mean, you have
|
feel natural to any JavaScript developer. Of course, this does not
|
||||||
to avoid all new JavaScript features like list comprehension or generators.
|
mean, you have to avoid all new JavaScript features like list
|
||||||
Use them, when they make sense, but don't use them, when the resulting code
|
comprehension or generators. Use them, when they make sense, but
|
||||||
is hard to read.
|
don't use them when the resulting code is hard to read.
|
||||||
|
|
||||||
=== The most important style issues are: ===
|
=== The most important style issues are: ===
|
||||||
|
|
||||||
@@ -40,29 +39,41 @@ is hard to read.
|
|||||||
* Use " for enclosing strings instead of ', unless using ' avoids escaping of lots of "
|
* Use " for enclosing strings instead of ', unless using ' avoids escaping of lots of "
|
||||||
Example: alert("foo") instead of alert('foo');
|
Example: alert("foo") instead of alert('foo');
|
||||||
|
|
||||||
|
* Use // regexp literals rather than RegExp constructors, unless
|
||||||
|
you're constructing an expression on the fly, or RegExp
|
||||||
|
constructors allow you to escape less /s than the additional
|
||||||
|
escaping of special characters required by string quoting.
|
||||||
|
|
||||||
|
Good: /application\/xhtml\+xml/
|
||||||
|
Bad: RegExp("application/xhtml\\+xml")
|
||||||
|
Good: RegExp("http://(www\\.)vimperator.org/(.*)/(.*)")
|
||||||
|
Bad: /http:\/\/(www\.)vimperator.org\/(.*)\/(.*)/
|
||||||
|
|
||||||
* Exactly one space after if/for/while/catch etc. and after a comma, but none
|
* Exactly one space after if/for/while/catch etc. and after a comma, but none
|
||||||
after a parenthesis or after a function call:
|
after a parenthesis or after a function call:
|
||||||
for (pre; condition; post)
|
for (pre; condition; post)
|
||||||
but:
|
but:
|
||||||
alert("foo");
|
alert("foo");
|
||||||
|
|
||||||
* Opening curly brackets { must be on a new line, unless it is used in a closure:
|
* Bracing is formatted as follows:
|
||||||
function myFunction ()
|
function myFunction () {
|
||||||
{
|
if (foo)
|
||||||
if (foo)
|
return bar;
|
||||||
{
|
else {
|
||||||
baz = false;
|
baz = false;
|
||||||
return bar;
|
return baz;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
var quux = frob("you",
|
||||||
return baz;
|
{
|
||||||
}
|
a: 1,
|
||||||
}
|
b: 42,
|
||||||
but:
|
c: {
|
||||||
setTimeout(function () {
|
hoopy: "frood"
|
||||||
...
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
When in doubt, look for similar code.
|
||||||
|
|
||||||
* No braces for one-line conditional statements:
|
* No braces for one-line conditional statements:
|
||||||
Right:
|
Right:
|
||||||
@@ -97,9 +108,9 @@ is hard to read.
|
|||||||
|
|
||||||
* Use UNIX new lines (\n), not windows (\r\n) or old Mac ones (\r)
|
* Use UNIX new lines (\n), not windows (\r\n) or old Mac ones (\r)
|
||||||
|
|
||||||
* Use Iterators, Array#forEach, or for (let i = 0; i < ary.length; i++)
|
* Use Iterators or Array#forEach to iterate over arrays. for (let i
|
||||||
to iterate over arrays. for (let i in ary) and for each (let i in ary)
|
in ary) and for each (let i in ary) include members in an
|
||||||
include members in an Array.prototype, which some extensions alter.
|
Array.prototype, which some extensions alter.
|
||||||
Right:
|
Right:
|
||||||
for (let [,elem] in Iterator(ary))
|
for (let [,elem] in Iterator(ary))
|
||||||
for (let [k, v] in Iterator(obj))
|
for (let [k, v] in Iterator(obj))
|
||||||
@@ -137,11 +148,4 @@ Additionally, maybe there should be some benchmark information here --
|
|||||||
something to let a developer know what's "too" slow...? Or general
|
something to let a developer know what's "too" slow...? Or general
|
||||||
guidelines about optimization?
|
guidelines about optimization?
|
||||||
|
|
||||||
== Source Code Management ==
|
|
||||||
|
|
||||||
TODO: Document the existence of remote branches and discuss when and how
|
|
||||||
to push to them. At least provide an index so that devs know where
|
|
||||||
to look if an old branch needs to be maintained or a feature needs
|
|
||||||
to be added to a new branch.
|
|
||||||
|
|
||||||
// vim: set ft=asciidoc fdm=marker sw=4 ts=4 et ai:
|
// vim: set ft=asciidoc fdm=marker sw=4 ts=4 et ai:
|
||||||
|
|||||||
275
common/content/autocommands.js
Normal file
275
common/content/autocommands.js
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
|
||||||
|
/** @scope modules */
|
||||||
|
|
||||||
|
const AutoCommand = new Struct("event", "pattern", "command");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @instance autocommands
|
||||||
|
*/
|
||||||
|
const AutoCommands = Module("autocommands", {
|
||||||
|
init: function () {
|
||||||
|
this._store = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
__iterator__: function () util.Array.itervalues(this._store),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new autocommand. <b>cmd</b> will be executed when one of the
|
||||||
|
* specified <b>events</b> occurs and the URL of the applicable buffer
|
||||||
|
* matches <b>regex</b>.
|
||||||
|
*
|
||||||
|
* @param {Array} events The array of event names for which this
|
||||||
|
* autocommand should be executed.
|
||||||
|
* @param {string} regex The URL pattern to match against the buffer URL.
|
||||||
|
* @param {string} cmd The Ex command to run.
|
||||||
|
*/
|
||||||
|
add: function (events, regex, cmd) {
|
||||||
|
if (typeof events == "string") {
|
||||||
|
events = events.split(",");
|
||||||
|
liberator.log("DEPRECATED: the events list arg to autocommands.add() should be an array of event names");
|
||||||
|
}
|
||||||
|
events.forEach(function (event) {
|
||||||
|
this._store.push(new AutoCommand(event, RegExp(regex), cmd));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all autocommands with a matching <b>event</b> and
|
||||||
|
* <b>regex</b>.
|
||||||
|
*
|
||||||
|
* @param {string} event The event name filter.
|
||||||
|
* @param {string} regex The URL pattern filter.
|
||||||
|
* @returns {AutoCommand[]}
|
||||||
|
*/
|
||||||
|
get: function (event, regex) {
|
||||||
|
return this._store.filter(function (autoCmd) matchAutoCmd(autoCmd, event, regex));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all autocommands with a matching <b>event</b> and
|
||||||
|
* <b>regex</b>.
|
||||||
|
*
|
||||||
|
* @param {string} event The event name filter.
|
||||||
|
* @param {string} regex The URL pattern filter.
|
||||||
|
*/
|
||||||
|
remove: function (event, regex) {
|
||||||
|
this._store = this._store.filter(function (autoCmd) !matchAutoCmd(autoCmd, event, regex));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all autocommands with a matching <b>event</b> and
|
||||||
|
* <b>regex</b>.
|
||||||
|
*
|
||||||
|
* @param {string} event The event name filter.
|
||||||
|
* @param {string} regex The URL pattern filter.
|
||||||
|
*/
|
||||||
|
list: function (event, regex) {
|
||||||
|
let cmds = {};
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
this._store.forEach(function (autoCmd) {
|
||||||
|
if (matchAutoCmd(autoCmd, event, regex)) {
|
||||||
|
cmds[autoCmd.event] = cmds[autoCmd.event] || [];
|
||||||
|
cmds[autoCmd.event].push(autoCmd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let list = template.commandOutput(
|
||||||
|
<table>
|
||||||
|
<tr highlight="Title">
|
||||||
|
<td colspan="2">----- Auto Commands -----</td>
|
||||||
|
</tr>
|
||||||
|
{
|
||||||
|
template.map(cmds, function ([event, items])
|
||||||
|
<tr highlight="Title">
|
||||||
|
<td colspan="2">{event}</td>
|
||||||
|
</tr>
|
||||||
|
+
|
||||||
|
template.map(items, function (item)
|
||||||
|
<tr>
|
||||||
|
<td> {item.pattern.source}</td>
|
||||||
|
<td>{item.command}</td>
|
||||||
|
</tr>))
|
||||||
|
}
|
||||||
|
</table>);
|
||||||
|
|
||||||
|
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the execution of all autocommands registered for
|
||||||
|
* <b>event</b>. A map of <b>args</b> is passed to each autocommand
|
||||||
|
* when it is being executed.
|
||||||
|
*
|
||||||
|
* @param {string} event The event to fire.
|
||||||
|
* @param {Object} args The args to pass to each autocommand.
|
||||||
|
*/
|
||||||
|
trigger: function (event, args) {
|
||||||
|
if (options.get("eventignore").has("all", event))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let autoCmds = this._store.filter(function (autoCmd) autoCmd.event == event);
|
||||||
|
|
||||||
|
liberator.echomsg("Executing " + event + " Auto commands for \"*\"", 8);
|
||||||
|
|
||||||
|
let lastPattern = null;
|
||||||
|
let url = args.url || "";
|
||||||
|
|
||||||
|
for (let [, autoCmd] in Iterator(autoCmds)) {
|
||||||
|
if (autoCmd.pattern.test(url)) {
|
||||||
|
if (!lastPattern || lastPattern.source != autoCmd.pattern.source)
|
||||||
|
liberator.echomsg("Executing " + event + " Auto commands for \"" + autoCmd.pattern.source + "\"", 8);
|
||||||
|
|
||||||
|
lastPattern = autoCmd.pattern;
|
||||||
|
liberator.echomsg("autocommand " + autoCmd.command, 9);
|
||||||
|
|
||||||
|
if (typeof autoCmd.command == "function") {
|
||||||
|
try {
|
||||||
|
autoCmd.command.call(autoCmd, args);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
liberator.reportError(e);
|
||||||
|
liberator.echoerr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
liberator.execute(commands.replaceTokens(autoCmd.command, args), null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
matchAutoCmd: function (autoCmd, event, regex) {
|
||||||
|
return (!event || autoCmd.event == event) && (!regex || autoCmd.pattern.source == regex);
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["au[tocmd]"],
|
||||||
|
"Execute commands automatically on events",
|
||||||
|
function (args) {
|
||||||
|
let [event, regex, cmd] = args;
|
||||||
|
let events = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
RegExp(regex);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
liberator.assert(false, "E475: Invalid argument: " + regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
|
||||||
|
let validEvents = config.autocommands.map(function (event) event[0]);
|
||||||
|
validEvents.push("*");
|
||||||
|
|
||||||
|
events = event.split(",");
|
||||||
|
liberator.assert(events.every(function (event) validEvents.indexOf(event) >= 0),
|
||||||
|
"E216: No such group or event: " + event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd) { // add new command, possibly removing all others with the same event/pattern
|
||||||
|
if (args.bang)
|
||||||
|
autocommands.remove(event, regex);
|
||||||
|
if (args["-javascript"])
|
||||||
|
cmd = eval("(function (args) { with(args) {" + cmd + "} })");
|
||||||
|
autocommands.add(events, regex, cmd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (event == "*")
|
||||||
|
event = null;
|
||||||
|
|
||||||
|
if (args.bang) {
|
||||||
|
// TODO: "*" only appears to work in Vim when there is a {group} specified
|
||||||
|
if (args[0] != "*" || regex)
|
||||||
|
autocommands.remove(event, regex); // remove all
|
||||||
|
}
|
||||||
|
else
|
||||||
|
autocommands.list(event, regex); // list all
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
bang: true,
|
||||||
|
completer: function (context) completion.autocmdEvent(context),
|
||||||
|
literal: 2,
|
||||||
|
options: [[["-javascript", "-js"], commands.OPTION_NOARG]]
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "do[autocmd]",
|
||||||
|
description: "Apply the autocommands matching the specified URL pattern to the current buffer"
|
||||||
|
}, {
|
||||||
|
name: "doautoa[ll]",
|
||||||
|
description: "Apply the autocommands matching the specified URL pattern to all buffers"
|
||||||
|
}
|
||||||
|
].forEach(function (command) {
|
||||||
|
commands.add([command.name],
|
||||||
|
command.description,
|
||||||
|
// TODO: Perhaps this should take -args to pass to the command?
|
||||||
|
function (args) {
|
||||||
|
// Vim compatible
|
||||||
|
if (args.length == 0)
|
||||||
|
return void liberator.echomsg("No matching autocommands");
|
||||||
|
|
||||||
|
let [event, url] = args;
|
||||||
|
let defaultURL = url || buffer.URL;
|
||||||
|
let validEvents = config.autocommands.map(function (e) e[0]);
|
||||||
|
|
||||||
|
// TODO: add command validators
|
||||||
|
liberator.assert(event != "*",
|
||||||
|
"E217: Can't execute autocommands for ALL events");
|
||||||
|
liberator.assert(validEvents.indexOf(event) >= 0,
|
||||||
|
"E216: No such group or event: " + args);
|
||||||
|
liberator.assert(autocommands.get(event).some(function (c) c.pattern.test(defaultURL)),
|
||||||
|
"No matching autocommands");
|
||||||
|
|
||||||
|
if (this.name == "doautoall" && liberator.has("tabs")) {
|
||||||
|
let current = tabs.index();
|
||||||
|
|
||||||
|
for (let i = 0; i < tabs.count; i++) {
|
||||||
|
tabs.select(i);
|
||||||
|
// if no url arg is specified use the current buffer's URL
|
||||||
|
autocommands.trigger(event, { url: url || buffer.URL });
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs.select(current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
autocommands.trigger(event, { url: defaultURL });
|
||||||
|
}, {
|
||||||
|
argCount: "*", // FIXME: kludged for proper error message should be "1".
|
||||||
|
completer: function (context) completion.autocmdEvent(context)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
completion: function () {
|
||||||
|
completion.setFunctionCompleter(autocommands.get, [function () config.autocommands]);
|
||||||
|
|
||||||
|
completion.autocmdEvent = function autocmdEvent(context) {
|
||||||
|
context.completions = config.autocommands;
|
||||||
|
};
|
||||||
|
|
||||||
|
completion.macro = function macro(context) {
|
||||||
|
context.title = ["Macro", "Keys"];
|
||||||
|
context.completions = [item for (item in events.getMacros())];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
options: function () {
|
||||||
|
options.add(["eventignore", "ei"],
|
||||||
|
"List of autocommand event names which should be ignored",
|
||||||
|
"stringlist", "",
|
||||||
|
{
|
||||||
|
completer: function () config.autocommands.concat([["all", "All events"]]),
|
||||||
|
validator: Option.validateCompleter
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["focuscontent", "fc"],
|
||||||
|
"Try to stay in normal mode after loading a web page",
|
||||||
|
"boolean", false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
293
common/content/base.js
Normal file
293
common/content/base.js
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
function array(obj) {
|
||||||
|
if (isgenerator(obj))
|
||||||
|
obj = [k for (k in obj)];
|
||||||
|
else if (obj.length)
|
||||||
|
obj = Array.slice(obj);
|
||||||
|
return util.Array(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function keys(obj) {
|
||||||
|
if ('__iterator__' in obj) {
|
||||||
|
var iter = obj.__iterator__;
|
||||||
|
yield '__iterator__';
|
||||||
|
// This is dangerous, but necessary.
|
||||||
|
delete obj.__iterator__;
|
||||||
|
}
|
||||||
|
for (var k in obj)
|
||||||
|
if (obj.hasOwnProperty(k))
|
||||||
|
yield k;
|
||||||
|
if (iter !== undefined)
|
||||||
|
obj.__iterator__ = iter;
|
||||||
|
}
|
||||||
|
function values(obj) {
|
||||||
|
for (var k in obj)
|
||||||
|
if (obj.hasOwnProperty(k))
|
||||||
|
yield obj[k];
|
||||||
|
}
|
||||||
|
function foreach(iter, fn, self) {
|
||||||
|
for (let val in iter)
|
||||||
|
fn.call(self, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dict(ary) {
|
||||||
|
var obj = {};
|
||||||
|
for (var i=0; i < ary.length; i++) {
|
||||||
|
var val = ary[i];
|
||||||
|
obj[val[0]] = val[1];
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(ary) {
|
||||||
|
var obj = {}
|
||||||
|
if (ary)
|
||||||
|
for (var i=0; i < ary.length; i++)
|
||||||
|
obj[ary[i]] = true;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
set.add = function(set, key) { set[key] = true }
|
||||||
|
set.remove = function(set, key) { delete set[key] }
|
||||||
|
|
||||||
|
function iter(obj) {
|
||||||
|
if (obj instanceof Ci.nsISimpleEnumerator)
|
||||||
|
return (function () {
|
||||||
|
while (obj.hasMoreElements())
|
||||||
|
yield obj.getNext();
|
||||||
|
})()
|
||||||
|
if (isinstance(obj, [Ci.nsIStringEnumerator, Ci.nsIUTF8StringEnumerator]))
|
||||||
|
return (function () {
|
||||||
|
while (obj.hasMore())
|
||||||
|
yield obj.getNext();
|
||||||
|
})();
|
||||||
|
if (isinstance(obj, Ci.nsIDOMNodeIterator))
|
||||||
|
return (function () {
|
||||||
|
try {
|
||||||
|
while (true)
|
||||||
|
yield obj.nextNode()
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
})();
|
||||||
|
if (isinstance(obj, [HTMLCollection, NodeList]))
|
||||||
|
return util.Array.iteritems(obj);
|
||||||
|
if (obj instanceof NamedNodeMap)
|
||||||
|
return (function () {
|
||||||
|
for (let i=0; i < obj.length; i++)
|
||||||
|
yield [obj.name, obj]
|
||||||
|
})();
|
||||||
|
return Iterator(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function issubclass(targ, src) {
|
||||||
|
return src === targ ||
|
||||||
|
targ && typeof targ === "function" && targ.prototype instanceof src;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isinstance(targ, src) {
|
||||||
|
const types = {
|
||||||
|
boolean: Boolean,
|
||||||
|
string: String,
|
||||||
|
function: Function,
|
||||||
|
number: Number,
|
||||||
|
}
|
||||||
|
src = Array.concat(src);
|
||||||
|
for (var i=0; i < src.length; i++) {
|
||||||
|
if (targ instanceof src[i])
|
||||||
|
return true;
|
||||||
|
var type = types[typeof targ];
|
||||||
|
if (type && issubclass(src[i], type))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isobject(obj) {
|
||||||
|
return typeof obj === "object" && obj != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isarray(obj) {
|
||||||
|
return Object.prototype.toString(obj) == "[object Array]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isgenerator(val) {
|
||||||
|
return Object.prototype.toString(obj) == "[object Generator]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isstring(val) {
|
||||||
|
return typeof val === "string" || val instanceof String;
|
||||||
|
}
|
||||||
|
|
||||||
|
function callable(val) {
|
||||||
|
return typeof val === "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
function call(fn) {
|
||||||
|
fn.apply(arguments[1], Array.slice(arguments, 2));
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function curry(fn, length, acc) {
|
||||||
|
if (length == null)
|
||||||
|
length = fn.length;
|
||||||
|
if (length == 0)
|
||||||
|
return fn;
|
||||||
|
|
||||||
|
/* Close over function with 'this' */
|
||||||
|
function close(self, fn) function () fn.apply(self, Array.slice(arguments));
|
||||||
|
|
||||||
|
let first = (arguments.length < 3);
|
||||||
|
if (acc == null)
|
||||||
|
acc = [];
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
let args = acc.concat(Array.slice(arguments));
|
||||||
|
|
||||||
|
/* The curried result should preserve 'this' */
|
||||||
|
if (arguments.length == 0)
|
||||||
|
return close(this, arguments.callee);
|
||||||
|
|
||||||
|
if (args.length >= length)
|
||||||
|
return fn.apply(this, args);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
fn = close(this, fn);
|
||||||
|
return curry(fn, length, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(targ) {
|
||||||
|
for (let i=1; i < arguments.length; i++) {
|
||||||
|
let src = arguments[i];
|
||||||
|
foreach(keys(src || {}), function(k) {
|
||||||
|
var get = src.__lookupGetter__(k),
|
||||||
|
set = src.__lookupSetter__(k);
|
||||||
|
if (!get && !set) {
|
||||||
|
var v = src[k];
|
||||||
|
targ[k] = v;
|
||||||
|
if (targ.__proto__ && callable(v)) {
|
||||||
|
v.superapply = function(self, args) {
|
||||||
|
return targ.__proto__[k].apply(self, args);
|
||||||
|
}
|
||||||
|
v.supercall = function(self) {
|
||||||
|
return v.superapply(self, Array.slice(arguments, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (get)
|
||||||
|
targ.__defineGetter__(k, get);
|
||||||
|
if (set)
|
||||||
|
targ.__defineSetter__(k, set);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return targ;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extend(subc, superc, overrides) {
|
||||||
|
subc.prototype = { __proto__: superc.prototype };
|
||||||
|
update(subc.prototype, overrides);
|
||||||
|
|
||||||
|
subc.superclass = superc.prototype;
|
||||||
|
subc.prototype.constructor = subc;
|
||||||
|
subc.prototype.__class__ = subc;
|
||||||
|
|
||||||
|
if (superc.prototype.constructor === Object.prototype.constructor)
|
||||||
|
superc.prototype.constructor = superc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Class() {
|
||||||
|
function constructor() {
|
||||||
|
let self = {
|
||||||
|
__proto__: Constructor.prototype,
|
||||||
|
constructor: Constructor,
|
||||||
|
get closure() {
|
||||||
|
delete this.closure;
|
||||||
|
const self = this;
|
||||||
|
return this.closure = dict([k for (k in this) if (!self.__lookupGetter__(k) && callable(self[k]))].map(
|
||||||
|
function (k) [k, function () self[k].apply(self, arguments)]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = self.init.apply(self, arguments);
|
||||||
|
return res !== undefined ? res : self
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = Array.slice(arguments);
|
||||||
|
if (isstring(args[0]))
|
||||||
|
var name = args.shift();
|
||||||
|
var superclass = Class;
|
||||||
|
if (callable(args[0]))
|
||||||
|
superclass = args.shift();
|
||||||
|
|
||||||
|
var Constructor = eval("(function " + (name || superclass.name) +
|
||||||
|
String.substr(constructor, 20) + ")");
|
||||||
|
|
||||||
|
if (!('init' in superclass.prototype)) {
|
||||||
|
var superc = superclass;
|
||||||
|
superclass = function Shim() {}
|
||||||
|
extend(superclass, superc, {
|
||||||
|
init: superc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(Constructor, superclass, args[0]);
|
||||||
|
update(Constructor, args[1]);
|
||||||
|
args = args.slice(2);
|
||||||
|
Array.forEach(args, function(obj) {
|
||||||
|
if (callable(obj))
|
||||||
|
obj = obj.prototype;
|
||||||
|
update(Constructor.prototype, obj);
|
||||||
|
});
|
||||||
|
return Constructor;
|
||||||
|
}
|
||||||
|
Class.toString = function () "[class " + this.constructor.name + "]",
|
||||||
|
Class.prototype = {
|
||||||
|
init: function() {},
|
||||||
|
toString: function () "[instance " + this.constructor.name + "]",
|
||||||
|
};
|
||||||
|
|
||||||
|
const Struct = Class("Struct", {
|
||||||
|
init: function () {
|
||||||
|
let args = Array.slice(arguments);
|
||||||
|
this.__defineGetter__("length", function () args.length);
|
||||||
|
this.__defineGetter__("members", function () args.slice());
|
||||||
|
for (let arg in Iterator(args)) {
|
||||||
|
let [i, name] = arg;
|
||||||
|
this.__defineGetter__(name, function () this[i]);
|
||||||
|
this.__defineSetter__(name, function (val) { this[i] = val; });
|
||||||
|
}
|
||||||
|
function Struct() {
|
||||||
|
let self = this instanceof arguments.callee ? this : new arguments.callee();
|
||||||
|
//for (let [k, v] in Iterator(Array.slice(arguments))) // That is makes using struct twice as slow as the following code:
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
if (arguments[i] != undefined)
|
||||||
|
self[i] = arguments[i];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
Struct.prototype = this;
|
||||||
|
Struct.defaultValue = function (key, val) {
|
||||||
|
let i = args.indexOf(key);
|
||||||
|
Struct.prototype.__defineGetter__(i, function () (this[i] = val.call(this), this[i])); // Kludge for FF 3.0
|
||||||
|
Struct.prototype.__defineSetter__(i, function (val) {
|
||||||
|
let value = val;
|
||||||
|
this.__defineGetter__(i, function () value);
|
||||||
|
this.__defineSetter__(i, function (val) { value = val });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return this.constructor = Struct;
|
||||||
|
},
|
||||||
|
|
||||||
|
clone: function clone() {
|
||||||
|
return this.constructor.apply(null, this.slice());
|
||||||
|
},
|
||||||
|
// Iterator over our named 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
|
||||||
|
// get length() won't work.
|
||||||
|
for (let [, k] in Iterator(["concat", "every", "filter", "forEach", "indexOf", "join", "lastIndexOf",
|
||||||
|
"map", "reduce", "reduceRight", "reverse", "slice", "some", "sort"]))
|
||||||
|
Struct.prototype[k] = Array.prototype[k];
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -9,15 +9,10 @@
|
|||||||
/**
|
/**
|
||||||
* @instance browser
|
* @instance browser
|
||||||
*/
|
*/
|
||||||
function Browser() //{{{
|
const Browser = Module("browser", {
|
||||||
{
|
}, {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
// TODO: support 'nrformats'? -> probably not worth it --mst
|
// TODO: support 'nrformats'? -> probably not worth it --mst
|
||||||
function incrementURL(count)
|
incrementURL: function (count) {
|
||||||
{
|
|
||||||
let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
|
let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
|
||||||
if (!matches)
|
if (!matches)
|
||||||
return void liberator.beep();
|
return void liberator.beep();
|
||||||
@@ -25,253 +20,219 @@ function Browser() //{{{
|
|||||||
let [, pre, number, post] = matches;
|
let [, pre, number, post] = matches;
|
||||||
let newNumber = parseInt(number, 10) + count;
|
let newNumber = parseInt(number, 10) + count;
|
||||||
let newNumberStr = String(newNumber > 0 ? newNumber : 0);
|
let newNumberStr = String(newNumber > 0 ? newNumber : 0);
|
||||||
if (number.match(/^0/)) // add 0009<C-a> should become 0010
|
if (number.match(/^0/)) { // add 0009<C-a> should become 0010
|
||||||
{
|
|
||||||
while (newNumberStr.length < number.length)
|
while (newNumberStr.length < number.length)
|
||||||
newNumberStr = "0" + newNumberStr;
|
newNumberStr = "0" + newNumberStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
liberator.open(pre + newNumberStr + post);
|
liberator.open(pre + newNumberStr + post);
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
options: function () {
|
||||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
options.add(["encoding", "enc"],
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
"Sets the current buffer's character encoding",
|
||||||
|
"string", "UTF-8",
|
||||||
options.add(["encoding", "enc"],
|
|
||||||
"Sets the current buffer's character encoding",
|
|
||||||
"string", "UTF-8",
|
|
||||||
{
|
|
||||||
scope: options.OPTION_SCOPE_LOCAL,
|
|
||||||
getter: function () getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset,
|
|
||||||
setter: function (val)
|
|
||||||
{
|
{
|
||||||
if (options["encoding"] == val)
|
scope: options.OPTION_SCOPE_LOCAL,
|
||||||
return val;
|
getter: function () getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset,
|
||||||
|
setter: function (val) {
|
||||||
|
if (options["encoding"] == val)
|
||||||
|
return val;
|
||||||
|
|
||||||
// Stolen from browser.jar/content/browser/browser.js, more or less.
|
// Stolen from browser.jar/content/browser/browser.js, more or less.
|
||||||
try
|
try {
|
||||||
{
|
getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
|
||||||
getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
|
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
|
||||||
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
|
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||||
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
}
|
||||||
}
|
catch (e) { liberator.reportError(e); }
|
||||||
catch (e) { liberator.reportError(e); }
|
|
||||||
},
|
|
||||||
completer: function (context) completion.charset(context),
|
|
||||||
validator: Option.validateCompleter
|
|
||||||
});
|
|
||||||
|
|
||||||
// only available in FF 3.5
|
|
||||||
services.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
|
|
||||||
if (services.get("privateBrowsing"))
|
|
||||||
{
|
|
||||||
options.add(["private", "pornmode"],
|
|
||||||
"Set the 'private browsing' option",
|
|
||||||
"boolean", false,
|
|
||||||
{
|
|
||||||
setter: function (value) services.get("privateBrowsing").privateBrowsingEnabled = value,
|
|
||||||
getter: function () services.get("privateBrowsing").privateBrowsingEnabled
|
|
||||||
});
|
|
||||||
let services = modules.services; // Storage objects are global to all windows, 'modules' isn't.
|
|
||||||
storage.newObject("private-mode", function () {
|
|
||||||
({
|
|
||||||
init: function ()
|
|
||||||
{
|
|
||||||
services.get("observer").addObserver(this, "private-browsing", false);
|
|
||||||
services.get("observer").addObserver(this, "quit-application", false);
|
|
||||||
this.private = services.get("privateBrowsing").privateBrowsingEnabled;
|
|
||||||
},
|
},
|
||||||
observe: function (subject, topic, data)
|
completer: function (context) completion.charset(context),
|
||||||
|
validator: Option.validateCompleter
|
||||||
|
});
|
||||||
|
|
||||||
|
// only available in FF 3.5
|
||||||
|
services.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
|
||||||
|
if (services.get("privateBrowsing")) {
|
||||||
|
options.add(["private", "pornmode"],
|
||||||
|
"Set the 'private browsing' option",
|
||||||
|
"boolean", false,
|
||||||
{
|
{
|
||||||
if (topic == "private-browsing")
|
setter: function (value) services.get("privateBrowsing").privateBrowsingEnabled = value,
|
||||||
{
|
getter: function () services.get("privateBrowsing").privateBrowsingEnabled
|
||||||
if (data == "enter")
|
});
|
||||||
storage.privateMode = true;
|
let services = modules.services; // Storage objects are global to all windows, 'modules' isn't.
|
||||||
else if (data == "exit")
|
storage.newObject("private-mode", function () {
|
||||||
storage.privateMode = false;
|
({
|
||||||
storage.fireEvent("private-mode", "change", storage.privateMode);
|
init: function () {
|
||||||
|
services.get("observer").addObserver(this, "private-browsing", false);
|
||||||
|
services.get("observer").addObserver(this, "quit-application", false);
|
||||||
|
this.private = services.get("privateBrowsing").privateBrowsingEnabled;
|
||||||
|
},
|
||||||
|
observe: function (subject, topic, data) {
|
||||||
|
if (topic == "private-browsing") {
|
||||||
|
if (data == "enter")
|
||||||
|
storage.privateMode = true;
|
||||||
|
else if (data == "exit")
|
||||||
|
storage.privateMode = false;
|
||||||
|
storage.fireEvent("private-mode", "change", storage.privateMode);
|
||||||
|
}
|
||||||
|
else if (topic == "quit-application") {
|
||||||
|
services.get("observer").removeObserver(this, "quit-application");
|
||||||
|
services.get("observer").removeObserver(this, "private-browsing");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (topic == "quit-application")
|
}).init();
|
||||||
{
|
}, { store: false });
|
||||||
services.get("observer").removeObserver(this, "quit-application");
|
storage.addObserver("private-mode",
|
||||||
services.get("observer").removeObserver(this, "private-browsing");
|
function (key, event, value) {
|
||||||
|
autocommands.trigger("PrivateMode", { state: value });
|
||||||
|
}, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.add(["urlseparator"],
|
||||||
|
"Set the separator regex used to separate multiple URL args",
|
||||||
|
"string", ",\\s");
|
||||||
|
},
|
||||||
|
|
||||||
|
mappings: function () {
|
||||||
|
mappings.add([modes.NORMAL],
|
||||||
|
["y"], "Yank current location to the clipboard",
|
||||||
|
function () { util.copyToClipboard(buffer.URL, true); });
|
||||||
|
|
||||||
|
// opening websites
|
||||||
|
mappings.add([modes.NORMAL],
|
||||||
|
["o"], "Open one or more URLs",
|
||||||
|
function () { commandline.open(":", "open ", modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["O"],
|
||||||
|
"Open one or more URLs, based on current location",
|
||||||
|
function () { commandline.open(":", "open " + buffer.URL, modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["t"],
|
||||||
|
"Open one or more URLs in a new tab",
|
||||||
|
function () { commandline.open(":", "tabopen ", modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["T"],
|
||||||
|
"Open one or more URLs in a new tab, based on current location",
|
||||||
|
function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["w"],
|
||||||
|
"Open one or more URLs in a new window",
|
||||||
|
function () { commandline.open(":", "winopen ", modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["W"],
|
||||||
|
"Open one or more URLs in a new window, based on current location",
|
||||||
|
function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL],
|
||||||
|
["<C-a>"], "Increment last number in URL",
|
||||||
|
function (count) { Browser.incrementURL(Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL],
|
||||||
|
["<C-x>"], "Decrement last number in URL",
|
||||||
|
function (count) { Browser.incrementURL(-Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["~"],
|
||||||
|
"Open home directory",
|
||||||
|
function () { liberator.open("~"); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["gh"],
|
||||||
|
"Open homepage",
|
||||||
|
function () { BrowserHome(); });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["gH"],
|
||||||
|
"Open homepage in a new tab",
|
||||||
|
function () {
|
||||||
|
let homepages = gHomeButton.getHomePage();
|
||||||
|
liberator.open(homepages, { from: "homepage", where: liberator.NEW_TAB });
|
||||||
|
});
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["gu"],
|
||||||
|
"Go to parent directory",
|
||||||
|
function (count) {
|
||||||
|
function isDirectory(url) {
|
||||||
|
if (/^file:\/|^\//.test(url)) {
|
||||||
|
let file = io.File(url);
|
||||||
|
return file.exists() && file.isDirectory();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// for all other locations just check if the URL ends with /
|
||||||
|
return /\/$/.test(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).init();
|
|
||||||
}, { store: false });
|
if (count < 1)
|
||||||
storage.addObserver("private-mode",
|
count = 1;
|
||||||
function (key, event, value) {
|
|
||||||
autocommands.trigger("PrivateMode", { state: value });
|
// XXX
|
||||||
}, window);
|
let url = buffer.URL;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
if (isDirectory(url))
|
||||||
|
url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
|
||||||
|
else
|
||||||
|
url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
|
||||||
|
}
|
||||||
|
url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end
|
||||||
|
|
||||||
|
if (url == buffer.URL)
|
||||||
|
liberator.beep();
|
||||||
|
else
|
||||||
|
liberator.open(url);
|
||||||
|
},
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["gU"],
|
||||||
|
"Go to the root of the website",
|
||||||
|
function () {
|
||||||
|
let uri = content.document.location;
|
||||||
|
if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
|
||||||
|
return void liberator.beep();
|
||||||
|
liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
|
||||||
|
});
|
||||||
|
|
||||||
|
mappings.add([modes.NORMAL], ["<C-l>"],
|
||||||
|
"Redraw the screen",
|
||||||
|
function () { commands.get("redraw").execute("", false); });
|
||||||
|
},
|
||||||
|
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["downl[oads]", "dl"],
|
||||||
|
"Show progress of current downloads",
|
||||||
|
function () {
|
||||||
|
liberator.open("chrome://mozapps/content/downloads/downloads.xul",
|
||||||
|
options.get("newtab").has("all", "downloads")
|
||||||
|
? liberator.NEW_TAB : liberator.CURRENT_TAB);
|
||||||
|
},
|
||||||
|
{ argCount: "0" });
|
||||||
|
|
||||||
|
commands.add(["o[pen]", "e[dit]"],
|
||||||
|
"Open one or more URLs in the current tab",
|
||||||
|
function (args) {
|
||||||
|
args = args.string;
|
||||||
|
|
||||||
|
if (args)
|
||||||
|
liberator.open(args);
|
||||||
|
else
|
||||||
|
liberator.open("about:blank");
|
||||||
|
}, {
|
||||||
|
completer: function (context) completion.url(context),
|
||||||
|
literal: 0,
|
||||||
|
privateData: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(["redr[aw]"],
|
||||||
|
"Redraw the screen",
|
||||||
|
function () {
|
||||||
|
let wu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
wu.redraw();
|
||||||
|
modes.show();
|
||||||
|
},
|
||||||
|
{ argCount: "0" });
|
||||||
}
|
}
|
||||||
|
});
|
||||||
options.add(["urlseparator"],
|
|
||||||
"Set the separator regex used to separate multiple URL args",
|
|
||||||
"string", ",\\s");
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL],
|
|
||||||
["y"], "Yank current location to the clipboard",
|
|
||||||
function () { util.copyToClipboard(buffer.URL, true); });
|
|
||||||
|
|
||||||
// opening websites
|
|
||||||
mappings.add([modes.NORMAL],
|
|
||||||
["o"], "Open one or more URLs",
|
|
||||||
function () { commandline.open(":", "open ", modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["O"],
|
|
||||||
"Open one or more URLs, based on current location",
|
|
||||||
function () { commandline.open(":", "open " + buffer.URL, modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["t"],
|
|
||||||
"Open one or more URLs in a new tab",
|
|
||||||
function () { commandline.open(":", "tabopen ", modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["T"],
|
|
||||||
"Open one or more URLs in a new tab, based on current location",
|
|
||||||
function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["w"],
|
|
||||||
"Open one or more URLs in a new window",
|
|
||||||
function () { commandline.open(":", "winopen ", modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["W"],
|
|
||||||
"Open one or more URLs in a new window, based on current location",
|
|
||||||
function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL],
|
|
||||||
["<C-a>"], "Increment last number in URL",
|
|
||||||
function (count) { incrementURL(Math.max(count, 1)); },
|
|
||||||
{ count: true });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL],
|
|
||||||
["<C-x>"], "Decrement last number in URL",
|
|
||||||
function (count) { incrementURL(-Math.max(count, 1)); },
|
|
||||||
{ count: true });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["~"],
|
|
||||||
"Open home directory",
|
|
||||||
function () { liberator.open("~"); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["gh"],
|
|
||||||
"Open homepage",
|
|
||||||
function () { BrowserHome(); });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["gH"],
|
|
||||||
"Open homepage in a new tab",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
let homepages = gHomeButton.getHomePage();
|
|
||||||
liberator.open(homepages, { from: "homepage", where: liberator.NEW_TAB });
|
|
||||||
});
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["gu"],
|
|
||||||
"Go to parent directory",
|
|
||||||
function (count)
|
|
||||||
{
|
|
||||||
function isDirectory(url)
|
|
||||||
{
|
|
||||||
if (/^file:\/|^\//.test(url))
|
|
||||||
{
|
|
||||||
let file = io.File(url);
|
|
||||||
return file.exists() && file.isDirectory();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// for all other locations just check if the URL ends with /
|
|
||||||
return /\/$/.test(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 1)
|
|
||||||
count = 1;
|
|
||||||
|
|
||||||
// XXX
|
|
||||||
let url = buffer.URL;
|
|
||||||
for (let i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (isDirectory(url))
|
|
||||||
url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
|
|
||||||
else
|
|
||||||
url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
|
|
||||||
}
|
|
||||||
url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end
|
|
||||||
|
|
||||||
if (url == buffer.URL)
|
|
||||||
liberator.beep();
|
|
||||||
else
|
|
||||||
liberator.open(url);
|
|
||||||
},
|
|
||||||
{ count: true });
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["gU"],
|
|
||||||
"Go to the root of the website",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
let uri = content.document.location;
|
|
||||||
if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
|
|
||||||
return void liberator.beep();
|
|
||||||
liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
|
|
||||||
});
|
|
||||||
|
|
||||||
mappings.add([modes.NORMAL], ["<C-l>"],
|
|
||||||
"Redraw the screen",
|
|
||||||
function () { commands.get("redraw").execute("", false); });
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
commands.add(["downl[oads]", "dl"],
|
|
||||||
"Show progress of current downloads",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
liberator.open("chrome://mozapps/content/downloads/downloads.xul",
|
|
||||||
options.get("newtab").has("all", "downloads")
|
|
||||||
? liberator.NEW_TAB : liberator.CURRENT_TAB);
|
|
||||||
},
|
|
||||||
{ argCount: "0" });
|
|
||||||
|
|
||||||
commands.add(["o[pen]", "e[dit]"],
|
|
||||||
"Open one or more URLs in the current tab",
|
|
||||||
function (args)
|
|
||||||
{
|
|
||||||
args = args.string;
|
|
||||||
|
|
||||||
if (args)
|
|
||||||
liberator.open(args);
|
|
||||||
else
|
|
||||||
liberator.open("about:blank");
|
|
||||||
},
|
|
||||||
{
|
|
||||||
completer: function (context) completion.url(context),
|
|
||||||
literal: 0,
|
|
||||||
privateData: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.add(["redr[aw]"],
|
|
||||||
"Redraw the screen",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
let wu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIDOMWindowUtils);
|
|
||||||
wu.redraw();
|
|
||||||
modes.show();
|
|
||||||
},
|
|
||||||
{ argCount: "0" });
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
return {
|
|
||||||
// TODO: extract browser-specific functionality from liberator
|
|
||||||
};
|
|
||||||
//}}}
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1867
common/content/commandline.js
Normal file
1867
common/content/commandline.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
try { __liberator_eval_result = eval(__liberator_eval_string);
|
try { __liberator_eval_result = eval(__liberator_eval_string);
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
__liberator_eval_error = e;
|
__liberator_eval_error = e;
|
||||||
}
|
}
|
||||||
// IMPORTANT: The eval statement *must* remain on the first line
|
// IMPORTANT: The eval statement *must* remain on the first line
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,70 +13,192 @@
|
|||||||
// : 'linksearch' searches should highlight link matches only
|
// : 'linksearch' searches should highlight link matches only
|
||||||
// : changing any search settings should also update the search state including highlighting
|
// : changing any search settings should also update the search state including highlighting
|
||||||
// : incremental searches shouldn't permanently update search modifiers
|
// : incremental searches shouldn't permanently update search modifiers
|
||||||
|
//
|
||||||
|
// TODO: Clean up this rat's nest. --Kris
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @instance finder
|
* @instance finder
|
||||||
*/
|
*/
|
||||||
function Finder() //{{{
|
const Finder = Module("finder", {
|
||||||
{
|
init: function () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
const self = this;
|
||||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
var found = false; // true if the last search was successful
|
this._found = false; // true if the last search was successful
|
||||||
var backwards = false; // currently searching backwards
|
this._backwards = false; // currently searching backwards
|
||||||
var searchString = ""; // current search string (without modifiers)
|
this._searchString = ""; // current search string (without modifiers)
|
||||||
var searchPattern = ""; // current search string (includes modifiers)
|
this._searchPattern = ""; // current search string (includes modifiers)
|
||||||
var lastSearchPattern = ""; // the last searched pattern (includes modifiers)
|
this._lastSearchPattern = ""; // the last searched pattern (includes modifiers)
|
||||||
var lastSearchString = ""; // the last searched string (without modifiers)
|
this._lastSearchString = ""; // the last searched string (without modifiers)
|
||||||
var lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
this._lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
||||||
var caseSensitive = false; // search string is case sensitive
|
this._caseSensitive = false; // search string is case sensitive
|
||||||
var linksOnly = false; // search is limited to link text only
|
this._linksOnly = false; // search is limited to link text only
|
||||||
|
|
||||||
// Event handlers for search - closure is needed
|
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
||||||
commandline.registerCallback("change", modes.SEARCH_FORWARD, function (str) { finder.onKeyPress(str); });
|
* methods were unstable, and changed. The new version is not remotely
|
||||||
commandline.registerCallback("submit", modes.SEARCH_FORWARD, function (str) { finder.onSubmit(str); });
|
* compatible with what we do.
|
||||||
commandline.registerCallback("cancel", modes.SEARCH_FORWARD, function () { finder.onCancel(); });
|
* The following only applies to this object, and may not be
|
||||||
// TODO: allow advanced myModes in register/triggerCallback
|
* necessary, or accurate, but, just in case:
|
||||||
commandline.registerCallback("change", modes.SEARCH_BACKWARD, function (str) { finder.onKeyPress(str); });
|
* The Original Code is mozilla.org viewsource frontend.
|
||||||
commandline.registerCallback("submit", modes.SEARCH_BACKWARD, function (str) { finder.onSubmit(str); });
|
*
|
||||||
commandline.registerCallback("cancel", modes.SEARCH_BACKWARD, function () { finder.onCancel(); });
|
* The Initial Developer of the Original Code is
|
||||||
|
* Netscape Communications Corporation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (c) 2003
|
||||||
|
* by the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Blake Ross <blake@cs.stanford.edu> (Original Author)
|
||||||
|
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||||
|
* Ben Basson <contact@cusser.net>
|
||||||
|
* Jason Barnabe <jason_barnabe@fastmail.fm>
|
||||||
|
* Asaf Romano <mano@mozilla.com>
|
||||||
|
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
||||||
|
* Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
|
||||||
|
*/
|
||||||
|
this._highlighter = {
|
||||||
|
|
||||||
|
doc: null,
|
||||||
|
|
||||||
|
spans: [],
|
||||||
|
|
||||||
|
search: function (aWord, matchCase) {
|
||||||
|
var finder = services.create("find");
|
||||||
|
if (matchCase !== undefined)
|
||||||
|
self._caseSensitive = matchCase;
|
||||||
|
|
||||||
|
var range;
|
||||||
|
while ((range = finder.Find(aWord, this.searchRange, this.startPt, this.endPt)))
|
||||||
|
yield range;
|
||||||
|
},
|
||||||
|
|
||||||
|
highlightDoc: function highlightDoc(win, aWord) {
|
||||||
|
this.doc = content.document; // XXX
|
||||||
|
Array.forEach(win.frames, function (frame) this._highlighter.highlightDoc(frame, aWord));
|
||||||
|
|
||||||
|
var doc = win.document;
|
||||||
|
if (!doc || !(doc instanceof HTMLDocument))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!aWord) {
|
||||||
|
let elems = this._highlighter.spans;
|
||||||
|
for (let i = elems.length; --i >= 0;) {
|
||||||
|
let elem = elems[i];
|
||||||
|
let docfrag = doc.createDocumentFragment();
|
||||||
|
let next = elem.nextSibling;
|
||||||
|
let parent = elem.parentNode;
|
||||||
|
|
||||||
|
let child;
|
||||||
|
while (child = elem.firstChild)
|
||||||
|
docfrag.appendChild(child);
|
||||||
|
|
||||||
|
parent.removeChild(elem);
|
||||||
|
parent.insertBefore(docfrag, next);
|
||||||
|
parent.normalize();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseNode = <span highlight="Search"/>;
|
||||||
|
baseNode = util.xmlToDom(baseNode, window.content.document);
|
||||||
|
|
||||||
|
var body = doc.body;
|
||||||
|
var count = body.childNodes.length;
|
||||||
|
this.searchRange = doc.createRange();
|
||||||
|
this.startPt = doc.createRange();
|
||||||
|
this.endPt = doc.createRange();
|
||||||
|
|
||||||
|
this.searchRange.setStart(body, 0);
|
||||||
|
this.searchRange.setEnd(body, count);
|
||||||
|
|
||||||
|
this.startPt.setStart(body, 0);
|
||||||
|
this.startPt.setEnd(body, 0);
|
||||||
|
this.endPt.setStart(body, count);
|
||||||
|
this.endPt.setEnd(body, count);
|
||||||
|
|
||||||
|
liberator.interrupted = false;
|
||||||
|
let n = 0;
|
||||||
|
for (let retRange in this.search(aWord, this._caseSensitive)) {
|
||||||
|
// Highlight
|
||||||
|
var nodeSurround = baseNode.cloneNode(true);
|
||||||
|
var node = this.highlight(retRange, nodeSurround);
|
||||||
|
this.startPt = node.ownerDocument.createRange();
|
||||||
|
this.startPt.setStart(node, node.childNodes.length);
|
||||||
|
this.startPt.setEnd(node, node.childNodes.length);
|
||||||
|
if (n++ % 20 == 0)
|
||||||
|
liberator.threadYield(true);
|
||||||
|
if (liberator.interrupted)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
highlight: function highlight(aRange, aNode) {
|
||||||
|
var startContainer = aRange.startContainer;
|
||||||
|
var startOffset = aRange.startOffset;
|
||||||
|
var endOffset = aRange.endOffset;
|
||||||
|
var docfrag = aRange.extractContents();
|
||||||
|
var before = startContainer.splitText(startOffset);
|
||||||
|
var parent = before.parentNode;
|
||||||
|
aNode.appendChild(docfrag);
|
||||||
|
parent.insertBefore(aNode, before);
|
||||||
|
this.spans.push(aNode);
|
||||||
|
return aNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all search highlighting.
|
||||||
|
*/
|
||||||
|
clear: function () {
|
||||||
|
this.spans.forEach(function (span) {
|
||||||
|
if (span.parentNode) {
|
||||||
|
let el = span.firstChild;
|
||||||
|
while (el) {
|
||||||
|
span.removeChild(el);
|
||||||
|
span.parentNode.insertBefore(el, span);
|
||||||
|
el = span.firstChild;
|
||||||
|
}
|
||||||
|
span.parentNode.removeChild(span);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.spans = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
isHighlighted: function (doc) this.doc == doc && this.spans.length > 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
// set searchString, searchPattern, caseSensitive, linksOnly
|
// set searchString, searchPattern, caseSensitive, linksOnly
|
||||||
function processUserPattern(pattern)
|
processUserPattern: function (pattern) {
|
||||||
{
|
|
||||||
//// strip off pattern terminator and offset
|
//// strip off pattern terminator and offset
|
||||||
//if (backwards)
|
//if (backwards)
|
||||||
// pattern = pattern.replace(/\?.*/, "");
|
// pattern = pattern.replace(/\?.*/, "");
|
||||||
//else
|
//else
|
||||||
// pattern = pattern.replace(/\/.*/, "");
|
// pattern = pattern.replace(/\/.*/, "");
|
||||||
|
|
||||||
searchPattern = pattern;
|
this._searchPattern = pattern;
|
||||||
|
|
||||||
// links only search - \l wins if both modifiers specified
|
// links only search - \l wins if both modifiers specified
|
||||||
if (/\\l/.test(pattern))
|
if (/\\l/.test(pattern))
|
||||||
linksOnly = true;
|
this._linksOnly = true;
|
||||||
else if (/\L/.test(pattern))
|
else if (/\L/.test(pattern))
|
||||||
linksOnly = false;
|
this._linksOnly = false;
|
||||||
else if (options["linksearch"])
|
else if (options["linksearch"])
|
||||||
linksOnly = true;
|
this._linksOnly = true;
|
||||||
else
|
else
|
||||||
linksOnly = false;
|
this._linksOnly = false;
|
||||||
|
|
||||||
// strip links-only modifiers
|
// strip links-only modifiers
|
||||||
pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||||
|
|
||||||
// case sensitivity - \c wins if both modifiers specified
|
// case sensitivity - \c wins if both modifiers specified
|
||||||
if (/\c/.test(pattern))
|
if (/\c/.test(pattern))
|
||||||
caseSensitive = false;
|
this._caseSensitive = false;
|
||||||
else if (/\C/.test(pattern))
|
else if (/\C/.test(pattern))
|
||||||
caseSensitive = true;
|
this._caseSensitive = true;
|
||||||
else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
|
else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
|
||||||
caseSensitive = true;
|
this._caseSensitive = true;
|
||||||
else if (options["ignorecase"])
|
else if (options["ignorecase"])
|
||||||
caseSensitive = false;
|
this._caseSensitive = false;
|
||||||
else
|
else
|
||||||
caseSensitive = true;
|
this._caseSensitive = true;
|
||||||
|
|
||||||
// strip case-sensitive modifiers
|
// strip case-sensitive modifiers
|
||||||
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||||
@@ -84,414 +206,253 @@ function Finder() //{{{
|
|||||||
// remove any modifier escape \
|
// remove any modifier escape \
|
||||||
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
|
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
|
||||||
|
|
||||||
searchString = pattern;
|
this._searchString = pattern;
|
||||||
}
|
},
|
||||||
|
|
||||||
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
/**
|
||||||
* methods were unstable, and changed. The new version is not remotely
|
* Called when the search dialog is requested.
|
||||||
* compatible with what we do.
|
|
||||||
* The following only applies to this object, and may not be
|
|
||||||
* necessary, or accurate, but, just in case:
|
|
||||||
* The Original Code is mozilla.org viewsource frontend.
|
|
||||||
*
|
*
|
||||||
* The Initial Developer of the Original Code is
|
* @param {number} mode The search mode, either modes.SEARCH_FORWARD or
|
||||||
* Netscape Communications Corporation.
|
* modes.SEARCH_BACKWARD.
|
||||||
* Portions created by the Initial Developer are Copyright (c) 2003
|
* @default modes.SEARCH_FORWARD
|
||||||
* by the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Blake Ross <blake@cs.stanford.edu> (Original Author)
|
|
||||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
|
||||||
* Ben Basson <contact@cusser.net>
|
|
||||||
* Jason Barnabe <jason_barnabe@fastmail.fm>
|
|
||||||
* Asaf Romano <mano@mozilla.com>
|
|
||||||
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
|
||||||
* Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
|
|
||||||
*/
|
*/
|
||||||
var highlighter = {
|
openPrompt: function (mode) {
|
||||||
|
this._backwards = mode == modes.SEARCH_BACKWARD;
|
||||||
|
commandline.open(this._backwards ? "?" : "/", "", mode);
|
||||||
|
// TODO: focus the top of the currently visible screen
|
||||||
|
},
|
||||||
|
|
||||||
doc: null,
|
// TODO: backwards seems impossible i fear :(
|
||||||
|
/**
|
||||||
|
* Searches the current buffer for <b>str</b>.
|
||||||
|
*
|
||||||
|
* @param {string} str The string to find.
|
||||||
|
*/
|
||||||
|
find: function (str) {
|
||||||
|
let fastFind = getBrowser().fastFind;
|
||||||
|
|
||||||
spans: [],
|
this._processUserPattern(str);
|
||||||
|
fastFind.caseSensitive = this._caseSensitive;
|
||||||
|
this._found = fastFind.find(this._searchString, this._linksOnly) != Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
||||||
|
|
||||||
search: function (aWord, matchCase)
|
if (!this._found)
|
||||||
{
|
setTimeout(function () liberator.echoerr("E486: Pattern not found: " + this._searchPattern, commandline.FORCE_SINGLELINE), 0);
|
||||||
var finder = services.create("find");
|
},
|
||||||
if (matchCase !== undefined)
|
|
||||||
finder.caseSensitive = matchCase;
|
|
||||||
|
|
||||||
var range;
|
/**
|
||||||
while ((range = finder.Find(aWord, this.searchRange, this.startPt, this.endPt)))
|
* Searches the current buffer again for the most recently used search
|
||||||
yield range;
|
* string.
|
||||||
},
|
*
|
||||||
|
* @param {boolean} reverse Whether to search forwards or backwards.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
findAgain: function (reverse) {
|
||||||
|
// This hack is needed to make n/N work with the correct string, if
|
||||||
|
// we typed /foo<esc> after the original search. Since searchString is
|
||||||
|
// readonly we have to call find() again to update it.
|
||||||
|
if (getBrowser().fastFind.searchString != this._lastSearchString)
|
||||||
|
this.find(this._lastSearchString);
|
||||||
|
|
||||||
highlightDoc: function highlightDoc(win, aWord)
|
let up = reverse ? !this._lastSearchBackwards : this._lastSearchBackwards;
|
||||||
{
|
let result = getBrowser().fastFind.findAgain(up, this._linksOnly);
|
||||||
this.doc = content.document; // XXX
|
|
||||||
Array.forEach(win.frames, function (frame) highlighter.highlightDoc(frame, aWord));
|
|
||||||
|
|
||||||
var doc = win.document;
|
if (result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
|
||||||
if (!doc || !(doc instanceof HTMLDocument))
|
liberator.echoerr("E486: Pattern not found: " + this._lastSearchPattern, commandline.FORCE_SINGLELINE);
|
||||||
return;
|
else if (result == Ci.nsITypeAheadFind.FIND_WRAPPED) {
|
||||||
|
// hack needed, because wrapping causes a "scroll" event which clears
|
||||||
if (!aWord)
|
// our command line
|
||||||
{
|
setTimeout(function () {
|
||||||
let elems = highlighter.spans;
|
let msg = up ? "search hit TOP, continuing at BOTTOM" : "search hit BOTTOM, continuing at TOP";
|
||||||
for (let i = elems.length; --i >= 0;)
|
commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
|
||||||
{
|
}, 0);
|
||||||
let elem = elems[i];
|
}
|
||||||
let docfrag = doc.createDocumentFragment();
|
else {
|
||||||
let next = elem.nextSibling;
|
commandline.echo((up ? "?" : "/") + this._lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
||||||
let parent = elem.parentNode;
|
|
||||||
|
|
||||||
let child;
|
|
||||||
while (child = elem.firstChild)
|
|
||||||
docfrag.appendChild(child);
|
|
||||||
|
|
||||||
parent.removeChild(elem);
|
|
||||||
parent.insertBefore(docfrag, next);
|
|
||||||
parent.normalize();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseNode = <span highlight="Search"/>;
|
|
||||||
baseNode = util.xmlToDom(baseNode, window.content.document);
|
|
||||||
|
|
||||||
var body = doc.body;
|
|
||||||
var count = body.childNodes.length;
|
|
||||||
this.searchRange = doc.createRange();
|
|
||||||
this.startPt = doc.createRange();
|
|
||||||
this.endPt = doc.createRange();
|
|
||||||
|
|
||||||
this.searchRange.setStart(body, 0);
|
|
||||||
this.searchRange.setEnd(body, count);
|
|
||||||
|
|
||||||
this.startPt.setStart(body, 0);
|
|
||||||
this.startPt.setEnd(body, 0);
|
|
||||||
this.endPt.setStart(body, count);
|
|
||||||
this.endPt.setEnd(body, count);
|
|
||||||
|
|
||||||
liberator.interrupted = false;
|
|
||||||
let n = 0;
|
|
||||||
for (let retRange in this.search(aWord, caseSensitive))
|
|
||||||
{
|
|
||||||
// Highlight
|
|
||||||
var nodeSurround = baseNode.cloneNode(true);
|
|
||||||
var node = this.highlight(retRange, nodeSurround);
|
|
||||||
this.startPt = node.ownerDocument.createRange();
|
|
||||||
this.startPt.setStart(node, node.childNodes.length);
|
|
||||||
this.startPt.setEnd(node, node.childNodes.length);
|
|
||||||
if (n++ % 20 == 0)
|
|
||||||
liberator.threadYield(true);
|
|
||||||
if (liberator.interrupted)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
highlight: function highlight(aRange, aNode)
|
|
||||||
{
|
|
||||||
var startContainer = aRange.startContainer;
|
|
||||||
var startOffset = aRange.startOffset;
|
|
||||||
var endOffset = aRange.endOffset;
|
|
||||||
var docfrag = aRange.extractContents();
|
|
||||||
var before = startContainer.splitText(startOffset);
|
|
||||||
var parent = before.parentNode;
|
|
||||||
aNode.appendChild(docfrag);
|
|
||||||
parent.insertBefore(aNode, before);
|
|
||||||
this.spans.push(aNode);
|
|
||||||
return aNode;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all search highlighting.
|
|
||||||
*/
|
|
||||||
clear: function ()
|
|
||||||
{
|
|
||||||
this.spans.forEach(function (span)
|
|
||||||
{
|
|
||||||
if (span.parentNode)
|
|
||||||
{
|
|
||||||
let el = span.firstChild;
|
|
||||||
while (el)
|
|
||||||
{
|
|
||||||
span.removeChild(el);
|
|
||||||
span.parentNode.insertBefore(el, span);
|
|
||||||
el = span.firstChild;
|
|
||||||
}
|
|
||||||
span.parentNode.removeChild(span);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.spans = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
isHighlighted: function (doc) this.doc == doc && this.spans.length > 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
options.add(["hlsearch", "hls"],
|
|
||||||
"Highlight previous search pattern matches",
|
|
||||||
"boolean", "false",
|
|
||||||
{
|
|
||||||
setter: function (value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
finder.highlight();
|
|
||||||
else
|
|
||||||
finder.clear();
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["ignorecase", "ic"],
|
|
||||||
"Ignore case in search patterns",
|
|
||||||
"boolean", true);
|
|
||||||
|
|
||||||
options.add(["incsearch", "is"],
|
|
||||||
"Show where the search pattern matches as it is typed",
|
|
||||||
"boolean", true);
|
|
||||||
|
|
||||||
options.add(["linksearch", "lks"],
|
|
||||||
"Limit the search to hyperlink text",
|
|
||||||
"boolean", false);
|
|
||||||
|
|
||||||
options.add(["smartcase", "scs"],
|
|
||||||
"Override the 'ignorecase' option if the pattern contains uppercase characters",
|
|
||||||
"boolean", true);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
var myModes = config.browserModes;
|
|
||||||
myModes = myModes.concat([modes.CARET]);
|
|
||||||
|
|
||||||
mappings.add(myModes,
|
|
||||||
["/"], "Search forward for a pattern",
|
|
||||||
function () { finder.openPrompt(modes.SEARCH_FORWARD); });
|
|
||||||
|
|
||||||
mappings.add(myModes,
|
|
||||||
["?"], "Search backwards for a pattern",
|
|
||||||
function () { finder.openPrompt(modes.SEARCH_BACKWARD); });
|
|
||||||
|
|
||||||
mappings.add(myModes,
|
|
||||||
["n"], "Find next",
|
|
||||||
function () { finder.findAgain(false); });
|
|
||||||
|
|
||||||
mappings.add(myModes,
|
|
||||||
["N"], "Find previous",
|
|
||||||
function () { finder.findAgain(true); });
|
|
||||||
|
|
||||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
|
|
||||||
"Find word under cursor",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
found = false;
|
|
||||||
finder.onSubmit(buffer.getCurrentWord(), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
|
|
||||||
"Find word under cursor backwards",
|
|
||||||
function ()
|
|
||||||
{
|
|
||||||
found = false;
|
|
||||||
finder.onSubmit(buffer.getCurrentWord(), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
commands.add(["noh[lsearch]"],
|
|
||||||
"Remove the search highlighting",
|
|
||||||
function () { finder.clear(); },
|
|
||||||
{ argCount: "0" });
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the search dialog is requested.
|
|
||||||
*
|
|
||||||
* @param {number} mode The search mode, either modes.SEARCH_FORWARD or
|
|
||||||
* modes.SEARCH_BACKWARD.
|
|
||||||
* @default modes.SEARCH_FORWARD
|
|
||||||
*/
|
|
||||||
openPrompt: function (mode)
|
|
||||||
{
|
|
||||||
backwards = mode == modes.SEARCH_BACKWARD;
|
|
||||||
commandline.open(backwards ? "?" : "/", "", mode);
|
|
||||||
// TODO: focus the top of the currently visible screen
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: backwards seems impossible i fear :(
|
|
||||||
/**
|
|
||||||
* Searches the current buffer for <b>str</b>.
|
|
||||||
*
|
|
||||||
* @param {string} str The string to find.
|
|
||||||
*/
|
|
||||||
find: function (str)
|
|
||||||
{
|
|
||||||
let fastFind = getBrowser().fastFind;
|
|
||||||
|
|
||||||
processUserPattern(str);
|
|
||||||
fastFind.caseSensitive = caseSensitive;
|
|
||||||
found = fastFind.find(searchString, linksOnly) != Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
setTimeout(function () liberator.echoerr("E486: Pattern not found: " + searchPattern, commandline.FORCE_SINGLELINE), 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches the current buffer again for the most recently used search
|
|
||||||
* string.
|
|
||||||
*
|
|
||||||
* @param {boolean} reverse Whether to search forwards or backwards.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
findAgain: function (reverse)
|
|
||||||
{
|
|
||||||
// This hack is needed to make n/N work with the correct string, if
|
|
||||||
// we typed /foo<esc> after the original search. Since searchString is
|
|
||||||
// readonly we have to call find() again to update it.
|
|
||||||
if (getBrowser().fastFind.searchString != lastSearchString)
|
|
||||||
this.find(lastSearchString);
|
|
||||||
|
|
||||||
let up = reverse ? !lastSearchBackwards : lastSearchBackwards;
|
|
||||||
let result = getBrowser().fastFind.findAgain(up, linksOnly);
|
|
||||||
|
|
||||||
if (result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
|
|
||||||
liberator.echoerr("E486: Pattern not found: " + lastSearchPattern, commandline.FORCE_SINGLELINE);
|
|
||||||
else if (result == Ci.nsITypeAheadFind.FIND_WRAPPED)
|
|
||||||
{
|
|
||||||
// hack needed, because wrapping causes a "scroll" event which clears
|
|
||||||
// our command line
|
|
||||||
setTimeout(function () {
|
|
||||||
let msg = up ? "search hit TOP, continuing at BOTTOM" : "search hit BOTTOM, continuing at TOP";
|
|
||||||
commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandline.echo((up ? "?" : "/") + lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
|
||||||
|
|
||||||
if (options["hlsearch"])
|
|
||||||
this.highlight(lastSearchString);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the user types a key in the search dialog. Triggers a
|
|
||||||
* search attempt if 'incsearch' is set.
|
|
||||||
*
|
|
||||||
* @param {string} str The search string.
|
|
||||||
*/
|
|
||||||
onKeyPress: function (str)
|
|
||||||
{
|
|
||||||
if (options["incsearch"])
|
|
||||||
this.find(str);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the <Enter> key is pressed to trigger a search.
|
|
||||||
*
|
|
||||||
* @param {string} str The search string.
|
|
||||||
* @param {boolean} forcedBackward Whether to search forwards or
|
|
||||||
* backwards. This overrides the direction set in
|
|
||||||
* (@link #openPrompt).
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
onSubmit: function (str, forcedBackward)
|
|
||||||
{
|
|
||||||
if (typeof forcedBackward === "boolean")
|
|
||||||
backwards = forcedBackward;
|
|
||||||
|
|
||||||
if (str)
|
|
||||||
var pattern = str;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
liberator.assert(lastSearchPattern, "E35: No previous search pattern");
|
|
||||||
pattern = lastSearchPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
if (!options["incsearch"] || !str || !found)
|
|
||||||
{
|
|
||||||
// prevent any current match from matching again
|
|
||||||
if (!window.content.getSelection().isCollapsed)
|
|
||||||
window.content.getSelection().getRangeAt(0).collapse(backwards);
|
|
||||||
|
|
||||||
this.find(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSearchBackwards = backwards;
|
|
||||||
//lastSearchPattern = pattern.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX
|
|
||||||
lastSearchPattern = pattern;
|
|
||||||
lastSearchString = searchString;
|
|
||||||
|
|
||||||
// TODO: move to find() when reverse incremental searching is kludged in
|
|
||||||
// need to find again for reverse searching
|
|
||||||
if (backwards)
|
|
||||||
setTimeout(function () { finder.findAgain(false); }, 0);
|
|
||||||
|
|
||||||
if (options["hlsearch"])
|
if (options["hlsearch"])
|
||||||
this.highlight(searchString);
|
this.highlight(this._lastSearchString);
|
||||||
|
|
||||||
modes.reset();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the search is canceled. For example, if someone presses
|
|
||||||
* <Esc> while typing a search.
|
|
||||||
*/
|
|
||||||
onCancel: function ()
|
|
||||||
{
|
|
||||||
// TODO: code to reposition the document to the place before search started
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlights all occurances of <b>str</b> in the buffer.
|
|
||||||
*
|
|
||||||
* @param {string} str The string to highlight.
|
|
||||||
*/
|
|
||||||
highlight: function (str)
|
|
||||||
{
|
|
||||||
// FIXME: Thunderbird incompatible
|
|
||||||
if (config.name == "Muttator")
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (highlighter.isHighlighted(content.document))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!str)
|
|
||||||
str = lastSearchString;
|
|
||||||
|
|
||||||
highlighter.highlightDoc(window.content, str);
|
|
||||||
|
|
||||||
// recreate selection since highlightDoc collapses the selection
|
|
||||||
if (window.content.getSelection().isCollapsed)
|
|
||||||
getBrowser().fastFind.findAgain(backwards, linksOnly);
|
|
||||||
|
|
||||||
// TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"])
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all search highlighting.
|
|
||||||
*/
|
|
||||||
clear: function ()
|
|
||||||
{
|
|
||||||
highlighter.clear();
|
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
//}}}
|
|
||||||
} //}}}
|
/**
|
||||||
|
* Called when the user types a key in the search dialog. Triggers a
|
||||||
|
* search attempt if 'incsearch' is set.
|
||||||
|
*
|
||||||
|
* @param {string} str The search string.
|
||||||
|
*/
|
||||||
|
onKeyPress: function (str) {
|
||||||
|
if (options["incsearch"])
|
||||||
|
this.find(str);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the <Enter> key is pressed to trigger a search.
|
||||||
|
*
|
||||||
|
* @param {string} str The search string.
|
||||||
|
* @param {boolean} forcedBackward Whether to search forwards or
|
||||||
|
* backwards. This overrides the direction set in
|
||||||
|
* (@link #openPrompt).
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
onSubmit: function (str, forcedBackward) {
|
||||||
|
if (typeof forcedBackward === "boolean")
|
||||||
|
this._backwards = forcedBackward;
|
||||||
|
|
||||||
|
if (str)
|
||||||
|
var pattern = str;
|
||||||
|
else {
|
||||||
|
liberator.assert(this._lastSearchPattern, "E35: No previous search pattern");
|
||||||
|
pattern = this._lastSearchPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
if (!options["incsearch"] || !str || !this._found) {
|
||||||
|
// prevent any current match from matching again
|
||||||
|
if (!window.content.getSelection().isCollapsed)
|
||||||
|
window.content.getSelection().getRangeAt(0).collapse(this._backwards);
|
||||||
|
|
||||||
|
this.find(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastSearchBackwards = this._backwards;
|
||||||
|
//lastSearchPattern = pattern.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX
|
||||||
|
this._lastSearchPattern = pattern;
|
||||||
|
this._lastSearchString = this._searchString;
|
||||||
|
|
||||||
|
// TODO: move to find() when reverse incremental searching is kludged in
|
||||||
|
// need to find again for reverse searching
|
||||||
|
if (this._backwards)
|
||||||
|
setTimeout(function () { finder.findAgain(false); }, 0);
|
||||||
|
|
||||||
|
if (options["hlsearch"])
|
||||||
|
this.highlight(this._searchString);
|
||||||
|
|
||||||
|
modes.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the search is canceled. For example, if someone presses
|
||||||
|
* <Esc> while typing a search.
|
||||||
|
*/
|
||||||
|
onCancel: function () {
|
||||||
|
// TODO: code to reposition the document to the place before search started
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights all occurances of <b>str</b> in the buffer.
|
||||||
|
*
|
||||||
|
* @param {string} str The string to highlight.
|
||||||
|
*/
|
||||||
|
highlight: function (str) {
|
||||||
|
// FIXME: Thunderbird incompatible
|
||||||
|
if (config.name == "Muttator")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._highlighter.isHighlighted(content.document))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
str = this._lastSearchString;
|
||||||
|
|
||||||
|
this._highlighter.highlightDoc(window.content, str);
|
||||||
|
|
||||||
|
// recreate selection since highlightDoc collapses the selection
|
||||||
|
if (window.content.getSelection().isCollapsed)
|
||||||
|
getBrowser().fastFind.findAgain(this._backwards, this._linksOnly);
|
||||||
|
|
||||||
|
// TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"])
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all search highlighting.
|
||||||
|
*/
|
||||||
|
clear: function () {
|
||||||
|
this._highlighter.clear();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
commandline: function () {
|
||||||
|
const self = this;
|
||||||
|
// Event handlers for search - closure is needed
|
||||||
|
commandline.registerCallback("change", modes.SEARCH_FORWARD, function (str) { self.onKeyPress(str); });
|
||||||
|
commandline.registerCallback("submit", modes.SEARCH_FORWARD, function (str) { self.onSubmit(str); });
|
||||||
|
commandline.registerCallback("cancel", modes.SEARCH_FORWARD, function () { self.onCancel(); });
|
||||||
|
// TODO: allow advanced myModes in register/triggerCallback
|
||||||
|
commandline.registerCallback("change", modes.SEARCH_BACKWARD, function (str) { self.onKeyPress(str); });
|
||||||
|
commandline.registerCallback("submit", modes.SEARCH_BACKWARD, function (str) { self.onSubmit(str); });
|
||||||
|
commandline.registerCallback("cancel", modes.SEARCH_BACKWARD, function () { self.onCancel(); });
|
||||||
|
|
||||||
|
},
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["noh[lsearch]"],
|
||||||
|
"Remove the search highlighting",
|
||||||
|
function () { finder.clear(); },
|
||||||
|
{ argCount: "0" });
|
||||||
|
},
|
||||||
|
mappings: function () {
|
||||||
|
var myModes = config.browserModes;
|
||||||
|
myModes = myModes.concat([modes.CARET]);
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["/"], "Search forward for a pattern",
|
||||||
|
function () { finder.openPrompt(modes.SEARCH_FORWARD); });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["?"], "Search backwards for a pattern",
|
||||||
|
function () { finder.openPrompt(modes.SEARCH_BACKWARD); });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["n"], "Find next",
|
||||||
|
function () { finder.findAgain(false); });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["N"], "Find previous",
|
||||||
|
function () { finder.findAgain(true); });
|
||||||
|
|
||||||
|
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
|
||||||
|
"Find word under cursor",
|
||||||
|
function () {
|
||||||
|
this._found = false;
|
||||||
|
finder.onSubmit(buffer.getCurrentWord(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
|
||||||
|
"Find word under cursor backwards",
|
||||||
|
function () {
|
||||||
|
this._found = false;
|
||||||
|
finder.onSubmit(buffer.getCurrentWord(), true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
options: function () {
|
||||||
|
options.add(["hlsearch", "hls"],
|
||||||
|
"Highlight previous search pattern matches",
|
||||||
|
"boolean", "false", {
|
||||||
|
setter: function (value) {
|
||||||
|
if (value)
|
||||||
|
finder.highlight();
|
||||||
|
else
|
||||||
|
finder.clear();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["ignorecase", "ic"],
|
||||||
|
"Ignore case in search patterns",
|
||||||
|
"boolean", true);
|
||||||
|
|
||||||
|
options.add(["incsearch", "is"],
|
||||||
|
"Show where the search pattern matches as it is typed",
|
||||||
|
"boolean", true);
|
||||||
|
|
||||||
|
options.add(["linksearch", "lks"],
|
||||||
|
"Limit the search to hyperlink text",
|
||||||
|
"boolean", false);
|
||||||
|
|
||||||
|
options.add(["smartcase", "scs"],
|
||||||
|
"Override the 'ignorecase' option if the pattern contains uppercase characters",
|
||||||
|
"boolean", true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
// given in the LICENSE.txt file included with this file.
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
|
||||||
function checkFragment()
|
function checkFragment() {
|
||||||
{
|
|
||||||
document.title = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "title")[0].textContent;
|
document.title = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "title")[0].textContent;
|
||||||
var frag = document.location.hash.substr(1);
|
var frag = document.location.hash.substr(1);
|
||||||
var elem = document.getElementById(frag);
|
var elem = document.getElementById(frag);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
233
common/content/history.js
Normal file
233
common/content/history.js
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
const History = Module("history", {
|
||||||
|
get format() bookmarks.format,
|
||||||
|
|
||||||
|
get service() services.get("history"),
|
||||||
|
|
||||||
|
get: function get(filter, maxItems) {
|
||||||
|
// no query parameters will get all history
|
||||||
|
let query = services.get("history").getNewQuery();
|
||||||
|
let options = services.get("history").getNewQueryOptions();
|
||||||
|
|
||||||
|
if (typeof filter == "string")
|
||||||
|
filter = { searchTerms: filter };
|
||||||
|
for (let [k, v] in Iterator(filter))
|
||||||
|
query[k] = v;
|
||||||
|
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
|
||||||
|
options.resultType = options.RESULTS_AS_URI;
|
||||||
|
if (maxItems > 0)
|
||||||
|
options.maxResults = maxItems;
|
||||||
|
|
||||||
|
// execute the query
|
||||||
|
let root = services.get("history").executeQuery(query, options).root;
|
||||||
|
root.containerOpen = true;
|
||||||
|
let items = util.map(util.range(0, root.childCount), function (i) {
|
||||||
|
let node = root.getChild(i);
|
||||||
|
return {
|
||||||
|
url: node.uri,
|
||||||
|
title: node.title,
|
||||||
|
icon: node.icon ? node.icon.spec : DEFAULT_FAVICON
|
||||||
|
};
|
||||||
|
});
|
||||||
|
root.containerOpen = false; // close a container after using it!
|
||||||
|
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
|
||||||
|
get session() {
|
||||||
|
let sh = window.getWebNavigation().sessionHistory;
|
||||||
|
let obj = [];
|
||||||
|
obj.index = sh.index;
|
||||||
|
obj.__iterator__ = function () util.Array.iteritems(this);
|
||||||
|
for (let i in util.range(0, sh.count)) {
|
||||||
|
obj[i] = { index: i, __proto__: sh.getEntryAtIndex(i, false) };
|
||||||
|
util.memoize(obj[i], "icon",
|
||||||
|
function (obj) services.get("favicon").getFaviconImageForPage(obj.URI).spec);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: better names
|
||||||
|
stepTo: function stepTo(steps) {
|
||||||
|
let start = 0;
|
||||||
|
let end = window.getWebNavigation().sessionHistory.count - 1;
|
||||||
|
let current = window.getWebNavigation().sessionHistory.index;
|
||||||
|
|
||||||
|
if (current == start && steps < 0 || current == end && steps > 0)
|
||||||
|
liberator.beep();
|
||||||
|
else {
|
||||||
|
let index = util.Math.constrain(current + steps, start, end);
|
||||||
|
window.getWebNavigation().gotoIndex(index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goToStart: function goToStart() {
|
||||||
|
let index = window.getWebNavigation().sessionHistory.index;
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
window.getWebNavigation().gotoIndex(0);
|
||||||
|
else
|
||||||
|
liberator.beep();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
goToEnd: function goToEnd() {
|
||||||
|
let sh = window.getWebNavigation().sessionHistory;
|
||||||
|
let max = sh.count - 1;
|
||||||
|
|
||||||
|
if (sh.index < max)
|
||||||
|
window.getWebNavigation().gotoIndex(max);
|
||||||
|
else
|
||||||
|
liberator.beep();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// if openItems is true, open the matching history items in tabs rather than display
|
||||||
|
list: function list(filter, openItems, maxItems) {
|
||||||
|
// FIXME: returning here doesn't make sense
|
||||||
|
// Why the hell doesn't it make sense? --Kris
|
||||||
|
// See comment at bookmarks.list --djk
|
||||||
|
if (!openItems)
|
||||||
|
return completion.listCompleter("history", filter, maxItems);
|
||||||
|
let items = completion.runCompleter("history", filter, maxItems);
|
||||||
|
|
||||||
|
if (items.length)
|
||||||
|
return liberator.open(items.map(function (i) i.url), liberator.NEW_TAB);
|
||||||
|
|
||||||
|
if (filter.length > 0)
|
||||||
|
liberator.echoerr("E283: No history matching \"" + filter + "\"");
|
||||||
|
else
|
||||||
|
liberator.echoerr("No history set");
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["ba[ck]"],
|
||||||
|
"Go back in the browser history",
|
||||||
|
function (args) {
|
||||||
|
let url = args.literalArg;
|
||||||
|
|
||||||
|
if (args.bang)
|
||||||
|
history.goToStart();
|
||||||
|
else {
|
||||||
|
if (url) {
|
||||||
|
let sh = history.session;
|
||||||
|
if (/^\d+(:|$)/.test(url) && sh.index - parseInt(url) in sh)
|
||||||
|
return void window.getWebNavigation().gotoIndex(sh.index - parseInt(url));
|
||||||
|
|
||||||
|
for (let [i, ent] in Iterator(sh.slice(0, sh.index).reverse()))
|
||||||
|
if (ent.URI.spec == url)
|
||||||
|
return void window.getWebNavigation().gotoIndex(i);
|
||||||
|
liberator.echoerr("Exxx: URL not found in history");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
history.stepTo(-Math.max(args.count, 1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argCount: "?",
|
||||||
|
bang: true,
|
||||||
|
completer: function completer(context) {
|
||||||
|
let sh = history.session;
|
||||||
|
|
||||||
|
context.anchored = false;
|
||||||
|
context.compare = CompletionContext.Sort.unsorted;
|
||||||
|
context.filters = [CompletionContext.Filter.textDescription];
|
||||||
|
context.completions = sh.slice(0, sh.index).reverse();
|
||||||
|
context.keys = { text: function (item) (sh.index - item.index) + ": " + item.URI.spec, description: "title", icon: "icon" };
|
||||||
|
},
|
||||||
|
count: true,
|
||||||
|
literal: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(["fo[rward]", "fw"],
|
||||||
|
"Go forward in the browser history",
|
||||||
|
function (args) {
|
||||||
|
let url = args.literalArg;
|
||||||
|
|
||||||
|
if (args.bang)
|
||||||
|
history.goToEnd();
|
||||||
|
else {
|
||||||
|
if (url) {
|
||||||
|
let sh = history.session;
|
||||||
|
if (/^\d+(:|$)/.test(url) && sh.index + parseInt(url) in sh)
|
||||||
|
return void window.getWebNavigation().gotoIndex(sh.index + parseInt(url));
|
||||||
|
|
||||||
|
for (let [i, ent] in Iterator(sh.slice(sh.index + 1)))
|
||||||
|
if (ent.URI.spec == url)
|
||||||
|
return void window.getWebNavigation().gotoIndex(i);
|
||||||
|
liberator.echoerr("Exxx: URL not found in history");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
history.stepTo(Math.max(args.count, 1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argCount: "?",
|
||||||
|
bang: true,
|
||||||
|
completer: function completer(context) {
|
||||||
|
let sh = history.session;
|
||||||
|
|
||||||
|
context.anchored = false;
|
||||||
|
context.compare = CompletionContext.Sort.unsorted;
|
||||||
|
context.filters = [CompletionContext.Filter.textDescription];
|
||||||
|
context.completions = sh.slice(sh.index + 1);
|
||||||
|
context.keys = { text: function (item) (item.index - sh.index) + ": " + item.URI.spec, description: "title", icon: "icon" };
|
||||||
|
},
|
||||||
|
count: true,
|
||||||
|
literal: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(["hist[ory]", "hs"],
|
||||||
|
"Show recently visited URLs",
|
||||||
|
function (args) { history.list(args.join(" "), args.bang, args["-max"] || 1000); }, {
|
||||||
|
bang: true,
|
||||||
|
completer: function (context) { context.quote = null; completion.history(context); },
|
||||||
|
// completer: function (filter) completion.history(filter)
|
||||||
|
options: [[["-max", "-m"], commands.OPTION_INT]]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
completion: function () {
|
||||||
|
completion.history = function _history(context, maxItems) {
|
||||||
|
context.format = history.format;
|
||||||
|
context.title = ["History"];
|
||||||
|
context.compare = CompletionContext.Sort.unsorted;
|
||||||
|
//context.background = true;
|
||||||
|
if (context.maxItems == null)
|
||||||
|
context.maxItems = 100;
|
||||||
|
context.regenerate = true;
|
||||||
|
context.generate = function () history.get(context.filter, this.maxItems);
|
||||||
|
};
|
||||||
|
|
||||||
|
completion.addUrlCompleter("h", "History", completion.history);
|
||||||
|
},
|
||||||
|
mappings: function () {
|
||||||
|
var myModes = config.browserModes;
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["<C-o>"], "Go to an older position in the jump list",
|
||||||
|
function (count) { history.stepTo(-Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["<C-i>"], "Go to a newer position in the jump list",
|
||||||
|
function (count) { history.stepTo(Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["H", "<A-Left>", "<M-Left>"], "Go back in the browser history",
|
||||||
|
function (count) { history.stepTo(-Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["L", "<A-Right>", "<M-Right>"], "Go forward in the browser history",
|
||||||
|
function (count) { history.stepTo(Math.max(count, 1)); },
|
||||||
|
{ count: true });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
1978
common/content/io.js
1978
common/content/io.js
File diff suppressed because it is too large
Load Diff
@@ -11,51 +11,51 @@
|
|||||||
|
|
||||||
const loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
const loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Components.interfaces.mozIJSSubScriptLoader);
|
.getService(Components.interfaces.mozIJSSubScriptLoader);
|
||||||
function load(script)
|
function load(script) {
|
||||||
{
|
for (let [i, base] in Iterator(prefix)) {
|
||||||
for (let [i, base] in Iterator(prefix))
|
try {
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
loader.loadSubScript(base + script, modules);
|
loader.loadSubScript(base + script, modules);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
if (i + 1 < prefix.length)
|
if (i + 1 < prefix.length)
|
||||||
continue;
|
continue;
|
||||||
if (Components.utils.reportError)
|
if (Components.utils.reportError)
|
||||||
Components.utils.reportError(e);
|
Components.utils.reportError(e);
|
||||||
dump("liberator: Loading script " + script + ": " + e + "\n");
|
dump("liberator: Loading script " + script + ": " + e + "\n");
|
||||||
|
dump(e.stack + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Components.utils.import("resource://liberator/storage.jsm", modules);
|
|
||||||
|
|
||||||
let prefix = [BASE];
|
let prefix = [BASE];
|
||||||
|
|
||||||
["services.js",
|
["base.js",
|
||||||
"liberator.js",
|
"modules.js",
|
||||||
"configbase.js",
|
"autocommands.js",
|
||||||
"config.js"].forEach(load);
|
|
||||||
modules.config.__proto__ = modules.configbase;
|
|
||||||
|
|
||||||
["util.js",
|
|
||||||
"style.js",
|
|
||||||
"buffer.js",
|
"buffer.js",
|
||||||
|
"commandline.js",
|
||||||
"commands.js",
|
"commands.js",
|
||||||
"completion.js",
|
"completion.js",
|
||||||
|
"config.js",
|
||||||
|
"configbase.js",
|
||||||
|
"liberator.js",
|
||||||
"editor.js",
|
"editor.js",
|
||||||
"events.js",
|
"events.js",
|
||||||
"finder.js",
|
"finder.js",
|
||||||
"hints.js",
|
"hints.js",
|
||||||
"io.js",
|
"io.js",
|
||||||
"mappings.js",
|
"mappings.js",
|
||||||
|
"marks.js",
|
||||||
"modes.js",
|
"modes.js",
|
||||||
"options.js",
|
"options.js",
|
||||||
|
"services.js",
|
||||||
|
"statusline.js",
|
||||||
|
"style.js",
|
||||||
"template.js",
|
"template.js",
|
||||||
"ui.js"].forEach(load);
|
"util.js",
|
||||||
|
].forEach(load);
|
||||||
|
modules.config.__proto__ = modules.configbase;
|
||||||
|
|
||||||
prefix.unshift("chrome://" + modules.config.name.toLowerCase() + "/content/");
|
prefix.unshift("chrome://" + modules.config.name.toLowerCase() + "/content/");
|
||||||
modules.config.scripts.forEach(load);
|
modules.config.scripts.forEach(load);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
|||||||
<?xml-stylesheet href="chrome://liberator/skin/liberator.css" type="text/css"?>
|
<?xml-stylesheet href="chrome://liberator/skin/liberator.css" type="text/css"?>
|
||||||
<!DOCTYPE overlay SYSTEM "liberator.dtd" [
|
<!DOCTYPE overlay SYSTEM "liberator.dtd" [
|
||||||
<!ENTITY liberator.content "chrome://liberator/content/">
|
<!ENTITY liberator.content "chrome://liberator/content/">
|
||||||
|
<!ENTITY and "&&">
|
||||||
]>
|
]>
|
||||||
|
|
||||||
<overlay id="liberator"
|
<overlay id="liberator"
|
||||||
@@ -28,8 +29,8 @@
|
|||||||
</stringbundleset>
|
</stringbundleset>
|
||||||
|
|
||||||
<keyset id="mainKeyset">
|
<keyset id="mainKeyset">
|
||||||
<key id="key_open_vimbar" key=":" oncommand="liberator.modules.commandline.open(':', '', liberator.modules.modes.EX);" modifiers=""/>
|
<key id="key_open_vimbar" key=":" oncommand="window.liberator ∧ liberator.modules.commandline.open(':', '', liberator.modules.modes.EX);" modifiers=""/>
|
||||||
<key id="key_stop" keycode="VK_ESCAPE" oncommand="liberator.modules.events.onEscape();"/>
|
<key id="key_stop" keycode="VK_ESCAPE" oncommand="window.liberator ∧ liberator.modules.events.onEscape();"/>
|
||||||
<!-- other keys are handled inside the event loop in events.js -->
|
<!-- other keys are handled inside the event loop in events.js -->
|
||||||
</keyset>
|
</keyset>
|
||||||
|
|
||||||
@@ -42,24 +43,24 @@
|
|||||||
<commandset id="onVimperatorFocus"
|
<commandset id="onVimperatorFocus"
|
||||||
commandupdater="true"
|
commandupdater="true"
|
||||||
events="focus"
|
events="focus"
|
||||||
oncommandupdate="if (liberator.modules.events != undefined) liberator.modules.events.onFocusChange(event);"/>
|
oncommandupdate="if (window.liberator ∧ liberator.modules.events != undefined) liberator.modules.events.onFocusChange(event);"/>
|
||||||
<commandset id="onVimperatorSelect"
|
<commandset id="onVimperatorSelect"
|
||||||
commandupdater="true"
|
commandupdater="true"
|
||||||
events="select"
|
events="select"
|
||||||
oncommandupdate="if (liberator.modules.events != undefined) liberator.modules.events.onSelectionChange(event);"/>
|
oncommandupdate="if (window.liberator ∧ liberator.modules.events != undefined) liberator.modules.events.onSelectionChange(event);"/>
|
||||||
|
|
||||||
<!-- As of Firefox 3.1pre, <iframe>.height changes do not seem to have immediate effect,
|
<!-- As of Firefox 3.1pre, <iframe>.height changes do not seem to have immediate effect,
|
||||||
therefore we need to put them into a <vbox> for which that works just fine -->
|
therefore we need to put them into a <vbox> for which that works just fine -->
|
||||||
<vbox class="liberator-container" hidden="false" collapsed="true">
|
<vbox class="liberator-container" hidden="false" collapsed="true">
|
||||||
<iframe id="liberator-multiline-output" src="chrome://liberator/content/buffer.xhtml"
|
<iframe id="liberator-multiline-output" src="chrome://liberator/content/buffer.xhtml"
|
||||||
flex="1" hidden="false" collapsed="false"
|
flex="1" hidden="false" collapsed="false"
|
||||||
onclick="liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
onclick="window.liberator ∧ liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<vbox class="liberator-container" hidden="false" collapsed="true">
|
<vbox class="liberator-container" hidden="false" collapsed="true">
|
||||||
<iframe id="liberator-completions" src="chrome://liberator/content/buffer.xhtml"
|
<iframe id="liberator-completions" src="chrome://liberator/content/buffer.xhtml"
|
||||||
flex="1" hidden="false" collapsed="false"
|
flex="1" hidden="false" collapsed="false"
|
||||||
onclick="liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
onclick="window.liberator ∧ liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<stack orient="horizontal" align="stretch" class="liberator-container" liberator:highlight="CmdLine">
|
<stack orient="horizontal" align="stretch" class="liberator-container" liberator:highlight="CmdLine">
|
||||||
@@ -67,18 +68,18 @@
|
|||||||
<hbox id="liberator-commandline" hidden="false" collapsed="true" class="liberator-container" liberator:highlight="Normal">
|
<hbox id="liberator-commandline" hidden="false" collapsed="true" class="liberator-container" liberator:highlight="Normal">
|
||||||
<label class="plain" id="liberator-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
|
<label class="plain" id="liberator-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
|
||||||
<textbox class="plain" id="liberator-commandline-command" flex="1" type="timed" timeout="100"
|
<textbox class="plain" id="liberator-commandline-command" flex="1" type="timed" timeout="100"
|
||||||
oninput="liberator.modules.commandline.onEvent(event);"
|
oninput="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||||
onkeyup="liberator.modules.commandline.onEvent(event);"
|
onkeyup="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||||
onfocus="liberator.modules.commandline.onEvent(event);"
|
onfocus="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||||
onblur="liberator.modules.commandline.onEvent(event);"/>
|
onblur="window.liberator ∧ liberator.modules.commandline.onEvent(event);"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
</stack>
|
</stack>
|
||||||
|
|
||||||
<vbox class="liberator-container" hidden="false" collapsed="false" liberator:highlight="CmdLine">
|
<vbox class="liberator-container" hidden="false" collapsed="false" liberator:highlight="CmdLine">
|
||||||
<textbox id="liberator-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true" liberator:highlight="Normal"
|
<textbox id="liberator-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true" liberator:highlight="Normal"
|
||||||
onkeypress="liberator.modules.commandline.onMultilineInputEvent(event);"
|
onkeypress="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||||
oninput="liberator.modules.commandline.onMultilineInputEvent(event);"
|
oninput="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||||
onblur="liberator.modules.commandline.onMultilineInputEvent(event);"/>
|
onblur="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
</window>
|
</window>
|
||||||
|
|||||||
@@ -27,52 +27,50 @@
|
|||||||
* @optional
|
* @optional
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function Map(modes, keys, description, action, extraInfo) //{{{
|
const Map = Class("Map", {
|
||||||
{
|
init: function (modes, keys, description, action, extraInfo) {
|
||||||
modes = Array.concat(modes);
|
modes = Array.concat(modes);
|
||||||
|
|
||||||
if (!extraInfo)
|
if (!extraInfo)
|
||||||
extraInfo = {};
|
extraInfo = {};
|
||||||
|
|
||||||
/** @property {number[]} All of the modes for which this mapping applies. */
|
/** @property {number[]} All of the modes for which this mapping applies. */
|
||||||
this.modes = modes;
|
this.modes = modes;
|
||||||
/** @property {string[]} All of this mapping's names (key sequences). */
|
/** @property {string[]} All of this mapping's names (key sequences). */
|
||||||
this.names = keys.map(events.canonicalKeys);
|
this.names = keys.map(events.canonicalKeys);
|
||||||
/** @property {function (number)} The function called to execute this mapping. */
|
/** @property {function (number)} The function called to execute this mapping. */
|
||||||
this.action = action;
|
this.action = action;
|
||||||
/** @property {string} This mapping's description, as shown in :viusage. */
|
/** @property {string} This mapping's description, as shown in :viusage. */
|
||||||
this.description = description || "";
|
this.description = description || "";
|
||||||
|
|
||||||
/** @property {boolean} Whether this mapping accepts an argument. */
|
/** @property {boolean} Whether this mapping accepts an argument. */
|
||||||
this.arg = extraInfo.arg || false;
|
this.arg = extraInfo.arg || false;
|
||||||
/** @property {boolean} Whether this mapping accepts a count. */
|
/** @property {boolean} Whether this mapping accepts a count. */
|
||||||
this.count = extraInfo.count || false;
|
this.count = extraInfo.count || false;
|
||||||
/**
|
/**
|
||||||
* @property {boolean} Whether the mapping accepts a motion mapping
|
* @property {boolean} Whether the mapping accepts a motion mapping
|
||||||
* as an argument.
|
* as an argument.
|
||||||
*/
|
*/
|
||||||
this.motion = extraInfo.motion || false;
|
this.motion = extraInfo.motion || false;
|
||||||
/**
|
/**
|
||||||
* @property {boolean} Whether the mapping's key events should be
|
* @property {boolean} Whether the mapping's key events should be
|
||||||
* propagated to the host application.
|
* propagated to the host application.
|
||||||
*/
|
*/
|
||||||
// TODO: I'm not sure this is the best name but it reflects that which it replaced. --djk
|
// TODO: I'm not sure this is the best name but it reflects that which it replaced. --djk
|
||||||
this.route = extraInfo.route || false;
|
this.route = extraInfo.route || false;
|
||||||
/** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */
|
/** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */
|
||||||
this.noremap = extraInfo.noremap || false;
|
this.noremap = extraInfo.noremap || false;
|
||||||
/** @property {boolean} Whether any output from the mapping should be echoed on the command line. */
|
/** @property {boolean} Whether any output from the mapping should be echoed on the command line. */
|
||||||
this.silent = extraInfo.silent || false;
|
this.silent = extraInfo.silent || false;
|
||||||
/** @property {string} The literal RHS expansion of this mapping. */
|
/** @property {string} The literal RHS expansion of this mapping. */
|
||||||
this.rhs = extraInfo.rhs || null;
|
this.rhs = extraInfo.rhs || null;
|
||||||
/**
|
/**
|
||||||
* @property {boolean} Specifies whether this is a user mapping. User
|
* @property {boolean} Specifies whether this is a user mapping. User
|
||||||
* mappings may be created by plugins, or directly by users. Users and
|
* mappings may be created by plugins, or directly by users. Users and
|
||||||
* plugin authors should create only user mappings.
|
* plugin authors should create only user mappings.
|
||||||
*/
|
*/
|
||||||
this.user = extraInfo.user || false;
|
this.user = extraInfo.user || false;
|
||||||
}
|
},
|
||||||
|
|
||||||
Map.prototype = {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this mapping can be invoked by a key sequence matching
|
* Returns whether this mapping can be invoked by a key sequence matching
|
||||||
@@ -93,8 +91,7 @@ Map.prototype = {
|
|||||||
* @param {string} argument The normal argument if accepted by this
|
* @param {string} argument The normal argument if accepted by this
|
||||||
* mapping. E.g. "a" for "ma"
|
* mapping. E.g. "a" for "ma"
|
||||||
*/
|
*/
|
||||||
execute: function (motion, count, argument)
|
execute: function (motion, count, argument) {
|
||||||
{
|
|
||||||
let args = [];
|
let args = [];
|
||||||
|
|
||||||
if (this.motion)
|
if (this.motion)
|
||||||
@@ -112,60 +109,46 @@ Map.prototype = {
|
|||||||
return liberator.trapErrors(repeat);
|
return liberator.trapErrors(repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
}; //}}}
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @instance mappings
|
* @instance mappings
|
||||||
*/
|
*/
|
||||||
function Mappings() //{{{
|
const Mappings = Module("mappings", {
|
||||||
{
|
requires: ["modes"],
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
var main = []; // default mappings
|
init: function () {
|
||||||
var user = []; // user created mappings
|
this._main = []; // default mappings
|
||||||
|
this._user = []; // user created mappings
|
||||||
|
},
|
||||||
|
|
||||||
for (let mode in modes)
|
_addMap: function (map) {
|
||||||
{
|
let where = map.user ? this._user : this._main;
|
||||||
main[mode] = [];
|
|
||||||
user[mode] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMap(map)
|
|
||||||
{
|
|
||||||
let where = map.user ? user : main;
|
|
||||||
map.modes.forEach(function (mode) {
|
map.modes.forEach(function (mode) {
|
||||||
if (!(mode in where))
|
if (!(mode in where))
|
||||||
where[mode] = [];
|
where[mode] = [];
|
||||||
where[mode].push(map);
|
where[mode].push(map);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
function getMap(mode, cmd, stack)
|
_getMap: function (mode, cmd, stack) {
|
||||||
{
|
|
||||||
let maps = stack[mode] || [];
|
let maps = stack[mode] || [];
|
||||||
|
|
||||||
for (let [, map] in Iterator(maps))
|
for (let [, map] in Iterator(maps)) {
|
||||||
{
|
|
||||||
if (map.hasName(cmd))
|
if (map.hasName(cmd))
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
|
|
||||||
function removeMap(mode, cmd)
|
_removeMap: function (mode, cmd) {
|
||||||
{
|
let maps = this._user[mode] || [];
|
||||||
let maps = user[mode] || [];
|
|
||||||
let names;
|
let names;
|
||||||
|
|
||||||
for (let [i, map] in Iterator(maps))
|
for (let [i, map] in Iterator(maps)) {
|
||||||
{
|
for (let [j, name] in Iterator(map.names)) {
|
||||||
for (let [j, name] in Iterator(map.names))
|
if (name == cmd) {
|
||||||
{
|
|
||||||
if (name == cmd)
|
|
||||||
{
|
|
||||||
map.names.splice(j, 1);
|
map.names.splice(j, 1);
|
||||||
if (map.names.length == 0)
|
if (map.names.length == 0)
|
||||||
maps.splice(i, 1);
|
maps.splice(i, 1);
|
||||||
@@ -173,152 +156,329 @@ function Mappings() //{{{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
function expandLeader(keyString) keyString.replace(/<Leader>/i, mappings.getMapLeader())
|
_expandLeader: function (keyString) keyString.replace(/<Leader>/i, mappings.getMapLeader()),
|
||||||
|
|
||||||
// Return all mappings present in all @modes
|
// Return all mappings present in all @modes
|
||||||
function mappingsIterator(modes, stack)
|
_mappingsIterator: function (modes, stack) {
|
||||||
{
|
|
||||||
modes = modes.slice();
|
modes = modes.slice();
|
||||||
return (map for ([i, map] in Iterator(stack[modes.shift()]))
|
return (map for ([i, map] in Iterator(stack[modes.shift()]))
|
||||||
if (modes.every(function (mode) stack[mode].some(
|
if (modes.every(function (mode) stack[mode].some(
|
||||||
function (m) m.rhs == map.rhs && m.names[0] == map.names[0]))))
|
function (m) m.rhs == map.rhs && m.names[0] == map.names[0]))))
|
||||||
}
|
},
|
||||||
|
|
||||||
function addMapCommands(ch, modes, modeDescription)
|
// NOTE: just normal mode for now
|
||||||
{
|
/** @property {Iterator(Map)} @private */
|
||||||
// 0 args -> list all maps
|
__iterator__: function () this._mappingsIterator([modes.NORMAL], this._main),
|
||||||
// 1 arg -> list the maps starting with args
|
|
||||||
// 2 args -> map arg1 to arg*
|
|
||||||
function map(args, modes, noremap)
|
|
||||||
{
|
|
||||||
if (!args.length)
|
|
||||||
{
|
|
||||||
mappings.list(modes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let [lhs, rhs] = args;
|
// used by :mkvimperatorrc to save mappings
|
||||||
|
/**
|
||||||
|
* Returns a user-defined mappings iterator for the specified
|
||||||
|
* <b>mode</b>.
|
||||||
|
*
|
||||||
|
* @param {number} mode The mode to return mappings from.
|
||||||
|
* @returns {Iterator(Map)}
|
||||||
|
*/
|
||||||
|
getUserIterator: function (mode) this._mappingsIterator(mode, this._user),
|
||||||
|
|
||||||
if (!rhs) // list the mapping
|
addMode: function (mode) {
|
||||||
mappings.list(modes, expandLeader(lhs));
|
if (!(mode in this._user || mode in this._main)) {
|
||||||
else
|
this._main[mode] = [];
|
||||||
{
|
this._user[mode] = [];
|
||||||
// this matches Vim's behaviour
|
}
|
||||||
if (/^<Nop>$/i.test(rhs))
|
},
|
||||||
noremap = true;
|
|
||||||
|
|
||||||
mappings.addUserMap(modes, [lhs],
|
/**
|
||||||
"User defined mapping",
|
* Adds a new default key mapping.
|
||||||
function (count) { events.feedkeys((count > -1 ? count : "") + this.rhs, this.noremap, this.silent); },
|
*
|
||||||
{
|
* @param {number[]} modes The modes that this mapping applies to.
|
||||||
count: true,
|
* @param {string[]} keys The key sequences which are bound to
|
||||||
rhs: events.canonicalKeys(rhs),
|
* <b>action</b>.
|
||||||
noremap: !!noremap,
|
* @param {string} description A description of the key mapping.
|
||||||
silent: "<silent>" in args
|
* @param {function} action The action invoked by each key sequence.
|
||||||
});
|
* @param {Object} extra An optional extra configuration hash.
|
||||||
}
|
* @optional
|
||||||
|
*/
|
||||||
|
add: function (modes, keys, description, action, extra) {
|
||||||
|
this._addMap(new Map(modes, keys, description, action, extra));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new user-defined key mapping.
|
||||||
|
*
|
||||||
|
* @param {number[]} modes The modes that this mapping applies to.
|
||||||
|
* @param {string[]} keys The key sequences which are bound to
|
||||||
|
* <b>action</b>.
|
||||||
|
* @param {string} description A description of the key mapping.
|
||||||
|
* @param {function} action The action invoked by each key sequence.
|
||||||
|
* @param {Object} extra An optional extra configuration hash (see
|
||||||
|
* {@link Map#extraInfo}).
|
||||||
|
* @optional
|
||||||
|
*/
|
||||||
|
addUserMap: function (modes, keys, description, action, extra) {
|
||||||
|
keys = keys.map(this._expandLeader);
|
||||||
|
extra = extra || {};
|
||||||
|
extra.user = true;
|
||||||
|
let map = new Map(modes, keys, description || "User defined mapping", action, extra);
|
||||||
|
|
||||||
|
// remove all old mappings to this key sequence
|
||||||
|
for (let [, name] in Iterator(map.names)) {
|
||||||
|
for (let [, mode] in Iterator(map.modes))
|
||||||
|
this._removeMap(mode, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
modeDescription = modeDescription ? " in " + modeDescription + " mode" : "";
|
this._addMap(map);
|
||||||
|
},
|
||||||
|
|
||||||
// :map, :noremap => NORMAL + VISUAL modes
|
/**
|
||||||
function isMultiMode(map, cmd)
|
* Returns the map from <b>mode</b> named <b>cmd</b>.
|
||||||
{
|
*
|
||||||
return map.modes.indexOf(modules.modes.NORMAL) >= 0
|
* @param {number} mode The mode to search.
|
||||||
&& map.modes.indexOf(modules.modes.VISUAL) >= 0
|
* @param {string} cmd The map name to match.
|
||||||
&& /^[nv](nore)?map$/.test(cmd);
|
* @returns {Map}
|
||||||
}
|
*/
|
||||||
|
get: function (mode, cmd) {
|
||||||
|
mode = mode || modes.NORMAL;
|
||||||
|
return this._getMap(mode, cmd, this._user) || this._getMap(mode, cmd, this._main);
|
||||||
|
},
|
||||||
|
|
||||||
const opts = {
|
/**
|
||||||
completer: function (context, args) completion.userMapping(context, args, modes),
|
* Returns the default map from <b>mode</b> named <b>cmd</b>.
|
||||||
options: [
|
*
|
||||||
[["<silent>", "<Silent>"], commands.OPTION_NOARG]
|
* @param {number} mode The mode to search.
|
||||||
],
|
* @param {string} cmd The map name to match.
|
||||||
literal: 1,
|
* @returns {Map}
|
||||||
serial: function ()
|
*/
|
||||||
{
|
getDefault: function (mode, cmd) {
|
||||||
let noremap = this.name.indexOf("noremap") > -1;
|
mode = mode || modes.NORMAL;
|
||||||
return [
|
return this._getMap(mode, cmd, this._main);
|
||||||
{
|
},
|
||||||
command: this.name,
|
|
||||||
options: map.silent ? { "<silent>": null } : {},
|
/**
|
||||||
arguments: [map.names[0]],
|
* Returns an array of maps with names starting with but not equal to
|
||||||
literalArg: map.rhs
|
* <b>prefix</b>.
|
||||||
}
|
*
|
||||||
for (map in mappingsIterator(modes, user))
|
* @param {number} mode The mode to search.
|
||||||
if (map.rhs && map.noremap == noremap && !isMultiMode(map, this.name))
|
* @param {string} prefix The map prefix string to match.
|
||||||
];
|
* @returns {Map[]}
|
||||||
|
*/
|
||||||
|
getCandidates: function (mode, prefix) {
|
||||||
|
let mappings = this._user[mode].concat(this._main[mode]);
|
||||||
|
let matches = [];
|
||||||
|
|
||||||
|
for (let [, map] in Iterator(mappings)) {
|
||||||
|
for (let [, name] in Iterator(map.names)) {
|
||||||
|
if (name.indexOf(prefix) == 0 && name.length > prefix.length) {
|
||||||
|
// for < only return a candidate if it doesn't look like a <c-x> mapping
|
||||||
|
if (prefix != "<" || !/^<.+>/.test(name))
|
||||||
|
matches.push(map);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commands.add([ch ? ch + "m[ap]" : "map"],
|
return matches;
|
||||||
"Map a key sequence" + modeDescription,
|
},
|
||||||
function (args) { map(args, modes, false); },
|
|
||||||
opts);
|
|
||||||
|
|
||||||
commands.add([ch + "no[remap]"],
|
/*
|
||||||
"Map a key sequence without remapping keys" + modeDescription,
|
* Returns the map leader string used to replace the special token
|
||||||
function (args) { map(args, modes, true); },
|
* "<Leader>" when user mappings are defined.
|
||||||
opts);
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
// FIXME: property
|
||||||
|
getMapLeader: function () {
|
||||||
|
let leaderRef = liberator.variableReference("mapleader");
|
||||||
|
return leaderRef[0] ? leaderRef[0][leaderRef[1]] : "\\";
|
||||||
|
},
|
||||||
|
|
||||||
commands.add([ch + "mapc[lear]"],
|
/**
|
||||||
"Remove all mappings" + modeDescription,
|
* Returns whether there is a user-defined mapping <b>cmd</b> for the
|
||||||
function () { modes.forEach(function (mode) { mappings.removeAll(mode); }); },
|
* specified <b>mode</b>.
|
||||||
{ argCount: "0" });
|
*
|
||||||
|
* @param {number} mode The mode to search.
|
||||||
|
* @param {string} cmd The candidate key mapping.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasMap: function (mode, cmd) this._user[mode].some(function (map) map.hasName(cmd)),
|
||||||
|
|
||||||
commands.add([ch + "unm[ap]"],
|
/**
|
||||||
"Remove a mapping" + modeDescription,
|
* Remove the user-defined mapping named <b>cmd</b> for <b>mode</b>.
|
||||||
function (args)
|
*
|
||||||
{
|
* @param {number} mode The mode to search.
|
||||||
args = args[0];
|
* @param {string} cmd The map name to match.
|
||||||
|
*/
|
||||||
|
remove: function (mode, cmd) {
|
||||||
|
this._removeMap(mode, cmd);
|
||||||
|
},
|
||||||
|
|
||||||
let found = false;
|
/**
|
||||||
for (let [, mode] in Iterator(modes))
|
* Remove all user-defined mappings for <b>mode</b>.
|
||||||
|
*
|
||||||
|
* @param {number} mode The mode to remove all mappings from.
|
||||||
|
*/
|
||||||
|
removeAll: function (mode) {
|
||||||
|
this._user[mode] = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all user-defined mappings matching <b>filter</b> for the
|
||||||
|
* specified <b>modes</b>.
|
||||||
|
*
|
||||||
|
* @param {number[]} modes An array of modes to search.
|
||||||
|
* @param {string} filter The filter string to match.
|
||||||
|
*/
|
||||||
|
list: function (modes, filter) {
|
||||||
|
let modeSign = "";
|
||||||
|
|
||||||
|
// TODO: Vim hides "nv" in a :map and "v" and "n" in :vmap and
|
||||||
|
// :nmap respectively if the map is not exclusive.
|
||||||
|
modes.forEach(function (mode) {
|
||||||
|
for (let m in modules.modes.mainModes)
|
||||||
|
if (mode == m.mask && modeSign.indexOf(m.char) == -1)
|
||||||
|
modeSign += m.char;
|
||||||
|
});
|
||||||
|
|
||||||
|
let maps = this._mappingsIterator(modes, this._user);
|
||||||
|
if (filter)
|
||||||
|
maps = [map for (map in maps) if (map.names[0] == filter)];
|
||||||
|
|
||||||
|
let list = <table>
|
||||||
{
|
{
|
||||||
if (mappings.hasMap(mode, args))
|
template.map(maps, function (map)
|
||||||
{
|
template.map(map.names, function (name)
|
||||||
mappings.remove(mode, args);
|
<tr>
|
||||||
found = true;
|
<td>{modeSign} {name}</td>
|
||||||
|
<td>{map.noremap ? "*" : " "}</td>
|
||||||
|
<td>{map.rhs || "function () { ... }"}</td>
|
||||||
|
</tr>))
|
||||||
|
}
|
||||||
|
</table>;
|
||||||
|
|
||||||
|
// TODO: Move this to an ItemList to show this automatically
|
||||||
|
if (list.*.length() == list.text().length()) {
|
||||||
|
liberator.echomsg("No mapping found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
function addMapCommands(ch, modes, modeDescription) {
|
||||||
|
// 0 args -> list all maps
|
||||||
|
// 1 arg -> list the maps starting with args
|
||||||
|
// 2 args -> map arg1 to arg*
|
||||||
|
function map(args, modes, noremap) {
|
||||||
|
if (!args.length) {
|
||||||
|
mappings.list(modes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [lhs, rhs] = args;
|
||||||
|
|
||||||
|
if (!rhs) // list the mapping
|
||||||
|
mappings.list(modes, this._expandLeader(lhs));
|
||||||
|
else {
|
||||||
|
// this matches Vim's behaviour
|
||||||
|
if (/^<Nop>$/i.test(rhs))
|
||||||
|
noremap = true;
|
||||||
|
|
||||||
|
mappings.addUserMap(modes, [lhs],
|
||||||
|
"User defined mapping",
|
||||||
|
function (count) { events.feedkeys((count > -1 ? count : "") + this.rhs, this.noremap, this.silent); }, {
|
||||||
|
count: true,
|
||||||
|
rhs: events.canonicalKeys(rhs),
|
||||||
|
noremap: !!noremap,
|
||||||
|
silent: "<silent>" in args
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modeDescription = modeDescription ? " in " + modeDescription + " mode" : "";
|
||||||
|
|
||||||
|
// :map, :noremap => NORMAL + VISUAL modes
|
||||||
|
function isMultiMode(map, cmd) {
|
||||||
|
return map.modes.indexOf(modules.modes.NORMAL) >= 0
|
||||||
|
&& map.modes.indexOf(modules.modes.VISUAL) >= 0
|
||||||
|
&& /^[nv](nore)?map$/.test(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
completer: function (context, args) completion.userMapping(context, args, modes),
|
||||||
|
options: [
|
||||||
|
[["<silent>", "<Silent>"], commands.OPTION_NOARG]
|
||||||
|
],
|
||||||
|
literal: 1,
|
||||||
|
serial: function () {
|
||||||
|
let noremap = this.name.indexOf("noremap") > -1;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
command: this.name,
|
||||||
|
options: map.silent ? { "<silent>": null } : {},
|
||||||
|
arguments: [map.names[0]],
|
||||||
|
literalArg: map.rhs
|
||||||
|
}
|
||||||
|
for (map in this._mappingsIterator(modes, this._user))
|
||||||
|
if (map.rhs && map.noremap == noremap && !isMultiMode(map, this.name))
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
if (!found)
|
|
||||||
liberator.echoerr("E31: No such mapping");
|
|
||||||
},
|
|
||||||
{
|
|
||||||
argCount: "1",
|
|
||||||
completer: function (context, args) completion.userMapping(context, args, modes)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
commands.add([ch ? ch + "m[ap]" : "map"],
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
"Map a key sequence" + modeDescription,
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
function (args) { map(args, modes, false); },
|
||||||
|
opts);
|
||||||
|
|
||||||
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
|
commands.add([ch + "no[remap]"],
|
||||||
|
"Map a key sequence without remapping keys" + modeDescription,
|
||||||
|
function (args) { map(args, modes, true); },
|
||||||
|
opts);
|
||||||
|
|
||||||
for (let mode in modes.mainModes)
|
commands.add([ch + "mapc[lear]"],
|
||||||
if (mode.char && !commands.get(mode.char + "map"))
|
"Remove all mappings" + modeDescription,
|
||||||
addMapCommands(mode.char,
|
function () { modes.forEach(function (mode) { mappings.removeAll(mode); }); },
|
||||||
[m.mask for (m in modes.mainModes) if (m.char == mode.char)],
|
{ argCount: "0" });
|
||||||
[mode.disp.toLowerCase()]);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
commands.add([ch + "unm[ap]"],
|
||||||
////////////////////// COMPLETIONS /////////////////////////////////////////////
|
"Remove a mapping" + modeDescription,
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
function (args) {
|
||||||
|
args = args[0];
|
||||||
|
|
||||||
liberator.registerObserver("load_completion", function () {
|
let found = false;
|
||||||
|
for (let [, mode] in Iterator(modes)) {
|
||||||
|
if (mappings.hasMap(mode, args)) {
|
||||||
|
mappings.remove(mode, args);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
liberator.echoerr("E31: No such mapping");
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argCount: "1",
|
||||||
|
completer: function (context, args) completion.userMapping(context, args, modes)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
|
||||||
|
|
||||||
|
for (let mode in modes.mainModes)
|
||||||
|
if (mode.char && !commands.get(mode.char + "map"))
|
||||||
|
addMapCommands(mode.char,
|
||||||
|
[m.mask for (m in modes.mainModes) if (m.char == mode.char)],
|
||||||
|
[mode.disp.toLowerCase()]);
|
||||||
|
},
|
||||||
|
completion: function () {
|
||||||
completion.setFunctionCompleter(mappings.get,
|
completion.setFunctionCompleter(mappings.get,
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
function (context, obj, args)
|
function (context, obj, args) {
|
||||||
{
|
|
||||||
let mode = args[0];
|
let mode = args[0];
|
||||||
return util.Array.flatten(
|
return util.Array.flatten(
|
||||||
[
|
[
|
||||||
[[name, map.description] for ([i, name] in Iterator(map.names))]
|
[[name, map.description] for ([i, name] in Iterator(map.names))]
|
||||||
for ([i, map] in Iterator(user[mode].concat(main[mode])))
|
for ([i, map] in Iterator(this._user[mode].concat(this._main[mode])))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -327,232 +487,18 @@ function Mappings() //{{{
|
|||||||
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
|
// FIXME: have we decided on a 'standard' way to handle this clash? --djk
|
||||||
modes = modes || [modules.modes.NORMAL];
|
modes = modes || [modules.modes.NORMAL];
|
||||||
|
|
||||||
if (args.completeArg == 0)
|
if (args.completeArg == 0) {
|
||||||
{
|
|
||||||
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
|
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
|
||||||
context.completions = maps;
|
context.completions = maps;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
modes: function () {
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
for (let mode in modes) {
|
||||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
this._main[mode] = [];
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
this._user[mode] = [];
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
// NOTE: just normal mode for now
|
|
||||||
/** @property {Iterator(Map)} @private */
|
|
||||||
__iterator__: function () mappingsIterator([modes.NORMAL], main),
|
|
||||||
|
|
||||||
// used by :mkvimperatorrc to save mappings
|
|
||||||
/**
|
|
||||||
* Returns a user-defined mappings iterator for the specified
|
|
||||||
* <b>mode</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to return mappings from.
|
|
||||||
* @returns {Iterator(Map)}
|
|
||||||
*/
|
|
||||||
getUserIterator: function (mode) mappingsIterator(mode, user),
|
|
||||||
|
|
||||||
addMode: function (mode)
|
|
||||||
{
|
|
||||||
if (!(mode in user || mode in main))
|
|
||||||
{
|
|
||||||
main[mode] = [];
|
|
||||||
user[mode] = [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new default key mapping.
|
|
||||||
*
|
|
||||||
* @param {number[]} modes The modes that this mapping applies to.
|
|
||||||
* @param {string[]} keys The key sequences which are bound to
|
|
||||||
* <b>action</b>.
|
|
||||||
* @param {string} description A description of the key mapping.
|
|
||||||
* @param {function} action The action invoked by each key sequence.
|
|
||||||
* @param {Object} extra An optional extra configuration hash.
|
|
||||||
* @optional
|
|
||||||
*/
|
|
||||||
add: function (modes, keys, description, action, extra)
|
|
||||||
{
|
|
||||||
addMap(new Map(modes, keys, description, action, extra));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new user-defined key mapping.
|
|
||||||
*
|
|
||||||
* @param {number[]} modes The modes that this mapping applies to.
|
|
||||||
* @param {string[]} keys The key sequences which are bound to
|
|
||||||
* <b>action</b>.
|
|
||||||
* @param {string} description A description of the key mapping.
|
|
||||||
* @param {function} action The action invoked by each key sequence.
|
|
||||||
* @param {Object} extra An optional extra configuration hash (see
|
|
||||||
* {@link Map#extraInfo}).
|
|
||||||
* @optional
|
|
||||||
*/
|
|
||||||
addUserMap: function (modes, keys, description, action, extra)
|
|
||||||
{
|
|
||||||
keys = keys.map(expandLeader);
|
|
||||||
extra = extra || {};
|
|
||||||
extra.user = true;
|
|
||||||
let map = new Map(modes, keys, description || "User defined mapping", action, extra);
|
|
||||||
|
|
||||||
// remove all old mappings to this key sequence
|
|
||||||
for (let [, name] in Iterator(map.names))
|
|
||||||
{
|
|
||||||
for (let [, mode] in Iterator(map.modes))
|
|
||||||
removeMap(mode, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMap(map);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the map from <b>mode</b> named <b>cmd</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to search.
|
|
||||||
* @param {string} cmd The map name to match.
|
|
||||||
* @returns {Map}
|
|
||||||
*/
|
|
||||||
get: function (mode, cmd)
|
|
||||||
{
|
|
||||||
mode = mode || modes.NORMAL;
|
|
||||||
return getMap(mode, cmd, user) || getMap(mode, cmd, main);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the default map from <b>mode</b> named <b>cmd</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to search.
|
|
||||||
* @param {string} cmd The map name to match.
|
|
||||||
* @returns {Map}
|
|
||||||
*/
|
|
||||||
getDefault: function (mode, cmd)
|
|
||||||
{
|
|
||||||
mode = mode || modes.NORMAL;
|
|
||||||
return getMap(mode, cmd, main);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of maps with names starting with but not equal to
|
|
||||||
* <b>prefix</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to search.
|
|
||||||
* @param {string} prefix The map prefix string to match.
|
|
||||||
* @returns {Map[]}
|
|
||||||
*/
|
|
||||||
getCandidates: function (mode, prefix)
|
|
||||||
{
|
|
||||||
let mappings = user[mode].concat(main[mode]);
|
|
||||||
let matches = [];
|
|
||||||
|
|
||||||
for (let [, map] in Iterator(mappings))
|
|
||||||
{
|
|
||||||
for (let [, name] in Iterator(map.names))
|
|
||||||
{
|
|
||||||
if (name.indexOf(prefix) == 0 && name.length > prefix.length)
|
|
||||||
{
|
|
||||||
// for < only return a candidate if it doesn't look like a <c-x> mapping
|
|
||||||
if (prefix != "<" || !/^<.+>/.test(name))
|
|
||||||
matches.push(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the map leader string used to replace the special token
|
|
||||||
* "<Leader>" when user mappings are defined.
|
|
||||||
*
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
// FIXME: property
|
|
||||||
getMapLeader: function ()
|
|
||||||
{
|
|
||||||
let leaderRef = liberator.variableReference("mapleader");
|
|
||||||
return leaderRef[0] ? leaderRef[0][leaderRef[1]] : "\\";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether there is a user-defined mapping <b>cmd</b> for the
|
|
||||||
* specified <b>mode</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to search.
|
|
||||||
* @param {string} cmd The candidate key mapping.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
hasMap: function (mode, cmd) user[mode].some(function (map) map.hasName(cmd)),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the user-defined mapping named <b>cmd</b> for <b>mode</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to search.
|
|
||||||
* @param {string} cmd The map name to match.
|
|
||||||
*/
|
|
||||||
remove: function (mode, cmd)
|
|
||||||
{
|
|
||||||
removeMap(mode, cmd);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all user-defined mappings for <b>mode</b>.
|
|
||||||
*
|
|
||||||
* @param {number} mode The mode to remove all mappings from.
|
|
||||||
*/
|
|
||||||
removeAll: function (mode)
|
|
||||||
{
|
|
||||||
user[mode] = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists all user-defined mappings matching <b>filter</b> for the
|
|
||||||
* specified <b>modes</b>.
|
|
||||||
*
|
|
||||||
* @param {number[]} modes An array of modes to search.
|
|
||||||
* @param {string} filter The filter string to match.
|
|
||||||
*/
|
|
||||||
list: function (modes, filter)
|
|
||||||
{
|
|
||||||
let modeSign = "";
|
|
||||||
|
|
||||||
// TODO: Vim hides "nv" in a :map and "v" and "n" in :vmap and
|
|
||||||
// :nmap respectively if the map is not exclusive.
|
|
||||||
modes.forEach(function (mode) {
|
|
||||||
for (let m in modules.modes.mainModes)
|
|
||||||
if (mode == m.mask && modeSign.indexOf(m.char) == -1)
|
|
||||||
modeSign += m.char;
|
|
||||||
});
|
|
||||||
|
|
||||||
let maps = mappingsIterator(modes, user);
|
|
||||||
if (filter)
|
|
||||||
maps = [map for (map in maps) if (map.names[0] == filter)];
|
|
||||||
|
|
||||||
let list = <table>
|
|
||||||
{
|
|
||||||
template.map(maps, function (map)
|
|
||||||
template.map(map.names, function (name)
|
|
||||||
<tr>
|
|
||||||
<td>{modeSign} {name}</td>
|
|
||||||
<td>{map.noremap ? "*" : " "}</td>
|
|
||||||
<td>{map.rhs || "function () { ... }"}</td>
|
|
||||||
</tr>))
|
|
||||||
}
|
|
||||||
</table>;
|
|
||||||
|
|
||||||
// TODO: Move this to an ItemList to show this automatically
|
|
||||||
if (list.*.length() == list.text().length())
|
|
||||||
{
|
|
||||||
liberator.echomsg("No mapping found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
//}}}
|
});
|
||||||
} //}}}
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
343
common/content/marks.js
Normal file
343
common/content/marks.js
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @scope modules
|
||||||
|
* @instance marks
|
||||||
|
*/
|
||||||
|
const Marks = Module("marks", {
|
||||||
|
requires: ["storage"],
|
||||||
|
|
||||||
|
init: function init() {
|
||||||
|
this._localMarks = storage.newMap("local-marks", { store: true, privateData: true });
|
||||||
|
this._urlMarks = storage.newMap("url-marks", { store: true, privateData: true });
|
||||||
|
|
||||||
|
this._pendingJumps = [];
|
||||||
|
|
||||||
|
var appContent = document.getElementById("appcontent");
|
||||||
|
if (appContent)
|
||||||
|
appContent.addEventListener("load", this.closure._onPageLoad, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {Array} Returns all marks, both local and URL, in a sorted
|
||||||
|
* array.
|
||||||
|
*/
|
||||||
|
get all() {
|
||||||
|
// local marks
|
||||||
|
let location = window.content.location.href;
|
||||||
|
let lmarks = [i for (i in this._localMarkIter()) if (i[1].location == location)];
|
||||||
|
lmarks.sort();
|
||||||
|
|
||||||
|
// URL marks
|
||||||
|
// FIXME: why does umarks.sort() cause a "Component is not available =
|
||||||
|
// NS_ERROR_NOT_AVAILABLE" exception when used here?
|
||||||
|
let umarks = [i for (i in this._urlMarks)];
|
||||||
|
umarks.sort(function (a, b) a[0].localeCompare(b[0]));
|
||||||
|
|
||||||
|
return lmarks.concat(umarks);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a named mark for the current buffer, at its current position.
|
||||||
|
* If mark matches [A-Z], it's considered a URL mark, and will jump to
|
||||||
|
* the same position at the same URL no matter what buffer it's
|
||||||
|
* selected from. If it matches [a-z'"], it's a local mark, and can
|
||||||
|
* only be recalled from a buffer with a matching URL.
|
||||||
|
*
|
||||||
|
* @param {string} mark The mark name.
|
||||||
|
* @param {boolean} silent Whether to output error messages.
|
||||||
|
*/
|
||||||
|
// TODO: add support for frameset pages
|
||||||
|
add: function (mark, silent) {
|
||||||
|
let win = window.content;
|
||||||
|
let doc = win.document;
|
||||||
|
|
||||||
|
if (!doc.body)
|
||||||
|
return;
|
||||||
|
if (doc.body instanceof HTMLFrameSetElement) {
|
||||||
|
if (!silent)
|
||||||
|
liberator.echoerr("Marks support for frameset pages not implemented yet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = win.scrollMaxX ? win.pageXOffset / win.scrollMaxX : 0;
|
||||||
|
let y = win.scrollMaxY ? win.pageYOffset / win.scrollMaxY : 0;
|
||||||
|
let position = { x: x, y: y };
|
||||||
|
|
||||||
|
if (Marks.isURLMark(mark)) {
|
||||||
|
this._urlMarks.set(mark, { location: win.location.href, position: position, tab: tabs.getTab() });
|
||||||
|
if (!silent)
|
||||||
|
liberator.log("Adding URL mark: " + Marks.markToString(mark, this._urlMarks.get(mark)), 5);
|
||||||
|
}
|
||||||
|
else if (Marks.isLocalMark(mark)) {
|
||||||
|
// remove any previous mark of the same name for this location
|
||||||
|
this._removeLocalMark(mark);
|
||||||
|
if (!this._localMarks.get(mark))
|
||||||
|
this._localMarks.set(mark, []);
|
||||||
|
let vals = { location: win.location.href, position: position };
|
||||||
|
this._localMarks.get(mark).push(vals);
|
||||||
|
if (!silent)
|
||||||
|
liberator.log("Adding local mark: " + Marks.markToString(mark, vals), 5);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all marks matching <b>filter</b>. If <b>special</b> is
|
||||||
|
* given, removes all local marks.
|
||||||
|
*
|
||||||
|
* @param {string} filter A string containing one character for each
|
||||||
|
* mark to be removed.
|
||||||
|
* @param {boolean} special Whether to delete all local marks.
|
||||||
|
*/
|
||||||
|
// FIXME: Shouldn't special be replaced with a null filter?
|
||||||
|
remove: function (filter, special) {
|
||||||
|
if (special) {
|
||||||
|
// :delmarks! only deletes a-z marks
|
||||||
|
for (let [mark, ] in this._localMarks)
|
||||||
|
this._removeLocalMark(mark);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let [mark, ] in this._urlMarks) {
|
||||||
|
if (filter.indexOf(mark) >= 0)
|
||||||
|
this._removeURLMark(mark);
|
||||||
|
}
|
||||||
|
for (let [mark, ] in this._localMarks) {
|
||||||
|
if (filter.indexOf(mark) >= 0)
|
||||||
|
this._removeLocalMark(mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jumps to the named mark. See {@link #add}
|
||||||
|
*
|
||||||
|
* @param {string} mark The mark to jump to.
|
||||||
|
*/
|
||||||
|
jumpTo: function (mark) {
|
||||||
|
let ok = false;
|
||||||
|
|
||||||
|
if (Marks.isURLMark(mark)) {
|
||||||
|
let slice = this._urlMarks.get(mark);
|
||||||
|
if (slice && slice.tab && slice.tab.linkedBrowser) {
|
||||||
|
if (slice.tab.parentNode != getBrowser().tabContainer) {
|
||||||
|
this._pendingJumps.push(slice);
|
||||||
|
// NOTE: this obviously won't work on generated pages using
|
||||||
|
// non-unique URLs :(
|
||||||
|
liberator.open(slice.location, liberator.NEW_TAB);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let index = tabs.index(slice.tab);
|
||||||
|
if (index != -1) {
|
||||||
|
tabs.select(index);
|
||||||
|
let win = slice.tab.linkedBrowser.contentWindow;
|
||||||
|
if (win.location.href != slice.location) {
|
||||||
|
this._pendingJumps.push(slice);
|
||||||
|
win.location.href = slice.location;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
liberator.log("Jumping to URL mark: " + Marks.markToString(mark, slice), 5);
|
||||||
|
buffer.scrollToPercent(slice.position.x * 100, slice.position.y * 100);
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Marks.isLocalMark(mark)) {
|
||||||
|
let win = window.content;
|
||||||
|
let slice = this._localMarks.get(mark) || [];
|
||||||
|
|
||||||
|
for (let [, lmark] in Iterator(slice)) {
|
||||||
|
if (win.location.href == lmark.location) {
|
||||||
|
liberator.log("Jumping to local mark: " + Marks.markToString(mark, lmark), 5);
|
||||||
|
buffer.scrollToPercent(lmark.position.x * 100, lmark.position.y * 100);
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
liberator.echoerr("E20: Mark not set");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all marks matching <b>filter</b>.
|
||||||
|
*
|
||||||
|
* @param {string} filter
|
||||||
|
*/
|
||||||
|
list: function (filter) {
|
||||||
|
let marks = this.all;
|
||||||
|
|
||||||
|
liberator.assert(marks.length > 0, "No marks set");
|
||||||
|
|
||||||
|
if (filter.length > 0) {
|
||||||
|
marks = marks.filter(function (mark) filter.indexOf(mark[0]) >= 0);
|
||||||
|
liberator.assert(marks.length > 0, "E283: No marks matching " + filter.quote());
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = template.tabular(
|
||||||
|
["Mark", "Line", "Column", "File"],
|
||||||
|
["", "text-align: right", "text-align: right", "color: green"],
|
||||||
|
([mark[0],
|
||||||
|
Math.round(mark[1].position.x * 100) + "%",
|
||||||
|
Math.round(mark[1].position.y * 100) + "%",
|
||||||
|
mark[1].location]
|
||||||
|
for ([, mark] in Iterator(marks))));
|
||||||
|
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPageLoad: function _onPageLoad(event) {
|
||||||
|
let win = event.originalTarget.defaultView;
|
||||||
|
for (let [i, mark] in Iterator(this._pendingJumps)) {
|
||||||
|
if (win && win.location.href == mark.location) {
|
||||||
|
buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100);
|
||||||
|
this._pendingJumps.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeLocalMark: function _removeLocalMark(mark) {
|
||||||
|
let localmark = this._localMarks.get(mark);
|
||||||
|
if (localmark) {
|
||||||
|
let win = window.content;
|
||||||
|
for (let [i, ] in Iterator(localmark)) {
|
||||||
|
if (localmark[i].location == win.location.href) {
|
||||||
|
liberator.log("Deleting local mark: " + Marks.markToString(mark, localmark[i]), 5);
|
||||||
|
localmark.splice(i, 1);
|
||||||
|
if (localmark.length == 0)
|
||||||
|
this._localMarks.remove(mark);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeURLMark: function _removeURLMark(mark) {
|
||||||
|
let urlmark = this._urlMarks.get(mark);
|
||||||
|
if (urlmark) {
|
||||||
|
liberator.log("Deleting URL mark: " + Marks.markToString(mark, urlmark), 5);
|
||||||
|
this._urlMarks.remove(mark);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_localMarkIter: function _localMarkIter() {
|
||||||
|
for (let [mark, value] in this._localMarks)
|
||||||
|
for (let [, val] in Iterator(value))
|
||||||
|
yield [mark, val];
|
||||||
|
},
|
||||||
|
|
||||||
|
}, {
|
||||||
|
markToString: function markToString(name, mark) {
|
||||||
|
return name + ", " + mark.location +
|
||||||
|
", (" + Math.round(mark.position.x * 100) +
|
||||||
|
"%, " + Math.round(mark.position.y * 100) + "%)" +
|
||||||
|
(("tab" in mark) ? ", tab: " + tabs.index(mark.tab) : "");
|
||||||
|
},
|
||||||
|
|
||||||
|
isLocalMark: function isLocalMark(mark) /^['`a-z]$/.test(mark),
|
||||||
|
isURLMark: function isURLMark(mark) /^[A-Z0-9]$/.test(mark),
|
||||||
|
}, {
|
||||||
|
mappings: function () {
|
||||||
|
var myModes = config.browserModes;
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["m"], "Set mark at the cursor position",
|
||||||
|
function (arg) {
|
||||||
|
if (/[^a-zA-Z]/.test(arg))
|
||||||
|
return void liberator.beep();
|
||||||
|
|
||||||
|
marks.add(arg);
|
||||||
|
},
|
||||||
|
{ arg: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["'", "`"], "Jump to the mark in the current buffer",
|
||||||
|
function (arg) { marks.jumpTo(arg); },
|
||||||
|
{ arg: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["delm[arks]"],
|
||||||
|
"Delete the specified marks",
|
||||||
|
function (args) {
|
||||||
|
let special = args.bang;
|
||||||
|
args = args.string;
|
||||||
|
|
||||||
|
// assert(special ^ args)
|
||||||
|
liberator.assert( special || args, "E471: Argument required");
|
||||||
|
liberator.assert(!special || !args, "E474: Invalid argument");
|
||||||
|
|
||||||
|
let matches;
|
||||||
|
if (matches = args.match(/(?:(?:^|[^a-zA-Z0-9])-|-(?:$|[^a-zA-Z0-9])|[^a-zA-Z0-9 -]).*/)) {
|
||||||
|
// NOTE: this currently differs from Vim's behavior which
|
||||||
|
// deletes any valid marks in the arg list, up to the first
|
||||||
|
// invalid arg, as well as giving the error message.
|
||||||
|
liberator.echoerr("E475: Invalid argument: " + matches[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// check for illegal ranges - only allow a-z A-Z 0-9
|
||||||
|
if (matches = args.match(/[a-zA-Z0-9]-[a-zA-Z0-9]/g)) {
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
let start = matches[i][0];
|
||||||
|
let end = matches[i][2];
|
||||||
|
if (/[a-z]/.test(start) != /[a-z]/.test(end) ||
|
||||||
|
/[A-Z]/.test(start) != /[A-Z]/.test(end) ||
|
||||||
|
/[0-9]/.test(start) != /[0-9]/.test(end) ||
|
||||||
|
start > end)
|
||||||
|
{
|
||||||
|
liberator.echoerr("E475: Invalid argument: " + args.match(matches[i] + ".*")[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marks.remove(args, special);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bang: true,
|
||||||
|
completer: function (context) completion.mark(context)
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(["ma[rk]"],
|
||||||
|
"Mark current location within the web page",
|
||||||
|
function (args) {
|
||||||
|
let mark = args[0];
|
||||||
|
liberator.assert(mark.length <= 1, "E488: Trailing characters");
|
||||||
|
liberator.assert(/[a-zA-Z]/.test(mark),
|
||||||
|
"E191: Argument must be a letter or forward/backward quote");
|
||||||
|
|
||||||
|
marks.add(mark);
|
||||||
|
},
|
||||||
|
{ argCount: "1" });
|
||||||
|
|
||||||
|
commands.add(["marks"],
|
||||||
|
"Show all location marks of current web page",
|
||||||
|
function (args) {
|
||||||
|
args = args.string;
|
||||||
|
|
||||||
|
// ignore invalid mark characters unless there are no valid mark chars
|
||||||
|
liberator.assert(!args || /[a-zA-Z]/.test(args),
|
||||||
|
"E283: No marks matching " + args.quote());
|
||||||
|
|
||||||
|
let filter = args.replace(/[^a-zA-Z]/g, "");
|
||||||
|
marks.list(filter);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
completion: function () {
|
||||||
|
completion.mark = function mark(context) {
|
||||||
|
function percent(i) Math.round(i * 100);
|
||||||
|
|
||||||
|
// FIXME: Line/Column doesn't make sense with %
|
||||||
|
context.title = ["Mark", "Line Column File"];
|
||||||
|
context.keys.description = function ([, m]) percent(m.position.y) + "% " + percent(m.position.x) + "% " + m.location;
|
||||||
|
context.completions = marks.all;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
@@ -5,29 +5,55 @@
|
|||||||
|
|
||||||
/** @scope modules */
|
/** @scope modules */
|
||||||
|
|
||||||
const modes = (function () //{{{
|
const Modes = Module("modes", {
|
||||||
{
|
requires: ["util"],
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
var main = 1; // NORMAL
|
init: function () {
|
||||||
var extended = 0; // NONE
|
this._main = 1; // NORMAL
|
||||||
|
this._extended = 0; // NONE
|
||||||
|
|
||||||
var lastShown = null;
|
this._lastShown = null;
|
||||||
|
|
||||||
var passNextKey = false;
|
this._passNextKey = false;
|
||||||
var passAllKeys = false;
|
this._passAllKeys = false;
|
||||||
var isRecording = false;
|
this._isRecording = false;
|
||||||
var isReplaying = false; // playing a macro
|
this._isReplaying = false; // playing a macro
|
||||||
|
|
||||||
var modeStack = [];
|
this._modeStack = [];
|
||||||
|
|
||||||
function getModeMessage()
|
this._mainModes = [self.NONE];
|
||||||
{
|
this._lastMode = 0;
|
||||||
if (passNextKey && !passAllKeys)
|
this._modeMap = {};
|
||||||
|
|
||||||
|
// main modes, only one should ever be active
|
||||||
|
this.addMode("NORMAL", { char: "n", display: -1 });
|
||||||
|
this.addMode("INSERT", { char: "i", input: true });
|
||||||
|
this.addMode("VISUAL", { char: "v", display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : "") });
|
||||||
|
this.addMode("COMMAND_LINE", { char: "c", input: true });
|
||||||
|
this.addMode("CARET"); // text cursor is visible
|
||||||
|
this.addMode("TEXTAREA", { char: "i" });
|
||||||
|
this.addMode("EMBED", { input: true });
|
||||||
|
this.addMode("CUSTOM", { display: function () plugins.mode });
|
||||||
|
// this._extended modes, can include multiple modes, and even main modes
|
||||||
|
this.addMode("EX", true);
|
||||||
|
this.addMode("HINTS", true);
|
||||||
|
this.addMode("INPUT_MULTILINE", true);
|
||||||
|
this.addMode("OUTPUT_MULTILINE", true);
|
||||||
|
this.addMode("SEARCH_FORWARD", true);
|
||||||
|
this.addMode("SEARCH_BACKWARD", true);
|
||||||
|
this.addMode("SEARCH_VIEW_FORWARD", true);
|
||||||
|
this.addMode("SEARCH_VIEW_BACKWARD", true);
|
||||||
|
this.addMode("MENU", true); // a popupmenu is active
|
||||||
|
this.addMode("LINE", true); // linewise visual mode
|
||||||
|
this.addMode("PROMPT", true);
|
||||||
|
|
||||||
|
config.modes.forEach(function (mode) { this.addMode.apply(this, mode); }, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getModeMessage: function () {
|
||||||
|
if (this._passNextKey && !this._passAllKeys)
|
||||||
return "-- PASS THROUGH (next) --";
|
return "-- PASS THROUGH (next) --";
|
||||||
else if (passAllKeys && !passNextKey)
|
else if (this._passAllKeys && !this._passNextKey)
|
||||||
return "-- PASS THROUGH --";
|
return "-- PASS THROUGH --";
|
||||||
|
|
||||||
// when recording a macro
|
// when recording a macro
|
||||||
@@ -38,57 +64,52 @@ const modes = (function () //{{{
|
|||||||
macromode = "replaying";
|
macromode = "replaying";
|
||||||
|
|
||||||
let ext = "";
|
let ext = "";
|
||||||
if (extended & modes.MENU) // TODO: desirable?
|
if (this._extended & modes.MENU) // TODO: desirable?
|
||||||
ext += " (menu)";
|
ext += " (menu)";
|
||||||
ext += " --" + macromode;
|
ext += " --" + macromode;
|
||||||
|
|
||||||
if (main in modeMap && typeof modeMap[main].display == "function")
|
if (this._main in this._modeMap && typeof this._modeMap[this._main].display == "function")
|
||||||
return "-- " + modeMap[main].display() + ext;
|
return "-- " + this._modeMap[this._main].display() + ext;
|
||||||
return macromode;
|
return macromode;
|
||||||
}
|
},
|
||||||
|
|
||||||
// NOTE: Pay attention that you don't run into endless loops
|
// NOTE: Pay attention that you don't run into endless loops
|
||||||
// Usually you should only indicate to leave a special mode like HINTS
|
// Usually you should only indicate to leave a special mode like HINTS
|
||||||
// by calling modes.reset() and adding the stuff which is needed
|
// by calling modes.reset() and adding the stuff which is needed
|
||||||
// for its cleanup here
|
// for its cleanup here
|
||||||
function handleModeChange(oldMode, newMode, oldExtended)
|
_handleModeChange: function (oldMode, newMode, oldExtended) {
|
||||||
{
|
|
||||||
|
|
||||||
switch (oldMode)
|
switch (oldMode) {
|
||||||
{
|
case modes.TEXTAREA:
|
||||||
case modes.TEXTAREA:
|
case modes.INSERT:
|
||||||
case modes.INSERT:
|
editor.unselectText();
|
||||||
editor.unselectText();
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case modes.VISUAL:
|
case modes.VISUAL:
|
||||||
if (newMode == modes.CARET)
|
if (newMode == modes.CARET) {
|
||||||
{
|
try { // clear any selection made; a simple if (selection) does not work
|
||||||
try
|
let selection = window.content.getSelection();
|
||||||
{ // clear any selection made; a simple if (selection) does not work
|
selection.collapseToStart();
|
||||||
let selection = window.content.getSelection();
|
|
||||||
selection.collapseToStart();
|
|
||||||
}
|
|
||||||
catch (e) {}
|
|
||||||
}
|
}
|
||||||
else
|
catch (e) {}
|
||||||
editor.unselectText();
|
}
|
||||||
break;
|
else
|
||||||
|
editor.unselectText();
|
||||||
|
break;
|
||||||
|
|
||||||
case modes.CUSTOM:
|
case modes.CUSTOM:
|
||||||
plugins.stop();
|
plugins.stop();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case modes.COMMAND_LINE:
|
case modes.COMMAND_LINE:
|
||||||
// clean up for HINT mode
|
// clean up for HINT mode
|
||||||
if (oldExtended & modes.HINTS)
|
if (oldExtended & modes.HINTS)
|
||||||
hints.hide();
|
hints.hide();
|
||||||
commandline.close();
|
commandline.close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newMode == modes.NORMAL)
|
if (newMode == modes.NORMAL) {
|
||||||
{
|
|
||||||
// disable caret mode when we want to switch to normal mode
|
// disable caret mode when we want to switch to normal mode
|
||||||
if (options.getPref("accessibility.browsewithcaret"))
|
if (options.getPref("accessibility.browsewithcaret"))
|
||||||
options.setPref("accessibility.browsewithcaret", false);
|
options.setPref("accessibility.browsewithcaret", false);
|
||||||
@@ -96,185 +117,134 @@ const modes = (function () //{{{
|
|||||||
statusline.updateUrl();
|
statusline.updateUrl();
|
||||||
liberator.focusContent(true);
|
liberator.focusContent(true);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
NONE: 0,
|
||||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
const self = {
|
__iterator__: function () util.Array.itervalues(this.all),
|
||||||
|
|
||||||
NONE: 0,
|
get all() this._mainModes.slice(),
|
||||||
|
|
||||||
__iterator__: function () util.Array.itervalues(this.all),
|
get mainModes() (mode for ([k, mode] in Iterator(modes._modeMap)) if (!mode.extended && mode.name == k)),
|
||||||
|
|
||||||
get all() mainModes.slice(),
|
get mainMode() this._modeMap[this._main],
|
||||||
|
|
||||||
get mainModes() (mode for ([k, mode] in Iterator(modeMap)) if (!mode.extended && mode.name == k)),
|
addMode: function (name, extended, options) {
|
||||||
|
let disp = name.replace("_", " ", "g");
|
||||||
|
this[name] = 1 << this._lastMode++;
|
||||||
|
if (typeof extended == "object") {
|
||||||
|
options = extended;
|
||||||
|
extended = false;
|
||||||
|
}
|
||||||
|
this._modeMap[name] = this._modeMap[this[name]] = util.extend({
|
||||||
|
extended: extended,
|
||||||
|
count: true,
|
||||||
|
input: false,
|
||||||
|
mask: this[name],
|
||||||
|
name: name,
|
||||||
|
disp: disp
|
||||||
|
}, options);
|
||||||
|
this._modeMap[name].display = this._modeMap[name].display || function () disp;
|
||||||
|
if (!extended)
|
||||||
|
this._mainModes.push(this[name]);
|
||||||
|
if ("mappings" in modules)
|
||||||
|
mappings.addMode(this[name]);
|
||||||
|
},
|
||||||
|
|
||||||
get mainMode() modeMap[main],
|
getMode: function (name) this._modeMap[name],
|
||||||
|
|
||||||
addMode: function (name, extended, options)
|
// show the current mode string in the command line
|
||||||
{
|
show: function () {
|
||||||
let disp = name.replace("_", " ", "g");
|
let msg = "";
|
||||||
this[name] = 1 << lastMode++;
|
if (options["showmode"])
|
||||||
if (typeof extended == "object")
|
msg = this._getModeMessage();
|
||||||
{
|
|
||||||
options = extended;
|
|
||||||
extended = false;
|
|
||||||
}
|
|
||||||
modeMap[name] = modeMap[this[name]] = util.extend({
|
|
||||||
extended: extended,
|
|
||||||
count: true,
|
|
||||||
input: false,
|
|
||||||
mask: this[name],
|
|
||||||
name: name,
|
|
||||||
disp: disp
|
|
||||||
}, options);
|
|
||||||
modeMap[name].display = modeMap[name].display || function () disp;
|
|
||||||
if (!extended)
|
|
||||||
mainModes.push(this[name]);
|
|
||||||
if ("mappings" in modules)
|
|
||||||
mappings.addMode(this[name]);
|
|
||||||
},
|
|
||||||
|
|
||||||
getMode: function (name) modeMap[name],
|
commandline.echo(msg, "ModeMsg", commandline.FORCE_SINGLELINE);
|
||||||
|
},
|
||||||
|
|
||||||
// show the current mode string in the command line
|
// add/remove always work on the this._extended mode only
|
||||||
show: function ()
|
add: function (mode) {
|
||||||
{
|
this._extended |= mode;
|
||||||
let msg = "";
|
this.show();
|
||||||
if (options["showmode"])
|
},
|
||||||
msg = getModeMessage();
|
|
||||||
|
|
||||||
commandline.echo(msg, "ModeMsg", commandline.FORCE_SINGLELINE);
|
// helper function to set both modes in one go
|
||||||
},
|
// if silent == true, you also need to take care of the mode handling changes yourself
|
||||||
|
set: function (mainMode, extendedMode, silent, stack) {
|
||||||
|
silent = (silent || this._main == mainMode && this._extended == extendedMode);
|
||||||
|
// if a this._main mode is set, the this._extended is always cleared
|
||||||
|
let oldMain = this._main, oldExtended = this._extended;
|
||||||
|
if (typeof extendedMode === "number")
|
||||||
|
this._extended = extendedMode;
|
||||||
|
if (typeof mainMode === "number") {
|
||||||
|
this._main = mainMode;
|
||||||
|
if (!extendedMode)
|
||||||
|
this._extended = modes.NONE;
|
||||||
|
|
||||||
// add/remove always work on the extended mode only
|
if (this._main != oldMain)
|
||||||
add: function (mode)
|
this._handleModeChange(oldMain, mainMode, oldExtended);
|
||||||
{
|
}
|
||||||
extended |= mode;
|
liberator.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended], stack);
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
this.show();
|
this.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
// helper function to set both modes in one go
|
push: function (mainMode, extendedMode, silent) {
|
||||||
// if silent == true, you also need to take care of the mode handling changes yourself
|
this._modeStack.push([this._main, this._extended]);
|
||||||
set: function (mainMode, extendedMode, silent, stack)
|
this.set(mainMode, extendedMode, silent, { push: this._modeStack[this._modeStack.length - 1] });
|
||||||
{
|
},
|
||||||
silent = (silent || main == mainMode && extended == extendedMode);
|
|
||||||
// if a main mode is set, the extended is always cleared
|
|
||||||
let oldMain = main, oldExtended = extended;
|
|
||||||
if (typeof extendedMode === "number")
|
|
||||||
extended = extendedMode;
|
|
||||||
if (typeof mainMode === "number")
|
|
||||||
{
|
|
||||||
main = mainMode;
|
|
||||||
if (!extendedMode)
|
|
||||||
extended = modes.NONE;
|
|
||||||
|
|
||||||
if (main != oldMain)
|
pop: function (silent) {
|
||||||
handleModeChange(oldMain, mainMode, oldExtended);
|
let a = this._modeStack.pop();
|
||||||
}
|
if (a)
|
||||||
liberator.triggerObserver("modeChange", [oldMain, oldExtended], [main, extended], stack);
|
this.set(a[0], a[1], silent, { pop: a });
|
||||||
|
else
|
||||||
|
this.reset(silent);
|
||||||
|
},
|
||||||
|
|
||||||
if (!silent)
|
// TODO: Deprecate this in favor of addMode? --Kris
|
||||||
this.show();
|
// Ya --djk
|
||||||
},
|
setCustomMode: function (modestr, oneventfunc, stopfunc) {
|
||||||
|
// TODO this.plugin[id]... ('id' maybe submode or what..)
|
||||||
|
plugins.mode = modestr;
|
||||||
|
plugins.onEvent = oneventfunc;
|
||||||
|
plugins.stop = stopfunc;
|
||||||
|
},
|
||||||
|
|
||||||
push: function (mainMode, extendedMode, silent)
|
// keeps recording state
|
||||||
{
|
reset: function (silent) {
|
||||||
modeStack.push([main, extended]);
|
this._modeStack = [];
|
||||||
this.set(mainMode, extendedMode, silent, { push: modeStack[modeStack.length - 1] });
|
if (config.isComposeWindow)
|
||||||
},
|
this.set(modes.COMPOSE, modes.NONE, silent);
|
||||||
|
else
|
||||||
|
this.set(modes.NORMAL, modes.NONE, silent);
|
||||||
|
},
|
||||||
|
|
||||||
pop: function (silent)
|
remove: function (mode) {
|
||||||
{
|
if (this._extended & mode) {
|
||||||
let a = modeStack.pop();
|
this._extended &= ~mode;
|
||||||
if (a)
|
this.show();
|
||||||
this.set(a[0], a[1], silent, { pop: a });
|
}
|
||||||
else
|
},
|
||||||
this.reset(silent);
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: Deprecate this in favor of addMode? --Kris
|
get passNextKey() this._passNextKey,
|
||||||
// Ya --djk
|
set passNextKey(value) { this._passNextKey = value; this.show(); },
|
||||||
setCustomMode: function (modestr, oneventfunc, stopfunc)
|
|
||||||
{
|
|
||||||
// TODO this.plugin[id]... ('id' maybe submode or what..)
|
|
||||||
plugins.mode = modestr;
|
|
||||||
plugins.onEvent = oneventfunc;
|
|
||||||
plugins.stop = stopfunc;
|
|
||||||
},
|
|
||||||
|
|
||||||
// keeps recording state
|
get passAllKeys() this._passAllKeys,
|
||||||
reset: function (silent)
|
set passAllKeys(value) { this._passAllKeys = value; this.show(); },
|
||||||
{
|
|
||||||
modeStack = [];
|
|
||||||
if (config.isComposeWindow)
|
|
||||||
this.set(modes.COMPOSE, modes.NONE, silent);
|
|
||||||
else
|
|
||||||
this.set(modes.NORMAL, modes.NONE, silent);
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function (mode)
|
get isRecording() this._isRecording,
|
||||||
{
|
set isRecording(value) { this._isRecording = value; this.show(); },
|
||||||
if (extended & mode)
|
|
||||||
{
|
|
||||||
extended &= ~mode;
|
|
||||||
this.show();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get passNextKey() passNextKey,
|
get isReplaying() this._isReplaying,
|
||||||
set passNextKey(value) { passNextKey = value; this.show(); },
|
set isReplaying(value) { this._isReplaying = value; this.show(); },
|
||||||
|
|
||||||
get passAllKeys() passAllKeys,
|
get main() this._main,
|
||||||
set passAllKeys(value) { passAllKeys = value; this.show(); },
|
set main(value) { this.set(value); },
|
||||||
|
|
||||||
get isRecording() isRecording,
|
get extended() this._extended,
|
||||||
set isRecording(value) { isRecording = value; this.show(); },
|
set extended(value) { this.set(null, value); }
|
||||||
|
});
|
||||||
get isReplaying() isReplaying,
|
|
||||||
set isReplaying(value) { isReplaying = value; this.show(); },
|
|
||||||
|
|
||||||
get main() main,
|
|
||||||
set main(value) { this.set(value); },
|
|
||||||
|
|
||||||
get extended() extended,
|
|
||||||
set extended(value) { this.set(null, value); }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var mainModes = [self.NONE];
|
|
||||||
var lastMode = 0;
|
|
||||||
var modeMap = {};
|
|
||||||
|
|
||||||
// main modes, only one should ever be active
|
|
||||||
self.addMode("NORMAL", { char: "n", display: -1 });
|
|
||||||
self.addMode("INSERT", { char: "i", input: true });
|
|
||||||
self.addMode("VISUAL", { char: "v", display: function () "VISUAL" + (extended & modes.LINE ? " LINE" : "") });
|
|
||||||
self.addMode("COMMAND_LINE", { char: "c", input: true });
|
|
||||||
self.addMode("CARET"); // text cursor is visible
|
|
||||||
self.addMode("TEXTAREA", { char: "i" });
|
|
||||||
self.addMode("EMBED", { input: true });
|
|
||||||
self.addMode("CUSTOM", { display: function () plugins.mode });
|
|
||||||
// extended modes, can include multiple modes, and even main modes
|
|
||||||
self.addMode("EX", true);
|
|
||||||
self.addMode("HINTS", true);
|
|
||||||
self.addMode("INPUT_MULTILINE", true);
|
|
||||||
self.addMode("OUTPUT_MULTILINE", true);
|
|
||||||
self.addMode("SEARCH_FORWARD", true);
|
|
||||||
self.addMode("SEARCH_BACKWARD", true);
|
|
||||||
self.addMode("SEARCH_VIEW_FORWARD", true);
|
|
||||||
self.addMode("SEARCH_VIEW_BACKWARD", true);
|
|
||||||
self.addMode("MENU", true); // a popupmenu is active
|
|
||||||
self.addMode("LINE", true); // linewise visual mode
|
|
||||||
self.addMode("PROMPT", true);
|
|
||||||
|
|
||||||
config.modes.forEach(function (mode) { self.addMode.apply(self, mode); });
|
|
||||||
|
|
||||||
return self;
|
|
||||||
//}}}
|
|
||||||
})(); //}}}
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
73
common/content/modules.js
Normal file
73
common/content/modules.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
const ModuleBase = Class("ModuleBase", { requires: [] });
|
||||||
|
function Module(name, inst, clas, moduleInit) {
|
||||||
|
const module = Class(name, ModuleBase, inst, clas);
|
||||||
|
module.INIT = moduleInit || {};
|
||||||
|
module.requires = inst.requires || [];
|
||||||
|
Module.list.push(module);
|
||||||
|
Module.constructors[name] = module;
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
Module.list = [];
|
||||||
|
Module.constructors = {};
|
||||||
|
|
||||||
|
window.addEventListener("load", function () {
|
||||||
|
function dump(str) window.dump(String.replace(str, /\n?$/, "\n").replace(/^/m, config.name.toLowerCase() + ": "));
|
||||||
|
const start = Date.now();
|
||||||
|
const deferredInit = { load: [] };
|
||||||
|
const seen = set();
|
||||||
|
|
||||||
|
function load(module, prereq) {
|
||||||
|
try {
|
||||||
|
if (module.name in modules)
|
||||||
|
return;
|
||||||
|
if (module.name in seen)
|
||||||
|
throw Error("Module dependency loop.");
|
||||||
|
set.add(seen, module.name);
|
||||||
|
|
||||||
|
for (let dep in values(module.requires))
|
||||||
|
load(Module.constructors[dep], module.name);
|
||||||
|
|
||||||
|
dump("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.name);
|
||||||
|
modules[module.name] = module();
|
||||||
|
|
||||||
|
function init(mod, module)
|
||||||
|
function () module.INIT[mod].call(modules[module.name], modules[mod]);
|
||||||
|
for (let [mod, ] in iter(module.INIT))
|
||||||
|
try {
|
||||||
|
if (mod in modules)
|
||||||
|
init(mod, module)();
|
||||||
|
else {
|
||||||
|
deferredInit[mod] = deferredInit[mod] || [];
|
||||||
|
deferredInit[mod].push(init(mod, module));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
if (modules.liberator)
|
||||||
|
liberator.reportError(e);
|
||||||
|
}
|
||||||
|
for (let [, fn] in iter(deferredInit[module.name] || []))
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
dump("Loading " + (module && module.name) + ": " + e);
|
||||||
|
if (e.stack)
|
||||||
|
dump(e.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Module.list.forEach(load);
|
||||||
|
deferredInit['load'].forEach(call)
|
||||||
|
|
||||||
|
for (let module in values(Module.list))
|
||||||
|
delete module.INIT;
|
||||||
|
|
||||||
|
dump("Loaded in " + (Date.now() - start) + "ms\n");
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
window.addEventListener("unload", function () {
|
||||||
|
for (let [, mod] in iter(modules))
|
||||||
|
if (mod instanceof ModuleBase && 'destroy' in mod)
|
||||||
|
mod.destroy();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
File diff suppressed because it is too large
Load Diff
173
common/content/quickmarks.js
Normal file
173
common/content/quickmarks.js
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
/** @scope modules */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @instance quickmarks
|
||||||
|
*/
|
||||||
|
const QuickMarks = Module("quickmarks", {
|
||||||
|
requires: ["storage"],
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this._qmarks = storage.newMap("quickmarks", { store: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new quickmark with name <b>qmark</b> referencing
|
||||||
|
* the URL <b>location</b>. Any existing quickmark with the same name
|
||||||
|
* will be replaced.
|
||||||
|
*
|
||||||
|
* @param {string} qmark The name of the quickmark {A-Z}.
|
||||||
|
* @param {string} location The URL accessed by this quickmark.
|
||||||
|
*/
|
||||||
|
add: function add(qmark, location) {
|
||||||
|
this._qmarks.set(qmark, location);
|
||||||
|
liberator.echomsg("Added Quick Mark '" + qmark + "': " + location, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the specified quickmarks. The <b>filter</b> is a list of
|
||||||
|
* quickmarks and ranges are supported. Eg. "ab c d e-k".
|
||||||
|
*
|
||||||
|
* @param {string} filter The list of quickmarks to delete.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
remove: function remove(filter) {
|
||||||
|
let pattern = RegExp("[" + filter.replace(/\s+/g, "") + "]");
|
||||||
|
|
||||||
|
for (let [qmark, ] in this._qmarks) {
|
||||||
|
if (pattern.test(qmark))
|
||||||
|
this._qmarks.remove(qmark);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all quickmarks.
|
||||||
|
*/
|
||||||
|
removeAll: function removeAll() {
|
||||||
|
this._qmarks.clear();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the URL referenced by the specified <b>qmark</b>.
|
||||||
|
*
|
||||||
|
* @param {string} qmark The quickmark to open.
|
||||||
|
* @param {number} where A constant describing where to open the page.
|
||||||
|
* See {@link Liberator#open}.
|
||||||
|
*/
|
||||||
|
jumpTo: function jumpTo(qmark, where) {
|
||||||
|
let url = this._qmarks.get(qmark);
|
||||||
|
|
||||||
|
if (url)
|
||||||
|
liberator.open(url, where);
|
||||||
|
else
|
||||||
|
liberator.echoerr("E20: QuickMark not set");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all quickmarks matching <b>filter</b> in the message window.
|
||||||
|
*
|
||||||
|
* @param {string} filter The list of quickmarks to display. Eg. "abc"
|
||||||
|
* Ranges are not supported.
|
||||||
|
*/
|
||||||
|
// FIXME: filter should match that of quickmarks.remove or vice versa
|
||||||
|
list: function list(filter) {
|
||||||
|
let marks = [k for ([k, v] in this._qmarks)];
|
||||||
|
let lowercaseMarks = marks.filter(function (x) /[a-z]/.test(x)).sort();
|
||||||
|
let uppercaseMarks = marks.filter(function (x) /[A-Z]/.test(x)).sort();
|
||||||
|
let numberMarks = marks.filter(function (x) /[0-9]/.test(x)).sort();
|
||||||
|
|
||||||
|
marks = Array.concat(lowercaseMarks, uppercaseMarks, numberMarks);
|
||||||
|
|
||||||
|
liberator.assert(marks.length > 0, "No QuickMarks set");
|
||||||
|
|
||||||
|
if (filter.length > 0) {
|
||||||
|
marks = marks.filter(function (qmark) filter.indexOf(qmark) >= 0);
|
||||||
|
liberator.assert(marks.length >= 0, "E283: No QuickMarks matching \"" + filter + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [[mark, this._qmarks.get(mark)] for ([k, mark] in Iterator(marks))];
|
||||||
|
template.genericTable(items, { title: ["QuickMark", "URL"] });
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["delqm[arks]"],
|
||||||
|
"Delete the specified QuickMarks",
|
||||||
|
function (args) {
|
||||||
|
// TODO: finish arg parsing - we really need a proper way to do this. :)
|
||||||
|
// assert(args.bang ^ args.string)
|
||||||
|
liberator.assert( args.bang || args.string, "E471: Argument required");
|
||||||
|
liberator.assert(!args.bang || !args.string, "E474: Invalid argument");
|
||||||
|
|
||||||
|
if (args.bang)
|
||||||
|
quickmarks.removeAll();
|
||||||
|
else
|
||||||
|
quickmarks.remove(args.string);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bang: true,
|
||||||
|
completer: function (context) {
|
||||||
|
context.title = ["QuickMark", "URL"];
|
||||||
|
context.completions = this._qmarks;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(["qma[rk]"],
|
||||||
|
"Mark a URL with a letter for quick access",
|
||||||
|
function (args) {
|
||||||
|
let matches = args.string.match(/^([a-zA-Z0-9])(?:\s+(.+))?$/);
|
||||||
|
if (!matches)
|
||||||
|
liberator.echoerr("E488: Trailing characters");
|
||||||
|
else if (!matches[2])
|
||||||
|
quickmarks.add(matches[1], buffer.URL);
|
||||||
|
else
|
||||||
|
quickmarks.add(matches[1], matches[2]);
|
||||||
|
},
|
||||||
|
{ argCount: "+" });
|
||||||
|
|
||||||
|
commands.add(["qmarks"],
|
||||||
|
"Show all QuickMarks",
|
||||||
|
function (args) {
|
||||||
|
args = args.string;
|
||||||
|
|
||||||
|
// ignore invalid qmark characters unless there are no valid qmark chars
|
||||||
|
liberator.assert(!args || /[a-zA-Z0-9]/.test(args), "E283: No QuickMarks matching \"" + args + "\"");
|
||||||
|
|
||||||
|
let filter = args.replace(/[^a-zA-Z0-9]/g, "");
|
||||||
|
quickmarks.list(filter);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mappings: function () {
|
||||||
|
var myModes = config.browserModes;
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["go"], "Jump to a QuickMark",
|
||||||
|
function (arg) { quickmarks.jumpTo(arg, liberator.CURRENT_TAB); },
|
||||||
|
{ arg: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["gn"], "Jump to a QuickMark in a new tab",
|
||||||
|
function (arg) {
|
||||||
|
quickmarks.jumpTo(arg,
|
||||||
|
/\bquickmark\b/.test(options["activate"]) ?
|
||||||
|
liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB);
|
||||||
|
},
|
||||||
|
{ arg: true });
|
||||||
|
|
||||||
|
mappings.add(myModes,
|
||||||
|
["M"], "Add new QuickMark for current URL",
|
||||||
|
function (arg) {
|
||||||
|
if (/[^a-zA-Z0-9]/.test(arg))
|
||||||
|
return void liberator.beep();
|
||||||
|
|
||||||
|
quickmarks.add(arg, buffer.URL);
|
||||||
|
},
|
||||||
|
{ arg: true });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
@@ -13,254 +13,36 @@
|
|||||||
// FIXME:
|
// FIXME:
|
||||||
// - finish 1.9.0 support if we're going to support sanitizing in Xulmus
|
// - finish 1.9.0 support if we're going to support sanitizing in Xulmus
|
||||||
|
|
||||||
function Sanitizer() //{{{
|
const Sanitizer = Module("sanitizer", {
|
||||||
{
|
requires: ["liberator"],
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
const local = {}; // XXX: is there some reason liberator.loadModule doesn't create modules with new?
|
init: function () {
|
||||||
services.get("subscriptLoader").loadSubScript("chrome://browser/content/sanitize.js", local);
|
const self = this;
|
||||||
const Sanitizer = local.Sanitizer;
|
liberator.loadScript("chrome://browser/content/sanitize.js", Sanitizer);
|
||||||
|
this.__proto__.__proto__ = new Sanitizer.Sanitizer; // Good enough.
|
||||||
|
|
||||||
var prefArgList = [["commandLine", "commandline"],
|
// TODO: remove this version test
|
||||||
["offlineApps", "offlineapps"],
|
if (/^1.9.1/.test(services.get("xulAppInfo").platformVersion))
|
||||||
["siteSettings", "sitesettings"]];
|
self.prefDomain = "privacy.cpd.";
|
||||||
|
else
|
||||||
|
self.prefDomain = "privacy.item.";
|
||||||
|
|
||||||
function prefToArg(pref)
|
self.prefDomain2 = "extensions.liberator.privacy.cpd.";
|
||||||
{
|
},
|
||||||
let pref = pref.replace(/.*\./, "");
|
|
||||||
return util.Array.toObject(prefArgList)[pref] || pref;
|
|
||||||
}
|
|
||||||
|
|
||||||
function argToPref(arg) [k for ([, [k, v]] in Iterator(prefArgList)) if (v == arg)][0] || arg
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
options.add(["sanitizeitems", "si"],
|
|
||||||
"The default list of private items to sanitize",
|
|
||||||
"stringlist", "cache,commandline,cookies,formdata,history,marks,sessions",
|
|
||||||
{
|
|
||||||
setter: function (values)
|
|
||||||
{
|
|
||||||
for (let [, pref] in Iterator(sanitizer.prefNames))
|
|
||||||
{
|
|
||||||
options.setPref(pref, false);
|
|
||||||
|
|
||||||
for (let [, value] in Iterator(this.parseValues(values)))
|
|
||||||
{
|
|
||||||
if (prefToArg(pref) == value)
|
|
||||||
{
|
|
||||||
options.setPref(pref, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
},
|
|
||||||
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(prefToArg).join(","),
|
|
||||||
completer: function (value) [
|
|
||||||
["cache", "Cache"],
|
|
||||||
["commandline", "Command-line history"],
|
|
||||||
["cookies", "Cookies"],
|
|
||||||
["downloads", "Download history"],
|
|
||||||
["formdata", "Saved form and search history"],
|
|
||||||
["history", "Browsing history"],
|
|
||||||
["macros", "Saved macros"],
|
|
||||||
["marks", "Local and URL marks"],
|
|
||||||
["offlineapps", "Offline website data"],
|
|
||||||
["passwords", "Saved passwords"],
|
|
||||||
["sessions", "Authenticated sessions"],
|
|
||||||
["sitesettings", "Site preferences"],
|
|
||||||
],
|
|
||||||
validator: Option.validateCompleter
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["sanitizetimespan", "sts"],
|
|
||||||
"The default sanitizer time span",
|
|
||||||
"number", 1,
|
|
||||||
{
|
|
||||||
setter: function (value)
|
|
||||||
{
|
|
||||||
options.setPref("privacy.sanitize.timeSpan", value);
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
getter: function () options.getPref("privacy.sanitize.timeSpan", this.defaultValue),
|
|
||||||
completer: function (value) [
|
|
||||||
["0", "Everything"],
|
|
||||||
["1", "Last hour"],
|
|
||||||
["2", "Last two hours"],
|
|
||||||
["3", "Last four hours"],
|
|
||||||
["4", "Today"]
|
|
||||||
],
|
|
||||||
validator: Option.validateCompleter
|
|
||||||
});
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
commands.add(["sa[nitize]"],
|
|
||||||
"Clear private data",
|
|
||||||
function (args)
|
|
||||||
{
|
|
||||||
if (options['private'])
|
|
||||||
return void liberator.echomsg("Cannot sanitize items in private mode");
|
|
||||||
|
|
||||||
let timespan = args["-timespan"] || options["sanitizetimespan"];
|
|
||||||
|
|
||||||
sanitizer.range = Sanitizer.getClearRange(timespan);
|
|
||||||
sanitizer.ignoreTimespan = !sanitizer.range;
|
|
||||||
|
|
||||||
if (args.bang)
|
|
||||||
{
|
|
||||||
liberator.assert(args.length == 0, "E488: Trailing characters");
|
|
||||||
|
|
||||||
liberator.log("Sanitizing all items in 'sanitizeitems'...");
|
|
||||||
|
|
||||||
let errors = sanitizer.sanitize();
|
|
||||||
|
|
||||||
if (errors)
|
|
||||||
{
|
|
||||||
for (let item in errors)
|
|
||||||
liberator.echoerr("Error sanitizing " + item + ": " + errors[item]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
liberator.assert(args.length > 0, "E471: Argument required");
|
|
||||||
|
|
||||||
for (let [, item] in Iterator(args.map(argToPref)))
|
|
||||||
{
|
|
||||||
liberator.log("Sanitizing " + item + " items...");
|
|
||||||
|
|
||||||
if (sanitizer.canClearItem(item))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sanitizer.items[item].range = sanitizer.range;
|
|
||||||
sanitizer.clearItem(item);
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
liberator.echoerr("Error sanitizing " + item + ": " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
liberator.echomsg("Cannot sanitize " + item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
argCount: "*", // FIXME: should be + and 0
|
|
||||||
bang: true,
|
|
||||||
completer: function (context) {
|
|
||||||
context.title = ["Privacy Item", "Description"];
|
|
||||||
context.completions = options.get("sanitizeitems").completer();
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
[["-timespan", "-t"],
|
|
||||||
commands.OPTION_INT,
|
|
||||||
function (arg) /^[0-4]$/.test(arg),
|
|
||||||
function () options.get("sanitizetimespan").completer()]
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
var self = new Sanitizer();
|
|
||||||
|
|
||||||
// TODO: remove this version test
|
|
||||||
if (/^1.9.1/.test(services.get("xulAppInfo").platformVersion))
|
|
||||||
self.prefDomain = "privacy.cpd.";
|
|
||||||
else
|
|
||||||
self.prefDomain = "privacy.item.";
|
|
||||||
|
|
||||||
self.prefDomain2 = "extensions.liberator.privacy.cpd.";
|
|
||||||
|
|
||||||
// add liberator-specific private items
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: "commandLine",
|
|
||||||
action: function ()
|
|
||||||
{
|
|
||||||
let stores = ["command", "search"];
|
|
||||||
|
|
||||||
if (self.range)
|
|
||||||
{
|
|
||||||
stores.forEach(function (store) {
|
|
||||||
storage["history-" + store].mutate("filter", function (item) {
|
|
||||||
let timestamp = item.timestamp * 1000;
|
|
||||||
return timestamp < self.range[0] || timestamp > self.range[1];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stores.forEach(function (store) { storage["history-" + store].truncate(0); });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "macros",
|
|
||||||
action: function () { storage["macros"].clear(); }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "marks",
|
|
||||||
action: function ()
|
|
||||||
{
|
|
||||||
storage["local-marks"].clear();
|
|
||||||
storage["url-marks"].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
].forEach(function (item) {
|
|
||||||
let pref = self.prefDomain2 + item.name;
|
|
||||||
|
|
||||||
if (options.getPref(pref) == null)
|
|
||||||
options.setPref(pref, false);
|
|
||||||
|
|
||||||
self.items[item.name] = {
|
|
||||||
canClear: true,
|
|
||||||
clear: item.action
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// call Sanitize autocommand
|
|
||||||
for (let [name, item] in Iterator(self.items))
|
|
||||||
{
|
|
||||||
let arg = prefToArg(name);
|
|
||||||
|
|
||||||
if (item.clear)
|
|
||||||
{
|
|
||||||
let func = item.clear;
|
|
||||||
item.clear = function () {
|
|
||||||
autocommands.trigger("Sanitize", { name: arg })
|
|
||||||
func.call(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getClearRange = Sanitizer.getClearRange;
|
|
||||||
|
|
||||||
// Largely ripped from from browser/base/content/sanitize.js so we can override
|
// Largely ripped from from browser/base/content/sanitize.js so we can override
|
||||||
// the pref strategy without stepping on the global prefs namespace.
|
// the pref strategy without stepping on the global prefs namespace.
|
||||||
self.sanitize = function () {
|
sanitize: function () {
|
||||||
const prefService = services.get("pref");
|
const prefService = services.get("pref");
|
||||||
let branch = prefService.getBranch(this.prefDomain);
|
let branch = prefService.getBranch(this.prefDomain);
|
||||||
let branch2 = prefService.getBranch(this.prefDomain2);
|
let branch2 = prefService.getBranch(this.prefDomain2);
|
||||||
let errors = null;
|
let errors = null;
|
||||||
|
|
||||||
function prefSet(name)
|
function prefSet(name) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
return branch.getBoolPref(name);
|
return branch.getBoolPref(name);
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
return branch2.getBoolPref(name);
|
return branch2.getBoolPref(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,25 +53,21 @@ function Sanitizer() //{{{
|
|||||||
else
|
else
|
||||||
range = this.range || Sanitizer.getClearRange();
|
range = this.range || Sanitizer.getClearRange();
|
||||||
|
|
||||||
for (let itemName in this.items)
|
for (let itemName in this.items) {
|
||||||
{
|
|
||||||
let item = this.items[itemName];
|
let item = this.items[itemName];
|
||||||
item.range = range;
|
item.range = range;
|
||||||
|
|
||||||
if ("clear" in item && item.canClear && prefSet(itemName))
|
if ("clear" in item && item.canClear && prefSet(itemName)) {
|
||||||
{
|
|
||||||
liberator.log("Sanitizing " + itemName + " items...");
|
liberator.log("Sanitizing " + itemName + " items...");
|
||||||
// Some of these clear() may raise exceptions (see bug #265028)
|
// Some of these clear() may raise exceptions (see bug #265028)
|
||||||
// to sanitize as much as possible, we catch and store them,
|
// to sanitize as much as possible, we catch and store them,
|
||||||
// rather than fail fast.
|
// rather than fail fast.
|
||||||
// Callers should check returned errors and give user feedback
|
// Callers should check returned errors and give user feedback
|
||||||
// about items that could not be sanitized
|
// about items that could not be sanitized
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
item.clear();
|
item.clear();
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
if (!errors)
|
if (!errors)
|
||||||
errors = {};
|
errors = {};
|
||||||
errors[itemName] = e;
|
errors[itemName] = e;
|
||||||
@@ -299,14 +77,193 @@ function Sanitizer() //{{{
|
|||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
},
|
||||||
|
|
||||||
self.__defineGetter__("prefNames",
|
get prefNames() util.Array.flatten([this.prefDomain, this.prefDomain2].map(options.allPrefs)),
|
||||||
function () util.Array.flatten([self.prefDomain, self.prefDomain2].map(options.allPrefs)));
|
}, {
|
||||||
//}}}
|
prefArgList: [["commandLine", "commandline"],
|
||||||
|
["offlineApps", "offlineapps"],
|
||||||
|
["siteSettings", "sitesettings"]],
|
||||||
|
prefToArg: function (pref) {
|
||||||
|
let pref = pref.replace(/.*\./, "");
|
||||||
|
return util.Array.toObject(Sanitizer.prefArgList)[pref] || pref;
|
||||||
|
},
|
||||||
|
|
||||||
return self;
|
argToPref: function (arg) [k for ([k, v] in values(Sanitizer.prefArgList)) if (v == arg)][0] || arg,
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["sa[nitize]"],
|
||||||
|
"Clear private data",
|
||||||
|
function (args) {
|
||||||
|
if (options['private'])
|
||||||
|
return void liberator.echomsg("Cannot sanitize items in private mode");
|
||||||
|
|
||||||
} //}}}
|
let timespan = args["-timespan"] || options["sanitizetimespan"];
|
||||||
|
|
||||||
|
sanitizer.range = Sanitizer.getClearRange(timespan);
|
||||||
|
sanitizer.ignoreTimespan = !sanitizer.range;
|
||||||
|
|
||||||
|
if (args.bang) {
|
||||||
|
liberator.assert(args.length == 0, "E488: Trailing characters");
|
||||||
|
|
||||||
|
liberator.log("Sanitizing all items in 'sanitizeitems'...");
|
||||||
|
|
||||||
|
let errors = sanitizer.sanitize();
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
for (let item in errors)
|
||||||
|
liberator.echoerr("Error sanitizing " + item + ": " + errors[item]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
liberator.assert(args.length > 0, "E471: Argument required");
|
||||||
|
|
||||||
|
for (let [, item] in Iterator(args.map(Sanitizer.argToPref))) {
|
||||||
|
liberator.log("Sanitizing " + item + " items...");
|
||||||
|
|
||||||
|
if (sanitizer.canClearItem(item)) {
|
||||||
|
try {
|
||||||
|
sanitizer.items[item].range = sanitizer.range;
|
||||||
|
sanitizer.clearItem(item);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
liberator.echoerr("Error sanitizing " + item + ": " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
liberator.echomsg("Cannot sanitize " + item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argCount: "*", // FIXME: should be + and 0
|
||||||
|
bang: true,
|
||||||
|
completer: function (context) {
|
||||||
|
context.title = ["Privacy Item", "Description"];
|
||||||
|
context.completions = options.get("sanitizeitems").completer();
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
[["-timespan", "-t"],
|
||||||
|
commands.OPTION_INT,
|
||||||
|
function (arg) /^[0-4]$/.test(arg),
|
||||||
|
function () options.get("sanitizetimespan").completer()]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
options: function () {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
// add liberator-specific private items
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "commandLine",
|
||||||
|
action: function () {
|
||||||
|
let stores = ["command", "search"];
|
||||||
|
|
||||||
|
if (self.range) {
|
||||||
|
stores.forEach(function (store) {
|
||||||
|
storage["history-" + store].mutate("filter", function (item) {
|
||||||
|
let timestamp = item.timestamp * 1000;
|
||||||
|
return timestamp < self.range[0] || timestamp > self.range[1];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stores.forEach(function (store) { storage["history-" + store].truncate(0); });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "macros",
|
||||||
|
action: function () { storage["macros"].clear(); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "marks",
|
||||||
|
action: function () {
|
||||||
|
storage["local-marks"].clear();
|
||||||
|
storage["url-marks"].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
].forEach(function (item) {
|
||||||
|
let pref = self.prefDomain2 + item.name;
|
||||||
|
|
||||||
|
if (options.getPref(pref) == null)
|
||||||
|
options.setPref(pref, false);
|
||||||
|
|
||||||
|
self.items[item.name] = {
|
||||||
|
canClear: true,
|
||||||
|
clear: item.action
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// call Sanitize autocommand
|
||||||
|
for (let [name, item] in Iterator(self.items)) {
|
||||||
|
let arg = Sanitizer.prefToArg(name);
|
||||||
|
|
||||||
|
if (item.clear) {
|
||||||
|
let func = item.clear;
|
||||||
|
item.clear = function () {
|
||||||
|
autocommands.trigger("Sanitize", { name: arg })
|
||||||
|
func.call(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options.add(["sanitizeitems", "si"],
|
||||||
|
"The default list of private items to sanitize",
|
||||||
|
"stringlist", "cache,commandline,cookies,formdata,history,marks,sessions",
|
||||||
|
{
|
||||||
|
setter: function (values) {
|
||||||
|
for (let [, pref] in Iterator(sanitizer.prefNames)) {
|
||||||
|
continue;
|
||||||
|
options.setPref(pref, false);
|
||||||
|
|
||||||
|
for (let [, value] in Iterator(this.parseValues(values))) {
|
||||||
|
if (Sanitizer.prefToArg(pref) == value) {
|
||||||
|
options.setPref(pref, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(Sanitizer.prefToArg).join(","),
|
||||||
|
completer: function (value) [
|
||||||
|
["cache", "Cache"],
|
||||||
|
["commandline", "Command-line history"],
|
||||||
|
["cookies", "Cookies"],
|
||||||
|
["downloads", "Download history"],
|
||||||
|
["formdata", "Saved form and search history"],
|
||||||
|
["history", "Browsing history"],
|
||||||
|
["macros", "Saved macros"],
|
||||||
|
["marks", "Local and URL marks"],
|
||||||
|
["offlineapps", "Offline website data"],
|
||||||
|
["passwords", "Saved passwords"],
|
||||||
|
["sessions", "Authenticated sessions"],
|
||||||
|
["sitesettings", "Site preferences"],
|
||||||
|
],
|
||||||
|
validator: Option.validateCompleter
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["sanitizetimespan", "sts"],
|
||||||
|
"The default sanitizer time span",
|
||||||
|
"number", 1,
|
||||||
|
{
|
||||||
|
setter: function (value) {
|
||||||
|
options.setPref("privacy.sanitize.timeSpan", value);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
getter: function () options.getPref("privacy.sanitize.timeSpan", this.defaultValue),
|
||||||
|
completer: function (value) [
|
||||||
|
["0", "Everything"],
|
||||||
|
["1", "Last hour"],
|
||||||
|
["2", "Last two hours"],
|
||||||
|
["3", "Last four hours"],
|
||||||
|
["4", "Today"]
|
||||||
|
],
|
||||||
|
validator: Option.validateCompleter
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
@@ -15,15 +15,46 @@ const Cu = Components.utils;
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function Services()
|
const Services = Module("services", {
|
||||||
{
|
init: function () {
|
||||||
const classes = {};
|
this.classes = {};
|
||||||
const services = {};
|
this.services = {};
|
||||||
|
|
||||||
function create(classes, ifaces, meth)
|
this.add("appStartup", "@mozilla.org/toolkit/app-startup;1", Ci.nsIAppStartup);
|
||||||
{
|
this.add("autoCompleteSearch", "@mozilla.org/autocomplete/search;1?name=history", Ci.nsIAutoCompleteSearch);
|
||||||
try
|
this.add("bookmarks", "@mozilla.org/browser/nav-bookmarks-service;1", Ci.nsINavBookmarksService);
|
||||||
{
|
this.add("browserSearch", "@mozilla.org/browser/search-service;1", Ci.nsIBrowserSearchService);
|
||||||
|
this.add("cache", "@mozilla.org/network/cache-service;1", Ci.nsICacheService);
|
||||||
|
this.add("console", "@mozilla.org/consoleservice;1", Ci.nsIConsoleService);
|
||||||
|
this.add("liberator:", "@mozilla.org/network/protocol;1?name=liberator");
|
||||||
|
this.add("directory", "@mozilla.org/file/directory_service;1", Ci.nsIProperties);
|
||||||
|
this.add("downloadManager", "@mozilla.org/download-manager;1", Ci.nsIDownloadManager);
|
||||||
|
this.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment);
|
||||||
|
this.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
|
||||||
|
this.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService);
|
||||||
|
this.add("history", "@mozilla.org/browser/global-history;2", [Ci.nsIGlobalHistory3, Ci.nsINavHistoryService, Ci.nsIBrowserHistory]);
|
||||||
|
this.add("io", "@mozilla.org/network/io-service;1", Ci.nsIIOService);
|
||||||
|
this.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance");
|
||||||
|
this.add("livemark", "@mozilla.org/browser/livemark-service;2", Ci.nsILivemarkService);
|
||||||
|
this.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService);
|
||||||
|
this.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefService, Ci.nsIPrefBranch, Ci.nsIPrefBranch2]);
|
||||||
|
this.add("profile", "@mozilla.org/toolkit/profile-service;1", Ci.nsIToolkitProfileService);
|
||||||
|
this.add("rdf", "@mozilla.org/rdf/rdf-service;1", Ci.nsIRDFService);
|
||||||
|
this.add("sessionStore", "@mozilla.org/browser/sessionstore;1", Ci.nsISessionStore);
|
||||||
|
this.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader);
|
||||||
|
this.add("threadManager", "@mozilla.org/thread-manager;1", Ci.nsIThreadManager);
|
||||||
|
this.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator);
|
||||||
|
this.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher);
|
||||||
|
this.add("xulAppInfo", "@mozilla.org/xre/app-info;1", Ci.nsIXULAppInfo);
|
||||||
|
|
||||||
|
this.addClass("file", "@mozilla.org/file/local;1", Ci.nsILocalFile);
|
||||||
|
this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler);
|
||||||
|
this.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
|
||||||
|
this.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
|
||||||
|
},
|
||||||
|
|
||||||
|
_create: function (classes, ifaces, meth) {
|
||||||
|
try {
|
||||||
let res = Cc[classes][meth || "getService"]();
|
let res = Cc[classes][meth || "getService"]();
|
||||||
if (!ifaces)
|
if (!ifaces)
|
||||||
return res.wrappedJSObject;
|
return res.wrappedJSObject;
|
||||||
@@ -31,97 +62,52 @@ function Services()
|
|||||||
ifaces.forEach(function (iface) res.QueryInterface(iface));
|
ifaces.forEach(function (iface) res.QueryInterface(iface));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
// liberator.log() is not defined at this time, so just dump any error
|
// liberator.log() is not defined at this time, so just dump any error
|
||||||
dump("Service creation failed for '" + classes + "': " + e + "\n");
|
dump("Service creation failed for '" + classes + "': " + e + "\n");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
const self = {
|
/**
|
||||||
/* @property {Object} A map of all cached services. */
|
* Adds a new XPCOM service to the cache.
|
||||||
get services() services,
|
*
|
||||||
|
* @param {string} name The service's cache key.
|
||||||
|
* @param {string} class The class's contract ID.
|
||||||
|
* @param {nsISupports|nsISupports[]} ifaces The interface or array of
|
||||||
|
* interfaces implemented by this service.
|
||||||
|
* @param {string} meth The name of the function used to instanciate
|
||||||
|
* the service.
|
||||||
|
*/
|
||||||
|
add: function (name, class, ifaces, meth) {
|
||||||
|
return this.services[name] = this._create(class, ifaces, meth);
|
||||||
|
},
|
||||||
|
|
||||||
/* @property {Object} A map of all cached classes. */
|
/**
|
||||||
get classes() classes,
|
* Adds a new XPCOM class to the cache.
|
||||||
|
*
|
||||||
|
* @param {string} name The class's cache key.
|
||||||
|
* @param {string} class The class's contract ID.
|
||||||
|
* @param {nsISupports|nsISupports[]} ifaces The interface or array of
|
||||||
|
* interfaces implemented by this class.
|
||||||
|
*/
|
||||||
|
addClass: function (name, class, ifaces) {
|
||||||
|
const self = this;
|
||||||
|
return this.classes[name] = function () self._create(class, ifaces, "createInstance");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new XPCOM service to the cache.
|
* Returns the cached service with the specified name.
|
||||||
*
|
*
|
||||||
* @param {string} name The service's cache key.
|
* @param {string} name The service's cache key.
|
||||||
* @param {string} class The class's contract ID.
|
*/
|
||||||
* @param {nsISupports|nsISupports[]} ifaces The interface or array of
|
get: function (name) this.services[name],
|
||||||
* interfaces implemented by this service.
|
|
||||||
* @param {string} meth The name of the function used to instanciate
|
|
||||||
* the service.
|
|
||||||
*/
|
|
||||||
add: function (name, class, ifaces, meth)
|
|
||||||
{
|
|
||||||
return services[name] = create(class, ifaces, meth);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new XPCOM class to the cache.
|
* Returns a new instance of the cached class with the specified name.
|
||||||
*
|
*
|
||||||
* @param {string} name The class's cache key.
|
* @param {string} name The class's cache key.
|
||||||
* @param {string} class The class's contract ID.
|
*/
|
||||||
* @param {nsISupports|nsISupports[]} ifaces The interface or array of
|
create: function (name) this.classes[name]()
|
||||||
* interfaces implemented by this class.
|
});
|
||||||
*/
|
|
||||||
addClass: function (name, class, ifaces)
|
|
||||||
{
|
|
||||||
return classes[name] = function () create(class, ifaces, "createInstance");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the cached service with the specified name.
|
|
||||||
*
|
|
||||||
* @param {string} name The service's cache key.
|
|
||||||
*/
|
|
||||||
get: function (name) services[name],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new instance of the cached class with the specified name.
|
|
||||||
*
|
|
||||||
* @param {string} name The class's cache key.
|
|
||||||
*/
|
|
||||||
create: function (name) classes[name]()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.add("appStartup", "@mozilla.org/toolkit/app-startup;1", Ci.nsIAppStartup);
|
|
||||||
self.add("autoCompleteSearch", "@mozilla.org/autocomplete/search;1?name=history", Ci.nsIAutoCompleteSearch);
|
|
||||||
self.add("bookmarks", "@mozilla.org/browser/nav-bookmarks-service;1", Ci.nsINavBookmarksService);
|
|
||||||
self.add("browserSearch", "@mozilla.org/browser/search-service;1", Ci.nsIBrowserSearchService);
|
|
||||||
self.add("cache", "@mozilla.org/network/cache-service;1", Ci.nsICacheService);
|
|
||||||
self.add("console", "@mozilla.org/consoleservice;1", Ci.nsIConsoleService);
|
|
||||||
self.add("directory", "@mozilla.org/file/directory_service;1", Ci.nsIProperties);
|
|
||||||
self.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment);
|
|
||||||
self.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
|
|
||||||
self.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService);
|
|
||||||
self.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance");
|
|
||||||
self.add("liberator:", "@mozilla.org/network/protocol;1?name=liberator");
|
|
||||||
self.add("livemark", "@mozilla.org/browser/livemark-service;2", Ci.nsILivemarkService);
|
|
||||||
self.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService);
|
|
||||||
self.add("io", "@mozilla.org/network/io-service;1", Ci.nsIIOService);
|
|
||||||
self.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefService, Ci.nsIPrefBranch, Ci.nsIPrefBranch2]);
|
|
||||||
self.add("profile", "@mozilla.org/toolkit/profile-service;1", Ci.nsIToolkitProfileService);
|
|
||||||
self.add("rdf", "@mozilla.org/rdf/rdf-service;1", Ci.nsIRDFService);
|
|
||||||
self.add("sessionStore", "@mozilla.org/browser/sessionstore;1", Ci.nsISessionStore);
|
|
||||||
self.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader);
|
|
||||||
self.add("threadManager", "@mozilla.org/thread-manager;1", Ci.nsIThreadManager);
|
|
||||||
self.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator);
|
|
||||||
self.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher);
|
|
||||||
self.add("xulAppInfo", "@mozilla.org/xre/app-info;1", Ci.nsIXULAppInfo);
|
|
||||||
|
|
||||||
self.addClass("file", "@mozilla.org/file/local;1", Ci.nsILocalFile);
|
|
||||||
self.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler);
|
|
||||||
self.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
|
|
||||||
self.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
|
|
||||||
self.addClass("zipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
var services = Services();
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
245
common/content/statusline.js
Normal file
245
common/content/statusline.js
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
|
||||||
|
/** @scope modules */
|
||||||
|
|
||||||
|
const StatusLine = Module("statusline", {
|
||||||
|
init: function () {
|
||||||
|
this._statusBar = document.getElementById("status-bar");
|
||||||
|
this._statusBar.collapsed = true; // it is later restored unless the user sets laststatus=0
|
||||||
|
|
||||||
|
// our status bar fields
|
||||||
|
this._statuslineWidget = document.getElementById("liberator-statusline");
|
||||||
|
this._urlWidget = document.getElementById("liberator-statusline-field-url");
|
||||||
|
this._inputBufferWidget = document.getElementById("liberator-statusline-field-inputbuffer");
|
||||||
|
this._progressWidget = document.getElementById("liberator-statusline-field-progress");
|
||||||
|
this._tabCountWidget = document.getElementById("liberator-statusline-field-tabcount");
|
||||||
|
this._bufferPositionWidget = document.getElementById("liberator-statusline-field-bufferposition");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the status bar to indicate how secure the website is:
|
||||||
|
* extended - Secure connection with Extended Validation(EV) certificate.
|
||||||
|
* secure - Secure connection with valid certificate.
|
||||||
|
* broken - Secure connection with invalid certificate, or
|
||||||
|
* mixed content.
|
||||||
|
* insecure - Insecure connection.
|
||||||
|
*
|
||||||
|
* @param {'extended'|'secure'|'broken'|'insecure'} type
|
||||||
|
*/
|
||||||
|
setClass: function setClass(type) {
|
||||||
|
const highlightGroup = {
|
||||||
|
extended: "StatusLineExtended",
|
||||||
|
secure: "StatusLineSecure",
|
||||||
|
broken: "StatusLineBroken",
|
||||||
|
insecure: "StatusLine"
|
||||||
|
};
|
||||||
|
|
||||||
|
this._statusBar.setAttributeNS(NS.uri, "highlight", highlightGroup[type]);
|
||||||
|
},
|
||||||
|
|
||||||
|
// update all fields of the statusline
|
||||||
|
update: function update() {
|
||||||
|
this.updateUrl();
|
||||||
|
this.updateInputBuffer();
|
||||||
|
this.updateProgress();
|
||||||
|
this.updateTabCount();
|
||||||
|
this.updateBufferPosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the URL displayed in the status line. Also displays status
|
||||||
|
* icons, [+-♥], when there are next and previous pages in the
|
||||||
|
* current tab's history, and when the current URL is bookmarked,
|
||||||
|
* respectively.
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to display.
|
||||||
|
* @default buffer.URL
|
||||||
|
*/
|
||||||
|
updateUrl: function updateUrl(url) {
|
||||||
|
// ripped from Firefox; modified
|
||||||
|
function losslessDecodeURI(url) {
|
||||||
|
// 1. decodeURI decodes %25 to %, which creates unintended
|
||||||
|
// encoding sequences.
|
||||||
|
url = url.split("%25").map(decodeURI).join("%25");
|
||||||
|
// 2. Re-encode whitespace so that it doesn't get eaten away
|
||||||
|
// by the location bar (bug 410726).
|
||||||
|
url = url.replace(/[\r\n\t]/g, encodeURIComponent);
|
||||||
|
|
||||||
|
// Encode invisible characters (soft hyphen, zero-width space, BOM,
|
||||||
|
// line and paragraph separator, word joiner, invisible times,
|
||||||
|
// invisible separator, object replacement character) (bug 452979)
|
||||||
|
url = url.replace(/[\v\x0c\x1c\x1d\x1e\x1f\u00ad\u200b\ufeff\u2028\u2029\u2060\u2062\u2063\ufffc]/g,
|
||||||
|
encodeURIComponent);
|
||||||
|
|
||||||
|
// Encode bidirectional formatting characters.
|
||||||
|
// (RFC 3987 sections 3.2 and 4.1 paragraph 6)
|
||||||
|
url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
|
||||||
|
encodeURIComponent);
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url == null)
|
||||||
|
// TODO: this probably needs a more general solution.
|
||||||
|
url = losslessDecodeURI(buffer.URL);
|
||||||
|
|
||||||
|
// make it even more Vim-like
|
||||||
|
if (url == "about:blank") {
|
||||||
|
if (!buffer.title)
|
||||||
|
url = "[No Name]";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url = url.replace(RegExp("^liberator://help/(\\S+)#(.*)"), function (m, n1, n2) n1 + " " + decodeURIComponent(n2) + " [Help]")
|
||||||
|
.replace(RegExp("^liberator://help/(\\S+)"), "$1 [Help]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// when session information is available, add [+] when we can go
|
||||||
|
// backwards, [-] when we can go forwards
|
||||||
|
let modified = "";
|
||||||
|
if (window.getWebNavigation) {
|
||||||
|
let sh = window.getWebNavigation().sessionHistory;
|
||||||
|
if (sh && sh.index > 0)
|
||||||
|
modified += "+";
|
||||||
|
if (sh && sh.index < sh.count -1)
|
||||||
|
modified += "-";
|
||||||
|
}
|
||||||
|
if (modules.bookmarks) {
|
||||||
|
if (bookmarks.isBookmarked(buffer.URL))
|
||||||
|
modified += "\u2764"; // a heart symbol: ❤
|
||||||
|
//modified += "\u2665"; // a heart symbol: ♥
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified)
|
||||||
|
url += " [" + modified + "]";
|
||||||
|
|
||||||
|
this._urlWidget.value = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the contents of the status line's input buffer to the given
|
||||||
|
* string. Used primarily when a key press requires further input
|
||||||
|
* before being processed, including mapping counts and arguments,
|
||||||
|
* along with multi-key mappings.
|
||||||
|
*
|
||||||
|
* @param {string} buffer
|
||||||
|
*/
|
||||||
|
updateInputBuffer: function updateInputBuffer(buffer) {
|
||||||
|
if (!buffer || typeof buffer != "string")
|
||||||
|
buffer = "";
|
||||||
|
|
||||||
|
this._inputBufferWidget.value = buffer;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the page load progress bar.
|
||||||
|
*
|
||||||
|
* @param {string|number} progress The current progress, as follows:
|
||||||
|
* A string - Displayed literally.
|
||||||
|
* A ratio 0 < n < 1 - Displayed as a progress bar.
|
||||||
|
* A number n <= 0 - Displayed as a "Loading" message.
|
||||||
|
* Any other number - The progress is cleared.
|
||||||
|
*/
|
||||||
|
updateProgress: function updateProgress(progress) {
|
||||||
|
if (!progress)
|
||||||
|
progress = "";
|
||||||
|
|
||||||
|
if (typeof progress == "string")
|
||||||
|
this._progressWidget.value = progress;
|
||||||
|
else if (typeof progress == "number") {
|
||||||
|
let progressStr = "";
|
||||||
|
if (progress <= 0)
|
||||||
|
progressStr = "[ Loading... ]";
|
||||||
|
else if (progress < 1) {
|
||||||
|
progress = Math.floor(progress * 20);
|
||||||
|
progressStr = "["
|
||||||
|
+ "====================".substr(0, progress)
|
||||||
|
+ ">"
|
||||||
|
+ " ".substr(0, 19 - progress)
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
this._progressWidget.value = progressStr;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the correct tabcount (e.g., [1/5]) on the status bar.
|
||||||
|
*
|
||||||
|
* @param {bool} delayed When true, update count after a
|
||||||
|
* brief timeout. Useful in the many cases when an
|
||||||
|
* event that triggers an update is broadcast before
|
||||||
|
* the tab state is fully updated.
|
||||||
|
*/
|
||||||
|
updateTabCount: function updateTabCount(delayed) {
|
||||||
|
if (liberator.has("tabs")) {
|
||||||
|
if (delayed)
|
||||||
|
return void setTimeout(function () statusline.updateTabCount(false), 0);
|
||||||
|
|
||||||
|
// update the ordinal which is used for numbered tabs
|
||||||
|
if (options.get("guioptions").has("n", "N"))
|
||||||
|
for (let [i, tab] in util.Array.iteritems(getBrowser().mTabs))
|
||||||
|
tab.setAttribute("ordinal", i + 1);
|
||||||
|
|
||||||
|
this._tabCountWidget.value = "[" + (tabs.index() + 1) + "/" + tabs.count + "]";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the main content's vertical scroll position in the status
|
||||||
|
* bar.
|
||||||
|
*
|
||||||
|
* @param {number} percent The position, as a percentage. @optional
|
||||||
|
*/
|
||||||
|
updateBufferPosition: function updateBufferPosition(percent) {
|
||||||
|
if (!percent || typeof percent != "number") {
|
||||||
|
let win = document.commandDispatcher.focusedWindow;
|
||||||
|
if (!win)
|
||||||
|
return;
|
||||||
|
percent = win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bufferPositionStr = "";
|
||||||
|
percent = Math.round(percent * 100);
|
||||||
|
if (percent < 0)
|
||||||
|
bufferPositionStr = "All";
|
||||||
|
else if (percent == 0)
|
||||||
|
bufferPositionStr = "Top";
|
||||||
|
else if (percent < 10)
|
||||||
|
bufferPositionStr = " " + percent + "%";
|
||||||
|
else if (percent >= 100)
|
||||||
|
bufferPositionStr = "Bot";
|
||||||
|
else
|
||||||
|
bufferPositionStr = percent + "%";
|
||||||
|
|
||||||
|
this._bufferPositionWidget.value = bufferPositionStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
options: function () {
|
||||||
|
options.add(["laststatus", "ls"],
|
||||||
|
"Show the status line",
|
||||||
|
"number", 2,
|
||||||
|
{
|
||||||
|
setter: function setter(value) {
|
||||||
|
if (value == 0)
|
||||||
|
document.getElementById("status-bar").collapsed = true;
|
||||||
|
else if (value == 1)
|
||||||
|
liberator.echoerr("show status line only with > 1 window not implemented yet");
|
||||||
|
else
|
||||||
|
document.getElementById("status-bar").collapsed = false;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
completer: function completer(context) [
|
||||||
|
["0", "Never display status line"],
|
||||||
|
["1", "Display status line only if there are multiple windows"],
|
||||||
|
["2", "Always display status line"]
|
||||||
|
],
|
||||||
|
validator: Option.validateCompleter
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
@@ -231,14 +231,13 @@ Highlights.prototype.CSS = <![CDATA[
|
|||||||
*
|
*
|
||||||
* @author Kris Maglione <maglione.k@gmail.com>
|
* @author Kris Maglione <maglione.k@gmail.com>
|
||||||
*/
|
*/
|
||||||
function Highlights(name, store)
|
function Highlights(name, store) {
|
||||||
{
|
|
||||||
let self = this;
|
let self = this;
|
||||||
let highlight = {};
|
let highlight = {};
|
||||||
let styles = storage.styles;
|
let styles = storage.styles;
|
||||||
|
|
||||||
const Highlight = Struct("class", "selector", "filter", "default", "value", "base");
|
const Highlight = Struct("class", "selector", "filter", "default", "value", "base");
|
||||||
Highlight.defaultValue("filter", function ()
|
Highlight.defaultValue("filter", function ()
|
||||||
this.base ? this.base.filter :
|
this.base ? this.base.filter :
|
||||||
["chrome://liberator/*",
|
["chrome://liberator/*",
|
||||||
"liberator:*",
|
"liberator:*",
|
||||||
@@ -256,8 +255,7 @@ function Highlights(name, store)
|
|||||||
this.__iterator__ = function () (highlight[v] for ([k, v] in Iterator(keys())));
|
this.__iterator__ = function () (highlight[v] for ([k, v] in Iterator(keys())));
|
||||||
|
|
||||||
this.get = function (k) highlight[k];
|
this.get = function (k) highlight[k];
|
||||||
this.set = function (key, newStyle, force, append)
|
this.set = function (key, newStyle, force, append) {
|
||||||
{
|
|
||||||
let [, class, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
|
let [, class, selectors] = key.match(/^([a-zA-Z_-]+)(.*)/);
|
||||||
|
|
||||||
if (!(class in highlight))
|
if (!(class in highlight))
|
||||||
@@ -270,10 +268,8 @@ function Highlights(name, store)
|
|||||||
newStyle = (style.value || "").replace(/;?\s*$/, "; " + newStyle);
|
newStyle = (style.value || "").replace(/;?\s*$/, "; " + newStyle);
|
||||||
if (/^\s*$/.test(newStyle))
|
if (/^\s*$/.test(newStyle))
|
||||||
newStyle = null;
|
newStyle = null;
|
||||||
if (newStyle == null)
|
if (newStyle == null) {
|
||||||
{
|
if (style.default == null) {
|
||||||
if (style.default == null)
|
|
||||||
{
|
|
||||||
delete highlight[style.class];
|
delete highlight[style.class];
|
||||||
styles.removeSheet(true, style.selector);
|
styles.removeSheet(true, style.selector);
|
||||||
return null;
|
return null;
|
||||||
@@ -284,8 +280,7 @@ function Highlights(name, store)
|
|||||||
|
|
||||||
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
|
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
|
||||||
.replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
|
.replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
|
||||||
if (!/^\s*(?:!\s*important\s*)?;*\s*$/.test(css))
|
if (!/^\s*(?:!\s*important\s*)?;*\s*$/.test(css)) {
|
||||||
{
|
|
||||||
css = style.selector + " { " + css + " }";
|
css = style.selector + " { " + css + " }";
|
||||||
|
|
||||||
let error = styles.addSheet(true, "highlight:" + style.class, style.filter, css, true);
|
let error = styles.addSheet(true, "highlight:" + style.class, style.filter, css, true);
|
||||||
@@ -301,8 +296,7 @@ function Highlights(name, store)
|
|||||||
*
|
*
|
||||||
* @param {string} class
|
* @param {string} class
|
||||||
*/
|
*/
|
||||||
this.selector = function (class)
|
this.selector = function (class) {
|
||||||
{
|
|
||||||
let [, hl, rest] = class.match(/^(\w*)(.*)/);
|
let [, hl, rest] = class.match(/^(\w*)(.*)/);
|
||||||
let pattern = "[liberator|highlight~=" + hl + "]"
|
let pattern = "[liberator|highlight~=" + hl + "]"
|
||||||
if (highlight[hl] && highlight[hl].class != class)
|
if (highlight[hl] && highlight[hl].class != class)
|
||||||
@@ -314,8 +308,7 @@ function Highlights(name, store)
|
|||||||
* Clears all highlighting rules. Rules with default values are
|
* Clears all highlighting rules. Rules with default values are
|
||||||
* reset.
|
* reset.
|
||||||
*/
|
*/
|
||||||
this.clear = function ()
|
this.clear = function () {
|
||||||
{
|
|
||||||
for (let [k, v] in Iterator(highlight))
|
for (let [k, v] in Iterator(highlight))
|
||||||
this.set(k, null, true);
|
this.set(k, null, true);
|
||||||
};
|
};
|
||||||
@@ -325,8 +318,7 @@ function Highlights(name, store)
|
|||||||
*
|
*
|
||||||
* @param {string} css The rules to load. See {@link Highlights#css}.
|
* @param {string} css The rules to load. See {@link Highlights#css}.
|
||||||
*/
|
*/
|
||||||
this.loadCSS = function (css)
|
this.loadCSS = function (css) {
|
||||||
{
|
|
||||||
css.replace(/^(\s*\S*\s+)\{((?:.|\n)*?)\}\s*$/gm, function (_, _1, _2) _1 + " " + _2.replace(/\n\s*/g, " "))
|
css.replace(/^(\s*\S*\s+)\{((?:.|\n)*?)\}\s*$/gm, function (_, _1, _2) _1 + " " + _2.replace(/\n\s*/g, " "))
|
||||||
.split("\n").filter(function (s) /\S/.test(s))
|
.split("\n").filter(function (s) /\S/.test(s))
|
||||||
.forEach(function (style)
|
.forEach(function (style)
|
||||||
@@ -340,8 +332,7 @@ function Highlights(name, store)
|
|||||||
if (old && old.value != old.default)
|
if (old && old.value != old.default)
|
||||||
style.value = old.value;
|
style.value = old.value;
|
||||||
});
|
});
|
||||||
for (let [class, hl] in Iterator(highlight))
|
for (let [class, hl] in Iterator(highlight)) {
|
||||||
{
|
|
||||||
if (hl.value == hl.default)
|
if (hl.value == hl.default)
|
||||||
this.set(class);
|
this.set(class);
|
||||||
}
|
}
|
||||||
@@ -356,8 +347,7 @@ function Highlights(name, store)
|
|||||||
*
|
*
|
||||||
* @author Kris Maglione <maglione.k@gmail.com>
|
* @author Kris Maglione <maglione.k@gmail.com>
|
||||||
*/
|
*/
|
||||||
function Styles(name, store)
|
function Styles(name, store) {
|
||||||
{
|
|
||||||
// Can't reference liberator or Components inside Styles --
|
// Can't reference liberator or Components inside Styles --
|
||||||
// they're members of the window object, which disappear
|
// they're members of the window object, which disappear
|
||||||
// with this window.
|
// with this window.
|
||||||
@@ -387,14 +377,12 @@ function Styles(name, store)
|
|||||||
Sheet.prototype.__defineGetter__("enabled", function () this._enabled);
|
Sheet.prototype.__defineGetter__("enabled", function () this._enabled);
|
||||||
Sheet.prototype.__defineSetter__("enabled", function (on) {
|
Sheet.prototype.__defineSetter__("enabled", function (on) {
|
||||||
this._enabled = Boolean(on);
|
this._enabled = Boolean(on);
|
||||||
if (on)
|
if (on) {
|
||||||
{
|
|
||||||
self.registerSheet(cssUri(this.fullCSS));
|
self.registerSheet(cssUri(this.fullCSS));
|
||||||
if (this.agent)
|
if (this.agent)
|
||||||
self.registerSheet(cssUri(this.fullCSS), true);
|
self.registerSheet(cssUri(this.fullCSS), true);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
self.unregisterSheet(cssUri(this.fullCSS));
|
self.unregisterSheet(cssUri(this.fullCSS));
|
||||||
self.unregisterSheet(cssUri(this.fullCSS), true);
|
self.unregisterSheet(cssUri(this.fullCSS), true);
|
||||||
}
|
}
|
||||||
@@ -428,8 +416,7 @@ function Styles(name, store)
|
|||||||
* "*" is matched as a prefix.
|
* "*" is matched as a prefix.
|
||||||
* @param {string} css The CSS to be applied.
|
* @param {string} css The CSS to be applied.
|
||||||
*/
|
*/
|
||||||
this.addSheet = function (system, name, filter, css, agent)
|
this.addSheet = function (system, name, filter, css, agent) {
|
||||||
{
|
|
||||||
let sheets = system ? systemSheets : userSheets;
|
let sheets = system ? systemSheets : userSheets;
|
||||||
let names = system ? systemNames : userNames;
|
let names = system ? systemNames : userNames;
|
||||||
if (name && name in names)
|
if (name && name in names)
|
||||||
@@ -437,12 +424,10 @@ function Styles(name, store)
|
|||||||
|
|
||||||
let sheet = Sheet(name, id++, filter.split(",").filter(util.identity), String(css), null, system, agent);
|
let sheet = Sheet(name, id++, filter.split(",").filter(util.identity), String(css), null, system, agent);
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
sheet.enabled = true;
|
sheet.enabled = true;
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
return e.echoerr || e;
|
return e.echoerr || e;
|
||||||
}
|
}
|
||||||
sheets.push(sheet);
|
sheets.push(sheet);
|
||||||
@@ -459,8 +444,7 @@ function Styles(name, store)
|
|||||||
* @param {string or number} sheet The sheet to retrieve. Strings indicate
|
* @param {string or number} sheet The sheet to retrieve. Strings indicate
|
||||||
* sheet names, while numbers indicate indices.
|
* sheet names, while numbers indicate indices.
|
||||||
*/
|
*/
|
||||||
this.get = function get(system, sheet)
|
this.get = function get(system, sheet) {
|
||||||
{
|
|
||||||
let sheets = system ? systemSheets : userSheets;
|
let sheets = system ? systemSheets : userSheets;
|
||||||
let names = system ? systemNames : userNames;
|
let names = system ? systemNames : userNames;
|
||||||
if (typeof sheet === "number")
|
if (typeof sheet === "number")
|
||||||
@@ -478,8 +462,7 @@ function Styles(name, store)
|
|||||||
* @param {string} css
|
* @param {string} css
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
*/
|
*/
|
||||||
this.findSheets = function (system, name, filter, css, index)
|
this.findSheets = function (system, name, filter, css, index) {
|
||||||
{
|
|
||||||
let sheets = system ? systemSheets : userSheets;
|
let sheets = system ? systemSheets : userSheets;
|
||||||
let names = system ? systemNames : userNames;
|
let names = system ? systemNames : userNames;
|
||||||
|
|
||||||
@@ -508,11 +491,9 @@ function Styles(name, store)
|
|||||||
* @param {string} css
|
* @param {string} css
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
*/
|
*/
|
||||||
this.removeSheet = function (system, name, filter, css, index)
|
this.removeSheet = function (system, name, filter, css, index) {
|
||||||
{
|
|
||||||
let self = this;
|
let self = this;
|
||||||
if (arguments.length == 0)
|
if (arguments.length == 0) {
|
||||||
{
|
|
||||||
var matches = [system];
|
var matches = [system];
|
||||||
system = sheet.system;
|
system = sheet.system;
|
||||||
}
|
}
|
||||||
@@ -531,8 +512,7 @@ function Styles(name, store)
|
|||||||
if (matches.length == 0)
|
if (matches.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (let [, sheet] in Iterator(matches.reverse()))
|
for (let [, sheet] in Iterator(matches.reverse())) {
|
||||||
{
|
|
||||||
sheet.enabled = false;
|
sheet.enabled = false;
|
||||||
if (name)
|
if (name)
|
||||||
delete names[name];
|
delete names[name];
|
||||||
@@ -540,8 +520,7 @@ function Styles(name, store)
|
|||||||
sheets.splice(sheets.indexOf(sheet), 1);
|
sheets.splice(sheets.indexOf(sheet), 1);
|
||||||
|
|
||||||
/* Re-add if we're only changing the site filter. */
|
/* Re-add if we're only changing the site filter. */
|
||||||
if (filter)
|
if (filter) {
|
||||||
{
|
|
||||||
let sites = sheet.sites.filter(function (f) f != filter);
|
let sites = sheet.sites.filter(function (f) f != filter);
|
||||||
if (sites.length)
|
if (sites.length)
|
||||||
this.addSheet(system, name, sites.join(","), css, sheet.agent);
|
this.addSheet(system, name, sites.join(","), css, sheet.agent);
|
||||||
@@ -558,8 +537,7 @@ function Styles(name, store)
|
|||||||
* @param {boolean} reload Whether to reload any sheets that are
|
* @param {boolean} reload Whether to reload any sheets that are
|
||||||
* already registered.
|
* already registered.
|
||||||
*/
|
*/
|
||||||
this.registerSheet = function (uri, agent, reload)
|
this.registerSheet = function (uri, agent, reload) {
|
||||||
{
|
|
||||||
if (reload)
|
if (reload)
|
||||||
this.unregisterSheet(uri, agent);
|
this.unregisterSheet(uri, agent);
|
||||||
uri = ios.newURI(uri, null, null);
|
uri = ios.newURI(uri, null, null);
|
||||||
@@ -572,298 +550,279 @@ function Styles(name, store)
|
|||||||
*
|
*
|
||||||
* @param {string} uri The URI of the sheet to unregister.
|
* @param {string} uri The URI of the sheet to unregister.
|
||||||
*/
|
*/
|
||||||
this.unregisterSheet = function (uri, agent)
|
this.unregisterSheet = function (uri, agent) {
|
||||||
{
|
|
||||||
uri = ios.newURI(uri, null, null);
|
uri = ios.newURI(uri, null, null);
|
||||||
if (sss.sheetRegistered(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET))
|
if (sss.sheetRegistered(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET))
|
||||||
sss.unregisterSheet(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET);
|
sss.unregisterSheet(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let (array = util.Array)
|
|
||||||
{
|
Module("styles", {
|
||||||
util.extend(Styles.prototype, {
|
requires: ["liberator", "storage", "util"],
|
||||||
get sites() array([v.sites for ([k, v] in this.userSheets)]).flatten().uniq().__proto__,
|
|
||||||
completeSite: function (context, content)
|
init: function () {
|
||||||
{
|
let (array = util.Array) {
|
||||||
context.anchored = false;
|
update(Styles.prototype, {
|
||||||
try
|
get sites() array([v.sites for ([k, v] in this.userSheets)]).flatten().uniq().__proto__,
|
||||||
{
|
completeSite: function (context, content) {
|
||||||
context.fork("current", 0, this, function (context) {
|
context.anchored = false;
|
||||||
context.title = ["Current Site"];
|
try {
|
||||||
context.completions = [
|
context.fork("current", 0, this, function (context) {
|
||||||
[content.location.host, "Current Host"],
|
context.title = ["Current Site"];
|
||||||
[content.location.href, "Current URL"]
|
context.completions = [
|
||||||
];
|
[content.location.host, "Current Host"],
|
||||||
});
|
[content.location.href, "Current URL"]
|
||||||
}
|
];
|
||||||
catch (e) {}
|
});
|
||||||
context.fork("others", 0, this, function (context) {
|
}
|
||||||
context.title = ["Site"];
|
catch (e) {}
|
||||||
context.completions = [[s, ""] for ([, s] in Iterator(styles.sites))];
|
context.fork("others", 0, this, function (context) {
|
||||||
|
context.title = ["Site"];
|
||||||
|
context.completions = [[s, ""] for ([, s] in Iterator(styles.sites))];
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
return storage.newObject("styles", Styles, { store: false });
|
||||||
}
|
},
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["sty[le]"],
|
||||||
|
"Add or list user styles",
|
||||||
|
function (args) {
|
||||||
|
let [filter, css] = args;
|
||||||
|
let name = args["-name"];
|
||||||
|
|
||||||
/**
|
if (!css) {
|
||||||
* @property {Styles}
|
let list = Array.concat([i for (i in styles.userNames)],
|
||||||
*/
|
[i for (i in styles.userSheets) if (!i[1].name)]);
|
||||||
const styles = storage.newObject("styles", Styles, { store: false });
|
let str = template.tabular(["", "Name", "Filter", "CSS"],
|
||||||
|
["min-width: 1em; text-align: center; color: red; font-weight: bold;",
|
||||||
/**
|
"padding: 0 1em 0 1ex; vertical-align: top;",
|
||||||
* @property {Highlights}
|
"padding: 0 1em 0 0; vertical-align: top;"],
|
||||||
*/
|
([sheet.enabled ? "" : "\u00d7",
|
||||||
const highlight = storage.newObject("highlight", Highlights, { store: false });
|
key,
|
||||||
|
sheet.sites.join(","),
|
||||||
if (highlight.CSS != Highlights.prototype.CSS)
|
sheet.css]
|
||||||
{
|
for ([i, [key, sheet]] in Iterator(list))
|
||||||
highlight.CSS = Highlights.prototype.CSS;
|
if ((!filter || sheet.sites.indexOf(filter) >= 0) && (!name || sheet.name == name))));
|
||||||
highlight.loadCSS(highlight.CSS);
|
commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
liberator.triggerObserver("load_styles", "styles");
|
if ("-append" in args) {
|
||||||
liberator.triggerObserver("load_highlight", "highlight");
|
let sheet = styles.get(false, name);
|
||||||
|
if (sheet) {
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
filter = sheet.sites.concat(filter).join(",");
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
css = sheet.css + " " + css;
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
}
|
||||||
|
|
||||||
liberator.registerObserver("load_commands", function () {
|
|
||||||
|
|
||||||
commands.add(["colo[rscheme]"],
|
|
||||||
"Load a color scheme",
|
|
||||||
function (args)
|
|
||||||
{
|
|
||||||
let scheme = args[0];
|
|
||||||
|
|
||||||
if (scheme == "default")
|
|
||||||
highlight.clear();
|
|
||||||
else
|
|
||||||
liberator.assert(!io.sourceFromRuntimePath(["colors/" + scheme + ".vimp"]),
|
|
||||||
"E185: Cannot find color scheme " + scheme);
|
|
||||||
autocommands.trigger("ColorScheme", { name: scheme });
|
|
||||||
},
|
|
||||||
{
|
|
||||||
argCount: "1",
|
|
||||||
completer: function (context) completion.colorScheme(context)
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.add(["sty[le]"],
|
|
||||||
"Add or list user styles",
|
|
||||||
function (args)
|
|
||||||
{
|
|
||||||
let [filter, css] = args;
|
|
||||||
let name = args["-name"];
|
|
||||||
|
|
||||||
if (!css)
|
|
||||||
{
|
|
||||||
let list = Array.concat([i for (i in styles.userNames)],
|
|
||||||
[i for (i in styles.userSheets) if (!i[1].name)]);
|
|
||||||
let str = template.tabular(["", "Name", "Filter", "CSS"],
|
|
||||||
["min-width: 1em; text-align: center; color: red; font-weight: bold;",
|
|
||||||
"padding: 0 1em 0 1ex; vertical-align: top;",
|
|
||||||
"padding: 0 1em 0 0; vertical-align: top;"],
|
|
||||||
([sheet.enabled ? "" : "\u00d7",
|
|
||||||
key,
|
|
||||||
sheet.sites.join(","),
|
|
||||||
sheet.css]
|
|
||||||
for ([i, [key, sheet]] in Iterator(list))
|
|
||||||
if ((!filter || sheet.sites.indexOf(filter) >= 0) && (!name || sheet.name == name))));
|
|
||||||
commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ("-append" in args)
|
|
||||||
{
|
|
||||||
let sheet = styles.get(false, name);
|
|
||||||
if (sheet)
|
|
||||||
{
|
|
||||||
filter = sheet.sites.concat(filter).join(",");
|
|
||||||
css = sheet.css + " " + css;
|
|
||||||
}
|
}
|
||||||
}
|
let err = styles.addSheet(false, name, filter, css);
|
||||||
let err = styles.addSheet(false, name, filter, css);
|
if (err)
|
||||||
if (err)
|
liberator.echoerr(err);
|
||||||
liberator.echoerr(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
bang: true,
|
|
||||||
completer: function (context, args)
|
|
||||||
{
|
|
||||||
let compl = [];
|
|
||||||
if (args.completeArg == 0)
|
|
||||||
styles.completeSite(context, content);
|
|
||||||
else if (args.completeArg == 1)
|
|
||||||
{
|
|
||||||
let sheet = styles.get(false, args["-name"]);
|
|
||||||
if (sheet)
|
|
||||||
context.completions = [[sheet.css, "Current Value"]];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hereDoc: true,
|
{
|
||||||
literal: 1,
|
bang: true,
|
||||||
options: [[["-name", "-n"], commands.OPTION_STRING, null, function () [[k, v.css] for ([k, v] in Iterator(styles.userNames))]],
|
completer: function (context, args) {
|
||||||
[["-append", "-a"], commands.OPTION_NOARG]],
|
let compl = [];
|
||||||
serial: function () [
|
if (args.completeArg == 0)
|
||||||
{
|
styles.completeSite(context, content);
|
||||||
command: this.name,
|
else if (args.completeArg == 1) {
|
||||||
bang: true,
|
let sheet = styles.get(false, args["-name"]);
|
||||||
options: sty.name ? { "-name": sty.name } : {},
|
if (sheet)
|
||||||
arguments: [sty.sites.join(",")],
|
context.completions = [[sheet.css, "Current Value"]];
|
||||||
literalArg: sty.css
|
}
|
||||||
} for ([k, sty] in styles.userSheets)
|
},
|
||||||
]
|
hereDoc: true,
|
||||||
});
|
literal: 1,
|
||||||
|
options: [[["-name", "-n"], commands.OPTION_STRING, null, function () [[k, v.css] for ([k, v] in Iterator(styles.userNames))]],
|
||||||
|
[["-append", "-a"], commands.OPTION_NOARG]],
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: ["stylee[nable]", "stye[nable]"],
|
name: ["stylee[nable]", "stye[nable]"],
|
||||||
desc: "Enable a user style sheet",
|
desc: "Enable a user style sheet",
|
||||||
action: function (sheet) sheet.enabled = true,
|
action: function (sheet) sheet.enabled = true,
|
||||||
filter: function (sheet) !sheet.enabled
|
filter: function (sheet) !sheet.enabled
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["styled[isable]", "styd[isable]"],
|
name: ["styled[isable]", "styd[isable]"],
|
||||||
desc: "Disable a user style sheet",
|
desc: "Disable a user style sheet",
|
||||||
action: function (sheet) sheet.enabled = false,
|
action: function (sheet) sheet.enabled = false,
|
||||||
filter: function (sheet) sheet.enabled
|
filter: function (sheet) sheet.enabled
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["stylet[oggle]", "styt[oggle]"],
|
name: ["stylet[oggle]", "styt[oggle]"],
|
||||||
desc: "Toggle a user style sheet",
|
desc: "Toggle a user style sheet",
|
||||||
action: function (sheet) sheet.enabled = !sheet.enabled
|
action: function (sheet) sheet.enabled = !sheet.enabled
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["dels[tyle]"],
|
name: ["dels[tyle]"],
|
||||||
desc: "Remove a user style sheet",
|
desc: "Remove a user style sheet",
|
||||||
action: function (sheet) styles.removeSheet(sheet)
|
action: function (sheet) styles.removeSheet(sheet)
|
||||||
|
}
|
||||||
|
].forEach(function (cmd) {
|
||||||
|
commands.add(cmd.name, cmd.desc,
|
||||||
|
function (args) {
|
||||||
|
styles.findSheets(false, args["-name"], args[0], args.literalArg, args["-index"])
|
||||||
|
.forEach(cmd.action);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
completer: function (context) { context.completions = styles.sites.map(function (site) [site, ""]); },
|
||||||
|
literal: 1,
|
||||||
|
options: [[["-index", "-i"], commands.OPTION_INT, null,
|
||||||
|
function (context) {
|
||||||
|
context.compare = CompletionContext.Sort.number;
|
||||||
|
return [[i, <>{sheet.sites.join(",")}: {sheet.css.replace("\n", "\\n")}</>]
|
||||||
|
for ([i, sheet] in styles.userSheets)
|
||||||
|
if (!cmd.filter || cmd.filter(sheet))]
|
||||||
|
}],
|
||||||
|
[["-name", "-n"], commands.OPTION_STRING, null,
|
||||||
|
function () [[name, sheet.css]
|
||||||
|
for ([name, sheet] in Iterator(styles.userNames))
|
||||||
|
if (!cmd.filter || cmd.filter(sheet))]]]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
completion: function () {
|
||||||
|
completion.setFunctionCompleter(["get", "addSheet", "removeSheet", "findSheets"].map(function (m) styles[m]),
|
||||||
|
[ // Prototype: (system, name, filter, css, index)
|
||||||
|
null,
|
||||||
|
function (context, obj, args) args[0] ? styles.systemNames : styles.userNames,
|
||||||
|
function (context, obj, args) styles.completeSite(context, content),
|
||||||
|
null,
|
||||||
|
function (context, obj, args) args[0] ? styles.systemSheets : styles.userSheets
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Module("highlight", {
|
||||||
|
requires: ["styles"],
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
const self = storage.newObject("highlight", Highlights, { store: false });
|
||||||
|
|
||||||
|
if (self.CSS != Highlights.prototype.CSS) {
|
||||||
|
self.CSS = Highlights.prototype.CSS;
|
||||||
|
self.loadCSS(self.CSS);
|
||||||
}
|
}
|
||||||
].forEach(function (cmd) {
|
return self;
|
||||||
commands.add(cmd.name, cmd.desc,
|
},
|
||||||
function (args)
|
}, {
|
||||||
{
|
}, {
|
||||||
styles.findSheets(false, args["-name"], args[0], args.literalArg, args["-index"])
|
commands: function () {
|
||||||
.forEach(cmd.action);
|
commands.add(["colo[rscheme]"],
|
||||||
|
"Load a color scheme",
|
||||||
|
function (args) {
|
||||||
|
let scheme = args[0];
|
||||||
|
|
||||||
|
if (scheme == "default")
|
||||||
|
highlight.clear();
|
||||||
|
else
|
||||||
|
liberator.assert(!io.sourceFromRuntimePath(["colors/" + scheme + ".vimp"]),
|
||||||
|
"E185: Cannot find color scheme " + scheme);
|
||||||
|
autocommands.trigger("ColorScheme", { name: scheme });
|
||||||
},
|
},
|
||||||
{
|
|
||||||
completer: function (context) { context.completions = styles.sites.map(function (site) [site, ""]); },
|
|
||||||
literal: 1,
|
|
||||||
options: [[["-index", "-i"], commands.OPTION_INT, null,
|
|
||||||
function (context) {
|
|
||||||
context.compare = CompletionContext.Sort.number;
|
|
||||||
return [[i, <>{sheet.sites.join(",")}: {sheet.css.replace("\n", "\\n")}</>]
|
|
||||||
for ([i, sheet] in styles.userSheets)
|
|
||||||
if (!cmd.filter || cmd.filter(sheet))]
|
|
||||||
}],
|
|
||||||
[["-name", "-n"], commands.OPTION_STRING, null,
|
|
||||||
function () [[name, sheet.css]
|
|
||||||
for ([name, sheet] in Iterator(styles.userNames))
|
|
||||||
if (!cmd.filter || cmd.filter(sheet))]]]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.add(["hi[ghlight]"],
|
|
||||||
"Set the style of certain display elements",
|
|
||||||
function (args)
|
|
||||||
{
|
|
||||||
let style = <![CDATA[
|
|
||||||
;
|
|
||||||
display: inline-block !important;
|
|
||||||
position: static !important;
|
|
||||||
margin: 0px !important; padding: 0px !important;
|
|
||||||
width: 3em !important; min-width: 3em !important; max-width: 3em !important;
|
|
||||||
height: 1em !important; min-height: 1em !important; max-height: 1em !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
]]>;
|
|
||||||
let clear = args[0] == "clear";
|
|
||||||
if (clear)
|
|
||||||
args.shift();
|
|
||||||
|
|
||||||
let [key, css] = args;
|
|
||||||
if (clear && css)
|
|
||||||
return liberator.echo("E488: Trailing characters");
|
|
||||||
|
|
||||||
if (!css && !clear)
|
|
||||||
{
|
{
|
||||||
// List matching keys
|
argCount: "1",
|
||||||
let str = template.tabular(["Key", "Sample", "CSS"],
|
completer: function (context) completion.colorScheme(context)
|
||||||
["padding: 0 1em 0 0; vertical-align: top",
|
});
|
||||||
"text-align: center"],
|
|
||||||
([h.class,
|
|
||||||
<span style={"text-align: center; line-height: 1em;" + h.value + style}>XXX</span>,
|
|
||||||
template.highlightRegexp(h.value, /\b[-\w]+(?=:)/g)]
|
|
||||||
for (h in highlight)
|
|
||||||
if (!key || h.class.indexOf(key) > -1)));
|
|
||||||
commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!key && clear)
|
|
||||||
return highlight.clear();
|
|
||||||
let error = highlight.set(key, css, clear, "-append" in args);
|
|
||||||
if (error)
|
|
||||||
liberator.echoerr(error);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// TODO: add this as a standard highlight completion function?
|
|
||||||
completer: function (context, args)
|
|
||||||
{
|
|
||||||
// Complete a highlight group on :hi clear ...
|
|
||||||
if (args.completeArg > 0 && args[0] == "clear")
|
|
||||||
args.completeArg = args.completeArg > 1 ? -1 : 0;
|
|
||||||
|
|
||||||
if (args.completeArg == 0)
|
commands.add(["hi[ghlight]"],
|
||||||
context.completions = [[v.class, v.value] for (v in highlight)];
|
"Set the style of certain display elements",
|
||||||
else if (args.completeArg == 1)
|
function (args) {
|
||||||
{
|
let style = <![CDATA[
|
||||||
let hl = highlight.get(args[0]);
|
;
|
||||||
if (hl)
|
display: inline-block !important;
|
||||||
context.completions = [[hl.value, "Current Value"], [hl.default || "", "Default Value"]];
|
position: static !important;
|
||||||
|
margin: 0px !important; padding: 0px !important;
|
||||||
|
width: 3em !important; min-width: 3em !important; max-width: 3em !important;
|
||||||
|
height: 1em !important; min-height: 1em !important; max-height: 1em !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
]]>;
|
||||||
|
let clear = args[0] == "clear";
|
||||||
|
if (clear)
|
||||||
|
args.shift();
|
||||||
|
|
||||||
|
let [key, css] = args;
|
||||||
|
if (clear && css)
|
||||||
|
return liberator.echo("E488: Trailing characters");
|
||||||
|
|
||||||
|
if (!css && !clear) {
|
||||||
|
// List matching keys
|
||||||
|
let str = template.tabular(["Key", "Sample", "CSS"],
|
||||||
|
["padding: 0 1em 0 0; vertical-align: top",
|
||||||
|
"text-align: center"],
|
||||||
|
([h.class,
|
||||||
|
<span style={"text-align: center; line-height: 1em;" + h.value + style}>XXX</span>,
|
||||||
|
template.highlightRegexp(h.value, /\b[-\w]+(?=:)/g)]
|
||||||
|
for (h in highlight)
|
||||||
|
if (!key || h.class.indexOf(key) > -1)));
|
||||||
|
commandline.echo(str, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (!key && clear)
|
||||||
|
return highlight.clear();
|
||||||
|
let error = highlight.set(key, css, clear, "-append" in args);
|
||||||
|
if (error)
|
||||||
|
liberator.echoerr(error);
|
||||||
},
|
},
|
||||||
hereDoc: true,
|
{
|
||||||
literal: 1,
|
// TODO: add this as a standard highlight completion function?
|
||||||
options: [[["-append", "-a"], commands.OPTION_NOARG]],
|
completer: function (context, args) {
|
||||||
serial: function () [
|
// Complete a highlight group on :hi clear ...
|
||||||
{
|
if (args.completeArg > 0 && args[0] == "clear")
|
||||||
command: this.name,
|
args.completeArg = args.completeArg > 1 ? -1 : 0;
|
||||||
arguments: [k],
|
|
||||||
literalArg: v
|
if (args.completeArg == 0)
|
||||||
}
|
context.completions = [[v.class, v.value] for (v in highlight)];
|
||||||
for ([k, v] in Iterator(highlight))
|
else if (args.completeArg == 1) {
|
||||||
if (v.value != v.default)
|
let hl = highlight.get(args[0]);
|
||||||
]
|
if (hl)
|
||||||
});
|
context.completions = [[hl.value, "Current Value"], [hl.default || "", "Default Value"]];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hereDoc: true,
|
||||||
|
literal: 1,
|
||||||
|
options: [[["-append", "-a"], commands.OPTION_NOARG]],
|
||||||
|
serial: function () [
|
||||||
|
{
|
||||||
|
command: this.name,
|
||||||
|
arguments: [k],
|
||||||
|
literalArg: v
|
||||||
|
}
|
||||||
|
for ([k, v] in Iterator(highlight))
|
||||||
|
if (v.value != v.default)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
completion: function () {
|
||||||
|
completion.colorScheme = function colorScheme(context) {
|
||||||
|
context.title = ["Color Scheme", "Runtime Path"];
|
||||||
|
context.keys = { text: function (f) f.leafName.replace(/\.vimp$/, ""), description: ".parent.path" };
|
||||||
|
context.completions = util.Array.flatten(
|
||||||
|
io.getRuntimeDirectories("colors").map(
|
||||||
|
function (dir) dir.readDirectory().filter(
|
||||||
|
function (file) /\.vimp$/.test(file.leafName))))
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
completion.highlightGroup = function highlightGroup(context) {
|
||||||
|
context.title = ["Highlight Group", "Value"];
|
||||||
|
context.completions = [[v.class, v.value] for (v in highlight)];
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMPLETIONS /////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
liberator.registerObserver("load_completion", function () {
|
|
||||||
completion.setFunctionCompleter(["get", "addSheet", "removeSheet", "findSheets"].map(function (m) styles[m]),
|
|
||||||
[ // Prototype: (system, name, filter, css, index)
|
|
||||||
null,
|
|
||||||
function (context, obj, args) args[0] ? styles.systemNames : styles.userNames,
|
|
||||||
function (context, obj, args) styles.completeSite(context, content),
|
|
||||||
null,
|
|
||||||
function (context, obj, args) args[0] ? styles.systemSheets : styles.userSheets
|
|
||||||
]);
|
|
||||||
|
|
||||||
completion.colorScheme = function colorScheme(context) {
|
|
||||||
context.title = ["Color Scheme", "Runtime Path"];
|
|
||||||
context.keys = { text: function (f) f.leafName.replace(/\.vimp$/, ""), description: ".parent.path" };
|
|
||||||
context.completions = util.Array.flatten(
|
|
||||||
io.getRuntimeDirectories("colors").map(
|
|
||||||
function (dir) dir.readDirectory().filter(
|
|
||||||
function (file) /\.vimp$/.test(file.leafName))))
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
completion.highlightGroup = function highlightGroup(context) {
|
|
||||||
context.title = ["Highlight Group", "Value"];
|
|
||||||
context.completions = [[v.class, v.value] for (v in highlight)];
|
|
||||||
};
|
|
||||||
});
|
|
||||||
//}}}
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,18 +6,16 @@
|
|||||||
|
|
||||||
/** @scope modules */
|
/** @scope modules */
|
||||||
|
|
||||||
const template = { //{{{
|
const Template = Module("template", {
|
||||||
add: function add(a, b) a + b,
|
add: function add(a, b) a + b,
|
||||||
join: function join(c) function (a, b) a + c + b,
|
join: function join(c) function (a, b) a + c + b,
|
||||||
|
|
||||||
map: function map(iter, func, sep, interruptable)
|
map: function map(iter, func, sep, interruptable) {
|
||||||
{
|
|
||||||
if (iter.length) // FIXME: Kludge?
|
if (iter.length) // FIXME: Kludge?
|
||||||
iter = util.Array.itervalues(iter);
|
iter = util.Array.itervalues(iter);
|
||||||
let ret = <></>;
|
let ret = <></>;
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for each (let i in Iterator(iter))
|
for each (let i in Iterator(iter)) {
|
||||||
{
|
|
||||||
let val = func(i);
|
let val = func(i);
|
||||||
if (val == undefined)
|
if (val == undefined)
|
||||||
continue;
|
continue;
|
||||||
@@ -30,30 +28,25 @@ const template = { //{{{
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
maybeXML: function maybeXML(xml)
|
maybeXML: function maybeXML(xml) {
|
||||||
{
|
|
||||||
if (typeof xml == "xml")
|
if (typeof xml == "xml")
|
||||||
return xml;
|
return xml;
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
return new XMLList(xml);
|
return new XMLList(xml);
|
||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
return <>{xml}</>;
|
return <>{xml}</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
completionRow: function completionRow(item, highlightGroup)
|
completionRow: function completionRow(item, highlightGroup) {
|
||||||
{
|
|
||||||
if (typeof icon == "function")
|
if (typeof icon == "function")
|
||||||
icon = icon();
|
icon = icon();
|
||||||
|
|
||||||
if (highlightGroup)
|
if (highlightGroup) {
|
||||||
{
|
|
||||||
var text = item[0] || "";
|
var text = item[0] || "";
|
||||||
var desc = item[1] || "";
|
var desc = item[1] || "";
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
var text = this.process[0].call(this, item, item.text);
|
var text = this.process[0].call(this, item, item.text);
|
||||||
var desc = this.process[1].call(this, item, item.description);
|
var desc = this.process[1].call(this, item, item.description);
|
||||||
}
|
}
|
||||||
@@ -85,8 +78,7 @@ const template = { //{{{
|
|||||||
}
|
}
|
||||||
</>,
|
</>,
|
||||||
|
|
||||||
icon: function (item, text)
|
icon: function (item, text) {
|
||||||
{
|
|
||||||
return <><span highlight="CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</span><span class="td-strut"/>{text}</>
|
return <><span highlight="CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</span><span class="td-strut"/>{text}</>
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -108,79 +100,69 @@ const template = { //{{{
|
|||||||
|
|
||||||
// if "processStrings" is true, any passed strings will be surrounded by " and
|
// if "processStrings" is true, any passed strings will be surrounded by " and
|
||||||
// any line breaks are displayed as \n
|
// any line breaks are displayed as \n
|
||||||
highlight: function highlight(arg, processStrings, clip)
|
highlight: function highlight(arg, processStrings, clip) {
|
||||||
{
|
|
||||||
// some objects like window.JSON or getBrowsers()._browsers need the try/catch
|
// some objects like window.JSON or getBrowsers()._browsers need the try/catch
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
let str = clip ? util.clip(String(arg), clip) : String(arg);
|
let str = clip ? util.clip(String(arg), clip) : String(arg);
|
||||||
switch (arg == null ? "undefined" : typeof arg)
|
switch (arg == null ? "undefined" : typeof arg) {
|
||||||
{
|
case "number":
|
||||||
case "number":
|
return <span highlight="Number">{str}</span>;
|
||||||
return <span highlight="Number">{str}</span>;
|
case "string":
|
||||||
case "string":
|
if (processStrings)
|
||||||
if (processStrings)
|
str = str.quote();
|
||||||
str = str.quote();
|
return <span highlight="String">{str}</span>;
|
||||||
return <span highlight="String">{str}</span>;
|
case "boolean":
|
||||||
case "boolean":
|
return <span highlight="Boolean">{str}</span>;
|
||||||
return <span highlight="Boolean">{str}</span>;
|
case "function":
|
||||||
case "function":
|
// Vim generally doesn't like /foo*/, because */ looks like a comment terminator.
|
||||||
// Vim generally doesn't like /foo*/, because */ looks like a comment terminator.
|
// Using /foo*(:?)/ instead.
|
||||||
// Using /foo*(:?)/ instead.
|
if (processStrings)
|
||||||
if (processStrings)
|
return <span highlight="Function">{str.replace(/\{(.|\n)*(?:)/g, "{ ... }")}</span>;
|
||||||
return <span highlight="Function">{str.replace(/\{(.|\n)*(?:)/g, "{ ... }")}</span>;
|
return <>{arg}</>;
|
||||||
return <>{arg}</>;
|
case "undefined":
|
||||||
case "undefined":
|
return <span highlight="Null">{arg}</span>;
|
||||||
return <span highlight="Null">{arg}</span>;
|
case "object":
|
||||||
case "object":
|
// for java packages value.toString() would crash so badly
|
||||||
// for java packages value.toString() would crash so badly
|
// that we cannot even try/catch it
|
||||||
// that we cannot even try/catch it
|
if (/^\[JavaPackage.*\]$/.test(arg))
|
||||||
if (/^\[JavaPackage.*\]$/.test(arg))
|
return <>[JavaPackage]</>;
|
||||||
return <>[JavaPackage]</>;
|
if (processStrings && false)
|
||||||
if (processStrings && false)
|
str = template.highlightFilter(str, "\n", function () <span highlight="NonText">^J</span>);
|
||||||
str = template.highlightFilter(str, "\n", function () <span highlight="NonText">^J</span>);
|
return <span highlight="Object">{str}</span>;
|
||||||
return <span highlight="Object">{str}</span>;
|
case "xml":
|
||||||
case "xml":
|
return arg;
|
||||||
return arg;
|
default:
|
||||||
default:
|
return <![CDATA[<unknown type>]]>;
|
||||||
return <![CDATA[<unknown type>]]>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
return<![CDATA[<unknown>]]>;
|
return<![CDATA[<unknown>]]>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
highlightFilter: function highlightFilter(str, filter, highlight)
|
highlightFilter: function highlightFilter(str, filter, highlight) {
|
||||||
{
|
return this.highlightSubstrings(str, (function () {
|
||||||
return this.highlightSubstrings(str, (function ()
|
|
||||||
{
|
|
||||||
if (filter.length == 0)
|
if (filter.length == 0)
|
||||||
return;
|
return;
|
||||||
let lcstr = String.toLowerCase(str);
|
let lcstr = String.toLowerCase(str);
|
||||||
let lcfilter = filter.toLowerCase();
|
let lcfilter = filter.toLowerCase();
|
||||||
let start = 0;
|
let start = 0;
|
||||||
while ((start = lcstr.indexOf(lcfilter, start)) > -1)
|
while ((start = lcstr.indexOf(lcfilter, start)) > -1) {
|
||||||
{
|
|
||||||
yield [start, filter.length];
|
yield [start, filter.length];
|
||||||
start += filter.length;
|
start += filter.length;
|
||||||
}
|
}
|
||||||
})(), highlight || template.filter);
|
})(), highlight || template.filter);
|
||||||
},
|
},
|
||||||
|
|
||||||
highlightRegexp: function highlightRegexp(str, re, highlight)
|
highlightRegexp: function highlightRegexp(str, re, highlight) {
|
||||||
{
|
return this.highlightSubstrings(str, (function () {
|
||||||
return this.highlightSubstrings(str, (function ()
|
|
||||||
{
|
|
||||||
let res;
|
let res;
|
||||||
while ((res = re.exec(str)) && res[0].length)
|
while ((res = re.exec(str)) && res[0].length)
|
||||||
yield [res.index, res[0].length];
|
yield [res.index, res[0].length];
|
||||||
})(), highlight || template.filter);
|
})(), highlight || template.filter);
|
||||||
},
|
},
|
||||||
|
|
||||||
highlightSubstrings: function highlightSubstrings(str, iter, highlight)
|
highlightSubstrings: function highlightSubstrings(str, iter, highlight) {
|
||||||
{
|
|
||||||
if (typeof str == "xml")
|
if (typeof str == "xml")
|
||||||
return str;
|
return str;
|
||||||
if (str == "")
|
if (str == "")
|
||||||
@@ -190,8 +172,7 @@ const template = { //{{{
|
|||||||
let s = <></>;
|
let s = <></>;
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for (let [i, length] in iter)
|
for (let [i, length] in iter) {
|
||||||
{
|
|
||||||
if (n++ > 50) // Prevent infinite loops.
|
if (n++ > 50) // Prevent infinite loops.
|
||||||
return s + <>{str.substr(start)}</>;
|
return s + <>{str.substr(start)}</>;
|
||||||
XML.ignoreWhitespace = false;
|
XML.ignoreWhitespace = false;
|
||||||
@@ -202,23 +183,20 @@ const template = { //{{{
|
|||||||
return s + <>{str.substr(start)}</>;
|
return s + <>{str.substr(start)}</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
highlightURL: function highlightURL(str, force)
|
highlightURL: function highlightURL(str, force) {
|
||||||
{
|
|
||||||
if (force || /^[a-zA-Z]+:\/\//.test(str))
|
if (force || /^[a-zA-Z]+:\/\//.test(str))
|
||||||
return <a highlight="URL" href={str}>{str}</a>;
|
return <a highlight="URL" href={str}>{str}</a>;
|
||||||
else
|
else
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
commandOutput: function generic(xml)
|
commandOutput: function generic(xml) {
|
||||||
{
|
|
||||||
return <>:{commandline.command}<br/>{xml}</>;
|
return <>:{commandline.command}<br/>{xml}</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
// every item must have a .xml property which defines how to draw itself
|
// every item must have a .xml property which defines how to draw itself
|
||||||
// @param headers is an array of strings, the text for the header columns
|
// @param headers is an array of strings, the text for the header columns
|
||||||
genericTable: function genericTable(items, format)
|
genericTable: function genericTable(items, format) {
|
||||||
{
|
|
||||||
completion.listCompleter(function (context) {
|
completion.listCompleter(function (context) {
|
||||||
context.filterFunc = null;
|
context.filterFunc = null;
|
||||||
if (format)
|
if (format)
|
||||||
@@ -227,8 +205,7 @@ const template = { //{{{
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
jumps: function jumps(index, elems)
|
jumps: function jumps(index, elems) {
|
||||||
{
|
|
||||||
// <e4x>
|
// <e4x>
|
||||||
return this.commandOutput(
|
return this.commandOutput(
|
||||||
<table>
|
<table>
|
||||||
@@ -248,8 +225,7 @@ const template = { //{{{
|
|||||||
// </e4x>
|
// </e4x>
|
||||||
},
|
},
|
||||||
|
|
||||||
options: function options(title, opts)
|
options: function options(title, opts) {
|
||||||
{
|
|
||||||
// <e4x>
|
// <e4x>
|
||||||
return this.commandOutput(
|
return this.commandOutput(
|
||||||
<table>
|
<table>
|
||||||
@@ -269,8 +245,7 @@ const template = { //{{{
|
|||||||
// </e4x>
|
// </e4x>
|
||||||
},
|
},
|
||||||
|
|
||||||
table: function table(title, data, indent)
|
table: function table(title, data, indent) {
|
||||||
{
|
|
||||||
let table =
|
let table =
|
||||||
// <e4x>
|
// <e4x>
|
||||||
<table>
|
<table>
|
||||||
@@ -290,8 +265,7 @@ const template = { //{{{
|
|||||||
return table;
|
return table;
|
||||||
},
|
},
|
||||||
|
|
||||||
tabular: function tabular(headings, style, iter)
|
tabular: function tabular(headings, style, iter) {
|
||||||
{
|
|
||||||
// TODO: This might be mind-bogglingly slow. We'll see.
|
// TODO: This might be mind-bogglingly slow. We'll see.
|
||||||
// <e4x>
|
// <e4x>
|
||||||
return this.commandOutput(
|
return this.commandOutput(
|
||||||
@@ -315,8 +289,7 @@ const template = { //{{{
|
|||||||
// </e4x>
|
// </e4x>
|
||||||
},
|
},
|
||||||
|
|
||||||
usage: function usage(iter)
|
usage: function usage(iter) {
|
||||||
{
|
|
||||||
// <e4x>
|
// <e4x>
|
||||||
return this.commandOutput(
|
return this.commandOutput(
|
||||||
<table>
|
<table>
|
||||||
@@ -330,6 +303,6 @@ const template = { //{{{
|
|||||||
</table>);
|
</table>);
|
||||||
// </e4x>
|
// </e4x>
|
||||||
}
|
}
|
||||||
}; //}}}
|
});
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
2312
common/content/ui.js
2312
common/content/ui.js
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,11 @@ const XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.
|
|||||||
const NS = Namespace("liberator", "http://vimperator.org/namespaces/liberator");
|
const NS = Namespace("liberator", "http://vimperator.org/namespaces/liberator");
|
||||||
default xml namespace = XHTML;
|
default xml namespace = XHTML;
|
||||||
|
|
||||||
const util = { //{{{
|
const Util = Module("util", {
|
||||||
|
init: function () {
|
||||||
|
this.Array = Util.Array;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if its argument is an Array object, regardless
|
* Returns true if its argument is an Array object, regardless
|
||||||
* of which context it comes from.
|
* of which context it comes from.
|
||||||
@@ -25,8 +29,7 @@ const util = { //{{{
|
|||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
cloneObject: function cloneObject(obj)
|
cloneObject: function cloneObject(obj) {
|
||||||
{
|
|
||||||
if (obj instanceof Array)
|
if (obj instanceof Array)
|
||||||
return obj.slice();
|
return obj.slice();
|
||||||
let newObj = {};
|
let newObj = {};
|
||||||
@@ -43,8 +46,7 @@ const util = { //{{{
|
|||||||
* @param {number} length The length of the returned string.
|
* @param {number} length The length of the returned string.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
clip: function clip(str, length)
|
clip: function clip(str, length) {
|
||||||
{
|
|
||||||
return str.length <= length ? str : str.substr(0, length - 3) + "...";
|
return str.length <= length ? str : str.substr(0, length - 3) + "...";
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -64,8 +66,7 @@ const util = { //{{{
|
|||||||
* @param {Node} node
|
* @param {Node} node
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
computedStyle: function computedStyle(node)
|
computedStyle: function computedStyle(node) {
|
||||||
{
|
|
||||||
while (node instanceof Text && node.parentNode)
|
while (node instanceof Text && node.parentNode)
|
||||||
node = node.parentNode;
|
node = node.parentNode;
|
||||||
return node.ownerDocument.defaultView.getComputedStyle(node, null);
|
return node.ownerDocument.defaultView.getComputedStyle(node, null);
|
||||||
@@ -78,8 +79,7 @@ const util = { //{{{
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @param {boolean} verbose
|
* @param {boolean} verbose
|
||||||
*/
|
*/
|
||||||
copyToClipboard: function copyToClipboard(str, verbose)
|
copyToClipboard: function copyToClipboard(str, verbose) {
|
||||||
{
|
|
||||||
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||||
clipboardHelper.copyString(str);
|
clipboardHelper.copyString(str);
|
||||||
|
|
||||||
@@ -94,8 +94,7 @@ const util = { //{{{
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
// FIXME: newURI needed too?
|
// FIXME: newURI needed too?
|
||||||
createURI: function createURI(str)
|
createURI: function createURI(str) {
|
||||||
{
|
|
||||||
const fixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
const fixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
||||||
return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||||
},
|
},
|
||||||
@@ -107,8 +106,7 @@ const util = { //{{{
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
escapeHTML: function escapeHTML(str)
|
escapeHTML: function escapeHTML(str) {
|
||||||
{
|
|
||||||
// XXX: the following code is _much_ slower than a simple .replace()
|
// XXX: the following code is _much_ slower than a simple .replace()
|
||||||
// :history display went down from 2 to 1 second after changing
|
// :history display went down from 2 to 1 second after changing
|
||||||
//
|
//
|
||||||
@@ -124,32 +122,28 @@ const util = { //{{{
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
escapeRegex: function escapeRegex(str)
|
escapeRegex: function escapeRegex(str) {
|
||||||
{
|
|
||||||
return str.replace(/([\\{}()[\].?*+])/g, "\\$1");
|
return str.replace(/([\\{}()[\].?*+])/g, "\\$1");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escapes quotes, newline and tab characters in <b>str</b>. The returned
|
* Escapes quotes, newline and tab characters in <b>str</b>. The returned
|
||||||
* string is delimited by <b>delimiter</b> or " if <b>delimiter</b> is not
|
* string is delimited by <b>delimiter</b> or " if <b>delimiter</b> is not
|
||||||
* specified.
|
* specified. {@see String#quote}.
|
||||||
*
|
*
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @param {string} delimiter
|
* @param {string} delimiter
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
escapeString: function escapeString(str, delimiter)
|
escapeString: function escapeString(str, delimiter) {
|
||||||
{
|
|
||||||
if (delimiter == undefined)
|
if (delimiter == undefined)
|
||||||
delimiter = '"';
|
delimiter = '"';
|
||||||
return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter;
|
return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter;
|
||||||
},
|
},
|
||||||
|
|
||||||
extend: function extend(dest)
|
extend: function extend(dest) {
|
||||||
{
|
|
||||||
Array.slice(arguments, 1).filter(util.identity).forEach(function (src) {
|
Array.slice(arguments, 1).filter(util.identity).forEach(function (src) {
|
||||||
for (let [k, v] in Iterator(src))
|
for (let [k, v] in Iterator(src)) {
|
||||||
{
|
|
||||||
let get = src.__lookupGetter__(k),
|
let get = src.__lookupGetter__(k),
|
||||||
set = src.__lookupSetter__(k);
|
set = src.__lookupSetter__(k);
|
||||||
if (!get && !set)
|
if (!get && !set)
|
||||||
@@ -171,8 +165,7 @@ const util = { //{{{
|
|||||||
* @param nodes {Array(string)}
|
* @param nodes {Array(string)}
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
makeXPath: function makeXPath(nodes)
|
makeXPath: function makeXPath(nodes) {
|
||||||
{
|
|
||||||
return util.Array(nodes).map(function (node) [node, "xhtml:" + node]).flatten()
|
return util.Array(nodes).map(function (node) [node, "xhtml:" + node]).flatten()
|
||||||
.map(function (node) "//" + node).join(" | ");
|
.map(function (node) "//" + node).join(" | ");
|
||||||
},
|
},
|
||||||
@@ -187,8 +180,7 @@ const util = { //{{{
|
|||||||
* passed as the first argument, <b>key</b> as the
|
* passed as the first argument, <b>key</b> as the
|
||||||
* second.
|
* second.
|
||||||
*/
|
*/
|
||||||
memoize: function memoize(obj, key, getter)
|
memoize: function memoize(obj, key, getter) {
|
||||||
{
|
|
||||||
obj.__defineGetter__(key, function () {
|
obj.__defineGetter__(key, function () {
|
||||||
delete obj[key];
|
delete obj[key];
|
||||||
obj[key] = getter(obj, key);
|
obj[key] = getter(obj, key);
|
||||||
@@ -209,14 +201,12 @@ const util = { //{{{
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @param {RegExp} marker
|
* @param {RegExp} marker
|
||||||
*/
|
*/
|
||||||
splitLiteral: function splitLiteral(str, marker)
|
splitLiteral: function splitLiteral(str, marker) {
|
||||||
{
|
|
||||||
let results = [];
|
let results = [];
|
||||||
let resep = RegExp(/^(([^\\'"]|\\.|'([^\\']|\\.)*'|"([^\\"]|\\.)*")*?)/.source + marker.source);
|
let resep = RegExp(/^(([^\\'"]|\\.|'([^\\']|\\.)*'|"([^\\"]|\\.)*")*?)/.source + marker.source);
|
||||||
let cont = true;
|
let cont = true;
|
||||||
|
|
||||||
while (cont)
|
while (cont) {
|
||||||
{
|
|
||||||
cont = false;
|
cont = false;
|
||||||
str = str.replace(resep, function (match, before) {
|
str = str.replace(resep, function (match, before) {
|
||||||
results.push(before);
|
results.push(before);
|
||||||
@@ -238,17 +228,14 @@ const util = { //{{{
|
|||||||
* @param {boolean} humanReadable Use byte multiples.
|
* @param {boolean} humanReadable Use byte multiples.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
formatBytes: function formatBytes(bytes, decimalPlaces, humanReadable)
|
formatBytes: function formatBytes(bytes, decimalPlaces, humanReadable) {
|
||||||
{
|
|
||||||
const unitVal = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
const unitVal = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
||||||
let unitIndex = 0;
|
let unitIndex = 0;
|
||||||
let tmpNum = parseInt(bytes, 10) || 0;
|
let tmpNum = parseInt(bytes, 10) || 0;
|
||||||
let strNum = [tmpNum + ""];
|
let strNum = [tmpNum + ""];
|
||||||
|
|
||||||
if (humanReadable)
|
if (humanReadable) {
|
||||||
{
|
while (tmpNum >= 1024) {
|
||||||
while (tmpNum >= 1024)
|
|
||||||
{
|
|
||||||
tmpNum /= 1024;
|
tmpNum /= 1024;
|
||||||
if (++unitIndex > (unitVal.length - 1))
|
if (++unitIndex > (unitVal.length - 1))
|
||||||
break;
|
break;
|
||||||
@@ -273,53 +260,7 @@ const util = { //{{{
|
|||||||
return strNum[0] + " " + unitVal[unitIndex];
|
return strNum[0] + " " + unitVal[unitIndex];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
exportHelp: function (path) {
|
||||||
* Generates an Asciidoc help entry.
|
|
||||||
*
|
|
||||||
* @param {Command|Map|Option} obj A liberator <b>Command</b>,
|
|
||||||
* <b>Map</b> or <b>Option</b> object
|
|
||||||
* @param {XMLList} extraHelp Extra help text beyond the description.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
generateHelp: function generateHelp(obj, extraHelp)
|
|
||||||
{
|
|
||||||
let spec = util.identity;
|
|
||||||
let tag = util.identity;
|
|
||||||
if (obj instanceof Command)
|
|
||||||
tag = spec = function (cmd) <>:{cmd}</>;
|
|
||||||
else if (obj instanceof Map && obj.count)
|
|
||||||
spec = function (map) <><oa xmlns="">count</oa>{map}</>;
|
|
||||||
else if (obj instanceof Option)
|
|
||||||
{
|
|
||||||
spec = function (opt) <o xmlns="">{opt}</o>;
|
|
||||||
tag = function (opt) <>'{opt}'</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// E4X has its warts.
|
|
||||||
let br = <>
|
|
||||||
</>;
|
|
||||||
|
|
||||||
default xml namespace = "";
|
|
||||||
XML.prettyPrinting = false;
|
|
||||||
XML.ignoreWhitespace = false;
|
|
||||||
|
|
||||||
return <></> +
|
|
||||||
<item>
|
|
||||||
<tags>{template.map(obj.names, tag, " ")}</tags>
|
|
||||||
<spec>{spec((obj.specs || obj.names)[0])}</spec>{
|
|
||||||
!obj.type ? "" : <>
|
|
||||||
<type>{obj.type}</type>
|
|
||||||
<default>{obj.defaultValue}</default></>}
|
|
||||||
<description>{
|
|
||||||
obj.description ? br+<p>{obj.description.replace(/\.?$/, ".")}</p> : "" }{
|
|
||||||
extraHelp ? br+extraHelp : "" }{
|
|
||||||
!(extraHelp || obj.description) ? br+<p>Sorry, no help available.</p> : "" }
|
|
||||||
</description>
|
|
||||||
</item>.toXMLString();
|
|
||||||
},
|
|
||||||
|
|
||||||
exportHelp: function (path)
|
|
||||||
{
|
|
||||||
const FILE = io.File(path);
|
const FILE = io.File(path);
|
||||||
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
|
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
|
||||||
const TIME = Date.now();
|
const TIME = Date.now();
|
||||||
@@ -337,8 +278,7 @@ const util = { //{{{
|
|||||||
.split(" ").map(Array.concat));
|
.split(" ").map(Array.concat));
|
||||||
|
|
||||||
let chrome = {};
|
let chrome = {};
|
||||||
for (let [file,] in Iterator(services.get("liberator:").FILE_MAP))
|
for (let [file,] in Iterator(services.get("liberator:").FILE_MAP)) {
|
||||||
{
|
|
||||||
liberator.open("liberator://help/" + file);
|
liberator.open("liberator://help/" + file);
|
||||||
events.waitForPageLoad();
|
events.waitForPageLoad();
|
||||||
let data = [
|
let data = [
|
||||||
@@ -346,10 +286,8 @@ const util = { //{{{
|
|||||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n',
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n',
|
||||||
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
|
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
|
||||||
];
|
];
|
||||||
function fix(node)
|
function fix(node) {
|
||||||
{
|
switch(node.nodeType) {
|
||||||
switch(node.nodeType)
|
|
||||||
{
|
|
||||||
case Node.ELEMENT_NODE:
|
case Node.ELEMENT_NODE:
|
||||||
if (node instanceof HTMLScriptElement)
|
if (node instanceof HTMLScriptElement)
|
||||||
return;
|
return;
|
||||||
@@ -358,22 +296,18 @@ const util = { //{{{
|
|||||||
if (node instanceof HTMLHtmlElement)
|
if (node instanceof HTMLHtmlElement)
|
||||||
data.push(' xmlns="' + XHTML.uri + '"');
|
data.push(' xmlns="' + XHTML.uri + '"');
|
||||||
|
|
||||||
for (let { name: name, value: value } in util.Array.itervalues(node.attributes))
|
for (let { name: name, value: value } in util.Array.itervalues(node.attributes)) {
|
||||||
{
|
if (name == "liberator:highlight") {
|
||||||
if (name == "liberator:highlight")
|
|
||||||
{
|
|
||||||
name = "class";
|
name = "class";
|
||||||
value = "hl-" + value;
|
value = "hl-" + value;
|
||||||
}
|
}
|
||||||
if (name == "href")
|
if (name == "href") {
|
||||||
{
|
|
||||||
if (value.indexOf("liberator://help-tag/") == 0)
|
if (value.indexOf("liberator://help-tag/") == 0)
|
||||||
value = services.get("io").newChannel(value, null, null).originalURI.path.substr(1);
|
value = services.get("io").newChannel(value, null, null).originalURI.path.substr(1);
|
||||||
if (!/[#\/]/.test(value))
|
if (!/[#\/]/.test(value))
|
||||||
value += ".xhtml";
|
value += ".xhtml";
|
||||||
}
|
}
|
||||||
if (name == "src" && value.indexOf(":") > 0)
|
if (name == "src" && value.indexOf(":") > 0) {
|
||||||
{
|
|
||||||
chrome[value] = value.replace(/.*\//, "");;
|
chrome[value] = value.replace(/.*\//, "");;
|
||||||
value = value.replace(/.*\//, "");
|
value = value.replace(/.*\//, "");
|
||||||
}
|
}
|
||||||
@@ -385,8 +319,7 @@ const util = { //{{{
|
|||||||
}
|
}
|
||||||
if (node.localName in empty)
|
if (node.localName in empty)
|
||||||
data.push(" />");
|
data.push(" />");
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
data.push(">");
|
data.push(">");
|
||||||
if (node instanceof HTMLHeadElement)
|
if (node instanceof HTMLHeadElement)
|
||||||
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
|
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
|
||||||
@@ -429,14 +362,11 @@ const util = { //{{{
|
|||||||
* @param {function(XMLHttpRequest)} callback
|
* @param {function(XMLHttpRequest)} callback
|
||||||
* @returns {XMLHttpRequest}
|
* @returns {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
httpGet: function httpGet(url, callback)
|
httpGet: function httpGet(url, callback) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
let xmlhttp = new XMLHttpRequest();
|
let xmlhttp = new XMLHttpRequest();
|
||||||
xmlhttp.mozBackgroundRequest = true;
|
xmlhttp.mozBackgroundRequest = true;
|
||||||
if (callback)
|
if (callback) {
|
||||||
{
|
|
||||||
xmlhttp.onreadystatechange = function () {
|
xmlhttp.onreadystatechange = function () {
|
||||||
if (xmlhttp.readyState == 4)
|
if (xmlhttp.readyState == 4)
|
||||||
callback(xmlhttp);
|
callback(xmlhttp);
|
||||||
@@ -446,8 +376,7 @@ const util = { //{{{
|
|||||||
xmlhttp.send(null);
|
xmlhttp.send(null);
|
||||||
return xmlhttp;
|
return xmlhttp;
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
liberator.log("Error opening " + url + ": " + e, 1);
|
liberator.log("Error opening " + url + ": " + e, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -465,8 +394,7 @@ const util = { //{{{
|
|||||||
* @param {boolean} asIterator Whether to return the results as an
|
* @param {boolean} asIterator Whether to return the results as an
|
||||||
* XPath iterator.
|
* XPath iterator.
|
||||||
*/
|
*/
|
||||||
evaluateXPath: function (expression, doc, elem, asIterator)
|
evaluateXPath: function (expression, doc, elem, asIterator) {
|
||||||
{
|
|
||||||
if (!doc)
|
if (!doc)
|
||||||
doc = window.content.document;
|
doc = window.content.document;
|
||||||
if (!elem)
|
if (!elem)
|
||||||
@@ -475,11 +403,11 @@ const util = { //{{{
|
|||||||
expression = util.makeXPath(expression);
|
expression = util.makeXPath(expression);
|
||||||
|
|
||||||
let result = doc.evaluate(expression, elem,
|
let result = doc.evaluate(expression, elem,
|
||||||
function lookupNamespaceURI(prefix)
|
function lookupNamespaceURI(prefix) {
|
||||||
{
|
|
||||||
return {
|
return {
|
||||||
xhtml: "http://www.w3.org/1999/xhtml",
|
xhtml: "http://www.w3.org/1999/xhtml",
|
||||||
xhtml2: "http://www.w3.org/2002/06/xhtml2",
|
xhtml2: "http://www.w3.org/2002/06/xhtml2",
|
||||||
|
liberator: NS.uri,
|
||||||
liberator: NS.uri
|
liberator: NS.uri
|
||||||
}[prefix] || null;
|
}[prefix] || null;
|
||||||
},
|
},
|
||||||
@@ -526,8 +454,7 @@ const util = { //{{{
|
|||||||
* @param {function} func
|
* @param {function} func
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
map: function map(obj, func)
|
map: function map(obj, func) {
|
||||||
{
|
|
||||||
let ary = [];
|
let ary = [];
|
||||||
for (let i in Iterator(obj))
|
for (let i in Iterator(obj))
|
||||||
ary.push(func(i));
|
ary.push(func(i));
|
||||||
@@ -558,8 +485,7 @@ const util = { //{{{
|
|||||||
* @returns {nsIURI}
|
* @returns {nsIURI}
|
||||||
*/
|
*/
|
||||||
// FIXME: createURI needed too?
|
// FIXME: createURI needed too?
|
||||||
newURI: function (uri)
|
newURI: function (uri) {
|
||||||
{
|
|
||||||
return services.get("io").newURI(uri, null, null);
|
return services.get("io").newURI(uri, null, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -571,10 +497,9 @@ const util = { //{{{
|
|||||||
* @param {boolean} color Whether the output should be colored.
|
* @param {boolean} color Whether the output should be colored.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
objectToString: function objectToString(object, color)
|
objectToString: function objectToString(object, color) {
|
||||||
{
|
|
||||||
// Use E4X literals so html is automatically quoted
|
// Use E4X literals so html is automatically quoted
|
||||||
// only when it's asked for. Noone wants to see <
|
// only when it's asked for. No one wants to see <
|
||||||
// on their console or :map :foo in their buffer
|
// on their console or :map :foo in their buffer
|
||||||
// when they expect :map <C-f> :foo.
|
// when they expect :map <C-f> :foo.
|
||||||
XML.prettyPrinting = false;
|
XML.prettyPrinting = false;
|
||||||
@@ -591,20 +516,17 @@ const util = { //{{{
|
|||||||
[XHTML, 'html'],
|
[XHTML, 'html'],
|
||||||
[XUL, 'xul']
|
[XUL, 'xul']
|
||||||
]);
|
]);
|
||||||
if (object instanceof Element)
|
if (object instanceof Element) {
|
||||||
{
|
|
||||||
let elem = object;
|
let elem = object;
|
||||||
if (elem.nodeType == elem.TEXT_NODE)
|
if (elem.nodeType == elem.TEXT_NODE)
|
||||||
return elem.data;
|
return elem.data;
|
||||||
function namespaced(node)
|
function namespaced(node) {
|
||||||
{
|
|
||||||
var ns = NAMESPACES[node.namespaceURI];
|
var ns = NAMESPACES[node.namespaceURI];
|
||||||
if (ns)
|
if (ns)
|
||||||
return ns + ":" + node.localName;
|
return ns + ":" + node.localName;
|
||||||
return node.localName.toLowerCase();
|
return node.localName.toLowerCase();
|
||||||
}
|
}
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
let tag = "<" + [namespaced(elem)].concat(
|
let tag = "<" + [namespaced(elem)].concat(
|
||||||
[namespaced(a) + "=" + template.highlight(a.value, true)
|
[namespaced(a) + "=" + template.highlight(a.value, true)
|
||||||
for ([i, a] in util.Array.iteritems(elem.attributes))]).join(" ");
|
for ([i, a] in util.Array.iteritems(elem.attributes))]).join(" ");
|
||||||
@@ -615,42 +537,34 @@ const util = { //{{{
|
|||||||
tag += '>...</' + namespaced(elem) + '>';
|
tag += '>...</' + namespaced(elem) + '>';
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
return {}.toString.call(elem);
|
return {}.toString.call(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try { // for window.JSON
|
||||||
{ // for window.JSON
|
|
||||||
var obj = String(object);
|
var obj = String(object);
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e) {
|
||||||
{
|
|
||||||
obj = "[Object]";
|
obj = "[Object]";
|
||||||
}
|
}
|
||||||
obj = template.highlightFilter(util.clip(obj, 150), "\n", !color ? function () "^J" : function () <span highlight="NonText">^J</span>);
|
obj = template.highlightFilter(util.clip(obj, 150), "\n", !color ? function () "^J" : function () <span highlight="NonText">^J</span>);
|
||||||
let string = <><span highlight="Title Object">{obj}</span>::<br/>
</>;
|
let string = <><span highlight="Title Object">{obj}</span>::<br/>
</>;
|
||||||
|
|
||||||
let keys = [];
|
let keys = [];
|
||||||
try // window.content often does not want to be queried with "var i in object"
|
try { // window.content often does not want to be queried with "var i in object"
|
||||||
{
|
|
||||||
let hasValue = !("__iterator__" in object);
|
let hasValue = !("__iterator__" in object);
|
||||||
if (modules.isPrototypeOf(object))
|
if (modules.isPrototypeOf(object)) {
|
||||||
{
|
|
||||||
object = Iterator(object);
|
object = Iterator(object);
|
||||||
hasValue = false;
|
hasValue = false;
|
||||||
}
|
}
|
||||||
for (let i in object)
|
for (let i in object) {
|
||||||
{
|
|
||||||
let value = <![CDATA[<no value>]]>;
|
let value = <![CDATA[<no value>]]>;
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
value = object[i];
|
value = object[i];
|
||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
if (!hasValue)
|
if (!hasValue) {
|
||||||
{
|
|
||||||
if (i instanceof Array && i.length == 2)
|
if (i instanceof Array && i.length == 2)
|
||||||
[i, value] = i;
|
[i, value] = i;
|
||||||
else
|
else
|
||||||
@@ -668,8 +582,7 @@ const util = { //{{{
|
|||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
|
|
||||||
function compare(a, b)
|
function compare(a, b) {
|
||||||
{
|
|
||||||
if (!isNaN(a[0]) && !isNaN(b[0]))
|
if (!isNaN(a[0]) && !isNaN(b[0]))
|
||||||
return a[0] - b[0];
|
return a[0] - b[0];
|
||||||
return String.localeCompare(a[0], b[0]);
|
return String.localeCompare(a[0], b[0]);
|
||||||
@@ -688,17 +601,14 @@ const util = { //{{{
|
|||||||
* negative. @default 1
|
* negative. @default 1
|
||||||
* @returns {Iterator(Object)}
|
* @returns {Iterator(Object)}
|
||||||
*/
|
*/
|
||||||
range: function range(start, end, step)
|
range: function range(start, end, step) {
|
||||||
{
|
|
||||||
if (!step)
|
if (!step)
|
||||||
step = 1;
|
step = 1;
|
||||||
if (step > 0)
|
if (step > 0) {
|
||||||
{
|
|
||||||
for (; start < end; start += step)
|
for (; start < end; start += step)
|
||||||
yield start;
|
yield start;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
while (start > end)
|
while (start > end)
|
||||||
yield start += step;
|
yield start += step;
|
||||||
}
|
}
|
||||||
@@ -713,13 +623,10 @@ const util = { //{{{
|
|||||||
* @param {number} time The time in milliseconds between thread yields.
|
* @param {number} time The time in milliseconds between thread yields.
|
||||||
* @returns {Iterator(Object)}
|
* @returns {Iterator(Object)}
|
||||||
*/
|
*/
|
||||||
interruptibleRange: function interruptibleRange(start, end, time)
|
interruptibleRange: function interruptibleRange(start, end, time) {
|
||||||
{
|
|
||||||
let endTime = Date.now() + time;
|
let endTime = Date.now() + time;
|
||||||
while (start < end)
|
while (start < end) {
|
||||||
{
|
if (Date.now() > endTime) {
|
||||||
if (Date.now() > endTime)
|
|
||||||
{
|
|
||||||
liberator.threadYield(true, true);
|
liberator.threadYield(true, true);
|
||||||
endTime = Date.now() + time;
|
endTime = Date.now() + time;
|
||||||
}
|
}
|
||||||
@@ -735,12 +642,10 @@ const util = { //{{{
|
|||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
readFromClipboard: function readFromClipboard()
|
readFromClipboard: function readFromClipboard() {
|
||||||
{
|
|
||||||
let str;
|
let str;
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||||
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||||
|
|
||||||
@@ -756,8 +661,7 @@ const util = { //{{{
|
|||||||
|
|
||||||
transferable.getTransferData("text/unicode", data, dataLen);
|
transferable.getTransferData("text/unicode", data, dataLen);
|
||||||
|
|
||||||
if (data)
|
if (data) {
|
||||||
{
|
|
||||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||||
str = data.data.substring(0, dataLen.value / 2);
|
str = data.data.substring(0, dataLen.value / 2);
|
||||||
}
|
}
|
||||||
@@ -776,8 +680,7 @@ const util = { //{{{
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
stringToURLArray: function stringToURLArray(str)
|
stringToURLArray: function stringToURLArray(str) {
|
||||||
{
|
|
||||||
let urls;
|
let urls;
|
||||||
|
|
||||||
if (options["urlseparator"])
|
if (options["urlseparator"])
|
||||||
@@ -786,8 +689,7 @@ const util = { //{{{
|
|||||||
urls = [str];
|
urls = [str];
|
||||||
|
|
||||||
return urls.map(function (url) {
|
return urls.map(function (url) {
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
// Try to find a matching file.
|
// Try to find a matching file.
|
||||||
let file = io.File(url);
|
let file = io.File(url);
|
||||||
if (file.exists() && file.isReadable())
|
if (file.exists() && file.isReadable())
|
||||||
@@ -834,197 +736,129 @@ const util = { //{{{
|
|||||||
* stored here, keyed to the value thereof.
|
* stored here, keyed to the value thereof.
|
||||||
* @returns {Node}
|
* @returns {Node}
|
||||||
*/
|
*/
|
||||||
xmlToDom: function xmlToDom(node, doc, nodes)
|
xmlToDom: function xmlToDom(node, doc, nodes) {
|
||||||
{
|
|
||||||
XML.prettyPrinting = false;
|
XML.prettyPrinting = false;
|
||||||
if (node.length() != 1)
|
if (node.length() != 1) {
|
||||||
{
|
|
||||||
let domnode = doc.createDocumentFragment();
|
let domnode = doc.createDocumentFragment();
|
||||||
for each (let child in node)
|
for each (let child in node)
|
||||||
domnode.appendChild(arguments.callee(child, doc, nodes));
|
domnode.appendChild(arguments.callee(child, doc, nodes));
|
||||||
return domnode;
|
return domnode;
|
||||||
}
|
}
|
||||||
switch (node.nodeKind())
|
switch (node.nodeKind()) {
|
||||||
{
|
case "text":
|
||||||
case "text":
|
return doc.createTextNode(node);
|
||||||
return doc.createTextNode(node);
|
case "element":
|
||||||
case "element":
|
let domnode = doc.createElementNS(node.namespace(), node.localName());
|
||||||
let domnode = doc.createElementNS(node.namespace(), node.localName());
|
for each (let attr in node.@*)
|
||||||
for each (let attr in node.@*)
|
domnode.setAttributeNS(attr.name() == "highlight" ? NS.uri : attr.namespace(), attr.name(), String(attr));
|
||||||
domnode.setAttributeNS(attr.name() == "highlight" ? NS.uri : attr.namespace(), attr.name(), String(attr));
|
for each (let child in node.*)
|
||||||
for each (let child in node.*)
|
domnode.appendChild(arguments.callee(child, doc, nodes));
|
||||||
domnode.appendChild(arguments.callee(child, doc, nodes));
|
if (nodes && node.@key)
|
||||||
if (nodes && node.@key)
|
nodes[node.@key] = domnode;
|
||||||
nodes[node.@key] = domnode;
|
return domnode;
|
||||||
return domnode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}; //}}}
|
}, {
|
||||||
|
// TODO: Why don't we just push all util.BuiltinType up into modules? --djk
|
||||||
// TODO: Why don't we just push all util.BuiltinType up into modules? --djk
|
/**
|
||||||
/**
|
* Array utility methods.
|
||||||
* Array utility methods.
|
*/
|
||||||
*/
|
Array: Class("Array", {
|
||||||
util.Array = function Array_(ary) {
|
init: function (ary) {
|
||||||
var obj = {
|
return {
|
||||||
__proto__: ary,
|
__proto__: ary,
|
||||||
__iterator__: function () this.iteritems(),
|
__iterator__: function () this.iteritems(),
|
||||||
__noSuchMethod__: function (meth, args)
|
__noSuchMethod__: function (meth, args) {
|
||||||
{
|
let res = (util.Array[meth] || Array[meth]).apply(null, [this.__proto__].concat(args));
|
||||||
let res = (util.Array[meth] || Array[meth]).apply(null, [this.__proto__].concat(args));
|
if (util.Array.isinstance(res))
|
||||||
if (util.Array.isinstance(res))
|
return util.Array(res);
|
||||||
return util.Array(res);
|
return res;
|
||||||
return res;
|
},
|
||||||
|
concat: function () [].concat.apply(this.__proto__, arguments),
|
||||||
|
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
isinstance: function isinstance(obj) {
|
||||||
|
return Object.prototype.toString.call(obj) == "[object Array]";
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Converts an array to an object. As in lisp, an assoc is an
|
||||||
|
* array of key-value pairs, which maps directly to an object,
|
||||||
|
* as such:
|
||||||
|
* [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
|
||||||
|
*
|
||||||
|
* @param {Array[]} assoc
|
||||||
|
* @... {string} 0 - Key
|
||||||
|
* @... 1 - Value
|
||||||
|
*/
|
||||||
|
toObject: function toObject(assoc) {
|
||||||
|
let obj = {};
|
||||||
|
assoc.forEach(function ([k, v]) { obj[k] = v; });
|
||||||
|
return obj;
|
||||||
},
|
},
|
||||||
concat: function () [].concat.apply(this.__proto__, arguments),
|
|
||||||
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
|
|
||||||
};
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
util.Array.isinstance = function isinstance(obj) {
|
|
||||||
return Object.prototype.toString.call(obj) == "[object Array]";
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Converts an array to an object. As in lisp, an assoc is an
|
|
||||||
* array of key-value pairs, which maps directly to an object,
|
|
||||||
* as such:
|
|
||||||
* [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
|
|
||||||
*
|
|
||||||
* @param {Array[]} assoc
|
|
||||||
* @... {string} 0 - Key
|
|
||||||
* @... 1 - Value
|
|
||||||
*/
|
|
||||||
util.Array.toObject = function toObject(assoc)
|
|
||||||
{
|
|
||||||
let obj = {};
|
|
||||||
assoc.forEach(function ([k, v]) { obj[k] = v; });
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens an array, such that all elements of the array are
|
* Flattens an array, such that all elements of the array are
|
||||||
* joined into a single array:
|
* joined into a single array:
|
||||||
* [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
|
* [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
|
||||||
*
|
*
|
||||||
* @param {Array} ary
|
* @param {Array} ary
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
util.Array.flatten = function flatten(ary) Array.concat.apply([], ary),
|
flatten: function flatten(ary) Array.concat.apply([], ary),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an Iterator for an array's values.
|
* Returns an Iterator for an array's values.
|
||||||
*
|
*
|
||||||
* @param {Array} ary
|
* @param {Array} ary
|
||||||
* @returns {Iterator(Object)}
|
* @returns {Iterator(Object)}
|
||||||
*/
|
*/
|
||||||
util.Array.itervalues = function itervalues(ary)
|
itervalues: function itervalues(ary) {
|
||||||
{
|
let length = ary.length;
|
||||||
let length = ary.length;
|
for (let i = 0; i < length; i++)
|
||||||
for (let i = 0; i < length; i++)
|
yield ary[i];
|
||||||
yield ary[i];
|
},
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an Iterator for an array's indices and values.
|
* Returns an Iterator for an array's indices and values.
|
||||||
*
|
*
|
||||||
* @param {Array} ary
|
* @param {Array} ary
|
||||||
* @returns {Iterator([{number}, {Object}])}
|
* @returns {Iterator([{number}, {Object}])}
|
||||||
*/
|
*/
|
||||||
util.Array.iteritems = function iteritems(ary)
|
iteritems: function iteritems(ary) {
|
||||||
{
|
let length = ary.length;
|
||||||
let length = ary.length;
|
for (let i = 0; i < length; i++)
|
||||||
for (let i = 0; i < length; i++)
|
yield [i, ary[i]];
|
||||||
yield [i, ary[i]];
|
},
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters out all duplicates from an array. If
|
* Filters out all duplicates from an array. If
|
||||||
* <b>unsorted</b> is false, the array is sorted before
|
* <b>unsorted</b> is false, the array is sorted before
|
||||||
* duplicates are removed.
|
* duplicates are removed.
|
||||||
*
|
*
|
||||||
* @param {Array} ary
|
* @param {Array} ary
|
||||||
* @param {boolean} unsorted
|
* @param {boolean} unsorted
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
util.Array.uniq = function uniq(ary, unsorted)
|
uniq: function uniq(ary, unsorted) {
|
||||||
{
|
let ret = [];
|
||||||
let ret = [];
|
if (unsorted) {
|
||||||
if (unsorted)
|
for (let [, item] in Iterator(ary))
|
||||||
{
|
if (ret.indexOf(item) == -1)
|
||||||
for (let [, item] in Iterator(ary))
|
ret.push(item);
|
||||||
if (ret.indexOf(item) == -1)
|
}
|
||||||
ret.push(item);
|
else {
|
||||||
}
|
for (let [, item] in Iterator(ary.sort())) {
|
||||||
else
|
if (item != last || !ret.length)
|
||||||
{
|
ret.push(item);
|
||||||
for (let [, item] in Iterator(ary.sort()))
|
var last = item;
|
||||||
{
|
}
|
||||||
if (item != last || !ret.length)
|
}
|
||||||
ret.push(item);
|
return ret;
|
||||||
var last = item;
|
},
|
||||||
}
|
}),
|
||||||
}
|
});
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Struct()
|
|
||||||
{
|
|
||||||
let self = this instanceof Struct ? this : new Struct();
|
|
||||||
if (!arguments.length)
|
|
||||||
return self;
|
|
||||||
|
|
||||||
let args = Array.slice(arguments);
|
|
||||||
self.__defineGetter__("length", function () args.length);
|
|
||||||
self.__defineGetter__("members", function () args.slice());
|
|
||||||
for (let arg in Iterator(args))
|
|
||||||
{
|
|
||||||
let [i, name] = arg;
|
|
||||||
self.__defineGetter__(name, function () this[i]);
|
|
||||||
self.__defineSetter__(name, function (val) { this[i] = val; });
|
|
||||||
}
|
|
||||||
function ConStructor()
|
|
||||||
{
|
|
||||||
let self = this instanceof arguments.callee ? this : new arguments.callee();
|
|
||||||
//for (let [k, v] in Iterator(Array.slice(arguments))) // That is makes using struct twice as slow as the following code:
|
|
||||||
for (let i = 0; i < arguments.length; i++)
|
|
||||||
{
|
|
||||||
if (arguments[i] != undefined)
|
|
||||||
self[i] = arguments[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
ConStructor.prototype = self;
|
|
||||||
ConStructor.defaultValue = function (key, val)
|
|
||||||
{
|
|
||||||
let i = args.indexOf(key);
|
|
||||||
ConStructor.prototype.__defineGetter__(i, function () (this[i] = val.call(this), this[i])); // Kludge for FF 3.0
|
|
||||||
ConStructor.prototype.__defineSetter__(i, function (val) {
|
|
||||||
let value = val;
|
|
||||||
this.__defineGetter__(i, function () value);
|
|
||||||
this.__defineSetter__(i, function (val) { value = val });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return self.constructor = ConStructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
Struct.prototype = {
|
|
||||||
clone: function clone()
|
|
||||||
{
|
|
||||||
return this.constructor.apply(null, this.slice());
|
|
||||||
},
|
|
||||||
// Iterator over our named 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
|
|
||||||
// get length() won't work.
|
|
||||||
for (let [, k] in Iterator(["concat", "every", "filter", "forEach", "indexOf", "join", "lastIndexOf",
|
|
||||||
"map", "reduce", "reduceRight", "reverse", "slice", "some", "sort"]))
|
|
||||||
Struct.prototype[k] = Array.prototype[k];
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Muttator";
|
const Name = "Muttator";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/about-handler.jsm");
|
// Components.utils.import("resource://liberator/about-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Muttator";
|
const Name = "Muttator";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -191,23 +191,11 @@ const config = (function () //{{{
|
|||||||
liberator.loadModule("hints", Hints);
|
liberator.loadModule("hints", Hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// STYLES //////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
commands.add(["pref[erences]", "prefs"],
|
commands.add(["pref[erences]", "prefs"],
|
||||||
"Show " + config.hostApplication + " preferences",
|
"Show " + config.hostApplication + " preferences",
|
||||||
function () { window.openOptionsDialog(); },
|
function () { window.openOptionsDialog(); },
|
||||||
{ argCount: "0" });
|
{ argCount: "0" });
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
// FIXME: comment obviously incorrect
|
// FIXME: comment obviously incorrect
|
||||||
// 0: never automatically edit externally
|
// 0: never automatically edit externally
|
||||||
// 1: automatically edit externally when message window is shown the first time
|
// 1: automatically edit externally when message window is shown the first time
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#### configuration
|
#### configuration
|
||||||
|
|
||||||
VERSION = 2.2
|
VERSION = 1.0pre
|
||||||
NAME = vimperator
|
NAME = vimperator
|
||||||
|
|
||||||
include ../common/Makefile
|
include ../common/Makefile
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ content liberator ../common/content/
|
|||||||
resource liberator ../common/modules/
|
resource liberator ../common/modules/
|
||||||
skin liberator classic/1.0 ../common/skin/
|
skin liberator classic/1.0 ../common/skin/
|
||||||
|
|
||||||
override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd
|
override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd
|
||||||
override chrome://liberator/content/config.js chrome://vimperator/content/config.js
|
override chrome://liberator/content/config.js chrome://vimperator/content/config.js
|
||||||
|
|
||||||
overlay chrome://browser/content/browser.xul chrome://liberator/content/liberator.xul
|
overlay chrome://browser/content/browser.xul chrome://liberator/content/liberator.xul
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Vimperator";
|
const Name = "Vimperator";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/about-handler.jsm");
|
// Components.utils.import("resource://liberator/about-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Vimperator";
|
const Name = "Vimperator";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ const config = { //{{{
|
|||||||
scripts: [
|
scripts: [
|
||||||
"browser.js",
|
"browser.js",
|
||||||
"bookmarks.js",
|
"bookmarks.js",
|
||||||
|
"history.js",
|
||||||
|
"quickmarks.js",
|
||||||
"sanitizer.js",
|
"sanitizer.js",
|
||||||
"tabs.js"
|
"tabs.js"
|
||||||
],
|
],
|
||||||
@@ -128,38 +130,6 @@ const config = { //{{{
|
|||||||
|
|
||||||
init: function ()
|
init: function ()
|
||||||
{
|
{
|
||||||
// load Vimperator specific modules
|
|
||||||
// FIXME: Why aren't these listed in config.scripts?
|
|
||||||
// FIXME: Why isn't this automatic? -> how would one know which classes to load where? --mst
|
|
||||||
// Something like:
|
|
||||||
// liberator.addModule("search", function Search() { ...
|
|
||||||
// for all modules, or something similar. For modules which
|
|
||||||
// require other modules, well, there's addObserver("load_foo",
|
|
||||||
// or we could just make sure that they're all sourced in order.
|
|
||||||
// The scripts could even just instantiate them themselves.
|
|
||||||
// --Kris
|
|
||||||
liberator.loadModule("browser", Browser);
|
|
||||||
liberator.loadModule("finder", Finder);
|
|
||||||
liberator.loadModule("bookmarks", Bookmarks);
|
|
||||||
liberator.loadModule("history", History);
|
|
||||||
liberator.loadModule("tabs", Tabs);
|
|
||||||
liberator.loadModule("marks", Marks);
|
|
||||||
liberator.loadModule("quickmarks", QuickMarks);
|
|
||||||
liberator.loadModule("hints", Hints);
|
|
||||||
liberator.loadModule("sanitizer", Sanitizer);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////// STYLES //////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////}}}
|
|
||||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
|
||||||
|
|
||||||
commands.add(["winon[ly]"],
|
commands.add(["winon[ly]"],
|
||||||
"Close all other windows",
|
"Close all other windows",
|
||||||
function ()
|
function ()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<em:optionsURL>chrome://liberator/content/preferences.xul</em:optionsURL>
|
<em:optionsURL>chrome://liberator/content/preferences.xul</em:optionsURL>
|
||||||
<em:file>
|
<em:file>
|
||||||
<Description about="urn:mozilla:extension:file:vimperator.jar">
|
<Description about="urn:mozilla:extension:file:vimperator.jar">
|
||||||
<em:package>content/vimperator/</em:package>
|
<em:package>content/liberator/</em:package>
|
||||||
</Description>
|
</Description>
|
||||||
</em:file>
|
</em:file>
|
||||||
<em:targetApplication>
|
<em:targetApplication>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Xulmus";
|
const Name = "Xulmus";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/about-handler.jsm");
|
// Components.utils.import("resource://liberator/about-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Header:
|
// Header:
|
||||||
const Name = "Xulmus";
|
const Name = "Xulmus";
|
||||||
// The following doesn't work here, so this module's code is sadly suplicated:
|
// The following doesn't work here, so this module's code is sadly duplicated:
|
||||||
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
// Components.utils.import("resource://liberator/commandline-handler.jsm");
|
||||||
|
|
||||||
// Copyright (c) 2009 by Doug Kearns
|
// Copyright (c) 2009 by Doug Kearns
|
||||||
|
|||||||
@@ -246,6 +246,20 @@ const config = { //{{{
|
|||||||
////////////////////// STYLES //////////////////////////////////////////////////
|
////////////////////// STYLES //////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
/////////////////////////////////////////////////////////////////////////////{{{
|
||||||
|
|
||||||
|
let img = Image();
|
||||||
|
img.src = "chrome://xulmus/content/logo.png";
|
||||||
|
img.onload = function () {
|
||||||
|
styles.addSheet(true, "logo", "chrome://liberator/locale/*",
|
||||||
|
".xulmus-logo {" + <>
|
||||||
|
display: inline-block;
|
||||||
|
background: url({img.src});
|
||||||
|
width: {img.width}px;
|
||||||
|
height: {img.height}px;
|
||||||
|
</> + "}",
|
||||||
|
true);
|
||||||
|
delete img;
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////////////////////////////////{{{
|
/////////////////////////////////////////////////////////////////////////////{{{
|
||||||
|
|||||||
Reference in New Issue
Block a user