jquery.icheck.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*!
  2. * iCheck v0.9.1, http://git.io/uhUPMA
  3. * =================================
  4. * Powerful jQuery plugin for checkboxes and radio buttons customization
  5. *
  6. * (c) 2013 Damir Foy, http://damirfoy.com
  7. * MIT Licensed
  8. */
  9. (function($) {
  10. // Cached vars
  11. var _iCheck = 'iCheck',
  12. _iCheckHelper = _iCheck + '-helper',
  13. _checkbox = 'checkbox',
  14. _radio = 'radio',
  15. _checked = 'checked',
  16. _unchecked = 'un' + _checked,
  17. _disabled = 'disabled',
  18. _determinate = 'determinate',
  19. _indeterminate = 'in' + _determinate,
  20. _update = 'update',
  21. _type = 'type',
  22. _click = 'click',
  23. _touch = 'touchbegin.i touchend.i',
  24. _add = 'addClass',
  25. _remove = 'removeClass',
  26. _callback = 'trigger',
  27. _label = 'label',
  28. _cursor = 'cursor',
  29. _mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);
  30. // Plugin init
  31. $.fn[_iCheck] = function(options, fire) {
  32. // Walker
  33. var handle = ':' + _checkbox + ', :' + _radio,
  34. stack = $(),
  35. walker = function(object) {
  36. object.each(function() {
  37. var self = $(this);
  38. if (self.is(handle)) {
  39. stack = stack.add(self);
  40. } else {
  41. stack = stack.add(self.find(handle));
  42. };
  43. });
  44. };
  45. // Check if we should operate with some method
  46. if (/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(options)) {
  47. // Normalize method's name
  48. options = options.toLowerCase();
  49. // Find checkboxes and radio buttons
  50. walker(this);
  51. return stack.each(function() {
  52. if (options == 'destroy') {
  53. tidy(this, 'ifDestroyed');
  54. } else {
  55. operate($(this), true, options);
  56. };
  57. // Fire method's callback
  58. if ($.isFunction(fire)) {
  59. fire();
  60. };
  61. });
  62. // Customization
  63. } else if (typeof options == 'object' || !options) {
  64. // Check if any options were passed
  65. var settings = $.extend({
  66. checkedClass: _checked,
  67. disabledClass: _disabled,
  68. indeterminateClass: _indeterminate,
  69. labelHover: true
  70. }, options),
  71. selector = settings.handle,
  72. hoverClass = settings.hoverClass || 'hover',
  73. focusClass = settings.focusClass || 'focus',
  74. activeClass = settings.activeClass || 'active',
  75. labelHover = !!settings.labelHover,
  76. labelHoverClass = settings.labelHoverClass || 'hover',
  77. // Setup clickable area
  78. area = ('' + settings.increaseArea).replace('%', '') | 0;
  79. // Selector limit
  80. if (selector == _checkbox || selector == _radio) {
  81. handle = ':' + selector;
  82. };
  83. // Clickable area limit
  84. if (area < -50) {
  85. area = -50;
  86. };
  87. // Walk around the selector
  88. walker(this);
  89. return stack.each(function() {
  90. // If already customized
  91. tidy(this);
  92. var self = $(this),
  93. node = this,
  94. id = node.id,
  95. // Layer styles
  96. offset = -area + '%',
  97. size = 100 + (area * 2) + '%',
  98. layer = {
  99. position: 'absolute',
  100. top: offset,
  101. left: offset,
  102. display: 'block',
  103. width: size,
  104. height: size,
  105. margin: 0,
  106. padding: 0,
  107. background: '#fff',
  108. border: 0,
  109. opacity: 0
  110. },
  111. // Choose how to hide input
  112. hide = _mobile ? {
  113. position: 'absolute',
  114. visibility: 'hidden'
  115. } : area ? layer : {
  116. position: 'absolute',
  117. opacity: 0
  118. },
  119. // Get proper class
  120. className = node[_type] == _checkbox ? settings.checkboxClass || 'i' + _checkbox : settings.radioClass || 'i' + _radio,
  121. // Find assigned labels
  122. label = $(_label + '[for="' + id + '"]').add(self.closest(_label)),
  123. // Wrap input
  124. parent = self.wrap('<div class="' + className + '"/>')[_callback]('ifCreated').parent().append(settings.insert),
  125. // Layer addition
  126. helper = $('<ins class="' + _iCheckHelper + '"/>').css(layer).appendTo(parent);
  127. // Finalize customization
  128. self.data(_iCheck, {o: settings, s: self.attr('style')}).css(hide);
  129. !!settings.inheritClass && parent[_add](node.className);
  130. !!settings.inheritID && id && parent.attr('id', _iCheck + '-' + id);
  131. parent.css('position') == 'static' && parent.css('position', 'relative');
  132. operate(self, true, _update);
  133. // Label events
  134. if (label.length) {
  135. label.on(_click + '.i mouseenter.i mouseleave.i ' + _touch, function(event) {
  136. var type = event[_type],
  137. item = $(this);
  138. // Do nothing if input is disabled
  139. if (!node[_disabled]) {
  140. // Click
  141. if (type == _click) {
  142. operate(self, false, true);
  143. // Hover state
  144. } else if (labelHover) {
  145. // mouseleave|touchend
  146. if (/ve|nd/.test(type)) {
  147. parent[_remove](hoverClass);
  148. item[_remove](labelHoverClass);
  149. } else {
  150. parent[_add](hoverClass);
  151. item[_add](labelHoverClass);
  152. };
  153. };
  154. if (_mobile) {
  155. event.stopPropagation();
  156. } else {
  157. return false;
  158. };
  159. };
  160. });
  161. };
  162. // Input events
  163. self.on(_click + '.i focus.i blur.i keyup.i keydown.i keypress.i', function(event) {
  164. var type = event[_type],
  165. key = event.keyCode;
  166. // Click
  167. if (type == _click) {
  168. return false;
  169. // Keydown
  170. } else if (type == 'keydown' && key == 32) {
  171. if (!(node[_type] == _radio && node[_checked])) {
  172. if (node[_checked]) {
  173. off(self, _checked);
  174. } else {
  175. on(self, _checked);
  176. };
  177. };
  178. return false;
  179. // Keyup
  180. } else if (type == 'keyup' && node[_type] == _radio) {
  181. !node[_checked] && on(self, _checked);
  182. // Focus/blur
  183. } else if (/us|ur/.test(type)) {
  184. parent[type == 'blur' ? _remove : _add](focusClass);
  185. };
  186. });
  187. // Helper events
  188. helper.on(_click + ' mousedown mouseup mouseover mouseout ' + _touch, function(event) {
  189. var type = event[_type],
  190. // mousedown|mouseup
  191. toggle = /wn|up/.test(type) ? activeClass : hoverClass;
  192. // Do nothing if input is disabled
  193. if (!node[_disabled]) {
  194. // Click
  195. if (type == _click) {
  196. operate(self, false, true);
  197. // Active and hover states
  198. } else {
  199. // State is on
  200. if (/wn|er|in/.test(type)) {
  201. // mousedown|mouseover|touchbegin
  202. parent[_add](toggle);
  203. // State is off
  204. } else {
  205. parent[_remove](toggle + ' ' + activeClass);
  206. };
  207. // Label hover
  208. if (label.length && labelHover && toggle == hoverClass) {
  209. // mouseout|touchend
  210. label[/ut|nd/.test(type) ? _remove : _add](labelHoverClass);
  211. };
  212. };
  213. if (_mobile) {
  214. event.stopPropagation();
  215. } else {
  216. return false;
  217. };
  218. };
  219. });
  220. });
  221. } else {
  222. return this;
  223. };
  224. };
  225. // Do something with inputs
  226. function operate(input, direct, method) {
  227. var node = input[0];
  228. state = /er/.test(method) ? _indeterminate : /bl/.test(method) ? _disabled : _checked,
  229. active = method == _update ? {
  230. checked: node[_checked],
  231. disabled: node[_disabled],
  232. indeterminate: input.attr(_indeterminate) == 'true' || input.attr(_determinate) == 'false'
  233. } : node[state];
  234. // Check, disable or indeterminate
  235. if (/^(ch|di|in)/.test(method) && !active) {
  236. on(input, state);
  237. // Uncheck, enable or determinate
  238. } else if (/^(un|en|de)/.test(method) && active) {
  239. off(input, state);
  240. // Update
  241. } else if (method == _update) {
  242. // Handle states
  243. for (var state in active) {
  244. if (active[state]) {
  245. on(input, state, true);
  246. } else {
  247. off(input, state, true);
  248. };
  249. };
  250. } else if (!direct || method == 'toggle') {
  251. // Helper or label was clicked
  252. if (!direct) {
  253. input[_callback]('ifClicked');
  254. };
  255. // Toggle checked state
  256. if (active) {
  257. if (node[_type] !== _radio) {
  258. off(input, state);
  259. };
  260. } else {
  261. on(input, state);
  262. };
  263. };
  264. };
  265. // Add checked, disabled or indeterminate state
  266. function on(input, state, keep) {
  267. var node = input[0],
  268. parent = input.parent(),
  269. checked = state == _checked,
  270. indeterminate = state == _indeterminate,
  271. callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
  272. regular = option(node, callback + capitalize(node[_type])),
  273. specific = option(node, state + capitalize(node[_type]));
  274. // Prevent unnecessary actions
  275. if (node[state] !== true) {
  276. // Toggle assigned radio buttons
  277. if (!keep && state == _checked && node[_type] == _radio && node.name) {
  278. var form = input.closest('form'),
  279. inputs = 'input[name="' + node.name + '"]';
  280. inputs = form.length ? form.find(inputs) : $(inputs);
  281. inputs.each(function() {
  282. if (this !== node && $.data(this, _iCheck)) {
  283. off($(this), state);
  284. };
  285. });
  286. };
  287. // Indeterminate state
  288. if (indeterminate) {
  289. // Add indeterminate state
  290. node[state] = true;
  291. // Remove checked state
  292. if (node[_checked]) {
  293. off(input, _checked, 'force');
  294. };
  295. // Checked or disabled state
  296. } else {
  297. // Add checked or disabled state
  298. if (!keep) {
  299. node[state] = true;
  300. };
  301. // Remove indeterminate state
  302. if (checked && node[_indeterminate]) {
  303. off(input, _indeterminate, false);
  304. };
  305. };
  306. // Trigger callbacks
  307. callbacks(input, checked, state, keep);
  308. };
  309. // Add proper cursor
  310. if (node[_disabled] && !!option(node, _cursor, true)) {
  311. parent.find('.' + _iCheckHelper).css(_cursor, 'default');
  312. };
  313. // Add state class
  314. parent[_add](specific || option(node, state));
  315. // Remove regular state class
  316. parent[_remove](regular || option(node, callback) || '');
  317. };
  318. // Remove checked, disabled or indeterminate state
  319. function off(input, state, keep) {
  320. var node = input[0],
  321. parent = input.parent(),
  322. checked = state == _checked,
  323. indeterminate = state == _indeterminate,
  324. callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
  325. regular = option(node, callback + capitalize(node[_type])),
  326. specific = option(node, state + capitalize(node[_type]));
  327. // Prevent unnecessary actions
  328. if (node[state] !== false) {
  329. // Toggle state
  330. if (indeterminate || !keep || keep == 'force') {
  331. node[state] = false;
  332. };
  333. // Trigger callbacks
  334. callbacks(input, checked, callback, keep);
  335. };
  336. // Add proper cursor
  337. if (!node[_disabled] && !!option(node, _cursor, true)) {
  338. parent.find('.' + _iCheckHelper).css(_cursor, 'pointer');
  339. };
  340. // Remove state class
  341. parent[_remove](specific || option(node, state) || '');
  342. // Add regular state class
  343. parent[_add](regular || option(node, callback));
  344. };
  345. // Remove all traces
  346. function tidy(node, callback) {
  347. if ($.data(node, _iCheck)) {
  348. var input = $(node);
  349. // Remove everything except input
  350. input.parent().html(input.attr('style', $.data(node, _iCheck).s || '')[_callback](callback || ''));
  351. // Unbind events
  352. input.off('.i').unwrap();
  353. $(_label + '[for="' + node.id + '"]').add(input.closest(_label)).off('.i');
  354. };
  355. };
  356. // Get some option
  357. function option(node, state, regular) {
  358. if ($.data(node, _iCheck)) {
  359. return $.data(node, _iCheck).o[state + (regular ? '' : 'Class')];
  360. };
  361. };
  362. // Capitalize some string
  363. function capitalize(string) {
  364. return string.charAt(0).toUpperCase() + string.slice(1);
  365. };
  366. // Executable handlers
  367. function callbacks(input, checked, callback, keep) {
  368. if (!keep) {
  369. if (checked) {
  370. input[_callback]('ifToggled');
  371. };
  372. input[_callback]('ifChanged')[_callback]('if' + capitalize(callback));
  373. };
  374. };
  375. })(jQuery);