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.

507 lines
14 KiB

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