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.

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