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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui_lazy/TimelineOverviewPane.js

Issue 2623743002: DevTools: extract modules (non-extensions) (Closed)
Patch Set: rebaseline Created 3 years, 11 months 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
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * @unrestricted
33 */
34 UI.TimelineOverviewPane = class extends UI.VBox {
35 /**
36 * @param {string} prefix
37 */
38 constructor(prefix) {
39 super();
40 this.element.id = prefix + '-overview-pane';
41
42 this._overviewCalculator = new UI.TimelineOverviewCalculator();
43 this._overviewGrid = new UI.OverviewGrid(prefix);
44 this.element.appendChild(this._overviewGrid.element);
45 this._cursorArea = this._overviewGrid.element.createChild('div', 'overview-g rid-cursor-area');
46 this._cursorElement = this._overviewGrid.element.createChild('div', 'overvie w-grid-cursor-position');
47 this._cursorArea.addEventListener('mousemove', this._onMouseMove.bind(this), true);
48 this._cursorArea.addEventListener('mouseleave', this._hideCursor.bind(this), true);
49
50 this._overviewGrid.setResizeEnabled(false);
51 this._overviewGrid.addEventListener(UI.OverviewGrid.Events.WindowChanged, th is._onWindowChanged, this);
52 this._overviewGrid.setClickHandler(this._onClick.bind(this));
53 this._overviewControls = [];
54 this._markers = new Map();
55
56 this._popoverHelper = new UI.PopoverHelper(this._cursorArea);
57 this._popoverHelper.initializeCallbacks(
58 this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._o nHidePopover.bind(this));
59 this._popoverHelper.setTimeout(0);
60
61 this._updateThrottler = new Common.Throttler(100);
62
63 this._cursorEnabled = false;
64 this._cursorPosition = 0;
65 this._lastWidth = 0;
66 }
67
68 /**
69 * @param {!Element} element
70 * @param {!Event} event
71 * @return {!Element|!AnchorBox|undefined}
72 */
73 _getPopoverAnchor(element, event) {
74 return this._cursorArea;
75 }
76
77 /**
78 * @param {!Element} anchor
79 * @param {!UI.Popover} popover
80 */
81 _showPopover(anchor, popover) {
82 this._buildPopoverContents().then(maybeShowPopover.bind(this));
83 /**
84 * @this {UI.TimelineOverviewPane}
85 * @param {!DocumentFragment} fragment
86 */
87 function maybeShowPopover(fragment) {
88 if (!fragment.firstChild)
89 return;
90 var content = new UI.TimelineOverviewPane.PopoverContents();
91 this._popoverContents = content.contentElement.createChild('div');
92 this._popoverContents.appendChild(fragment);
93 this._popover = popover;
94 popover.showView(content, this._cursorElement);
95 }
96 }
97
98 _onHidePopover() {
99 this._popover = null;
100 this._popoverContents = null;
101 }
102
103 /**
104 * @param {!Event} event
105 */
106 _onMouseMove(event) {
107 if (!this._cursorEnabled)
108 return;
109 this._cursorPosition = event.offsetX + event.target.offsetLeft;
110 this._cursorElement.style.left = this._cursorPosition + 'px';
111 this._cursorElement.style.visibility = 'visible';
112 if (!this._popover)
113 return;
114 this._buildPopoverContents().then(updatePopover.bind(this));
115 this._popover.positionElement(this._cursorElement);
116
117 /**
118 * @param {!DocumentFragment} fragment
119 * @this {UI.TimelineOverviewPane}
120 */
121 function updatePopover(fragment) {
122 if (!this._popoverContents)
123 return;
124 this._popoverContents.removeChildren();
125 this._popoverContents.appendChild(fragment);
126 }
127 }
128
129 /**
130 * @return {!Promise<!DocumentFragment>}
131 */
132 _buildPopoverContents() {
133 var document = this.element.ownerDocument;
134 var x = this._cursorPosition;
135 var promises = this._overviewControls.map(control => control.popoverElementP romise(x));
136 return Promise.all(promises).then(buildFragment);
137
138 /**
139 * @param {!Array<?Element>} elements
140 * @return {!DocumentFragment}
141 */
142 function buildFragment(elements) {
143 var fragment = document.createDocumentFragment();
144 elements.remove(null);
145 fragment.appendChildren.apply(fragment, elements);
146 return fragment;
147 }
148 }
149
150 _hideCursor() {
151 this._cursorElement.style.visibility = 'hidden';
152 }
153
154 /**
155 * @override
156 */
157 wasShown() {
158 this._update();
159 }
160
161 /**
162 * @override
163 */
164 willHide() {
165 this._popoverHelper.hidePopover();
166 }
167
168 /**
169 * @override
170 */
171 onResize() {
172 var width = this.element.offsetWidth;
173 if (width === this._lastWidth)
174 return;
175 this._lastWidth = width;
176 this.scheduleUpdate();
177 }
178
179 /**
180 * @param {!Array.<!UI.TimelineOverview>} overviewControls
181 */
182 setOverviewControls(overviewControls) {
183 for (var i = 0; i < this._overviewControls.length; ++i)
184 this._overviewControls[i].dispose();
185
186 for (var i = 0; i < overviewControls.length; ++i) {
187 overviewControls[i].setCalculator(this._overviewCalculator);
188 overviewControls[i].show(this._overviewGrid.element);
189 }
190 this._overviewControls = overviewControls;
191 this._update();
192 }
193
194 /**
195 * @param {number} minimumBoundary
196 * @param {number} maximumBoundary
197 */
198 setBounds(minimumBoundary, maximumBoundary) {
199 this._overviewCalculator.setBounds(minimumBoundary, maximumBoundary);
200 this._overviewGrid.setResizeEnabled(true);
201 this._cursorEnabled = true;
202 }
203
204 scheduleUpdate() {
205 this._updateThrottler.schedule(process.bind(this));
206 /**
207 * @this {UI.TimelineOverviewPane}
208 * @return {!Promise.<undefined>}
209 */
210 function process() {
211 this._update();
212 return Promise.resolve();
213 }
214 }
215
216 _update() {
217 if (!this.isShowing())
218 return;
219 this._overviewCalculator.setDisplayWindow(this._overviewGrid.clientWidth());
220 for (var i = 0; i < this._overviewControls.length; ++i)
221 this._overviewControls[i].update();
222 this._overviewGrid.updateDividers(this._overviewCalculator);
223 this._updateMarkers();
224 this._updateWindow();
225 }
226
227 /**
228 * @param {!Map<number, !Element>} markers
229 */
230 setMarkers(markers) {
231 this._markers = markers;
232 this._updateMarkers();
233 }
234
235 _updateMarkers() {
236 var filteredMarkers = new Map();
237 for (var time of this._markers.keys()) {
238 var marker = this._markers.get(time);
239 var position = Math.round(this._overviewCalculator.computePosition(time));
240 // Limit the number of markers to one per pixel.
241 if (filteredMarkers.has(position))
242 continue;
243 filteredMarkers.set(position, marker);
244 marker.style.left = position + 'px';
245 }
246 this._overviewGrid.removeEventDividers();
247 this._overviewGrid.addEventDividers(filteredMarkers.valuesArray());
248 }
249
250 reset() {
251 this._windowStartTime = 0;
252 this._windowEndTime = Infinity;
253 this._overviewCalculator.reset();
254 this._overviewGrid.reset();
255 this._overviewGrid.setResizeEnabled(false);
256 this._overviewGrid.updateDividers(this._overviewCalculator);
257 this._cursorEnabled = false;
258 this._hideCursor();
259 this._markers = new Map();
260 for (var i = 0; i < this._overviewControls.length; ++i)
261 this._overviewControls[i].reset();
262 this._popoverHelper.hidePopover();
263 this._update();
264 }
265
266 /**
267 * @param {!Event} event
268 * @return {boolean}
269 */
270 _onClick(event) {
271 for (var overviewControl of this._overviewControls) {
272 if (overviewControl.onClick(event))
273 return true;
274 }
275 return false;
276 }
277
278 /**
279 * @param {!Common.Event} event
280 */
281 _onWindowChanged(event) {
282 if (this._muteOnWindowChanged)
283 return;
284 // Always use first control as a time converter.
285 if (!this._overviewControls.length)
286 return;
287 var windowTimes =
288 this._overviewControls[0].windowTimes(this._overviewGrid.windowLeft(), t his._overviewGrid.windowRight());
289 this._windowStartTime = windowTimes.startTime;
290 this._windowEndTime = windowTimes.endTime;
291 this.dispatchEventToListeners(UI.TimelineOverviewPane.Events.WindowChanged, windowTimes);
292 }
293
294 /**
295 * @param {number} startTime
296 * @param {number} endTime
297 */
298 requestWindowTimes(startTime, endTime) {
299 if (startTime === this._windowStartTime && endTime === this._windowEndTime)
300 return;
301 this._windowStartTime = startTime;
302 this._windowEndTime = endTime;
303 this._updateWindow();
304 this.dispatchEventToListeners(
305 UI.TimelineOverviewPane.Events.WindowChanged, {startTime: startTime, end Time: endTime});
306 }
307
308 _updateWindow() {
309 if (!this._overviewControls.length)
310 return;
311 var windowBoundaries = this._overviewControls[0].windowBoundaries(this._wind owStartTime, this._windowEndTime);
312 this._muteOnWindowChanged = true;
313 this._overviewGrid.setWindow(windowBoundaries.left, windowBoundaries.right);
314 this._muteOnWindowChanged = false;
315 }
316 };
317
318 /** @enum {symbol} */
319 UI.TimelineOverviewPane.Events = {
320 WindowChanged: Symbol('WindowChanged')
321 };
322
323 /**
324 * @unrestricted
325 */
326 UI.TimelineOverviewPane.PopoverContents = class extends UI.VBox {
327 constructor() {
328 super(true);
329 this.contentElement.classList.add('timeline-overview-popover');
330 }
331 };
332
333 /**
334 * @implements {UI.TimelineGrid.Calculator}
335 * @unrestricted
336 */
337 UI.TimelineOverviewCalculator = class {
338 constructor() {
339 this.reset();
340 }
341
342 /**
343 * @override
344 * @return {number}
345 */
346 paddingLeft() {
347 return this._paddingLeft;
348 }
349
350 /**
351 * @override
352 * @param {number} time
353 * @return {number}
354 */
355 computePosition(time) {
356 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingA rea + this._paddingLeft;
357 }
358
359 /**
360 * @param {number} position
361 * @return {number}
362 */
363 positionToTime(position) {
364 return (position - this._paddingLeft) / this._workingArea * this.boundarySpa n() + this._minimumBoundary;
365 }
366
367 /**
368 * @param {number} minimumBoundary
369 * @param {number} maximumBoundary
370 */
371 setBounds(minimumBoundary, maximumBoundary) {
372 this._minimumBoundary = minimumBoundary;
373 this._maximumBoundary = maximumBoundary;
374 }
375
376 /**
377 * @param {number} clientWidth
378 * @param {number=} paddingLeft
379 */
380 setDisplayWindow(clientWidth, paddingLeft) {
381 this._paddingLeft = paddingLeft || 0;
382 this._workingArea = clientWidth - this._paddingLeft;
383 }
384
385 reset() {
386 this.setBounds(0, 100);
387 }
388
389 /**
390 * @override
391 * @param {number} value
392 * @param {number=} precision
393 * @return {string}
394 */
395 formatValue(value, precision) {
396 return Number.preciseMillisToString(value - this.zeroTime(), precision);
397 }
398
399 /**
400 * @override
401 * @return {number}
402 */
403 maximumBoundary() {
404 return this._maximumBoundary;
405 }
406
407 /**
408 * @override
409 * @return {number}
410 */
411 minimumBoundary() {
412 return this._minimumBoundary;
413 }
414
415 /**
416 * @override
417 * @return {number}
418 */
419 zeroTime() {
420 return this._minimumBoundary;
421 }
422
423 /**
424 * @override
425 * @return {number}
426 */
427 boundarySpan() {
428 return this._maximumBoundary - this._minimumBoundary;
429 }
430 };
431
432 /**
433 * @interface
434 */
435 UI.TimelineOverview = function() {};
436
437 UI.TimelineOverview.prototype = {
438 /**
439 * @param {!Element} parentElement
440 * @param {?Element=} insertBefore
441 */
442 show(parentElement, insertBefore) {},
443
444 update() {},
445
446 dispose() {},
447
448 reset() {},
449
450 /**
451 * @param {number} x
452 * @return {!Promise<?Element>}
453 */
454 popoverElementPromise(x) {},
455
456 /**
457 * @param {!Event} event
458 * @return {boolean}
459 */
460 onClick(event) {},
461
462 /**
463 * @param {number} windowLeft
464 * @param {number} windowRight
465 * @return {!{startTime: number, endTime: number}}
466 */
467 windowTimes(windowLeft, windowRight) {},
468
469 /**
470 * @param {number} startTime
471 * @param {number} endTime
472 * @return {!{left: number, right: number}}
473 */
474 windowBoundaries(startTime, endTime) {},
475
476 timelineStarted() {},
477
478 timelineStopped() {},
479 };
480
481 /**
482 * @implements {UI.TimelineOverview}
483 * @unrestricted
484 */
485 UI.TimelineOverviewBase = class extends UI.VBox {
486 constructor() {
487 super();
488 /** @type {?UI.TimelineOverviewCalculator} */
489 this._calculator = null;
490 this._canvas = this.element.createChild('canvas', 'fill');
491 this._context = this._canvas.getContext('2d');
492 }
493
494 /** @return {number} */
495 width() {
496 return this._canvas.width;
497 }
498
499 /** @return {number} */
500 height() {
501 return this._canvas.height;
502 }
503
504 /** @return {!CanvasRenderingContext2D} */
505 context() {
506 return this._context;
507 }
508
509 /**
510 * @protected
511 * @return {?UI.TimelineOverviewCalculator}
512 */
513 calculator() {
514 return this._calculator;
515 }
516
517 /**
518 * @override
519 */
520 update() {
521 this.resetCanvas();
522 }
523
524 /**
525 * @override
526 */
527 dispose() {
528 this.detach();
529 }
530
531 /**
532 * @override
533 */
534 reset() {
535 }
536
537 /**
538 * @override
539 * @param {number} x
540 * @return {!Promise<?Element>}
541 */
542 popoverElementPromise(x) {
543 return Promise.resolve(/** @type {?Element} */ (null));
544 }
545
546 /**
547 * @override
548 */
549 timelineStarted() {
550 }
551
552 /**
553 * @override
554 */
555 timelineStopped() {
556 }
557
558 /**
559 * @param {!UI.TimelineOverviewCalculator} calculator
560 */
561 setCalculator(calculator) {
562 this._calculator = calculator;
563 }
564
565 /**
566 * @override
567 * @param {!Event} event
568 * @return {boolean}
569 */
570 onClick(event) {
571 return false;
572 }
573
574 /**
575 * @override
576 * @param {number} windowLeft
577 * @param {number} windowRight
578 * @return {!{startTime: number, endTime: number}}
579 */
580 windowTimes(windowLeft, windowRight) {
581 var absoluteMin = this._calculator.minimumBoundary();
582 var timeSpan = this._calculator.maximumBoundary() - absoluteMin;
583 return {startTime: absoluteMin + timeSpan * windowLeft, endTime: absoluteMin + timeSpan * windowRight};
584 }
585
586 /**
587 * @override
588 * @param {number} startTime
589 * @param {number} endTime
590 * @return {!{left: number, right: number}}
591 */
592 windowBoundaries(startTime, endTime) {
593 var absoluteMin = this._calculator.minimumBoundary();
594 var timeSpan = this._calculator.maximumBoundary() - absoluteMin;
595 var haveRecords = absoluteMin > 0;
596 return {
597 left: haveRecords && startTime ? Math.min((startTime - absoluteMin) / time Span, 1) : 0,
598 right: haveRecords && endTime < Infinity ? (endTime - absoluteMin) / timeS pan : 1
599 };
600 }
601
602 resetCanvas() {
603 this._canvas.width = this.element.clientWidth * window.devicePixelRatio;
604 this._canvas.height = this.element.clientHeight * window.devicePixelRatio;
605 }
606 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698