Title: [204775] trunk/Source/WebInspectorUI
Revision
204775
Author
[email protected]
Date
2016-08-23 00:23:24 -0700 (Tue, 23 Aug 2016)

Log Message

Web Inspector: Add visual editor for spring() timing-function
https://bugs.webkit.org/show_bug.cgi?id=159315

Patch by Devin Rousso <[email protected]> on 2016-08-23
Reviewed by Joseph Pecoraro.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Main.html:

* UserInterface/Base/Utilities.js:
(Number.prototype.maxDecimals):
Limits the number of decimals for the number object via rounding.

* UserInterface/Controllers/CodeMirrorSpringEditingController.js:
(WebInspector.CodeMirrorSpringEditingController.prototype.get initialValue):
(WebInspector.CodeMirrorSpringEditingController.prototype.get cssClassName):
(WebInspector.CodeMirrorSpringEditingController.prototype.popoverWillPresent):
(WebInspector.CodeMirrorSpringEditingController.prototype.popoverDidPresent):
(WebInspector.CodeMirrorSpringEditingController.prototype.popoverDidDismiss):
(WebInspector.CodeMirrorSpringEditingController.prototype._springEditorSpringChanged):
Controller for Spring editors inside CodeMirror instances.

* UserInterface/Models/Geometry.js:
(WebInspector.Spring):
(WebInspector.Spring.fromValues):
(WebInspector.Spring.fromString):
(WebInspector.Spring.prototype.copy):
(WebInspector.Spring.prototype.toString):
(WebInspector.Spring.prototype.solve):
(WebInspector.Spring.prototype.calculateDuration):
Spring data class based on <https://webkit.org/demos/spring/>.

* UserInterface/Models/TextMarker.js:
(WebInspector.TextMarker.Type):
Added Spring marker type.

* UserInterface/Views/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createInlineSwatches):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createInlineSwatches.update):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._inlineSwatchValueChanged):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._inlineSwatchValueChanged.update):

* UserInterface/Views/CodeMirrorTextMarkers.js:
(createCodeMirrorSpringTextMarkers):

* UserInterface/Views/InlineSwatch.css:
(.inline-swatch:matches(.bezier, .spring)):
(.inline-swatch:matches(.bezier, .spring):hover):
(.inline-swatch:matches(.bezier, .spring):active):
(.inline-swatch:matches(.bezier, .spring) > span):
(.inline-swatch.bezier): Deleted.
(.inline-swatch.bezier:hover): Deleted.
(.inline-swatch.bezier:active): Deleted.
(.inline-swatch.bezier > span): Deleted.

* UserInterface/Views/InlineSwatch.js:
(WebInspector.InlineSwatch):
(WebInspector.InlineSwatch.prototype._fallbackValue):
(WebInspector.InlineSwatch.prototype._swatchElementClicked):
(WebInspector.InlineSwatch.prototype._valueEditorValueDidChange):
(WebInspector.InlineSwatch.Type):
Added support for Spring editor Inline Swatches.

* UserInterface/Views/SourceCodeTextEditor.js:
(WebInspector.SourceCodeTextEditor.prototype._updateEditableMarkers):
(WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedMarkedExpression):

* UserInterface/Views/SpringEditor.css:
(.spring-editor):
(.spring-editor > .spring-preview):
(.spring-editor > .spring-preview > div):
(.spring-editor > .spring-timing):
(.spring-editor > .spring-timing > div):
(.spring-editor > .spring-timing:matches(::before, ::after)):
(.spring-editor > .spring-timing::before):
(.spring-editor > .spring-timing::after):
(.spring-editor > :matches(.spring-preview, .spring-timing) > div):
(.spring-editor > .animate:matches(.spring-preview, .spring-timing) > div):
(.spring-editor > .number-input-container > .number-input-row):
(.spring-editor > .number-input-container > .number-input-row > .number-input-row-title):
(.spring-editor > .number-input-container > .number-input-row > input):
(.spring-editor > .number-input-container > .number-input-row > input[type="range"]):

* UserInterface/Views/SpringEditor.js:
(WebInspector.SpringEditor):
(WebInspector.SpringEditor.prototype.get element):
(WebInspector.SpringEditor.prototype.set spring):
(WebInspector.SpringEditor.prototype.get spring):
(WebInspector.SpringEditor.prototype._handleNumberInputInput):
(WebInspector.SpringEditor.prototype._handleNumberInputKeydown):
(WebInspector.SpringEditor.prototype._handleNumberSliderInput):
(WebInspector.SpringEditor.prototype._handleNumberSliderMousedown):
(WebInspector.SpringEditor.prototype._handleNumberSliderMouseup):
(WebInspector.SpringEditor.prototype._changeSpringForInput):
(WebInspector.SpringEditor.prototype._resetPreviewAnimation):
(WebInspector.SpringEditor.prototype._updatePreviewAnimation):
(WebInspector.SpringEditor.Event):
Visual editor for CSS Spring timing function values.  Organized into 5 rows:
 - Preview: shows movement of spring and total duration
 - Mass: slider and input for "mass" parameter
 - Stiffness: slider and input for "stiffness" parameter
 - Damping: slider and input for "damping" parameter
 - Initial Velocity: slider and input for "initial velocity" parameter
The preview animation will loop indefinitely until the user clicks the animation row
or changes the value of one of the parameters, at which point the duration will also
be recalculated.

* UserInterface/Views/TextEditor.js:
(WebInspector.TextEditor.prototype.createSpringMarkers):
(WebInspector.TextEditor.prototype.editingControllerForMarker):

* UserInterface/Views/VisualStyleCommaSeparatedKeywordEditor.js:
(WebInspector.VisualStyleCommaSeparatedKeywordEditor.prototype.set value):
(WebInspector.VisualStyleCommaSeparatedKeywordEditor.prototype._generateTextFromLonghandProperties):
Add support for CSS spring timing functions by allowing parenthesis in comma separated
values so long as there are no commas inside the parenthesis.

* UserInterface/Views/VisualStyleTimingEditor.css:
(.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch:matches(.bezier, .spring)):
(.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.spring-value) > .inline-swatch.spring):
(.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch.bezier): Deleted.
(.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.bezier-value) > .inline-swatch.bezier): Deleted.

