mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-22 16:47:59 +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:
64
HACKING
64
HACKING
@@ -18,16 +18,15 @@ important, please ask.
|
||||
== Coding Style ==
|
||||
|
||||
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
|
||||
necessarily need to have a good understanding of Vimperator's source code, nor
|
||||
do they probably know in-depth concepts of other languages like Lisp or Python.
|
||||
Therefore, the coding style should feel natural to any JavaScript developer
|
||||
so it is easy to read and understand. Of course, this does not mean, you have
|
||||
to avoid all new JavaScript features like list comprehension or generators.
|
||||
Use them, when they make sense, but don't use them, when the resulting code
|
||||
is hard to read.
|
||||
We try to target experienced JavaScript developers who do not
|
||||
necessarily need to have a good understanding of Vimperator's source
|
||||
code, nor necessarily understand in-depth concepts of other
|
||||
languages like Lisp or Python. Therefore, the coding style should
|
||||
feel natural to any JavaScript developer. Of course, this does not
|
||||
mean, you have to avoid all new JavaScript features like list
|
||||
comprehension or generators. Use them, when they make sense, but
|
||||
don't use them when the resulting code is hard to read.
|
||||
|
||||
=== The most important style issues are: ===
|
||||
|
||||
@@ -40,30 +39,42 @@ is hard to read.
|
||||
* Use " for enclosing strings instead of ', unless using ' avoids escaping of lots of "
|
||||
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
|
||||
after a parenthesis or after a function call:
|
||||
for (pre; condition; post)
|
||||
but:
|
||||
alert("foo");
|
||||
|
||||
* Opening curly brackets { must be on a new line, unless it is used in a closure:
|
||||
function myFunction ()
|
||||
{
|
||||
* Bracing is formatted as follows:
|
||||
function myFunction () {
|
||||
if (foo)
|
||||
{
|
||||
baz = false;
|
||||
return bar;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
baz = false;
|
||||
return baz;
|
||||
}
|
||||
}
|
||||
but:
|
||||
setTimeout(function () {
|
||||
...
|
||||
var quux = frob("you",
|
||||
{
|
||||
a: 1,
|
||||
b: 42,
|
||||
c: {
|
||||
hoopy: "frood"
|
||||
}
|
||||
});
|
||||
|
||||
When in doubt, look for similar code.
|
||||
|
||||
* No braces for one-line conditional statements:
|
||||
Right:
|
||||
if (foo)
|
||||
@@ -97,9 +108,9 @@ is hard to read.
|
||||
|
||||
* 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++)
|
||||
to iterate over arrays. for (let i in ary) and for each (let i in ary)
|
||||
include members in an Array.prototype, which some extensions alter.
|
||||
* Use Iterators or Array#forEach to iterate over arrays. for (let i
|
||||
in ary) and for each (let i in ary) include members in an
|
||||
Array.prototype, which some extensions alter.
|
||||
Right:
|
||||
for (let [,elem] in Iterator(ary))
|
||||
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
|
||||
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:
|
||||
|
||||
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
|
||||
*/
|
||||
function Browser() //{{{
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
const Browser = Module("browser", {
|
||||
}, {
|
||||
// TODO: support 'nrformats'? -> probably not worth it --mst
|
||||
function incrementURL(count)
|
||||
{
|
||||
incrementURL: function (count) {
|
||||
let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
|
||||
if (!matches)
|
||||
return void liberator.beep();
|
||||
@@ -25,33 +20,27 @@ function Browser() //{{{
|
||||
let [, pre, number, post] = matches;
|
||||
let newNumber = parseInt(number, 10) + count;
|
||||
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)
|
||||
newNumberStr = "0" + newNumberStr;
|
||||
}
|
||||
|
||||
liberator.open(pre + newNumberStr + post);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
}, {
|
||||
options: function () {
|
||||
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)
|
||||
{
|
||||
setter: function (val) {
|
||||
if (options["encoding"] == val)
|
||||
return val;
|
||||
|
||||
// Stolen from browser.jar/content/browser/browser.js, more or less.
|
||||
try
|
||||
{
|
||||
try {
|
||||
getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
|
||||
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
|
||||
getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
@@ -64,8 +53,7 @@ function Browser() //{{{
|
||||
|
||||
// only available in FF 3.5
|
||||
services.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
|
||||
if (services.get("privateBrowsing"))
|
||||
{
|
||||
if (services.get("privateBrowsing")) {
|
||||
options.add(["private", "pornmode"],
|
||||
"Set the 'private browsing' option",
|
||||
"boolean", false,
|
||||
@@ -76,24 +64,20 @@ function Browser() //{{{
|
||||
let services = modules.services; // Storage objects are global to all windows, 'modules' isn't.
|
||||
storage.newObject("private-mode", function () {
|
||||
({
|
||||
init: 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)
|
||||
{
|
||||
if (topic == "private-browsing")
|
||||
{
|
||||
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")
|
||||
{
|
||||
else if (topic == "quit-application") {
|
||||
services.get("observer").removeObserver(this, "quit-application");
|
||||
services.get("observer").removeObserver(this, "private-browsing");
|
||||
}
|
||||
@@ -109,11 +93,9 @@ function Browser() //{{{
|
||||
options.add(["urlseparator"],
|
||||
"Set the separator regex used to separate multiple URL args",
|
||||
"string", ",\\s");
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
mappings: function () {
|
||||
mappings.add([modes.NORMAL],
|
||||
["y"], "Yank current location to the clipboard",
|
||||
function () { util.copyToClipboard(buffer.URL, true); });
|
||||
@@ -145,12 +127,12 @@ function Browser() //{{{
|
||||
|
||||
mappings.add([modes.NORMAL],
|
||||
["<C-a>"], "Increment last number in URL",
|
||||
function (count) { incrementURL(Math.max(count, 1)); },
|
||||
function (count) { Browser.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)); },
|
||||
function (count) { Browser.incrementURL(-Math.max(count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.NORMAL], ["~"],
|
||||
@@ -163,25 +145,20 @@ function Browser() //{{{
|
||||
|
||||
mappings.add([modes.NORMAL], ["gH"],
|
||||
"Open homepage in a new tab",
|
||||
function ()
|
||||
{
|
||||
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))
|
||||
{
|
||||
function (count) {
|
||||
function isDirectory(url) {
|
||||
if (/^file:\/|^\//.test(url)) {
|
||||
let file = io.File(url);
|
||||
return file.exists() && file.isDirectory();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// for all other locations just check if the URL ends with /
|
||||
return /\/$/.test(url);
|
||||
}
|
||||
@@ -192,8 +169,7 @@ function Browser() //{{{
|
||||
|
||||
// XXX
|
||||
let url = buffer.URL;
|
||||
for (let i = 0; i < count; i++)
|
||||
{
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (isDirectory(url))
|
||||
url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
|
||||
else
|
||||
@@ -210,8 +186,7 @@ function Browser() //{{{
|
||||
|
||||
mappings.add([modes.NORMAL], ["gU"],
|
||||
"Go to the root of the website",
|
||||
function ()
|
||||
{
|
||||
function () {
|
||||
let uri = content.document.location;
|
||||
if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
|
||||
return void liberator.beep();
|
||||
@@ -221,15 +196,12 @@ function Browser() //{{{
|
||||
mappings.add([modes.NORMAL], ["<C-l>"],
|
||||
"Redraw the screen",
|
||||
function () { commands.get("redraw").execute("", false); });
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
commands: function () {
|
||||
commands.add(["downl[oads]", "dl"],
|
||||
"Show progress of current downloads",
|
||||
function ()
|
||||
{
|
||||
function () {
|
||||
liberator.open("chrome://mozapps/content/downloads/downloads.xul",
|
||||
options.get("newtab").has("all", "downloads")
|
||||
? liberator.NEW_TAB : liberator.CURRENT_TAB);
|
||||
@@ -238,16 +210,14 @@ function Browser() //{{{
|
||||
|
||||
commands.add(["o[pen]", "e[dit]"],
|
||||
"Open one or more URLs in the current tab",
|
||||
function (args)
|
||||
{
|
||||
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,
|
||||
@@ -255,23 +225,14 @@ function Browser() //{{{
|
||||
|
||||
commands.add(["redr[aw]"],
|
||||
"Redraw the screen",
|
||||
function ()
|
||||
{
|
||||
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:
|
||||
|
||||
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);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
__liberator_eval_error = e;
|
||||
}
|
||||
// IMPORTANT: The eval statement *must* remain on the first line
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,79 +13,25 @@
|
||||
// : 'linksearch' searches should highlight link matches only
|
||||
// : changing any search settings should also update the search state including highlighting
|
||||
// : incremental searches shouldn't permanently update search modifiers
|
||||
//
|
||||
// TODO: Clean up this rat's nest. --Kris
|
||||
|
||||
/**
|
||||
* @instance finder
|
||||
*/
|
||||
function Finder() //{{{
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
const Finder = Module("finder", {
|
||||
init: function () {
|
||||
const self = this;
|
||||
|
||||
var found = false; // true if the last search was successful
|
||||
var backwards = false; // currently searching backwards
|
||||
var searchString = ""; // current search string (without modifiers)
|
||||
var searchPattern = ""; // current search string (includes modifiers)
|
||||
var lastSearchPattern = ""; // the last searched pattern (includes modifiers)
|
||||
var 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
|
||||
var caseSensitive = false; // search string is case sensitive
|
||||
var linksOnly = false; // search is limited to link text only
|
||||
|
||||
// Event handlers for search - closure is needed
|
||||
commandline.registerCallback("change", modes.SEARCH_FORWARD, function (str) { finder.onKeyPress(str); });
|
||||
commandline.registerCallback("submit", modes.SEARCH_FORWARD, function (str) { finder.onSubmit(str); });
|
||||
commandline.registerCallback("cancel", modes.SEARCH_FORWARD, function () { finder.onCancel(); });
|
||||
// TODO: allow advanced myModes in register/triggerCallback
|
||||
commandline.registerCallback("change", modes.SEARCH_BACKWARD, function (str) { finder.onKeyPress(str); });
|
||||
commandline.registerCallback("submit", modes.SEARCH_BACKWARD, function (str) { finder.onSubmit(str); });
|
||||
commandline.registerCallback("cancel", modes.SEARCH_BACKWARD, function () { finder.onCancel(); });
|
||||
|
||||
// set searchString, searchPattern, caseSensitive, linksOnly
|
||||
function processUserPattern(pattern)
|
||||
{
|
||||
//// strip off pattern terminator and offset
|
||||
//if (backwards)
|
||||
// pattern = pattern.replace(/\?.*/, "");
|
||||
//else
|
||||
// pattern = pattern.replace(/\/.*/, "");
|
||||
|
||||
searchPattern = pattern;
|
||||
|
||||
// links only search - \l wins if both modifiers specified
|
||||
if (/\\l/.test(pattern))
|
||||
linksOnly = true;
|
||||
else if (/\L/.test(pattern))
|
||||
linksOnly = false;
|
||||
else if (options["linksearch"])
|
||||
linksOnly = true;
|
||||
else
|
||||
linksOnly = false;
|
||||
|
||||
// strip links-only modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// case sensitivity - \c wins if both modifiers specified
|
||||
if (/\c/.test(pattern))
|
||||
caseSensitive = false;
|
||||
else if (/\C/.test(pattern))
|
||||
caseSensitive = true;
|
||||
else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
|
||||
caseSensitive = true;
|
||||
else if (options["ignorecase"])
|
||||
caseSensitive = false;
|
||||
else
|
||||
caseSensitive = true;
|
||||
|
||||
// strip case-sensitive modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// remove any modifier escape \
|
||||
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
|
||||
|
||||
searchString = pattern;
|
||||
}
|
||||
this._found = false; // true if the last search was successful
|
||||
this._backwards = false; // currently searching backwards
|
||||
this._searchString = ""; // current search string (without modifiers)
|
||||
this._searchPattern = ""; // current search string (includes modifiers)
|
||||
this._lastSearchPattern = ""; // the last searched pattern (includes modifiers)
|
||||
this._lastSearchString = ""; // the last searched string (without modifiers)
|
||||
this._lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
||||
this._caseSensitive = false; // search string is case sensitive
|
||||
this._linksOnly = false; // search is limited to link text only
|
||||
|
||||
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
||||
* methods were unstable, and changed. The new version is not remotely
|
||||
@@ -108,37 +54,33 @@ function Finder() //{{{
|
||||
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
||||
* Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
|
||||
*/
|
||||
var highlighter = {
|
||||
this._highlighter = {
|
||||
|
||||
doc: null,
|
||||
|
||||
spans: [],
|
||||
|
||||
search: function (aWord, matchCase)
|
||||
{
|
||||
search: function (aWord, matchCase) {
|
||||
var finder = services.create("find");
|
||||
if (matchCase !== undefined)
|
||||
finder.caseSensitive = matchCase;
|
||||
self._caseSensitive = matchCase;
|
||||
|
||||
var range;
|
||||
while ((range = finder.Find(aWord, this.searchRange, this.startPt, this.endPt)))
|
||||
yield range;
|
||||
},
|
||||
|
||||
highlightDoc: function highlightDoc(win, aWord)
|
||||
{
|
||||
highlightDoc: function highlightDoc(win, aWord) {
|
||||
this.doc = content.document; // XXX
|
||||
Array.forEach(win.frames, function (frame) highlighter.highlightDoc(frame, aWord));
|
||||
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 = highlighter.spans;
|
||||
for (let i = elems.length; --i >= 0;)
|
||||
{
|
||||
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;
|
||||
@@ -174,8 +116,7 @@ function Finder() //{{{
|
||||
|
||||
liberator.interrupted = false;
|
||||
let n = 0;
|
||||
for (let retRange in this.search(aWord, caseSensitive))
|
||||
{
|
||||
for (let retRange in this.search(aWord, this._caseSensitive)) {
|
||||
// Highlight
|
||||
var nodeSurround = baseNode.cloneNode(true);
|
||||
var node = this.highlight(retRange, nodeSurround);
|
||||
@@ -189,8 +130,7 @@ function Finder() //{{{
|
||||
}
|
||||
},
|
||||
|
||||
highlight: function highlight(aRange, aNode)
|
||||
{
|
||||
highlight: function highlight(aRange, aNode) {
|
||||
var startContainer = aRange.startContainer;
|
||||
var startOffset = aRange.startOffset;
|
||||
var endOffset = aRange.endOffset;
|
||||
@@ -206,15 +146,11 @@ function Finder() //{{{
|
||||
/**
|
||||
* Clears all search highlighting.
|
||||
*/
|
||||
clear: function ()
|
||||
{
|
||||
this.spans.forEach(function (span)
|
||||
{
|
||||
if (span.parentNode)
|
||||
{
|
||||
clear: function () {
|
||||
this.spans.forEach(function (span) {
|
||||
if (span.parentNode) {
|
||||
let el = span.firstChild;
|
||||
while (el)
|
||||
{
|
||||
while (el) {
|
||||
span.removeChild(el);
|
||||
span.parentNode.insertBefore(el, span);
|
||||
el = span.firstChild;
|
||||
@@ -227,17 +163,271 @@ function Finder() //{{{
|
||||
|
||||
isHighlighted: function (doc) this.doc == doc && this.spans.length > 0
|
||||
};
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
// set searchString, searchPattern, caseSensitive, linksOnly
|
||||
processUserPattern: function (pattern) {
|
||||
//// strip off pattern terminator and offset
|
||||
//if (backwards)
|
||||
// pattern = pattern.replace(/\?.*/, "");
|
||||
//else
|
||||
// pattern = pattern.replace(/\/.*/, "");
|
||||
|
||||
this._searchPattern = pattern;
|
||||
|
||||
// links only search - \l wins if both modifiers specified
|
||||
if (/\\l/.test(pattern))
|
||||
this._linksOnly = true;
|
||||
else if (/\L/.test(pattern))
|
||||
this._linksOnly = false;
|
||||
else if (options["linksearch"])
|
||||
this._linksOnly = true;
|
||||
else
|
||||
this._linksOnly = false;
|
||||
|
||||
// strip links-only modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// case sensitivity - \c wins if both modifiers specified
|
||||
if (/\c/.test(pattern))
|
||||
this._caseSensitive = false;
|
||||
else if (/\C/.test(pattern))
|
||||
this._caseSensitive = true;
|
||||
else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
|
||||
this._caseSensitive = true;
|
||||
else if (options["ignorecase"])
|
||||
this._caseSensitive = false;
|
||||
else
|
||||
this._caseSensitive = true;
|
||||
|
||||
// strip case-sensitive modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// remove any modifier escape \
|
||||
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
|
||||
|
||||
this._searchString = pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this._backwards = mode == modes.SEARCH_BACKWARD;
|
||||
commandline.open(this._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;
|
||||
|
||||
this._processUserPattern(str);
|
||||
fastFind.caseSensitive = this._caseSensitive;
|
||||
this._found = fastFind.find(this._searchString, this._linksOnly) != Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
||||
|
||||
if (!this._found)
|
||||
setTimeout(function () liberator.echoerr("E486: Pattern not found: " + this._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 != this._lastSearchString)
|
||||
this.find(this._lastSearchString);
|
||||
|
||||
let up = reverse ? !this._lastSearchBackwards : this._lastSearchBackwards;
|
||||
let result = getBrowser().fastFind.findAgain(up, this._linksOnly);
|
||||
|
||||
if (result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
|
||||
liberator.echoerr("E486: Pattern not found: " + this._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 ? "?" : "/") + this._lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
||||
|
||||
if (options["hlsearch"])
|
||||
this.highlight(this._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")
|
||||
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)
|
||||
{
|
||||
"boolean", "false", {
|
||||
setter: function (value) {
|
||||
if (value)
|
||||
finder.highlight();
|
||||
else
|
||||
@@ -262,236 +452,7 @@ function Finder() //{{{
|
||||
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"])
|
||||
this.highlight(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 (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();
|
||||
}
|
||||
};
|
||||
//}}}
|
||||
} //}}}
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
// 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;
|
||||
var frag = document.location.hash.substr(1);
|
||||
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:
|
||||
1148
common/content/io.js
1148
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"]
|
||||
.getService(Components.interfaces.mozIJSSubScriptLoader);
|
||||
function load(script)
|
||||
{
|
||||
for (let [i, base] in Iterator(prefix))
|
||||
{
|
||||
try
|
||||
{
|
||||
function load(script) {
|
||||
for (let [i, base] in Iterator(prefix)) {
|
||||
try {
|
||||
loader.loadSubScript(base + script, modules);
|
||||
return;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
if (i + 1 < prefix.length)
|
||||
continue;
|
||||
if (Components.utils.reportError)
|
||||
Components.utils.reportError(e);
|
||||
dump("liberator: Loading script " + script + ": " + e + "\n");
|
||||
dump(e.stack + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Components.utils.import("resource://liberator/storage.jsm", modules);
|
||||
|
||||
let prefix = [BASE];
|
||||
|
||||
["services.js",
|
||||
"liberator.js",
|
||||
"configbase.js",
|
||||
"config.js"].forEach(load);
|
||||
modules.config.__proto__ = modules.configbase;
|
||||
|
||||
["util.js",
|
||||
"style.js",
|
||||
["base.js",
|
||||
"modules.js",
|
||||
"autocommands.js",
|
||||
"buffer.js",
|
||||
"commandline.js",
|
||||
"commands.js",
|
||||
"completion.js",
|
||||
"config.js",
|
||||
"configbase.js",
|
||||
"liberator.js",
|
||||
"editor.js",
|
||||
"events.js",
|
||||
"finder.js",
|
||||
"hints.js",
|
||||
"io.js",
|
||||
"mappings.js",
|
||||
"marks.js",
|
||||
"modes.js",
|
||||
"options.js",
|
||||
"services.js",
|
||||
"statusline.js",
|
||||
"style.js",
|
||||
"template.js",
|
||||
"ui.js"].forEach(load);
|
||||
"util.js",
|
||||
].forEach(load);
|
||||
modules.config.__proto__ = modules.configbase;
|
||||
|
||||
prefix.unshift("chrome://" + modules.config.name.toLowerCase() + "/content/");
|
||||
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"?>
|
||||
<!DOCTYPE overlay SYSTEM "liberator.dtd" [
|
||||
<!ENTITY liberator.content "chrome://liberator/content/">
|
||||
<!ENTITY and "&&">
|
||||
]>
|
||||
|
||||
<overlay id="liberator"
|
||||
@@ -28,8 +29,8 @@
|
||||
</stringbundleset>
|
||||
|
||||
<keyset id="mainKeyset">
|
||||
<key id="key_open_vimbar" key=":" oncommand="liberator.modules.commandline.open(':', '', liberator.modules.modes.EX);" modifiers=""/>
|
||||
<key id="key_stop" keycode="VK_ESCAPE" oncommand="liberator.modules.events.onEscape();"/>
|
||||
<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="window.liberator ∧ liberator.modules.events.onEscape();"/>
|
||||
<!-- other keys are handled inside the event loop in events.js -->
|
||||
</keyset>
|
||||
|
||||
@@ -42,24 +43,24 @@
|
||||
<commandset id="onVimperatorFocus"
|
||||
commandupdater="true"
|
||||
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"
|
||||
commandupdater="true"
|
||||
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,
|
||||
therefore we need to put them into a <vbox> for which that works just fine -->
|
||||
<vbox class="liberator-container" hidden="false" collapsed="true">
|
||||
<iframe id="liberator-multiline-output" src="chrome://liberator/content/buffer.xhtml"
|
||||
flex="1" hidden="false" collapsed="false"
|
||||
onclick="liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
onclick="window.liberator ∧ liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
</vbox>
|
||||
|
||||
<vbox class="liberator-container" hidden="false" collapsed="true">
|
||||
<iframe id="liberator-completions" src="chrome://liberator/content/buffer.xhtml"
|
||||
flex="1" hidden="false" collapsed="false"
|
||||
onclick="liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
onclick="window.liberator ∧ liberator.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
</vbox>
|
||||
|
||||
<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">
|
||||
<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"
|
||||
oninput="liberator.modules.commandline.onEvent(event);"
|
||||
onkeyup="liberator.modules.commandline.onEvent(event);"
|
||||
onfocus="liberator.modules.commandline.onEvent(event);"
|
||||
onblur="liberator.modules.commandline.onEvent(event);"/>
|
||||
oninput="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
onkeyup="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
onfocus="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
onblur="window.liberator ∧ liberator.modules.commandline.onEvent(event);"/>
|
||||
</hbox>
|
||||
</stack>
|
||||
|
||||
<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"
|
||||
onkeypress="liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||
oninput="liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||
onblur="liberator.modules.commandline.onMultilineInputEvent(event);"/>
|
||||
onkeypress="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||
oninput="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"
|
||||
onblur="window.liberator ∧ liberator.modules.commandline.onMultilineInputEvent(event);"/>
|
||||
</vbox>
|
||||
|
||||
</window>
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
* @optional
|
||||
* @private
|
||||
*/
|
||||
function Map(modes, keys, description, action, extraInfo) //{{{
|
||||
{
|
||||
const Map = Class("Map", {
|
||||
init: function (modes, keys, description, action, extraInfo) {
|
||||
modes = Array.concat(modes);
|
||||
|
||||
if (!extraInfo)
|
||||
@@ -70,9 +70,7 @@ function Map(modes, keys, description, action, extraInfo) //{{{
|
||||
* plugin authors should create only user mappings.
|
||||
*/
|
||||
this.user = extraInfo.user || false;
|
||||
}
|
||||
|
||||
Map.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* mapping. E.g. "a" for "ma"
|
||||
*/
|
||||
execute: function (motion, count, argument)
|
||||
{
|
||||
execute: function (motion, count, argument) {
|
||||
let args = [];
|
||||
|
||||
if (this.motion)
|
||||
@@ -112,60 +109,46 @@ Map.prototype = {
|
||||
return liberator.trapErrors(repeat);
|
||||
}
|
||||
|
||||
}; //}}}
|
||||
});
|
||||
|
||||
/**
|
||||
* @instance mappings
|
||||
*/
|
||||
function Mappings() //{{{
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
const Mappings = Module("mappings", {
|
||||
requires: ["modes"],
|
||||
|
||||
var main = []; // default mappings
|
||||
var user = []; // user created mappings
|
||||
init: function () {
|
||||
this._main = []; // default mappings
|
||||
this._user = []; // user created mappings
|
||||
},
|
||||
|
||||
for (let mode in modes)
|
||||
{
|
||||
main[mode] = [];
|
||||
user[mode] = [];
|
||||
}
|
||||
|
||||
function addMap(map)
|
||||
{
|
||||
let where = map.user ? user : main;
|
||||
_addMap: function (map) {
|
||||
let where = map.user ? this._user : this._main;
|
||||
map.modes.forEach(function (mode) {
|
||||
if (!(mode in where))
|
||||
where[mode] = [];
|
||||
where[mode].push(map);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
function getMap(mode, cmd, stack)
|
||||
{
|
||||
_getMap: function (mode, cmd, stack) {
|
||||
let maps = stack[mode] || [];
|
||||
|
||||
for (let [, map] in Iterator(maps))
|
||||
{
|
||||
for (let [, map] in Iterator(maps)) {
|
||||
if (map.hasName(cmd))
|
||||
return map;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
function removeMap(mode, cmd)
|
||||
{
|
||||
let maps = user[mode] || [];
|
||||
_removeMap: function (mode, cmd) {
|
||||
let maps = this._user[mode] || [];
|
||||
let names;
|
||||
|
||||
for (let [i, map] in Iterator(maps))
|
||||
{
|
||||
for (let [j, name] in Iterator(map.names))
|
||||
{
|
||||
if (name == cmd)
|
||||
{
|
||||
for (let [i, map] in Iterator(maps)) {
|
||||
for (let [j, name] in Iterator(map.names)) {
|
||||
if (name == cmd) {
|
||||
map.names.splice(j, 1);
|
||||
if (map.names.length == 0)
|
||||
maps.splice(i, 1);
|
||||
@@ -173,28 +156,221 @@ 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
|
||||
function mappingsIterator(modes, stack)
|
||||
{
|
||||
_mappingsIterator: function (modes, stack) {
|
||||
modes = modes.slice();
|
||||
return (map for ([i, map] in Iterator(stack[modes.shift()]))
|
||||
if (modes.every(function (mode) stack[mode].some(
|
||||
function (m) m.rhs == map.rhs && m.names[0] == map.names[0]))))
|
||||
},
|
||||
|
||||
// NOTE: just normal mode for now
|
||||
/** @property {Iterator(Map)} @private */
|
||||
__iterator__: function () this._mappingsIterator([modes.NORMAL], this._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) this._mappingsIterator(mode, this._user),
|
||||
|
||||
addMode: function (mode) {
|
||||
if (!(mode in this._user || mode in this._main)) {
|
||||
this._main[mode] = [];
|
||||
this._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) {
|
||||
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);
|
||||
}
|
||||
|
||||
function addMapCommands(ch, modes, modeDescription)
|
||||
this._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 this._getMap(mode, cmd, this._user) || this._getMap(mode, cmd, this._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 this._getMap(mode, cmd, this._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 = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) this._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) {
|
||||
this._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) {
|
||||
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>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}, {
|
||||
}, {
|
||||
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)
|
||||
{
|
||||
function map(args, modes, noremap) {
|
||||
if (!args.length) {
|
||||
mappings.list(modes);
|
||||
return;
|
||||
}
|
||||
@@ -202,17 +378,15 @@ function Mappings() //{{{
|
||||
let [lhs, rhs] = args;
|
||||
|
||||
if (!rhs) // list the mapping
|
||||
mappings.list(modes, expandLeader(lhs));
|
||||
else
|
||||
{
|
||||
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); },
|
||||
{
|
||||
function (count) { events.feedkeys((count > -1 ? count : "") + this.rhs, this.noremap, this.silent); }, {
|
||||
count: true,
|
||||
rhs: events.canonicalKeys(rhs),
|
||||
noremap: !!noremap,
|
||||
@@ -224,8 +398,7 @@ function Mappings() //{{{
|
||||
modeDescription = modeDescription ? " in " + modeDescription + " mode" : "";
|
||||
|
||||
// :map, :noremap => NORMAL + VISUAL modes
|
||||
function isMultiMode(map, cmd)
|
||||
{
|
||||
function isMultiMode(map, cmd) {
|
||||
return map.modes.indexOf(modules.modes.NORMAL) >= 0
|
||||
&& map.modes.indexOf(modules.modes.VISUAL) >= 0
|
||||
&& /^[nv](nore)?map$/.test(cmd);
|
||||
@@ -237,8 +410,7 @@ function Mappings() //{{{
|
||||
[["<silent>", "<Silent>"], commands.OPTION_NOARG]
|
||||
],
|
||||
literal: 1,
|
||||
serial: function ()
|
||||
{
|
||||
serial: function () {
|
||||
let noremap = this.name.indexOf("noremap") > -1;
|
||||
return [
|
||||
{
|
||||
@@ -247,7 +419,7 @@ function Mappings() //{{{
|
||||
arguments: [map.names[0]],
|
||||
literalArg: map.rhs
|
||||
}
|
||||
for (map in mappingsIterator(modes, user))
|
||||
for (map in this._mappingsIterator(modes, this._user))
|
||||
if (map.rhs && map.noremap == noremap && !isMultiMode(map, this.name))
|
||||
];
|
||||
}
|
||||
@@ -270,15 +442,12 @@ function Mappings() //{{{
|
||||
|
||||
commands.add([ch + "unm[ap]"],
|
||||
"Remove a mapping" + modeDescription,
|
||||
function (args)
|
||||
{
|
||||
function (args) {
|
||||
args = args[0];
|
||||
|
||||
let found = false;
|
||||
for (let [, mode] in Iterator(modes))
|
||||
{
|
||||
if (mappings.hasMap(mode, args))
|
||||
{
|
||||
for (let [, mode] in Iterator(modes)) {
|
||||
if (mappings.hasMap(mode, args)) {
|
||||
mappings.remove(mode, args);
|
||||
found = true;
|
||||
}
|
||||
@@ -292,10 +461,6 @@ function Mappings() //{{{
|
||||
});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
|
||||
|
||||
for (let mode in modes.mainModes)
|
||||
@@ -303,22 +468,17 @@ function Mappings() //{{{
|
||||
addMapCommands(mode.char,
|
||||
[m.mask for (m in modes.mainModes) if (m.char == mode.char)],
|
||||
[mode.disp.toLowerCase()]);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMPLETIONS /////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
liberator.registerObserver("load_completion", function () {
|
||||
},
|
||||
completion: function () {
|
||||
completion.setFunctionCompleter(mappings.get,
|
||||
[
|
||||
null,
|
||||
function (context, obj, args)
|
||||
{
|
||||
function (context, obj, args) {
|
||||
let mode = args[0];
|
||||
return util.Array.flatten(
|
||||
[
|
||||
[[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
|
||||
modes = modes || [modules.modes.NORMAL];
|
||||
|
||||
if (args.completeArg == 0)
|
||||
{
|
||||
if (args.completeArg == 0) {
|
||||
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
|
||||
context.completions = maps;
|
||||
}
|
||||
};
|
||||
},
|
||||
modes: function () {
|
||||
for (let mode in modes) {
|
||||
this._main[mode] = [];
|
||||
this._user[mode] = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
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:
|
||||
|
||||
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 */
|
||||
|
||||
const modes = (function () //{{{
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
const Modes = Module("modes", {
|
||||
requires: ["util"],
|
||||
|
||||
var main = 1; // NORMAL
|
||||
var extended = 0; // NONE
|
||||
init: function () {
|
||||
this._main = 1; // NORMAL
|
||||
this._extended = 0; // NONE
|
||||
|
||||
var lastShown = null;
|
||||
this._lastShown = null;
|
||||
|
||||
var passNextKey = false;
|
||||
var passAllKeys = false;
|
||||
var isRecording = false;
|
||||
var isReplaying = false; // playing a macro
|
||||
this._passNextKey = false;
|
||||
this._passAllKeys = false;
|
||||
this._isRecording = false;
|
||||
this._isReplaying = false; // playing a macro
|
||||
|
||||
var modeStack = [];
|
||||
this._modeStack = [];
|
||||
|
||||
function getModeMessage()
|
||||
{
|
||||
if (passNextKey && !passAllKeys)
|
||||
this._mainModes = [self.NONE];
|
||||
this._lastMode = 0;
|
||||
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) --";
|
||||
else if (passAllKeys && !passNextKey)
|
||||
else if (this._passAllKeys && !this._passNextKey)
|
||||
return "-- PASS THROUGH --";
|
||||
|
||||
// when recording a macro
|
||||
@@ -38,34 +64,30 @@ const modes = (function () //{{{
|
||||
macromode = "replaying";
|
||||
|
||||
let ext = "";
|
||||
if (extended & modes.MENU) // TODO: desirable?
|
||||
if (this._extended & modes.MENU) // TODO: desirable?
|
||||
ext += " (menu)";
|
||||
ext += " --" + macromode;
|
||||
|
||||
if (main in modeMap && typeof modeMap[main].display == "function")
|
||||
return "-- " + modeMap[main].display() + ext;
|
||||
if (this._main in this._modeMap && typeof this._modeMap[this._main].display == "function")
|
||||
return "-- " + this._modeMap[this._main].display() + ext;
|
||||
return macromode;
|
||||
}
|
||||
},
|
||||
|
||||
// NOTE: Pay attention that you don't run into endless loops
|
||||
// Usually you should only indicate to leave a special mode like HINTS
|
||||
// by calling modes.reset() and adding the stuff which is needed
|
||||
// for its cleanup here
|
||||
function handleModeChange(oldMode, newMode, oldExtended)
|
||||
{
|
||||
_handleModeChange: function (oldMode, newMode, oldExtended) {
|
||||
|
||||
switch (oldMode)
|
||||
{
|
||||
switch (oldMode) {
|
||||
case modes.TEXTAREA:
|
||||
case modes.INSERT:
|
||||
editor.unselectText();
|
||||
break;
|
||||
|
||||
case modes.VISUAL:
|
||||
if (newMode == modes.CARET)
|
||||
{
|
||||
try
|
||||
{ // clear any selection made; a simple if (selection) does not work
|
||||
if (newMode == modes.CARET) {
|
||||
try { // clear any selection made; a simple if (selection) does not work
|
||||
let selection = window.content.getSelection();
|
||||
selection.collapseToStart();
|
||||
}
|
||||
@@ -87,8 +109,7 @@ const modes = (function () //{{{
|
||||
break;
|
||||
}
|
||||
|
||||
if (newMode == modes.NORMAL)
|
||||
{
|
||||
if (newMode == modes.NORMAL) {
|
||||
// disable caret mode when we want to switch to normal mode
|
||||
if (options.getPref("accessibility.browsewithcaret"))
|
||||
options.setPref("accessibility.browsewithcaret", false);
|
||||
@@ -96,34 +117,26 @@ const modes = (function () //{{{
|
||||
statusline.updateUrl();
|
||||
liberator.focusContent(true);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
const self = {
|
||||
},
|
||||
|
||||
NONE: 0,
|
||||
|
||||
__iterator__: function () util.Array.itervalues(this.all),
|
||||
|
||||
get all() mainModes.slice(),
|
||||
get all() this._mainModes.slice(),
|
||||
|
||||
get mainModes() (mode for ([k, mode] in Iterator(modeMap)) if (!mode.extended && mode.name == k)),
|
||||
get mainModes() (mode for ([k, mode] in Iterator(modes._modeMap)) if (!mode.extended && mode.name == k)),
|
||||
|
||||
get mainMode() modeMap[main],
|
||||
get mainMode() this._modeMap[this._main],
|
||||
|
||||
addMode: function (name, extended, options)
|
||||
{
|
||||
addMode: function (name, extended, options) {
|
||||
let disp = name.replace("_", " ", "g");
|
||||
this[name] = 1 << lastMode++;
|
||||
if (typeof extended == "object")
|
||||
{
|
||||
this[name] = 1 << this._lastMode++;
|
||||
if (typeof extended == "object") {
|
||||
options = extended;
|
||||
extended = false;
|
||||
}
|
||||
modeMap[name] = modeMap[this[name]] = util.extend({
|
||||
this._modeMap[name] = this._modeMap[this[name]] = util.extend({
|
||||
extended: extended,
|
||||
count: true,
|
||||
input: false,
|
||||
@@ -131,65 +144,59 @@ const modes = (function () //{{{
|
||||
name: name,
|
||||
disp: disp
|
||||
}, options);
|
||||
modeMap[name].display = modeMap[name].display || function () disp;
|
||||
this._modeMap[name].display = this._modeMap[name].display || function () disp;
|
||||
if (!extended)
|
||||
mainModes.push(this[name]);
|
||||
this._mainModes.push(this[name]);
|
||||
if ("mappings" in modules)
|
||||
mappings.addMode(this[name]);
|
||||
},
|
||||
|
||||
getMode: function (name) modeMap[name],
|
||||
getMode: function (name) this._modeMap[name],
|
||||
|
||||
// show the current mode string in the command line
|
||||
show: function ()
|
||||
{
|
||||
show: function () {
|
||||
let msg = "";
|
||||
if (options["showmode"])
|
||||
msg = getModeMessage();
|
||||
msg = this._getModeMessage();
|
||||
|
||||
commandline.echo(msg, "ModeMsg", commandline.FORCE_SINGLELINE);
|
||||
},
|
||||
|
||||
// add/remove always work on the extended mode only
|
||||
add: function (mode)
|
||||
{
|
||||
extended |= mode;
|
||||
// add/remove always work on the this._extended mode only
|
||||
add: function (mode) {
|
||||
this._extended |= mode;
|
||||
this.show();
|
||||
},
|
||||
|
||||
// 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 || main == mainMode && extended == extendedMode);
|
||||
// if a main mode is set, the extended is always cleared
|
||||
let oldMain = main, oldExtended = extended;
|
||||
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")
|
||||
extended = extendedMode;
|
||||
if (typeof mainMode === "number")
|
||||
{
|
||||
main = mainMode;
|
||||
this._extended = extendedMode;
|
||||
if (typeof mainMode === "number") {
|
||||
this._main = mainMode;
|
||||
if (!extendedMode)
|
||||
extended = modes.NONE;
|
||||
this._extended = modes.NONE;
|
||||
|
||||
if (main != oldMain)
|
||||
handleModeChange(oldMain, mainMode, oldExtended);
|
||||
if (this._main != oldMain)
|
||||
this._handleModeChange(oldMain, mainMode, oldExtended);
|
||||
}
|
||||
liberator.triggerObserver("modeChange", [oldMain, oldExtended], [main, extended], stack);
|
||||
liberator.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended], stack);
|
||||
|
||||
if (!silent)
|
||||
this.show();
|
||||
},
|
||||
|
||||
push: function (mainMode, extendedMode, silent)
|
||||
{
|
||||
modeStack.push([main, extended]);
|
||||
this.set(mainMode, extendedMode, silent, { push: modeStack[modeStack.length - 1] });
|
||||
push: function (mainMode, extendedMode, silent) {
|
||||
this._modeStack.push([this._main, this._extended]);
|
||||
this.set(mainMode, extendedMode, silent, { push: this._modeStack[this._modeStack.length - 1] });
|
||||
},
|
||||
|
||||
pop: function (silent)
|
||||
{
|
||||
let a = modeStack.pop();
|
||||
pop: function (silent) {
|
||||
let a = this._modeStack.pop();
|
||||
if (a)
|
||||
this.set(a[0], a[1], silent, { pop: a });
|
||||
else
|
||||
@@ -198,8 +205,7 @@ const modes = (function () //{{{
|
||||
|
||||
// TODO: Deprecate this in favor of addMode? --Kris
|
||||
// Ya --djk
|
||||
setCustomMode: function (modestr, oneventfunc, stopfunc)
|
||||
{
|
||||
setCustomMode: function (modestr, oneventfunc, stopfunc) {
|
||||
// TODO this.plugin[id]... ('id' maybe submode or what..)
|
||||
plugins.mode = modestr;
|
||||
plugins.onEvent = oneventfunc;
|
||||
@@ -207,74 +213,38 @@ const modes = (function () //{{{
|
||||
},
|
||||
|
||||
// keeps recording state
|
||||
reset: function (silent)
|
||||
{
|
||||
modeStack = [];
|
||||
reset: function (silent) {
|
||||
this._modeStack = [];
|
||||
if (config.isComposeWindow)
|
||||
this.set(modes.COMPOSE, modes.NONE, silent);
|
||||
else
|
||||
this.set(modes.NORMAL, modes.NONE, silent);
|
||||
},
|
||||
|
||||
remove: function (mode)
|
||||
{
|
||||
if (extended & mode)
|
||||
{
|
||||
extended &= ~mode;
|
||||
remove: function (mode) {
|
||||
if (this._extended & mode) {
|
||||
this._extended &= ~mode;
|
||||
this.show();
|
||||
}
|
||||
},
|
||||
|
||||
get passNextKey() passNextKey,
|
||||
set passNextKey(value) { passNextKey = value; this.show(); },
|
||||
get passNextKey() this._passNextKey,
|
||||
set passNextKey(value) { this._passNextKey = value; this.show(); },
|
||||
|
||||
get passAllKeys() passAllKeys,
|
||||
set passAllKeys(value) { passAllKeys = value; this.show(); },
|
||||
get passAllKeys() this._passAllKeys,
|
||||
set passAllKeys(value) { this._passAllKeys = value; this.show(); },
|
||||
|
||||
get isRecording() isRecording,
|
||||
set isRecording(value) { isRecording = value; this.show(); },
|
||||
get isRecording() this._isRecording,
|
||||
set isRecording(value) { this._isRecording = value; this.show(); },
|
||||
|
||||
get isReplaying() isReplaying,
|
||||
set isReplaying(value) { isReplaying = value; this.show(); },
|
||||
get isReplaying() this._isReplaying,
|
||||
set isReplaying(value) { this._isReplaying = value; this.show(); },
|
||||
|
||||
get main() main,
|
||||
get main() this._main,
|
||||
set main(value) { this.set(value); },
|
||||
|
||||
get extended() extended,
|
||||
get extended() this._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:
|
||||
|
||||
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,100 +13,88 @@
|
||||
// FIXME:
|
||||
// - finish 1.9.0 support if we're going to support sanitizing in Xulmus
|
||||
|
||||
function Sanitizer() //{{{
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
const Sanitizer = Module("sanitizer", {
|
||||
requires: ["liberator"],
|
||||
|
||||
const local = {}; // XXX: is there some reason liberator.loadModule doesn't create modules with new?
|
||||
services.get("subscriptLoader").loadSubScript("chrome://browser/content/sanitize.js", local);
|
||||
const Sanitizer = local.Sanitizer;
|
||||
init: function () {
|
||||
const self = this;
|
||||
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
|
||||
if (/^1.9.1/.test(services.get("xulAppInfo").platformVersion))
|
||||
self.prefDomain = "privacy.cpd.";
|
||||
else
|
||||
self.prefDomain = "privacy.item.";
|
||||
|
||||
self.prefDomain2 = "extensions.liberator.privacy.cpd.";
|
||||
},
|
||||
|
||||
// Largely ripped from from browser/base/content/sanitize.js so we can override
|
||||
// the pref strategy without stepping on the global prefs namespace.
|
||||
sanitize: function () {
|
||||
const prefService = services.get("pref");
|
||||
let branch = prefService.getBranch(this.prefDomain);
|
||||
let branch2 = prefService.getBranch(this.prefDomain2);
|
||||
let errors = null;
|
||||
|
||||
function prefSet(name) {
|
||||
try {
|
||||
return branch.getBoolPref(name);
|
||||
}
|
||||
catch (e) {
|
||||
return branch2.getBoolPref(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the range of times to clear
|
||||
if (this.ignoreTimespan)
|
||||
var range = null; // If we ignore timespan, clear everything
|
||||
else
|
||||
range = this.range || Sanitizer.getClearRange();
|
||||
|
||||
for (let itemName in this.items) {
|
||||
let item = this.items[itemName];
|
||||
item.range = range;
|
||||
|
||||
if ("clear" in item && item.canClear && prefSet(itemName)) {
|
||||
liberator.log("Sanitizing " + itemName + " items...");
|
||||
// Some of these clear() may raise exceptions (see bug #265028)
|
||||
// to sanitize as much as possible, we catch and store them,
|
||||
// rather than fail fast.
|
||||
// Callers should check returned errors and give user feedback
|
||||
// about items that could not be sanitized
|
||||
try {
|
||||
item.clear();
|
||||
}
|
||||
catch (e) {
|
||||
if (!errors)
|
||||
errors = {};
|
||||
errors[itemName] = e;
|
||||
dump("Error sanitizing " + itemName + ": " + e + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
},
|
||||
|
||||
get prefNames() util.Array.flatten([this.prefDomain, this.prefDomain2].map(options.allPrefs)),
|
||||
}, {
|
||||
prefArgList: [["commandLine", "commandline"],
|
||||
["offlineApps", "offlineapps"],
|
||||
["siteSettings", "sitesettings"]];
|
||||
|
||||
function prefToArg(pref)
|
||||
{
|
||||
["siteSettings", "sitesettings"]],
|
||||
prefToArg: function (pref) {
|
||||
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;
|
||||
return util.Array.toObject(Sanitizer.prefArgList)[pref] || pref;
|
||||
},
|
||||
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 ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
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)
|
||||
{
|
||||
function (args) {
|
||||
if (options['private'])
|
||||
return void liberator.echomsg("Cannot sanitize items in private mode");
|
||||
|
||||
@@ -115,37 +103,30 @@ function Sanitizer() //{{{
|
||||
sanitizer.range = Sanitizer.getClearRange(timespan);
|
||||
sanitizer.ignoreTimespan = !sanitizer.range;
|
||||
|
||||
if (args.bang)
|
||||
{
|
||||
if (args.bang) {
|
||||
liberator.assert(args.length == 0, "E488: Trailing characters");
|
||||
|
||||
liberator.log("Sanitizing all items in 'sanitizeitems'...");
|
||||
|
||||
let errors = sanitizer.sanitize();
|
||||
|
||||
if (errors)
|
||||
{
|
||||
if (errors) {
|
||||
for (let item in errors)
|
||||
liberator.echoerr("Error sanitizing " + item + ": " + errors[item]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
liberator.assert(args.length > 0, "E471: Argument required");
|
||||
|
||||
for (let [, item] in Iterator(args.map(argToPref)))
|
||||
{
|
||||
for (let [, item] in Iterator(args.map(Sanitizer.argToPref))) {
|
||||
liberator.log("Sanitizing " + item + " items...");
|
||||
|
||||
if (sanitizer.canClearItem(item))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sanitizer.canClearItem(item)) {
|
||||
try {
|
||||
sanitizer.items[item].range = sanitizer.range;
|
||||
sanitizer.clearItem(item);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
liberator.echoerr("Error sanitizing " + item + ": " + e);
|
||||
}
|
||||
}
|
||||
@@ -168,31 +149,18 @@ function Sanitizer() //{{{
|
||||
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.";
|
||||
},
|
||||
options: function () {
|
||||
const self = this;
|
||||
|
||||
// add liberator-specific private items
|
||||
[
|
||||
{
|
||||
name: "commandLine",
|
||||
action: function ()
|
||||
{
|
||||
action: function () {
|
||||
let stores = ["command", "search"];
|
||||
|
||||
if (self.range)
|
||||
{
|
||||
if (self.range) {
|
||||
stores.forEach(function (store) {
|
||||
storage["history-" + store].mutate("filter", function (item) {
|
||||
let timestamp = item.timestamp * 1000;
|
||||
@@ -210,8 +178,7 @@ function Sanitizer() //{{{
|
||||
},
|
||||
{
|
||||
name: "marks",
|
||||
action: function ()
|
||||
{
|
||||
action: function () {
|
||||
storage["local-marks"].clear();
|
||||
storage["url-marks"].clear();
|
||||
}
|
||||
@@ -229,12 +196,10 @@ function Sanitizer() //{{{
|
||||
});
|
||||
|
||||
// call Sanitize autocommand
|
||||
for (let [name, item] in Iterator(self.items))
|
||||
{
|
||||
let arg = prefToArg(name);
|
||||
for (let [name, item] in Iterator(self.items)) {
|
||||
let arg = Sanitizer.prefToArg(name);
|
||||
|
||||
if (item.clear)
|
||||
{
|
||||
if (item.clear) {
|
||||
let func = item.clear;
|
||||
item.clear = function () {
|
||||
autocommands.trigger("Sanitize", { name: arg })
|
||||
@@ -243,70 +208,62 @@ function Sanitizer() //{{{
|
||||
}
|
||||
}
|
||||
|
||||
self.getClearRange = Sanitizer.getClearRange;
|
||||
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);
|
||||
|
||||
// Largely ripped from from browser/base/content/sanitize.js so we can override
|
||||
// the pref strategy without stepping on the global prefs namespace.
|
||||
self.sanitize = function () {
|
||||
const prefService = services.get("pref");
|
||||
let branch = prefService.getBranch(this.prefDomain);
|
||||
let branch2 = prefService.getBranch(this.prefDomain2);
|
||||
let errors = null;
|
||||
|
||||
function prefSet(name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return branch.getBoolPref(name);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return branch2.getBoolPref(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the range of times to clear
|
||||
if (this.ignoreTimespan)
|
||||
var range = null; // If we ignore timespan, clear everything
|
||||
else
|
||||
range = this.range || Sanitizer.getClearRange();
|
||||
|
||||
for (let itemName in this.items)
|
||||
{
|
||||
let item = this.items[itemName];
|
||||
item.range = range;
|
||||
|
||||
if ("clear" in item && item.canClear && prefSet(itemName))
|
||||
{
|
||||
liberator.log("Sanitizing " + itemName + " items...");
|
||||
// Some of these clear() may raise exceptions (see bug #265028)
|
||||
// to sanitize as much as possible, we catch and store them,
|
||||
// rather than fail fast.
|
||||
// Callers should check returned errors and give user feedback
|
||||
// about items that could not be sanitized
|
||||
try
|
||||
{
|
||||
item.clear();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
if (!errors)
|
||||
errors = {};
|
||||
errors[itemName] = e;
|
||||
dump("Error sanitizing " + itemName + ": " + e + "\n");
|
||||
for (let [, value] in Iterator(this.parseValues(values))) {
|
||||
if (Sanitizer.prefToArg(pref) == value) {
|
||||
options.setPref(pref, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
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
|
||||
});
|
||||
|
||||
self.__defineGetter__("prefNames",
|
||||
function () util.Array.flatten([self.prefDomain, self.prefDomain2].map(options.allPrefs)));
|
||||
//}}}
|
||||
|
||||
return self;
|
||||
|
||||
} //}}}
|
||||
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:
|
||||
|
||||
@@ -15,15 +15,46 @@ const Cu = Components.utils;
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function Services()
|
||||
{
|
||||
const classes = {};
|
||||
const services = {};
|
||||
const Services = Module("services", {
|
||||
init: function () {
|
||||
this.classes = {};
|
||||
this.services = {};
|
||||
|
||||
function create(classes, ifaces, meth)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.add("appStartup", "@mozilla.org/toolkit/app-startup;1", Ci.nsIAppStartup);
|
||||
this.add("autoCompleteSearch", "@mozilla.org/autocomplete/search;1?name=history", Ci.nsIAutoCompleteSearch);
|
||||
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"]();
|
||||
if (!ifaces)
|
||||
return res.wrappedJSObject;
|
||||
@@ -31,19 +62,11 @@ function Services()
|
||||
ifaces.forEach(function (iface) res.QueryInterface(iface));
|
||||
return res;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
// liberator.log() is not defined at this time, so just dump any error
|
||||
dump("Service creation failed for '" + classes + "': " + e + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const self = {
|
||||
/* @property {Object} A map of all cached services. */
|
||||
get services() services,
|
||||
|
||||
/* @property {Object} A map of all cached classes. */
|
||||
get classes() classes,
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new XPCOM service to the cache.
|
||||
@@ -55,9 +78,8 @@ function Services()
|
||||
* @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);
|
||||
add: function (name, class, ifaces, meth) {
|
||||
return this.services[name] = this._create(class, ifaces, meth);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -68,9 +90,9 @@ function Services()
|
||||
* @param {nsISupports|nsISupports[]} ifaces The interface or array of
|
||||
* interfaces implemented by this class.
|
||||
*/
|
||||
addClass: function (name, class, ifaces)
|
||||
{
|
||||
return classes[name] = function () create(class, ifaces, "createInstance");
|
||||
addClass: function (name, class, ifaces) {
|
||||
const self = this;
|
||||
return this.classes[name] = function () self._create(class, ifaces, "createInstance");
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -78,50 +100,14 @@ function Services()
|
||||
*
|
||||
* @param {string} name The service's cache key.
|
||||
*/
|
||||
get: function (name) services[name],
|
||||
get: function (name) this.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();
|
||||
create: function (name) this.classes[name]()
|
||||
});
|
||||
|
||||
// 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,8 +231,7 @@ Highlights.prototype.CSS = <![CDATA[
|
||||
*
|
||||
* @author Kris Maglione <maglione.k@gmail.com>
|
||||
*/
|
||||
function Highlights(name, store)
|
||||
{
|
||||
function Highlights(name, store) {
|
||||
let self = this;
|
||||
let highlight = {};
|
||||
let styles = storage.styles;
|
||||
@@ -256,8 +255,7 @@ function Highlights(name, store)
|
||||
this.__iterator__ = function () (highlight[v] for ([k, v] in Iterator(keys())));
|
||||
|
||||
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_-]+)(.*)/);
|
||||
|
||||
if (!(class in highlight))
|
||||
@@ -270,10 +268,8 @@ function Highlights(name, store)
|
||||
newStyle = (style.value || "").replace(/;?\s*$/, "; " + newStyle);
|
||||
if (/^\s*$/.test(newStyle))
|
||||
newStyle = null;
|
||||
if (newStyle == null)
|
||||
{
|
||||
if (style.default == null)
|
||||
{
|
||||
if (newStyle == null) {
|
||||
if (style.default == null) {
|
||||
delete highlight[style.class];
|
||||
styles.removeSheet(true, style.selector);
|
||||
return null;
|
||||
@@ -284,8 +280,7 @@ function Highlights(name, store)
|
||||
|
||||
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
|
||||
.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 + " }";
|
||||
|
||||
let error = styles.addSheet(true, "highlight:" + style.class, style.filter, css, true);
|
||||
@@ -301,8 +296,7 @@ function Highlights(name, store)
|
||||
*
|
||||
* @param {string} class
|
||||
*/
|
||||
this.selector = function (class)
|
||||
{
|
||||
this.selector = function (class) {
|
||||
let [, hl, rest] = class.match(/^(\w*)(.*)/);
|
||||
let pattern = "[liberator|highlight~=" + hl + "]"
|
||||
if (highlight[hl] && highlight[hl].class != class)
|
||||
@@ -314,8 +308,7 @@ function Highlights(name, store)
|
||||
* Clears all highlighting rules. Rules with default values are
|
||||
* reset.
|
||||
*/
|
||||
this.clear = function ()
|
||||
{
|
||||
this.clear = function () {
|
||||
for (let [k, v] in Iterator(highlight))
|
||||
this.set(k, null, true);
|
||||
};
|
||||
@@ -325,8 +318,7 @@ function Highlights(name, store)
|
||||
*
|
||||
* @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, " "))
|
||||
.split("\n").filter(function (s) /\S/.test(s))
|
||||
.forEach(function (style)
|
||||
@@ -340,8 +332,7 @@ function Highlights(name, store)
|
||||
if (old && old.value != old.default)
|
||||
style.value = old.value;
|
||||
});
|
||||
for (let [class, hl] in Iterator(highlight))
|
||||
{
|
||||
for (let [class, hl] in Iterator(highlight)) {
|
||||
if (hl.value == hl.default)
|
||||
this.set(class);
|
||||
}
|
||||
@@ -356,8 +347,7 @@ function Highlights(name, store)
|
||||
*
|
||||
* @author Kris Maglione <maglione.k@gmail.com>
|
||||
*/
|
||||
function Styles(name, store)
|
||||
{
|
||||
function Styles(name, store) {
|
||||
// Can't reference liberator or Components inside Styles --
|
||||
// they're members of the window object, which disappear
|
||||
// with this window.
|
||||
@@ -387,14 +377,12 @@ function Styles(name, store)
|
||||
Sheet.prototype.__defineGetter__("enabled", function () this._enabled);
|
||||
Sheet.prototype.__defineSetter__("enabled", function (on) {
|
||||
this._enabled = Boolean(on);
|
||||
if (on)
|
||||
{
|
||||
if (on) {
|
||||
self.registerSheet(cssUri(this.fullCSS));
|
||||
if (this.agent)
|
||||
self.registerSheet(cssUri(this.fullCSS), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
self.unregisterSheet(cssUri(this.fullCSS));
|
||||
self.unregisterSheet(cssUri(this.fullCSS), true);
|
||||
}
|
||||
@@ -428,8 +416,7 @@ function Styles(name, store)
|
||||
* "*" is matched as a prefix.
|
||||
* @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 names = system ? systemNames : userNames;
|
||||
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);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
sheet.enabled = true;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
return e.echoerr || e;
|
||||
}
|
||||
sheets.push(sheet);
|
||||
@@ -459,8 +444,7 @@ function Styles(name, store)
|
||||
* @param {string or number} sheet The sheet to retrieve. Strings indicate
|
||||
* sheet names, while numbers indicate indices.
|
||||
*/
|
||||
this.get = function get(system, sheet)
|
||||
{
|
||||
this.get = function get(system, sheet) {
|
||||
let sheets = system ? systemSheets : userSheets;
|
||||
let names = system ? systemNames : userNames;
|
||||
if (typeof sheet === "number")
|
||||
@@ -478,8 +462,7 @@ function Styles(name, store)
|
||||
* @param {string} css
|
||||
* @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 names = system ? systemNames : userNames;
|
||||
|
||||
@@ -508,11 +491,9 @@ function Styles(name, store)
|
||||
* @param {string} css
|
||||
* @param {number} index
|
||||
*/
|
||||
this.removeSheet = function (system, name, filter, css, index)
|
||||
{
|
||||
this.removeSheet = function (system, name, filter, css, index) {
|
||||
let self = this;
|
||||
if (arguments.length == 0)
|
||||
{
|
||||
if (arguments.length == 0) {
|
||||
var matches = [system];
|
||||
system = sheet.system;
|
||||
}
|
||||
@@ -531,8 +512,7 @@ function Styles(name, store)
|
||||
if (matches.length == 0)
|
||||
return;
|
||||
|
||||
for (let [, sheet] in Iterator(matches.reverse()))
|
||||
{
|
||||
for (let [, sheet] in Iterator(matches.reverse())) {
|
||||
sheet.enabled = false;
|
||||
if (name)
|
||||
delete names[name];
|
||||
@@ -540,8 +520,7 @@ function Styles(name, store)
|
||||
sheets.splice(sheets.indexOf(sheet), 1);
|
||||
|
||||
/* Re-add if we're only changing the site filter. */
|
||||
if (filter)
|
||||
{
|
||||
if (filter) {
|
||||
let sites = sheet.sites.filter(function (f) f != filter);
|
||||
if (sites.length)
|
||||
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
|
||||
* already registered.
|
||||
*/
|
||||
this.registerSheet = function (uri, agent, reload)
|
||||
{
|
||||
this.registerSheet = function (uri, agent, reload) {
|
||||
if (reload)
|
||||
this.unregisterSheet(uri, agent);
|
||||
uri = ios.newURI(uri, null, null);
|
||||
@@ -572,22 +550,23 @@ function Styles(name, store)
|
||||
*
|
||||
* @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);
|
||||
if (sss.sheetRegistered(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET))
|
||||
sss.unregisterSheet(uri, agent ? sss.AGENT_SHEET : sss.USER_SHEET);
|
||||
};
|
||||
}
|
||||
let (array = util.Array)
|
||||
{
|
||||
util.extend(Styles.prototype, {
|
||||
|
||||
Module("styles", {
|
||||
requires: ["liberator", "storage", "util"],
|
||||
|
||||
init: function () {
|
||||
let (array = util.Array) {
|
||||
update(Styles.prototype, {
|
||||
get sites() array([v.sites for ([k, v] in this.userSheets)]).flatten().uniq().__proto__,
|
||||
completeSite: function (context, content)
|
||||
{
|
||||
completeSite: function (context, content) {
|
||||
context.anchored = false;
|
||||
try
|
||||
{
|
||||
try {
|
||||
context.fork("current", 0, this, function (context) {
|
||||
context.title = ["Current Site"];
|
||||
context.completions = [
|
||||
@@ -604,59 +583,18 @@ let (array = util.Array)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {Styles}
|
||||
*/
|
||||
const styles = storage.newObject("styles", Styles, { store: false });
|
||||
|
||||
/**
|
||||
* @property {Highlights}
|
||||
*/
|
||||
const highlight = storage.newObject("highlight", Highlights, { store: false });
|
||||
|
||||
if (highlight.CSS != Highlights.prototype.CSS)
|
||||
{
|
||||
highlight.CSS = Highlights.prototype.CSS;
|
||||
highlight.loadCSS(highlight.CSS);
|
||||
}
|
||||
|
||||
liberator.triggerObserver("load_styles", "styles");
|
||||
liberator.triggerObserver("load_highlight", "highlight");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
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 });
|
||||
return storage.newObject("styles", Styles, { store: false });
|
||||
},
|
||||
{
|
||||
argCount: "1",
|
||||
completer: function (context) completion.colorScheme(context)
|
||||
});
|
||||
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["sty[le]"],
|
||||
"Add or list user styles",
|
||||
function (args)
|
||||
{
|
||||
function (args) {
|
||||
let [filter, css] = args;
|
||||
let name = args["-name"];
|
||||
|
||||
if (!css)
|
||||
{
|
||||
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"],
|
||||
@@ -671,13 +609,10 @@ liberator.registerObserver("load_commands", function () {
|
||||
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)
|
||||
{
|
||||
else {
|
||||
if ("-append" in args) {
|
||||
let sheet = styles.get(false, name);
|
||||
if (sheet)
|
||||
{
|
||||
if (sheet) {
|
||||
filter = sheet.sites.concat(filter).join(",");
|
||||
css = sheet.css + " " + css;
|
||||
}
|
||||
@@ -689,13 +624,11 @@ liberator.registerObserver("load_commands", function () {
|
||||
},
|
||||
{
|
||||
bang: true,
|
||||
completer: function (context, args)
|
||||
{
|
||||
completer: function (context, args) {
|
||||
let compl = [];
|
||||
if (args.completeArg == 0)
|
||||
styles.completeSite(context, content);
|
||||
else if (args.completeArg == 1)
|
||||
{
|
||||
else if (args.completeArg == 1) {
|
||||
let sheet = styles.get(false, args["-name"]);
|
||||
if (sheet)
|
||||
context.completions = [[sheet.css, "Current Value"]];
|
||||
@@ -741,8 +674,7 @@ liberator.registerObserver("load_commands", function () {
|
||||
}
|
||||
].forEach(function (cmd) {
|
||||
commands.add(cmd.name, cmd.desc,
|
||||
function (args)
|
||||
{
|
||||
function (args) {
|
||||
styles.findSheets(false, args["-name"], args[0], args.literalArg, args["-index"])
|
||||
.forEach(cmd.action);
|
||||
},
|
||||
@@ -762,11 +694,54 @@ liberator.registerObserver("load_commands", function () {
|
||||
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);
|
||||
}
|
||||
return self;
|
||||
},
|
||||
}, {
|
||||
}, {
|
||||
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(["hi[ghlight]"],
|
||||
"Set the style of certain display elements",
|
||||
function (args)
|
||||
{
|
||||
function (args) {
|
||||
let style = <![CDATA[
|
||||
;
|
||||
display: inline-block !important;
|
||||
@@ -784,8 +759,7 @@ liberator.registerObserver("load_commands", function () {
|
||||
if (clear && css)
|
||||
return liberator.echo("E488: Trailing characters");
|
||||
|
||||
if (!css && !clear)
|
||||
{
|
||||
if (!css && !clear) {
|
||||
// List matching keys
|
||||
let str = template.tabular(["Key", "Sample", "CSS"],
|
||||
["padding: 0 1em 0 0; vertical-align: top",
|
||||
@@ -806,16 +780,14 @@ liberator.registerObserver("load_commands", function () {
|
||||
},
|
||||
{
|
||||
// TODO: add this as a standard highlight completion function?
|
||||
completer: function (context, args)
|
||||
{
|
||||
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)
|
||||
context.completions = [[v.class, v.value] for (v in highlight)];
|
||||
else if (args.completeArg == 1)
|
||||
{
|
||||
else if (args.completeArg == 1) {
|
||||
let hl = highlight.get(args[0]);
|
||||
if (hl)
|
||||
context.completions = [[hl.value, "Current Value"], [hl.default || "", "Default Value"]];
|
||||
@@ -834,21 +806,8 @@ liberator.registerObserver("load_commands", function () {
|
||||
if (v.value != v.default)
|
||||
]
|
||||
});
|
||||
});
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// 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: function () {
|
||||
completion.colorScheme = function colorScheme(context) {
|
||||
context.title = ["Color Scheme", "Runtime Path"];
|
||||
context.keys = { text: function (f) f.leafName.replace(/\.vimp$/, ""), description: ".parent.path" };
|
||||
@@ -863,7 +822,7 @@ liberator.registerObserver("load_completion", function () {
|
||||
context.title = ["Highlight Group", "Value"];
|
||||
context.completions = [[v.class, v.value] for (v in highlight)];
|
||||
};
|
||||
},
|
||||
});
|
||||
//}}}
|
||||
|
||||
// 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 */
|
||||
|
||||
const template = { //{{{
|
||||
const Template = Module("template", {
|
||||
add: function add(a, b) a + 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?
|
||||
iter = util.Array.itervalues(iter);
|
||||
let ret = <></>;
|
||||
let n = 0;
|
||||
for each (let i in Iterator(iter))
|
||||
{
|
||||
for each (let i in Iterator(iter)) {
|
||||
let val = func(i);
|
||||
if (val == undefined)
|
||||
continue;
|
||||
@@ -30,30 +28,25 @@ const template = { //{{{
|
||||
return ret;
|
||||
},
|
||||
|
||||
maybeXML: function maybeXML(xml)
|
||||
{
|
||||
maybeXML: function maybeXML(xml) {
|
||||
if (typeof xml == "xml")
|
||||
return xml;
|
||||
try
|
||||
{
|
||||
try {
|
||||
return new XMLList(xml);
|
||||
}
|
||||
catch (e) {}
|
||||
return <>{xml}</>;
|
||||
},
|
||||
|
||||
completionRow: function completionRow(item, highlightGroup)
|
||||
{
|
||||
completionRow: function completionRow(item, highlightGroup) {
|
||||
if (typeof icon == "function")
|
||||
icon = icon();
|
||||
|
||||
if (highlightGroup)
|
||||
{
|
||||
if (highlightGroup) {
|
||||
var text = item[0] || "";
|
||||
var desc = item[1] || "";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
var text = this.process[0].call(this, item, item.text);
|
||||
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}</>
|
||||
},
|
||||
|
||||
@@ -108,14 +100,11 @@ const template = { //{{{
|
||||
|
||||
// if "processStrings" is true, any passed strings will be surrounded by " and
|
||||
// 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
|
||||
try
|
||||
{
|
||||
try {
|
||||
let str = clip ? util.clip(String(arg), clip) : String(arg);
|
||||
switch (arg == null ? "undefined" : typeof arg)
|
||||
{
|
||||
switch (arg == null ? "undefined" : typeof arg) {
|
||||
case "number":
|
||||
return <span highlight="Number">{str}</span>;
|
||||
case "string":
|
||||
@@ -146,41 +135,34 @@ const template = { //{{{
|
||||
return <![CDATA[<unknown type>]]>;
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
return<![CDATA[<unknown>]]>;
|
||||
}
|
||||
},
|
||||
|
||||
highlightFilter: function highlightFilter(str, filter, highlight)
|
||||
{
|
||||
return this.highlightSubstrings(str, (function ()
|
||||
{
|
||||
highlightFilter: function highlightFilter(str, filter, highlight) {
|
||||
return this.highlightSubstrings(str, (function () {
|
||||
if (filter.length == 0)
|
||||
return;
|
||||
let lcstr = String.toLowerCase(str);
|
||||
let lcfilter = filter.toLowerCase();
|
||||
let start = 0;
|
||||
while ((start = lcstr.indexOf(lcfilter, start)) > -1)
|
||||
{
|
||||
while ((start = lcstr.indexOf(lcfilter, start)) > -1) {
|
||||
yield [start, filter.length];
|
||||
start += filter.length;
|
||||
}
|
||||
})(), highlight || template.filter);
|
||||
},
|
||||
|
||||
highlightRegexp: function highlightRegexp(str, re, highlight)
|
||||
{
|
||||
return this.highlightSubstrings(str, (function ()
|
||||
{
|
||||
highlightRegexp: function highlightRegexp(str, re, highlight) {
|
||||
return this.highlightSubstrings(str, (function () {
|
||||
let res;
|
||||
while ((res = re.exec(str)) && res[0].length)
|
||||
yield [res.index, res[0].length];
|
||||
})(), highlight || template.filter);
|
||||
},
|
||||
|
||||
highlightSubstrings: function highlightSubstrings(str, iter, highlight)
|
||||
{
|
||||
highlightSubstrings: function highlightSubstrings(str, iter, highlight) {
|
||||
if (typeof str == "xml")
|
||||
return str;
|
||||
if (str == "")
|
||||
@@ -190,8 +172,7 @@ const template = { //{{{
|
||||
let s = <></>;
|
||||
let start = 0;
|
||||
let n = 0;
|
||||
for (let [i, length] in iter)
|
||||
{
|
||||
for (let [i, length] in iter) {
|
||||
if (n++ > 50) // Prevent infinite loops.
|
||||
return s + <>{str.substr(start)}</>;
|
||||
XML.ignoreWhitespace = false;
|
||||
@@ -202,23 +183,20 @@ const template = { //{{{
|
||||
return s + <>{str.substr(start)}</>;
|
||||
},
|
||||
|
||||
highlightURL: function highlightURL(str, force)
|
||||
{
|
||||
highlightURL: function highlightURL(str, force) {
|
||||
if (force || /^[a-zA-Z]+:\/\//.test(str))
|
||||
return <a highlight="URL" href={str}>{str}</a>;
|
||||
else
|
||||
return str;
|
||||
},
|
||||
|
||||
commandOutput: function generic(xml)
|
||||
{
|
||||
commandOutput: function generic(xml) {
|
||||
return <>:{commandline.command}<br/>{xml}</>;
|
||||
},
|
||||
|
||||
// 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
|
||||
genericTable: function genericTable(items, format)
|
||||
{
|
||||
genericTable: function genericTable(items, format) {
|
||||
completion.listCompleter(function (context) {
|
||||
context.filterFunc = null;
|
||||
if (format)
|
||||
@@ -227,8 +205,7 @@ const template = { //{{{
|
||||
});
|
||||
},
|
||||
|
||||
jumps: function jumps(index, elems)
|
||||
{
|
||||
jumps: function jumps(index, elems) {
|
||||
// <e4x>
|
||||
return this.commandOutput(
|
||||
<table>
|
||||
@@ -248,8 +225,7 @@ const template = { //{{{
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
options: function options(title, opts)
|
||||
{
|
||||
options: function options(title, opts) {
|
||||
// <e4x>
|
||||
return this.commandOutput(
|
||||
<table>
|
||||
@@ -269,8 +245,7 @@ const template = { //{{{
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
table: function table(title, data, indent)
|
||||
{
|
||||
table: function table(title, data, indent) {
|
||||
let table =
|
||||
// <e4x>
|
||||
<table>
|
||||
@@ -290,8 +265,7 @@ const template = { //{{{
|
||||
return table;
|
||||
},
|
||||
|
||||
tabular: function tabular(headings, style, iter)
|
||||
{
|
||||
tabular: function tabular(headings, style, iter) {
|
||||
// TODO: This might be mind-bogglingly slow. We'll see.
|
||||
// <e4x>
|
||||
return this.commandOutput(
|
||||
@@ -315,8 +289,7 @@ const template = { //{{{
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
usage: function usage(iter)
|
||||
{
|
||||
usage: function usage(iter) {
|
||||
// <e4x>
|
||||
return this.commandOutput(
|
||||
<table>
|
||||
@@ -330,6 +303,6 @@ const template = { //{{{
|
||||
</table>);
|
||||
// </e4x>
|
||||
}
|
||||
}; //}}}
|
||||
});
|
||||
|
||||
// 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");
|
||||
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
|
||||
* of which context it comes from.
|
||||
@@ -25,8 +29,7 @@ const util = { //{{{
|
||||
* @param {Object} obj
|
||||
* @returns {Object}
|
||||
*/
|
||||
cloneObject: function cloneObject(obj)
|
||||
{
|
||||
cloneObject: function cloneObject(obj) {
|
||||
if (obj instanceof Array)
|
||||
return obj.slice();
|
||||
let newObj = {};
|
||||
@@ -43,8 +46,7 @@ const util = { //{{{
|
||||
* @param {number} length The length of the returned string.
|
||||
* @returns {string}
|
||||
*/
|
||||
clip: function clip(str, length)
|
||||
{
|
||||
clip: function clip(str, length) {
|
||||
return str.length <= length ? str : str.substr(0, length - 3) + "...";
|
||||
},
|
||||
|
||||
@@ -64,8 +66,7 @@ const util = { //{{{
|
||||
* @param {Node} node
|
||||
* @returns {Object}
|
||||
*/
|
||||
computedStyle: function computedStyle(node)
|
||||
{
|
||||
computedStyle: function computedStyle(node) {
|
||||
while (node instanceof Text && node.parentNode)
|
||||
node = node.parentNode;
|
||||
return node.ownerDocument.defaultView.getComputedStyle(node, null);
|
||||
@@ -78,8 +79,7 @@ const util = { //{{{
|
||||
* @param {string} str
|
||||
* @param {boolean} verbose
|
||||
*/
|
||||
copyToClipboard: function copyToClipboard(str, verbose)
|
||||
{
|
||||
copyToClipboard: function copyToClipboard(str, verbose) {
|
||||
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
clipboardHelper.copyString(str);
|
||||
|
||||
@@ -94,8 +94,7 @@ const util = { //{{{
|
||||
* @returns {Object}
|
||||
*/
|
||||
// FIXME: newURI needed too?
|
||||
createURI: function createURI(str)
|
||||
{
|
||||
createURI: function createURI(str) {
|
||||
const fixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
||||
return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||
},
|
||||
@@ -107,8 +106,7 @@ const util = { //{{{
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
escapeHTML: function escapeHTML(str)
|
||||
{
|
||||
escapeHTML: function escapeHTML(str) {
|
||||
// XXX: the following code is _much_ slower than a simple .replace()
|
||||
// :history display went down from 2 to 1 second after changing
|
||||
//
|
||||
@@ -124,32 +122,28 @@ const util = { //{{{
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
escapeRegex: function escapeRegex(str)
|
||||
{
|
||||
escapeRegex: function escapeRegex(str) {
|
||||
return str.replace(/([\\{}()[\].?*+])/g, "\\$1");
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* specified.
|
||||
* specified. {@see String#quote}.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} delimiter
|
||||
* @returns {string}
|
||||
*/
|
||||
escapeString: function escapeString(str, delimiter)
|
||||
{
|
||||
escapeString: function escapeString(str, delimiter) {
|
||||
if (delimiter == undefined)
|
||||
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) {
|
||||
for (let [k, v] in Iterator(src))
|
||||
{
|
||||
for (let [k, v] in Iterator(src)) {
|
||||
let get = src.__lookupGetter__(k),
|
||||
set = src.__lookupSetter__(k);
|
||||
if (!get && !set)
|
||||
@@ -171,8 +165,7 @@ const util = { //{{{
|
||||
* @param nodes {Array(string)}
|
||||
* @returns {string}
|
||||
*/
|
||||
makeXPath: function makeXPath(nodes)
|
||||
{
|
||||
makeXPath: function makeXPath(nodes) {
|
||||
return util.Array(nodes).map(function (node) [node, "xhtml:" + node]).flatten()
|
||||
.map(function (node) "//" + node).join(" | ");
|
||||
},
|
||||
@@ -187,8 +180,7 @@ const util = { //{{{
|
||||
* passed as the first argument, <b>key</b> as the
|
||||
* second.
|
||||
*/
|
||||
memoize: function memoize(obj, key, getter)
|
||||
{
|
||||
memoize: function memoize(obj, key, getter) {
|
||||
obj.__defineGetter__(key, function () {
|
||||
delete obj[key];
|
||||
obj[key] = getter(obj, key);
|
||||
@@ -209,14 +201,12 @@ const util = { //{{{
|
||||
* @param {string} str
|
||||
* @param {RegExp} marker
|
||||
*/
|
||||
splitLiteral: function splitLiteral(str, marker)
|
||||
{
|
||||
splitLiteral: function splitLiteral(str, marker) {
|
||||
let results = [];
|
||||
let resep = RegExp(/^(([^\\'"]|\\.|'([^\\']|\\.)*'|"([^\\"]|\\.)*")*?)/.source + marker.source);
|
||||
let cont = true;
|
||||
|
||||
while (cont)
|
||||
{
|
||||
while (cont) {
|
||||
cont = false;
|
||||
str = str.replace(resep, function (match, before) {
|
||||
results.push(before);
|
||||
@@ -238,17 +228,14 @@ const util = { //{{{
|
||||
* @param {boolean} humanReadable Use byte multiples.
|
||||
* @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"];
|
||||
let unitIndex = 0;
|
||||
let tmpNum = parseInt(bytes, 10) || 0;
|
||||
let strNum = [tmpNum + ""];
|
||||
|
||||
if (humanReadable)
|
||||
{
|
||||
while (tmpNum >= 1024)
|
||||
{
|
||||
if (humanReadable) {
|
||||
while (tmpNum >= 1024) {
|
||||
tmpNum /= 1024;
|
||||
if (++unitIndex > (unitVal.length - 1))
|
||||
break;
|
||||
@@ -273,53 +260,7 @@ const util = { //{{{
|
||||
return strNum[0] + " " + unitVal[unitIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
exportHelp: function (path) {
|
||||
const FILE = io.File(path);
|
||||
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
|
||||
const TIME = Date.now();
|
||||
@@ -337,8 +278,7 @@ const util = { //{{{
|
||||
.split(" ").map(Array.concat));
|
||||
|
||||
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);
|
||||
events.waitForPageLoad();
|
||||
let data = [
|
||||
@@ -346,10 +286,8 @@ const util = { //{{{
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n',
|
||||
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
|
||||
];
|
||||
function fix(node)
|
||||
{
|
||||
switch(node.nodeType)
|
||||
{
|
||||
function fix(node) {
|
||||
switch(node.nodeType) {
|
||||
case Node.ELEMENT_NODE:
|
||||
if (node instanceof HTMLScriptElement)
|
||||
return;
|
||||
@@ -358,22 +296,18 @@ const util = { //{{{
|
||||
if (node instanceof HTMLHtmlElement)
|
||||
data.push(' xmlns="' + XHTML.uri + '"');
|
||||
|
||||
for (let { name: name, value: value } in util.Array.itervalues(node.attributes))
|
||||
{
|
||||
if (name == "liberator:highlight")
|
||||
{
|
||||
for (let { name: name, value: value } in util.Array.itervalues(node.attributes)) {
|
||||
if (name == "liberator:highlight") {
|
||||
name = "class";
|
||||
value = "hl-" + value;
|
||||
}
|
||||
if (name == "href")
|
||||
{
|
||||
if (name == "href") {
|
||||
if (value.indexOf("liberator://help-tag/") == 0)
|
||||
value = services.get("io").newChannel(value, null, null).originalURI.path.substr(1);
|
||||
if (!/[#\/]/.test(value))
|
||||
value += ".xhtml";
|
||||
}
|
||||
if (name == "src" && value.indexOf(":") > 0)
|
||||
{
|
||||
if (name == "src" && value.indexOf(":") > 0) {
|
||||
chrome[value] = value.replace(/.*\//, "");;
|
||||
value = value.replace(/.*\//, "");
|
||||
}
|
||||
@@ -385,8 +319,7 @@ const util = { //{{{
|
||||
}
|
||||
if (node.localName in empty)
|
||||
data.push(" />");
|
||||
else
|
||||
{
|
||||
else {
|
||||
data.push(">");
|
||||
if (node instanceof HTMLHeadElement)
|
||||
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
|
||||
@@ -429,14 +362,11 @@ const util = { //{{{
|
||||
* @param {function(XMLHttpRequest)} callback
|
||||
* @returns {XMLHttpRequest}
|
||||
*/
|
||||
httpGet: function httpGet(url, callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
httpGet: function httpGet(url, callback) {
|
||||
try {
|
||||
let xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.mozBackgroundRequest = true;
|
||||
if (callback)
|
||||
{
|
||||
if (callback) {
|
||||
xmlhttp.onreadystatechange = function () {
|
||||
if (xmlhttp.readyState == 4)
|
||||
callback(xmlhttp);
|
||||
@@ -446,8 +376,7 @@ const util = { //{{{
|
||||
xmlhttp.send(null);
|
||||
return xmlhttp;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
liberator.log("Error opening " + url + ": " + e, 1);
|
||||
}
|
||||
},
|
||||
@@ -465,8 +394,7 @@ const util = { //{{{
|
||||
* @param {boolean} asIterator Whether to return the results as an
|
||||
* XPath iterator.
|
||||
*/
|
||||
evaluateXPath: function (expression, doc, elem, asIterator)
|
||||
{
|
||||
evaluateXPath: function (expression, doc, elem, asIterator) {
|
||||
if (!doc)
|
||||
doc = window.content.document;
|
||||
if (!elem)
|
||||
@@ -475,11 +403,11 @@ const util = { //{{{
|
||||
expression = util.makeXPath(expression);
|
||||
|
||||
let result = doc.evaluate(expression, elem,
|
||||
function lookupNamespaceURI(prefix)
|
||||
{
|
||||
function lookupNamespaceURI(prefix) {
|
||||
return {
|
||||
xhtml: "http://www.w3.org/1999/xhtml",
|
||||
xhtml2: "http://www.w3.org/2002/06/xhtml2",
|
||||
liberator: NS.uri,
|
||||
liberator: NS.uri
|
||||
}[prefix] || null;
|
||||
},
|
||||
@@ -526,8 +454,7 @@ const util = { //{{{
|
||||
* @param {function} func
|
||||
* @returns {Array}
|
||||
*/
|
||||
map: function map(obj, func)
|
||||
{
|
||||
map: function map(obj, func) {
|
||||
let ary = [];
|
||||
for (let i in Iterator(obj))
|
||||
ary.push(func(i));
|
||||
@@ -558,8 +485,7 @@ const util = { //{{{
|
||||
* @returns {nsIURI}
|
||||
*/
|
||||
// FIXME: createURI needed too?
|
||||
newURI: function (uri)
|
||||
{
|
||||
newURI: function (uri) {
|
||||
return services.get("io").newURI(uri, null, null);
|
||||
},
|
||||
|
||||
@@ -571,8 +497,7 @@ const util = { //{{{
|
||||
* @param {boolean} color Whether the output should be colored.
|
||||
* @returns {string}
|
||||
*/
|
||||
objectToString: function objectToString(object, color)
|
||||
{
|
||||
objectToString: function objectToString(object, color) {
|
||||
// Use E4X literals so html is automatically quoted
|
||||
// only when it's asked for. No one wants to see <
|
||||
// on their console or :map :foo in their buffer
|
||||
@@ -591,20 +516,17 @@ const util = { //{{{
|
||||
[XHTML, 'html'],
|
||||
[XUL, 'xul']
|
||||
]);
|
||||
if (object instanceof Element)
|
||||
{
|
||||
if (object instanceof Element) {
|
||||
let elem = object;
|
||||
if (elem.nodeType == elem.TEXT_NODE)
|
||||
return elem.data;
|
||||
function namespaced(node)
|
||||
{
|
||||
function namespaced(node) {
|
||||
var ns = NAMESPACES[node.namespaceURI];
|
||||
if (ns)
|
||||
return ns + ":" + node.localName;
|
||||
return node.localName.toLowerCase();
|
||||
}
|
||||
try
|
||||
{
|
||||
try {
|
||||
let tag = "<" + [namespaced(elem)].concat(
|
||||
[namespaced(a) + "=" + template.highlight(a.value, true)
|
||||
for ([i, a] in util.Array.iteritems(elem.attributes))]).join(" ");
|
||||
@@ -615,42 +537,34 @@ const util = { //{{{
|
||||
tag += '>...</' + namespaced(elem) + '>';
|
||||
return tag;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
return {}.toString.call(elem);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{ // for window.JSON
|
||||
try { // for window.JSON
|
||||
var obj = String(object);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
obj = "[Object]";
|
||||
}
|
||||
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 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);
|
||||
if (modules.isPrototypeOf(object))
|
||||
{
|
||||
if (modules.isPrototypeOf(object)) {
|
||||
object = Iterator(object);
|
||||
hasValue = false;
|
||||
}
|
||||
for (let i in object)
|
||||
{
|
||||
for (let i in object) {
|
||||
let value = <![CDATA[<no value>]]>;
|
||||
try
|
||||
{
|
||||
try {
|
||||
value = object[i];
|
||||
}
|
||||
catch (e) {}
|
||||
if (!hasValue)
|
||||
{
|
||||
if (!hasValue) {
|
||||
if (i instanceof Array && i.length == 2)
|
||||
[i, value] = i;
|
||||
else
|
||||
@@ -668,8 +582,7 @@ const util = { //{{{
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
function compare(a, b)
|
||||
{
|
||||
function compare(a, b) {
|
||||
if (!isNaN(a[0]) && !isNaN(b[0]))
|
||||
return a[0] - b[0];
|
||||
return String.localeCompare(a[0], b[0]);
|
||||
@@ -688,17 +601,14 @@ const util = { //{{{
|
||||
* negative. @default 1
|
||||
* @returns {Iterator(Object)}
|
||||
*/
|
||||
range: function range(start, end, step)
|
||||
{
|
||||
range: function range(start, end, step) {
|
||||
if (!step)
|
||||
step = 1;
|
||||
if (step > 0)
|
||||
{
|
||||
if (step > 0) {
|
||||
for (; start < end; start += step)
|
||||
yield start;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
while (start > end)
|
||||
yield start += step;
|
||||
}
|
||||
@@ -713,13 +623,10 @@ const util = { //{{{
|
||||
* @param {number} time The time in milliseconds between thread yields.
|
||||
* @returns {Iterator(Object)}
|
||||
*/
|
||||
interruptibleRange: function interruptibleRange(start, end, time)
|
||||
{
|
||||
interruptibleRange: function interruptibleRange(start, end, time) {
|
||||
let endTime = Date.now() + time;
|
||||
while (start < end)
|
||||
{
|
||||
if (Date.now() > endTime)
|
||||
{
|
||||
while (start < end) {
|
||||
if (Date.now() > endTime) {
|
||||
liberator.threadYield(true, true);
|
||||
endTime = Date.now() + time;
|
||||
}
|
||||
@@ -735,12 +642,10 @@ const util = { //{{{
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
readFromClipboard: function readFromClipboard()
|
||||
{
|
||||
readFromClipboard: function readFromClipboard() {
|
||||
let str;
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||
|
||||
@@ -756,8 +661,7 @@ const util = { //{{{
|
||||
|
||||
transferable.getTransferData("text/unicode", data, dataLen);
|
||||
|
||||
if (data)
|
||||
{
|
||||
if (data) {
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||
str = data.data.substring(0, dataLen.value / 2);
|
||||
}
|
||||
@@ -776,8 +680,7 @@ const util = { //{{{
|
||||
* @param {string} str
|
||||
* @returns {string[]}
|
||||
*/
|
||||
stringToURLArray: function stringToURLArray(str)
|
||||
{
|
||||
stringToURLArray: function stringToURLArray(str) {
|
||||
let urls;
|
||||
|
||||
if (options["urlseparator"])
|
||||
@@ -786,8 +689,7 @@ const util = { //{{{
|
||||
urls = [str];
|
||||
|
||||
return urls.map(function (url) {
|
||||
try
|
||||
{
|
||||
try {
|
||||
// Try to find a matching file.
|
||||
let file = io.File(url);
|
||||
if (file.exists() && file.isReadable())
|
||||
@@ -834,18 +736,15 @@ const util = { //{{{
|
||||
* stored here, keyed to the value thereof.
|
||||
* @returns {Node}
|
||||
*/
|
||||
xmlToDom: function xmlToDom(node, doc, nodes)
|
||||
{
|
||||
xmlToDom: function xmlToDom(node, doc, nodes) {
|
||||
XML.prettyPrinting = false;
|
||||
if (node.length() != 1)
|
||||
{
|
||||
if (node.length() != 1) {
|
||||
let domnode = doc.createDocumentFragment();
|
||||
for each (let child in node)
|
||||
domnode.appendChild(arguments.callee(child, doc, nodes));
|
||||
return domnode;
|
||||
}
|
||||
switch (node.nodeKind())
|
||||
{
|
||||
switch (node.nodeKind()) {
|
||||
case "text":
|
||||
return doc.createTextNode(node);
|
||||
case "element":
|
||||
@@ -859,18 +758,17 @@ const util = { //{{{
|
||||
return domnode;
|
||||
}
|
||||
}
|
||||
}; //}}}
|
||||
|
||||
}, {
|
||||
// TODO: Why don't we just push all util.BuiltinType up into modules? --djk
|
||||
/**
|
||||
* Array utility methods.
|
||||
*/
|
||||
util.Array = function Array_(ary) {
|
||||
var obj = {
|
||||
Array: Class("Array", {
|
||||
init: function (ary) {
|
||||
return {
|
||||
__proto__: ary,
|
||||
__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));
|
||||
if (util.Array.isinstance(res))
|
||||
return util.Array(res);
|
||||
@@ -879,11 +777,11 @@ util.Array = function Array_(ary) {
|
||||
concat: function () [].concat.apply(this.__proto__, arguments),
|
||||
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
util.Array.isinstance = function isinstance(obj) {
|
||||
},
|
||||
}, {
|
||||
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,
|
||||
@@ -894,12 +792,11 @@ util.Array.isinstance = function isinstance(obj) {
|
||||
* @... {string} 0 - Key
|
||||
* @... 1 - Value
|
||||
*/
|
||||
util.Array.toObject = function toObject(assoc)
|
||||
{
|
||||
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
|
||||
@@ -909,7 +806,7 @@ util.Array.toObject = function toObject(assoc)
|
||||
* @param {Array} ary
|
||||
* @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.
|
||||
@@ -917,12 +814,11 @@ util.Array.flatten = function flatten(ary) Array.concat.apply([], ary),
|
||||
* @param {Array} ary
|
||||
* @returns {Iterator(Object)}
|
||||
*/
|
||||
util.Array.itervalues = function itervalues(ary)
|
||||
{
|
||||
itervalues: function itervalues(ary) {
|
||||
let length = ary.length;
|
||||
for (let i = 0; i < length; i++)
|
||||
yield ary[i];
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an Iterator for an array's indices and values.
|
||||
@@ -930,12 +826,11 @@ util.Array.itervalues = function itervalues(ary)
|
||||
* @param {Array} ary
|
||||
* @returns {Iterator([{number}, {Object}])}
|
||||
*/
|
||||
util.Array.iteritems = function iteritems(ary)
|
||||
{
|
||||
iteritems: function iteritems(ary) {
|
||||
let length = ary.length;
|
||||
for (let i = 0; i < length; i++)
|
||||
yield [i, ary[i]];
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Filters out all duplicates from an array. If
|
||||
@@ -946,85 +841,24 @@ util.Array.iteritems = function iteritems(ary)
|
||||
* @param {boolean} unsorted
|
||||
* @returns {Array}
|
||||
*/
|
||||
util.Array.uniq = function uniq(ary, unsorted)
|
||||
{
|
||||
uniq: function uniq(ary, unsorted) {
|
||||
let ret = [];
|
||||
if (unsorted)
|
||||
{
|
||||
if (unsorted) {
|
||||
for (let [, item] in Iterator(ary))
|
||||
if (ret.indexOf(item) == -1)
|
||||
ret.push(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (let [, item] in Iterator(ary.sort()))
|
||||
{
|
||||
else {
|
||||
for (let [, item] in Iterator(ary.sort())) {
|
||||
if (item != last || !ret.length)
|
||||
ret.push(item);
|
||||
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:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -191,23 +191,11 @@ const config = (function () //{{{
|
||||
liberator.loadModule("hints", Hints);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// STYLES //////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
commands.add(["pref[erences]", "prefs"],
|
||||
"Show " + config.hostApplication + " preferences",
|
||||
function () { window.openOptionsDialog(); },
|
||||
{ argCount: "0" });
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
// FIXME: comment obviously incorrect
|
||||
// 0: never automatically edit externally
|
||||
// 1: automatically edit externally when message window is shown the first time
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#### configuration
|
||||
|
||||
VERSION = 2.2
|
||||
VERSION = 1.0pre
|
||||
NAME = vimperator
|
||||
|
||||
include ../common/Makefile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -110,6 +110,8 @@ const config = { //{{{
|
||||
scripts: [
|
||||
"browser.js",
|
||||
"bookmarks.js",
|
||||
"history.js",
|
||||
"quickmarks.js",
|
||||
"sanitizer.js",
|
||||
"tabs.js"
|
||||
],
|
||||
@@ -128,38 +130,6 @@ const config = { //{{{
|
||||
|
||||
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]"],
|
||||
"Close all other windows",
|
||||
function ()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<em:optionsURL>chrome://liberator/content/preferences.xul</em:optionsURL>
|
||||
<em:file>
|
||||
<Description about="urn:mozilla:extension:file:vimperator.jar">
|
||||
<em:package>content/vimperator/</em:package>
|
||||
<em:package>content/liberator/</em:package>
|
||||
</Description>
|
||||
</em:file>
|
||||
<em:targetApplication>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Header:
|
||||
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");
|
||||
|
||||
// Copyright (c) 2009 by Doug Kearns
|
||||
|
||||
@@ -246,6 +246,20 @@ const config = { //{{{
|
||||
////////////////////// 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 ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
Reference in New Issue
Block a user