You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
13 KiB

10 years ago
  1. /*
  2. * # Semantic - Checkbox
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.fn.checkbox = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. returnedValue
  23. ;
  24. $allModules
  25. .each(function() {
  26. var
  27. settings = $.extend(true, {}, $.fn.checkbox.settings, parameters),
  28. className = settings.className,
  29. namespace = settings.namespace,
  30. selector = settings.selector,
  31. error = settings.error,
  32. eventNamespace = '.' + namespace,
  33. moduleNamespace = 'module-' + namespace,
  34. $module = $(this),
  35. $label = $(this).next(selector.label).first(),
  36. $input = $(this).find(selector.input),
  37. instance = $module.data(moduleNamespace),
  38. observer,
  39. element = this,
  40. module
  41. ;
  42. module = {
  43. initialize: function() {
  44. module.verbose('Initializing checkbox', settings);
  45. $module
  46. .on('click' + eventNamespace, module.toggle)
  47. .on('keydown' + eventNamespace, selector.input, module.event.keydown)
  48. ;
  49. if( module.is.checked() ) {
  50. module.set.checked();
  51. if(settings.fireOnInit) {
  52. $.proxy(settings.onChecked, $input.get())();
  53. }
  54. }
  55. else {
  56. module.remove.checked();
  57. if(settings.fireOnInit) {
  58. $.proxy(settings.onUnchecked, $input.get())();
  59. }
  60. }
  61. module.observeChanges();
  62. module.instantiate();
  63. },
  64. instantiate: function() {
  65. module.verbose('Storing instance of module', module);
  66. instance = module;
  67. $module
  68. .data(moduleNamespace, module)
  69. ;
  70. },
  71. destroy: function() {
  72. module.verbose('Destroying previous module');
  73. $module
  74. .off(eventNamespace)
  75. .removeData(moduleNamespace)
  76. ;
  77. $input
  78. .off(eventNamespace, module.event.keydown)
  79. ;
  80. $label
  81. .off(eventNamespace)
  82. ;
  83. },
  84. refresh: function() {
  85. $module = $(this);
  86. $label = $(this).next(selector.label).first();
  87. $input = $(this).find(selector.input);
  88. },
  89. observeChanges: function() {
  90. if(MutationObserver !== undefined) {
  91. observer = new MutationObserver(function(mutations) {
  92. module.debug('DOM tree modified, updating selector cache');
  93. module.refresh();
  94. });
  95. observer.observe(element, {
  96. childList : true,
  97. subtree : true
  98. });
  99. module.debug('Setting up mutation observer', observer);
  100. }
  101. },
  102. attachEvents: function(selector, event) {
  103. var
  104. $toggle = $(selector)
  105. ;
  106. event = $.isFunction(module[event])
  107. ? module[event]
  108. : module.toggle
  109. ;
  110. if($toggle.size() > 0) {
  111. module.debug('Attaching checkbox events to element', selector, event);
  112. $toggle
  113. .on('click' + eventNamespace, event)
  114. ;
  115. }
  116. else {
  117. module.error(error.notFound);
  118. }
  119. },
  120. event: {
  121. keydown: function(event) {
  122. var
  123. key = event.which,
  124. keyCode = {
  125. enter : 13,
  126. escape : 27
  127. }
  128. ;
  129. if( key == keyCode.escape) {
  130. module.verbose('Escape key pressed blurring field');
  131. $module
  132. .blur()
  133. ;
  134. }
  135. if(!event.ctrlKey && key == keyCode.enter) {
  136. module.verbose('Enter key pressed, toggling checkbox');
  137. $.proxy(module.toggle, this)();
  138. event.preventDefault();
  139. }
  140. }
  141. },
  142. is: {
  143. radio: function() {
  144. return $module.hasClass(className.radio);
  145. },
  146. checked: function() {
  147. return $input.prop('checked') !== undefined && $input.prop('checked');
  148. },
  149. unchecked: function() {
  150. return !module.is.checked();
  151. }
  152. },
  153. can: {
  154. change: function() {
  155. return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') );
  156. },
  157. uncheck: function() {
  158. return (typeof settings.uncheckable === 'boolean')
  159. ? settings.uncheckable
  160. : !module.is.radio()
  161. ;
  162. }
  163. },
  164. set: {
  165. checked: function() {
  166. $module.addClass(className.checked);
  167. }
  168. },
  169. remove: {
  170. checked: function() {
  171. $module.removeClass(className.checked);
  172. }
  173. },
  174. disable: function() {
  175. module.debug('Enabling checkbox functionality');
  176. $module.addClass(className.disabled);
  177. $input.removeProp('disabled');
  178. $.proxy(settings.onDisabled, $input.get())();
  179. },
  180. enable: function() {
  181. module.debug('Disabling checkbox functionality');
  182. $module.removeClass(className.disabled);
  183. $input.prop('disabled', 'disabled');
  184. $.proxy(settings.onEnabled, $input.get())();
  185. },
  186. check: function() {
  187. module.debug('Enabling checkbox', $input);
  188. $input
  189. .prop('checked', true)
  190. .trigger('change')
  191. ;
  192. module.set.checked();
  193. $.proxy(settings.onChange, $input.get())();
  194. $.proxy(settings.onChecked, $input.get())();
  195. },
  196. uncheck: function() {
  197. module.debug('Disabling checkbox');
  198. $input
  199. .prop('checked', false)
  200. .trigger('change')
  201. ;
  202. module.remove.checked();
  203. $.proxy(settings.onChange, $input.get())();
  204. $.proxy(settings.onUnchecked, $input.get())();
  205. },
  206. toggle: function(event) {
  207. if( !module.can.change() ) {
  208. console.log(module.can.change());
  209. module.debug('Checkbox is read-only or disabled, ignoring toggle');
  210. return;
  211. }
  212. module.verbose('Determining new checkbox state');
  213. if( module.is.unchecked() ) {
  214. module.check();
  215. }
  216. else if( module.is.checked() && module.can.uncheck() ) {
  217. module.uncheck();
  218. }
  219. },
  220. setting: function(name, value) {
  221. module.debug('Changing setting', name, value);
  222. if( $.isPlainObject(name) ) {
  223. $.extend(true, settings, name);
  224. }
  225. else if(value !== undefined) {
  226. settings[name] = value;
  227. }
  228. else {
  229. return settings[name];
  230. }
  231. },
  232. internal: function(name, value) {
  233. if( $.isPlainObject(name) ) {
  234. $.extend(true, module, name);
  235. }
  236. else if(value !== undefined) {
  237. module[name] = value;
  238. }
  239. else {
  240. return module[name];
  241. }
  242. },
  243. debug: function() {
  244. if(settings.debug) {
  245. if(settings.performance) {
  246. module.performance.log(arguments);
  247. }
  248. else {
  249. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  250. module.debug.apply(console, arguments);
  251. }
  252. }
  253. },
  254. verbose: function() {
  255. if(settings.verbose && settings.debug) {
  256. if(settings.performance) {
  257. module.performance.log(arguments);
  258. }
  259. else {
  260. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  261. module.verbose.apply(console, arguments);
  262. }
  263. }
  264. },
  265. error: function() {
  266. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  267. module.error.apply(console, arguments);
  268. },
  269. performance: {
  270. log: function(message) {
  271. var
  272. currentTime,
  273. executionTime,
  274. previousTime
  275. ;
  276. if(settings.performance) {
  277. currentTime = new Date().getTime();
  278. previousTime = time || currentTime;
  279. executionTime = currentTime - previousTime;
  280. time = currentTime;
  281. performance.push({
  282. 'Name' : message[0],
  283. 'Arguments' : [].slice.call(message, 1) || '',
  284. 'Element' : element,
  285. 'Execution Time' : executionTime
  286. });
  287. }
  288. clearTimeout(module.performance.timer);
  289. module.performance.timer = setTimeout(module.performance.display, 100);
  290. },
  291. display: function() {
  292. var
  293. title = settings.name + ':',
  294. totalTime = 0
  295. ;
  296. time = false;
  297. clearTimeout(module.performance.timer);
  298. $.each(performance, function(index, data) {
  299. totalTime += data['Execution Time'];
  300. });
  301. title += ' ' + totalTime + 'ms';
  302. if(moduleSelector) {
  303. title += ' \'' + moduleSelector + '\'';
  304. }
  305. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  306. console.groupCollapsed(title);
  307. if(console.table) {
  308. console.table(performance);
  309. }
  310. else {
  311. $.each(performance, function(index, data) {
  312. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  313. });
  314. }
  315. console.groupEnd();
  316. }
  317. performance = [];
  318. }
  319. },
  320. invoke: function(query, passedArguments, context) {
  321. var
  322. object = instance,
  323. maxDepth,
  324. found,
  325. response
  326. ;
  327. passedArguments = passedArguments || queryArguments;
  328. context = element || context;
  329. if(typeof query == 'string' && object !== undefined) {
  330. query = query.split(/[\. ]/);
  331. maxDepth = query.length - 1;
  332. $.each(query, function(depth, value) {
  333. var camelCaseValue = (depth != maxDepth)
  334. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  335. : query
  336. ;
  337. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  338. object = object[camelCaseValue];
  339. }
  340. else if( object[camelCaseValue] !== undefined ) {
  341. found = object[camelCaseValue];
  342. return false;
  343. }
  344. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  345. object = object[value];
  346. }
  347. else if( object[value] !== undefined ) {
  348. found = object[value];
  349. return false;
  350. }
  351. else {
  352. return false;
  353. }
  354. });
  355. }
  356. if ( $.isFunction( found ) ) {
  357. response = found.apply(context, passedArguments);
  358. }
  359. else if(found !== undefined) {
  360. response = found;
  361. }
  362. if($.isArray(returnedValue)) {
  363. returnedValue.push(response);
  364. }
  365. else if(returnedValue !== undefined) {
  366. returnedValue = [returnedValue, response];
  367. }
  368. else if(response !== undefined) {
  369. returnedValue = response;
  370. }
  371. return found;
  372. }
  373. };
  374. if(methodInvoked) {
  375. if(instance === undefined) {
  376. module.initialize();
  377. }
  378. module.invoke(query);
  379. }
  380. else {
  381. if(instance !== undefined) {
  382. module.destroy();
  383. }
  384. module.initialize();
  385. }
  386. })
  387. ;
  388. return (returnedValue !== undefined)
  389. ? returnedValue
  390. : this
  391. ;
  392. };
  393. $.fn.checkbox.settings = {
  394. name : 'Checkbox',
  395. namespace : 'checkbox',
  396. debug : false,
  397. verbose : true,
  398. performance : true,
  399. // delegated event context
  400. uncheckable : 'auto',
  401. fireOnInit : true,
  402. onChange : function(){},
  403. onChecked : function(){},
  404. onUnchecked : function(){},
  405. onEnabled : function(){},
  406. onDisabled : function(){},
  407. className : {
  408. checked : 'checked',
  409. disabled : 'disabled',
  410. radio : 'radio',
  411. readOnly : 'read-only'
  412. },
  413. error : {
  414. method : 'The method you called is not defined.'
  415. },
  416. selector : {
  417. input : 'input[type=checkbox], input[type=radio]',
  418. label : 'label'
  419. }
  420. };
  421. })( jQuery, window , document );