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

Merge branch 'master' of kmaglione@git.vimperator.org:/git/vimperator/liberator

This commit is contained in:
Kris Maglione
2009-05-19 13:06:48 -04:00
20 changed files with 452 additions and 171 deletions

View File

@@ -135,6 +135,19 @@ function Buffer() //{{{
pageInfo[option] = [fn, title]; pageInfo[option] = [fn, title];
} }
function openUploadPrompt(elem)
{
commandline.input("Upload file: ", function (path) {
let file = io.getFile(path);
if (!file.exists())
return liberator.beep();
elem.value = file.path;
},
{ completer: completion.file, default: elem.value });
}
/////////////////////////////////////////////////////////////////////////////}}} /////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS ///////////////////////////////////////////////// ////////////////////// OPTIONS /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{ /////////////////////////////////////////////////////////////////////////////{{{
@@ -360,16 +373,13 @@ function Buffer() //{{{
function (count) function (count)
{ {
if (count < 1 && buffer.lastInputField) if (count < 1 && buffer.lastInputField)
{ buffer.focusElement(buffer.lastInputField);
buffer.lastInputField.focus();
}
else else
{ {
let elements = []; let elements = [];
let matches = buffer.evaluateXPath( let matches = buffer.evaluateXPath(
// TODO: type="file" "//input[not(@type) or @type='text' or @type='password' or @type='file'] | //textarea[not(@disabled) and not(@readonly)] |" +
"//input[not(@type) or @type='text' or @type='password'] | //textarea[not(@disabled) and not(@readonly)] |" + "//xhtml:input[not(@type) or @type='text' or @type='password' or @type='file'] | //xhtml:textarea[not(@disabled) and not(@readonly)]"
"//xhtml:input[not(@type) or @type='text' or @type='password'] | //xhtml:textarea[not(@disabled) and not(@readonly)]"
); );
for (let match in matches) for (let match in matches)
@@ -382,12 +392,10 @@ function Buffer() //{{{
if (elements.length > 0) if (elements.length > 0)
{ {
count = Math.min(Math.max(count, 1), elements.length); count = Math.min(Math.max(count, 1), elements.length);
elements[count - 1].focus(); buffer.focusElement(elements[count - 1]);
} }
else else
{
liberator.beep(); liberator.beep();
}
} }
}, },
{ flags: Mappings.flags.COUNT }); { flags: Mappings.flags.COUNT });
@@ -509,6 +517,14 @@ function Buffer() //{{{
////////////////////// COMMANDS //////////////////////////////////////////////// ////////////////////// COMMANDS ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{ /////////////////////////////////////////////////////////////////////////////{{{
commands.add(["frameo[nly]"],
"Show only the current frame's page",
function (args)
{
liberator.open(tabs.localStore.focusedFrame.document.documentURI);
},
{ argCount: "0" });
commands.add(["ha[rdcopy]"], commands.add(["ha[rdcopy]"],
"Print current document", "Print current document",
function (args) function (args)
@@ -1063,18 +1079,10 @@ function Buffer() //{{{
elem.contentWindow.focus(); elem.contentWindow.focus();
return; return;
} }
else if (elemTagName == "input" && elem.getAttribute('type').toLowerCase() == "file") else if (elemTagName == "input" && elem.type.toLowerCase() == "file")
{ {
commandline.input("Upload file: ", function (path) openUploadPrompt(elem);
{ buffer.lastInputField = elem;
let file = io.getFile(path);
if (!file.exists())
return liberator.beep();
elem.value = file.path;
}
, {completer: completion.file, default: elem.value});
return; return;
} }
@@ -1182,18 +1190,9 @@ function Buffer() //{{{
offsetX = Number(coords[0]) + 1; offsetX = Number(coords[0]) + 1;
offsetY = Number(coords[1]) + 1; offsetY = Number(coords[1]) + 1;
} }
else if (localName == "input" && elem.getAttribute('type').toLowerCase() == "file") else if (localName == "input" && elem.type.toLowerCase() == "file")
{ {
commandline.input("Upload file: ", function (path) openUploadPrompt(elem);
{
let file = io.getFile(path);
if (!file.exists())
return liberator.beep();
elem.value = file.path;
}
, {completer: completion.file, default: elem.value});
return; return;
} }
@@ -1931,7 +1930,7 @@ function Marks() //{{{
} }
if (!ok) if (!ok)
liberator.echoerr("E20: Mark not set"); // FIXME: move up? liberator.echoerr("E20: Mark not set");
}, },
/** /**

View File

@@ -34,13 +34,25 @@ the terms of any one of the MPL, the GPL or the LGPL.
* A class representing Ex commands. Instances are created by * A class representing Ex commands. Instances are created by
* the {@link Commands} class. * the {@link Commands} class.
* *
* @param {string[]} specs The names by which this command can be invoked.
* These are specified in the form "com[mand]" where "com" is a unique
* command name prefix.
* @param {string} description A short one line description of the command.
* @param {function} action The action invoked by this command when executed.
* @param {Object} extraInfo An optional extra configuration hash. The
* following properties are supported.
* argCount - See (@link Command#argCount)
* bang - See (@link Command#bang)
* completer - See (@link Command#completer)
* count - See (@link Command#count)
* heredoc - See (@link Command#heredoc)
* literal - See (@link Command#literal)
* options - See (@link Command#options)
* serial - See (@link Command#serial)
* @private * @private
*/ */
function Command(specs, description, action, extraInfo) //{{{ function Command(specs, description, action, extraInfo) //{{{
{ {
if (!specs || !action)
return null;
if (!extraInfo) if (!extraInfo)
extraInfo = {}; extraInfo = {};
@@ -91,7 +103,7 @@ function Command(specs, description, action, extraInfo) //{{{
/** @property {string[]} All of this command's long and short names. */ /** @property {string[]} All of this command's long and short names. */
this.names = expandedSpecs.names; // return all command name aliases this.names = expandedSpecs.names; // return all command name aliases
/** @property {string} This command's description, as shown in :exinfo */ /** @property {string} This command's description, as shown in :exusage */
this.description = description || ""; this.description = description || "";
/** @property {function (Args)} The function called to execute this command. */ /** @property {function (Args)} The function called to execute this command. */
this.action = action; this.action = action;

View File

@@ -46,7 +46,6 @@ the terms of any one of the MPL, the GPL or the LGPL.
* @author Kris Maglione <maglione.k@gmail.com> * @author Kris Maglione <maglione.k@gmail.com>
* @constructor * @constructor
*/ */
function CompletionContext(editor, name, offset) //{{{ function CompletionContext(editor, name, offset) //{{{
{ {
if (!(this instanceof arguments.callee)) if (!(this instanceof arguments.callee))

View File

@@ -690,7 +690,7 @@ function Editor() //{{{
// motion = b, 0, gg, G, etc. // motion = b, 0, gg, G, etc.
executeCommandWithMotion: function (cmd, motion, count) executeCommandWithMotion: function (cmd, motion, count)
{ {
if (!typeof count == "number" || count < 1) if (typeof count != "number" || count < 1)
count = 1; count = 1;
if (cmd == motion) if (cmd == motion)
@@ -705,12 +705,12 @@ function Editor() //{{{
{ {
case "j": case "j":
this.executeCommand("cmd_beginLine", 1); this.executeCommand("cmd_beginLine", 1);
this.executeCommand("cmd_selectLineNext", count+1); this.executeCommand("cmd_selectLineNext", count + 1);
break; break;
case "k": case "k":
this.executeCommand("cmd_beginLine", 1); this.executeCommand("cmd_beginLine", 1);
this.executeCommand("cmd_lineNext", 1); this.executeCommand("cmd_lineNext", 1);
this.executeCommand("cmd_selectLinePrevious", count+1); this.executeCommand("cmd_selectLinePrevious", count + 1);
break; break;
case "h": case "h":
this.executeCommand("cmd_selectCharPrevious", count); this.executeCommand("cmd_selectCharPrevious", count);

View File

@@ -1733,38 +1733,6 @@ function Events() //{{{
// Stub for something else, presumably. Not in any documented // Stub for something else, presumably. Not in any documented
// interface. // interface.
onLinkIconAvailable: function () {} onLinkIconAvailable: function () {}
},
// TODO: move to options.js?
prefObserver: {
register: function ()
{
// better way to monitor all changes?
this._branch = services.get("pref").getBranch("").QueryInterface(Ci.nsIPrefBranch2);
this._branch.addObserver("", this, false);
},
unregister: function ()
{
if (this._branch)
this._branch.removeObserver("", this);
},
observe: function (aSubject, aTopic, aData)
{
if (aTopic != "nsPref:changed")
return;
// aSubject is the nsIPrefBranch we're observing (after appropriate QI)
// aData is the name of the pref that's been changed (relative to aSubject)
switch (aData)
{
case "accessibility.browsewithcaret":
let value = options.getPref("accessibility.browsewithcaret", false);
liberator.mode = value ? modes.CARET : modes.NORMAL;
break;
}
}
} }
}; //}}} }; //}}}
@@ -1782,10 +1750,8 @@ function Events() //{{{
} }
catch (e) {} catch (e) {}
self.prefObserver.register();
liberator.registerObserver("shutdown", function () { liberator.registerObserver("shutdown", function () {
self.destroy(); self.destroy();
self.prefObserver.unregister();
}); });
window.addEventListener("keypress", wrapListener("onKeyPress"), true); window.addEventListener("keypress", wrapListener("onKeyPress"), true);

View File

@@ -30,7 +30,6 @@ the terms of any one of the MPL, the GPL or the LGPL.
// TODO: proper backwards search - implement our own component? // TODO: proper backwards search - implement our own component?
// : implement our own highlighter? // : implement our own highlighter?
// : frameset pages
// : <ESC> should cancel search highlighting in 'incsearch' mode and jump // : <ESC> should cancel search highlighting in 'incsearch' mode and jump
// back to the presearch page location - can probably use the same // back to the presearch page location - can probably use the same
// solution as marks // solution as marks
@@ -48,8 +47,6 @@ function Finder() //{{{
////////////////////// PRIVATE SECTION ///////////////////////////////////////// ////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{ /////////////////////////////////////////////////////////////////////////////{{{
// FIXME:
//var self = this; // needed for callbacks since "this" is the "liberator" object in a callback
var found = false; // true if the last search was successful var found = false; // true if the last search was successful
var backwards = false; // currently searching backwards var backwards = false; // currently searching backwards
var searchString = ""; // current search string (without modifiers) var searchString = ""; // current search string (without modifiers)

View File

@@ -638,8 +638,8 @@ function Hints() //{{{
* @param {boolean} allowWordOverleaping Whether to allow non-contiguous * @param {boolean} allowWordOverleaping Whether to allow non-contiguous
* words to match. * words to match.
* *
* @return {function(String):bool} A function that will filter only * @return {function(String):boolean} A function that will filter only
* hints that match as above. * hints that match as above.
*/ */
function wordStartsWithMatcher(hintString, allowWordOverleaping) //{{{ function wordStartsWithMatcher(hintString, allowWordOverleaping) //{{{
{ {
@@ -886,7 +886,7 @@ function Hints() //{{{
context.completions = [[k, v.prompt] for ([k, v] in Iterator(hintModes))]; context.completions = [[k, v.prompt] for ([k, v] in Iterator(hintModes))];
}, },
onChange: function () { modes.pop() }, onChange: function () { modes.pop() },
onCancel: function (arg) { arg && setTimeout(function () hints.show(arg), 0); }, onCancel: function (arg) { arg && setTimeout(function () hints.show(arg), 0); }
}); });
}, { flags: Mappings.flags.COUNT }); }, { flags: Mappings.flags.COUNT });
@@ -897,14 +897,16 @@ function Hints() //{{{
return { return {
/** /**
* Create a new hint mode * Creates a new hint mode.
* *
* @param {String} mode The letter that identifies this mode. * @param {String} mode The letter that identifies this mode.
* @param {String} description The description to display to the user about this mode. * @param {String} description The description to display to the user
* @param {function(Node)} callback The function to be called with the element that matches. * about this mode.
* @param {function():String} selector The function that returns an XPath selector to decide * @param {function(Node)} callback The function to be called with the
* which elements can be hinted (the default returns * element that matches.
* options.hinttags) * @param {function():String} selector The function that returns an
* XPath selector to decide which elements can be hinted (the
* default returns options.hinttags).
*/ */
addMode: function (mode) addMode: function (mode)
{ {
@@ -912,11 +914,11 @@ function Hints() //{{{
}, },
/** /**
* Update the display of hints. * Updates the display of hints.
* *
* @param {String} minor Which hint mode to use. * @param {String} minor Which hint mode to use.
* @param {String} filter The filter to use. * @param {String} filter The filter to use.
* @param {Object} win The window in which we are showing hints * @param {Object} win The window in which we are showing hints.
*/ */
show: function (minor, filter, win) show: function (minor, filter, win)
{ {
@@ -968,7 +970,7 @@ function Hints() //{{{
}, },
/** /**
* Handle an event * Handle an event.
* *
* @param {Event} event The event to handle. * @param {Event} event The event to handle.
*/ */

View File

@@ -335,7 +335,7 @@ function IO() //{{{
function () function ()
{ {
let list = template.tabular(["<SNR>", "Filename"], ["text-align: right; padding-right: 1em;"], let list = template.tabular(["<SNR>", "Filename"], ["text-align: right; padding-right: 1em;"],
([i + 1, file] for ([i, file] in Iterator(scriptNames)))); // TODO: add colon? ([i + 1, file] for ([i, file] in Iterator(scriptNames)))); // TODO: add colon and remove column titles for pedantic Vim compatibility?
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE); commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
}, },
@@ -345,11 +345,13 @@ function IO() //{{{
"Read Ex commands from a file", "Read Ex commands from a file",
function (args) function (args)
{ {
// FIXME: "E172: Only one file name allowed" if (args.length > 1)
io.source(args[0], args.bang); liberator.echoerr("E172: Only one file name allowed");
else
io.source(args[0], args.bang);
}, },
{ {
argCount: "1", argCount: "+", // FIXME: should be "1" but kludged for proper error message
bang: true, bang: true,
completer: function (context) completion.file(context, true) completer: function (context) completion.file(context, true)
}); });
@@ -878,7 +880,6 @@ lookup:
let dirs = getPathsFromPathList(options["runtimepath"]); let dirs = getPathsFromPathList(options["runtimepath"]);
let found = false; let found = false;
// FIXME: should use original arg string
liberator.echomsg("Searching for \"" + paths.join(" ") + "\" in \"" + options["runtimepath"] + "\"", 2); liberator.echomsg("Searching for \"" + paths.join(" ") + "\" in \"" + options["runtimepath"] + "\"", 2);
outer: outer:
@@ -902,7 +903,7 @@ lookup:
} }
if (!found) if (!found)
liberator.echomsg("not found in 'runtimepath': \"" + paths.join(" ") + "\"", 1); // FIXME: should use original arg string liberator.echomsg("not found in 'runtimepath': \"" + paths.join(" ") + "\"", 1);
return found; return found;
}, },
@@ -1001,7 +1002,7 @@ lookup:
{ {
let lineNumber = i + 1; let lineNumber = i + 1;
// FIXME: messages need to be able to specify // TODO: messages need to be able to specify
// whether they can be cleared/overwritten or // whether they can be cleared/overwritten or
// should be appended to and the MOW opened // should be appended to and the MOW opened
liberator.echoerr("Error detected while processing " + file.path, commandline.FORCE_MULTILINE); liberator.echoerr("Error detected while processing " + file.path, commandline.FORCE_MULTILINE);

View File

@@ -1069,7 +1069,7 @@ const liberator = (function () //{{{
* Opens one or more URLs. Returns true when load was initiated, or * Opens one or more URLs. Returns true when load was initiated, or
* false on error. * false on error.
* *
* @param {FIXME} urls Either a URL string or an array of URLs. * @param {string|string[]} urls Either a URL string or an array of URLs.
* The array can look like this: * The array can look like this:
* ["url1", "url2", "url3", ...] * ["url1", "url2", "url3", ...]
* or: * or:

View File

@@ -30,33 +30,68 @@ the terms of any one of the MPL, the GPL or the LGPL.
// Do NOT create instances of this class yourself, use the helper method // Do NOT create instances of this class yourself, use the helper method
// mappings.add() instead // mappings.add() instead
function Map(modes, cmds, description, action, extraInfo) //{{{ /**
* A class representing key mappings. Instances are created by the
* {@link Mappings} class.
*
* @param {number[]} modes The modes in which this mapping is active.
* @param {string[]} keys The key sequences which are bound to
* <b>action</b>.
* @param {string} description A short one line description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extraInfo An optional extra configuration hash. The
* following properties are supported.
* flags - See (@link Map#flags)
* noremap - See (@link Map#noremap)
* rhs - See (@link Map#rhs)
* silent - See (@link Map#silent)
* @private
*/
function Map(modes, keys, description, action, extraInfo) //{{{
{ {
if (!modes || (!cmds || !cmds.length) || !action)
return null;
if (!extraInfo) if (!extraInfo)
extraInfo = {}; extraInfo = {};
/** @property {number[]} All of the modes for which this mapping applies. */
this.modes = modes; this.modes = modes;
// only store keysyms with uppercase modifier strings /** @property {string[]} All of this mapping's names (key sequences). */
this.names = cmds.map(function (cmd) cmd.replace(/[casm]-/g, String.toUpperCase)); this.names = keys.map(function (cmd) cmd.replace(/[casm]-/g, String.toUpperCase)); // only store keysyms with uppercase modifier strings
/** @property {function (number)} The function called to execute this mapping. */
this.action = action; this.action = action;
/** @property {number} See (@link Mappings#flags) */
this.flags = extraInfo.flags || 0; this.flags = extraInfo.flags || 0;
/** @property {string} This mapping's description, as shown in :viusage. */
this.description = description || ""; this.description = description || "";
/** @property {string} The literal RHS expansion of this mapping. */
this.rhs = extraInfo.rhs || null; this.rhs = extraInfo.rhs || null;
/** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */
this.noremap = extraInfo.noremap || false; this.noremap = extraInfo.noremap || false;
/** @property {boolean} Whether any output from the mapping should be echoed on the command line. */
this.silent = extraInfo.silent || false; this.silent = extraInfo.silent || false;
}; };
Map.prototype = { Map.prototype = {
hasName: function (name) /**
{ * Returns whether this mapping can be invoked by a key sequence matching
return this.names.indexOf(name) >= 0; * <b>name</b>.
}, *
* @param {string} name The name to query.
* @returns {boolean}
*/
hasName: function (name) this.names.indexOf(name) >= 0,
/**
* Execute the action for this mapping.
*
* @param {string} motion The motion argument if accepted by this mapping.
* E.g. "w" for "dw"
* @param {number} count The associated count. E.g. "5" for "5j"
* @default -1
* @param {string} argument The normal argument if accepted by this
* mapping. E.g. "a" for "ma"
*/
execute: function (motion, count, argument) execute: function (motion, count, argument)
{ {
let args = []; let args = [];
@@ -100,9 +135,9 @@ function Mappings() //{{{
{ {
let where = userMap ? user : main; let where = userMap ? user : main;
map.modes.forEach(function (mode) { map.modes.forEach(function (mode) {
if (!(mode in where)) if (!(mode in where))
where[mode] = []; where[mode] = [];
where[mode].push(map); where[mode].push(map);
}); });
} }
@@ -255,6 +290,7 @@ function Mappings() //{{{
/////////////////////////////////////////////////////////////////////////////{{{ /////////////////////////////////////////////////////////////////////////////{{{
addMapCommands("", [modes.NORMAL, modes.VISUAL], ""); addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
for (let mode in modes.mainModes) for (let mode in modes.mainModes)
if (mode.char) if (mode.char)
addMapCommands(mode.char, addMapCommands(mode.char,
@@ -296,13 +332,40 @@ function Mappings() //{{{
__iterator__: function () mappingsIterator([modes.NORMAL], main), __iterator__: function () mappingsIterator([modes.NORMAL], main),
// used by :mkvimperatorrc to save mappings // used by :mkvimperatorrc to save mappings
/**
* Returns a user-defined mappings iterator for the specified
* <b>mode</b>.
*
* @param {number} mode The mode to return mappings from.
* @returns {Iterator(Map)}
*/
getUserIterator: function (mode) mappingsIterator(mode, user), getUserIterator: function (mode) mappingsIterator(mode, user),
/**
* Add a new default key mapping.
*
* @param {number[]} modes The modes that this mapping applies to.
* @param {string[]} keys The key sequences which are bound to
* <b>action</b>.
* @param {string} description A description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extra An optional extra configuration hash.
*/
add: function (modes, keys, description, action, extra) add: function (modes, keys, description, action, extra)
{ {
addMap(new Map(modes, keys, description, action, extra), false); addMap(new Map(modes, keys, description, action, extra), false);
}, },
/**
* Add a new user-defined key mapping.
*
* @param {number[]} modes The modes that this mapping applies to.
* @param {string[]} keys The key sequences which are bound to
* <b>action</b>.
* @param {string} description A description of the key mapping.
* @param {function} action The action invoked by each key sequence.
* @param {Object} extra An optional extra configuration hash.
*/
addUserMap: function (modes, keys, description, action, extra) addUserMap: function (modes, keys, description, action, extra)
{ {
keys = keys.map(expandLeader); keys = keys.map(expandLeader);
@@ -318,20 +381,41 @@ function Mappings() //{{{
addMap(map, true); addMap(map, true);
}, },
/**
* Returns the map from <b>mode</b> named <b>cmd</b>.
*
* @param {number} mode The mode to search.
* @param {string} cmd The map name to match.
* @returns {Map}
*/
get: function (mode, cmd) get: function (mode, cmd)
{ {
mode = mode || modes.NORMAL; mode = mode || modes.NORMAL;
return getMap(mode, cmd, user) || getMap(mode, cmd, main); return getMap(mode, cmd, user) || getMap(mode, cmd, main);
}, },
/**
* Returns the default map from <b>mode</b> named <b>cmd</b>.
*
* @param {number} mode The mode to search.
* @param {string} cmd The map name to match.
* @returns {Map}
*/
getDefault: function (mode, cmd) getDefault: function (mode, cmd)
{ {
mode = mode || modes.NORMAL; mode = mode || modes.NORMAL;
return getMap(mode, cmd, main); return getMap(mode, cmd, main);
}, },
// returns an array of mappings with names which START with "cmd" (but are NOT "cmd") /**
getCandidates: function (mode, cmd) * Returns an array of maps with names starting with but not equal to
* <b>prefix</b>.
*
* @param {number} mode The mode to search.
* @param {string} prefix The map prefix string to match.
* @returns {Map[]}
*/
getCandidates: function (mode, prefix)
{ {
let mappings = user[mode].concat(main[mode]); let mappings = user[mode].concat(main[mode]);
let matches = []; let matches = [];
@@ -340,10 +424,10 @@ function Mappings() //{{{
{ {
for (let [,name] in Iterator(map.names)) for (let [,name] in Iterator(map.names))
{ {
if (name.indexOf(cmd) == 0 && name.length > cmd.length) if (name.indexOf(prefix) == 0 && name.length > prefix.length)
{ {
// for < only return a candidate if it doesn't look like a <c-x> mapping // for < only return a candidate if it doesn't look like a <c-x> mapping
if (cmd != "<" || !/^<.+>/.test(name)) if (prefix != "<" || !/^<.+>/.test(name))
matches.push(map); matches.push(map);
} }
} }
@@ -352,28 +436,57 @@ function Mappings() //{{{
return matches; return matches;
}, },
/*
* Returns the map leader string used to replace the special token
* "<Leader>" when user mappings are defined.
*
* @returns {string}
*/
// FIXME: property
getMapLeader: function () getMapLeader: function ()
{ {
let leaderRef = liberator.variableReference("mapleader"); let leaderRef = liberator.variableReference("mapleader");
return leaderRef[0] ? leaderRef[0][leaderRef[1]] : "\\"; return leaderRef[0] ? leaderRef[0][leaderRef[1]] : "\\";
}, },
// returns whether the user added a custom user map /**
hasMap: function (mode, cmd) * Returns whether there is a user-defined mapping <b>cmd</b> for the
{ * specified <b>mode</b>.
return user[mode].some(function (map) map.hasName(cmd)); *
}, * @param {number} mode The mode to search.
* @param {string} cmd The candidate key mapping.
* @returns {boolean}
*/
hasMap: function (mode, cmd) user[mode].some(function (map) map.hasName(cmd)),
/**
* Remove the user-defined mapping named <b>cmd</b> for <b>mode</b>.
*
* @param {number} mode The mode to search.
* @param {string} cmd The map name to match.
*/
remove: function (mode, cmd) remove: function (mode, cmd)
{ {
removeMap(mode, cmd); removeMap(mode, cmd);
}, },
/**
* Remove all user-defined mappings for <b>mode</b>.
*
* @param {number} mode The mode to remove all mappings from.
*/
removeAll: function (mode) removeAll: function (mode)
{ {
user[mode] = []; user[mode] = [];
}, },
/**
* Lists all user-defined mappings matching <b>filter</b> for the
* specified <b>modes</b>.
*
* @param {number[]} modes An array of modes to search.
* @param {string} filter The filter string to match.
*/
list: function (modes, filter) list: function (modes, filter)
{ {
let modeSign = ""; let modeSign = "";
@@ -413,7 +526,6 @@ function Mappings() //{{{
} }
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE); commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
} }
}; };
//}}} //}}}
}; //}}} }; //}}}

View File

@@ -32,9 +32,6 @@ the terms of any one of the MPL, the GPL or the LGPL.
// options.add() instead // options.add() instead
function Option(names, description, type, defaultValue, extraInfo) //{{{ function Option(names, description, type, defaultValue, extraInfo) //{{{
{ {
if (!names || !type)
return null;
if (!extraInfo) if (!extraInfo)
extraInfo = {}; extraInfo = {};
@@ -808,7 +805,7 @@ function Options() //{{{
.map(function (pref) [pref, ""])]); .map(function (pref) [pref, ""])]);
}); });
return { const self = {
OPTION_SCOPE_GLOBAL: 1, OPTION_SCOPE_GLOBAL: 1,
OPTION_SCOPE_LOCAL: 2, OPTION_SCOPE_LOCAL: 2,
@@ -820,6 +817,37 @@ function Options() //{{{
return (v for ([k, v] in Iterator(sorted))); return (v for ([k, v] in Iterator(sorted)));
}, },
prefObserver: {
register: function ()
{
// better way to monitor all changes?
this._branch = services.get("pref").getBranch("").QueryInterface(Ci.nsIPrefBranch2);
this._branch.addObserver("", this, false);
},
unregister: function ()
{
if (this._branch)
this._branch.removeObserver("", this);
},
observe: function (subject, topic, data)
{
if (topic != "nsPref:changed")
return;
// subject is the nsIPrefBranch we're observing (after appropriate QI)
// data is the name of the pref that's been changed (relative to subject)
switch (data)
{
case "accessibility.browsewithcaret":
let value = options.getPref("accessibility.browsewithcaret", false);
liberator.mode = value ? modes.CARET : modes.NORMAL;
break;
}
}
},
add: function (names, description, type, defaultValue, extraInfo) add: function (names, description, type, defaultValue, extraInfo)
{ {
if (!extraInfo) if (!extraInfo)
@@ -1067,8 +1095,15 @@ function Options() //{{{
this.popContext(); this.popContext();
} }
} }
}; }; //}}}
//}}}
self.prefObserver.register();
liberator.registerObserver("shutdown", function () {
self.prefObserver.unregister();
});
return self;
}; //}}} }; //}}}
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -30,6 +30,9 @@ the terms of any one of the MPL, the GPL or the LGPL.
// TODO: many methods do not work with Thunderbird correctly yet // TODO: many methods do not work with Thunderbird correctly yet
/**
* @instance tabs
*/
function Tabs() //{{{ function Tabs() //{{{
{ {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -307,7 +310,6 @@ function Tabs() //{{{
if (arg) if (arg)
{ {
arg = arg.toLowerCase();
let removed = 0; let removed = 0;
let matches = arg.match(/^(\d+):?/); let matches = arg.match(/^(\d+):?/);
@@ -318,15 +320,27 @@ function Tabs() //{{{
} }
else else
{ {
let str = arg.toLowerCase();
let browsers = getBrowser().browsers; let browsers = getBrowser().browsers;
for (let i = browsers.length - 1; i >= 0; i--) for (let i = browsers.length - 1; i >= 0; i--)
{ {
let title = browsers[i].contentTitle.toLowerCase() || ""; let host, title, uri = browsers[i].currentURI.spec;
let uri = browsers[i].currentURI.spec.toLowerCase(); if (browsers[i].currentURI.schemeIs("about"))
let host = browsers[i].currentURI.host.toLowerCase(); {
host = "";
title = "(Untitled)";
}
else
{
host = browsers[i].currentURI.host;
title = browsers[i].contentTitle;
}
if (host.indexOf(arg) >= 0 || uri == arg || [host, title, uri] = [host, title, uri].map(String.toLowerCase);
(special && (title.indexOf(arg) >= 0 || uri.indexOf(arg) >= 0)))
if (host.indexOf(str) >= 0 || uri == str ||
(special && (title.indexOf(str) >= 0 || uri.indexOf(str) >= 0)))
{ {
tabs.remove(tabs.getTab(i)); tabs.remove(tabs.getTab(i));
removed++; removed++;
@@ -398,7 +412,7 @@ function Tabs() //{{{
if (arg) if (arg)
{ {
if (/^\d+$/.test(arg)) if (/^\d+$/.test(arg))
tabs.select("-" + arg, true); // FIXME: urgh! tabs.select("-" + arg, true);
else else
liberator.echoerr("E488: Trailing characters"); liberator.echoerr("E488: Trailing characters");
} }
@@ -676,8 +690,16 @@ function Tabs() //{{{
return { return {
/**
* @property {Object} The previously accessed tab or null if no tab
* other than the current one has been accessed.
*/
get alternate() alternates[1], get alternate() alternates[1],
/**
* @property {Iterator(Object)} A genenerator that returns all browsers
* in the current window.
*/
get browsers() get browsers()
{ {
let browsers = getBrowser().browsers; let browsers = getBrowser().browsers;
@@ -685,10 +707,13 @@ function Tabs() //{{{
yield [i, browsers[i]]; yield [i, browsers[i]];
}, },
get tabsBound() { /**
return Boolean(styles.get(true, "tab-binding")) * @property {boolean} Whether the tab numbering XBL binding has been
}, * applied.
set tabsBound(val) { */
get tabsBound() Boolean(styles.get(true, "tab-binding")),
set tabsBound(val)
{
let fragment = liberator.has("MacUnix") ? "tab-mac" : "tab"; let fragment = liberator.has("MacUnix") ? "tab-mac" : "tab";
if (!val) if (!val)
styles.removeSheet(true, "tab-binding"); styles.removeSheet(true, "tab-binding");
@@ -699,8 +724,14 @@ function Tabs() //{{{
".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }"); ".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }");
}, },
/**
* @property {number} The number of tabs in the current window.
*/
get count() getBrowser().mTabs.length, get count() getBrowser().mTabs.length,
/**
* @property {Object} The local options store for the current tab.
*/
get options() get options()
{ {
let store = this.localStore; let store = this.localStore;
@@ -709,6 +740,15 @@ function Tabs() //{{{
return store.options; return store.options;
}, },
/**
* Returns the local state store for the tab at the specified
* <b>tabIndex</b>. If <b>tabIndex</b> is not specified then the
* current tab is used.
*
* @param {number} tabIndex
* @returns {Object}
*/
// FIXME: why not a tab arg? Why this and the property?
getLocalStore: function (tabIndex) getLocalStore: function (tabIndex)
{ {
let tab = this.getTab(tabIndex); let tab = this.getTab(tabIndex);
@@ -717,8 +757,15 @@ function Tabs() //{{{
return tab.liberatorStore; return tab.liberatorStore;
}, },
/**
* @property {Object} The local state store for the currently selected
* tab.
*/
get localStore() this.getLocalStore(), get localStore() this.getLocalStore(),
/**
* @property {Object} The tab browser strip.
*/
get tabStrip() get tabStrip()
{ {
let tabStrip = null; let tabStrip = null;
@@ -732,18 +779,40 @@ function Tabs() //{{{
return tabStrip; return tabStrip;
}, },
// @returns the index of the currently selected tab starting with 0 /**
* @property {Object[]} The array of closed tabs for the current
* session.
*/
get closedTabs()
{
return services.get("json").decode(services.get("sessionStore").getClosedTabData(window));
},
/**
* Returns the index of <b>tab</b> or the index of the currently
* selected tab if <b>tab</b> is not specified. This is a 0-based
* index.
*
* @param {Object} tab A tab from the current tab list.
* @returns {number}
*/
index: function (tab) index: function (tab)
{ {
if (tab) if (tab)
return Array.indexOf(getBrowser().mTabs, tab); return Array.indexOf(getBrowser().mTabs, tab);
else
return getBrowser().mTabContainer.selectedIndex; return getBrowser().mTabContainer.selectedIndex;
}, },
// TODO: implement filter // TODO: implement filter
// @returns an array of tabs which match filter /**
get: function (filter) * Returns an array of all tabs in the tab list.
*
* @returns {Object[]}
*/
// FIXME: why not return the tab element?
// : unused? Remove me.
get: function ()
{ {
let buffers = []; let buffers = [];
for (let [i, browser] in this.browsers) for (let [i, browser] in this.browsers)
@@ -756,6 +825,13 @@ function Tabs() //{{{
return buffers; return buffers;
}, },
/**
* Returns the index of the tab containing <b>content</b>.
*
* @param {Object} content Either a content window or a content
* document.
*/
// FIXME: Only called once...necessary?
getContentIndex: function (content) getContentIndex: function (content)
{ {
for (let [i, browser] in this.browsers) for (let [i, browser] in this.browsers)
@@ -766,6 +842,14 @@ function Tabs() //{{{
return -1; return -1;
}, },
/**
* Returns the tab at the specified <b>index</b> or the currently
* selected tab if <b>index</b> is not specified. This is a 0-based
* index.
*
* @param {number} index The index of the tab required.
* @returns {Object}
*/
getTab: function (index) getTab: function (index)
{ {
if (index != undefined) if (index != undefined)
@@ -774,26 +858,44 @@ function Tabs() //{{{
return getBrowser().mCurrentTab; return getBrowser().mCurrentTab;
}, },
get closedTabs() /**
{ * Lists all tabs matching <b>filter</b>.
return services.get("json").decode(services.get("sessionStore").getClosedTabData(window)); *
}, * @param {string} filter A filter matching a substring of the tab's
* document title or URL.
*/
list: function (filter) list: function (filter)
{ {
completion.listCompleter("buffer", filter); completion.listCompleter("buffer", filter);
}, },
// wrap causes the movement to wrap around the start and end of the tab list /**
// NOTE: position is a 0 based index * Moves a tab to a new position in the tab list.
*
* @param {Object} tab The tab to move.
* @param {string} spec See {@link indexFromSpec}.
* @param {boolean} wrap Whether an out of bounds <b>spec</b> causes
* the destination position to wrap around the start/end of the tab
* list.
*/
move: function (tab, spec, wrap) move: function (tab, spec, wrap)
{ {
let index = indexFromSpec(spec, wrap); let index = indexFromSpec(spec, wrap);
getBrowser().moveTabTo(tab, index); getBrowser().moveTabTo(tab, index);
}, },
// quitOnLastTab = 1: quit without saving session /**
// quitOnLastTab = 2: quit and save session * Removes the specified <b>tab</b> from the tab list.
*
* @param {Object} tab
* @param {number} count
* @param {boolean} focusLeftTab Focus the tab to the left of the removed tab.
* @param {number} quitOnLastTab Whether to quit if the tab being
* deleted is the only tab in the tab list:
* 1 - quit without saving session
* 2 - quit and save session
*/
// FIXME: what is quitOnLastTab {1,2} all about then, eh? --djk
remove: function (tab, count, focusLeftTab, quitOnLastTab) remove: function (tab, count, focusLeftTab, quitOnLastTab)
{ {
let removeOrBlankTab = { let removeOrBlankTab = {
@@ -874,25 +976,38 @@ function Tabs() //{{{
} }
}, },
/**
* Removes all tabs from the tab list except the specified <b>tab</b>.
*
* @param {Object} tab The tab to keep.
*/
keepOnly: function (tab) keepOnly: function (tab)
{ {
getBrowser().removeAllTabsBut(tab); getBrowser().removeAllTabsBut(tab);
}, },
/**
* Selects the tab at the position specified by <b>spec</b>.
*
* @param {string} spec See {@link indexFromSpec}
* @param {boolean} wrap Whether an out of bounds <b>spec</b> causes
* the selection position to wrap around the start/end of the tab
* list.
*/
select: function (spec, wrap) select: function (spec, wrap)
{ {
let index = indexFromSpec(spec, wrap); let index = indexFromSpec(spec, wrap);
// FIXME: // FIXME:
if (index == -1) if (index == -1)
{ {
liberator.beep(); // XXX: move to ex-handling? liberator.beep();
return; return;
} }
getBrowser().mTabContainer.selectedIndex = index; getBrowser().mTabContainer.selectedIndex = index;
}, },
/** /**
* Reload the specified tab. * Reloads the specified tab.
* *
* @param {Object} tab The tab to reload. * @param {Object} tab The tab to reload.
* @param {boolean} bypassCache Whether to bypass the cache when * @param {boolean} bypassCache Whether to bypass the cache when
@@ -912,7 +1027,7 @@ function Tabs() //{{{
}, },
/** /**
* Reload all tabs. * Reloads all tabs.
* *
* @param {boolean} bypassCache Whether to bypass the cache when * @param {boolean} bypassCache Whether to bypass the cache when
* reloading. * reloading.
@@ -941,7 +1056,7 @@ function Tabs() //{{{
}, },
/** /**
* Stop loading the specified tab. * Stops loading the specified tab.
* *
* @param {Object} tab The tab to stop loading. * @param {Object} tab The tab to stop loading.
*/ */
@@ -954,7 +1069,7 @@ function Tabs() //{{{
}, },
/** /**
* Stop loading all tabs. * Stops loading all tabs.
*/ */
stopAll: function () stopAll: function ()
{ {
@@ -962,8 +1077,20 @@ function Tabs() //{{{
browser.stop(); browser.stop();
}, },
// "buffer" is a string which matches the URL or title of a buffer, if it /**
// is null, the last used string is used again * Selects the tab containing the specified <b>buffer</b>.
*
* @param {string} buffer A string which matches the URL or title of a
* buffer, if it is null, the last used string is used again.
* @param {boolean} allowNonUnique Whether to select the first of
* multiple matches.
* @param {number} count If there are multiple matches select the
* count'th match.
* @param {boolean} reverse Whether to search the buffer list in
* reverse order.
*
*/
// FIXME: help!
switchTo: function (buffer, allowNonUnique, count, reverse) switchTo: function (buffer, allowNonUnique, count, reverse)
{ {
if (buffer == "") if (buffer == "")
@@ -1037,6 +1164,12 @@ function Tabs() //{{{
} }
}, },
/**
* Clones the specified <b>tab</b> and append it to the tab list.
*
* @param {Object} tab The tab to clone.
* @param {boolean} activate Whether to select the newly cloned tab.
*/
cloneTab: function (tab, activate) cloneTab: function (tab, activate)
{ {
let newTab = getBrowser().addTab(); let newTab = getBrowser().addTab();
@@ -1048,6 +1181,12 @@ function Tabs() //{{{
return newTab; return newTab;
}, },
/**
* Detaches the specified <b>tab</b> and open it in a new window. If no
* tab is specified the currently selected tab is detached.
*
* @param {Object} tab The tab to detach.
*/
detachTab: function (tab) detachTab: function (tab)
{ {
if (!tab) if (!tab)
@@ -1060,6 +1199,9 @@ function Tabs() //{{{
this.remove(tab, 1, false, 1); this.remove(tab, 1, false, 1);
}, },
/**
* Selects the alternate tab.
*/
selectAlternateTab: function () selectAlternateTab: function ()
{ {
if (tabs.alternate == null || tabs.getTab() == tabs.alternate) if (tabs.alternate == null || tabs.getTab() == tabs.alternate)
@@ -1085,6 +1227,10 @@ function Tabs() //{{{
// tab that was selected when the session was created. As a result the // tab that was selected when the session was created. As a result the
// alternate after a restart is often incorrectly tab 1 when there // alternate after a restart is often incorrectly tab 1 when there
// shouldn't be one yet. // shouldn't be one yet.
/**
* Called on each TabSelect event to update the tab selection history.
* See (@link tabs.alternate).
*/
updateSelectionHistory: function () updateSelectionHistory: function ()
{ {
alternates = [this.getTab(), alternates[0]]; alternates = [this.getTab(), alternates[0]];

View File

@@ -44,12 +44,12 @@ const template = {
return <>{xml}</>; return <>{xml}</>;
}, },
completionRow: function completionRow(item, class) completionRow: function completionRow(item, highlightGroup)
{ {
if (typeof icon == "function") if (typeof icon == "function")
icon = icon(); icon = icon();
if (class) if (highlightGroup)
{ {
var text = item[0] || ""; var text = item[0] || "";
var desc = item[1] || ""; var desc = item[1] || "";
@@ -61,7 +61,7 @@ const template = {
} }
// <e4x> // <e4x>
return <div highlight={class || "CompItem"} style="white-space: nowrap"> return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap">
<!-- The non-breaking spaces prevent empty elements <!-- The non-breaking spaces prevent empty elements
- from pushing the baseline down and enlarging - from pushing the baseline down and enlarging
- the row. - the row.

View File

@@ -187,9 +187,9 @@ const config = { //{{{
var stateListener = var stateListener =
{ {
QueryInterface: function (aIID) QueryInterface: function (id)
{ {
if (aIID.equals(Components.interfaces.nsIDocumentStateListener)) if (id.equals(Components.interfaces.nsIDocumentStateListener))
return this; return this;
throw Components.results.NS_NOINTERFACE; throw Components.results.NS_NOINTERFACE;
}, },

View File

@@ -4,6 +4,7 @@
* TabMixPlus (and other tab extensions) should work much better now * TabMixPlus (and other tab extensions) should work much better now
together with Vimperator unless you :set guioptions+=[nN] together with Vimperator unless you :set guioptions+=[nN]
* remove 'preload' option. You can fake it by some custom javascript in your init file * remove 'preload' option. You can fake it by some custom javascript in your init file
* add :frameonly
* add :stopall * add :stopall
* add :tabdo * add :tabdo
* add 'encoding' * add 'encoding'

View File

@@ -5,6 +5,7 @@ ARCHITECTURE:
- modular help system - modular help system
BUGS: BUGS:
- :mkvimperatorrc now adds spurious vmap and nmap entries
- add window resize support to hints - add window resize support to hints
- searching backwards incrementally does not work i.e. with 'incsearch' set - searching backwards incrementally does not work i.e. with 'incsearch' set
- http://msdn2.microsoft.com/en-us/library/ms535258.aspx does not scroll with j/k/etc. - http://msdn2.microsoft.com/en-us/library/ms535258.aspx does not scroll with j/k/etc.

View File

@@ -601,19 +601,19 @@ function Bookmarks() //{{{
getSearchURL: function getSearchURL(text, useDefsearch) getSearchURL: function getSearchURL(text, useDefsearch)
{ {
let url = null; let url = null;
let aPostDataRef = {}; let postData = {};
let searchString = (useDefsearch ? options["defsearch"] + " " : "") + text; let searchString = (useDefsearch ? options["defsearch"] + " " : "") + text;
// we need to make sure our custom alias have been set, even if the user // we need to make sure our custom alias have been set, even if the user
// did not :open <tab> once before // did not :open <tab> once before
this.getSearchEngines(); this.getSearchEngines();
url = window.getShortcutOrURI(searchString, aPostDataRef); url = window.getShortcutOrURI(searchString, postData);
if (url == searchString) if (url == searchString)
url = null; url = null;
if (aPostDataRef && aPostDataRef.value) if (postData && postData.value)
return [url, aPostDataRef.value]; return [url, postData.value];
else else
return url; // can be null return url; // can be null
}, },

View File

@@ -327,6 +327,14 @@ Normally this command operates on the text zoom; if used with [!], it
operates on full zoom. operates on full zoom.
________________________________________________________________________________ ________________________________________________________________________________
section:Working{nbsp}with{nbsp}frames[frames]
|:frameo| |:frameonly|
||:frameo[nly]|| +
________________________________________________________________________________
Show only the current frame's page.
________________________________________________________________________________
section:Copying{nbsp}text[copying,yanking] section:Copying{nbsp}text[copying,yanking]
When running in X11, the text of the following commands is not only When running in X11, the text of the following commands is not only

View File

@@ -183,6 +183,7 @@ section:Ex{nbsp}commands[ex-cmd-index,:index]
||[c]:execute[c]|| Execute the argument as an Ex command + ||[c]:execute[c]|| Execute the argument as an Ex command +
||[c]:exusage[c]|| List all Ex commands with a short description + ||[c]:exusage[c]|| List all Ex commands with a short description +
||[c]:finish[c]|| Stop sourcing a script file + ||[c]:finish[c]|| Stop sourcing a script file +
||[c]:frameonly[c]|| Show only the current frame's page +
||[c]:forward[c]|| Go forward in the browser history + ||[c]:forward[c]|| Go forward in the browser history +
||[c]:hardcopy[c]|| Print current document + ||[c]:hardcopy[c]|| Print current document +
||[c]:help[c]|| Display help + ||[c]:help[c]|| Display help +

View File

@@ -601,13 +601,14 @@ function Bookmarks() //{{{
getSearchURL: function getSearchURL(text, useDefsearch) getSearchURL: function getSearchURL(text, useDefsearch)
{ {
let url = null; let url = null;
let aPostDataRef = {}; let postData = {};
let searchString = (useDefsearch ? options["defsearch"] + " " : "") + text; let searchString = (useDefsearch ? options["defsearch"] + " " : "") + text;
// we need to make sure our custom alias have been set, even if the user // we need to make sure our custom alias have been set, even if the user
// did not :open <tab> once before // did not :open <tab> once before
this.getSearchEngines(); this.getSearchEngines();
// ripped from Firefox
function getShortcutOrURI(aURL, aPostDataRef) function getShortcutOrURI(aURL, aPostDataRef)
{ {
var shortcutURL = null; var shortcutURL = null;
@@ -676,13 +677,13 @@ function Bookmarks() //{{{
return shortcutURL; return shortcutURL;
} }
url = getShortcutOrURI(searchString, aPostDataRef); url = getShortcutOrURI(searchString, postData);
if (url == searchString) if (url == searchString)
url = null; url = null;
if (aPostDataRef && aPostDataRef.value) if (postData && postData.value)
return [url, aPostDataRef.value]; return [url, postData.value];
else else
return url; // can be null return url; // can be null
}, },