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.

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