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.

558 lines
17 KiB

10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
  1. /*
  2. * # Semantic - Accordion
  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.accordion = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. time = new Date().getTime(),
  17. performance = [],
  18. query = arguments[0],
  19. methodInvoked = (typeof query == 'string'),
  20. queryArguments = [].slice.call(arguments, 1),
  21. requestAnimationFrame = window.requestAnimationFrame
  22. || window.mozRequestAnimationFrame
  23. || window.webkitRequestAnimationFrame
  24. || window.msRequestAnimationFrame
  25. || function(callback) { setTimeout(callback, 0); },
  26. returnedValue
  27. ;
  28. $allModules
  29. .each(function() {
  30. var
  31. settings = ( $.isPlainObject(parameters) )
  32. ? $.extend(true, {}, $.fn.accordion.settings, parameters)
  33. : $.extend({}, $.fn.accordion.settings),
  34. className = settings.className,
  35. namespace = settings.namespace,
  36. selector = settings.selector,
  37. error = settings.error,
  38. eventNamespace = '.' + namespace,
  39. moduleNamespace = 'module-' + namespace,
  40. moduleSelector = $allModules.selector || '',
  41. $module = $(this),
  42. $title = $module.find(selector.title),
  43. $content = $module.find(selector.content),
  44. element = this,
  45. instance = $module.data(moduleNamespace),
  46. observer,
  47. module
  48. ;
  49. module = {
  50. initialize: function() {
  51. module.debug('Initializing accordion with bound events', $module);
  52. $module
  53. .on('click' + eventNamespace, selector.title, module.event.click)
  54. ;
  55. module.observeChanges();
  56. module.instantiate();
  57. },
  58. instantiate: function() {
  59. instance = module;
  60. $module
  61. .data(moduleNamespace, module)
  62. ;
  63. },
  64. destroy: function() {
  65. module.debug('Destroying previous accordion for', $module);
  66. $module
  67. .removeData(moduleNamespace)
  68. ;
  69. $title
  70. .off(eventNamespace)
  71. ;
  72. },
  73. refresh: function() {
  74. $title = $module.find(selector.title);
  75. $content = $module.find(selector.content);
  76. },
  77. observeChanges: function() {
  78. if('MutationObserver' in window) {
  79. observer = new MutationObserver(function(mutations) {
  80. module.debug('DOM tree modified, updating selector cache');
  81. module.refresh();
  82. });
  83. observer.observe(element, {
  84. childList : true,
  85. subtree : true
  86. });
  87. module.debug('Setting up mutation observer', observer);
  88. }
  89. },
  90. event: {
  91. click: function() {
  92. module.toggle.call(this);
  93. }
  94. },
  95. toggle: function(query) {
  96. var
  97. $activeTitle = (query !== undefined)
  98. ? (typeof query === 'number')
  99. ? $title.eq(query)
  100. : $(query)
  101. : $(this),
  102. $activeContent = $activeTitle.next($content),
  103. contentIsOpen = $activeContent.is(':visible')
  104. ;
  105. module.debug('Toggling visibility of content', $activeTitle);
  106. if(contentIsOpen) {
  107. if(settings.collapsible) {
  108. module.close.call($activeTitle);
  109. }
  110. else {
  111. module.debug('Cannot close accordion content collapsing is disabled');
  112. }
  113. }
  114. else {
  115. module.open.call($activeTitle);
  116. }
  117. },
  118. open: function(query) {
  119. var
  120. $activeTitle = (query !== undefined)
  121. ? (typeof query === 'number')
  122. ? $title.eq(query)
  123. : $(query)
  124. : $(this),
  125. $activeContent = $activeTitle.next($content),
  126. currentlyAnimating = $activeContent.is(':animated'),
  127. currentlyActive = $activeContent.hasClass(className.active)
  128. ;
  129. if(!currentlyAnimating && !currentlyActive) {
  130. module.debug('Opening accordion content', $activeTitle);
  131. if(settings.exclusive) {
  132. module.closeOthers.call($activeTitle);
  133. }
  134. $activeTitle
  135. .addClass(className.active)
  136. ;
  137. if(settings.animateChildren) {
  138. if($.fn.transition !== undefined && $module.transition('is supported')) {
  139. $activeContent
  140. .children()
  141. .transition({
  142. animation : 'fade in',
  143. useFailSafe : true,
  144. debug : settings.debug,
  145. verbose : settings.verbose,
  146. duration : settings.duration
  147. })
  148. ;
  149. }
  150. else {
  151. $activeContent
  152. .children()
  153. .stop()
  154. .animate({
  155. opacity: 1
  156. }, settings.duration, module.resetOpacity)
  157. ;
  158. }
  159. }
  160. $activeContent
  161. .stop()
  162. .slideDown(settings.duration, settings.easing, function() {
  163. $activeContent
  164. .addClass(className.active)
  165. ;
  166. module.reset.display.call(this);
  167. settings.onOpen.call(this);
  168. settings.onChange.call(this);
  169. })
  170. ;
  171. }
  172. },
  173. close: function(query) {
  174. var
  175. $activeTitle = (query !== undefined)
  176. ? (typeof query === 'number')
  177. ? $title.eq(query)
  178. : $(query)
  179. : $(this),
  180. $activeContent = $activeTitle.next($content),
  181. isActive = $activeContent.hasClass(className.active)
  182. ;
  183. if(isActive) {
  184. module.debug('Closing accordion content', $activeContent);
  185. $activeTitle
  186. .removeClass(className.active)
  187. ;
  188. $activeContent
  189. .removeClass(className.active)
  190. .show()
  191. ;
  192. if(settings.animateChildren) {
  193. if($.fn.transition !== undefined && $module.transition('is supported')) {
  194. $activeContent
  195. .children()
  196. .transition({
  197. animation : 'fade out',
  198. useFailSafe : true,
  199. debug : settings.debug,
  200. verbose : settings.verbose,
  201. duration : settings.duration
  202. })
  203. ;
  204. }
  205. else {
  206. $activeContent
  207. .children()
  208. .stop()
  209. .animate({
  210. opacity: 0
  211. }, settings.duration, module.resetOpacity)
  212. ;
  213. }
  214. }
  215. $activeContent
  216. .stop()
  217. .slideUp(settings.duration, settings.easing, function() {
  218. module.reset.display.call(this);
  219. settings.onClose.call(this);
  220. settings.onChange.call(this);
  221. })
  222. ;
  223. }
  224. },
  225. closeOthers: function(index) {
  226. var
  227. $activeTitle = (index !== undefined)
  228. ? $title.eq(index)
  229. : $(this),
  230. $parentTitles = $activeTitle.parents(selector.content).prev(selector.title),
  231. $activeAccordion = $activeTitle.closest(selector.accordion),
  232. activeSelector = selector.title + '.' + className.active + ':visible',
  233. activeContent = selector.content + '.' + className.active + ':visible',
  234. $openTitles,
  235. $nestedTitles,
  236. $openContents
  237. ;
  238. if(settings.closeNested) {
  239. $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
  240. $openContents = $openTitles.next($content);
  241. }
  242. else {
  243. $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
  244. $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
  245. $openTitles = $openTitles.not($nestedTitles);
  246. $openContents = $openTitles.next($content);
  247. }
  248. if( ($openTitles.length > 0) ) {
  249. module.debug('Exclusive enabled, closing other content', $openTitles);
  250. $openTitles
  251. .removeClass(className.active)
  252. ;
  253. if(settings.animateChildren) {
  254. if($.fn.transition !== undefined && $module.transition('is supported')) {
  255. $openContents
  256. .children()
  257. .transition({
  258. animation : 'fade out',
  259. useFailSafe : true,
  260. debug : settings.debug,
  261. verbose : settings.verbose,
  262. duration : settings.duration
  263. })
  264. ;
  265. }
  266. else {
  267. $openContents
  268. .children()
  269. .stop()
  270. .animate({
  271. opacity: 0
  272. }, settings.duration, module.resetOpacity)
  273. ;
  274. }
  275. }
  276. $openContents
  277. .stop()
  278. .slideUp(settings.duration , settings.easing, function() {
  279. $(this).removeClass(className.active);
  280. module.reset.display.call(this);
  281. })
  282. ;
  283. }
  284. },
  285. reset: {
  286. display: function() {
  287. module.verbose('Removing inline display from element', this);
  288. $(this).css('display', '');
  289. if( $(this).attr('style') === '') {
  290. $(this)
  291. .attr('style', '')
  292. .removeAttr('style')
  293. ;
  294. }
  295. },
  296. opacity: function() {
  297. module.verbose('Removing inline opacity from element', this);
  298. $(this).css('opacity', '');
  299. if( $(this).attr('style') === '') {
  300. $(this)
  301. .attr('style', '')
  302. .removeAttr('style')
  303. ;
  304. }
  305. },
  306. },
  307. setting: function(name, value) {
  308. module.debug('Changing setting', name, value);
  309. if( $.isPlainObject(name) ) {
  310. $.extend(true, settings, name);
  311. }
  312. else if(value !== undefined) {
  313. settings[name] = value;
  314. }
  315. else {
  316. return settings[name];
  317. }
  318. },
  319. internal: function(name, value) {
  320. module.debug('Changing internal', name, value);
  321. if(value !== undefined) {
  322. if( $.isPlainObject(name) ) {
  323. $.extend(true, module, name);
  324. }
  325. else {
  326. module[name] = value;
  327. }
  328. }
  329. else {
  330. return module[name];
  331. }
  332. },
  333. debug: function() {
  334. if(settings.debug) {
  335. if(settings.performance) {
  336. module.performance.log(arguments);
  337. }
  338. else {
  339. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  340. module.debug.apply(console, arguments);
  341. }
  342. }
  343. },
  344. verbose: function() {
  345. if(settings.verbose && settings.debug) {
  346. if(settings.performance) {
  347. module.performance.log(arguments);
  348. }
  349. else {
  350. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  351. module.verbose.apply(console, arguments);
  352. }
  353. }
  354. },
  355. error: function() {
  356. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  357. module.error.apply(console, arguments);
  358. },
  359. performance: {
  360. log: function(message) {
  361. var
  362. currentTime,
  363. executionTime,
  364. previousTime
  365. ;
  366. if(settings.performance) {
  367. currentTime = new Date().getTime();
  368. previousTime = time || currentTime;
  369. executionTime = currentTime - previousTime;
  370. time = currentTime;
  371. performance.push({
  372. 'Name' : message[0],
  373. 'Arguments' : [].slice.call(message, 1) || '',
  374. 'Element' : element,
  375. 'Execution Time' : executionTime
  376. });
  377. }
  378. clearTimeout(module.performance.timer);
  379. module.performance.timer = setTimeout(module.performance.display, 100);
  380. },
  381. display: function() {
  382. var
  383. title = settings.name + ':',
  384. totalTime = 0
  385. ;
  386. time = false;
  387. clearTimeout(module.performance.timer);
  388. $.each(performance, function(index, data) {
  389. totalTime += data['Execution Time'];
  390. });
  391. title += ' ' + totalTime + 'ms';
  392. if(moduleSelector) {
  393. title += ' \'' + moduleSelector + '\'';
  394. }
  395. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  396. console.groupCollapsed(title);
  397. if(console.table) {
  398. console.table(performance);
  399. }
  400. else {
  401. $.each(performance, function(index, data) {
  402. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  403. });
  404. }
  405. console.groupEnd();
  406. }
  407. performance = [];
  408. }
  409. },
  410. invoke: function(query, passedArguments, context) {
  411. var
  412. object = instance,
  413. maxDepth,
  414. found,
  415. response
  416. ;
  417. passedArguments = passedArguments || queryArguments;
  418. context = element || context;
  419. if(typeof query == 'string' && object !== undefined) {
  420. query = query.split(/[\. ]/);
  421. maxDepth = query.length - 1;
  422. $.each(query, function(depth, value) {
  423. var camelCaseValue = (depth != maxDepth)
  424. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  425. : query
  426. ;
  427. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  428. object = object[camelCaseValue];
  429. }
  430. else if( object[camelCaseValue] !== undefined ) {
  431. found = object[camelCaseValue];
  432. return false;
  433. }
  434. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  435. object = object[value];
  436. }
  437. else if( object[value] !== undefined ) {
  438. found = object[value];
  439. return false;
  440. }
  441. else {
  442. module.error(error.method, query);
  443. return false;
  444. }
  445. });
  446. }
  447. if ( $.isFunction( found ) ) {
  448. response = found.apply(context, passedArguments);
  449. }
  450. else if(found !== undefined) {
  451. response = found;
  452. }
  453. if($.isArray(returnedValue)) {
  454. returnedValue.push(response);
  455. }
  456. else if(returnedValue !== undefined) {
  457. returnedValue = [returnedValue, response];
  458. }
  459. else if(response !== undefined) {
  460. returnedValue = response;
  461. }
  462. return found;
  463. }
  464. };
  465. if(methodInvoked) {
  466. if(instance === undefined) {
  467. module.initialize();
  468. }
  469. module.invoke(query);
  470. }
  471. else {
  472. if(instance !== undefined) {
  473. module.destroy();
  474. }
  475. module.initialize();
  476. }
  477. })
  478. ;
  479. return (returnedValue !== undefined)
  480. ? returnedValue
  481. : this
  482. ;
  483. };
  484. $.fn.accordion.settings = {
  485. name : 'Accordion',
  486. namespace : 'accordion',
  487. debug : false,
  488. verbose : true,
  489. performance : true,
  490. exclusive : true,
  491. collapsible : true,
  492. closeNested : false,
  493. animateChildren : true,
  494. duration : 500,
  495. easing : 'easeOutQuint',
  496. onOpen : function(){},
  497. onClose : function(){},
  498. onChange : function(){},
  499. error: {
  500. method : 'The method you called is not defined'
  501. },
  502. className : {
  503. active : 'active'
  504. },
  505. selector : {
  506. accordion : '.accordion',
  507. title : '.title',
  508. content : '.content'
  509. }
  510. };
  511. // Adds easing
  512. $.extend( $.easing, {
  513. easeOutQuint: function (x, t, b, c, d) {
  514. return c*((t=t/d-1)*t*t*t*t + 1) + b;
  515. }
  516. });
  517. })( jQuery, window , document );