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.

404 lines
12 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.error,
  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. module.debug.apply(console, arguments);
  209. }
  210. }
  211. },
  212. verbose: function() {
  213. if(settings.verbose && settings.debug) {
  214. if(settings.performance) {
  215. module.performance.log(arguments);
  216. }
  217. else {
  218. module.verbose = Function.prototype.bind.call(console.info, console, settings.moduleName + ':');
  219. module.verbose.apply(console, arguments);
  220. }
  221. }
  222. },
  223. error: function() {
  224. module.error = Function.prototype.bind.call(console.error, console, settings.moduleName + ':');
  225. module.error.apply(console, arguments);
  226. },
  227. performance: {
  228. log: function(message) {
  229. var
  230. currentTime,
  231. executionTime,
  232. previousTime
  233. ;
  234. if(settings.performance) {
  235. currentTime = new Date().getTime();
  236. previousTime = time || currentTime;
  237. executionTime = currentTime - previousTime;
  238. time = currentTime;
  239. performance.push({
  240. 'Element' : element,
  241. 'Name' : message[0],
  242. 'Arguments' : [].slice.call(message, 1) || '',
  243. 'Execution Time' : executionTime
  244. });
  245. }
  246. clearTimeout(module.performance.timer);
  247. module.performance.timer = setTimeout(module.performance.display, 100);
  248. },
  249. display: function() {
  250. var
  251. title = settings.moduleName + ':',
  252. totalTime = 0
  253. ;
  254. time = false;
  255. clearTimeout(module.performance.timer);
  256. $.each(performance, function(index, data) {
  257. totalTime += data['Execution Time'];
  258. });
  259. title += ' ' + totalTime + 'ms';
  260. if(moduleSelector) {
  261. title += ' \'' + moduleSelector + '\'';
  262. }
  263. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  264. console.groupCollapsed(title);
  265. if(console.table) {
  266. console.table(performance);
  267. }
  268. else {
  269. $.each(performance, function(index, data) {
  270. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  271. });
  272. }
  273. console.groupEnd();
  274. }
  275. performance = [];
  276. }
  277. },
  278. invoke: function(query, passedArguments, context) {
  279. var
  280. maxDepth,
  281. found,
  282. response
  283. ;
  284. passedArguments = passedArguments || queryArguments;
  285. context = element || context;
  286. if(typeof query == 'string' && instance !== undefined) {
  287. query = query.split(/[\. ]/);
  288. maxDepth = query.length - 1;
  289. $.each(query, function(depth, value) {
  290. var camelCaseValue = (depth != maxDepth)
  291. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  292. : query
  293. ;
  294. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  295. instance = instance[value];
  296. }
  297. else if( $.isPlainObject( instance[camelCaseValue] ) && (depth != maxDepth) ) {
  298. instance = instance[camelCaseValue];
  299. }
  300. else if( instance[value] !== undefined ) {
  301. found = instance[value];
  302. return false;
  303. }
  304. else if( instance[camelCaseValue] !== undefined ) {
  305. found = instance[camelCaseValue];
  306. return false;
  307. }
  308. else {
  309. module.error(error.method);
  310. return false;
  311. }
  312. });
  313. }
  314. if ( $.isFunction( found ) ) {
  315. response = found.apply(context, passedArguments);
  316. }
  317. else if(found !== undefined) {
  318. response = found;
  319. }
  320. if($.isArray(invokedResponse)) {
  321. invokedResponse.push(response);
  322. }
  323. else if(typeof invokedResponse == 'string') {
  324. invokedResponse = [invokedResponse, response];
  325. }
  326. else if(response !== undefined) {
  327. invokedResponse = response;
  328. }
  329. return found;
  330. }
  331. };
  332. if(methodInvoked) {
  333. if(instance === undefined) {
  334. module.initialize();
  335. }
  336. module.invoke(query);
  337. }
  338. else {
  339. if(instance !== undefined) {
  340. module.destroy();
  341. }
  342. module.initialize();
  343. }
  344. })
  345. ;
  346. return (invokedResponse !== undefined)
  347. ? invokedResponse
  348. : this
  349. ;
  350. };
  351. $.fn.accordion.settings = {
  352. moduleName : 'Accordion',
  353. debug : true,
  354. verbose : true,
  355. performance : true,
  356. exclusive : true,
  357. collapsible : true,
  358. onOpen : function(){},
  359. onClose : function(){},
  360. onChange : function(){},
  361. error: {
  362. method : 'The method you called is not defined'
  363. },
  364. className : {
  365. active : 'active',
  366. hover : 'hover'
  367. },
  368. selector : {
  369. title : '.title',
  370. icon : '.icon',
  371. content : '.content'
  372. },
  373. speed : 500,
  374. easing : 'easeInOutQuint'
  375. };
  376. })( jQuery, window , document );