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

Lots of rangefinder and Gecko 2 tabbing fixes.

This commit is contained in:
Kris Maglione
2010-08-28 18:02:03 -04:00
parent e3a2df403d
commit 4e40abe6b0
30 changed files with 856 additions and 883 deletions

View File

@@ -119,7 +119,7 @@ const AutoCommands = Module("autocommands", {
let autoCmds = this._store.filter(function (autoCmd) autoCmd.event == event);
dactyl.echomsg("Executing " + event + " Auto commands for \"*\"", 8);
dactyl.echomsg('Executing ' + event + ' Auto commands for "*"', 8);
let lastPattern = null;
let url = args.url || "";
@@ -127,7 +127,7 @@ const AutoCommands = Module("autocommands", {
for (let [, autoCmd] in Iterator(autoCmds)) {
if (autoCmd.pattern.test(url)) {
if (!lastPattern || lastPattern.source != autoCmd.pattern.source)
dactyl.echomsg("Executing " + event + " Auto commands for \"" + autoCmd.pattern.source + "\"", 8);
dactyl.echomsg("Executing " + event + " Auto commands for " + autoCmd.pattern.source.quote(), 8);
lastPattern = autoCmd.pattern;
dactyl.echomsg("autocommand " + autoCmd.command, 9);
@@ -167,7 +167,7 @@ const AutoCommands = Module("autocommands", {
if (event) {
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
let validEvents = config.autocommands.map(function (event) event[0]);
let validEvents = keys(config.autocommands);
validEvents.push("*");
events = event.split(",");
@@ -227,7 +227,7 @@ const AutoCommands = Module("autocommands", {
let [event, url] = args;
let defaultURL = url || buffer.URL;
let validEvents = config.autocommands.map(function (e) e[0]);
let validEvents = keys(config.autocommands);
// TODO: add command validators
dactyl.assert(event != "*",
@@ -257,10 +257,10 @@ const AutoCommands = Module("autocommands", {
});
},
completion: function () {
JavaScript.setCompleter(this.get, [function () config.autocommands]);
JavaScript.setCompleter(this.get, [function () Iterator(config.autocommands)]);
completion.autocmdEvent = function autocmdEvent(context) {
context.completions = config.autocommands;
context.completions = Iterator(config.autocommands);
};
completion.macro = function macro(context) {
@@ -273,7 +273,7 @@ const AutoCommands = Module("autocommands", {
"List of autocommand event names which should be ignored",
"stringlist", "",
{
completer: function () config.autocommands.concat([["all", "All events"]])
completer: function () Iterator(update({ all: "All Events" }, config.autocommands))
});
options.add(["strictfocus", "sf"],

View File

@@ -88,13 +88,18 @@ function dict(ary) {
}
function set(ary) {
var obj = {};
let obj = {};
if (ary)
for (var i = 0; i < ary.length; i++)
obj[ary[i]] = true;
return obj;
}
set.add = function (set, key) { set[key] = true; }
set.add = function (set, key) {
let res = this.has(set, key);
set[key] = true;
return res;
}
set.has = function (set, key) Object.prototype.hasOwnProperty.call(set, key);
set.remove = function (set, key) { delete set[key]; }
function iter(obj) {
@@ -142,6 +147,8 @@ function isinstance(targ, src) {
for (var i = 0; i < src.length; i++) {
if (targ instanceof src[i])
return true;
if (typeof src[i] == "string")
return Object.prototype.toString(targ) == "[object " + src[i] + "]";
var type = types[typeof targ];
if (type && issubclass(src[i], type))
return true;

View File

@@ -12,15 +12,18 @@
<children/>
</content>
</binding>
<binding id="compitem-td">
<!-- No white space. The table is white-space: pre; :( -->
<content><html:span class="td-strut"/><html:span class="td-span"><children/></html:span></content>
</binding>
<binding id="tab" display="xul:hbox"
extends="chrome://global/content/bindings/tabbox.xml#tab">
<content chromedir="ltr" closetabtext="Close Tab">
extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab">
<content context="tabContextMenu" closetabtext="Close Tab">
<xul:stack class="tab-icon dactyl-tab-stack">
<xul:image xbl:inherits="validate,src=image" class="tab-icon-image" dactyl:highlight="TabIcon"/>
<xul:image xbl:inherits="validate,src=image" role="presentation"
class="tab-icon-image" dactyl:highlight="TabIcon"/>
<xul:vbox>
<xul:spring flex="1"/>
<xul:label xbl:inherits="value=ordinal" dactyl:highlight="TabIconNumber"/>
@@ -28,10 +31,16 @@
</xul:vbox>
</xul:stack>
<xul:label xbl:inherits="value=ordinal" dactyl:highlight="TabNumber"/>
<xul:label flex="1" xbl:inherits="value=label,crop,accesskey" class="tab-text" dactyl:highlight="TabText"/>
<xul:toolbarbutton anonid="close-button" tabindex="-1" class="tab-close-button" dactyl:highlight="TabClose"/>
<xul:label flex="1"
xbl:inherits="value=label,crop,accesskey"
class="tab-text" dactyl:highlight="TabText"
role="presentation"/>
<xul:toolbarbutton anonid="close-button"
tabindex="-1"
class="tab-close-button" dactyl:highlight="TabClose"/>
</content>
</binding>
<binding id="tab-mac"
extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab">
<content chromedir="ltr" closetabtext="Close Tab">

View File

@@ -435,11 +435,11 @@ const Bookmarks = Module("bookmarks", {
return dactyl.open(items.map(function (i) i.url), dactyl.NEW_TAB);
if (filter.length > 0 && tags.length > 0)
dactyl.echoerr("E283: No bookmarks matching tags: \"" + tags + "\" and string: \"" + filter + "\"");
dactyl.echoerr("E283: No bookmarks matching tags: " + tags.quote() + " and string: " + filter.quote());
else if (filter.length > 0)
dactyl.echoerr("E283: No bookmarks matching string: \"" + filter + "\"");
dactyl.echoerr("E283: No bookmarks matching string: " + filter.quote());
else if (tags.length > 0)
dactyl.echoerr("E283: No bookmarks matching tags: \"" + tags + "\"");
dactyl.echoerr("E283: No bookmarks matching tags: " + tags.quote());
else
dactyl.echoerr("No bookmarks set");
return null;
@@ -497,7 +497,7 @@ const Bookmarks = Module("bookmarks", {
dactyl.echomsg("Added bookmark: " + url + extra, 1, commandline.FORCE_SINGLELINE);
}
else
dactyl.echoerr("Exxx: Could not add bookmark `" + title + "'", commandline.FORCE_SINGLELINE);
dactyl.echoerr("Exxx: Could not add bookmark " + title.quote(), commandline.FORCE_SINGLELINE);
}, {
argCount: "?",
bang: true,

View File

@@ -582,7 +582,7 @@ const Buffer = Module("buffer", {
let offsetX = 1;
let offsetY = 1;
if (isinstance(elem [HTMLFrameElement, HTMLIFrameElement])) {
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement])) {
buffer.focusElement(elem);
return;
}
@@ -1308,39 +1308,55 @@ const Buffer = Module("buffer", {
};
completion.buffer = function buffer(context) {
filter = context.filter.toLowerCase();
context.anchored = false;
context.title = ["Buffer", "URL"];
context.keys = { text: "text", description: "url", icon: "icon" };
context.compare = CompletionContext.Sort.number;
let process = context.process[0];
context.process = [function (item, text)
<>
<span highlight="Indicator" style="display: inline-block; width: 1.5em; text-align: center">{item.item.indicator}</span>
{ process.call(this, item, text) }
</>];
context.completions = util.map(tabs.browsers, function ([i, browser]) {
let indicator = " ";
if (i == tabs.index())
indicator = "%"
else if (i == tabs.index(tabs.alternate))
indicator = "#";
let tab = tabs.getTab(i);
let url = browser.contentDocument.location.href;
i = i + 1;
return {
text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
url: template.highlightURL(url),
indicator: indicator,
icon: tab.image || DEFAULT_FAVICON
};
let filter = context.filter.toLowerCase();
let defItem = { parent: { getTitle: function () "" } };
let tabGroups = {};
tabs.getGroups();
tabs.allTabs.forEach(function (tab, i) {
let group = (tab.tabItem || defItem).parent || defItem.parent;
if (!set.has(tabGroups, group.id))
tabGroups[group.id] = [group.getTitle(), []];
group = tabGroups[group.id];
group[1].push([i, tab.linkedBrowser]);
});
let orig = context;
for (let [id, [name, browsers]] in Iterator(tabGroups)) {
context = orig.fork(id, 0);
context.anchored = false;
context.title = [name || "Buffers"];
context.keys = { text: "text", description: "url", icon: "icon" };
context.compare = CompletionContext.Sort.number;
let process = context.process[0];
context.process = [function (item, text)
<>
<span highlight="Indicator" style="display: inline-block; width: 1.5em; text-align: center">{item.item.indicator}</span>
{ process.call(this, item, text) }
</>];
context.completions = util.map(util.Array.itervalues(browsers), function ([i, browser]) {
let indicator = " ";
if (i == tabs.index())
indicator = "%"
else if (i == tabs.index(tabs.alternate))
indicator = "#";
let tab = tabs.getTab(i);
let url = browser.contentDocument.location.href;
i = i + 1;
return {
text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
url: template.highlightURL(url),
indicator: indicator,
icon: tab.image || DEFAULT_FAVICON
};
});
}
};
},
events: function () {
/*
window.XULBrowserWindow = this.progressListener;
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
@@ -1349,6 +1365,7 @@ const Buffer = Module("buffer", {
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow)
.XULBrowserWindow = this.progressListener;
*/
try {
config.browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
@@ -1592,7 +1609,7 @@ const Buffer = Module("buffer", {
function (count) { buffer.zoomOut(Math.max(count, 1) * 3, true); },
{ count: true });
mappings.add(myModes, ["ZZ", "zZ"],
mappings.add(myModes, ["zZ"],
"Set full zoom value of current web page",
function (count) { buffer.fullZoom = count > 1 ? count : 100; },
{ count: true });

View File

@@ -613,78 +613,83 @@ const CommandLine = Module("commandline", {
* @private
*/
onEvent: function onEvent(event) {
let command = this.command;
try {
let command = this.command;
if (event.type == "blur") {
// prevent losing focus, there should be a better way, but it just didn't work otherwise
this.setTimeout(function () {
if (this._commandShown() && event.originalTarget == this._commandWidget.inputField)
this._commandWidget.inputField.focus();
}, 0);
}
else if (event.type == "focus") {
if (!this._commandShown() && event.target == this._commandWidget.inputField) {
event.target.blur();
dactyl.beep();
if (event.type == "blur") {
// prevent losing focus, there should be a better way, but it just didn't work otherwise
this.setTimeout(function () {
if (this._commandShown() && event.originalTarget == this._commandWidget.inputField)
this._commandWidget.inputField.focus();
}, 0);
}
}
else if (event.type == "input") {
this.resetCompletions();
commandline.triggerCallback("change", this._currentExtendedMode, command);
}
else if (event.type == "keypress") {
let key = events.toString(event);
if (this._completions)
this._completions.previewClear();
if (!this._currentExtendedMode)
return;
// user pressed <Enter> to carry out a command
// user pressing <Esc> is handled in the global onEscape
// FIXME: <Esc> should trigger "cancel" event
if (events.isAcceptKey(key)) {
let mode = this._currentExtendedMode; // save it here, as modes.pop() resets it
this._keepCommand = !userContext.hidden_option_no_command_afterimage;
this._currentExtendedMode = null; // Don't let modes.pop trigger "cancel"
modes.pop(!this._silent);
commandline.triggerCallback("submit", mode, command);
}
// user pressed <Up> or <Down> arrow to cycle this._history completion
else if (/^<(Up|Down|S-Up|S-Down|PageUp|PageDown)>$/.test(key)) {
// prevent tab from moving to the next field
event.preventDefault();
event.stopPropagation();
dactyl.assert(this._history);
this._history.select(/Up/.test(key), !/(Page|S-)/.test(key));
}
// user pressed <Tab> to get completions of a command
else if (/^<(Tab|S-Tab)>$/.test(key)) {
// prevent tab from moving to the next field
event.preventDefault();
event.stopPropagation();
this._tabTimer.tell(event);
}
else if (key == "<BS>") {
// reset the tab completion
//this.resetCompletions();
// and blur the command line if there is no text left
if (command.length == 0) {
commandline.triggerCallback("cancel", this._currentExtendedMode);
modes.pop();
else if (event.type == "focus") {
if (!this._commandShown() && event.target == this._commandWidget.inputField) {
event.target.blur();
dactyl.beep();
}
}
else { // any other key
//this.resetCompletions();
else if (event.type == "input") {
this.resetCompletions();
commandline.triggerCallback("change", this._currentExtendedMode, command);
}
else if (event.type == "keypress") {
let key = events.toString(event);
if (this._completions)
this._completions.previewClear();
if (!this._currentExtendedMode)
return;
// user pressed <Enter> to carry out a command
// user pressing <Esc> is handled in the global onEscape
// FIXME: <Esc> should trigger "cancel" event
if (events.isAcceptKey(key)) {
let mode = this._currentExtendedMode; // save it here, as modes.pop() resets it
this._keepCommand = !userContext.hidden_option_no_command_afterimage;
this._currentExtendedMode = null; // Don't let modes.pop trigger "cancel"
modes.pop(!this._silent);
commandline.triggerCallback("submit", mode, command);
}
// user pressed <Up> or <Down> arrow to cycle this._history completion
else if (/^<(Up|Down|S-Up|S-Down|PageUp|PageDown)>$/.test(key)) {
// prevent tab from moving to the next field
event.preventDefault();
event.stopPropagation();
dactyl.assert(this._history);
this._history.select(/Up/.test(key), !/(Page|S-)/.test(key));
}
// user pressed <Tab> to get completions of a command
else if (/^<(Tab|S-Tab)>$/.test(key)) {
// prevent tab from moving to the next field
event.preventDefault();
event.stopPropagation();
this._tabTimer.tell(event);
}
else if (key == "<BS>") {
// reset the tab completion
//this.resetCompletions();
// and blur the command line if there is no text left
if (command.length == 0) {
commandline.triggerCallback("cancel", this._currentExtendedMode);
modes.pop();
}
}
else { // any other key
//this.resetCompletions();
}
// allow this event to be handled by the host app
}
else if (event.type == "keyup") {
let key = events.toString(event);
if (/^<(Tab|S-Tab)>$/.test(key))
this._tabTimer.flush();
}
// allow this event to be handled by the host app
}
else if (event.type == "keyup") {
let key = events.toString(event);
if (/^<(Tab|S-Tab)>$/.test(key))
this._tabTimer.flush();
catch (e) {
dactyl.reportError(e);
}
},
@@ -1724,6 +1729,8 @@ const ItemList = Class("ItemList", {
// if @param selectedItem is given, show the list and select that item
setItems: function setItems(newItems, selectedItem) {
if (this._selItem > -1)
this._getCompletion(this._selItem).removeAttribute("selected");
if (this._container.collapsed)
this._minHeight = 0;
this._startIndex = this._endIndex = this._selIndex = -1;
@@ -1737,9 +1744,6 @@ const ItemList = Class("ItemList", {
// select index, refill list if necessary
selectItem: function selectItem(index) {
//if (this._container.collapsed) // FIXME
// return;
//let now = Date.now();
if (this._div == null)

View File

@@ -670,8 +670,10 @@ const Completion = Module("completion", {
let list = template.commandOutput(
<div highlight="Completions">
{ template.completionRow(context.title, "CompTitle") }
{ template.map(context.items, function (item) context.createRow(item), null, 100) }
{ template.map(context.contextList.filter(function (c) c.hasItems),
function (context)
template.completionRow(context.title, "CompTitle") +
template.map(context.items, function (item) context.createRow(item), null, 100)) }
</div>);
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
},

View File

@@ -60,7 +60,6 @@
prefix.unshift("chrome://" + modules.Config.prototype.name.toLowerCase() + "/content/");
modules.Config.prototype.scripts.forEach(load);
})();
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -73,11 +73,11 @@ const Dactyl = Module("dactyl", {
},
destroy: function () {
autocommands.trigger(config.name + "LeavePre", {});
autocommands.trigger("LeavePre", {});
storage.saveAll();
dactyl.triggerObserver("shutdown", null);
dactyl.dump("All dactyl modules destroyed\n");
autocommands.trigger(config.name + "Leave", {});
autocommands.trigger("Leave", {});
},
/**
@@ -682,11 +682,12 @@ const Dactyl = Module("dactyl", {
return;
}
dactyl.echomsg('Searching for "plugin/**/*.{js,vimp}" in "'
+ [dir.path.replace(/.plugin$/, "") for ([, dir] in Iterator(dirs))].join(",") + '"', 2);
dactyl.echomsg('Searching for "plugin/**/*.{js,vimp}" in '
+ [dir.path.replace(/.plugin$/, "") for ([, dir] in Iterator(dirs))]
.join(",").quote(), 2);
dirs.forEach(function (dir) {
dactyl.echomsg("Searching for \"" + (dir.path + "/**/*.{js,vimp}") + "\"", 3);
dactyl.echomsg("Searching for " + (dir.path + "/**/*.{js,vimp}").quote(), 3);
sourceDirectory(dir);
});
},
@@ -735,11 +736,6 @@ const Dactyl = Module("dactyl", {
* @returns {boolean}
*/
open: function (urls, params, force) {
// convert the string to an array of converted URLs
// -> see dactyl.stringToURLArray for more details
//
// This is strange. And counterintuitive. Is it really
// necessary? --Kris
if (typeof urls == "string")
urls = dactyl.stringToURLArray(urls);
@@ -867,7 +863,7 @@ const Dactyl = Module("dactyl", {
urls = [str];
return urls.map(function (url) {
if (/^\.?\//.test(url)) {
if (/^[.~]?\//.test(url)) {
try {
// Try to find a matching file.
let file = io.File(url);
@@ -1176,7 +1172,7 @@ const Dactyl = Module("dactyl", {
else
styles.removeSheet(true, "scrollbar");
options.safeSetPref("layout.scrollbar.side", opts.indexOf("l") >= 0 ? 3 : 2,
"See 'guioptions' scrollbar flags.");
"See 'guioptions' scrollbar flags.");
},
validator: function (opts) (opts.indexOf("l") < 0 || opts.indexOf("r") < 0)
},
@@ -1271,7 +1267,7 @@ const Dactyl = Module("dactyl", {
{
setter: function (value) {
options.safeSetPref("accessibility.typeaheadfind.enablesound", !value,
"See 'visualbell' option");
"See 'visualbell' option");
return value;
}
});
@@ -1444,7 +1440,7 @@ const Dactyl = Module("dactyl", {
else if (file.isReadable() && file.isFile())
AddonManager.getInstallForFile(file, callResult("install"), "application/x-xpinstall");
else if (file.isDirectory())
dactyl.echomsg("Cannot install a directory: \"" + file.path + "\"", 0);
dactyl.echomsg("Cannot install a directory: " + file.path.quote(), 0);
else
dactyl.echoerr("E484: Can't open file " + file.path);
}, {
@@ -1552,7 +1548,7 @@ const Dactyl = Module("dactyl", {
}
else {
if (filter)
dactyl.echoerr("Exxx: No extension matching \"" + filter + "\"");
dactyl.echoerr("Exxx: No extension matching " + filter.quote());
else
dactyl.echoerr("No extensions installed");
}
@@ -1953,7 +1949,7 @@ const Dactyl = Module("dactyl", {
});
dactyl.triggerObserver("enter", null);
autocommands.trigger(config.name + "Enter", {});
autocommands.trigger("Enter", {});
}, 0);
statusline.update();

View File

@@ -18,7 +18,7 @@ const RangeFinder = Module("rangefinder", {
let backwards = mode == modes.FIND_BACKWARD;
commandline.open(backwards ? "?" : "/", "", mode);
if (this.rangeFind)
if (this.rangeFind && this.rangeFind.window.get() == window)
this.rangeFind.reset();
this.find("", backwards);
},
@@ -28,6 +28,7 @@ const RangeFinder = Module("rangefinder", {
this.rangeFind = null;
let highlighted = this.rangeFind && this.rangeFind.highlighted;
let selections = this.rangeFind && this.rangeFind.selections;
let matchCase = !(options["ignorecase"] || options["smartcase"] && !/[A-Z]/.test(str));
let linksOnly = options["linksearch"];
@@ -45,15 +46,19 @@ const RangeFinder = Module("rangefinder", {
return "";
});
// It's possible, with :tabdetach, for the rangeFind to actually move
// from one window to another, which breaks things.
if (!this.rangeFind || this.rangeFind.window.get() != window ||
linksOnly ^ !!this.rangeFind.elementPath ||
matchCase ^ this.rangeFind.matchCase || backward ^ this.rangeFind.reverse) {
// It's possible, with :tabdetach for instance, for the rangeFind to
// actually move from one window to another, which breaks things.
if (!this.rangeFind
|| this.rangeFind.window.get() != window
|| linksOnly != !!this.rangeFind.elementPath
|| matchCase != this.rangeFind.matchCase
|| !!backward != this.rangeFind.reverse) {
if (this.rangeFind)
this.rangeFind.cancel();
this.rangeFind = RangeFind(matchCase, backward, linksOnly && options["hinttags"]);
this.rangeFind.highlighted = highlighted;
this.rangeFind.selections = selections;
}
return str;
},
@@ -71,15 +76,15 @@ const RangeFinder = Module("rangefinder", {
this.find(this.lastSearchPattern);
else if (!this.rangeFind.search(null, reverse))
dactyl.echoerr("E486: Pattern not found: " + this.lastSearchPattern);
else if (this.rangeFind.wrapped) {
// hack needed, because wrapping causes a "scroll" event which clears
// our command line
else if (this.rangeFind.wrapped)
// hack needed, because wrapping causes a "scroll" event which
// clears our command line
this.setTimeout(function () {
let msg = this.rangeFind.backward ? "search hit TOP, continuing at BOTTOM"
: "search hit BOTTOM, continuing at TOP";
commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
commandline.echo(msg, commandline.HL_WARNINGMSG,
commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
}, 0);
}
else
commandline.echo((this.rangeFind.backward ? "?" : "/") + this.lastSearchPattern, null, commandline.FORCE_SINGLELINE);
@@ -197,6 +202,10 @@ const RangeFinder = Module("rangefinder", {
modes.addMode("FIND_BACKWARD", true);
},
options: function () {
options.safeSetPref("accessibility.typeaheadfind.autostart", false);
// The above should be sufficient, but: https://bugzilla.mozilla.org/show_bug.cgi?id=348187
options.safeSetPref("accessibility.typeaheadfind", false);
options.add(["hlsearch", "hls"],
"Highlight previous search pattern matches",
"boolean", "false", {
@@ -267,27 +276,31 @@ const RangeFind = Class("RangeFind", {
this.reset();
this.highlighted = null;
this.selections = [];
this.lastString = "";
},
get backward() this.finder.findBackwards,
get searchString() this.lastString,
get selectedRange() {
let range = RangeFind.Range(tabs.localStore.focusedFrame || content);
return (range.selection.rangeCount ? range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
let selection = (tabs.localStore.focusedFrame || content).getSelection();
return (selection.rangeCount ? selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
},
set selectedRange(range) {
this.range.selection.removeAllRanges();
this.range.selection.addRange(range);
this.range.selectionController.scrollSelectionIntoView(
this.range.selectionController.SELECTION_NORMAL, 0, false);
},
reset: function () {
this.startRange = this.selectedRange;
this.startRange.collapse(!this.reverse);
this.lastRange = this.selectedRange;
this.range = this.findRange(this.startRange);
this.ranges.first = this.range;
this.ranges.forEach(function (range) range.save());
this.forward = null;
this.found = false;
cancel: function () {
this.purgeListeners();
this.range.deselect();
this.range.descroll();
},
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
compareRanges: function (r1, r2)
this.backward ? r1.compareBoundaryPoints(Range.END_TO_START, r2)
: -r1.compareBoundaryPoints(Range.START_TO_END, r2),
@@ -296,9 +309,7 @@ const RangeFind = Class("RangeFind", {
let doc = range.startContainer.ownerDocument;
let win = doc.defaultView;
let ranges = this.ranges.filter(function (r)
r.window == win &&
r.range.compareBoundaryPoints(Range.START_TO_END, range) >= 0 &&
r.range.compareBoundaryPoints(Range.END_TO_START, range) <= 0);
r.window == win && RangeFind.contains(r.range, range));
if (this.backward)
return ranges[ranges.length - 1];
@@ -308,10 +319,8 @@ const RangeFind = Class("RangeFind", {
findSubRanges: function (range) {
let doc = range.startContainer.ownerDocument;
for (let elem in util.evaluateXPath(this.elementPath, doc)) {
let r = doc.createRange();
r.selectNode(elem);
if (range.compareBoundaryPoints(Range.START_TO_END, r) >= 0 &&
range.compareBoundaryPoints(Range.END_TO_START, r) <= 0)
let r = RangeFind.nodeRange(elem);
if (RangeFind.contains(range, r))
yield r;
}
},
@@ -322,7 +331,62 @@ const RangeFind = Class("RangeFind", {
this.lastRange.commonAncestorContainer).snapshotItem(0);
if(node) {
node.focus();
this.search(null, false); // Rehighlight collapsed range
// Rehighlight collapsed selection
this.selectedRange = this.lastRange;
}
},
highlight: function (clear) {
if (!clear && (!this.lastString || this.lastString == this.highlighted))
return;
if (clear && !this.highlighted)
return;
if (!clear && this.highlighted)
this.highlight(true);
if (clear) {
this.selections.forEach(function (selection) {
selection.removeAllRanges();
});
this.selections = [];
this.highlighted = null;
}
else {
this.selections = [];
let string = this.lastString;
for (let r in this.iter(string)) {
let controller = this.range.selectionController;
for (let node=r.startContainer; node; node=node.parentNode)
if (node instanceof Ci.nsIDOMNSEditableElement) {
controller = node.editor.selectionController;
break;
}
let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
sel.addRange(r);
if (this.selections.indexOf(sel) < 0)
this.selections.push(sel);
}
this.highlighted = this.lastString;
this.selectedRange = this.lastRange;
this.addListeners();
}
},
iter: function (word) {
let saved = ["range", "lastRange", "lastString"].map(function (s) [s, this[s]], this);
try {
this.range = this.ranges[0];
this.lastRange = null;
this.lastString = word;
var res;
while ((res = this.search(null, this.reverse, true)))
yield res;
}
finally {
saved.forEach(function ([k, v]) this[k] = v, this);
}
},
@@ -334,8 +398,7 @@ const RangeFind = Class("RangeFind", {
function pushRange(start, end) {
function push(r) {
r = RangeFind.Range(r, frames.length);
if (r)
if (r = RangeFind.Range(r, frames.length))
frames.push(r);
}
@@ -351,18 +414,19 @@ const RangeFind = Class("RangeFind", {
}
function rec(win) {
let doc = win.document;
let pageRange = doc.createRange();
pageRange.selectNode(doc.body || doc.documentElement.lastChild);
let pageRange = RangeFind.nodeRange(doc.body || doc.documentElement.lastChild);
backup = backup || pageRange;
let pageStart = RangeFind.endpoint(pageRange, true);
let pageEnd = RangeFind.endpoint(pageRange, false);
for (let frame in util.Array.itervalues(win.frames)) {
let range = doc.createRange();
range.selectNode(frame.frameElement);
pushRange(pageStart, RangeFind.endpoint(range, true));
pageStart = RangeFind.endpoint(range, false);
rec(frame);
if (util.computedStyle(frame.frameElement).visibility == "visible") {
range.selectNode(frame.frameElement);
pushRange(pageStart, RangeFind.endpoint(range, true));
pageStart = RangeFind.endpoint(range, false);
rec(frame);
}
}
pushRange(pageStart, pageEnd);
}
@@ -372,6 +436,17 @@ const RangeFind = Class("RangeFind", {
return frames;
},
reset: function () {
this.startRange = this.selectedRange;
this.startRange.collapse(!this.reverse);
this.lastRange = this.selectedRange;
this.range = this.findRange(this.startRange);
this.ranges.first = this.range;
this.ranges.forEach(function (range) range.save());
this.forward = null;
this.found = false;
},
// This doesn't work yet.
resetCaret: function () {
let equal = RangeFind.equal;
@@ -406,24 +481,6 @@ const RangeFind = Class("RangeFind", {
return null;
},
get searchString() this.lastString,
get backward() this.finder.findBackwards,
iter: function (word) {
let saved = ["range", "lastRange", "lastString"].map(this.closure(function (s) [s, this[s]]));
try {
this.range = this.ranges[0];
this.lastRange = null;
this.lastString = word;
var res;
while ((res = this.search(null, this.reverse, true)))
yield res;
}
finally {
saved.forEach(function ([k, v]) this[k] = v, this);
}
},
search: function (word, reverse, private_) {
if (!private_ && this.lastRange && !RangeFind.equal(this.selectedRange, this.lastRange))
this.reset();
@@ -450,110 +507,57 @@ const RangeFind = Class("RangeFind", {
else {
function indices() {
let idx = this.range.index;
for (let i in this.backward ? util.range(idx + 1, 0, -1) : util.range(idx, this.ranges.length))
yield i;
if (private_)
return;
this.wrapped = true;
this.lastRange = null;
for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx + 1))
if (this.backward)
var groups = [util.range(idx + 1, 0, -1), util.range(this.ranges.length, idx, -1)];
else
var groups = [util.range(idx, this.ranges.length), util.range(0, idx + 1)];
for (let i in groups[0])
yield i;
if (!private_) {
this.wrapped = true;
this.lastRange = null;
for (let i in groups[1])
yield i;
}
}
for (let i in indices.call(this)) {
if (!private_ && this.range.window != this.ranges[i].window && this.range.window != this.ranges[i].window.parent) {
this.range.descroll();
this.range.deselect();
}
this.range = this.ranges[i];
let start = this.sameDocument(this.lastRange, this.range.range) && this.range.intersects(this.lastRange) ?
RangeFind.endpoint(this.lastRange, !(again ^ this.backward)) :
RangeFind.endpoint(this.range.range, !this.backward);;
let start = RangeFind.sameDocument(this.lastRange, this.range.range) && this.range.intersects(this.lastRange) ?
RangeFind.endpoint(this.lastRange, !(again ^ this.backward)) :
RangeFind.endpoint(this.range.range, !this.backward);;
if (this.backward && !again)
start = RangeFind.endpoint(this.startRange, false);
var range = this.finder.Find(word, this.range.range, start, this.range.range);
if (range)
break;
if (!private_) {
this.range.descroll();
this.range.deselect();
}
}
}
if (range)
this.lastRange = range.cloneRange();
if (private_)
return range;
this.lastString = word;
if (range == null) {
this.cancel();
this.found = false;
return null;
if (!private_) {
this.lastString = word;
if (range == null) {
this.cancel();
this.found = false;
return null;
}
this.found = true;
}
this.range.selection.removeAllRanges();
this.range.selection.addRange(range);
this.range.selectionController.scrollSelectionIntoView(
this.range.selectionController.SELECTION_NORMAL, 0, false);
this.found = true;
if (range && (!private_ || private_ < 0))
this.selectedRange = range;
return range;
},
highlight: function (clear) {
if (!clear && (!this.lastString || this.lastString == this.highlighted))
return;
if (!clear && this.highlighted)
this.highlight(true);
if (clear && !this.highlighted)
return;
let span = util.xmlToDom(<span highlight="Search"/>, this.range.document);
function highlight(range) {
let startContainer = range.startContainer;
let startOffset = range.startOffset;
let node = startContainer.ownerDocument.importNode(span, true);
let docfrag = range.extractContents();
let before = startContainer.splitText(startOffset);
let parent = before.parentNode;
node.appendChild(docfrag);
parent.insertBefore(node, before);
range.selectNode(node);
}
function unhighlight(range) {
let elem = range.startContainer;
while (!(elem instanceof Element) && elem.parentNode)
elem = elem.parentNode;
if (elem.getAttributeNS(NS.uri, "highlight") != "Search")
return;
let docfrag = range.extractContents();
let parent = elem.parentNode;
parent.replaceChild(docfrag, elem);
parent.normalize();
}
let action = clear ? unhighlight : highlight;
let string = this[clear ? "highlighted" : "lastString"];
for (let r in this.iter(string)) {
action(r);
this.lastRange = r;
}
if (clear) {
this.highlighted = null;
this.purgeListeners();
}
else {
this.highlighted = this.lastString;
this.addListeners();
this.search(null, false); // Rehighlight collapsed range
}
},
addListeners: function () {
for (let range in values(this.ranges))
range.window.addEventListener("unload", this.closure.onUnload, true);
@@ -562,32 +566,23 @@ const RangeFind = Class("RangeFind", {
for (let range in values(this.ranges))
range.window.removeEventListener("unload", this.closure.onUnload, true);
},
onUnload: function (event) {
this.purgeListeners();
if (this.highlighted)
this.highlight(false);
this.stale = true;
},
cancel: function () {
this.purgeListeners();
this.range.deselect();
this.range.descroll();
}
}, {
Range: Class("RangeFind.Range", {
init: function (range, index) {
if (range instanceof Ci.nsIDOMWindow) { // Kludge
this.document = range.document;
return;
}
this.index = index;
this.document = range.startContainer.ownerDocument;
this.window = this.document.defaultView;
this.range = range;
this.document = range.startContainer.ownerDocument;
this.window = this.document.defaultView;
this.docShell = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (this.selection == null)
return false;
@@ -617,14 +612,6 @@ const RangeFind = Class("RangeFind", {
this.selection.addRange(this.initialSelection);
},
get docShell() {
if (this._docShell)
return this._docShell;
for (let shell in iter(config.browser.docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS)))
if (shell.QueryInterface(nsIWebNavigation).document == this.document)
return this._docShell = shell;
throw Error();
},
get selectionController() this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsISelectionDisplay)
@@ -637,8 +624,9 @@ const RangeFind = Class("RangeFind", {
}}
}),
selectNodePath: ["ancestor-or-self::" + s for ([i, s] in Iterator(
["a", "xhtml:a", "*[@onclick]"]))].join(" | "),
contains: function (range, r)
range.compareBoundaryPoints(Range.START_TO_END, r) >= 0 &&
range.compareBoundaryPoints(Range.END_TO_START, r) <= 0,
endpoint: function (range, before) {
range = range.cloneRange();
range.collapse(before);
@@ -650,7 +638,15 @@ const RangeFind = Class("RangeFind", {
}
catch (e) {}
return false;
}
},
nodeRange: function (node) {
let range = node.ownerDocument.createRange();
range.selectNode(node);
return range;
},
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
selectNodePath: ["a", "xhtml:a", "*[@onclick]"].map(
function (p) "ancestor-or-self::" + p).join(" | ")
});
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -104,7 +104,7 @@ const History = Module("history", {
return dactyl.open(items.map(function (i) i.url), dactyl.NEW_TAB);
if (filter.length > 0)
dactyl.echoerr("E283: No history matching \"" + filter + "\"");
dactyl.echoerr("E283: No history matching " + filter.quote());
else
dactyl.echoerr("No history set");
return null;

View File

@@ -426,7 +426,7 @@ const IO = Module("io", {
let dir = File(newDir);
if (!dir.exists() || !dir.isDirectory()) {
dactyl.echoerr("E344: Can't find directory \"" + dir.path + "\" in path");
dactyl.echoerr("E344: Can't find directory " + dir.path.quote());
return null;
}
@@ -564,14 +564,14 @@ lookup:
let dirs = File.getPathsFromPathList(options["runtimepath"]);
let found = false;
dactyl.echomsg("Searching for \"" + paths.join(" ") + "\" in \"" + options["runtimepath"] + "\"", 2);
dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + options["runtimepath"].quote(), 2);
outer:
for (let [, dir] in Iterator(dirs)) {
for (let [, path] in Iterator(paths)) {
let file = File.joinPaths(dir, path);
dactyl.echomsg("Searching for \"" + file.path + "\"", 3);
dactyl.echomsg("Searching for " + file.path.quote(), 3);
if (file.exists() && file.isFile() && file.isReadable()) {
io.source(file.path, false);
@@ -584,7 +584,7 @@ lookup:
}
if (!found)
dactyl.echomsg("not found in 'runtimepath': \"" + paths.join(" ") + "\"", 1);
dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1);
return found;
},
@@ -609,9 +609,9 @@ lookup:
if (!file.exists() || !file.isReadable() || file.isDirectory()) {
if (!silent) {
if (file.exists() && file.isDirectory())
dactyl.echomsg("Cannot source a directory: \"" + filename + "\"", 0);
dactyl.echomsg("Cannot source a directory: " + filename.quote(), 0);
else
dactyl.echomsg("could not source: \"" + filename + "\"", 1);
dactyl.echomsg("could not source: " + filename.quote(), 1);
dactyl.echoerr("E484: Can't open file " + filename);
}
@@ -619,7 +619,7 @@ lookup:
return;
}
dactyl.echomsg("sourcing \"" + filename + "\"", 2);
dactyl.echomsg("sourcing " + filename.quote(), 2);
let str = file.read();
let uri = services.get("io").newFileURI(file);
@@ -705,7 +705,7 @@ lookup:
if (this._scriptNames.indexOf(file.path) == -1)
this._scriptNames.push(file.path);
dactyl.echomsg("finished sourcing \"" + filename + "\"", 2);
dactyl.echomsg("finished sourcing " + filename.quote(), 2);
dactyl.log("Sourced: " + filename, 3);
}
@@ -880,7 +880,7 @@ lookup:
let file = File(filename);
dactyl.assert(!file.exists() || args.bang,
"E189: \"" + filename + "\" exists (add ! to override)");
"E189: " + filename.quote() + " exists (add ! to override)");
// TODO: Use a set/specifiable list here:
let lines = [cmd.serial().map(commands.commandToString) for (cmd in commands) if (cmd.serial)];
@@ -903,7 +903,7 @@ lookup:
file.write(lines.join("\n"));
}
catch (e) {
dactyl.echoerr("E190: Cannot open \"" + filename + "\" for writing");
dactyl.echoerr("E190: Cannot open " + filename.quote() + " for writing");
dactyl.log("Could not write to " + file.path + ": " + e.message); // XXX
}
}, {

View File

@@ -49,10 +49,8 @@ const JavaScript = Module("javascript", {
for (; obj; obj = !toplevel && obj.__proto__) {
services.get("debugger").wrapValue(obj).getProperties(ret, {});
for (let prop in values(ret.value)) {
let name = '|' + prop.name.stringValue;
if (name in seen)
if (set.add(seen, prop.name.stringValue))
continue;
seen[name] = 1;
if (toplevel || obj !== orig)
yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()]
}
@@ -61,7 +59,7 @@ const JavaScript = Module("javascript", {
// This only lists ENUMERABLE properties.
try {
for (let k in orig)
if (k in orig && !('|' + k in seen)
if (k in orig && !(set.has(seen, k))
&& Object.hasOwnProperty(orig, k) == toplevel)
yield [k, this.getKey(orig, k)]
}
@@ -298,14 +296,18 @@ const JavaScript = Module("javascript", {
continue;
if (dot > stop || dot <= prev)
break;
let s = this._str.substring(prev, dot);
let s = this._str.substring(prev, dot);
if (prev != statement)
s = JavaScript.EVAL_TMP + "." + s;
cacheKey = this._str.substring(statement, dot);
if (this._checkFunction(prev, dot, cacheKey))
return [];
if (prev != statement && obj == null) {
this.context.message = "Error: " + cacheKey.quote() + " is " + String(obj);
return [];
}
prev = dot + 1;
obj = this.eval(s, cacheKey, obj);
@@ -338,7 +340,7 @@ const JavaScript = Module("javascript", {
context.anchored = anchored;
context.filter = key;
context.itemCache = context.parent.itemCache;
context.key = name;
context.key = name + last;
if (last != null)
context.quote = [last, function (text) util.escapeString(text.substr(offset), ""), last];
@@ -435,7 +437,7 @@ const JavaScript = Module("javascript", {
}
this.context.getCache("eval", Object);
this.context.getCache("evalContext", function () ({ __proto__: userContext }));
this.context.getCache("evalContext", function () ({ __proto__: userContext }));;
// Okay, have parse stack. Figure out what we're completing.

View File

@@ -792,10 +792,11 @@ const Options = Module("options", {
*/
// FIXME: Well it used to. I'm looking at you mst! --djk
safeSetPref: function (name, value, message) {
let val = this._loadPreference(name, null, false);
let def = this._loadPreference(name, null, true);
let lib = this._loadPreference(Options.SAVED + name);
if (lib == null && val != def || val != lib) {
let curval = this._loadPreference(name, null, false);
let defval = this._loadPreference(name, null, true);
let saved = this._loadPreference(Options.SAVED + name);
if (saved == null && curval != defval || curval != saved) {
let msg = "Warning: setting preference " + name + ", but it's changed from its default value.";
if (message)
msg += " " + message;
@@ -952,14 +953,6 @@ const Options = Module("options", {
SAVED: "extensions.dactyl.saved.",
OLD_SAVED: "dactyl.saved."
}, {
commandline: function () {
// TODO: maybe reset in .destroy()?
// TODO: move to buffer.js
// we have our own typeahead find implementation
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=348187
options.safeSetPref("accessibility.typeaheadfind.autostart", false);
options.safeSetPref("accessibility.typeaheadfind", false); // actually the above setting should do it, but has no effect in Firefox
},
commands: function () {
function setAction(args, modifiers) {
let bang = args.bang;

View File

@@ -89,7 +89,7 @@ const QuickMarks = Module("quickmarks", {
if (filter.length > 0) {
marks = marks.filter(function (qmark) filter.indexOf(qmark) >= 0);
dactyl.assert(marks.length >= 0, "E283: No QuickMarks matching \"" + filter + "\"");
dactyl.assert(marks.length >= 0, "E283: No QuickMarks matching " + filter.quote());
}
let items = [[mark, this._qmarks.get(mark)] for ([k, mark] in Iterator(marks))];
@@ -138,7 +138,7 @@ const QuickMarks = Module("quickmarks", {
args = args.string;
// ignore invalid qmark characters unless there are no valid qmark chars
dactyl.assert(!args || /[a-zA-Z0-9]/.test(args), "E283: No QuickMarks matching \"" + args + "\"");
dactyl.assert(!args || /[a-zA-Z0-9]/.test(args), "E283: No QuickMarks matching " + args.quote());
let filter = args.replace(/[^a-zA-Z0-9]/g, "");
quickmarks.list(filter);

View File

@@ -7,7 +7,7 @@
// TODO:
// - fix Sanitize autocommand
// - add warning for TIMESPAN_EVERYTHING?
// - respect privacy.clearOnShutdown et al or recommend PentadactylLeave autocommand?
// - respect privacy.clearOnShutdown et al or recommend Leave autocommand?
// - add support for :set sanitizeitems=all like 'eventignore'?
// - integrate with the Clear Private Data dialog?

View File

@@ -22,7 +22,7 @@ const Services = Module("services", {
this.add("browserSearch", "@mozilla.org/browser/search-service;1", Ci.nsIBrowserSearchService);
this.add("cache", "@mozilla.org/network/cache-service;1", Ci.nsICacheService);
this.add("console", "@mozilla.org/consoleservice;1", Ci.nsIConsoleService);
this.add("dactyl:", "@mozilla.org/network/protocol;1?name=dactyl");
this.add("dactyl:", "@mozilla.org/network/protocol;1?name=dactyl");
this.add("debugger", "@mozilla.org/js/jsd/debugger-service;1", Ci.jsdIDebuggerService);
this.add("directory", "@mozilla.org/file/directory_service;1", Ci.nsIProperties);
this.add("downloadManager", "@mozilla.org/download-manager;1", Ci.nsIDownloadManager);

View File

@@ -188,10 +188,10 @@ const StatusLine = Module("statusline", {
// update the ordinal which is used for numbered tabs
if (options.get("guioptions").has("n", "N"))
for (let [i, tab] in util.Array.iteritems(config.browser.mTabs))
for (let [i, tab] in Iterator(tabs.visibleTabs))
tab.setAttribute("ordinal", i + 1);
this.widgets.tabcount.value = "[" + (tabs.index() + 1) + "/" + tabs.count + "]";
this.widgets.tabcount.value = "[" + (tabs.index(null, true) + 1) + "/" + tabs.visibleTabs.length + "]";
}
},

View File

@@ -43,6 +43,8 @@ const Tabs = Module("tabs", {
setTimeout(function () { dactyl.focusContent(true); }, 10); // just make sure, that no widget has focus
},
get allTabs() Array.slice(config.tabbrowser.tabContainer.childNodes),
/**
* @property {Object} The previously accessed tab or null if no tab
* other than the current one has been accessed.
@@ -59,6 +61,21 @@ const Tabs = Module("tabs", {
yield [i, browsers[i]];
},
/**
* @property {number} The number of tabs in the current window.
*/
get count() config.tabbrowser.mTabs.length,
/**
* @property {Object} The local options store for the current tab.
*/
get options() {
let store = this.localStore;
if (!("options" in store))
store.options = {};
return store.options;
},
/**
* @property {boolean} Whether the tab numbering XBL binding has been
* applied.
@@ -75,20 +92,7 @@ const Tabs = Module("tabs", {
".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() config.tabbrowser.mTabs.length,
/**
* @property {Object} The local options store for the current tab.
*/
get options() {
let store = this.localStore;
if (!("options" in store))
store.options = {};
return store.options;
},
get visibleTabs() config.tabbrowser.visibleTabs || this.allTabs.filter(function (tab) !tab.hidden),
/**
* Returns the local state store for the tab at the specified
@@ -123,37 +127,33 @@ const Tabs = Module("tabs", {
get closedTabs() 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.
* Clones the specified <b>tab</b> and append it to the tab list.
*
* @param {Object} tab A tab from the current tab list.
* @returns {number}
* @param {Object} tab The tab to clone.
* @param {boolean} activate Whether to select the newly cloned tab.
*/
index: function (tab) {
if (tab)
return Array.indexOf(config.tabbrowser.mTabs, tab);
else
return config.tabbrowser.mTabContainer.selectedIndex;
cloneTab: function (tab, activate) {
let newTab = config.tabbrowser.addTab();
Tabs.copyTab(newTab, tab);
if (activate)
config.tabbrowser.mTabContainer.selectedItem = newTab;
return newTab;
},
// TODO: implement filter
/**
* Returns an array of all tabs in the tab list.
* Detaches the specified <b>tab</b> and open it in a new window. If no
* tab is specified the currently selected tab is detached.
*
* @returns {Object[]}
* @param {Object} tab The tab to detach.
*/
// FIXME: why not return the tab element?
// : unused? Remove me.
get: function () {
let buffers = [];
for (let [i, browser] in this.browsers) {
let title = browser.contentTitle || "(Untitled)";
let uri = browser.currentURI.spec;
let number = i + 1;
buffers.push([number, title, uri]);
}
return buffers;
detachTab: function (tab) {
if (!tab)
tab = config.tabbrowser.mTabContainer.selectedItem;
services.get("windowWatcher")
.openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab);
},
/**
@@ -171,6 +171,26 @@ const Tabs = Module("tabs", {
return -1;
},
/**
* If TabView exists, returns the Panorama window. If the Panorama
* is has not yet initialized, this function will not return until
* it has.
*
* @returns {Window}
*/
getGroups: function () {
if ("_groups" in this)
return this._groups;
if (window.TabView && TabView._initFrame)
TabView._initFrame();
let iframe = document.getElementById("tab-view");
this._groups = this._groups = iframe ? iframe.contentWindow : null;
if (this._groups)
while (!this._groups.TabItems)
dactyl.threadYield(false, true);
return this._groups;
},
/**
* 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
@@ -180,12 +200,72 @@ const Tabs = Module("tabs", {
* @returns {Object}
*/
getTab: function (index) {
if (index != undefined)
if (index != null)
return config.tabbrowser.mTabs[index];
else
return config.tabbrowser.mCurrentTab;
},
/**
* 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 {<xul:tab/>} tab A tab from the current tab list.
* @param {boolean} visible Whether to consider only visible tabs.
* @returns {number}
*/
index: function (tab, visible) {
let tabs = this[visible ? "visibleTabs" : "allTabs"];
return tabs.indexOf(tab || config.tabbrowser.mCurrentTab);
},
/**
* @param spec can either be:
* - an absolute integer
* - "" for the current tab
* - "+1" for the next tab
* - "-3" for the tab, which is 3 positions left of the current
* - "$" for the last tab
*/
indexFromSpec: function (spec, wrap) {
if (spec instanceof Node)
return this.allTabs.indexOf(spec);
let tabs = this.visibleTabs;
let position = this.index(null, true);
if (spec == null || spec === "")
return position;
if (typeof spec === "number")
position = spec;
else if (spec === "$")
position = tabs.length - 1;
else if (/^[+-]\d+$/.test(spec))
position += parseInt(spec, 10);
else if (/^\d+$/.test(spec))
position = parseInt(spec, 10);
else
return -1;
if (position >= tabs.length)
position = wrap ? position % tabs.length : tabs.length - 1;
else if (position < 0)
position = wrap ? (position % tabs.length) + tabs.length : 0;
return this.allTabs.indexOf(tabs[position]);
},
/**
* Removes all tabs from the tab list except the specified <b>tab</b>.
*
* @param {Object} tab The tab to keep.
*/
keepOnly: function (tab) {
config.tabbrowser.removeAllTabsBut(tab);
},
/**
* Lists all tabs matching <b>filter</b>.
*
@@ -206,7 +286,7 @@ const Tabs = Module("tabs", {
* list.
*/
move: function (tab, spec, wrap) {
let index = Tabs.indexFromSpec(spec, wrap);
let index = tabs.indexFromSpec(spec, wrap);
config.tabbrowser.moveTabTo(tab, index);
},
@@ -223,96 +303,31 @@ const Tabs = Module("tabs", {
*/
// FIXME: what is quitOnLastTab {1,2} all about then, eh? --djk
remove: function (tab, count, focusLeftTab, quitOnLastTab) {
let removeOrBlankTab = {
Firefox: function (tab) {
if (config.tabbrowser.mTabs.length > 1)
config.tabbrowser.removeTab(tab);
else {
if (buffer.URL != "about:blank" ||
window.getWebNavigation().sessionHistory.count > 0) {
dactyl.open("about:blank", dactyl.NEW_BACKGROUND_TAB);
config.tabbrowser.removeTab(tab);
}
else
dactyl.beep();
}
},
Thunderbird: function (tab) {
if (config.tabbrowser.mTabs.length > 1)
config.tabbrowser.removeTab(tab);
else
dactyl.beep();
},
Songbird: function (tab) {
if (config.tabbrowser.mTabs.length > 1)
config.tabbrowser.removeTab(tab);
else {
if (buffer.URL != "about:blank" || window.getWebNavigation().sessionHistory.count > 0) {
dactyl.open("about:blank", dactyl.NEW_BACKGROUND_TAB);
config.tabbrowser.removeTab(tab);
}
else
dactyl.beep();
}
}
}[config.hostApplication] || function () {};
count = Math.max(count, 1);
if (typeof count != "number" || count < 1)
count = 1;
if (quitOnLastTab >= 1 && config.tabbrowser.mTabs.length <= count) {
if (quitOnLastTab >= 1 && this.count <= count) {
if (dactyl.windows.length > 1)
window.close();
else
dactyl.quit(quitOnLastTab == 2);
return;
}
let index = this.index(tab);
if (focusLeftTab) {
let lastRemovedTab = 0;
for (let i = index; i > index - count && i >= 0; i--) {
removeOrBlankTab(this.getTab(i));
lastRemovedTab = i > 0 ? i : 1;
}
config.tabbrowser.mTabContainer.selectedIndex = lastRemovedTab - 1;
}
else {
let i = index + count - 1;
if (i >= this.count)
i = this.count - 1;
let tabs = this.visibleTabs
if (tabs.indexOf(tab) < 0)
tabs = this.allTabs;
let index = tabs.indexOf(tab);
for (; i >= index; i--)
removeOrBlankTab(this.getTab(i));
config.tabbrowser.mTabContainer.selectedIndex = index;
}
},
let next = index + (focusLeftTab ? -count : count);
if (!(next in tabs))
next = index + (focusLeftTab ? 1 : -1);
if (next in tabs)
config.tabbrowser.mTabContainer.selectedItem = tabs[next];
/**
* Removes all tabs from the tab list except the specified <b>tab</b>.
*
* @param {Object} tab The tab to keep.
*/
keepOnly: function (tab) {
config.tabbrowser.removeAllTabsBut(tab);
},
/**
* Selects the tab at the position specified by <b>spec</b>.
*
* @param {string} spec See {@link Tabs.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) {
let index = Tabs.indexFromSpec(spec, wrap);
// FIXME:
if (index == -1)
dactyl.beep();
if (focusLeftTab)
tabs.slice(Math.max(0, index+1 - count), index+1).forEach(config.removeTab);
else
config.tabbrowser.mTabContainer.selectedIndex = index;
tabs.slice(index, index + count).forEach(config.removeTab);
},
/**
@@ -353,6 +368,40 @@ const Tabs = Module("tabs", {
config.tabbrowser.reloadAllTabs();
},
/**
* Selects the tab at the position specified by <b>spec</b>.
*
* @param {string} spec See {@link Tabs.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) {
let index = tabs.indexFromSpec(spec, wrap);
if (index == -1)
dactyl.beep();
else
config.tabbrowser.mTabContainer.selectedIndex = index;
},
/**
* Selects the alternate tab.
*/
selectAlternateTab: function () {
dactyl.assert(tabs.alternate != null && tabs.getTab() != tabs.alternate,
"E23: No alternate page");
// NOTE: this currently relies on v.tabs.index() returning the
// currently selected tab index when passed null
let index = tabs.index(tabs.alternate);
// TODO: since a tab close is more like a bdelete for us we
// should probably reopen the closed tab when a 'deleted'
// alternate is selected
dactyl.assert(index >= 0, "E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
tabs.select(index);
},
/**
* Stops loading the specified tab.
*
@@ -398,7 +447,7 @@ const Tabs = Module("tabs", {
}
else {
buffer = this._lastBufferSwitchArgs;
if (allowNonUnique === undefined || allowNonUnique == null) // XXX
if (allowNonUnique == null) // XXX
allowNonUnique = this._lastBufferSwitchSpecial;
}
@@ -407,14 +456,12 @@ const Tabs = Module("tabs", {
return;
}
if (!count || count < 1)
count = 1;
if (typeof reverse != "boolean")
reverse = false;
count = Math.max(1, count || 1);
reverse = Boolean(reverse);
let matches = buffer.match(/^(\d+):?/);
if (matches) {
tabs.select(parseInt(matches[1], 10) - 1, false); // make it zero-based
tabs.select(this.allTabs[parseInt(matches[1], 10) - 1], false);
return;
}
@@ -424,8 +471,9 @@ const Tabs = Module("tabs", {
let nbrowsers = config.tabbrowser.browsers.length;
for (let [i, ] in tabs.browsers) {
let index = (i + first) % nbrowsers;
let url = config.tabbrowser.getBrowserAtIndex(index).contentDocument.location.href;
let title = config.tabbrowser.getBrowserAtIndex(index).contentDocument.title.toLowerCase();
let browser = config.tabbrowser.getBrowserAtIndex(index);
let url = browser.contentDocument.location.href;
let title = browser.contentDocument.title.toLowerCase();
if (url == buffer) {
tabs.select(index, false);
return;
@@ -434,71 +482,21 @@ const Tabs = Module("tabs", {
if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
matches.push(index);
}
if (matches.length == 0)
dactyl.echoerr("E94: No matching buffer for " + buffer);
else if (matches.length > 1 && !allowNonUnique)
dactyl.echoerr("E93: More than one match for " + buffer);
else {
if (reverse) {
let index = (count - 1) % matches.length;
if (reverse)
index = matches.length - count;
while (index < 0)
index += matches.length;
}
else
index = (count - 1) % matches.length;
tabs.select(matches[index], false);
index = Array.indexOf(config.tabbrowser.browsers, matches[index]);
tabs.select(index, false);
}
},
/**
* 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) {
let newTab = config.tabbrowser.addTab();
Tabs.copyTab(newTab, tab);
if (activate)
config.tabbrowser.mTabContainer.selectedItem = 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) {
if (!tab)
tab = config.tabbrowser.mTabContainer.selectedItem;
services.get("windowWatcher")
.openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab);
},
/**
* Selects the alternate tab.
*/
selectAlternateTab: function () {
dactyl.assert(tabs.alternate != null && tabs.getTab() != tabs.alternate,
"E23: No alternate page");
// NOTE: this currently relies on v.tabs.index() returning the
// currently selected tab index when passed null
let index = tabs.index(tabs.alternate);
// TODO: since a tab close is more like a bdelete for us we
// should probably reopen the closed tab when a 'deleted'
// alternate is selected
dactyl.assert(index >= 0, "E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
tabs.select(index);
},
// NOTE: when restarting a session FF selects the first tab and then 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
@@ -520,41 +518,6 @@ const Tabs = Module("tabs", {
let tabState = services.get("sessionStore").getTabState(from);
services.get("sessionStore").setTabState(to, tabState);
},
/**
* @param spec can either be:
* - an absolute integer
* - "" for the current tab
* - "+1" for the next tab
* - "-3" for the tab, which is 3 positions left of the current
* - "$" for the last tab
*/
indexFromSpec: function (spec, wrap) {
let position = config.tabbrowser.mTabContainer.selectedIndex;
let length = config.tabbrowser.mTabs.length;
let last = length - 1;
if (spec === undefined || spec === "")
return position;
if (typeof spec === "number")
position = spec;
else if (spec === "$")
position = last;
else if (/^[+-]\d+$/.test(spec))
position += parseInt(spec, 10);
else if (/^\d+$/.test(spec))
position = parseInt(spec, 10);
else
return -1;
if (position > last)
position = wrap ? position % length : last;
else if (position < 0)
position = wrap ? (position % length) + length : 0;
return position;
}
}, {
commands: function () {
@@ -1037,7 +1000,7 @@ const Tabs = Module("tabs", {
let pref = "browser.tabStrip.autoHide";
if (options.getPref(pref) == null) // Try for FF 3.0 & 3.1
pref = "browser.tabs.autoHide";
options.safeSetPref(pref, value == 1);
options.safeSetPref(pref, value == 1, "See 'showtabline' option.");
tabStrip.collapsed = false;
}
@@ -1098,8 +1061,10 @@ const Tabs = Module("tabs", {
restriction = 2;
}
options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option.");
options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option.");
options.safeSetPref("browser.link.open_newwindow", open,
"See 'popups' option.");
options.safeSetPref("browser.link.open_newwindow.restriction", restriction,
"See 'popups' option.");
return values;
},
completer: function (context) [

View File

@@ -452,7 +452,8 @@ const Util = Module("util", {
let result = doc.evaluate(expression, elem,
function lookupNamespaceURI(prefix) {
return {
xhtml: "http://www.w3.org/1999/xhtml",
xul: XUL.uri,
xhtml: XHTML.uri,
xhtml2: "http://www.w3.org/2002/06/xhtml2",
dactyl: NS.uri
}[prefix] || null;

View File

@@ -25,14 +25,20 @@
<p>Search forward for the first occurrence of <a>pattern</a>.</p>
<p>
If <str>\c</str> appears anywhere in the pattern the whole pattern is handled as though
<o>ignorecase</o> is on. <str>\C</str> forces case-sensitive matching for the whole pattern.
</p>
<p>
If <str>\l</str> appears in the pattern only the text of links is searched for a
match as though <o>linksearch</o> is on. <str>\L</str> forces the entire page to be searched
for a match.
The following escape sequences can be used to modify the
behavior of the search. When flags conflict, the last to
appear is the one that takes effect.
</p>
<dl>
<dt>\c</dt> <dd>Perform case insensitive search (default
if <o>ignorecase</o> is set).</dd>
<dt>\C</dt> <dd>Perform case sensitive search</dd>
<dt>\l</dt> <dd>Search only in links, as defined by
<o>hinttags</o>. (default if <o>linksearch</o> is
set).</dd>
<dt>\L</dt> <dd>Do not search only in links.</dd>
</dl>
</description>
</item>
@@ -54,7 +60,7 @@
<tags>n</tags>
<spec>n</spec>
<description>
<p>Find next. Repeat the last search 1 time (until count is supported).</p>
<p>Find next. Repeat the last search.</p>
</description>
</item>
@@ -63,10 +69,7 @@
<tags>N</tags>
<spec>N</spec>
<description>
<p>
Find previous. Repeat the last search 1 time (until count is supported) in the
opposite direction.
</p>
<p>Find previous. Repeat the last search in the opposite direction.</p>
</description>
</item>