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.

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