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
766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.requireStylesheet('tracing.tracks.slice_track');
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('base.sorted_array_utils');
106833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennisbase.require('tracing.tracks.heading_track');
1166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('tracing.fast_rect_renderer');
1266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('tracing.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
226833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis   * @extends {HeadingTrack}
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis  var SliceTrack = ui.define(
266833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      'slice-track', tracing.tracks.HeadingTrack);
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown  SliceTrack.prototype = {
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
306833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    __proto__: tracing.tracks.HeadingTrack.prototype,
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Should we elide text on trace labels?
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Without eliding, text that is too wide isn't drawn at all.
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Disable if you feel this causes a performance problem.
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * This is a default value that can be overridden in tracks for testing.
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @const
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    SHOULD_ELIDE_TEXT: true,
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    decorate: function(viewport) {
426833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
4388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      this.classList.add('slice-track');
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.elidedTitleCache = new ElidedTitleCache();
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.asyncStyle_ = false;
466833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      this.slices_ = null;
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get asyncStyle() {
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.asyncStyle_;
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set asyncStyle(v) {
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.asyncStyle_ = !!v;
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get slices() {
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.slices_;
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set slices(slices) {
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.slices_ = slices || [];
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get height() {
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return window.getComputedStyle(this).height;
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    set height(height) {
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.style.height = height;
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    get hasVisibleContent() {
7466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.slices.length > 0;
7566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
7666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    labelWidth: function(title) {
7866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return quickMeasureText(this.context(), title) + 2;
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    labelWidthWorld: function(title, pixWidth) {
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return this.labelWidth(title) * pixWidth;
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
856833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    draw: function(type, viewLWorld, viewRWorld) {
866833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      switch (type) {
876833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        case tracing.tracks.DrawType.SLICE:
886833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          this.drawSlices_(viewLWorld, viewRWorld);
896833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          break;
906833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      }
916833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    },
926833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
936833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    drawSlices_: function(viewLWorld, viewRWorld) {
9466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var ctx = this.context();
9566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var bounds = this.getBoundingClientRect();
9866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var height = bounds.height * pixelRatio;
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Culling parameters.
10166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var vp = this.viewport;
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixWidth = vp.xViewVectorToWorld(1);
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Begin rendering in world space.
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.save();
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      vp.applyTransformToCanvas(ctx);
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Slices.
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (this.asyncStyle_)
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.globalAlpha = 0.25;
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tr = new tracing.FastRectRenderer(ctx, 2 * pixWidth, 2 * pixWidth,
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                            palette);
11366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      tr.setYandH(0, height);
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var slices = this.slices_;
11566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var lowSlice = base.findLowIndexInSortedArray(
11666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          slices,
11766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          function(slice) { return slice.start + slice.duration; },
11866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          viewLWorld);
11966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = lowSlice; i < slices.length; ++i) {
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var slice = slices[i];
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var x = slice.start;
1236833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        if (x > viewRWorld)
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          break;
1256833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Less than 0.001 causes short events to disappear when zoomed in.
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var w = Math.max(slice.duration, 0.001);
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var colorId = slice.selected ?
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            slice.colorId + highlightIdBoost :
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            slice.colorId;
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (w < pixWidth)
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          w = pixWidth;
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (slice.duration > 0) {
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          tr.fillRect(x, w, colorId);
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        } else {
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Instant: draw a triangle.  If zoomed too far, collapse
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // into the FastRectRenderer.
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (pixWidth > 0.001) {
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            tr.fillRect(x, pixWidth, colorId);
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          } else {
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillStyle = palette[colorId];
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.beginPath();
14466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            ctx.moveTo(x - (4 * pixWidth), height);
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.lineTo(x, 0);
14666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            ctx.lineTo(x + (4 * pixWidth), height);
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.closePath();
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fill();
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      tr.flush();
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.restore();
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Labels.
15666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (height > 8) {
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.textAlign = 'center';
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.textBaseline = 'top';
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.font = (10 * pixelRatio) + 'px sans-serif';
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.strokeStyle = 'rgb(0,0,0)';
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.fillStyle = 'rgb(0,0,0)';
16266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Don't render text until until it is 20px wide
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var quickDiscardThresshold = pixWidth * 20;
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var shouldElide = this.SHOULD_ELIDE_TEXT;
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (var i = lowSlice; i < slices.length; ++i) {
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var slice = slices[i];
16866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          if (slice.start > viewRWorld)
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            break;
17066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
17166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          if (slice.duration <= quickDiscardThresshold)
17266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            continue;
17366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
17466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          var title = slice.title +
17566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis              (slice.didNotFinish ? ' (Did Not Finish)' : '');
17666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
17766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          var drawnTitle = title;
17866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          var drawnWidth = this.labelWidth(drawnTitle);
17966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          if (shouldElide &&
18066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis              this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
18166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            var elidedValues = this.elidedTitleCache.get(
18266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis                this, pixWidth,
18366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis                drawnTitle, drawnWidth,
18466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis                slice.duration);
18566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            drawnTitle = elidedValues.string;
18666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            drawnWidth = elidedValues.width;
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
18966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          if (drawnWidth * pixWidth < slice.duration) {
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
19166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
19266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            ctx.fillText(drawnTitle, cX, 2.5 * pixelRatio, drawnWidth);
19366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          }
19466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        }
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
19866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    addIntersectingItemsInRangeToSelectionInWorldSpace: function(
19966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        loWX, hiWX, viewPixWidthWorld, selection) {
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      function onPickHit(slice) {
2016833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        var hit = selection.addSlice(this, slice);
2026833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        this.decorateHit(hit);
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
20466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      base.iterateOverIntersectingIntervals(this.slices_,
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.start; },
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.duration; },
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          loWX, hiWX,
2086833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          onPickHit.bind(this));
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Find the index for the given slice.
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {index} Index of the given slice, or undefined.
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @private
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    indexOfSlice_: function(slice) {
21766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var index = base.findLowIndexInSortedArray(this.slices_,
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function(x) { return x.start; },
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.start);
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      while (index < this.slices_.length &&
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.start == this.slices_[index].start &&
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          slice.colorId != this.slices_[index].colorId) {
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        index++;
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return index < this.slices_.length ? index : undefined;
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Add the item to the left or right of the provided hit, if any, to the
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * selection.
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {slice} The current slice.
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {Number} offset Number of slices away from the hit to look.
23388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {Selection} selection The selection to add a hit to,
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * if found.
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {boolean} Whether a hit was found.
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @private
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addItemNearToProvidedHitToSelection: function(hit, offset, selection) {
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!hit.slice)
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var index = this.indexOfSlice_(hit.slice);
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (index === undefined)
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var newIndex = index + offset;
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (newIndex < 0 || newIndex >= this.slices_.length)
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return false;
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var hit = selection.addSlice(this, this.slices_[newIndex]);
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.decorateHit(hit);
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return true;
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addAllObjectsMatchingFilterToSelection: function(filter, selection) {
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < this.slices_.length; ++i) {
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (filter.matchSlice(this.slices_[i])) {
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var hit = selection.addSlice(this, this.slices_[i]);
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          this.decorateHit(hit);
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost();
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // TODO(jrg): possibly obsoleted with the elided string cache.
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Consider removing.
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var textWidthMap = { };
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function quickMeasureText(ctx, text) {
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    var w = textWidthMap[text];
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (!w) {
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      w = ctx.measureText(text).width;
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      textWidthMap[text] = w;
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return w;
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * Cache for elided strings.
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * Moved from the ElidedTitleCache protoype to a "global" for speed
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * (variable reference is 100x faster).
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *   key: String we wish to elide.
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *   value: Another dict whose key is width
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   *     and value is an ElidedStringWidthPair.
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var elidedTitleCacheDict = {};
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * A cache for elided strings.
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function ElidedTitleCache() {
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ElidedTitleCache.prototype = {
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Return elided text.
29988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {track} A slice track or other object that defines
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *                functions labelWidth() and labelWidthWorld().
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {pixWidth} Pixel width.
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {title} Original title text.
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {width} Drawn width in world coords.
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {sliceDuration} Where the title must fit (in world coords).
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {ElidedStringWidthPair} Elided string and width.
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    get: function(track, pixWidth, title, width, sliceDuration) {
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var elidedDict = elidedTitleCacheDict[title];
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!elidedDict) {
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDict = {};
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedTitleCacheDict[title] = elidedDict;
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var elidedDictForPixWidth = elidedDict[pixWidth];
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!elidedDictForPixWidth) {
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDict[pixWidth] = {};
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDictForPixWidth = elidedDict[pixWidth];
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var stringWidthPair = elidedDictForPixWidth[sliceDuration];
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (stringWidthPair === undefined) {
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var newtitle = title;
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var elided = false;
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
32366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          if (newtitle.length * 0.75 < 1)
32466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis            break;
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          newtitle = newtitle.substring(0, newtitle.length * 0.75);
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          elided = true;
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (elided && newtitle.length > 3)
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        stringWidthPair = new ElidedStringWidthPair(
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            newtitle,
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            track.labelWidth(newtitle));
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elidedDictForPixWidth[sliceDuration] = stringWidthPair;
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return stringWidthPair;
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * A pair representing an elided string and world-coordinate width
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * to draw it.
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function ElidedStringWidthPair(string, width) {
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    this.string = string;
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    this.width = width;
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return {
35088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown    SliceTrack: SliceTrack
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis});
353