1<!DOCTYPE html> 2<!-- 3Copyright (c) 2012 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.html"> 9<link rel="import" href="/tracing/base/guid.html"> 10<link rel="import" href="/tracing/base/range.html"> 11<link rel="import" href="/tracing/base/iteration_helpers.html"> 12<link rel="import" href="/tracing/model/event_registry.html"> 13 14<script> 15'use strict'; 16 17tr.exportTo('tr.model', function() { 18 19 var EventRegistry = tr.model.EventRegistry; 20 21 var RequestSelectionChangeEvent = tr.b.Event.bind( 22 undefined, 'requestSelectionChange', true, false); 23 24 /** 25 * Represents a event set within a and its associated set of tracks. 26 * @constructor 27 */ 28 function EventSet(opt_events) { 29 this.bounds_dirty_ = true; 30 this.bounds_ = new tr.b.Range(); 31 this.length_ = 0; 32 this.guid_ = tr.b.GUID.allocate(); 33 this.pushed_guids_ = {}; 34 35 if (opt_events) { 36 if (opt_events instanceof Array) { 37 for (var i = 0; i < opt_events.length; i++) 38 this.push(opt_events[i]); 39 } else if (opt_events instanceof EventSet) { 40 this.addEventSet(opt_events); 41 } else { 42 this.push(opt_events); 43 } 44 } 45 } 46 EventSet.prototype = { 47 __proto__: Object.prototype, 48 49 get bounds() { 50 if (this.bounds_dirty_) 51 this.resolveBounds_(); 52 return this.bounds_; 53 }, 54 55 get duration() { 56 if (this.bounds_.isEmpty) 57 return 0; 58 return this.bounds_.max - this.bounds_.min; 59 }, 60 61 get length() { 62 return this.length_; 63 }, 64 65 get guid() { 66 return this.guid_; 67 }, 68 69 clear: function() { 70 for (var i = 0; i < this.length_; ++i) 71 delete this[i]; 72 this.length_ = 0; 73 this.bounds_dirty_ = true; 74 }, 75 76 resolveBounds_: function() { 77 this.bounds_.reset(); 78 for (var i = 0; i < this.length_; i++) 79 this[i].addBoundsToRange(this.bounds_); 80 this.bounds_dirty_ = false; 81 }, 82 83 // push pushes only unique events. 84 // If an event has been already pushed, do nothing. 85 push: function(event) { 86 if (event.guid == undefined) 87 throw new Error('Event must have a GUID'); 88 89 if (this.contains(event)) 90 return event; 91 92 this.pushed_guids_[event.guid] = true; 93 this[this.length_++] = event; 94 this.bounds_dirty_ = true; 95 return event; 96 }, 97 98 contains: function(event) { 99 return this.pushed_guids_[event.guid]; 100 }, 101 102 indexOf: function(event) { 103 for (var i = 0; i < this.length; i++) { 104 if (this[i].guid === event.guid) 105 return i; 106 } 107 return -1; 108 }, 109 110 addEventSet: function(eventSet) { 111 for (var i = 0; i < eventSet.length; i++) 112 this.push(eventSet[i]); 113 }, 114 115 subEventSet: function(index, count) { 116 count = count || 1; 117 118 var eventSet = new EventSet(); 119 eventSet.bounds_dirty_ = true; 120 if (index < 0 || index + count > this.length_) 121 throw new Error('Index out of bounds'); 122 123 for (var i = index; i < index + count; i++) 124 eventSet.push(this[i]); 125 126 return eventSet; 127 }, 128 129 intersectionIsEmpty: function(otherEventSet) { 130 return !this.some(function(event) { 131 return otherEventSet.contains(event); 132 }); 133 }, 134 135 equals: function(that) { 136 if (this.length !== that.length) 137 return false; 138 for (var i = 0; i < this.length; i++) { 139 var event = this[i]; 140 if (that.pushed_guids_[event.guid] === undefined) 141 return false; 142 } 143 return true; 144 }, 145 146 getEventsOrganizedByBaseType: function(opt_pruneEmpty) { 147 var allTypeInfos = EventRegistry.getAllRegisteredTypeInfos(); 148 149 var events = this.getEventsOrganizedByCallback(function(event) { 150 var maxEventIndex = -1; 151 var maxEventTypeInfo = undefined; 152 153 allTypeInfos.forEach(function(eventTypeInfo, eventIndex) { 154 if (!(event instanceof eventTypeInfo.constructor)) 155 return; 156 if (eventIndex > maxEventIndex) { 157 maxEventIndex = eventIndex; 158 maxEventTypeInfo = eventTypeInfo; 159 } 160 }); 161 162 if (maxEventIndex == -1) { 163 console.log(event); 164 throw new Error('Unrecognized event type'); 165 } 166 167 return maxEventTypeInfo.metadata.name; 168 }); 169 170 if (!opt_pruneEmpty) { 171 allTypeInfos.forEach(function(eventTypeInfo) { 172 if (events[eventTypeInfo.metadata.name] === undefined) 173 events[eventTypeInfo.metadata.name] = new EventSet(); 174 }); 175 } 176 177 return events; 178 }, 179 180 getEventsOrganizedByTitle: function() { 181 return this.getEventsOrganizedByCallback(function(event) { 182 if (event.title === undefined) 183 throw new Error('An event didn\'t have a title!'); 184 return event.title; 185 }); 186 }, 187 188 getEventsOrganizedByCallback: function(cb) { 189 var eventsByCallback = {}; 190 for (var i = 0; i < this.length; i++) { 191 var event = this[i]; 192 var key = cb(event); 193 194 if (key === undefined) 195 throw new Error('An event could not be organized'); 196 197 if (eventsByCallback[key] === undefined) 198 eventsByCallback[key] = new EventSet(); 199 200 eventsByCallback[key].push(event); 201 } 202 return eventsByCallback; 203 }, 204 205 enumEventsOfType: function(type, func) { 206 for (var i = 0; i < this.length_; i++) 207 if (this[i] instanceof type) 208 func(this[i]); 209 }, 210 211 get userFriendlyName() { 212 if (this.length === 0) { 213 throw new Error('Empty event set'); 214 } 215 216 var eventsByBaseType = this.getEventsOrganizedByBaseType(true); 217 var eventTypeName = tr.b.dictionaryKeys(eventsByBaseType)[0]; 218 219 if (this.length === 1) { 220 var tmp = EventRegistry.getUserFriendlySingularName(eventTypeName); 221 return this[0].userFriendlyName; 222 } 223 224 var numEventTypes = tr.b.dictionaryLength(eventsByBaseType); 225 if (numEventTypes !== 1) { 226 return this.length + ' events of various types'; 227 } 228 229 var tmp = EventRegistry.getUserFriendlyPluralName(eventTypeName); 230 return this.length + ' ' + tmp; 231 }, 232 233 filter: function(fn, opt_this) { 234 var res = new EventSet(); 235 236 this.forEach(function(slice) { 237 if (fn.call(this, slice)) 238 res.push(slice); 239 }, opt_this); 240 241 return res; 242 }, 243 244 toArray: function() { 245 var ary = []; 246 for (var i = 0; i < this.length; i++) 247 ary.push(this[i]); 248 return ary; 249 }, 250 251 forEach: function(fn, opt_this) { 252 for (var i = 0; i < this.length; i++) 253 fn.call(opt_this, this[i], i); 254 }, 255 256 map: function(fn, opt_this) { 257 var res = []; 258 for (var i = 0; i < this.length; i++) 259 res.push(fn.call(opt_this, this[i], i)); 260 return res; 261 }, 262 263 every: function(fn, opt_this) { 264 for (var i = 0; i < this.length; i++) 265 if (!fn.call(opt_this, this[i], i)) 266 return false; 267 return true; 268 }, 269 270 some: function(fn, opt_this) { 271 for (var i = 0; i < this.length; i++) 272 if (fn.call(opt_this, this[i], i)) 273 return true; 274 return false; 275 }, 276 277 asDict: function() { 278 var stable_ids = []; 279 this.forEach(function(event) { 280 stable_ids.push(event.stableId); 281 }); 282 return {'events': stable_ids}; 283 } 284 }; 285 286 EventSet.IMMUTABLE_EMPTY_SET = (function() { 287 var s = new EventSet(); 288 s.resolveBounds_(); 289 s.push = function() { 290 throw new Error('Cannot push to an immutable event set'); 291 }; 292 s.addEventSet = function() { 293 throw new Error('Cannot add to an immutable event set'); 294 }; 295 Object.freeze(s); 296 return s; 297 })(); 298 299 return { 300 EventSet: EventSet, 301 RequestSelectionChangeEvent: RequestSelectionChangeEvent 302 }; 303}); 304</script> 305