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.

504 lines
15 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
  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 !== undefined) {
  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. $.proxy(module.toggle, 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. $.proxy(module.close, $activeTitle)();
  109. }
  110. else {
  111. module.debug('Cannot close accordion content collapsing is disabled');
  112. }
  113. }
  114. else {
  115. $.proxy(module.open, $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. $.proxy(module.closeOthers, $activeTitle)();
  133. }
  134. $activeTitle
  135. .addClass(className.active)
  136. ;
  137. $activeContent
  138. .stop()
  139. .children()
  140. .stop()
  141. .animate({
  142. opacity: 1
  143. }, settings.duration, module.reset.display)
  144. .end()
  145. .slideDown(settings.duration, settings.easing, function() {
  146. $activeContent
  147. .addClass(className.active)
  148. ;
  149. $.proxy(module.reset.display, this)();
  150. $.proxy(settings.onOpen, this)();
  151. $.proxy(settings.onChange, this)();
  152. })
  153. ;
  154. }
  155. },
  156. close: function(query) {
  157. var
  158. $activeTitle = (query !== undefined)
  159. ? (typeof query === 'number')
  160. ? $title.eq(query)
  161. : $(query)
  162. : $(this),
  163. $activeContent = $activeTitle.next($content),
  164. isActive = $activeContent.hasClass(className.active)
  165. ;
  166. if(isActive) {
  167. module.debug('Closing accordion content', $activeContent);
  168. $activeTitle
  169. .removeClass(className.active)
  170. ;
  171. $activeContent
  172. .removeClass(className.active)
  173. .show()
  174. .stop()
  175. .children()
  176. .stop()
  177. .animate({
  178. opacity: 0
  179. }, settings.duration, module.reset.opacity)
  180. .end()
  181. .slideUp(settings.duration, settings.easing, function() {
  182. $.proxy(module.reset.display, this)();
  183. $.proxy(settings.onClose, this)();
  184. $.proxy(settings.onChange, this)();
  185. })
  186. ;
  187. }
  188. },
  189. closeOthers: function(index) {
  190. var
  191. $activeTitle = (index !== undefined)
  192. ? $title.eq(index)
  193. : $(this),
  194. $parentTitles = $activeTitle.parents(selector.content).prev(selector.title),
  195. $activeAccordion = $activeTitle.closest(selector.accordion),
  196. activeSelector = selector.title + '.' + className.active + ':visible',
  197. activeContent = selector.content + '.' + className.active + ':visible',
  198. $openTitles,
  199. $nestedTitles,
  200. $openContents
  201. ;
  202. if(settings.closeNested) {
  203. $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
  204. $openContents = $openTitles.next($content);
  205. }
  206. else {
  207. $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
  208. $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
  209. $openTitles = $openTitles.not($nestedTitles);
  210. $openContents = $openTitles.next($content);
  211. }
  212. if( ($openTitles.size() > 0) ) {
  213. module.debug('Exclusive enabled, closing other content', $openTitles);
  214. $openTitles
  215. .removeClass(className.active)
  216. ;
  217. $openContents
  218. .stop()
  219. .children()
  220. .stop()
  221. .animate({
  222. opacity: 0
  223. }, settings.duration, module.resetOpacity)
  224. .end()
  225. .slideUp(settings.duration , settings.easing, function() {
  226. $(this).removeClass(className.active);
  227. $.proxy(module.reset.display, this)();
  228. })
  229. ;
  230. }
  231. },
  232. reset: {
  233. display: function() {
  234. module.verbose('Removing inline display from element', this);
  235. $(this).css('display', '');
  236. if( $(this).attr('style') === '') {
  237. $(this)
  238. .attr('style', '')
  239. .removeAttr('style')
  240. ;
  241. }
  242. },
  243. opacity: function() {
  244. module.verbose('Removing inline opacity from element', this);
  245. $(this).css('opacity', '');
  246. if( $(this).attr('style') === '') {
  247. $(this)
  248. .attr('style', '')
  249. .removeAttr('style')
  250. ;
  251. }
  252. },
  253. },
  254. setting: function(name, value) {
  255. module.debug('Changing setting', name, value);
  256. if( $.isPlainObject(name) ) {
  257. $.extend(true, settings, name);
  258. }
  259. else if(value !== undefined) {
  260. settings[name] = value;
  261. }
  262. else {
  263. return settings[name];
  264. }
  265. },
  266. internal: function(name, value) {
  267. module.debug('Changing internal', name, value);
  268. if(value !== undefined) {
  269. if( $.isPlainObject(name) ) {
  270. $.extend(true, module, name);
  271. }
  272. else {
  273. module[name] = value;
  274. }
  275. }
  276. else {
  277. return module[name];
  278. }
  279. },
  280. debug: function() {
  281. if(settings.debug) {
  282. if(settings.performance) {
  283. module.performance.log(arguments);
  284. }
  285. else {
  286. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  287. module.debug.apply(console, arguments);
  288. }
  289. }
  290. },
  291. verbose: function() {
  292. if(settings.verbose && settings.debug) {
  293. if(settings.performance) {
  294. module.performance.log(arguments);
  295. }
  296. else {
  297. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  298. module.verbose.apply(console, arguments);
  299. }
  300. }
  301. },
  302. error: function() {
  303. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  304. module.error.apply(console, arguments);
  305. },
  306. performance: {
  307. log: function(message) {
  308. var
  309. currentTime,
  310. executionTime,
  311. previousTime
  312. ;
  313. if(settings.performance) {
  314. currentTime = new Date().getTime();
  315. previousTime = time || currentTime;
  316. executionTime = currentTime - previousTime;
  317. time = currentTime;
  318. performance.push({
  319. 'Name' : message[0],
  320. 'Arguments' : [].slice.call(message, 1) || '',
  321. 'Element' : element,
  322. 'Execution Time' : executionTime
  323. });
  324. }
  325. clearTimeout(module.performance.timer);
  326. module.performance.timer = setTimeout(module.performance.display, 100);
  327. },
  328. display: function() {
  329. var
  330. title = settings.name + ':',
  331. totalTime = 0
  332. ;
  333. time = false;
  334. clearTimeout(module.performance.timer);
  335. $.each(performance, function(index, data) {
  336. totalTime += data['Execution Time'];
  337. });
  338. title += ' ' + totalTime + 'ms';
  339. if(moduleSelector) {
  340. title += ' \'' + moduleSelector + '\'';
  341. }
  342. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  343. console.groupCollapsed(title);
  344. if(console.table) {
  345. console.table(performance);
  346. }
  347. else {
  348. $.each(performance, function(index, data) {
  349. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  350. });
  351. }
  352. console.groupEnd();
  353. }
  354. performance = [];
  355. }
  356. },
  357. invoke: function(query, passedArguments, context) {
  358. var
  359. object = instance,
  360. maxDepth,
  361. found,
  362. response
  363. ;
  364. passedArguments = passedArguments || queryArguments;
  365. context = element || context;
  366. if(typeof query == 'string' && object !== undefined) {
  367. query = query.split(/[\. ]/);
  368. maxDepth = query.length - 1;
  369. $.each(query, function(depth, value) {
  370. var camelCaseValue = (depth != maxDepth)
  371. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  372. : query
  373. ;
  374. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  375. object = object[camelCaseValue];
  376. }
  377. else if( object[camelCaseValue] !== undefined ) {
  378. found = object[camelCaseValue];
  379. return false;
  380. }
  381. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  382. object = object[value];
  383. }
  384. else if( object[value] !== undefined ) {
  385. found = object[value];
  386. return false;
  387. }
  388. else {
  389. return false;
  390. }
  391. });
  392. }
  393. if ( $.isFunction( found ) ) {
  394. response = found.apply(context, passedArguments);
  395. }
  396. else if(found !== undefined) {
  397. response = found;
  398. }
  399. if($.isArray(returnedValue)) {
  400. returnedValue.push(response);
  401. }
  402. else if(returnedValue !== undefined) {
  403. returnedValue = [returnedValue, response];
  404. }
  405. else if(response !== undefined) {
  406. returnedValue = response;
  407. }
  408. return found;
  409. }
  410. };
  411. if(methodInvoked) {
  412. if(instance === undefined) {
  413. module.initialize();
  414. }
  415. module.invoke(query);
  416. }
  417. else {
  418. if(instance !== undefined) {
  419. module.destroy();
  420. }
  421. module.initialize();
  422. }
  423. })
  424. ;
  425. return (returnedValue !== undefined)
  426. ? returnedValue
  427. : this
  428. ;
  429. };
  430. $.fn.accordion.settings = {
  431. name : 'Accordion',
  432. namespace : 'accordion',
  433. debug : false,
  434. verbose : true,
  435. performance : true,
  436. exclusive : true,
  437. collapsible : true,
  438. closeNested : false,
  439. duration : 500,
  440. easing : 'easeInOutQuint',
  441. onOpen : function(){},
  442. onClose : function(){},
  443. onChange : function(){},
  444. error: {
  445. method : 'The method you called is not defined'
  446. },
  447. className : {
  448. active : 'active'
  449. },
  450. selector : {
  451. accordion : '.accordion',
  452. title : '.title',
  453. content : '.content'
  454. }
  455. };
  456. // Adds easing
  457. $.extend( $.easing, {
  458. easeInOutQuint: function (x, t, b, c, d) {
  459. if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
  460. return c/2*((t-=2)*t*t*t*t + 2) + b;
  461. }
  462. });
  463. })( jQuery, window , document );