OLD | NEW |
---|---|
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 * @fileoverview Interactive visualizaiton of TimelineModel objects | 6 * @fileoverview Interactive visualizaiton of TimelineModel objects |
7 * based loosely on gantt charts. Each thread in the TimelineModel is given a | 7 * based loosely on gantt charts. Each thread in the TimelineModel is given a |
8 * set of TimelineTracks, one per subrow in the thread. The Timeline class | 8 * set of TimelineTracks, one per subrow in the thread. The Timeline class |
9 * acts as a controller, creating the individual tracks, while TimelineTracks | 9 * acts as a controller, creating the individual tracks, while TimelineTracks |
10 * do actual drawing. | 10 * do actual drawing. |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
128 get gridStep() { | 128 get gridStep() { |
129 return this.gridStep_; | 129 return this.gridStep_; |
130 }, | 130 }, |
131 | 131 |
132 applyTransformToCanavs: function(ctx) { | 132 applyTransformToCanavs: function(ctx) { |
133 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); | 133 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); |
134 } | 134 } |
135 }; | 135 }; |
136 | 136 |
137 /** | 137 /** |
138 * Uses an embedded iframe to measure provided elements without forcing layout | |
139 * on the main document. | |
140 * @constructor | |
141 * @extends {Object} | |
142 */ | |
143 function MeasuringStick() | |
James Hawkins
2011/11/05 17:51:37
Move this into a separate file.
nduca
2011/11/09 22:52:19
Done.
| |
144 { | |
James Hawkins
2011/11/05 17:51:37
Opening brace should be on the same line as the me
nduca
2011/11/09 22:52:19
Done.
| |
145 var iframe = document.createElement('iframe'); | |
146 iframe.style.cssText = 'width:100%;height:0;border:0;visibility:hidden'; | |
147 document.body.appendChild(iframe); | |
148 this._doc = iframe.contentDocument; | |
149 this._window = iframe.contentWindow; | |
150 this._doc.body.style.cssText = 'padding:0;margin:0;overflow:hidden'; | |
151 | |
152 var stylesheets = document.querySelectorAll('link[rel=stylesheet]'); | |
153 for (var i = 0; i < stylesheets.length; ++i) { | |
154 var stylesheet = stylesheets[i]; | |
155 var link = this._doc.createElement('link'); | |
156 link.rel = 'stylesheet'; | |
157 link.href = stylesheet.href; | |
158 this._doc.head.appendChild(link); | |
159 } | |
160 } | |
161 | |
162 MeasuringStick.prototype = { | |
163 __proto__: Object.prototype, | |
164 | |
165 /** | |
166 * Converts measures like 50px to their size in pixels, if possible. | |
167 */ | |
168 convertMeasureToPixels_: function(str) { | |
James Hawkins
2011/11/05 17:51:37
No need for this method.
parseInt(str, 10);
nduca
2011/11/09 22:52:19
Done.
| |
169 var g = /(\d+)px/.exec(str); | |
170 if (g) | |
171 return parseInt(g[0]); | |
172 throw 'Unrecognized unit on ' + str; | |
173 }, | |
174 | |
175 /** | |
176 * Measures the provided element without forcing layout on the main | |
177 * document. | |
178 */ | |
179 measure: function(element) | |
180 { | |
181 this._doc.body.appendChild(element); | |
182 var style = this._window.getComputedStyle(element); | |
183 var width = this.convertMeasureToPixels_(style.width); | |
184 var height = this.convertMeasureToPixels_(style.height); | |
185 this._doc.body.removeChild(element); | |
186 return { width: width, height: height }; | |
187 } | |
188 }; | |
189 | |
190 /** | |
138 * Renders a TimelineModel into a div element, making one | 191 * Renders a TimelineModel into a div element, making one |
139 * TimelineTrack for each subrow in each thread of the model, managing | 192 * TimelineTrack for each subrow in each thread of the model, managing |
140 * overall track layout, and handling user interaction with the | 193 * overall track layout, and handling user interaction with the |
141 * viewport. | 194 * viewport. |
142 * | 195 * |
143 * @constructor | 196 * @constructor |
144 * @extends {HTMLDivElement} | 197 * @extends {HTMLDivElement} |
145 */ | 198 */ |
146 Timeline = cr.ui.define('div'); | 199 Timeline = cr.ui.define('div'); |
147 | 200 |
(...skipping 12 matching lines...) Expand all Loading... | |
160 | 213 |
161 this.invalidatePending_ = false; | 214 this.invalidatePending_ = false; |
162 | 215 |
163 this.tracks_ = this.ownerDocument.createElement('div'); | 216 this.tracks_ = this.ownerDocument.createElement('div'); |
164 this.tracks_.invalidate = this.invalidate.bind(this); | 217 this.tracks_.invalidate = this.invalidate.bind(this); |
165 this.appendChild(this.tracks_); | 218 this.appendChild(this.tracks_); |
166 | 219 |
167 this.dragBox_ = this.ownerDocument.createElement('div'); | 220 this.dragBox_ = this.ownerDocument.createElement('div'); |
168 this.dragBox_.className = 'timeline-drag-box'; | 221 this.dragBox_.className = 'timeline-drag-box'; |
169 this.appendChild(this.dragBox_); | 222 this.appendChild(this.dragBox_); |
223 this.hideDragBox_(); | |
170 | 224 |
171 // The following code uses a setInterval to monitor the timeline control | 225 // The following code uses a setInterval to monitor the timeline control |
172 // for size changes. This is so that we can keep the canvas' bitmap size | 226 // for size changes. This is so that we can keep the canvas' bitmap size |
173 // correctly synchronized with its presentation size. | 227 // correctly synchronized with its presentation size. |
174 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. | 228 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. |
175 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; | 229 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; |
176 this.ownerDocument.defaultView.setInterval(function() { | 230 this.checkForResizeInterval_ = |
231 this.ownerDocument.defaultView.setInterval(function() { | |
232 if (!this.isAttachedToDocument_) | |
233 return; | |
177 var curSize = this.clientWidth + 'x' + this.clientHeight; | 234 var curSize = this.clientWidth + 'x' + this.clientHeight; |
178 if (this.clientWidth && curSize != this.lastSize_) { | 235 if (this.clientWidth && curSize != this.lastSize_) { |
179 this.lastSize_ = curSize; | 236 this.lastSize_ = curSize; |
180 this.onResize(); | 237 this.onResize(); |
181 } | 238 } |
182 }.bind(this), 250); | 239 }.bind(this), 250); |
183 | 240 |
184 document.addEventListener('keypress', this.onKeypress_.bind(this)); | 241 this.safeAddEventListener(document, 'keypress', this.onKeypress_, this); |
185 document.addEventListener('keydown', this.onKeydown_.bind(this)); | 242 this.safeAddEventListener(document, 'keydown', this.onKeydown_, this); |
186 document.addEventListener('mousedown', this.onMouseDown_.bind(this)); | 243 this.safeAddEventListener(document, 'mousedown', this.onMouseDown_, this); |
187 document.addEventListener('mousemove', this.onMouseMove_.bind(this)); | 244 this.safeAddEventListener(document, 'mousemove', this.onMouseMove_, this); |
188 document.addEventListener('mouseup', this.onMouseUp_.bind(this)); | 245 this.safeAddEventListener(document, 'mouseup', this.onMouseUp_, this); |
189 document.addEventListener('dblclick', this.onDblClick_.bind(this)); | 246 this.safeAddEventListener(document, 'dblclick', this.onDblClick_, this); |
190 | 247 |
191 this.lastMouseViewPos_ = {x: 0, y: 0}; | 248 this.lastMouseViewPos_ = {x: 0, y: 0}; |
192 | 249 |
193 this.selection_ = []; | 250 this.selection_ = []; |
194 }, | 251 }, |
195 | 252 |
253 safeAddEventListener: function(object, event, func, target) { | |
James Hawkins
2011/11/05 17:51:37
Document this method and the params.
In the comme
James Hawkins
2011/11/05 17:51:37
This method should likely be private.
nduca
2011/11/09 22:52:19
Done.
nduca
2011/11/09 22:52:19
Done.
| |
254 if (!this.boundListeners_) | |
255 this.boundListeners_ = []; | |
256 var boundFunc = func.bind(target); | |
257 this.boundListeners_.push({object: object, | |
258 event: event, | |
259 boundFunc: boundFunc}); | |
260 object.addEventListener(event, boundFunc); | |
261 }, | |
262 | |
263 detach: function() { | |
264 for (var i = 0; i < this.boundListeners_.length; ++i) { | |
265 var binding = this.boundListeners_[i]; | |
266 binding.object.removeEventListener(binding.event, binding.boundFunc); | |
267 } | |
268 this.boundListeners_ = undefined; | |
269 window.clearInterval(this.checkForResizeInterval_); | |
270 this.checkForResizeInterval_ = undefined; | |
271 }, | |
272 | |
196 get model() { | 273 get model() { |
197 return this.model_; | 274 return this.model_; |
198 }, | 275 }, |
199 | 276 |
200 set model(model) { | 277 set model(model) { |
201 if (!model) | 278 if (!model) |
202 throw Error('Model cannot be null'); | 279 throw Error('Model cannot be null'); |
203 if (this.model) { | 280 if (this.model) { |
204 throw Error('Cannot set model twice.'); | 281 throw Error('Cannot set model twice.'); |
205 } | 282 } |
206 this.model_ = model; | 283 this.model_ = model; |
207 | 284 |
208 // Create tracks. | 285 // Create tracks and measure their heading size |
James Hawkins
2011/11/05 17:51:37
nit: Period at end of sentence.
nduca
2011/11/09 22:52:19
Done.
| |
209 this.tracks_.textContent = ''; | |
210 var threads = model.getAllThreads(); | 286 var threads = model.getAllThreads(); |
211 threads.sort(tracing.TimelineThread.compare); | 287 var maxHeadingWidth = 0; |
288 var tracks = []; | |
289 var measuringStick = new MeasuringStick(); | |
290 var headingEl = document.createElement('div'); | |
291 headingEl.style.position = 'fixed'; | |
292 headingEl.className = 'timeline-slice-track-title'; | |
212 for (var tI = 0; tI < threads.length; tI++) { | 293 for (var tI = 0; tI < threads.length; tI++) { |
213 var thread = threads[tI]; | 294 var thread = threads[tI]; |
214 var track = new TimelineThreadTrack(); | 295 var track = new TimelineThreadTrack(); |
215 track.thread = thread; | 296 track.thread = thread; |
216 track.viewport = this.viewport_; | 297 track.viewport = this.viewport_; |
298 tracks.push(track); | |
299 headingEl.textContent = track.heading; | |
300 var w = measuringStick.measure(headingEl).width; | |
301 // limit heading width to 300px | |
James Hawkins
2011/11/05 17:51:37
Comment sentence should start with a capital lette
nduca
2011/11/09 22:52:19
Done.
| |
302 if (w > 300) | |
303 w = 300; | |
304 if (w > maxHeadingWidth) | |
305 maxHeadingWidth = w; | |
306 } | |
307 maxHeadingWidth += maxHeadingWidth + 4 | |
308 | |
309 // Attach tracks and set width | |
James Hawkins
2011/11/05 17:51:37
Add period.
nduca
2011/11/09 22:52:19
Done.
| |
310 this.tracks_.textContent = ''; | |
311 threads.sort(tracing.TimelineThread.compare); | |
312 for (var tI = 0; tI < tracks.length; tI++) { | |
313 var track = tracks[tI]; | |
314 track.headingWidth = maxHeadingWidthWithUnit + 'px'; | |
217 this.tracks_.appendChild(track); | 315 this.tracks_.appendChild(track); |
218 | |
219 } | 316 } |
220 | 317 |
221 this.needsViewportReset_ = true; | 318 if (this.isAttachedToDocument_) |
319 this.onResize(); | |
320 else | |
321 this.needsViewportReset_ = true; | |
222 }, | 322 }, |
223 | 323 |
224 viewportChange_: function() { | 324 viewportChange_: function() { |
225 this.invalidate(); | 325 this.invalidate(); |
226 }, | 326 }, |
227 | 327 |
228 invalidate: function() { | 328 invalidate: function() { |
229 if (this.invalidatePending_) | 329 if (this.invalidatePending_) |
230 return; | 330 return; |
231 this.invalidatePending_ = true; | 331 this.invalidatePending_ = true; |
232 window.setTimeout(function() { | 332 if (this.isAttachedToDocument_) |
James Hawkins
2011/11/05 17:51:37
Braces required for multi-line blocks.
nduca
2011/11/09 22:52:19
Done.
| |
233 this.invalidatePending_ = false; | 333 window.setTimeout(function() { |
234 this.redrawAllTracks_(); | 334 this.invalidatePending_ = false; |
235 }.bind(this), 0); | 335 this.redrawAllTracks_(); |
336 }.bind(this), 0); | |
337 }, | |
338 | |
339 get isAttachedToDocument_() { | |
James Hawkins
2011/11/05 17:51:37
Document return type.
nduca
2011/11/09 22:52:19
Done.
| |
340 var cur = this; | |
341 while (cur.parentNode) | |
342 cur = cur.parentNode; | |
343 return cur == this.ownerDocument; | |
236 }, | 344 }, |
237 | 345 |
238 onResize: function() { | 346 onResize: function() { |
347 if (!this.isAttachedToDocument_) | |
348 throw 'Not attached to document!'; | |
239 for (var i = 0; i < this.tracks_.children.length; ++i) { | 349 for (var i = 0; i < this.tracks_.children.length; ++i) { |
240 var track = this.tracks_.children[i]; | 350 var track = this.tracks_.children[i]; |
241 track.onResize(); | 351 track.onResize(); |
242 } | 352 } |
353 if (this.invalidatePending_) { | |
354 this.invalidatePending_ = false; | |
355 this.redrawAllTracks_(); | |
356 } | |
243 }, | 357 }, |
244 | 358 |
245 redrawAllTracks_: function() { | 359 redrawAllTracks_: function() { |
246 if (this.needsViewportReset_ && this.clientWidth != 0) { | 360 if (this.needsViewportReset_ && this.clientWidth != 0) { |
361 if (!this.isAttachedToDocument_) | |
362 throw 'Not attached to document!'; | |
247 this.needsViewportReset_ = false; | 363 this.needsViewportReset_ = false; |
248 /* update viewport */ | 364 /* update viewport */ |
249 var rangeTimestamp = this.model_.maxTimestamp - | 365 var rangeTimestamp = this.model_.maxTimestamp - |
250 this.model_.minTimestamp; | 366 this.model_.minTimestamp; |
251 var w = this.firstCanvas.width; | 367 var w = this.firstCanvas.width; |
252 console.log('viewport was reset with w=', w); | |
253 var scaleX = w / rangeTimestamp; | 368 var scaleX = w / rangeTimestamp; |
254 var panX = -this.model_.minTimestamp; | 369 var panX = -this.model_.minTimestamp; |
255 this.viewport_.setPanAndScale(panX, scaleX); | 370 this.viewport_.setPanAndScale(panX, scaleX); |
256 } | 371 } |
257 for (var i = 0; i < this.tracks_.children.length; ++i) { | 372 for (var i = 0; i < this.tracks_.children.length; ++i) { |
258 this.tracks_.children[i].redraw(); | 373 this.tracks_.children[i].redraw(); |
259 } | 374 } |
260 }, | 375 }, |
261 | 376 |
262 updateChildViewports_: function() { | 377 updateChildViewports_: function() { |
263 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { | 378 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { |
264 var child = this.tracks_.children[cI]; | 379 var child = this.tracks_.children[cI]; |
265 child.setViewport(this.panX, this.scaleX); | 380 child.setViewport(this.panX, this.scaleX); |
266 } | 381 } |
267 }, | 382 }, |
268 | 383 |
384 get listenToKeys_() { | |
385 if (this.parentElement.parentElement.tabIndex >= 0) | |
386 return document.activeElement == this.parentElement.parentElement; | |
387 return true; | |
388 }, | |
389 | |
269 onKeypress_: function(e) { | 390 onKeypress_: function(e) { |
270 var vp = this.viewport_; | 391 var vp = this.viewport_; |
271 if (!this.firstCanvas) | 392 if (!this.firstCanvas) |
272 return; | 393 return; |
394 if (!this.listenToKeys_) | |
395 return; | |
273 var viewWidth = this.firstCanvas.clientWidth; | 396 var viewWidth = this.firstCanvas.clientWidth; |
274 var curMouseV, curCenterW; | 397 var curMouseV, curCenterW; |
275 switch (e.keyCode) { | 398 switch (e.keyCode) { |
276 case 101: // e | 399 case 101: // e |
277 var vX = this.lastMouseViewPos_.x; | 400 var vX = this.lastMouseViewPos_.x; |
278 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); | 401 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); |
279 var distFromCenter = vX - (viewWidth / 2); | 402 var distFromCenter = vX - (viewWidth / 2); |
280 var percFromCenter = distFromCenter / viewWidth; | 403 var percFromCenter = distFromCenter / viewWidth; |
281 var percFromCenterSq = percFromCenter * percFromCenter; | 404 var percFromCenterSq = percFromCenter * percFromCenter; |
282 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); | 405 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); |
(...skipping 26 matching lines...) Expand all Loading... | |
309 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); | 432 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); |
310 break; | 433 break; |
311 case 68: // D | 434 case 68: // D |
312 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); | 435 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); |
313 break; | 436 break; |
314 } | 437 } |
315 }, | 438 }, |
316 | 439 |
317 // Not all keys send a keypress. | 440 // Not all keys send a keypress. |
318 onKeydown_: function(e) { | 441 onKeydown_: function(e) { |
442 if (!this.listenToKeys_) | |
443 return; | |
319 switch (e.keyCode) { | 444 switch (e.keyCode) { |
320 case 37: // left arrow | 445 case 37: // left arrow |
321 this.selectPrevious_(e); | 446 this.selectPrevious_(e); |
322 e.preventDefault(); | 447 e.preventDefault(); |
323 break; | 448 break; |
324 case 39: // right arrow | 449 case 39: // right arrow |
325 this.selectNext_(e); | 450 this.selectNext_(e); |
326 e.preventDefault(); | 451 e.preventDefault(); |
327 break; | 452 break; |
328 case 9: // TAB | 453 case 9: // TAB |
329 if (e.shiftKey) | 454 if (this.parentElement.parentElement.tabIndex == -1) { |
330 this.selectPrevious_(e); | 455 if (e.shiftKey) |
331 else | 456 this.selectPrevious_(e); |
332 this.selectNext_(e); | 457 else |
333 e.preventDefault(); | 458 this.selectNext_(e); |
459 e.preventDefault(); | |
460 } | |
334 break; | 461 break; |
335 } | 462 } |
336 }, | 463 }, |
337 | 464 |
338 /** | 465 /** |
339 * Zoom in or out on the timeline by the given scale factor. | 466 * Zoom in or out on the timeline by the given scale factor. |
340 * @param {integer} scale The scale factor to apply. If <1, zooms out. | 467 * @param {integer} scale The scale factor to apply. If <1, zooms out. |
341 */ | 468 */ |
342 zoomBy_: function(scale) { | 469 zoomBy_: function(scale) { |
343 if (!this.firstCanvas) | 470 if (!this.firstCanvas) |
(...skipping 22 matching lines...) Expand all Loading... | |
366 * @param {boolean} forwardp If true, select one forward (next). | 493 * @param {boolean} forwardp If true, select one forward (next). |
367 * Else, select previous. | 494 * Else, select previous. |
368 */ | 495 */ |
369 selectAdjoining_: function(e, forwardp) { | 496 selectAdjoining_: function(e, forwardp) { |
370 var i, track, slice, adjoining; | 497 var i, track, slice, adjoining; |
371 var selection = []; | 498 var selection = []; |
372 // Clear old selection; try and select next. | 499 // Clear old selection; try and select next. |
373 for (i = 0; i < this.selection_.length; ++i) { | 500 for (i = 0; i < this.selection_.length; ++i) { |
374 adjoining = undefined; | 501 adjoining = undefined; |
375 this.selection_[i].slice.selected = false; | 502 this.selection_[i].slice.selected = false; |
376 var track = this.selection_[i].track; | 503 track = this.selection_[i].track; |
377 var slice = this.selection_[i].slice; | 504 slice = this.selection_[i].slice; |
378 if (slice) { | 505 if (slice) { |
379 if (forwardp) | 506 if (forwardp) |
380 adjoining = track.pickNext(slice); | 507 adjoining = track.pickNext(slice); |
381 else | 508 else |
382 adjoining = track.pickPrevious(slice); | 509 adjoining = track.pickPrevious(slice); |
383 } | 510 } |
384 if (adjoining != undefined) | 511 if (adjoining != undefined) |
385 selection.push({track: track, slice: adjoining}); | 512 selection.push({track: track, slice: adjoining}); |
386 } | 513 } |
387 // Activate the new selection. | 514 // Activate the new selection. |
388 this.selection_ = selection; | 515 this.selection_ = selection; |
389 for (i = 0; i < this.selection_.length; ++i) | 516 for (i = 0; i < this.selection_.length; ++i) |
390 this.selection_[i].slice.selected = true; | 517 this.selection_[i].slice.selected = true; |
391 cr.dispatchSimpleEvent(this, 'selectionChange'); | 518 cr.dispatchSimpleEvent(this, 'selectionChange'); |
392 this.invalidate(); // Cause tracks to redraw. | 519 this.invalidate(); // Cause tracks to redraw. |
393 e.preventDefault(); | 520 e.preventDefault(); |
394 }, | 521 }, |
395 | 522 |
396 get keyHelp() { | 523 get keyHelp() { |
397 return 'Keyboard shortcuts:\n' + | 524 var help = 'Keyboard shortcuts:\n' + |
398 ' w/s : Zoom in/out (with shift: go faster)\n' + | 525 ' w/s : Zoom in/out (with shift: go faster)\n' + |
399 ' a/d : Pan left/right\n' + | 526 ' a/d : Pan left/right\n' + |
400 ' e : Center on mouse\n' + | 527 ' e : Center on mouse\n' + |
401 ' g/G : Shows grid at the start/end of the selected task\n' + | 528 ' g/G : Shows grid at the start/end of the selected task\n'; |
402 ' <-,^TAB : Select previous event on current timeline\n' + | |
403 ' ->, TAB : Select next event on current timeline\n' + | |
404 '\n' + | |
405 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | |
406 | 529 |
530 if (this.parentElement.parentElement.tabIndex) | |
531 help += ' <- : Select previous event on current timeline\n' + | |
532 ' -> : Select next event on current timeline\n'; | |
533 else | |
534 help += ' <-,^TAB : Select previous event on current timeline\n' + | |
535 ' ->, TAB : Select next event on current timeline\n'; | |
407 | 536 |
537 help += | |
538 '\n' + | |
539 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | |
540 return help; | |
408 }, | 541 }, |
409 | 542 |
410 get selection() { | 543 get selection() { |
411 return this.selection_; | 544 return this.selection_; |
412 }, | 545 }, |
413 | 546 |
414 get firstCanvas() { | 547 get firstCanvas() { |
415 return this.tracks_.firstChild ? | 548 return this.tracks_.firstChild ? |
416 this.tracks_.firstChild.firstCanvas : undefined; | 549 this.tracks_.firstChild.firstCanvas : undefined; |
417 }, | 550 }, |
418 | 551 |
419 showDragBox_: function() { | |
420 this.dragBox_.hidden = false; | |
421 }, | |
422 | |
423 hideDragBox_: function() { | 552 hideDragBox_: function() { |
424 this.dragBox_.style.left = '-1000px'; | 553 this.dragBox_.style.left = '-1000px'; |
425 this.dragBox_.style.top = '-1000px'; | 554 this.dragBox_.style.top = '-1000px'; |
426 this.dragBox_.style.width = 0; | 555 this.dragBox_.style.width = 0; |
427 this.dragBox_.style.height = 0; | 556 this.dragBox_.style.height = 0; |
428 this.dragBox_.hidden = true; | |
429 }, | |
430 | |
431 get dragBoxVisible_() { | |
432 return this.dragBox_.hidden == false; | |
433 }, | 557 }, |
434 | 558 |
435 setDragBoxPosition_: function(eDown, eCur) { | 559 setDragBoxPosition_: function(eDown, eCur) { |
436 var loX = Math.min(eDown.clientX, eCur.clientX); | 560 var loX = Math.min(eDown.clientX, eCur.clientX); |
437 var hiX = Math.max(eDown.clientX, eCur.clientX); | 561 var hiX = Math.max(eDown.clientX, eCur.clientX); |
438 var loY = Math.min(eDown.clientY, eCur.clientY); | 562 var loY = Math.min(eDown.clientY, eCur.clientY); |
439 var hiY = Math.max(eDown.clientY, eCur.clientY); | 563 var hiY = Math.max(eDown.clientY, eCur.clientY); |
440 | 564 |
441 this.dragBox_.style.left = loX + 'px'; | 565 this.dragBox_.style.left = loX + 'px'; |
442 this.dragBox_.style.top = loY + 'px'; | 566 this.dragBox_.style.top = loY + 'px'; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 var canv = this.firstCanvas; | 607 var canv = this.firstCanvas; |
484 var pos = { | 608 var pos = { |
485 x: e.clientX - canv.offsetLeft, | 609 x: e.clientX - canv.offsetLeft, |
486 y: e.clientY - canv.offsetTop | 610 y: e.clientY - canv.offsetTop |
487 }; | 611 }; |
488 | 612 |
489 var wX = this.viewport_.xViewToWorld(pos.x); | 613 var wX = this.viewport_.xViewToWorld(pos.x); |
490 | 614 |
491 this.dragBeginEvent_ = e; | 615 this.dragBeginEvent_ = e; |
492 e.preventDefault(); | 616 e.preventDefault(); |
617 if (this.parentElement.parentElement.tabIndex) | |
618 this.parentElement.parentElement.focus(); | |
493 }, | 619 }, |
494 | 620 |
495 onMouseMove_: function(e) { | 621 onMouseMove_: function(e) { |
496 if (!this.firstCanvas) | 622 if (!this.firstCanvas) |
497 return; | 623 return; |
498 var canv = this.firstCanvas; | 624 var canv = this.firstCanvas; |
499 var pos = { | 625 var pos = { |
500 x: e.clientX - canv.offsetLeft, | 626 x: e.clientX - canv.offsetLeft, |
501 y: e.clientY - canv.offsetTop | 627 y: e.clientY - canv.offsetTop |
502 }; | 628 }; |
503 | 629 |
504 // Remember position. Used during keyboard zooming. | 630 // Remember position. Used during keyboard zooming. |
505 this.lastMouseViewPos_ = pos; | 631 this.lastMouseViewPos_ = pos; |
506 | 632 |
507 // Initiate the drag box if needed. | |
508 if (this.dragBeginEvent_ && !this.dragBoxVisible_) { | |
509 this.showDragBox_(); | |
510 this.setDragBoxPosition_(e, e); | |
511 } | |
512 | |
513 // Update the drag box | 633 // Update the drag box |
514 if (this.dragBeginEvent_) { | 634 if (this.dragBeginEvent_) { |
515 this.setDragBoxPosition_(this.dragBeginEvent_, e); | 635 this.setDragBoxPosition_(this.dragBeginEvent_, e); |
516 } | 636 } |
517 }, | 637 }, |
518 | 638 |
519 onMouseUp_: function(e) { | 639 onMouseUp_: function(e) { |
520 var i; | 640 var i; |
521 if (this.dragBeginEvent_) { | 641 if (this.dragBeginEvent_) { |
522 // Stop the dragging. | 642 // Stop the dragging. |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
565 this.invalidate(); // Cause tracks to redraw. | 685 this.invalidate(); // Cause tracks to redraw. |
566 } | 686 } |
567 }, | 687 }, |
568 | 688 |
569 onDblClick_: function(e) { | 689 onDblClick_: function(e) { |
570 var scale = 4; | 690 var scale = 4; |
571 if (e.shiftKey) | 691 if (e.shiftKey) |
572 scale = 1 / scale; | 692 scale = 1 / scale; |
573 this.zoomBy_(scale); | 693 this.zoomBy_(scale); |
574 e.preventDefault(); | 694 e.preventDefault(); |
575 }, | 695 } |
James Hawkins
2011/11/05 17:51:37
Optional: It's generally safer to leave the traili
nduca
2011/11/09 22:52:19
Done.
| |
576 }; | 696 }; |
577 | 697 |
578 /** | 698 /** |
579 * The TimelineModel being viewed by the timeline | 699 * The TimelineModel being viewed by the timeline |
580 * @type {TimelineModel} | 700 * @type {TimelineModel} |
581 */ | 701 */ |
582 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); | 702 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); |
583 | 703 |
584 return { | 704 return { |
585 Timeline: Timeline | 705 Timeline: Timeline |
586 }; | 706 }; |
587 }); | 707 }); |
OLD | NEW |