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.

469 lines
13 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
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' in window) {
  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. tab: function() {
  169. if( $input.attr('tabindex') === undefined) {
  170. $input
  171. .attr('tabindex', 0)
  172. ;
  173. }
  174. }
  175. },
  176. remove: {
  177. checked: function() {
  178. $module.removeClass(className.checked);
  179. }
  180. },
  181. enable: function() {
  182. module.debug('Enabling checkbox functionality');
  183. $module.addClass(className.disabled);
  184. $input.prop('disabled', false);
  185. $.proxy(settings.onDisabled, $input.get())();
  186. },
  187. disable: function() {
  188. module.debug('Disabling checkbox functionality');
  189. $module.removeClass(className.disabled);
  190. $input.prop('disabled', 'disabled');
  191. $.proxy(settings.onEnabled, $input.get())();
  192. },
  193. check: function() {
  194. module.debug('Enabling checkbox', $input);
  195. $input
  196. .prop('checked', true)
  197. .trigger('change')
  198. ;
  199. module.set.checked();
  200. $.proxy(settings.onChange, $input.get())();
  201. $.proxy(settings.onChecked, $input.get())();
  202. },
  203. uncheck: function() {
  204. module.debug('Disabling checkbox');
  205. $input
  206. .prop('checked', false)
  207. .trigger('change')
  208. ;
  209. module.remove.checked();
  210. $.proxy(settings.onChange, $input.get())();
  211. $.proxy(settings.onUnchecked, $input.get())();
  212. },
  213. toggle: function(event) {
  214. if( !module.can.change() ) {
  215. console.log(module.can.change());
  216. module.debug('Checkbox is read-only or disabled, ignoring toggle');
  217. return;
  218. }
  219. module.verbose('Determining new checkbox state');
  220. if( module.is.unchecked() ) {
  221. module.check();
  222. }
  223. else if( module.is.checked() && module.can.uncheck() ) {
  224. module.uncheck();
  225. }
  226. },
  227. setting: function(name, value) {
  228. module.debug('Changing setting', name, value);
  229. if( $.isPlainObject(name) ) {
  230. $.extend(true, settings, name);
  231. }
  232. else if(value !== undefined) {
  233. settings[name] = value;
  234. }
  235. else {
  236. return settings[name];
  237. }
  238. },
  239. internal: function(name, value) {
  240. if( $.isPlainObject(name) ) {
  241. $.extend(true, module, name);
  242. }
  243. else if(value !== undefined) {
  244. module[name] = value;
  245. }
  246. else {
  247. return module[name];
  248. }
  249. },
  250. debug: function() {
  251. if(settings.debug) {
  252. if(settings.performance) {
  253. module.performance.log(arguments);
  254. }
  255. else {
  256. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  257. module.debug.apply(console, arguments);
  258. }
  259. }
  260. },
  261. verbose: function() {
  262. if(settings.verbose && settings.debug) {
  263. if(settings.performance) {
  264. module.performance.log(arguments);
  265. }
  266. else {
  267. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  268. module.verbose.apply(console, arguments);
  269. }
  270. }
  271. },
  272. error: function() {
  273. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  274. module.error.apply(console, arguments);
  275. },
  276. performance: {
  277. log: function(message) {
  278. var
  279. currentTime,
  280. executionTime,
  281. previousTime
  282. ;
  283. if(settings.performance) {
  284. currentTime = new Date().getTime();
  285. previousTime = time || currentTime;
  286. executionTime = currentTime - previousTime;
  287. time = currentTime;
  288. performance.push({
  289. 'Name' : message[0],
  290. 'Arguments' : [].slice.call(message, 1) || '',
  291. 'Element' : element,
  292. 'Execution Time' : executionTime
  293. });
  294. }
  295. clearTimeout(module.performance.timer);
  296. module.performance.timer = setTimeout(module.performance.display, 100);
  297. },
  298. display: function() {
  299. var
  300. title = settings.name + ':',
  301. totalTime = 0
  302. ;
  303. time = false;
  304. clearTimeout(module.performance.timer);
  305. $.each(performance, function(index, data) {
  306. totalTime += data['Execution Time'];
  307. });
  308. title += ' ' + totalTime + 'ms';
  309. if(moduleSelector) {
  310. title += ' \'' + moduleSelector + '\'';
  311. }
  312. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  313. console.groupCollapsed(title);
  314. if(console.table) {
  315. console.table(performance);
  316. }
  317. else {
  318. $.each(performance, function(index, data) {
  319. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  320. });
  321. }
  322. console.groupEnd();
  323. }
  324. performance = [];
  325. }
  326. },
  327. invoke: function(query, passedArguments, context) {
  328. var
  329. object = instance,
  330. maxDepth,
  331. found,
  332. response
  333. ;
  334. passedArguments = passedArguments || queryArguments;
  335. context = element || context;
  336. if(typeof query == 'string' && object !== undefined) {
  337. query = query.split(/[\. ]/);
  338. maxDepth = query.length - 1;
  339. $.each(query, function(depth, value) {
  340. var camelCaseValue = (depth != maxDepth)
  341. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  342. : query
  343. ;
  344. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  345. object = object[camelCaseValue];
  346. }
  347. else if( object[camelCaseValue] !== undefined ) {
  348. found = object[camelCaseValue];
  349. return false;
  350. }
  351. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  352. object = object[value];
  353. }
  354. else if( object[value] !== undefined ) {
  355. found = object[value];
  356. return false;
  357. }
  358. else {
  359. return false;
  360. }
  361. });
  362. }
  363. if ( $.isFunction( found ) ) {
  364. response = found.apply(context, passedArguments);
  365. }
  366. else if(found !== undefined) {
  367. response = found;
  368. }
  369. if($.isArray(returnedValue)) {
  370. returnedValue.push(response);
  371. }
  372. else if(returnedValue !== undefined) {
  373. returnedValue = [returnedValue, response];
  374. }
  375. else if(response !== undefined) {
  376. returnedValue = response;
  377. }
  378. return found;
  379. }
  380. };
  381. if(methodInvoked) {
  382. if(instance === undefined) {
  383. module.initialize();
  384. }
  385. module.invoke(query);
  386. }
  387. else {
  388. if(instance !== undefined) {
  389. module.destroy();
  390. }
  391. module.initialize();
  392. }
  393. })
  394. ;
  395. return (returnedValue !== undefined)
  396. ? returnedValue
  397. : this
  398. ;
  399. };
  400. $.fn.checkbox.settings = {
  401. name : 'Checkbox',
  402. namespace : 'checkbox',
  403. debug : false,
  404. verbose : true,
  405. performance : true,
  406. // delegated event context
  407. uncheckable : 'auto',
  408. fireOnInit : true,
  409. onChange : function(){},
  410. onChecked : function(){},
  411. onUnchecked : function(){},
  412. onEnabled : function(){},
  413. onDisabled : function(){},
  414. className : {
  415. checked : 'checked',
  416. disabled : 'disabled',
  417. radio : 'radio',
  418. readOnly : 'read-only'
  419. },
  420. error : {
  421. method : 'The method you called is not defined.'
  422. },
  423. selector : {
  424. input : 'input[type=checkbox], input[type=radio]',
  425. label : 'label'
  426. }
  427. };
  428. })( jQuery, window , document );