OLD | NEW |
1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
2 <!-- | 2 <!-- |
3 Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 4 Use of this source code is governed by a BSD-style license that can be |
5 found in the LICENSE file. | 5 found in the LICENSE file. |
6 --> | 6 --> |
7 | 7 |
8 <link rel="import" href="/tracing/ui/base/deep_utils.html"> | 8 <link rel="import" href="/tracing/ui/base/deep_utils.html"> |
9 <link rel="import" href="/tracing/value/numeric.html"> | 9 <link rel="import" href="/tracing/value/numeric.html"> |
10 <link rel="import" href="/tracing/value/unit.html"> | 10 <link rel="import" href="/tracing/value/unit.html"> |
| 11 <link rel="import" href="/tracing/value/value.html"> |
11 | 12 |
12 <script> | 13 <script> |
13 'use strict'; | 14 'use strict'; |
14 tr.exportTo('tr.v.ui', function() { | 15 tr.exportTo('tr.v.ui', function() { |
| 16 var emojiPrefix = String.fromCharCode(55357); |
| 17 var Emoji = { |
| 18 GRINNING_FACE: emojiPrefix + String.fromCharCode(56835), |
| 19 NEUTRAL_FACE: emojiPrefix + String.fromCharCode(56848), |
| 20 CONFOUNDED_FACE: emojiPrefix + String.fromCharCode(56854) |
| 21 }; |
| 22 |
| 23 /** |
| 24 * @param {undefined|tr.v.NumericValue|tr.v.Numeric} value |
| 25 * @param {Object=} opt_config |
| 26 * @param {number=} opt_config.total |
| 27 * @param {boolean=} opt_config.rightAlign |
| 28 * @param {!tr.v.Unit=} opt_config.unit |
| 29 * @param {tr.v.Significance=} opt_config.significance |
| 30 * @return {string|Element} |
| 31 */ |
15 function createScalarSpan(value, opt_config) { | 32 function createScalarSpan(value, opt_config) { |
16 if (value === undefined) | 33 if (value === undefined) |
17 return ''; | 34 return ''; |
18 | 35 |
19 var config = opt_config || {}; | 36 var config = opt_config || {}; |
20 var ownerDocument = config.ownerDocument || document; | 37 var ownerDocument = config.ownerDocument || document; |
21 | 38 |
22 var span = ownerDocument.createElement('tr-v-ui-scalar-span'); | 39 var span = ownerDocument.createElement('tr-v-ui-scalar-span'); |
23 | 40 |
| 41 if (value instanceof tr.v.NumericValue) { |
| 42 value = value.numeric; |
| 43 config.unit = value.unit; |
| 44 } |
| 45 |
24 var numericValue; | 46 var numericValue; |
25 if (value instanceof tr.v.ScalarNumeric) { | 47 if (value instanceof tr.v.ScalarNumeric) { |
26 span.value = value; | 48 span.value = value; |
27 numericValue = value.value; | 49 numericValue = value.value; |
| 50 } else if (value instanceof tr.v.Numeric) { |
| 51 numericValue = value.average; |
| 52 if (numericValue === undefined) |
| 53 return ''; |
| 54 span.setValueAndUnit(numericValue, value.unit); |
28 } else { | 55 } else { |
29 var unit = config.unit; | 56 var unit = config.unit; |
30 if (unit === undefined) { | 57 if (unit === undefined) { |
31 throw new Error( | 58 throw new Error( |
32 'Unit must be provided in config when value is a number'); | 59 'Unit must be provided in config when value is a number'); |
33 } | 60 } |
34 span.setValueAndUnit(value, unit); | 61 span.setValueAndUnit(value, unit); |
35 numericValue = value; | 62 numericValue = value; |
36 } | 63 } |
37 | 64 |
38 if (config.context) | 65 if (config.context) |
39 span.context = config.context; | 66 span.context = config.context; |
40 | 67 |
41 if (config.total) | 68 if (config.total) |
42 span.percentage = numericValue / config.total; | 69 span.percentage = numericValue / config.total; |
43 | 70 |
44 if (config.rightAlign) | 71 if (config.rightAlign) |
45 span.rightAlign = true; | 72 span.rightAlign = true; |
46 | 73 |
| 74 if (config.significance !== undefined) |
| 75 span.significance = config.significance; |
| 76 |
47 return span; | 77 return span; |
48 } | 78 } |
49 | 79 |
50 return { | 80 return { |
| 81 Emoji: Emoji, |
51 createScalarSpan: createScalarSpan | 82 createScalarSpan: createScalarSpan |
52 }; | 83 }; |
53 }); | 84 }); |
54 </script> | 85 </script> |
55 | 86 |
56 <dom-module id='tr-v-ui-scalar-span'> | 87 <dom-module id='tr-v-ui-scalar-span'> |
57 <template> | 88 <template> |
58 <style> | 89 <style> |
59 :host { | 90 :host { |
60 display: block; | 91 display: block; |
61 position: relative; | 92 position: relative; |
62 } | 93 } |
63 #content.right-align { | 94 #content.right-align { |
64 text-align: right; | 95 text-align: right; |
65 position: relative; | 96 position: relative; |
66 display: block; | 97 display: block; |
67 } | 98 } |
| 99 #significance.better, #content.better { |
| 100 color: green; |
| 101 } |
| 102 #significance.worse, #content.worse { |
| 103 color: red; |
| 104 } |
68 #sparkline { | 105 #sparkline { |
69 width: 0%; | 106 width: 0%; |
70 position: absolute; | 107 position: absolute; |
71 bottom: 0; | 108 bottom: 0; |
72 right: 0; | 109 right: 0; |
73 display: none; | 110 display: none; |
74 height: 100%; | 111 height: 100%; |
75 background-color: hsla(216, 100%, 94.5%, .75); | 112 background-color: hsla(216, 100%, 94.5%, .75); |
76 border-left: 1px solid hsl(216, 100%, 89%); | 113 border-left: 1px solid hsl(216, 100%, 89%); |
77 box-sizing: border-box; | 114 box-sizing: border-box; |
78 } | 115 } |
79 #warning { | 116 #warning { |
80 margin-left: 4px; | 117 margin-left: 4px; |
81 font-size: 66%; | 118 font-size: 66%; |
82 } | 119 } |
| 120 #significance { |
| 121 font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Ti
mes, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort; |
| 122 font-size: 13pt; |
| 123 } |
83 </style> | 124 </style> |
84 <span id="sparkline"></span> | 125 <span id="sparkline"></span> |
| 126 <span id="significance"></span> |
85 <span id="content"></span> | 127 <span id="content"></span> |
86 <span id="warning" style="display:none">⚠</span> | 128 <span id="warning" style="display:none">⚠</span> |
87 </template> | 129 </template> |
88 </dom-module> | 130 </dom-module> |
89 <script> | 131 <script> |
90 'use strict'; | 132 'use strict'; |
91 | 133 |
92 Polymer({ | 134 Polymer({ |
93 is: 'tr-v-ui-scalar-span', | 135 is: 'tr-v-ui-scalar-span', |
94 | 136 |
95 ready: function() { | 137 created: function() { |
96 this.value_ = undefined; | 138 this.value_ = undefined; |
97 this.unit_ = undefined; | 139 this.unit_ = undefined; |
| 140 this.context_ = undefined; |
98 | 141 |
99 this.warning_ = undefined; | 142 this.warning_ = undefined; |
100 this.percentage_ = undefined; | 143 this.percentage_ = undefined; |
| 144 this.significance_ = tr.v.Significance.DONT_CARE; |
| 145 }, |
| 146 |
| 147 get significance() { |
| 148 return this.significance_; |
| 149 }, |
| 150 |
| 151 set significance(s) { |
| 152 this.significance_ = s; |
| 153 this.updateContent_(); |
| 154 }, |
| 155 |
| 156 set contentTextDecoration(deco) { |
| 157 this.$.content.style.textDecoration = deco; |
| 158 }, |
| 159 |
| 160 get value() { |
| 161 return this.value_; |
| 162 }, |
| 163 |
| 164 set value(value) { |
| 165 if (value instanceof tr.v.ScalarNumeric) { |
| 166 this.value_ = value.value; |
| 167 this.unit_ = value.unit; |
| 168 } else { |
| 169 this.value_ = value; |
| 170 } |
| 171 this.updateContent_(); |
101 }, | 172 }, |
102 | 173 |
103 attached: function() { | 174 attached: function() { |
104 tr.v.Unit.addEventListener( | 175 tr.v.Unit.addEventListener( |
105 'display-mode-changed', this.updateContent_.bind(this)); | 176 'display-mode-changed', this.updateContent_.bind(this)); |
106 }, | 177 }, |
107 | 178 |
108 detached: function() { | 179 detached: function() { |
109 tr.v.Unit.removeEventListener( | 180 tr.v.Unit.removeEventListener( |
110 'display-mode-changed', this.updateContent_.bind(this)); | 181 'display-mode-changed', this.updateContent_.bind(this)); |
(...skipping 10 matching lines...) Expand all Loading... |
121 set value(value) { | 192 set value(value) { |
122 if (value instanceof tr.v.ScalarNumeric) { | 193 if (value instanceof tr.v.ScalarNumeric) { |
123 this.value_ = value.value; | 194 this.value_ = value.value; |
124 this.unit_ = value.unit; | 195 this.unit_ = value.unit; |
125 } else { | 196 } else { |
126 this.value_ = value; | 197 this.value_ = value; |
127 } | 198 } |
128 this.updateContent_(); | 199 this.updateContent_(); |
129 }, | 200 }, |
130 | 201 |
131 get context() { | 202 get context() { |
132 return this.context_; | 203 return this.context_; |
133 }, | 204 }, |
134 | 205 |
135 set context(context) { | 206 set context(context) { |
136 this.context_ = context; | 207 this.context_ = context; |
137 this.updateContent_(); | 208 this.updateContent_(); |
138 }, | 209 }, |
139 | 210 |
140 get unit() { | 211 get unit() { |
141 return this.unit_; | 212 return this.unit_; |
142 }, | 213 }, |
143 | 214 |
144 set unit(unit) { | 215 set unit(unit) { |
145 this.unit_ = unit; | 216 this.unit_ = unit; |
146 this.updateContent_(); | 217 this.updateContent_(); |
147 }, | 218 }, |
148 | 219 |
(...skipping 27 matching lines...) Expand all Loading... |
176 if (this.percentage_ === undefined) { | 247 if (this.percentage_ === undefined) { |
177 this.$.sparkline.style.display = 'none'; | 248 this.$.sparkline.style.display = 'none'; |
178 this.$.sparkline.style.width = '0'; | 249 this.$.sparkline.style.width = '0'; |
179 } else { | 250 } else { |
180 this.$.sparkline.style.display = 'block'; | 251 this.$.sparkline.style.display = 'block'; |
181 this.$.sparkline.style.width = (this.percentage_ * 100) + '%'; | 252 this.$.sparkline.style.width = (this.percentage_ * 100) + '%'; |
182 } | 253 } |
183 }, | 254 }, |
184 | 255 |
185 updateContent_: function() { | 256 updateContent_: function() { |
186 if (this.unit_ === undefined) { | 257 Polymer.dom(this.$.significance).textContent = ''; |
187 Polymer.dom(this.$.content).textContent = ''; | 258 Polymer.dom(this.$.content).textContent = ''; |
188 this.$.content.style.color = ''; | 259 Polymer.dom(this.$.content).classList.remove('better'); |
| 260 Polymer.dom(this.$.content).classList.remove('worse'); |
| 261 Polymer.dom(this.$.significance).classList.remove('better'); |
| 262 Polymer.dom(this.$.significance).classList.remove('worse'); |
| 263 |
| 264 if (this.unit_ === undefined) |
189 return; | 265 return; |
| 266 |
| 267 this.$.content.title = ''; |
| 268 Polymer.dom(this.$.content).textContent = |
| 269 this.unit_.format(this.value, this.context); |
| 270 this.updateDelta_(); |
| 271 }, |
| 272 |
| 273 updateDelta_: function() { |
| 274 if (!this.unit_.isDelta) |
| 275 return; |
| 276 |
| 277 var biggerIsBetter = false; |
| 278 var smallerIsBetter = false; |
| 279 switch (this.unit_.improvementDirection) { |
| 280 case tr.v.ImprovementDirection.DONT_CARE: |
| 281 return; |
| 282 |
| 283 case tr.v.ImprovementDirection.BIGGER_IS_BETTER: |
| 284 biggerIsBetter = true; |
| 285 break; |
| 286 |
| 287 case tr.v.ImprovementDirection.SMALLER_IS_BETTER: |
| 288 smallerIsBetter = true; |
| 289 break; |
190 } | 290 } |
191 | 291 |
192 Polymer.dom(this.$.content).textContent = | 292 var better = ((biggerIsBetter && this.value > 0) || |
193 this.unit_.format(this.value, this.context); | 293 (smallerIsBetter && this.value < 0)); |
| 294 var worse = ((biggerIsBetter && this.value < 0) || |
| 295 (smallerIsBetter && this.value > 0)); |
194 | 296 |
195 var BIGGER_IS_BETTER = tr.v.ImprovementDirection.BIGGER_IS_BETTER; | 297 var changeClass = ''; |
196 var SMALLER_IS_BETTER = tr.v.ImprovementDirection.SMALLER_IS_BETTER; | 298 var emoji = tr.v.ui.Emoji.NEUTRAL_FACE; |
197 var color = ''; | 299 var title = ''; |
198 if (this.unit_.isDelta) { | 300 |
199 var improvementDirection = this.unit_.improvementDirection; | 301 if (better) { |
200 if (this.value > 0) { | 302 changeClass = 'better'; |
201 // Positive delta. | 303 emoji = tr.v.ui.Emoji.GRINNING_FACE; |
202 switch (improvementDirection) { | 304 title = 'improvement'; |
203 case BIGGER_IS_BETTER: | 305 } else if (worse) { |
204 color = 'green'; | 306 changeClass = 'worse'; |
205 break; | 307 emoji = tr.v.ui.Emoji.CONFOUNDED_FACE; |
206 case SMALLER_IS_BETTER: | 308 title = 'regression'; |
207 color = 'red'; | 309 } else { |
208 break; | 310 title = 'no change'; |
209 } | 311 } |
210 } else if (this.value < 0) { | 312 |
211 // Negative delta. | 313 // Set the content class separately from the significance class so that |
212 switch (improvementDirection) { | 314 // NEUTRAL_FACE is always a neutral color. |
213 case BIGGER_IS_BETTER: | 315 if (changeClass) |
214 color = 'red'; | 316 Polymer.dom(this.$.content).classList.add(changeClass); |
215 break; | 317 |
216 case SMALLER_IS_BETTER: | 318 switch (this.significance) { |
217 color = 'green'; | 319 case tr.v.Significance.DONT_CARE: |
218 break; | 320 emoji = ''; |
219 } | 321 changeClass = ''; |
220 } | 322 break; |
| 323 |
| 324 case tr.v.Significance.INSIGNIFICANT: |
| 325 changeClass = ''; |
| 326 emoji = tr.v.ui.Emoji.NEUTRAL_FACE; |
| 327 if (better || worse) |
| 328 title = 'insignificant ' + title; |
| 329 break; |
| 330 |
| 331 case tr.v.Significance.SIGNIFICANT: |
| 332 if (!better && !worse) |
| 333 throw new Error('How can no change be significant?'); |
| 334 |
| 335 title = 'significant ' + title; |
| 336 break; |
| 337 } |
| 338 |
| 339 Polymer.dom(this.$.significance).textContent = emoji; |
| 340 this.$.significance.title = title; |
| 341 this.$.content.title = title; |
| 342 if (changeClass) |
| 343 Polymer.dom(this.$.significance).classList.add(changeClass); |
| 344 }, |
| 345 |
| 346 get warning() { |
| 347 return this.warning_; |
| 348 }, |
| 349 |
| 350 set warning(warning) { |
| 351 this.warning_ = warning; |
| 352 var warningEl = this.$.warning; |
| 353 if (this.warning_) { |
| 354 warningEl.title = warning; |
| 355 warningEl.style.display = ''; |
| 356 } else { |
| 357 warningEl.title = ''; |
| 358 warningEl.style.display = 'none'; |
221 } | 359 } |
222 this.$.content.style.color = color; | 360 this.$.content.style.color = color; |
223 }, | 361 }, |
224 | 362 |
225 get warning() { | 363 get warning() { |
226 return this.warning_; | 364 return this.warning_; |
227 }, | 365 }, |
228 | 366 |
229 set warning(warning) { | 367 set warning(warning) { |
230 this.warning_ = warning; | 368 this.warning_ = warning; |
(...skipping 27 matching lines...) Expand all Loading... |
258 | 396 |
259 set duration(duration) { | 397 set duration(duration) { |
260 if (duration instanceof tr.b.u.TimeDuration) { | 398 if (duration instanceof tr.b.u.TimeDuration) { |
261 this.value = duration; | 399 this.value = duration; |
262 return; | 400 return; |
263 } | 401 } |
264 this.setValueAndUnit(duration, tr.b.u.Units.timeDurationInMs); | 402 this.setValueAndUnit(duration, tr.b.u.Units.timeDurationInMs); |
265 } | 403 } |
266 }); | 404 }); |
267 </script> | 405 </script> |
OLD | NEW |