cros: Remove default pinned apps trial.
[chromium-blink-merge.git] / chrome / browser / resources / net_internals / cros_log_visualizer.js
blob73db4c367c47174bd9ca42c251769a45d14bd943
1 // Copyright 2013 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.
5 /**
6  * This visualizer displays the log in a timeline graph
7  *
8  *   - Use HTML5 canvas
9  *   - Can zoom in result by select time range
10  *   - Display different levels of logs in different layers of canvases
11  *
12  */
13 var CrosLogVisualizer = (function() {
14   'use strict';
16   // HTML attributes of canvas
17   var LOG_VISUALIZER_CANVAS_CLASS = 'cros-log-visualizer-visualizer-canvas';
18   var LOG_VISUALIZER_CANVAS_WIDTH = 980;
19   var LOG_VISUALIZER_CANVAS_HEIGHT = 100;
21   // Special HTML classes
22   var LOG_VISUALIZER_TIMELINE_ID = 'cros-log-visualizer-visualizer-timeline';
23   var LOG_VISUALIZER_TIME_DISPLAY_CLASS =
24       'cros-log-visualizer-visualizer-time-display';
25   var LOG_VISUALIZER_RESET_BTN_ID =
26       'cros-log-visualizer-visualizer-reset-btn';
27   var LOG_VISUALIZER_TRACKING_LAYER_ID =
28       'cros-log-visualizer-visualizer-tracking-layer';
30   /**
31    * Event level list
32    * This list is used for intialization of canvases. And the canvas
33    * with lowest priority should be created first. Hence the list is
34    * sorted in decreasing order.
35    */
36   var LOG_EVENT_LEVEL_PRIORITY_LIST = {
37     'Unknown': 4,
38     'Warning': 2,
39     'Info': 3,
40     'Error': 1
41   };
43   // Color mapping of different levels
44   var LOG_EVENT_COLORS_LIST = {
45     'Error': '#FF99A3',
46     'Warning': '#FAE5C3',
47     'Info': '#C3E3FA',
48     'Unknown': 'gray'
49   };
51   /**
52    * @constructor
53    */
54   function CrosLogVisualizer(logVisualizer, containerID) {
55     /**
56      * Pass the LogVisualizer in as a reference so the visualizer can
57      * synchrous with the log filter.
58      */
59     this.logVisualizer = logVisualizer;
61     // If the data is initialized
62     this.dataIntialized = false;
63     // Stores all the log entries as events
64     this.events = [];
65     // A front layer that handles control events
66     this.trackingLayer = this.createTrackingLayer();
68     // References to HTML elements
69     this.container = document.getElementById(containerID);
70     this.timeline = this.createTimeline();
71     this.timeDisplay = this.createTimeDisplay();
72     this.btnReset = this.createBtnReset();
73     // Canvases
74     this.canvases = {};
75     for (var level in LOG_EVENT_LEVEL_PRIORITY_LIST) {
76       this.canvases[level] = this.createCanvas();
77       this.container.appendChild(this.canvases[level]);
78     }
80     // Append all the elements to the container
81     this.container.appendChild(this.timeline);
82     this.container.appendChild(this.timeDisplay);
83     this.container.appendChild(this.trackingLayer);
84     this.container.appendChild(this.btnReset);
86     this.container.addEventListener('webkitAnimationEnd', function() {
87       this.container.classList.remove('cros-log-visualizer-flash');
88     }.bind(this), false);
89   }
91   CrosLogVisualizer.prototype = {
92     /**
93      * Called during the initialization of the View. Create a overlay
94      * DIV on top of the canvas that handles the mouse events
95      */
96     createTrackingLayer: function() {
97       var trackingLayer = document.createElement('div');
98       trackingLayer.setAttribute('id', LOG_VISUALIZER_TRACKING_LAYER_ID);
99       trackingLayer.addEventListener('mousemove', this.onHovered_.bind(this));
100       trackingLayer.addEventListener('mousedown', this.onMouseDown_.bind(this));
101       trackingLayer.addEventListener('mouseup', this.onMouseUp_.bind(this));
102       return trackingLayer;
103     },
105     /**
106      * This function is called during the initialization of the view.
107      * It creates the timeline that moves along with the mouse on canvas.
108      * When user click, a rectangle can be dragged out to select the range
109      * to zoom.
110      */
111     createTimeline: function() {
112       var timeline = document.createElement('div');
113       timeline.setAttribute('id', LOG_VISUALIZER_TIMELINE_ID);
114       timeline.style.height = LOG_VISUALIZER_CANVAS_HEIGHT + 'px';
115       timeline.addEventListener('mousedown', function(event) { return false; });
116       return timeline;
117     },
119     /**
120      * This function is called during the initialization of the view.
121      * It creates a time display that moves with the timeline
122      */
123     createTimeDisplay: function() {
124       var timeDisplay = document.createElement('p');
125       timeDisplay.className = LOG_VISUALIZER_TIME_DISPLAY_CLASS;
126       timeDisplay.style.top = LOG_VISUALIZER_CANVAS_HEIGHT + 'px';
127       return timeDisplay;
128     },
130     /**
131      * Called during the initialization of the View. Create a button that
132      * resets the canvas to initial status (without zoom)
133      */
134     createBtnReset: function() {
135       var btnReset = document.createElement('input');
136       btnReset.setAttribute('type', 'button');
137       btnReset.setAttribute('value', 'Reset');
138       btnReset.setAttribute('id', LOG_VISUALIZER_RESET_BTN_ID);
139       btnReset.addEventListener('click', this.reset.bind(this));
140       return btnReset;
141     },
143     /**
144      * Called during the initialization of the View. Create a empty canvas
145      * that visualizes log when the data is ready
146      */
147     createCanvas: function() {
148       var canvas = document.createElement('canvas');
149       canvas.width = LOG_VISUALIZER_CANVAS_WIDTH;
150       canvas.height = LOG_VISUALIZER_CANVAS_HEIGHT;
151       canvas.className = LOG_VISUALIZER_CANVAS_CLASS;
152       return canvas;
153     },
155     /**
156      * Returns the context of corresponding canvas based on level
157      */
158     getContext: function(level) {
159       return this.canvases[level].getContext('2d');
160     },
162     /**
163      * Erases everything from all the canvases
164      */
165     clearCanvas: function() {
166       for (var level in LOG_EVENT_LEVEL_PRIORITY_LIST) {
167         var ctx = this.getContext(level);
168         ctx.clearRect(0, 0, LOG_VISUALIZER_CANVAS_WIDTH,
169             LOG_VISUALIZER_CANVAS_HEIGHT);
170       }
171     },
173     /**
174      * Initializes the parameters needed for drawing:
175      *  - lower/upperBound: Time range (Events out of range will be skipped)
176      *  - totalDuration: The length of time range
177      *  - unitDuration: The unit time length per pixel
178      */
179     initialize: function() {
180       if (this.events.length == 0)
181         return;
182       this.dragMode = false;
183       this.dataIntialized = true;
184       this.events.sort(this.compareTime);
185       this.lowerBound = this.events[0].time;
186       this.upperBound = this.events[this.events.length - 1].time;
187       this.totalDuration = Math.abs(this.upperBound.getTime() -
188           this.lowerBound.getTime());
189       this.unitDuration = this.totalDuration / LOG_VISUALIZER_CANVAS_WIDTH;
190     },
192     /**
193      * CSS3 fadeIn/fadeOut effects
194      */
195     flashEffect: function() {
196       this.container.classList.add('cros-log-visualizer-flash');
197     },
199     /**
200      * Reset the canvas to the initial time range
201      * Redraw everything on the canvas
202      * Fade in/out effects while redrawing
203      */
204     reset: function() {
205       // Reset all the parameters as initial
206       this.initialize();
207       // Reset the visibility of the entries in the log table
208       this.logVisualizer.filterLog();
209       this.flashEffect();
210      },
212     /**
213      * A wrapper function for drawing
214      */
215     drawEvents: function() {
216       if (this.events.length == 0)
217         return;
218       for (var i in this.events) {
219         this.drawEvent(this.events[i]);
220       }
221     },
223     /**
224      * The main function that handles drawing on the canvas.
225      * Every event is represented as a vertical line.
226      */
227     drawEvent: function(event) {
228       if (!event.visibility) {
229         // Skip hidden events
230         return;
231       }
232       var ctx = this.getContext(event.level);
233       ctx.beginPath();
234       // Get the x-coordinate of the line
235       var startPosition = this.getPosition(event.time);
236       if (startPosition != this.old) {
237         this.old = startPosition;
238       }
239       ctx.rect(startPosition, 0, 2, LOG_VISUALIZER_CANVAS_HEIGHT);
240       // Get the color of the line
241       ctx.fillStyle = LOG_EVENT_COLORS_LIST[event.level];
242       ctx.fill();
243       ctx.closePath();
244     },
246     /**
247      * This function is called every time the graph is zoomed.
248      * It recalculates all the parameters based on the distance and direction
249      * of dragging.
250      */
251     reCalculate: function() {
252       if (this.dragDistance >= 0) {
253         // if user drags to right
254         this.upperBound = new Date((this.timelineLeft + this.dragDistance) *
255             this.unitDuration + this.lowerBound.getTime());
256         this.lowerBound = new Date(this.timelineLeft * this.unitDuration +
257             this.lowerBound.getTime());
258       } else {
259         // if user drags to left
260         this.upperBound = new Date(this.timelineLeft * this.unitDuration +
261             this.lowerBound.getTime());
262         this.lowerBound = new Date((this.timelineLeft + this.dragDistance) *
263             this.unitDuration + this.lowerBound.getTime());
264       }
265       this.totalDuration = this.upperBound.getTime() -
266           this.lowerBound.getTime();
267       this.unitDuration = this.totalDuration / LOG_VISUALIZER_CANVAS_WIDTH;
268     },
270     /**
271      * Check if the time of a event is out of bound
272      */
273     isOutOfBound: function(event) {
274       return event.time.getTime() < this.lowerBound.getTime() ||
275               event.time.getTime() > this.upperBound.getTime();
276     },
278     /**
279      * This function returns the offset on x-coordinate of canvas based on
280      * the time
281      */
282     getPosition: function(time) {
283       return (time.getTime() - this.lowerBound.getTime()) / this.unitDuration;
284     },
286     /**
287      * This function updates the events array and refresh the canvas.
288      */
289     updateEvents: function(newEvents) {
290       this.events.length = 0;
291       for (var i in newEvents) {
292         this.events.push(newEvents[i]);
293       }
294       if (!this.dataIntialized) {
295         this.initialize();
296       }
297       this.clearCanvas();
298       this.drawEvents();
299     },
301     /**
302      * This is a helper function that returns the time object based on the
303      * offset of x-coordinate on the canvs.
304      */
305     getOffsetTime: function(offset) {
306       return new Date(this.lowerBound.getTime() + offset * this.unitDuration);
307     },
309     /**
310      * This function is triggered when the hovering event is detected
311      * When the mouse is hovering we have two control mode:
312      *  - If it is in drag mode, we need to resize the width of the timeline
313      *  - If not, we need to move the timeline and time display to the
314      * x-coordinate position of the mouse
315      */
316     onHovered_: function(event) {
317       var offsetX = event.offsetX;
318       if (this.lastOffsetX == offsetX) {
319         // If the mouse does not move, we just skip the event
320         return;
321       }
323       if (this.dragMode == true) {
324         // If the mouse is in drag mode
325         this.dragDistance = offsetX - this.timelineLeft;
326         if (this.dragDistance >= 0) {
327           // If the mouse is moving right
328           this.timeline.style.width = this.dragDistance + 'px';
329         } else {
330           // If the mouse is moving left
331           this.timeline.style.width = -this.dragDistance + 'px';
332           this.timeline.style.left = offsetX + 'px';
333         }
334       } else {
335         // If the mouse is not in drag mode we just move the timeline
336         this.timeline.style.width = '2px';
337         this.timeline.style.left = offsetX + 'px';
338       }
340       // update time display
341       this.timeDisplay.style.left = offsetX + 'px';
342       this.timeDisplay.textContent =
343           this.getOffsetTime(offsetX).toTimeString().substr(0, 8);
344       // update the last offset
345       this.lastOffsetX = offsetX;
346     },
348     /**
349      * This function is the handler for the onMouseDown event on the canvas
350      */
351     onMouseDown_: function(event) {
352       // Enter drag mode which let user choose a time range to zoom in
353       this.dragMode = true;
354       this.timelineLeft = event.offsetX;
355       // Create a duration display to indicate the duration of range.
356       this.timeDurationDisplay = this.createTimeDisplay();
357       this.container.appendChild(this.timeDurationDisplay);
358     },
360     /**
361      * This function is the handler for the onMouseUp event on the canvas
362      */
363     onMouseUp_: function(event) {
364       // Remove the duration display
365       this.container.removeChild(this.timeDurationDisplay);
366       // End the drag mode
367       this.dragMode = false;
368       // Recalculate the pamameter based on the range user select
369       this.reCalculate();
370       // Filter the log table and hide the entries that are not in the range
371       this.logVisualizer.filterLog();
372     },
373   };
375   return CrosLogVisualizer;
376 })();