]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/history.js
Import 1.0rc1 supporting Firefox up to 11.*
[dactyl.git] / common / content / history.js
index a4bb2838a2102317a65682a4a562f8df7404f146..94985d2648024d9d7afb2b7906b009c3a58aaa78 100644 (file)
 //
 // 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,
-            ["<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 });
     }
 });