1<!DOCTYPE html> 2<!-- 3Copyright (c) 2015 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/event_target.html"> 9<link rel="import" href="/tracing/base/task.html"> 10<link rel="import" href="/tracing/ui/brushing_state.html"> 11<link rel="import" href="/tracing/ui/timeline_viewport.html"> 12<link rel="import" href="/tracing/ui/base/ui_state.html"> 13<link rel="import" href="/tracing/model/event_set.html"> 14<link rel="import" href="/tracing/model/selection_state.html"> 15 16<script> 17'use strict'; 18 19tr.exportTo('tr.c', function() { 20 var BrushingState = tr.ui.b.BrushingState; 21 var EventSet = tr.model.EventSet; 22 var SelectionState = tr.model.SelectionState; 23 var Viewport = tr.ui.TimelineViewport; 24 25 function BrushingStateController(timelineView) { 26 tr.b.EventTarget.call(this); 27 28 this.timelineView_ = timelineView; 29 this.currentBrushingState_ = new BrushingState(); 30 31 this.onPopState_ = this.onPopState_.bind(this); 32 this.historyEnabled_ = false; 33 this.selections_ = {}; 34 } 35 36 BrushingStateController.prototype = { 37 __proto__: tr.b.EventTarget.prototype, 38 39 dispatchChangeEvent_: function() { 40 var e = new tr.b.Event('change', false, false); 41 this.dispatchEvent(e); 42 }, 43 44 get model() { 45 if (!this.timelineView_) 46 return undefined; 47 return this.timelineView_.model; 48 }, 49 50 get trackView() { 51 if (!this.timelineView_) 52 return undefined; 53 return this.timelineView_.trackView; 54 }, 55 56 get viewport() { 57 if (!this.timelineView_) 58 return undefined; 59 if (!this.timelineView_.trackView) 60 return undefined; 61 return this.timelineView_.trackView.viewport; 62 }, 63 64 /* History system */ 65 get historyEnabled() { 66 return this.historyEnabled_; 67 }, 68 69 set historyEnabled(historyEnabled) { 70 this.historyEnabled_ = !!historyEnabled; 71 if (historyEnabled) 72 window.addEventListener('popstate', this.onPopState_); 73 else 74 window.removeEventListener('popstate', this.onPopState_); 75 }, 76 77 modelWillChange: function() { 78 if (this.currentBrushingState_.isAppliedToModel) 79 this.currentBrushingState_.unapplyFromModelSelectionState(); 80 }, 81 82 modelDidChange: function() { 83 this.selections_ = {}; 84 85 this.currentBrushingState_ = new BrushingState(); 86 this.currentBrushingState_.applyToModelSelectionState(this.model); 87 88 var e = new tr.b.Event('model-changed', false, false); 89 this.dispatchEvent(e); 90 91 this.dispatchChangeEvent_(); 92 }, 93 94 onUserInitiatedSelectionChange_: function() { 95 var selection = this.selection; 96 if (this.historyEnabled) { 97 // Save the selection so that when back button is pressed, 98 // it could be retrieved. 99 this.selections_[selection.guid] = selection; 100 var state = { 101 selection_guid: selection.guid 102 }; 103 104 window.history.pushState(state, document.title); 105 } 106 }, 107 108 onPopState_: function(e) { 109 if (e.state === null) 110 return; 111 112 var selection = this.selections_[e.state.selection_guid]; 113 if (selection) { 114 var newState = this.currentBrushingState_.clone(); 115 newState.selection = selection; 116 this.currentBrushingState = newState; 117 } 118 e.stopPropagation(); 119 }, 120 121 get selection() { 122 return this.currentBrushingState_.selection; 123 }, 124 get findMatches() { 125 return this.currentBrushingState_.findMatches; 126 }, 127 128 get selectionOfInterest() { 129 return this.currentBrushingState_.selectionOfInterest; 130 }, 131 132 get currentBrushingState() { 133 return this.currentBrushingState_; 134 }, 135 136 set currentBrushingState(newBrushingState) { 137 if (newBrushingState.isAppliedToModel) 138 throw new Error('Cannot apply this state, it is applied'); 139 140 // This function uses value-equality on the states so that state can 141 // changed to a clone of itself without causing a change event, while 142 // still having the actual state object change to the new clone. 143 var hasValueChanged = !this.currentBrushingState_.equals( 144 newBrushingState); 145 146 if (newBrushingState !== this.currentBrushingState_ && !hasValueChanged) { 147 if (this.currentBrushingState_.isAppliedToModel) { 148 this.currentBrushingState_.transferModelOwnershipToClone( 149 newBrushingState); 150 } 151 this.currentBrushingState_ = newBrushingState; 152 return; 153 } 154 155 if (this.currentBrushingState_.isAppliedToModel) 156 this.currentBrushingState_.unapplyFromModelSelectionState(); 157 158 this.currentBrushingState_ = newBrushingState; 159 160 if (this.model) 161 this.currentBrushingState_.applyToModelSelectionState(this.model); 162 163 this.dispatchChangeEvent_(); 164 }, 165 166 /** 167 * @param {Filter} filter The filter to use for finding matches. 168 * @param {Selection} selection The selection to add matches to. 169 * @return {Task} which performs the filtering. 170 */ 171 addAllEventsMatchingFilterToSelectionAsTask: function(filter, selection) { 172 var timelineView = this.timelineView_.trackView; 173 if (!timelineView) 174 return new tr.b.Task(); 175 return timelineView.addAllEventsMatchingFilterToSelectionAsTask( 176 filter, selection); 177 }, 178 179 findTextChangedTo: function(allPossibleMatches) { 180 var newBrushingState = this.currentBrushingState_.clone(); 181 newBrushingState.findMatches = allPossibleMatches; 182 this.currentBrushingState = newBrushingState; 183 }, 184 185 findFocusChangedTo: function(currentFocus) { 186 var newBrushingState = this.currentBrushingState_.clone(); 187 newBrushingState.selection = currentFocus; 188 this.currentBrushingState = newBrushingState; 189 190 this.onUserInitiatedSelectionChange_(); 191 }, 192 193 findTextCleared: function() { 194 if (this.xNavStringMarker_ !== undefined) { 195 this.model.removeAnnotation(this.xNavStringMarker_); 196 this.xNavStringMarker_ = undefined; 197 } 198 199 if (this.guideLineAnnotation_ !== undefined) { 200 this.model.removeAnnotation(this.guideLineAnnotation_); 201 this.guideLineAnnotation_ = undefined; 202 } 203 204 var newBrushingState = this.currentBrushingState_.clone(); 205 newBrushingState.selection = new EventSet(); 206 newBrushingState.findMatches = new EventSet(); 207 this.currentBrushingState = newBrushingState; 208 209 this.onUserInitiatedSelectionChange_(); 210 }, 211 212 uiStateFromString: function(string) { 213 return tr.ui.b.UIState.fromUserFriendlyString( 214 this.model, this.viewport, string); 215 }, 216 217 navToPosition: function(uiState, showNavLine) { 218 this.trackView.navToPosition(uiState, showNavLine); 219 }, 220 221 changeSelectionFromTimeline: function(selection) { 222 var newBrushingState = this.currentBrushingState_.clone(); 223 newBrushingState.selection = selection; 224 newBrushingState.findMatches = new EventSet(); 225 this.currentBrushingState = newBrushingState; 226 227 this.onUserInitiatedSelectionChange_(); 228 }, 229 230 showScriptControlSelection: function(selection) { 231 var newBrushingState = this.currentBrushingState_.clone(); 232 newBrushingState.selection = selection; 233 newBrushingState.findMatches = new EventSet(); 234 this.currentBrushingState = newBrushingState; 235 }, 236 237 changeSelectionFromRequestSelectionChangeEvent: function(selection) { 238 var newBrushingState = this.currentBrushingState_.clone(); 239 newBrushingState.selection = selection; 240 newBrushingState.findMatches = new EventSet(); 241 this.currentBrushingState = newBrushingState; 242 243 this.onUserInitiatedSelectionChange_(); 244 }, 245 246 changeAnalysisViewRelatedEvents: function(eventSet) { 247 var newBrushingState = this.currentBrushingState_.clone(); 248 newBrushingState.analysisViewRelatedEvents = eventSet; 249 this.currentBrushingState = newBrushingState; 250 }, 251 252 changeAnalysisLinkHoveredEvents: function(eventSet) { 253 var newBrushingState = this.currentBrushingState_.clone(); 254 newBrushingState.analysisLinkHoveredEvents = eventSet; 255 this.currentBrushingState = newBrushingState; 256 }, 257 258 getViewSpecificBrushingState: function(viewId) { 259 return this.currentBrushingState.viewSpecificBrushingStates[viewId]; 260 }, 261 262 changeViewSpecificBrushingState: function(viewId, newState) { 263 var oldStates = this.currentBrushingState_.viewSpecificBrushingStates; 264 var newStates = {}; 265 for (var id in oldStates) 266 newStates[id] = oldStates[id]; 267 if (newState === undefined) 268 delete newStates[viewId]; 269 else 270 newStates[viewId] = newState; 271 272 var newBrushingState = this.currentBrushingState_.clone(); 273 newBrushingState.viewSpecificBrushingStates = newStates; 274 this.currentBrushingState = newBrushingState; 275 } 276 }; 277 278 BrushingStateController.getControllerForElement = function(element) { 279 if (tr.isHeadless) 280 throw new Error('Unsupported'); 281 var currentElement = element; 282 while (currentElement) { 283 if (currentElement.brushingStateController) 284 return currentElement.brushingStateController; 285 286 // Walk up the DOM. 287 if (currentElement.parentElement) { 288 currentElement = currentElement.parentElement; 289 continue; 290 } 291 292 // Possibly inside a shadow DOM. 293 var currentNode = currentElement; 294 while (currentNode.parentNode) 295 currentNode = currentNode.parentNode; 296 currentElement = currentNode.host; 297 } 298 return undefined; 299 }; 300 301 return { 302 BrushingStateController: BrushingStateController 303 }; 304}); 305</script> 306