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.

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