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 if (typeof Polymer == 'undefined') | 5 if (typeof Polymer == 'undefined') |
6 Polymer = {dom: 'shadow'}; | 6 Polymer = {dom: 'shadow'}; |
7 else | 7 else |
8 console.error('Polymer is already defined.'); | 8 console.error('Polymer is already defined.'); |
9 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 9 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
10 // Use of this source code is governed by a BSD-style license that can be | 10 // Use of this source code is governed by a BSD-style license that can be |
(...skipping 1513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1524 * @param {string} chromeSendName | 1524 * @param {string} chromeSendName |
1525 * @return {function(string):void} A chrome.send() callback with curried name. | 1525 * @return {function(string):void} A chrome.send() callback with curried name. |
1526 */ | 1526 */ |
1527 function chromeSendWithId(chromeSendName) { | 1527 function chromeSendWithId(chromeSendName) { |
1528 return function(id) { chrome.send(chromeSendName, [id]); }; | 1528 return function(id) { chrome.send(chromeSendName, [id]); }; |
1529 } | 1529 } |
1530 | 1530 |
1531 /** @constructor */ | 1531 /** @constructor */ |
1532 function ActionService() {} | 1532 function ActionService() {} |
1533 | 1533 |
| 1534 /** |
| 1535 * @param {string} s |
| 1536 * @return {string} |s| without whitespace at the beginning or end. |
| 1537 */ |
| 1538 function trim(s) { return s.trim(); } |
| 1539 |
| 1540 /** |
| 1541 * @param {string|undefined} value |
| 1542 * @return {boolean} Whether |value| is truthy. |
| 1543 */ |
| 1544 function truthy(value) { return !!value; } |
| 1545 |
| 1546 /** |
| 1547 * @param {string} searchText Input typed by the user into a search box. |
| 1548 * @return {Array<string>} A list of terms extracted from |searchText|. |
| 1549 */ |
| 1550 ActionService.splitTerms = function(searchText) { |
| 1551 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). |
| 1552 return searchText.split(/"([^"]*)"/).map(trim).filter(truthy); |
| 1553 }; |
| 1554 |
1534 ActionService.prototype = { | 1555 ActionService.prototype = { |
1535 /** @param {string} id ID of the download to cancel. */ | 1556 /** @param {string} id ID of the download to cancel. */ |
1536 cancel: chromeSendWithId('cancel'), | 1557 cancel: chromeSendWithId('cancel'), |
1537 | 1558 |
1538 /** Instructs the browser to clear all finished downloads. */ | 1559 /** Instructs the browser to clear all finished downloads. */ |
1539 clearAll: function() { | 1560 clearAll: function() { |
1540 if (loadTimeData.getBoolean('allowDeletingHistory')) { | 1561 if (loadTimeData.getBoolean('allowDeletingHistory')) { |
1541 chrome.send('clearAll'); | 1562 chrome.send('clearAll'); |
1542 this.search(''); | 1563 this.search(''); |
1543 } | 1564 } |
1544 }, | 1565 }, |
1545 | 1566 |
1546 /** @param {string} id ID of the dangerous download to discard. */ | 1567 /** @param {string} id ID of the dangerous download to discard. */ |
1547 discardDangerous: chromeSendWithId('discardDangerous'), | 1568 discardDangerous: chromeSendWithId('discardDangerous'), |
1548 | 1569 |
1549 /** @param {string} url URL of a file to download. */ | 1570 /** @param {string} url URL of a file to download. */ |
1550 download: function(url) { | 1571 download: function(url) { |
1551 var a = document.createElement('a'); | 1572 var a = document.createElement('a'); |
1552 a.href = url; | 1573 a.href = url; |
1553 a.setAttribute('download', ''); | 1574 a.setAttribute('download', ''); |
1554 a.click(); | 1575 a.click(); |
1555 }, | 1576 }, |
1556 | 1577 |
1557 /** @param {string} id ID of the download that the user started dragging. */ | 1578 /** @param {string} id ID of the download that the user started dragging. */ |
1558 drag: chromeSendWithId('drag'), | 1579 drag: chromeSendWithId('drag'), |
1559 | 1580 |
| 1581 /** @private {boolean} */ |
| 1582 isSearching_: false, |
| 1583 |
1560 /** | 1584 /** |
1561 * @return {boolean} Whether the user is currently searching for downloads | 1585 * @return {boolean} Whether the user is currently searching for downloads |
1562 * (i.e. has a non-empty search term). | 1586 * (i.e. has a non-empty search term). |
1563 */ | 1587 */ |
1564 isSearching: function() { | 1588 isSearching: function() { |
1565 return this.searchText_.length > 0; | 1589 return this.isSearching_; |
1566 }, | 1590 }, |
1567 | 1591 |
1568 /** Opens the current local destination for downloads. */ | 1592 /** Opens the current local destination for downloads. */ |
1569 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), | 1593 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), |
1570 | 1594 |
1571 /** | 1595 /** |
1572 * @param {string} id ID of the download to run locally on the user's box. | 1596 * @param {string} id ID of the download to run locally on the user's box. |
1573 */ | 1597 */ |
1574 openFile: chromeSendWithId('openFile'), | 1598 openFile: chromeSendWithId('openFile'), |
1575 | 1599 |
(...skipping 12 matching lines...) Expand all Loading... |
1588 */ | 1612 */ |
1589 saveDangerous: chromeSendWithId('saveDangerous'), | 1613 saveDangerous: chromeSendWithId('saveDangerous'), |
1590 | 1614 |
1591 /** @param {string} searchText What to search for. */ | 1615 /** @param {string} searchText What to search for. */ |
1592 search: function(searchText) { | 1616 search: function(searchText) { |
1593 if (this.searchText_ == searchText) | 1617 if (this.searchText_ == searchText) |
1594 return; | 1618 return; |
1595 | 1619 |
1596 this.searchText_ = searchText; | 1620 this.searchText_ = searchText; |
1597 | 1621 |
1598 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). | 1622 var terms = ActionService.splitTerms(searchText); |
1599 function trim(s) { return s.trim(); } | 1623 this.isSearching_ = terms.length > 0; |
1600 chrome.send('getDownloads', searchText.split(/"([^"]*)"/).map(trim)); | 1624 |
| 1625 chrome.send('getDownloads', terms); |
1601 }, | 1626 }, |
1602 | 1627 |
1603 /** | 1628 /** |
1604 * Shows the local folder a finished download resides in. | 1629 * Shows the local folder a finished download resides in. |
1605 * @param {string} id ID of the download to show. | 1630 * @param {string} id ID of the download to show. |
1606 */ | 1631 */ |
1607 show: chromeSendWithId('show'), | 1632 show: chromeSendWithId('show'), |
1608 | 1633 |
1609 /** Undo download removal. */ | 1634 /** Undo download removal. */ |
1610 undo: chrome.send.bind(chrome, 'undo'), | 1635 undo: chrome.send.bind(chrome, 'undo'), |
(...skipping 15319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
16930 // Copyright 2015 The Chromium Authors. All rights reserved. | 16955 // Copyright 2015 The Chromium Authors. All rights reserved. |
16931 // Use of this source code is governed by a BSD-style license that can be | 16956 // Use of this source code is governed by a BSD-style license that can be |
16932 // found in the LICENSE file. | 16957 // found in the LICENSE file. |
16933 | 16958 |
16934 cr.define('downloads', function() { | 16959 cr.define('downloads', function() { |
16935 var Manager = Polymer({ | 16960 var Manager = Polymer({ |
16936 is: 'downloads-manager', | 16961 is: 'downloads-manager', |
16937 | 16962 |
16938 properties: { | 16963 properties: { |
16939 hasDownloads_: { | 16964 hasDownloads_: { |
| 16965 observer: 'hasDownloadsChanged_', |
16940 type: Boolean, | 16966 type: Boolean, |
16941 value: false, | |
16942 }, | 16967 }, |
16943 | 16968 |
16944 items_: { | 16969 items_: { |
16945 type: Array, | 16970 type: Array, |
| 16971 value: function() { return []; }, |
16946 }, | 16972 }, |
16947 }, | 16973 }, |
16948 | 16974 |
16949 hostAttributes: { | 16975 hostAttributes: { |
16950 loading: true, | 16976 loading: true, |
16951 }, | 16977 }, |
16952 | 16978 |
| 16979 observers: [ |
| 16980 'itemsChanged_(items_.*)', |
| 16981 ], |
| 16982 |
| 16983 /** @private */ |
| 16984 clearAll_: function() { |
| 16985 this.set('items_', []); |
| 16986 }, |
| 16987 |
| 16988 /** @private */ |
| 16989 hasDownloadsChanged_: function() { |
| 16990 if (loadTimeData.getBoolean('allowDeletingHistory')) |
| 16991 this.$.toolbar.downloadsShowing = this.hasDownloads_; |
| 16992 |
| 16993 if (this.hasDownloads_) { |
| 16994 this.$['downloads-list'].fire('iron-resize'); |
| 16995 } else { |
| 16996 var isSearching = downloads.ActionService.getInstance().isSearching(); |
| 16997 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; |
| 16998 this.$['no-downloads'].querySelector('span').textContent = |
| 16999 loadTimeData.getString(messageToShow); |
| 17000 } |
| 17001 }, |
| 17002 |
| 17003 /** |
| 17004 * @param {number} index |
| 17005 * @param {!Array<!downloads.Data>} list |
| 17006 * @private |
| 17007 */ |
| 17008 insertItems_: function(index, list) { |
| 17009 this.splice.apply(this, ['items_', index, 0].concat(list)); |
| 17010 this.updateHideDates_(index, index + list.length); |
| 17011 this.removeAttribute('loading'); |
| 17012 }, |
| 17013 |
| 17014 /** @private */ |
| 17015 itemsChanged_: function() { |
| 17016 this.hasDownloads_ = this.items_.length > 0; |
| 17017 }, |
| 17018 |
16953 /** | 17019 /** |
16954 * @param {Event} e | 17020 * @param {Event} e |
16955 * @private | 17021 * @private |
16956 */ | 17022 */ |
16957 onCanExecute_: function(e) { | 17023 onCanExecute_: function(e) { |
16958 e = /** @type {cr.ui.CanExecuteEvent} */(e); | 17024 e = /** @type {cr.ui.CanExecuteEvent} */(e); |
16959 switch (e.command.id) { | 17025 switch (e.command.id) { |
16960 case 'undo-command': | 17026 case 'undo-command': |
16961 e.canExecute = this.$.toolbar.canUndo(); | 17027 e.canExecute = this.$.toolbar.canUndo(); |
16962 break; | 17028 break; |
(...skipping 18 matching lines...) Expand all Loading... |
16981 onLoad_: function() { | 17047 onLoad_: function() { |
16982 cr.ui.decorate('command', cr.ui.Command); | 17048 cr.ui.decorate('command', cr.ui.Command); |
16983 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); | 17049 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
16984 document.addEventListener('command', this.onCommand_.bind(this)); | 17050 document.addEventListener('command', this.onCommand_.bind(this)); |
16985 | 17051 |
16986 // Shows all downloads. | 17052 // Shows all downloads. |
16987 downloads.ActionService.getInstance().search(''); | 17053 downloads.ActionService.getInstance().search(''); |
16988 }, | 17054 }, |
16989 | 17055 |
16990 /** | 17056 /** |
16991 * @return {number} The number of downloads shown on the page. | 17057 * @param {number} index |
16992 * @private | 17058 * @private |
16993 */ | 17059 */ |
16994 size_: function() { | 17060 removeItem_: function(index) { |
16995 return this.items_.length; | 17061 this.splice('items_', index, 1); |
| 17062 this.updateHideDates_(index, index); |
16996 }, | 17063 }, |
16997 | 17064 |
16998 /** | 17065 /** |
16999 * Called when all items need to be updated. | 17066 * @param {number} start |
17000 * @param {!Array<!downloads.Data>} list A list of new download data. | 17067 * @param {number} end |
17001 * @private | 17068 * @private |
17002 */ | 17069 */ |
17003 updateAll_: function(list) { | 17070 updateHideDates_: function(start, end) { |
17004 /** @private {!Object<number>} */ | 17071 for (var i = start; i <= end; ++i) { |
17005 this.idToIndex_ = {}; | 17072 var current = this.items_[i]; |
17006 | 17073 if (!current) |
17007 for (var i = 0; i < list.length; ++i) { | 17074 continue; |
17008 var data = list[i]; | 17075 var prev = this.items_[i - 1]; |
17009 | 17076 current.hideDate = !!prev && prev.date_string == current.date_string; |
17010 this.idToIndex_[data.id] = data.index = i; | |
17011 | |
17012 var prev = list[i - 1]; | |
17013 data.hideDate = !!prev && prev.date_string == data.date_string; | |
17014 } | 17077 } |
17015 | |
17016 // TODO(dbeam): this resets the scroll position, which is a huge bummer. | |
17017 // Removing something from the bottom of the list should not scroll you | |
17018 // back to the top. The grand plan is to restructure how the C++ sends the | |
17019 // JS data so that it only gets updates (rather than the most recent set | |
17020 // of items). TL;DR - we can't ship with this bug. | |
17021 this.items_ = list; | |
17022 | |
17023 var hasDownloads = this.size_() > 0; | |
17024 if (!hasDownloads) { | |
17025 var isSearching = downloads.ActionService.getInstance().isSearching(); | |
17026 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; | |
17027 this.$['no-downloads'].querySelector('span').textContent = | |
17028 loadTimeData.getString(messageToShow); | |
17029 } | |
17030 this.hasDownloads_ = hasDownloads; | |
17031 | |
17032 if (loadTimeData.getBoolean('allowDeletingHistory')) | |
17033 this.$.toolbar.downloadsShowing = this.hasDownloads_; | |
17034 | |
17035 this.removeAttribute('loading'); | |
17036 }, | 17078 }, |
17037 | 17079 |
17038 /** | 17080 /** |
| 17081 * @param {number} index |
17039 * @param {!downloads.Data} data | 17082 * @param {!downloads.Data} data |
17040 * @private | 17083 * @private |
17041 */ | 17084 */ |
17042 updateItem_: function(data) { | 17085 updateItem_: function(index, data) { |
17043 var index = this.idToIndex_[data.id]; | |
17044 this.set('items_.' + index, data); | 17086 this.set('items_.' + index, data); |
| 17087 this.updateHideDates_(index, index); |
17045 this.$['downloads-list'].updateSizeForItem(index); | 17088 this.$['downloads-list'].updateSizeForItem(index); |
17046 }, | 17089 }, |
17047 }); | 17090 }); |
17048 | 17091 |
17049 Manager.size = function() { | 17092 Manager.clearAll = function() { |
17050 return document.querySelector('downloads-manager').size_(); | 17093 Manager.get().clearAll_(); |
17051 }; | 17094 }; |
17052 | 17095 |
17053 Manager.updateAll = function(list) { | 17096 /** @return {!downloads.Manager} */ |
17054 document.querySelector('downloads-manager').updateAll_(list); | 17097 Manager.get = function() { |
| 17098 return /** @type {!downloads.Manager} */( |
| 17099 queryRequiredElement('downloads-manager')); |
17055 }; | 17100 }; |
17056 | 17101 |
17057 Manager.updateItem = function(item) { | 17102 Manager.insertItems = function(index, list) { |
17058 document.querySelector('downloads-manager').updateItem_(item); | 17103 Manager.get().insertItems_(index, list); |
17059 }; | 17104 }; |
17060 | 17105 |
17061 Manager.onLoad = function() { | 17106 Manager.onLoad = function() { |
17062 document.querySelector('downloads-manager').onLoad_(); | 17107 Manager.get().onLoad_(); |
| 17108 }; |
| 17109 |
| 17110 Manager.removeItem = function(index) { |
| 17111 Manager.get().removeItem_(index); |
| 17112 }; |
| 17113 |
| 17114 Manager.updateItem = function(index, data) { |
| 17115 Manager.get().updateItem_(index, data); |
17063 }; | 17116 }; |
17064 | 17117 |
17065 return {Manager: Manager}; | 17118 return {Manager: Manager}; |
17066 }); | 17119 }); |
17067 // Copyright 2015 The Chromium Authors. All rights reserved. | 17120 // Copyright 2015 The Chromium Authors. All rights reserved. |
17068 // Use of this source code is governed by a BSD-style license that can be | 17121 // Use of this source code is governed by a BSD-style license that can be |
17069 // found in the LICENSE file. | 17122 // found in the LICENSE file. |
17070 | 17123 |
17071 window.addEventListener('load', downloads.Manager.onLoad); | 17124 window.addEventListener('load', downloads.Manager.onLoad); |
OLD | NEW |