/components/foundation/javascripts/jquery.foundation.forms.js

https://bitbucket.org/ceoaliongroo/torredelprior · JavaScript · 486 lines · 365 code · 28 blank · 93 comment · 6 complexity · 67a3f0d91d5fc4142fe71cda2de49271 MD5 · raw file

  1. /*
  2. * jQuery Custom Forms Plugin 1.0
  3. * www.ZURB.com
  4. * Copyright 2010, ZURB
  5. * Free to use under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. */
  8. (function( $ ){
  9. /**
  10. * Helper object used to quickly adjust all hidden parent element's, display and visibility properties.
  11. * This is currently used for the custom drop downs. When the dropdowns are contained within a reveal modal
  12. * we cannot accurately determine the list-item elements width property, since the modal's display property is set
  13. * to 'none'.
  14. *
  15. * This object will help us work around that problem.
  16. *
  17. * NOTE: This could also be plugin.
  18. *
  19. * @function hiddenFix
  20. */
  21. var hiddenFix = function() {
  22. return {
  23. /**
  24. * Sets all hidden parent elements and self to visibile.
  25. *
  26. * @method adjust
  27. * @param {jQuery Object} $child
  28. */
  29. // We'll use this to temporarily store style properties.
  30. tmp : [],
  31. // We'll use this to set hidden parent elements.
  32. hidden : null,
  33. adjust : function( $child ) {
  34. // Internal reference.
  35. var _self = this;
  36. // Set all hidden parent elements, including this element.
  37. _self.hidden = $child.parents().andSelf().filter( ":hidden" );
  38. // Loop through all hidden elements.
  39. _self.hidden.each( function() {
  40. // Cache the element.
  41. var $elem = $( this );
  42. // Store the style attribute.
  43. // Undefined if element doesn't have a style attribute.
  44. _self.tmp.push( $elem.attr( 'style' ) );
  45. // Set the element's display property to block,
  46. // but ensure it's visibility is hidden.
  47. $elem.css( { 'visibility' : 'hidden', 'display' : 'block' } );
  48. });
  49. }, // end adjust
  50. /**
  51. * Resets the elements previous state.
  52. *
  53. * @method reset
  54. */
  55. reset : function() {
  56. // Internal reference.
  57. var _self = this;
  58. // Loop through our hidden element collection.
  59. _self.hidden.each( function( i ) {
  60. // Cache this element.
  61. var $elem = $( this ),
  62. _tmp = _self.tmp[ i ]; // Get the stored 'style' value for this element.
  63. // If the stored value is undefined.
  64. if( _tmp === undefined )
  65. // Remove the style attribute.
  66. $elem.removeAttr( 'style' );
  67. else
  68. // Otherwise, reset the element style attribute.
  69. $elem.attr( 'style', _tmp );
  70. });
  71. // Reset the tmp array.
  72. _self.tmp = [];
  73. // Reset the hidden elements variable.
  74. _self.hidden = null;
  75. } // end reset
  76. }; // end return
  77. };
  78. jQuery.foundation = jQuery.foundation || {};
  79. jQuery.foundation.customForms = jQuery.foundation.customForms || {};
  80. $.foundation.customForms.appendCustomMarkup = function ( options ) {
  81. var defaults = {
  82. disable_class: "js-disable-custom"
  83. };
  84. options = $.extend( defaults, options );
  85. function appendCustomMarkup(idx, sel) {
  86. var $this = $(sel).hide(),
  87. type = $this.attr('type'),
  88. $span = $this.next('span.custom.' + type);
  89. if ($span.length === 0) {
  90. $span = $('<span class="custom ' + type + '"></span>').insertAfter($this);
  91. }
  92. $span.toggleClass('checked', $this.is(':checked'));
  93. $span.toggleClass('disabled', $this.is(':disabled'));
  94. }
  95. function appendCustomSelect(idx, sel) {
  96. var hiddenFixObj = hiddenFix();
  97. //
  98. // jQueryify the <select> element and cache it.
  99. //
  100. var $this = $( sel ),
  101. //
  102. // Find the custom drop down element.
  103. //
  104. $customSelect = $this.next( 'div.custom.dropdown' ),
  105. //
  106. // Find the custom select element within the custom drop down.
  107. //
  108. $customList = $customSelect.find( 'ul' ),
  109. //
  110. // Find the custom a.current element.
  111. //
  112. $selectCurrent = $customSelect.find( ".current" ),
  113. //
  114. // Find the custom a.selector element (the drop-down icon).
  115. //
  116. $selector = $customSelect.find( ".selector" ),
  117. //
  118. // Get the <options> from the <select> element.
  119. //
  120. $options = $this.find( 'option' ),
  121. //
  122. // Filter down the selected options
  123. //
  124. $selectedOption = $options.filter( ':selected' ),
  125. //
  126. // Initial max width.
  127. //
  128. maxWidth = 0,
  129. //
  130. // We'll use this variable to create the <li> elements for our custom select.
  131. //
  132. liHtml = '',
  133. //
  134. // We'll use this to cache the created <li> elements within our custom select.
  135. //
  136. $listItems
  137. ;
  138. var $currentSelect = false;
  139. //
  140. // Should we not create a custom list?
  141. //
  142. if ( $this.hasClass( 'no-custom' ) ) return;
  143. //
  144. // Did we not create a custom select element yet?
  145. //
  146. if ( $customSelect.length === 0 ) {
  147. //
  148. // Let's create our custom select element!
  149. //
  150. //
  151. // Determine what select size to use.
  152. //
  153. var customSelectSize = $this.hasClass( 'small' ) ? 'small' :
  154. $this.hasClass( 'medium' ) ? 'medium' :
  155. $this.hasClass( 'large' ) ? 'large' :
  156. $this.hasClass( 'expand' ) ? 'expand' : ''
  157. ;
  158. //
  159. // Build our custom list.
  160. //
  161. $customSelect = $('<div class="' + ['custom', 'dropdown', customSelectSize ].join( ' ' ) + '"><a href="#" class="selector"></a><ul /></div>"');
  162. //
  163. // Grab the selector element
  164. //
  165. $selector = $customSelect.find( ".selector" );
  166. //
  167. // Grab the unordered list element from the custom list.
  168. //
  169. $customList = $customSelect.find( "ul" );
  170. //
  171. // Build our <li> elements.
  172. //
  173. liHtml = $options.map( function() { return "<li>" + $( this ).html() + "</li>"; } ).get().join( '' );
  174. //
  175. // Append our <li> elements to the custom list (<ul>).
  176. //
  177. $customList.append( liHtml );
  178. //
  179. // Insert the the currently selected list item before all other elements.
  180. // Then, find the element and assign it to $currentSelect.
  181. //
  182. $currentSelect = $customSelect.prepend( '<a href="#" class="current">' + $selectedOption.html() + '</a>' ).find( ".current" );
  183. //
  184. // Add the custom select element after the <select> element.
  185. //
  186. $this.after( $customSelect )
  187. //
  188. //then hide the <select> element.
  189. //
  190. .hide();
  191. } else {
  192. //
  193. // Create our list item <li> elements.
  194. //
  195. liHtml = $options.map( function() { return "<li>" + $( this ).html() + "</li>"; } ).get().join( '' );
  196. //
  197. // Refresh the ul with options from the select in case the supplied markup doesn't match.
  198. // Clear what's currently in the <ul> element.
  199. //
  200. $customList.html( '' )
  201. //
  202. // Populate the list item <li> elements.
  203. //
  204. .append( liHtml );
  205. } // endif $customSelect.length === 0
  206. //
  207. // Determine whether or not the custom select element should be disabled.
  208. //
  209. $customSelect.toggleClass( 'disabled', $this.is( ':disabled' ) );
  210. //
  211. // Cache our List item elements.
  212. //
  213. $listItems = $customList.find( 'li' );
  214. //
  215. // Determine which elements to select in our custom list.
  216. //
  217. $options.each( function ( index ) {
  218. if ( this.selected ) {
  219. //
  220. // Add the selected class to the current li element
  221. //
  222. $listItems.eq( index ).addClass( 'selected' );
  223. //
  224. // Update the current element with the option value.
  225. //
  226. if ($currentSelect) {
  227. $currentSelect.html( $( this ).html() );
  228. }
  229. }
  230. });
  231. //
  232. // Update the custom <ul> list width property.
  233. //
  234. $customList.css( 'width', 'inherit' );
  235. //
  236. // Set the custom select width property.
  237. //
  238. $customSelect.css( 'width', 'inherit' );
  239. //
  240. // If we're not specifying a predetermined form size.
  241. //
  242. if ( !$customSelect.is( '.small, .medium, .large, .expand' ) ) {
  243. // ------------------------------------------------------------------------------------
  244. // This is a work-around for when elements are contained within hidden parents.
  245. // For example, when custom-form elements are inside of a hidden reveal modal.
  246. //
  247. // We need to display the current custom list element as well as hidden parent elements
  248. // in order to properly calculate the list item element's width property.
  249. // -------------------------------------------------------------------------------------
  250. //
  251. // Show the drop down.
  252. // This should ensure that the list item's width values are properly calculated.
  253. //
  254. $customSelect.addClass( 'open' );
  255. //
  256. // Quickly, display all parent elements.
  257. // This should help us calcualate the width of the list item's within the drop down.
  258. //
  259. hiddenFixObj.adjust( $customList );
  260. //
  261. // Grab the largest list item width.
  262. //
  263. maxWidth = ( $listItems.outerWidth() > maxWidth ) ? $listItems.outerWidth() : maxWidth;
  264. //
  265. // Okay, now reset the parent elements.
  266. // This will hide them again.
  267. //
  268. hiddenFixObj.reset();
  269. //
  270. // Finally, hide the drop down.
  271. //
  272. $customSelect.removeClass( 'open' );
  273. //
  274. // Set the custom list width.
  275. //
  276. $customSelect.width( maxWidth + 18);
  277. //
  278. // Set the custom list element (<ul />) width.
  279. //
  280. $customList.width( maxWidth + 16 );
  281. } // endif
  282. }
  283. $('form.custom input:radio[data-customforms!=disabled]').each(appendCustomMarkup);
  284. $('form.custom input:checkbox[data-customforms!=disabled]').each(appendCustomMarkup);
  285. $('form.custom select[data-customforms!=disabled]').each(appendCustomSelect);
  286. };
  287. var refreshCustomSelect = function($select) {
  288. var maxWidth = 0,
  289. $customSelect = $select.next();
  290. $options = $select.find('option');
  291. $customSelect.find('ul').html('');
  292. $options.each(function () {
  293. $li = $('<li>' + $(this).html() + '</li>');
  294. $customSelect.find('ul').append($li);
  295. });
  296. // re-populate
  297. $options.each(function (index) {
  298. if (this.selected) {
  299. $customSelect.find('li').eq(index).addClass('selected');
  300. $customSelect.find('.current').html($(this).html());
  301. }
  302. });
  303. // fix width
  304. $customSelect.removeAttr('style')
  305. .find('ul').removeAttr('style');
  306. $customSelect.find('li').each(function () {
  307. $customSelect.addClass('open');
  308. if ($(this).outerWidth() > maxWidth) {
  309. maxWidth = $(this).outerWidth();
  310. }
  311. $customSelect.removeClass('open');
  312. });
  313. $customSelect.css('width', maxWidth + 18 + 'px');
  314. $customSelect.find('ul').css('width', maxWidth + 16 + 'px');
  315. };
  316. var toggleCheckbox = function($element) {
  317. var $input = $element.prev(),
  318. input = $input[0];
  319. if (false === $input.is(':disabled')) {
  320. input.checked = ((input.checked) ? false : true);
  321. $element.toggleClass('checked');
  322. $input.trigger('change');
  323. }
  324. };
  325. var toggleRadio = function($element) {
  326. var $input = $element.prev(),
  327. input = $input[0];
  328. if (false === $input.is(':disabled')) {
  329. $('input:radio[name="' + $input.attr('name') + '"]').next().not($element).removeClass('checked');
  330. if ( !$element.hasClass('checked') ) {
  331. $element.toggleClass('checked');
  332. }
  333. input.checked = $element.hasClass('checked');
  334. $input.trigger('change');
  335. }
  336. };
  337. $(document).on('click', 'form.custom span.custom.checkbox', function (event) {
  338. event.preventDefault();
  339. event.stopPropagation();
  340. toggleCheckbox($(this));
  341. });
  342. $(document).on('click', 'form.custom span.custom.radio', function (event) {
  343. event.preventDefault();
  344. event.stopPropagation();
  345. toggleRadio($(this));
  346. });
  347. $(document).on('change', 'form.custom select[data-customforms!=disabled]', function (event) {
  348. refreshCustomSelect($(this));
  349. });
  350. $(document).on('click', 'form.custom label', function (event) {
  351. var $associatedElement = $('#' + $(this).attr('for') + '[data-customforms!=disabled]'),
  352. $customCheckbox,
  353. $customRadio;
  354. if ($associatedElement.length !== 0) {
  355. if ($associatedElement.attr('type') === 'checkbox') {
  356. event.preventDefault();
  357. $customCheckbox = $(this).find('span.custom.checkbox');
  358. toggleCheckbox($customCheckbox);
  359. } else if ($associatedElement.attr('type') === 'radio') {
  360. event.preventDefault();
  361. $customRadio = $(this).find('span.custom.radio');
  362. toggleRadio($customRadio);
  363. }
  364. }
  365. });
  366. $(document).on('click', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (event) {
  367. var $this = $(this),
  368. $dropdown = $this.closest('div.custom.dropdown'),
  369. $select = $dropdown.prev();
  370. event.preventDefault();
  371. $('div.dropdown').removeClass('open');
  372. if (false === $select.is(':disabled')) {
  373. $dropdown.toggleClass('open');
  374. if ($dropdown.hasClass('open')) {
  375. $(document).bind('click.customdropdown', function (event) {
  376. $dropdown.removeClass('open');
  377. $(document).unbind('.customdropdown');
  378. });
  379. } else {
  380. $(document).unbind('.customdropdown');
  381. }
  382. return false;
  383. }
  384. });
  385. $(document).on('click', 'form.custom div.custom.dropdown li', function (event) {
  386. var $this = $(this),
  387. $customDropdown = $this.closest('div.custom.dropdown'),
  388. $select = $customDropdown.prev(),
  389. selectedIndex = 0;
  390. event.preventDefault();
  391. event.stopPropagation();
  392. $('div.dropdown').removeClass('open');
  393. $this
  394. .closest('ul')
  395. .find('li')
  396. .removeClass('selected');
  397. $this.addClass('selected');
  398. $customDropdown
  399. .removeClass('open')
  400. .find('a.current')
  401. .html($this.html());
  402. $this.closest('ul').find('li').each(function (index) {
  403. if ($this[0] == this) {
  404. selectedIndex = index;
  405. }
  406. });
  407. $select[0].selectedIndex = selectedIndex;
  408. $select.trigger('change');
  409. });
  410. $.fn.foundationCustomForms = $.foundation.customForms.appendCustomMarkup;
  411. })( jQuery );