* UserInterface/Views/VisualStyleTimingEditor.js:
(WebInspector.VisualStyleTimingEditor):
(WebInspector.VisualStyleTimingEditor.prototype.get value):
(WebInspector.VisualStyleTimingEditor.prototype.set value):
(WebInspector.VisualStyleTimingEditor.prototype.get synthesizedValue):
(WebInspector.VisualStyleTimingEditor.prototype.parseValue):
(WebInspector.VisualStyleTimingEditor.prototype.get _bezierValue):
(WebInspector.VisualStyleTimingEditor.prototype.set _bezierValue):
(WebInspector.VisualStyleTimingEditor.prototype.get _springValue):
(WebInspector.VisualStyleTimingEditor.prototype.set _springValue):
(WebInspector.VisualStyleTimingEditor.prototype._handleKeywordChanged):
(WebInspector.VisualStyleTimingEditor.prototype._springSwatchValueChanged):
(WebInspector.VisualStyleTimingEditor.prototype.get bezierValue): Renamed.
(WebInspector.VisualStyleTimingEditor.prototype.set bezierValue): Renamed.
Duplicate Bezier custom timing function logic for Spring timing functions.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (204774 => 204775)


--- trunk/Source/WebInspectorUI/ChangeLog	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/ChangeLog	2016-08-23 07:23:24 UTC (rev 204775)
@@ -1,3 +1,143 @@
+2016-08-23  Devin Rousso  <[email protected]>
+
+        Web Inspector: Add visual editor for spring() timing-function
+        https://bugs.webkit.org/show_bug.cgi?id=159315
+
+        Reviewed by Joseph Pecoraro.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Main.html:
+
+        * UserInterface/Base/Utilities.js:
+        (Number.prototype.maxDecimals):
+        Limits the number of decimals for the number object via rounding.
+
+        * UserInterface/Controllers/CodeMirrorSpringEditingController.js:
+        (WebInspector.CodeMirrorSpringEditingController.prototype.get initialValue):
+        (WebInspector.CodeMirrorSpringEditingController.prototype.get cssClassName):
+        (WebInspector.CodeMirrorSpringEditingController.prototype.popoverWillPresent):
+        (WebInspector.CodeMirrorSpringEditingController.prototype.popoverDidPresent):
+        (WebInspector.CodeMirrorSpringEditingController.prototype.popoverDidDismiss):
+        (WebInspector.CodeMirrorSpringEditingController.prototype._springEditorSpringChanged):
+        Controller for Spring editors inside CodeMirror instances.
+
+        * UserInterface/Models/Geometry.js:
+        (WebInspector.Spring):
+        (WebInspector.Spring.fromValues):
+        (WebInspector.Spring.fromString):
+        (WebInspector.Spring.prototype.copy):
+        (WebInspector.Spring.prototype.toString):
+        (WebInspector.Spring.prototype.solve):
+        (WebInspector.Spring.prototype.calculateDuration):
+        Spring data class based on <https://webkit.org/demos/spring/>.
+
+        * UserInterface/Models/TextMarker.js:
+        (WebInspector.TextMarker.Type):
+        Added Spring marker type.
+
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createInlineSwatches):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createInlineSwatches.update):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._inlineSwatchValueChanged):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._inlineSwatchValueChanged.update):
+
+        * UserInterface/Views/CodeMirrorTextMarkers.js:
+        (createCodeMirrorSpringTextMarkers):
+
+        * UserInterface/Views/InlineSwatch.css:
+        (.inline-swatch:matches(.bezier, .spring)):
+        (.inline-swatch:matches(.bezier, .spring):hover):
+        (.inline-swatch:matches(.bezier, .spring):active):
+        (.inline-swatch:matches(.bezier, .spring) > span):
+        (.inline-swatch.bezier): Deleted.
+        (.inline-swatch.bezier:hover): Deleted.
+        (.inline-swatch.bezier:active): Deleted.
+        (.inline-swatch.bezier > span): Deleted.
+
+        * UserInterface/Views/InlineSwatch.js:
+        (WebInspector.InlineSwatch):
+        (WebInspector.InlineSwatch.prototype._fallbackValue):
+        (WebInspector.InlineSwatch.prototype._swatchElementClicked):
+        (WebInspector.InlineSwatch.prototype._valueEditorValueDidChange):
+        (WebInspector.InlineSwatch.Type):
+        Added support for Spring editor Inline Swatches.
+
+        * UserInterface/Views/SourceCodeTextEditor.js:
+        (WebInspector.SourceCodeTextEditor.prototype._updateEditableMarkers):
+        (WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedMarkedExpression):
+
+        * UserInterface/Views/SpringEditor.css:
+        (.spring-editor):
+        (.spring-editor > .spring-preview):
+        (.spring-editor > .spring-preview > div):
+        (.spring-editor > .spring-timing):
+        (.spring-editor > .spring-timing > div):
+        (.spring-editor > .spring-timing:matches(::before, ::after)):
+        (.spring-editor > .spring-timing::before):
+        (.spring-editor > .spring-timing::after):
+        (.spring-editor > :matches(.spring-preview, .spring-timing) > div):
+        (.spring-editor > .animate:matches(.spring-preview, .spring-timing) > div):
+        (.spring-editor > .number-input-container > .number-input-row):
+        (.spring-editor > .number-input-container > .number-input-row > .number-input-row-title):
+        (.spring-editor > .number-input-container > .number-input-row > input):
+        (.spring-editor > .number-input-container > .number-input-row > input[type="range"]):
+
+        * UserInterface/Views/SpringEditor.js:
+        (WebInspector.SpringEditor):
+        (WebInspector.SpringEditor.prototype.get element):
+        (WebInspector.SpringEditor.prototype.set spring):
+        (WebInspector.SpringEditor.prototype.get spring):
+        (WebInspector.SpringEditor.prototype._handleNumberInputInput):
+        (WebInspector.SpringEditor.prototype._handleNumberInputKeydown):
+        (WebInspector.SpringEditor.prototype._handleNumberSliderInput):
+        (WebInspector.SpringEditor.prototype._handleNumberSliderMousedown):
+        (WebInspector.SpringEditor.prototype._handleNumberSliderMouseup):
+        (WebInspector.SpringEditor.prototype._changeSpringForInput):
+        (WebInspector.SpringEditor.prototype._resetPreviewAnimation):
+        (WebInspector.SpringEditor.prototype._updatePreviewAnimation):
+        (WebInspector.SpringEditor.Event):
+        Visual editor for CSS Spring timing function values.  Organized into 5 rows:
+         - Preview: shows movement of spring and total duration
+         - Mass: slider and input for "mass" parameter
+         - Stiffness: slider and input for "stiffness" parameter
+         - Damping: slider and input for "damping" parameter
+         - Initial Velocity: slider and input for "initial velocity" parameter
+        The preview animation will loop indefinitely until the user clicks the animation row
+        or changes the value of one of the parameters, at which point the duration will also
+        be recalculated.
+
+        * UserInterface/Views/TextEditor.js:
+        (WebInspector.TextEditor.prototype.createSpringMarkers):
+        (WebInspector.TextEditor.prototype.editingControllerForMarker):
+
+        * UserInterface/Views/VisualStyleCommaSeparatedKeywordEditor.js:
+        (WebInspector.VisualStyleCommaSeparatedKeywordEditor.prototype.set value):
+        (WebInspector.VisualStyleCommaSeparatedKeywordEditor.prototype._generateTextFromLonghandProperties):
+        Add support for CSS spring timing functions by allowing parenthesis in comma separated
+        values so long as there are no commas inside the parenthesis.
+
+        * UserInterface/Views/VisualStyleTimingEditor.css:
+        (.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch:matches(.bezier, .spring)):
+        (.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.spring-value) > .inline-swatch.spring):
+        (.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch.bezier): Deleted.
+        (.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.bezier-value) > .inline-swatch.bezier): Deleted.
+
+        * UserInterface/Views/VisualStyleTimingEditor.js:
+        (WebInspector.VisualStyleTimingEditor):
+        (WebInspector.VisualStyleTimingEditor.prototype.get value):
+        (WebInspector.VisualStyleTimingEditor.prototype.set value):
+        (WebInspector.VisualStyleTimingEditor.prototype.get synthesizedValue):
+        (WebInspector.VisualStyleTimingEditor.prototype.parseValue):
+        (WebInspector.VisualStyleTimingEditor.prototype.get _bezierValue):
+        (WebInspector.VisualStyleTimingEditor.prototype.set _bezierValue):
+        (WebInspector.VisualStyleTimingEditor.prototype.get _springValue):
+        (WebInspector.VisualStyleTimingEditor.prototype.set _springValue):
+        (WebInspector.VisualStyleTimingEditor.prototype._handleKeywordChanged):
+        (WebInspector.VisualStyleTimingEditor.prototype._springSwatchValueChanged):
+        (WebInspector.VisualStyleTimingEditor.prototype.get bezierValue): Renamed.
+        (WebInspector.VisualStyleTimingEditor.prototype.set bezierValue): Renamed.
+        Duplicate Bezier custom timing function logic for Spring timing functions.
+
 2016-08-22  Devin Rousso  <[email protected]>
 
         Web Inspector: Styles -> Computed -> Box Model shows NaN x NaN for invisible elements

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -102,6 +102,7 @@
 localizedStrings["Back (%s)"] = "Back (%s)";
 localizedStrings["Background"] = "Background";
 localizedStrings["Basis"] = "Basis";
