// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-try {"use strict";
+"use strict";
-Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("buffer", {
exports: ["Buffer", "buffer"],
require: ["prefs", "services", "util"]
-}, this);
+});
-this.lazyRequire("finder", ["RangeFind"]);
-this.lazyRequire("overlay", ["overlay"]);
-this.lazyRequire("storage", ["storage"]);
-this.lazyRequire("template", ["template"]);
+lazyRequire("bookmarkcache", ["bookmarkcache"]);
+lazyRequire("contexts", ["Group"]);
+lazyRequire("io", ["io"]);
+lazyRequire("finder", ["RangeFind"]);
+lazyRequire("overlay", ["overlay"]);
+lazyRequire("sanitizer", ["sanitizer"]);
+lazyRequire("storage", ["File", "storage"]);
+lazyRequire("template", ["template"]);
/**
* A class to manage the primary web content buffer. The name comes
let win = services.focus.focusedWindow;
if (!win || win == window || util.topWindow(win) != window)
- return window.content
+ return window.content;
if (win.top == window)
return win;
return win.top;
);
},
+ /**
+ * Gets a content preference for the given buffer.
+ *
+ * @param {string} pref The preference to get.
+ * @param {function(string|number|boolean)} callback The callback to
+ * call with the preference value. @optional
+ * @returns {string|number|boolean} The value of the preference, if
+ * callback is not provided.
+ */
+ getPref: function getPref(pref, callback) {
+ // God damn it.
+ if (config.haveGecko("19.0a1"))
+ services.contentPrefs.getPref(this.uri, pref,
+ sanitizer.getContext(this.win), callback);
+ else
+ services.contentPrefs.getPref(this.uri, pref, callback);
+ },
+
+ /**
+ * Sets a content preference for the given buffer.
+ *
+ * @param {string} pref The preference to set.
+ * @param {string} value The value to store.
+ */
+ setPref: function setPref(pref, value) {
+ services.contentPrefs.setPref(
+ this.uri, pref, value, sanitizer.getContext(this.win));
+ },
+
+ /**
+ * Clear a content preference for the given buffer.
+ *
+ * @param {string} pref The preference to clear.
+ */
+ clearPref: function clearPref(pref) {
+ services.contentPrefs.removePref(
+ this.uri, pref, sanitizer.getContext(this.win));
+ },
+
climbUrlPath: function climbUrlPath(count) {
let { dactyl } = this.modules;
dactyl.assert(url instanceof Ci.nsIURL);
while (count-- && url.path != "/")
- url.path = url.path.replace(/[^\/]+\/*$/, "");
+ url.path = url.path.replace(/[^\/]*\/*$/, "");
dactyl.assert(!url.equals(this.documentURI));
dactyl.open(url.spec);
*/
get zoomLevel() {
let v = this.contentViewer;
- return v[v.textZoom == 1 ? "fullZoom" : "textZoom"] * 100
+ return v[v.textZoom == 1 ? "fullZoom" : "textZoom"] * 100;
},
set zoomLevel(value) { this.setZoom(value, this.fullZoom); },
})(win || this.win);
if (focusedFirst)
- return frames.filter(function (f) f === this.focusedFrame).concat(
- frames.filter(function (f) f !== this.focusedFrame));
+ return frames.filter(function (f) f === this.focusedFrame, this).concat(
+ frames.filter(function (f) f !== this.focusedFrame, this));
return frames;
},
function a(regexp, elem) regexp.test(elem.textContent) === regexp.result ||
Array.some(elem.childNodes, function (child) regexp.test(child.alt) === regexp.result);
- function b(regexp, elem) regexp.test(elem.title);
+ function b(regexp, elem) regexp.test(elem.title) === regexp.result;
let res = Array.filter(frame.document.querySelectorAll(selector), Hints.isVisible);
for (let test in values([a, b]))
let { dactyl } = this.modules;
let ctrlKey = false, shiftKey = false;
+ let button = 0;
switch (dactyl.forceTarget || where) {
case dactyl.NEW_TAB:
case dactyl.NEW_BACKGROUND_TAB:
- ctrlKey = true;
+ button = 1;
shiftKey = dactyl.forceBackground != null ? dactyl.forceBackground
: where != dactyl.NEW_BACKGROUND_TAB;
break;
prefs.withContext(function () {
prefs.set("browser.tabs.loadInBackground", true);
let params = {
- screenX: offsetX, screenY: offsetY,
+ button: button, screenX: offsetX, screenY: offsetY,
ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey
};
*/
get selectionController() util.selectionController(this.focusedFrame),
+ /**
+ * @property {string|null} The canonical short URL for the current
+ * document.
+ */
+ get shortURL() {
+ let { uri, doc } = this;
+
+ for each (let shortener in Buffer.uriShorteners)
+ try {
+ let shortened = shortener(uri, doc);
+ if (shortened)
+ return shortened.spec;
+ }
+ catch (e) {
+ util.reportError(e);
+ }
+
+ let link = DOM("link[href][rev=canonical], \
+ link[href][rel=shortlink]", doc);
+ if (link)
+ return link.attr("href");
+
+ return null;
+ },
+
/**
* Opens the appropriate context menu for *elem*.
*
* Saves a page link to disk.
*
* @param {HTMLAnchorElement} elem The page link to save.
+ * @param {boolean} overwrite If true, overwrite any existing file.
*/
- saveLink: function saveLink(elem) {
+ saveLink: function saveLink(elem, overwrite) {
let { completion, dactyl, io } = this.modules;
let self = this;
if (file.exists() && file.isDirectory())
file.append(Buffer.getDefaultNames(elem)[0][0]);
+ util.assert(!file.exists() || overwrite, _("io.existsNoOverride", file.path));
+
try {
if (!file.exists())
file.create(File.NORMAL_FILE_TYPE, octal(644));
util.assert(false, _("save.invalidDestination", e.name));
}
- self.saveURI(uri, file);
+ self.saveURI({ uri: uri, file: file, context: elem });
},
completer: function (context) completion.savePage(context, elem)
* @param {nsIURI} uri The URI to save
* @param {nsIFile} file The file into which to write the result.
*/
- saveURI: function saveURI(uri, file, callback, self) {
+ saveURI: function saveURI(params) {
+ if (params instanceof Ci.nsIURI)
+ // Deprecated?
+ params = { uri: arguments[0], file: arguments[1],
+ callback: arguments[2], self: arguments[3] };
+
var persist = services.Persist();
persist.persistFlags = persist.PERSIST_FLAGS_FROM_CACHE
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
let window = this.topWindow;
- let downloadListener = new window.DownloadListener(window,
- services.Transfer(uri, File(file).URI, "",
- null, null, null, persist));
+ let privacy = sanitizer.getContext(params.context || this.win);
+ let file = File(params.file);
+ if (!file.exists())
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
- persist.progressListener = update(Object.create(downloadListener), {
- onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
- if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
- util.trapErrors(callback, self, uri, file, progress, request, flags, status);
-
- return onStateChange.superapply(this, arguments);
- })
- });
+ let downloadListener = new window.DownloadListener(window,
+ services.Transfer(params.uri, file.URI, "", null, null, null,
+ persist, privacy && privacy.usePrivateBrowsing));
+
+ var { callback, self } = params;
+ if (callback)
+ persist.progressListener = update(Object.create(downloadListener), {
+ onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
+ if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
+ util.trapErrors(callback, self, params.uri, file.file,
+ progress, request, flags, status);
+
+ return onStateChange.superapply(this, arguments);
+ })
+ });
+ else
+ persist.progressListener = downloadListener;
- persist.saveURI(uri, null, null, null, null, file);
+ persist.saveURI(params.uri, null, null, null, null,
+ file.file, privacy);
},
/**
* vertical percentages. See {@link Buffer.scrollToPercent} for
* parameters.
*/
- scrollToPercent: function scrollToPercent(horizontal, vertical)
- Buffer.scrollToPercent(this.findScrollable(0, vertical == null), horizontal, vertical),
+ scrollToPercent: function scrollToPercent(horizontal, vertical, dir)
+ Buffer.scrollToPercent(this.findScrollable(dir || 0, vertical == null), horizontal, vertical),
/**
* Scrolls the currently active element to the given horizontal and
var sel = this.focusedFrame.getSelection();
}
catch (e) {}
+
if (!elem && sel && sel.rangeCount)
elem = sel.getRangeAt(0).startContainer;
+
+ if (!elem) {
+ let area = -1;
+ for (let e in DOM(Buffer.SCROLLABLE_SEARCH_SELECTOR,
+ this.focusedFrame.document)) {
+ if (Buffer.isScrollable(e, dir, horizontal)) {
+ let r = DOM(e).rect;
+ let a = r.width * r.height;
+ if (a > area) {
+ area = a;
+ elem = e;
+ }
+ }
+ }
+ if (elem)
+ util.trapErrors("focus", elem);
+ }
if (elem)
elem = find(elem);
let distance = reverse ? function (rect) -rect.top : function (rect) rect.top;
let elems = [[e, distance(e.getBoundingClientRect())] for (e in path.matcher(this.focusedFrame.document))]
.filter(function (e) e[1] > FUDGE)
- .sort(function (a, b) a[1] - b[1])
+ .sort(function (a, b) a[1] - b[1]);
if (offScreen && !reverse)
elems = elems.filter(function (e) e[1] > this, this.topWindow.innerHeight);
// add the frame indicator
let doc = frames[next].document;
- let indicator = DOM(<div highlight="FrameIndicator"/>, doc)
+ let indicator = DOM(["div", { highlight: "FrameIndicator" }], doc)
.appendTo(doc.body || doc.documentElement || doc);
util.timeout(function () { indicator.remove(); }, 500);
// Doesn't unattach
- //doc.body.setAttributeNS(NS.uri, "activeframe", "true");
- //util.timeout(function () { doc.body.removeAttributeNS(NS.uri, "activeframe"); }, 500);
+ //doc.body.setAttributeNS(NS, "activeframe", "true");
+ //util.timeout(function () { doc.body.removeAttributeNS(NS, "activeframe"); }, 500);
},
// similar to pageInfo
showElementInfo: function showElementInfo(elem) {
let { dactyl } = this.modules;
- XML.ignoreWhitespace = XML.prettyPrinting = false;
- dactyl.echo(<><!--L-->Element:<br/>{util.objectToString(elem, true)}</>);
+ dactyl.echo(["", /*L*/"Element:", ["br"], util.objectToString(elem, true)]);
},
/**
showPageInfo: function showPageInfo(verbose, sections) {
let { commandline, dactyl, options } = this.modules;
- let self = this;
-
// Ctrl-g single line output
if (!verbose) {
let file = this.win.location.pathname.split("/").pop() || _("buffer.noName");
let info = template.map(
(sections || options["pageinfo"])
- .map(function (opt) Buffer.pageInfo[opt].action.call(self)),
+ .map((opt) => Buffer.pageInfo[opt].action.call(this)),
function (res) res && iter(res).join(", ") || undefined,
- ", ");
+ ", ").join("");
if (bookmarkcache.isBookmarked(this.URL))
info += ", " + _("buffer.bookmarked");
- let pageInfoText = <>{file.quote()} [{info}] {title}</>;
+ let pageInfoText = [file.quote(), " [", info, "] ", title].join("");
dactyl.echo(pageInfoText, commandline.FORCE_SINGLELINE);
return;
}
- let list = template.map(sections || options["pageinfo"], function (option) {
+ let list = template.map(sections || options["pageinfo"], (option) => {
let { action, title } = Buffer.pageInfo[option];
- return template.table(title, action.call(self, true));
- }, <br/>);
+ return template.table(title, action.call(this, true));
+ }, ["br"]);
commandline.commandOutput(list);
},
return true;
};
- let uri = isString(doc) ? util.newURI(doc) : util.newURI(doc.location.href);
+ if (isString(doc)) {
+ var privacyContext = null;
+ var uri = util.newURI(doc);
+ }
+ else {
+ privacyContext = sanitizer.getContext(doc);
+ uri = util.newURI(doc.location.href);
+ }
+
let ext = uri.fileExtension || "txt";
if (doc.contentType)
- ext = services.mime.getPrimaryExtension(doc.contentType, ext);
+ try {
+ ext = services.mime.getPrimaryExtension(doc.contentType, ext);
+ }
+ catch (e) {}
if (!isString(doc))
return io.withTempFiles(function (temp) {
var persist = services.Persist();
persist.persistFlags = persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
persist.progressListener = this;
- persist.saveURI(uri, null, null, null, null, this.file);
+ persist.saveURI(uri, null, null, null, null, this.file,
+ privacyContext);
}
return null;
},
* closed range [Buffer.ZOOM_MIN, Buffer.ZOOM_MAX].
*/
setZoom: function setZoom(value, fullZoom) {
- let { dactyl, statusline } = this.modules;
+ let { dactyl, statusline, storage } = this.modules;
let { ZoomManager } = this;
if (fullZoom === undefined)
return dactyl.echoerr(_("zoom.illegal"));
}
- if (services.has("contentPrefs") && !storage.privateMode
- && prefs.get("browser.zoom.siteSpecific")) {
- services.contentPrefs[value != 1 ? "setPref" : "removePref"]
- (this.uri, "browser.content.full-zoom", value);
- services.contentPrefs[value != 1 ? "setPref" : "removePref"]
- (this.uri, "dactyl.content.full-zoom", fullZoom);
+ if (prefs.get("browser.zoom.siteSpecific")) {
+ var privacy = sanitizer.getContext(this.win);
+ if (value == 1) {
+ this.clearPref("browser.content.full-zoom");
+ this.clearPref("dactyl.content.full-zoom");
+ }
+ else {
+ this.setPref("browser.content.full-zoom", value);
+ this.setPref("dactyl.content.full-zoom", fullZoom);
+ }
}
statusline.updateZoomLevel();
* Updates the zoom level of this buffer from a content preference.
*/
updateZoom: util.wrapCallback(function updateZoom() {
- let self = this;
let uri = this.uri;
- if (services.has("contentPrefs") && prefs.get("browser.zoom.siteSpecific"))
- services.contentPrefs.getPref(uri, "dactyl.content.full-zoom", function (val) {
- if (val != null && uri.equals(self.uri) && val != prefs.get("browser.zoom.full"))
- [self.contentViewer.textZoom, self.contentViewer.fullZoom] =
- [self.contentViewer.fullZoom, self.contentViewer.textZoom];
+ if (prefs.get("browser.zoom.siteSpecific")) {
+ this.getPref("dactyl.content.full-zoom", (val) => {
+ if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full"))
+ [this.contentViewer.textZoom, this.contentViewer.fullZoom] =
+ [this.contentViewer.fullZoom, this.contentViewer.textZoom];
});
+ }
}),
/**
scrollTo: deprecated("Buffer.scrollTo", function scrollTo(x, y) this.win.scrollTo(x, y)),
textZoom: deprecated("buffer.zoomValue/buffer.fullZoom", function textZoom() this.contentViewer.markupDocumentViewer.textZoom * 100)
}, {
+ /**
+ * The pattern used to search for a scrollable element when we have
+ * no starting point.
+ */
+ SCROLLABLE_SEARCH_SELECTOR: "html, body, div",
+
PageInfo: Struct("PageInfo", "name", "title", "action")
.localize("title"),
this.pageInfo[option] = Buffer.PageInfo(option, title, func);
},
+ uriShorteners: [],
+
+ /**
+ * Adds a new URI shortener for documents matching the given filter.
+ *
+ * @param {string|function(URI, Document):boolean} filter A site filter
+ * string or a function which accepts a URI and a document and
+ * returns true if it can shorten the document's URI.
+ * @param {function(URI, Document):URI} shortener Returns a shortened
+ * URL for the given URI and document.
+ */
+ addURIShortener: function addURIShortener(filter, shortener) {
+ if (isString(filter))
+ filter = Group.compileFilter(filter);
+
+ this.uriShorteners.push(function uriShortener(uri, doc) {
+ if (filter(uri, doc))
+ return shortener(uri, doc);
+ });
+ },
+
Scrollable: function Scrollable(elem) {
if (elem instanceof Ci.nsIDOMElement)
return elem;
get scrollWidth() this.win.scrollMaxX + this.win.innerWidth,
get scrollHeight() this.win.scrollMaxY + this.win.innerHeight,
+ get scrollLeftMax() this.win.scrollMaxX,
+ get scrollRightMax() this.win.scrollMaxY,
+
get scrollLeft() this.win.scrollX,
- set scrollLeft(val) { this.win.scrollTo(val, this.win.scrollY) },
+ set scrollLeft(val) { this.win.scrollTo(val, this.win.scrollY); },
get scrollTop() this.win.scrollY,
- set scrollTop(val) { this.win.scrollTo(this.win.scrollX, val) }
+ set scrollTop(val) { this.win.scrollTo(this.win.scrollX, val); }
};
return elem;
},
},
canScroll: function canScroll(elem, dir, horizontal) {
- let pos = "scrollTop", size = "clientHeight", max = "scrollHeight", layoutSize = "offsetHeight",
+ let pos = "scrollTop", size = "clientHeight", end = "scrollHeight", layoutSize = "offsetHeight",
overflow = "overflowX", border1 = "borderTopWidth", border2 = "borderBottomWidth";
if (horizontal)
- pos = "scrollLeft", size = "clientWidth", max = "scrollWidth", layoutSize = "offsetWidth",
+ pos = "scrollLeft", size = "clientWidth", end = "scrollWidth", layoutSize = "offsetWidth",
overflow = "overflowX", border1 = "borderLeftWidth", border2 = "borderRightWidth";
+ if (dir < 0)
+ return elem[pos] > 0;
+
+ let max = pos + "Max";
+ if (max in elem) {
+ if (elem[pos] < elem[max])
+ return true;
+ if (dir > 0)
+ return false;
+ return elem[pos] > 0;
+ }
+
let style = DOM(elem).style;
let borderSize = Math.round(parseFloat(style[border1]) + parseFloat(style[border2]));
let realSize = elem[size];
// Stupid Gecko eccentricities. May fail for quirks mode documents.
- if (elem[size] + borderSize == elem[max] || elem[size] == 0) // Stupid, fallible heuristic.
+ if (elem[size] + borderSize >= elem[end] || elem[size] == 0) // Stupid, fallible heuristic.
return false;
if (style[overflow] == "hidden")
realSize += borderSize;
- return dir < 0 && elem[pos] > 0 || dir > 0 && elem[pos] + realSize < elem[max] || !dir && realSize < elem[max];
+ return dir > 0 && elem[pos] + realSize < elem[end] || !dir && realSize < elem[end];
},
/**
/**
* Scrolls the given element vertically.
*
- * @param {Element} elem The element to scroll.
+ * @param {Node} node The node to scroll.
* @param {string} unit The increment by which to scroll.
* Possible values are: "lines", "pages"
* @param {number} number The possibly fractional number of
return {
x: elem.scrollLeft && elem.scrollLeft / this._exWidth(node),
y: elem.scrollTop / parseFloat(style.lineHeight)
- }
+ };
},
_exWidth: function _exWidth(elem) {
try {
- let div = DOM(<elem style="width: 1ex !important; position: absolute !important; padding: 0 !important; display: block;"/>,
+ let div = DOM(["elem", { style: "width: 1ex !important; position: absolute !important; padding: 0 !important; display: block;" }],
elem.ownerDocument).appendTo(elem.body || elem);
try {
return parseFloat(div.style.width);
dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows,
_("error.trailingCharacters"));
- const PRINTER = "PostScript/default";
- const BRANCH = "print.printer_" + PRINTER + ".";
+ const PRINTER = "PostScript/default";
+ const BRANCH = "printer_" + PRINTER + ".";
+ const BRANCHES = ["print.", BRANCH, "print." + BRANCH];
+ function set(pref, value) {
+ BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value); });
+ }
prefs.withContext(function () {
if (arg) {
prefs.set("print.print_printer", PRINTER);
- prefs.set( "print.print_to_file", true);
- prefs.set(BRANCH + "print_to_file", true);
-
- prefs.set( "print.print_to_filename", io.File(arg.substr(1)).path);
- prefs.set(BRANCH + "print_to_filename", io.File(arg.substr(1)).path);
+ set("print_to_file", true);
+ set("print_to_filename", io.File(arg.substr(1)).path);
dactyl.echomsg(_("print.toFile", arg.substr(1)));
}
dactyl.echomsg(_("print.sending"));
prefs.set("print.always_print_silent", args.bang);
- prefs.set("print.show_print_progress", !args.bang);
+ if (false)
+ prefs.set("print.show_print_progress", !args.bang);
config.browser.contentWindow.print();
});
- if (arg)
- dactyl.echomsg(_("print.printed", arg.substr(1)));
- else
- dactyl.echomsg(_("print.sent"));
+ dactyl.echomsg(_("print.sent"));
},
{
argCount: "?",
bang: true,
+ completer: function (context, args) {
+ if (args.bang && /^>/.test(context.filter))
+ context.fork("file", 1, modules.completion, "file");
+ },
literal: 0
});
function (file) {
let output = io.system(filename.substr(1), file);
commandline.command = command;
- commandline.commandOutput(<span highlight="CmdOutput">{output}</span>);
+ commandline.commandOutput(["span", { highlight: "CmdOutput" }, output]);
});
if (/^>>/.test(filename)) {
dactyl.assert(args.bang || !file.exists(), _("io.exists"));
- chosenData = { file: file, uri: util.newURI(doc.location.href) };
+ chosenData = { file: file.file, uri: util.newURI(doc.location.href) };
}
// if browser.download.useDownloadDir = false then the "Save As"
window.internalSave(doc.location.href, doc, null, contentDisposition,
doc.contentType, false, null, chosenData,
doc.referrer ? window.makeURI(doc.referrer) : null,
- true);
+ doc, true);
},
{
argCount: "?",
context.incomplete = false;
try {
if (/filename="(.*?)"/.test(xhr.getResponseHeader("Content-Disposition")))
- context.completions.push([decodeURIComponent(RegExp.$1), _("buffer.save.suggested")]);
+ context.completions.push([decodeURIComponent(RegExp.$1),
+ _("buffer.save.suggested")]);
}
finally {
context.completions = context.completions.slice();
uri.query = uri.query.replace(/(?:^|&)utm_[^&]+/g, "")
.replace(/^&/, "");
- let link = DOM("link[href][rev=canonical], link[href][rel=shortlink]", doc);
- let url = link.length && options.get("yankshort").getKey(uri) ? link.attr("href") : uri.spec;
+ let url = options.get("yankshort").getKey(uri) && buffer.shortURL || uri.spec;
dactyl.clipboardWrite(url, true);
});
mappings.add([modes.NORMAL],
["<C-a>", "<increment-url-path>"], "Increment last number in URL",
- function (args) { buffer.incrementURL(Math.max(args.count, 1)); },
+ function ({ count }) { buffer.incrementURL(Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL],
["<C-x>", "<decrement-url-path>"], "Decrement last number in URL",
- function (args) { buffer.incrementURL(-Math.max(args.count, 1)); },
+ function ({ count }) { buffer.incrementURL(-Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["gu", "<open-parent-path>"],
"Go to parent directory",
- function (args) { buffer.climbUrlPath(Math.max(args.count, 1)); },
+ function ({ count }) { buffer.climbUrlPath(Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["gU", "<open-root-path>"],
mappings.add([modes.COMMAND], [".", "<repeat-key>"],
"Repeat the last key event",
- function (args) {
+ function ({ count }) {
if (mappings.repeat) {
- for (let i in util.interruptibleRange(0, Math.max(args.count, 1), 100))
+ for (let i in util.interruptibleRange(0, Math.max(count, 1), 100))
mappings.repeat();
}
},
// scrolling
mappings.add([modes.NORMAL], ["j", "<Down>", "<C-e>", "<scroll-down-line>"],
"Scroll document down",
- function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollVertical("lines", Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["k", "<Up>", "<C-y>", "<scroll-up-line>"],
"Scroll document up",
- function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollVertical("lines", -Math.max(count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], dactyl.has("mail") ? ["h", "<scroll-left-column>"] : ["h", "<Left>", "<scroll-left-column>"],
+ mappings.add([modes.NORMAL], dactyl.has("mail") ? ["h", "<scroll-left-column>"] : ["h", "<Left>", "<scroll-left-column>"],
"Scroll document to the left",
- function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollHorizontal("columns", -Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"],
"Scroll document to the right",
- function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollHorizontal("columns", Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["0", "^", "<scroll-begin>"],
mappings.add([modes.NORMAL], ["gg", "<Home>", "<scroll-top>"],
"Go to the top of the document",
- function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); },
+ function ({ count }) { buffer.scrollToPercent(null, count != null ? count : 0,
+ count != null ? 0 : -1); },
{ count: true });
mappings.add([modes.NORMAL], ["G", "<End>", "<scroll-bottom>"],
"Go to the end of the document",
- function (args) {
- if (args.count)
+ function ({ count }) {
+ if (count)
var elem = options.get("linenumbers")
.getLine(buffer.focusedFrame.document,
- args.count);
+ count);
if (elem)
elem.scrollIntoView(true);
- else if (args.count)
- buffer.scrollToPosition(null, args.count);
+ else if (count)
+ buffer.scrollToPosition(null, count);
else
- buffer.scrollToPercent(null, 100);
+ buffer.scrollToPercent(null, 100, 1);
},
{ count: true });
mappings.add([modes.NORMAL], ["%", "<scroll-percent>"],
"Scroll to {count} percent of the document",
- function (args) {
- dactyl.assert(args.count > 0 && args.count <= 100);
- buffer.scrollToPercent(null, args.count);
+ function ({ count }) {
+ dactyl.assert(count > 0 && count <= 100);
+ buffer.scrollToPercent(null, count);
},
{ count: true });
mappings.add([modes.NORMAL], ["<C-d>", "<scroll-down>"],
"Scroll window downwards in the buffer",
- function (args) { buffer._scrollByScrollSize(args.count, true); },
+ function ({ count }) { buffer._scrollByScrollSize(count, true); },
{ count: true });
mappings.add([modes.NORMAL], ["<C-u>", "<scroll-up>"],
"Scroll window upwards in the buffer",
- function (args) { buffer._scrollByScrollSize(args.count, false); },
+ function ({ count }) { buffer._scrollByScrollSize(count, false); },
{ count: true });
mappings.add([modes.NORMAL], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-up-page>"],
"Scroll up a full page",
- function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollVertical("pages", -Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["<Space>"],
"Scroll down a full page",
- function (args) {
+ function ({ count }) {
if (isinstance((services.focus.focusedWindow || buffer.win).document.activeElement,
[Ci.nsIDOMHTMLInputElement,
Ci.nsIDOMHTMLButtonElement,
Ci.nsIDOMXULButtonElement]))
return Events.PASS;
- buffer.scrollVertical("pages", Math.max(args.count, 1));
+ buffer.scrollVertical("pages", Math.max(count, 1));
},
{ count: true });
mappings.add([modes.NORMAL], ["<C-f>", "<PageDown>", "<scroll-down-page>"],
"Scroll down a full page",
- function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
+ function ({ count }) { buffer.scrollVertical("pages", Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["]f", "<previous-frame>"],
"Focus next frame",
- function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
+ function ({ count }) { buffer.shiftFrameFocus(Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["[f", "<next-frame>"],
"Focus previous frame",
- function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
+ function ({ count }) { buffer.shiftFrameFocus(-Math.max(count, 1)); },
{ count: true });
mappings.add([modes.NORMAL], ["["],
"Jump to the previous element as defined by 'jumptags'",
- function (args) { buffer.findJump(args.arg, args.count, true); },
+ function ({ arg, count }) { buffer.findJump(arg, count, true); },
{ arg: true, count: true });
mappings.add([modes.NORMAL], ["g]"],
"Jump to the next off-screen element as defined by 'jumptags'",
- function (args) { buffer.findJump(args.arg, args.count, false, true); },
+ function ({ arg, count }) { buffer.findJump(arg, count, false, true); },
{ arg: true, count: true });
mappings.add([modes.NORMAL], ["]"],
"Jump to the next element as defined by 'jumptags'",
- function (args) { buffer.findJump(args.arg, args.count, false); },
+ function ({ arg, count }) { buffer.findJump(arg, count, false); },
{ arg: true, count: true });
mappings.add([modes.NORMAL], ["{"],
"Jump to the previous paragraph",
- function (args) { buffer.findJump("p", args.count, true); },
+ function ({ count }) { buffer.findJump("p", count, true); },
{ count: true });
mappings.add([modes.NORMAL], ["}"],
"Jump to the next paragraph",
- function (args) { buffer.findJump("p", args.count, false); },
+ function ({ count }) { buffer.findJump("p", count, false); },
{ count: true });
mappings.add([modes.NORMAL], ["]]", "<next-page>"],
"Follow the link labeled 'next' or '>' if it exists",
- function (args) {
- buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true);
+ function ({ count }) {
+ buffer.findLink("next", options["nextpattern"], (count || 1) - 1, true);
},
{ count: true });
mappings.add([modes.NORMAL], ["[[", "<previous-page>"],
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
- function (args) {
- buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
+ function ({ count }) {
+ buffer.findLink("prev", options["previouspattern"], (count || 1) - 1, true);
},
{ count: true });
mappings.add([modes.NORMAL], ["gi", "<focus-input>"],
"Focus last used input field",
- function (args) {
+ function ({ count }) {
let elem = buffer.lastInputField;
- if (args.count >= 1 || !elem || !events.isContentNode(elem)) {
+ if (count >= 1 || !elem || !events.isContentNode(elem)) {
let xpath = ["frame", "iframe", "input", "xul:textbox", "textarea[not(@disabled) and not(@readonly)]"];
let frames = buffer.allFrames(null, true);
});
dactyl.assert(elements.length > 0);
- elem = elements[Math.constrain(args.count, 1, elements.length) - 1];
+ elem = elements[Math.constrain(count, 1, elements.length) - 1];
}
buffer.focusElement(elem);
DOM(elem).scrollIntoView();
// zooming
mappings.add([modes.NORMAL], ["zi", "+", "<text-zoom-in>"],
"Enlarge text zoom of current web page",
- function (args) { buffer.zoomIn(Math.max(args.count, 1), false); },
+ function ({ count }) { buffer.zoomIn(Math.max(count, 1), false); },
{ count: true });
mappings.add([modes.NORMAL], ["zm", "<text-zoom-more>"],
"Enlarge text zoom of current web page by a larger amount",
- function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); },
+ function ({ count }) { buffer.zoomIn(Math.max(count, 1) * 3, false); },
{ count: true });
mappings.add([modes.NORMAL], ["zo", "-", "<text-zoom-out>"],
"Reduce text zoom of current web page",
- function (args) { buffer.zoomOut(Math.max(args.count, 1), false); },
+ function ({ count }) { buffer.zoomOut(Math.max(count, 1), false); },
{ count: true });
mappings.add([modes.NORMAL], ["zr", "<text-zoom-reduce>"],
"Reduce text zoom of current web page by a larger amount",
- function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); },
+ function ({ count }) { buffer.zoomOut(Math.max(count, 1) * 3, false); },
{ count: true });
mappings.add([modes.NORMAL], ["zz", "<text-zoom>"],
"Set text zoom value of current web page",
- function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); },
+ function ({ count }) { buffer.setZoom(count > 1 ? count : 100, false); },
{ count: true });
mappings.add([modes.NORMAL], ["ZI", "zI", "<full-zoom-in>"],
"Enlarge full zoom of current web page",
- function (args) { buffer.zoomIn(Math.max(args.count, 1), true); },
+ function ({ count }) { buffer.zoomIn(Math.max(count, 1), true); },
{ count: true });
mappings.add([modes.NORMAL], ["ZM", "zM", "<full-zoom-more>"],
"Enlarge full zoom of current web page by a larger amount",
- function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); },
+ function ({ count }) { buffer.zoomIn(Math.max(count, 1) * 3, true); },
{ count: true });
mappings.add([modes.NORMAL], ["ZO", "zO", "<full-zoom-out>"],
"Reduce full zoom of current web page",
- function (args) { buffer.zoomOut(Math.max(args.count, 1), true); },
+ function ({ count }) { buffer.zoomOut(Math.max(count, 1), true); },
{ count: true });
mappings.add([modes.NORMAL], ["ZR", "zR", "<full-zoom-reduce>"],
"Reduce full zoom of current web page by a larger amount",
- function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); },
+ function ({ count }) { buffer.zoomOut(Math.max(count, 1) * 3, true); },
{ count: true });
mappings.add([modes.NORMAL], ["zZ", "<full-zoom>"],
"Set full zoom value of current web page",
- function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); },
+ function ({ count }) { buffer.setZoom(count > 1 ? count : 100, true); },
{ count: true });
// page info
options.add(["linenumbers", "ln"],
"Patterns used to determine line numbers used by G",
"sitemap", {
+ // Make sure to update the docs when you change this.
+ "view-source:*": 'body,[id^=line]',
"code.google.com": '#nums [id^="nums_table"] a[href^="#"]',
"github.com": '.line_numbers>*',
"mxr.mozilla.org": 'a.l',
"pastebin.com": '#code_frame>div>ol>li',
"addons.mozilla.org": '.gutter>.line>a',
+ "bugzilla.mozilla.org": ".bz_comment:not(.bz_first_comment):not(.ih_history)",
"*": '/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr'
},
{
options.add(["nextpattern"],
"Patterns to use when guessing the next page in a document sequence",
- "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source),
+ "regexplist", UTF8(/'^Next [>»]','^Next »','\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source),
{ regexpFlags: "i" });
options.add(["previouspattern"],
"Patterns to use when guessing the previous page in a document sequence",
- "regexplist", UTF8(/'\bprev|previous\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source),
+ "regexplist", UTF8(/'[<«] Prev$','« Prev$','\bprev(ious)?\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source),
{ regexpFlags: "i" });
options.add(["pageinfo", "pa"],
if (verbose)
for (let link in engines)
yield [link.title || /*L*/ "Engine " + n++,
- <a xmlns={XHTML} href={link.href}
- onclick="if (event.button == 0) { window.external.AddSearchProvider(this.href); return false; }"
- highlight="URL">{link.href}</a>];
+ ["a", { href: link.href, highlight: "URL",
+ onclick: "if (event.button == 0) { window.external.AddSearchProvider(this.href); return false; }" },
+ link.href]];
}
if (!verbose && nEngines)
nFeed++;
let type = feedTypes[feed.type] || "RSS";
if (verbose)
- yield [feed.title, template.highlightURL(feed.href, true) + <span class="extra-info"> ({type})</span>];
+ yield [feed.title, [template.highlightURL(feed.href, true),
+ ["span", { class: "extra-info" }, " (" + type + ")"]]];
}
}
yield ["Title", doc.title];
yield ["URL", template.highlightURL(doc.location.href, true)];
+ let { shortURL } = this;
+ if (shortURL)
+ yield ["Short URL", template.highlightURL(shortURL, true)];
+
let ref = "referrer" in doc && doc.referrer;
if (ref)
yield ["Referrer", template.highlightURL(ref, true)];
// get meta tag data, sort and put into pageMeta[]
let metaNodes = this.focusedFrame.document.getElementsByTagName("meta");
- return Array.map(metaNodes, function (node) [(node.name || node.httpEquiv), template.highlightURL(node.content)])
+ return Array.map(metaNodes, function (node) [(node.name || node.httpEquiv),
+ template.highlightURL(node.content)])
.sort(function (a, b) util.compareIgnoreCase(a[0], b[0]));
});
Buffer.addPageInfoSection("s", "Security", function (verbose) {
- let { statusline } = this.modules
+ let { statusline } = this.modules;
let identity = this.topWindow.gIdentityHandler;
}
});
-} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
+// catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
endModule();
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: