text: define(["Colorer", "util"], function (Colorer, util) {
      /**
       *
       * @class AnimationPanel
       *
       * Creates tab for the animation panel and handles their events events.
       *
       * @param{Object} animator The object that creates the animations
       *
       * @return {AnimationPanel}
       * construct AnimationPanel
       */
      function AnimationPanel(animator) {
          // used in event triggers
          this.animator = animator;
  
          // animation GUI components
          this.colorSelect = document.getElementById("animate-color-select");
          this.reverseColorChk = document.getElementById(
              "animate-color-reverse-chk"
          );
          this.gradient = document.getElementById("animate-gradient");
          this.trajectory = document.getElementById("animate-trajectory");
          this.collapseChk = document.getElementById("animate-collapse-chk");
          this.lWidth = document.getElementById("animate-line-width");
          this.startBtn = document.getElementById("animate-start-btn");
          this.stopBtn = document.getElementById("animate-stop-btn");
          this.pauseBtn = document.getElementById("animate-pause-btn");
          this.resumeBtn = document.getElementById("animate-resume-btn");
          this.prevFrameBtn = document.getElementById("animate-prev-btn");
          this.nextFrameBtn = document.getElementById("animate-next-btn");
  
          /**
           * @type {Function}
           * Function to execute when an animation starts.
           * @private
           */
          this._onAnimationStarted = null;
  
          /**
           * @type {Function}
           * Function to execute when an animation stops.
           * @private
           */
          this._onAnimationStopped = null;
      }
  
      /**
       * Makes the play button visible. This is the menu shown before user has
       * started the animation.
       */
      AnimationPanel.prototype.startOptions = function () {
          // hide the following buttons
          this.stopBtn.classList.add("hidden");
          this.pauseBtn.classList.add("hidden");
          this.resumeBtn.classList.add("hidden");
          this.prevFrameBtn.classList.add("hidden");
          this.nextFrameBtn.classList.add("hidden");
  
          // show the following buttons
          this.startBtn.classList.remove("hidden");
      };
  
      /**
       * Makes the stop/pause buttons visible. This is the menu shown during the
       * animation.
       *
       * @private
       */
      AnimationPanel.prototype.__pauseOptions = function () {
          // hide the following buttons
          this.startBtn.classList.add("hidden");
          this.resumeBtn.classList.add("hidden");
          this.prevFrameBtn.classList.add("hidden");
          this.nextFrameBtn.classList.add("hidden");
  
          // show the following buttons
          this.stopBtn.classList.remove("hidden");
          this.pauseBtn.classList.remove("hidden");
      };
  
      /**
       * Makes the prev/next/stop/resume buttons visible. This is the menu shown
       * when user pauses the animation.
       *
       * @private
       */
      AnimationPanel.prototype.__resumeOptions = function () {
          // hide the following buttons
          this.pauseBtn.classList.add("hidden");
          this.startBtn.classList.add("hidden");
  
          // show the following buttons
          this.stopBtn.classList.remove("hidden");
          this.resumeBtn.classList.remove("hidden");
          this.prevFrameBtn.classList.remove("hidden");
          this.nextFrameBtn.classList.remove("hidden");
  
          // dont show previous button on frame 1
          this.prevFrameBtn.disabled = this.animator.onFirstFrame();
  
          // dont show next button on last frame
          this.nextFrameBtn.disabled = this.animator.onLastFrame();
      };
  
      /**
       * Enables/disable the drop down menus. When the animation is playing,
       * the drop down menus are disabled in order to prevent user from changing
       * the gradient/trajectory during an animation.
       *
       * @private
       */
      AnimationPanel.prototype._toggleSelects = function (disableStatus) {
          this.colorSelect.disabled = disableStatus;
          this.reverseColorChk.disabled = disableStatus;
          this.gradient.disabled = disableStatus;
          this.trajectory.disabled = disableStatus;
      };
  
      /**
       * Enable/disable all the UI elements in the panel except for the line
       * width control and the clade collapsing checkbox.
       *
       * @param{Bool} enabled Whether controls should be enabled (true) or
       *                      disabled (false).
       */
      AnimationPanel.prototype.setEnabled = function (enabled) {
          var isDisabled = !enabled;
          this.colorSelect.disabled = isDisabled;
          this.reverseColorChk.disabled = isDisabled;
          this.gradient.disabled = isDisabled;
          this.trajectory.disabled = isDisabled;
  
          this.pauseBtn.disabled = isDisabled;
          this.startBtn.disabled = isDisabled;
          this.stopBtn.disabled = isDisabled;
          this.resumeBtn.disabled = isDisabled;
          this.prevFrameBtn.disabled = isDisabled;
          this.nextFrameBtn.disabled = isDisabled;
      };
  
      /**
       * Set a callback to execute when an animation is started.
       *
       * @param {Function} callback Callback to execute when an animation starts.
       */
      AnimationPanel.prototype.setOnAnimationStarted = function (callback) {
          this._onAnimationStarted = callback;
      };
  
      /**
       * Set a callback to execute when an animation completes.
       *
       * @param {Function} callback Callback to execute when an animation
       *                            stops.
       */
      AnimationPanel.prototype.setOnAnimationStopped = function (callback) {
          this._onAnimationStopped = callback;
      };
  
      /**
       * Initializes GUI components/set up callback events
       */
      AnimationPanel.prototype.addAnimationTab = function () {
          // used in closures
          var scope = this;
  
          // hide play/pause/next/previous/stop buttons
          this.startOptions();
  
          // The color map selector
          Colorer.addColorsToSelect(this.colorSelect);
  
          // retrive gradient/trajectory categories
          var categories = this.animator.getSampleCategories();
  
          // add categories options to gradient drop down menu
          for (var i = 0; i < categories.length; i++) {
              var opt = document.createElement("option");
              opt.value = categories[i];
              opt.innerHTML = categories[i];
              this.gradient.appendChild(opt);
          }
  
          // copy options and add them to trajectory drop down menu
          var options = this.gradient.innerHTML;
          this.trajectory.innerHTML = options;
  
          /**
           * Event: triggers when user changes value of collapse check box
           * Sets collapse parameter in the animation state machine
           */
          this.collapseChk.onclick = function () {
              scope.animator.setCollapse(this.checked);
              scope.animator.drawFrame();
          };
  
          /**
           * Event: triggers when user changes value of line width.
           * Sets line width parameter in animation state machine.
           */
          this.lWidth.onchange = function () {
              var lw = util.parseAndValidateNum(scope.lWidth);
              // pass line width to state machine
              scope.animator.setLineWidth(lw);
              scope.animator.drawFrame();
          };
  
          /**
           * Event: triggers when user clicks on the start button.
           * Starts the animation.
           *
           * @return {null}
           */
          this.startBtn.onclick = function () {
              // change GUI components
              scope._toggleSelects(true);
              scope.__pauseOptions();
  
              // run a callback once the animation starts
              if (scope._onAnimationStarted !== null) {
                  scope._onAnimationStarted();
              }
  
              // collect starting conditions for the animation
              var gradient = scope.gradient.value;
              var trajectory = scope.trajectory.value;
              var cm = scope.colorSelect.value;
              var reverse = scope.reverseColorChk.checked;
              var collapse = scope.collapseChk.checked;
              var lWidth = scope.lWidth.value;
  
              // pass parameters to state machine
              scope.animator.setAnimationParameters(
                  trajectory,
                  gradient,
                  cm,
                  collapse,
                  lWidth,
                  reverse
              );
  
              // start animation
              scope.animator.startAnimation();
          };
  
          /**
           * Event: triggers when user clicks on pause button.
           * Pauses the animation.
           */
          this.pauseBtn.onclick = function () {
              scope.__resumeOptions();
              scope.animator.pauseAnimation();
          };
  
          /**
           * Event: triggers when user clicks on resume button.
           * Resumes the animation.
           */
          this.resumeBtn.onclick = function () {
              scope.__pauseOptions();
              scope.animator.resumeAnimation();
          };
  
          /**
           * Event: triggers when user clicks on stop button.
           * Stops the animation and clears the state machine
           */
          this.stopBtn.onclick = function () {
              scope._toggleSelects(false);
              scope.startOptions();
              scope.animator.stopAnimation();
  
              // run a callback once the animation starts
              if (scope._onAnimationStopped !== null) {
                  scope._onAnimationStopped();
              }
          };
  
          /**
           * Event: triggers when user clicks on previous button.
           * Shows the previous frame in the animation
           */
          this.prevFrameBtn.onclick = function () {
              scope.animator.prevFrame();
              scope.__resumeOptions();
          };
  
          /**
           * Event: triggers when user clicks on next button.
           * Shows the next frame in the animation.
           */
          this.nextFrameBtn.onclick = function () {
              scope.animator.nextFrame();
              scope.__resumeOptions();
          };
      };
  
      return AnimationPanel;
  });
  