| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @fileoverview Code for adding auto-refreshing bar charts and realtime |
| * annotated time line on the basis of the data in statistics page. |
| * |
| * TODO(xqyin): Integrate this into console page. |
| * |
| * @author oschaaf@we-amp.com (Otto van der Schaaf) |
| * @author xqyin@google.com (XiaoQian Yin) |
| */ |
| |
| goog.provide('pagespeed.Graphs'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.events'); |
| goog.require('goog.net.EventType'); |
| goog.require('goog.net.XhrIo'); |
| goog.require('goog.string'); |
| |
| // Google Charts API. |
| // Requires <script src='https://www.google.com/jsapi'></script> loaded in HTML. |
| google.load('visualization', '1', |
| {'packages': ['table', 'corechart', 'annotatedtimeline']}); |
| |
| |
| /** @typedef {{name: string, value:string}} */ |
| pagespeed.StatsNode; |
| |
| |
| /** @typedef {{messages: Array.<pagespeed.StatsNode>, timeReceived: Date}} */ |
| pagespeed.StatsArray; |
| |
| |
| |
| /** |
| * @constructor |
| * @param {goog.testing.net.XhrIo=} opt_xhr Optional mock XmlHttpRequests |
| * handler for testing. |
| */ |
| pagespeed.Graphs = function(opt_xhr) { |
| /** |
| * The XmlHttpRequests handler for auto-refresh. We pass a mock XhrIo |
| * when testing. Default is a normal XhrIo. |
| * @private {goog.net.XhrIo|goog.testing.net.XhrIo} |
| */ |
| this.xhr_ = opt_xhr || new goog.net.XhrIo(); |
| |
| /** |
| * This array of arrays collects historical data from statistics log file |
| * for the first refresh and updates from back end to get snapshot of the |
| * current data. The i-th entry of the array is the statistics corresponds |
| * to the i-th time stamp, which is an array of special nodes. Each node |
| * contains certain statistics name and the value. We use the last entry to |
| * generate pie charts showing the most recent data. The whole array of |
| * arrays is used to generate line charts showing statistics history. We only |
| * keep one-day data to avoid infinite growth of this array. |
| * @private {!Array.<pagespeed.StatsArray>} |
| */ |
| this.psolMessages_ = []; |
| |
| /** |
| * The option of auto-refresh. If true, the page will automatically refresh |
| * itself every 5 seconds. |
| * @private {boolean} |
| */ |
| this.autoRefresh_ = false; |
| // We set the default to false because auto-refresh would impact the user |
| // experience of annotated time line. |
| |
| /** |
| * The flag of whether the first refresh is started. We need to call a |
| * refresh when loading the page for the initialization. Default is false, |
| * which means the page hasn't started the first refresh yet. |
| * @private {boolean} |
| */ |
| this.firstRefreshStarted_ = false; |
| |
| /** |
| * The flag of whether the second refresh is started. Since we fetch data |
| * from statistics log file for the first refresh, the data may be stale. |
| * We need to do the second refresh immediately to make sure the data we are |
| * showing is up-to-date. |
| * @private {boolean} |
| */ |
| this.secondRefreshStarted_ = false; |
| |
| /** |
| * Maps chart titles to the chart object. |
| * @private {Object} |
| */ |
| this.chartCache_ = {}; |
| |
| // Hide all div elements. |
| // To use AnnotatedTimeLine charts, we must specify the size of the container |
| // elements explicitly instead of setting the size in the options of charts. |
| // Since we cannot get the size of an element with 'display:none;', we hide |
| // all the elements by positioning them off the left hand side. |
| for (var i in pagespeed.Graphs.DisplayDiv) { |
| var chartDiv = document.getElementById(pagespeed.Graphs.DisplayDiv[i]); |
| chartDiv.className = 'pagespeed-hidden-offscreen'; |
| } |
| |
| // The navigation bar to switch among different display modes. |
| var navElement = document.createElement('table'); |
| navElement.id = 'nav-bar'; |
| navElement.className = 'pagespeed-sub-tabs'; |
| navElement.innerHTML = |
| '<tr><td><a id="' + pagespeed.Graphs.DisplayMode.CACHE_APPLIED + |
| '" href="javascript:void(0);">' + |
| 'Per application cache stats</a> - </td>' + |
| '<td><a id="' + pagespeed.Graphs.DisplayMode.CACHE_TYPE + |
| '" href="javascript:void(0);">' + |
| 'Per type cache stats</a> - </td>' + |
| '<td><a id="' + pagespeed.Graphs.DisplayMode.IPRO + |
| '" href="javascript:void(0);">' + |
| 'IPRO status</a> - </td>' + |
| '<td><a id="' + pagespeed.Graphs.DisplayMode.REWRITE_IMAGE + |
| '" href="javascript:void(0);">' + |
| 'Image rewriting</a> - </td>' + |
| '<td><a id="' + pagespeed.Graphs.DisplayMode.REALTIME + |
| '" href="javascript:void(0);">' + |
| 'Realtime</a></td></tr>'; |
| // The UI table of auto-refresh. |
| var uiTable = document.createElement('div'); |
| uiTable.id = 'ui-div'; |
| uiTable.innerHTML = |
| '<table id="ui-table" border=1 style="border-collapse: ' + |
| 'collapse;border-color:silver;"><tr valign="center">' + |
| '<td>Auto refresh (every 5 seconds): <input type="checkbox" ' + |
| 'id="auto-refresh" ' + (this.autoRefresh_ ? 'checked' : '') + |
| '></td></tr></table>'; |
| document.body.insertBefore( |
| uiTable, |
| document.getElementById(pagespeed.Graphs.DisplayDiv.CACHE_APPLIED)); |
| document.body.insertBefore(navElement, document.getElementById('ui-div')); |
| }; |
| |
| |
| /** |
| * Show the chosen div element and hide all other elements. |
| * @param {string} div The chosen div element to display. |
| */ |
| pagespeed.Graphs.prototype.show = function(div) { |
| // Only shows the div chosen by users. |
| var i; |
| for (i in pagespeed.Graphs.DisplayDiv) { |
| var chartDiv = pagespeed.Graphs.DisplayDiv[i]; |
| if (chartDiv == div) { |
| document.getElementById(chartDiv).className = ''; |
| } else { |
| document.getElementById(chartDiv).className = |
| 'pagespeed-hidden-offscreen'; |
| } |
| } |
| |
| // Underline the current tab. |
| var currentTab = document.getElementById(div + '_mode'); |
| for (i in pagespeed.Graphs.DisplayMode) { |
| var link = document.getElementById(pagespeed.Graphs.DisplayMode[i]); |
| if (link == currentTab) { |
| link.className = 'pagespeed-underline-link'; |
| } else { |
| link.className = ''; |
| } |
| } |
| |
| location.href = location.href.split('#')[0] + '#' + div; |
| // TODO(xqyin): Note that there are similar changes under caches page as |
| // well. We should factor out the logic and let similar codes like this |
| // being shared by different pages. |
| }; |
| |
| |
| /** |
| * Parse the location URL to get its anchor part and show the div element. |
| */ |
| pagespeed.Graphs.prototype.parseLocation = function() { |
| var div = location.hash.substr(1); |
| if (div == '') { |
| this.show(pagespeed.Graphs.DisplayDiv.CACHE_APPLIED); |
| } else if (goog.object.contains(pagespeed.Graphs.DisplayDiv, div)) { |
| this.show(div); |
| } |
| }; |
| |
| |
| /** |
| * The option of the display mode of the graphs page. Users can switch modes |
| * by the second level navigation bar shown on the page. The page would show |
| * the corresponding div elements containing charts. |
| * @enum {string} |
| */ |
| pagespeed.Graphs.DisplayMode = { |
| CACHE_APPLIED: 'cache_applied_mode', |
| CACHE_TYPE: 'cache_type_mode', |
| IPRO: 'ipro_mode', |
| REWRITE_IMAGE: 'image_rewriting_mode', |
| REALTIME: 'realtime_mode' |
| }; |
| |
| |
| /** |
| * The id of the div element that should be displayed in each mode. Only the |
| * chosen div element would be shown. Others would be hidden. |
| * @enum {string} |
| */ |
| pagespeed.Graphs.DisplayDiv = { |
| CACHE_APPLIED: 'cache_applied', |
| CACHE_TYPE: 'cache_type', |
| IPRO: 'ipro', |
| REWRITE_IMAGE: 'image_rewriting', |
| REALTIME: 'realtime' |
| }; |
| |
| |
| /** |
| * Updates the option of auto-refresh. |
| */ |
| pagespeed.Graphs.prototype.toggleAutorefresh = function() { |
| this.autoRefresh_ = !this.autoRefresh_; |
| }; |
| |
| |
| /** |
| * Generate a URL to request statistics data over default time period. |
| * @return {string} The query URL incorporating all time parameters. |
| */ |
| pagespeed.Graphs.prototype.createQueryUrl = function() { |
| var granularityMs = pagespeed.Graphs.FREQUENCY_ * 1000; |
| var durationMs = pagespeed.Graphs.TIMERANGE_ * granularityMs; // 1 Day. |
| var endTime = new Date(); |
| var startTime = new Date(endTime - durationMs); |
| var queryString = '?json'; |
| queryString += '&start_time=' + startTime.getTime(); |
| queryString += '&end_time=' + endTime.getTime(); |
| queryString += '&granularity=' + granularityMs; |
| return queryString; |
| }; |
| |
| |
| /** |
| * Parses historical data sent by sever. |
| * @param {string} text The statistics dumped in JSON format. |
| */ |
| pagespeed.Graphs.prototype.parseHistoricalMessages = function(text) { |
| var jsonData = JSON.parse(text); |
| var timestamps = jsonData['timestamps']; |
| var variables = jsonData['variables']; |
| for (var i = 0; i < timestamps.length; ++i) { |
| var messages = []; |
| for (var name in variables) { |
| var node = { |
| name: name, |
| value: variables[name][i] |
| }; |
| messages.push(node); |
| } |
| var newArray = { |
| messages: messages, |
| timeReceived: new Date(timestamps[i]) |
| }; |
| this.psolMessages_.push(newArray); |
| } |
| }; |
| |
| |
| /** |
| * Parses the most recent data sent by sever. |
| * @param {string} text The statistics dumped in JSON format. |
| */ |
| pagespeed.Graphs.prototype.parseSnapshotMessages = function(text) { |
| var jsonData = JSON.parse(text); |
| var variables = jsonData['variables']; |
| var messages = []; |
| for (var name in variables) { |
| var node = { |
| name: name, |
| value: variables[name] |
| }; |
| messages.push(node); |
| } |
| var newArray = { |
| messages: messages, |
| timeReceived: new Date() |
| }; |
| this.psolMessages_.push(newArray); |
| }; |
| |
| |
| /** |
| * Capture the pathname and return the URL for request. |
| * @return {string} The URL to request the updated data in JSON format. |
| */ |
| pagespeed.Graphs.prototype.requestUrl = function() { |
| var pathName = location.pathname; |
| // Ignore the trailing '/'. |
| var n = pathName.lastIndexOf('/', pathName.length - 2); |
| // e.g. /pagespeed_admin/foo or pagespeed_admin/foo |
| if (n > 0) { |
| return pathName.substring(0, n) + '/stats_json'; |
| } else { |
| // e.g. /pagespeed_admin or pagespeed_admin |
| return pathName + '/stats_json'; |
| } |
| }; |
| |
| |
| /** |
| * Refreshes the page by making requsts to server. |
| */ |
| pagespeed.Graphs.prototype.performRefresh = function() { |
| // TODO(xqyin): Figure out if there is a potential timing issue that the |
| // xhr.isActive() would be false before the callback starts. |
| if (!this.xhr_.isActive()) { |
| // If the first refresh has not started yet, then do the refresh no matter |
| // what the autoRefresh option is. Because the page needs to send a query |
| // URL to request historical data to get initialized. |
| if (!this.firstRefreshStarted_) { |
| this.firstRefreshStarted_ = true; |
| this.xhr_.send(this.createQueryUrl()); |
| // The page needs to do the second refresh to finish initialization. |
| // Otherwise, only refresh when the autoRefresh option is true. |
| } else if (!this.secondRefreshStarted_ || this.autoRefresh_) { |
| this.secondRefreshStarted_ = true; |
| this.xhr_.send(this.requestUrl()); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Parses the response sent by server and draws charts. |
| */ |
| pagespeed.Graphs.prototype.parseAjaxResponse = function() { |
| if (this.xhr_.isSuccess()) { |
| var text = this.xhr_.getResponseText(); |
| if (!this.secondRefreshStarted_) { |
| // We collect historical data in the first refresh and wait for the |
| // second refresh to update data. We won't call functions to draw charts |
| // until we make data up-to-date. |
| this.parseHistoricalMessages(text); |
| // Add the second refresh to the queue with no delay time. So it would be |
| // implemented immediately after the first refresh completion. |
| window.setTimeout(goog.bind(this.performRefresh, this), 0); |
| } else { |
| this.parseSnapshotMessages(text); |
| // Only keep one-day statistics. |
| if (this.psolMessages_.length > pagespeed.Graphs.TIMERANGE_) { |
| this.psolMessages_.shift(); |
| } |
| this.drawVisualization(); |
| } |
| } else { |
| console.log(this.xhr_.getLastError()); |
| } |
| }; |
| |
| |
| /** |
| * Initialization for drawing all the charts. |
| */ |
| pagespeed.Graphs.prototype.drawVisualization = function() { |
| this.drawBarChart('pcache-cohorts-dom', 'Property cache dom cohorts', |
| pagespeed.Graphs.DisplayDiv.CACHE_APPLIED); |
| this.drawBarChart('pcache-cohorts-beacon', 'Property cache beacon cohorts', |
| pagespeed.Graphs.DisplayDiv.CACHE_APPLIED); |
| this.drawBarChart('rewrite_cached_output', 'Rewrite cached output', |
| pagespeed.Graphs.DisplayDiv.CACHE_APPLIED); |
| this.drawBarChart('url_input', 'URL Input', |
| pagespeed.Graphs.DisplayDiv.CACHE_APPLIED); |
| |
| this.drawBarChart('cache', 'Cache', |
| pagespeed.Graphs.DisplayDiv.CACHE_TYPE); |
| this.drawBarChart('file_cache', 'File Cache', |
| pagespeed.Graphs.DisplayDiv.CACHE_TYPE); |
| this.drawBarChart('memcached', 'Memcached', |
| pagespeed.Graphs.DisplayDiv.CACHE_TYPE); |
| this.drawBarChart('lru_cache', 'LRU', |
| pagespeed.Graphs.DisplayDiv.CACHE_TYPE); |
| this.drawBarChart('shm_cache', 'Shared Memory', |
| pagespeed.Graphs.DisplayDiv.CACHE_TYPE); |
| |
| this.drawBarChart('ipro', 'In place resource optimization', |
| pagespeed.Graphs.DisplayDiv.IPRO); |
| |
| this.drawBarChart('image_rewrite', 'Image rewrite', |
| pagespeed.Graphs.DisplayDiv.REWRITE_IMAGE); |
| this.drawBarChart('image_rewrites_dropped', 'Image rewrites dropped', |
| pagespeed.Graphs.DisplayDiv.REWRITE_IMAGE); |
| |
| this.drawHistoryChart('http', 'Http', pagespeed.Graphs.DisplayDiv.REALTIME); |
| this.drawHistoryChart('file_cache', 'File Cache RT', |
| pagespeed.Graphs.DisplayDiv.REALTIME); |
| this.drawHistoryChart('lru_cache', 'LRU Cache RT', |
| pagespeed.Graphs.DisplayDiv.REALTIME); |
| this.drawHistoryChart('serf_fetch', 'Serf stats RT', |
| pagespeed.Graphs.DisplayDiv.REALTIME); |
| this.drawHistoryChart('rewrite', 'Rewrite stats RT', |
| pagespeed.Graphs.DisplayDiv.REALTIME); |
| }; |
| |
| |
| /** |
| * Screens data to generate charts according to the setting prefix. |
| * @param {string} prefix The setting prefix to match. |
| * @param {string} name The name of the statistics. |
| * @return {boolean} Return true if the data should be used in the chart. |
| */ |
| pagespeed.Graphs.screenData = function(prefix, name) { |
| var use = true; |
| if (name.indexOf(prefix) != 0) { |
| use = false; |
| // We skip here because the statistics below won't be used in any charts. |
| } else if (name.indexOf('cache_flush_timestamp_ms') >= 0) { |
| use = false; |
| } else if (name.indexOf('cache_flush_count') >= 0) { |
| use = false; |
| } else if (name.indexOf('cache_time_us') >= 0) { |
| use = false; |
| } |
| return use; |
| }; |
| |
| |
| /** |
| * Initialize a chart using Google Charts API. |
| * @param {string} id The ID of the chart div to create. |
| * @param {string} title The title of the chart. |
| * @param {string} chartType The type of chart. AnnotatedTimeLine or BarChart. |
| * @param {string} targetId The id of the target HTML element. |
| * @return {Object} The created chart object. |
| */ |
| pagespeed.Graphs.prototype.initChart = function( |
| id, title, chartType, targetId) { |
| var theChart; |
| // TODO(oschaaf): Title might not be unique |
| if (this.chartCache_[title]) { |
| theChart = this.chartCache_[title]; |
| } else { |
| // The element identified by the id must exist. |
| var targetElement = document.getElementById(targetId); |
| // Remove the status info when it starts to draw charts. |
| if (targetElement.textContent == 'Loading Charts...') { |
| targetElement.textContent = ''; |
| } |
| var dest = document.createElement('div'); |
| if (chartType == 'AnnotatedTimeLine') { |
| dest.className = 'pagespeed-graphs-chart'; |
| } |
| dest.id = id; |
| var chartTitle = document.createElement('p'); |
| chartTitle.textContent = title; |
| |
| chartTitle.className = 'pagespeed-graphs-title'; |
| targetElement.appendChild(chartTitle); |
| targetElement.appendChild(dest); |
| theChart = new google.visualization[chartType](dest); |
| this.chartCache_[title] = theChart; |
| } |
| return theChart; |
| }; |
| |
| |
| /** |
| * Draw a bar chart using Google Charts API. |
| * @param {string} prefix The statistics prefix of data for the chart. |
| * @param {string} title The title of the chart. |
| * @param {string} targetId The id of the target HTML element. |
| */ |
| pagespeed.Graphs.prototype.drawBarChart = function(prefix, title, targetId) { |
| var id = 'pagespeed-graphs-' + prefix; |
| var settingPrefix = prefix + '_'; |
| var theChart = this.initChart(id, title, 'BarChart', targetId); |
| var dest = document.getElementById(id); |
| var rows = []; |
| var data = new google.visualization.DataTable(); |
| |
| var messages = goog.array.clone( |
| this.psolMessages_[this.psolMessages_.length - 1].messages); |
| var numBars = 0; |
| for (var i = 0; i < messages.length; ++i) { |
| if (!pagespeed.Graphs.screenData(settingPrefix, messages[i].name)) { |
| continue; |
| } |
| ++numBars; |
| |
| // Removes the prefix. |
| var caption = messages[i].name.substring(settingPrefix.length); |
| // We use regexp here to replace underscores all at once. |
| // Using '_' would only replace one underscore at a time. |
| caption = caption.replace(/_/g, ' '); |
| rows.push([caption, Number(messages[i].value)]); |
| } |
| data.addColumn('string', 'Name'); |
| data.addColumn('number', 'Value'); |
| data.addRows(rows); |
| |
| var view = new google.visualization.DataView(data); |
| var getStats = function(dataTable, rowNum) { |
| var sum = 0; |
| for (var i = 0; i < dataTable.getNumberOfRows(); ++i) { |
| sum += dataTable.getValue(i, 1); |
| } |
| var value = dataTable.getValue(rowNum, 1); |
| var percent = value * 100 / ((sum == 0) ? 1 : sum); |
| return value.toString() + ' (' + percent.toFixed(2).toString() + '%)'; |
| }; |
| view.setColumns( |
| [0, 1, {'calc': getStats, 'type': 'string', 'role': 'annotation'}]); |
| |
| var barHeight = 40 * numBars + 10; |
| dest.style.height = barHeight + 20; |
| |
| // The options used for drawing google.visualization.barChart graphs. |
| var barChartOptions = { |
| 'annotations': { |
| // TODO(jmarantz): Although I kind of prefer the consistency of |
| // always showing labels outside the bar, it does waste a bit of |
| // horizontal space. However we have no choice at the moment |
| // due to a Charts API bug estimating text sizes in the context |
| // of bars that are not high enough to fit two lines of text. |
| // |
| // Once this bug is fixed (best estimate: Oct 2014) we should |
| // consider setting 'alwaysOutside' to 'false', and then we can |
| // try changing the chartArea width percentage below from 60% to 80%. |
| 'alwaysOutside': true, // Always put the "value(%)" outside the bar. |
| 'highContrast': true, |
| 'textStyle': { |
| 'fontSize': 12, |
| 'color': 'black' |
| } |
| }, |
| 'hAxis': { |
| 'direction': 1 |
| }, |
| 'vAxis': { |
| 'textPosition': 'out' // position of left-most vertical axis label. |
| }, |
| 'legend': { |
| 'position': 'none' |
| }, |
| 'width': 800, |
| 'height': barHeight, |
| 'chartArea': { |
| 'left': 225, |
| 'top': 0, |
| 'width': '60%', // Tweak this to avoid clipping outside labels. |
| 'height': '80%' |
| |
| } |
| }; |
| theChart.draw(view, barChartOptions); |
| }; |
| |
| |
| /** |
| * Draw a history chart using Google Charts API. |
| * @param {string} prefix The statistics prefix of data for the chart. |
| * @param {string} title The title of the chart. |
| * @param {string} targetId The id of the target HTML element. |
| */ |
| pagespeed.Graphs.prototype.drawHistoryChart = function( |
| prefix, title, targetId) { |
| var id = 'pagespeed-graphs-' + prefix; |
| var settingPrefix = prefix + '_'; |
| var theChart = this.initChart(id, title, 'AnnotatedTimeLine', targetId); |
| var dest = document.getElementById(id); |
| var rows = []; |
| var data = new google.visualization.DataTable(); |
| |
| // The annotated time line for data history. |
| data.addColumn('datetime', 'Time'); |
| var first = true; |
| for (var i = 0; i < this.psolMessages_.length; ++i) { |
| var messages = goog.array.clone(this.psolMessages_[i].messages); |
| var row = []; |
| row.push(this.psolMessages_[i].timeReceived); |
| for (var j = 0; j < messages.length; ++j) { |
| if (!pagespeed.Graphs.screenData(settingPrefix, messages[j].name)) { |
| continue; |
| } |
| row.push(Number(messages[j].value)); |
| if (first) { |
| var caption = messages[j].name.substring(settingPrefix.length); |
| caption = caption.replace(/_/g, ' '); |
| data.addColumn('number', caption); |
| } |
| } |
| first = false; |
| rows.push(row); |
| } |
| data.addRows(rows); |
| theChart.draw(data, pagespeed.Graphs.ANNOTATED_TIMELINE_OPTIONS_); |
| }; |
| |
| |
| /** |
| * The options used for drawing google.visualization.AnnotatedTimeLine graphs. |
| * @private {Object} |
| * @const |
| */ |
| pagespeed.Graphs.ANNOTATED_TIMELINE_OPTIONS_ = { |
| 'thickness': 1, |
| 'displayExactValues': true, |
| 'legendPosition': 'newRow' |
| }; |
| |
| |
| /** |
| * The frequency of auto-refresh. Default is once per 5 seconds. |
| * @private {number} |
| * @const |
| */ |
| pagespeed.Graphs.FREQUENCY_ = 5; |
| |
| |
| /** |
| * The size limit to the data array. Default is one day. |
| * @private {number} |
| * @const |
| */ |
| pagespeed.Graphs.TIMERANGE_ = 24 * 60 * 60 / |
| pagespeed.Graphs.FREQUENCY_; |
| |
| |
| /** |
| * The Main entry to start processing. |
| * @export |
| */ |
| pagespeed.Graphs.Start = function() { |
| var graphsOnload = function() { |
| var graphsObj = new pagespeed.Graphs(); |
| graphsObj.parseLocation(); |
| for (var i in pagespeed.Graphs.DisplayDiv) { |
| goog.events.listen( |
| document.getElementById(pagespeed.Graphs.DisplayMode[i]), 'click', |
| goog.bind(graphsObj.show, graphsObj, pagespeed.Graphs.DisplayDiv[i])); |
| } |
| // IE6/7 don't support this event. Then the back button would not work |
| // because no requests captured. |
| goog.events.listen(window, 'hashchange', |
| goog.bind(graphsObj.parseLocation, graphsObj)); |
| |
| goog.events.listen(document.getElementById('auto-refresh'), 'change', |
| goog.bind(graphsObj.toggleAutorefresh, |
| graphsObj)); |
| // We call listen() here so this listener is added to the xhr only once. |
| // If we call listen() inside performRefresh() method, we are adding a new |
| // listener to the xhr every time it auto-refreshes, which would cause |
| // fetchContent() being called multiple times. Users will see an obvious |
| // delay because we draw the same charts multiple times in one refresh. |
| goog.events.listen( |
| graphsObj.xhr_, goog.net.EventType.COMPLETE, |
| goog.bind(graphsObj.parseAjaxResponse, graphsObj)); |
| setInterval(goog.bind(graphsObj.performRefresh, graphsObj), |
| pagespeed.Graphs.FREQUENCY_ * 1000); |
| graphsObj.performRefresh(); |
| }; |
| goog.events.listen(window, 'load', graphsOnload); |
| }; |