tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimingPath.js (13754B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  PureComponent,
      9 } = require("resource://devtools/client/shared/vendor/react.mjs");
     10 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     11 
     12 // Show max 10 iterations for infinite animations
     13 // to give users a clue that the animation does repeat.
     14 const MAX_INFINITE_ANIMATIONS_ITERATIONS = 10;
     15 
     16 class TimingPath extends PureComponent {
     17  /**
     18   * Render a graph of given parameters and return as <path> element list.
     19   *
     20   * @param {object} state
     21   *        State of animation.
     22   * @param {SummaryGraphHelper} helper
     23   *        Instance of SummaryGraphHelper.
     24   * @return {Array}
     25   *         list of <path> element.
     26   */
     27  renderGraph(state, helper) {
     28    // Starting time of main iteration.
     29    let mainIterationStartTime = 0;
     30    let iterationStart = state.iterationStart;
     31    let iterationCount = state.iterationCount ? state.iterationCount : Infinity;
     32 
     33    const pathList = [];
     34 
     35    // Append delay.
     36    if (state.delay > 0) {
     37      this.renderDelay(pathList, state, helper);
     38      mainIterationStartTime = state.delay;
     39    } else {
     40      const negativeDelayCount = -state.delay / state.duration;
     41      // Move to forward the starting point for negative delay.
     42      iterationStart += negativeDelayCount;
     43      // Consume iteration count by negative delay.
     44      if (iterationCount !== Infinity) {
     45        iterationCount -= negativeDelayCount;
     46      }
     47    }
     48 
     49    if (state.duration === Infinity) {
     50      this.renderInfinityDuration(
     51        pathList,
     52        state,
     53        mainIterationStartTime,
     54        helper
     55      );
     56      return pathList;
     57    }
     58 
     59    // Append 1st section of iterations,
     60    // This section is only useful in cases where iterationStart has decimals.
     61    // e.g.
     62    // if { iterationStart: 0.25, iterations: 3 }, firstSectionCount is 0.75.
     63    const firstSectionCount =
     64      iterationStart % 1 === 0
     65        ? 0
     66        : Math.min(1 - (iterationStart % 1), iterationCount);
     67 
     68    if (firstSectionCount) {
     69      this.renderFirstIteration(
     70        pathList,
     71        state,
     72        mainIterationStartTime,
     73        firstSectionCount,
     74        helper
     75      );
     76    }
     77 
     78    if (iterationCount === Infinity) {
     79      // If the animation repeats infinitely,
     80      // we fill the remaining area with iteration paths.
     81      this.renderInfinity(
     82        pathList,
     83        state,
     84        mainIterationStartTime,
     85        firstSectionCount,
     86        helper
     87      );
     88    } else {
     89      // Otherwise, we show remaining iterations, endDelay and fill.
     90 
     91      // Append forwards fill-mode.
     92      if (state.fill === "both" || state.fill === "forwards") {
     93        this.renderForwardsFill(
     94          pathList,
     95          state,
     96          mainIterationStartTime,
     97          iterationCount,
     98          helper
     99        );
    100      }
    101 
    102      // Append middle section of iterations.
    103      // e.g.
    104      // if { iterationStart: 0.25, iterations: 3 }, middleSectionCount is 2.
    105      const middleSectionCount = Math.floor(iterationCount - firstSectionCount);
    106      this.renderMiddleIterations(
    107        pathList,
    108        state,
    109        mainIterationStartTime,
    110        firstSectionCount,
    111        middleSectionCount,
    112        helper
    113      );
    114 
    115      // Append last section of iterations, if there is remaining iteration.
    116      // e.g.
    117      // if { iterationStart: 0.25, iterations: 3 }, lastSectionCount is 0.25.
    118      const lastSectionCount =
    119        iterationCount - middleSectionCount - firstSectionCount;
    120      if (lastSectionCount) {
    121        this.renderLastIteration(
    122          pathList,
    123          state,
    124          mainIterationStartTime,
    125          firstSectionCount,
    126          middleSectionCount,
    127          lastSectionCount,
    128          helper
    129        );
    130      }
    131 
    132      // Append endDelay.
    133      if (state.endDelay > 0) {
    134        this.renderEndDelay(
    135          pathList,
    136          state,
    137          mainIterationStartTime,
    138          iterationCount,
    139          helper
    140        );
    141      }
    142    }
    143    return pathList;
    144  }
    145 
    146  /**
    147   * Render 'delay' part in animation and add a <path> element to given pathList.
    148   *
    149   * @param {Array} pathList
    150   *        Add rendered <path> element to this array.
    151   * @param {object} state
    152   *        State of animation.
    153   * @param {SummaryGraphHelper} helper
    154   *        Instance of SummaryGraphHelper.
    155   */
    156  renderDelay(pathList, state, helper) {
    157    const startSegment = helper.getSegment(0);
    158    const endSegment = { x: state.delay, y: startSegment.y };
    159    const segments = [startSegment, endSegment];
    160    pathList.push(
    161      dom.path({
    162        className: "animation-delay-path",
    163        d: helper.toPathString(segments),
    164      })
    165    );
    166  }
    167 
    168  /**
    169   * Render 1st section of iterations and add a <path> element to given pathList.
    170   * This section is only useful in cases where iterationStart has decimals.
    171   *
    172   * @param {Array} pathList
    173   *        Add rendered <path> element to this array.
    174   * @param {object} state
    175   *        State of animation.
    176   * @param {number} mainIterationStartTime
    177   *        Start time of main iteration.
    178   * @param {number} firstSectionCount
    179   *        Iteration count of first section.
    180   * @param {SummaryGraphHelper} helper
    181   *        Instance of SummaryGraphHelper.
    182   */
    183  renderFirstIteration(
    184    pathList,
    185    state,
    186    mainIterationStartTime,
    187    firstSectionCount,
    188    helper
    189  ) {
    190    const startTime = mainIterationStartTime;
    191    const endTime = startTime + firstSectionCount * state.duration;
    192    const segments = helper.createPathSegments(startTime, endTime);
    193    pathList.push(
    194      dom.path({
    195        className: "animation-iteration-path",
    196        d: helper.toPathString(segments),
    197      })
    198    );
    199  }
    200 
    201  /**
    202   * Render middle iterations and add <path> elements to given pathList.
    203   *
    204   * @param {Array} pathList
    205   *        Add rendered <path> elements to this array.
    206   * @param {object} state
    207   *        State of animation.
    208   * @param {number} mainIterationStartTime
    209   *        Starting time of main iteration.
    210   * @param {number} firstSectionCount
    211   *        Iteration count of first section.
    212   * @param {number} middleSectionCount
    213   *        Iteration count of middle section.
    214   * @param {SummaryGraphHelper} helper
    215   *        Instance of SummaryGraphHelper.
    216   */
    217  renderMiddleIterations(
    218    pathList,
    219    state,
    220    mainIterationStartTime,
    221    firstSectionCount,
    222    middleSectionCount,
    223    helper
    224  ) {
    225    const offset = mainIterationStartTime + firstSectionCount * state.duration;
    226    for (let i = 0; i < middleSectionCount; i++) {
    227      // Get the path segments of each iteration.
    228      const startTime = offset + i * state.duration;
    229      const endTime = startTime + state.duration;
    230      const segments = helper.createPathSegments(startTime, endTime);
    231      pathList.push(
    232        dom.path({
    233          className: "animation-iteration-path",
    234          d: helper.toPathString(segments),
    235        })
    236      );
    237    }
    238  }
    239 
    240  /**
    241   * Render last section of iterations and add a <path> element to given pathList.
    242   * This section is only useful in cases where iterationStart has decimals.
    243   *
    244   * @param {Array} pathList
    245   *        Add rendered <path> elements to this array.
    246   * @param {object} state
    247   *        State of animation.
    248   * @param {number} mainIterationStartTime
    249   *        Starting time of main iteration.
    250   * @param {number} firstSectionCount
    251   *        Iteration count of first section.
    252   * @param {number} middleSectionCount
    253   *        Iteration count of middle section.
    254   * @param {number} lastSectionCount
    255   *        Iteration count of last section.
    256   * @param {SummaryGraphHelper} helper
    257   *        Instance of SummaryGraphHelper.
    258   */
    259  renderLastIteration(
    260    pathList,
    261    state,
    262    mainIterationStartTime,
    263    firstSectionCount,
    264    middleSectionCount,
    265    lastSectionCount,
    266    helper
    267  ) {
    268    const startTime =
    269      mainIterationStartTime +
    270      (firstSectionCount + middleSectionCount) * state.duration;
    271    const endTime = startTime + lastSectionCount * state.duration;
    272    const segments = helper.createPathSegments(startTime, endTime);
    273    pathList.push(
    274      dom.path({
    275        className: "animation-iteration-path",
    276        d: helper.toPathString(segments),
    277      })
    278    );
    279  }
    280 
    281  /**
    282   * Render infinity iterations and add <path> elements to given pathList.
    283   *
    284   * @param {Array} pathList
    285   *        Add rendered <path> elements to this array.
    286   * @param {object} state
    287   *        State of animation.
    288   * @param {number} mainIterationStartTime
    289   *        Starting time of main iteration.
    290   * @param {number} firstSectionCount
    291   *        Iteration count of first section.
    292   * @param {SummaryGraphHelper} helper
    293   *        Instance of SummaryGraphHelper.
    294   */
    295  renderInfinity(
    296    pathList,
    297    state,
    298    mainIterationStartTime,
    299    firstSectionCount,
    300    helper
    301  ) {
    302    // Calculate the number of iterations to display,
    303    // with a maximum of MAX_INFINITE_ANIMATIONS_ITERATIONS
    304    let uncappedInfinityIterationCount =
    305      (helper.totalDuration - firstSectionCount * state.duration) /
    306      state.duration;
    307    // If there is a small floating point error resulting in, e.g. 1.0000001
    308    // ceil will give us 2 so round first.
    309    uncappedInfinityIterationCount = parseFloat(
    310      uncappedInfinityIterationCount.toPrecision(6)
    311    );
    312    const infinityIterationCount = Math.min(
    313      MAX_INFINITE_ANIMATIONS_ITERATIONS,
    314      Math.ceil(uncappedInfinityIterationCount)
    315    );
    316 
    317    // Append first full iteration path.
    318    const firstStartTime =
    319      mainIterationStartTime + firstSectionCount * state.duration;
    320    const firstEndTime = firstStartTime + state.duration;
    321    const firstSegments = helper.createPathSegments(
    322      firstStartTime,
    323      firstEndTime
    324    );
    325    pathList.push(
    326      dom.path({
    327        className: "animation-iteration-path",
    328        d: helper.toPathString(firstSegments),
    329      })
    330    );
    331 
    332    // Append other iterations. We can copy first segments.
    333    const isAlternate = state.direction.match(/alternate/);
    334    for (let i = 1; i < infinityIterationCount; i++) {
    335      const startTime = firstStartTime + i * state.duration;
    336      let segments;
    337      if (isAlternate && i % 2) {
    338        // Copy as reverse.
    339        segments = firstSegments.map(segment => {
    340          return { x: firstEndTime - segment.x + startTime, y: segment.y };
    341        });
    342      } else {
    343        // Copy as is.
    344        segments = firstSegments.map(segment => {
    345          return { x: segment.x - firstStartTime + startTime, y: segment.y };
    346        });
    347      }
    348      pathList.push(
    349        dom.path({
    350          className: "animation-iteration-path infinity",
    351          d: helper.toPathString(segments),
    352        })
    353      );
    354    }
    355  }
    356 
    357  /**
    358   * Render infinity duration.
    359   *
    360   * @param {Array} pathList
    361   *        Add rendered <path> elements to this array.
    362   * @param {object} state
    363   *        State of animation.
    364   * @param {number} mainIterationStartTime
    365   *        Starting time of main iteration.
    366   * @param {SummaryGraphHelper} helper
    367   *        Instance of SummaryGraphHelper.
    368   */
    369  renderInfinityDuration(pathList, state, mainIterationStartTime, helper) {
    370    const startSegment = helper.getSegment(mainIterationStartTime);
    371    const endSegment = { x: helper.totalDuration, y: startSegment.y };
    372    const segments = [startSegment, endSegment];
    373    pathList.push(
    374      dom.path({
    375        className: "animation-iteration-path infinity-duration",
    376        d: helper.toPathString(segments),
    377      })
    378    );
    379  }
    380 
    381  /**
    382   * Render 'endDelay' part in animation and add a <path> element to given pathList.
    383   *
    384   * @param {Array} pathList
    385   *        Add rendered <path> element to this array.
    386   * @param {object} state
    387   *        State of animation.
    388   * @param {number} mainIterationStartTime
    389   *        Starting time of main iteration.
    390   * @param {number} iterationCount
    391   *        Iteration count of whole animation.
    392   * @param {SummaryGraphHelper} helper
    393   *        Instance of SummaryGraphHelper.
    394   */
    395  renderEndDelay(
    396    pathList,
    397    state,
    398    mainIterationStartTime,
    399    iterationCount,
    400    helper
    401  ) {
    402    const startTime = mainIterationStartTime + iterationCount * state.duration;
    403    const startSegment = helper.getSegment(startTime);
    404    const endSegment = { x: startTime + state.endDelay, y: startSegment.y };
    405    pathList.push(
    406      dom.path({
    407        className: "animation-enddelay-path",
    408        d: helper.toPathString([startSegment, endSegment]),
    409      })
    410    );
    411  }
    412 
    413  /**
    414   * Render 'fill' for forwards part in animation and
    415   * add a <path> element to given pathList.
    416   *
    417   * @param {Array} pathList
    418   *        Add rendered <path> element to this array.
    419   * @param {object} state
    420   *        State of animation.
    421   * @param {number} mainIterationStartTime
    422   *        Starting time of main iteration.
    423   * @param {number} iterationCount
    424   *        Iteration count of whole animation.
    425   * @param {SummaryGraphHelper} helper
    426   *        Instance of SummaryGraphHelper.
    427   */
    428  renderForwardsFill(
    429    pathList,
    430    state,
    431    mainIterationStartTime,
    432    iterationCount,
    433    helper
    434  ) {
    435    const startTime =
    436      mainIterationStartTime +
    437      iterationCount * state.duration +
    438      (state.endDelay > 0 ? state.endDelay : 0);
    439    const startSegment = helper.getSegment(startTime);
    440    const endSegment = { x: helper.totalDuration, y: startSegment.y };
    441    pathList.push(
    442      dom.path({
    443        className: "animation-fill-forwards-path",
    444        d: helper.toPathString([startSegment, endSegment]),
    445      })
    446    );
    447  }
    448 }
    449 
    450 module.exports = TimingPath;