//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-"use strict";
+/* use strict */
var History = Module("history", {
+ SORT_DEFAULT: "-date",
+
get format() bookmarks.format,
get service() services.history,
- get: function get(filter, maxItems, order) {
+ get: function get(filter, maxItems, sort) {
+ sort = sort || this.SORT_DEFAULT;
+
+ if (isString(filter))
+ filter = { searchTerms: filter };
+
// no query parameters will get all history
let query = services.history.getNewQuery();
let options = services.history.getNewQueryOptions();
- if (typeof filter == "string")
- filter = { searchTerms: filter };
for (let [k, v] in Iterator(filter))
query[k] = v;
- order = order || "+date";
- dactyl.assert((order = /^([+-])(.+)/.exec(order)) &&
- (order = "SORT_BY_" + order[2].toUpperCase() + "_" +
- (order[1] == "+" ? "ASCENDING" : "DESCENDING")) &&
- order in options,
- _("error.invalidSort", order));
+ let res = /^([+-])(.+)/.exec(sort);
+ dactyl.assert(res, _("error.invalidSort", sort));
- options.sortingMode = options[order];
+ let [, dir, field] = res;
+ let _sort = "SORT_BY_" + field.toUpperCase() + "_" +
+ { "+": "ASCENDING", "-": "DESCENDING" }[dir];
+
+ dactyl.assert(_sort in options,
+ _("error.invalidSort", sort));
+
+ options.sortingMode = options[_sort];
options.resultType = options.RESULTS_AS_URI;
if (maxItems > 0)
options.maxResults = maxItems;
- // execute the query
let root = services.history.executeQuery(query, options).root;
root.containerOpen = true;
- let items = iter(util.range(0, root.childCount)).map(function (i) {
- let node = root.getChild(i);
- return {
- url: node.uri,
- title: node.title,
- icon: node.icon ? node.icon : DEFAULT_FAVICON
- };
- }).toArray();
- root.containerOpen = false; // close a container after using it!
+ try {
+ var items = iter(util.range(0, root.childCount)).map(function (i) {
+ let node = root.getChild(i);
+ return {
+ url: node.uri,
+ title: node.title,
+ icon: node.icon ? node.icon : BookmarkCache.DEFAULT_FAVICON
+ };
+ }).toArray();
+ }
+ finally {
+ root.containerOpen = false;
+ }
return items;
},
get session() {
- let sh = window.getWebNavigation().sessionHistory;
+ let webNav = window.getWebNavigation()
+ let sh = webNav.sessionHistory;
+
let obj = [];
- obj.index = sh.index;
+ obj.__defineGetter__("index", function () sh.index);
+ obj.__defineSetter__("index", function (val) { webNav.gotoIndex(val) });
obj.__iterator__ = function () array.iterItems(this);
- for (let i in util.range(0, sh.count)) {
- obj[i] = update(Object.create(sh.getEntryAtIndex(i, false)),
- { index: i });
- memoize(obj[i], "icon",
- function () services.favicon.getFaviconImageForPage(this.URI).spec);
- }
+
+ for (let item in iter(sh.SHistoryEnumerator, Ci.nsIHistoryEntry))
+ obj.push(update(Object.create(item), {
+ index: obj.length,
+ icon: Class.Memoize(function () services.favicon.getFaviconImageForPage(this.URI).spec)
+ }));
return obj;
},
- stepTo: function stepTo(steps) {
- let start = 0;
- let end = window.getWebNavigation().sessionHistory.count - 1;
- let current = window.getWebNavigation().sessionHistory.index;
+ /**
+ * Step to the given offset in the history stack.
+ *
+ * @param {number} steps The possibly negative number of steps to
+ * step.
+ * @param {boolean} jumps If true, take into account jumps in the
+ * marks stack. @optional
+ */
+ stepTo: function stepTo(steps, jumps) {
+ if (dactyl.forceOpen.target == dactyl.NEW_TAB)
+ tabs.cloneTab(tabs.getTab(), true);
+
+ if (jumps)
+ steps -= marks.jump(steps);
+ if (steps == 0)
+ return;
+
+ let sh = this.session;
+ dactyl.assert(steps > 0 && sh.index < sh.length - 1 || steps < 0 && sh.index > 0);
+
+ try {
+ sh.index = Math.constrain(sh.index + steps, 0, sh.length - 1);
+ }
+ catch (e if e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {}
+ },
- if (current == start && steps < 0 || current == end && steps > 0)
- dactyl.beep();
- else {
- let index = Math.constrain(current + steps, start, end);
- try {
- window.getWebNavigation().gotoIndex(index);
+ /**
+ * Search for the *steps*th next *item* in the history list.
+ *
+ * @param {string} item The nebulously defined item to search for.
+ * @param {number} steps The number of steps to step.
+ */
+ search: function search(item, steps) {
+ var ctxt;
+ var filter = function (item) true;
+ if (item == "domain")
+ var filter = function (item) {
+ let res = item.URI.hostPort != ctxt;
+ ctxt = item.URI.hostPort;
+ return res;
+ };
+
+ let sh = this.session;
+ let idx;
+ let sign = steps / Math.abs(steps);
+
+ filter(sh[sh.index]);
+ for (let i = sh.index + sign; steps && i >= 0 && i < sh.length; i += sign)
+ if (filter(sh[i])) {
+ idx = i;
+ steps -= sign;
}
- catch (e) {} // We get NS_ERROR_FILE_NOT_FOUND if files in history don't exist
- }
+
+ dactyl.assert(idx != null);
+ sh.index = idx;
},
goToStart: function goToStart() {
],
privateData: true
});
+
+ commands.add(["ju[mps]"],
+ "Show jumplist",
+ function () {
+ let sh = history.session;
+ let index = sh.index;
+
+ let jumps = marks.jumps;
+ if (jumps.index < 0)
+ jumps = [sh[sh.index]];
+ else {
+ index += jumps.index;
+ jumps = jumps.locations.map(function (l) ({
+ __proto__: l,
+ title: buffer.title,
+ get URI() util.newURI(this.location)
+ }));
+ }
+
+ let list = sh.slice(0, sh.index)
+ .concat(jumps)
+ .concat(sh.slice(sh.index + 1));
+
+ commandline.commandOutput(template.jumps(index, list));
+ },
+ { argCount: "0" });
+
},
completion: function () {
completion.domain = function (context) {
context.generate = function () history.get(context.filter, this.maxItems, sort);
};
- completion.addUrlCompleter("h", "History", completion.history);
+ completion.addUrlCompleter("history", "History", completion.history);
},
mappings: function () {
- var myModes = config.browserModes;
-
- mappings.add(myModes,
- ["<C-o>"], "Go to an older position in the jump list",
- function (args) { history.stepTo(-Math.max(args.count, 1)); },
- { count: true });
-
- mappings.add(myModes,
- ["<C-i>"], "Go to a newer position in the jump list",
- function (args) { history.stepTo(Math.max(args.count, 1)); },
- { count: true });
-
- mappings.add(myModes,
- ["H", "<A-Left>", "<M-Left>"], "Go back in the browser history",
- function (args) { history.stepTo(-Math.max(args.count, 1)); },
- { count: true });
-
- mappings.add(myModes,
- ["L", "<A-Right>", "<M-Right>"], "Go forward in the browser history",
- function (args) { history.stepTo(Math.max(args.count, 1)); },
- { count: true });
+ function bind() mappings.add.apply(mappings, [config.browserModes].concat(Array.slice(arguments)));
+
+ bind(["<C-o>"], "Go to an older position in the jump list",
+ function ({ count }) { history.stepTo(-Math.max(count, 1), true); },
+ { count: true });
+
+ bind(["<C-i>"], "Go to a newer position in the jump list",
+ function ({ count }) { history.stepTo(Math.max(count, 1), true); },
+ { count: true });
+
+ bind(["H", "<A-Left>", "<M-Left>"], "Go back in the browser history",
+ function ({ count }) { history.stepTo(-Math.max(count, 1)); },
+ { count: true });
+
+ bind(["L", "<A-Right>", "<M-Right>"], "Go forward in the browser history",
+ function ({ count }) { history.stepTo(Math.max(count, 1)); },
+ { count: true });
+
+ bind(["[d"], "Go back to the previous domain in the browser history",
+ function ({ count }) { history.search("domain", -Math.max(count, 1)) },
+ { count: true });
+
+ bind(["]d"], "Go forward to the next domain in the browser history",
+ function ({ count }) { history.search("domain", Math.max(count, 1)) },
+ { count: true });
}
});