1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const {createNode} = require("devtools/client/animationinspector/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
new LocalizationHelper("devtools/client/locales/animationinspector.properties");
// List of playback rate presets displayed in the timeline toolbar.
const PLAYBACK_RATES = [.1, .25, .5, 1, 2, 5, 10];
/**
* UI component responsible for displaying a playback rate selector UI.
* The rendering logic is such that a predefined list of rates is generated.
* If *all* animations passed to render share the same rate, then that rate is
* selected in the <select> element, otherwise, the empty value is selected.
* If the rate that all animations share isn't part of the list of predefined
* rates, than that rate is added to the list.
*/
function RateSelector() {
this.onRateChanged = this.onRateChanged.bind(this);
EventEmitter.decorate(this);
}
exports.RateSelector = RateSelector;
RateSelector.prototype = {
init: function (containerEl) {
this.selectEl = createNode({
parent: containerEl,
nodeType: "select",
attributes: {
"class": "devtools-button",
"title": L10N.getStr("timeline.rateSelectorTooltip")
}
});
this.selectEl.addEventListener("change", this.onRateChanged);
},
destroy: function () {
this.selectEl.removeEventListener("change", this.onRateChanged);
this.selectEl.remove();
this.selectEl = null;
},
getAnimationsRates: function (animations) {
return sortedUnique(animations.map(a => a.state.playbackRate));
},
getAllRates: function (animations) {
let animationsRates = this.getAnimationsRates(animations);
if (animationsRates.length > 1) {
return PLAYBACK_RATES;
}
return sortedUnique(PLAYBACK_RATES.concat(animationsRates));
},
render: function (animations) {
let allRates = this.getAnimationsRates(animations);
let hasOneRate = allRates.length === 1;
this.selectEl.innerHTML = "";
if (!hasOneRate) {
// When the animations displayed have mixed playback rates, we can't
// select any of the predefined ones, instead, insert an empty rate.
createNode({
parent: this.selectEl,
nodeType: "option",
attributes: {value: "", selector: "true"},
textContent: "-"
});
}
for (let rate of this.getAllRates(animations)) {
let option = createNode({
parent: this.selectEl,
nodeType: "option",
attributes: {value: rate},
textContent: L10N.getFormatStr("player.playbackRateLabel", rate)
});
// If there's only one rate and this is the option for it, select it.
if (hasOneRate && rate === allRates[0]) {
option.setAttribute("selected", "true");
}
}
},
onRateChanged: function () {
let rate = parseFloat(this.selectEl.value);
if (!isNaN(rate)) {
this.emit("rate-changed", rate);
}
}
};
let sortedUnique = arr => [...new Set(arr)].sort((a, b) => a > b);
|