1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2026-02-13 14:45:45 +01:00

Merge branch 'master' into vimperator-2.1

Conflicts:
	common/content/io.js
	common/content/style.js
This commit is contained in:
Kris Maglione
2009-01-07 00:26:37 -05:00
54 changed files with 1801 additions and 1013 deletions

View File

@@ -32,7 +32,7 @@ const Point = new Struct("x", "y");
/**
* A class to manage the primary web content buffer. The name comes
* from vim's term, 'buffer', which signifies instances of open
* from Vim's term, 'buffer', which signifies instances of open
* files.
* @instance buffer
*/
@@ -41,7 +41,8 @@ function Buffer() //{{{
////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
/* FIXME: This doesn't belong here. */
// FIXME: This doesn't belong here.
let mainWindowID = config.mainWindowID || "main-window";
let fontSize = util.computedStyle(document.getElementById(mainWindowID)).fontSize;
@@ -197,7 +198,7 @@ function Buffer() //{{{
{
if (mappings.repeat)
{
for (let i in util.interruptableRange(0, Math.max(count, 1), 100))
for (let i in util.interruptibleRange(0, Math.max(count, 1), 100))
mappings.repeat();
}
},
@@ -330,7 +331,7 @@ function Buffer() //{{{
"//xhtml:input[not(@type) or @type='text' or @type='password'] | //xhtml:textarea[not(@disabled) and not(@readonly)]"
);
for (match in matches)
for (let match in matches)
{
let computedStyle = util.computedStyle(match);
if (computedStyle.visibility != "hidden" && computedStyle.display != "none")
@@ -339,11 +340,7 @@ function Buffer() //{{{
if (elements.length > 0)
{
if (count > elements.length)
count = elements.length;
else if (count < 1)
count = 1;
count = Math.min(Math.max(count, 1), elements.length);
elements[count - 1].focus();
}
else
@@ -482,7 +479,7 @@ function Buffer() //{{{
if (arg && (liberator.has("Win32") || arg[0] != ">"))
return liberator.echoerr("E488: Trailing characters");
options.temporaryContext(function () {
options.withContext(function () {
if (arg)
{
options.setPref("print.print_to_file", "true");
@@ -570,12 +567,9 @@ function Buffer() //{{{
let file = io.getFile(filename);
if (file.exists() && !args.bang)
{
liberator.echoerr("E13: File exists (add ! to override)");
return;
}
return void liberator.echoerr("E13: File exists (add ! to override)");
chosenData = { file: file, uri: makeURI(doc.location.href, doc.characterSet) };
chosenData = { file: file, uri: window.makeURI(doc.location.href, doc.characterSet) };
}
// if browser.download.useDownloadDir = false then the "Save As"
@@ -682,7 +676,7 @@ function Buffer() //{{{
if (!isFeed)
{
let type = data.type && data.type.toLowerCase();
var type = data.type && data.type.toLowerCase();
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
@@ -817,8 +811,8 @@ function Buffer() //{{{
return {
/**
* The alternative stylesheets for the current buffer. Only
* returns stylesheets for the 'screen' media type.
* @property {Array} The alternative style sheets for the current
* buffer. Only returns style sheets for the 'screen' media type.
*/
get alternateStyleSheets()
{
@@ -836,10 +830,11 @@ function Buffer() //{{{
get pageInfo() pageInfo,
/**
* Returns whether the buffer is loaded. Values may be:
* 0 - Loading.
* 1 - Fully loaded.
* 2 - Load failed.
* @property {number} A value indicating whether the buffer is loaded.
* Values may be:
* 0 - Loading.
* 1 - Fully loaded.
* 2 - Load failed.
*/
get loaded()
{
@@ -854,8 +849,8 @@ function Buffer() //{{{
},
/**
* The last focused input field in the buffer. Used by the
* "gi" key binding.
* @property {Object} The last focused input field in the buffer. Used
* by the "gi" key binding.
*/
get lastInputField()
{
@@ -870,21 +865,24 @@ function Buffer() //{{{
},
/**
* The current top-level document's URL.
* @property {string} The current top-level document's URL.
*/
get URL()
{
return window.content.document.location.href;
},
/**
* @property {number} The buffer's height in pixels.
*/
get pageHeight()
{
return window.content.innerHeight;
},
/**
* The current browser's text zoom level, as a percentage with
* 100 as 'normal'. Only affects text size.
* @property {number} The current browser's text zoom level, as a
* percentage with 100 as 'normal'. Only affects text size.
*/
get textZoom()
{
@@ -896,9 +894,9 @@ function Buffer() //{{{
},
/**
* The current browser's text zoom level, as a percentage with
* 100 as 'normal'. Affects text size, as well as image size
* and block size.
* @property {number} The current browser's text zoom level, as a
* percentage with 100 as 'normal'. Affects text size, as well as
* image size and block size.
*/
get fullZoom()
{
@@ -910,7 +908,7 @@ function Buffer() //{{{
},
/**
* The current document's title.
* @property {string} The current document's title.
*/
get title()
{
@@ -918,6 +916,7 @@ function Buffer() //{{{
},
/**
* Adds a new section to the page information output.
*
* @param {string} option The section's value in 'pageinfo'.
* @param {string} title The heading for this section's
@@ -972,6 +971,8 @@ function Buffer() //{{{
* positioned in.
*
* NOTE: might change the selection
*
* @returns {string}
*/
// FIXME: getSelection() doesn't always preserve line endings, see:
// https://www.mozdev.org/bugs/show_bug.cgi?id=19303
@@ -1026,9 +1027,8 @@ function Buffer() //{{{
},
/**
* Try to guess links the like of "next" and "prev". Though it
* has a singularly horrendous name, it turns out to be quite
* useful.
* Tries to guess links the like of "next" and "prev". Though it has a
* singularly horrendous name, it turns out to be quite useful.
*
* @param {string} rel The relationship to look for. Looks for
* links with matching @rel or @rev attributes, and,
@@ -1083,12 +1083,12 @@ function Buffer() //{{{
return false;
}
let retVal = followFrame(window.content);
if (!retVal)
// only loop through frames if the main content didnt match
retVal = Array.some(window.content.frames, followFrame);
let ret = followFrame(window.content);
if (!ret)
// only loop through frames if the main content didn't match
ret = Array.some(window.content.frames, followFrame);
if (!retVal)
if (!ret)
liberator.beep();
},
@@ -1125,7 +1125,7 @@ function Buffer() //{{{
case liberator.NEW_TAB:
case liberator.NEW_BACKGROUND_TAB:
ctrlKey = true;
shiftKey = (where == liberator.NEW_BACKGROUND_TAB);
shiftKey = (where != liberator.NEW_BACKGROUND_TAB);
break;
case liberator.NEW_WINDOW:
shiftKey = true;
@@ -1147,13 +1147,19 @@ function Buffer() //{{{
},
/**
* The current document's selection controller.
* @property {Object} The current document's selection controller.
*/
get selectionController() getBrowser().docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsISelectionDisplay)
.QueryInterface(Ci.nsISelectionController),
/**
* Saves a page link to disk.
*
* @param {Object} elem The page link to save.
* @param {boolean} skipPrompt Whether to open the "Save Link As..." dialog
*/
saveLink: function (elem, skipPrompt)
{
let doc = elem.ownerDocument;
@@ -1229,7 +1235,13 @@ function Buffer() //{{{
win.scrollByPages(pages);
},
scrollByScrollSize: function (count, direction)
/**
* Scrolls the buffer vertically <b>count</b> * 'scroll' rows.
*
* @param {number} count The multiple of 'scroll' lines to scroll.
* @param {number} direction The direction to scroll, down if 1 and up if -1.
*/
scrollByScrollSize: function (count, direction) // XXX: boolean
{
if (count > 0)
options["scroll"] = count;
@@ -1244,7 +1256,9 @@ function Buffer() //{{{
},
/**
* Scrolls the current buffer vertically to <b>percentage</b>
* Scrolls the current buffer vertically to <b>percentage</b>.
*
* @param {number} percentage The page percentile to scroll the buffer to.
*/
scrollToPercentile: function (percentage)
{
@@ -1279,9 +1293,16 @@ function Buffer() //{{{
},
// TODO: allow callback for filtering out unwanted frames? User defined?
/**
* Shifts the focus to another frame within the buffer. Each buffer
* contains at least one frame.
*
* @param {number} count The number of frames to skip through.
* @param {boolean} forward The direction of motion.
*/
shiftFrameFocus: function (count, forward)
{
if (!window.content.document instanceof HTMLDocument)
if (!(window.content.document instanceof HTMLDocument))
return;
count = Math.max(count, 1);
@@ -1357,11 +1378,23 @@ function Buffer() //{{{
// similar to pageInfo
// TODO: print more useful information, just like the DOM inspector
/**
* Displays information about the specified element.
*
* @param {Object} elem
*/
showElementInfo: function (elem)
{
liberator.echo(<>Element:<br/>{util.objectToString(elem, true)}</>, commandline.FORCE_MULTILINE);
},
/**
* Displays information about the current buffer.
*
* @param {boolean} verbose Display more verbose information.
* @param {string} sections A string limiting the displayed sections.
* @default The value of 'pageinfo'.
*/
showPageInfo: function (verbose, sections)
{
// Ctrl-g single line output
@@ -1391,6 +1424,9 @@ function Buffer() //{{{
liberator.echo(list, commandline.FORCE_MULTILINE);
},
/**
* Opens a viewer to inspect the source of the currently selected range.
*/
viewSelectionSource: function ()
{
// copied (and tuned somebit) from browser.jar -> nsContextMenu.js
@@ -1411,6 +1447,15 @@ function Buffer() //{{{
docUrl, docCharset, reference, "selection");
},
/**
* Opens a viewer to inspect the source of the current buffer or the
* specified <b>url</b>. Either the default viewer or the configured
* external editor is used.
*
* @param {string} url The URL of the source.
* @default The current buffer.
* @param {boolean} useExternalEditor View the source in the external editor.
*/
viewSource: function (url, useExternalEditor)
{
url = url || buffer.URL;
@@ -1421,11 +1466,23 @@ function Buffer() //{{{
liberator.open("view-source:" + url);
},
/**
* Increases the zoom level of the current buffer.
*
* @param {number} steps The number of zoom levels to jump.
* @param {boolean} fullZoom Whether to use full zoom or text zoom.
*/
zoomIn: function (steps, fullZoom)
{
bumpZoomLevel(steps, fullZoom);
},
/**
* Decreases the zoom level of the current buffer.
*
* @param {number} steps The number of zoom levels to jump.
* @param {boolean} fullZoom Whether to use full zoom or text zoom.
*/
zoomOut: function (steps, fullZoom)
{
bumpZoomLevel(-steps, fullZoom);
@@ -1443,8 +1500,8 @@ function Marks() //{{{
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
var localMarks = storage.newMap('local-marks', true);
var urlMarks = storage.newMap('url-marks', true);
var localMarks = storage.newMap("local-marks", true);
var urlMarks = storage.newMap("url-marks", true);
var pendingJumps = [];
var appContent = document.getElementById("appcontent");
@@ -1471,7 +1528,7 @@ function Marks() //{{{
return name + ", " + mark.location +
", (" + Math.round(mark.position.x * 100) +
"%, " + Math.round(mark.position.y * 100) + "%)" +
(('tab' in mark) ? ", tab: " + tabs.index(mark.tab) : "");
(("tab" in mark) ? ", tab: " + tabs.index(mark.tab) : "");
}
function removeLocalMark(mark)

View File

@@ -28,14 +28,14 @@ the terms of any one of the MPL, the GPL or the LGPL.
/** @scope modules */
// Do NOT create instances of this class yourself, use the helper method
// commands.add() instead
/**
* A class representing EX commands. Instances are created by
* A class representing Ex commands. Instances are created by
* the {@link Commands} class.
*
* @private
*/
// Do NOT create instances of this class yourself, use the helper method
// commands.add() instead
function Command(specs, description, action, extraInfo) //{{{
{
if (!specs || !action)
@@ -86,7 +86,7 @@ function Command(specs, description, action, extraInfo) //{{{
/** @property {string[]} All of this command's long names, e.g., "command" */
this.longNames = expandedSpecs.longNames;
/** @property {string} The command's cannonical name. */
/** @property {string} The command's canonical name. */
this.name = this.longNames[0];
/** @property {string[]} All of this command's long and short names. */
this.names = expandedSpecs.names; // return all command name aliases
@@ -112,7 +112,7 @@ function Command(specs, description, action, extraInfo) //{{{
* arguments begin. For instance, with a value of 2, all arguments
* starting with the third are parsed as a single string, with all
* quoting characters passed literally. This is especially useful for
* commands which take key mappings or ex command lines as
* commands which take key mappings or Ex command lines as
* arguments.
*/
this.literal = extraInfo.literal == null ? null : extraInfo.literal;
@@ -133,7 +133,7 @@ function Command(specs, description, action, extraInfo) //{{{
this.isUserCommand = extraInfo.isUserCommand || false;
/**
* @property {string} For commands defined via :command, contains
* the EX command line to be executed upon invocation.
* the Ex command line to be executed upon invocation.
*/
this.replacementText = extraInfo.replacementText || null;
};
@@ -148,7 +148,7 @@ Command.prototype = {
* @param {boolean} bang @deprecated Whether this command was
* executed with a trailing !.
* @param {number} count @deprecated Whether this command was
* executed a leading count.
* executed with a leading count.
* @param modifiers Any modifiers to be passed to
* {@link action}
*/
@@ -189,6 +189,7 @@ Command.prototype = {
* Returns whether this command may be invoked via <b>name</b>.
*
* @param {string} name
* @returns {boolean}
*/
hasName: function (name)
{
@@ -214,7 +215,7 @@ Command.prototype = {
* purposes.
* @param {Object} extra Extra keys to be spliced into the
* returned Args object.
* @returns Args
* @returns {Args}
* @see Commands#parseArgs
*/
parseArgs: function (args, complete, extra) commands.parseArgs(args, this.options, this.argCount, false, this.literal, complete, extra)
@@ -306,7 +307,7 @@ function Commands() //{{{
completion.setFunctionCompleter(commands.get, [function () ([c.name, c.description] for (c in commands))]);
});
var commandManager = {
const self = {
// FIXME: remove later, when our option handler is better
OPTION_ANY: 0, // can be given no argument or an argument of any type,
@@ -717,27 +718,18 @@ function Commands() //{{{
return args;
},
// return [null, null, null, null, heredoc_tag || false];
// [count, cmd, special, args] = match;
parseCommand: function (str, tag)
parseCommand: function (str)
{
// remove comments
str.replace(/\s*".*$/, "");
if (tag) // we already have a multiline heredoc construct
{
if (str == tag)
return [null, null, null, null, false];
else
return [null, null, null, str, tag];
}
// 0 - count, 1 - cmd, 2 - special, 3 - args, 4 - heredoc tag
// 0 - count, 1 - cmd, 2 - special, 3 - args
let matches = str.match(/^:*(\d+|%)?([a-zA-Z]+|!)(!)?(?:\s*(.*?))?$/);
//var matches = str.match(/^:*(\d+|%)?([a-zA-Z]+|!)(!)?(?:\s*(.*?)\s*)?$/);
if (!matches)
return [null, null, null, null, null];
let [, count, cmd, special, args, heredoc] = matches;
return [null, null, null, null];
let [, count, cmd, special, args] = matches;
// parse count
if (count)
@@ -745,14 +737,7 @@ function Commands() //{{{
else
count = this.COUNT_NONE;
if (args)
{
tag = args.match(/<<\s*(\w+)\s*$/);
if (tag && tag[1])
heredoc = tag[1];
}
return [count, cmd, !!special, args || "", heredoc];
return [count, cmd, !!special, args || ""];
},
get complQuote() complQuote,
@@ -769,7 +754,7 @@ function Commands() //{{{
{
return str.replace(/<((?:q-)?)([a-zA-Z]+)?>/g, function (match, quote, token)
{
if (token == "lt") // Don't quote, as in vim (but, why so in vim? You'd think people wouldn't say <q-lt> if they didn't want it)
if (token == "lt") // Don't quote, as in Vim (but, why so in Vim? You'd think people wouldn't say <q-lt> if they didn't want it)
return "<";
let res = tokens[token];
if (res == undefined) // Ignore anything undefined
@@ -811,7 +796,7 @@ function Commands() //{{{
// TODO: Vim allows commands to be defined without {rep} if there are {attr}s
// specified - useful?
commandManager.add(["com[mand]"],
self.add(["com[mand]"],
"List and define commands",
function (args)
{
@@ -918,13 +903,13 @@ function Commands() //{{{
bang: true,
completer: function (context) completion.userCommand(context),
options: [
[["-nargs"], commandManager.OPTION_STRING,
[["-nargs"], self.OPTION_STRING,
function (arg) /^[01*?+]$/.test(arg), ["0", "1", "*", "?", "+"]],
[["-bang"], commandManager.OPTION_NOARG],
[["-count"], commandManager.OPTION_NOARG],
[["-complete"], commandManager.OPTION_STRING,
[["-bang"], self.OPTION_NOARG],
[["-count"], self.OPTION_NOARG],
[["-complete"], self.OPTION_STRING,
function (arg) arg in completeOptionMap || /custom,\w+/.test(arg),
function (context) [[k, ''] for ([k, v] in Iterator(completeOptionMap))]]
function (context) [[k, ""] for ([k, v] in Iterator(completeOptionMap))]]
],
literal: 1,
serial: function () [
@@ -945,7 +930,7 @@ function Commands() //{{{
]
});
commandManager.add(["comc[lear]"],
self.add(["comc[lear]"],
"Delete all user-defined commands",
function ()
{
@@ -953,7 +938,7 @@ function Commands() //{{{
},
{ argCount: "0" });
commandManager.add(["delc[ommand]"],
self.add(["delc[ommand]"],
"Delete the specified user-defined command",
function (args)
{
@@ -971,7 +956,7 @@ function Commands() //{{{
//}}}
return commandManager;
return self;
}; //}}}

View File

@@ -66,7 +66,8 @@ function CompletionContext(editor, name, offset) //{{{
self.contexts[name] = this;
/**
* @property {CompletionContext} This context's parent. {null} when this is a top-level context.
* @property {CompletionContext} This context's parent. {null} when
* this is a top-level context.
*/
self.parent = parent;
@@ -82,7 +83,7 @@ function CompletionContext(editor, name, offset) //{{{
/**
* @property {boolean} Specifies that this context is not finished
* generating results.
* generating results.
* @default false
*/
self.incomplete = false;
@@ -111,6 +112,12 @@ function CompletionContext(editor, name, offset) //{{{
this.editor = editor;
this.compare = function (a, b) String.localeCompare(a.text, b.text);
/**
* @property {function} This function is called when we close
* a completion window with Esc or Ctrl-c. Usually this callback
* is only needed for long, asynchronous completions
*/
this.cancel = null;
/**
* @property {function} The function used to filter the results.
* @default Selects all results which match every predicate in the
@@ -141,16 +148,18 @@ function CompletionContext(editor, name, offset) //{{{
}];
/**
* @property {boolean} Specifies whether this context results must
* match the filter at the begining of the string.
* match the filter at the beginning of the string.
* @default true
*/
this.anchored = true;
/**
* @property {Object} A map of all contexts, keyed on their names.
* Names are assigned when a context is forked, with its specified
* name appended, after a '/', to its parent's name.
* name appended, after a '/', to its parent's name. May
* contain inactive contexts. For active contexts, see
* {@link #contextList}.
*/
this.contexts = { name: this };
this.contexts = { "/": this };
/**
* @property {Object} A mapping of keys, for {@link #getKey}. Given
* { key: value }, getKey(item, key) will return values as such:
@@ -159,7 +168,7 @@ function CompletionContext(editor, name, offset) //{{{
*/
this.keys = { text: 0, description: 1, icon: "icon" };
/**
* @property {number} This context's offset from the begining of
* @property {number} This context's offset from the beginning of
* {@link #editor}'s value.
*/
this.offset = offset || 0;
@@ -519,6 +528,15 @@ CompletionContext.prototype = {
this._filter = this._filter.substr(count);
},
cancelAll: function ()
{
for (let [,context] in Iterator(this.contextList))
{
if (context.cancel)
context.cancel();
}
},
/**
* Gets a key from {@link #cache}, setting it to <b>defVal</b> if it
* doesn't already exists.
@@ -622,6 +640,11 @@ CompletionContext.prototype = {
for (let type in this.selectionTypes)
this.highlight(0, 0, type);
/**
* @property {[CompletionContext]} A list of active
* completion contexts, in the order in which they were
* instantiated.
*/
this.contextList = [];
this.offset = 0;
this.process = [];
@@ -631,6 +654,8 @@ CompletionContext.prototype = {
this.updateAsync = false;
this.waitingForTab = false;
this.cancelAll();
if (this.editor)
{
this.value = this.editor.selection.focusNode.textContent;
@@ -653,7 +678,7 @@ CompletionContext.prototype = {
/**
* Wait for all subcontexts to complete.
*
* @param {boolean} interruptable When true, the call may be interrupted
* @param {boolean} interruptible When true, the call may be interrupted
* via <C-c>. In this case, "Interrupted" may be thrown.
* @param {number} timeout The maximum time, in milliseconds, to wait.
*/
@@ -682,10 +707,10 @@ function Completion() //{{{
const OFFSET = 0, CHAR = 1, STATEMENTS = 2, DOTS = 3, FULL_STATEMENTS = 4, COMMA = 5, FUNCTIONS = 6;
let stack = [];
let functions = [];
let top = []; /* The element on the top of the stack. */
let last = ""; /* The last opening char pushed onto the stack. */
let lastNonwhite = ""; /* Last non-whitespace character we saw. */
let lastChar = ""; /* Last character we saw, used for \ escaping quotes. */
let top = []; // The element on the top of the stack.
let last = ""; // The last opening char pushed onto the stack.
let lastNonwhite = ""; // Last non-whitespace character we saw.
let lastChar = ""; // Last character we saw, used for \ escaping quotes.
let compl = [];
let str = "";
@@ -732,11 +757,10 @@ function Completion() //{{{
return iterator;
}
/* Search the object for strings starting with @key.
* If @last is defined, key is a quoted string, it's
* wrapped in @last after @offset characters are sliced
* off of it and it's quoted.
*/
// Search the object for strings starting with @key.
// If @last is defined, key is a quoted string, it's
// wrapped in @last after @offset characters are sliced
// off of it and it's quoted.
this.objectKeys = function objectKeys(obj)
{
// Things we can dereference
@@ -808,11 +832,10 @@ function Completion() //{{{
}
}
/* Get an element from the stack. If @n is negative,
* count from the top of the stack, otherwise, the bottom.
* If @m is provided, return the @mth value of element @o
* of the stack entey at @n.
*/
// Get an element from the stack. If @n is negative,
// count from the top of the stack, otherwise, the bottom.
// If @m is provided, return the @mth value of element @o
// of the stack entey at @n.
let get = function get(n, m, o)
{
let a = stack[n >= 0 ? n : stack.length + n];
@@ -826,7 +849,7 @@ function Completion() //{{{
function buildStack(filter)
{
let self = this;
/* Push and pop the stack, maintaining references to 'top' and 'last'. */
// Push and pop the stack, maintaining references to 'top' and 'last'.
let push = function push(arg)
{
top = [i, arg, [i], [], [], [], []];
@@ -854,7 +877,7 @@ function Completion() //{{{
return ret;
}
let i = 0, c = ""; /* Current index and character, respectively. */
let i = 0, c = ""; // Current index and character, respectively.
// Reuse the old stack.
if (str && filter.substr(0, str.length) == str)
@@ -870,11 +893,10 @@ function Completion() //{{{
push("#root");
}
/* Build a parse stack, discarding entries as opening characters
* match closing characters. The stack is walked from the top entry
* and down as many levels as it takes us to figure out what it is
* that we're completing.
*/
// Build a parse stack, discarding entries as opening characters
// match closing characters. The stack is walked from the top entry
// and down as many levels as it takes us to figure out what it is
// that we're completing.
str = filter;
let length = str.length;
for (; i < length; lastChar = c, i++)
@@ -906,7 +928,7 @@ function Completion() //{{{
switch (c)
{
case "(":
/* Function call, or if/while/for/... */
// Function call, or if/while/for/...
if (/[\w$]/.test(lastNonwhite))
{
functions.push(i);
@@ -927,7 +949,7 @@ function Completion() //{{{
break;
case ")": pop("("); break;
case "]": pop("["); break;
case "}": pop("{"); /* Fallthrough */
case "}": pop("{"); // Fallthrough
case ";":
top[FULL_STATEMENTS].push(i);
break;
@@ -972,7 +994,7 @@ function Completion() //{{{
this.context.getCache("eval", Object);
this.context.getCache("evalContext", function () ({ __proto__: userContext }));
/* Okay, have parse stack. Figure out what we're completing. */
// Okay, have parse stack. Figure out what we're completing.
// Find any complete statements that we can eval before we eval our object.
// This allows for things like: let doc = window.content.document; let elem = doc.createElement...; elem.<Tab>
@@ -1039,7 +1061,7 @@ function Completion() //{{{
cacheKey = null;
let obj = [[cache.evalContext, "Local Variables"], [userContext, "Global Variables"],
[modules, "modules"], [window, "window"]]; // Default objects;
/* Is this an object dereference? */
// Is this an object dereference?
if (dot < statement) // No.
dot = statement - 1;
else // Yes. Set the object to the string before the dot.
@@ -1073,7 +1095,7 @@ function Completion() //{{{
compl = function (context, obj)
{
context.process = [null, function highlight(item, v) template.highlight(v, true)];
// Sort in a logical fasion for object keys:
// Sort in a logical fashion for object keys:
// Numbers are sorted as numbers, rather than strings, and appear first.
// Constants are unsorted, and appear before other non-null strings.
// Other strings are sorted in the default manner.
@@ -1112,11 +1134,11 @@ function Completion() //{{{
// Otherwise, do nothing.
if (last == "'" || last == '"')
{
/*
* str = "foo[bar + 'baz"
* obj = "foo"
* key = "bar + ''"
*/
//
// str = "foo[bar + 'baz"
// obj = "foo"
// key = "bar + ''"
//
// The top of the stack is the sting we're completing.
// Wrap it in its delimiters and eval it to process escape sequences.
@@ -1133,21 +1155,20 @@ function Completion() //{{{
return this.eval(key);
}
/* Is this an object accessor? */
// Is this an object accessor?
if (get(-2)[CHAR] == "[") // Are we inside of []?
{
/* Stack:
* [-1]: "...
* [-2]: [...
* [-3]: base statement
*/
// Stack:
// [-1]: "...
// [-2]: [...
// [-3]: base statement
// Yes. If the [ starts at the begining of a logical
// Yes. If the [ starts at the beginning of a logical
// statement, we're in an array literal, and we're done.
if (get(-3, 0, STATEMENTS) == get(-2)[OFFSET])
return;
// Begining of the statement upto the opening [
// Beginning of the statement upto the opening [
let obj = getObj(-3, get(-2)[OFFSET]);
return complete.call(this, obj, getKey(), null, string, last);
@@ -1156,11 +1177,10 @@ function Completion() //{{{
// Is this a function call?
if (get(-2)[CHAR] == "(")
{
/* Stack:
* [-1]: "...
* [-2]: (...
* [-3]: base statement
*/
// Stack:
// [-1]: "...
// [-2]: (...
// [-3]: base statement
// Does the opening "(" mark a function call?
if (get(-3, 0, FUNCTIONS) != get(-2)[OFFSET])
@@ -1209,15 +1229,15 @@ function Completion() //{{{
return;
}
/*
* str = "foo.bar.baz"
* obj = "foo.bar"
* key = "baz"
*
* str = "foo"
* obj = [modules, window]
* key = "foo"
*/
//
// str = "foo.bar.baz"
// obj = "foo.bar"
// key = "baz"
//
// str = "foo"
// obj = [modules, window]
// key = "foo"
//
let [offset, obj, key] = getObjKey(-1);
@@ -1230,7 +1250,7 @@ function Completion() //{{{
}
if (!/^(?:[a-zA-Z_$][\w$]*)?$/.test(key))
return; /* Not a word. Forget it. Can this even happen? */
return; // Not a word. Forget it. Can this even happen?
try
{ // FIXME
@@ -1250,7 +1270,7 @@ function Completion() //{{{
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
let self = {
const self = {
setFunctionCompleter: function setFunctionCompleter(funcs, completers)
{
@@ -1338,6 +1358,7 @@ function Completion() //{{{
context.completions = config.autocommands;
},
// TODO: shouldn't these app-specific completers be moved to config.js? --djk
bookmark: function bookmark(context, tags, extra)
{
context.title = ["Bookmark", "Title"];
@@ -1552,13 +1573,15 @@ function Completion() //{{{
location: function location(context)
{
if (!services.get("autoCompleteSearch"))
return
return;
context.anchored = false;
context.title = ["Smart Completions"];
context.keys.icon = 2;
context.incomplete = true;
context.hasItems = context.completions.length > 0; // XXX
context.filterFunc = null;
context.cancel = function () services.get("autoCompleteSearch").stopSearch();
context.compare = null;
let timer = new Timer(50, 100, function (result) {
context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
@@ -1627,9 +1650,8 @@ function Completion() //{{{
let completions = completer(context);
if (!completions)
return;
/* Not vim compatible, but is a significant enough improvement
* that it's worth breaking compatibility.
*/
// Not Vim compatible, but is a significant enough improvement
// that it's worth breaking compatibility.
if (newValues instanceof Array)
{
completions = completions.filter(function (val) newValues.indexOf(val[0]) == -1);

View File

@@ -217,7 +217,7 @@ function Editor() //{{{
var myModes = [modes.INSERT, modes.COMMAND_LINE];
/* KEYS COUNT CARET TEXTAREA VISUAL_TEXTAREA */
// KEYS COUNT CARET TEXTAREA VISUAL_TEXTAREA
addMovementMap(["k", "<Up>"], true, "lineMove", false, "cmd_linePrevious", selectPreviousLine);
addMovementMap(["j", "<Down>", "<Return>"], true, "lineMove", true, "cmd_lineNext", selectNextLine);
addMovementMap(["h", "<Left>", "<BS>"], true, "characterMove", false, "cmd_charPrevious", "cmd_selectCharPrevious");
@@ -371,10 +371,14 @@ function Editor() //{{{
{ flags: Mappings.flags.COUNT });
// visual mode
mappings.add([modes.CARET, modes.TEXTAREA, modes.VISUAL],
mappings.add([modes.CARET, modes.TEXTAREA],
["v"], "Start visual mode",
function (count) { modes.set(modes.VISUAL, liberator.mode); });
mappings.add([modes.VISUAL],
["v"], "End visual mode",
function (count) { events.onEscape() });
mappings.add([modes.TEXTAREA],
["V"], "Start visual line mode",
function (count)
@@ -557,9 +561,9 @@ function Editor() //{{{
unselectText: function ()
{
let elt = window.document.commandDispatcher.focusedElement;
if (elt && elt.selectionEnd)
elt.selectionEnd = elt.selectionStart;
let elem = window.document.commandDispatcher.focusedElement;
if (elem && elem.selectionEnd)
elem.selectionEnd = elem.selectionStart;
},
selectedText: function ()
@@ -570,20 +574,20 @@ function Editor() //{{{
pasteClipboard: function ()
{
let elt = window.document.commandDispatcher.focusedElement;
let elem = window.document.commandDispatcher.focusedElement;
if (elt.setSelectionRange && util.readFromClipboard())
if (elem.setSelectionRange && util.readFromClipboard())
// readFromClipboard would return 'undefined' if not checked
// dunno about .setSelectionRange
{
let rangeStart = elt.selectionStart; // caret position
let rangeEnd = elt.selectionEnd;
let tempStr1 = elt.value.substring(0, rangeStart);
let rangeStart = elem.selectionStart; // caret position
let rangeEnd = elem.selectionEnd;
let tempStr1 = elem.value.substring(0, rangeStart);
let tempStr2 = util.readFromClipboard();
let tempStr3 = elt.value.substring(rangeEnd);
elt.value = tempStr1 + tempStr2 + tempStr3;
elt.selectionStart = rangeStart + tempStr2.length;
elt.selectionEnd = elt.selectionStart;
let tempStr3 = elem.value.substring(rangeEnd);
elem.value = tempStr1 + tempStr2 + tempStr3;
elem.selectionStart = rangeStart + tempStr2.length;
elem.selectionEnd = elem.selectionStart;
}
},
@@ -709,7 +713,7 @@ function Editor() //{{{
// This function will move/select up to given "pos"
// Simple setSelectionRange() would be better, but we want to maintain the correct
// order of selectionStart/End (a firefox bug always makes selectionStart <= selectionEnd)
// order of selectionStart/End (a Firefox bug always makes selectionStart <= selectionEnd)
// Use only for small movements!
moveToPosition: function (pos, forward, select)
{
@@ -809,7 +813,7 @@ function Editor() //{{{
}
args.push(path);
liberator.callFunctionInThread(null, io.run, args.shift(), args, true);
liberator.callFunctionInThread(null, io.run, io.expandPath(args.shift()), args, true);
},
// TODO: clean up with 2 functions for textboxes and currentEditor?
@@ -900,7 +904,7 @@ function Editor() //{{{
//
// if filter == ! remove all and add it as only END
//
// variant 1: rhs matches anywere in loop
// variant 1: rhs matches anywhere in loop
//
// 1 mod matches anywhere in loop
// a) simple replace and
@@ -913,7 +917,7 @@ function Editor() //{{{
// (b) a ! is there. do nothing END)
//
// variant 2: rhs matches *no*were in loop and filter is c or i
// everykind of current combo is possible to 1 {c,i,!} or two {c and i}
// every kind of current combo is possible to 1 {c,i,!} or two {c and i}
//
// 1 mod is ! split into two i + c END
// 1 not !: opposite mode (first), add/change 'second' and END

View File

@@ -4,7 +4,7 @@ catch (e)
{
__liberator_eval_error = e;
}
// Important: The eval statement *must* remain on the first line
// IMPORTANT: The eval statement *must* remain on the first line
// in order for line numbering in any errors to remain correct.
//
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -37,13 +37,12 @@ function AutoCommands() //{{{
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
const AutoCommand = new Struct("event", "pattern", "command");
var store = [];
var lastFocus = null;
function matchAutoCmd(autoCmd, event, regex)
{
return (!event || autoCmd.event == event) &&
(!regex || autoCmd.pattern.source == regex);
return (!event || autoCmd.event == event) && (!regex || autoCmd.pattern.source == regex);
}
/////////////////////////////////////////////////////////////////////////////}}}
@@ -72,6 +71,7 @@ function AutoCommands() //{{{
{
let [event, regex, cmd] = args;
let events = null;
if (event)
{
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
@@ -96,6 +96,7 @@ function AutoCommands() //{{{
{
if (event == "*")
event = null;
if (args.bang)
{
// TODO: "*" only appears to work in Vim when there is a {group} specified
@@ -181,6 +182,16 @@ function AutoCommands() //{{{
__iterator__: function () util.Array.iterator(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")
@@ -188,20 +199,43 @@ function AutoCommands() //{{{
events = events.split(",");
liberator.log("DEPRECATED: the events list arg to autocommands.add() should be an array of event names");
}
events.forEach(function (event)
store.push({ event: event, pattern: RegExp(regex), command: cmd }));
events.forEach(function (event) {
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 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)
{
store = 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 = {};
@@ -237,6 +271,14 @@ function AutoCommands() //{{{
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))
@@ -273,7 +315,7 @@ function AutoCommands() //{{{
}
else
{
liberator.execute(commands.replaceTokens(autoCmd.command, args));
liberator.execute(commands.replaceTokens(autoCmd.command, args), null, true);
}
}
}
@@ -295,10 +337,12 @@ function Events() //{{{
var fullscreen = window.fullScreen;
var lastFocus = null;
var inputBufferLength = 0; // count the number of keys in v.input.buffer (can be different from v.input.buffer.length)
var skipMap = false; // while feeding the keys (stored in v.input.buffer | no map found) - ignore mappings
var macros = storage.newMap('macros', true);
var macros = storage.newMap("macros", true);
var currentMacro = "";
var lastMacro = "";
@@ -457,14 +501,14 @@ function Events() //{{{
function isFormElemFocused()
{
let elt = window.document.commandDispatcher.focusedElement;
if (elt == null)
let elem = window.document.commandDispatcher.focusedElement;
if (elem == null)
return false;
try
{ // sometimes the elt doesn't have .localName
let tagname = elt.localName.toLowerCase();
let type = elt.type.toLowerCase();
{ // sometimes the elem doesn't have .localName
let tagname = elem.localName.toLowerCase();
let type = elem.type.toLowerCase();
if ((tagname == "input" && (type != "image")) ||
tagname == "textarea" ||
@@ -524,7 +568,7 @@ function Events() //{{{
{
// hacky way to get rid of "Transfering data from ..." on sites with frames
// when you click on a link inside a frameset, because asyncUpdateUI
// is not triggered there (firefox bug?)
// is not triggered there (Firefox bug?)
setTimeout(statusline.updateUrl, 10);
return;
}
@@ -568,7 +612,7 @@ function Events() //{{{
{
try
{
eventManager[method](event);
self[method](event);
}
catch (e)
{
@@ -587,7 +631,6 @@ function Events() //{{{
// load all macros inside ~/.vimperator/macros/
// setTimeout needed since io. is loaded after events.
setTimeout (function () {
// FIXME: largely duplicated for loading plugins
try
{
let dirs = io.getRuntimeDirectories("macros");
@@ -715,7 +758,7 @@ function Events() //{{{
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
var eventManager = {
const self = {
feedingKeys: false,
@@ -805,7 +848,7 @@ function Events() //{{{
{
if (lastMacro.length == 1)
// TODO: ignore this like Vim?
liberator.echoerr("Exxx: Register " + lastMacro + " not set");
liberator.echoerr("Exxx: Register '" + lastMacro + "' not set");
else
liberator.echoerr("Exxx: Named macro '" + lastMacro + "' not set");
}
@@ -832,12 +875,18 @@ function Events() //{{{
}
},
// This method pushes keys into the event queue from liberator
// it is similar to vim's feedkeys() method, but cannot cope with
// 2 partially-fed strings, you have to feed one parsable string
//
// @param keys: a string like "2<C-f>" to pass
// if you want < to be taken literally, prepend it with a \\
/**
* Pushes keys into the event queue from liberator it is similar to
* Vim's feedkeys() method, but cannot cope with 2 partially-fed
* strings, you have to feed one parsable string.
*
* @param {string} keys A string like "2<C-f>" to pass if you want "<"
* to be taken literally, prepend it with a "\\".
* @param {boolean} noremap Allow recursive mappings.
* @param {boolean} silent Whether the command should be echoed to the
* command-line.
* @returns {boolean}
*/
feedkeys: function (keys, noremap, silent)
{
let doc = window.document;
@@ -965,7 +1014,7 @@ function Events() //{{{
if (event.metaKey)
modifier += "M-";
if (event.type == "keypress")
if (/^key/.test(event.type))
{
if (event.charCode == 0)
{
@@ -1043,8 +1092,9 @@ function Events() //{{{
if (buffer.loaded == 1)
return true;
const maxWaitTime = 25;
let start = Date.now();
let end = start + 25000; // maximum time to wait - TODO: add option
let end = start + (maxWaitTime * 1000); // maximum time to wait - TODO: add option
let now;
while (now = Date.now(), now < end)
{
@@ -1061,14 +1111,14 @@ function Events() //{{{
break;
}
else
liberator.echomsg("Waiting for page to load...");
liberator.echo("Waiting for page to load...", commandline.DISALLOW_MULTILINE);
}
modes.show();
// TODO: allow macros to be continued when page does not fully load with an option
let ret = (buffer.loaded == 1);
if (!ret)
liberator.echoerr("Page did not load completely in " + ms + " milliseconds. Macro stopped.");
liberator.echoerr("Page did not load completely in " + maxWaitTime + " seconds. Macro stopped.");
liberator.dump("done waiting: " + ret);
// sometimes the input widget had focus when replaying a macro
@@ -1078,10 +1128,10 @@ function Events() //{{{
return ret;
},
// argument "event" is delibarately not used, as i don't seem to have
// argument "event" is deliberately not used, as i don't seem to have
// access to the real focus target
//
// the ugly wantsModeReset is needed, because firefox generates a massive
// the ugly wantsModeReset is needed, because Firefox generates a massive
// amount of focus changes for things like <C-v><C-k> (focusing the search field)
onFocusChange: function (event)
{
@@ -1128,7 +1178,7 @@ function Events() //{{{
if (config.name == "Muttator")
{
// we switch to -- MESSAGE -- mode for muttator, when the main HTML widget gets focus
// we switch to -- MESSAGE -- mode for Muttator, when the main HTML widget gets focus
if (hasHTMLDocument(win) || elem instanceof HTMLAnchorElement)
{
if (config.isComposeWindow)
@@ -1334,7 +1384,7 @@ function Events() //{{{
return false;
}
// XXX: ugly hack for now pass certain keys to firefox as they are without beeping
// XXX: ugly hack for now pass certain keys to Firefox as they are without beeping
// also fixes key navigation in combo boxes, submitting forms, etc.
// FIXME: breaks iabbr for now --mst
if ((config.name == "Vimperator" && liberator.mode == modes.NORMAL)
@@ -1373,7 +1423,7 @@ function Events() //{{{
return false;
}
// others are left to generate the 'input' event or handled by firefox
// others are left to generate the 'input' event or handled by Firefox
return;
}
}
@@ -1503,7 +1553,7 @@ function Events() //{{{
if (key != "<Esc>" && key != "<C-[>")
{
// allow key to be passed to firefox if we can't handle it
// allow key to be passed to Firefox if we can't handle it
stop = false;
if (liberator.mode == modes.COMMAND_LINE)
@@ -1673,7 +1723,7 @@ function Events() //{{{
}
}; //}}}
window.XULBrowserWindow = eventManager.progressListener;
window.XULBrowserWindow = self.progressListener;
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
@@ -1682,21 +1732,21 @@ function Events() //{{{
.XULBrowserWindow = window.XULBrowserWindow;
try
{
getBrowser().addProgressListener(eventManager.progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
getBrowser().addProgressListener(self.progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
}
catch (e) {}
eventManager.prefObserver.register();
self.prefObserver.register();
liberator.registerObserver("shutdown", function () {
eventManager.destroy();
eventManager.prefObserver.unregister();
self.destroy();
self.prefObserver.unregister();
});
window.addEventListener("keypress", wrapListener("onKeyPress"), true);
window.addEventListener("keydown", wrapListener("onKeyUpOrDown"), true);
window.addEventListener("keyup", wrapListener("onKeyUpOrDown"), true);
return eventManager;
return self;
}; //}}}

View File

@@ -108,7 +108,7 @@ function Search() //{{{
// strip case-sensitive modifiers
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
// remove any modifer escape \
// remove any modifier escape \
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
searchString = pattern;
@@ -440,7 +440,7 @@ function Search() //{{{
// TODO: code to reposition the document to the place before search started
},
// FIXME: thunderbird incompatible
// FIXME: Thunderbird incompatible
// this is not dependent on the value of 'hlsearch'
highlight: function (text)
{
@@ -469,7 +469,6 @@ function Search() //{{{
// highlighted
getBrowser().fastFind.collapseSelection();
}
};
//}}}
}; //}}}

View File

@@ -9,3 +9,4 @@ let url = page ? "chrome://liberator/locale/" + page : content.history.previous;
win.getBrowser().loadURIWithFlags(url, Components.interfaces.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY, null, null, null);
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -47,7 +47,7 @@ function Hints() //{{{
var hintNumber = 0; // only the numerical part of the hint
var usedTabKey = false; // when we used <Tab> to select an element
var prevInput = ""; // record previous user input type, "text" || "number"
var extendedhintCount; // for the count arugument of Mode#action (extended hint only)
var extendedhintCount; // for the count argument of Mode#action (extended hint only)
// hints[] = [elem, text, span, imgspan, elem.style.backgroundColor, elem.style.color]
var pageHints = [];
@@ -64,25 +64,43 @@ function Hints() //{{{
Mode.defaultValue("tags", function () function () options.hinttags);
function extended() options.extendedhinttags;
const hintModes = {
";": Mode("Focus hint", function (elem) buffer.focusElement(elem), extended),
a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)),
f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () "//body | //xhtml:body"),
s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)),
o: Mode("Follow hint", function (elem) buffer.followLink(elem, liberator.CURRENT_TAB)),
t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, liberator.NEW_TAB)),
b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB)),
v: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, false), extended),
V: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, true), extended),
w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, liberator.NEW_WINDOW), extended),
"?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
O: Mode("Open location based on hint", function (elem, loc) commandline.open(":", "open " + loc, modes.EX)),
T: Mode("Open new tab based on hint", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX)),
W: Mode("Open new window based on hint", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX)),
y: Mode("Yank hint location", function (elem, loc) util.copyToClipboard(loc, true)),
Y: Mode("Yank hint description", function (elem) util.copyToClipboard(elem.textContent || "", true), extended)
";": Mode("Focus hint", function (elem) buffer.focusElement(elem), extended),
"?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)),
a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)),
f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () "//body | //xhtml:body"),
o: Mode("Follow hint", function (elem) buffer.followLink(elem, liberator.CURRENT_TAB)),
t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, liberator.NEW_TAB)),
b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB)),
w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, liberator.NEW_WINDOW), extended),
F: Mode("Follow hint sequence in tabs", hintSequenceElement),
O: Mode("Preselect hint in an :open query", function (elem, loc) commandline.open(":", "open " + loc, modes.EX)),
T: Mode("Preselect hint in a :tabopen query", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX)),
W: Mode("Preselect hint in a :winopen query", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX)),
v: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, false), extended),
V: Mode("View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true), extended),
y: Mode("Yank hint location", function (elem, loc) util.copyToClipboard(loc, true)),
Y: Mode("Yank hint description", function (elem) util.copyToClipboard(elem.textContent || "", true), extended)
};
// Used to open multiple hints
function hintSequenceElement(elem)
{
// Want to always open sequence hints in background
// (remember: NEW_BACKGROUND_TAB and NEW_TAB semantics assume
// that loadInBackground=true)
if (options.getPref("browser.tabs.loadInBackground"))
buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB);
else
buffer.followLink(elem, liberator.NEW_TAB);
// Move to next element in sequence
// TODO: Maybe we find a *simple* way to keep the hints displayed rather than
// showing them again, or is this short flash actually needed as a "usability
// feature"? --mst
hints.show("F");
}
// reset all important variables
function reset()
{
@@ -209,7 +227,7 @@ function Hints() //{{{
let scrollY = doc.defaultView.scrollY;
inner:
for (let i in (util.interruptableRange(start, end + 1, 500)))
for (let i in (util.interruptibleRange(start, end + 1, 500)))
{
let hint = pageHints[i];
[elem, text, span, imgspan] = hint;
@@ -340,7 +358,7 @@ function Hints() //{{{
removeHints(timeout);
if (timeout == 0)
// force a possible mode change, based on wheter an input field has focus
// force a possible mode change, based on whether an input field has focus
events.onFocusChange();
setTimeout(function () {
if (modes.extended & modes.HINTS)

View File

@@ -66,7 +66,7 @@ function IO() //{{{
const WINDOWS = liberator.has("Win32");
const EXTENSION_NAME = config.name.toLowerCase(); // "vimperator" or "muttator"
const downloadManager = Cc["@mozilla.org/download-manager;1"].createInstance(Ci.nsIDownloadManager);
const downloadManager = Cc["@mozilla.org/download-manager;1"].createInstance(Ci.nsIDownloadManager);
var processDir = services.get("directory").get("CurWorkD", Ci.nsIFile);
var cwd = processDir;
@@ -108,10 +108,10 @@ function IO() //{{{
function joinPaths(head, tail)
{
let path = ioManager.getFile(head);
let path = self.getFile(head);
try
{
path.appendRelativePath(ioManager.expandPath(tail, true)); // FIXME: should only expand env vars and normalise path separators
path.appendRelativePath(self.expandPath(tail, true)); // FIXME: should only expand env vars and normalise path separators
if (path.exists() && path.normalize)
path.normalize();
}
@@ -379,14 +379,14 @@ function IO() //{{{
liberator.registerObserver("load_completion", function ()
{
completion.setFunctionCompleter([ioManager.getFile, ioManager.expandPath],
completion.setFunctionCompleter([self.getFile, self.expandPath],
[function (context, obj, args) {
context.quote[2] = "";
completion.file(context, true);
}]);
});
var ioManager = {
const self = {
MODE_RDONLY: 0x01,
MODE_WRONLY: 0x02,
@@ -405,7 +405,7 @@ function IO() //{{{
// Firefox's CWD - see // https://bugzilla.mozilla.org/show_bug.cgi?id=280953
getCurrentDirectory: function ()
{
let dir = ioManager.getFile(cwd.path);
let dir = self.getFile(cwd.path);
// NOTE: the directory could have been deleted underneath us so
// fallback to Firefox's CWD
@@ -425,7 +425,7 @@ function IO() //{{{
}
else
{
let dir = ioManager.getFile(newdir);
let dir = self.getFile(newdir);
if (!dir.exists() || !dir.isDirectory())
{
@@ -436,7 +436,7 @@ function IO() //{{{
[cwd, oldcwd] = [dir, this.getCurrentDirectory()];
}
return ioManager.getCurrentDirectory();
return self.getCurrentDirectory();
},
getRuntimeDirectories: function (specialDirectory)
@@ -482,10 +482,10 @@ function IO() //{{{
}
else
{
let expandedPath = ioManager.expandPath(path);
let expandedPath = self.expandPath(path);
if (!isAbsolutePath(expandedPath) && !noCheckPWD)
file = joinPaths(ioManager.getCurrentDirectory().path, expandedPath);
file = joinPaths(self.getCurrentDirectory().path, expandedPath);
else
file.initWithPath(expandedPath);
}
@@ -502,7 +502,7 @@ function IO() //{{{
switch (EXTENSION_NAME)
{
case "muttator":
tmpName = "mutt-ator-mail"; // to allow vim to :set ft=mail automatically
tmpName = "mutt-ator-mail"; // to allow Vim to :set ft=mail automatically
break;
case "vimperator":
try
@@ -529,7 +529,7 @@ function IO() //{{{
readDirectory: function (file, sort)
{
if (typeof file == "string")
file = ioManager.getFile(file);
file = self.getFile(file);
else if (!(file instanceof Ci.nsILocalFile))
throw Cr.NS_ERROR_INVALID_ARG; // FIXME: does not work as expected, just shows undefined: undefined
@@ -543,11 +543,12 @@ function IO() //{{{
array.push(entry.QueryInterface(Ci.nsIFile));
}
if (sort)
return array.sort(function (a, b) b.isDirectory() - a.isDirectory() || String.localeCompare(a.path, b.path));
array.sort(function (a, b) b.isDirectory() - a.isDirectory() || String.localeCompare(a.path, b.path));
return array;
}
else
return []; // XXX: or should it throw an error, probably yes?
// Yes --djk
},
// file is either a full pathname or an instance of file instanceof nsILocalFile
@@ -559,7 +560,7 @@ function IO() //{{{
let toCharset = "UTF-8";
if (typeof file == "string")
file = ioManager.getFile(file);
file = self.getFile(file);
else if (!(file instanceof Ci.nsILocalFile))
throw Cr.NS_ERROR_INVALID_ARG; // FIXME: does not work as expected, just shows undefined: undefined
@@ -587,14 +588,14 @@ function IO() //{{{
let charset = "UTF-8"; // Can be any character encoding name that Mozilla supports
if (typeof file == "string")
file = ioManager.getFile(file);
file = self.getFile(file);
else if (!(file instanceof Ci.nsILocalFile))
throw Cr.NS_ERROR_INVALID_ARG; // FIXME: does not work as expected, just shows undefined: undefined
if (mode == ">>")
mode = ioManager.MODE_WRONLY | ioManager.MODE_CREATE | ioManager.MODE_APPEND;
mode = self.MODE_WRONLY | self.MODE_CREATE | self.MODE_APPEND;
else if (!mode || mode == ">")
mode = ioManager.MODE_WRONLY | ioManager.MODE_CREATE | ioManager.MODE_TRUNCATE;
mode = self.MODE_WRONLY | self.MODE_CREATE | self.MODE_TRUNCATE;
if (!perms)
perms = 0644;
@@ -616,12 +617,12 @@ function IO() //{{{
if (isAbsolutePath(program))
{
file = ioManager.getFile(program, true);
file = self.getFile(program, true);
}
else
{
let dirs = services.get("environment").get("PATH").split(WINDOWS ? ";" : ":");
// Windows tries the cwd first TODO: desirable?
// Windows tries the CWD first TODO: desirable?
if (WINDOWS)
dirs = [io.getCurrentDirectory().path].concat(dirs);
@@ -693,9 +694,9 @@ lookup:
}
if (res > 0) // FIXME: Is this really right? Shouldn't we always show both?
var output = ioManager.readFile(stderr) + "\nshell returned " + res;
var output = self.readFile(stderr) + "\nshell returned " + res;
else
output = ioManager.readFile(stdout);
output = self.readFile(stdout);
// if there is only one \n at the end, chop it off
if (output && output.indexOf("\n") == output.length - 1)
@@ -740,15 +741,15 @@ lookup:
return found;
},
// files which end in .js are sourced as pure javascript files,
// files which end in .js are sourced as pure JavaScript files,
// no need (actually forbidden) to add: js <<EOF ... EOF around those files
source: function (filename, silent)
{
let wasSourcing = ioManager.sourcing;
let wasSourcing = self.sourcing;
try
{
var file = ioManager.getFile(filename);
ioManager.sourcing = {
var file = self.getFile(filename);
self.sourcing = {
file: file.path,
line: 0
};
@@ -770,10 +771,10 @@ lookup:
liberator.echomsg("sourcing " + filename.quote(), 2);
let str = ioManager.readFile(file);
let str = self.readFile(file);
let uri = ioService.newFileURI(file);
// handle pure javascript files specially
// handle pure JavaScript files specially
if (/\.js$/.test(filename))
{
try
@@ -797,7 +798,7 @@ lookup:
{
let heredoc = "";
let heredocEnd = null; // the string which ends the heredoc
let lines = str.split("\n");
let lines = str.split(/\r\n|[\r\n]/);
for (let [i, line] in Iterator(lines))
{
@@ -816,7 +817,7 @@ lookup:
}
else
{
ioManager.sourcing.line = i + 1;
self.sourcing.line = i + 1;
// skip line comments and blank lines
line = line.replace(/\r$/, "");
@@ -891,7 +892,7 @@ lookup:
}
finally
{
ioManager.sourcing = wasSourcing;
self.sourcing = wasSourcing;
}
},
@@ -911,7 +912,7 @@ lookup:
}
}; //}}}
return ioManager;
return self;
}; //}}}
@@ -921,8 +922,16 @@ IO.PATH_SEP = (function () {
return file.path[0];
})();
IO.__defineGetter__("runtimePath", function () services.get("environment").get(config.name.toUpperCase() + "_RUNTIME") ||
"~/" + (liberator.has("Win32") ? "" : ".") + config.name.toLowerCase());
IO.__defineGetter__("runtimePath", function () {
const rtpvar = config.name.toUpperCase() + "_RUNTIME";
let rtp = services.get("environment").get(rtpvar);
if (!rtp)
{
rtp = "~/" + (liberator.has("Win32") ? "" : ".") + config.name.toLowerCase();
services.get("environment").set(rtpvar, rtp);
}
return rtp;
});
IO.expandPath = function (path, relative)
{
@@ -947,7 +956,7 @@ IO.expandPath = function (path, relative)
// Try $HOME first, on all systems
let home = services.get("environment").get("HOME");
// Windows has its own ideosyncratic $HOME variables.
// Windows has its own idiosyncratic $HOME variables.
if (!home && WINDOWS)
home = services.get("environment").get("USERPROFILE") ||
services.get("environment").get("HOMEDRIVE") + services.get("environment").get("HOMEPATH");

View File

@@ -57,11 +57,14 @@ const liberator = (function () //{{{
run: function () { this.func.apply(this.self, this.args); }
};
var callbacks = [];
var observers = [];
const callbacks = {};
const observers = {};
function registerObserver(type, callback)
{
observers.push([type, callback]);
if (!(type in observers))
observers[type] = [];
observers[type].push(callback);
}
let nError = 0;
@@ -83,7 +86,7 @@ const liberator = (function () //{{{
}
}
// Only general options are added here, which are valid for all vimperator like extensions
// Only general options are added here, which are valid for all Vimperator like extensions
registerObserver("load_options", function ()
{
options.add(["errorbells", "eb"],
@@ -364,7 +367,7 @@ const liberator = (function () //{{{
"Run a JavaScript command through eval()",
function (args)
{
if (args.bang) // open javascript console
if (args.bang) // open JavaScript console
{
liberator.open("chrome://global/content/console.xul",
(options["newtab"] && options.get("newtab").has("all", "javascript"))
@@ -449,7 +452,7 @@ const liberator = (function () //{{{
let each, eachUnits, totalUnits;
let total = 0;
for (let i in util.interruptableRange(0, count, 500))
for (let i in util.interruptibleRange(0, count, 500))
{
let now = Date.now();
method();
@@ -605,6 +608,11 @@ const liberator = (function () //{{{
// ###VERSION### and ###DATE### are replaced by the Makefile
version: "###VERSION### (created: ###DATE###)",
// NOTE: services.get("profile").selectedProfile.name is not rightness.
// If default profile Firefox runs without arguments,
// then selectedProfile returns last selected profile! (not current one!)
profileName: services.get("directory").get("ProfD", Ci.nsIFile).leafName.replace(/^.+?\./, ""),
// TODO: move to events.js?
input: {
buffer: "", // partial command storage
@@ -622,19 +630,15 @@ const liberator = (function () //{{{
// TODO: move to ui.js?
registerCallback: function (type, mode, func)
{
// TODO: check if callback is already registered
callbacks.push([type, mode, func]);
if (!(type in callbacks))
callbacks[type] = {};
callbacks[type][mode] = func;
},
triggerCallback: function (type, mode, data)
{
// liberator.dump("type: " + type + " mode: " + mode + "data: " + data + "\n");
for (let i = 0; i < callbacks.length; i++)
{
let [thistype, thismode, thisfunc] = callbacks[i];
if (mode == thismode && type == thistype)
return thisfunc.call(this, data);
}
if (callbacks[type] && callbacks[type][mode])
return callbacks[type][mode].call(this, data);
return false;
},
@@ -642,16 +646,14 @@ const liberator = (function () //{{{
unregisterObserver: function (type, callback)
{
observers = observers.filter(function ([t, c]) t != type || c != callback);
if (type in observers)
observers[type] = observers[type].filter(function (c) c != callback);
},
triggerObserver: function (type)
{
for (let [,[thistype, callback]] in Iterator(observers))
{
if (thistype == type)
callback.apply(null, Array.slice(arguments, 1));
}
for (let [,fn] in Iterator(observers[type] || []))
fn.apply(null, Array.slice(arguments, 1));
},
beep: function ()
@@ -988,49 +990,53 @@ const liberator = (function () //{{{
loadPlugins: function ()
{
// FIXME: largely duplicated for loading macros
try
function sourceDirectory(dir)
{
let dirs = io.getRuntimeDirectories("plugin");
if (dirs.length == 0)
if (!dir.isReadable())
{
liberator.log("No user plugin directory found", 3);
liberator.echoerr("E484: Can't open file " + dir.path);
return;
}
for (let [,dir] in Iterator(dirs))
{
// TODO: search plugins/**/* for plugins
liberator.echomsg('Searching for "plugin/*.{js,vimp}" in ' + dir.path.quote(), 2);
liberator.log("Sourcing plugin directory: " + dir.path + "...", 3);
let files = io.readDirectory(dir.path, true);
files.forEach(function (file) {
if (!file.isDirectory() && /\.(js|vimp)$/i.test(file.path) && !(file.path in liberator.pluginFiles))
liberator.log("Sourcing plugin directory: " + dir.path + "...", 3);
io.readDirectory(dir.path, true).forEach(function (file) {
if (file.isFile() && /\.(js|vimp)$/i.test(file.path) && !(file.path in liberator.pluginFiles))
{
try
{
try
{
io.source(file.path, false);
liberator.pluginFiles[file.path] = true;
}
catch (e)
{
liberator.reportError(e);
}
io.source(file.path, false);
liberator.pluginFiles[file.path] = true;
}
});
}
catch (e)
{
liberator.reportError(e);
}
}
else if (file.isDirectory())
{
sourceDirectory(file);
}
});
}
catch (e)
let dirs = io.getRuntimeDirectories("plugin");
if (dirs.length == 0)
{
// thrown if directory does not exist
liberator.log("Error sourcing plugin directory: " + e, 9);
liberator.log("No user plugin directory found", 3);
return;
}
liberator.echomsg('Searching for "plugin/**/*.{js,vimp}" in '
+ [dir.path.replace(/.plugin$/, "") for each (dir in dirs)].join(",").quote(), 2);
dirs.forEach(function (dir) {
liberator.echomsg("Searching for " + (dir.path + "/**/*.{js,vimp}").quote(), 3);
sourceDirectory(dir);
});
},
// logs a message to the javascript error console
// logs a message to the JavaScript error console
// if msg is an object, it is beautified
// TODO: add proper level constants
log: function (msg, level)
@@ -1049,20 +1055,24 @@ const liberator = (function () //{{{
if (typeof msg == "object")
msg = util.objectToString(msg, false);
const consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
consoleService.logStringMessage(config.name.toLowerCase() + ": " + msg);
services.get("console").logStringMessage(config.name.toLowerCase() + ": " + msg);
},
// open one or more URLs
//
// @param urls: either a string or an array of urls
// The array can look like this:
// ["url1", "url2", "url3", ...] or:
// [["url1", postdata1], ["url2", postdata2], ...]
// @param where: if ommited, CURRENT_TAB is assumed
// but NEW_TAB is set when liberator.forceNewTab is true.
// @param force: Don't prompt whether to open more than 20 tabs.
// @returns true when load was initiated, or false on error
/**
* Opens one or more URLs. Returns true when load was initiated, or
* false on error.
*
* @param {FIXME} urls Either a URL string or an array of URLs.
* The array can look like this:
* ["url1", "url2", "url3", ...]
* or:
* [["url1", postdata1], ["url2", postdata2], ...]
* @param {number} where If ommited, CURRENT_TAB is assumed but NEW_TAB
* is set when liberator.forceNewTab is true.
* @param {boolean} force Don't prompt whether to open more than 20
* tabs.
* @returns {boolean}
*/
open: function (urls, where, force)
{
// convert the string to an array of converted URLs
@@ -1083,6 +1093,8 @@ const liberator = (function () //{{{
if (urls.length == 0)
return false;
let browser = window.getBrowser();
function open(urls, where)
{
let url = Array.concat(urls)[0];
@@ -1092,7 +1104,7 @@ const liberator = (function () //{{{
switch (where)
{
case liberator.CURRENT_TAB:
getBrowser().loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, postdata);
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, postdata);
break;
case liberator.NEW_BACKGROUND_TAB:
@@ -1100,21 +1112,21 @@ const liberator = (function () //{{{
if (!liberator.has("tabs"))
return open(urls, liberator.NEW_WINDOW);
let tab = getBrowser().addTab(url, null, null, postdata);
if (where == liberator.NEW_TAB)
getBrowser().selectedTab = tab;
options.withContext(function () {
options.setPref("browser.tabs.loadInBackground", true);
browser.loadOneTab(url, null, null, postdata, where == liberator.NEW_BACKGROUND_TAB);
});
break;
case liberator.NEW_WINDOW:
window.open();
services.get("windowMediator").getMostRecentWindow("navigator:browser")
.loadURI(url, null, postdata);
let win = services.get("windowMediator").getMostRecentWindow("navigator:browser");
win.loadURI(url, null, postdata);
browser = win.getBrowser();
break;
default:
liberator.echoerr("Exxx: Invalid 'where' directive in liberator.open(...)");
return false;
throw Error("Invalid 'where' directive in liberator.open(...)");
}
}
@@ -1123,7 +1135,7 @@ const liberator = (function () //{{{
else if (!where)
where = liberator.CURRENT_TAB;
for (let [i, url] in Iterator(urls))
for (let [,url] in Iterator(urls))
{
open(url, where);
where = liberator.NEW_BACKGROUND_TAB;
@@ -1227,7 +1239,7 @@ const liberator = (function () //{{{
let infoPath = services.create("file");
infoPath.initWithPath(IO.expandPath(IO.runtimePath.replace(/,.*/, "")));
infoPath.append("info");
infoPath.append(services.get("profile").selectedProfile.name);
infoPath.append(liberator.profileName);
storage.infoPath = infoPath;
}
catch (e)
@@ -1235,8 +1247,6 @@ const liberator = (function () //{{{
liberator.reportError(e);
}
liberator.triggerObserver("load");
// commands must always be the first module to be initialized
loadModule("commands", Commands);
loadModule("options", Options);
@@ -1254,6 +1264,8 @@ const liberator = (function () //{{{
if (config.init)
config.init();
liberator.triggerObserver("load");
liberator.log("All modules loaded", 3);
// first time intro message
@@ -1404,6 +1416,21 @@ const liberator = (function () //{{{
window.liberator = liberator;
// FIXME: Ugly, etc.
window.addEventListener("liberatorHelpLink", function (event) {
let elem = event.target;
liberator.dump(String(elem));
if (/^(option|mapping|command)$/.test(elem.className))
var tag = elem.textContent.replace(/\s.*/, "");
if (elem.className == "command")
tag = tag.replace(/\[.*?\]/g, "");
if (tag)
var page = liberator.findHelp(tag);
if (page)
elem.href = "chrome://liberator/locale/" + page;
},
true, true);
// called when the chrome is fully loaded and before the main window is shown
window.addEventListener("load", liberator.startup, false);
window.addEventListener("unload", liberator.shutdown, false);

View File

@@ -85,6 +85,7 @@ the terms of any one of the MPL, the GPL or the LGPL.
<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);"/>
</hbox>

View File

@@ -283,7 +283,7 @@ function Mappings() //{{{
// FIXME:
Mappings.flags = {
ALLOW_EVENT_ROUTING: 1 << 0, // if set, return true inside the map command to pass the event further to firefox
ALLOW_EVENT_ROUTING: 1 << 0, // if set, return true inside the map command to pass the event further to Firefox
MOTION: 1 << 1,
COUNT: 1 << 2,
ARGUMENT: 1 << 3

View File

@@ -126,7 +126,8 @@ const modes = (function () //{{{
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
var self = {
const self = {
NONE: 0,
__iterator__: function () util.Array.iterator(this.all),

View File

@@ -125,7 +125,7 @@ Option.prototype = {
aValue = this.globalvalue;
if (this.getter)
this.getter.call(this, aValue);
return this.getter.call(this, aValue);
return aValue;
},
@@ -393,11 +393,196 @@ function Options() //{{{
}
}
function setAction(args, modifiers)
{
let bang = args.bang;
if (!args.length)
args[0] = "";
for (let [,arg] in args)
{
if (bang)
{
let onlyNonDefault = false;
let reset = false;
let invertBoolean = false;
if (args[0] == "")
{
var name = "all";
onlyNonDefault = true;
}
else
{
var [matches, name, postfix, valueGiven, operator, value] =
arg.match(/^\s*?([a-zA-Z0-9\.\-_{}]+)([?&!])?\s*(([-+^]?)=(.*))?\s*$/);
reset = (postfix == "&");
invertBoolean = (postfix == "!");
}
if (name == "all" && reset)
liberator.echoerr("You can't reset all options, it could make " + config.hostApplication + " unusable.");
else if (name == "all")
options.listPrefs(onlyNonDefault, "");
else if (reset)
options.resetPref(name);
else if (invertBoolean)
options.invertPref(name);
else if (valueGiven)
{
switch (value)
{
case undefined:
value = "";
break;
case "true":
value = true;
break;
case "false":
value = false;
break;
default:
if (/^\d+$/.test(value))
value = parseInt(value, 10);
}
options.setPref(name, value);
}
else
{
options.listPrefs(onlyNonDefault, name);
}
return;
}
let opt = options.parseOpt(arg, modifiers);
if (!opt)
{
liberator.echoerr("Error parsing :set command: " + arg);
return;
}
let option = opt.option;
if (option == null && !opt.all)
{
liberator.echoerr("No such option: " + opt.name);
return;
}
// reset a variable to its default value
if (opt.reset)
{
if (opt.all)
{
for (let option in options)
option.reset();
}
else
{
option.reset();
}
}
// read access
else if (opt.get)
{
if (opt.all)
{
options.list(opt.onlyNonDefault, opt.scope);
}
else
{
if (option.type == "boolean")
liberator.echo((opt.optionValue ? " " : "no") + option.name);
else
liberator.echo(" " + option.name + "=" + opt.optionValue);
}
}
// write access
// NOTE: the behavior is generally Vim compatible but could be
// improved. i.e. Vim's behavior is pretty sloppy to no real benefit
else
{
if (opt.option.type == "boolean")
{
if (opt.valueGiven)
{
liberator.echoerr("E474: Invalid argument: " + arg);
return;
}
opt.values = !opt.unsetBoolean;
}
let res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
if (res)
liberator.echoerr(res);
}
}
}
function setCompleter(context, args, modifiers)
{
let filter = context.filter;
if (args.bang) // list completions for about:config entries
{
if (filter[filter.length - 1] == "=")
{
context.advance(filter.length);
filter = filter.substr(0, filter.length - 1);
context.completions = [
[loadPreference(filter, null, false), "Current Value"],
[loadPreference(filter, null, true), "Default Value"]
].filter(function ([k]) k != null);
return;
}
return completion.preference(context);
}
let opt = options.parseOpt(filter, modifiers);
let prefix = opt.prefix;
if (context.filter.indexOf("=") == -1)
{
if (prefix)
context.filters.push(function ({ item: opt }) opt.type == "boolean" || prefix == "inv" && opt.values instanceof Array);
return completion.option(context, opt.scope);
}
else if (prefix == "no")
return;
if (prefix)
context.advance(prefix.length);
let option = opt.option;
context.advance(context.filter.indexOf("=") + 1);
if (!option)
{
context.message = "No such option: " + opt.name;
context.highlight(0, name.length, "SPELLCHECK");
}
if (opt.get || opt.reset || !option || prefix)
return;
if (!opt.value)
{
context.fork("default", 0, this, function (context) {
context.title = ["Extra Completions"];
context.completions = [
[option.value, "Current value"],
[option.defaultValue, "Default value"]
].filter(function (f) f[0] != "");
});
}
completion.optionValue(context, opt.name, opt.operator);
}
//
// firefox preferences which need to be changed to work well with vimperator
// Firefox preferences which need to be changed to work well with Vimperator
//
// work around firefox popup blocker
// work around Firefox popup blocker
// TODO: Make this work like safeSetPref
var popupAllowedEvents = loadPreference("dom.popup_allowed_events", "change click dblclick mouseup reset submit");
if (!/keypress/.test(popupAllowedEvents))
@@ -417,7 +602,7 @@ function Options() //{{{
// TODO: move to buffer.js
// we have our own typeahead find implementation
options.safeSetPref("accessibility.typeaheadfind.autostart", false);
options.safeSetPref("accessibility.typeaheadfind", false); // actually the above setting should do it, but has no effect in firefox
options.safeSetPref("accessibility.typeaheadfind", false); // actually the above setting should do it, but has no effect in Firefox
});
// start with saved session
@@ -521,14 +706,14 @@ function Options() //{{{
"Set local option",
function (args)
{
commands.get("set").execute(args.string, args.bang, args.count, { scope: options.OPTION_SCOPE_LOCAL });
return setAction(args, { scope: options.OPTION_SCOPE_LOCAL });
},
{
bang: true,
count: true,
completer: function (context, args)
{
return commands.get("set").completer(context.filter, args.bang, args.count, { scope: options.OPTION_SCOPE_LOCAL });
return setCompleter(context, args, { scope: options.OPTION_SCOPE_LOCAL });
},
literal: 0
}
@@ -538,14 +723,14 @@ function Options() //{{{
"Set global option",
function (args)
{
commands.get("set").execute(args.string, args.bang, args.count, { scope: options.OPTION_SCOPE_GLOBAL });
return setAction(args, { scope: options.OPTION_SCOPE_GLOBAL });
},
{
bang: true,
count: true,
completer: function (context, args)
{
return commands.get("set").completer(context.filter, args.bang, args.count, { scope: options.OPTION_SCOPE_GLOBAL });
return setCompleter(context, args, { scope: options.OPTION_SCOPE_GLOBAL });
},
literal: 0
}
@@ -553,190 +738,15 @@ function Options() //{{{
commands.add(["se[t]"],
"Set an option",
function (args, modifiers)
function (args)
{
let bang = args.bang;
if (!args.length)
args[0] = "";
for (let [,arg] in args)
{
if (bang)
{
let onlyNonDefault = false;
let reset = false;
let invertBoolean = false;
if (args[0] == "")
{
var name = "all";
onlyNonDefault = true;
}
else
{
var [matches, name, postfix, valueGiven, operator, value] =
arg.match(/^\s*?([a-zA-Z0-9\.\-_{}]+)([?&!])?\s*(([-+^]?)=(.*))?\s*$/);
reset = (postfix == "&");
invertBoolean = (postfix == "!");
}
if (name == "all" && reset)
liberator.echoerr("You can't reset all options, it could make " + config.hostApplication + " unusable.");
else if (name == "all")
options.listPrefs(onlyNonDefault, "");
else if (reset)
options.resetPref(name);
else if (invertBoolean)
options.invertPref(name);
else if (valueGiven)
{
switch (value)
{
case undefined:
value = "";
break;
case "true":
value = true;
break;
case "false":
value = false;
break;
default:
if (/^\d+$/.test(value))
value = parseInt(value, 10);
}
options.setPref(name, value);
}
else
{
options.listPrefs(onlyNonDefault, name);
}
return;
}
let opt = options.parseOpt(arg, modifiers);
if (!opt)
{
liberator.echoerr("Error parsing :set command: " + arg);
return;
}
let option = opt.option;
if (option == null && !opt.all)
{
liberator.echoerr("No such option: " + opt.name);
return;
}
// reset a variable to its default value
if (opt.reset)
{
if (opt.all)
{
for (let option in options)
option.reset();
}
else
{
option.reset();
}
}
// read access
else if (opt.get)
{
if (opt.all)
{
options.list(opt.onlyNonDefault, opt.scope);
}
else
{
if (option.type == "boolean")
liberator.echo((opt.optionValue ? " " : "no") + option.name);
else
liberator.echo(" " + option.name + "=" + opt.optionValue);
}
}
// write access
// NOTE: the behavior is generally Vim compatible but could be
// improved. i.e. Vim's behavior is pretty sloppy to no real benefit
else
{
if (opt.option.type == "boolean")
{
if (opt.valueGiven)
{
liberator.echoerr("E474: Invalid argument: " + arg);
return;
}
opt.values = !opt.unsetBoolean;
}
let res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
if (res)
liberator.echoerr(res);
}
}
return setAction(args);
},
{
bang: true,
completer: function (context, args, modifiers)
completer: function (context, args)
{
let filter = context.filter;
if (args.bang) // list completions for about:config entries
{
if (filter[filter.length - 1] == "=")
{
context.advance(filter.length);
filter = filter.substr(0, filter.length - 1);
context.completions = [
[loadPreference(filter, null, false), "Current Value"],
[loadPreference(filter, null, true), "Default Value"]
].filter(function ([k]) k != null);
return;
}
return completion.preference(context);
}
let opt = options.parseOpt(filter, modifiers);
let prefix = opt.prefix;
if (context.filter.indexOf("=") == -1)
{
if (prefix)
context.filters.push(function ({ item: opt }) opt.type == "boolean" || prefix == "inv" && opt.values instanceof Array);
return completion.option(context, opt.scope);
}
else if (prefix == "no")
return;
if (prefix)
context.advance(prefix.length);
let option = opt.option;
context.advance(context.filter.indexOf("=") + 1);
if (!option)
{
context.message = "No such option: " + opt.name;
context.highlight(0, name.length, "SPELLCHECK");
}
if (opt.get || opt.reset || !option || prefix)
return;
if (!opt.value)
{
context.fork("default", 0, this, function (context) {
context.title = ["Extra Completions"];
context.completions = [
[option.value, "Current value"],
[option.defaultValue, "Default value"]
].filter(function (f) f[0] != "");
});
}
completion.optionValue(context, opt.name, opt.operator);
return setCompleter(context, args);
},
serial: function () [
{
@@ -760,7 +770,6 @@ function Options() //{{{
//for (let i = 0, name = names[i]; i < length; name = names[++i])
for (let [,name] in args)
{
let name = args[i];
let reference = liberator.variableReference(name);
if (!reference[0])
{
@@ -857,7 +866,7 @@ function Options() //{{{
isDefault: opt.value == opt.defaultValue,
name: opt.name,
default: opt.defaultValue,
pre: "\u00a0\u00a0", /* Unicode nonbreaking space. */
pre: "\u00a0\u00a0", // Unicode nonbreaking space.
value: <></>
};
@@ -905,7 +914,7 @@ function Options() //{{{
default: loadPreference(pref, null, true),
value: <>={template.highlight(value, true, 100)}</>,
name: pref,
pre: "\u00a0\u00a0" /* Unicode nonbreaking space. */
pre: "\u00a0\u00a0" // Unicode nonbreaking space.
};
yield option;
@@ -982,14 +991,14 @@ function Options() //{{{
setPref: function (name, value)
{
return storePreference(name, value);
storePreference(name, value);
},
resetPref: function (name)
{
try
{
return services.get("pref").clearUserPref(name);
services.get("pref").clearUserPref(name);
}
catch (e)
{
@@ -1017,7 +1026,7 @@ function Options() //{{{
storePreference(k, v);
},
temporaryContext: function (fn, self)
withContext: function (fn, self)
{
try
{

View File

@@ -15,7 +15,7 @@ const Cu = Components.utils;
/**
* Cached XPCOM services and instances.
*
*
* @constructor
*/
function Services()
@@ -45,6 +45,7 @@ function Services()
get: function (name) services[name],
create: function (name) classes[name]()
};
self.add("appStartup", "@mozilla.org/toolkit/app-startup;1", Ci.nsIAppStartup);
self.add("autoCompleteSearch", "@mozilla.org/browser/global-history;2", Ci.nsIAutoCompleteSearch);
self.add("browserSearch", "@mozilla.org/browser/search-service;1", Ci.nsIBrowserSearchService);
@@ -55,7 +56,8 @@ function Services()
self.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
self.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance");
self.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService);
self.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefService, Ci.nsIPrefBranch, Ci.nsIPrefBranch2]);
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("sessionStore", "@mozilla.org/browser/sessionstore;1", Ci.nsISessionStore);
self.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader);
@@ -63,11 +65,13 @@ function Services()
self.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator);
self.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher);
self.addClass("file", "@mozilla.org/file/local;1", Ci.nsILocalFile);
self.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
self.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
self.addClass("file", "@mozilla.org/file/local;1", Ci.nsILocalFile);
self.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
self.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
return self;
};
var services = Services();
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -72,8 +72,8 @@ Highlights.prototype.CSS = <![CDATA[
Question color: green; background: white; font-weight: bold;
StatusLine color: white; background: black;
StatusLineBroken color: black; background: #FF6060; /* light-red */
StatusLineSecure color: black; background: #B0FF00; /* light-green */
StatusLineBroken color: black; background: #FF6060 /* light-red */
StatusLineSecure color: black; background: #B0FF00 /* light-green */
TabClose
TabIcon
@@ -129,7 +129,7 @@ Highlights.prototype.CSS = <![CDATA[
/**
* A class to manage highlighting rules. The parameters are the
* standard paramaters for any {@link Storage} object.
* standard parameters for any {@link Storage} object.
*
* @author Kris Maglione <maglione.k@gmail.com>
*/
@@ -178,17 +178,22 @@ function Highlights(name, store, serial)
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
.replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
css = style.selector + " { " + css + " }";
if (!/^\s*(?:!\s*important\s*)?;*\s*$/.test(css))
{
css = style.selector + " { " + css + " }";
let error = styles.addSheet(true, style.selector, style.filter, css);
if (error)
return error;
let error = styles.addSheet(true, style.selector, style.filter, css);
if (error)
return error;
}
style.value = newStyle;
highlight[style.class] = style;
};
/**
* Gets a CSS selector given a highlight group.
*
* @param {string} class
*/
this.selector = function (class)
{
@@ -233,30 +238,28 @@ function Highlights(name, store, serial)
}
/**
* Manages named and unnamed user stylesheets, which apply to both
* Manages named and unnamed user style sheets, which apply to both
* chrome and content pages. The parameters are the standard
* paramaters for any {@link Storage} object.
* parameters for any {@link Storage} object.
*
* @author Kris Maglione <maglione.k@gmail.com>
*/
function Styles(name, store, serial)
{
/* Can't reference liberator or Components inside Styles --
* they're members of the window object, which disappear
* with this window.
*/
// Can't reference liberator or Components inside Styles --
// they're members of the window object, which disappear
// with this window.
const util = modules.util;
const sleep = liberator.sleep;
const storage = modules.storage;
const consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
const ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
const ios = services.get("io");
const sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
const namespace = '@namespace html "' + XHTML + '";\n' +
'@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";\n' +
'@namespace liberator "' + NS.uri + '";\n';
const Sheet = new Struct("name", "sites", "css", "ref");
let cssUri = function (css) "chrome-data:text/css," + encodeURI(css);
let cssUri = function (css) "chrome-data:text/css," + window.encodeURI(css);
let userSheets = [];
let systemSheets = [];
@@ -270,13 +273,12 @@ function Styles(name, store, serial)
this.__defineGetter__("userNames", function () Iterator(userNames));
/**
* Add a new stylesheet.
* Add a new style sheet.
*
* @param {boolean} system Declares whether this is a system or
* user sheet. System sheets are used internally by
* @liberator.
>>>>>>> master:common/content/style.js
* @param {string} name The name given to the stylesheet by
* @param {string} name The name given to the style sheet by
* which it may be later referenced.
* @param {string} filter The sites to which this sheet will
* apply. Can be a domain name or a URL. Any URL ending in
@@ -335,6 +337,12 @@ function Styles(name, store, serial)
/**
* Find sheets matching the parameters. See {@link #addSheet}
* for parameters.
*
* @param {boolean} system
* @param {string} name
* @param {string} filter
* @param {string} css
* @param {number} index
*/
this.findSheets = function (system, name, filter, css, index)
{
@@ -355,10 +363,16 @@ function Styles(name, store, serial)
};
/**
* Remove a stylesheet. See {@link #addSheet} for parameters.
* Remove a style sheet. See {@link #addSheet} for parameters.
* In cases where <b>filter</b> is supplied, the given filters
* are removed from matching sheets. If any remain, the sheet is
* left in place.
*
* @param {boolean} system
* @param {string} name
* @param {string} filter
* @param {string} css
* @param {number} index
*/
this.removeSheet = function (system, name, filter, css, index)
{
@@ -403,9 +417,9 @@ function Styles(name, store, serial)
};
/**
* Register a user stylesheet at the given URI.
* Register a user style sheet at the given URI.
*
* @param {string} uri The UrI of the sheet to register.
* @param {string} uri The URI of the sheet to register.
* @param {boolean} reload Whether to reload any sheets that are
* already registered.
*/
@@ -420,6 +434,8 @@ function Styles(name, store, serial)
/**
* Unregister a sheet at the given URI.
*
* @param {string} uri The URI of the sheet to unregister.
*/
this.unregisterSheet = function (uri)
{
@@ -430,7 +446,9 @@ function Styles(name, store, serial)
// FIXME
/**
* Register an agent stylesheet.
* Register an agent style sheet.
*
* @param {string} uri The URI of the sheet to register.
* @deprecated
*/
this.registerAgentSheet = function (uri)
@@ -441,7 +459,9 @@ function Styles(name, store, serial)
};
/**
* Unregister an agent stylesheet.
* Unregister an agent style sheet.
*
* @param {string} uri The URI of the sheet to unregister.
* @deprecated
*/
this.unregisterAgentSheet = function (uri)
@@ -514,7 +534,6 @@ liberator.registerObserver("load_completion", function ()
liberator.registerObserver("load_commands", function ()
{
// TODO: :colo default needs :hi clear
commands.add(["colo[rscheme]"],
"Load a color scheme",
function (args)
@@ -598,7 +617,7 @@ liberator.registerObserver("load_commands", function ()
});
commands.add(["dels[tyle]"],
"Remove a user stylesheet",
"Remove a user style sheet",
function (args)
{
styles.removeSheet(false, args["-name"], args[0], args.literalArg, args["-index"]);

View File

@@ -44,12 +44,12 @@ function Tabs() //{{{
{
if (!tabmail)
{
tabmail = document.getElementById('tabmail');
tabmail.__defineGetter__('mTabContainer', function () this.tabContainer);
tabmail.__defineGetter__('mTabs', function () this.tabContainer.childNodes);
tabmail.__defineGetter__('mCurrentTab', function () this.tabContainer.selectedItem);
tabmail.__defineGetter__('mStrip', function () this.tabStrip);
tabmail.__defineGetter__('browsers', function () [browser for (browser in Iterator(this.mTabs))] );
tabmail = document.getElementById("tabmail");
tabmail.__defineGetter__("mTabContainer", function () this.tabContainer);
tabmail.__defineGetter__("mTabs", function () this.tabContainer.childNodes);
tabmail.__defineGetter__("mCurrentTab", function () this.tabContainer.selectedItem);
tabmail.__defineGetter__("mStrip", function () this.tabStrip);
tabmail.__defineGetter__("browsers", function () [browser for (browser in Iterator(this.mTabs))] );
}
return tabmail;
};
@@ -123,7 +123,7 @@ function Tabs() //{{{
let tabStrip = tabs.tabStrip;
if (!tabStrip)
return options['showtabline']; // XXX
return options["showtabline"]; // XXX
if (value == 0)
{
@@ -677,9 +677,9 @@ function Tabs() //{{{
return store.options;
},
getLocalStore: function (tab)
getLocalStore: function (tabIndex)
{
let tab = this.getTab(tab);
let tab = this.getTab(tabIndex);
if (!tab.liberatorStore)
tab.liberatorStore = {};
return tab.liberatorStore;
@@ -737,8 +737,8 @@ function Tabs() //{{{
{
if (index != undefined)
return getBrowser().mTabs[index];
return getBrowser().mCurrentTab;
else
return getBrowser().mCurrentTab;
},
get closedTabs()

View File

@@ -7,7 +7,7 @@ const template = {
map: function map(iter, fn, sep, interruptable)
{
if (iter.length) /* Kludge? */
if (iter.length) // FIXME: Kludge?
iter = util.Array.iterator(iter);
let ret = <></>;
let n = 0;
@@ -287,7 +287,7 @@ const template = {
tabular: function tabular(headings, style, iter)
{
/* This might be mind-bogglingly slow. We'll see. */
// TODO: This might be mind-bogglingly slow. We'll see.
// <e4x>
return this.commandOutput(
<table>

View File

@@ -28,7 +28,7 @@ the terms of any one of the MPL, the GPL or the LGPL.
/** @scope modules */
/*
/**
* This class is used for prompting of user input and echoing of messages
*
* it consists of a prompt and command field
@@ -75,6 +75,12 @@ function CommandLine() //{{{
var keepCommand = false;
var lastEcho = null;
/**
* A class for managing the history of an inputField
*
* @param {Object} inputField
* @param {string} mode
*/
function History(inputField, mode)
{
if (!(this instanceof arguments.callee))
@@ -85,27 +91,42 @@ function CommandLine() //{{{
this.reset();
}
History.prototype = {
/**
* Empties the history.
*/
reset: function ()
{
this.index = null;
},
/**
* Permanently save the history
*/
save: function ()
{
let str = this.input.value;
if (/^\s*$/.test(str))
return;
this.store.mutate('filter', function (line) line != str);
this.store.mutate("filter", function (line) line != str);
this.store.push(str);
this.store.truncate(options["history"], true);
},
/**
* Set the current match to val
*
* @param {string} val
*/
replace: function (val)
{
this.input.value = val;
liberator.triggerCallback("change", currentExtendedMode, val);
},
/**
* move up or (if backward) down in the history
*
* @param {boolean} backward
* @param {boolean} matchCurrent XXX: what?
*/
select: function (backward, matchCurrent)
{
// always reset the tab completion if we use up/down keys
@@ -154,6 +175,11 @@ function CommandLine() //{{{
}
};
/**
* A class for tab completions on an input field
*
* @param {Object} input
*/
function Completions(input)
{
if (!(this instanceof arguments.callee))
@@ -326,7 +352,7 @@ function CommandLine() //{{{
{
case this.UP:
if (this.selected == null)
idx = this.items.length - 1;
idx = -2
else
idx = this.selected - 1;
break;
@@ -343,8 +369,8 @@ function CommandLine() //{{{
idx = Math.max(0, Math.min(this.items.length - 1, idx));
break;
}
this.itemList.selectItem(idx);
if (idx < 0 || idx >= this.items.length || idx == null)
if (idx == -1 || this.items.length && idx >= this.items.length || idx == null)
{
// Wrapped. Start again.
this.selected = null;
@@ -352,28 +378,41 @@ function CommandLine() //{{{
}
else
{
// Wait for contexts to complete if necessary.
// FIXME: Need to make idx relative to individual contexts.
let list = this.context.contextList.reverse();
if (idx == -2)
list = list.slice().reverse();
let n = 0;
for (let [,context] in Iterator(list))
{
function done() !(idx >= n + context.items.length || idx == -2 && !context.items.length);
while (context.incomplete && !done())
liberator.threadYield(true, true);
if (done())
break;
n += context.items.length;
}
// See previous FIXME. This will break if new items in
// a previous context come in.
if (idx < 0)
idx = this.items.length - 1;
this.selected = idx;
this.completion = this.items[idx].text;
}
this.itemList.selectItem(idx);
},
tab: function tab(reverse)
{
autocompleteTimer.flush();
// Check if we need to run the completer.
if (this.context.waitingForTab || this.wildIndex == -1)
this.complete(true, true);
if (this.items.length == 0)
{
// No items. Wait for any unfinished completers.
let end = Date.now() + 5000;
while (this.context.incomplete && this.items.length == 0 && Date.now() < end)
liberator.threadYield();
if (this.items.length == 0)
return liberator.beep();
}
switch (this.wildtype.replace(/.*:/, ""))
{
case "":
@@ -392,6 +431,9 @@ function CommandLine() //{{{
break;
}
if (this.items.length == 0)
return void liberator.beep();
if (this.type.list)
completionList.show();
@@ -511,16 +553,28 @@ function CommandLine() //{{{
var multilineRegexp = null;
var multilineCallback = null;
/**
* @private - highlight the messageBox according to group
*/
function setHighlightGroup(group)
{
messageBox.setAttributeNS(NS.uri, "highlight", group);
}
// Whether the command line must be open.
/**
* @private - Determines whether the command line should be visible.
*
* @return {boolean}
*/
function commandShown() modes.main == modes.COMMAND_LINE &&
!(modes.extended & (modes.INPUT_MULTILINE | modes.OUTPUT_MULTILINE));
// sets the prompt - for example, : or /
/**
* @private - set the prompt to val styled with highlightGroup
*
* @param {string} val
* @param {string} highlightGroup
*/
function setPrompt(val, highlightGroup)
{
promptWidget.value = val;
@@ -529,7 +583,11 @@ function CommandLine() //{{{
promptWidget.setAttributeNS(NS.uri, "highlight", highlightGroup || commandline.HL_NORMAL);
}
// sets the command - e.g. 'tabopen', 'open http://example.com/'
/**
* @private - set the command to cmd and move the user's cursor to the end.
*
* @param {string} cmd
*/
function setCommand(cmd)
{
commandWidget.value = cmd;
@@ -537,6 +595,14 @@ function CommandLine() //{{{
commandWidget.selectionEnd = cmd.length;
}
/**
* @private - display a message styled with highlightGroup
* and, if forceSingle is true, ensure it takes only one line.
*
* @param {string} str
* @param {string} highlightGroup
* @param {boolean} forceSingle
*/
function echoLine(str, highlightGroup, forceSingle)
{
setHighlightGroup(highlightGroup);
@@ -552,7 +618,14 @@ function CommandLine() //{{{
echoMultiline(<span highlight="Message">{str}</span>, highlightGroup);
}
// TODO: resize upon a window resize
/**
* Display a multiline message, possible through a "more" like interface
*
* TODO: resize upon a window resize
*
* @param {string} str
* @param {string} highlightGroup
*/
function echoMultiline(str, highlightGroup)
{
let doc = multilineOutputWidget.contentDocument;
@@ -560,11 +633,10 @@ function CommandLine() //{{{
liberator.triggerObserver("echoMultiline", str, highlightGroup);
/* If it's already XML, assume it knows what it's doing.
* Otherwise, white space is significant.
* The problem elsewhere is that E4X tends to insert new lines
* after interpolated data.
*/
// If it's already XML, assume it knows what it's doing.
// Otherwise, white space is significant.
// The problem elsewhere is that E4X tends to insert new lines
// after interpolated data.
XML.ignoreWhitespace = typeof str != "xml";
lastMowOutput = <div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}>{template.maybeXML(str)}</div>;
let output = util.xmlToDom(lastMowOutput, doc);
@@ -598,6 +670,10 @@ function CommandLine() //{{{
commandline.updateMorePrompt();
}
/**
* @private - ensure that the Multiline input widget is the
* correct size.
*/
function autosizeMultilineInputWidget()
{
let lines = multilineInputWidget.value.split("\n").length - 1;
@@ -605,7 +681,16 @@ function CommandLine() //{{{
multilineInputWidget.setAttribute("rows", Math.max(lines, 1));
}
// used for the :echo[err] commands
/**
* @private - eval()s a javascript expression
* and returns a string suitable to be echo'd.
*
* If useColor is true, util.objectToString will
* colorize object output.
*
* @param {string} arg
* @param {boolean} useColor
*/
function echoArgumentToString(arg, useColor)
{
if (!arg)
@@ -885,6 +970,9 @@ function CommandLine() //{{{
storage.styles.removeSheet(true, "silent-mode");
},
/**
* XXX: This function is not used!
*/
runSilently: function (fn, self)
{
let wasSilent = this.silent;
@@ -912,6 +1000,16 @@ function CommandLine() //{{{
get message() messageBox.value,
/**
* Changes the command line to display the following prompt (usually ":")
* followed by the command, in the given mode. Valid modes are
* attributes of the "modes" variable, and modes.EX is probably
* a good choice.
*
* @param {string} prompt
* @param {string} cmd
* @param {number} mode
*/
open: function open(prompt, cmd, extendedMode)
{
// save the current prompts, we need it later if the command widget
@@ -936,7 +1034,11 @@ function CommandLine() //{{{
liberator.triggerCallback("change", currentExtendedMode, cmd);
},
// normally used when pressing esc, does not execute a command
/**
* Removes any input from the command line, without executing its
* contents. Removes any "More" windows or other such output.
* Pressing <ESC> in EX mode normally has this effect.
*/
close: function close()
{
let mode = currentExtendedMode;
@@ -946,6 +1048,7 @@ function CommandLine() //{{{
if (history)
history.save();
this.resetCompletions(); // cancels any asynchronous completion still going on, must be before completions = null
completions = null;
history = null;
@@ -969,14 +1072,33 @@ function CommandLine() //{{{
keepCommand = false;
},
/**
* Hide any auto-completion/More-ing that is happening.
*/
hide: function hide()
{
commandlineWidget.collapsed = true;
},
// liberator.echo uses different order of flags as it omits the hightlight group, change v.commandline.echo argument order? --mst
/**
* Output the given string onto the command line coloured
* using the rules according to highlightGroup. If not
* given higlightGroup defaults to commandline.HL_NORMAL
* and other possibe values are at commandline.HL_[A-Z]*.
*
* Flags can be any of:
* commandline.APPEND_TO_MESSAGES (causes message to be added to the messagesHistory)
* commandline.FORCE_SINGLELINE | commandline.DISALLOW_MULTILINE
* commandline.FORCE_MULTILINE
*
* @param {string} str
* @param {string} highlightGroup
* @param {number} flags
*/
echo: function echo(str, highlightGroup, flags)
{
// liberator.echo uses different order of flags as it omits the highlight group, change v.commandline.echo argument order? --mst
if (silent)
return false;
@@ -1021,8 +1143,23 @@ function CommandLine() //{{{
return true;
},
// this will prompt the user for a string
// commandline.input("(s)ave or (o)pen the file?")
/**
* Prompt the user for a string and execute the given
* callback with that as the only argument on <CR>
* extra can have any of the following attributes:
*
* onChange: A function to be called with the current input every time it changes
* completer: A function called with a ?context? when the user tries to tabcomplete
* promptHighlight: The HighlightGroup to use (default commandline.HL_QUESTION, others
* can be found at commandline.HL_[A-Z]*)
*
* This function sets the mode to modes.COMMAND_LINE, and thus popping the mode will
* stop further input from being waited for (useful for stopping onChange)
*
* @param {string} prompt
* @param {function(string)} callback
* @param {Object} extra
*/
input: function input(prompt, callback, extra)
{
extra = extra || {};
@@ -1042,8 +1179,14 @@ function CommandLine() //{{{
completions = Completions(commandWidget.inputField);
},
// reads a multi line input and returns the string once the last line matches
// @param untilRegexp
/**
* Get a multiline input from a user, up to but not including
* the line which matches the given regular expression. Then
* execute the callback with that string as a parameter.
*
* @param {RegExp} untilRegexp
* @param {function(string)} callbackFunc
*/
inputMultiline: function inputMultiline(untilRegexp, callbackFunc)
{
// Kludge.
@@ -1063,6 +1206,13 @@ function CommandLine() //{{{
setTimeout(function () { multilineInputWidget.focus(); }, 10);
},
/**
* Handle events, the come from liberator when liberator.mode = modes.COMMAND_LINE
* but also takes blur/focus/input events raw from #liberator-commandline-command
* in the XUL
*
* @param {Event} event
*/
onEvent: function onEvent(event)
{
let command = this.command;
@@ -1085,7 +1235,7 @@ function CommandLine() //{{{
}
else if (event.type == "input")
{
this.resetCompletions();
//this.resetCompletions(); -> already handled by "keypress" below (hopefully), so don't do it twice
liberator.triggerCallback("change", currentExtendedMode, command);
}
else if (event.type == "keypress")
@@ -1150,11 +1300,18 @@ function CommandLine() //{{{
}
else if (event.type == "keyup")
{
let key = events.toString(event);
if (key == "<Tab>" || key == "<S-Tab>")
tabTimer.flush();
}
},
/**
* Multiline input events, they will come straight from
* #liberator-multiline-input in the XUL.
*
* @param {Event} event
*/
onMultilineInputEvent: function onMultilineInputEvent(event)
{
if (event.type == "keypress")
@@ -1189,8 +1346,16 @@ function CommandLine() //{{{
return true;
},
// FIXME: if 'more' is set and the MOW is not scrollable we should still
// allow a down motion after an up rather than closing
/**
* Handle events when we are in multiline output mode,
* these come from liberator when modes.extended & modes.MULTILINE_OUTPUT
* and also from #liberator-multiline-output in the XUL
*
* FIXME: if 'more' is set and the MOW is not scrollable we should still
* allow a down motion after an up rather than closing
*
* @param {Event} event
*/
onMultilineOutputEvent: function onMultilineOutputEvent(event)
{
let win = multilineOutputWidget.contentWindow;
@@ -1298,7 +1463,7 @@ function CommandLine() //{{{
}
break;
// let firefox handle those to select table cells or show a context menu
// let Firefox handle those to select table cells or show a context menu
case "<C-LeftMouse>":
case "<RightMouse>":
case "<C-S-LeftMouse>":
@@ -1400,6 +1565,15 @@ function CommandLine() //{{{
}
},
/**
* Refresh or remove the prompt that displays when in multiline mode.
* showHelp will cause the possible key-options to be displayed,
* force will cause a display of the default message even if it
* could be at the end of the output.
*
* @param {boolean} force
* @param {boolean} showHelp
*/
updateMorePrompt: function updateMorePrompt(force, showHelp)
{
if (outputContainer.collapsed)
@@ -1417,6 +1591,14 @@ function CommandLine() //{{{
echoLine("Press ENTER or type command to continue", this.HL_QUESTION, true);
},
/**
* Changes the height of the multilineOutputWidget to fit
* its contents, if <b>open</b> is true, it will cause the
* widget to uncollapse, if not it will leave the widget
* closed.
*
* @param {boolean} open
*/
updateOutputHeight: function updateOutputHeight(open)
{
if (!open && outputContainer.collapsed)
@@ -1439,11 +1621,18 @@ function CommandLine() //{{{
outputContainer.collapsed = false;
},
/**
* Disable any active completion functions by calling their cancelFunc's
* Will also remove the completions preview window.
*/
resetCompletions: function resetCompletions()
{
autocompleteTimer.reset();
// liberator.dump("Resetting completions...");
if (completions)
{
completions.context.cancelAll();
completions.wildIndex = -1;
completions.previewClear();
}
@@ -1457,9 +1646,9 @@ function CommandLine() //{{{
/**
* The list which is used for the completion box (and QuickFix window in future)
*
* @param id: the id of the the XUL <iframe> which we want to fill
* it MUST be inside a <vbox> (or any other html element,
* because otherwise setting the height does not work properly
* @param {string} id The id of the XUL <iframe> which we want to fill it
* MUST be inside a <vbox> (or any other html element, because otherwise
* setting the height does not work properly
*/
function ItemList(id) //{{{
{
@@ -1505,7 +1694,7 @@ function ItemList(id) //{{{
minHeight = Math.max(minHeight, divNodes.completions.getBoundingClientRect().bottom);
container.height = minHeight;
if (container.collapsed)
div.style.minWidth = undefined;
div.style.minWidth = "";
// FIXME: Belongs elsewhere.
commandline.updateOutputHeight(false);
}
@@ -1550,10 +1739,10 @@ function ItemList(id) //{{{
}
/**
* uses the entries in "items" to fill the listbox
* does incremental filling to speed up things
* Uses the entries in "items" to fill the listbox and
* does incremental filling to speed up things.
*
* @param offset: start at this index and show maxItems
* @param {number} offset Start at this index and show maxItems
*/
function fill(offset)
{
@@ -1671,7 +1860,7 @@ function ItemList(id) //{{{
// select index, refill list if necessary
selectItem: function selectItem(index)
{
//if (container.collapsed) // fixme
//if (container.collapsed) // FIXME
// return;
//let now = Date.now();
@@ -1773,6 +1962,14 @@ function StatusLine() //{{{
return {
/**
* Update the status bar to indicate how secure the website is
* secure => https:// with valid certificate
* broken => https:// with invalid certificate
* insecure => http://
*
* @param {'secure'|'broken'|'insecure'} type
*/
setClass: function setClass(type)
{
const highlightGroup = {
@@ -1794,7 +1991,14 @@ function StatusLine() //{{{
this.updateBufferPosition();
},
// if "url" is ommited, build a usable string for the URL
/**
* Update which URL is displayed on the status line,
* if url is omitted then buffer.URL is used instead and
* status icons [+-❤] are updated to match whether one can
* go back/forwards in history/have bookmarked the page.
*
* @param {string} url
*/
updateUrl: function updateUrl(url)
{
if (typeof url == "string")
@@ -1805,7 +2009,7 @@ function StatusLine() //{{{
url = buffer.URL;
// make it even more vim-like
// make it even more Vim-like
if (url == "about:blank")
{
if (!buffer.title)
@@ -1837,6 +2041,13 @@ function StatusLine() //{{{
urlWidget.value = url;
},
/**
* Set the contents of the status line's input buffer
* to the given string.
*
* Used for displaying partial key combinations in
* normal mode.
*/
updateInputBuffer: function updateInputBuffer(buffer)
{
if (!buffer || typeof buffer != "string")
@@ -1845,6 +2056,17 @@ function StatusLine() //{{{
inputBufferWidget.value = buffer;
},
/**
* Update the display of the progress bar.
* If the parameter is a string, it will be
* displayed literally. Otherwise it must be a number
* less than one.
* Negative numbers cause a "Loading..." status,
* Positive (< 1) numbers cause an arrow ==> of length
* proportional to the arrow.
*
* @param {string|number} progress
*/
updateProgress: function updateProgress(progress)
{
if (!progress)
@@ -1872,7 +2094,13 @@ function StatusLine() //{{{
}
},
// you can omit either of the 2 arguments
/**
* Display the correct tabcount (e.g. [1/5]) on the status bar.
* If either parameter is omitted, they will be calculated.
*
* @param {number} currentIndex
* @param {number} totalTabs
*/
updateTabCount: function updateTabCount(currentIndex, totalTabs)
{
if (!liberator.has("tabs"))
@@ -1897,7 +2125,16 @@ function StatusLine() //{{{
tabCountWidget.value = "[" + currentIndex + "/" + totalTabs + "]";
},
// percent is given between 0 and 1
/**
* Display the correct position on the status bar, if the
* percent parameter is omitted it will be calculated.
*
* Negative numbers are set to "All", Zero implies "Top"
* One or above is "Bot", and anything else is multiplied by
* a hundred and displayed as a percentage.
*
* @param {number} percent
*/
updateBufferPosition: function updateBufferPosition(percent)
{
if (!percent || typeof percent != "number")

View File

@@ -57,15 +57,19 @@ const util = { //{{{
},
/**
* Flatten an array, such that all elements of the array are
* Flattens an array, such that all elements of the array are
* joined into a single array:
* [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
*
* @param {Array} ary
* @returns {Array}
*/
flatten: function flatten(ary) Array.concat.apply([], ary),
/**
* Returns an Iterator for an array's values.
*
* @param {Array} ary
* @returns {Iterator(Object)}
*/
iterator: function iterator(ary)
@@ -76,8 +80,9 @@ const util = { //{{{
},
/**
* Returns an Iterator for the arrays indices and values.
* Returns an Iterator for an array's indices and values.
*
* @param {Array} ary
* @returns {Iterator([{number}, {Object}])}
*/
iterator2: function (ary)
@@ -94,6 +99,7 @@ const util = { //{{{
*
* @param {Array} ary
* @param {boolean} unsorted
* @returns {Array}
*/
uniq: function uniq(ary, unsorted)
{
@@ -135,10 +141,10 @@ const util = { //{{{
/**
* Clips a string to a given length. If the input string is longer
* than <b>length</b>, an elipsis is appended.
* than <b>length</b>, an ellipsis is appended.
*
* @param {string} str
* @param {number} length
* @param {string} str The string to truncate.
* @param {number} length The length of the returned string.
* @returns {string}
*/
clip: function clip(str, length)
@@ -169,6 +175,13 @@ const util = { //{{{
return node.ownerDocument.defaultView.getComputedStyle(node, null);
},
/**
* Copies a string to the system clipboard. If <b>verbose</b> is specified
* the copied string is also echoed to the command-line.
*
* @param {string} str
* @param {boolean} verbose
*/
copyToClipboard: function copyToClipboard(str, verbose)
{
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
@@ -178,12 +191,26 @@ const util = { //{{{
liberator.echo("Yanked " + str, commandline.FORCE_SINGLELINE);
},
/**
* Converts any arbitrary string into an URI object.
*
* @param {string} str
* @returns {Object}
*/
// FIXME: newURI needed too?
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);
},
/**
* Converts HTML special characters in <b>str</b> to the equivalent HTML
* entities.
*
* @param {string} str
* @returns {string}
*/
escapeHTML: function escapeHTML(str)
{
// XXX: the following code is _much_ slower than a simple .replace()
@@ -195,11 +222,26 @@ const util = { //{{{
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
},
/**
* Escapes Regular Expression special characters in <b>str</b>.
*
* @param {string} str
* @returns {string}
*/
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.
*
* @param {string} str
* @param {string} delimiter
* @returns {string}
*/
escapeString: function escapeString(str, delimiter)
{
if (delimiter == undefined)
@@ -207,11 +249,20 @@ const util = { //{{{
return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter;
},
formatBytes: function formatBytes(num, decimalPlaces, humanReadable)
/**
* Converts <b>bytes</b> to a pretty printed data size string.
*
* @param {number} bytes The number of bytes.
* @param {string} decimalPlaces The number of decimal places to use if
* <b>humanReadable</b> is true.
* @param {boolean} humanReadable Use byte multiples.
* @returns {string}
*/
formatBytes: function formatBytes(bytes, decimalPlaces, humanReadable)
{
const unitVal = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
let unitIndex = 0;
let tmpNum = parseInt(num, 10) || 0;
let tmpNum = parseInt(bytes, 10) || 0;
let strNum = [tmpNum + ""];
if (humanReadable)
@@ -242,33 +293,40 @@ const util = { //{{{
return strNum[0] + " " + unitVal[unitIndex];
},
// generates an Asciidoc help entry, "command" can also be a mapping
generateHelp: function generateHelp(command, extraHelp)
/**
* Generates an Asciidoc help entry.
*
* @param {Object} obj A liberator <b>Command</b>, <b>Mapping</b> or
* <b>Option</b> object
* @param {string} extraHelp Extra help text beyond the description.
* @returns {string}
*/
generateHelp: function generateHelp(obj, extraHelp)
{
let start = "", end = "";
if (command instanceof liberator.Command)
if (obj instanceof Command)
start = ":";
else if (command instanceof liberator.Option)
else if (obj instanceof Option)
start = end = "'";
let ret = "";
let longHelp = false;
if ((command.help && command.description) && (command.help.length + command.description.length) > 50)
if ((obj.help && obj.description) && (obj.help.length + obj.description.length) > 50)
longHelp = true;
// the tags which are printed on the top right
for (let j = command.names.length - 1; j >= 0; j--)
ret += "|" + start + command.names[j] + end + "| ";
for (let j = obj.names.length - 1; j >= 0; j--)
ret += "|" + start + obj.names[j] + end + "| ";
if (longHelp)
ret += "+";
ret += "\n";
// the usage information for the command
let usage = command.names[0];
if (command.specs) // for :commands
usage = command.specs[0];
// the usage information
let usage = obj.names[0];
if (obj.specs) // for :commands
usage = obj.specs[0];
usage = usage.replace(/{/, "\\\\{").replace(/}/, "\\\\}");
usage = usage.replace(/'/, "\\'").replace(/`/, "\\`");
@@ -279,9 +337,9 @@ const util = { //{{{
ret += "\n________________________________________________________________________________\n";
// the actual help text
if (command.description)
if (obj.description)
{
ret += command.description + "."; // the help description
ret += obj.description + "."; // the help description
if (extraHelp)
ret += " +\n" + extraHelp;
}
@@ -294,6 +352,16 @@ const util = { //{{{
return ret;
},
/**
* Sends a synchronous HTTP request to <b>url</b> and returns the
* XMLHttpRequest object. If <b>callback</b> is specified the request is
* asynchronous and the <b>callback</b> is invoked with the object as its
* argument.
*
* @param {string} url
* @param {function} callback
* @returns {Object}
*/
httpGet: function httpGet(url, callback)
{
try
@@ -317,8 +385,21 @@ const util = { //{{{
}
},
/**
* The identity function.
*
* @param {Object} k
* @returns {Object}
*/
identity: function identity(k) k,
/**
* Returns the intersection of two rectangles.
*
* @param {Object} r1
* @param {Object} r2
* @returns {Object}
*/
intersection: function (r1, r2) ({
get width() this.right - this.left,
get height() this.bottom - this.top,
@@ -328,6 +409,14 @@ const util = { //{{{
bottom: Math.min(r1.bottom, r2.bottom)
}),
/**
* Returns the array that results from applying <b>fn</b> to each property
* of <b>obj</b>.
*
* @param {Object} obj
* @param {function} fn
* @returns {Array}
*/
map: function map(obj, fn)
{
let ary = [];
@@ -336,20 +425,32 @@ const util = { //{{{
return ary;
},
newURI: function (url)
/**
* Converts a URI string into an URI object.
*
* @param {string} uri
* @returns {Object}
*/
// FIXME: createURI needed too?
newURI: function (uri)
{
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
return ioService.newURI(url, null, null);
return services.get("io").newURI(uri, null, null);
},
// if color = true it uses HTML markup to color certain items
/**
* Pretty print a JavaScript object. Use HTML markup to color certain items
* if <b>color</b> is true.
*
* @param {Object} object The object to pretty print.
* @param {boolean} color Whether the output should be colored.
* @returns {string}
*/
objectToString: function objectToString(object, color)
{
/* Use E4X literals so html is automatically quoted
* only when it's asked for. Noone wants to see &lt;
* on their console or :map :foo in their buffer
* when they expect :map <C-f> :foo.
*/
// Use E4X literals so html is automatically quoted
// only when it's asked for. Noone wants to see &lt;
// on their console or :map :foo in their buffer
// when they expect :map <C-f> :foo.
XML.prettyPrinting = false;
XML.ignoreWhitespace = false;
@@ -417,6 +518,15 @@ const util = { //{{{
return color ? string : [s for each (s in string)].join("");
},
/**
* A generator that returns the values between <b>start</b> and <b>end</b>.
* If <b>reverse</b> is true then the values are returned in reverse order.
*
* @param {number} start The interval's start value.
* @param {number} end The interval's end value.
* @param {boolean} reverse Reverse the order in which the values are produced.
* @returns {Iterator(Object)}
*/
range: function range(start, end, reverse)
{
if (!reverse)
@@ -431,7 +541,16 @@ const util = { //{{{
}
},
interruptableRange: function interruptableRange(start, end, time)
/**
* An interruptible generator that returns all values between <b>start</b>
* and <b>end</b>. The thread yields every <b>time</b> milliseconds.
*
* @param {number} start The interval's start value.
* @param {number} end The interval's end value.
* @param {number} time The time in milliseconds between thread yields.
* @returns {Iterator(Object)}
*/
interruptibleRange: function interruptibleRange(start, end, time)
{
let endTime = Date.now() + time;
while (start < end)
@@ -445,15 +564,22 @@ const util = { //{{{
}
},
// same as Firefox's readFromClipboard function, but needed for apps like Thunderbird
/**
* Reads a string from the system clipboard.
*
* This is same as Firefox's readFromClipboard function, but is needed for
* apps like Thunderbird which do not provide it.
*
* @returns {string}
*/
readFromClipboard: function readFromClipboard()
{
let url;
try
{
const clipboard = Cc['@mozilla.org/widget/clipboard;1'].getService(Ci.nsIClipboard);
const transferable = Cc['@mozilla.org/widget/transferable;1'].createInstance(Ci.nsITransferable);
const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
transferable.addDataFlavor("text/unicode");
@@ -478,11 +604,18 @@ const util = { //{{{
return url;
},
// takes a string like 'google bla, www.osnews.com'
// and returns an array ['www.google.com/search?q=bla', 'www.osnews.com']
/**
* Returns an array of URLs parsed from <b>str</b>.
*
* Given a string like 'google bla, www.osnews.com' return an array
* ['www.google.com/search?q=bla', 'www.osnews.com']
*
* @param {string} str
* @returns {Array}
*/
stringToURLArray: function stringToURLArray(str)
{
let urls = str.split(RegExp("\s*" + options["urlseparator"] + "\s*"));
let urls = str.split(RegExp("\\s*" + options["urlseparator"] + "\\s*"));
return urls.map(function (url) {
try
@@ -525,6 +658,14 @@ const util = { //{{{
});
},
/**
* Converts an E4X XML literal to a DOM node.
*
* @param {Node} node
* @param {Document} doc
* @param {Object} nodes
* @returns {Node}
*/
xmlToDom: function xmlToDom(node, doc, nodes)
{
XML.prettyPrinting = false;

View File

@@ -34,6 +34,7 @@ const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
// XXX: does not belong here
function Timer(minInterval, maxInterval, callback)
{
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -57,7 +58,7 @@ function Timer(minInterval, maxInterval, callback)
};
this.tell = function (arg)
{
if (arg !== undefined)
if (arguments.length > 0)
this.arg = arg;
let now = Date.now();