Project

General

Profile

1
// Native Javascript for Bootstrap 3 | Collapse
2
// by dnp_theme
3

    
4
(function(factory){
5

    
6
  // CommonJS/RequireJS and "native" compatibility
7
  if(typeof module !== "undefined" && typeof exports == "object") {
8
    // A commonJS/RequireJS environment
9
    if(typeof window != "undefined") {
10
      // Window and document exist, so return the factory's return value.
11
      module.exports = factory();
12
    } else {
13
      // Let the user give the factory a Window and Document.
14
      module.exports = factory;
15
    }
16
  } else {
17
    // Assume a traditional browser.
18
    window.Collapse = factory();
19
  }
20

    
21
})(function(){
22

    
23
  // COLLAPSE DEFINITION
24
  // ===================
25
  var Collapse = function( element, options ) {
26
    options = options || {};
27
    this.isIE = (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) ? parseFloat( RegExp.$1 ) : false;
28
    this.btn = typeof element === 'object' ? element : document.querySelector(element);
29
    this.accordion = null;
30
    this.collapse = null;
31
    this.duration = 300; // default collapse transition duration
32
    this.options = {};
33
    this.options.duration = (this.isIE && this.isIE < 10) ? 0 : (options.duration || this.duration);
34
    this.init();
35
  };
36

    
37
  // COLLAPSE METHODS
38
  // ================
39
  Collapse.prototype = {
40

    
41
    init : function() {
42
      this.actions();
43
      this.addEvent();
44
    },
45

    
46
    actions : function() {
47
      var self = this;
48
      var getOuterHeight = function (el) {
49
        var s = el && el.currentStyle || window.getComputedStyle(el), // the getComputedStyle polyfill would do this for us, but we want to make sure it does
50
          btp = s.borderTopWidth || 0,
51
          mtp = /px/.test(s.marginTop)  ? Math.round(s.marginTop.replace('px',''))    : 0,
52
          mbp = /px/.test(s.marginBottom)  ? Math.round(s.marginBottom.replace('px',''))  : 0,
53
          mte = /em/.test(s.marginTop)  ? Math.round(s.marginTop.replace('em','')    * parseInt(s.fontSize)) : 0,
54
          mbe = /em/.test(s.marginBottom)  ? Math.round(s.marginBottom.replace('em','')  * parseInt(s.fontSize)) : 0;
55

    
56
        return el.clientHeight + parseInt( btp ) + parseInt( mtp ) + parseInt( mbp ) + parseInt( mte ) + parseInt( mbe ); //we need an accurate margin value
57
      };
58

    
59
      this.toggle = function(e) {
60
        self.btn = self.getTarget(e).btn;
61
        self.collapse = self.getTarget(e).collapse;
62

    
63
        if (!/\bin/.test(self.collapse.className)) {
64
          self.open(e);
65
        } else {
66
          self.close(e);
67
        }
68
      },
69
      this.close = function(e) {
70
        e.preventDefault();
71
        self.btn = self.getTarget(e).btn;
72
        self.collapse = self.getTarget(e).collapse;
73
        self._close(self.collapse);
74
        self.removeClass(self.btn,'collapsed');
75
      },
76
      this.open = function(e) {
77
        e.preventDefault();
78
        self.btn = self.getTarget(e).btn;
79
        self.collapse = self.getTarget(e).collapse;
80
        self.accordion = self.btn.getAttribute('data-parent') && self.getClosest(self.btn, self.btn.getAttribute('data-parent'));
81

    
82
        self._open(self.collapse);
83
        self.addClass(self.btn,'collapsed');
84

    
85
        if ( self.accordion !== null ) {
86
          var active = self.accordion.querySelectorAll('.collapse.in'), al = active.length, i = 0;
87
          for (i;i<al;i++) {
88
            if ( active[i] !== self.collapse) self._close(active[i]);
89
          }
90
        }
91
      },
92
      this._open = function(c) {
93
        self.removeEvent();
94
        self.addClass(c,'in');
95
        c.setAttribute('aria-expanded','true');
96
        self.addClass(c,'collapsing');
97
        setTimeout(function() {
98
          var h = self.getMaxHeight(c);
99
          c.style.height = h + 'px';                    
100
          c.style.overflowY = 'hidden';
101
        }, 0);  
102
        setTimeout(function() {
103
          c.style.height = ''; 
104
          c.style.overflowY = '';
105
          self.removeClass(c,'collapsing');
106
          self.addEvent();
107
        }, self.options.duration);
108
      },
109
      this._close = function(c) {
110
        self.removeEvent();
111
        c.setAttribute('aria-expanded','false');        
112
        c.style.height = self.getMaxHeight(c) + 'px';        
113
        setTimeout(function() {
114
          c.style.height = '0px';    
115
          c.style.overflowY = 'hidden';
116
          self.addClass(c,'collapsing');
117
        }, 0);
118
        
119
        setTimeout(function() {
120
          self.removeClass(c,'collapsing');
121
          self.removeClass(c,'in'); 
122
          c.style.overflowY = '';
123
          c.style.height = '';          
124
          self.addEvent();
125
        }, self.options.duration);
126
      },
127
      this.getMaxHeight = function(l) { // get collapse trueHeight and border
128
        var h = 0;
129
        for (var k = 0, ll = l.children.length; k < ll; k++) {
130
          h += getOuterHeight(l.children[k]);
131
        }
132
        return h;
133
      },
134
      this.removeEvent = function() {
135
        this.btn.removeEventListener('click', this.toggle, false);
136
      },
137
      this.addEvent = function() {
138
        this.btn.addEventListener('click', this.toggle, false);
139
      },
140
      this.getTarget = function(e) {
141
        var t = e.currentTarget || e.srcElement,
142
          h = t.href && t.getAttribute('href').replace('#',''),
143
          d = t.getAttribute('data-target') && ( t.getAttribute('data-target') ),
144
          id = h || ( d && /#/.test(d)) && d.replace('#',''),
145
          cl = (d && d.charAt(0) === '.') && d, //the navbar collapse trigger targets a class
146
          c = id && document.getElementById(id) || cl && document.querySelector(cl);
147

    
148
        return {
149
          btn : t,
150
          collapse : c
151
        };
152
      },
153

    
154
      this.getClosest = function (el, s) { //el is the element and s the selector of the closest item to find
155
      // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
156
        var f = s.charAt(0);
157
        for ( ; el && el !== document; el = el.parentNode ) {// Get closest match
158
          if ( f === '.' ) {// If selector is a class
159
            if ( document.querySelector(s) !== undefined ) { return el; }
160
          }
161
          if ( f === '#' ) { // If selector is an ID
162
            if ( el.id === s.substr(1) ) { return el; }
163
          }
164
        }
165
        return false;
166
      };
167
      this.addClass = function(el,c) {  
168
        if (el.classList) { el.classList.add(c); } else { el.className += ' '+c; }
169
      };
170
      this.removeClass = function(el,c) {
171
        if (el.classList) { el.classList.remove(c); } else { el.className = el.className.replace(c,'').replace(/^\s+|\s+$/g,''); }
172
      };
173
    }
174
  };
175

    
176
  // COLLAPSE DATA API
177
  // =================
178
  var Collapses = document.querySelectorAll('[data-toggle="collapse"]'), i = 0, cll = Collapses.length;
179
  for (i;i<cll;i++) {
180
    var item = Collapses[i], options = {};
181
    options.duration = item.getAttribute('data-duration');
182
    new Collapse(item,options);
183
  }
184

    
185
  return Collapse;
186

    
187
});
(6-6/17)