Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: chrome/test/functional/perf/endure_graphs/js/plotter.js

Issue 10832403: Add Chrome Endure graph plotting code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed first round of review comments. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/test/functional/perf/endure_graphs/js/coordinates.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 Use of this source code is governed by a BSD-style license that can be
4 found in the LICENSE file.
5 */
6
7 /**
8 * @fileoverview Collection of functions and classes used to plot data in a
9 * <canvas>. Create a Plotter() to generate a plot.
10 */
11
12 /**
13 * Adds commas to a given number.
14 *
15 * Examples:
16 * 1234.56 => "1,234.56"
17 * 99999 => "99,999"
18 *
19 * @param {string|number} number The number to format.
20 * @return {string} String representation of |number| with commas for every
21 * three digits to the left of a decimal point.
22 */
23 function addCommas(number) {
24 number += ''; // Convert number to string if not already a string.
25 var numberParts = number.split('.');
26 var integralPart = numberParts[0];
27 var fractionalPart = numberParts.length > 1 ? '.' + numberParts[1] : '';
28 var reThreeDigits = /(\d+)(\d{3})/;
29 while (reThreeDigits.test(integralPart))
30 integralPart = integralPart.replace(reThreeDigits, '$1' + ',' + '$2');
31 return integralPart + fractionalPart;
32 }
33
34 /**
35 * Vertical marker to highlight data points that are being hovered over by the
36 * mouse.
37 *
38 * @param {string} color The color to make the marker, e.g., 'rgb(100,80,240)'.
39 * @return {Element} A div Element object representing the vertical marker.
40 */
41 function VerticalMarker(color) {
42 var m = document.createElement('div');
43 m.style.backgroundColor = color;
44 m.style.opacity = '0.3';
45 m.style.position = 'absolute';
46 m.style.left = '-2px';
47 m.style.top = '-2px';
48 m.style.width = '0px';
49 m.style.height = '0px';
50 return m;
51 }
52
53 /**
54 * Class representing a horizontal marker at the indicated mouse location.
55 * @constructor
56 *
57 * @param {Object} canvasRect The canvas bounds (in client coords).
58 * @param {Number} yPixelClicked The vertical mouse click location that spawned
59 * the marker, in the client coordinate space.
60 * @param {Number} yValue The data value corresponding to the vertical click
61 * location.
62 * @param {Number} yOtherValue If the plot is overlaying two coordinate systems,
63 * this is the data value corresponding to the vertical click location in
64 * the second coordinate system. Can be null.
65 */
66 function HorizontalMarker(canvasRect, yPixelClicked, yValue, yOtherValue) {
67 var m = document.createElement('div');
68 m.style.backgroundColor = HorizontalMarker.COLOR;
69 m.style.opacity = '0.3';
70 m.style.position = 'absolute';
71 m.style.left = canvasRect.offsetLeft;
72 var h = HorizontalMarker.HEIGHT;
73 m.style.top = (yPixelClicked + document.body.scrollTop - (h / 2)).toFixed(0) +
74 'px';
75 m.style.width = canvasRect.offsetWidth + 'px';
76 m.style.height = h + 'px';
77
78 this.markerDiv = m;
79 this.value = yValue;
80 this.otherValue = yOtherValue;
81 }
82
83 HorizontalMarker.HEIGHT = 5;
84 HorizontalMarker.COLOR = 'rgb(0,100,100)';
85
86 /**
87 * Removes the horizontal marker from the graph.
88 */
89 HorizontalMarker.prototype.remove = function() {
90 this.markerDiv.parentNode.removeChild(this.markerDiv);
91 };
92
93 /**
94 * Main class that does the actual plotting.
95 *
96 * Draws a chart using a canvas element. Takes an array of lines to draw.
97 * @constructor
98 *
99 * @param {Array} plotData list of arrays that represent individual lines. The
100 * line itself is an Array of points.
101 * @param {Array} dataDescriptions list of data descriptions for each line in
102 * |plotData|.
103 * @param {string} eventName The string name of an event to overlay on the
104 * graph. Should be 'null' if there are no events to overlay.
105 * @param {Object} eventInfo If |eventName| is specified, an array of event
106 * points to overlay on the graph. Each event point in the array is itself
107 * a 2-element array, where the first element is the x-axis value at which
108 * the event occurred during the test, and the second element is a
109 * dictionary of kay/value pairs representing metadata associated with the
110 * event.
111 * @param {string} unitsX The x-axis units of the data being plotted.
112 * @param {string} unitsY The y-axis units of the data being plotted.
113 * @param {string} unitsYOther If another graph (with different y-axis units) is
114 * being overlayed over the first graph, this represents the units of the
115 * other graph. Otherwise, this should be 'null'.
116 * @param {string} resultNode A DOM Element object representing the DOM node to
117 * which the plot should be attached.
118 * @param {boolean} Whether or not the graph should be drawn in 'lookout' mode,
119 * which is a summarized view that is made for overview pages when the graph
120 * is drawn in a more confined space.
121 *
122 * Example of the |plotData|:
123 * [
124 * [line 1 data],
125 * [line 2 data]
126 * ].
127 * Line data looks like [[point one], [point two]].
128 * And individual points are [x value, y value]
129 */
130 function Plotter(plotData, dataDescriptions, eventName, eventInfo, unitsX,
131 unitsY, unitsYOther, resultNode, is_lookout) {
132 this.plotData_ = plotData;
133 this.dataDescriptions_ = dataDescriptions;
134 this.eventName_ = eventName;
135 this.eventInfo_ = eventInfo;
136 this.unitsX_ = unitsX;
137 this.unitsY_ = unitsY;
138 this.unitsYOther_ = unitsYOther;
139 this.resultNode_ = resultNode;
140 this.is_lookout_ = is_lookout;
141
142 this.dataColors_ = [];
143
144 this.coordinates = null;
145 this.coordinatesOther = null;
146 if (this.unitsYOther_) {
147 // Need two different coordinate systems to overlay on the same graph.
148 this.coordinates = new Coordinates([plotData[0]]);
149 this.coordinatesOther = new Coordinates([plotData[1]]);
150 } else {
151 this.coordinates = new Coordinates(plotData);
152 }
153
154 // A color palette that's unambigous for normal and color-deficient viewers.
155 // Values are (red, green, blue) on a scale of 255.
156 // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf.
157 this.colors = [[0, 114, 178], // Blue.
158 [230, 159, 0], // Orange.
159 [0, 158, 115], // Green.
160 [204, 121, 167], // Purplish pink.
161 [86, 180, 233], // Sky blue.
162 [213, 94, 0], // Dark orange.
163 [0, 0, 0], // Black.
164 [240, 228, 66] // Yellow.
165 ];
166
167 for (var i = 0, colorIndex = 0; i < this.dataDescriptions_.length; ++i)
168 this.dataColors_[i] = this.makeColor(colorIndex++);
169 }
170
171 /**
172 * Generates a string representing a color corresponding to the given index
173 * in a color array. Handles wrapping around the color array if necessary.
174 *
175 * @param {number} i An index into the |this.colors| array.
176 * @return {string} A string representing a color in 'rgb(X,Y,Z)' format.
177 */
178 Plotter.prototype.makeColor = function(i) {
179 var index = i % this.colors.length;
180 return 'rgb(' + this.colors[index][0] + ',' +
181 this.colors[index][1] + ',' +
182 this.colors[index][2] + ')';
183 };
184
185 /**
186 * Same as function makeColor above, but also takes a transparency value
187 * indicating how transparent to make the color appear.
188 *
189 * @param {number} i An index into the |this.colors| array.
190 * @param {number} transparencyPercent Percentage transparency to make the
191 * color, e.g., 0.75.
192 * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format,
193 * where A is the percentage transparency.
194 */
195 Plotter.prototype.makeColorTransparent = function(i, transparencyPercent) {
196 var index = i % this.colors.length;
197 return 'rgba(' + this.colors[index][0] + ',' +
198 this.colors[index][1] + ',' +
199 this.colors[index][2] + ',' + transparencyPercent + ')';
200 };
201
202 /**
203 * Gets the data color value associated with a specified color index.
204 *
205 * @param {number} i An index into the |this.colors| array.
206 * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format,
207 * where A is the percentage transparency.
208 */
209 Plotter.prototype.getDataColor = function(i) {
210 if (this.dataColors_[i])
211 return this.dataColors_[i];
212 else
213 return this.makeColor(i);
214 };
215
216 /**
217 * Does the actual plotting.
218 */
219 Plotter.prototype.plot = function() {
220 this.canvasElement_ = this.canvas_();
221 this.rulerDiv_ = this.ruler_();
222
223 // Markers for the result point(s)/events that the mouse is currently
224 // hovering over.
225 this.cursorDiv_ = new VerticalMarker('rgb(100,80,240)');
226 this.cursorDivOther_ = new VerticalMarker('rgb(50,50,50)');
227 this.eventDiv_ = new VerticalMarker('rgb(255, 0, 0)');
228
229 this.resultNode_.appendChild(this.canvasElement_);
230 this.resultNode_.appendChild(this.coordinates_());
231 this.resultNode_.appendChild(this.rulerDiv_);
232 this.resultNode_.appendChild(this.cursorDiv_);
233 this.resultNode_.appendChild(this.cursorDivOther_);
234 this.resultNode_.appendChild(this.eventDiv_);
235 this.attachEventListeners_();
236
237 // Now draw the canvas.
238 var ctx = this.canvasElement_.getContext('2d');
239
240 // Clear it with white: otherwise canvas will draw on top of existing data.
241 ctx.clearRect(0, 0, this.canvasElement_.width, this.canvasElement_.height);
242
243 // Draw all data lines.
244 for (var i = 0; i < this.plotData_.length; ++i) {
245 var coordinateSystem = this.coordinates;
246 if (i > 0 && this.unitsYOther_)
247 coordinateSystem = this.coordinatesOther;
248 this.plotLine_(ctx, this.getDataColor(i), this.plotData_[i],
249 coordinateSystem);
250 }
251
252 // Draw events overlayed on graph if needed.
253 if (this.eventName_ && this.eventInfo_)
254 this.plotEvents_(ctx, 'rgb(255, 150, 150)', this.coordinates);
255
256 this.graduation_divs_ = this.graduations_(this.coordinates, 0, false);
257 if (this.unitsYOther_) {
258 this.graduation_divs_ = this.graduation_divs_.concat(
259 this.graduations_(this.coordinatesOther, 1, true));
260 }
261 for (var i = 0; i < this.graduation_divs_.length; ++i)
262 this.resultNode_.appendChild(this.graduation_divs_[i]);
263 };
264
265 /**
266 * Draws events overlayed on top of an existing graph.
267 *
268 * @param {Object} ctx A canvas element object for drawing.
269 * @param {string} strokeStyles A string representing the drawing style.
270 * @param {Object} coordinateSystem A Coordinates object representing the
271 * coordinate system of the graph.
272 */
273 Plotter.prototype.plotEvents_ = function(ctx, strokeStyles, coordinateSystem) {
274 ctx.strokeStyle = strokeStyles;
275 ctx.fillStyle = strokeStyles;
276 ctx.lineWidth = 1.0;
277
278 ctx.beginPath();
279 var data = this.eventInfo_;
280 for (var index = 0; index < data.length; ++index) {
281 var event_time = data[index][0];
282 var x = coordinateSystem.xPixel(event_time);
283 ctx.moveTo(x, 0);
284 ctx.lineTo(x, this.canvasElement_.offsetHeight);
285 }
286 ctx.closePath();
287 ctx.stroke();
288 };
289
290 /**
291 * Draws a line on the graph.
292 *
293 * @param {Object} ctx A canvas element object for drawing.
294 * @param {string} strokeStyles A string representing the drawing style.
295 * @param {Array} data A list of [x, y] values representing the line to plot.
296 * @param {Object} coordinateSystem A Coordinates object representing the
297 * coordinate system of the graph.
298 */
299 Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data,
300 coordinateSystem) {
301 ctx.strokeStyle = strokeStyles;
302 ctx.fillStyle = strokeStyles;
303 ctx.lineWidth = 2.0;
304
305 ctx.beginPath();
306 var initial = true;
307 var allPoints = [];
308 for (var i = 0; i < data.length; ++i) {
309 var pointX = parseFloat(data[i][0]);
310 var pointY = parseFloat(data[i][1]);
311 var x = coordinateSystem.xPixel(pointX);
312 var y = 0.0;
313 if (isNaN(pointY)) {
314 // Re-set 'initial' if we're at a gap in the data.
315 initial = true;
316 } else {
317 y = coordinateSystem.yPixel(pointY);
318 if (initial)
319 initial = false;
320 else
321 ctx.lineTo(x, y);
322 }
323
324 ctx.moveTo(x, y);
325 allPoints.push([x, y]);
326 }
327 ctx.closePath();
328 ctx.stroke();
329
330 if (!this.is_lookout_) {
331 // Draw a small dot at each point.
332 for (var i = 0; i < allPoints.length; ++i) {
333 ctx.beginPath();
334 ctx.arc(allPoints[i][0], allPoints[i][1], 3, 0, Math.PI*2, true);
335 ctx.fill();
336 }
337 }
338 };
339
340 /**
341 * Attaches event listeners to DOM nodes.
342 */
343 Plotter.prototype.attachEventListeners_ = function() {
344 var self = this;
345 this.canvasElement_.parentNode.addEventListener(
346 'mousemove', function(evt) { self.onMouseMove_(evt); }, false);
347 this.cursorDiv_.addEventListener(
348 'click', function(evt) { self.onMouseClick_(evt); }, false);
349 this.cursorDivOther_.addEventListener(
350 'click', function(evt) { self.onMouseClick_(evt); }, false);
351 this.eventDiv_.addEventListener(
352 'click', function(evt) { self.onMouseClick_(evt); }, false);
353 };
354
355 /**
356 * Update the horizontal line that is following where the mouse is hovering.
357 *
358 * @param {Object} evt A mouse event object representing a mouse move event.
359 */
360 Plotter.prototype.updateRuler_ = function(evt) {
361 var r = this.rulerDiv_;
362 r.style.left = this.canvasElement_.offsetLeft + 'px';
363 r.style.top = this.canvasElement_.offsetTop + 'px';
364 r.style.width = this.canvasElement_.offsetWidth + 'px';
365 var h = evt.clientY + document.body.scrollTop - this.canvasElement_.offsetTop;
366 if (h > this.canvasElement_.offsetHeight)
367 h = this.canvasElement_.offsetHeight;
368 r.style.height = h + 'px';
369 };
370
371 /**
372 * Update the highlighted data point at the x value that the mouse is hovering
373 * over.
374 *
375 * @param {Object} coordinateSystem A Coordinates object representing the
376 * coordinate system of the graph.
377 * @param {number} currentIndex The index into the |this.plotData| array of the
378 * data point being hovered over, for a given line.
379 * @param {Object} cursorDiv A DOM element div object representing the highlight
380 * itself.
381 * @param {number} dataIndex The index into the |this.plotData| array of the
382 * line being hovered over.
383 */
384 Plotter.prototype.updateCursor_ = function(coordinateSystem, currentIndex,
385 cursorDiv, dataIndex) {
386 var c = cursorDiv;
387 c.style.top = this.canvasElement_.offsetTop + 'px';
388 c.style.height = this.canvasElement_.offsetHeight + 'px';
389
390 // Left point is half-way to the previous x value, unless it's the first
391 // point, in which case it's the x value of the current point.
392 var leftPoint = null;
393 if (currentIndex == 0) {
394 leftPoint = this.canvasElement_.offsetLeft +
395 coordinateSystem.xPixel(this.plotData_[dataIndex][0][0]);
396 }
397 else {
398 var left_x = this.canvasElement_.offsetLeft +
399 coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex - 1][0]);
400 var curr_x = this.canvasElement_.offsetLeft +
401 coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
402 leftPoint = (left_x + curr_x) / 2;
403 }
404 c.style.left = leftPoint;
405
406 // Width is half-way to the next x value minus the left point, unless it's
407 // the last point, in which case it's the x value of the current point minus
408 // the left point.
409 if (currentIndex == this.plotData_[dataIndex].length - 1) {
410 var curr_x = this.canvasElement_.offsetLeft +
411 coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
412 c.style.width = curr_x - left_point;
413 }
414 else {
415 var next_x = this.canvasElement_.offsetLeft +
416 coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex + 1][0]);
417 var curr_x = this.canvasElement_.offsetLeft +
418 coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
419 c.style.width = ((next_x + curr_x) / 2) - leftPoint;
420 }
421 };
422
423 /**
424 * Update the highlighted event at the x value that the mouse is hovering over.
425 *
426 * @param {number} x The x-value (pixel) at which to draw the event highlight
427 * div.
428 * @param {boolean} show Whether or not to show the highlight div.
429 */
430 Plotter.prototype.updateEventDiv_ = function(x, show) {
431 var c = this.eventDiv_;
432 c.style.top = this.canvasElement_.offsetTop + 'px';
433 c.style.height = this.canvasElement_.offsetHeight + 'px';
434
435 if (show) {
436 c.style.left = this.canvasElement_.offsetLeft + (x - 2);
437 c.style.width = 8;
438 } else {
439 c.style.width = 0;
440 }
441 };
442
443 /**
444 * Handle a mouse move event.
445 *
446 * @param {Object} evt A mouse event object representing a mouse move event.
447 */
448 Plotter.prototype.onMouseMove_ = function(evt) {
449 var canvas = evt.currentTarget.firstChild;
450 var positionX = evt.clientX + document.body.scrollLeft -
451 this.canvasElement_.offsetLeft;
452 var positionY = evt.clientY + document.body.scrollTop -
453 this.canvasElement_.offsetTop;
454
455 // Identify the index of the x value that is closest to the mouse x value.
456 var xValue = this.coordinates.xValue(positionX);
457 var min_diff = Math.abs(this.plotData_[0][0][0] - xValue);
458 indexValueX = 0;
459 for (var i = 1; i < this.plotData_[0].length; ++i) {
460 var diff = Math.abs(this.plotData_[0][i][0] - xValue);
461 if (diff < min_diff) {
462 min_diff = diff;
463 indexValueX = i;
464 }
465 }
466
467 // Identify the index of the x value closest to the mouse x value for the
468 // other graph being overlayed on top of the original graph, if one exists.
469 if (this.unitsYOther_) {
470 var xValue = this.coordinatesOther.xValue(positionX);
471 var min_diff = Math.abs(this.plotData_[1][0][0] - xValue);
472 var indexValueXOther = 0;
473 for (var i = 1; i < this.plotData_[1].length; ++i) {
474 var diff = Math.abs(this.plotData_[1][i][0] - xValue);
475 if (diff < min_diff) {
476 min_diff = diff;
477 indexValueXOther = i;
478 }
479 }
480 }
481
482 // Update coordinate information displayed directly underneath the graph.
483 var yValue = this.coordinates.yValue(positionY);
484
485 this.coordinatesTd_.innerHTML =
486 '<font style="color:' + this.dataColors_[0] + '">' +
487 this.plotData_[0][indexValueX][0] + ' ' + this.unitsX_ + ': ' +
488 addCommas(this.plotData_[0][indexValueX][1].toFixed(2)) + ' ' +
489 this.unitsY_ + '</font> [hovering at ' + addCommas(yValue.toFixed(2)) +
490 ' ' + this.unitsY_ + ']';
491
492 if (this.unitsYOther_) {
493 var yValue2 = this.coordinatesOther.yValue(positionY);
494 this.coordinatesTdOther_.innerHTML =
495 '<font style="color:' + this.dataColors_[1] + '">' +
496 this.plotData_[1][indexValueXOther][0] + ' ' + this.unitsX_ + ': ' +
497 addCommas(this.plotData_[1][indexValueXOther][1].toFixed(2)) + ' ' +
498 (this.unitsYOther_ ? this.unitsYOther_ : this.unitsY_) +
499 '</font> [hovering at ' + addCommas(yValue2.toFixed(2)) + ' ' +
500 this.unitsYOther_ + ']';
501 }
502 else if (this.dataDescriptions_.length > 1) {
503 this.coordinatesTdOther_.innerHTML =
504 '<font style="color:' + this.dataColors_[1] + '">' +
505 this.plotData_[1][indexValueX][0] + ' ' + this.unitsX_ + ': ' +
506 addCommas(this.plotData_[1][indexValueX][1].toFixed(2)) + ' ' +
507 (this.unitsYOther_ ? this.unitsYOther_ : this.unitsY_) + '</font>';
508 }
509
510 // If there is a horizontal marker, also display deltas relative to it.
511 if (this.horizontal_marker_) {
512 var baseline = this.horizontal_marker_.value;
513 var delta = yValue - baseline;
514 var fraction = delta / baseline; // Allow division by 0.
515
516 var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' +
517 this.unitsY_;
518 var percentStr = (fraction >= 0 ? '+' : '') + (fraction * 100).toFixed(3) +
519 '%';
520
521 this.baselineDeltasTd_.innerHTML = deltaStr + ': ' + percentStr;
522
523 if (this.unitsYOther_) {
524 var baseline = this.horizontal_marker_.otherValue;
525 var yValue2 = this.coordinatesOther.yValue(positionY);
526 var delta = yValue2 - baseline;
527 var fraction = delta / baseline; // Allow division by 0.
528
529 var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' +
530 this.unitsYOther_;
531 var percentStr = (fraction >= 0 ? '+' : '') +
532 (fraction * 100).toFixed(3) + '%';
533 this.baselineDeltasTd_.innerHTML += '<br>' + deltaStr + ': ' + percentStr;
534 }
535 }
536
537 this.updateRuler_(evt);
538 this.updateCursor_(this.coordinates, indexValueX, this.cursorDiv_, 0);
539 if (this.unitsYOther_) {
540 this.updateCursor_(this.coordinatesOther, indexValueXOther,
541 this.cursorDivOther_, 1);
542 }
543
544 // If there are events displayed, see if we're hovering close to an existing
545 // event on the graph, and if so, display the metadata associated with it.
546 if (this.eventName_ != null && this.eventInfo_ != null) {
547 var data = this.eventInfo_;
548 var showed_event = false;
549 var x = 0;
550 for (var index = 0; index < data.length; ++index) {
551 var event_time = data[index][0];
552 x = this.coordinates.xPixel(event_time);
553 if (positionX >= x - 10 && positionX <= x + 10) {
554 var metadata = data[index][1];
555 var metadata_str = "";
556 for (var meta_key in metadata)
557 metadata_str += meta_key + ': ' + metadata[meta_key] + ', ';
558 metadata_str = metadata_str.substring(0, metadata_str.length - 2);
559 this.coordinatesTdOther_.innerHTML = event_time + ' ' + this.unitsX_ +
560 ': {' + metadata_str + '}';
561 showed_event = true;
562 this.updateEventDiv_(x, true);
563 break;
564 }
565 }
566 if (!showed_event) {
567 this.coordinatesTdOther_.innerHTML =
568 'move mouse close to vertical event marker';
569 this.updateEventDiv_(x, false);
570 }
571 }
572 };
573
574 /**
575 * Handle a mouse click event.
576 *
577 * @param {Object} evt A mouse event object representing a mouse click event.
578 */
579 Plotter.prototype.onMouseClick_ = function(evt) {
580 // Shift-click controls the horizontal reference line.
581 if (evt.shiftKey) {
582 if (this.horizontal_marker_)
583 this.horizontal_marker_.remove();
584
585 var canvasY = evt.clientY - this.canvasElement_.offsetTop;
586 this.horizontal_marker_ = new HorizontalMarker(
587 this.canvasElement_, evt.clientY, this.coordinates.yValue(canvasY),
588 (this.coordinatesOther ? this.coordinatesOther.yValue(canvasY) : null));
589 // Insert before cursor node, otherwise it catches clicks.
590 this.cursorDiv_.parentNode.insertBefore(
591 this.horizontal_marker_.markerDiv, this.cursorDiv_);
592 }
593 };
594
595 /**
596 * Generates and returns a list of div objects representing horizontal lines in
597 * the graph that indicate y-axis values at a computed interval.
598 *
599 * @param {Object} coordinateSystem a Coordinates object representing the
600 * coordinate system for which the graduations should be created.
601 * @param {number} colorIndex An index into the |this.colors| array representing
602 * the color to make the graduations in the event that two graphs with
603 * different coordinate systems are being overlayed on the same plot.
604 * @param {boolean} isRightSide Whether or not the graduations should have
605 * right-aligned text (used when the graduations are for a second graph
606 * that is being overlayed on top of another graph).
607 * @return {Array} An array of DOM Element objects representing the divs.
608 */
609 Plotter.prototype.graduations_ = function(coordinateSystem, colorIndex,
610 isRightSide) {
611 // Don't allow a graduation in the bottom 5% of the chart or the number label
612 // would overflow the chart bounds.
613 var yMin = coordinateSystem.yMinValue + .05 * coordinateSystem.yValueRange();
614 var yRange = coordinateSystem.yMaxValue - yMin;
615
616 // Use the largest scale that fits 3 or more graduations.
617 // We allow scales of [...,500, 250, 100, 50, 25, 10,...].
618 var scale = 5000000000;
619 while (scale) {
620 if (Math.floor(yRange / scale) > 2) break; // 5s.
621 scale /= 2;
622 if (Math.floor(yRange / scale) > 2) break; // 2.5s.
623 scale /= 2.5;
624 if (Math.floor(yRange / scale) > 2) break; // 1s.
625 scale /= 2;
626 }
627
628 var graduationPosition = yMin + (scale - yMin % scale);
629 var graduationDivs = [];
630 while (graduationPosition < coordinateSystem.yMaxValue || yRange == 0) {
631 var graduation = document.createElement('div');
632 var canvasPosition;
633 if (yRange == 0) {
634 // Center the graduation vertically.
635 canvasPosition = this.canvasElement_.offsetHeight / 2;
636 } else {
637 canvasPosition = coordinateSystem.yPixel(graduationPosition);
638 }
639 if (this.unitsYOther_) {
640 graduation.style.borderTop = '1px dashed ' +
641 this.makeColorTransparent(colorIndex, 0.4)
642 } else {
643 graduation.style.borderTop = '1px dashed rgba(0,0,0,.08)';
644 }
645 graduation.style.position = 'absolute';
646 graduation.style.left = this.canvasElement_.offsetLeft + 'px';
647 graduation.style.top = canvasPosition + this.canvasElement_.offsetTop +
648 'px';
649 graduation.style.width = this.canvasElement_.offsetWidth -
650 this.canvasElement_.offsetLeft + 'px';
651 graduation.style.paddingLeft = '4px';
652 if (this.unitsYOther_)
653 graduation.style.color = this.makeColorTransparent(colorIndex, 0.9)
654 else
655 graduation.style.color = 'rgba(0,0,0,.4)';
656 graduation.style.fontSize = '9px';
657 graduation.style.paddingTop = '0';
658 graduation.style.zIndex = '-1';
659 if (isRightSide)
660 graduation.style.textAlign = 'right';
661 if (yRange == 0)
662 graduation.innerHTML = addCommas(yMin);
663 else
664 graduation.innerHTML = addCommas(graduationPosition);
665 graduationDivs.push(graduation);
666 if (yRange == 0)
667 break;
668 graduationPosition += scale;
669 }
670 return graduationDivs;
671 };
672
673 /**
674 * Generates and returns a div object representing the horizontal line that
675 * follows the mouse pointer around the plot.
676 *
677 * @return {Object} A DOM Element object representing the div.
678 */
679 Plotter.prototype.ruler_ = function() {
680 var ruler = document.createElement('div');
681 ruler.setAttribute('class', 'plot-ruler');
682 ruler.style.borderBottom = '1px dotted black';
683 ruler.style.position = 'absolute';
684 ruler.style.left = '-2px';
685 ruler.style.top = '-2px';
686 ruler.style.width = '0px';
687 ruler.style.height = '0px';
688 return ruler;
689 };
690
691 /**
692 * Generates and returns a canvas object representing the plot itself.
693 *
694 * @return {Object} A DOM Element object representing the canvas.
695 */
696 Plotter.prototype.canvas_ = function() {
697 var canvas = document.createElement('canvas');
698 canvas.setAttribute('id', '_canvas');
699 canvas.setAttribute('class', 'plot');
700 canvas.setAttribute('width', this.coordinates.widthMax);
701 canvas.setAttribute('height', this.coordinates.heightMax);
702 canvas.plotter = this;
703 return canvas;
704 };
705
706 /**
707 * Generates and returns a div object representing the coordinate information
708 * displayed directly underneath a graph.
709 *
710 * @return {Object} A DOM Element object representing the div.
711 */
712 Plotter.prototype.coordinates_ = function() {
713 var coordinatesDiv = document.createElement('div');
714 var table_html = '<table border=0 width="100%"';
715 if (this.is_lookout_) {
716 table_html += ' style="font-size:0.8em"';
717 }
718 table_html += '><tbody><tr>'
719
720 table_html += '<td><span class="legend_item" style="color:' +
721 this.getDataColor(0) + '">' + this.dataDescriptions_[0] +
722 '</span>:&nbsp;&nbsp;<span class="plot-coordinates">' +
723 '<i>move mouse over graph</i></span></td>';
724
725 table_html += '<td align="right">x-axis is ' + this.unitsX_ + '</td>';
726
727 table_html += '</tr><tr>'
728
729 table_html += '<td>';
730 if (this.dataDescriptions_.length > 1) {
731 // A second line will be drawn, so add information about it.
732 table_html += '<span class="legend_item" style="color:' +
733 this.getDataColor(1) + '">' + this.dataDescriptions_[1] +
734 '</span>:&nbsp;&nbsp;<span class="plot-coordinates">' +
735 '<i>move mouse over graph</i></span>';
736 } else if (this.eventName_ != null) {
737 // Event information will be overlayed on the graph, so add info about it.
738 table_html += '<span class="legend_item" style="color:' +
739 this.getDataColor(1) + '">Event "' + this.eventName_ + '":</span>' +
740 '&nbsp;&nbsp;<span class="plot-coordinates">' +
741 '<i>move mouse over graph</i></span>';
742 }
743 table_html += '</td>';
744
745 if (!this.is_lookout_) {
746 table_html += '<td align="right" style="color: ' + HorizontalMarker.COLOR +
747 '"><i>Shift-click to place baseline.</i></td>';
748 }
749 table_html += '</tr></tbody></table>';
750 coordinatesDiv.innerHTML = table_html;
751
752 var tr = coordinatesDiv.firstChild.firstChild.childNodes[0];
753 this.coordinatesTd_ = tr.childNodes[0].childNodes[2];
754 tr = coordinatesDiv.firstChild.firstChild.childNodes[1];
755 if (this.dataDescriptions_.length > 1) {
756 // For second graph line.
757 this.coordinatesTdOther_ = tr.childNodes[0].childNodes[2];
758 } else if (this.eventName_ != null) {
759 // For event metadata.
760 this.coordinatesTdOther_ = tr.childNodes[0].childNodes[2];
761 }
762 this.baselineDeltasTd_ = tr.childNodes[1];
763
764 return coordinatesDiv;
765 };
OLDNEW
« no previous file with comments | « chrome/test/functional/perf/endure_graphs/js/coordinates.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698