| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 'use strict'; | 5 'use strict'; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * @fileoverview TimelineView visualizes TRACE_EVENT events using the | 8 * @fileoverview TimelineView visualizes TRACE_EVENT events using the |
| 9 * tracing.Timeline component and adds in selection summary and control buttons. | 9 * tracing.Timeline component and adds in selection summary and control buttons. |
| 10 */ | 10 */ |
| 11 cr.define('tracing', function() { | 11 cr.define('tracing', function() { |
| 12 function tsRound(ts) { | |
| 13 return Math.round(ts * 1000.0) / 1000.0; | |
| 14 } | |
| 15 function getPadding(text, width) { | |
| 16 width = width || 0; | |
| 17 | |
| 18 if (typeof text != 'string') | |
| 19 text = String(text); | |
| 20 | |
| 21 if (text.length >= width) | |
| 22 return ''; | |
| 23 | |
| 24 var pad = ''; | |
| 25 for (var i = 0; i < width - text.length; i++) | |
| 26 pad += ' '; | |
| 27 return pad; | |
| 28 } | |
| 29 | |
| 30 function leftAlign(text, width) { | |
| 31 return text + getPadding(text, width); | |
| 32 } | |
| 33 | |
| 34 function rightAlign(text, width) { | |
| 35 return getPadding(text, width) + text; | |
| 36 } | |
| 37 | |
| 38 /** | 12 /** |
| 39 * TimelineFindControl | 13 * TimelineFindControl |
| 40 * @constructor | 14 * @constructor |
| 41 * @extends {tracing.Overlay} | 15 * @extends {tracing.Overlay} |
| 42 */ | 16 */ |
| 43 var TimelineFindControl = cr.ui.define('div'); | 17 var TimelineFindControl = cr.ui.define('div'); |
| 44 | 18 |
| 45 TimelineFindControl.prototype = { | 19 TimelineFindControl.prototype = { |
| 46 __proto__: tracing.Overlay.prototype, | 20 __proto__: tracing.Overlay.prototype, |
| 47 | 21 |
| 48 decorate: function() { | 22 decorate: function() { |
| 49 tracing.Overlay.prototype.decorate.call(this); | 23 tracing.Overlay.prototype.decorate.call(this); |
| 50 | 24 |
| 51 this.className = 'timeline-find-control'; | 25 this.className = 'timeline-find-control'; |
| 52 | 26 |
| 53 this.hitCountEl_ = document.createElement('span'); | 27 this.hitCountEl_ = document.createElement('div'); |
| 54 this.hitCountEl_.className = 'hit-count-label'; | 28 this.hitCountEl_.className = 'hit-count-label'; |
| 55 this.hitCountEl_.textContent = '1 of 7'; | 29 this.hitCountEl_.textContent = '1 of 7'; |
| 56 | 30 |
| 57 var findPreviousBn = document.createElement('span'); | 31 var findPreviousBn = document.createElement('div'); |
| 58 findPreviousBn.className = 'find-button find-previous'; | 32 findPreviousBn.className = 'timeline-button find-previous'; |
| 59 findPreviousBn.textContent = '\u2190'; | 33 findPreviousBn.textContent = '\u2190'; |
| 60 findPreviousBn.addEventListener('click', function() { | 34 findPreviousBn.addEventListener('click', function() { |
| 61 this.controller.findPrevious(); | 35 this.controller.findPrevious(); |
| 62 this.updateHitCountEl_(); | 36 this.updateHitCountEl_(); |
| 63 }.bind(this)); | 37 }.bind(this)); |
| 64 | 38 |
| 65 var findNextBn = document.createElement('span'); | 39 var findNextBn = document.createElement('div'); |
| 66 findNextBn.className = 'find-button find-next'; | 40 findNextBn.className = 'timeline-button find-next'; |
| 67 findNextBn.textContent = '\u2192'; | 41 findNextBn.textContent = '\u2192'; |
| 68 findNextBn.addEventListener('click', function() { | 42 findNextBn.addEventListener('click', function() { |
| 69 this.controller.findNext(); | 43 this.controller.findNext(); |
| 70 this.updateHitCountEl_(); | 44 this.updateHitCountEl_(); |
| 71 }.bind(this)); | 45 }.bind(this)); |
| 72 | 46 |
| 73 // Filter input element. | 47 // Filter input element. |
| 74 this.filterEl_ = document.createElement('input'); | 48 this.filterEl_ = document.createElement('input'); |
| 75 this.filterEl_.type = 'input'; | 49 this.filterEl_.type = 'input'; |
| 76 | 50 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 this.hitCountEl_.textContent = '0 of 0'; | 106 this.hitCountEl_.textContent = '0 of 0'; |
| 133 else | 107 else |
| 134 this.hitCountEl_.textContent = (i + 1) + ' of ' + n; | 108 this.hitCountEl_.textContent = (i + 1) + ' of ' + n; |
| 135 } | 109 } |
| 136 }; | 110 }; |
| 137 | 111 |
| 138 function TimelineFindController() { | 112 function TimelineFindController() { |
| 139 this.timeline_ = undefined; | 113 this.timeline_ = undefined; |
| 140 this.model_ = undefined; | 114 this.model_ = undefined; |
| 141 this.filterText_ = ''; | 115 this.filterText_ = ''; |
| 116 this.filterHits_ = new tracing.TimelineSelection(); |
| 142 this.filterHitsDirty_ = true; | 117 this.filterHitsDirty_ = true; |
| 143 this.currentHitIndex_ = 0; | 118 this.currentHitIndex_ = 0; |
| 144 }; | 119 }; |
| 145 | 120 |
| 146 TimelineFindController.prototype = { | 121 TimelineFindController.prototype = { |
| 147 __proto__: Object.prototype, | 122 __proto__: Object.prototype, |
| 148 | 123 |
| 149 get timeline() { | 124 get timeline() { |
| 150 return this.timeline_; | 125 return this.timeline_; |
| 151 }, | 126 }, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 165 this.filterText_ = f; | 140 this.filterText_ = f; |
| 166 this.filterHitsDirty_ = true; | 141 this.filterHitsDirty_ = true; |
| 167 this.findNext(); | 142 this.findNext(); |
| 168 }, | 143 }, |
| 169 | 144 |
| 170 get filterHits() { | 145 get filterHits() { |
| 171 if (this.filterHitsDirty_) { | 146 if (this.filterHitsDirty_) { |
| 172 this.filterHitsDirty_ = false; | 147 this.filterHitsDirty_ = false; |
| 173 if (this.timeline_) { | 148 if (this.timeline_) { |
| 174 var filter = new tracing.TimelineFilter(this.filterText); | 149 var filter = new tracing.TimelineFilter(this.filterText); |
| 175 this.filterHits_ = this.timeline.findAllObjectsMatchingFilter(filter); | 150 this.filterHits_.clear(); |
| 151 this.timeline.addAllObjectsMatchingFilterToSelection( |
| 152 filter, this.filterHits_); |
| 176 this.currentHitIndex_ = this.filterHits_.length - 1; | 153 this.currentHitIndex_ = this.filterHits_.length - 1; |
| 177 } else { | 154 } else { |
| 178 this.filterHits_ = []; | 155 this.filterHits_.clear(); |
| 179 this.currentHitIndex_ = 0; | 156 this.currentHitIndex_ = 0; |
| 180 } | 157 } |
| 181 } | 158 } |
| 182 return this.filterHits_; | 159 return this.filterHits_; |
| 183 }, | 160 }, |
| 184 | 161 |
| 185 get currentHitIndex() { | 162 get currentHitIndex() { |
| 186 return this.currentHitIndex_; | 163 return this.currentHitIndex_; |
| 187 }, | 164 }, |
| 188 | 165 |
| 189 find_: function(dir) { | 166 find_: function(dir) { |
| 190 if (!this.timeline) | 167 if (!this.timeline) |
| 191 return; | 168 return; |
| 192 | 169 |
| 193 var N = this.filterHits.length; | 170 var N = this.filterHits.length; |
| 194 this.currentHitIndex_ = this.currentHitIndex_ + dir; | 171 this.currentHitIndex_ = this.currentHitIndex_ + dir; |
| 195 | 172 |
| 196 if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1; | 173 if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1; |
| 197 if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0; | 174 if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0; |
| 198 | 175 |
| 199 if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) { | 176 if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) { |
| 200 this.timeline.selection = []; | 177 this.timeline.selection = new tracing.TimelineSelection(); |
| 201 return; | 178 return; |
| 202 } | 179 } |
| 203 | 180 |
| 204 var hit = this.filterHits[this.currentHitIndex_]; | |
| 205 | |
| 206 // We allow the zoom level to change on the first hit level. But, when | 181 // We allow the zoom level to change on the first hit level. But, when |
| 207 // then cycling through subsequent changes, restrict it to panning. | 182 // then cycling through subsequent changes, restrict it to panning. |
| 208 var zoomAllowed = this.currentHitIndex_ == 0; | 183 var zoomAllowed = this.currentHitIndex_ == 0; |
| 209 this.timeline.setSelectionAndMakeVisible([hit], zoomAllowed); | 184 var subSelection = this.filterHits.subSelection(this.currentHitIndex_); |
| 185 this.timeline.setSelectionAndMakeVisible(subSelection, zoomAllowed); |
| 210 }, | 186 }, |
| 211 | 187 |
| 212 findNext: function() { | 188 findNext: function() { |
| 213 this.find_(1); | 189 this.find_(1); |
| 214 }, | 190 }, |
| 215 | 191 |
| 216 findPrevious: function() { | 192 findPrevious: function() { |
| 217 this.find_(-1); | 193 this.find_(-1); |
| 218 }, | 194 }, |
| 219 }; | 195 }; |
| 220 | 196 |
| 221 /** | 197 /** |
| 222 * TimelineView | 198 * TimelineView |
| 223 * @constructor | 199 * @constructor |
| 224 * @extends {HTMLDivElement} | 200 * @extends {HTMLDivElement} |
| 225 */ | 201 */ |
| 226 var TimelineView = cr.ui.define('div'); | 202 var TimelineView = cr.ui.define('div'); |
| 227 | 203 |
| 228 TimelineView.prototype = { | 204 TimelineView.prototype = { |
| 229 __proto__: HTMLDivElement.prototype, | 205 __proto__: HTMLDivElement.prototype, |
| 230 | 206 |
| 231 decorate: function() { | 207 decorate: function() { |
| 232 this.classList.add('timeline-view'); | 208 this.classList.add('timeline-view'); |
| 233 | 209 |
| 234 // Create individual elements. | 210 // Create individual elements. |
| 235 this.titleEl_ = document.createElement('span'); | 211 this.titleEl_ = document.createElement('div'); |
| 236 this.titleEl_.textContent = 'Tracing: '; | 212 this.titleEl_.textContent = 'Tracing: '; |
| 237 | 213 |
| 238 this.controlDiv_ = document.createElement('div'); | 214 this.controlDiv_ = document.createElement('div'); |
| 239 this.controlDiv_.className = 'control'; | 215 this.controlDiv_.className = 'control'; |
| 240 | 216 |
| 241 this.leftControlsEl_ = document.createElement('div'); | 217 this.leftControlsEl_ = document.createElement('div'); |
| 218 this.leftControlsEl_.className = 'controls'; |
| 242 this.rightControlsEl_ = document.createElement('div'); | 219 this.rightControlsEl_ = document.createElement('div'); |
| 220 this.rightControlsEl_.className = 'controls'; |
| 243 | 221 |
| 244 var spacingEl = document.createElement('div'); | 222 var spacingEl = document.createElement('div'); |
| 245 spacingEl.className = 'spacer'; | 223 spacingEl.className = 'spacer'; |
| 246 | 224 |
| 247 this.timelineContainer_ = document.createElement('div'); | 225 this.timelineContainer_ = document.createElement('div'); |
| 248 this.timelineContainer_.className = 'timeline-container'; | 226 this.timelineContainer_.className = 'timeline-container'; |
| 249 | 227 |
| 250 var summaryContainer_ = document.createElement('div'); | 228 var analysisContainer_ = document.createElement('div'); |
| 251 summaryContainer_.className = 'summary-container'; | 229 analysisContainer_.className = 'analysis-container'; |
| 252 | 230 |
| 253 this.summaryEl_ = document.createElement('pre'); | 231 this.analysisEl_ = new tracing.TimelineAnalysisView(); |
| 254 this.summaryEl_.className = 'summary'; | |
| 255 | 232 |
| 256 this.findCtl_ = new TimelineFindControl(); | 233 this.findCtl_ = new TimelineFindControl(); |
| 257 this.findCtl_.controller = new TimelineFindController(); | 234 this.findCtl_.controller = new TimelineFindController(); |
| 258 | 235 |
| 259 // Connect everything up. | 236 // Connect everything up. |
| 260 this.rightControls.appendChild(this.findCtl_); | 237 this.rightControls.appendChild(this.findCtl_); |
| 261 this.controlDiv_.appendChild(this.titleEl_); | 238 this.controlDiv_.appendChild(this.titleEl_); |
| 262 this.controlDiv_.appendChild(this.leftControlsEl_); | 239 this.controlDiv_.appendChild(this.leftControlsEl_); |
| 263 this.controlDiv_.appendChild(spacingEl); | 240 this.controlDiv_.appendChild(spacingEl); |
| 264 this.controlDiv_.appendChild(this.rightControlsEl_); | 241 this.controlDiv_.appendChild(this.rightControlsEl_); |
| 265 this.appendChild(this.controlDiv_); | 242 this.appendChild(this.controlDiv_); |
| 266 | 243 |
| 267 this.appendChild(this.timelineContainer_); | 244 this.appendChild(this.timelineContainer_); |
| 268 | 245 |
| 269 summaryContainer_.appendChild(this.summaryEl_); | 246 analysisContainer_.appendChild(this.analysisEl_); |
| 270 this.appendChild(summaryContainer_); | 247 this.appendChild(analysisContainer_); |
| 248 |
| 249 this.rightControls.appendChild(this.createHelpButton_()); |
| 271 | 250 |
| 272 // Bookkeeping. | 251 // Bookkeeping. |
| 273 this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this); | 252 this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this); |
| 274 document.addEventListener('keypress', this.onKeypress_.bind(this), true); | 253 document.addEventListener('keypress', this.onKeypress_.bind(this), true); |
| 275 }, | 254 }, |
| 276 | 255 |
| 256 createHelpButton_: function() { |
| 257 var dlg = new tracing.Overlay(); |
| 258 dlg.classList.add('timeline-view-help-overlay'); |
| 259 |
| 260 var showHelpEl = document.createElement('div'); |
| 261 showHelpEl.className = 'timeline-button timeline-view-help-button'; |
| 262 showHelpEl.textContent = '?'; |
| 263 |
| 264 var helpTextEl = document.createElement('div'); |
| 265 helpTextEl.style.whiteSpace = 'pre'; |
| 266 helpTextEl.style.fontFamily = 'monospace'; |
| 267 |
| 268 function onClick() { |
| 269 dlg.visible = true; |
| 270 helpTextEl.textContent = this.timeline_.keyHelp; |
| 271 document.addEventListener('keydown', onKey, true); |
| 272 } |
| 273 |
| 274 function onKey(e) { |
| 275 if (!dlg.visible) |
| 276 return; |
| 277 |
| 278 if (e.keyCode == 27 || e.keyCode == '?'.charCodeAt(0)) { |
| 279 e.preventDefault(); |
| 280 document.removeEventListener('keydown', onKey); |
| 281 dlg.visible = false; |
| 282 } |
| 283 } |
| 284 showHelpEl.addEventListener('click', onClick.bind(this)); |
| 285 |
| 286 dlg.appendChild(helpTextEl); |
| 287 |
| 288 return showHelpEl; |
| 289 }, |
| 290 |
| 277 get leftControls() { | 291 get leftControls() { |
| 278 return this.leftControlsEl_; | 292 return this.leftControlsEl_; |
| 279 }, | 293 }, |
| 280 | 294 |
| 281 get rightControls() { | 295 get rightControls() { |
| 282 return this.rightControlsEl_; | 296 return this.rightControlsEl_; |
| 283 }, | 297 }, |
| 284 | 298 |
| 285 get title() { | 299 get title() { |
| 286 return this.titleEl_.textContent.substring( | 300 return this.titleEl_.textContent.substring( |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 return true; | 382 return true; |
| 369 if (this.focusElement.tabIndex >= 0) | 383 if (this.focusElement.tabIndex >= 0) |
| 370 return document.activeElement == this.focusElement; | 384 return document.activeElement == this.focusElement; |
| 371 return true; | 385 return true; |
| 372 }, | 386 }, |
| 373 | 387 |
| 374 onKeypress_: function(e) { | 388 onKeypress_: function(e) { |
| 375 if (!this.listenToKeys_) | 389 if (!this.listenToKeys_) |
| 376 return; | 390 return; |
| 377 | 391 |
| 378 if (event.keyCode == 47) { | 392 if (event.keyCode == '/'.charCodeAt(0)) { // / key |
| 379 this.findCtl_.focus(); | 393 this.findCtl_.focus(); |
| 380 event.preventDefault(); | 394 event.preventDefault(); |
| 381 return; | 395 return; |
| 396 } else if (e.keyCode == '?'.charCodeAt(0)) { |
| 397 this.querySelector('.timeline-view-help-button').click(); |
| 398 e.preventDefault(); |
| 382 } | 399 } |
| 383 }, | 400 }, |
| 384 | 401 |
| 385 beginFind: function() { | 402 beginFind: function() { |
| 386 if (this.findInProgress_) | 403 if (this.findInProgress_) |
| 387 return; | 404 return; |
| 388 this.findInProgress_ = true; | 405 this.findInProgress_ = true; |
| 389 var dlg = TimelineFindControl(); | 406 var dlg = TimelineFindControl(); |
| 390 dlg.controller = new TimelineFindController(); | 407 dlg.controller = new TimelineFindController(); |
| 391 dlg.controller.timeline = this.timeline; | 408 dlg.controller.timeline = this.timeline; |
| 392 dlg.visible = true; | 409 dlg.visible = true; |
| 393 dlg.addEventListener('close', function() { | 410 dlg.addEventListener('close', function() { |
| 394 this.findInProgress_ = false; | 411 this.findInProgress_ = false; |
| 395 }.bind(this)); | 412 }.bind(this)); |
| 396 dlg.addEventListener('findNext', function() { | 413 dlg.addEventListener('findNext', function() { |
| 397 }); | 414 }); |
| 398 dlg.addEventListener('findPrevious', function() { | 415 dlg.addEventListener('findPrevious', function() { |
| 399 }); | 416 }); |
| 400 }, | 417 }, |
| 401 | 418 |
| 402 onSelectionChanged_: function(e) { | 419 onSelectionChanged_: function(e) { |
| 403 var timeline = this.timeline_; | |
| 404 var selection = timeline.selection; | |
| 405 if (!selection.length) { | |
| 406 var oldScrollTop = this.timelineContainer_.scrollTop; | |
| 407 this.summaryEl_.textContent = timeline.keyHelp; | |
| 408 this.timelineContainer_.scrollTop = oldScrollTop; | |
| 409 return; | |
| 410 } | |
| 411 | |
| 412 var text = ''; | |
| 413 if (selection.length == 1) { | |
| 414 var c0Width = 14; | |
| 415 var slice = selection[0].slice; | |
| 416 text = 'Selected item:\n'; | |
| 417 text += leftAlign('Title', c0Width) + ': ' + slice.title + '\n'; | |
| 418 text += leftAlign('Start', c0Width) + ': ' + | |
| 419 tsRound(slice.start) + ' ms\n'; | |
| 420 text += leftAlign('Duration', c0Width) + ': ' + | |
| 421 tsRound(slice.duration) + ' ms\n'; | |
| 422 if (slice.durationInUserTime) | |
| 423 text += leftAlign('Duration (U)', c0Width) + ': ' + | |
| 424 tsRound(slice.durationInUserTime) + ' ms\n'; | |
| 425 | |
| 426 var n = 0; | |
| 427 for (var argName in slice.args) { | |
| 428 n += 1; | |
| 429 } | |
| 430 if (n > 0) { | |
| 431 text += leftAlign('Args', c0Width) + ':\n'; | |
| 432 for (var argName in slice.args) { | |
| 433 var argVal = slice.args[argName]; | |
| 434 text += leftAlign(' ' + argName, c0Width) + ': ' + argVal + '\n'; | |
| 435 } | |
| 436 } | |
| 437 } else { | |
| 438 var c0Width = 55; | |
| 439 var c1Width = 12; | |
| 440 var c2Width = 5; | |
| 441 text = 'Selection summary:\n'; | |
| 442 var tsLo = Math.min.apply(Math, selection.map( | |
| 443 function(s) {return s.slice.start;})); | |
| 444 var tsHi = Math.max.apply(Math, selection.map( | |
| 445 function(s) {return s.slice.end;})); | |
| 446 | |
| 447 // compute total selection duration | |
| 448 var titles = selection.map(function(i) { return i.slice.title; }); | |
| 449 | |
| 450 var slicesByTitle = {}; | |
| 451 for (var i = 0; i < selection.length; i++) { | |
| 452 var slice = selection[i].slice; | |
| 453 if (!slicesByTitle[slice.title]) | |
| 454 slicesByTitle[slice.title] = { | |
| 455 slices: [] | |
| 456 }; | |
| 457 slicesByTitle[slice.title].slices.push(slice); | |
| 458 } | |
| 459 var totalDuration = 0; | |
| 460 for (var sliceGroupTitle in slicesByTitle) { | |
| 461 var sliceGroup = slicesByTitle[sliceGroupTitle]; | |
| 462 var duration = 0; | |
| 463 for (i = 0; i < sliceGroup.slices.length; i++) | |
| 464 duration += sliceGroup.slices[i].duration; | |
| 465 totalDuration += duration; | |
| 466 | |
| 467 text += ' ' + | |
| 468 leftAlign(sliceGroupTitle, c0Width) + ': ' + | |
| 469 rightAlign(tsRound(duration) + 'ms', c1Width) + ' ' + | |
| 470 rightAlign(String(sliceGroup.slices.length), c2Width) + | |
| 471 ' occurrences' + '\n'; | |
| 472 } | |
| 473 | |
| 474 text += leftAlign('*Totals', c0Width) + ' : ' + | |
| 475 rightAlign(tsRound(totalDuration) + 'ms', c1Width) + ' ' + | |
| 476 rightAlign(String(selection.length), c2Width) + ' occurrences' + | |
| 477 '\n'; | |
| 478 | |
| 479 text += '\n'; | |
| 480 | |
| 481 text += leftAlign('Selection start', c0Width) + ' : ' + | |
| 482 rightAlign(tsRound(tsLo) + 'ms', c1Width) + | |
| 483 '\n'; | |
| 484 text += leftAlign('Selection extent', c0Width) + ' : ' + | |
| 485 rightAlign(tsRound(tsHi - tsLo) + 'ms', c1Width) + | |
| 486 '\n'; | |
| 487 } | |
| 488 | |
| 489 // done | |
| 490 var oldScrollTop = this.timelineContainer_.scrollTop; | 420 var oldScrollTop = this.timelineContainer_.scrollTop; |
| 491 this.summaryEl_.textContent = text; | 421 this.analysisEl_.selection = this.timeline_.selection; |
| 492 this.timelineContainer_.scrollTop = oldScrollTop; | 422 this.timelineContainer_.scrollTop = oldScrollTop; |
| 493 } | 423 } |
| 494 }; | 424 }; |
| 495 | 425 |
| 496 return { | 426 return { |
| 497 TimelineFindControl: TimelineFindControl, | 427 TimelineFindControl: TimelineFindControl, |
| 498 TimelineFindController: TimelineFindController, | 428 TimelineFindController: TimelineFindController, |
| 499 TimelineView: TimelineView | 429 TimelineView: TimelineView |
| 500 }; | 430 }; |
| 501 }); | 431 }); |
| OLD | NEW |