1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-22 08:27:58 +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); 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 lastPattern = null;
let url = args.url || ""; let url = args.url || "";
@@ -127,7 +127,7 @@ const AutoCommands = Module("autocommands", {
for (let [, autoCmd] in Iterator(autoCmds)) { for (let [, autoCmd] in Iterator(autoCmds)) {
if (autoCmd.pattern.test(url)) { if (autoCmd.pattern.test(url)) {
if (!lastPattern || lastPattern.source != autoCmd.pattern.source) 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; lastPattern = autoCmd.pattern;
dactyl.echomsg("autocommand " + autoCmd.command, 9); dactyl.echomsg("autocommand " + autoCmd.command, 9);
@@ -167,7 +167,7 @@ const AutoCommands = Module("autocommands", {
if (event) { if (event) {
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}| // 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("*"); validEvents.push("*");
events = event.split(","); events = event.split(",");
@@ -227,7 +227,7 @@ const AutoCommands = Module("autocommands", {
let [event, url] = args; let [event, url] = args;
let defaultURL = url || buffer.URL; let defaultURL = url || buffer.URL;
let validEvents = config.autocommands.map(function (e) e[0]); let validEvents = keys(config.autocommands);
// TODO: add command validators // TODO: add command validators
dactyl.assert(event != "*", dactyl.assert(event != "*",
@@ -257,10 +257,10 @@ const AutoCommands = Module("autocommands", {
}); });
}, },
completion: function () { completion: function () {
JavaScript.setCompleter(this.get, [function () config.autocommands]); JavaScript.setCompleter(this.get, [function () Iterator(config.autocommands)]);
completion.autocmdEvent = function autocmdEvent(context) { completion.autocmdEvent = function autocmdEvent(context) {
context.completions = config.autocommands; context.completions = Iterator(config.autocommands);
}; };
completion.macro = function macro(context) { completion.macro = function macro(context) {
@@ -273,7 +273,7 @@ const AutoCommands = Module("autocommands", {
"List of autocommand event names which should be ignored", "List of autocommand event names which should be ignored",
"stringlist", "", "stringlist", "",
{ {
completer: function () config.autocommands.concat([["all", "All events"]]) completer: function () Iterator(update({ all: "All Events" }, config.autocommands))
}); });
options.add(["strictfocus", "sf"], options.add(["strictfocus", "sf"],

View File

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

View File

@@ -12,15 +12,18 @@
<children/> <children/>
</content> </content>
</binding> </binding>
<binding id="compitem-td"> <binding id="compitem-td">
<!-- No white space. The table is white-space: pre; :( --> <!-- No white space. The table is white-space: pre; :( -->
<content><html:span class="td-strut"/><html:span class="td-span"><children/></html:span></content> <content><html:span class="td-strut"/><html:span class="td-span"><children/></html:span></content>
</binding> </binding>
<binding id="tab" display="xul:hbox" <binding id="tab" display="xul:hbox"
extends="chrome://global/content/bindings/tabbox.xml#tab"> extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab">
<content chromedir="ltr" closetabtext="Close Tab"> <content context="tabContextMenu" closetabtext="Close Tab">
<xul:stack class="tab-icon dactyl-tab-stack"> <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:vbox>
<xul:spring flex="1"/> <xul:spring flex="1"/>
<xul:label xbl:inherits="value=ordinal" dactyl:highlight="TabIconNumber"/> <xul:label xbl:inherits="value=ordinal" dactyl:highlight="TabIconNumber"/>
@@ -28,10 +31,16 @@
</xul:vbox> </xul:vbox>
</xul:stack> </xul:stack>
<xul:label xbl:inherits="value=ordinal" dactyl:highlight="TabNumber"/> <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:label flex="1"
<xul:toolbarbutton anonid="close-button" tabindex="-1" class="tab-close-button" dactyl:highlight="TabClose"/> 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> </content>
</binding> </binding>
<binding id="tab-mac" <binding id="tab-mac"
extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab"> extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab">
<content chromedir="ltr" closetabtext="Close 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); return dactyl.open(items.map(function (i) i.url), dactyl.NEW_TAB);
if (filter.length > 0 && tags.length > 0) 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) 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) else if (tags.length > 0)
dactyl.echoerr("E283: No bookmarks matching tags: \"" + tags + "\""); dactyl.echoerr("E283: No bookmarks matching tags: " + tags.quote());
else else
dactyl.echoerr("No bookmarks set"); dactyl.echoerr("No bookmarks set");
return null; return null;
@@ -497,7 +497,7 @@ const Bookmarks = Module("bookmarks", {
dactyl.echomsg("Added bookmark: " + url + extra, 1, commandline.FORCE_SINGLELINE); dactyl.echomsg("Added bookmark: " + url + extra, 1, commandline.FORCE_SINGLELINE);
} }
else 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: "?", argCount: "?",
bang: true, bang: true,

View File

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

View File

@@ -613,78 +613,83 @@ const CommandLine = Module("commandline", {
* @private * @private
*/ */
onEvent: function onEvent(event) { onEvent: function onEvent(event) {
let command = this.command; try {
let command = this.command;
if (event.type == "blur") { if (event.type == "blur") {
// prevent losing focus, there should be a better way, but it just didn't work otherwise // prevent losing focus, there should be a better way, but it just didn't work otherwise
this.setTimeout(function () { this.setTimeout(function () {
if (this._commandShown() && event.originalTarget == this._commandWidget.inputField) if (this._commandShown() && event.originalTarget == this._commandWidget.inputField)
this._commandWidget.inputField.focus(); this._commandWidget.inputField.focus();
}, 0); }, 0);
}
else if (event.type == "focus") {
if (!this._commandShown() && event.target == this._commandWidget.inputField) {
event.target.blur();
dactyl.beep();
} }
} else if (event.type == "focus") {
else if (event.type == "input") { if (!this._commandShown() && event.target == this._commandWidget.inputField) {
this.resetCompletions(); event.target.blur();
commandline.triggerCallback("change", this._currentExtendedMode, command); dactyl.beep();
}
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 else if (event.type == "input") {
//this.resetCompletions(); 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") { catch (e) {
let key = events.toString(event); dactyl.reportError(e);
if (/^<(Tab|S-Tab)>$/.test(key))
this._tabTimer.flush();
} }
}, },
@@ -1724,6 +1729,8 @@ const ItemList = Class("ItemList", {
// if @param selectedItem is given, show the list and select that item // if @param selectedItem is given, show the list and select that item
setItems: function setItems(newItems, selectedItem) { setItems: function setItems(newItems, selectedItem) {
if (this._selItem > -1)
this._getCompletion(this._selItem).removeAttribute("selected");
if (this._container.collapsed) if (this._container.collapsed)
this._minHeight = 0; this._minHeight = 0;
this._startIndex = this._endIndex = this._selIndex = -1; this._startIndex = this._endIndex = this._selIndex = -1;
@@ -1737,9 +1744,6 @@ const ItemList = Class("ItemList", {
// select index, refill list if necessary // select index, refill list if necessary
selectItem: function selectItem(index) { selectItem: function selectItem(index) {
//if (this._container.collapsed) // FIXME
// return;
//let now = Date.now(); //let now = Date.now();
if (this._div == null) if (this._div == null)

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ const RangeFinder = Module("rangefinder", {
let backwards = mode == modes.FIND_BACKWARD; let backwards = mode == modes.FIND_BACKWARD;
commandline.open(backwards ? "?" : "/", "", mode); commandline.open(backwards ? "?" : "/", "", mode);
if (this.rangeFind) if (this.rangeFind && this.rangeFind.window.get() == window)
this.rangeFind.reset(); this.rangeFind.reset();
this.find("", backwards); this.find("", backwards);
}, },
@@ -28,6 +28,7 @@ const RangeFinder = Module("rangefinder", {
this.rangeFind = null; this.rangeFind = null;
let highlighted = this.rangeFind && this.rangeFind.highlighted; 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 matchCase = !(options["ignorecase"] || options["smartcase"] && !/[A-Z]/.test(str));
let linksOnly = options["linksearch"]; let linksOnly = options["linksearch"];
@@ -45,15 +46,19 @@ const RangeFinder = Module("rangefinder", {
return ""; return "";
}); });
// It's possible, with :tabdetach, for the rangeFind to actually move // It's possible, with :tabdetach for instance, for the rangeFind to
// from one window to another, which breaks things. // actually move from one window to another, which breaks things.
if (!this.rangeFind || this.rangeFind.window.get() != window || if (!this.rangeFind
linksOnly ^ !!this.rangeFind.elementPath || || this.rangeFind.window.get() != window
matchCase ^ this.rangeFind.matchCase || backward ^ this.rangeFind.reverse) { || linksOnly != !!this.rangeFind.elementPath
|| matchCase != this.rangeFind.matchCase
|| !!backward != this.rangeFind.reverse) {
if (this.rangeFind) if (this.rangeFind)
this.rangeFind.cancel(); this.rangeFind.cancel();
this.rangeFind = RangeFind(matchCase, backward, linksOnly && options["hinttags"]); this.rangeFind = RangeFind(matchCase, backward, linksOnly && options["hinttags"]);
this.rangeFind.highlighted = highlighted; this.rangeFind.highlighted = highlighted;
this.rangeFind.selections = selections;
} }
return str; return str;
}, },
@@ -71,15 +76,15 @@ const RangeFinder = Module("rangefinder", {
this.find(this.lastSearchPattern); this.find(this.lastSearchPattern);
else if (!this.rangeFind.search(null, reverse)) else if (!this.rangeFind.search(null, reverse))
dactyl.echoerr("E486: Pattern not found: " + this.lastSearchPattern); dactyl.echoerr("E486: Pattern not found: " + this.lastSearchPattern);
else if (this.rangeFind.wrapped) { else if (this.rangeFind.wrapped)
// hack needed, because wrapping causes a "scroll" event which clears // hack needed, because wrapping causes a "scroll" event which
// our command line // clears our command line
this.setTimeout(function () { this.setTimeout(function () {
let msg = this.rangeFind.backward ? "search hit TOP, continuing at BOTTOM" let msg = this.rangeFind.backward ? "search hit TOP, continuing at BOTTOM"
: "search hit BOTTOM, continuing at TOP"; : "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); }, 0);
}
else else
commandline.echo((this.rangeFind.backward ? "?" : "/") + this.lastSearchPattern, null, commandline.FORCE_SINGLELINE); commandline.echo((this.rangeFind.backward ? "?" : "/") + this.lastSearchPattern, null, commandline.FORCE_SINGLELINE);
@@ -197,6 +202,10 @@ const RangeFinder = Module("rangefinder", {
modes.addMode("FIND_BACKWARD", true); modes.addMode("FIND_BACKWARD", true);
}, },
options: function () { 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"], options.add(["hlsearch", "hls"],
"Highlight previous search pattern matches", "Highlight previous search pattern matches",
"boolean", "false", { "boolean", "false", {
@@ -267,27 +276,31 @@ const RangeFind = Class("RangeFind", {
this.reset(); this.reset();
this.highlighted = null; this.highlighted = null;
this.selections = [];
this.lastString = ""; this.lastString = "";
}, },
get backward() this.finder.findBackwards,
get searchString() this.lastString,
get selectedRange() { get selectedRange() {
let range = RangeFind.Range(tabs.localStore.focusedFrame || content); let selection = (tabs.localStore.focusedFrame || content).getSelection();
return (range.selection.rangeCount ? range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange(); 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 () { cancel: function () {
this.startRange = this.selectedRange; this.purgeListeners();
this.startRange.collapse(!this.reverse); this.range.deselect();
this.lastRange = this.selectedRange; this.range.descroll();
this.range = this.findRange(this.startRange);
this.ranges.first = this.range;
this.ranges.forEach(function (range) range.save());
this.forward = null;
this.found = false;
}, },
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
compareRanges: function (r1, r2) compareRanges: function (r1, r2)
this.backward ? r1.compareBoundaryPoints(Range.END_TO_START, r2) this.backward ? r1.compareBoundaryPoints(Range.END_TO_START, r2)
: -r1.compareBoundaryPoints(Range.START_TO_END, r2), : -r1.compareBoundaryPoints(Range.START_TO_END, r2),
@@ -296,9 +309,7 @@ const RangeFind = Class("RangeFind", {
let doc = range.startContainer.ownerDocument; let doc = range.startContainer.ownerDocument;
let win = doc.defaultView; let win = doc.defaultView;
let ranges = this.ranges.filter(function (r) let ranges = this.ranges.filter(function (r)
r.window == win && r.window == win && RangeFind.contains(r.range, range));
r.range.compareBoundaryPoints(Range.START_TO_END, range) >= 0 &&
r.range.compareBoundaryPoints(Range.END_TO_START, range) <= 0);
if (this.backward) if (this.backward)
return ranges[ranges.length - 1]; return ranges[ranges.length - 1];
@@ -308,10 +319,8 @@ const RangeFind = Class("RangeFind", {
findSubRanges: function (range) { findSubRanges: function (range) {
let doc = range.startContainer.ownerDocument; let doc = range.startContainer.ownerDocument;
for (let elem in util.evaluateXPath(this.elementPath, doc)) { for (let elem in util.evaluateXPath(this.elementPath, doc)) {
let r = doc.createRange(); let r = RangeFind.nodeRange(elem);
r.selectNode(elem); if (RangeFind.contains(range, r))
if (range.compareBoundaryPoints(Range.START_TO_END, r) >= 0 &&
range.compareBoundaryPoints(Range.END_TO_START, r) <= 0)
yield r; yield r;
} }
}, },
@@ -322,7 +331,62 @@ const RangeFind = Class("RangeFind", {
this.lastRange.commonAncestorContainer).snapshotItem(0); this.lastRange.commonAncestorContainer).snapshotItem(0);
if(node) { if(node) {
node.focus(); 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 pushRange(start, end) {
function push(r) { function push(r) {
r = RangeFind.Range(r, frames.length); if (r = RangeFind.Range(r, frames.length))
if (r)
frames.push(r); frames.push(r);
} }
@@ -351,18 +414,19 @@ const RangeFind = Class("RangeFind", {
} }
function rec(win) { function rec(win) {
let doc = win.document; let doc = win.document;
let pageRange = doc.createRange(); let pageRange = RangeFind.nodeRange(doc.body || doc.documentElement.lastChild);
pageRange.selectNode(doc.body || doc.documentElement.lastChild);
backup = backup || pageRange; backup = backup || pageRange;
let pageStart = RangeFind.endpoint(pageRange, true); let pageStart = RangeFind.endpoint(pageRange, true);
let pageEnd = RangeFind.endpoint(pageRange, false); let pageEnd = RangeFind.endpoint(pageRange, false);
for (let frame in util.Array.itervalues(win.frames)) { for (let frame in util.Array.itervalues(win.frames)) {
let range = doc.createRange(); let range = doc.createRange();
range.selectNode(frame.frameElement); if (util.computedStyle(frame.frameElement).visibility == "visible") {
pushRange(pageStart, RangeFind.endpoint(range, true)); range.selectNode(frame.frameElement);
pageStart = RangeFind.endpoint(range, false); pushRange(pageStart, RangeFind.endpoint(range, true));
rec(frame); pageStart = RangeFind.endpoint(range, false);
rec(frame);
}
} }
pushRange(pageStart, pageEnd); pushRange(pageStart, pageEnd);
} }
@@ -372,6 +436,17 @@ const RangeFind = Class("RangeFind", {
return frames; 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. // This doesn't work yet.
resetCaret: function () { resetCaret: function () {
let equal = RangeFind.equal; let equal = RangeFind.equal;
@@ -406,24 +481,6 @@ const RangeFind = Class("RangeFind", {
return null; 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_) { search: function (word, reverse, private_) {
if (!private_ && this.lastRange && !RangeFind.equal(this.selectedRange, this.lastRange)) if (!private_ && this.lastRange && !RangeFind.equal(this.selectedRange, this.lastRange))
this.reset(); this.reset();
@@ -450,110 +507,57 @@ const RangeFind = Class("RangeFind", {
else { else {
function indices() { function indices() {
let idx = this.range.index; let idx = this.range.index;
for (let i in this.backward ? util.range(idx + 1, 0, -1) : util.range(idx, this.ranges.length)) if (this.backward)
yield i; var groups = [util.range(idx + 1, 0, -1), util.range(this.ranges.length, idx, -1)];
if (private_) else
return; var groups = [util.range(idx, this.ranges.length), util.range(0, idx + 1)];
this.wrapped = true;
this.lastRange = null; for (let i in groups[0])
for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx + 1))
yield i; 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)) { 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]; this.range = this.ranges[i];
let start = this.sameDocument(this.lastRange, this.range.range) && this.range.intersects(this.lastRange) ? let start = RangeFind.sameDocument(this.lastRange, this.range.range) && this.range.intersects(this.lastRange) ?
RangeFind.endpoint(this.lastRange, !(again ^ this.backward)) : RangeFind.endpoint(this.lastRange, !(again ^ this.backward)) :
RangeFind.endpoint(this.range.range, !this.backward);; RangeFind.endpoint(this.range.range, !this.backward);;
if (this.backward && !again) if (this.backward && !again)
start = RangeFind.endpoint(this.startRange, false); start = RangeFind.endpoint(this.startRange, false);
var range = this.finder.Find(word, this.range.range, start, this.range.range); var range = this.finder.Find(word, this.range.range, start, this.range.range);
if (range) if (range)
break; break;
if (!private_) {
this.range.descroll();
this.range.deselect();
}
} }
} }
if (range) if (range)
this.lastRange = range.cloneRange(); this.lastRange = range.cloneRange();
if (private_) if (!private_) {
return range; this.lastString = word;
if (range == null) {
this.lastString = word; this.cancel();
if (range == null) { this.found = false;
this.cancel(); return null;
this.found = false; }
return null; this.found = true;
} }
this.range.selection.removeAllRanges(); if (range && (!private_ || private_ < 0))
this.range.selection.addRange(range); this.selectedRange = range;
this.range.selectionController.scrollSelectionIntoView(
this.range.selectionController.SELECTION_NORMAL, 0, false);
this.found = true;
return 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 () { addListeners: function () {
for (let range in values(this.ranges)) for (let range in values(this.ranges))
range.window.addEventListener("unload", this.closure.onUnload, true); range.window.addEventListener("unload", this.closure.onUnload, true);
@@ -562,32 +566,23 @@ const RangeFind = Class("RangeFind", {
for (let range in values(this.ranges)) for (let range in values(this.ranges))
range.window.removeEventListener("unload", this.closure.onUnload, true); range.window.removeEventListener("unload", this.closure.onUnload, true);
}, },
onUnload: function (event) { onUnload: function (event) {
this.purgeListeners(); this.purgeListeners();
if (this.highlighted) if (this.highlighted)
this.highlight(false); this.highlight(false);
this.stale = true; this.stale = true;
},
cancel: function () {
this.purgeListeners();
this.range.deselect();
this.range.descroll();
} }
}, { }, {
Range: Class("RangeFind.Range", { Range: Class("RangeFind.Range", {
init: function (range, index) { init: function (range, index) {
if (range instanceof Ci.nsIDOMWindow) { // Kludge
this.document = range.document;
return;
}
this.index = index; this.index = index;
this.document = range.startContainer.ownerDocument;
this.window = this.document.defaultView;
this.range = range; 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) if (this.selection == null)
return false; return false;
@@ -617,14 +612,6 @@ const RangeFind = Class("RangeFind", {
this.selection.addRange(this.initialSelection); 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 get selectionController() this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor) .QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsISelectionDisplay) .getInterface(Ci.nsISelectionDisplay)
@@ -637,8 +624,9 @@ const RangeFind = Class("RangeFind", {
}} }}
}), }),
selectNodePath: ["ancestor-or-self::" + s for ([i, s] in Iterator( contains: function (range, r)
["a", "xhtml:a", "*[@onclick]"]))].join(" | "), range.compareBoundaryPoints(Range.START_TO_END, r) >= 0 &&
range.compareBoundaryPoints(Range.END_TO_START, r) <= 0,
endpoint: function (range, before) { endpoint: function (range, before) {
range = range.cloneRange(); range = range.cloneRange();
range.collapse(before); range.collapse(before);
@@ -650,7 +638,15 @@ const RangeFind = Class("RangeFind", {
} }
catch (e) {} catch (e) {}
return false; 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: // 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); return dactyl.open(items.map(function (i) i.url), dactyl.NEW_TAB);
if (filter.length > 0) if (filter.length > 0)
dactyl.echoerr("E283: No history matching \"" + filter + "\""); dactyl.echoerr("E283: No history matching " + filter.quote());
else else
dactyl.echoerr("No history set"); dactyl.echoerr("No history set");
return null; return null;

View File

@@ -426,7 +426,7 @@ const IO = Module("io", {
let dir = File(newDir); let dir = File(newDir);
if (!dir.exists() || !dir.isDirectory()) { 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; return null;
} }
@@ -564,14 +564,14 @@ lookup:
let dirs = File.getPathsFromPathList(options["runtimepath"]); let dirs = File.getPathsFromPathList(options["runtimepath"]);
let found = false; 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: outer:
for (let [, dir] in Iterator(dirs)) { for (let [, dir] in Iterator(dirs)) {
for (let [, path] in Iterator(paths)) { for (let [, path] in Iterator(paths)) {
let file = File.joinPaths(dir, path); 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()) { if (file.exists() && file.isFile() && file.isReadable()) {
io.source(file.path, false); io.source(file.path, false);
@@ -584,7 +584,7 @@ lookup:
} }
if (!found) if (!found)
dactyl.echomsg("not found in 'runtimepath': \"" + paths.join(" ") + "\"", 1); dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1);
return found; return found;
}, },
@@ -609,9 +609,9 @@ lookup:
if (!file.exists() || !file.isReadable() || file.isDirectory()) { if (!file.exists() || !file.isReadable() || file.isDirectory()) {
if (!silent) { if (!silent) {
if (file.exists() && file.isDirectory()) if (file.exists() && file.isDirectory())
dactyl.echomsg("Cannot source a directory: \"" + filename + "\"", 0); dactyl.echomsg("Cannot source a directory: " + filename.quote(), 0);
else else
dactyl.echomsg("could not source: \"" + filename + "\"", 1); dactyl.echomsg("could not source: " + filename.quote(), 1);
dactyl.echoerr("E484: Can't open file " + filename); dactyl.echoerr("E484: Can't open file " + filename);
} }
@@ -619,7 +619,7 @@ lookup:
return; return;
} }
dactyl.echomsg("sourcing \"" + filename + "\"", 2); dactyl.echomsg("sourcing " + filename.quote(), 2);
let str = file.read(); let str = file.read();
let uri = services.get("io").newFileURI(file); let uri = services.get("io").newFileURI(file);
@@ -705,7 +705,7 @@ lookup:
if (this._scriptNames.indexOf(file.path) == -1) if (this._scriptNames.indexOf(file.path) == -1)
this._scriptNames.push(file.path); this._scriptNames.push(file.path);
dactyl.echomsg("finished sourcing \"" + filename + "\"", 2); dactyl.echomsg("finished sourcing " + filename.quote(), 2);
dactyl.log("Sourced: " + filename, 3); dactyl.log("Sourced: " + filename, 3);
} }
@@ -880,7 +880,7 @@ lookup:
let file = File(filename); let file = File(filename);
dactyl.assert(!file.exists() || args.bang, 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: // TODO: Use a set/specifiable list here:
let lines = [cmd.serial().map(commands.commandToString) for (cmd in commands) if (cmd.serial)]; let lines = [cmd.serial().map(commands.commandToString) for (cmd in commands) if (cmd.serial)];
@@ -903,7 +903,7 @@ lookup:
file.write(lines.join("\n")); file.write(lines.join("\n"));
} }
catch (e) { 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 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__) { for (; obj; obj = !toplevel && obj.__proto__) {
services.get("debugger").wrapValue(obj).getProperties(ret, {}); services.get("debugger").wrapValue(obj).getProperties(ret, {});
for (let prop in values(ret.value)) { for (let prop in values(ret.value)) {
let name = '|' + prop.name.stringValue; if (set.add(seen, prop.name.stringValue))
if (name in seen)
continue; continue;
seen[name] = 1;
if (toplevel || obj !== orig) if (toplevel || obj !== orig)
yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()] yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()]
} }
@@ -61,7 +59,7 @@ const JavaScript = Module("javascript", {
// This only lists ENUMERABLE properties. // This only lists ENUMERABLE properties.
try { try {
for (let k in orig) 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) && Object.hasOwnProperty(orig, k) == toplevel)
yield [k, this.getKey(orig, k)] yield [k, this.getKey(orig, k)]
} }
@@ -298,14 +296,18 @@ const JavaScript = Module("javascript", {
continue; continue;
if (dot > stop || dot <= prev) if (dot > stop || dot <= prev)
break; break;
let s = this._str.substring(prev, dot);
let s = this._str.substring(prev, dot);
if (prev != statement) if (prev != statement)
s = JavaScript.EVAL_TMP + "." + s; s = JavaScript.EVAL_TMP + "." + s;
cacheKey = this._str.substring(statement, dot); cacheKey = this._str.substring(statement, dot);
if (this._checkFunction(prev, dot, cacheKey)) if (this._checkFunction(prev, dot, cacheKey))
return []; return [];
if (prev != statement && obj == null) {
this.context.message = "Error: " + cacheKey.quote() + " is " + String(obj);
return [];
}
prev = dot + 1; prev = dot + 1;
obj = this.eval(s, cacheKey, obj); obj = this.eval(s, cacheKey, obj);
@@ -338,7 +340,7 @@ const JavaScript = Module("javascript", {
context.anchored = anchored; context.anchored = anchored;
context.filter = key; context.filter = key;
context.itemCache = context.parent.itemCache; context.itemCache = context.parent.itemCache;
context.key = name; context.key = name + last;
if (last != null) if (last != null)
context.quote = [last, function (text) util.escapeString(text.substr(offset), ""), last]; 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("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. // 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 // FIXME: Well it used to. I'm looking at you mst! --djk
safeSetPref: function (name, value, message) { safeSetPref: function (name, value, message) {
let val = this._loadPreference(name, null, false); let curval = this._loadPreference(name, null, false);
let def = this._loadPreference(name, null, true); let defval = this._loadPreference(name, null, true);
let lib = this._loadPreference(Options.SAVED + name); let saved = this._loadPreference(Options.SAVED + name);
if (lib == null && val != def || val != lib) {
if (saved == null && curval != defval || curval != saved) {
let msg = "Warning: setting preference " + name + ", but it's changed from its default value."; let msg = "Warning: setting preference " + name + ", but it's changed from its default value.";
if (message) if (message)
msg += " " + message; msg += " " + message;
@@ -952,14 +953,6 @@ const Options = Module("options", {
SAVED: "extensions.dactyl.saved.", SAVED: "extensions.dactyl.saved.",
OLD_SAVED: "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 () { commands: function () {
function setAction(args, modifiers) { function setAction(args, modifiers) {
let bang = args.bang; let bang = args.bang;

View File

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

View File

@@ -7,7 +7,7 @@
// TODO: // TODO:
// - fix Sanitize autocommand // - fix Sanitize autocommand
// - add warning for TIMESPAN_EVERYTHING? // - 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'? // - add support for :set sanitizeitems=all like 'eventignore'?
// - integrate with the Clear Private Data dialog? // - 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("browserSearch", "@mozilla.org/browser/search-service;1", Ci.nsIBrowserSearchService);
this.add("cache", "@mozilla.org/network/cache-service;1", Ci.nsICacheService); this.add("cache", "@mozilla.org/network/cache-service;1", Ci.nsICacheService);
this.add("console", "@mozilla.org/consoleservice;1", Ci.nsIConsoleService); 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("debugger", "@mozilla.org/js/jsd/debugger-service;1", Ci.jsdIDebuggerService);
this.add("directory", "@mozilla.org/file/directory_service;1", Ci.nsIProperties); this.add("directory", "@mozilla.org/file/directory_service;1", Ci.nsIProperties);
this.add("downloadManager", "@mozilla.org/download-manager;1", Ci.nsIDownloadManager); 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 // update the ordinal which is used for numbered tabs
if (options.get("guioptions").has("n", "N")) 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); 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 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 * @property {Object} The previously accessed tab or null if no tab
* other than the current one has been accessed. * other than the current one has been accessed.
@@ -59,6 +61,21 @@ const Tabs = Module("tabs", {
yield [i, browsers[i]]; 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 * @property {boolean} Whether the tab numbering XBL binding has been
* applied. * 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; }"); ".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }");
}, },
/** get visibleTabs() config.tabbrowser.visibleTabs || this.allTabs.filter(function (tab) !tab.hidden),
* @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;
},
/** /**
* Returns the local state store for the tab at the specified * 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)), get closedTabs() services.get("json").decode(services.get("sessionStore").getClosedTabData(window)),
/** /**
* Returns the index of <b>tab</b> or the index of the currently * Clones the specified <b>tab</b> and append it to the tab list.
* 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. * @param {Object} tab The tab to clone.
* @returns {number} * @param {boolean} activate Whether to select the newly cloned tab.
*/ */
index: function (tab) { cloneTab: function (tab, activate) {
if (tab) let newTab = config.tabbrowser.addTab();
return Array.indexOf(config.tabbrowser.mTabs, tab); Tabs.copyTab(newTab, tab);
else
return config.tabbrowser.mTabContainer.selectedIndex; 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? detachTab: function (tab) {
// : unused? Remove me. if (!tab)
get: function () { tab = config.tabbrowser.mTabContainer.selectedItem;
let buffers = [];
for (let [i, browser] in this.browsers) { services.get("windowWatcher")
let title = browser.contentTitle || "(Untitled)"; .openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab);
let uri = browser.currentURI.spec;
let number = i + 1;
buffers.push([number, title, uri]);
}
return buffers;
}, },
/** /**
@@ -171,6 +171,26 @@ const Tabs = Module("tabs", {
return -1; 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 * 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 * selected tab if <b>index</b> is not specified. This is a 0-based
@@ -180,12 +200,72 @@ const Tabs = Module("tabs", {
* @returns {Object} * @returns {Object}
*/ */
getTab: function (index) { getTab: function (index) {
if (index != undefined) if (index != null)
return config.tabbrowser.mTabs[index]; return config.tabbrowser.mTabs[index];
else else
return config.tabbrowser.mCurrentTab; 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>. * Lists all tabs matching <b>filter</b>.
* *
@@ -206,7 +286,7 @@ const Tabs = Module("tabs", {
* list. * list.
*/ */
move: function (tab, spec, wrap) { move: function (tab, spec, wrap) {
let index = Tabs.indexFromSpec(spec, wrap); let index = tabs.indexFromSpec(spec, wrap);
config.tabbrowser.moveTabTo(tab, index); config.tabbrowser.moveTabTo(tab, index);
}, },
@@ -223,96 +303,31 @@ const Tabs = Module("tabs", {
*/ */
// FIXME: what is quitOnLastTab {1,2} all about then, eh? --djk // 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 = { count = Math.max(count, 1);
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 () {};
if (typeof count != "number" || count < 1) if (quitOnLastTab >= 1 && this.count <= count) {
count = 1;
if (quitOnLastTab >= 1 && config.tabbrowser.mTabs.length <= count) {
if (dactyl.windows.length > 1) if (dactyl.windows.length > 1)
window.close(); window.close();
else else
dactyl.quit(quitOnLastTab == 2); dactyl.quit(quitOnLastTab == 2);
return; return;
} }
let index = this.index(tab); let tabs = this.visibleTabs
if (focusLeftTab) { if (tabs.indexOf(tab) < 0)
let lastRemovedTab = 0; tabs = this.allTabs;
for (let i = index; i > index - count && i >= 0; i--) { let index = tabs.indexOf(tab);
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;
for (; i >= index; i--) let next = index + (focusLeftTab ? -count : count);
removeOrBlankTab(this.getTab(i)); if (!(next in tabs))
config.tabbrowser.mTabContainer.selectedIndex = index; next = index + (focusLeftTab ? 1 : -1);
} if (next in tabs)
}, config.tabbrowser.mTabContainer.selectedItem = tabs[next];
/** if (focusLeftTab)
* Removes all tabs from the tab list except the specified <b>tab</b>. tabs.slice(Math.max(0, index+1 - count), index+1).forEach(config.removeTab);
*
* @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();
else 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(); 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. * Stops loading the specified tab.
* *
@@ -398,7 +447,7 @@ const Tabs = Module("tabs", {
} }
else { else {
buffer = this._lastBufferSwitchArgs; buffer = this._lastBufferSwitchArgs;
if (allowNonUnique === undefined || allowNonUnique == null) // XXX if (allowNonUnique == null) // XXX
allowNonUnique = this._lastBufferSwitchSpecial; allowNonUnique = this._lastBufferSwitchSpecial;
} }
@@ -407,14 +456,12 @@ const Tabs = Module("tabs", {
return; return;
} }
if (!count || count < 1) count = Math.max(1, count || 1);
count = 1; reverse = Boolean(reverse);
if (typeof reverse != "boolean")
reverse = false;
let matches = buffer.match(/^(\d+):?/); let matches = buffer.match(/^(\d+):?/);
if (matches) { 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; return;
} }
@@ -424,8 +471,9 @@ const Tabs = Module("tabs", {
let nbrowsers = config.tabbrowser.browsers.length; let nbrowsers = config.tabbrowser.browsers.length;
for (let [i, ] in tabs.browsers) { for (let [i, ] in tabs.browsers) {
let index = (i + first) % nbrowsers; let index = (i + first) % nbrowsers;
let url = config.tabbrowser.getBrowserAtIndex(index).contentDocument.location.href; let browser = config.tabbrowser.getBrowserAtIndex(index);
let title = config.tabbrowser.getBrowserAtIndex(index).contentDocument.title.toLowerCase(); let url = browser.contentDocument.location.href;
let title = browser.contentDocument.title.toLowerCase();
if (url == buffer) { if (url == buffer) {
tabs.select(index, false); tabs.select(index, false);
return; return;
@@ -434,71 +482,21 @@ const Tabs = Module("tabs", {
if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0) if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
matches.push(index); matches.push(index);
} }
if (matches.length == 0) if (matches.length == 0)
dactyl.echoerr("E94: No matching buffer for " + buffer); dactyl.echoerr("E94: No matching buffer for " + buffer);
else if (matches.length > 1 && !allowNonUnique) else if (matches.length > 1 && !allowNonUnique)
dactyl.echoerr("E93: More than one match for " + buffer); dactyl.echoerr("E93: More than one match for " + buffer);
else { else {
if (reverse) { let index = (count - 1) % matches.length;
if (reverse)
index = matches.length - count; 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 // 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 // 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
@@ -520,41 +518,6 @@ const Tabs = Module("tabs", {
let tabState = services.get("sessionStore").getTabState(from); let tabState = services.get("sessionStore").getTabState(from);
services.get("sessionStore").setTabState(to, tabState); 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 () { commands: function () {
@@ -1037,7 +1000,7 @@ const Tabs = Module("tabs", {
let pref = "browser.tabStrip.autoHide"; let pref = "browser.tabStrip.autoHide";
if (options.getPref(pref) == null) // Try for FF 3.0 & 3.1 if (options.getPref(pref) == null) // Try for FF 3.0 & 3.1
pref = "browser.tabs.autoHide"; pref = "browser.tabs.autoHide";
options.safeSetPref(pref, value == 1); options.safeSetPref(pref, value == 1, "See 'showtabline' option.");
tabStrip.collapsed = false; tabStrip.collapsed = false;
} }
@@ -1098,8 +1061,10 @@ const Tabs = Module("tabs", {
restriction = 2; restriction = 2;
} }
options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option."); options.safeSetPref("browser.link.open_newwindow", open,
options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option."); "See 'popups' option.");
options.safeSetPref("browser.link.open_newwindow.restriction", restriction,
"See 'popups' option.");
return values; return values;
}, },
completer: function (context) [ completer: function (context) [

View File

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

View File

@@ -25,14 +25,20 @@
<p>Search forward for the first occurrence of <a>pattern</a>.</p> <p>Search forward for the first occurrence of <a>pattern</a>.</p>
<p> <p>
If <str>\c</str> appears anywhere in the pattern the whole pattern is handled as though The following escape sequences can be used to modify the
<o>ignorecase</o> is on. <str>\C</str> forces case-sensitive matching for the whole pattern. behavior of the search. When flags conflict, the last to
</p> appear is the one that takes effect.
<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.
</p> </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> </description>
</item> </item>
@@ -54,7 +60,7 @@
<tags>n</tags> <tags>n</tags>
<spec>n</spec> <spec>n</spec>
<description> <description>
<p>Find next. Repeat the last search 1 time (until count is supported).</p> <p>Find next. Repeat the last search.</p>
</description> </description>
</item> </item>
@@ -63,10 +69,7 @@
<tags>N</tags> <tags>N</tags>
<spec>N</spec> <spec>N</spec>
<description> <description>
<p> <p>Find previous. Repeat the last search in the opposite direction.</p>
Find previous. Repeat the last search 1 time (until count is supported) in the
opposite direction.
</p>
</description> </description>
</item> </item>

View File

@@ -14,83 +14,53 @@ const Config = Module("config", ConfigBase, {
hostApplication: "Thunderbird", // TODO: can this be found out otherwise? gBrandBundle.getString("brandShortName"); hostApplication: "Thunderbird", // TODO: can this be found out otherwise? gBrandBundle.getString("brandShortName");
// Yes, but it will be localized unlike all other strings. So, it's best left until we i18n dactyl. --djk // Yes, but it will be localized unlike all other strings. So, it's best left until we i18n dactyl. --djk
get mainWindowId() this.isComposeWindow ? "msgcomposeWindow" : "messengerWindow", autocommands: {
DOMLoad: "Triggered when a page's DOM content has fully loaded",
FolderLoad: "Triggered after switching folders in Thunderbird",
PageLoadPre: "Triggered after a page load is initiated",
PageLoad: "Triggered when a page gets (re)loaded/opened",
Enter: "Triggered after Thunderbird starts",
Leave: "Triggered before exiting Thunderbird",
LeavePre: "Triggered before exiting Thunderbird",
},
get browser() getBrowser(),
dialogs: {
about: ["About Thunderbird",
function () { window.openAboutDialog(); }],
addons: ["Manage Add-ons",
function () { window.openAddonsMgr(); }],
addressbook: ["Address book",
function () { window.toAddressBook(); }],
checkupdates: ["Check for updates",
function () { window.checkForUpdates(); }],
console: ["JavaScript console",
function () { window.toJavaScriptConsole(); }],
dominspector: ["DOM Inspector",
function () { window.inspectDOMDocument(content.document); }],
downloads: ["Manage Downloads",
function () { window.toOpenWindowByType('Download:Manager', 'chrome://mozapps/content/downloads/downloads.xul', 'chrome,dialog=no,resizable'); }],
preferences: ["Show Thunderbird preferences dialog",
function () { openOptionsDialog(); }],
printsetup: ["Setup the page size and orientation before printing",
function () { PrintUtils.showPageSetup(); }],
print: ["Show print dialog",
function () { PrintUtils.print(); }],
saveframe: ["Save frame to disk",
function () { window.saveFrameDocument(); }],
savepage: ["Save page to disk",
function () { window.saveDocument(window.content.document); }],
},
/*** optional options, there are checked for existence and a fallback provided ***/
features: ["hints", "mail", "marks", "addressbook", "tabs"],
defaults: { defaults: {
guioptions: "frb", guioptions: "frb",
showtabline: 1, showtabline: 1,
titlestring: "Muttator" titlestring: "Muttator"
}, },
guioptions: { /*** optional options, there are checked for existence and a fallback provided ***/
m: ["MenuBar", ["mail-toolbar-menubar2"]], features: ["hints", "mail", "marks", "addressbook", "tabs"],
T: ["Toolbar" , ["mail-bar2"]],
f: ["Folder list", ["folderPaneBox", "folderpane_splitter"]],
F: ["Folder list header", ["folderPaneHeader"]]
},
get isComposeWindow() window.wintype == "msgcompose",
get browserModes() [modes.MESSAGE],
get mailModes() [modes.NORMAL],
// focusContent() focuses this widget
get mainWidget() this.isComposeWindow ? document.getElementById("content-frame") : GetThreadTree(),
get visualbellWindow() document.getElementById(this.mainWindowId),
styleableChrome: ["chrome://messenger/content/messenger.xul",
"chrome://messenger/content/messengercompose/messengercompose.xul"],
autocommands: [["DOMLoad", "Triggered when a page's DOM content has fully loaded"],
["FolderLoad", "Triggered after switching folders in Thunderbird"],
["PageLoadPre", "Triggered after a page load is initiated"],
["PageLoad", "Triggered when a page gets (re)loaded/opened"],
["MuttatorEnter", "Triggered after Thunderbird starts"],
["MuttatorLeave", "Triggered before exiting Thunderbird"],
["MuttatorLeavePre", "Triggered before exiting Thunderbird"]],
dialogs: [
["about", "About Thunderbird",
function () { window.openAboutDialog(); }],
["addons", "Manage Add-ons",
function () { window.openAddonsMgr(); }],
["addressbook", "Address book",
function () { window.toAddressBook(); }],
["checkupdates", "Check for updates",
function () { window.checkForUpdates(); }],
/*["cleardata", "Clear private data",
function () { Cc[GLUE_CID].getService(Ci.nsIBrowserGlue).sanitize(window || null); }],*/
["console", "JavaScript console",
function () { window.toJavaScriptConsole(); }],
/*["customizetoolbar", "Customize the Toolbar",
function () { BrowserCustomizeToolbar(); }],*/
["dominspector", "DOM Inspector",
function () { window.inspectDOMDocument(content.document); }],
["downloads", "Manage Downloads",
function () { window.toOpenWindowByType('Download:Manager', 'chrome://mozapps/content/downloads/downloads.xul', 'chrome,dialog=no,resizable'); }],
/*["import", "Import Preferences, Bookmarks, History, etc. from other browsers",
function () { BrowserImport(); }],
["openfile", "Open the file selector dialog",
function () { BrowserOpenFileWindow(); }],
["pageinfo", "Show information about the current page",
function () { BrowserPageInfo(); }],
["pagesource", "View page source",
function () { BrowserViewSourceOfDocument(content.document); }],*/
["preferences", "Show Thunderbird preferences dialog",
function () { openOptionsDialog(); }],
/*["printpreview", "Preview the page before printing",
function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],*/
["printsetup", "Setup the page size and orientation before printing",
function () { PrintUtils.showPageSetup(); }],
["print", "Show print dialog",
function () { PrintUtils.print(); }],
["saveframe", "Save frame to disk",
function () { window.saveFrameDocument(); }],
["savepage", "Save page to disk",
function () { window.saveDocument(window.content.document); }],
/*["searchengines", "Manage installed search engines",
function () { openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
["selectionsource", "View selection source",
function () { buffer.viewSelectionSource(); }]*/
],
focusChange: function (win) { focusChange: function (win) {
// we switch to -- MESSAGE -- mode for Muttator, when the main HTML widget gets focus // we switch to -- MESSAGE -- mode for Muttator, when the main HTML widget gets focus
@@ -102,23 +72,28 @@ const Config = Module("config", ConfigBase, {
} }
}, },
get browser() getBrowser(), guioptions: {
tabbrowser: { m: ["MenuBar", ["mail-toolbar-menubar2"]],
__proto__: document.getElementById("tabmail"), T: ["Toolbar" , ["mail-bar2"]],
get mTabContainer() this.tabContainer, f: ["Folder list", ["folderPaneBox", "folderpane_splitter"]],
get mTabs() this.tabContainer.childNodes, F: ["Folder list header", ["folderPaneHeader"]]
get mCurrentTab() this.tabContainer.selectedItem,
get mStrip() this.tabStrip,
get browsers() [browser for (browser in Iterator(this.mTabs))]
}, },
// they are sorted by relevance, not alphabetically // they are sorted by relevance, not alphabetically
helpFiles: ["intro.html", "version.html"], helpFiles: ["intro.html", "version.html"],
get isComposeWindow() window.wintype == "msgcompose",
get mainWidget() this.isComposeWindow ? document.getElementById("content-frame") : GetThreadTree(),
get mainWindowId() this.isComposeWindow ? "msgcomposeWindow" : "messengerWindow",
modes: [ modes: [
["MESSAGE", { char: "m" }], ["MESSAGE", { char: "m" }],
["COMPOSE"] ["COMPOSE"]
], ],
get browserModes() [modes.MESSAGE],
get mailModes() [modes.NORMAL],
// NOTE: as I don't use TB I have no idea how robust this is. --djk // NOTE: as I don't use TB I have no idea how robust this is. --djk
get outputHeight() { get outputHeight() {
@@ -137,15 +112,35 @@ const Config = Module("config", ConfigBase, {
return document.getElementById("appcontent").boxObject.height; return document.getElementById("appcontent").boxObject.height;
}, },
removeTab: function (tab) {
if (config.tabbrowser.mTabs.length > 1)
config.tabbrowser.removeTab(tab);
else
dactyl.beep();
},
get scripts() this.isComposeWindow ? ["compose/compose.js"] : [ get scripts() this.isComposeWindow ? ["compose/compose.js"] : [
"addressbook.js", "addressbook.js",
"mail.js", "mail.js",
"tabs.js", "tabs.js",
], ],
styleableChrome: ["chrome://messenger/content/messenger.xul",
"chrome://messenger/content/messengercompose/messengercompose.xul"],
tabbrowser: {
__proto__: document.getElementById("tabmail"),
get mTabContainer() this.tabContainer,
get mTabs() this.tabContainer.childNodes,
get mCurrentTab() this.tabContainer.selectedItem,
get mStrip() this.tabStrip,
get browsers() [browser for (browser in Iterator(this.mTabs))]
},
// to allow Vim to :set ft=mail automatically // to allow Vim to :set ft=mail automatically
tempFile: "mutt-ator-mail", tempFile: "mutt-ator-mail",
get visualbellWindow() document.getElementById(this.mainWindowId),
}, { }, {
}, { }, {
commands: function () { commands: function () {

View File

@@ -1,9 +1,11 @@
2009-XX-XX: 2009-XX-XX:
* Use only visible tabs for tab numbering, gt/gn/gN, etc.
* Group tabs in :buffer completions by panorama groups
* Replaced 'focuscontent' with 'strictfocus' * Replaced 'focuscontent' with 'strictfocus'
* Replaced previous incremental search implementation * Replaced previous incremental search implementation
* gf now toggles between source and content view. * gf now toggles between source and content view.
'|' key binding has been removed. '|' key binding has been removed.
* :open now only opens files begining with / or ./ * :open now only opens files begining with /, ./, or ~/
* Page zoom information is now shown in the status bar * Page zoom information is now shown in the status bar
* Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR * Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR
* Add basic plugin authorship documentation * Add basic plugin authorship documentation

View File

@@ -13,8 +13,26 @@ const Config = Module("config", ConfigBase, {
name: "Pentadactyl", name: "Pentadactyl",
hostApplication: "Firefox", hostApplication: "Firefox",
/*** optional options, there are checked for existence and a fallback provided ***/ get visualbellWindow() getBrowser().mPanelContainer,
features: ["bookmarks", "hints", "history", "marks", "quickmarks", "sanitizer", "session", "tabs", "tabs_undo", "windows"], styleableChrome: ["chrome://browser/content/browser.xul"],
autocommands: {
BookmarkAdd: "Triggered after a page is bookmarked",
ColorScheme: "Triggered after a color scheme has been loaded",
DOMLoad: "Triggered when a page's DOM content has fully loaded",
DownloadPost: "Triggered when a download has completed",
Fullscreen: "Triggered when the browser's fullscreen state changes",
LocationChange: "Triggered when changing tabs or when navigation to a new location",
PageLoadPre: "Triggered after a page load is initiated",
PageLoad: "Triggered when a page gets (re)loaded/opened",
PrivateMode: "Triggered when private mode is activated or deactivated",
Sanitize: "Triggered when a sanitizeable item is cleared",
ShellCmdPost: "Triggered after executing a shell command with :!cmd",
Enter: "Triggered after Firefox starts",
LeavePre: "Triggered before exiting Firefox, just before destroying each module",
Leave: "Triggered before exiting Firefox",
},
defaults: { defaults: {
complete: "slf", complete: "slf",
guioptions: "rb", guioptions: "rb",
@@ -22,30 +40,6 @@ const Config = Module("config", ConfigBase, {
titlestring: "Pentadactyl" titlestring: "Pentadactyl"
}, },
guioptions: {
m: ["Menubar", ["toolbar-menubar"]],
T: ["Toolbar", ["nav-bar"]],
B: ["Bookmark bar", ["PersonalToolbar"]]
},
get visualbellWindow() getBrowser().mPanelContainer,
styleableChrome: ["chrome://browser/content/browser.xul"],
autocommands: [["BookmarkAdd", "Triggered after a page is bookmarked"],
["ColorScheme", "Triggered after a color scheme has been loaded"],
["DOMLoad", "Triggered when a page's DOM content has fully loaded"],
["DownloadPost", "Triggered when a download has completed"],
["Fullscreen", "Triggered when the browser's fullscreen state changes"],
["LocationChange", "Triggered when changing tabs or when navigation to a new location"],
["PageLoadPre", "Triggered after a page load is initiated"],
["PageLoad", "Triggered when a page gets (re)loaded/opened"],
["PrivateMode", "Triggered when private mode is activated or deactivated"],
["Sanitize", "Triggered when a sanitizeable item is cleared"],
["ShellCmdPost", "Triggered after executing a shell command with :!cmd"],
["PentadactylEnter", "Triggered after Firefox starts"],
["PentadactylLeavePre","Triggered before exiting Firefox, just before destroying each module"],
["PentadactylLeave", "Triggered before exiting Firefox"]],
dialogs: { dialogs: {
about: ["About Firefox", about: ["About Firefox",
function () { window.openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }], function () { window.openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
@@ -99,10 +93,34 @@ const Config = Module("config", ConfigBase, {
function () { buffer.viewSelectionSource(); }] function () { buffer.viewSelectionSource(); }]
}, },
features: [
"bookmarks", "hints", "history", "marks", "quickmarks", "sanitizer",
"session", "tabs", "tabs_undo", "windows"
],
guioptions: {
m: ["Menubar", ["toolbar-menubar"]],
T: ["Toolbar", ["nav-bar"]],
B: ["Bookmark bar", ["PersonalToolbar"]]
},
hasTabbrowser: true, hasTabbrowser: true,
ignoreKeys: {}, ignoreKeys: {},
removeTab: 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();
}
},
scripts: [ scripts: [
"browser.js", "browser.js",
"bookmarks.js", "bookmarks.js",
@@ -114,7 +132,6 @@ const Config = Module("config", ConfigBase, {
get tempFile() { get tempFile() {
let prefix = this.name.toLowerCase(); let prefix = this.name.toLowerCase();
try { try {
prefix += "-" + window.content.document.location.hostname; prefix += "-" + window.content.document.location.hostname;
} }

View File

@@ -6,6 +6,8 @@
<!ENTITY dactyl.name "pentadactyl"> <!ENTITY dactyl.name "pentadactyl">
<!ENTITY dactyl.idname "PENTADACTYL"> <!ENTITY dactyl.idname "PENTADACTYL">
<!ENTITY dactyl.appname "Pentadactyl"> <!ENTITY dactyl.appname "Pentadactyl">
<!ENTITY dactyl.apphome "http://dactyl.googlecode.com/">
<!ENTITY dactyl.maillist "pentadactyl@googlegroups.com">
<!ENTITY dactyl.host "&brandShortName;"> <!ENTITY dactyl.host "&brandShortName;">
<!ENTITY dactyl.hostbin "firefox"> <!ENTITY dactyl.hostbin "firefox">
<!ENTITY dactyl.statusBefore "statusbar-display"> <!ENTITY dactyl.statusBefore "statusbar-display">

View File

@@ -19,9 +19,9 @@
<dt>PrivateMode</dt> <dd>Triggered when private mode is activated or deactivated</dd> <dt>PrivateMode</dt> <dd>Triggered when private mode is activated or deactivated</dd>
<dt>Sanitize</dt> <dd>Triggered when privata data are sanitized</dd> <dt>Sanitize</dt> <dd>Triggered when privata data are sanitized</dd>
<dt>ShellCmdPost</dt> <dd>Triggered after executing a shell command with <ex>:!</ex><a>cmd</a></dd> <dt>ShellCmdPost</dt> <dd>Triggered after executing a shell command with <ex>:!</ex><a>cmd</a></dd>
<dt>&dactyl.appname;Enter</dt> <dd>Triggered after &dactyl.host; starts</dd> <dt>Enter</dt> <dd>Triggered after &dactyl.host; starts</dd>
<dt>&dactyl.appname;LeavePre</dt><dd>Triggered before exiting &dactyl.host;, just before destroying each module</dd> <dt>LeavePre</dt> <dd>Triggered before exiting &dactyl.host;, just before destroying each module</dd>
<dt>&dactyl.appname;Leave</dt> <dd>Triggered before exiting &dactyl.host;</dd> <dt>Leave</dt> <dd>Triggered before exiting &dactyl.host;</dd>
</dl> </dl>
<dl tag="autocommand-args" replace="autocommand-args"> <dl tag="autocommand-args" replace="autocommand-args">

View File

@@ -14,49 +14,29 @@
<link topic="http://vimperator.org">&dactyl.appname;</link> is a free browser add-on for &dactyl.host;, <link topic="http://vimperator.org">&dactyl.appname;</link> is a free browser add-on for &dactyl.host;,
which makes it look and behave like the which makes it look and behave like the
<link topic="http://www.vim.org">Vim</link> <link topic="http://www.vim.org">Vim</link>
text editor. It has similar key bindings, and you could call it a modal text editor.
web browser, as key bindings differ according to which mode you are in.
<warning tag="warning"> <warning tag="warning">
To provide the most authentic Vim experience, the &dactyl.host; menubar and toolbar are hidden. To provide the most authentic Vim experience, the &dactyl.host;
menubar and toolbar are hidden.
<p>
If you really need them, type: <ex>:set guioptions+=mT</ex> to
get them back.
</p>
<p>
If you don't like &dactyl.appname; at all, you can uninstall it
by typing <ex>:extdelete &dactyl.appname;</ex> or disable it
with <ex>:extdisable &dactyl.appname;</ex>
</p>
<p>
If you like it but can't remember the shortcuts, then type
<key name="F1"/> or <ex>:help</ex> to get this help window back.
</p>
</warning> </warning>
<p>If you really need them, type: <ex>:set guioptions+=mT</ex> to get them back.</p>
<p>
If you don't like &dactyl.appname; at all, you can uninstall it by typing
<ex>:extdelete &dactyl.appname;</ex> or <ex>:extdisable &dactyl.appname;</ex> to disable it.
</p>
<p>
If you like it but can't remember the shortcuts, then press
<key name="F1"/> or <ex>:help</ex> to get this help window back.
</p>
<tags>author donaton sponsor</tags>
<strut/>
<p>
&dactyl.appname; was initially written by
<link topic="mailto:stubenschrott@vimperator.org">Martin
Stubenschrott</link> but has found many other
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/Authors">contributors</link>
in the meanwhile. If you appreciate the work on &dactyl.appname; and want to
encourage us working on it more, you can send us greetings, patches, or
donations (thanks a lot to
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/Donors">these
people</link>
who already did):
</p>
<pan><handle/></pan>
<p>
If you prefer getting some nice products for your money, you can also support
us by buying some cool
<link topic="http://www.zazzle.com/maxauthority*">merchandise</link> like
t-shirts or mugs. Of course, as we believe in free, open source software, only
support us financially if you really like &dactyl.appname; and the money doesn't hurt
— otherwise just use it, recommend it, and like it :)
</p>
<h2 tag="overview">Help topics</h2> <h2 tag="overview">Help topics</h2>
<ol> <ol>
@@ -156,10 +136,11 @@ web browser, as key bindings differ according to which mode you are in.
</ol> </ol>
<p> <p>
You can also jump directly to the help of a specific command with <ex>:help o</ex> You can also jump directly to the help of a specific command via the <ex>:help</ex> command.
or <ex>:help :set</ex>.
</p> </p>
<example><ex>:help :help</ex></example>
<h2 tag="features">Features</h2> <h2 tag="features">Features</h2>
<ul> <ul>
@@ -188,15 +169,14 @@ or <ex>:help :set</ex>.
<h2 tag="contact">Contact</h2> <h2 tag="contact">Contact</h2>
<p> <p>
Please send comments/bug reports/patches to the mailing list, where we will Please send comments, questions, or patches to the
properly answer any questions. You can also join the <link topic="mailto:&dactyl.maillist;">mailing list</link>,
<link topic="irc://irc.freenode.net/pentadactyl">#pentadactyl</link> IRC channel where we will do our best to answer any questions. You can also
on <link target="http://www.freenode.net/">Freenode</link> or check the check the
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/Wiki">Wiki</link> for <link topic="http://code.google.com/p/dactyl/wiki">wiki</link> for
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/FAQ"> <link topic="http://code.google.com/p/dactyl/wiki/FAQ">FAQ</link>.
frequently asked questions (FAQ) Issue reports can be entered in the
</link>. Make sure, you have read the TODO file first, as we are aware of many <link topic="http://code.google.com/p/dactyl/issues/list">issue tracker</link>.
things which can be improved when we find time for it or receive patches.
</p> </p>
</document> </document>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>
<!DOCTYPE overlay SYSTEM "chrome://dactyl/content/dactyl.dtd">
<overlay
xmlns="http://vimperator.org/namespaces/liberator"
xmlns:html="http://www.w3.org/1999/xhtml">
<item insertbefore=":set">
<tags>:seg :way</tags>
<spec>:se[a]</spec>
<description>
<p>Show all options that differ from their default value.</p>
</description>
</item>
<item insertafter=":set-default">
<tags>:sturm :drang</tags>
<spec>:und</spec>
<description>
<p>Show all options that differ from their default value.</p>
</description>
</item>
</overlay>
<!-- vim:se sts=4 sw=4 et: -->

View File

@@ -122,20 +122,24 @@
Scrolling the browser window is done with simple keystrokes: Scrolling the browser window is done with simple keystrokes:
</p> </p>
<ul> <dl>
<li><k>j</k>/<k>k</k> <dt><k>j</k>/<k>k</k></dt>
<dd>
scroll window down/up by one line, respectively scroll window down/up by one line, respectively
</li> </dd>
<li><k>h</k>/<k>l</k> <dt><k>h</k>/<k>l</k></dt>
<dd>
scroll window left/right scroll window left/right
</li> </dd>
<li><k name="Space"/>/<k name="C-b"/> <dt><k name="Space"/>/<k name="C-b"/></dt>
<dd>
scroll down/up by one page scroll down/up by one page
</li> </dd>
<li><k name="C-d"/>/<k name="C-u"/> <dt><k name="C-d"/>/<k name="C-u"/></dt>
<dd>
scroll down/up by 1/2 page scroll down/up by 1/2 page
</li> </dd>
</ul> </dl>
<p> <p>
Your standard buttons (<k name="Up"/>/<k name="Down"/>/<k name="PgUp"/>/<k name="PgDn"/>) will Your standard buttons (<k name="Up"/>/<k name="Down"/>/<k name="PgUp"/>/<k name="PgDn"/>) will
@@ -149,31 +153,36 @@
scrolling. scrolling.
</p> </p>
<ul> <dl>
<li><k name="C-o"/>/<k name="C-i"/> <dt><k name="C-o"/>/<k name="C-i"/></dt>
<dd>
move Back/Forward in the current window/tab's history, respectively move Back/Forward in the current window/tab's history, respectively
</li> </dd>
</ul> </dl>
<p> <p>
Move between tabs using these keystrokes which may also be familiar to tabbing Move between tabs using these keystrokes which may also be familiar to tabbing
Vimmers. Vimmers.
</p> </p>
<ul> <dl>
<li><k>gt</k>/<k name="C-n"/> <dt><k>gt</k>/<k name="C-n"/></dt>
<dd>
go to the next tab go to the next tab
</li> </dd>
<li><k>gT</k>/<k name="C-p"/> <dt><k>gT</k>/<k name="C-p"/></dt>
<dd>
go to the previous tab go to the previous tab
</li> </dd>
<li><k>g0</k>/<k>g$</k> <dt><k>g0</k>/<k>g$</k></dt>
<dd>
go to the first/last tab go to the first/last tab
</li> </dd>
<li><k>d</k> <dt><k>d</k></dt>
<dd>
close the active tab (delete the buffer) close the active tab (delete the buffer)
</li> </dd>
</ul> </dl>
<p> <p>
To open a web page in a new tab, use the <ex>:tabopen <a>url</a></ex>. To open a URL in To open a web page in a new tab, use the <ex>:tabopen <a>url</a></ex>. To open a URL in
@@ -186,8 +195,8 @@
<ex>:open my.webmail.com<key name="CR"/></ex> <ex>:open my.webmail.com<key name="CR"/></ex>
<k>o</k>my.webmail.com<key name="CR"/> <k>o</k>my.webmail.com<key name="CR"/>
<ex>:tabopen vimperator.org<key name="CR"/></ex> <ex>:tabopen google.com<key name="CR"/></ex>
<k>t</k>vimperator.org<key name="CR"/> <k>t</k>google.com<key name="CR"/>
</code> </code>
<h2 tag="hints-tutorial">Some hints about surfing…</h2> <h2 tag="hints-tutorial">Some hints about surfing…</h2>
@@ -225,7 +234,7 @@
</p> </p>
<p> <p>
To test it, try this link: <link target="http://vimperator.org/">&dactyl.appname; Homepage</link>. To test it, try this link: <link target="&dactyl.apphome;">&dactyl.appname; Homepage</link>.
Activate QuickHint mode with <k>f</k> or <k>F</k> to highlight all currently Activate QuickHint mode with <k>f</k> or <k>F</k> to highlight all currently
visible links. Then start typing the text of the link. The link should be visible links. Then start typing the text of the link. The link should be
uniquely identified soon, and &dactyl.appname; will open it. Once you're done, uniquely identified soon, and &dactyl.appname; will open it. Once you're done,
@@ -250,7 +259,7 @@
<code><k name="Esc"/></code> <code><k name="Esc"/></code>
<h2 tag="pentadactylrc">Saving for posterity - pentadactylrc</h2> <h2 tag="pentadactylrc">Saving for posteritypentadactylrc</h2>
<p> <p>
Once you get &dactyl.appname; set up with your desired options, maps, and commands, Once you get &dactyl.appname; set up with your desired options, maps, and commands,
@@ -275,17 +284,24 @@
&dactyl.appname; supports all of Vim's classic methods of exiting. &dactyl.appname; supports all of Vim's classic methods of exiting.
</p> </p>
<ul> <dl>
<li><ex>:xall</ex> command to quit and save the current browsing <dt><ex>:xall</ex></dt>
session for next time; the default. <dd>
</li> command to quit and save the current browsing session for next time; the default.
<li><ex>:qall</ex> command to quit <em>without</em> saving the session </dd>
</li> <dt><ex>:qall</ex></dt>
<li><k>ZZ</k> Normal mode mapping equivalent to <ex>:xall</ex> <dd>
</li> command to quit <em>without</em> saving the session
<li><k>ZQ</k> Normal mode mapping equivalent to <ex>:qall</ex> </dd>
</li> <dt><k>ZZ</k></dt>
</ul> <dd>
Normal mode mapping equivalent to <ex>:xall</ex>
</dd>
<dt><k>ZQ</k></dt>
<dd>
Normal mode mapping equivalent to <ex>:qall</ex>
</dd>
</dl>
<h2 tag="whither-&dactyl.host;">Where did &dactyl.host; go?</h2> <h2 tag="whither-&dactyl.host;">Where did &dactyl.host; go?</h2>
@@ -296,24 +312,28 @@
make the best use of them. make the best use of them.
</p> </p>
<ul> <dl>
<li><ex>:dialog</ex> <dt><ex>:dialog</ex></dt>
<dd>
To access some of &dactyl.host;'s many dialog windows, you can use the To access some of &dactyl.host;'s many dialog windows, you can use the
<ex>:dialog</ex> command. See <ex>:help :dialog</ex>. <ex>:dialog</ex> command. See <ex>:help :dialog</ex>.
</li> </dd>
<li><ex>:bmarks</ex> <dt><ex>:bmarks</ex></dt>
<dd>
&dactyl.appname; provides a new interface to bookmarks, but they're still your &dactyl.appname; provides a new interface to bookmarks, but they're still your
standard &dactyl.host; bookmarks under the hood. <ex>:bmark</ex> will add a new standard &dactyl.host; bookmarks under the hood. <ex>:bmark</ex> will add a new
bookmark, while <ex>:bmarks</ex> will list the bookmarks currently defined. bookmark, while <ex>:bmarks</ex> will list the bookmarks currently defined.
</li> </dd>
<li><ex>:history</ex> <dt><ex>:history</ex></dt>
<dd>
It's exactly what it sounds like. This command will display a colorized, It's exactly what it sounds like. This command will display a colorized,
scrollable and clickable list of the locations in &dactyl.appname;'s history. scrollable and clickable list of the locations in &dactyl.appname;'s history.
</li> </dd>
<li><ex>:emenu</ex> <dt><ex>:emenu</ex></dt>
<dd>
Access the &dactyl.host; menus through the &dactyl.appname; command line. Access the &dactyl.host; menus through the &dactyl.appname; command line.
</li> </dd>
</ul> </dl>
<p> <p>
Feel free to explore at this point. If you use the <ex>:tabopen</ex> command, Feel free to explore at this point. If you use the <ex>:tabopen</ex> command,

View File

@@ -44,98 +44,74 @@ const Config = Module("config", ConfigBase, {
styleableChrome: ["chrome://gonzo/content/xul/mainplayer.xul"], styleableChrome: ["chrome://gonzo/content/xul/mainplayer.xul"],
autocommands: [["BookmarkAdd", "Triggered after a page is bookmarked"], autocommands: {
["ColorScheme", "Triggered after a color scheme has been loaded"], BookmarkAdd: "Triggered after a page is bookmarked",
["DOMLoad", "Triggered when a page's DOM content has fully loaded"], ColorScheme: "Triggered after a color scheme has been loaded",
["DownloadPost", "Triggered when a download has completed"], DOMLoad: "Triggered when a page's DOM content has fully loaded",
["Fullscreen", "Triggered when the browser's fullscreen state changes"], DownloadPost: "Triggered when a download has completed",
["LocationChange", "Triggered when changing tabs or when navigation to a new location"], Fullscreen: "Triggered when the browser's fullscreen state changes",
["PageLoadPre", "Triggered after a page load is initiated"], LocationChange: "Triggered when changing tabs or when navigation to a new location",
["PageLoad", "Triggered when a page gets (re)loaded/opened"], PageLoadPre: "Triggered after a page load is initiated",
["ShellCmdPost", "Triggered after executing a shell command with :!cmd"], PageLoad: "Triggered when a page gets (re)loaded/opened",
["TrackChangePre", "Triggered before a playing track is changed"], ShellCmdPost: "Triggered after executing a shell command with :!cmd",
["TrackChange", "Triggered after a playing track has changed"], TrackChangePre: "Triggered before a playing track is changed",
["ViewChangePre", "Triggered before a sequencer view is changed"], TrackChange: "Triggered after a playing track has changed",
["ViewChange", "Triggered after a sequencer view is changed"], ViewChangePre: "Triggered before a sequencer view is changed",
["StreamStart", "Triggered after a stream has started"], ViewChange: "Triggered after a sequencer view is changed",
["StreamPause", "Triggered after a stream has paused"], StreamStart: "Triggered after a stream has started",
["StreamEnd", "Triggered after a stream has ended"], StreamPause: "Triggered after a stream has paused",
["StreamStop", "Triggered after a stream has stopped"], StreamEnd: "Triggered after a stream has ended",
["XulmusEnter", "Triggered after Songbird starts"], StreamStop: "Triggered after a stream has stopped",
["XulmusLeavePre", "Triggered before exiting Songbird, just before destroying each module"], Enter: "Triggered after Songbird starts",
["XulmusLeave", "Triggered before exiting Songbird"]], LeavePre: "Triggered before exiting Songbird, just before destroying each module",
Leave: "Triggered before exiting Songbird",
},
// TODO: remove those which don't make sense, can't be provided. // TODO: remove those which don't make sense, can't be provided.
dialogs: [ dialogs: {
["about", "About Songbird", about: ["About Songbird",
function () { window.openDialog("chrome://songbird/content/xul/about.xul", "_blank", "chrome,dialog,modal,centerscreen"); }], function () { window.openDialog("chrome://songbird/content/xul/about.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
/* addons: ["Manage Add-ons",
["addbookmark", "Add bookmark for the current page",
function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }],
*/
["addons", "Manage Add-ons",
function () { SBOpenPreferences("paneAddons"); }], function () { SBOpenPreferences("paneAddons"); }],
/* checkupdates: ["Check for updates",
["bookmarks", "List your bookmarks",
function () { window.openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }],
*/
["checkupdates", "Check for updates",
function () { window.checkForUpdates(); }], function () { window.checkForUpdates(); }],
["cleardata", "Clear private data", cleardata: ["Clear private data",
function () { Sanitizer.showUI(); }], function () { Sanitizer.showUI(); }],
["cookies", "List your cookies", cookies: ["List your cookies",
function () { window.toOpenWindowByType("Browser:Cookies", "chrome://browser/content/preferences/cookies.xul", "chrome,dialog=no,resizable"); }], function () { window.toOpenWindowByType("Browser:Cookies", "chrome://browser/content/preferences/cookies.xul", "chrome,dialog=no,resizable"); }],
["console", "JavaScript console", console: ["JavaScript console",
function () { window.toJavaScriptConsole(); }], function () { window.toJavaScriptConsole(); }],
/* dominspector: ["DOM Inspector",
["customizetoolbar", "Customize the Toolbar",
function () { window.BrowserCustomizeToolbar(); }],
*/
["dominspector", "DOM Inspector",
function () { try { window.inspectDOMDocument(content.document); } catch (e) { dactyl.echoerr("DOM Inspector extension not installed"); } }], function () { try { window.inspectDOMDocument(content.document); } catch (e) { dactyl.echoerr("DOM Inspector extension not installed"); } }],
["downloads", "Manage Downloads", downloads: ["Manage Downloads",
function () { window.toOpenWindowByType("Download:Manager", "chrome://mozapps/content/downloads/downloads.xul", "chrome,dialog=no,resizable"); }], function () { window.toOpenWindowByType("Download:Manager", "chrome://mozapps/content/downloads/downloads.xul", "chrome,dialog=no,resizable"); }],
/* jumpto: ["Jump to a media item",
["history", "List your history",
function () { window.openDialog("chrome://browser/content/history/history-panel.xul", "History", "dialog,centerscreen,width=600,height=600"); }],
["import", "Import Preferences, Bookmarks, History, etc. from other browsers",
function () { window.BrowserImport(); }],
*/
["jumpto", "Jump to a media item",
function () { onJumpToFileKey(); }], function () { onJumpToFileKey(); }],
["newsmartplaylist", "Open the file selector dialog", newsmartplaylist: ["Open the file selector dialog",
function () { SBNewSmartPlaylist(); }], function () { SBNewSmartPlaylist(); }],
["openfile", "Open the file selector dialog", openfile: ["Open the file selector dialog",
function () { SBFileOpen(); }], function () { SBFileOpen(); }],
/* pagesource: ["View page source",
["pageinfo", "Show information about the current page",
function () { window.BrowserPageInfo(); }],
*/
["pagesource", "View page source",
function () { window.BrowserViewSourceOfDocument(content.document); }], function () { window.BrowserViewSourceOfDocument(content.document); }],
["places", "Places Organizer: Manage your bookmarks and history", places: ["Places Organizer: Manage your bookmarks and history",
function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }], function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }],
["preferences", "Show Songbird preferences dialog", preferences: ["Show Songbird preferences dialog",
function () { window.openPreferences(); }], function () { window.openPreferences(); }],
/* printsetup: ["Setup the page size and orientation before printing",
["printpreview", "Preview the page before printing",
function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],
*/
["printsetup", "Setup the page size and orientation before printing",
function () { PrintUtils.showPageSetup(); }], function () { PrintUtils.showPageSetup(); }],
["print", "Show print dialog", print: ["Show print dialog",
function () { PrintUtils.print(); }], function () { PrintUtils.print(); }],
["saveframe", "Save frame to disk", saveframe: ["Save frame to disk",
function () { window.saveFrameDocument(); }], function () { window.saveFrameDocument(); }],
["savepage", "Save page to disk", savepage: ["Save page to disk",
function () { window.saveDocument(window.content.document); }], function () { window.saveDocument(window.content.document); }],
["searchengines", "Manage installed search engines", searchengines: ["Manage installed search engines",
function () { window.openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }], function () { window.openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
["selectionsource", "View selection source", selectionsource: ["View selection source",
function () { buffer.viewSelectionSource(); }], function () { buffer.viewSelectionSource(); }],
["subscribe", "Add a new subscription", subscribe: ["Add a new subscription",
function () { SBSubscribe(); }] function () { SBSubscribe(); }]
], },
focusChange: function () { focusChange: function () {
// Switch to -- PLAYER -- mode for Songbird Media Player. // Switch to -- PLAYER -- mode for Songbird Media Player.
@@ -153,14 +129,27 @@ const Config = Module("config", ConfigBase, {
modes: [["PLAYER", { char: "p" }]], modes: [["PLAYER", { char: "p" }]],
removeTab: 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();
}
},
scripts: [ scripts: [
"browser.js", "browser",
"bookmarks.js", "bookmarks",
"history.js", "history",
"quickmarks.js", "quickmarks",
"tabs.js", "tabs",
"player.js", "player",
"library.js" "library"
], ],
// FIXME: tab arg and media tab exception? // FIXME: tab arg and media tab exception?