+localizedStrings["Bezier"] = "Bezier";
 localizedStrings["Blend"] = "Blend";
 localizedStrings["Block Variables"] = "Block Variables";
 localizedStrings["Blur"] = "Blur";
@@ -153,6 +154,7 @@
 localizedStrings["Click to enable the selected rule"] = "Click to enable the selected rule";
 localizedStrings["Click to link property values"] = "Click to link property values";
 localizedStrings["Click to open a cubic-bezier editor."] = "Click to open a cubic-bezier editor.";
+localizedStrings["Click to open a spring editor."] = "Click to open a spring editor.";
 localizedStrings["Click to remove link"] = "Click to remove link";
 localizedStrings["Click to remove the selected item."] = "Click to remove the selected item.";
 localizedStrings["Click to restart the animation"] = "Click to restart the animation";
@@ -208,8 +210,8 @@
 localizedStrings["Create a new tab"] = "Create a new tab";
 localizedStrings["Current"] = "Current";
 localizedStrings["Cursor"] = "Cursor";
-localizedStrings["Custom"] = "Custom";
 localizedStrings["DOM Content Loaded \u2014 %s"] = "DOM Content Loaded \u2014 %s";
+localizedStrings["Damping"] = "Damping";
 localizedStrings["Dash Array"] = "Dash Array";
 localizedStrings["Data"] = "Data";
 localizedStrings["Data returned from the database is too large."] = "Data returned from the database is too large.";
@@ -402,6 +404,7 @@
 localizedStrings["Info: "] = "Info: ";
 localizedStrings["Inherited From: "] = "Inherited From: ";
 localizedStrings["Inherited from %s"] = "Inherited from %s";
+localizedStrings["Initial Velocity"] = "Initial Velocity";
 localizedStrings["Initiated"] = "Initiated";
 localizedStrings["Initiator"] = "Initiator";
 localizedStrings["Input: "] = "Input: ";
@@ -454,6 +457,7 @@
 localizedStrings["Main Frame"] = "Main Frame";
 localizedStrings["Manifest URL"] = "Manifest URL";
 localizedStrings["Margin"] = "Margin";
+localizedStrings["Mass"] = "Mass";
 localizedStrings["Max"] = "Max";
 localizedStrings["Max Comparison"] = "Max Comparison";
 localizedStrings["Maximum"] = "Maximum";
@@ -674,6 +678,7 @@
 localizedStrings["Specificity: No value for selected element"] = "Specificity: No value for selected element";
 localizedStrings["Spelling"] = "Spelling";
 localizedStrings["Spread"] = "Spread";
