OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('downloads', function() { | 5 cr.define('downloads', function() { |
6 /** | 6 var Manager = Polymer({ |
7 * Class to own and manage download items. | 7 is: 'downloads-manager', |
8 * @constructor | |
9 */ | |
10 function Manager() {} | |
11 | 8 |
12 cr.addSingletonGetter(Manager); | 9 created: function() { |
| 10 /** @private {!downloads.ActionService} */ |
| 11 this.actionService_ = new downloads.ActionService; |
| 12 }, |
13 | 13 |
14 Manager.prototype = { | 14 properties: { |
15 /** @private {string} */ | 15 hasDownloads_: { |
16 searchText_: '', | 16 type: Boolean, |
17 | 17 value: false, |
18 /** | 18 }, |
19 * Sets the search text, updates related UIs, and tells the browser. | |
20 * @param {string} searchText Text we're searching for. | |
21 * @private | |
22 */ | |
23 setSearchText_: function(searchText) { | |
24 this.searchText_ = searchText; | |
25 | |
26 $('downloads-summary-text').textContent = this.searchText_ ? | |
27 loadTimeData.getStringF('searchResultsFor', this.searchText_) : ''; | |
28 | |
29 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). | |
30 function trim(s) { return s.trim(); } | |
31 chrome.send('getDownloads', searchText.split(/"([^"]*)"/).map(trim)); | |
32 }, | 19 }, |
33 | 20 |
34 /** | 21 /** |
35 * @return {number} A guess at how many items could be visible at once. | 22 * @return {number} A guess at how many items could be visible at once. |
36 * @private | 23 * @private |
37 */ | 24 */ |
38 guesstimateNumberOfVisibleItems_: function() { | 25 guesstimateNumberOfVisibleItems_: function() { |
39 var toolbarHeight = $('downloads-toolbar').offsetHeight; | 26 var toolbarHeight = this.$.toolbar.offsetHeight; |
40 var summaryHeight = $('downloads-summary').offsetHeight; | 27 return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1; |
41 var nonItemSpace = toolbarHeight + summaryHeight; | |
42 return Math.floor((window.innerHeight - nonItemSpace) / 46) + 1; | |
43 }, | 28 }, |
44 | 29 |
45 /** | 30 /** |
| 31 * @param {Event} e |
| 32 * @private |
| 33 */ |
| 34 onCanExecute_: function(e) { |
| 35 e = /** @type {cr.ui.CanExecuteEvent} */(e); |
| 36 switch (e.command.id) { |
| 37 case 'undo-command': |
| 38 e.canExecute = this.$.toolbar.canUndo(); |
| 39 break; |
| 40 case 'clear-all-command': |
| 41 e.canExecute = true; |
| 42 break; |
| 43 } |
| 44 }, |
| 45 |
| 46 /** |
| 47 * @param {Event} e |
| 48 * @private |
| 49 */ |
| 50 onCommand_: function(e) { |
| 51 if (e.command.id == 'clear-all-command') |
| 52 this.actionService_.clearAll(); |
| 53 else if (e.command.id == 'undo-command') |
| 54 this.actionService_.undo(); |
| 55 }, |
| 56 |
| 57 /** @private */ |
| 58 onLoad_: function() { |
| 59 this.$.toolbar.setActionService(this.actionService_); |
| 60 |
| 61 cr.ui.decorate('command', cr.ui.Command); |
| 62 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
| 63 document.addEventListener('command', this.onCommand_.bind(this)); |
| 64 |
| 65 // Shows all downloads. |
| 66 this.actionService_.search(''); |
| 67 }, |
| 68 |
| 69 /** |
| 70 * @return {number} The number of downloads shown on the page. |
| 71 * @private |
| 72 */ |
| 73 size_: function() { |
| 74 return this.items_.length; |
| 75 }, |
| 76 |
| 77 /** |
46 * Called when all items need to be updated. | 78 * Called when all items need to be updated. |
47 * @param {!Array<!downloads.Data>} list A list of new download data. | 79 * @param {!Array<!downloads.Data>} list A list of new download data. |
48 * @private | 80 * @private |
49 */ | 81 */ |
50 updateAll_: function(list) { | 82 updateAll_: function(list) { |
51 var oldIdMap = this.idMap_ || {}; | 83 var oldIdMap = this.idMap_ || {}; |
52 | 84 |
53 /** @private {!Object<!downloads.ItemView>} */ | 85 /** @private {!Object<!downloads.ItemView>} */ |
54 this.idMap_ = {}; | 86 this.idMap_ = {}; |
55 | 87 |
56 /** @private {!Array<!downloads.ItemView>} */ | 88 /** @private {!Array<!downloads.ItemView>} */ |
57 this.items_ = []; | 89 this.items_ = []; |
58 | 90 |
59 if (!this.iconLoader_) { | 91 if (!this.iconLoader_) { |
60 var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); | 92 var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); |
61 /** @private {downloads.ThrottledIconLoader} */ | 93 /** @private {downloads.ThrottledIconLoader} */ |
62 this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); | 94 this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); |
63 } | 95 } |
64 | 96 |
65 for (var i = 0; i < list.length; ++i) { | 97 for (var i = 0; i < list.length; ++i) { |
66 var data = list[i]; | 98 var data = list[i]; |
67 var id = data.id; | 99 var id = data.id; |
68 | 100 |
69 // Re-use old items when possible (saves work, preserves focus). | 101 // Re-use old items when possible (saves work, preserves focus). |
70 var item = oldIdMap[id] || new downloads.ItemView(this.iconLoader_); | 102 var item = oldIdMap[id] || |
| 103 new downloads.ItemView(this.iconLoader_, this.actionService_); |
71 | 104 |
72 this.idMap_[id] = item; // Associated by ID for fast lookup. | 105 this.idMap_[id] = item; // Associated by ID for fast lookup. |
73 this.items_.push(item); // Add to sorted list for order. | 106 this.items_.push(item); // Add to sorted list for order. |
74 | 107 |
75 // Render |item| but don't actually add to the DOM yet. |this.items_| | 108 // Render |item| but don't actually add to the DOM yet. |this.items_| |
76 // must be fully created to be able to find the right spot to insert. | 109 // must be fully created to be able to find the right spot to insert. |
77 item.update(data); | 110 item.update(data); |
78 | 111 |
79 // Collapse redundant dates. | 112 // Collapse redundant dates. |
80 var prev = list[i - 1]; | 113 var prev = list[i - 1]; |
(...skipping 14 matching lines...) Expand all Loading... |
95 if (item.parentNode) // Already in the DOM; skip. | 128 if (item.parentNode) // Already in the DOM; skip. |
96 continue; | 129 continue; |
97 | 130 |
98 var before = null; | 131 var before = null; |
99 // Find the next rendered item after this one, and insert before it. | 132 // Find the next rendered item after this one, and insert before it. |
100 for (var j = i + 1; !before && j < this.items_.length; ++j) { | 133 for (var j = i + 1; !before && j < this.items_.length; ++j) { |
101 if (this.items_[j].parentNode) | 134 if (this.items_[j].parentNode) |
102 before = this.items_[j]; | 135 before = this.items_[j]; |
103 } | 136 } |
104 // If |before| is null, |item| will just get added at the end. | 137 // If |before| is null, |item| will just get added at the end. |
105 this.node_.insertBefore(item, before); | 138 this.$['downloads-list'].insertBefore(item, before); |
106 } | 139 } |
107 | 140 |
108 var noDownloadsOrResults = $('no-downloads-or-results'); | 141 this.hasDownloads_ = this.size_() > 0; |
109 noDownloadsOrResults.textContent = loadTimeData.getString( | |
110 this.searchText_ ? 'noSearchResults' : 'noDownloads'); | |
111 | |
112 var hasDownloads = this.size_() > 0; | |
113 this.node_.hidden = !hasDownloads; | |
114 noDownloadsOrResults.hidden = hasDownloads; | |
115 | 142 |
116 if (loadTimeData.getBoolean('allowDeletingHistory')) | 143 if (loadTimeData.getBoolean('allowDeletingHistory')) |
117 $('clear-all').hidden = !hasDownloads || this.searchText_.length > 0; | 144 this.$.toolbar.canClearAll = this.hasDownloads_; |
118 }, | 145 }, |
119 | 146 |
120 /** | 147 /** |
121 * @param {!downloads.Data} data | 148 * @param {!downloads.Data} data |
122 * @private | 149 * @private |
123 */ | 150 */ |
124 updateItem_: function(data) { | 151 updateItem_: function(data) { |
125 this.idMap_[data.id].update(data); | 152 this.idMap_[data.id].update(data); |
126 }, | 153 }, |
| 154 }); |
127 | 155 |
128 /** | 156 Manager.size = function() { |
129 * @return {number} The number of downloads shown on the page. | 157 return document.querySelector('downloads-manager').size_(); |
130 * @private | |
131 */ | |
132 size_: function() { | |
133 return this.items_.length; | |
134 }, | |
135 | |
136 /** @private */ | |
137 clearAll_: function() { | |
138 if (loadTimeData.getBoolean('allowDeletingHistory')) { | |
139 chrome.send('clearAll'); | |
140 this.setSearchText_(''); | |
141 } | |
142 }, | |
143 | |
144 /** @private */ | |
145 onLoad_: function() { | |
146 this.node_ = $('downloads-display'); | |
147 | |
148 $('clear-all').onclick = function() { | |
149 this.clearAll_(); | |
150 }.bind(this); | |
151 | |
152 $('open-downloads-folder').onclick = function() { | |
153 chrome.send('openDownloadsFolder'); | |
154 }; | |
155 | |
156 $('search-button').onclick = function() { | |
157 if (!$('search-term').hidden) | |
158 return; | |
159 $('clear-search').hidden = false; | |
160 $('search-term').hidden = false; | |
161 }; | |
162 | |
163 $('clear-search').onclick = function() { | |
164 $('clear-search').hidden = true; | |
165 $('search-term').hidden = true; | |
166 $('search-term').value = ''; | |
167 this.setSearchText_(''); | |
168 }.bind(this); | |
169 | |
170 // TODO(dbeam): this previously used onsearch, which batches keystrokes | |
171 // together. This should probably be re-instated eventually. | |
172 $('search-term').oninput = function(e) { | |
173 this.setSearchText_($('search-term').value); | |
174 }.bind(this); | |
175 | |
176 cr.ui.decorate('command', cr.ui.Command); | |
177 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); | |
178 document.addEventListener('command', this.onCommand_.bind(this)); | |
179 | |
180 this.setSearchText_(''); | |
181 }, | |
182 | |
183 /** | |
184 * @param {Event} e | |
185 * @private | |
186 */ | |
187 onCanExecute_: function(e) { | |
188 e = /** @type {cr.ui.CanExecuteEvent} */(e); | |
189 switch (e.command.id) { | |
190 case 'undo-command': | |
191 e.canExecute = !$('search-term').contains(document.activeElement); | |
192 break; | |
193 case 'clear-all-command': | |
194 e.canExecute = true; | |
195 break; | |
196 } | |
197 }, | |
198 | |
199 /** | |
200 * @param {Event} e | |
201 * @private | |
202 */ | |
203 onCommand_: function(e) { | |
204 if (e.command.id == 'undo-command') | |
205 chrome.send('undo'); | |
206 else if (e.command.id == 'clear-all-command') | |
207 this.clearAll_(); | |
208 }, | |
209 }; | 158 }; |
210 | 159 |
211 Manager.updateAll = function(list) { | 160 Manager.updateAll = function(list) { |
212 Manager.getInstance().updateAll_(list); | 161 document.querySelector('downloads-manager').updateAll_(list); |
213 }; | 162 }; |
214 | 163 |
215 Manager.updateItem = function(item) { | 164 Manager.updateItem = function(item) { |
216 Manager.getInstance().updateItem_(item); | 165 document.querySelector('downloads-manager').updateItem_(item); |
217 }; | |
218 | |
219 Manager.setSearchText = function(searchText) { | |
220 Manager.getInstance().setSearchText_(searchText); | |
221 }; | 166 }; |
222 | 167 |
223 Manager.onLoad = function() { | 168 Manager.onLoad = function() { |
224 Manager.getInstance().onLoad_(); | 169 document.querySelector('downloads-manager').onLoad_(); |
225 }; | |
226 | |
227 Manager.size = function() { | |
228 return Manager.getInstance().size_(); | |
229 }; | 170 }; |
230 | 171 |
231 return {Manager: Manager}; | 172 return {Manager: Manager}; |
232 }); | 173 }); |
233 | 174 |
234 window.addEventListener('load', downloads.Manager.onLoad); | 175 window.addEventListener('load', downloads.Manager.onLoad); |
OLD | NEW |