slice_track.js revision 88448d9ae4dfff1805045790ef5f32495d62abcc
12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Use of this source code is governed by a BSD-style license that can be
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// found in the LICENSE file.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis'use strict';
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
788448d9ae4dfff1805045790ef5f32495d62abccJeff Brownbase.requireStylesheet('tracks.slice_track');
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
988448d9ae4dfff1805045790ef5f32495d62abccJeff Brownbase.require('tracks.canvas_based_track');
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('sorted_array_utils');
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('fast_rect_renderer');
1288448d9ae4dfff1805045790ef5f32495d62abccJeff Brownbase.require('color_scheme');
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('ui');
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1588448d9ae4dfff1805045790ef5f32495d62abccJeff Brownbase.exportTo('tracing.tracks', function() {
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var palette = tracing.getColorPalette();
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
2088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown   * A track that displays an array of Slice objects.
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @extends {CanvasBasedTrack}
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown  var SliceTrack = tracing.ui.define(tracing.tracks.CanvasBasedTrack);
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown  SliceTrack.prototype = {
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown    __proto__: tracing.tracks.CanvasBasedTrack.prototype,
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Should we elide text on trace labels?
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Without eliding, text that is too wide isn't drawn at all.
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Disable if you feel this causes a performance problem.
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * This is a default value that can be overridden in tracks for testing.
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @const
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    SHOULD_ELIDE_TEXT: true,
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    decorate: function() {
4188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      this.classList.add('slice-track');
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.elidedTitleCache = new ElidedTitleCache();
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.asyncStyle_ = false;
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Called by all the addToSelection functions on the created selection
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * hit objects. Override this function on parent classes to add
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * context-specific information to the hit.
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    decorateHit: function(hit) {
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get asyncStyle() {
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.asyncStyle_;
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set asyncStyle(v) {
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.asyncStyle_ = !!v;
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.invalidate();
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get slices() {
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.slices_;
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set slices(slices) {
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.slices_ = slices || [];
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!slices)
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        this.visible = false;
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.invalidate();
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get height() {
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return window.getComputedStyle(this).height;
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set height(height) {
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.style.height = height;
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.invalidate();
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    labelWidth: function(title) {
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return quickMeasureText(this.ctx_, title) + 2;
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    labelWidthWorld: function(title, pixWidth) {
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.labelWidth(title) * pixWidth;
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    redraw: function() {
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ctx = this.ctx_;
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var canvasW = this.canvas_.width;
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var canvasH = this.canvas_.height;
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.clearRect(0, 0, canvasW, canvasH);
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Culling parameters.
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var vp = this.viewport_;
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixWidth = vp.xViewVectorToWorld(1);
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var viewLWorld = vp.xViewToWorld(0);
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var viewRWorld = vp.xViewToWorld(canvasW);
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Give the viewport a chance to draw onto this canvas.
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      vp.drawUnderContent(ctx, viewLWorld, viewRWorld, canvasH);
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Begin rendering in world space.
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.save();
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      vp.applyTransformToCanvas(ctx);
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Slices.
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (this.asyncStyle_)
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.globalAlpha = 0.25;
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tr = new tracing.FastRectRenderer(ctx, 2 * pixWidth, 2 * pixWidth,
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                            palette);
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      tr.setYandH(0, canvasH);
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var slices = this.slices_;
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var lowSlice = tracing.findLowIndexInSortedArray(slices,
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                       function(slice) {
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                         return slice.start +
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                                slice.duration;
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                       },
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                       viewLWorld);
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = lowSlice; i < slices.length; ++i) {
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var slice = slices[i];
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var x = slice.start;
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (x > viewRWorld) {
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          break;
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Less than 0.001 causes short events to disappear when zoomed in.
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var w = Math.max(slice.duration, 0.001);
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var colorId = slice.selected ?
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            slice.colorId + highlightIdBoost :
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            slice.colorId;
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (w < pixWidth)
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          w = pixWidth;
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (slice.duration > 0) {
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          tr.fillRect(x, w, colorId);
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        } else {
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Instant: draw a triangle.  If zoomed too far, collapse
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // into the FastRectRenderer.
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (pixWidth > 0.001) {
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            tr.fillRect(x, pixWidth, colorId);
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          } else {
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillStyle = palette[colorId];
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.beginPath();
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.moveTo(x - (4 * pixWidth), canvasH);
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.lineTo(x, 0);
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.lineTo(x + (4 * pixWidth), canvasH);
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.closePath();
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fill();
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      tr.flush();
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.restore();
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Labels.
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (canvasH > 8) {
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.textAlign = 'center';
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.textBaseline = 'top';
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.font = (10 * pixelRatio) + 'px sans-serif';
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.strokeStyle = 'rgb(0,0,0)';
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.fillStyle = 'rgb(0,0,0)';
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Don't render text until until it is 20px wide
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var quickDiscardThresshold = pixWidth * 20;
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var shouldElide = this.SHOULD_ELIDE_TEXT;
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (var i = lowSlice; i < slices.length; ++i) {
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var slice = slices[i];
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (slice.start > viewRWorld) {
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            break;
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (slice.duration > quickDiscardThresshold) {
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var title = slice.title;
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (slice.didNotFinish) {
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              title += ' (Did Not Finish)';
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            }
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var drawnTitle = title;
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var drawnWidth = this.labelWidth(drawnTitle);
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (shouldElide &&
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              var elidedValues = this.elidedTitleCache.get(
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  this, pixWidth,
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  drawnTitle, drawnWidth,
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  slice.duration);
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              drawnTitle = elidedValues.string;
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              drawnWidth = elidedValues.width;
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            }
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (drawnWidth * pixWidth < slice.duration) {
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              ctx.fillText(drawnTitle, cX, 2.5 * pixelRatio, drawnWidth);
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            }
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Give the viewport a chance to draw over this canvas.
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      vp.drawOverContent(ctx, viewLWorld, viewRWorld, canvasH);
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Finds slices intersecting the given interval.
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} vX X location to search at, in viewspace.
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} vY Y location to search at, in viewspace.
20788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {Selection} selection Selection to which to add hits.
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {boolean} true if a slice was found, otherwise false.
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addIntersectingItemsToSelection: function(vX, vY, selection) {
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var clientRect = this.getBoundingClientRect();
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (vY < clientRect.top || vY >= clientRect.bottom)
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var wX = this.viewport_.xViewVectorToWorld(vX * devicePixelRatio);
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var x = tracing.findLowIndexInSortedIntervals(this.slices_,
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.start; },
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.duration; },
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          wX);
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (x >= 0 && x < this.slices_.length) {
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var hit = selection.addSlice(this, this.slices_[x]);
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        this.decorateHit(hit);
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return true;
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return false;
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Adds items intersecting the given range to a selection.
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVX Lower X bound of the interval to search, in
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVX Upper X bound of the interval to search, in
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVY Lower Y bound of the interval to search, in
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVY Upper Y bound of the interval to search, in
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
23888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {Selection} selection Selection to which to add hits.
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addIntersectingItemsInRangeToSelection: function(
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        loVX, hiVX, loVY, hiVY, selection) {
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var loWX = this.viewport_.xViewToWorld(loVX * pixelRatio);
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var hiWX = this.viewport_.xViewToWorld(hiVX * pixelRatio);
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var clientRect = this.getBoundingClientRect();
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var a = Math.max(loVY, clientRect.top);
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var b = Math.min(hiVY, clientRect.bottom);
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (a > b)
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var that = this;
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      function onPickHit(slice) {
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var hit = selection.addSlice(that, slice);
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        that.decorateHit(hit);
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      tracing.iterateOverIntersectingIntervals(this.slices_,
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.start; },
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.duration; },
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          loWX, hiWX,
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          onPickHit);
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Find the index for the given slice.
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {index} Index of the given slice, or undefined.
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @private
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    indexOfSlice_: function(slice) {
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var index = tracing.findLowIndexInSortedArray(this.slices_,
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.start; },
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.start);
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      while (index < this.slices_.length &&
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.start == this.slices_[index].start &&
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.colorId != this.slices_[index].colorId) {
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        index++;
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return index < this.slices_.length ? index : undefined;
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Add the item to the left or right of the provided hit, if any, to the
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * selection.
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {slice} The current slice.
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {Number} offset Number of slices away from the hit to look.
28788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {Selection} selection The selection to add a hit to,
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * if found.
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {boolean} Whether a hit was found.
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @private
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addItemNearToProvidedHitToSelection: function(hit, offset, selection) {
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!hit.slice)
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var index = this.indexOfSlice_(hit.slice);
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (index === undefined)
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var newIndex = index + offset;
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (newIndex < 0 || newIndex >= this.slices_.length)
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var hit = selection.addSlice(this, this.slices_[newIndex]);
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.decorateHit(hit);
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return true;
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addAllObjectsMatchingFilterToSelection: function(filter, selection) {
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < this.slices_.length; ++i) {
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (filter.matchSlice(this.slices_[i])) {
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var hit = selection.addSlice(this, this.slices_[i]);
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          this.decorateHit(hit);
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost();
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // TODO(jrg): possibly obsoleted with the elided string cache.
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Consider removing.
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var textWidthMap = { };
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function quickMeasureText(ctx, text) {
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    var w = textWidthMap[text];
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (!w) {
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      w = ctx.measureText(text).width;
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      textWidthMap[text] = w;
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return w;
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * Cache for elided strings.
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * Moved from the ElidedTitleCache protoype to a "global" for speed
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * (variable reference is 100x faster).
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *   key: String we wish to elide.
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *   value: Another dict whose key is width
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *     and value is an ElidedStringWidthPair.
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var elidedTitleCacheDict = {};
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * A cache for elided strings.
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function ElidedTitleCache() {
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ElidedTitleCache.prototype = {
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Return elided text.
35388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {track} A slice track or other object that defines
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *                functions labelWidth() and labelWidthWorld().
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {pixWidth} Pixel width.
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {title} Original title text.
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {width} Drawn width in world coords.
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {sliceDuration} Where the title must fit (in world coords).
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {ElidedStringWidthPair} Elided string and width.
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get: function(track, pixWidth, title, width, sliceDuration) {
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var elidedDict = elidedTitleCacheDict[title];
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!elidedDict) {
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDict = {};
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedTitleCacheDict[title] = elidedDict;
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var elidedDictForPixWidth = elidedDict[pixWidth];
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!elidedDictForPixWidth) {
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDict[pixWidth] = {};
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDictForPixWidth = elidedDict[pixWidth];
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var stringWidthPair = elidedDictForPixWidth[sliceDuration];
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (stringWidthPair === undefined) {
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var newtitle = title;
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var elided = false;
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          newtitle = newtitle.substring(0, newtitle.length * 0.75);
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          elided = true;
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (elided && newtitle.length > 3)
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        stringWidthPair = new ElidedStringWidthPair(
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            newtitle,
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            track.labelWidth(newtitle));
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDictForPixWidth[sliceDuration] = stringWidthPair;
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return stringWidthPair;
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * A pair representing an elided string and world-coordinate width
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * to draw it.
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function ElidedStringWidthPair(string, width) {
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    this.string = string;
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    this.width = width;
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return {
40288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown    SliceTrack: SliceTrack
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis});
405