Home | History | Annotate | Download | only in value
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright 2015 The Chromium Authors. All rights reserved.
      4 Use of this source code is governed by a BSD-style license that can be
      5 found 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 
     15 tr.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