1<!DOCTYPE html> 2<!-- 3Copyright 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.html"> 9<link rel="import" href="/tracing/base/event_target.html"> 10<link rel="import" href="/tracing/value/time_display_mode.html"> 11 12<script> 13'use strict'; 14 15tr.exportTo('tr.v', function() { 16 var TimeDisplayModes = tr.v.TimeDisplayModes; 17 18 var BINARY_PREFIXES = ['', 'Ki', 'Mi', 'Gi', 'Ti']; 19 20 var PLUS_MINUS_SIGN = String.fromCharCode(177); 21 22 function max(a, b) { 23 if (a === undefined) 24 return b; 25 if (b === undefined) 26 return a; 27 return a.scale > b.scale ? a : b; 28 } 29 30 /** @enum */ 31 var ImprovementDirection = { 32 DONT_CARE: 0, 33 BIGGER_IS_BETTER: 1, 34 SMALLER_IS_BETTER: 2 35 }; 36 37 /** @constructor */ 38 function Unit(unitName, jsonName, isDelta, improvementDirection, 39 formatValue) { 40 this.unitName = unitName; 41 this.jsonName = jsonName; 42 this.isDelta = isDelta; 43 this.improvementDirection = improvementDirection; 44 this.formatValue_ = formatValue; 45 this.correspondingDeltaUnit = undefined; 46 } 47 48 Unit.prototype = { 49 asJSON: function() { 50 return this.jsonName; 51 }, 52 53 format: function(value) { 54 var formattedValue = this.formatValue_(value); 55 if (!this.isDelta || value < 0 /* already contains negative sign */) 56 return formattedValue; 57 if (value === 0) 58 return PLUS_MINUS_SIGN + formattedValue; 59 else 60 return '+' + formattedValue; 61 } 62 }; 63 64 Unit.reset = function() { 65 Unit.currentTimeDisplayMode = TimeDisplayModes.ms; 66 }; 67 68 Unit.timestampFromUs = function(us) { 69 return us / 1000; 70 }; 71 72 Unit.maybeTimestampFromUs = function(us) { 73 return us === undefined ? undefined : us / 1000; 74 }; 75 76 Object.defineProperty(Unit, 'currentTimeDisplayMode', { 77 get: function() { 78 return Unit.currentTimeDisplayMode_; 79 }, 80 // Use tr-v-ui-preferred-display-unit element instead of directly setting. 81 set: function(value) { 82 if (Unit.currentTimeDisplayMode_ === value) 83 return; 84 85 Unit.currentTimeDisplayMode_ = value; 86 Unit.dispatchEvent(new tr.b.Event('display-mode-changed')); 87 } 88 }); 89 90 Unit.didPreferredTimeDisplayUnitChange = function() { 91 var largest = undefined; 92 var els = tr.b.findDeepElementsMatching(document.body, 93 'tr-v-ui-preferred-display-unit'); 94 els.forEach(function(el) { 95 largest = max(largest, el.preferredTimeDisplayMode); 96 }); 97 98 Unit.currentDisplayUnit = largest === undefined ? 99 TimeDisplayModes.ms : largest; 100 }; 101 102 Unit.byName = {}; 103 Unit.byJSONName = {}; 104 105 Unit.fromJSON = function(object) { 106 var u = Unit.byJSONName[object]; 107 if (u) { 108 return u; 109 } 110 throw new Error('Unrecognized unit'); 111 }; 112 113 /** 114 * Define all combinations of a unit with isDelta and improvementDirection 115 * flags. For example, the following code: 116 * 117 * Unit.define({ 118 * baseUnitName: 'powerInWatts' 119 * baseJsonName: 'W' 120 * formatValue: function(value) { 121 * // Code for formatting the unit (independent of isDelta and 122 * // improvementDirection flags). 123 * } 124 * }); 125 * 126 * generates the following six units (JSON names shown in parentheses): 127 * 128 * Unit.byName.powerInWatts (W) 129 * Unit.byName.powerInWatts_smallerIsBetter (W_smallerIsBetter) 130 * Unit.byName.powerInWatts_biggerIsBetter (W_biggerIsBetter) 131 * Unit.byName.powerInWattsDelta (WDelta) 132 * Unit.byName.powerInWattsDelta_smallerIsBetter (WDelta_smallerIsBetter) 133 * Unit.byName.powerInWattsDelta_biggerIsBetter (WDelta_biggerIsBetter) 134 * 135 * with the appropriate flags and formatting code (including +/- prefixes 136 * for deltas). 137 */ 138 Unit.define = function(params) { 139 tr.b.iterItems(ImprovementDirection, function(_, improvementDirection) { 140 var regularUnit = 141 Unit.defineUnitVariant_(params, false, improvementDirection); 142 var deltaUnit = 143 Unit.defineUnitVariant_(params, true, improvementDirection); 144 145 regularUnit.correspondingDeltaUnit = deltaUnit; 146 deltaUnit.correspondingDeltaUnit = deltaUnit; 147 }); 148 }; 149 150 Unit.defineUnitVariant_ = function(params, isDelta, improvementDirection) { 151 var nameSuffix = isDelta ? 'Delta' : ''; 152 switch (improvementDirection) { 153 case ImprovementDirection.DONT_CARE: 154 break; 155 case ImprovementDirection.BIGGER_IS_BETTER: 156 nameSuffix += '_biggerIsBetter'; 157 break; 158 case ImprovementDirection.SMALLER_IS_BETTER: 159 nameSuffix += '_smallerIsBetter'; 160 break; 161 default: 162 throw new Error( 163 'Unknown improvement direction: ' + improvementDirection); 164 } 165 166 var unitName = params.baseUnitName + nameSuffix; 167 var jsonName = params.baseJsonName + nameSuffix; 168 if (Unit.byName[unitName] !== undefined) 169 throw new Error('Unit \'' + unitName + '\' already exists'); 170 if (Unit.byJSONName[jsonName] !== undefined) 171 throw new Error('JSON unit \'' + jsonName + '\' alread exists'); 172 173 var unit = new Unit( 174 unitName, jsonName, isDelta, improvementDirection, params.formatValue); 175 Unit.byName[unitName] = unit; 176 Unit.byJSONName[jsonName] = unit; 177 178 return unit; 179 }; 180 181 tr.b.EventTarget.decorate(Unit); 182 Unit.reset(); 183 184 // Known display units follow. 185 ////////////////////////////////////////////////////////////////////////////// 186 187 Unit.define({ 188 baseUnitName: 'timeDurationInMs', 189 baseJsonName: 'ms', 190 formatValue: function(value) { 191 return Unit.currentTimeDisplayMode_.format(value); 192 } 193 }); 194 195 Unit.define({ 196 baseUnitName: 'timeStampInMs', 197 baseJsonName: 'tsMs', 198 formatValue: function(value) { 199 return Unit.currentTimeDisplayMode_.format(value); 200 } 201 }); 202 203 Unit.define({ 204 baseUnitName: 'normalizedPercentage', 205 baseJsonName: 'n%', 206 formatValue: function(value) { 207 var tmp = new Number(Math.round(value * 100000) / 1000); 208 return tmp.toLocaleString(undefined, { minimumFractionDigits: 3 }) + '%'; 209 } 210 }); 211 212 Unit.define({ 213 baseUnitName: 'sizeInBytes', 214 baseJsonName: 'sizeInBytes', 215 formatValue: function(value) { 216 var signPrefix = ''; 217 if (value < 0) { 218 signPrefix = '-'; 219 value = -value; 220 } 221 222 var i = 0; 223 while (value >= 1024 && i < BINARY_PREFIXES.length - 1) { 224 value /= 1024; 225 i++; 226 } 227 228 return signPrefix + value.toFixed(1) + ' ' + BINARY_PREFIXES[i] + 'B'; 229 } 230 }); 231 232 Unit.define({ 233 baseUnitName: 'energyInJoules', 234 baseJsonName: 'J', 235 formatValue: function(value) { 236 return value.toLocaleString( 237 undefined, { minimumFractionDigits: 3 }) + ' J'; 238 } 239 }); 240 241 Unit.define({ 242 baseUnitName: 'powerInWatts', 243 baseJsonName: 'W', 244 formatValue: function(value) { 245 return value.toLocaleString( 246 undefined, { minimumFractionDigits: 3 }) + ' W'; 247 } 248 }); 249 250 Unit.define({ 251 baseUnitName: 'unitlessNumber', 252 baseJsonName: 'unitless', 253 formatValue: function(value) { 254 return value.toLocaleString( 255 undefined, { minimumFractionDigits: 3, maximumFractionDigits: 3 }); 256 } 257 }); 258 259 return { 260 ImprovementDirection: ImprovementDirection, 261 Unit: Unit 262 }; 263}); 264</script> 265