Source: ui/controls/wizard.js

/**
 * Created by Florin Chelaru ( florinc [at] umd [dot] edu )
 * Date: 3/30/14
 * Time: 6:21 PM
 */

goog.provide('epiviz.ui.controls.Wizard');

/**
 * @param {string} title
 * @param {{finish: function(*)=, close: function()=}} handlers
 * @param {Array.<epiviz.ui.controls.Wizard.Step>} steps
 * @param {*} initialData
 * @param {string} [width]
 * @param {string} [height]
 * @param {boolean} [showTabs]
 * @constructor
 * @extends {epiviz.ui.controls.Dialog}
 */
epiviz.ui.controls.Wizard = function(title, handlers, steps, initialData, width, height, showTabs) {
  epiviz.ui.controls.Dialog.call(this, title, handlers);

  /**
   * @type {Array.<epiviz.ui.controls.Wizard.Step>}
   * @private
   */
  this._steps = steps;

  /**
   * @type {*}
   * @private
   */
  this._initialData = initialData;

  /**
   * @type {string=}
   * @private
   */
  this._width = width;

  /**
   * @type {string=}
   * @private
   */
  this._height = height;

  /**
   * @type {boolean}
   * @private
   */
  this._showTabs = showTabs || false;

  /**
   * @type {?jQuery}
   * @private
   */
  this._tabs = null;

  this._initialize();
};

/**
 * Copy methods from upper class
 */
epiviz.ui.controls.Wizard.prototype = epiviz.utils.mapCopy(epiviz.ui.controls.Dialog.prototype);
epiviz.ui.controls.Wizard.constructor = epiviz.ui.controls.Wizard;

/**
 * @private
 */
epiviz.ui.controls.Wizard.prototype._initialize = function() {
  var self = this;
  this._dialog = $('#' + this._id);
  this._dialog.append(
    '<div id="wizardDialog" class="wizard-dialog">' +
      '<div class="wizard-tabs">' +
        '<ul class="wizard-tabs-title-list"></ul>' +
      '</div>' +
    '</div>');

  this._tabs = this._dialog.find('.wizard-tabs');

  /** @type {jQuery} */
  var titleList = this._tabs.find('.wizard-tabs-title-list');

  for (var i = 0; i < this._steps.length; ++i) {
    titleList.append(sprintf('<li><a href="#%s-tab-%s">%s</a></li>', this._id, i, this._steps[i].title()));
    this._tabs.append(sprintf('<div id="%s-tab-%s"></div>', this._id, i));
  }

  if (!this._showTabs) {
    titleList.css('visibility', 'hidden');
    titleList.css('position', 'absolute');
  }

  this._tabs.tabs({
    activate: function(e, ui) { self._tabActivate(ui); },
    disabled: epiviz.utils.range(this._steps.length - 1, 1)
  });

  this._dialog.dialog({
    autoOpen: false,
    resizable: true,
    width: this._width || undefined,
    height: this._height || undefined,
    buttons: {
      Back: function() {
        var selectedTabIndex = self._tabs.tabs('option', 'active');
        if (selectedTabIndex == 0) { return; }

        self._tabs.tabs('option', 'disabled', epiviz.utils.range(self._steps.length - selectedTabIndex, selectedTabIndex));
        self._tabs.tabs('option', 'active', selectedTabIndex - 1);
      },
      Next: function() {
        var selectedTabIndex = self._tabs.tabs('option', 'active');

        /** @type {{data: *=, error: string=}} */
        var result = self._steps[selectedTabIndex].next();

        if (result.error) {
          var errorDialog = new epiviz.ui.controls.MessageDialog('Error', { Ok: function() {} }, result.error, epiviz.ui.controls.MessageDialog.Icon.ERROR);
          errorDialog.show();
          return;
        }

        self._steps[selectedTabIndex+1].initialize($(sprintf('#%s-tab-%s', self._id, selectedTabIndex+1)), result.data);

        self._tabs.tabs('option', 'disabled', epiviz.utils.range(self._steps.length - selectedTabIndex - 2, selectedTabIndex + 2));
        self._tabs.tabs('option', 'active', selectedTabIndex + 1);
      },
      Finish: function() {
        /** @type {{data: *=, error: string=}} */
        var result = self._steps[self._steps.length - 1].next();

        if (result.error) {
          var errorDialog = new epiviz.ui.controls.MessageDialog('Error', { Ok: function() {} }, result.error, epiviz.ui.controls.MessageDialog.Icon.ERROR);
          errorDialog.show();
          return;
        }

        if (self._handlers.finish) {
          self._handlers.finish(result.data);
        }

        $(this).dialog('close');
      },
      Cancel: function() {
        if (self._handlers.close) {
          self._handlers.close();
        }
        $(this).dialog('close');
      }
    },
    modal: true
  });

  if (this._steps.length > 1) {
    this._dialog.parent().find('button:contains("Finish")').button('disable');
    this._dialog.parent().find('button:contains("Next")').button('enable');
  } else {
    this._dialog.parent().find('button:contains("Finish")').button('enable');
    this._dialog.parent().find('button:contains("Next")').button('disable');
  }
  this._steps[0].initialize($(sprintf('#%s-tab-0', self._id)), this._initialData);
  this._dialog.css('overflow', 'visible');
};

/**
 * @param ui
 * @private
 */
epiviz.ui.controls.Wizard.prototype._tabActivate = function(ui) {
  var selectedTabIndex = this._tabs.tabs('option', 'active');
  var finishButton = this._dialog.parent().find('button:contains("Finish")');
  var nextButton = this._dialog.parent().find('button:contains("Next")');

  if (selectedTabIndex == this._steps.length - 1) {
    nextButton.button('disable');
    finishButton.button('enable');
  } else {
    nextButton.button('enable');
    finishButton.button('disable');
  }

  this._tabs.tabs('option', 'disabled', epiviz.utils.range(this._steps.length - selectedTabIndex - 1, selectedTabIndex + 1));
};

/**
 */
epiviz.ui.controls.Wizard.prototype.show = function() {
  var self = this;

  this._dialog.dialog('open');
  this._dialog.dialog('option', 'position', 'center');

  // This makes the dialog only able to open once:
  this._dialog.dialog({
    close: function(event, ui) {
      $(this).remove();
      self._dialog = null;
    }
  });
};

goog.provide('epiviz.ui.controls.Wizard.Step');

/**
 * @interface
 * @template I, O
 */
epiviz.ui.controls.Wizard.Step = function() {};

/**
 * @param {jQuery} container
 * @param {I} [data]
 */
epiviz.ui.controls.Wizard.Step.prototype.initialize = function(container, data) {};

/**
 * Gets the data resulted from manipulation of this wizard step, or, if
 * there is an error, error contains the details of the error that occurred.
 * @returns {{data: O=, error: string=}}
 */
epiviz.ui.controls.Wizard.Step.prototype.next = function() {};

/**
 * @returns {string}
 */
epiviz.ui.controls.Wizard.Step.prototype.title = function() {};