| /* | |
| * Copyright (C) 2010 Apple Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| * THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.ApplicationCacheItemsView = class extends UI.SimpleView { | |
| constructor(model, frameId) { | |
| super(Common.UIString('AppCache')); | |
| this._model = model; | |
| this.element.classList.add('storage-view', 'table'); | |
| this._deleteButton = new UI.ToolbarButton(Common.UIString('Delete'), 'largeicon-delete'); | |
| this._deleteButton.setVisible(false); | |
| this._deleteButton.addEventListener(UI.ToolbarButton.Events.Click, this._deleteButtonClicked, this); | |
| this._connectivityIcon = createElement('span', 'dt-icon-label'); | |
| this._connectivityIcon.style.margin = '0 2px 0 5px'; | |
| this._statusIcon = createElement('span', 'dt-icon-label'); | |
| this._statusIcon.style.margin = '0 2px 0 5px'; | |
| this._frameId = frameId; | |
| this._emptyWidget = new UI.EmptyWidget(Common.UIString('No Application Cache information available.')); | |
| this._emptyWidget.show(this.element); | |
| this._markDirty(); | |
| const status = this._model.frameManifestStatus(frameId); | |
| this.updateStatus(status); | |
| this.updateNetworkState(this._model.onLine); | |
| // FIXME: Status bar items don't work well enough yet, so they are being hidden. | |
| // http://webkit.org/b/41637 Web Inspector: Give Semantics to "Refresh" and "Delete" Buttons in ApplicationCache DataGrid | |
| this._deleteButton.element.style.display = 'none'; | |
| } | |
| /** | |
| * @override | |
| * @return {!Array.<!UI.ToolbarItem>} | |
| */ | |
| syncToolbarItems() { | |
| return [ | |
| this._deleteButton, new UI.ToolbarItem(this._connectivityIcon), new UI.ToolbarSeparator(), | |
| new UI.ToolbarItem(this._statusIcon) | |
| ]; | |
| } | |
| /** | |
| * @override | |
| */ | |
| wasShown() { | |
| this._maybeUpdate(); | |
| } | |
| /** | |
| * @override | |
| */ | |
| willHide() { | |
| this._deleteButton.setVisible(false); | |
| } | |
| _maybeUpdate() { | |
| if (!this.isShowing() || !this._viewDirty) { | |
| return; | |
| } | |
| this._update(); | |
| this._viewDirty = false; | |
| } | |
| _markDirty() { | |
| this._viewDirty = true; | |
| } | |
| /** | |
| * @param {number} status | |
| */ | |
| updateStatus(status) { | |
| const oldStatus = this._status; | |
| this._status = status; | |
| const statusInformation = {}; | |
| // We should never have UNCACHED status, since we remove frames with UNCACHED application cache status from the tree. | |
| statusInformation[Resources.ApplicationCacheModel.UNCACHED] = {type: 'smallicon-red-ball', text: 'UNCACHED'}; | |
| statusInformation[Resources.ApplicationCacheModel.IDLE] = {type: 'smallicon-green-ball', text: 'IDLE'}; | |
| statusInformation[Resources.ApplicationCacheModel.CHECKING] = {type: 'smallicon-orange-ball', text: 'CHECKING'}; | |
| statusInformation[Resources.ApplicationCacheModel.DOWNLOADING] = { | |
| type: 'smallicon-orange-ball', | |
| text: 'DOWNLOADING' | |
| }; | |
| statusInformation[Resources.ApplicationCacheModel.UPDATEREADY] = { | |
| type: 'smallicon-green-ball', | |
| text: 'UPDATEREADY' | |
| }; | |
| statusInformation[Resources.ApplicationCacheModel.OBSOLETE] = {type: 'smallicon-red-ball', text: 'OBSOLETE'}; | |
| const info = statusInformation[status] || statusInformation[Resources.ApplicationCacheModel.UNCACHED]; | |
| this._statusIcon.type = info.type; | |
| this._statusIcon.textContent = info.text; | |
| if (this.isShowing() && this._status === Resources.ApplicationCacheModel.IDLE && | |
| (oldStatus === Resources.ApplicationCacheModel.UPDATEREADY || !this._resources)) { | |
| this._markDirty(); | |
| } | |
| this._maybeUpdate(); | |
| } | |
| /** | |
| * @param {boolean} isNowOnline | |
| */ | |
| updateNetworkState(isNowOnline) { | |
| if (isNowOnline) { | |
| this._connectivityIcon.type = 'smallicon-green-ball'; | |
| this._connectivityIcon.textContent = Common.UIString('Online'); | |
| } else { | |
| this._connectivityIcon.type = 'smallicon-red-ball'; | |
| this._connectivityIcon.textContent = Common.UIString('Offline'); | |
| } | |
| } | |
| async _update() { | |
| const applicationCache = await this._model.requestApplicationCache(this._frameId); | |
| if (!applicationCache || !applicationCache.manifestURL) { | |
| delete this._manifest; | |
| delete this._creationTime; | |
| delete this._updateTime; | |
| delete this._size; | |
| delete this._resources; | |
| this._emptyWidget.show(this.element); | |
| this._deleteButton.setVisible(false); | |
| if (this._dataGrid) { | |
| this._dataGrid.element.classList.add('hidden'); | |
| } | |
| return; | |
| } | |
| // FIXME: are these variables needed anywhere else? | |
| this._manifest = applicationCache.manifestURL; | |
| this._creationTime = applicationCache.creationTime; | |
| this._updateTime = applicationCache.updateTime; | |
| this._size = applicationCache.size; | |
| this._resources = applicationCache.resources; | |
| if (!this._dataGrid) { | |
| this._createDataGrid(); | |
| } | |
| this._populateDataGrid(); | |
| this._dataGrid.autoSizeColumns(20, 80); | |
| this._dataGrid.element.classList.remove('hidden'); | |
| this._emptyWidget.detach(); | |
| this._deleteButton.setVisible(true); | |
| // FIXME: For Chrome, put creationTime and updateTime somewhere. | |
| // NOTE: localizedString has not yet been added. | |
| // Common.UIString("(%s) Created: %s Updated: %s", this._size, this._creationTime, this._updateTime); | |
| } | |
| _createDataGrid() { | |
| const columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([ | |
| {id: 'resource', title: Common.UIString('Resource'), sort: DataGrid.DataGrid.Order.Ascending, sortable: true}, | |
| {id: 'type', title: Common.UIString('Type'), sortable: true}, | |
| {id: 'size', title: Common.UIString('Size'), align: DataGrid.DataGrid.Align.Right, sortable: true} | |
| ]); | |
| this._dataGrid = new DataGrid.DataGrid(columns); | |
| this._dataGrid.setStriped(true); | |
| this._dataGrid.asWidget().show(this.element); | |
| this._dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._populateDataGrid, this); | |
| } | |
| _populateDataGrid() { | |
| const selectedResource = this._dataGrid.selectedNode ? this._dataGrid.selectedNode.resource : null; | |
| const sortDirection = this._dataGrid.isSortOrderAscending() ? 1 : -1; | |
| function numberCompare(field, resource1, resource2) { | |
| return sortDirection * (resource1[field] - resource2[field]); | |
| } | |
| function localeCompare(field, resource1, resource2) { | |
| return sortDirection * (resource1[field] + '').localeCompare(resource2[field] + ''); | |
| } | |
| let comparator; | |
| switch (this._dataGrid.sortColumnId()) { | |
| case 'resource': | |
| comparator = localeCompare.bind(null, 'url'); | |
| break; | |
| case 'type': | |
| comparator = localeCompare.bind(null, 'type'); | |
| break; | |
| case 'size': | |
| comparator = numberCompare.bind(null, 'size'); | |
| break; | |
| default: | |
| localeCompare.bind(null, 'resource'); // FIXME: comparator = ? | |
| } | |
| this._resources.sort(comparator); | |
| this._dataGrid.rootNode().removeChildren(); | |
| let nodeToSelect; | |
| for (let i = 0; i < this._resources.length; ++i) { | |
| const data = {}; | |
| const resource = this._resources[i]; | |
| data.resource = resource.url; | |
| data.type = resource.type; | |
| data.size = Number.bytesToString(resource.size); | |
| const node = new DataGrid.DataGridNode(data); | |
| node.resource = resource; | |
| node.selectable = true; | |
| this._dataGrid.rootNode().appendChild(node); | |
| if (resource === selectedResource) { | |
| nodeToSelect = node; | |
| nodeToSelect.selected = true; | |
| } | |
| } | |
| if (!nodeToSelect && this._dataGrid.rootNode().children.length) { | |
| this._dataGrid.rootNode().children[0].selected = true; | |
| } | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _deleteButtonClicked(event) { | |
| if (!this._dataGrid || !this._dataGrid.selectedNode) { | |
| return; | |
| } | |
| // FIXME: Delete Button semantics are not yet defined. (Delete a single, or all?) | |
| this._deleteCallback(this._dataGrid.selectedNode); | |
| } | |
| _deleteCallback(node) { | |
| // FIXME: Should we delete a single (selected) resource or all resources? | |
| // Protocol.inspectorBackend.deleteCachedResource(...) | |
| // this._update(); | |
| } | |
| }; |