• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5export class CSSColor {
6  static _cache = new Map();
7
8  static get(name) {
9    let color = this._cache.get(name);
10    if (color !== undefined) return color;
11    const style = getComputedStyle(document.body);
12    color = style.getPropertyValue(`--${name}`);
13    if (color === undefined) {
14      throw new Error(`CSS color does not exist: ${name}`);
15    }
16    color = color.trim();
17    this._cache.set(name, color);
18    return color;
19  }
20
21  static reset() {
22    this._cache.clear();
23  }
24
25  static get backgroundColor() {
26    return this.get('background-color');
27  }
28  static get surfaceColor() {
29    return this.get('surface-color');
30  }
31  static get primaryColor() {
32    return this.get('primary-color');
33  }
34  static get secondaryColor() {
35    return this.get('secondary-color');
36  }
37  static get onSurfaceColor() {
38    return this.get('on-surface-color');
39  }
40  static get onBackgroundColor() {
41    return this.get('on-background-color');
42  }
43  static get onPrimaryColor() {
44    return this.get('on-primary-color');
45  }
46  static get onSecondaryColor() {
47    return this.get('on-secondary-color');
48  }
49  static get defaultColor() {
50    return this.get('default-color');
51  }
52  static get errorColor() {
53    return this.get('error-color');
54  }
55  static get mapBackgroundColor() {
56    return this.get('map-background-color');
57  }
58  static get timelineBackgroundColor() {
59    return this.get('timeline-background-color');
60  }
61  static get red() {
62    return this.get('red');
63  }
64  static get green() {
65    return this.get('green');
66  }
67  static get yellow() {
68    return this.get('yellow');
69  }
70  static get blue() {
71    return this.get('blue');
72  }
73
74  static get orange() {
75    return this.get('orange');
76  }
77
78  static get violet() {
79    return this.get('violet');
80  }
81
82  static at(index) {
83    return this.list[index % this.list.length];
84  }
85
86  static darken(hexColorString, amount = -50) {
87    if (hexColorString[0] !== '#') {
88      throw new Error(`Unsupported color: ${hexColorString}`);
89    }
90    let color = parseInt(hexColorString.substring(1), 16);
91    let b = Math.min(Math.max((color & 0xFF) + amount, 0), 0xFF);
92    let g = Math.min(Math.max(((color >> 8) & 0xFF) + amount, 0), 0xFF);
93    let r = Math.min(Math.max(((color >> 16) & 0xFF) + amount, 0), 0xFF);
94    color = (r << 16) + (g << 8) + b;
95    return `#${color.toString(16).padStart(6, '0')}`;
96  }
97
98  static get list() {
99    if (!this._colors) {
100      this._colors = [
101        this.green,
102        this.violet,
103        this.orange,
104        this.yellow,
105        this.primaryColor,
106        this.red,
107        this.blue,
108        this.yellow,
109        this.secondaryColor,
110        this.darken(this.green),
111        this.darken(this.violet),
112        this.darken(this.orange),
113        this.darken(this.yellow),
114        this.darken(this.primaryColor),
115        this.darken(this.red),
116        this.darken(this.blue),
117        this.darken(this.yellow),
118        this.darken(this.secondaryColor),
119      ];
120    }
121    return this._colors;
122  }
123}
124
125import {DOM} from '../../js/web-api-helper.mjs';
126
127const SVGNamespace = 'http://www.w3.org/2000/svg';
128export class SVG {
129  static element(type, classes) {
130    const node = document.createElementNS(SVGNamespace, type);
131    if (classes !== undefined) DOM.addClasses(node, classes);
132    return node;
133  }
134
135  static svg(classes) {
136    return this.element('svg', classes);
137  }
138
139  static rect(classes) {
140    return this.element('rect', classes);
141  }
142
143  static g(classes) {
144    return this.element('g', classes);
145  }
146}
147
148export function $(id) {
149  return document.querySelector(id)
150}
151
152import {V8CustomElement} from '../../js/web-api-helper.mjs'
153
154export class CollapsableElement extends V8CustomElement {
155  constructor(templateText) {
156    super(templateText);
157    this._hasPendingUpdate = false;
158    this._closer.onclick = _ => this._requestUpdateIfVisible();
159  }
160
161  get _closer() {
162    return this.$('#closer');
163  }
164
165  get _contentIsVisible() {
166    return !this._closer.checked;
167  }
168
169  hide() {
170    if (this._contentIsVisible) {
171      this._closer.checked = true;
172      this._requestUpdateIfVisible();
173    }
174  }
175
176  show() {
177    if (!this._contentIsVisible) {
178      this._closer.checked = false;
179      this._requestUpdateIfVisible();
180    }
181    this.scrollIntoView({behavior: 'smooth', block: 'center'});
182  }
183
184  requestUpdate(useAnimation = false) {
185    // A pending update will be resolved later, no need to try again.
186    if (this._hasPendingUpdate) return;
187    this._hasPendingUpdate = true;
188    this._requestUpdateIfVisible(useAnimation);
189  }
190
191  _requestUpdateIfVisible(useAnimation = true) {
192    if (!this._contentIsVisible) return;
193    return super.requestUpdate(useAnimation);
194  }
195
196  forceUpdate() {
197    this._hasPendingUpdate = false;
198    super.forceUpdate();
199  }
200}
201
202export class ExpandableText {
203  constructor(node, string, limit = 200) {
204    this._node = node;
205    this._string = string;
206    this._delta = limit / 2;
207    this._start = 0;
208    this._end = string.length;
209    this._button = this._createExpandButton();
210    this.expand();
211  }
212
213  _createExpandButton() {
214    const button = DOM.element('button');
215    button.innerText = '...';
216    button.onclick = (e) => {
217      e.stopImmediatePropagation();
218      this.expand(e.shiftKey);
219    };
220    button.title = 'Expand text. Use SHIFT-click to show all.'
221    return button;
222  }
223
224  expand(showAll = false) {
225    DOM.removeAllChildren(this._node);
226    this._start = this._start + this._delta;
227    this._end = this._end - this._delta;
228    if (this._start >= this._end || showAll) {
229      this._node.innerText = this._string;
230      this._button.onclick = undefined;
231      return;
232    }
233    this._node.appendChild(DOM.text(this._string.substring(0, this._start)));
234    this._node.appendChild(this._button);
235    this._node.appendChild(
236        DOM.text(this._string.substring(this._end, this._string.length)));
237  }
238}
239
240export class Chunked {
241  constructor(iterable, limit) {
242    this._iterator = iterable[Symbol.iterator]();
243    this._limit = limit;
244  }
245
246  * next(limit = undefined) {
247    for (let i = 0; i < (limit ?? this._limit); i++) {
248      const {value, done} = this._iterator.next();
249      if (done) {
250        this._iterator = undefined;
251        return;
252      };
253      yield value;
254    }
255  }
256
257  get hasMore() {
258    return this._iterator !== undefined;
259  }
260}
261
262export class LazyTable {
263  constructor(table, rowData, rowElementCreator, limit = 100) {
264    this._table = table;
265    this._chunkedRowData = new Chunked(rowData, limit);
266    this._rowElementCreator = rowElementCreator;
267    if (table.tBodies.length == 0) {
268      table.appendChild(DOM.tbody());
269    } else {
270      table.replaceChild(DOM.tbody(), table.tBodies[0]);
271    }
272    if (!table.tFoot) this._addFooter();
273    table.tFoot.addEventListener('click', this._clickHandler);
274    this._addMoreRows();
275  }
276
277  _addFooter() {
278    const td = DOM.td();
279    td.setAttribute('colspan', 100);
280    for (let addCount of [10, 100, 250, 500]) {
281      const button = DOM.element('button');
282      button.innerText = `+${addCount}`;
283      button.onclick = (e) => this._addMoreRows(addCount);
284      td.appendChild(button);
285    }
286    this._table.appendChild(DOM.element('tfoot'))
287        .appendChild(DOM.tr())
288        .appendChild(td);
289  }
290
291  _addMoreRows(count = undefined) {
292    const fragment = new DocumentFragment();
293    for (let row of this._chunkedRowData.next(count)) {
294      const tr = this._rowElementCreator(row);
295      fragment.appendChild(tr);
296    }
297    this._table.tBodies[0].appendChild(fragment);
298    if (!this._chunkedRowData.hasMore) {
299      DOM.removeAllChildren(this._table.tFoot);
300    }
301  }
302}
303
304export function gradientStopsFromGroups(
305    totalLength, maxHeight, groups, colorFn) {
306  const kMaxHeight = maxHeight === '%' ? 100 : maxHeight;
307  const kUnit = maxHeight === '%' ? '%' : 'px';
308  let increment = 0;
309  let lastHeight = 0.0;
310  const stops = [];
311  for (let group of groups) {
312    const color = colorFn(group.key);
313    increment += group.length;
314    const height = (increment / totalLength * kMaxHeight) | 0;
315    stops.push(`${color} ${lastHeight}${kUnit} ${height}${kUnit}`)
316    lastHeight = height;
317  }
318  return stops;
319}
320
321export * from '../helper.mjs';
322export * from '../../js/web-api-helper.mjs'
323