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

View File

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

View File

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

View File

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

View File

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

View File

@@ -613,6 +613,7 @@ const CommandLine = Module("commandline", {
* @private
*/
onEvent: function onEvent(event) {
try {
let command = this.command;
if (event.type == "blur") {
@@ -686,6 +687,10 @@ const CommandLine = Module("commandline", {
if (/^<(Tab|S-Tab)>$/.test(key))
this._tabTimer.flush();
}
}
catch (e) {
dactyl.reportError(e);
}
},
/**
@@ -1724,6 +1729,8 @@ const ItemList = Class("ItemList", {
// if @param selectedItem is given, show the list and select that item
setItems: function setItems(newItems, selectedItem) {
if (this._selItem > -1)
this._getCompletion(this._selItem).removeAttribute("selected");
if (this._container.collapsed)
this._minHeight = 0;
this._startIndex = this._endIndex = this._selIndex = -1;
@@ -1737,9 +1744,6 @@ const ItemList = Class("ItemList", {
// select index, refill list if necessary
selectItem: function selectItem(index) {
//if (this._container.collapsed) // FIXME
// return;
//let now = Date.now();
if (this._div == null)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,83 +14,53 @@ const Config = Module("config", ConfigBase, {
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
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: {
guioptions: "frb",
showtabline: 1,
titlestring: "Muttator"
},
guioptions: {
m: ["MenuBar", ["mail-toolbar-menubar2"]],
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(); }]*/
],
/*** optional options, there are checked for existence and a fallback provided ***/
features: ["hints", "mail", "marks", "addressbook", "tabs"],
focusChange: function (win) {
// 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(),
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))]
guioptions: {
m: ["MenuBar", ["mail-toolbar-menubar2"]],
T: ["Toolbar" , ["mail-bar2"]],
f: ["Folder list", ["folderPaneBox", "folderpane_splitter"]],
F: ["Folder list header", ["folderPaneHeader"]]
},
// they are sorted by relevance, not alphabetically
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: [
["MESSAGE", { char: "m" }],
["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
get outputHeight() {
@@ -137,15 +112,35 @@ const Config = Module("config", ConfigBase, {
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"] : [
"addressbook.js",
"mail.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
tempFile: "mutt-ator-mail",
get visualbellWindow() document.getElementById(this.mainWindowId),
}, {
}, {
commands: function () {

View File

@@ -1,9 +1,11 @@
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 previous incremental search implementation
* gf now toggles between source and content view.
'|' 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
* Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR
* Add basic plugin authorship documentation

View File

@@ -13,8 +13,26 @@ const Config = Module("config", ConfigBase, {
name: "Pentadactyl",
hostApplication: "Firefox",
/*** optional options, there are checked for existence and a fallback provided ***/
features: ["bookmarks", "hints", "history", "marks", "quickmarks", "sanitizer", "session", "tabs", "tabs_undo", "windows"],
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",
Enter: "Triggered after Firefox starts",
LeavePre: "Triggered before exiting Firefox, just before destroying each module",
Leave: "Triggered before exiting Firefox",
},
defaults: {
complete: "slf",
guioptions: "rb",
@@ -22,30 +40,6 @@ const Config = Module("config", ConfigBase, {
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: {
about: ["About Firefox",
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(); }]
},
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,
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: [
"browser.js",
"bookmarks.js",
@@ -114,7 +132,6 @@ const Config = Module("config", ConfigBase, {
get tempFile() {
let prefix = this.name.toLowerCase();
try {
prefix += "-" + window.content.document.location.hostname;
}

View File

@@ -6,6 +6,8 @@
<!ENTITY dactyl.name "pentadactyl">
<!ENTITY dactyl.idname "PENTADACTYL">
<!ENTITY dactyl.appname "Pentadactyl">
<!ENTITY dactyl.apphome "http://dactyl.googlecode.com/">
<!ENTITY dactyl.maillist "pentadactyl@googlegroups.com">
<!ENTITY dactyl.host "&brandShortName;">
<!ENTITY dactyl.hostbin "firefox">
<!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>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>&dactyl.appname;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>&dactyl.appname;Leave</dt> <dd>Triggered before exiting &dactyl.host;</dd>
<dt>Enter</dt> <dd>Triggered after &dactyl.host; starts</dd>
<dt>LeavePre</dt> <dd>Triggered before exiting &dactyl.host;, just before destroying each module</dd>
<dt>Leave</dt> <dd>Triggered before exiting &dactyl.host;</dd>
</dl>
<dl tag="autocommand-args" replace="autocommand-args">

View File

@@ -14,48 +14,28 @@
<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
<link topic="http://www.vim.org">Vim</link>
text editor. It has similar key bindings, and you could call it a modal
web browser, as key bindings differ according to which mode you are in.
text editor.
<warning tag="warning">
To provide the most authentic Vim experience, the &dactyl.host; menubar and toolbar are hidden.
</warning>
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 <ex>:extdisable &dactyl.appname;</ex> to disable it.
If you really need them, type: <ex>:set guioptions+=mT</ex> to
get them back.
</p>
<p>
If you like it but can't remember the shortcuts, then press
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>
<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>
</warning>
<h2 tag="overview">Help topics</h2>
@@ -156,10 +136,11 @@ web browser, as key bindings differ according to which mode you are in.
</ol>
<p>
You can also jump directly to the help of a specific command with <ex>:help o</ex>
or <ex>:help :set</ex>.
You can also jump directly to the help of a specific command via the <ex>:help</ex> command.
</p>
<example><ex>:help :help</ex></example>
<h2 tag="features">Features</h2>
<ul>
@@ -188,15 +169,14 @@ or <ex>:help :set</ex>.
<h2 tag="contact">Contact</h2>
<p>
Please send comments/bug reports/patches to the mailing list, where we will
properly answer any questions. You can also join the
<link topic="irc://irc.freenode.net/pentadactyl">#pentadactyl</link> IRC channel
on <link target="http://www.freenode.net/">Freenode</link> or check the
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/Wiki">Wiki</link> for
<link topic="http://vimperator.org/trac/wiki/&dactyl.appname;/FAQ">
frequently asked questions (FAQ)
</link>. Make sure, you have read the TODO file first, as we are aware of many
things which can be improved when we find time for it or receive patches.
Please send comments, questions, or patches to the
<link topic="mailto:&dactyl.maillist;">mailing list</link>,
where we will do our best to answer any questions. You can also
check the
<link topic="http://code.google.com/p/dactyl/wiki">wiki</link> for
<link topic="http://code.google.com/p/dactyl/wiki/FAQ">FAQ</link>.
Issue reports can be entered in the
<link topic="http://code.google.com/p/dactyl/issues/list">issue tracker</link>.
</p>
</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:
</p>
<ul>
<li><k>j</k>/<k>k</k>
<dl>
<dt><k>j</k>/<k>k</k></dt>
<dd>
scroll window down/up by one line, respectively
</li>
<li><k>h</k>/<k>l</k>
</dd>
<dt><k>h</k>/<k>l</k></dt>
<dd>
scroll window left/right
</li>
<li><k name="Space"/>/<k name="C-b"/>
</dd>
<dt><k name="Space"/>/<k name="C-b"/></dt>
<dd>
scroll down/up by one page
</li>
<li><k name="C-d"/>/<k name="C-u"/>
</dd>
<dt><k name="C-d"/>/<k name="C-u"/></dt>
<dd>
scroll down/up by 1/2 page
</li>
</ul>
</dd>
</dl>
<p>
Your standard buttons (<k name="Up"/>/<k name="Down"/>/<k name="PgUp"/>/<k name="PgDn"/>) will
@@ -149,31 +153,36 @@
scrolling.
</p>
<ul>
<li><k name="C-o"/>/<k name="C-i"/>
<dl>
<dt><k name="C-o"/>/<k name="C-i"/></dt>
<dd>
move Back/Forward in the current window/tab's history, respectively
</li>
</ul>
</dd>
</dl>
<p>
Move between tabs using these keystrokes which may also be familiar to tabbing
Vimmers.
</p>
<ul>
<li><k>gt</k>/<k name="C-n"/>
<dl>
<dt><k>gt</k>/<k name="C-n"/></dt>
<dd>
go to the next tab
</li>
<li><k>gT</k>/<k name="C-p"/>
</dd>
<dt><k>gT</k>/<k name="C-p"/></dt>
<dd>
go to the previous tab
</li>
<li><k>g0</k>/<k>g$</k>
</dd>
<dt><k>g0</k>/<k>g$</k></dt>
<dd>
go to the first/last tab
</li>
<li><k>d</k>
</dd>
<dt><k>d</k></dt>
<dd>
close the active tab (delete the buffer)
</li>
</ul>
</dd>
</dl>
<p>
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>
<k>o</k>my.webmail.com<key name="CR"/>
<ex>:tabopen vimperator.org<key name="CR"/></ex>
<k>t</k>vimperator.org<key name="CR"/>
<ex>:tabopen google.com<key name="CR"/></ex>
<k>t</k>google.com<key name="CR"/>
</code>
<h2 tag="hints-tutorial">Some hints about surfing…</h2>
@@ -225,7 +234,7 @@
</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
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,
@@ -250,7 +259,7 @@
<code><k name="Esc"/></code>
<h2 tag="pentadactylrc">Saving for posterity - pentadactylrc</h2>
<h2 tag="pentadactylrc">Saving for posteritypentadactylrc</h2>
<p>
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.
</p>
<ul>
<li><ex>:xall</ex> command to quit and save the current browsing
session for next time; the default.
</li>
<li><ex>:qall</ex> command to quit <em>without</em> saving the session
</li>
<li><k>ZZ</k> Normal mode mapping equivalent to <ex>:xall</ex>
</li>
<li><k>ZQ</k> Normal mode mapping equivalent to <ex>:qall</ex>
</li>
</ul>
<dl>
<dt><ex>:xall</ex></dt>
<dd>
command to quit and save the current browsing session for next time; the default.
</dd>
<dt><ex>:qall</ex></dt>
<dd>
command to quit <em>without</em> saving the session
</dd>
<dt><k>ZZ</k></dt>
<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>
@@ -296,24 +312,28 @@
make the best use of them.
</p>
<ul>
<li><ex>:dialog</ex>
<dl>
<dt><ex>:dialog</ex></dt>
<dd>
To access some of &dactyl.host;'s many dialog windows, you can use the
<ex>:dialog</ex> command. See <ex>:help :dialog</ex>.
</li>
<li><ex>:bmarks</ex>
</dd>
<dt><ex>:bmarks</ex></dt>
<dd>
&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
bookmark, while <ex>:bmarks</ex> will list the bookmarks currently defined.
</li>
<li><ex>:history</ex>
</dd>
<dt><ex>:history</ex></dt>
<dd>
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.
</li>
<li><ex>:emenu</ex>
</dd>
<dt><ex>:emenu</ex></dt>
<dd>
Access the &dactyl.host; menus through the &dactyl.appname; command line.
</li>
</ul>
</dd>
</dl>
<p>
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"],
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"],
["ShellCmdPost", "Triggered after executing a shell command with :!cmd"],
["TrackChangePre", "Triggered before a playing track is changed"],
["TrackChange", "Triggered after a playing track has changed"],
["ViewChangePre", "Triggered before a sequencer view is changed"],
["ViewChange", "Triggered after a sequencer view is changed"],
["StreamStart", "Triggered after a stream has started"],
["StreamPause", "Triggered after a stream has paused"],
["StreamEnd", "Triggered after a stream has ended"],
["StreamStop", "Triggered after a stream has stopped"],
["XulmusEnter", "Triggered after Songbird starts"],
["XulmusLeavePre", "Triggered before exiting Songbird, just before destroying each module"],
["XulmusLeave", "Triggered before exiting Songbird"]],
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",
ShellCmdPost: "Triggered after executing a shell command with :!cmd",
TrackChangePre: "Triggered before a playing track is changed",
TrackChange: "Triggered after a playing track has changed",
ViewChangePre: "Triggered before a sequencer view is changed",
ViewChange: "Triggered after a sequencer view is changed",
StreamStart: "Triggered after a stream has started",
StreamPause: "Triggered after a stream has paused",
StreamEnd: "Triggered after a stream has ended",
StreamStop: "Triggered after a stream has stopped",
Enter: "Triggered after Songbird starts",
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.
dialogs: [
["about", "About Songbird",
dialogs: {
about: ["About Songbird",
function () { window.openDialog("chrome://songbird/content/xul/about.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
/*
["addbookmark", "Add bookmark for the current page",
function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }],
*/
["addons", "Manage Add-ons",
addons: ["Manage Add-ons",
function () { SBOpenPreferences("paneAddons"); }],
/*
["bookmarks", "List your bookmarks",
function () { window.openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }],
*/
["checkupdates", "Check for updates",
checkupdates: ["Check for updates",
function () { window.checkForUpdates(); }],
["cleardata", "Clear private data",
cleardata: ["Clear private data",
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"); }],
["console", "JavaScript console",
console: ["JavaScript console",
function () { window.toJavaScriptConsole(); }],
/*
["customizetoolbar", "Customize the Toolbar",
function () { window.BrowserCustomizeToolbar(); }],
*/
["dominspector", "DOM Inspector",
dominspector: ["DOM Inspector",
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"); }],
/*
["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",
jumpto: ["Jump to a media item",
function () { onJumpToFileKey(); }],
["newsmartplaylist", "Open the file selector dialog",
newsmartplaylist: ["Open the file selector dialog",
function () { SBNewSmartPlaylist(); }],
["openfile", "Open the file selector dialog",
openfile: ["Open the file selector dialog",
function () { SBFileOpen(); }],
/*
["pageinfo", "Show information about the current page",
function () { window.BrowserPageInfo(); }],
*/
["pagesource", "View page source",
pagesource: ["View page source",
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); }],
["preferences", "Show Songbird preferences dialog",
preferences: ["Show Songbird preferences dialog",
function () { window.openPreferences(); }],
/*
["printpreview", "Preview the page before printing",
function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],
*/
["printsetup", "Setup the page size and orientation before printing",
printsetup: ["Setup the page size and orientation before printing",
function () { PrintUtils.showPageSetup(); }],
["print", "Show print dialog",
print: ["Show print dialog",
function () { PrintUtils.print(); }],
["saveframe", "Save frame to disk",
saveframe: ["Save frame to disk",
function () { window.saveFrameDocument(); }],
["savepage", "Save page to disk",
savepage: ["Save page to disk",
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"); }],
["selectionsource", "View selection source",
selectionsource: ["View selection source",
function () { buffer.viewSelectionSource(); }],
["subscribe", "Add a new subscription",
subscribe: ["Add a new subscription",
function () { SBSubscribe(); }]
],
},
focusChange: function () {
// Switch to -- PLAYER -- mode for Songbird Media Player.
@@ -153,14 +129,27 @@ const Config = Module("config", ConfigBase, {
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: [
"browser.js",
"bookmarks.js",
"history.js",
"quickmarks.js",
"tabs.js",
"player.js",
"library.js"
"browser",
"bookmarks",
"history",
"quickmarks",
"tabs",
"player",
"library"
],
// FIXME: tab arg and media tab exception?