+localizedStrings["Spring"] = "Spring";
 localizedStrings["Start Playback"] = "Start Playback";
 localizedStrings["Start Recording"] = "Start Recording";
 localizedStrings["Start Time"] = "Start Time";
@@ -685,6 +690,7 @@
 localizedStrings["Step into (%s or %s)"] = "Step into (%s or %s)";
 localizedStrings["Step out (%s or %s)"] = "Step out (%s or %s)";
 localizedStrings["Step over (%s or %s)"] = "Step over (%s or %s)";
+localizedStrings["Stiffness"] = "Stiffness";
 localizedStrings["Stop Recording"] = "Stop Recording";
 localizedStrings["Stop element selection (%s)"] = "Stop element selection (%s)";
 localizedStrings["Stop recording (%s)"] = "Stop recording (%s)";

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -1069,6 +1069,15 @@
     }
 });
 
+Object.defineProperty(Number.prototype, "maxDecimals",
+{
+    value(decimals)
+    {
+        let power = 10 ** decimals;
+        return Math.round(this * power) / power;
+    }
+});
+
 Object.defineProperty(Uint32Array, "isLittleEndian",
 {
     value: function()

Copied: trunk/Source/WebInspectorUI/UserInterface/Controllers/CodeMirrorSpringEditingController.js (from rev 204772, trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.css) (0 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/CodeMirrorSpringEditingController.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/CodeMirrorSpringEditingController.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 Devin Rousso <[email protected]>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CodeMirrorSpringEditingController = class CodeMirrorSpringEditingController extends WebInspector.CodeMirrorEditingController
+{
+    // Public
+
+    get initialValue()
+    {
+        return WebInspector.Spring.fromString(this.text);
+    }
+
+    get cssClassName()
+    {
+        return "spring";
+    }
+
+    popoverWillPresent(popover)
+    {
+        this._springEditor = new WebInspector.SpringEditor;
+        this._springEditor.addEventListener(WebInspector.SpringEditor.Event.SpringChanged, this._springEditorSpringChanged, this);
+        popover.content = this._springEditor.element;
+    }
+
+    popoverDidPresent(popover)
+    {
+        this._springEditor.spring = this.value;
+    }
+
+    popoverDidDismiss(popover)
+    {
+        this._springEditor.removeListeners();
+    }
+
+    // Private
+
+    _springEditorSpringChanged(event)
+    {
+        this.value = event.data.spring;
+    }
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2016-08-23 07:23:24 UTC (rev 204775)
@@ -151,6 +151,7 @@
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
+    <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
@@ -621,6 +622,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""
@@ -680,6 +682,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -476,3 +476,96 @@
     "ease-in-out":  [0.42, 0, 0.58, 1],
     "linear":       [0, 0, 1, 1]
 };
+
+WebInspector.Spring = class Spring
+{
+    constructor(mass, stiffness, damping, initialVelocity)
+    {
+        this.mass = Math.max(1, mass);
+        this.stiffness = Math.max(1, stiffness);
+        this.damping = Math.max(0, damping);
+        this.initialVelocity = initialVelocity;
+    }
+
+    // Static
+
+    static fromValues(values)
+    {
+        if (!values || values.length < 4)
+            return null;
+
+        values = values.map(Number);
+        if (values.includes(NaN))
+            return null;
+
+        return new WebInspector.Spring(...values);
+    }
+
+    static fromString(text)
+    {
+        if (!text || !text.length)
+            return null;
+
+        let trimmedText = text.toLowerCase().trim();
+        if (!trimmedText.length)
+            return null;
+
+        let matches = trimmedText.match(/^spring\(([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([-\d.]+)\)$/);
+        if (!matches)
+            return null;
+
+        return WebInspector.Spring.fromValues(matches.slice(1));
+    }
+
+    // Public
+
+    copy()
+    {
+        return new WebInspector.Spring(this.mass, this.stiffness, this.damping, this.initialVelocity);
+    }
+
+    toString()
+    {
+        return `spring(${this.mass} ${this.stiffness} ${this.damping} ${this.initialVelocity})`;
+    }
+
+    solve(t)
+    {
+        let w0 = Math.sqrt(this.stiffness / this.mass);
+        let zeta = this.damping / (2 * Math.sqrt(this.stiffness * this.mass));
+
+        let wd = 0;
+        let A = 1;
+        let B = -this.initialVelocity + w0;
+        if (zeta < 1) {
+            // Under-damped.
+            wd = w0 * Math.sqrt(1 - zeta * zeta);
+            A = 1;
+            B = (zeta * w0 + -this.initialVelocity) / wd;
+        }
+
+        if (zeta < 1) // Under-damped
+            t = Math.exp(-t * zeta * w0) * (A * Math.cos(wd * t) + B * Math.sin(wd * t));
+        else // Critically damped (ignoring over-damped case).
+            t = (A + B * t) * Math.exp(-t * w0);
+
+        return 1 - t; // Map range from [1..0] to [0..1].
+    }
+
+    calculateDuration(epsilon)
+    {
+        epsilon = epsilon || 0.0001;
+        let t = 0;
+        let current = 0;
+        let minimum = Number.POSITIVE_INFINITY;
+        while (current >= epsilon || minimum >= epsilon) {
+            current = Math.abs(1 - this.solve(t)); // Undo the range mapping
+            if (minimum < epsilon && current >= epsilon)
+                minimum = Number.POSITIVE_INFINITY; // Spring reversed direction
+            else if (current < minimum)
+                minimum = current;
+            t += 0.1;
+        }
+        return t;
+    }
+}

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/TextMarker.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Models/TextMarker.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/TextMarker.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -83,5 +83,6 @@
     Color: "text-marker-type-color",
     Gradient: "text-marker-type-gradient",
     Plain: "text-marker-type-plain",
-    CubicBezier: "text-marker-type-cubic-bezier"
+    CubicBezier: "text-marker-type-cubic-bezier",
+    Spring: "text-marker-type-spring",
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -886,6 +886,12 @@
                 let swatch = new WebInspector.InlineSwatch(WebInspector.InlineSwatch.Type.Bezier, bezier, this._codeMirror.getOption("readOnly"));
                 createSwatch.call(this, swatch, marker, bezier, bezierString);
             });
+
+            // Look for spring strings and add swatches in front of them.
+            createCodeMirrorSpringTextMarkers(this._codeMirror, range, (marker, spring, springString) => {
+                let swatch = new WebInspector.InlineSwatch(WebInspector.InlineSwatch.Type.Spring, spring, this._codeMirror.getOption("readOnly"));
+                createSwatch.call(this, swatch, marker, spring, springString);
+            });
         }
 
         if (nonatomic)
@@ -1318,8 +1324,9 @@
 
                 for (let mark of marks) {
                     let type = WebInspector.TextMarker.textMarkerForCodeMirrorTextMarker(mark).type;
-                    if (type !== WebInspector.TextMarker.Type.Color && type !== WebInspector.TextMarker.Type.Gradient && type !== WebInspector.TextMarker.Type.CubicBezier)
+                    if (Object.values(WebInspector.TextMarker.Type).includes(type))
                         continue;
+
                     textMarker = mark;
                     break;
                 }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorTextMarkers.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorTextMarkers.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorTextMarkers.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -195,3 +195,9 @@
     var cubicBezierRegex = /(cubic-bezier\([^)]+\)|\b\w+\b(?:-\b\w+\b){0,2})/g;
     return createCodeMirrorTextMarkers("CubicBezier", cubicBezierRegex, null, codeMirror, range, callback);
 }
