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.

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