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.

426 lines
12 KiB

  1. /*
  2. * # Semantic - Accordion
  3. * http://github.com/jlukic/semantic-ui/
  4. *
  5. *
  6. * Copyright 2013 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ($, window, document, undefined) {
  12. $.fn.accordion = function(parameters) {
  13. var
  14. $allModules = $(this),
  15. time = new Date().getTime(),
  16. performance = [],
  17. query = arguments[0],
  18. methodInvoked = (typeof query == 'string'),
  19. queryArguments = [].slice.call(arguments, 1),
  20. invokedResponse
  21. ;
  22. $allModules
  23. .each(function() {
  24. var
  25. settings = ( $.isPlainObject(parameters) )
  26. ? $.extend(true, {}, $.fn.accordion.settings, parameters)
  27. : $.extend({}, $.fn.accordion.settings),
  28. className = settings.className,
  29. namespace = settings.namespace,
  30. selector = settings.selector,
  31. error = settings.error,
  32. eventNamespace = '.' + namespace,
  33. moduleNamespace = 'module-' + namespace,
  34. moduleSelector = $allModules.selector || '',
  35. $module = $(this),
  36. $title = $module.find(selector.title),
  37. $content = $module.find(selector.content),
  38. element = this,
  39. instance = $module.data(moduleNamespace),
  40. module
  41. ;
  42. module = {
  43. initialize: function() {
  44. module.debug('Initializing accordion with bound events', $module);
  45. // initializing
  46. $title
  47. .on('click' + eventNamespace, module.event.click)
  48. ;
  49. module.instantiate();
  50. },
  51. instantiate: function() {
  52. instance = module;
  53. $module
  54. .data(moduleNamespace, module)
  55. ;
  56. },
  57. destroy: function() {
  58. module.debug('Destroying previous accordion for', $module);
  59. $module
  60. .removeData(moduleNamespace)
  61. ;
  62. $title
  63. .off(eventNamespace)
  64. ;
  65. },
  66. event: {
  67. click: function() {
  68. module.verbose('Title clicked', this);
  69. var
  70. $activeTitle = $(this),
  71. index = $title.index($activeTitle)
  72. ;
  73. module.toggle(index);
  74. },
  75. resetStyle: function() {
  76. module.verbose('Resetting styles on element', this);
  77. $(this)
  78. .attr('style', '')
  79. .removeAttr('style')
  80. .children()
  81. .attr('style', '')
  82. .removeAttr('style')
  83. ;
  84. }
  85. },
  86. toggle: function(index) {
  87. module.debug('Toggling content content at index', index);
  88. var
  89. $activeTitle = $title.eq(index),
  90. $activeContent = $activeTitle.next($content),
  91. contentIsOpen = $activeContent.is(':visible')
  92. ;
  93. if(contentIsOpen) {
  94. if(settings.collapsible) {
  95. module.close(index);
  96. }
  97. else {
  98. module.debug('Cannot close accordion content collapsing is disabled');
  99. }
  100. }
  101. else {
  102. module.open(index);
  103. }
  104. },
  105. open: function(index) {
  106. var
  107. $activeTitle = $title.eq(index),
  108. $activeContent = $activeTitle.next($content),
  109. $previousTitle = $title.filter('.' + className.active),
  110. $previousContent = $previousTitle.next($title),
  111. contentIsOpen = ($previousTitle.size() > 0)
  112. ;
  113. if( !$activeContent.is(':animated') ) {
  114. module.debug('Opening accordion content', $activeTitle);
  115. if(settings.exclusive && contentIsOpen) {
  116. $previousTitle
  117. .removeClass(className.active)
  118. ;
  119. $previousContent
  120. .stop()
  121. .children()
  122. .animate({
  123. opacity: 0
  124. }, settings.duration, module.event.resetStyle)
  125. .end()
  126. .slideUp(settings.duration , settings.easing, function() {
  127. $previousContent
  128. .removeClass(className.active)
  129. .attr('style', '')
  130. .removeAttr('style')
  131. .children()
  132. .attr('style', '')
  133. .removeAttr('style')
  134. ;
  135. })
  136. ;
  137. }
  138. $activeTitle
  139. .addClass(className.active)
  140. ;
  141. $activeContent
  142. .stop()
  143. .children()
  144. .attr('style', '')
  145. .removeAttr('style')
  146. .end()
  147. .slideDown(settings.duration, settings.easing, function() {
  148. $activeContent
  149. .addClass(className.active)
  150. .attr('style', '')
  151. .removeAttr('style')
  152. ;
  153. $.proxy(settings.onOpen, $activeContent)();
  154. $.proxy(settings.onChange, $activeContent)();
  155. })
  156. ;
  157. }
  158. },
  159. close: function(index) {
  160. var
  161. $activeTitle = $title.eq(index),
  162. $activeContent = $activeTitle.next($content)
  163. ;
  164. module.debug('Closing accordion content', $activeTitle);
  165. $activeTitle
  166. .removeClass(className.active)
  167. ;
  168. $activeContent
  169. .removeClass(className.active)
  170. .show()
  171. .stop()
  172. .children()
  173. .animate({
  174. opacity: 0
  175. }, settings.duration, module.event.resetStyle)
  176. .end()
  177. .slideUp(settings.duration, settings.easing, function(){
  178. $activeContent
  179. .attr('style', '')
  180. .removeAttr('style')
  181. ;
  182. $.proxy(settings.onClose, $activeContent)();
  183. $.proxy(settings.onChange, $activeContent)();
  184. })
  185. ;
  186. },
  187. setting: function(name, value) {
  188. module.debug('Changing setting', name, value);
  189. if(value !== undefined) {
  190. if( $.isPlainObject(name) ) {
  191. $.extend(true, settings, name);
  192. }
  193. else {
  194. settings[name] = value;
  195. }
  196. }
  197. else {
  198. return settings[name];
  199. }
  200. },
  201. internal: function(name, value) {
  202. module.debug('Changing internal', name, value);
  203. if(value !== undefined) {
  204. if( $.isPlainObject(name) ) {
  205. $.extend(true, module, name);
  206. }
  207. else {
  208. module[name] = value;
  209. }
  210. }
  211. else {
  212. return module[name];
  213. }
  214. },
  215. debug: function() {
  216. if(settings.debug) {
  217. if(settings.performance) {
  218. module.performance.log(arguments);
  219. }
  220. else {
  221. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  222. module.debug.apply(console, arguments);
  223. }
  224. }
  225. },
  226. verbose: function() {
  227. if(settings.verbose && settings.debug) {
  228. if(settings.performance) {
  229. module.performance.log(arguments);
  230. }
  231. else {
  232. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  233. module.verbose.apply(console, arguments);
  234. }
  235. }
  236. },
  237. error: function() {
  238. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  239. module.error.apply(console, arguments);
  240. },
  241. performance: {
  242. log: function(message) {
  243. var
  244. currentTime,
  245. executionTime,
  246. previousTime
  247. ;
  248. if(settings.performance) {
  249. currentTime = new Date().getTime();
  250. previousTime = time || currentTime;
  251. executionTime = currentTime - previousTime;
  252. time = currentTime;
  253. performance.push({
  254. 'Element' : element,
  255. 'Name' : message[0],
  256. 'Arguments' : [].slice.call(message, 1) || '',
  257. 'Execution Time' : executionTime
  258. });
  259. }
  260. clearTimeout(module.performance.timer);
  261. module.performance.timer = setTimeout(module.performance.display, 100);
  262. },
  263. display: function() {
  264. var
  265. title = settings.name + ':',
  266. totalTime = 0
  267. ;
  268. time = false;
  269. clearTimeout(module.performance.timer);
  270. $.each(performance, function(index, data) {
  271. totalTime += data['Execution Time'];
  272. });
  273. title += ' ' + totalTime + 'ms';
  274. if(moduleSelector) {
  275. title += ' \'' + moduleSelector + '\'';
  276. }
  277. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  278. console.groupCollapsed(title);
  279. if(console.table) {
  280. console.table(performance);
  281. }
  282. else {
  283. $.each(performance, function(index, data) {
  284. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  285. });
  286. }
  287. console.groupEnd();
  288. }
  289. performance = [];
  290. }
  291. },
  292. invoke: function(query, passedArguments, context) {
  293. var
  294. maxDepth,
  295. found,
  296. response
  297. ;
  298. passedArguments = passedArguments || queryArguments;
  299. context = element || context;
  300. if(typeof query == 'string' && instance !== undefined) {
  301. query = query.split(/[\. ]/);
  302. maxDepth = query.length - 1;
  303. $.each(query, function(depth, value) {
  304. var camelCaseValue = (depth != maxDepth)
  305. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  306. : query
  307. ;
  308. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  309. instance = instance[value];
  310. }
  311. else if( $.isPlainObject( instance[camelCaseValue] ) && (depth != maxDepth) ) {
  312. instance = instance[camelCaseValue];
  313. }
  314. else if( instance[value] !== undefined ) {
  315. found = instance[value];
  316. return false;
  317. }
  318. else if( instance[camelCaseValue] !== undefined ) {
  319. found = instance[camelCaseValue];
  320. return false;
  321. }
  322. else {
  323. module.error(error.method);
  324. return false;
  325. }
  326. });
  327. }
  328. if ( $.isFunction( found ) ) {
  329. response = found.apply(context, passedArguments);
  330. }
  331. else if(found !== undefined) {
  332. response = found;
  333. }
  334. if($.isArray(invokedResponse)) {
  335. invokedResponse.push(response);
  336. }
  337. else if(typeof invokedResponse == 'string') {
  338. invokedResponse = [invokedResponse, response];
  339. }
  340. else if(response !== undefined) {
  341. invokedResponse = response;
  342. }
  343. return found;
  344. }
  345. };
  346. if(methodInvoked) {
  347. if(instance === undefined) {
  348. module.initialize();
  349. }
  350. module.invoke(query);
  351. }
  352. else {
  353. if(instance !== undefined) {
  354. module.destroy();
  355. }
  356. module.initialize();
  357. }
  358. })
  359. ;
  360. return (invokedResponse !== undefined)
  361. ? invokedResponse
  362. : this
  363. ;
  364. };
  365. $.fn.accordion.settings = {
  366. name : 'Accordion',
  367. namespace : 'accordion',
  368. debug : true,
  369. verbose : true,
  370. performance : true,
  371. exclusive : true,
  372. collapsible : true,
  373. duration : 500,
  374. easing : 'easeInOutQuint',
  375. onOpen : function(){},
  376. onClose : function(){},
  377. onChange : function(){},
  378. error: {
  379. method : 'The method you called is not defined'
  380. },
  381. className : {
  382. active : 'active'
  383. },
  384. selector : {
  385. title : '.title',
  386. content : '.content'
  387. },
  388. };
  389. // Adds easing
  390. $.extend( $.easing, {
  391. easeInOutQuint: function (x, t, b, c, d) {
  392. if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
  393. return c/2*((t-=2)*t*t*t*t + 2) + b;
  394. }
  395. });
  396. })( jQuery, window , document );