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
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.requireStylesheet('tracks.timeline_viewport_track');
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('tracks.timeline_track');
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('tracks.timeline_canvas_based_track');
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('ui');
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.exportTo('tracks', function() {
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * A track that displays the viewport size and scale.
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @extends {CanvasBasedTrack}
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var TimelineViewportTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var logOf10 = Math.log(10);
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function log10(x) {
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return Math.log(x) / logOf10;
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  TimelineViewportTrack.prototype = {
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    __proto__: tracks.TimelineCanvasBasedTrack.prototype,
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    decorate: function() {
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.classList.add('timeline-viewport-track');
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.strings_secs_ = [];
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.strings_msecs_ = [];
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.addEventListener('mousedown', this.onMouseDown);
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    onMouseDown: function(e) {
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (e.button != 0)
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.placeAndBeginDraggingMarker(e.clientX);
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    placeAndBeginDraggingMarker: function(clientX) {
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var viewX = clientX - this.canvasContainer_.offsetLeft;
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var worldX = this.viewport_.xViewToWorld(viewX);
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var marker = this.viewport_.findMarkerNear(worldX, 6);
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var createdMarker = false;
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var movedMarker = false;
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!marker) {
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        marker = this.viewport_.addMarker(worldX);
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        createdMarker = true;
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      marker.selected = true;
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var that = this;
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var onMouseMove = function(e) {
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var viewX = e.clientX - that.canvasContainer_.offsetLeft;
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var worldX = that.viewport_.xViewToWorld(viewX);
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        marker.positionWorld = worldX;
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        movedMarker = true;
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      };
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var onMouseUp = function(e) {
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        marker.selected = false;
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (!movedMarker && !createdMarker)
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          that.viewport_.removeMarker(marker);
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('mouseup', onMouseUp);
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('mousemove', onMouseMove);
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      };
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      document.addEventListener('mouseup', onMouseUp);
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      document.addEventListener('mousemove', onMouseMove);
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    drawLine_: function(ctx, x1, y1, x2, y2, color) {
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.beginPath();
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.moveTo(x1, y1);
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(x2, y2);
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.closePath();
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.strokeStyle = color;
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.stroke();
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    drawArrow_: function(ctx, x1, y1, x2, y2, arrowWidth, color) {
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.drawLine_(ctx, x1, y1, x2, y2, color);
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var dx = x2 - x1;
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var dy = y2 - y1;
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var len = Math.sqrt(dx * dx + dy * dy);
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var perc = (len - 10) / len;
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var bx = x1 + perc * dx;
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var by = y1 + perc * dy;
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ux = dx / len;
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var uy = dy / len;
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ax = uy * arrowWidth;
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ay = -ux * arrowWidth;
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.beginPath();
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fillStyle = color;
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.moveTo(bx + ax, by + ay);
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(x2, y2);
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(bx - ax, by - ay);
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(bx + ax, by + ay);
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.closePath();
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fill();
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    redraw: function() {
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ctx = this.ctx_;
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var canvasW = this.canvas_.width;
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var canvasH = this.canvas_.height;
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.clearRect(0, 0, canvasW, canvasH);
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Culling parametrs.
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var vp = this.viewport_;
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixWidth = vp.xViewVectorToWorld(1);
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var viewLWorld = vp.xViewToWorld(0);
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var viewRWorld = vp.xViewToWorld(canvasW);
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var measurements = this.classList.contains('timeline-viewport' +
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          '-track-with-distance-measurements');
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var rulerHeight = measurements ? canvasH / 2 : canvasH;
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < vp.markers.length; ++i) {
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        vp.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                    canvasH, rulerHeight, vp);
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var idealMajorMarkDistancePix = 150;
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var idealMajorMarkDistanceWorld =
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          vp.xViewVectorToWorld(idealMajorMarkDistancePix);
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var majorMarkDistanceWorld;
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var unit;
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var unitDivisor;
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tickLabels;
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // The conservative guess is the nearest enclosing 0.1, 1, 10, 100, etc.
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var conservativeGuess =
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          Math.pow(10, Math.ceil(log10(idealMajorMarkDistanceWorld)));
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Once we have a conservative guess, consider things that evenly add up
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // to the conservative guess, e.g. 0.5, 0.2, 0.1 Pick the one that still
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // exceeds the ideal mark distance.
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var divisors = [10, 5, 2, 1];
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < divisors.length; ++i) {
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var tightenedGuess = conservativeGuess / divisors[i];
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          continue;
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        break;
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tickLabels = undefined;
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (majorMarkDistanceWorld < 100) {
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unit = 'ms';
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unitDivisor = 1;
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        tickLabels = this.strings_msecs_;
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      } else {
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unit = 's';
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unitDivisor = 1000;
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        tickLabels = this.strings_secs_;
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var numTicksPerMajor = 5;
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var minorMarkDistanceWorld = majorMarkDistanceWorld / numTicksPerMajor;
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var firstMajorMark =
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          Math.floor(viewLWorld / majorMarkDistanceWorld) *
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              majorMarkDistanceWorld;
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var minorTickH = Math.floor(canvasH * 0.25);
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fillStyle = 'rgb(0, 0, 0)';
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.strokeStyle = 'rgb(0, 0, 0)';
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.textAlign = 'left';
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.textBaseline = 'top';
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.font = (9 * pixelRatio) + 'px sans-serif';
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Each iteration of this loop draws one major mark
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // and numTicksPerMajor minor ticks.
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      //
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Rendering can't be done in world space because canvas transforms
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // affect line width. So, do the conversions manually.
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var curX = firstMajorMark;
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           curX < viewRWorld;
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           curX += majorMarkDistanceWorld) {
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var curXView = Math.floor(vp.xWorldToView(curX));
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var unitValue = curX / unitDivisor;
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var roundedUnitValue = Math.floor(unitValue * 100000) / 100000;
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (!tickLabels[roundedUnitValue])
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          tickLabels[roundedUnitValue] = roundedUnitValue + ' ' + unit;
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.fillText(tickLabels[roundedUnitValue],
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                     curXView + 2 * pixelRatio, 0);
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.beginPath();
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Major mark
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.moveTo(curXView, 0);
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.lineTo(curXView, rulerHeight);
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Minor marks
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (var i = 1; i < numTicksPerMajor; ++i) {
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var xView = Math.floor(curXView + minorMarkDistancePx * i);
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          ctx.moveTo(xView, rulerHeight - minorTickH);
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          ctx.lineTo(xView, rulerHeight);
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.stroke();
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Give distance between directly adjacent markers.
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (measurements) {
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Divide canvas horizontally between ruler and measurements.
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.moveTo(0, rulerHeight);
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.lineTo(canvasW, rulerHeight);
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.stroke();
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Obtain a sorted array of markers
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var sortedMarkers = vp.markers.slice();
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sortedMarkers.sort(function(a, b) {
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          return a.positionWorld_ - b.positionWorld_;
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        });
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Distance Variables.
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var displayDistance;
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var unitDivisor;
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var displayTextColor = 'rgb(0,0,0)';
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var measurementsPosY = rulerHeight + 2;
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Arrow Variables.
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowSpacing = 10;
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowColor = 'rgb(128,121,121)';
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowPosY = measurementsPosY + 4;
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowWidthView = 3;
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var spaceForArrowsView = 2 * (arrowWidthView + arrowSpacing);
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (i = 0; i < sortedMarkers.length - 1; i++) {
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var rightMarker = sortedMarkers[i + 1];
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var leftMarker = sortedMarkers[i];
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var distanceBetweenMarkers =
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightMarker.positionWorld - leftMarker.positionWorld;
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var distanceBetweenMarkersView =
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              vp.xWorldVectorToView(distanceBetweenMarkers);
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var positionInMiddleOfMarkers = leftMarker.positionWorld +
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                              distanceBetweenMarkers / 2;
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var positionInMiddleOfMarkersView =
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              vp.xWorldToView(positionInMiddleOfMarkers);
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Determine units.
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (distanceBetweenMarkers < 100) {
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unit = 'ms';
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unitDivisor = 1;
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          } else {
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unit = 's';
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unitDivisor = 1000;
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Calculate display value to print.
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          displayDistance = distanceBetweenMarkers / unitDivisor;
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var roundedDisplayDistance =
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              Math.abs((Math.floor(displayDistance * 1000) / 1000));
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textToDraw = roundedDisplayDistance + ' ' + unit;
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textWidthView = ctx.measureText(textToDraw).width;
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textWidthWorld = vp.xViewVectorToWorld(textWidthView);
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var spaceForArrowsAndTextView = textWidthView +
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                          spaceForArrowsView + arrowSpacing;
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Set text positions.
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textLeft = leftMarker.positionWorld +
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              (distanceBetweenMarkers / 2) - (textWidthWorld / 2);
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textRight = textLeft + textWidthWorld;
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textPosY = measurementsPosY;
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textLeftView = vp.xWorldToView(textLeft);
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textRightView = vp.xWorldToView(textRight);
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var leftMarkerView = vp.xWorldToView(leftMarker.positionWorld);
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var rightMarkerView = vp.xWorldToView(rightMarker.positionWorld);
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textDrawn = false;
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (spaceForArrowsAndTextView <= distanceBetweenMarkersView) {
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Print the display distance text.
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillStyle = displayTextColor;
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillText(textToDraw, textLeftView, textPosY);
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            textDrawn = true;
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (spaceForArrowsView <= distanceBetweenMarkersView) {
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var leftArrowStart;
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var rightArrowStart;
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (textDrawn) {
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              leftArrowStart = textLeftView - arrowSpacing;
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightArrowStart = textRightView + arrowSpacing;
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            } else {
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              leftArrowStart = positionInMiddleOfMarkersView;
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightArrowStart = positionInMiddleOfMarkersView;
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            }
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Draw left arrow.
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            this.drawArrow_(ctx, leftArrowStart, arrowPosY,
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                leftMarkerView, arrowPosY, arrowWidthView, arrowColor);
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Draw right arrow.
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            this.drawArrow_(ctx, rightArrowStart, arrowPosY,
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                rightMarkerView, arrowPosY, arrowWidthView, arrowColor);
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Adds items intersecting a point to a selection.
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} vX X location to search at, in viewspace.
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} vY Y location to search at, in viewspace.
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {TimelineSelection} selection Selection to which to add hits.
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @return {boolean} true if a slice was found, otherwise false.
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addIntersectingItemsToSelection: function(vX, vY, selection) {
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Does nothing. There's nothing interesting to pick on the viewport
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // track.
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Adds items intersecting the given range to a selection.
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVX Lower X bound of the interval to search, in
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVX Upper X bound of the interval to search, in
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVY Lower Y bound of the interval to search, in
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVY Upper Y bound of the interval to search, in
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {TimelineSelection} selection Selection to which to add hits.
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addIntersectingItemsInRangeToSelection: function(
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        loVX, hiVX, loY, hiY, selection) {
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Does nothing. There's nothing interesting to pick on the viewport
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // track.
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addAllObjectsMatchingFilterToSelection: function(filter, selection) {
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return {
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    TimelineViewportTrack: TimelineViewportTrack
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis});
357