1 /* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 WebInspector.ApplicationCacheItemsView = function(treeElement, appcacheDomain) 27 { 28 WebInspector.View.call(this); 29 30 this.element.addStyleClass("storage-view"); 31 this.element.addStyleClass("table"); 32 33 // FIXME: Delete Button semantics are not yet defined. 34 // FIXME: Needs better tooltip. (Localized) 35 this.deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item"); 36 this.deleteButton.visible = false; 37 this.deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false); 38 39 // FIXME: Refresh Button semantics are not yet defined. 40 // FIXME: Needs better tooltip. (Localized) 41 this.refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item"); 42 this.refreshButton.addEventListener("click", this._refreshButtonClicked.bind(this), false); 43 44 if (Preferences.onlineDetectionEnabled) { 45 this.connectivityIcon = document.createElement("img"); 46 this.connectivityIcon.className = "storage-application-cache-connectivity-icon"; 47 this.connectivityIcon.src = ""; 48 this.connectivityMessage = document.createElement("span"); 49 this.connectivityMessage.className = "storage-application-cache-connectivity"; 50 this.connectivityMessage.textContent = ""; 51 } 52 53 this.divider = document.createElement("span"); 54 this.divider.className = "status-bar-item status-bar-divider"; 55 56 this.statusIcon = document.createElement("img"); 57 this.statusIcon.className = "storage-application-cache-status-icon"; 58 this.statusIcon.src = ""; 59 this.statusMessage = document.createElement("span"); 60 this.statusMessage.className = "storage-application-cache-status"; 61 this.statusMessage.textContent = ""; 62 63 this._treeElement = treeElement; 64 this._appcacheDomain = appcacheDomain; 65 66 this._emptyMsgElement = document.createElement("div"); 67 this._emptyMsgElement.className = "storage-empty-view"; 68 this._emptyMsgElement.textContent = WebInspector.UIString("No Application Cache information available."); 69 this.element.appendChild(this._emptyMsgElement); 70 71 this.updateStatus(applicationCache.UNCACHED); 72 } 73 74 WebInspector.ApplicationCacheItemsView.prototype = { 75 get statusBarItems() 76 { 77 if (Preferences.onlineDetectionEnabled) { 78 return [ 79 this.refreshButton.element, this.deleteButton.element, 80 this.connectivityIcon, this.connectivityMessage, this.divider, 81 this.statusIcon, this.statusMessage 82 ]; 83 } else { 84 return [ 85 this.refreshButton.element, this.deleteButton.element, this.divider, 86 this.statusIcon, this.statusMessage 87 ]; 88 } 89 }, 90 91 show: function(parentElement) 92 { 93 WebInspector.View.prototype.show.call(this, parentElement); 94 this.updateNetworkState(navigator.onLine); 95 this._update(); 96 }, 97 98 hide: function() 99 { 100 WebInspector.View.prototype.hide.call(this); 101 this.deleteButton.visible = false; 102 }, 103 104 updateStatus: function(status) 105 { 106 var statusInformation = {}; 107 statusInformation[applicationCache.UNCACHED] = { src: "Images/warningOrangeDot.png", text: "UNCACHED" }; 108 statusInformation[applicationCache.IDLE] = { src: "Images/warningOrangeDot.png", text: "IDLE" }; 109 statusInformation[applicationCache.CHECKING] = { src: "Images/successGreenDot.png", text: "CHECKING" }; 110 statusInformation[applicationCache.DOWNLOADING] = { src: "Images/successGreenDot.png", text: "DOWNLOADING" }; 111 statusInformation[applicationCache.UPDATEREADY] = { src: "Images/successGreenDot.png", text: "UPDATEREADY" }; 112 statusInformation[applicationCache.OBSOLETE] = { src: "Images/errorRedDot.png", text: "OBSOLETE" }; 113 114 var info = statusInformation[status]; 115 if (!info) { 116 console.error("Unknown Application Cache Status was Not Handled: %d", status); 117 return; 118 } 119 120 this.statusIcon.src = info.src; 121 this.statusMessage.textContent = info.text; 122 }, 123 124 updateNetworkState: function(isNowOnline) 125 { 126 if (Preferences.onlineDetectionEnabled) { 127 if (isNowOnline) { 128 this.connectivityIcon.src = "Images/successGreenDot.png"; 129 this.connectivityMessage.textContent = WebInspector.UIString("Online"); 130 } else { 131 this.connectivityIcon.src = "Images/errorRedDot.png"; 132 this.connectivityMessage.textContent = WebInspector.UIString("Offline"); 133 } 134 } 135 }, 136 137 _update: function() 138 { 139 WebInspector.ApplicationCacheDispatcher.getApplicationCachesAsync(this._updateCallback.bind(this)); 140 }, 141 142 _updateCallback: function(applicationCaches) 143 { 144 // FIXME: applicationCaches is just one cache. 145 // FIXME: are these variables needed anywhere else? 146 this._manifest = applicationCaches.manifest; 147 this._creationTime = applicationCaches.creationTime; 148 this._updateTime = applicationCaches.updateTime; 149 this._size = applicationCaches.size; 150 this._resources = applicationCaches.resources; 151 var lastPathComponent = applicationCaches.lastPathComponent; 152 153 if (!this._manifest) { 154 this._emptyMsgElement.removeStyleClass("hidden"); 155 this.deleteButton.visible = false; 156 if (this._dataGrid) 157 this._dataGrid.element.addStyleClass("hidden"); 158 return; 159 } 160 161 if (!this._dataGrid) 162 this._createDataGrid(); 163 164 this._populateDataGrid(); 165 this._dataGrid.autoSizeColumns(20, 80); 166 this._dataGrid.element.removeStyleClass("hidden"); 167 this._emptyMsgElement.addStyleClass("hidden"); 168 this.deleteButton.visible = true; 169 170 var totalSizeString = Number.bytesToString(this._size); 171 this._treeElement.subtitle = WebInspector.UIString("%s (%s)", lastPathComponent, totalSizeString); 172 173 // FIXME: For Chrome, put creationTime and updateTime somewhere. 174 // NOTE: localizedString has not yet been added. 175 // WebInspector.UIString("(%s) Created: %s Updated: %s", this._size, this._creationTime, this._updateTime); 176 }, 177 178 _createDataGrid: function() 179 { 180 var columns = { 0: {}, 1: {}, 2: {} }; 181 columns[0].title = WebInspector.UIString("Resource"); 182 columns[0].sort = "ascending"; 183 columns[0].sortable = true; 184 columns[1].title = WebInspector.UIString("Type"); 185 columns[1].sortable = true; 186 columns[2].title = WebInspector.UIString("Size"); 187 columns[2].aligned = "right"; 188 columns[2].sortable = true; 189 this._dataGrid = new WebInspector.DataGrid(columns); 190 this.element.appendChild(this._dataGrid.element); 191 this._dataGrid.addEventListener("sorting changed", this._populateDataGrid, this); 192 this._dataGrid.updateWidths(); 193 }, 194 195 _populateDataGrid: function() 196 { 197 var selectedResource = this._dataGrid.selectedNode ? this._dataGrid.selectedNode.resource : null; 198 var sortDirection = this._dataGrid.sortOrder === "ascending" ? 1 : -1; 199 200 function numberCompare(field, resource1, resource2) 201 { 202 return sortDirection * (resource1[field] - resource2[field]); 203 } 204 function localeCompare(field, resource1, resource2) 205 { 206 return sortDirection * (resource1[field] + "").localeCompare(resource2[field] + "") 207 } 208 209 var comparator; 210 switch (parseInt(this._dataGrid.sortColumnIdentifier)) { 211 case 0: comparator = localeCompare.bind(this, "name"); break; 212 case 1: comparator = localeCompare.bind(this, "type"); break; 213 case 2: comparator = numberCompare.bind(this, "size"); break; 214 default: localeCompare.bind(this, "resource"); // FIXME: comparator = ? 215 } 216 217 this._resources.sort(comparator); 218 this._dataGrid.removeChildren(); 219 220 var nodeToSelect; 221 for (var i = 0; i < this._resources.length; ++i) { 222 var data = {}; 223 var resource = this._resources[i]; 224 data[0] = resource.name; 225 data[1] = resource.type; 226 data[2] = Number.bytesToString(resource.size); 227 var node = new WebInspector.DataGridNode(data); 228 node.resource = resource; 229 node.selectable = true; 230 this._dataGrid.appendChild(node); 231 if (resource === selectedResource) { 232 nodeToSelect = node; 233 nodeToSelect.selected = true; 234 } 235 } 236 237 if (!nodeToSelect) 238 this._dataGrid.children[0].selected = true; 239 }, 240 241 resize: function() 242 { 243 if (this._dataGrid) 244 this._dataGrid.updateWidths(); 245 }, 246 247 _deleteButtonClicked: function(event) 248 { 249 if (!this._dataGrid || !this._dataGrid.selectedNode) 250 return; 251 252 // FIXME: Delete Button semantics are not yet defined. (Delete a single, or all?) 253 this._deleteCallback(this._dataGrid.selectedNode); 254 }, 255 256 _deleteCallback: function(node) 257 { 258 // FIXME: Should we delete a single (selected) resource or all resources? 259 // InspectorBackend.deleteCachedResource(...) 260 // this._update(); 261 }, 262 263 _refreshButtonClicked: function(event) 264 { 265 // FIXME: Is this a refresh button or a re-fetch manifest button? 266 // this._update(); 267 } 268 } 269 270 WebInspector.ApplicationCacheItemsView.prototype.__proto__ = WebInspector.View.prototype; 271 272 WebInspector.ApplicationCacheDispatcher = function() 273 { 274 } 275 276 WebInspector.ApplicationCacheDispatcher.getApplicationCachesAsync = function(callback) 277 { 278 function mycallback(error, applicationCaches) 279 { 280 // FIXME: Currently, this list only returns a single application cache. 281 if (!error && applicationCaches) 282 callback(applicationCaches); 283 } 284 285 ApplicationCacheAgent.getApplicationCaches(mycallback); 286 } 287 288 WebInspector.ApplicationCacheDispatcher.prototype = { 289 updateApplicationCacheStatus: function(status) 290 { 291 WebInspector.panels.resources.updateApplicationCacheStatus(status); 292 }, 293 294 updateNetworkState: function(isNowOnline) 295 { 296 WebInspector.panels.resources.updateNetworkState(isNowOnline); 297 } 298 } 299 300 InspectorBackend.registerDomainDispatcher("ApplicationCache", new WebInspector.ApplicationCacheDispatcher()); 301