X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fcontent%2Fhistory.js;fp=common%2Fcontent%2Fhistory.js;h=94985d2648024d9d7afb2b7906b009c3a58aaa78;hb=9044153cb63835e39b9de8ec4ade237c03e3888a;hp=a4bb2838a2102317a65682a4a562f8df7404f146;hpb=70740024f9c028c1fd63e1a1850ab062ff956054;p=dactyl.git diff --git a/common/content/history.js b/common/content/history.js index a4bb283..94985d2 100644 --- a/common/content/history.js +++ b/common/content/history.js @@ -4,79 +4,134 @@ // // 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() { @@ -238,6 +293,33 @@ var History = Module("history", { ], 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) { @@ -267,30 +349,34 @@ var History = Module("history", { 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, - [""], "Go to an older position in the jump list", - function (args) { history.stepTo(-Math.max(args.count, 1)); }, - { count: true }); - - mappings.add(myModes, - [""], "Go to a newer position in the jump list", - function (args) { history.stepTo(Math.max(args.count, 1)); }, - { count: true }); - - mappings.add(myModes, - ["H", "", ""], "Go back in the browser history", - function (args) { history.stepTo(-Math.max(args.count, 1)); }, - { count: true }); - - mappings.add(myModes, - ["L", "", ""], "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([""], "Go to an older position in the jump list", + function ({ count }) { history.stepTo(-Math.max(count, 1), true); }, + { count: true }); + + bind([""], "Go to a newer position in the jump list", + function ({ count }) { history.stepTo(Math.max(count, 1), true); }, + { count: true }); + + bind(["H", "", ""], "Go back in the browser history", + function ({ count }) { history.stepTo(-Math.max(count, 1)); }, + { count: true }); + + bind(["L", "", ""], "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 }); } });