function Animation(container, viewModel) {
  //>>includeStart('debug', pragmas.debug);
  if (!defined(container)) {
    throw new DeveloperError("container is required.");
  }
  if (!defined(viewModel)) {
    throw new DeveloperError("viewModel is required.");
  }
  //>>includeEnd('debug');

  container = getElement(container);
  this._viewModel = viewModel;
  this._container = container;
  this._centerX = 0;
  this._centerY = 0;
  this._defsElement = undefined;
  this._svgNode = undefined;
  this._topG = undefined;
  this._lastHeight = undefined;
  this._lastWidth = undefined;

  const ownerDocument = container.ownerDocument;

  // Firefox requires SVG references to be included directly, not imported from external CSS.
  // Also, CSS minifiers get confused by this being in an external CSS file.
  const cssStyle = document.createElement("style");
  cssStyle.textContent =
    ".cesium-animation-rectButton .cesium-animation-buttonGlow { filter: url(#animation_blurred); }\
.cesium-animation-rectButton .cesium-animation-buttonMain { fill: url(#animation_buttonNormal); }\
.cesium-animation-buttonToggled .cesium-animation-buttonMain { fill: url(#animation_buttonToggled); }\
.cesium-animation-rectButton:hover .cesium-animation-buttonMain { fill: url(#animation_buttonHovered); }\
.cesium-animation-buttonDisabled .cesium-animation-buttonMain { fill: url(#animation_buttonDisabled); }\
.cesium-animation-shuttleRingG .cesium-animation-shuttleRingSwoosh { fill: url(#animation_shuttleRingSwooshGradient); }\
.cesium-animation-shuttleRingG:hover .cesium-animation-shuttleRingSwoosh { fill: url(#animation_shuttleRingSwooshHovered); }\
.cesium-animation-shuttleRingPointer { fill: url(#animation_shuttleRingPointerGradient); }\
.cesium-animation-shuttleRingPausePointer { fill: url(#animation_shuttleRingPointerPaused); }\
.cesium-animation-knobOuter { fill: url(#animation_knobOuter); }\
.cesium-animation-knobInner { fill: url(#animation_knobInner); }";

  ownerDocument.head.insertBefore(cssStyle, ownerDocument.head.childNodes[0]);

  const themeEle = document.createElement("div");
  themeEle.className = "cesium-animation-theme";
  themeEle.innerHTML =
    '<div class="cesium-animation-themeNormal"></div>\
<div class="cesium-animation-themeHover"></div>\
<div class="cesium-animation-themeSelect"></div>\
<div class="cesium-animation-themeDisabled"></div>\
<div class="cesium-animation-themeKnob"></div>\
<div class="cesium-animation-themePointer"></div>\
<div class="cesium-animation-themeSwoosh"></div>\
<div class="cesium-animation-themeSwooshHover"></div>';

  this._theme = themeEle;
  this._themeNormal = themeEle.childNodes[0];
  this._themeHover = themeEle.childNodes[1];
  this._themeSelect = themeEle.childNodes[2];
  this._themeDisabled = themeEle.childNodes[3];
  this._themeKnob = themeEle.childNodes[4];
  this._themePointer = themeEle.childNodes[5];
  this._themeSwoosh = themeEle.childNodes[6];
  this._themeSwooshHover = themeEle.childNodes[7];

  const svg = document.createElementNS(svgNS, "svg:svg");
  this._svgNode = svg;

  // Define the XLink namespace that SVG uses
  svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", xlinkNS);

  const topG = document.createElementNS(svgNS, "g");
  this._topG = topG;

  this._realtimeSVG = new SvgButton(
    wingButton(3, 4, "animation_pathClock"),
    viewModel.playRealtimeViewModel,
  );
  this._playReverseSVG = new SvgButton(
    rectButton(44, 99, "animation_pathPlayReverse"),
    viewModel.playReverseViewModel,
  );
  this._playForwardSVG = new SvgButton(
    rectButton(124, 99, "animation_pathPlay"),
    viewModel.playForwardViewModel,
  );
  this._pauseSVG = new SvgButton(
    rectButton(84, 99, "animation_pathPause"),
    viewModel.pauseViewModel,
  );

  const buttonsG = document.createElementNS(svgNS, "g");
  buttonsG.appendChild(this._realtimeSVG.svgElement);
  buttonsG.appendChild(this._playReverseSVG.svgElement);
  buttonsG.appendChild(this._playForwardSVG.svgElement);
  buttonsG.appendChild(this._pauseSVG.svgElement);

  const shuttleRingBackPanel = svgFromObject({
    tagName: "circle",
    class: "cesium-animation-shuttleRingBack",
    cx: 100,
    cy: 100,
    r: 99,
  });
  this._shuttleRingBackPanel = shuttleRingBackPanel;

  const swooshIconInfo = svgIconsById["animation_pathSwooshFX"];
  const shuttleRingPointerIconInfo = svgIconsById["animation_pathPointer"];

  const shuttleRingSwooshG = svgFromObject({
    tagName: "g",
    class: "cesium-animation-shuttleRingSwoosh",
    children: [
      {
        tagName: swooshIconInfo.tagName,
        transform: "translate(100,97) scale(-1,1)",
        id: "animation_pathSwooshFX",
        d: swooshIconInfo.d,
      },
      {
        tagName: swooshIconInfo.tagName,
        transform: "translate(100,97)",
        id: "animation_pathSwooshFX",
        d: swooshIconInfo.d,
      },
      {
        tagName: "line",
        x1: 100,
        y1: 8,
        x2: 100,
        y2: 22,
      },
    ],
  });
  this._shuttleRingSwooshG = shuttleRingSwooshG;

  this._shuttleRingPointer = svgFromObject({
    class: "cesium-animation-shuttleRingPointer",
    id: "animation_pathPointer",
    tagName: shuttleRingPointerIconInfo.tagName,
    d: shuttleRingPointerIconInfo.d,
  });

  const knobG = svgFromObject({
    tagName: "g",
    transform: "translate(100,100)",
  });

  this._knobOuter = svgFromObject({
    tagName: "circle",
    class: "cesium-animation-knobOuter",
    cx: 0,
    cy: 0,
    r: 71,
  });

  const knobInnerAndShieldSize = 61;

  const knobInner = svgFromObject({
    tagName: "circle",
    class: "cesium-animation-knobInner",
    cx: 0,
    cy: 0,
    r: knobInnerAndShieldSize,
  });

  this._knobDate = svgText(0, -24, "");
  this._knobTime = svgText(0, -7, "");
  this._knobStatus = svgText(0, -41, "");

  // widget shield catches clicks on the knob itself (even while DOM elements underneath are changing).
  const knobShield = svgFromObject({
    tagName: "circle",
    class: "cesium-animation-blank",
    cx: 0,
    cy: 0,
    r: knobInnerAndShieldSize,
  });

  const shuttleRingBackG = document.createElementNS(svgNS, "g");
  shuttleRingBackG.setAttribute("class", "cesium-animation-shuttleRingG");

  container.appendChild(themeEle);
  topG.appendChild(shuttleRingBackG);
  topG.appendChild(knobG);
  topG.appendChild(buttonsG);

  shuttleRingBackG.appendChild(shuttleRingBackPanel);
  shuttleRingBackG.appendChild(shuttleRingSwooshG);
  shuttleRingBackG.appendChild(this._shuttleRingPointer);

  knobG.appendChild(this._knobOuter);
  knobG.appendChild(knobInner);
  knobG.appendChild(this._knobDate);
  knobG.appendChild(this._knobTime);
  knobG.appendChild(this._knobStatus);
  knobG.appendChild(knobShield);

  svg.appendChild(topG);
  container.appendChild(svg);

  const that = this;
  function mouseCallback(e) {
    setShuttleRingFromMouseOrTouch(that, e);
  }
  this._mouseCallback = mouseCallback;

  shuttleRingBackPanel.addEventListener("mousedown", mouseCallback, true);
  shuttleRingBackPanel.addEventListener("touchstart", mouseCallback, true);
  shuttleRingSwooshG.addEventListener("mousedown", mouseCallback, true);
  shuttleRingSwooshG.addEventListener("touchstart", mouseCallback, true);
  ownerDocument.addEventListener("mousemove", mouseCallback, true);
  ownerDocument.addEventListener("touchmove", mouseCallback, true);
  ownerDocument.addEventListener("mouseup", mouseCallback, true);
  ownerDocument.addEventListener("touchend", mouseCallback, true);
  ownerDocument.addEventListener("touchcancel", mouseCallback, true);
  this._shuttleRingPointer.addEventListener("mousedown", mouseCallback, true);
  this._shuttleRingPointer.addEventListener("touchstart", mouseCallback, true);
  this._knobOuter.addEventListener("mousedown", mouseCallback, true);
  this._knobOuter.addEventListener("touchstart", mouseCallback, true);

  //TODO: Since the animation widget uses SVG and has no HTML backing,
  //we need to wire everything up manually.  Knockout can supposedly
  //bind to SVG, so we we figure that out we can modify our SVG
  //to include the binding information directly.

  const timeNode = this._knobTime.childNodes[0];
  const dateNode = this._knobDate.childNodes[0];
  const statusNode = this._knobStatus.childNodes[0];
  let isPaused;
  this._subscriptions = [
    //
    subscribeAndEvaluate(viewModel.pauseViewModel, "toggled", function (value) {
      if (isPaused !== value) {
        isPaused = value;
        if (isPaused) {
          that._shuttleRingPointer.setAttribute(
            "class",
            "cesium-animation-shuttleRingPausePointer",
          );
        } else {
          that._shuttleRingPointer.setAttribute(
            "class",
            "cesium-animation-shuttleRingPointer",
          );
        }
      }
    }),

    subscribeAndEvaluate(viewModel, "shuttleRingAngle", function (value) {
      setShuttleRingPointer(that._shuttleRingPointer, that._knobOuter, value);
    }),

    subscribeAndEvaluate(viewModel, "dateLabel", function (value) {
      if (dateNode.textContent !== value) {
        dateNode.textContent = value;
      }
    }),

    subscribeAndEvaluate(viewModel, "timeLabel", function (value) {
      if (timeNode.textContent !== value) {
        timeNode.textContent = value;
      }
    }),

    subscribeAndEvaluate(viewModel, "multiplierLabel", function (value) {
      if (statusNode.textContent !== value) {
        statusNode.textContent = value;
      }
    }),
  ];

  this.applyThemeChanges();
  this.resize();
}
<span style="display: block; text-align: center;">
<img src="Images/AnimationWidget.png" width="211" height="142" alt="" />
<br />Animation widget
</span>
<br /><br />
The Animation widget provides buttons for play, pause, and reverse, along with the
current time and date, surrounded by a "shuttle ring" for controlling the speed of animation.
<br /><br />
The "shuttle ring" concept is borrowed from video editing, where typically a
"jog wheel" can be rotated to move past individual animation frames very slowly, and
a surrounding shuttle ring can be twisted to control direction and speed of fast playback.
Cesium typically treats time as continuous (not broken into pre-defined animation frames),
so this widget offers no jog wheel.  Instead, the shuttle ring is capable of both fast and
very slow playback.  Click and drag the shuttle ring pointer itself (shown above in green),
or click in the rest of the ring area to nudge the pointer to the next preset speed in that direction.
<br /><br />
The Animation widget also provides a "realtime" button (in the upper-left) that keeps
animation time in sync with the end user's system clock, typically displaying
"today" or "right now."  This mode is not available in {@link ClockRange.CLAMPED} or
{@link ClockRange.LOOP_STOP} mode if the current time is outside of {@link Clock}'s startTime and endTime.

@alias Animation
@constructor

@param {Element|string} container The DOM element or ID that will contain the widget.
@param {AnimationViewModel} viewModel The view model used by this widget.

@exception {DeveloperError} Element with id "container" does not exist in the document.


@example
// In HTML head, include a link to Animation.css stylesheet,
// and in the body, include: <div id="animationContainer"></div>

const clock = new Cesium.Clock();
const clockViewModel = new Cesium.ClockViewModel(clock);
const viewModel = new Cesium.AnimationViewModel(clockViewModel);
const widget = new Cesium.Animation('animationContainer', viewModel);

function tick() {
    clock.tick();
    requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

@see AnimationViewModel
@see Clock