Chromium Code Reviews| 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 var Manager = Polymer({ | 6 var Manager = Polymer({ |
| 7 is: 'downloads-manager', | 7 is: 'downloads-manager', |
| 8 | 8 |
| 9 properties: { | 9 properties: { |
| 10 hasDownloads_: { | 10 hasDownloads_: { |
| 11 type: Boolean, | 11 type: Boolean, |
| 12 value: false, | 12 value: false, |
| 13 }, | 13 }, |
| 14 | |
| 15 items_: { | |
| 16 type: Array, | |
| 17 }, | |
| 14 }, | 18 }, |
| 15 | 19 |
| 16 /** | 20 /** |
| 17 * @return {number} A guess at how many items could be visible at once. | |
| 18 * @private | |
| 19 */ | |
| 20 guesstimateNumberOfVisibleItems_: function() { | |
| 21 var toolbarHeight = this.$.toolbar.offsetHeight; | |
| 22 return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1; | |
| 23 }, | |
| 24 | |
| 25 /** | |
| 26 * @param {Event} e | 21 * @param {Event} e |
| 27 * @private | 22 * @private |
| 28 */ | 23 */ |
| 29 onCanExecute_: function(e) { | 24 onCanExecute_: function(e) { |
| 30 e = /** @type {cr.ui.CanExecuteEvent} */(e); | 25 e = /** @type {cr.ui.CanExecuteEvent} */(e); |
| 31 switch (e.command.id) { | 26 switch (e.command.id) { |
| 32 case 'undo-command': | 27 case 'undo-command': |
| 33 e.canExecute = this.$.toolbar.canUndo(); | 28 e.canExecute = this.$.toolbar.canUndo(); |
| 34 break; | 29 break; |
| 35 case 'clear-all-command': | 30 case 'clear-all-command': |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 52 /** @private */ | 47 /** @private */ |
| 53 onLoad_: function() { | 48 onLoad_: function() { |
| 54 cr.ui.decorate('command', cr.ui.Command); | 49 cr.ui.decorate('command', cr.ui.Command); |
| 55 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); | 50 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
| 56 document.addEventListener('command', this.onCommand_.bind(this)); | 51 document.addEventListener('command', this.onCommand_.bind(this)); |
| 57 | 52 |
| 58 // Shows all downloads. | 53 // Shows all downloads. |
| 59 downloads.ActionService.getInstance().search(''); | 54 downloads.ActionService.getInstance().search(''); |
| 60 }, | 55 }, |
| 61 | 56 |
| 62 /** @private */ | |
| 63 rebuildFocusGrid_: function() { | |
| 64 var activeElement = this.shadowRoot.activeElement; | |
| 65 | |
| 66 var activeItem; | |
| 67 if (activeElement && activeElement.tagName == 'downloads-item') | |
| 68 activeItem = activeElement; | |
| 69 | |
| 70 var activeControl = activeItem && activeItem.shadowRoot.activeElement; | |
| 71 | |
| 72 /** @private {!cr.ui.FocusGrid} */ | |
| 73 this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid; | |
| 74 this.focusGrid_.destroy(); | |
| 75 | |
| 76 var boundary = this.$['downloads-list']; | |
| 77 | |
| 78 this.items_.forEach(function(item) { | |
| 79 var focusRow = new downloads.FocusRow(item.content, boundary); | |
| 80 this.focusGrid_.addRow(focusRow); | |
| 81 | |
| 82 if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl)) | |
| 83 focusRow.getEquivalentElement(activeControl).focus(); | |
| 84 }, this); | |
| 85 | |
| 86 this.focusGrid_.ensureRowActive(); | |
| 87 }, | |
| 88 | |
| 89 /** | 57 /** |
| 90 * @return {number} The number of downloads shown on the page. | 58 * @return {number} The number of downloads shown on the page. |
| 91 * @private | 59 * @private |
| 92 */ | 60 */ |
| 93 size_: function() { | 61 size_: function() { |
| 94 return this.items_.length; | 62 return this.items_.length; |
| 95 }, | 63 }, |
| 96 | 64 |
| 97 /** | 65 /** |
| 98 * Called when all items need to be updated. | 66 * Called when all items need to be updated. |
| 99 * @param {!Array<!downloads.Data>} list A list of new download data. | 67 * @param {!Array<!downloads.Data>} list A list of new download data. |
| 100 * @private | 68 * @private |
| 101 */ | 69 */ |
| 102 updateAll_: function(list) { | 70 updateAll_: function(list) { |
| 103 var oldIdMap = this.idMap_ || {}; | |
| 104 | |
| 105 /** @private {!Object<!downloads.Item>} */ | 71 /** @private {!Object<!downloads.Item>} */ |
| 106 this.idMap_ = {}; | 72 this.idMap_ = {}; |
| 107 | 73 |
| 108 /** @private {!Array<!downloads.Item>} */ | 74 var items = []; |
| 109 this.items_ = []; | |
| 110 | |
| 111 if (!this.iconLoader_) { | |
| 112 var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); | |
| 113 /** @private {downloads.ThrottledIconLoader} */ | |
| 114 this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); | |
| 115 } | |
| 116 | 75 |
| 117 for (var i = 0; i < list.length; ++i) { | 76 for (var i = 0; i < list.length; ++i) { |
| 118 var data = list[i]; | 77 var data = list[i]; |
| 119 var id = data.id; | 78 var id = data.id; |
| 120 | 79 |
| 121 // Re-use old items when possible (saves work, preserves focus). | 80 this.idMap_[id] = i; // Associated by ID for fast lookup. |
|
esprehn
2015/10/03 05:11:54
I hate right side comments, but I guess this is no
Dan Beam
2015/10/04 08:27:08
JS/closure is fine with them
| |
| 122 var item = oldIdMap[id] || new downloads.Item(this.iconLoader_); | |
| 123 | 81 |
| 124 this.idMap_[id] = item; // Associated by ID for fast lookup. | 82 var prev = list[i - 1]; |
| 125 this.items_.push(item); // Add to sorted list for order. | |
| 126 | 83 |
| 127 // Render |item| but don't actually add to the DOM yet. |this.items_| | 84 items.push({ |
| 128 // must be fully created to be able to find the right spot to insert. | 85 index: i, |
| 129 item.update(data); | 86 item: data, |
| 130 | 87 hideDate: !!prev && prev.date_string == data.date_string, |
| 131 // Collapse redundant dates. | 88 }); |
| 132 var prev = list[i - 1]; | |
| 133 item.hideDate = !!prev && prev.date_string == data.date_string; | |
| 134 | |
| 135 delete oldIdMap[id]; | |
| 136 } | 89 } |
| 137 | 90 |
| 138 // Remove stale, previously rendered items from the DOM. | 91 this.items_ = items; |
| 139 for (var id in oldIdMap) { | |
| 140 if (oldIdMap[id].parentNode) | |
| 141 oldIdMap[id].parentNode.removeChild(oldIdMap[id]); | |
| 142 delete oldIdMap[id]; | |
| 143 } | |
| 144 | |
| 145 for (var i = 0; i < this.items_.length; ++i) { | |
| 146 var item = this.items_[i]; | |
| 147 if (item.parentNode) // Already in the DOM; skip. | |
| 148 continue; | |
| 149 | |
| 150 var before = null; | |
| 151 // Find the next rendered item after this one, and insert before it. | |
| 152 for (var j = i + 1; !before && j < this.items_.length; ++j) { | |
| 153 if (this.items_[j].parentNode) | |
| 154 before = this.items_[j]; | |
| 155 } | |
| 156 // If |before| is null, |item| will just get added at the end. | |
| 157 this.$['downloads-list'].insertBefore(item, before); | |
| 158 } | |
| 159 | 92 |
| 160 var hasDownloads = this.size_() > 0; | 93 var hasDownloads = this.size_() > 0; |
| 161 if (!hasDownloads) { | 94 if (!hasDownloads) { |
| 162 var isSearching = downloads.ActionService.getInstance().isSearching(); | 95 var isSearching = downloads.ActionService.getInstance().isSearching(); |
| 163 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; | 96 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; |
| 164 this.$['no-downloads'].querySelector('span').textContent = | 97 this.$['no-downloads'].querySelector('span').textContent = |
| 165 loadTimeData.getString(messageToShow); | 98 loadTimeData.getString(messageToShow); |
| 166 } | 99 } |
| 167 this.hasDownloads_ = hasDownloads; | 100 this.hasDownloads_ = hasDownloads; |
| 168 | 101 |
| 169 if (loadTimeData.getBoolean('allowDeletingHistory')) | 102 if (loadTimeData.getBoolean('allowDeletingHistory')) |
| 170 this.$.toolbar.downloadsShowing = this.hasDownloads_; | 103 this.$.toolbar.downloadsShowing = this.hasDownloads_; |
| 171 | 104 |
| 172 this.$.panel.classList.remove('loading'); | 105 this.$.panel.classList.remove('loading'); |
| 173 | |
| 174 var allReady = this.items_.map(function(i) { return i.readyPromise; }); | |
| 175 Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this)); | |
| 176 }, | 106 }, |
| 177 | 107 |
| 178 /** | 108 /** |
| 179 * @param {!downloads.Data} data | 109 * @param {!downloads.Data} data |
| 180 * @private | 110 * @private |
| 181 */ | 111 */ |
| 182 updateItem_: function(data) { | 112 updateItem_: function(data) { |
| 183 var item = this.idMap_[data.id]; | 113 this.set('items_.' + this.idMap_[data.id] + '.item', data); |
|
esprehn
2015/10/03 05:11:54
idMap is more like indexMap right?
Dan Beam
2015/10/04 08:27:08
Done.
| |
| 184 | 114 this.$['downloads-list'].fire('iron-resize'); |
| 185 var activeControl = this.shadowRoot.activeElement == item ? | |
| 186 item.shadowRoot.activeElement : null; | |
| 187 | |
| 188 item.update(data); | |
| 189 | |
| 190 this.async(function() { | |
| 191 if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) { | |
| 192 var focusRow = this.focusGrid_.getRowForRoot(item.content); | |
| 193 focusRow.getEquivalentElement(activeControl).focus(); | |
| 194 } | |
| 195 }.bind(this)); | |
| 196 }, | 115 }, |
| 197 }); | 116 }); |
| 198 | 117 |
| 199 Manager.size = function() { | 118 Manager.size = function() { |
| 200 return document.querySelector('downloads-manager').size_(); | 119 return document.querySelector('downloads-manager').size_(); |
| 201 }; | 120 }; |
| 202 | 121 |
| 203 Manager.updateAll = function(list) { | 122 Manager.updateAll = function(list) { |
| 204 document.querySelector('downloads-manager').updateAll_(list); | 123 document.querySelector('downloads-manager').updateAll_(list); |
| 205 }; | 124 }; |
| 206 | 125 |
| 207 Manager.updateItem = function(item) { | 126 Manager.updateItem = function(item) { |
| 208 document.querySelector('downloads-manager').updateItem_(item); | 127 document.querySelector('downloads-manager').updateItem_(item); |
| 209 }; | 128 }; |
| 210 | 129 |
| 211 Manager.onLoad = function() { | 130 Manager.onLoad = function() { |
| 212 document.querySelector('downloads-manager').onLoad_(); | 131 document.querySelector('downloads-manager').onLoad_(); |
| 213 }; | 132 }; |
| 214 | 133 |
| 215 return {Manager: Manager}; | 134 return {Manager: Manager}; |
| 216 }); | 135 }); |
| 217 | 136 |
| 218 window.addEventListener('load', downloads.Manager.onLoad); | 137 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |