text: /*jslint unparam: true, browser: true, indent: 2 */
  
  ;(function ($, window, document, undefined) {
    'use strict';
  
    Foundation.libs.forms = {
      name : 'forms',
  
      version : '4.0.4',
  
      settings : {
        disable_class: 'no-custom'
      },
  
      init : function (scope, method, options) {
        this.scope = scope || this.scope;
  
        if (typeof method === 'object') {
          $.extend(true, this.settings, method);
        }
  
        if (typeof method != 'string') {
          if (!this.settings.init) {
            this.events();
          }
  
          this.assemble();
  
          return this.settings.init;
        } else {
          return this[method].call(this, options);
        }
      },
  
      assemble : function () {
        $('form.custom input[type="radio"]').not('[data-customforms="disabled"]')
          .each(this.append_custom_markup);
        $('form.custom input[type="checkbox"]').not('[data-customforms="disabled"]')
          .each(this.append_custom_markup);
        $('form.custom select').not('[data-customforms="disabled"]')
          .each(this.append_custom_select);
      },
  
      events : function () {
        var self = this;
  
        $(this.scope)
          .on('click.fndtn.forms', 'form.custom span.custom.checkbox', function (e) {
            e.preventDefault();
            e.stopPropagation();
            self.toggle_checkbox($(this));
          })
          .on('click.fndtn.forms', 'form.custom span.custom.radio', function (e) {
            e.preventDefault();
            e.stopPropagation();
            self.toggle_radio($(this));
          })
          .on('change.fndtn.forms', 'form.custom select:not([data-customforms="disabled"])', function (e) {
            self.refresh_custom_select($(this));
          })
          .on('click.fndtn.forms', 'form.custom label', function (e) {
            var $associatedElement = $('#' + self.escape($(this).attr('for')) + ':not([data-customforms="disabled"])'),
                $customCheckbox,
                $customRadio;
            if ($associatedElement.length !== 0) {
              if ($associatedElement.attr('type') === 'checkbox') {
                e.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');
                }
                self.toggle_checkbox($customCheckbox);
              } else if ($associatedElement.attr('type') === 'radio') {
                e.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');
                }
                self.toggle_radio($customRadio);
              }
            }
          })
          .on('click.fndtn.forms', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (e) {
            var $this = $(this),
                $dropdown = $this.closest('div.custom.dropdown'),
                $select = $dropdown.prev();
  
            // make sure other dropdowns close
            if(!$dropdown.hasClass('open'))
              $(self.scope).trigger('click');
  
            e.preventDefault();
            if (false === $select.is(':disabled')) {
              $dropdown.toggleClass('open');
  
              if ($dropdown.hasClass('open')) {
                $(self.scope).on('click.fndtn.forms.customdropdown', function () {
                  $dropdown.removeClass('open');
                  $(self.scope).off('.fndtn.forms.customdropdown');
                });
              } else {
                $(self.scope).on('.fndtn.forms.customdropdown');
              }
              return false;
            }
          })
          .on('click.fndtn.forms touchend.fndtn.forms', 'form.custom div.custom.dropdown li', function (e) {
            var $this = $(this),
                $customDropdown = $this.closest('div.custom.dropdown'),
                $select = $customDropdown.prev(),
                selectedIndex = 0;
  
            e.preventDefault();
            e.stopPropagation();
  
            if ( ! $(this).hasClass('disabled')) {
              $('div.dropdown').not($customDropdown).removeClass('open');
  
              var $oldThis= $this
                .closest('ul')
                .find('li.selected');
              $oldThis.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;
  
              //store the old value in data
              $select.data('prevalue', $oldThis.html());
              $select.trigger('change');
            }
          });
  
        this.settings.init = true;
      },
  
      append_custom_markup : function (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'));
      },
  
      append_custom_select : function (idx, sel) {
        var self = Foundation.libs.forms,
            $this = $( sel ),
            $customSelect = $this.next( 'div.custom.dropdown' ),
            $customList = $customSelect.find( 'ul' ),
            $selectCurrent = $customSelect.find( ".current" ),
            $selector = $customSelect.find( ".selector" ),
            $options = $this.find( 'option' ),
            $selectedOption = $options.filter( ':selected' ),
            maxWidth = 0,
            liHtml = '',
            $listItems,
            $currentSelect = false;
  
        if ($this.hasClass(self.settings.disable_class)) return;
  
        if ($customSelect.length === 0) {
          var customSelectSize = $this.hasClass( 'small' )   ? 'small'   :
                                 $this.hasClass( 'medium' )  ? 'medium'  :
                                 $this.hasClass( 'large' )   ? 'large'   :
                                 $this.hasClass( 'expand' )  ? 'expand'  : '';
  
          $customSelect = $('<div class="' + ['custom', 'dropdown', customSelectSize ].join( ' ' ) + '"><a href="#" class="selector"></a><ul /></div>');
          $selector = $customSelect.find(".selector");
          $customList = $customSelect.find("ul");
          liHtml = $options.map(function() { return "<li>" + $( this ).html() + "</li>"; } ).get().join( '' );
          $customList.append(liHtml);
          $currentSelect = $customSelect.prepend('<a href="#" class="current">' + $selectedOption.html() + '</a>' ).find( ".current" );
          $this
            .after( $customSelect )
            .hide();
  
        } else {
          liHtml = $options.map(function() {
              return "<li>" + $( this ).html() + "</li>";
            })
            .get().join('');
          $customList
            .html('')
            .append(liHtml);
  
        } // endif $customSelect.length === 0
        $customSelect.toggleClass('disabled', $this.is( ':disabled' ) );
        $listItems = $customList.find( 'li' );
  
        $options.each( function ( index ) {
          if ( this.selected ) {
            $listItems.eq( index ).addClass( 'selected' );
  
            if ($currentSelect) {
              $currentSelect.html( $( this ).html() );
            }
  
          }
          if ($(this).is(':disabled')) {
            $listItems.eq( index ).addClass( 'disabled' );
          }
        });
  
        //
        // 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.
          // -------------------------------------------------------------------------------------
  
          $customSelect.addClass( 'open' );
          //
          // Quickly, display all parent elements.
          // This should help us calcualate the width of the list item's within the drop down.
          //
          var self = Foundation.libs.forms;
          self.hidden_fix.adjust( $customList );
  
          maxWidth = ( self.outerWidth($listItems) > maxWidth ) ? self.outerWidth($listItems) : maxWidth;
  
          Foundation.libs.forms.hidden_fix.reset();
  
          $customSelect.removeClass( 'open' );
  
        } // endif
  
      },
  
      refresh_custom_select : function ($select) {
        var self = this;
        var maxWidth = 0,
          $customSelect = $select.next(),
          $options = $select.find('option');
  
        $customSelect.find('ul').html('');
  
        $options.each(function () {
          var $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());
          }
          if ($(this).is(':disabled')) {
            $customSelect.find('li').eq(index).addClass('disabled');
          }
        });
  
        // fix width
        $customSelect.removeAttr('style')
          .find('ul').removeAttr('style');
        $customSelect.find('li').each(function () {
          $customSelect.addClass('open');
          if (self.outerWidth($(this)) > maxWidth) {
            maxWidth = self.outerWidth($(this));
          }
          $customSelect.removeClass('open');
        });
      },
  
      toggle_checkbox : 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');
        }
      },
  
      toggle_radio : function ($element) {
        var $input = $element.prev(),
            $form = $input.closest('form.custom'),
            input = $input[0];
  
        if (false === $input.is(':disabled')) {
          $form.find('input[type="radio"][name="' + this.escape($input.attr('name')) + '"]').next().not($element).removeClass('checked');
          if ( !$element.hasClass('checked') ) {
            $element.toggleClass('checked');
          }
          input.checked = $element.hasClass('checked');
  
          $input.trigger('change');
        }
      },
  
      escape : function (text) {
        return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
      },
  
      hidden_fix : {
        /**
         * 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
  
      },
  
      off : function () {
        $(this.scope).off('.fndtn.forms');
      }
    };
  }(Foundation.zj, this, this.document));