/*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"]', $(this.scope)).not('[data-customforms="disabled"]') .each(this.append_custom_markup); $('form.custom input[type="checkbox"]', $(this.scope)).not('[data-customforms="disabled"]') .each(this.append_custom_markup); $('form.custom select', $(this.scope)) .not('[data-customforms="disabled"]') .not('[multiple=multiple]') .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 or inside of another element if ($customCheckbox.length == 0) { $customCheckbox = $associatedElement.add(this).siblings('span.custom.checkbox').first(); } 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 or inside of another element if ($customRadio.length == 0) { $customRadio = $associatedElement.add(this).siblings('span.custom.radio').first(); } 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'); } }); $(window).on('keydown', function (e) { var focus = document.activeElement, dropdown = $('.custom.dropdown.open'); if (dropdown.length > 0) { e.preventDefault(); if (e.which === 13) { dropdown.find('li.selected').trigger('click'); } if (e.which === 38) { var current = dropdown.find('li.selected'), prev = current.prev(':not(.disabled)'); if (prev.length > 0) { current.removeClass('selected'); prev.addClass('selected'); } } else if (e.which === 40) { var current = dropdown.find('li.selected'), next = current.next(':not(.disabled)'); if (next.length > 0) { current.removeClass('selected'); next.addClass('selected'); } } } }); this.settings.init = true; }, append_custom_markup : function (idx, sel) { var $this = $(sel).addClass('hidden-field'), type = $this.attr('type'), $span = $this.next('span.custom.' + type); if ($span.length === 0) { $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' ), copyClasses = $this.attr('class') ? $this.attr('class').split(' ') : [], 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 = $('
'); $selector = $customSelect.find(".selector"); $customList = $customSelect.find("ul"); liHtml = $options.map(function() { return "
  • " + $( this ).html() + "
  • "; } ).get().join( '' ); $customList.append(liHtml); $currentSelect = $customSelect.prepend('' + $selectedOption.html() + '' ).find( ".current" ); $this .after( $customSelect ) .addClass('hidden-field'); } else { liHtml = $options.map(function() { return "
  • " + $( this ).html() + "
  • "; }) .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 = $('
  • ' + $(this).html() + '
  • '); $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));