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