checkbox.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Fuel UX Checkbox
  3. * https://github.com/ExactTarget/fuelux
  4. *
  5. * Copyright (c) 2014 ExactTarget
  6. * Licensed under the BSD New license.
  7. */
  8. // -- BEGIN UMD WRAPPER PREFACE --
  9. // For more information on UMD visit:
  10. // https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
  11. (function (factory) {
  12. if (typeof define === 'function' && define.amd) {
  13. // if AMD loader is available, register as an anonymous module.
  14. define(['jquery'], factory);
  15. } else {
  16. // OR use browser globals if AMD is not present
  17. factory(jQuery);
  18. }
  19. }(function ($) {
  20. // -- END UMD WRAPPER PREFACE --
  21. // -- BEGIN MODULE CODE HERE --
  22. var old = $.fn.checkbox;
  23. // CHECKBOX CONSTRUCTOR AND PROTOTYPE
  24. var Checkbox = function (element, options) {
  25. this.options = $.extend({}, $.fn.checkbox.defaults, options);
  26. // cache elements
  27. this.$element = $(element).is('input[type="checkbox"]') ? $(element) : $(element).find('input[type="checkbox"]:first');
  28. this.$label = this.$element.parent();
  29. this.$parent = this.$label.parent('.checkbox');
  30. this.$toggleContainer = this.$element.attr('data-toggle');
  31. this.state = {
  32. disabled: false,
  33. checked: false
  34. };
  35. if (this.$parent.length === 0) {
  36. this.$parent = null;
  37. }
  38. if (Boolean(this.$toggleContainer)) {
  39. this.$toggleContainer = $(this.$toggleContainer);
  40. } else {
  41. this.$toggleContainer = null;
  42. }
  43. // handle events
  44. this.$element.on('change.fu.checkbox', $.proxy(this.itemchecked, this));
  45. this.$label.unbind('click', $.proxy(this.toggle, this));//unbind previous binds so that double clickage doesn't happen (thus making checkbox appear to not work)
  46. this.$label.on('click', $.proxy(this.toggle, this));//make repeated label clicks work
  47. // set default state
  48. this.setState();
  49. };
  50. Checkbox.prototype = {
  51. constructor: Checkbox,
  52. setState: function ($chk) {
  53. $chk = $chk || this.$element;
  54. this.state.disabled = Boolean($chk.prop('disabled'));
  55. this.state.checked = Boolean($chk.is(':checked'));
  56. this._resetClasses();
  57. // set state of checkbox
  58. this._toggleCheckedState();
  59. this._toggleDisabledState();
  60. //toggle container
  61. this.toggleContainer();
  62. },
  63. enable: function () {
  64. this.state.disabled = false;
  65. this.$element.removeAttr('disabled');
  66. this.$element.prop('disabled', false);
  67. this._resetClasses();
  68. this.$element.trigger('enabled.fu.checkbox');
  69. },
  70. disable: function () {
  71. this.state.disabled = true;
  72. this.$element.prop('disabled', true);
  73. this.$element.attr('disabled', 'disabled');
  74. this._setDisabledClass();
  75. this.$element.trigger('disabled.fu.checkbox');
  76. },
  77. check: function () {
  78. this.state.checked = true;
  79. this.$element.prop('checked', true);
  80. this.$element.attr('checked', 'checked');
  81. this._setCheckedClass();
  82. this.$element.trigger('checked.fu.checkbox');
  83. },
  84. uncheck: function () {
  85. this.state.checked = false;
  86. this.$element.prop('checked', false);
  87. this.$element.removeAttr('checked');
  88. this._resetClasses();
  89. this.$element.trigger('unchecked.fu.checkbox');
  90. },
  91. isChecked: function () {
  92. return this.state.checked;
  93. },
  94. toggle: function (e) {
  95. //keep checkbox from being used if it is disabled. You can't rely on this.state.disabled, because on bind time it might not be disabled, but, state.disabled may be set to true after bind time (and this.state.disabled won't be updated for this bound instance)
  96. //To see how this works, uncomment the next line of code and go to http://0.0.0.0:8000/index.html click the "disable #myCustomCheckbox1" and then click on the first checkbox and see the disparity in the output between this.state and this.$element.attr
  97. //console.log('is disabled? this.state says, "' + this.state.disabled + '"; this.$element.attr says, "' + this.$element.attr('disabled') + '"');
  98. if (/* do not change this to this.state.disabled. It will break edge cases */ this.$element.prop('disabled')) {
  99. return;
  100. }
  101. //keep event from firing twice in Chrome
  102. if (!e || (e.target === e.originalEvent.target)) {
  103. this.state.checked = !this.state.checked;
  104. this._toggleCheckedState();
  105. if (Boolean(e)) {
  106. //stop bubbling, otherwise event fires twice in Firefox.
  107. e.preventDefault();
  108. //make change event still fire (prevented by preventDefault to avoid firefox bug, see preceeding line)
  109. this.$element.trigger('change', e);
  110. }
  111. }
  112. },
  113. toggleContainer: function () {
  114. if (Boolean(this.$toggleContainer)) {
  115. if (this.state.checked) {
  116. this.$toggleContainer.removeClass('hide hidden');
  117. this.$toggleContainer.attr('aria-hidden', 'false');
  118. } else {
  119. this.$toggleContainer.addClass('hidden');
  120. this.$toggleContainer.attr('aria-hidden', 'true');
  121. }
  122. }
  123. },
  124. itemchecked: function (element) {
  125. this.setState($(element.target));
  126. },
  127. destroy: function () {
  128. this.$parent.remove();
  129. // remove any external bindings
  130. // [none]
  131. // empty elements to return to original markup
  132. // [none]
  133. return this.$parent[0].outerHTML;
  134. },
  135. _resetClasses: function () {
  136. var classesToRemove = [];
  137. if (!this.state.checked) {
  138. classesToRemove.push('checked');
  139. }
  140. if (!this.state.disabled) {
  141. classesToRemove.push('disabled');
  142. }
  143. classesToRemove = classesToRemove.join(' ');
  144. this.$label.removeClass(classesToRemove);
  145. if (this.$parent) {
  146. this.$parent.removeClass(classesToRemove);
  147. }
  148. },
  149. _toggleCheckedState: function () {
  150. if (this.state.checked) {
  151. this.check();
  152. } else {
  153. this.uncheck();
  154. }
  155. },
  156. _toggleDisabledState: function () {
  157. if (this.state.disabled) {
  158. this.disable();
  159. } else {
  160. this.enable();
  161. }
  162. },
  163. _setCheckedClass: function () {
  164. this.$label.addClass('checked');
  165. if (this.$parent) {
  166. this.$parent.addClass('checked');
  167. }
  168. },
  169. _setDisabledClass: function () {
  170. this.$label.addClass('disabled');
  171. if (this.$parent) {
  172. this.$parent.addClass('disabled');
  173. }
  174. }
  175. };
  176. // CHECKBOX PLUGIN DEFINITION
  177. $.fn.checkbox = function (option) {
  178. var args = Array.prototype.slice.call(arguments, 1);
  179. var methodReturn;
  180. var $set = this.each(function () {
  181. var $this = $(this);
  182. var data = $this.data('fu.checkbox');
  183. var options = typeof option === 'object' && option;
  184. if (!data) {
  185. $this.data('fu.checkbox', (data = new Checkbox(this, options)));
  186. }
  187. if (typeof option === 'string') {
  188. methodReturn = data[option].apply(data, args);
  189. }
  190. });
  191. return (methodReturn === undefined) ? $set : methodReturn;
  192. };
  193. $.fn.checkbox.defaults = {};
  194. $.fn.checkbox.Constructor = Checkbox;
  195. $.fn.checkbox.noConflict = function () {
  196. $.fn.checkbox = old;
  197. return this;
  198. };
  199. // DATA-API
  200. $(document).on('mouseover.fu.checkbox.data-api', '[data-initialize=checkbox]', function (e) {
  201. var $control = $(e.target).closest('.checkbox').find('[type=checkbox]');
  202. if (!$control.data('fu.checkbox')) {
  203. $control.checkbox($control.data());
  204. }
  205. });
  206. // Must be domReady for AMD compatibility
  207. $(function () {
  208. $('[data-initialize=checkbox] [type=checkbox]').each(function () {
  209. var $this = $(this);
  210. if (!$this.data('fu.checkbox')) {
  211. $this.checkbox($this.data());
  212. }
  213. });
  214. });
  215. // -- BEGIN UMD WRAPPER AFTERWORD --
  216. }));
  217. // -- END UMD WRAPPER AFTERWORD --