(function ($) {
  Drupal.behaviors.dateSelect = {};

  Drupal.behaviors.dateSelect.attach = function (context, settings) {
    var $widget = $('.form-type-date-select').parents('fieldset').once('date');
    var i;
    for (i = 0; i < $widget.length; i++) {
      new Drupal.date.EndDateHandler($widget[i]);
    }
  };

  Drupal.date = Drupal.date || {};

  /**
   * Constructor for the EndDateHandler object.
   *
   * The EndDateHandler is responsible for synchronizing a date select widget's
   * end date with its start date. This behavior lasts until the user
   * interacts with the end date widget.
   *
   * @param widget
   *   The fieldset DOM element containing the from and to dates.
   */
  Drupal.date.EndDateHandler = function (widget) {
    this.$widget = $(widget);
    this.$start = this.$widget.find('.form-type-date-select[class$=value]');
    this.$end = this.$widget.find('.form-type-date-select[class$=value2]');
    if (this.$end.length === 0) {
      return;
    }
    this.initializeSelects();
    // Only act on date fields where the end date is completely blank or already
    // the same as the start date. Otherwise, we do not want to override whatever
    // the default value was.
    if (this.endDateIsBlank() || this.endDateIsSame()) {
      this.bindClickHandlers();
      // Start out with identical start and end dates.
      this.syncEndDate();
    }
  };

  /**
   * Store all the select dropdowns in an array on the object, for later use.
   */
  Drupal.date.EndDateHandler.prototype.initializeSelects = function () {
    var $starts = this.$start.find('select');
    var $end, $start, endId, i, id;
    this.selects = {};
    for (i = 0; i < $starts.length; i++) {
      $start = $($starts[i]);
      id = $start.attr('id');
      endId = id.replace('-value-', '-value2-');
      $end = $('#' + endId);
      this.selects[id] = {
        'id': id,
        'start': $start,
        'end': $end
      };
    }
  };

  /**
   * Returns true if all dropdowns in the end date widget are blank.
   */
  Drupal.date.EndDateHandler.prototype.endDateIsBlank = function () {
    var id;
    for (id in this.selects) {
      if (this.selects.hasOwnProperty(id)) {
        if (this.selects[id].end.val() !== '') {
          return false;
        }
      }
    }
    return true;
  };

  /**
   * Returns true if the end date widget has the same value as the start date.
   */
  Drupal.date.EndDateHandler.prototype.endDateIsSame = function () {
    var id;
    for (id in this.selects) {
      if (this.selects.hasOwnProperty(id)) {
        if (this.selects[id].end.val() != this.selects[id].start.val()) {
          return false;
        }
      }
    }
    return true;
  };

  /**
   * Add a click handler to each of the start date's select dropdowns.
   */
  Drupal.date.EndDateHandler.prototype.bindClickHandlers = function () {
    var id;
    for (id in this.selects) {
      if (this.selects.hasOwnProperty(id)) {
        this.selects[id].start.bind('click.endDateHandler', this.startClickHandler.bind(this));
        this.selects[id].end.bind('focus', this.endFocusHandler.bind(this));
      }
    }
  };

  /**
   * Click event handler for each of the start date's select dropdowns.
   */
  Drupal.date.EndDateHandler.prototype.startClickHandler = function (event) {
    this.syncEndDate();
  };

  /**
   * Focus event handler for each of the end date's select dropdowns.
   */
  Drupal.date.EndDateHandler.prototype.endFocusHandler = function (event) {
    var id;
    for (id in this.selects) {
      if (this.selects.hasOwnProperty(id)) {
        this.selects[id].start.unbind('click.endDateHandler');
      }
    }
    $(event.target).unbind('focus', this.endFocusHandler);
  };

  Drupal.date.EndDateHandler.prototype.syncEndDate = function () {
    var id;
    for (id in this.selects) {
      if (this.selects.hasOwnProperty(id)) {
        this.selects[id].end.val(this.selects[id].start.val());
      }
    }
  };

}(jQuery));

/**
 * Function.prototype.bind polyfill for older browsers.
 *
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
 */
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // Closest thing possible to the ECMAScript5 internal IsCallable function.
      throw new TypeError("Function.prototype.bind - what is trying to be fBound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
