| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 /** | |
| 6 * The ProfilingApp encapsulates the application's global state, | |
| 7 * and controls its subviews. | |
| 8 * | |
| 9 * @constructor | |
| 10 */ | |
| 11 function ProfilingApp() { | |
| 12 // Factories for each of the view types. | |
| 13 this.viewFactories_ = { | |
| 14 list: CreateListView, | |
| 15 stats: CreateStatsView, | |
| 16 }; | |
| 17 | |
| 18 // Views which have been created so far, keyed by their name. | |
| 19 this.liveViews_ = {}; | |
| 20 | |
| 21 // The last list of entries fetched from the server. | |
| 22 this.entries_ = null; | |
| 23 | |
| 24 // The last query filters that entries were fetched from the server for. | |
| 25 this.filters_ = null; | |
| 26 | |
| 27 this.pending_request_ = null; | |
| 28 this.filters_names_ = [ | |
| 29 "platform", "domain", "executable", "first_arg", "limit"]; | |
| 30 this.filters_titles_ = [ | |
| 31 "Platform", "Domain", "Executable", "First Arg", "Limit"]; | |
| 32 | |
| 33 window.addEventListener("load", this.OnPageLoaded.bind(this)); | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * This is the main entry point to the application, when the page has | |
| 38 * finished loading. | |
| 39 */ | |
| 40 ProfilingApp.prototype.OnPageLoaded = function() { | |
| 41 this.CreateFilterTable(); | |
| 42 this.CreateViewSelectionTable(); | |
| 43 this.ApplyQueryParameters(); | |
| 44 this.OnQueryChanged(); | |
| 45 }; | |
| 46 | |
| 47 ProfilingApp.prototype.CreateFilterTable = function() { | |
| 48 var container = document.getElementById("filters_container"); | |
| 49 var form = DomUtil.AddNode(container, "form"); | |
| 50 form.method = "GET"; | |
| 51 // Create a table inside. | |
| 52 var table = DomUtil.AddNode(form, "table"); | |
| 53 for (var i in this.filters_names_) { | |
| 54 var tr = DomUtil.AddNode(table, "tr"); | |
| 55 var th = DomUtil.AddNode(tr, "th"); | |
| 56 DomUtil.AddText(th, this.filters_titles_[i]); | |
| 57 var td = DomUtil.AddNode(tr, "td"); | |
| 58 var input = DomUtil.AddNode(td, "input"); | |
| 59 input.type = "search"; | |
| 60 input.incremental = true; | |
| 61 input.id = this.filters_names_[i]; | |
| 62 input.addEventListener("search", this.OnQueryChanged.bind(this)); | |
| 63 } | |
| 64 }; | |
| 65 | |
| 66 ProfilingApp.prototype.CreateViewSelectionTable = function() { | |
| 67 var container = document.getElementById("view_selection_container"); | |
| 68 var first = true; | |
| 69 for (var i in this.viewFactories_) { | |
| 70 var span = DomUtil.AddNode(container, "span"); | |
| 71 span.id = i + "_badge"; | |
| 72 span.className = "viewBadge"; | |
| 73 if (first) { | |
| 74 span.className += " Selected"; | |
| 75 first = false; | |
| 76 } | |
| 77 var a = DomUtil.AddNode(span, "a"); | |
| 78 a.href = "javascript:void(0)"; | |
| 79 a.onclick = this.SwitchToView.bind(this, i); | |
| 80 DomUtil.AddText(a, this.viewFactories_[i].display_name); | |
| 81 } | |
| 82 }; | |
| 83 | |
| 84 /** | |
| 85 * This method is called whenever the query parameters changed and we need to | |
| 86 * fetch new data off the server to update the views. | |
| 87 */ | |
| 88 ProfilingApp.prototype.OnQueryChanged = function() { | |
| 89 if (this.pending_request_ != null) { | |
| 90 this.pending_request_.abort(); | |
| 91 this.pending_request_ = null; | |
| 92 } | |
| 93 var filters = this.GetFilters(); | |
| 94 | |
| 95 // Invalidate data derived from the previous filters. | |
| 96 this.filters_ = null; | |
| 97 this.entries_ = null; | |
| 98 | |
| 99 // Clear all views. | |
| 100 for (var i in this.liveViews_) { | |
| 101 this.ShowView(i, false); | |
| 102 } | |
| 103 this.liveViews_ = {}; | |
| 104 | |
| 105 // Use AJAX to fetch the tree status data for the new filters. | |
| 106 // On completion it will call OnDataAvailable() with the data. | |
| 107 this.SetLoadingIndicator("Fetching data..."); | |
| 108 var callback = this.OnDataAvailable.bind(this, filters); | |
| 109 this.pending_request_ = DataFetcher.GetEntries(filters, callback); | |
| 110 }; | |
| 111 | |
| 112 /** | |
| 113 * This method is called when new data has been received from the server. | |
| 114 */ | |
| 115 ProfilingApp.prototype.OnDataAvailable = function(filters, entries) { | |
| 116 this.SetLoadingIndicator(""); | |
| 117 | |
| 118 // Set the new data as current. | |
| 119 this.pending_request_ = null; | |
| 120 this.filters_ = filters; | |
| 121 this.entries_ = entries; | |
| 122 | |
| 123 if (this.entries_) { | |
| 124 // Force the view to redraw itself using the new data. | |
| 125 this.SwitchToView(this.GetCurrentViewName()); | |
| 126 } | |
| 127 }; | |
| 128 | |
| 129 /** | |
| 130 * Gets the filters the user is interested in. | |
| 131 * @return {Stuff} | |
| 132 */ | |
| 133 ProfilingApp.prototype.GetFilters = function() { | |
| 134 var filters = {}; | |
| 135 for (var i = 0; i < this.filters_names_.length; ++i) { | |
| 136 var name = this.filters_names_[i]; | |
| 137 var d = document.getElementById(name); | |
| 138 filters[name] = d.value; | |
| 139 } | |
| 140 return filters; | |
| 141 }; | |
| 142 | |
| 143 /** | |
| 144 * Gets the current window's URL's query parameters, as a | |
| 145 * dictionary of key/value pairs. | |
| 146 * | |
| 147 * @return {Object} Dictionary of name/value pairs. | |
| 148 */ | |
| 149 function GetQueryParameters() { | |
| 150 var values = {}; | |
| 151 if (window.location.search) { | |
| 152 params = window.location.search.substr(1).split("&"); | |
| 153 for (var i = 0; i < params.length; ++i) { | |
| 154 var parts = params[i].split("="); | |
| 155 if (parts.length == 2) { | |
| 156 values[parts[0]] = decodeURIComponent(parts[1]); | |
| 157 } | |
| 158 } | |
| 159 } | |
| 160 return values; | |
| 161 } | |
| 162 | |
| 163 /** | |
| 164 * Checks the URL for query parameters, and applies any with meaning to us. | |
| 165 */ | |
| 166 ProfilingApp.prototype.ApplyQueryParameters = function() { | |
| 167 var params = GetQueryParameters(); | |
| 168 for (var i = 0; i < this.filters_names_.length; ++i) { | |
| 169 var d = document.getElementById(this.filters_names_[i]); | |
| 170 if (this.filters_names_[i] in params) { | |
| 171 d.value = params[this.filters_names_[i]]; | |
| 172 } | |
| 173 } | |
| 174 }; | |
| 175 | |
| 176 /** | |
| 177 * Updates a part of the UI to show we are waiting for stuff to happen. | |
| 178 * @param {string} text The message to display. | |
| 179 */ | |
| 180 ProfilingApp.prototype.SetLoadingIndicator = function(text) { | |
| 181 var d = document.getElementById("loading"); | |
| 182 d.innerHTML = text; | |
| 183 DomUtil.DisplayNode(d, text != ""); | |
| 184 }; | |
| 185 | |
| 186 /** | |
| 187 * Gets the name of the currently active view. | |
| 188 * @return {string} | |
| 189 */ | |
| 190 ProfilingApp.prototype.GetCurrentViewName = function() { | |
| 191 var previous_view = ClassUtil.FindElementByClassName( | |
| 192 "viewBadge Selected").match("^(.+)_badge$")[1]; | |
| 193 if (!previous_view) { | |
| 194 throw "Previous view cannot be found"; | |
| 195 } | |
| 196 return previous_view; | |
| 197 }; | |
| 198 | |
| 199 /** | |
| 200 * Sets |viewName| as the active view. | |
| 201 * @param {string} viewName | |
| 202 */ | |
| 203 ProfilingApp.prototype.SetCurrentViewName = function(viewName) { | |
| 204 if (!viewName) { | |
| 205 throw "Can't switch to undefined"; | |
| 206 } | |
| 207 ClassUtil.ResetClass("viewBadge", viewName + "_badge", "Selected"); | |
| 208 }; | |
| 209 | |
| 210 /** | |
| 211 * Switches |viewName| to be the active view. | |
| 212 * @param {string} viewName | |
| 213 */ | |
| 214 ProfilingApp.prototype.SwitchToView = function(viewName) { | |
| 215 var prevViewName = this.GetCurrentViewName(); | |
| 216 this.SetCurrentViewName(viewName); | |
| 217 | |
| 218 // Hide the previously active view. | |
| 219 if (this.liveViews_[prevViewName]) { | |
| 220 this.ShowView(prevViewName, false); | |
| 221 } | |
| 222 | |
| 223 // If a view hasn't been created yet for |viewName|, do so. | |
| 224 if (!this.liveViews_[viewName]) { | |
| 225 this.liveViews_[viewName] = | |
| 226 this.viewFactories_[viewName](this.filters_, this.entries_); | |
| 227 } | |
| 228 | |
| 229 // Show the now active view. | |
| 230 this.ShowView(viewName, true); | |
| 231 }; | |
| 232 | |
| 233 /** | |
| 234 * Generic method to change the styling of the view's tab handle to indicate | |
| 235 * whether it is active, and to show/hide its content pane. | |
| 236 * This assumes we have given consistent IDs to the elements. | |
| 237 * | |
| 238 * @param {string} viewName | |
| 239 * @param {boolean} visible | |
| 240 */ | |
| 241 ProfilingApp.prototype.ShowView = function(viewName, visible) { | |
| 242 var element = document.getElementById(viewName + "_container"); | |
| 243 if (!element) { | |
| 244 throw "Couldn't find " + viewName; | |
| 245 } | |
| 246 DomUtil.DisplayNode(element, visible); | |
| 247 this.liveViews_[viewName].OnShow(visible); | |
| 248 }; | |
| OLD | NEW |