| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/base/event_target.html"> |
| <link rel="import" href="/base/task.html"> |
| <link rel="import" href="/ui/base/brushing_state.html"> |
| <link rel="import" href="/ui/timeline_viewport.html"> |
| <link rel="import" href="/ui/base/ui_state.html"> |
| <link rel="import" href="/model/event_set.html"> |
| <link rel="import" href="/model/selection_state.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.c', function() { |
| var BrushingState = tr.ui.b.BrushingState; |
| var EventSet = tr.model.EventSet; |
| var SelectionState = tr.model.SelectionState; |
| var Viewport = tr.ui.TimelineViewport; |
| |
| function BrushingStateController(timelineView) { |
| tr.b.EventTarget.call(this); |
| |
| this.timelineView_ = timelineView; |
| this.currentBrushingState_ = new BrushingState(); |
| |
| this.onPopState_ = this.onPopState_.bind(this); |
| this.historyEnabled_ = false; |
| this.selections_ = {}; |
| } |
| |
| BrushingStateController.prototype = { |
| __proto__: tr.b.EventTarget.prototype, |
| |
| dispatchChangeEvent_: function() { |
| var e = new tr.b.Event('change', false, false); |
| this.dispatchEvent(e); |
| }, |
| |
| get model() { |
| if (!this.timelineView_) |
| return undefined; |
| return this.timelineView_.model; |
| }, |
| |
| get trackView() { |
| if (!this.timelineView_) |
| return undefined; |
| return this.timelineView_.trackView; |
| }, |
| |
| get viewport() { |
| if (!this.timelineView_) |
| return undefined; |
| if (!this.timelineView_.trackView) |
| return undefined; |
| return this.timelineView_.trackView.viewport; |
| }, |
| |
| /* History system */ |
| get historyEnabled() { |
| return this.historyEnabled_; |
| }, |
| |
| set historyEnabled(historyEnabled) { |
| this.historyEnabled_ = !!historyEnabled; |
| if (historyEnabled) |
| window.addEventListener('popstate', this.onPopState_); |
| else |
| window.removeEventListener('popstate', this.onPopState_); |
| }, |
| |
| modelWillChange: function() { |
| if (this.currentBrushingState_.isAppliedToModel) |
| this.currentBrushingState_.unapplyFromModelSelectionState(); |
| }, |
| |
| modelDidChange: function() { |
| this.selections_ = {}; |
| |
| this.currentBrushingState_ = new BrushingState(); |
| this.currentBrushingState_.applyToModelSelectionState(this.model); |
| |
| var e = new tr.b.Event('model-changed', false, false); |
| this.dispatchEvent(e); |
| |
| this.dispatchChangeEvent_(); |
| }, |
| |
| onUserInitiatedSelectionChange_: function() { |
| var selection = this.selection; |
| if (this.historyEnabled) { |
| // Save the selection so that when back button is pressed, |
| // it could be retrieved. |
| this.selections_[selection.guid] = selection; |
| var state = { |
| selection_guid: selection.guid |
| }; |
| |
| window.history.pushState(state, document.title); |
| } |
| }, |
| |
| onPopState_: function(e) { |
| if (e.state === null) |
| return; |
| |
| var selection = this.selections_[e.state.selection_guid]; |
| if (selection) { |
| var newState = this.currentBrushingState_.clone(); |
| newState.selection = selection; |
| this.currentBrushingState = newState; |
| } |
| e.stopPropagation(); |
| }, |
| |
| get selection() { |
| return this.currentBrushingState_.selection; |
| }, |
| get findMatches() { |
| return this.currentBrushingState_.findMatches; |
| }, |
| |
| get selectionOfInterest() { |
| return this.currentBrushingState_.selectionOfInterest; |
| }, |
| |
| get currentBrushingState() { |
| return this.currentBrushingState_; |
| }, |
| |
| set currentBrushingState(newBrushingState) { |
| if (newBrushingState.isAppliedToModel) |
| throw new Error('Cannot apply this state, it is applied'); |
| |
| // This function uses value-equality on the states so that state can |
| // changed to a clone of itself without causing a change event, while |
| // still having the actual state object change to the new clone. |
| var hasValueChanged = !this.currentBrushingState_.equals( |
| newBrushingState); |
| |
| if (newBrushingState !== this.currentBrushingState_ && !hasValueChanged) { |
| if (this.currentBrushingState_.isAppliedToModel) { |
| this.currentBrushingState_.transferModelOwnershipToClone( |
| newBrushingState); |
| } |
| this.currentBrushingState_ = newBrushingState; |
| return; |
| } |
| |
| if (this.currentBrushingState_.isAppliedToModel) |
| this.currentBrushingState_.unapplyFromModelSelectionState(); |
| |
| this.currentBrushingState_ = newBrushingState; |
| |
| if (this.model) |
| this.currentBrushingState_.applyToModelSelectionState(this.model); |
| |
| this.dispatchChangeEvent_(); |
| }, |
| |
| /** |
| * @param {Filter} filter The filter to use for finding matches. |
| * @param {Selection} selection The selection to add matches to. |
| * @return {Task} which performs the filtering. |
| */ |
| addAllEventsMatchingFilterToSelectionAsTask: function(filter, selection) { |
| var timelineView = this.timelineView_.trackView; |
| if (!timelineView) |
| return new tr.b.Task(); |
| return timelineView.addAllEventsMatchingFilterToSelectionAsTask( |
| filter, selection); |
| }, |
| |
| findTextChangedTo: function(allPossibleMatches) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.findMatches = allPossibleMatches; |
| this.currentBrushingState = newBrushingState; |
| }, |
| |
| findFocusChangedTo: function(currentFocus) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.selection = currentFocus; |
| this.currentBrushingState = newBrushingState; |
| |
| this.onUserInitiatedSelectionChange_(); |
| }, |
| |
| findTextCleared: function() { |
| if (this.xNavStringMarker_ !== undefined) { |
| this.model.removeAnnotation(this.xNavStringMarker_); |
| this.xNavStringMarker_ = undefined; |
| } |
| |
| if (this.guideLineAnnotation_ !== undefined) { |
| this.model.removeAnnotation(this.guideLineAnnotation_); |
| this.guideLineAnnotation_ = undefined; |
| } |
| |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.selection = new EventSet(); |
| newBrushingState.findMatches = new EventSet(); |
| this.currentBrushingState = newBrushingState; |
| |
| this.onUserInitiatedSelectionChange_(); |
| }, |
| |
| uiStateFromString: function(string) { |
| return tr.ui.b.UIState.fromUserFriendlyString( |
| this.model, this.viewport, string); |
| }, |
| |
| navToPosition: function(uiState, showNavLine) { |
| this.trackView.navToPosition(uiState, showNavLine); |
| }, |
| |
| changeSelectionFromTimeline: function(selection) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.selection = selection; |
| newBrushingState.findMatches = new EventSet(); |
| this.currentBrushingState = newBrushingState; |
| |
| this.onUserInitiatedSelectionChange_(); |
| }, |
| |
| showScriptControlSelection: function(selection) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.selection = selection; |
| newBrushingState.findMatches = new EventSet(); |
| this.currentBrushingState = newBrushingState; |
| }, |
| |
| changeSelectionFromRequestSelectionChangeEvent: function(selection) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.selection = selection; |
| newBrushingState.findMatches = new EventSet(); |
| this.currentBrushingState = newBrushingState; |
| |
| this.onUserInitiatedSelectionChange_(); |
| }, |
| |
| changeAnalysisViewRelatedEvents: function(eventSet) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.analysisViewRelatedEvents = eventSet; |
| this.currentBrushingState = newBrushingState; |
| }, |
| |
| changeAnalysisLinkHoveredEvents: function(eventSet) { |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.analysisLinkHoveredEvents = eventSet; |
| this.currentBrushingState = newBrushingState; |
| }, |
| |
| getViewSpecificBrushingState: function(viewId) { |
| return this.currentBrushingState.viewSpecificBrushingStates[viewId]; |
| }, |
| |
| changeViewSpecificBrushingState: function(viewId, newState) { |
| var oldStates = this.currentBrushingState_.viewSpecificBrushingStates; |
| var newStates = {}; |
| for (var id in oldStates) |
| newStates[id] = oldStates[id]; |
| if (newState === undefined) |
| delete newStates[viewId]; |
| else |
| newStates[viewId] = newState; |
| |
| var newBrushingState = this.currentBrushingState_.clone(); |
| newBrushingState.viewSpecificBrushingStates = newStates; |
| this.currentBrushingState = newBrushingState; |
| } |
| }; |
| |
| BrushingStateController.getControllerForElement = function(element) { |
| if (tr.isHeadless) |
| throw new Error('Unsupported'); |
| var currentElement = element; |
| while (currentElement) { |
| if (currentElement.brushingStateController) |
| return currentElement.brushingStateController; |
| |
| // Walk up the DOM. |
| if (currentElement.parentElement) { |
| currentElement = currentElement.parentElement; |
| continue; |
| } |
| |
| // Possibly inside a shadow DOM. |
| var currentNode = currentElement; |
| while (currentNode.parentNode) |
| currentNode = currentNode.parentNode; |
| currentElement = currentNode.host; |
| } |
| return undefined; |
| }; |
| |
| return { |
| BrushingStateController: BrushingStateController |
| }; |
| }); |
| </script> |