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 Gennis/** 888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown * @fileoverview Code for the viewport. 92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 1066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('base.events'); 112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 122da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.exportTo('tracing', function() { 132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * The TimelineViewport manages the transform used for navigating 162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * within the timeline. It is a simple transform: 172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * x' = (x+pan) * scale 182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * 192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * The timeline code tries to avoid directly accessing this transform, 202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * instead using this class to do conversion between world and viewspace, 212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * as well as the math for centering the viewport in various interesting 222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * ways. 232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * 242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * @constructor 252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * @extends {base.EventTarget} 262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function TimelineViewport(parentEl) { 282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.parentEl_ = parentEl; 2966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.modelTrackContainer_ = null; 302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.scaleX_ = 1; 312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.panX_ = 0; 3266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.panY_ = 0; 332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.gridTimebase_ = 0; 342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.gridStep_ = 1000 / 60; 352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.gridEnabled_ = false; 362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.hasCalledSetupFunction_ = false; 372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.onResize_ = this.onResize_.bind(this); 396833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.onModelTrackControllerScroll_ = 406833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.onModelTrackControllerScroll_.bind(this); 412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis // The following code uses an interval to detect when the parent element 432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis // is attached to the document. That is a trigger to run the setup function 442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis // and install a resize listener. 452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.checkForAttachInterval_ = setInterval( 462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.checkForAttach_.bind(this), 250); 472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.markers = []; 492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis TimelineViewport.prototype = { 522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis __proto__: base.EventTarget.prototype, 532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * Allows initialization of the viewport when the viewport's parent element 562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * has been attached to the document and given a size. 572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * @param {Function} fn Function to call when the viewport can be safely 582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * initialized. 592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis setWhenPossible: function(fn) { 612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.pendingSetFunction_ = fn; 622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * @return {boolean} Whether the current timeline is attached to the 662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * document. 672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get isAttachedToDocument_() { 692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var cur = this.parentEl_; 7066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis // Allow not providing a parent element, used by tests. 7166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (cur === undefined) 7266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return; 732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while (cur.parentNode) 742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis cur = cur.parentNode; 752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return cur == this.parentEl_.ownerDocument; 762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis onResize_: function() { 792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * Checks whether the parentNode is attached to the document. 842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * When it is, it installs the iframe-based resize detection hook 852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * and then runs the pendingSetFunction_, if present. 862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis checkForAttach_: function() { 882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (!this.isAttachedToDocument_ || this.clientWidth == 0) 892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return; 902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (!this.iframe_) { 922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.iframe_ = document.createElement('iframe'); 932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.iframe_.style.cssText = 942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'position:absolute;width:100%;height:0;border:0;visibility:hidden;'; 952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.parentEl_.appendChild(this.iframe_); 962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 9766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.iframe_.contentWindow.addEventListener('resize', this.onResize_); 982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 10066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var curSize = this.parentEl_.clientWidth + 'x' + 10166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.parentEl_.clientHeight; 1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.pendingSetFunction_) { 1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.lastSize_ = curSize; 1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try { 1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.pendingSetFunction_(); 1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } catch (ex) { 10766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis console.log('While running setWhenPossible:', 10866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ex.message ? ex.message + '\n' + ex.stack : ex.stack); 1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.pendingSetFunction_ = undefined; 1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis window.clearInterval(this.checkForAttachInterval_); 1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.checkForAttachInterval_ = undefined; 1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * Fires the change event on this viewport. Used to notify listeners 1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * to redraw when the underlying model has been mutated. 1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis dispatchChangeEvent: function() { 1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.dispatchSimpleEvent(this, 'change'); 1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis dispatchMarkersChangeEvent_: function() { 1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.dispatchSimpleEvent(this, 'markersChange'); 1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis detach: function() { 1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.checkForAttachInterval_) { 1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis window.clearInterval(this.checkForAttachInterval_); 1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.checkForAttachInterval_ = undefined; 1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.iframe_) { 13566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.iframe_.removeEventListener('resize', this.onResize_); 1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.parentEl_.removeChild(this.iframe_); 1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 14066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis getStateInViewCoordinates: function() { 14166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return { 14266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis panX: this.xWorldVectorToView(this.panX), 14366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis panY: this.panY, 14466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis scaleX: this.scaleX 14566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }; 14666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 14766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 14866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis setStateInViewCoordinates: function(state) { 14966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.panX = this.xViewVectorToWorld(state.panX); 15066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.panY = state.panY; 15166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 15266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 1536833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis onModelTrackControllerScroll_: function(e) { 1546833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.panY_ = this.modelTrackContainer_.scrollTop; 1556833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis }, 1566833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis 15766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis set modelTrackContainer(m) { 1586833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis 1596833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis if (this.modelTrackContainer_) 1606833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.modelTrackContainer_.removeEventListener('scroll', 1616833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.onModelTrackControllerScroll_); 1626833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis 16366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.modelTrackContainer_ = m; 1646833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.modelTrackContainer_.addEventListener('scroll', 1656833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis this.onModelTrackControllerScroll_); 16666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 16766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get scaleX() { 1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.scaleX_; 1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set scaleX(s) { 1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var changed = this.scaleX_ != s; 1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (changed) { 1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.scaleX_ = s; 1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get panX() { 1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.panX_; 1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set panX(p) { 1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var changed = this.panX_ != p; 1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (changed) { 1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.panX_ = p; 1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 19066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis get panY() { 19166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return this.panY_; 19266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 19366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis set panY(p) { 19466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.panY_ = p; 19566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.modelTrackContainer_.scrollTop = p; 19666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 19766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis setPanAndScale: function(p, s) { 1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var changed = this.scaleX_ != s || this.panX_ != p; 2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (changed) { 2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.scaleX_ = s; 2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.panX_ = p; 2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis xWorldToView: function(x) { 2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return (x + this.panX_) * this.scaleX_; 2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis xWorldVectorToView: function(x) { 2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return x * this.scaleX_; 2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis xViewToWorld: function(x) { 2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return (x / this.scaleX_) - this.panX_; 2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis xViewVectorToWorld: function(x) { 2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return x / this.scaleX_; 2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) { 2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (typeof viewX == 'string') { 2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (viewX == 'left') { 2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis viewX = 0; 2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } else if (viewX == 'center') { 2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis viewX = viewWidth / 2; 2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } else if (viewX == 'right') { 2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis viewX = viewWidth - 1; 2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } else { 2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis throw new Error('unrecognized string for viewPos. left|center|right'); 2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.panX = (viewX / this.scaleX_) - worldX; 2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 23888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown xPanWorldBoundsIntoView: function(worldMin, worldMax, viewWidth) { 2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.xWorldToView(worldMin) < 0) 2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth); 2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else if (this.xWorldToView(worldMax) > viewWidth) 2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth); 2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 24588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown xSetWorldBounds: function(worldMin, worldMax, viewWidth) { 24688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown var worldWidth = worldMax - worldMin; 24788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown var scaleX = viewWidth / worldWidth; 2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var panX = -worldMin; 2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.setPanAndScale(panX, scaleX); 2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get gridEnabled() { 2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.gridEnabled_; 2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set gridEnabled(enabled) { 2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.gridEnabled_ == enabled) 2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return; 25966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.gridEnabled_ = enabled && true; 2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get gridTimebase() { 2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.gridTimebase_; 2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set gridTimebase(timebase) { 2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.gridTimebase_ == timebase) 2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return; 2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.gridTimebase_ = timebase; 27266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.dispatchChangeEvent(); 2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get gridStep() { 2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.gridStep_; 2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis applyTransformToCanvas: function(ctx) { 2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); 2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis addMarker: function(positionWorld) { 28488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown var marker = new ViewportMarker(this, positionWorld); 2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.markers.push(marker); 2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchMarkersChangeEvent_(); 2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return marker; 2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis removeMarker: function(marker) { 2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for (var i = 0; i < this.markers.length; ++i) { 2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.markers[i] === marker) { 2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.markers.splice(i, 1); 2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchChangeEvent(); 2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.dispatchMarkersChangeEvent_(); 2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return true; 2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis findMarkerNear: function(positionWorld, nearnessInViewPixels) { 3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis // Converts pixels into distance in world. 3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var nearnessThresholdWorld = this.xViewVectorToWorld( 3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis nearnessInViewPixels); 3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for (var i = 0; i < this.markers.length; ++i) { 3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (Math.abs(this.markers[i].positionWorld - positionWorld) <= 3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis nearnessThresholdWorld) { 3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var marker = this.markers[i]; 3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return marker; 3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return undefined; 31466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 31566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 31666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis drawGridLines: function(ctx, viewLWorld, viewRWorld) { 31766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (!this.gridEnabled) 31866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return; 31966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 32066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var x = this.gridTimebase; 32166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 32266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.beginPath(); 32366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis while (x < viewRWorld) { 32466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (x >= viewLWorld) { 32566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis // Do conversion to viewspace here rather than on 32666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis // x to avoid precision issues. 32766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var vx = this.xWorldToView(x); 32866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.moveTo(vx, 0); 32966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.lineTo(vx, ctx.canvas.height); 33066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 33166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis x += this.gridStep; 33266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 33366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.strokeStyle = 'rgba(255,0,0,0.25)'; 33466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.stroke(); 33566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 33666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 33766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis drawMarkerArrows: function(ctx, viewLWorld, viewRWorld, drawHeight) { 33866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis for (var i = 0; i < this.markers.length; ++i) { 33966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld, 34066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.canvas.height, drawHeight, this); 34166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 34266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 34366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 34466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis drawMarkerLines: function(ctx, viewLWorld, viewRWorld) { 34566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis for (var i = 0; i < this.markers.length; ++i) { 34666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.markers[i].drawLine(ctx, viewLWorld, viewRWorld, 34766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.canvas.height, this); 34866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }; 3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis /** 3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * Represents a marked position in the world, at a viewport level. 3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis * @constructor 3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis */ 35688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown function ViewportMarker(vp, positionWorld) { 3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.viewport_ = vp; 3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.positionWorld_ = positionWorld; 3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.selected_ = false; 3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 36288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown ViewportMarker.prototype = { 3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get positionWorld() { 3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.positionWorld_; 3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set positionWorld(positionWorld) { 3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.positionWorld_ = positionWorld; 3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.viewport_.dispatchChangeEvent(); 3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis set selected(selected) { 3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.selected_ = selected; 3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis this.viewport_.dispatchChangeEvent(); 3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get selected() { 3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return this.selected_; 3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis get color() { 3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (this.selected) 3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 'rgb(255,0,0)'; 3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 'rgb(0,0,0)'; 3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis drawTriangle_: function(ctx, viewLWorld, viewRWorld, 3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis canvasH, rulerHeight, vp) { 3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.beginPath(); 39066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var ts = this.positionWorld_; 39266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (ts < viewLWorld || ts > viewRWorld) 39366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return; 39466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 39566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var viewX = vp.xWorldToView(ts); 39666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.moveTo(viewX, rulerHeight); 39766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.lineTo(viewX - 3, rulerHeight / 2); 39866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.lineTo(viewX + 3, rulerHeight / 2); 39966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.lineTo(viewX, rulerHeight); 40066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.closePath(); 40166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.fillStyle = this.color; 40266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.fill(); 40366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 40466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (rulerHeight === canvasH) 40566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return; 40666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 40766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis // Draw line from bottom of triangle to the bottom of our canvas. 40866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.beginPath(); 40966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.moveTo(viewX, rulerHeight); 41066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.lineTo(viewX, canvasH); 41166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.closePath(); 41266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.strokeStyle = this.color; 41366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis ctx.stroke(); 4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }, 4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) { 4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.beginPath(); 4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var ts = this.positionWorld_; 4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (ts >= viewLWorld && ts < viewRWorld) { 4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis var viewX = vp.xWorldToView(ts); 4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.moveTo(viewX, 0); 4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.lineTo(viewX, canvasH); 4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.strokeStyle = this.color; 4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ctx.stroke(); 4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis } 4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }; 4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return { 4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis TimelineViewport: TimelineViewport, 43188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown ViewportMarker: ViewportMarker 4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis }; 4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis}); 434