blob: f2be8d8c8ddd298f06dbcb5616e21143639862f0 [file] [log] [blame]
<!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>