Bug 1210796 - Part 12: Unite common codes into utils.js and use. r=pbro draft
authorDaisuke Akatsuka <daisuke@mozilla-japan.org>
Tue, 18 Apr 2017 12:15:56 +0900
changeset 564125 153619c923b9a15576610f1eef813c1651fc0e2d
parent 564124 8c3c8034d13d3b56519c6c2fa115e822dc185b0c
child 564126 254d333f555fc715a166670cb2d89d7031017b32
push id54524
push userbmo:dakatsuka@mozilla.com
push dateTue, 18 Apr 2017 09:24:06 +0000
reviewerspbro
bugs1210796
milestone55.0a1
Bug 1210796 - Part 12: Unite common codes into utils.js and use. r=pbro MozReview-Commit-ID: AXzA9aEOc8N
devtools/client/animationinspector/components/animation-details.js
devtools/client/animationinspector/components/animation-time-block.js
devtools/client/animationinspector/test/unit/test_getCssPropertyName.js
devtools/client/animationinspector/utils.js
--- a/devtools/client/animationinspector/components/animation-details.js
+++ b/devtools/client/animationinspector/components/animation-details.js
@@ -3,17 +3,18 @@
 /* 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 {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
-const {createNode} = require("devtools/client/animationinspector/utils");
+const {createNode, getCssPropertyName} =
+  require("devtools/client/animationinspector/utils");
 const {Keyframes} = require("devtools/client/animationinspector/components/keyframes");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const L10N =
   new LocalizationHelper("devtools/client/locales/animationinspector.properties");
 
 /**
  * UI component responsible for displaying detailed information for a given
@@ -323,19 +324,8 @@ AnimationDetails.prototype = {
     this.progressIndicatorEl.style.left =
       `${ this.dummyAnimation.effect.getComputedTiming().progress * 100 }%`;
   },
 
   get win() {
     return this.containerEl.ownerDocument.defaultView;
   }
 };
-
-/**
- * Turn propertyName into property-name.
- * @param {String} jsPropertyName A camelcased CSS property name. Typically
- * something that comes out of computed styles. E.g. borderBottomColor
- * @return {String} The corresponding CSS property name: border-bottom-color
- */
-function getCssPropertyName(jsPropertyName) {
-  return jsPropertyName.replace(/[A-Z]/g, "-$&").toLowerCase();
-}
-exports.getCssPropertyName = getCssPropertyName;
--- a/devtools/client/animationinspector/components/animation-time-block.js
+++ b/devtools/client/animationinspector/components/animation-time-block.js
@@ -2,42 +2,28 @@
 /* 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, TimeScale, getFormattedAnimationTitle} =
+const {createNode, createSVGNode, TimeScale, getFormattedAnimationTitle} =
   require("devtools/client/animationinspector/utils");
+const {createPathSegments, appendPathElement, DEFAULT_MIN_PROGRESS_THRESHOLD} =
+  require("devtools/client/animationinspector/graph-helper");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const L10N =
       new LocalizationHelper("devtools/client/locales/animationinspector.properties");
 
-// In the createPathSegments function, an animation duration is divided by
-// DURATION_RESOLUTION in order to draw the way the animation progresses.
-// But depending on the timing-function, we may be not able to make the graph
-// smoothly progress if this resolution is not high enough.
-// So, if the difference of animation progress between 2 divisions is more than
-// MIN_PROGRESS_THRESHOLD, then createPathSegments re-divides
-// by DURATION_RESOLUTION.
-// DURATION_RESOLUTION shoud be integer and more than 2.
-const DURATION_RESOLUTION = 4;
-// MIN_PROGRESS_THRESHOLD shoud be between more than 0 to 1.
-const MIN_PROGRESS_THRESHOLD = 0.1;
-// BOUND_EXCLUDING_TIME should be less than 1ms and is used to exclude start
-// and end bounds when dividing  duration in createPathSegments.
-const BOUND_EXCLUDING_TIME = 0.001;
 // Show max 10 iterations for infinite animations
 // to give users a clue that the animation does repeat.
 const MAX_INFINITE_ANIMATIONS_ITERATIONS = 10;
-// SVG namespace
-const SVG_NS = "http://www.w3.org/2000/svg";
 
 /**
  * UI component responsible for displaying a single animation timeline, which
  * basically looks like a rectangle that shows the delay and iterations.
  */
 function AnimationTimeBlock() {
   EventEmitter.decorate(this);
   this.onClick = this.onClick.bind(this);
@@ -72,19 +58,18 @@ AnimationTimeBlock.prototype = {
 
     // Create a container element to hold the delay and iterations.
     // It is positioned according to its delay (divided by the playbackrate),
     // and its width is according to its duration (divided by the playbackrate).
     const {x, delayX, delayW, endDelayX, endDelayW} =
       TimeScale.getAnimationDimensions(animation);
 
     // Animation summary graph element.
-    const summaryEl = createNode({
+    const summaryEl = createSVGNode({
       parent: this.containerEl,
-      namespace: "http://www.w3.org/2000/svg",
       nodeType: "svg",
       attributes: {
         "class": "summary",
         "preserveAspectRatio": "none",
         "style": `left: ${ x - (state.delay > 0 ? delayW : 0) }%`
       }
     });
 
@@ -103,17 +88,17 @@ AnimationTimeBlock.prototype = {
 
     // Get a helper function that returns the path segment of timing-function.
     const segmentHelper = getSegmentHelper(state, this.win);
 
     // Minimum segment duration is the duration of one pixel.
     const minSegmentDuration =
       totalDisplayedDuration / this.containerEl.clientWidth;
     // Minimum progress threshold.
-    let minProgressThreshold = MIN_PROGRESS_THRESHOLD;
+    let minProgressThreshold = DEFAULT_MIN_PROGRESS_THRESHOLD;
     // If the easing is step function,
     // minProgressThreshold should be changed by the steps.
     const stepFunction = state.easing.match(/steps\((\d+)/);
     if (stepFunction) {
       minProgressThreshold = 1 / (parseInt(stepFunction[1], 10) + 1);
     }
 
     // Starting time of main iteration.
@@ -610,91 +595,8 @@ function getSegmentHelper(state, win) {
       } else {
         this.animation.currentTime = time;
       }
       const progress = this.animation.effect.getComputedTiming().progress;
       return { x: time, y: Math.max(progress, 0) };
     }
   };
 }
-
-/**
- * Create the path segments from given parameters.
- * @param {Number} startTime - Starting time of animation.
- * @param {Number} endTime - Ending time of animation.
- * @param {Number} minSegmentDuration - Minimum segment duration.
- * @param {Number} minProgressThreshold - Minimum progress threshold.
- * @param {Object} segmentHelper - The object of getSegmentHelper.
- * @return {Array} path segments -
- *                 [{x: {Number} time, y: {Number} progress}, ...]
- */
-function createPathSegments(startTime, endTime, minSegmentDuration,
-                            minProgressThreshold, segmentHelper) {
-  // If the duration is too short, early return.
-  if (endTime - startTime < minSegmentDuration) {
-    return [segmentHelper.getSegment(startTime),
-            segmentHelper.getSegment(endTime)];
-  }
-
-  // Otherwise, start creating segments.
-  let pathSegments = [];
-
-  // Append the segment for the startTime position.
-  const startTimeSegment = segmentHelper.getSegment(startTime);
-  pathSegments.push(startTimeSegment);
-  let previousSegment = startTimeSegment;
-
-  // Split the duration in equal intervals, and iterate over them.
-  // See the definition of DURATION_RESOLUTION for more information about this.
-  const interval = (endTime - startTime) / DURATION_RESOLUTION;
-  for (let index = 1; index <= DURATION_RESOLUTION; index++) {
-    // Create a segment for this interval.
-    const currentSegment =
-      segmentHelper.getSegment(startTime + index * interval);
-
-    // If the distance between the Y coordinate (the animation's progress) of
-    // the previous segment and the Y coordinate of the current segment is too
-    // large, then recurse with a smaller duration to get more details
-    // in the graph.
-    if (Math.abs(currentSegment.y - previousSegment.y) > minProgressThreshold) {
-      // Divide the current interval (excluding start and end bounds
-      // by adding/subtracting BOUND_EXCLUDING_TIME).
-      pathSegments = pathSegments.concat(
-        createPathSegments(previousSegment.x + BOUND_EXCLUDING_TIME,
-                           currentSegment.x - BOUND_EXCLUDING_TIME,
-                           minSegmentDuration, minProgressThreshold,
-                           segmentHelper));
-    }
-
-    pathSegments.push(currentSegment);
-    previousSegment = currentSegment;
-  }
-
-  return pathSegments;
-}
-
-/**
- * Append path element.
- * @param {Element} parentEl - Parent element of this appended path element.
- * @param {Array} pathSegments - Path segments. Please see createPathSegments.
- * @param {String} cls - Class name.
- * @return {Element} path element.
- */
-function appendPathElement(parentEl, pathSegments, cls) {
-  // Create path string.
-  let path = `M${ pathSegments[0].x },0`;
-  pathSegments.forEach(pathSegment => {
-    path += ` L${ pathSegment.x },${ pathSegment.y }`;
-  });
-  path += ` L${ pathSegments[pathSegments.length - 1].x },0 Z`;
-  // Append and return the path element.
-  return createNode({
-    parent: parentEl,
-    namespace: SVG_NS,
-    nodeType: "path",
-    attributes: {
-      "d": path,
-      "class": cls,
-      "vector-effect": "non-scaling-stroke",
-      "transform": "scale(1, -1)"
-    }
-  });
-}
--- a/devtools/client/animationinspector/test/unit/test_getCssPropertyName.js
+++ b/devtools/client/animationinspector/test/unit/test_getCssPropertyName.js
@@ -2,17 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var Cu = Components.utils;
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {getCssPropertyName} = require("devtools/client/animationinspector/components/animation-details");
+const {getCssPropertyName} = require("devtools/client/animationinspector/utils");
 
 const TEST_DATA = [{
   jsName: "alllowercase",
   cssName: "alllowercase"
 }, {
   jsName: "borderWidth",
   cssName: "border-width"
 }, {
--- a/devtools/client/animationinspector/utils.js
+++ b/devtools/client/animationinspector/utils.js
@@ -305,16 +305,27 @@ function getJsPropertyName(cssPropertyNa
   // https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
   return cssPropertyName.replace(/-([a-z])/gi, (str, group) => {
     return group.toUpperCase();
   });
 }
 exports.getJsPropertyName = getJsPropertyName;
 
 /**
+ * Turn propertyName into property-name.
+ * @param {String} jsPropertyName A camelcased CSS property name. Typically
+ * something that comes out of computed styles. E.g. borderBottomColor
+ * @return {String} The corresponding CSS property name: border-bottom-color
+ */
+function getCssPropertyName(jsPropertyName) {
+  return jsPropertyName.replace(/[A-Z]/g, "-$&").toLowerCase();
+}
+exports.getCssPropertyName = getCssPropertyName;
+
+/**
  * Get a formatted title for this animation. This will be either:
  * "some-name", "some-name : CSS Transition", "some-name : CSS Animation",
  * "some-name : Script Animation", or "Script Animation", depending
  * if the server provides the type, what type it is and if the animation
  * has a name
  * @param {AnimationPlayerFront} animation
  */
 function getFormattedAnimationTitle({state}) {