/**
 * @fileoverview JavaScript library used in conjunction with
 * http://www.google.com/css/modules/tabs/g-tabs_*.css.
 *
 * This file requires http://www.google.com/js/gweb/core.js
 * to work correctly.
 *
 * @author drewniak (Michal Drewniak)
 */

/**
 * See if the global namespace object 'goog' exists. If it doesn't, create it.
 */
var gweb = gweb || {};

/**
 * See if the object goog.ui exists. If it doesn't create it.
 */
gweb.ui = gweb.ui || {};

/**
 * Constructor for a tabs widget.
 * @constructor
 */
gweb.ui.GTabs = function() {
  this.tabSets_ = this.findTabSets_();
  this.initListeners_();
  this.showTabs_();
};

/**
 * An object key used to access content associated with a tab.
 * @type {string}
 */
gweb.ui.GTabs.KEY_CONTENT = 'content';

/**
 * An object key prefix for a set of tabs.
 * @type {string}
 */
gweb.ui.GTabs.KEY_SET = 'set';

/**
 * Default hash value for a tab, used when no user specified hash exists.
 * Also a key in an object holding tab <li> element and content associated
 * with it.
 * @type {string}
 */
gweb.ui.GTabs.KEY_TAB = 'tab';

/**
 * Parses a page for all sections which contain tabs (elements containing
 * g-tab class name).
 * @private
 * @return {Object} Sets of tabs found on the page. A set of tabs is
 *     a collection of tabs and contents associated with them.
 */
gweb.ui.GTabs.prototype.findTabSets_ = function() {
  var contSets = gweb.dom.getElementsByTagNameAndClass('div', 'g-tab-contents');
  var tabSets = gweb.dom.getElementsByTagNameAndClass('div', 'g-tabs');
  var contSetsLen = contSets.length;
  var tabSetsLen = tabSets.length;
  var tabs = {};

  if (contSetsLen != tabSetsLen) return null;

  for (var i = 0; i < tabSetsLen; i++) {
    tabs[gweb.ui.GTabs.KEY_SET + '-' + i] = [];
    contSet = contSets[i];
    tabSet = tabSets[i];

    var contDivs = gweb.dom.getElementsByTagNameAndClass('div', 'g-tab-content',
                                                         contSet);
    var tabLis = gweb.dom.getElementsByTagNameAndClass('li', 'g-tab', tabSet);
    var contDivsLen = contDivs.length;
    var tabLisLen = tabLis.length;

    if (contDivsLen != tabLisLen) return null;

    // Constructs an object which holds a tab div element and content div
    // associated with that tab.
    for (var j = 0; j < tabLisLen; j++) {
      var tab = {};

      tab[gweb.ui.GTabs.KEY_TAB] = tabLis[j];
      tab[gweb.ui.GTabs.KEY_CONTENT] = contDivs[j];
      tabs[gweb.ui.GTabs.KEY_SET + '-' + i].push(tab);
    }
  }

  return tabs;
};

/**
 * Initializes and attaches event listeners to all tabs found on the page.
 * @private
 */
gweb.ui.GTabs.prototype.initListeners_ = function() {
  for (var tabSet in this.tabSets_) {
    var tabs = this.tabSets_[tabSet];

    for (var i = 0, len = tabs.length; i < len; i++) {
      var tab = tabs[i][gweb.ui.GTabs.KEY_TAB];
      gweb.events.listen(tab, 'click', this.select_(tabs, i), false, this);
    }
  }
};

/**
 * Reads hash associated with the tab.
 * @param {Element} tab <li> element which contains the tab.
 * @param {number} tabNum n-order number identifying a tab on the page.
 * @private
 * @return {string?} Hash associated with the tab.
 */
gweb.ui.GTabs.prototype.readHash_ = function(tab, tabNum) {
  var anchor = tab.getElementsByTagName('a');
  var hash = anchor ? anchor[0].hash : null;

  return hash || '#' + gweb.ui.GTabs.KEY_TAB + tabNum;
};

/**
 * Selects tab and shows content associated with it. Updates hash value
 * in the browser's url. Executes when user clicks on a tab.
 * @param {Array.<Object>} tabs List of tabs and contents associated
 *     with each tab. Each element in the array is an object which contains
 *     the following properties:
 *       'tab': div containg the tab
 *       'content': div containing the content associated with the tab.
 * @param {number} selectedIndex Position in the tabs array
 *    of the selected (clicked) tab.
 * @private
 * @return {Function} Function which exectues when user clicks on a tab.
 */
gweb.ui.GTabs.prototype.select_ = function(tabs, selectedIndex) {
  return function() {
    var event = arguments[0] || null;

    this.show_(tabs, selectedIndex);
    this.setHash_();
    if (event) event.preventDefault();
  }
};

/**
 * Updates hash value in the browser's url. If a tab has no hash associated
 * with it, default hash will be shown.
 * @private
 * @return {string} Hash value with which the url was updated.
 */
gweb.ui.GTabs.prototype.setHash_ = function() {
  var tabLis = gweb.dom.getElementsByTagNameAndClass('li', 'g-tab');
  var hash = '';

  for (var i = 0, tab; tab = tabLis[i]; i++) {
    if (gweb.dom.classes.has(tab, 'g-tab-selected')) {
      hash += this.readHash_(tab, i)
    }
  }

  window.location.hash = hash;
  return hash;
};

/**
 * Selects tab and shows content associated with it.
 * @param {Array.<Object>} tabs List of tabs and contents associated
 *     with each tab. Each element in the array is an object which contains
 *     the following properties:
 *       'tab': div containg the tab
 *       'content': div containing the content associated with the tab.
 * @param {number} selectedIndex Position in the tabs array
 *    of the selected (clicked) tab.
 * @private
 */
gweb.ui.GTabs.prototype.show_ = function(tabs, selectedIndex) {
  for (var i = 0, len = tabs.length; i < len; i++) {
    gweb.dom.classes.remove(tabs[i][gweb.ui.GTabs.KEY_TAB], 'g-tab-selected');
    gweb.dom.classes.add(tabs[i][gweb.ui.GTabs.KEY_CONTENT],
                         'g-tab-content-hidden');
  }

  gweb.dom.classes.add(tabs[selectedIndex][gweb.ui.GTabs.KEY_TAB],
                       'g-tab-selected');
  gweb.dom.classes.remove(tabs[selectedIndex][gweb.ui.GTabs.KEY_CONTENT],
                          'g-tab-content-hidden');
};

/**
 * Implementation of "sticky urls." Shows content of the tabs based on the
 * hash values passed via url. If no recognizable hash values were passed,
 * method will display contents of the first tab in each tab set.
 * @private
 */
gweb.ui.GTabs.prototype.showTabs_ = function() {
  var locationHash = unescape(window.location.hash).replace(/#/ig, '|#');
  var browserHashes = locationHash.split('|');
  var tabNum = 0;

  for (var tabSet in this.tabSets_) {
    var hashInUrl = false;
    var tabs = this.tabSets_[tabSet];

    for (var i = 0, len = tabs.length; i < len; i++) {
      var tab = tabs[i][gweb.ui.GTabs.KEY_TAB];
      tabHash = this.readHash_(tab, tabNum++);
      if (gweb.array.contains(browserHashes, tabHash)) {
        hashInUrl = true;
        this.show_(tabs, i);
      }
    }

    if (!hashInUrl) this.show_(tabs, 0);
  }
};
