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.

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