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.

412 lines
12 KiB

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