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.

354 lines
10 KiB

  1. /* ******************************
  2. Accordion
  3. Author: Jack Lukic
  4. Notes: First Commit July 19, 2012
  5. Simple accordion design
  6. ****************************** */
  7. ;(function ($, window, document, undefined) {
  8. $.fn.accordion = function(parameters) {
  9. var
  10. $allModules = $(this),
  11. settings = ( $.isPlainObject(parameters) )
  12. ? $.extend(true, {}, $.fn.accordion.settings, parameters)
  13. : $.fn.accordion.settings,
  14. eventNamespace = '.' + settings.namespace,
  15. moduleNamespace = 'module-' + settings.namespace,
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. invokedResponse,
  23. allModules
  24. ;
  25. $allModules
  26. .each(function() {
  27. var
  28. $module = $(this),
  29. $title = $module.find(settings.selector.title),
  30. $icon = $module.find(settings.selector.icon),
  31. $content = $module.find(settings.selector.content),
  32. selector = $module.selector || '',
  33. element = this,
  34. instance = $module.data('module-' + settings.namespace),
  35. className = settings.className,
  36. metadata = settings.metadata,
  37. namespace = settings.namespace,
  38. animation = settings.animation,
  39. errors = settings.errors,
  40. module
  41. ;
  42. module = {
  43. initialize: function() {
  44. module.debug('Initializing accordion with bound events', $module);
  45. // initializing
  46. $title
  47. .on('click', module.event.click)
  48. ;
  49. $module
  50. .data('module', module)
  51. ;
  52. },
  53. destroy: function() {
  54. module.debug('Destroying previous accordion for', $module);
  55. $module
  56. .off(namespace)
  57. ;
  58. },
  59. event: {
  60. click: function() {
  61. var
  62. $activeTitle = $(this),
  63. activeIndex = $title.index($activeTitle),
  64. contentIsOpen = $activeTitle.next($content).hasClass(className.active)
  65. ;
  66. module.verbose('Accordion title clicked', $activeTitle);
  67. if(contentIsOpen) {
  68. if(settings.collapsible) {
  69. module.close(activeIndex);
  70. }
  71. else {
  72. module.debug('Cannot close accordion content collapsing is disabled');
  73. }
  74. }
  75. else {
  76. module.open(activeIndex);
  77. }
  78. }
  79. },
  80. open: function(index) {
  81. var
  82. $activeTitle = $title.eq(index),
  83. $activeContent = $activeTitle.next($content),
  84. $previousTitle = $title.filter('.' + className.active),
  85. $previousContent = $previousTitle.next($title),
  86. contentIsOpen = ($previousTitle.size() > 0)
  87. ;
  88. module.debug('Opening accordion content', $activeTitle);
  89. if(settings.exclusive && contentIsOpen) {
  90. $previousTitle
  91. .removeClass(className.active)
  92. ;
  93. $previousContent
  94. .stop()
  95. .children()
  96. .animate({
  97. opacity: 0
  98. }, settings.speed)
  99. .end()
  100. .slideUp(settings.speed , settings.easing, function() {
  101. $previousContent
  102. .removeClass(className.active)
  103. .removeAttr('style')
  104. .children()
  105. .removeAttr('style')
  106. ;
  107. })
  108. ;
  109. }
  110. $activeTitle
  111. .addClass(className.active)
  112. ;
  113. $activeContent
  114. .stop()
  115. .children()
  116. .removeAttr('style')
  117. .end()
  118. .slideDown(settings.speed, settings.easing, function() {
  119. $activeContent
  120. .addClass(className.active)
  121. .removeAttr('style')
  122. ;
  123. $.proxy(settings.onOpen, $activeContent)();
  124. $.proxy(settings.onChange, $activeContent)();
  125. })
  126. ;
  127. },
  128. close: function(index) {
  129. var
  130. $activeTitle = $title.eq(index),
  131. $activeContent = $activeTitle.next($content)
  132. ;
  133. module.debug('Closing accordion content', $activeTitle);
  134. $activeTitle
  135. .removeClass(className.active)
  136. ;
  137. $activeContent
  138. .removeClass(className.active)
  139. .show()
  140. .stop()
  141. .slideUp(settings.speed, settings.easing, function(){
  142. $activeContent
  143. .removeAttr('style')
  144. ;
  145. $.proxy(settings.onClose, $activeContent)();
  146. $.proxy(settings.onChange, $activeContent)();
  147. })
  148. ;
  149. },
  150. setting: function(name, value) {
  151. module.debug('Changing setting', name, value);
  152. if(value !== undefined) {
  153. if( $.isPlainObject(name) ) {
  154. $.extend(true, settings, name);
  155. }
  156. else {
  157. settings[name] = value;
  158. }
  159. }
  160. else {
  161. return settings[name];
  162. }
  163. },
  164. internal: function(name, value) {
  165. module.debug('Changing internal', name, value);
  166. if(value !== undefined) {
  167. if( $.isPlainObject(name) ) {
  168. $.extend(true, module, name);
  169. }
  170. else {
  171. module[name] = value;
  172. }
  173. }
  174. else {
  175. return module[name];
  176. }
  177. },
  178. debug: function() {
  179. if(settings.debug) {
  180. if(settings.performance) {
  181. module.performance.log(arguments);
  182. }
  183. else {
  184. module.debug = Function.prototype.bind.call(console.info, console, settings.moduleName + ':');
  185. }
  186. }
  187. },
  188. verbose: function() {
  189. if(settings.verbose && settings.debug) {
  190. if(settings.performance) {
  191. module.performance.log(arguments);
  192. }
  193. else {
  194. module.verbose = Function.prototype.bind.call(console.info, console, settings.moduleName + ':');
  195. }
  196. }
  197. },
  198. error: function() {
  199. module.error = Function.prototype.bind.call(console.log, console, settings.moduleName + ':');
  200. },
  201. performance: {
  202. log: function(message) {
  203. var
  204. currentTime,
  205. executionTime,
  206. previousTime
  207. ;
  208. if(settings.performance) {
  209. currentTime = new Date().getTime();
  210. previousTime = time || currentTime,
  211. executionTime = currentTime - previousTime;
  212. time = currentTime;
  213. performance.push({
  214. 'Element' : element,
  215. 'Name' : message[0],
  216. 'Arguments' : message[1] || '',
  217. 'Execution Time' : executionTime
  218. });
  219. }
  220. clearTimeout(module.performance.timer);
  221. module.performance.timer = setTimeout(module.performance.display, 100);
  222. },
  223. display: function() {
  224. var
  225. title = settings.moduleName + ':',
  226. totalTime = 0
  227. ;
  228. time = false;
  229. $.each(performance, function(index, data) {
  230. totalTime += data['Execution Time'];
  231. });
  232. title += ' ' + totalTime + 'ms';
  233. if(moduleSelector) {
  234. title += ' \'' + moduleSelector + '\'';
  235. }
  236. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  237. console.groupCollapsed(title);
  238. if(console.table) {
  239. console.table(performance);
  240. }
  241. else {
  242. $.each(performance, function(index, data) {
  243. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  244. });
  245. }
  246. console.groupEnd();
  247. }
  248. performance = [];
  249. }
  250. },
  251. invoke: function(query, passedArguments, context) {
  252. var
  253. maxDepth,
  254. found
  255. ;
  256. passedArguments = passedArguments || queryArguments;
  257. context = element || context;
  258. if(typeof query == 'string' && instance !== undefined) {
  259. query = query.split('.');
  260. maxDepth = query.length - 1;
  261. $.each(query, function(depth, value) {
  262. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  263. instance = instance[value];
  264. return true;
  265. }
  266. else if( instance[value] !== undefined ) {
  267. found = instance[value];
  268. return true;
  269. }
  270. module.error(errors.method);
  271. return false;
  272. });
  273. }
  274. if ( $.isFunction( found ) ) {
  275. instance.verbose('Executing invoked function', found);
  276. return found.apply(context, passedArguments);
  277. }
  278. return found || false;
  279. }
  280. };
  281. if(methodInvoked) {
  282. if(instance === undefined) {
  283. module.initialize();
  284. }
  285. invokedResponse = module.invoke(query);
  286. }
  287. else {
  288. if(instance !== undefined) {
  289. module.destroy();
  290. }
  291. module.initialize();
  292. }
  293. })
  294. ;
  295. return (invokedResponse)
  296. ? invokedResponse
  297. : this
  298. ;
  299. };
  300. $.fn.accordion.settings = {
  301. moduleName : 'Accordion',
  302. debug : true,
  303. verbose : true,
  304. performance : false,
  305. exclusive : true,
  306. collapsible : true,
  307. onOpen : function(){},
  308. onClose : function(){},
  309. onChange : function(){},
  310. errors: {
  311. method : 'The method you called is not defined'
  312. },
  313. className : {
  314. active : 'active',
  315. hover : 'hover'
  316. },
  317. selector : {
  318. title : '.title',
  319. icon : '.icon',
  320. content : '.content'
  321. },
  322. speed : 500,
  323. easing : 'easeInOutQuint'
  324. };
  325. })( jQuery, window , document );