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