db4a90520bc809b43beac5a3bb40a7288632ca2f
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SpringEditor.js
1 /*
2  * Copyright (C) 2016 Devin Rousso <dcrousso+webkit@gmail.com>. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.SpringEditor = class SpringEditor extends WebInspector.Object
27 {
28     constructor()
29     {
30         super();
31
32         this._element = document.createElement("div");
33         this._element.classList.add("spring-editor");
34
35         this._previewContainer = this._element.createChild("div", "spring-preview");
36         this._previewContainer.title = WebInspector.UIString("Click to restart the animation");
37         this._previewContainer.addEventListener("mousedown", this._resetPreviewAnimation.bind(this));
38
39         this._previewElement = this._previewContainer.createChild("div");
40         this._previewElement.addEventListener("transitionend", this.debounce(500)._resetPreviewAnimation);
41
42         this._timingContainer = this._element.createChild("div", "spring-timing");
43
44         this._timingElement = this._timingContainer.createChild("div");
45
46         this._numberInputContainer = this._element.createChild("div", "number-input-container");
47
48         function createInputsForParameter(id, title)
49         {
50             let row = this._numberInputContainer.createChild("div", `number-input-row ${id}`);
51
52             row.createChild("div", "number-input-row-title").textContent = title;
53
54             let sliderKey = `_${id}Slider`;
55             this[sliderKey] = row.createChild("input");
56             this[sliderKey].type = "range";
57             this[sliderKey].addEventListener("input", this._handleNumberSliderInput.bind(this));
58             this[sliderKey].addEventListener("mousedown", this._handleNumberSliderMousedown.bind(this));
59             this[sliderKey].addEventListener("mouseup", this._handleNumberSliderMouseup.bind(this));
60
61             let inputKey = `_${id}Input`;
62             this[inputKey] = row.createChild("input");
63             this[inputKey].type = "number";
64             this[inputKey].addEventListener("input", this._handleNumberInputInput.bind(this));
65             this[inputKey].addEventListener("keydown", this._handleNumberInputKeydown.bind(this));
66         }
67
68         createInputsForParameter.call(this, "mass", WebInspector.UIString("Mass"));
69         this._massInput.min = this._massSlider.min = 1;
70
71         createInputsForParameter.call(this, "stiffness", WebInspector.UIString("Stiffness"));
72         this._stiffnessInput.min = this._stiffnessSlider.min = 1;
73
74         createInputsForParameter.call(this, "damping", WebInspector.UIString("Damping"));
75         this._dampingInput.min = this._dampingSlider.min = 0;
76
77         createInputsForParameter.call(this, "initialVelocity", WebInspector.UIString("Initial Velocity"));
78
79         this._spring = new WebInspector.Spring(1, 100, 10, 0);
80     }
81
82     // Public
83
84     get element()
85     {
86         return this._element;
87     }
88
89     get spring()
90     {
91         return this._spring;
92     }
93
94     set spring(spring)
95     {
96         if (!spring)
97             return;
98
99         let isSpring = spring instanceof WebInspector.Spring;
100         console.assert(isSpring);
101         if (!isSpring)
102             return;
103
104         this._spring = spring;
105         this._massInput.value = this._massSlider.value = this._spring.mass;
106         this._stiffnessInput.value = this._stiffnessSlider.value = this._spring.stiffness;
107         this._dampingInput.value = this._dampingSlider.value = this._spring.damping;
108         this._initialVelocityInput.value = this._initialVelocitySlider.value = this._spring.initialVelocity;
109         this._resetPreviewAnimation();
110     }
111
112     // Private
113
114     _handleNumberInputInput(event)
115     {
116         this._changeSpringForInput(event.target, event.target.value);
117     }
118
119     _handleNumberInputKeydown(event)
120     {
121         let shift = 0;
122         if (event.keyIdentifier === "Up")
123             shift = 1;
124          else if (event.keyIdentifier === "Down")
125             shift = -1;
126
127         if (!shift)
128             return;
129
130         if (event.shiftKey)
131             shift *= 10;
132         else if (event.altKey)
133             shift /= 10;
134
135         event.preventDefault();
136         this._changeSpringForInput(event.target, parseFloat(event.target.value) + shift);
137     }
138
139     _handleNumberSliderInput(event)
140     {
141         this._changeSpringForInput(event.target, event.target.value);
142     }
143
144     _handleNumberSliderMousedown(event)
145     {
146         this._changeSpringForInput(event.target, event.target.value);
147     }
148
149     _handleNumberSliderMouseup(event)
150     {
151         this._changeSpringForInput(event.target, event.target.value);
152     }
153
154     _changeSpringForInput(target, value)
155     {
156         value = parseFloat(value);
157
158         switch (target) {
159         case this._massInput:
160         case this._massSlider:
161             this._spring.mass = Math.max(1, value);
162             this._massInput.value = this._massSlider.value = this._spring.mass.maxDecimals(3);
163             break;
164         case this._stiffnessInput:
165         case this._stiffnessSlider:
166             this._spring.stiffness = Math.max(1, value);
167             this._stiffnessInput.value = this._stiffnessSlider.value = this._spring.stiffness.maxDecimals(3);
168             break;
169         case this._dampingInput:
170         case this._dampingSlider:
171             this._spring.damping = Math.max(0, value);
172             this._dampingInput.value = this._dampingSlider.value = this._spring.damping.maxDecimals(3);
173             break;
174         case this._initialVelocityInput:
175         case this._initialVelocitySlider:
176             this._spring.initialVelocity = value;
177             this._initialVelocityInput.value = this._initialVelocitySlider.value = this._spring.initialVelocity.maxDecimals(3);
178             break;
179         default:
180             return;
181         }
182
183         this.dispatchEventToListeners(WebInspector.SpringEditor.Event.SpringChanged, {spring: this._spring});
184
185         this._resetPreviewAnimation();
186     }
187
188     _resetPreviewAnimation(event)
189     {
190         this._previewContainer.classList.remove("animate");
191         this._previewElement.style.transitionTimingFunction = null;
192         this._previewElement.style.transform = null;
193
194         this._timingContainer.classList.remove("animate");
195         this._timingElement.style.transform = null;
196
197         // Only reset the duration text when a spring parameter is changed.
198         if (!event)
199             this._timingContainer.dataset.duration = "0";
200
201         this.debounce(500)._updatePreviewAnimation(event);
202     }
203
204     _updatePreviewAnimation(event)
205     {
206         this._previewContainer.classList.add("animate");
207         this._previewElement.style.transform = "translateX(85px)";
208         this._previewElement.style.transitionTimingFunction = this._spring.toString();
209
210         this._timingContainer.classList.add("animate");
211         this._timingElement.style.transform = "translateX(170px)";
212
213         // Only calculate the duration when a spring parameter is changed.
214         if (!event) {
215             let duration = this._spring.calculateDuration();
216
217             this._timingContainer.dataset.duration = duration.toFixed(2);
218             this._timingElement.style.transitionDuration = `${duration}s`;
219
220             this._previewElement.style.transitionDuration = `${duration}s`;
221         }
222     }
223 };
224
225 WebInspector.SpringEditor.Event = {
226     SpringChanged: "spring-editor-spring-changed"
227 };