this.evaluateXPath = util.evaluateXPath;
this.pageInfo = {};
+ this.addPageInfoSection("e", "Search Engines", function (verbose) {
+
+ let n = 1;
+ let nEngines = 0;
+ for (let { document: doc } in values(buffer.allFrames())) {
+ let engines = util.evaluateXPath(["link[@href and @rel='search' and @type='application/opensearchdescription+xml']"], doc);
+ nEngines += engines.snapshotLength;
+
+ 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>];
+ }
+
+ if (!verbose && nEngines)
+ yield nEngines + /*L*/" engine" + (nEngines > 1 ? "s" : "");
+ });
+
this.addPageInfoSection("f", "Feeds", function (verbose) {
const feedTypes = {
"application/rss+xml": "RSS",
}
if (!verbose && nFeed)
- yield nFeed + " feed" + (nFeed > 1 ? "s" : "");
+ yield nFeed + /*L*/" feed" + (nFeed > 1 ? "s" : "");
});
this.addPageInfoSection("g", "General Info", function (verbose) {
if (!verbose) {
if (pageSize[0])
- yield (pageSize[1] || pageSize[0]) + " bytes";
+ yield (pageSize[1] || pageSize[0]) + /*L*/" bytes";
yield lastMod;
return;
}
});
this.addPageInfoSection("m", "Meta Tags", function (verbose) {
+ if (!verbose)
+ return [];
+
// get meta tag data, sort and put into pageMeta[]
let metaNodes = buffer.focusedFrame.document.getElementsByTagName("meta");
.sort(function (a, b) util.compareIgnoreCase(a[0], b[0]));
});
+ let identity = window.gIdentityHandler;
+ this.addPageInfoSection("s", "Security", function (verbose) {
+ if (!verbose || !identity)
+ return; // For now
+
+ // Modified from Firefox
+ function location(data) array.compact([
+ data.city, data.state, data.country
+ ]).join(", ");
+
+ switch (statusline.security) {
+ case "secure":
+ case "extended":
+ var data = identity.getIdentityData();
+
+ yield ["Host", identity.getEffectiveHost()];
+
+ if (statusline.security === "extended")
+ yield ["Owner", data.subjectOrg];
+ else
+ yield ["Owner", _("pageinfo.s.ownerUnverified", data.subjectOrg)];
+
+ if (location(data).length)
+ yield ["Location", location(data)];
+
+ yield ["Verified by", data.caOrg];
+
+ if (identity._overrideService.hasMatchingOverride(identity._lastLocation.hostname,
+ (identity._lastLocation.port || 443),
+ data.cert, {}, {}))
+ yield ["User exception", /*L*/"true"];
+ break;
+ }
+ });
+
dactyl.commands["buffer.viewSource"] = function (event) {
let elem = event.originalTarget;
- buffer.viewSource([elem.getAttribute("href"), Number(elem.getAttribute("line"))]);
+ let obj = { url: elem.getAttribute("href"), line: Number(elem.getAttribute("line")) };
+ if (elem.hasAttribute("column"))
+ obj.column = elem.getAttribute("column");
+
+ buffer.viewSource(obj);
};
},
allFrames: function allFrames(win, focusedFirst) {
let frames = [];
(function rec(frame) {
- if (frame.document.body instanceof HTMLBodyElement)
+ if (true || frame.document.body instanceof HTMLBodyElement)
frames.push(frame);
Array.forEach(frame.frames, rec);
})(win || content);
* @returns {string}
*/
get currentWord() Buffer.currentWord(this.focusedFrame),
- getCurrentWord: deprecated("buffer.currentWord", function getCurrentWord() this.currentWord),
+ getCurrentWord: deprecated("buffer.currentWord", function getCurrentWord() Buffer.currentWord(this.focusedFrame, true)),
/**
* Returns true if a scripts are allowed to focus the given input
focusAllowed: function focusAllowed(elem) {
if (elem instanceof Window && !Editor.getEditor(elem))
return true;
+
let doc = elem.ownerDocument || elem.document || elem;
- return !options["strictfocus"] || doc.dactylFocusAllowed;
+ switch (options.get("strictfocus").getKey(doc.documentURIObject || util.newURI(doc.documentURI), "moderate")) {
+ case "despotic":
+ return elem.dactylFocusAllowed || elem.frameElement && elem.frameElement.dactylFocusAllowed;
+ case "moderate":
+ return doc.dactylFocusAllowed || elem.frameElement && elem.frameElement.ownerDocument.dactylFocusAllowed;
+ default:
+ return true;
+ }
},
/**
*/
focusElement: function focusElement(elem) {
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
+ elem.dactylFocusAllowed = true;
win.document.dactylFocusAllowed = true;
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
},
/**
- * Find the counth last link on a page matching one of the given
+ * Find the *count*th last link on a page matching one of the given
* regular expressions, or with a @rel or @rev attribute matching
* the given relation. Each frame is searched beginning with the
* last link and progressing to the first, once checking for
*/
followLink: function followLink(elem, where) {
let doc = elem.ownerDocument;
- let view = doc.defaultView;
+ let win = doc.defaultView;
let { left: offsetX, top: offsetY } = elem.getBoundingClientRect();
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey
}));
});
+ let sel = util.selectionController(win);
+ sel.getSelection(sel.SELECTION_FOCUS_REGION).collapseToStart();
});
},
* @property {nsISelectionController} The current document's selection
* controller.
*/
- get selectionController() config.browser.docShell
- .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay)
- .QueryInterface(Ci.nsISelectionController),
+ get selectionController() util.selectionController(this.focusedFrame),
/**
* Opens the appropriate context menu for *elem*.
try {
window.urlSecurityCheck(uri.spec, doc.nodePrincipal);
- io.CommandFileMode("Save link: ", {
+ io.CommandFileMode(_("buffer.prompt.saveLink") + " ", {
onSubmit: function (path) {
let file = io.File(path);
if (file.exists() && file.isDirectory())
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
let downloadListener = new window.DownloadListener(window,
- services.Transfer(uri, services.io.newFileURI(file), "",
+ services.Transfer(uri, File(file).URI, "",
null, null, null, persist));
persist.progressListener = update(Object.create(downloadListener), {
- onStateChange: function onStateChange(progress, request, flag, status) {
- if (callback && (flag & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
- dactyl.trapErrors(callback, self, uri, file, progress, request, flag, status);
+ onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
+ if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
+ dactyl.trapErrors(callback, self, uri, file, progress, request, flags, status);
return onStateChange.superapply(this, arguments);
- }
+ })
});
persist.saveURI(uri, null, null, null, null, file);
*/
findScrollable: function findScrollable(dir, horizontal) {
function find(elem) {
- while (!(elem instanceof Element) && elem.parentNode)
+ while (elem && !(elem instanceof Element) && elem.parentNode)
elem = elem.parentNode;
for (; elem && elem.parentNode instanceof Element; elem = elem.parentNode)
if (Buffer.isScrollable(elem, dir, horizontal))
doc.documentElement);
}
let doc = this.focusedFrame.document;
- return elem || doc.body || doc.documentElement;
+ return dactyl.assert(elem || doc.body || doc.documentElement);
},
/**
return win;
},
+ /**
+ * Finds the next visible element for the node path in 'jumptags'
+ * for *arg*.
+ *
+ * @param {string} arg The element in 'jumptags' to use for the search.
+ * @param {number} count The number of elements to jump.
+ * @optional
+ * @param {boolean} reverse If true, search backwards. @optional
+ */
+ findJump: function findJump(arg, count, reverse) {
+ const FUDGE = 10;
+
+ let path = options["jumptags"][arg];
+ dactyl.assert(path, _("error.invalidArgument", arg));
+
+ 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])
+
+ let idx = Math.min((count || 1) - 1, elems.length);
+ dactyl.assert(idx in elems);
+
+ let elem = elems[idx][0];
+ elem.scrollIntoView(true);
+
+ let sel = elem.ownerDocument.defaultView.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(RangeFind.endpoint(RangeFind.nodeRange(elem), true));
+ },
+
// TODO: allow callback for filtering out unwanted frames? User defined?
/**
* Shifts the focus to another frame within the buffer. Each buffer
* contains at least one frame.
*
- * @param {number} count The number of frames to skip through. A negative
+ * @param {number} count The number of frames to skip through. A negative
* count skips backwards.
*/
shiftFrameFocus: function shiftFrameFocus(count) {
* @param {Node} elem The element to query.
*/
showElementInfo: function showElementInfo(elem) {
- dactyl.echo(<>Element:<br/>{util.objectToString(elem, true)}</>, commandline.FORCE_MULTILINE);
+ dactyl.echo(<><!--L-->Element:<br/>{util.objectToString(elem, true)}</>, commandline.FORCE_MULTILINE);
},
/**
showPageInfo: function showPageInfo(verbose, sections) {
// Ctrl-g single line output
if (!verbose) {
- let file = content.location.pathname.split("/").pop() || "[No Name]";
- let title = content.document.title || "[No Title]";
+ let file = content.location.pathname.split("/").pop() || _("buffer.noName");
+ let title = content.document.title || _("buffer.noTitle");
- let info = template.map("gf",
+ let info = template.map(sections || options["pageinfo"],
function (opt) template.map(buffer.pageInfo[opt].action(), util.identity, ", "),
", ");
if (bookmarkcache.isBookmarked(this.URL))
- info += ", bookmarked";
+ info += ", " + _("buffer.bookmarked");
let pageInfoText = <>{file.quote()} [{info}] {title}</>;
dactyl.echo(pageInfoText, commandline.FORCE_SINGLELINE);
* specified *url*. Either the default viewer or the configured external
* editor is used.
*
- * @param {string} url The URL of the source.
- * @default The current buffer.
+ * @param {string|object|null} loc If a string, the URL of the source,
+ * otherwise an object with some or all of the following properties:
+ *
+ * url: The URL to view.
+ * doc: The document to view.
+ * line: The line to select.
+ * column: The column to select.
+ *
+ * If no URL is provided, the current document is used.
+ * @default The current buffer.
* @param {boolean} useExternalEditor View the source in the external editor.
*/
- viewSource: function viewSource(url, useExternalEditor) {
+ viewSource: function viewSource(loc, useExternalEditor) {
let doc = this.focusedFrame.document;
- if (isArray(url)) {
- if (options.get("editor").has("line"))
- this.viewSourceExternally(url[0] || doc, url[1]);
+ if (isObject(loc)) {
+ if (options.get("editor").has("line") || !loc.url)
+ this.viewSourceExternally(loc.doc || loc.url || doc, loc);
else
window.openDialog("chrome://global/content/viewSource.xul",
"_blank", "all,dialog=no",
- url[0], null, null, url[1]);
+ loc.url, null, null, loc.line);
}
else {
if (useExternalEditor)
- this.viewSourceExternally(url || doc);
+ this.viewSourceExternally(loc || doc);
else {
- url = url || doc.location.href;
+ let url = loc || doc.location.href;
const PREFIX = "view-source:";
if (url.indexOf(PREFIX) == 0)
url = url.substr(PREFIX.length);
* immediately.
*
* @param {Document} doc The document to view.
+ * @param {function|object} callback If a function, the callback to be
+ * called with two arguments: the nsIFile of the file, and temp, a
+ * boolean which is true if the file is temporary. Otherwise, an object
+ * with line and column properties used to determine where to open the
+ * source.
+ * @optional
*/
viewSourceExternally: Class("viewSourceExternally",
XPCOM([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), {
init: function init(doc, callback) {
this.callback = callable(callback) ? callback :
function (file, temp) {
- editor.editFileExternally({ file: file.path, line: callback },
+ editor.editFileExternally(update({ file: file.path }, callback || {}),
function () { temp && file.remove(false); });
return true;
};
return null;
},
- onStateChange: function onStateChange(progress, request, flag, status) {
- if ((flag & this.STATE_STOP) && status == 0) {
+ onStateChange: function onStateChange(progress, request, flags, status) {
+ if ((flags & this.STATE_STOP) && status == 0) {
try {
var ok = this.callback(this.file, true);
}
* Adjusts the page zoom of the current buffer relative to the
* current zoom level.
*
- * @param {number} steps The integral number of natural fractions by
- * which to adjust the current page zoom. If positive, the zoom
- * level is increased, if negative it is decreased.
+ * @param {number} steps The integral number of natural fractions by which
+ * to adjust the current page zoom. If positive, the zoom level is
+ * increased, if negative it is decreased.
* @param {boolean} fullZoom If true, zoom all content of the page,
- * including raster images. If false, zoom only text. If omitted,
- * use the current zoom function. @optional
- * @throws {FailedAssertion} if the buffer's zoom level is already
- * at its extreme in the given direction.
+ * including raster images. If false, zoom only text. If omitted, use
+ * the current zoom function. @optional
+ * @throws {FailedAssertion} if the buffer's zoom level is already at its
+ * extreme in the given direction.
*/
bumpZoomLevel: function bumpZoomLevel(steps, fullZoom) {
if (fullZoom === undefined)
this.setZoom(Math.round(values[i] * 100), fullZoom);
},
- getAllFrames: deprecated("buffer.allFrames", function getAllFrames() buffer.getAllFrames.apply(buffer, arguments)),
+ getAllFrames: deprecated("buffer.allFrames", "allFrames"),
scrollTop: deprecated("buffer.scrollToPercent", function scrollTop() buffer.scrollToPercent(null, 0)),
scrollBottom: deprecated("buffer.scrollToPercent", function scrollBottom() buffer.scrollToPercent(null, 100)),
scrollStart: deprecated("buffer.scrollToPercent", function scrollStart() buffer.scrollToPercent(0, null)),
scrollColumns: deprecated("buffer.scrollHorizontal", function scrollColumns(cols) buffer.scrollHorizontal("columns", cols)),
scrollPages: deprecated("buffer.scrollHorizontal", function scrollPages(pages) buffer.scrollVertical("pages", pages)),
scrollTo: deprecated("Buffer.scrollTo", function scrollTo(x, y) content.scrollTo(x, y)),
- textZoom: deprecated("buffer.zoomValue and buffer.fullZoom", function textZoom() config.browser.markupDocumentViewer.textZoom * 100)
+ textZoom: deprecated("buffer.zoomValue/buffer.fullZoom", function textZoom() config.browser.markupDocumentViewer.textZoom * 100)
}, {
PageInfo: Struct("PageInfo", "name", "title", "action")
.localize("title"),
*
* @returns {string}
*/
- currentWord: function currentWord(win) {
+ currentWord: function currentWord(win, select) {
let selection = win.getSelection();
if (selection.rangeCount == 0)
return "";
let range = selection.getRangeAt(0).cloneRange();
- if (range.collapsed) {
+ if (range.collapsed && range.startContainer instanceof Text) {
let re = options.get("iskeyword").regexp;
Editor.extendRange(range, true, re, true);
Editor.extendRange(range, false, re, true);
}
+ if (select) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
return util.domToString(range);
},
var names = [];
if (node.title)
- names.push([node.title, "Page Name"]);
+ names.push([node.title, /*L*/"Page Name"]);
if (node.alt)
- names.push([node.alt, "Alternate Text"]);
+ names.push([node.alt, /*L*/"Alternate Text"]);
if (!isinstance(node, Document) && node.textContent)
- names.push([node.textContent, "Link Text"]);
+ names.push([node.textContent, /*L*/"Link Text"]);
names.push([decodeURIComponent(url.replace(/.*?([^\/]*)\/*$/, "$1")), "File Name"]);
elem.scrollLeft = left;
if (top != null)
elem.scrollTop = top;
+
+ if (util.haveGecko("2.0") && !util.haveGecko("7.*"))
+ elem.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
+ .redraw();
},
/**
else
throw Error();
+ dactyl.assert(number < 0 ? elem.scrollLeft > 0 : elem.scrollLeft < elem.scrollWidth - elem.clientWidth);
+
let left = elem.dactylScrollDestX !== undefined ? elem.dactylScrollDestX : elem.scrollLeft;
elem.dactylScrollDestX = undefined;
- dactyl.assert(number < 0 ? left > 0 : left < elem.scrollWidth - elem.clientWidth);
Buffer.scrollTo(elem, left + number * increment, null);
},
else
throw Error();
+ dactyl.assert(number < 0 ? elem.scrollTop > 0 : elem.scrollTop < elem.scrollHeight - elem.clientHeight);
+
let top = elem.dactylScrollDestY !== undefined ? elem.dactylScrollDestY : elem.scrollTop;
elem.dactylScrollDestY = undefined;
- dactyl.assert(number < 0 ? top > 0 : top < elem.scrollHeight - elem.clientHeight);
Buffer.scrollTo(elem, null, top + number * increment);
},
},
openUploadPrompt: function openUploadPrompt(elem) {
- io.CommandFileMode("Upload file: ", {
+ io.CommandFileMode(_("buffer.prompt.uploadFile") + " ", {
onSubmit: function onSubmit(path) {
let file = io.File(path);
dactyl.assert(file.exists());
// FIXME: arg handling is a bit of a mess, check for filename
dactyl.assert(!arg || arg[0] == ">" && !util.OS.isWindows,
- _("error.trailing"));
+ _("error.trailingCharacters"));
+
+ const PRINTER = "PostScript/default";
+ const BRANCH = "print.printer_" + PRINTER + ".";
prefs.withContext(function () {
if (arg) {
- prefs.set("print.print_to_file", "true");
- prefs.set("print.print_to_filename", io.File(arg.substr(1)).path);
+ 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);
+
dactyl.echomsg(_("print.toFile", arg.substr(1)));
}
else
level = Math.constrain(level, Buffer.ZOOM_MIN, Buffer.ZOOM_MAX);
}
else
- dactyl.assert(false, _("error.trailing"));
+ dactyl.assert(false, _("error.trailingCharacters"));
buffer.setZoom(level, args.bang);
},
let styles = iter([s.title, []] for (s in values(buffer.alternateStyleSheets))).toObject();
buffer.alternateStyleSheets.forEach(function (style) {
- styles[style.title].push(style.href || "inline");
+ styles[style.title].push(style.href || _("style.inline"));
});
context.completions = [[title, href.join(", ")] for ([title, href] in Iterator(styles))];
};
- completion.buffer = function buffer(context) {
+ completion.buffer = function buffer(context, visible) {
let filter = context.filter.toLowerCase();
+
let defItem = { parent: { getTitle: function () "" } };
+
let tabGroups = {};
tabs.getGroups();
- tabs.allTabs.forEach(function (tab, i) {
+ tabs[visible ? "visibleTabs" : "allTabs"].forEach(function (tab, i) {
let group = (tab.tabItem || tab._tabViewTabItem || defItem).parent || defItem.parent;
- if (!set.has(tabGroups, group.id))
+ if (!Set.has(tabGroups, group.id))
tabGroups[group.id] = [group.getTitle(), []];
+
group = tabGroups[group.id];
group[1].push([i, tab.linkedBrowser]);
});
command: function () "tabs.select"
};
context.compare = CompletionContext.Sort.number;
- context.filters = [CompletionContext.Filter.textDescription];
+ context.filters[0] = CompletionContext.Filter.textDescription;
for (let [id, vals] in Iterator(tabGroups))
context.fork(id, 0, this, function (context, [name, browsers]) {
else if (i == tabs.index(tabs.alternate))
indicator = "#";
- let tab = tabs.getTab(i);
+ let tab = tabs.getTab(i, visible);
let url = browser.contentDocument.location.href;
i = i + 1;
return {
- text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
+ text: [i + ": " + (tab.label || /*L*/"(Untitled)"), i + ": " + url],
tab: tab,
id: i - 1,
url: url,
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
mappings.add([modes.NORMAL],
- ["<C-a>"], "Increment last number in URL",
+ ["<C-a>", "<increment-url-path>"], "Increment last number in URL",
function (args) { buffer.incrementURL(Math.max(args.count, 1)); },
{ count: true });
mappings.add([modes.NORMAL],
- ["<C-x>"], "Decrement last number in URL",
+ ["<C-x>", "<decrement-url-path>"], "Decrement last number in URL",
function (args) { buffer.incrementURL(-Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.NORMAL], ["gu"],
+ mappings.add([modes.NORMAL], ["gu", "<open-parent-path>"],
"Go to parent directory",
function (args) { buffer.climbUrlPath(Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.NORMAL], ["gU"],
+ mappings.add([modes.NORMAL], ["gU", "<open-root-path>"],
"Go to the root of the website",
function () { buffer.climbUrlPath(-1); });
},
{ count: true });
- mappings.add([modes.COMMAND], ["i", "<Insert>"],
- "Start caret mode",
+ mappings.add([modes.NORMAL], ["i", "<Insert>"],
+ "Start Caret mode",
function () { modes.push(modes.CARET); });
- mappings.add([modes.COMMAND], ["<C-c>"],
+ mappings.add([modes.NORMAL], ["<C-c>", "<stop-load>"],
"Stop loading the current web page",
function () { ex.stop(); });
"Scroll to the absolute right of the document",
function () { buffer.scrollToPercent(100, null); });
- mappings.add([modes.COMMAND], ["gg", "<Home>"],
+ mappings.add([modes.COMMAND], ["gg", "<Home>", "<scroll-top>"],
"Go to the top of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 0); },
{ count: true });
- mappings.add([modes.COMMAND], ["G", "<End>"],
+ mappings.add([modes.COMMAND], ["G", "<End>", "<scroll-bottom>"],
"Go to the end of the document",
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
{ count: true });
function (args) { buffer._scrollByScrollSize(args.count, false); },
{ count: true });
- mappings.add([modes.COMMAND], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-page-up>"],
+ mappings.add([modes.COMMAND], ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-up-page>"],
"Scroll up a full page",
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["<C-f>", "<PageDown>", "<Space>", "<scroll-page-down>"],
+ mappings.add([modes.COMMAND], ["<Space>"],
+ "Scroll down a full page",
+ function (args) {
+ if (isinstance(content.document.activeElement, [HTMLInputElement, HTMLButtonElement]))
+ return Events.PASS;
+ buffer.scrollVertical("pages", Math.max(args.count, 1));
+ },
+ { count: true });
+
+ mappings.add([modes.COMMAND], ["<C-f>", "<PageDown>", "<scroll-down-page>"],
"Scroll down a full page",
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["]f", "<previous-frame>"],
+ mappings.add([modes.NORMAL], ["]f", "<previous-frame>"],
"Focus next frame",
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["[f", "<next-frame>"],
+ mappings.add([modes.NORMAL], ["[f", "<next-frame>"],
"Focus previous frame",
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
{ count: true });
- mappings.add([modes.COMMAND], ["]]", "<next-page>"],
+ mappings.add([modes.NORMAL], ["["],
+ "Jump to the previous element as defined by 'jumptags'",
+ function (args) { buffer.findJump(args.arg, args.count, 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); },
+ { arg: true, count: true });
+
+ mappings.add([modes.NORMAL], ["{"],
+ "Jump to the previous paragraph",
+ function (args) { buffer.findJump("p", args.count, true); },
+ { count: true });
+
+ mappings.add([modes.NORMAL], ["}"],
+ "Jump to the next paragraph",
+ function (args) { buffer.findJump("p", args.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);
},
{ count: true });
- mappings.add([modes.COMMAND], ["[[", "<previous-page>"],
+ 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);
},
{ count: true });
- mappings.add([modes.COMMAND], ["gf", "<view-source>"],
+ mappings.add([modes.NORMAL], ["gf", "<view-source>"],
"Toggle between rendered and source view",
function () { buffer.viewSource(null, false); });
- mappings.add([modes.COMMAND], ["gF", "<view-source-externally>"],
+ mappings.add([modes.NORMAL], ["gF", "<view-source-externally>"],
"View source with an external editor",
function () { buffer.viewSource(null, true); });
- mappings.add([modes.COMMAND], ["gi", "<focus-input>"],
+ mappings.add([modes.NORMAL], ["gi", "<focus-input>"],
"Focus last used input field",
function (args) {
let elem = buffer.lastInputField;
if (args.count >= 1 || !elem || !events.isContentNode(elem)) {
- let xpath = ["frame", "iframe", "input", "textarea[not(@disabled) and not(@readonly)]"];
+ let xpath = ["frame", "iframe", "input", "xul:textbox", "textarea[not(@disabled) and not(@readonly)]"];
let frames = buffer.allFrames(null, true);
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
return Editor.getEditor(elem.contentWindow);
- if (elem.readOnly || elem instanceof HTMLInputElement && !set.has(util.editableInputs, elem.type))
+ if (elem.readOnly || elem instanceof HTMLInputElement && !Set.has(util.editableInputs, elem.type))
return false;
let computedStyle = util.computedStyle(elem);
let rect = elem.getBoundingClientRect();
return computedStyle.visibility != "hidden" && computedStyle.display != "none" &&
- computedStyle.MozUserFocus != "ignore" && rect.width && rect.height;
+ (elem instanceof Ci.nsIDOMXULTextBoxElement || computedStyle.MozUserFocus != "ignore") &&
+ rect.width && rect.height;
});
dactyl.assert(elements.length > 0);
},
{ count: true });
- mappings.add([modes.COMMAND], ["gP"],
- "Open (]put) a URL based on the current clipboard contents in a new buffer",
+ function url() {
+ let url = dactyl.clipboardRead();
+ dactyl.assert(url, _("error.clipboardEmpty"));
+
+ let proto = /^([-\w]+):/.exec(url);
+ if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc && !RegExp(options["urlseparator"]).test(url))
+ return url.replace(/\s+/g, "");
+ return url;
+ }
+
+ mappings.add([modes.NORMAL], ["gP"],
+ "Open (put) a URL based on the current clipboard contents in a new background buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
+ dactyl.open(url(), { from: "paste", where: dactyl.NEW_TAB, background: true });
});
- mappings.add([modes.COMMAND], ["p", "<MiddleMouse>", "<open-clipboard-url>"],
+ mappings.add([modes.NORMAL], ["p", "<MiddleMouse>", "<open-clipboard-url>"],
"Open (put) a URL based on the current clipboard contents in the current buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url);
+ dactyl.open(url());
});
- mappings.add([modes.COMMAND], ["P", "<tab-open-clipboard-url>"],
+ mappings.add([modes.NORMAL], ["P", "<tab-open-clipboard-url>"],
"Open (put) a URL based on the current clipboard contents in a new buffer",
function () {
- let url = dactyl.clipboardRead();
- dactyl.assert(url, _("error.clipboardEmpty"));
- dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB });
+ dactyl.open(url(), { from: "paste", where: dactyl.NEW_TAB });
});
// reloading
- mappings.add([modes.COMMAND], ["r", "<reload>"],
+ mappings.add([modes.NORMAL], ["r", "<reload>"],
"Reload the current web page",
function () { tabs.reload(tabs.getTab(), false); });
- mappings.add([modes.COMMAND], ["R", "<full-reload>"],
+ mappings.add([modes.NORMAL], ["R", "<full-reload>"],
"Reload while skipping the cache",
function () { tabs.reload(tabs.getTab(), true); });
});
// zooming
- mappings.add([modes.COMMAND], ["zi", "+", "<text-zoom-in>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["zm", "<text-zoom-more>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["zo", "-", "<text-zoom-out>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["zr", "<text-zoom-reduce>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["zz", "<text-zoom>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZI", "zI", "<full-zoom-in>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZM", "zM", "<full-zoom-more>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZO", "zO", "<full-zoom-out>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["ZR", "zR", "<full-zoom-reduce>"],
+ 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); },
{ count: true });
- mappings.add([modes.COMMAND], ["zZ", "<full-zoom>"],
+ 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); },
{ count: true });
// page info
- mappings.add([modes.COMMAND], ["<C-g>", "<page-info>"],
+ mappings.add([modes.NORMAL], ["<C-g>", "<page-info>"],
"Print the current file name",
function () { buffer.showPageInfo(false); });
- mappings.add([modes.COMMAND], ["g<C-g>", "<more-page-info>"],
+ mappings.add([modes.NORMAL], ["g<C-g>", "<more-page-info>"],
"Print file information",
function () { buffer.showPageInfo(true); });
},
validator: function (value) RegExp(value)
});
+ options.add(["jumptags", "jt"],
+ "XPath or CSS selector strings of jumpable elements for extended hint modes",
+ "stringmap", {
+ "p": "p,table,ul,ol,blockquote",
+ "h": "h1,h2,h3,h4,h5,h6"
+ },
+ {
+ keepQuotes: true,
+ setter: function (vals) {
+ for (let [k, v] in Iterator(vals))
+ vals[k] = update(new String(v), { matcher: util.compileMatcher(Option.splitList(v)) });
+ return vals;
+ },
+ validator: function (value) util.validateMatcher.call(this, value)
+ && Object.keys(value).every(function (v) v.length == 1)
+ });
+
options.add(["nextpattern"],
"Patterns to use when guessing the next page in a document sequence",
"regexplist", UTF8("'\\bnext\\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\\bmore\\b'"),
options.add(["pageinfo", "pa"],
"Define which sections are shown by the :pageinfo command",
- "charlist", "gfm",
+ "charlist", "gesfm",
{ get values() values(buffer.pageInfo).toObject() });
options.add(["scroll", "scr"],