| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2012 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="/tracing/base/event.html"> |
| <link rel="import" href="/tracing/base/guid.html"> |
| <link rel="import" href="/tracing/base/iteration_helpers.html"> |
| <link rel="import" href="/tracing/base/range.html"> |
| <link rel="import" href="/tracing/model/event_registry.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.model', function() { |
| |
| var EventRegistry = tr.model.EventRegistry; |
| |
| var RequestSelectionChangeEvent = tr.b.Event.bind( |
| undefined, 'requestSelectionChange', true, false); |
| |
| /** |
| * Represents a event set within a and its associated set of tracks. |
| * @constructor |
| */ |
| function EventSet(opt_events) { |
| this.bounds_ = new tr.b.Range(); |
| this.events_ = new Set(); |
| |
| if (opt_events) { |
| if (opt_events instanceof Array) { |
| for (var event of opt_events) |
| this.push(event); |
| } else if (opt_events instanceof EventSet) { |
| this.addEventSet(opt_events); |
| } else { |
| this.push(opt_events); |
| } |
| } |
| } |
| |
| EventSet.prototype = { |
| __proto__: Object.prototype, |
| |
| get bounds() { |
| return this.bounds_; |
| }, |
| |
| get duration() { |
| if (this.bounds_.isEmpty) |
| return 0; |
| return this.bounds_.max - this.bounds_.min; |
| }, |
| |
| get length() { |
| return this.events_.size; |
| }, |
| |
| get guid() { |
| return this.guid_; |
| }, |
| |
| *[Symbol.iterator]() { |
| for (var event of this.events_) |
| yield event; |
| }, |
| |
| clear: function() { |
| this.bounds_ = new tr.b.Range(); |
| this.events_.clear(); |
| }, |
| |
| // push pushes only unique events. |
| // If an event has been already pushed, do nothing. |
| push: function(event) { |
| if (event.guid == undefined) |
| throw new Error('Event must have a GUID'); |
| |
| if (!this.events_.has(event)) { |
| this.events_.add(event); |
| // Some uses of eventSet, particularly in tests, have Events as objects |
| // that don't have addBoundsToRange as a function. Thus we need to |
| // handle this case. |
| if (event.addBoundsToRange) |
| if (this.bounds_ !== undefined) |
| event.addBoundsToRange(this.bounds_); |
| } |
| |
| return event; |
| }, |
| |
| contains: function(event) { |
| if (this.events_.has(event)) |
| return event; |
| else |
| return undefined; |
| }, |
| |
| addEventSet: function(eventSet) { |
| for (var event of eventSet) |
| this.push(event); |
| }, |
| |
| intersectionIsEmpty: function(otherEventSet) { |
| return !this.some(event => otherEventSet.contains(event)); |
| }, |
| |
| equals: function(that) { |
| if (this.length !== that.length) |
| return false; |
| return this.every(event => that.contains(event)); |
| }, |
| |
| sortEvents: function(compare) { |
| // Convert to array, then sort, then convert back |
| var ary = this.toArray(); |
| ary.sort(compare); |
| |
| this.clear(); |
| for (var event of ary) |
| this.push(event); |
| }, |
| |
| getEventsOrganizedByBaseType: function(opt_pruneEmpty) { |
| var allTypeInfos = EventRegistry.getAllRegisteredTypeInfos(); |
| |
| var events = this.getEventsOrganizedByCallback(function(event) { |
| var maxEventIndex = -1; |
| var maxEventTypeInfo = undefined; |
| |
| allTypeInfos.forEach(function(eventTypeInfo, eventIndex) { |
| if (!(event instanceof eventTypeInfo.constructor)) |
| return; |
| if (eventIndex > maxEventIndex) { |
| maxEventIndex = eventIndex; |
| maxEventTypeInfo = eventTypeInfo; |
| } |
| }); |
| |
| if (maxEventIndex == -1) { |
| console.log(event); |
| throw new Error('Unrecognized event type'); |
| } |
| |
| return maxEventTypeInfo.metadata.name; |
| }); |
| |
| if (!opt_pruneEmpty) { |
| allTypeInfos.forEach(function(eventTypeInfo) { |
| if (events[eventTypeInfo.metadata.name] === undefined) |
| events[eventTypeInfo.metadata.name] = new EventSet(); |
| }); |
| } |
| |
| return events; |
| }, |
| |
| getEventsOrganizedByTitle: function() { |
| return this.getEventsOrganizedByCallback(function(event) { |
| if (event.title === undefined) |
| throw new Error('An event didn\'t have a title!'); |
| return event.title; |
| }); |
| }, |
| |
| /** |
| * @param {!function(!tr.model.Event):string} cb |
| * @param {*=} opt_this |
| * @return {!Object} |
| */ |
| getEventsOrganizedByCallback: function(cb, opt_this) { |
| var groupedEvents = tr.b.group(this, cb, opt_this || this); |
| return tr.b.mapItems(groupedEvents, (_, events) => new EventSet(events)); |
| }, |
| |
| enumEventsOfType: function(type, func) { |
| for (var event of this) |
| if (event instanceof type) |
| func(event); |
| }, |
| |
| get userFriendlyName() { |
| if (this.length === 0) { |
| throw new Error('Empty event set'); |
| } |
| |
| var eventsByBaseType = this.getEventsOrganizedByBaseType(true); |
| var eventTypeName = tr.b.dictionaryKeys(eventsByBaseType)[0]; |
| |
| if (this.length === 1) { |
| var tmp = EventRegistry.getUserFriendlySingularName(eventTypeName); |
| return tr.b.getOnlyElement(this.events_).userFriendlyName; |
| } |
| |
| var numEventTypes = tr.b.dictionaryLength(eventsByBaseType); |
| if (numEventTypes !== 1) { |
| return this.length + ' events of various types'; |
| } |
| |
| var tmp = EventRegistry.getUserFriendlyPluralName(eventTypeName); |
| return this.length + ' ' + tmp; |
| }, |
| |
| filter: function(fn, opt_this) { |
| var res = new EventSet(); |
| for (var event of this) |
| if (fn.call(opt_this, event)) |
| res.push(event); |
| |
| return res; |
| }, |
| |
| toArray: function() { |
| var ary = []; |
| for (var event of this) |
| ary.push(event); |
| return ary; |
| }, |
| |
| forEach: function(fn, opt_this) { |
| for (var event of this) |
| fn.call(opt_this, event); |
| }, |
| |
| map: function(fn, opt_this) { |
| var res = []; |
| for (var event of this) |
| res.push(fn.call(opt_this, event)); |
| return res; |
| }, |
| |
| every: function(fn, opt_this) { |
| for (var event of this) |
| if (!fn.call(opt_this, event)) |
| return false; |
| return true; |
| }, |
| |
| some: function(fn, opt_this) { |
| for (var event of this) |
| if (fn.call(opt_this, event)) |
| return true; |
| return false; |
| }, |
| |
| asDict: function() { |
| var stable_ids = []; |
| for (var event of this) |
| stable_ids.push(event.stableId); |
| return {'events': stable_ids}; |
| }, |
| |
| asSet: function() { |
| return this.events_; |
| } |
| }; |
| |
| EventSet.IMMUTABLE_EMPTY_SET = (function() { |
| var s = new EventSet(); |
| s.push = function() { |
| throw new Error('Cannot push to an immutable event set'); |
| }; |
| s.addEventSet = function() { |
| throw new Error('Cannot add to an immutable event set'); |
| }; |
| Object.freeze(s); |
| return s; |
| })(); |
| |
| return { |
| EventSet: EventSet, |
| RequestSelectionChangeEvent: RequestSelectionChangeEvent |
| }; |
| }); |
| </script> |