]> git.donarmstrong.com Git - dactyl.git/blob - common/content/history.js
Import 1.0b7.1 supporting Firefox up to 8.*
[dactyl.git] / common / content / history.js
1 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
2 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
3 // Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
4 //
5 // This work is licensed for reuse under an MIT license. Details are
6 // given in the LICENSE.txt file included with this file.
7 "use strict";
8
9 var History = Module("history", {
10     get format() bookmarks.format,
11
12     get service() services.history,
13
14     get: function get(filter, maxItems, order) {
15         // no query parameters will get all history
16         let query = services.history.getNewQuery();
17         let options = services.history.getNewQueryOptions();
18
19         if (typeof filter == "string")
20             filter = { searchTerms: filter };
21         for (let [k, v] in Iterator(filter))
22             query[k] = v;
23
24         order = order || "+date";
25         dactyl.assert((order = /^([+-])(.+)/.exec(order)) &&
26                       (order = "SORT_BY_" + order[2].toUpperCase() + "_" +
27                         (order[1] == "+" ? "ASCENDING" : "DESCENDING")) &&
28                       order in options,
29                      _("error.invalidSort", order));
30
31         options.sortingMode = options[order];
32         options.resultType = options.RESULTS_AS_URI;
33         if (maxItems > 0)
34             options.maxResults = maxItems;
35
36         // execute the query
37         let root = services.history.executeQuery(query, options).root;
38         root.containerOpen = true;
39         let items = iter(util.range(0, root.childCount)).map(function (i) {
40             let node = root.getChild(i);
41             return {
42                 url: node.uri,
43                 title: node.title,
44                 icon: node.icon ? node.icon : DEFAULT_FAVICON
45             };
46         }).toArray();
47         root.containerOpen = false; // close a container after using it!
48
49         return items;
50     },
51
52     get session() {
53         let sh = window.getWebNavigation().sessionHistory;
54         let obj = [];
55         obj.index = sh.index;
56         obj.__iterator__ = function () array.iterItems(this);
57         for (let i in util.range(0, sh.count)) {
58             obj[i] = update(Object.create(sh.getEntryAtIndex(i, false)),
59                             { index: i });
60             memoize(obj[i], "icon",
61                 function () services.favicon.getFaviconImageForPage(this.URI).spec);
62         }
63         return obj;
64     },
65
66     stepTo: function stepTo(steps) {
67         let start = 0;
68         let end = window.getWebNavigation().sessionHistory.count - 1;
69         let current = window.getWebNavigation().sessionHistory.index;
70
71         if (current == start && steps < 0 || current == end && steps > 0)
72             dactyl.beep();
73         else {
74             let index = Math.constrain(current + steps, start, end);
75             try {
76                 window.getWebNavigation().gotoIndex(index);
77             }
78             catch (e) {} // We get NS_ERROR_FILE_NOT_FOUND if files in history don't exist
79         }
80     },
81
82     goToStart: function goToStart() {
83         let index = window.getWebNavigation().sessionHistory.index;
84
85         if (index > 0)
86             window.getWebNavigation().gotoIndex(0);
87         else
88             dactyl.beep();
89
90     },
91
92     goToEnd: function goToEnd() {
93         let sh = window.getWebNavigation().sessionHistory;
94         let max = sh.count - 1;
95
96         if (sh.index < max)
97             window.getWebNavigation().gotoIndex(max);
98         else
99             dactyl.beep();
100
101     },
102
103     // if openItems is true, open the matching history items in tabs rather than display
104     list: function list(filter, openItems, maxItems, sort) {
105         // FIXME: returning here doesn't make sense
106         //   Why the hell doesn't it make sense? --Kris
107         // See comment at bookmarks.list --djk
108         if (!openItems)
109             return completion.listCompleter("history", filter, maxItems, maxItems, sort);
110         let items = completion.runCompleter("history", filter, maxItems, maxItems, sort);
111
112         if (items.length)
113             return dactyl.open(items.map(function (i) i.url), dactyl.NEW_TAB);
114
115         if (filter.length > 0)
116             dactyl.echoerr(_("history.noMatching", filter.quote()));
117         else
118             dactyl.echoerr(_("history.none"));
119         return null;
120     }
121 }, {
122 }, {
123     commands: function () {
124         commands.add(["ba[ck]"],
125             "Go back in the browser history",
126             function (args) {
127                 let url = args[0];
128
129                 if (args.bang)
130                     history.goToStart();
131                 else {
132                     if (url) {
133                         let sh = history.session;
134                         if (/^\d+(:|$)/.test(url) && sh.index - parseInt(url) in sh)
135                             return void window.getWebNavigation().gotoIndex(sh.index - parseInt(url));
136
137                         for (let [i, ent] in Iterator(sh.slice(0, sh.index).reverse()))
138                             if (ent.URI.spec == url)
139                                 return void window.getWebNavigation().gotoIndex(i);
140                         dactyl.echoerr(_("history.noURL"));
141                     }
142                     else
143                         history.stepTo(-Math.max(args.count, 1));
144                 }
145                 return null;
146             },
147             {
148                 argCount: "?",
149                 bang: true,
150                 completer: function completer(context) {
151                     let sh = history.session;
152
153                     context.anchored = false;
154                     context.compare = CompletionContext.Sort.unsorted;
155                     context.filters = [CompletionContext.Filter.textDescription];
156                     context.completions = sh.slice(0, sh.index).reverse();
157                     context.keys = { text: function (item) (sh.index - item.index) + ": " + item.URI.spec, description: "title", icon: "icon" };
158                 },
159                 count: true,
160                 literal: 0,
161                 privateData: true
162             });
163
164         commands.add(["fo[rward]", "fw"],
165             "Go forward in the browser history",
166             function (args) {
167                 let url = args.literalArg;
168
169                 if (args.bang)
170                     history.goToEnd();
171                 else {
172                     if (url) {
173                         let sh = history.session;
174                         if (/^\d+(:|$)/.test(url) && sh.index + parseInt(url) in sh)
175                             return void window.getWebNavigation().gotoIndex(sh.index + parseInt(url));
176
177                         for (let [i, ent] in Iterator(sh.slice(sh.index + 1)))
178                             if (ent.URI.spec == url)
179                                 return void window.getWebNavigation().gotoIndex(i);
180                         dactyl.echoerr(_("history.noURL"));
181                     }
182                     else
183                         history.stepTo(Math.max(args.count, 1));
184                 }
185                 return null;
186             },
187             {
188                 argCount: "?",
189                 bang: true,
190                 completer: function completer(context) {
191                     let sh = history.session;
192
193                     context.anchored = false;
194                     context.compare = CompletionContext.Sort.unsorted;
195                     context.filters = [CompletionContext.Filter.textDescription];
196                     context.completions = sh.slice(sh.index + 1);
197                     context.keys = { text: function (item) (item.index - sh.index) + ": " + item.URI.spec, description: "title", icon: "icon" };
198                 },
199                 count: true,
200                 literal: 0,
201                 privateData: true
202             });
203
204         commands.add(["hist[ory]", "hs"],
205             "Show recently visited URLs",
206             function (args) { history.list(args.join(" "), args.bang, args["-max"], args["-sort"]); }, {
207                 bang: true,
208                 completer: function (context, args) completion.history(context, args["-max"], args["-sort"]),
209                 options: [
210                     {
211                         names: ["-max", "-m"],
212                         description: "The maximum number of items to list",
213                         default: 1000,
214                         type: CommandOption.INT
215                     },
216                     {
217                         names: ["-sort", "-s"],
218                         type: CommandOption.STRING,
219                         description: "The sort order of the results",
220                         completer: function (context, args) {
221                             context.compare = CompletionContext.Sort.unsorted;
222                             return array.flatten([
223                                 "annotation",
224                                 "date",
225                                 "date added",
226                                 "keyword",
227                                 "last modified",
228                                 "tags",
229                                 "title",
230                                 "uri",
231                                 "visitcount"
232                             ].map(function (order) [
233                                   ["+" + order.replace(" ", ""), /*L*/"Sort by " + order + " ascending"],
234                                   ["-" + order.replace(" ", ""), /*L*/"Sort by " + order + " descending"]
235                             ]));
236                         }
237                     }
238                 ],
239                 privateData: true
240             });
241     },
242     completion: function () {
243         completion.domain = function (context) {
244             context.anchored = false;
245             context.compare = function (a, b) String.localeCompare(a.key, b.key);
246             context.keys = { text: util.identity, description: util.identity,
247                 key: function (host) host.split(".").reverse().join(".") };
248
249             // FIXME: Schema-specific
250             context.generate = function () [
251                 Array.slice(row.rev_host).reverse().join("").slice(1)
252                 for (row in iter(services.history.DBConnection
253                                          .createStatement("SELECT DISTINCT rev_host FROM moz_places WHERE rev_host IS NOT NULL;")))
254             ].slice(2);
255         };
256
257         completion.history = function _history(context, maxItems, sort) {
258             context.format = history.format;
259             context.title = ["History"];
260             context.compare = CompletionContext.Sort.unsorted;
261             //context.background = true;
262             if (maxItems == null)
263                 context.maxItems = maxItems;
264             if (maxItems && context.maxItems == null)
265                 context.maxItems = 100;
266             context.regenerate = true;
267             context.generate = function () history.get(context.filter, this.maxItems, sort);
268         };
269
270         completion.addUrlCompleter("h", "History", completion.history);
271     },
272     mappings: function () {
273         var myModes = config.browserModes;
274
275         mappings.add(myModes,
276             ["<C-o>"], "Go to an older position in the jump list",
277             function (args) { history.stepTo(-Math.max(args.count, 1)); },
278             { count: true });
279
280         mappings.add(myModes,
281             ["<C-i>"], "Go to a newer position in the jump list",
282             function (args) { history.stepTo(Math.max(args.count, 1)); },
283             { count: true });
284
285         mappings.add(myModes,
286             ["H", "<A-Left>", "<M-Left>"], "Go back in the browser history",
287             function (args) { history.stepTo(-Math.max(args.count, 1)); },
288             { count: true });
289
290         mappings.add(myModes,
291             ["L", "<A-Right>", "<M-Right>"], "Go forward in the browser history",
292             function (args) { history.stepTo(Math.max(args.count, 1)); },
293             { count: true });
294     }
295 });
296
297 // vim: set fdm=marker sw=4 ts=4 et: