(function ($, window, document, undefined) { 'use strict'; Foundation.libs.forms = { name: 'forms', version: '4.2.3', cache: {}, settings: { disable_class: 'no-custom', last_combo : null }, init: function (scope, method, options) { 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"]') .not('.' + this.settings.disable_class) .each(this.append_custom_markup); $('form.custom input[type="checkbox"]', $(this.scope)) .not('[data-customforms="disabled"]') .not('.' + this.settings.disable_class) .each(this.append_custom_markup); $('form.custom select', $(this.scope)) .not('[data-customforms="disabled"]') .not('.' + this.settings.disable_class) .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', function (e, force_refresh) { if ($(this).is('[data-customforms="disabled"]')) return; self.refresh_custom_select($(this), force_refresh); }) .on('click.fndtn.forms', 'form.custom label', function (e) { if ($(e.target).is('label')) { 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('mousedown.fndtn.forms', 'form.custom div.custom.dropdown', function () { return false; }) .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 = getFirstPrevSibling($dropdown, 'select'); // 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 = getFirstPrevSibling($customDropdown, 'select'), 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') .text($this.text()); $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, self = Foundation.libs.forms, dropdown = $('.custom.dropdown.open'); if (dropdown.length > 0) { e.preventDefault(); if (e.which === 13) { dropdown.find('li.selected').trigger('click'); } if (e.which === 27) { dropdown.removeClass('open'); } if (e.which >= 65 && e.which <= 90) { var next = self.go_to(dropdown, e.which), current = dropdown.find('li.selected'); if (next) { current.removeClass('selected'); self.scrollTo(next.addClass('selected'), 300); } } if (e.which === 38) { var current = dropdown.find('li.selected'), prev = current.prev(':not(.disabled)'); if (prev.length > 0) { prev.parent()[0].scrollTop = prev.parent().scrollTop() - self.outerHeight(prev); 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) { next.parent()[0].scrollTop = next.parent().scrollTop() + self.outerHeight(next); current.removeClass('selected'); next.addClass('selected'); } } } }); this.settings.init = true; }, go_to: function (dropdown, character) { var lis = dropdown.find('li'), count = lis.length; if (count > 0) { for (var i = 0; i < count; i++) { var first_letter = lis.eq(i).text().charAt(0).toLowerCase(); if (first_letter === String.fromCharCode(character).toLowerCase()) return lis.eq(i); } } }, scrollTo: function (el, duration) { if (duration < 0) return; var parent = el.parent(); var li_height = this.outerHeight(el); var difference = (li_height * (el.index())) - parent.scrollTop(); var perTick = difference / duration * 10; this.scrollToTimerCache = setTimeout(function () { if (!isNaN(parseInt(perTick, 10))) { parent[0].scrollTop = parent.scrollTop() + perTick; this.scrollTo(el, duration - 10); } }.bind(this), 10); }, append_custom_markup: function (idx, sel) { var $this = $(sel), type = $this.attr('type'), $span = $this.next('span.custom.' + type); if (!$this.parent().hasClass('switch')) { $this.addClass('hidden-field'); } 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 ($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 () { var copyClasses = $(this).attr('class') ? $(this).attr('class') : ''; 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 self.assign_id($this, $customSelect); $customSelect.toggleClass('disabled', $this.is(':disabled')); $listItems = $customList.find('li'); // cache list length self.cache[$customSelect.data('id')] = $listItems.length; $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 }, assign_id: function ($select, $customSelect) { var id = [+new Date(), Foundation.random_str(5)].join('-'); $select.attr('data-id', id); $customSelect.attr('data-id', id); }, refresh_custom_select: function ($select, force_refresh) { var self = this; var maxWidth = 0, $customSelect = $select.next(), $options = $select.find('option'), $listItems = $customSelect.find('li'); if ($listItems.length !== this.cache[$customSelect.data('id')] || force_refresh) { $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'); }); $listItems = $customSelect.find('li'); // cache list length this.cache[$customSelect.data('id')] = $listItems.length; } }, 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) { if (!text) return ''; 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(); _self.hidden = _self.hidden.add($child).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'); }, reflow : function () {} }; var getFirstPrevSibling = function($el, selector) { var $el = $el.prev(); while ($el.length) { if ($el.is(selector)) return $el; $el = $el.prev(); } return $(); }; }(Foundation.zj, this, this.document));