diff --git a/.gitignore b/.gitignore
index 9d29ba75..e72f960a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,7 @@
*/locale/*/*.html
*/chrome
*~
+.*.swp
+.*.swo
+.swp
+.DS_Store
diff --git a/HACKING b/HACKING
new file mode 100644
index 00000000..85cc55c0
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,148 @@
+= Hacking =
+
+If you've taken to hacking Vimperator source code, we hope that you'll share
+your changes. In case you do, please keep the following in mind, and we'll be
+happy to accept your patches.
+
+== Documentation ==
+
+First of all, all new features and all user-visible changes to existing
+features need to be documented. That means editing the appropriate help files
+and adding a NEWS entry where appropriate. When editing the NEWS file, you
+should add your change to the top of the list of changes. If your change
+alters an interface (key binding, command) and is likely to cause trouble,
+prefix it with 'IMPORTANT:', otherwise, place it below the other 'IMPORTANT'
+entries. If you're not sure if your change merits a news entry, or if it's
+important, please ask.
+
+== Coding Style ==
+
+In general: Just look at the existing source code!
+We try to be quite consistent, but of course, that's not always possible.
+
+=== The most important style issues are: ===
+
+* Use 4 spaces to indent things, no tabs, not 2, nor 8 spaces. If you use Vim,
+ this should be taken care of automatically by the modeline (like the
+ one below).
+
+* No trailing whitespace.
+
+* Use " for enclosing strings instead of ', unless using ' avoids escaping of lots of "
+ Example: alert("foo") instead of alert('foo');
+
+* Exactly one space after if/for/while/catch etc. and after a comma, but none
+ after a parenthesis or after a function call:
+ for (pre; condition; post)
+ but:
+ alert("foo");
+
+* Opening curly brackets { must be on a new line, unless it is used in a closure:
+ function myFunction ()
+ {
+ if (foo)
+ {
+ baz = false;
+ return bar;
+ }
+ else
+ {
+ return baz;
+ }
+ }
+ but:
+ setTimeout(function () {
+ ...
+ });
+
+* No braces for one-line conditional statements:
+ Right:
+ if (foo)
+ frob();
+ else
+ unfrob();
+
+* Prefer lambda-style functions where suitable:
+ Right: list.filter(function (elem) elem.good != elem.BAD);
+ Wrong: list.filter(function (elem) { return elem.good != elem.BAD });
+
+* Anonymous function definitions should be formatted with a space after the
+ keyword "function". Example: function () {}, not function() {}.
+
+* Prefer the use of let over var i.e. only use var when required.
+ For more details, see
+ https://developer.mozilla.org/en/New_in_JavaScript_1.7#Block_scope_with_let
+
+* Reuse common local variable names E.g. "elem" is generally used for element,
+ "win" for windows etc.
+
+* Prefer // over /* */ comments (exceptions for big comments are usually OK)
+ Right: if (HACK) // TODO: remove hack
+ Wrong: if (HACK) /* TODO: remove hack */
+ Documentation comment blocks use /** ... */
+
+* Only wrap lines if it makes the code obviously clearer. Lines longer than 132
+ characters should probably be broken up rather than wrapped anyway.
+
+* Use UNIX new lines (\n), not windows (\r\n) or old Mac ones (\r)
+
+* Use Iterators, Array#forEach, or for (let i = 0; i < ary.length; i++)
+ to iterate over arrays. for (let i in ary) and for each (let i in ary)
+ include members in an Array.prototype, which some extensions alter.
+ Right:
+ for (let [,elem] in Iterator(ary))
+ for (let [k, v] in Iterator(obj))
+ ary.forEach(function (elem) { ...
+ Wrong:
+ for each (let elem in ary)
+
+ The exceptions to this rule are for objects with __iterator__ set,
+ and for XML objects (see README.E4X).
+
+* Avoid using 'new' with constructors where possible, and use [] and
+ {} rather than new Array/new Object.
+ Right:
+ RegExp("^" + foo + "$")
+ Function(code)
+ new Date
+ Wrong:
+ new RegExp("^" + foo + "$")
+ new Function(code)
+ Date() // Right if you want a string-representation of the date
+
+ NOTE by mst: That one is debateable, actually I like the "new" as one
+ immediately sees that a new object of a class is created which is not
+ so obvious by var x = CompletionContext(); which could also mean that
+ CompletionContext() could return a cached object. What's thesnowdog's
+ opinion and why do you, Kris, think it's better/cleaner?
+
+ I don't think it's better/cleaner, it just seemed to be a consensus.
+ --Kris
+
+ I don't like unnecessary use of 'new', I don't like 'new'. --djk
+
+== Testing/Optimization ==
+
+TODO: Add some information here about testing/validation/etc.
+Information about how/when to use :regressions might be nice.
+Additionally, maybe there should be some benchmark information here --
+something to let a developer know what's "too" slow...? Or general
+guidelines about optimization?
+
+== Source Code Management ==
+
+TODO: Document the existence of remote branches and discuss when and how
+ to push to them. At least provide an index so that devs know where
+ to look if an old branch needs to be maintained or a feature needs
+ to be added to a new branch. Keep in mind that git is not the most
+ intuitive SCM.
+
+ I don't agree. git is about as intuitive as any other SCM, but,
+ regardless, it's by far one of the most popular. There are
+ countless git walkthroughs, FAQs, tips pages (not to mention 'git
+ help') that I don't see the need to duplicate them here. As for
+ branches, 'git branch' should be sufficient, and, if not, there's
+ a list on gitweb.
+ --Kris
+
+# vim: set fdm=marker sw=4 ts=4 et ai:
diff --git a/common/content/buffer.js b/common/content/buffer.js
index d17a0620..76bfc36d 100644
--- a/common/content/buffer.js
+++ b/common/content/buffer.js
@@ -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 count * '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 percentage
+ * Scrolls the current buffer vertically to percentage.
+ *
+ * @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:
{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 url. 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)
diff --git a/common/content/commands.js b/common/content/commands.js
index 6e74f680..e8a0a50f 100644
--- a/common/content/commands.js
+++ b/common/content/commands.js
@@ -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 name.
*
* @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 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 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;
}; //}}}
diff --git a/common/content/completion.js b/common/content/completion.js
index 95c8e0c5..c12b2bff 100644
--- a/common/content/completion.js
+++ b/common/content/completion.js
@@ -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 defVal 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 . 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.
@@ -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);
diff --git a/common/content/editor.js b/common/content/editor.js
index 5c9ffedb..237d0bcc 100644
--- a/common/content/editor.js
+++ b/common/content/editor.js
@@ -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", ""], true, "lineMove", false, "cmd_linePrevious", selectPreviousLine);
addMovementMap(["j", "", ""], true, "lineMove", true, "cmd_lineNext", selectNextLine);
addMovementMap(["h", "", ""], 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
diff --git a/common/content/eval.js b/common/content/eval.js
index daf0dba6..ff7310e2 100644
--- a/common/content/eval.js
+++ b/common/content/eval.js
@@ -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:
diff --git a/common/content/events.js b/common/content/events.js
index 35a4fada..685754fe 100644
--- a/common/content/events.js
+++ b/common/content/events.js
@@ -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. cmd will be executed when one of the
+ * specified events occurs and the URL of the applicable buffer
+ * matches regex.
+ *
+ * @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 event and
+ * regex.
+ *
+ * @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 event and
+ * regex.
+ *
+ * @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 event and
+ * regex.
+ *
+ * @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
+ * event. A map of args 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" 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" 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 (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 != "" && key != "")
{
- // 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;
}; //}}}
diff --git a/common/content/find.js b/common/content/find.js
index 8e67fbb4..0bb125cb 100644
--- a/common/content/find.js
+++ b/common/content/find.js
@@ -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();
}
-
};
//}}}
}; //}}}
diff --git a/common/content/help.js b/common/content/help.js
index c2ee6b87..12a639a8 100644
--- a/common/content/help.js
+++ b/common/content/help.js
@@ -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:
diff --git a/common/content/hints.js b/common/content/hints.js
index edca3fb6..cdc90dfb 100644
--- a/common/content/hints.js
+++ b/common/content/hints.js
@@ -47,7 +47,7 @@ function Hints() //{{{
var hintNumber = 0; // only the numerical part of the hint
var usedTabKey = false; // when we used 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)
diff --git a/common/content/io.js b/common/content/io.js
index b87e1e6f..c98ed57a 100644
--- a/common/content/io.js
+++ b/common/content/io.js
@@ -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 <
diff --git a/common/content/mappings.js b/common/content/mappings.js
index d47501b3..ed3d78eb 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -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
diff --git a/common/content/modes.js b/common/content/modes.js
index 4f5d3de9..df20bff0 100644
--- a/common/content/modes.js
+++ b/common/content/modes.js
@@ -126,7 +126,8 @@ const modes = (function () //{{{
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
- var self = {
+ const self = {
+
NONE: 0,
__iterator__: function () util.Array.iterator(this.all),
diff --git a/common/content/options.js b/common/content/options.js
index 9877c741..2b451649 100644
--- a/common/content/options.js
+++ b/common/content/options.js
@@ -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
{
diff --git a/common/content/services.js b/common/content/services.js
index 2b22c702..79bc3e73 100644
--- a/common/content/services.js
+++ b/common/content/services.js
@@ -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:
diff --git a/common/content/style.js b/common/content/style.js
index 7940765a..6da2d5c3 100644
--- a/common/content/style.js
+++ b/common/content/style.js
@@ -72,8 +72,8 @@ Highlights.prototype.CSS =
*/
@@ -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
*/
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 filter 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"]);
diff --git a/common/content/tabs.js b/common/content/tabs.js
index 50d791bb..0fcefb70 100644
--- a/common/content/tabs.js
+++ b/common/content/tabs.js
@@ -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()
diff --git a/common/content/template.js b/common/content/template.js
index 0baaaf00..60f986df 100644
--- a/common/content/template.js
+++ b/common/content/template.js
@@ -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.
//
return this.commandOutput(
diff --git a/common/content/ui.js b/common/content/ui.js
index 0440885a..8f1cd0a5 100644
--- a/common/content/ui.js
+++ b/common/content/ui.js
@@ -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({str}, 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 = {template.maybeXML(str)}
;
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 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
+ * 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 == "" || key == "")
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 "":
case "":
case "":
@@ -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 open 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