1 /* 2 * Copyright (C) 2011 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 WebInspector.AuditLauncherView = function(runnerCallback) 32 { 33 WebInspector.View.call(this); 34 this._runnerCallback = runnerCallback; 35 this._categoryIdPrefix = "audit-category-item-"; 36 this._auditRunning = false; 37 38 this.element.addStyleClass("audit-launcher-view"); 39 40 this._contentElement = document.createElement("div"); 41 this._contentElement.className = "audit-launcher-view-content"; 42 this.element.appendChild(this._contentElement); 43 this._boundCategoryClickListener = this._categoryClicked.bind(this); 44 45 this._resetResourceCount(); 46 47 this._sortedCategories = []; 48 49 this._headerElement = document.createElement("h1"); 50 this._headerElement.className = "no-audits"; 51 this._headerElement.textContent = WebInspector.UIString("No audits to run"); 52 this._contentElement.appendChild(this._headerElement); 53 54 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this); 55 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceFinished, this); 56 } 57 58 WebInspector.AuditLauncherView.prototype = { 59 _resetResourceCount: function() 60 { 61 this._loadedResources = 0; 62 this._totalResources = 0; 63 }, 64 65 _onResourceStarted: function(event) 66 { 67 var resource = event.data; 68 // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway. 69 if (resource.type === WebInspector.Resource.Type.WebSocket) 70 return; 71 ++this._totalResources; 72 this._updateResourceProgress(); 73 }, 74 75 _onResourceFinished: function(event) 76 { 77 var resource = event.data; 78 // See resorceStarted for details. 79 if (resource.type === WebInspector.Resource.Type.WebSocket) 80 return; 81 ++this._loadedResources; 82 this._updateResourceProgress(); 83 }, 84 85 addCategory: function(category) 86 { 87 if (!this._sortedCategories.length) 88 this._createLauncherUI(); 89 90 var categoryElement = this._createCategoryElement(category.displayName, category.id); 91 category._checkboxElement = categoryElement.firstChild; 92 if (this._selectAllCheckboxElement.checked) { 93 category._checkboxElement.checked = true; 94 ++this._currentCategoriesCount; 95 } 96 97 function compareCategories(a, b) 98 { 99 var aTitle = a.displayName || ""; 100 var bTitle = b.displayName || ""; 101 return aTitle.localeCompare(bTitle); 102 } 103 var insertBefore = insertionIndexForObjectInListSortedByFunction(category, this._sortedCategories, compareCategories); 104 this._categoriesElement.insertBefore(categoryElement, this._categoriesElement.children[insertBefore]); 105 this._sortedCategories.splice(insertBefore, 0, category); 106 this._updateButton(); 107 }, 108 109 _setAuditRunning: function(auditRunning) 110 { 111 if (this._auditRunning === auditRunning) 112 return; 113 this._auditRunning = auditRunning; 114 this._updateButton(); 115 this._updateResourceProgress(); 116 }, 117 118 _launchButtonClicked: function(event) 119 { 120 var catIds = []; 121 var childNodes = this._categoriesElement.childNodes; 122 for (var category = 0; category < this._sortedCategories.length; ++category) { 123 if (this._sortedCategories[category]._checkboxElement.checked) 124 catIds.push(this._sortedCategories[category].id); 125 } 126 127 this._setAuditRunning(true); 128 this._runnerCallback(catIds, this._auditPresentStateElement.checked, this._setAuditRunning.bind(this, false)); 129 }, 130 131 _selectAllClicked: function(checkCategories) 132 { 133 var childNodes = this._categoriesElement.childNodes; 134 for (var i = 0, length = childNodes.length; i < length; ++i) 135 childNodes[i].firstChild.checked = checkCategories; 136 this._currentCategoriesCount = checkCategories ? this._sortedCategories.length : 0; 137 this._updateButton(); 138 }, 139 140 _categoryClicked: function(event) 141 { 142 this._currentCategoriesCount += event.target.checked ? 1 : -1; 143 this._selectAllCheckboxElement.checked = this._currentCategoriesCount === this._sortedCategories.length; 144 this._updateButton(); 145 }, 146 147 _createCategoryElement: function(title, id) 148 { 149 var labelElement = document.createElement("label"); 150 labelElement.id = this._categoryIdPrefix + id; 151 152 var element = document.createElement("input"); 153 element.type = "checkbox"; 154 if (id !== "") 155 element.addEventListener("click", this._boundCategoryClickListener, false); 156 labelElement.appendChild(element); 157 labelElement.appendChild(document.createTextNode(title)); 158 159 return labelElement; 160 }, 161 162 _createLauncherUI: function() 163 { 164 this._headerElement = document.createElement("h1"); 165 this._headerElement.textContent = WebInspector.UIString("Select audits to run"); 166 167 for (var child = 0; child < this._contentElement.children.length; ++child) 168 this._contentElement.removeChild(this._contentElement.children[child]); 169 170 this._contentElement.appendChild(this._headerElement); 171 172 function handleSelectAllClick(event) 173 { 174 this._selectAllClicked(event.target.checked); 175 } 176 var categoryElement = this._createCategoryElement(WebInspector.UIString("Select All"), ""); 177 categoryElement.id = "audit-launcher-selectall"; 178 this._selectAllCheckboxElement = categoryElement.firstChild; 179 this._selectAllCheckboxElement.checked = true; 180 this._selectAllCheckboxElement.addEventListener("click", handleSelectAllClick.bind(this), false); 181 this._contentElement.appendChild(categoryElement); 182 183 this._categoriesElement = document.createElement("div"); 184 this._categoriesElement.className = "audit-categories-container"; 185 this._contentElement.appendChild(this._categoriesElement); 186 187 this._currentCategoriesCount = 0; 188 189 var flexibleSpaceElement = document.createElement("div"); 190 flexibleSpaceElement.className = "flexible-space"; 191 this._contentElement.appendChild(flexibleSpaceElement); 192 193 this._buttonContainerElement = document.createElement("div"); 194 this._buttonContainerElement.className = "button-container"; 195 196 var labelElement = document.createElement("label"); 197 this._auditPresentStateElement = document.createElement("input"); 198 this._auditPresentStateElement.name = "audit-mode"; 199 this._auditPresentStateElement.type = "radio"; 200 this._auditPresentStateElement.checked = true; 201 this._auditPresentStateLabelElement = document.createTextNode(WebInspector.UIString("Audit Present State")); 202 labelElement.appendChild(this._auditPresentStateElement); 203 labelElement.appendChild(this._auditPresentStateLabelElement); 204 this._buttonContainerElement.appendChild(labelElement); 205 206 labelElement = document.createElement("label"); 207 this.auditReloadedStateElement = document.createElement("input"); 208 this.auditReloadedStateElement.name = "audit-mode"; 209 this.auditReloadedStateElement.type = "radio"; 210 labelElement.appendChild(this.auditReloadedStateElement); 211 labelElement.appendChild(document.createTextNode("Reload Page and Audit on Load")); 212 this._buttonContainerElement.appendChild(labelElement); 213 214 this._launchButton = document.createElement("button"); 215 this._launchButton.type = "button"; 216 this._launchButton.textContent = WebInspector.UIString("Run"); 217 this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false); 218 this._buttonContainerElement.appendChild(this._launchButton); 219 220 this._resourceProgressContainer = document.createElement("span"); 221 this._resourceProgressContainer.className = "resource-progress"; 222 var resourceProgressImage = document.createElement("img"); 223 this._resourceProgressContainer.appendChild(resourceProgressImage); 224 this._resourceProgressTextElement = document.createElement("span"); 225 this._resourceProgressContainer.appendChild(this._resourceProgressTextElement); 226 this._buttonContainerElement.appendChild(this._resourceProgressContainer); 227 228 this._contentElement.appendChild(this._buttonContainerElement); 229 230 this._selectAllClicked(this._selectAllCheckboxElement.checked); 231 this._updateButton(); 232 this._updateResourceProgress(); 233 }, 234 235 _updateResourceProgress: function() 236 { 237 if (!this._resourceProgressContainer) 238 return; 239 240 if (!this._auditRunning) { 241 this._resetResourceCount(); 242 this._resourceProgressContainer.addStyleClass("hidden"); 243 } else 244 this._resourceProgressContainer.removeStyleClass("hidden"); 245 this._resourceProgressTextElement.textContent = WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources); 246 }, 247 248 _updateButton: function() 249 { 250 this._launchButton.disabled = !this._currentCategoriesCount || this._auditRunning; 251 } 252 } 253 254 WebInspector.AuditLauncherView.prototype.__proto__ = WebInspector.View.prototype; 255