+
+function createCodeMirrorSpringTextMarkers(codeMirror, range, callback)
+{
+    const springRegex = /(spring\([^)]+\))/g;
+    return createCodeMirrorTextMarkers("Spring", springRegex, null, codeMirror, range, callback);
+}

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css	2016-08-23 07:23:24 UTC (rev 204775)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2015 Apple Inc. All rights reserved.
- * Copyright (C) 2015 Devin Rousso <[email protected]>. All rights reserved.
+ * Copyright (C) 2015-2016 Devin Rousso <[email protected]>. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,17 +43,20 @@
     cursor: default;
 }
 
-.inline-swatch.bezier {
+.inline-swatch:matches(.bezier, .spring) {
+    content: url(../Images/CubicBezier.svg);
+}
+
+.inline-swatch:matches(.bezier, .spring) {
     margin-right: 2px;
-    content: url(../Images/CubicBezier.svg);
     background: none;
 }
 
-.inline-swatch.bezier:hover {
+.inline-swatch:matches(.bezier, .spring):hover {
     filter: brightness(0.9);
 }
 
-.inline-swatch.bezier:active {
+.inline-swatch:matches(.bezier, .spring):active {
     filter: brightness(0.8);
 }
 
@@ -85,7 +88,7 @@
     border-color: hsl(0, 0%, 25%);
 }
 
-.inline-swatch.bezier > span {
+.inline-swatch:matches(.bezier, .spring) > span {
     display: none;
 }
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -39,6 +39,9 @@
         case WebInspector.InlineSwatch.Type.Bezier:
             this._swatchElement.title = WebInspector.UIString("Click to open a cubic-bezier editor.");
             break;
+        case WebInspector.InlineSwatch.Type.Spring:
+            this._swatchElement.title = WebInspector.UIString("Click to open a spring editor.");
+            break;
         case WebInspector.InlineSwatch.Type.Gradient:
             this._swatchElement.title = WebInspector.UIString("Click to select a gradient.");
             break;
@@ -98,6 +101,8 @@
         switch (this._type) {
         case WebInspector.InlineSwatch.Type.Bezier:
             return WebInspector.CubicBezier.fromString("linear");
+        case WebInspector.InlineSwatch.Type.Spring:
+            return WebInspector.Spring.fromString("1 100 10 0");
         case WebInspector.InlineSwatch.Type.Gradient:
             return WebInspector.Gradient.fromString("linear-gradient(transparent, transparent)");
         case WebInspector.InlineSwatch.Type.Color:
@@ -143,6 +148,9 @@
         if (this._type === WebInspector.InlineSwatch.Type.Bezier) {
             this._valueEditor = new WebInspector.BezierEditor;
             this._valueEditor.addEventListener(WebInspector.BezierEditor.Event.BezierChanged, this._valueEditorValueDidChange, this);
+        } else if (this._type === WebInspector.InlineSwatch.Type.Spring) {
+            this._valueEditor = new WebInspector.SpringEditor;
+            this._valueEditor.addEventListener(WebInspector.SpringEditor.Event.SpringChanged, this._valueEditorValueDidChange, this);
         } else if (this._type === WebInspector.InlineSwatch.Type.Gradient) {
             this._valueEditor = new WebInspector.GradientEditor;
             this._valueEditor.addEventListener(WebInspector.GradientEditor.Event.GradientChanged, this._valueEditorValueDidChange, this);
@@ -158,6 +166,8 @@
         let value = this._value || this._fallbackValue();
         if (this._type === WebInspector.InlineSwatch.Type.Bezier)
             this._valueEditor.bezier = value;
+        else if (this._type === WebInspector.InlineSwatch.Type.Spring)
+            this._valueEditor.spring = value;
         else if (this._type === WebInspector.InlineSwatch.Type.Gradient)
             this._valueEditor.gradient = value;
         else
@@ -168,6 +178,8 @@
     {
         if (this._type === WebInspector.InlineSwatch.Type.Bezier)
             this._value = event.data.bezier;
+        else if (this._type === WebInspector.InlineSwatch.Type.Spring)
+            this._value = event.data.spring;
         else if (this._type === WebInspector.InlineSwatch.Type.Gradient)
             this._value = event.data.gradient;
         else
@@ -279,7 +291,8 @@
 WebInspector.InlineSwatch.Type = {
     Color: "inline-swatch-type-color",
     Gradient: "inline-swatch-type-gradient",
-    Bezier: "inline-swatch-type-bezier"
+    Bezier: "inline-swatch-type-bezier",
+    Spring: "inline-swatch-type-spring",
 };
 
 WebInspector.InlineSwatch.Event = {

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -1604,6 +1604,7 @@
             this.createColorMarkers(range);
             this.createGradientMarkers(range);
             this.createCubicBezierMarkers(range);
+            this.createSpringMarkers(range);
         }
 
         this._updateTokenTrackingControllerState();
@@ -1614,7 +1615,7 @@
         // Look for the outermost editable marker.
         var editableMarker;
         for (var marker of markers) {
-            if (!marker.range || (marker.type !== WebInspector.TextMarker.Type.Color && marker.type !== WebInspector.TextMarker.Type.Gradient && marker.type !== WebInspector.TextMarker.Type.CubicBezier))
+            if (!marker.range || !Object.values(WebInspector.TextMarker.Type).includes(marker.type))
                 continue;
 
             if (!editableMarker || (marker.range.startLine < editableMarker.range.startLine || (marker.range.startLine === editableMarker.range.startLine && marker.range.startColumn < editableMarker.range.startColumn)))

Added: trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.css (0 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.css	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.css	2016-08-23 07:23:24 UTC (rev 204775)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Devin Rousso <[email protected]>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.spring-editor {
+    width: 200px;
+    height: 210px;
+}
+
+.spring-editor > .spring-preview {
+    width: calc(100% - 10px);
+    height: 25px;
+    margin: 5px 5px 0;
+    border-bottom: 1px solid lightgrey;
+}
+
+.spring-editor > .spring-preview > div {
+    position: relative;
+    width: 20px;
+    height: 20px;
+    background-color: var(--selected-background-color);
+    border-radius: 50%;
+}
+
+.spring-editor > .spring-timing {
+    position: relative;
+    margin: -0.5px 11px 0;
+}
+
+.spring-editor > .spring-timing > div {
+    width: 0;
+    border-left: 4px solid transparent;
+    border-right: 4px solid transparent;
+    border-top: 4px solid black;
+    transition-timing-function: linear;
+}
+
+.spring-editor > .spring-timing:matches(::before, ::after) {
+    position: absolute;
+    top: 2.5px;
+    font-size: 9px;
+}
+
+.spring-editor > .spring-timing::before {
+    left: 0;
+    content: "0s";
+}
+
+.spring-editor > .spring-timing::after {
+    right: 0;
+    content: attr(data-duration) "s";
+}
+
+.spring-editor > :matches(.spring-preview, .spring-timing) > div {
+    transition-property: none;
+}
+
+.spring-editor > .animate:matches(.spring-preview, .spring-timing) > div {
+    transition-property: transform;
+}
+
+.spring-editor > .number-input-container > .number-input-row {
+    display: flex;
+    flex-wrap: wrap;
+    margin-bottom: 3px;
+}
+
+.spring-editor > .number-input-container > .number-input-row > .number-input-row-title {
+    width: 100%;
+    margin: 7px 0 3px;
+    text-align: center;
+}
+
+.spring-editor > .number-input-container > .number-input-row > input {
+    width: calc(50% - 10px);
+    margin: 0 5px;
+}
+
+.spring-editor > .number-input-container > .number-input-row > input[type="range"] {
+    background-color: transparent;
+}

Added: trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.js (0 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpringEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 Devin Rousso <[email protected]>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SpringEditor = class SpringEditor extends WebInspector.Object
+{
+    constructor()
+    {
+        super();
+
+        this._element = document.createElement("div");
+        this._element.classList.add("spring-editor");
+
+        this._previewContainer = this._element.createChild("div", "spring-preview");
+        this._previewContainer.title = WebInspector.UIString("Click to restart the animation");
+        this._previewContainer.addEventListener("mousedown", this._resetPreviewAnimation.bind(this));
+
+        this._previewElement = this._previewContainer.createChild("div");
+        this._previewElement.addEventListener("transitionend", this.debounce(500)._resetPreviewAnimation);
+
+        this._timingContainer = this._element.createChild("div", "spring-timing");
+
+        this._timingElement = this._timingContainer.createChild("div");
+
+        this._numberInputContainer = this._element.createChild("div", "number-input-container");
+
+        function createInputsForParameter(id, title)
+        {
+            let row = this._numberInputContainer.createChild("div", `number-input-row ${id}`);
+
+            row.createChild("div", "number-input-row-title").textContent = title;
+
+            let sliderKey = `_${id}Slider`;
+            this[sliderKey] = row.createChild("input");
+            this[sliderKey].type = "range";
+            this[sliderKey].addEventListener("input", this._handleNumberSliderInput.bind(this));
+            this[sliderKey].addEventListener("mousedown", this._handleNumberSliderMousedown.bind(this));
+            this[sliderKey].addEventListener("mouseup", this._handleNumberSliderMouseup.bind(this));
+
+            let inputKey = `_${id}Input`;
+            this[inputKey] = row.createChild("input");
+            this[inputKey].type = "number";
+            this[inputKey].addEventListener("input", this._handleNumberInputInput.bind(this));
+            this[inputKey].addEventListener("keydown", this._handleNumberInputKeydown.bind(this));
+        }
+
+        createInputsForParameter.call(this, "mass", WebInspector.UIString("Mass"));
+        this._massInput.min = this._massSlider.min = 1;
+
+        createInputsForParameter.call(this, "stiffness", WebInspector.UIString("Stiffness"));
+        this._stiffnessInput.min = this._stiffnessSlider.min = 1;
+
+        createInputsForParameter.call(this, "damping", WebInspector.UIString("Damping"));
+        this._dampingInput.min = this._dampingSlider.min = 0;
+
+        createInputsForParameter.call(this, "initialVelocity", WebInspector.UIString("Initial Velocity"));
+
+        this._spring = new WebInspector.Spring(1, 100, 10, 0);
+    }
+
+    // Public
+
+    get element()
+    {
+        return this._element;
+    }
+
+    get spring()
+    {
+        return this._spring;
+    }
+
+    set spring(spring)
+    {
+        if (!spring)
+            return;
+
+        let isSpring = spring instanceof WebInspector.Spring;
+        console.assert(isSpring);
+        if (!isSpring)
+            return;
+
+        this._spring = spring;
+        this._massInput.value = this._massSlider.value = this._spring.mass;
+        this._stiffnessInput.value = this._stiffnessSlider.value = this._spring.stiffness;
+        this._dampingInput.value = this._dampingSlider.value = this._spring.damping;
+        this._initialVelocityInput.value = this._initialVelocitySlider.value = this._spring.initialVelocity;
+        this._resetPreviewAnimation();
+    }
+
+    // Private
+
+    _handleNumberInputInput(event)
+    {
+        this._changeSpringForInput(event.target, event.target.value);
+    }
+
+    _handleNumberInputKeydown(event)
+    {
+        let shift = 0;
+        if (event.keyIdentifier === "Up")
+            shift = 1;
+         else if (event.keyIdentifier === "Down")
+            shift = -1;
+
+        if (!shift)
+            return;
+
+        if (event.shiftKey)
+            shift *= 10;
+        else if (event.altKey)
+            shift /= 10;
+
+        event.preventDefault();
+        this._changeSpringForInput(event.target, parseFloat(event.target.value) + shift);
+    }
+
+    _handleNumberSliderInput(event)
+    {
+        this._changeSpringForInput(event.target, event.target.value);
+    }
+
+    _handleNumberSliderMousedown(event)
+    {
+        this._changeSpringForInput(event.target, event.target.value);
+    }
+
+    _handleNumberSliderMouseup(event)
+    {
+        this._changeSpringForInput(event.target, event.target.value);
+    }
+
+    _changeSpringForInput(target, value)
+    {
+        value = parseFloat(value);
+
+        switch (target) {
+        case this._massInput:
+        case this._massSlider:
+            this._spring.mass = Math.max(1, value);
+            this._massInput.value = this._massSlider.value = this._spring.mass.maxDecimals(3);
+            break;
+        case this._stiffnessInput:
+        case this._stiffnessSlider:
+            this._spring.stiffness = Math.max(1, value);
+            this._stiffnessInput.value = this._stiffnessSlider.value = this._spring.stiffness.maxDecimals(3);
+            break;
+        case this._dampingInput:
+        case this._dampingSlider:
+            this._spring.damping = Math.max(0, value);
+            this._dampingInput.value = this._dampingSlider.value = this._spring.damping.maxDecimals(3);
+            break;
+        case this._initialVelocityInput:
+        case this._initialVelocitySlider:
+            this._spring.initialVelocity = value;
+            this._initialVelocityInput.value = this._initialVelocitySlider.value = this._spring.initialVelocity.maxDecimals(3);
+            break;
+        default:
+            return;
+        }
+
+        this.dispatchEventToListeners(WebInspector.SpringEditor.Event.SpringChanged, {spring: this._spring});
+
+        this._resetPreviewAnimation();
+    }
+
+    _resetPreviewAnimation(event)
+    {
+        this._previewContainer.classList.remove("animate");
+        this._previewElement.style.transitionTimingFunction = null;
+        this._previewElement.style.transform = null;
+
+        this._timingContainer.classList.remove("animate");
+        this._timingElement.style.transform = null;
+
+        // Only reset the duration text when a spring parameter is changed.
+        if (!event)
+            this._timingContainer.dataset.duration = "0";
+
+        this.debounce(500)._updatePreviewAnimation(event);
+    }
+
+    _updatePreviewAnimation(event)
+    {
+        this._previewContainer.classList.add("animate");
+        this._previewElement.style.transform = "translateX(85px)";
+        this._previewElement.style.transitionTimingFunction = this._spring.toString();
+
+        this._timingContainer.classList.add("animate");
+        this._timingElement.style.transform = "translateX(170px)";
+
+        // Only calculate the duration when a spring parameter is changed.
+        if (!event) {
+            let duration = this._spring.calculateDuration();
+
+            this._timingContainer.dataset.duration = duration.toFixed(2);
+            this._timingElement.style.transitionDuration = `${duration}s`;
+
+            this._previewElement.style.transitionDuration = `${duration}s`;
+        }
+    }
+};
+
+WebInspector.SpringEditor.Event = {
+    SpringChanged: "spring-editor-spring-changed"
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TextEditor.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TextEditor.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TextEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -617,6 +617,11 @@
         return createCodeMirrorCubicBezierTextMarkers(this._codeMirror, range);
     }
 
+    createSpringMarkers(range)
+    {
+        return createCodeMirrorSpringTextMarkers(this._codeMirror, range);
+    }
+
     editingControllerForMarker(editableMarker)
     {
         switch (editableMarker.type) {
@@ -626,6 +631,8 @@
             return new WebInspector.CodeMirrorGradientEditingController(this._codeMirror, editableMarker);
         case WebInspector.TextMarker.Type.CubicBezier:
             return new WebInspector.CodeMirrorBezierEditingController(this._codeMirror, editableMarker);
+        case WebInspector.TextMarker.Type.Spring:
+            return new WebInspector.CodeMirrorSpringEditingController(this._codeMirror, editableMarker);
         default:
             return new WebInspector.CodeMirrorEditingController(this._codeMirror, editableMarker);
         }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleCommaSeparatedKeywordEditor.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleCommaSeparatedKeywordEditor.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleCommaSeparatedKeywordEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -111,7 +111,9 @@
             values[i] += (openParentheses - closedParenthesis === 1) ? ")" : "";
         }
 
-        if (values.length === 1 && !values[0].includes("(") && !values[0].includes(")"))
+        // Allow splitting with parenthesis if the parenthesis does not have any commas.
+        let hasParenthesis = values[0] && (values[0].includes("(") || values[0].includes(")"));
+        if (values.length === 1 && (!hasParenthesis || !/\([^\)]*,[^\)]*\)/.test(values[0])))
             values = values[0].split(/\s*,\s*/);
 
         for (let value of values)
@@ -176,7 +178,7 @@
         let count = 0;
         while (count < valuesCount) {
             if (count > 0)
-                text += ",";
+                text += ", ";
 
             for (let valueList of valueLists)
                 text += valueList[count > valueList.length - 1 ? valueList.length - 1 : count] + " ";

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.css (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.css	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.css	2016-08-23 07:23:24 UTC (rev 204775)
@@ -31,12 +31,13 @@
     order: 1;
 }
 
-.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch.bezier {
+.visual-style-property-container.timing-editor > .visual-style-property-value-container > .inline-swatch:matches(.bezier, .spring) {
     width: 23px;
     height: 20px;
 }
 
-.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.bezier-value) > .inline-swatch.bezier {
+.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.bezier-value) > .inline-swatch.bezier,
+.visual-style-property-container.timing-editor > .visual-style-property-value-container:not(.spring-value) > .inline-swatch.spring {
     display: none;
 }
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.js (204774 => 204775)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.js	2016-08-23 07:17:11 UTC (rev 204774)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleTimingEditor.js	2016-08-23 07:23:24 UTC (rev 204775)
@@ -31,77 +31,122 @@
 
         this.element.classList.add("timing-editor");
 
-        this._customValueOptionElement = document.createElement("option");
-        this._customValueOptionElement.value = "custom";
-        this._customValueOptionElement.text = WebInspector.UIString("Custom");
-        this._keywordSelectElement.appendChild(this._customValueOptionElement);
+        this._keywordSelectElement.createChild("hr");
 
+        this._customBezierOptionElement = this._keywordSelectElement.createChild("option");
+        this._customBezierOptionElement.value = "bezier";
+        this._customBezierOptionElement.text = WebInspector.UIString("Bezier");
+
+        this._customSpringOptionElement = this._keywordSelectElement.createChild("option");
+        this._customSpringOptionElement.value = "spring";
+        this._customSpringOptionElement.text = WebInspector.UIString("Spring");
+
         this._bezierSwatch = new WebInspector.InlineSwatch(WebInspector.InlineSwatch.Type.Bezier);
         this._bezierSwatch.addEventListener(WebInspector.InlineSwatch.Event.ValueChanged, this._bezierSwatchValueChanged, this);
         this.contentElement.appendChild(this._bezierSwatch.element);
-    }
 
-    // Public
-
-    get bezierValue()
-    {
-        let bezier = this._bezierSwatch.value;
-        if (!bezier)
-            return null;
-
-        return bezier.toString();
+        this._springSwatch = new WebInspector.InlineSwatch(WebInspector.InlineSwatch.Type.Spring);
+        this._springSwatch.addEventListener(WebInspector.InlineSwatch.Event.ValueChanged, this._springSwatchValueChanged, this);
+        this.contentElement.appendChild(this._springSwatch.element);
     }
 
-    set bezierValue(text)
-    {
-        let bezier = WebInspector.CubicBezier.fromString(text);
-        this._bezierSwatch.value = bezier;
-    }
-
     // Protected
 
     get value()
     {
-        return this._customValueOptionElement.selected ? this.bezierValue : super.value;
+        if (this._customBezierOptionElement.selected)
+            return this._bezierValue;
+
+        if (this._customSpringOptionElement.selected)
+            return this._springValue;
+
+        return super.value;
     }
 
     set value(value)
     {
-        this.bezierValue = value;
+        this._bezierValue = value;
+        this._springValue = value;
         if (this.valueIsSupportedKeyword(value)) {
             super.value = value;
-            this.contentElement.classList.remove("bezier-value");
+            this.contentElement.classList.remove("bezier-value", "spring-value");
             return;
         }
 
-        let bezier = this.bezierValue;
-        this._customValueOptionElement.selected = !!bezier;
+        let bezier = this._bezierValue;
+        this._customBezierOptionElement.selected = !!bezier;
         this.contentElement.classList.toggle("bezier-value", !!bezier);
-        this.specialPropertyPlaceholderElement.hidden = !!bezier;
-        if (!bezier)
+
+        let spring = this._springValue;
+        this._customSpringOptionElement.selected = !!spring;
+        this.contentElement.classList.toggle("spring-value", !!spring);
+
+        this.specialPropertyPlaceholderElement.hidden = !!bezier || !!spring;
+        if (!bezier && !spring)
             super.value = value;
     }
 
     get synthesizedValue()
     {
-        return this._customValueOptionElement.selected ? this.bezierValue : super.synthesizedValue;
+        if (this._customBezierOptionElement.selected)
+            return this._bezierValue;
+
+        if (this._customSpringOptionElement.selected)
+            return this._springValue;
+
+        return super.synthesizedValue;
     }
 
     parseValue(text)
     {
-        return /(cubic-bezier\(.+\))/.exec(text);
+        return /((?:cubic-bezier|spring)\(.+\))/.exec(text);
     }
 
     // Private
 
+    get _bezierValue()
+    {
+        let bezier = this._bezierSwatch.value;
+        if (!bezier)
+            return null;
+
+        return bezier.toString();
+    }
+
+    set _bezierValue(text)
+    {
+        this._bezierSwatch.value = WebInspector.CubicBezier.fromString(text);
+    }
+
+    get _springValue()
+    {
+        let spring = this._springSwatch.value;
+        if (!spring)
+            return null;
+
+        return spring.toString();
+    }
+
+    set _springValue(text)
+    {
+        this._springSwatch.value = WebInspector.Spring.fromString(text);
+    }
+
     _handleKeywordChanged()
     {
         super._handleKeywordChanged();
-        let customOptionSelected = this._customValueOptionElement.selected;
-        this.contentElement.classList.toggle("bezier-value", !!customOptionSelected);
-        this.specialPropertyPlaceholderElement.hidden = !!customOptionSelected;
-        if (customOptionSelected)
-            this.bezierValue = "linear";
+
+        let customBezierOptionSelected = this._customBezierOptionElement.selected;
+        this.contentElement.classList.toggle("bezier-value", !!customBezierOptionSelected);
+        if (customBezierOptionSelected)
+            this._bezierValue = "linear";
+
+        let customSpringOptionSelected = this._customSpringOptionElement.selected;
+        this.contentElement.classList.toggle("spring-value", !!customSpringOptionSelected);
+        if (customSpringOptionSelected)
+            this._springValue = "1 100 10 0";
+
+        this.specialPropertyPlaceholderElement.hidden = !!customBezierOptionSelected || !!customSpringOptionSelected;
     }
 
     _bezierSwatchValueChanged(event)
@@ -108,4 +153,9 @@
     {
         this._valueDidChange();
     }
+
+    _springSwatchValueChanged(event)
+    {
+        this._valueDidChange();
+    }
 };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to