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

Side by Side Diff: chrome/browser/resources/tracing/timeline_track.js

Issue 8513009: Add TRACE_COUNTER support to about:tracing (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 5
6 /** 6 /**
7 * @fileoverview Renders an array of slices into the provided div, 7 * @fileoverview Renders an array of slices into the provided div,
8 * using a child canvas element. Uses a FastRectRenderer to draw only 8 * using a child canvas element. Uses a FastRectRenderer to draw only
9 * the visible slices. 9 * the visible slices.
10 */ 10 */
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 var textWidthMap = { }; 59 var textWidthMap = { };
60 function quickMeasureText(ctx, text) { 60 function quickMeasureText(ctx, text) {
61 var w = textWidthMap[text]; 61 var w = textWidthMap[text];
62 if (!w) { 62 if (!w) {
63 w = ctx.measureText(text).width; 63 w = ctx.measureText(text).width;
64 textWidthMap[text] = w; 64 textWidthMap[text] = w;
65 } 65 }
66 return w; 66 return w;
67 } 67 }
68 68
69 function addTrack(thisTrack, slices) {
70 var track = new TimelineSliceTrack();
71
72 track.heading = '';
73 track.slices = slices;
74 track.viewport = thisTrack.viewport_;
75
76 thisTrack.tracks_.push(track);
77 thisTrack.appendChild(track);
78 }
79
80 /** 69 /**
81 * Generic base class for timeline tracks 70 * A generic track that contains other tracks as its children.
71 * @constructor
82 */ 72 */
83 TimelineThreadTrack = cr.ui.define('div'); 73 var TimelineContainerTrack = cr.ui.define('div');
84 TimelineThreadTrack.prototype = { 74 TimelineContainerTrack.prototype = {
85 __proto__: HTMLDivElement.prototype, 75 __proto__: HTMLDivElement.prototype,
86 76
87 decorate: function() { 77 decorate: function() {
88 this.className = 'timeline-thread-track'; 78 this.tracks_ = [];
89 }, 79 },
90 80
91 set thread(thread) { 81 detach: function() {
92 this.thread_ = thread; 82 for (var i = 0; i < this.tracks_.length; i++)
93 this.updateChildTracks_(); 83 this.tracks_[i].detach();
94 }, 84 },
95 85
96 /** 86 get viewport() {
97 * @return {string} A human-readable name for the track. 87 return this.viewport_;
98 */
99 get heading() {
100 if (!this.thread_)
101 return '';
102 var tname = this.thread_.name || this.thread_.tid;
103 return this.thread_.parent.pid + ': ' +
104 tname + ':';
105 },
106
107 set headingWidth(width) {
108 for (var i = 0; i < this.tracks_.length; i++)
109 this.tracks_[i].headingWidth = width;
110 }, 88 },
111 89
112 set viewport(v) { 90 set viewport(v) {
113 this.viewport_ = v; 91 this.viewport_ = v;
114 for (var i = 0; i < this.tracks_.length; i++) 92 for (var i = 0; i < this.tracks_.length; i++)
115 this.tracks_[i].viewport = v; 93 this.tracks_[i].viewport = v;
116 this.invalidate(); 94 this.updateChildTracks_();
117 },
118
119 invalidate: function() {
120 if (this.parentNode)
121 this.parentNode.invalidate();
122 },
123
124 onResize: function() {
125 for (var i = 0; i < this.tracks_.length; i++)
126 this.tracks_[i].onResize();
127 }, 95 },
128 96
129 get firstCanvas() { 97 get firstCanvas() {
130 if (this.tracks_.length) 98 if (this.tracks_.length)
131 return this.tracks_[0].firstCanvas; 99 return this.tracks_[0].firstCanvas;
132 return undefined; 100 return undefined;
133 }, 101 },
134 102
135 redraw: function() {
136 for (var i = 0; i < this.tracks_.length; i++)
137 this.tracks_[i].redraw();
138 },
139
140 updateChildTracks_: function() {
141 this.textContent = '';
142 this.tracks_ = [];
143 if (this.thread_) {
144 for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) {
145 addTrack(this, this.thread_.nonNestedSubRows[srI]);
146 }
147 for (var srI = 0; srI < this.thread_.subRows.length; ++srI) {
148 addTrack(this, this.thread_.subRows[srI]);
149 }
150 if (this.tracks_.length > 0) {
151 this.tracks_[0].heading = this.heading;
152 this.tracks_[0].tooltip = 'pid: ' + this.thread_.parent.pid +
153 ', tid: ' + this.thread_.tid +
154 (this.thread_.name ? ', name: ' + this.thread_.name : '');
155 }
156 }
157 },
158
159 /** 103 /**
160 * Picks a slice, if any, at a given location. 104 * Picks a slice, if any, at a given location.
161 * @param {number} wX X location to search at, in worldspace. 105 * @param {number} wX X location to search at, in worldspace.
162 * @param {number} wY Y location to search at, in offset space. 106 * @param {number} wY Y location to search at, in offset space.
163 * offset space. 107 * offset space.
164 * @param {function():*} onHitCallback Callback to call with the slice, 108 * @param {function():*} onHitCallback Callback to call with the slice,
165 * if one is found. 109 * if one is found.
166 * @return {boolean} true if a slice was found, otherwise false. 110 * @return {boolean} true if a slice was found, otherwise false.
167 */ 111 */
168 pick: function(wX, wY, onHitCallback) { 112 pick: function(wX, wY, onHitCallback) {
(...skipping 23 matching lines...) Expand all
192 var trackClientRect = this.tracks_[i].getBoundingClientRect(); 136 var trackClientRect = this.tracks_[i].getBoundingClientRect();
193 var a = Math.max(loY, trackClientRect.top); 137 var a = Math.max(loY, trackClientRect.top);
194 var b = Math.min(hiY, trackClientRect.bottom); 138 var b = Math.min(hiY, trackClientRect.bottom);
195 if (a <= b) 139 if (a <= b)
196 this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback); 140 this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback);
197 } 141 }
198 } 142 }
199 }; 143 };
200 144
201 /** 145 /**
202 * Creates a new timeline track div element 146 * Visualizes a TimelineThread using a series of of TimelineSliceTracks.
147 * @constructor
148 */
149 var TimelineThreadTrack = cr.ui.define(TimelineContainerTrack);
150 TimelineThreadTrack.prototype = {
151 __proto__: TimelineContainerTrack.prototype,
152
153 decorate: function() {
154 this.classList.add('timeline-thread-track');
155 },
156
157 get thread(thread) {
158 return this.thread_;
159 },
160
161 set thread(thread) {
162 this.thread_ = thread;
163 this.updateChildTracks_();
164 },
165
166 get tooltip() {
167 return this.tooltip_;
168 },
169
170 set tooltip(value) {
171 this.tooltip_ = value;
172 this.updateChildTracks_();
173 },
174
175 get heading() {
176 return this.heading_;
177 },
178
179 set heading(h) {
180 this.heading_ = h;
181 this.updateChildTracks_();
182 },
183
184 get headingWidth() {
185 return this.headingWidth_;
186 },
187
188 set headingWidth(width) {
189 this.headingWidth_ = width;
190 this.updateChildTracks_();
191 },
192
193 addTrack_: function(slices) {
194 var track = new TimelineSliceTrack();
195 track.heading = '';
196 track.slices = slices;
197 track.headingWidth = this.headingWidth_;
198 track.viewport = this.viewport_;
199
200 this.tracks_.push(track);
201 this.appendChild(track);
202 },
203
204 updateChildTracks_: function() {
205 this.detach();
206 this.textContent = '';
207 this.tracks_ = [];
208 if (this.thread_) {
209 for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) {
210 this.addTrack_(this.thread_.nonNestedSubRows[srI]);
211 }
212 for (var srI = 0; srI < this.thread_.subRows.length; ++srI) {
213 this.addTrack_(this.thread_.subRows[srI]);
214 }
215 if (this.tracks_.length > 0) {
216 this.tracks_[0].heading = this.heading_;
217 this.tracks_[0].tooltip = this.tooltip_;
218 }
219 }
220 }
221 };
222
223 /**
224 * A canvas-based track constructed. Provides the basic heading and
225 * invalidation-managment infrastructure. Subclasses must implement drawing
226 * and picking code.
203 * @constructor 227 * @constructor
204 * @extends {HTMLDivElement} 228 * @extends {HTMLDivElement}
205 */ 229 */
206 TimelineSliceTrack = cr.ui.define('div'); 230 var CanvasBasedTrack = cr.ui.define('div');
207 231
208 TimelineSliceTrack.prototype = { 232 CanvasBasedTrack.prototype = {
209 __proto__: HTMLDivElement.prototype, 233 __proto__: HTMLDivElement.prototype,
210 234
211 decorate: function() { 235 decorate: function() {
212 this.className = 'timeline-slice-track'; 236 this.className = 'timeline-canvas-based-track';
213 this.slices_ = null; 237 this.slices_ = null;
214 238
215 this.headingDiv_ = document.createElement('div'); 239 this.headingDiv_ = document.createElement('div');
216 this.headingDiv_.className = 'timeline-slice-track-title'; 240 this.headingDiv_.className = 'timeline-canvas-based-track-title';
217 this.appendChild(this.headingDiv_); 241 this.appendChild(this.headingDiv_);
218 242
219 this.canvasContainer_ = document.createElement('div'); 243 this.canvasContainer_ = document.createElement('div');
220 this.canvasContainer_.className = 'timeline-slice-track-canvas-container'; 244 this.canvasContainer_.className =
245 'timeline-canvas-based-track-canvas-container';
221 this.appendChild(this.canvasContainer_); 246 this.appendChild(this.canvasContainer_);
222 this.canvas_ = document.createElement('canvas'); 247 this.canvas_ = document.createElement('canvas');
223 this.canvas_.className = 'timeline-slice-track-canvas'; 248 this.canvas_.className = 'timeline-canvas-based-track-canvas';
224 this.canvasContainer_.appendChild(this.canvas_); 249 this.canvasContainer_.appendChild(this.canvas_);
225 250
226 this.ctx_ = this.canvas_.getContext('2d'); 251 this.ctx_ = this.canvas_.getContext('2d');
227 }, 252 },
228 253
254 detach: function() {
255 if (this.viewport_)
256 this.viewport_.removeEventListener('change',
257 this.viewportChangeBoundToThis_);
258 },
259
229 set headingWidth(width) { 260 set headingWidth(width) {
230 this.headingDiv_.style.width = width; 261 this.headingDiv_.style.width = width;
231 }, 262 },
232 263
233 get heading() { 264 get heading() {
234 return this.headingDiv_.textContent; 265 return this.headingDiv_.textContent;
235 }, 266 },
236 267
237 set heading(text) { 268 set heading(text) {
238 this.headingDiv_.textContent = text; 269 this.headingDiv_.textContent = text;
239 }, 270 },
240 271
241 set tooltip(text) { 272 set tooltip(text) {
242 this.headingDiv_.title = text; 273 this.headingDiv_.title = text;
243 }, 274 },
244 275
276 get viewport() {
277 return this.viewport_;
278 },
279
280 set viewport(v) {
281 this.viewport_ = v;
282 if (this.viewport_)
283 this.viewport_.removeEventListener('change',
284 this.viewportChangeBoundToThis_);
285 this.viewport_ = v;
286 if (this.viewport_) {
287 this.viewportChangeBoundToThis_ = this.viewportChange_.bind(this);
288 this.viewport_.addEventListener('change',
289 this.viewportChangeBoundToThis_);
290 }
291 this.invalidate();
292 },
293
294 viewportChange_: function() {
295 this.invalidate();
296 },
297
298 invalidate: function() {
299 if (this.rafPending_)
300 return;
301 webkitRequestAnimationFrame(function() {
302 this.rafPending_ = false;
303 if (!this.viewport_)
304 return;
305
306 if (this.canvas_.width != this.canvasContainer_.clientWidth)
307 this.canvas_.width = this.canvasContainer_.clientWidth;
308 if (this.canvas_.height != this.canvasContainer_.clientHeight)
309 this.canvas_.height = this.canvasContainer_.clientHeight;
310
311 this.redraw();
312 }.bind(this), this);
313 this.rafPending_ = true;
314 },
315
316 get firstCanvas() {
317 return this.canvas_;
318 }
319
320 };
321
322 /**
323 * A track that displays an array of TimelineSlice objects.
324 * @constructor
325 * @extends {CanvasBasedTrack}
326 */
327
328 var TimelineSliceTrack = cr.ui.define(CanvasBasedTrack);
329
330 TimelineSliceTrack.prototype = {
331
332 __proto__: CanvasBasedTrack.prototype,
333
334 decorate: function() {
335 this.classList.add('timeline-slice-track');
336 },
337
338 get slices() {
339 return this.slices_;
340 },
341
245 set slices(slices) { 342 set slices(slices) {
246 this.slices_ = slices; 343 this.slices_ = slices;
247 this.invalidate(); 344 this.invalidate();
248 }, 345 },
249 346
250 set viewport(v) {
251 this.viewport_ = v;
252 this.invalidate();
253 },
254
255 invalidate: function() {
256 if (this.parentNode)
257 this.parentNode.invalidate();
258 },
259
260 get firstCanvas() {
261 return this.canvas_;
262 },
263
264 onResize: function() {
265 this.canvas_.width = this.canvasContainer_.clientWidth;
266 this.canvas_.height = this.canvasContainer_.clientHeight;
267 this.invalidate();
268 },
269
270 redraw: function() { 347 redraw: function() {
271 if (!this.viewport_)
272 return;
273 var ctx = this.ctx_; 348 var ctx = this.ctx_;
274 var canvasW = this.canvas_.width; 349 var canvasW = this.canvas_.width;
275 var canvasH = this.canvas_.height; 350 var canvasH = this.canvas_.height;
276 351
277 ctx.clearRect(0, 0, canvasW, canvasH); 352 ctx.clearRect(0, 0, canvasW, canvasH);
278 353
279 // culling... 354 // Culling parameters.
280 var vp = this.viewport_; 355 var vp = this.viewport_;
281 var pixWidth = vp.xViewVectorToWorld(1); 356 var pixWidth = vp.xViewVectorToWorld(1);
282 var viewLWorld = vp.xViewToWorld(0); 357 var viewLWorld = vp.xViewToWorld(0);
283 var viewRWorld = vp.xViewToWorld(canvasW); 358 var viewRWorld = vp.xViewToWorld(canvasW);
284 359
285 // Draw grid without a transform because the scale 360 // Draw grid without a transform because the scale
286 // affects line width. 361 // affects line width.
287 if (vp.gridEnabled) { 362 if (vp.gridEnabled) {
288 var x = vp.gridTimebase; 363 var x = vp.gridTimebase;
289 ctx.beginPath(); 364 ctx.beginPath();
290 while (x < viewRWorld) { 365 while (x < viewRWorld) {
291 if (x >= viewLWorld) { 366 if (x >= viewLWorld) {
292 // Do conversion to viewspace here rather than on 367 // Do conversion to viewspace here rather than on
293 // x to avoid precision issues. 368 // x to avoid precision issues.
294 var vx = vp.xWorldToView(x); 369 var vx = vp.xWorldToView(x);
295 ctx.moveTo(vx, 0); 370 ctx.moveTo(vx, 0);
296 ctx.lineTo(vx, canvasH); 371 ctx.lineTo(vx, canvasH);
297 } 372 }
298 x += vp.gridStep; 373 x += vp.gridStep;
299 } 374 }
300 ctx.strokeStyle = 'rgba(255,0,0,0.25)'; 375 ctx.strokeStyle = 'rgba(255,0,0,0.25)';
301 ctx.stroke(); 376 ctx.stroke();
302 } 377 }
303 378
304 // begin rendering in world space 379 // Begin rendering in world space.
305 ctx.save(); 380 ctx.save();
306 vp.applyTransformToCanavs(ctx); 381 vp.applyTransformToCanavs(ctx);
307 382
308 // tracks 383 // Slices.
309 var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth, 384 var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
310 2 * pixWidth, viewRWorld, pallette); 385 2 * pixWidth, viewRWorld, pallette);
311 tr.setYandH(0, canvasH); 386 tr.setYandH(0, canvasH);
312 var slices = this.slices_; 387 var slices = this.slices_;
313 for (var i = 0; i < slices.length; ++i) { 388 for (var i = 0; i < slices.length; ++i) {
314 var slice = slices[i]; 389 var slice = slices[i];
315 var x = slice.start; 390 var x = slice.start;
316 // Less than 0.001 causes short events to disappear when zoomed in. 391 // Less than 0.001 causes short events to disappear when zoomed in.
317 var w = Math.max(slice.duration, 0.001); 392 var w = Math.max(slice.duration, 0.001);
318 var colorId; 393 var colorId;
(...skipping 17 matching lines...) Expand all
336 ctx.lineTo(x, 0); 411 ctx.lineTo(x, 0);
337 ctx.lineTo(x + (4 * pixWidth), canvasH); 412 ctx.lineTo(x + (4 * pixWidth), canvasH);
338 ctx.closePath(); 413 ctx.closePath();
339 ctx.fill(); 414 ctx.fill();
340 } 415 }
341 } 416 }
342 } 417 }
343 tr.flush(); 418 tr.flush();
344 ctx.restore(); 419 ctx.restore();
345 420
346 // labels 421 // Labels.
347 ctx.textAlign = 'center'; 422 ctx.textAlign = 'center';
348 ctx.textBaseline = 'top'; 423 ctx.textBaseline = 'top';
349 ctx.font = '10px sans-serif'; 424 ctx.font = '10px sans-serif';
350 ctx.strokeStyle = 'rgb(0,0,0)'; 425 ctx.strokeStyle = 'rgb(0,0,0)';
351 ctx.fillStyle = 'rgb(0,0,0)'; 426 ctx.fillStyle = 'rgb(0,0,0)';
352 var quickDiscardThresshold = pixWidth * 20; // dont render until 20px wide 427 var quickDiscardThresshold = pixWidth * 20; // dont render until 20px wide
353 for (var i = 0; i < slices.length; ++i) { 428 for (var i = 0; i < slices.length; ++i) {
354 var slice = slices[i]; 429 var slice = slices[i];
355 if (slice.duration > quickDiscardThresshold) { 430 if (slice.duration > quickDiscardThresshold) {
356 var title = slice.title; 431 var title = slice.title;
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 var index = this.indexOfSlice_(slice); 541 var index = this.indexOfSlice_(slice);
467 if (index == 0) 542 if (index == 0)
468 return undefined; 543 return undefined;
469 else if ((index != undefined) && (index > 0)) 544 else if ((index != undefined) && (index > 0))
470 index--; 545 index--;
471 return index != undefined ? this.slices_[index] : undefined; 546 return index != undefined ? this.slices_[index] : undefined;
472 } 547 }
473 548
474 }; 549 };
475 550
551 /**
552 * A track that displays a TimelineCounter object.
553 * @constructor
554 * @extends {CanvasBasedTrack}
555 */
556
557 var TimelineCounterTrack = cr.ui.define(CanvasBasedTrack);
558
559 TimelineCounterTrack.prototype = {
560
561 __proto__: CanvasBasedTrack.prototype,
562
563 decorate: function() {
564 this.classList.add('timeline-counter-track');
565 },
566
567 get counter() {
568 return this.counter_;
569 },
570
571 set counter(counter) {
572 this.counter_ = counter;
573 this.invalidate();
574 },
575
576 redraw: function() {
577 var ctr = this.counter_;
578 var ctx = this.ctx_;
579 var canvasW = this.canvas_.width;
580 var canvasH = this.canvas_.height;
581
582 ctx.clearRect(0, 0, canvasW, canvasH);
583
584 // Culling parametrs.
585 var vp = this.viewport_;
586 var pixWidth = vp.xViewVectorToWorld(1);
587 var viewLWorld = vp.xViewToWorld(0);
588 var viewRWorld = vp.xViewToWorld(canvasW);
589
590 // Drop sampels that are less than skipDistancePix apart.
591 var skipDistancePix = 16;
592 var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix);
593
594 // Begin rendering in world space.
595 ctx.save();
596 vp.applyTransformToCanavs(ctx);
597
598 // Figure out where drawing should begin.
599 var numSeries = ctr.numSeries;
600 var numSamples = ctr.numSamples;
601 var startIndex = tracing.findLowIndexInSortedArray(ctr.timestamps,
602 function() {
603 },
604 viewLWorld);
605
606 // Draw indices one by one until we fall off the viewRWorld.
607 var yScale = canvasH / ctr.maxTotal;
608 for (var seriesIndex = ctr.numSeries - 1;
609 seriesIndex >= 0; seriesIndex--) {
610 var colorId = ctr.seriesColors[seriesIndex];
611 ctx.fillStyle = pallette[colorId];
612 ctx.beginPath();
613
614 // Set iLast and xLast such that the first sample we draw is the
615 // startIndex sample.
616 var iLast = startIndex - 1;
617 var xLast = iLast >= 0 ? ctr.timestamps[iLast] - skipDistanceWorld : -1;
618 var yLastView = canvasH;
619
620 // Iterate over samples from iLast onward until we either fall off the
621 // viewRWorld or we run out of samples. To avoid drawing too much, after
622 // drawing a sample at xLast, skip subsequent samples that are less than
623 // skipDistanceWorld from xLast.
624 var hasMoved = false;
625 while (true) {
626 var i = iLast + 1;
627 if (i >= numSamples) {
628 ctx.lineTo(xLast, yLastView);
629 ctx.lineTo(xLast + 8 * pixWidth, yLastView);
630 ctx.lineTo(xLast + 8 * pixWidth, canvasH);
631 break;
632 }
633
634 var x = ctr.timestamps[i];
635
636 var y = ctr.totals[i * numSeries + seriesIndex];
637 var yView = canvasH - (yScale * y);
638
639 if (x > viewRWorld) {
640 ctx.lineTo(x, yLastView);
641 ctx.lineTo(x, canvasH);
642 break;
643 }
644
645 if (x - xLast < skipDistanceWorld) {
646 iLast = i;
647 continue;
648 }
649
650 if (!hasMoved) {
651 ctx.moveTo(viewLWorld, canvasH);
652 hasMoved = true;
653 }
654 ctx.lineTo(x, yLastView);
655 ctx.lineTo(x, yView);
656 iLast = i;
657 xLast = x;
658 yLastView = yView;
659 }
660 ctx.closePath();
661 ctx.fill();
662 }
663 ctx.restore();
664 },
665
666 /**
667 * Picks a slice, if any, at a given location.
668 * @param {number} wX X location to search at, in worldspace.
669 * @param {number} wY Y location to search at, in offset space.
670 * offset space.
671 * @param {function():*} onHitCallback Callback to call with the slice,
672 * if one is found.
673 * @return {boolean} true if a slice was found, otherwise false.
674 */
675 pick: function(wX, wY, onHitCallback) {
676 },
677
678 /**
679 * Finds slices intersecting the given interval.
680 * @param {number} loWX Lower X bound of the interval to search, in
681 * worldspace.
682 * @param {number} hiWX Upper X bound of the interval to search, in
683 * worldspace.
684 * @param {number} loY Lower Y bound of the interval to search, in
685 * offset space.
686 * @param {number} hiY Upper Y bound of the interval to search, in
687 * offset space.
688 * @param {function():*} onHitCallback Function to call for each slice
689 * intersecting the interval.
690 */
691 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
692 }
693
694 };
695
476 return { 696 return {
697 TimelineCounterTrack: TimelineCounterTrack,
477 TimelineSliceTrack: TimelineSliceTrack, 698 TimelineSliceTrack: TimelineSliceTrack,
478 TimelineThreadTrack: TimelineThreadTrack 699 TimelineThreadTrack: TimelineThreadTrack
479 }; 700 };
480 }); 701 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698