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.

204 lines
6.6 KiB

  1. // Native Javascript for Bootstrap 3 | Collapse
  2. // by dnp_theme
  3. (function(factory){
  4. // CommonJS/RequireJS and "native" compatibility
  5. if(typeof module !== "undefined" && typeof exports == "object") {
  6. // A commonJS/RequireJS environment
  7. if(typeof window != "undefined") {
  8. // Window and document exist, so return the factory's return value.
  9. module.exports = factory();
  10. } else {
  11. // Let the user give the factory a Window and Document.
  12. module.exports = factory;
  13. }
  14. } else {
  15. // Assume a traditional browser.
  16. window.Collapse = factory();
  17. }
  18. })(function(){
  19. // COLLAPSE DEFINITION
  20. // ===================
  21. var Collapse = function( element, options ) {
  22. options = options || {};
  23. this.btn = typeof element === 'object' ? element : document.querySelector(element);
  24. this.accordion = null;
  25. this.collapse = null;
  26. this.duration = 300; // default collapse transition duration
  27. this.options = {};
  28. this.options.duration = /ie/.test(document.documentElement.className) ? 0 : (options.duration || this.duration);
  29. this.init();
  30. }
  31. // COLLAPSE METHODS
  32. // ================
  33. Collapse.prototype = {
  34. init : function() {
  35. this.actions();
  36. this.btn.addEventListener('click', this.toggle, false);
  37. // allows the collapse to expand
  38. // ** when window gets resized
  39. // ** or via internal clicks handers such as dropwowns or any other
  40. document.addEventListener('click', this.update, false);
  41. window.addEventListener('resize', this.update, false)
  42. },
  43. actions : function() {
  44. var self = this;
  45. this.toggle = function(e) {
  46. self.btn = self.getTarget(e).btn;
  47. self.collapse = self.getTarget(e).collapse;
  48. if (!/in/.test(self.collapse.className)) {
  49. self.open(e)
  50. } else {
  51. self.close(e)
  52. }
  53. },
  54. this.close = function(e) {
  55. e.preventDefault();
  56. self.btn = self.getTarget(e).btn;
  57. self.collapse = self.getTarget(e).collapse;
  58. self._close(self.collapse);
  59. self.btn.className = self.btn.className.replace(' collapsed','');
  60. },
  61. this.open = function(e) {
  62. e.preventDefault();
  63. self.btn = self.getTarget(e).btn;
  64. self.collapse = self.getTarget(e).collapse;
  65. self.accordion = self.btn.getAttribute('data-parent') && self.getClosest(self.btn, self.btn.getAttribute('data-parent'));
  66. self._open(self.collapse);
  67. self.btn.className += ' collapsed';
  68. if ( self.accordion !== null ) {
  69. var active = self.accordion.querySelectorAll('.collapse.in'), al = active.length, i = 0;
  70. for (i;i<al;i++) {
  71. if ( active[i] !== self.collapse) self._close(active[i]);
  72. }
  73. }
  74. },
  75. this._open = function(c) {
  76. c.className += ' in';
  77. c.style.height = 0;
  78. c.style.overflow = 'hidden';
  79. c.setAttribute('area-expanded','true');
  80. // the collapse MUST have a childElement div to wrap them all inside, just like accordion/well
  81. var oh = this.getMaxHeight(c).oh, br = this.getMaxHeight(c).br;
  82. c.style.height = oh + br + 'px';
  83. setTimeout(function() {
  84. c.style.overflow = '';
  85. }, self.options.duration)
  86. },
  87. this._close = function(c) {
  88. c.style.overflow = 'hidden';
  89. c.style.height = 0;
  90. setTimeout(function() {
  91. c.className = c.className.replace(' in','');
  92. c.style.overflow = '';
  93. c.setAttribute('area-expanded','false');
  94. }, self.options.duration)
  95. },
  96. this.update = function(e) {
  97. var evt = e.type, tg = e.target, closest = self.getClosest(tg,'.collapse'),
  98. itms = document.querySelectorAll('.collapse.in'), i = 0, il = itms.length;
  99. for (i;i<il;i++) {
  100. var itm = itms[i], oh = self.getMaxHeight(itm).oh, br = self.getMaxHeight(itm).br;
  101. if ( evt === 'resize' && !/ie/.test(document.documentElement.className) ){
  102. setTimeout(function() {
  103. itm.style.height = oh + br + 'px';
  104. }, self.options.duration)
  105. } else if ( evt === 'click' && closest === itm ) {
  106. itm.style.height = oh + br + 'px';
  107. }
  108. }
  109. },
  110. this.getMaxHeight = function(l) { // get collapse trueHeight and border
  111. var t = l.children[0];
  112. var cs = l.currentStyle || window.getComputedStyle(l);
  113. return {
  114. oh : getOuterHeight(t),
  115. br : parseInt(cs.borderTop||0) + parseInt(cs.borderBottom||0)
  116. }
  117. },
  118. this.getTarget = function(e) {
  119. var t = e.currentTarget || e.srcElement,
  120. h = t.href && t.getAttribute('href').replace('#',''),
  121. d = t.getAttribute('data-target') && ( t.getAttribute('data-target') ),
  122. id = h || ( d && /#/.test(d)) && d.replace('#',''),
  123. cl = (d && d.charAt(0) === '.') && d, //the navbar collapse trigger targets a class
  124. c = id && document.getElementById(id) || cl && document.querySelector(cl);
  125. return {
  126. btn : t,
  127. collapse : c
  128. }
  129. },
  130. this.getClosest = function (el, s) { //el is the element and s the selector of the closest item to find
  131. // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
  132. var f = s.charAt(0);
  133. for ( ; el && el !== document; el = el.parentNode ) {// Get closest match
  134. if ( f === '.' ) {// If selector is a class
  135. if ( document.querySelector(s) !== undefined ) { return el; }
  136. }
  137. if ( f === '#' ) { // If selector is an ID
  138. if ( el.id === s.substr(1) ) { return el; }
  139. }
  140. }
  141. return false;
  142. }
  143. }
  144. }
  145. var getOuterHeight = function (el) {
  146. var s = el && el.currentStyle || window.getComputedStyle(el),
  147. mtp = /px/.test(s.marginTop) ? Math.round(s.marginTop.replace('px','')) : 0,
  148. mbp = /px/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('px','')) : 0,
  149. mte = /em/.test(s.marginTop) ? Math.round(s.marginTop.replace('em','') * parseInt(s.fontSize)) : 0,
  150. mbe = /em/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('em','') * parseInt(s.fontSize)) : 0;
  151. return el.offsetHeight + parseInt( mtp ) + parseInt( mbp ) + parseInt( mte ) + parseInt( mbe ) //we need an accurate margin value
  152. }
  153. // COLLAPSE DATA API
  154. // =================
  155. var Collapses = document.querySelectorAll('[data-toggle="collapse"]'), i = 0, cll = Collapses.length;
  156. for (i;i<cll;i++) {
  157. var item = Collapses[i], options = {};
  158. options.duration = item.getAttribute('data-duration');
  159. new Collapse(item,options);
  160. }
  161. //we must add the height to the pre-opened collapses
  162. window.addEventListener('load', function() {
  163. var openedCollapses = document.querySelectorAll('.collapse'), i = 0, ocl = openedCollapses.length;
  164. for (i;i<ocl;i++) {
  165. var oc = openedCollapses[i];
  166. if (/in/.test(oc.className)) {
  167. var s = oc.currentStyle || window.getComputedStyle(oc);
  168. var oh = getOuterHeight(oc.children[0]);
  169. var br = parseInt(s.borderTop||0) + parseInt(s.borderBottom||0);
  170. oc.style.height = oh + br + 'px';
  171. }
  172. }
  173. });
  174. return Collapse;
  175. });