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
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 - 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. instance.invoke('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 );