Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / resources / md_downloads / manager.js
bloba7376895a7c18b2ea0595c906e7a0a8975431fff
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
3 // found in the LICENSE file.
5 cr.define('downloads', function() {
6   var Manager = Polymer({
7     is: 'downloads-manager',
9     created: function() {
10       /** @private {!downloads.ActionService} */
11       this.actionService_ = new downloads.ActionService;
12     },
14     properties: {
15       hasDownloads_: {
16         type: Boolean,
17         value: false,
18       },
19     },
21     /**
22      * @return {number} A guess at how many items could be visible at once.
23      * @private
24      */
25     guesstimateNumberOfVisibleItems_: function() {
26       var toolbarHeight = this.$.toolbar.offsetHeight;
27       return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1;
28     },
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 = this.$.toolbar.canClearAll();
42           break;
43       }
44     },
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     },
57     /** @private */
58     onLoad_: function() {
59       this.$.toolbar.setActionService(this.actionService_);
61       cr.ui.decorate('command', cr.ui.Command);
62       document.addEventListener('canExecute', this.onCanExecute_.bind(this));
63       document.addEventListener('command', this.onCommand_.bind(this));
65       // Shows all downloads.
66       this.actionService_.search('');
67     },
69     /** @private */
70     rebuildFocusGrid_: function() {
71       var activeElement = this.shadowRoot.activeElement;
73       var activeItem;
74       if (activeElement && activeElement.tagName == 'downloads-item')
75         activeItem = activeElement;
77       var activeControl = activeItem && activeItem.shadowRoot.activeElement;
79       /** @private {!cr.ui.FocusGrid} */
80       this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid;
81       this.focusGrid_.destroy();
83       var boundary = this.$['downloads-list'];
85       this.items_.forEach(function(item) {
86         var focusRow = new downloads.FocusRow(item.content, boundary);
87         this.focusGrid_.addRow(focusRow);
89         if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl))
90           focusRow.getEquivalentElement(activeControl).focus();
91       }, this);
93       this.focusGrid_.ensureRowActive();
94     },
96     /**
97      * @return {number} The number of downloads shown on the page.
98      * @private
99      */
100     size_: function() {
101       return this.items_.length;
102     },
104     /**
105      * Called when all items need to be updated.
106      * @param {!Array<!downloads.Data>} list A list of new download data.
107      * @private
108      */
109     updateAll_: function(list) {
110       var oldIdMap = this.idMap_ || {};
112       /** @private {!Object<!downloads.Item>} */
113       this.idMap_ = {};
115       /** @private {!Array<!downloads.Item>} */
116       this.items_ = [];
118       if (!this.iconLoader_) {
119         var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1);
120         /** @private {downloads.ThrottledIconLoader} */
121         this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate);
122       }
124       for (var i = 0; i < list.length; ++i) {
125         var data = list[i];
126         var id = data.id;
128         // Re-use old items when possible (saves work, preserves focus).
129         var item = oldIdMap[id] ||
130             new downloads.Item(this.iconLoader_, this.actionService_);
132         this.idMap_[id] = item;  // Associated by ID for fast lookup.
133         this.items_.push(item);  // Add to sorted list for order.
135         // Render |item| but don't actually add to the DOM yet. |this.items_|
136         // must be fully created to be able to find the right spot to insert.
137         item.update(data);
139         // Collapse redundant dates.
140         var prev = list[i - 1];
141         item.hideDate = !!prev && prev.date_string == data.date_string;
143         delete oldIdMap[id];
144       }
146       // Remove stale, previously rendered items from the DOM.
147       for (var id in oldIdMap) {
148         if (oldIdMap[id].parentNode)
149           oldIdMap[id].parentNode.removeChild(oldIdMap[id]);
150         delete oldIdMap[id];
151       }
153       for (var i = 0; i < this.items_.length; ++i) {
154         var item = this.items_[i];
155         if (item.parentNode)  // Already in the DOM; skip.
156           continue;
158         var before = null;
159         // Find the next rendered item after this one, and insert before it.
160         for (var j = i + 1; !before && j < this.items_.length; ++j) {
161           if (this.items_[j].parentNode)
162             before = this.items_[j];
163         }
164         // If |before| is null, |item| will just get added at the end.
165         this.$['downloads-list'].insertBefore(item, before);
166       }
168       var hasDownloads = this.size_() > 0;
169       if (!hasDownloads) {
170         var isSearching = this.actionService_.isSearching();
171         var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads';
172         this.$['no-downloads'].querySelector('span').textContent =
173             loadTimeData.getString(messageToShow);
174       }
175       this.hasDownloads_ = hasDownloads;
177       if (loadTimeData.getBoolean('allowDeletingHistory'))
178         this.$.toolbar.downloadsShowing = this.hasDownloads_;
180       this.$.panel.classList.remove('loading');
182       var allReady = this.items_.map(function(i) { return i.readyPromise; });
183       Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this));
184     },
186     /**
187      * @param {!downloads.Data} data
188      * @private
189      */
190     updateItem_: function(data) {
191       var item = this.idMap_[data.id];
193       var activeControl = this.shadowRoot.activeElement == item ?
194           item.shadowRoot.activeElement : null;
196       item.update(data);
198       this.async(function() {
199         if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) {
200           var focusRow = this.focusGrid_.getRowForRoot(item.content);
201           focusRow.getEquivalentElement(activeControl).focus();
202         }
203       }.bind(this));
204     },
205   });
207   Manager.size = function() {
208     return document.querySelector('downloads-manager').size_();
209   };
211   Manager.updateAll = function(list) {
212     document.querySelector('downloads-manager').updateAll_(list);
213   };
215   Manager.updateItem = function(item) {
216     document.querySelector('downloads-manager').updateItem_(item);
217   };
219   Manager.onLoad = function() {
220     document.querySelector('downloads-manager').onLoad_();
221   };
223   return {Manager: Manager};
226 window.addEventListener('load', downloads.Manager.onLoad);