/*
 * jQuery Custom Forms Plugin 1.0
 * www.ZURB.com
 * Copyright 2010, ZURB
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
*/

(function( $ ){

  /**
   * Helper object used to quickly adjust all hidden parent element's, display and visibility properties.
   * This is currently used for the custom drop downs. When the dropdowns are contained within a reveal modal
   * we cannot accurately determine the list-item elements width property, since the modal's display property is set
   * to 'none'.
   *
   * This object will help us work around that problem.
   *
   * NOTE: This could also be plugin.
   *
   * @function hiddenFix
   */
  var hiddenFix = function() {

    return {
      /**
       * Sets all hidden parent elements and self to visibile.
       *
       * @method adjust
       * @param {jQuery Object} $child
       */

      // We'll use this to temporarily store style properties.
      tmp : [],

      // We'll use this to set hidden parent elements.
      hidden : null,

      adjust : function( $child ) {
        // Internal reference.
        var _self = this;

        // Set all hidden parent elements, including this element.
        _self.hidden = $child.parents().andSelf().filter( ":hidden" );

        // Loop through all hidden elements.
        _self.hidden.each( function() {

          // Cache the element.
          var $elem = $( this );

          // Store the style attribute.
          // Undefined if element doesn't have a style attribute.
          _self.tmp.push( $elem.attr( 'style' ) );

          // Set the element's display property to block,
          // but ensure it's visibility is hidden.
          $elem.css( { 'visibility' : 'hidden', 'display' : 'block' } );
        });

      }, // end adjust

      /**
       * Resets the elements previous state.
       *
       * @method reset
       */
      reset : function() {
        // Internal reference.
        var _self = this;
        // Loop through our hidden element collection.
        _self.hidden.each( function( i ) {
          // Cache this element.
          var $elem = $( this ),
              _tmp = _self.tmp[ i ]; // Get the stored 'style' value for this element.

          // If the stored value is undefined.
          if( _tmp === undefined )
            // Remove the style attribute.
            $elem.removeAttr( 'style' );
          else
            // Otherwise, reset the element style attribute.
            $elem.attr( 'style', _tmp );

        });
        // Reset the tmp array.
        _self.tmp = [];
        // Reset the hidden elements variable.
        _self.hidden = null;

      } // end reset

    }; // end return

  };

  jQuery.foundation = jQuery.foundation || {};
  jQuery.foundation.customForms = jQuery.foundation.customForms || {};

  $.foundation.customForms.appendCustomMarkup = function ( options ) {

    var defaults = {
      disable_class: "no-custom"
    };

    options = $.extend( defaults, options );

    function appendCustomMarkup(idx, sel) {
      var $this = $(sel).hide(),
          type  = $this.attr('type'),
          $span = $this.next('span.custom.' + type);

      if ($span.length === 0) {
        $span = $('<span class="custom ' + type + '"></span>').insertAfter($this);
      }

      $span.toggleClass('checked', $this.is(':checked'));
      $span.toggleClass('disabled', $this.is(':disabled'));
    }

    function appendCustomSelect(idx, sel) {
      var hiddenFixObj = hiddenFix();
          //
          // jQueryify the <select> element and cache it.
          //
      var $this = $( sel ),
          //
          // Find the custom drop down element.
          //
          $customSelect = $this.next( 'div.custom.dropdown' ),
          //
          // Find the custom select element within the custom drop down.
          //
          $customList = $customSelect.find( 'ul' ),
          //
          // Find the custom a.current element.
          //
          $selectCurrent = $customSelect.find( ".current" ),
          //
          // Find the custom a.selector element (the drop-down icon).
          //
          $selector = $customSelect.find( ".selector" ),
          //
          // Get the <options> from the <select> element.
          //
          $options = $this.find( 'option' ),
          //
          // Filter down the selected options
          //
          $selectedOption = $options.filter( ':selected' ),
          //
          // Initial max width.
          //
          maxWidth = 0,
          //
          // We'll use this variable to create the <li> elements for our custom select.
          //
          liHtml = '',
          //
          // We'll use this to cache the created <li> elements within our custom select.
          //
          $listItems
      ;
      var $currentSelect = false;
      //
      // Should we not create a custom list?
      //
      if ( $this.hasClass( options.disable_class ) ) return;

      //
      // Did we not create a custom select element yet?
      //
      if ( $customSelect.length === 0 ) {
        //
        // Let's create our custom select element!
        //

            //
            // Determine what select size to use.
            //
        var customSelectSize = $this.hasClass( 'small' )   ? 'small'   :
                               $this.hasClass( 'medium' )  ? 'medium'  :
                               $this.hasClass( 'large' )   ? 'large'   :
                               $this.hasClass( 'expand' )  ? 'expand'  : ''
        ;
        //
        // Build our custom list.
        //
        $customSelect = $('<div class="' + ['custom', 'dropdown', customSelectSize ].join( ' ' ) + '"><a href="#" class="selector"></a><ul /></div>');
        //
        // Grab the selector element
        //
        $selector = $customSelect.find( ".selector" );
        //
        // Grab the unordered list element from the custom list.
        //
        $customList = $customSelect.find( "ul" );
        //
        // Build our <li> elements.
        //
        liHtml = $options.map( function() { return "<li>" + $( this ).html() + "</li>"; } ).get().join( '' );
        //
        // Append our <li> elements to the custom list (<ul>).
        //
        $customList.append( liHtml );
        //
        // Insert the the currently selected list item before all other elements.
        // Then, find the element and assign it to $currentSelect.
        //

        $currentSelect = $customSelect.prepend( '<a href="#" class="current">' + $selectedOption.html() + '</a>' ).find( ".current" );
        //
        // Add the custom select element after the <select> element.
        //
        $this.after( $customSelect )
        //
        //then hide the <select> element.
        //
        .hide();

      } else {
        //
        // Create our list item <li> elements.
        //
        liHtml = $options.map( function() { return "<li>" + $( this ).html() + "</li>"; } ).get().join( '' );
        //
        // Refresh the ul with options from the select in case the supplied markup doesn't match.
        // Clear what's currently in the <ul> element.
        //
        $customList.html( '' )
        //
        // Populate the list item <li> elements.
        //
        .append( liHtml );

      } // endif $customSelect.length === 0

      //
      // Determine whether or not the custom select element should be disabled.
      //
      $customSelect.toggleClass( 'disabled', $this.is( ':disabled' ) );
      //
      // Cache our List item elements.
      //
      $listItems = $customList.find( 'li' );

      //
      // Determine which elements to select in our custom list.
      //
      $options.each( function ( index ) {

        if ( this.selected ) {
          //
          // Add the selected class to the current li element
          //
          $listItems.eq( index ).addClass( 'selected' );
          //
          // Update the current element with the option value.
          //
          if ($currentSelect) {
            $currentSelect.html( $( this ).html() );
          }

        }

      });

      //
      // Update the custom <ul> list width property.
      //
      $customList.css( 'width', 'auto' );
      //
      // Set the custom select width property.
      //
      $customSelect.css( 'width', 'auto' );

      //
      // If we're not specifying a predetermined form size.
      //
      if ( !$customSelect.is( '.small, .medium, .large, .expand' ) ) {

        // ------------------------------------------------------------------------------------
        // This is a work-around for when elements are contained within hidden parents.
        // For example, when custom-form elements are inside of a hidden reveal modal.
        //
        // We need to display the current custom list element as well as hidden parent elements
        // in order to properly calculate the list item element's width property.
        // -------------------------------------------------------------------------------------

        //
        // Show the drop down.
        // This should ensure that the list item's width values are properly calculated.
        //
        $customSelect.addClass( 'open' );
        //
        // Quickly, display all parent elements.
        // This should help us calcualate the width of the list item's within the drop down.
        //
        hiddenFixObj.adjust( $customList );
        //
        // Grab the largest list item width.
        //
        maxWidth = ( $listItems.outerWidth() > maxWidth ) ? $listItems.outerWidth() : maxWidth;
        //
        // Okay, now reset the parent elements.
        // This will hide them again.
        //
        hiddenFixObj.reset();
        //
        // Finally, hide the drop down.
        //
        $customSelect.removeClass( 'open' );
        //
        // Set the custom list width.
        //
        $customSelect.width( maxWidth + 18);
        //
        // Set the custom list element (<ul />) width.
        //
        $customList.width(  maxWidth + 16 );

      } // endif

    }

    $('form.custom input:radio[data-customforms!=disabled]').each(appendCustomMarkup);
    $('form.custom input:checkbox[data-customforms!=disabled]').each(appendCustomMarkup);
    $('form.custom select[data-customforms!=disabled]').each(appendCustomSelect);
  };

  var refreshCustomSelect = function($select) {
    var maxWidth = 0,
        $customSelect = $select.next();
    $options = $select.find('option');
    $customSelect.find('ul').html('');

    $options.each(function () {
      $li = $('<li>' + $(this).html() + '</li>');
      $customSelect.find('ul').append($li);
    });

    // re-populate
    $options.each(function (index) {
      if (this.selected) {
        $customSelect.find('li').eq(index).addClass('selected');
        $customSelect.find('.current').html($(this).html());
      }
    });

    // fix width
    $customSelect.removeAttr('style')
      .find('ul').removeAttr('style');
    $customSelect.find('li').each(function () {
      $customSelect.addClass('open');
      if ($(this).outerWidth() > maxWidth) {
        maxWidth = $(this).outerWidth();
      }
      $customSelect.removeClass('open');
    });
    $customSelect.css('width', maxWidth + 18 + 'px');
    $customSelect.find('ul').css('width', maxWidth + 16 + 'px');

  };

  var toggleCheckbox = function($element) {
    var $input = $element.prev(),
        input = $input[0];

    if (false === $input.is(':disabled')) {
        input.checked = ((input.checked) ? false : true);
        $element.toggleClass('checked');

        $input.trigger('change');
    }
  };

  var toggleRadio = function($element) {
    var $input = $element.prev(),
        $form = $input.closest('form.custom'),
        input = $input[0];

    if (false === $input.is(':disabled')) {
      $form.find('input:radio[name="' + $input.attr('name') + '"]').next().not($element).removeClass('checked');
      if ( !$element.hasClass('checked') ) {
        $element.toggleClass('checked');
      }
      input.checked = $element.hasClass('checked');

      $input.trigger('change');
    }
  };

  $(document).on('click', 'form.custom span.custom.checkbox', function (event) {
    event.preventDefault();
    event.stopPropagation();

    toggleCheckbox($(this));
  });

  $(document).on('click', 'form.custom span.custom.radio', function (event) {
    event.preventDefault();
    event.stopPropagation();

    toggleRadio($(this));
  });

  $(document).on('change', 'form.custom select[data-customforms!=disabled]', function (event) {
    refreshCustomSelect($(this));
  });

  $(document).on('click', 'form.custom label', function (event) {
    var $associatedElement = $('#' + $(this).attr('for') + '[data-customforms!=disabled]'),
        $customCheckbox,
        $customRadio;
    if ($associatedElement.length !== 0) {
      if ($associatedElement.attr('type') === 'checkbox') {
        event.preventDefault();
        $customCheckbox = $(this).find('span.custom.checkbox');
        //the checkbox might be outside after the label
        if ($customCheckbox.length == 0) {
            $customCheckbox = $(this).next('span.custom.checkbox');
        }
        //the checkbox might be outside before the label
        if ($customCheckbox.length == 0) {
            $customCheckbox = $(this).prev('span.custom.checkbox');
        }
        toggleCheckbox($customCheckbox);
      } else if ($associatedElement.attr('type') === 'radio') {
        event.preventDefault();
        $customRadio = $(this).find('span.custom.radio');
        //the radio might be outside after the label
        if ($customRadio.length == 0) {
            $customRadio = $(this).next('span.custom.radio');
        }
        //the radio might be outside before the label
        if ($customRadio.length == 0) {
            $customRadio = $(this).prev('span.custom.radio');
        }
        toggleRadio($customRadio);
      }
    }
  });

  $(document).on('click', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (event) {
    var $this = $(this),
        $dropdown = $this.closest('div.custom.dropdown'),
        $select = $dropdown.prev();

    event.preventDefault();
    $('div.dropdown').removeClass('open');

    if (false === $select.is(':disabled')) {
        $dropdown.toggleClass('open');

        if ($dropdown.hasClass('open')) {
          $(document).bind('click.customdropdown', function (event) {
            $dropdown.removeClass('open');
            $(document).unbind('.customdropdown');
          });
        } else {
          $(document).unbind('.customdropdown');
        }
        return false;
    }
  });

  $(document).on('click', 'form.custom div.custom.dropdown li', function (event) {
    var $this = $(this),
        $customDropdown = $this.closest('div.custom.dropdown'),
        $select = $customDropdown.prev(),
        selectedIndex = 0;

    event.preventDefault();
    event.stopPropagation();
    $('div.dropdown').removeClass('open');

    $this
      .closest('ul')
      .find('li')
      .removeClass('selected');
    $this.addClass('selected');

    $customDropdown
      .removeClass('open')
      .find('a.current')
      .html($this.html());

    $this.closest('ul').find('li').each(function (index) {
      if ($this[0] == this) {
        selectedIndex = index;
      }

    });
    $select[0].selectedIndex = selectedIndex;

    $select.trigger('change');
  });


  $.fn.foundationCustomForms = $.foundation.customForms.appendCustomMarkup;

})( jQuery );
