bootstrap-native.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Native Javascript for Bootstrap 3 v1.1.0 | © dnp_theme | MIT-License
  2. (function(root, factory) {
  3. if (typeof define === 'function' && define.amd) {
  4. // AMD support:
  5. define([], factory);
  6. } else if (typeof module === 'object' && module.exports) {
  7. // CommonJS-like:
  8. module.exports = factory();
  9. } else {
  10. // Browser globals (root is window)
  11. var bsn = factory();
  12. root.Affix = bsn.Affix;
  13. root.Alert = bsn.Alert;
  14. root.Button = bsn.Button;
  15. root.Carousel = bsn.Carousel;
  16. root.Collapse = bsn.Collapse;
  17. root.Dropdown = bsn.Dropdown;
  18. root.Modal = bsn.Modal;
  19. root.Popover = bsn.Popover;
  20. root.ScrollSpy = bsn.ScrollSpy;
  21. root.Tab = bsn.Tab;
  22. root.Tooltip = bsn.Tooltip;
  23. }
  24. }(this, function() {
  25. // Native Javascript for Bootstrap 3 | Internal Utility Functions
  26. // by dnp_theme
  27. var addClass = function(el, c) { // where modern browsers fail, use classList
  28. if (el.classList) {
  29. el.classList.add(c);
  30. } else {
  31. el.className += ' ' + c;
  32. el.offsetWidth;
  33. }
  34. },
  35. removeClass = function(el, c) {
  36. if (el.classList) {
  37. el.classList.remove(c);
  38. } else {
  39. el.className = el.className.replace(c, '').replace(/^\s+|\s+$/g, '');
  40. }
  41. },
  42. isIE = (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) ? parseFloat(RegExp.$1) : false,
  43. getClosest = function(el, s) { //el is the element and s the selector of the closest item to find
  44. // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
  45. var f = s.charAt(0);
  46. for (; el && el !== document; el = el.parentNode) { // Get closest match
  47. if (f === '.') { // If selector is a class
  48. if (document.querySelector(s) !== undefined) {
  49. return el;
  50. }
  51. }
  52. if (f === '#') { // If selector is an ID
  53. if (el.id === s.substr(1)) {
  54. return el;
  55. }
  56. }
  57. }
  58. return false;
  59. },
  60. // tooltip / popover stuff
  61. isElementInViewport = function(t) { // check if this.tooltip is in viewport
  62. var r = t.getBoundingClientRect();
  63. return (
  64. r.top >= 0 && r.left >= 0 && r.bottom <= (window.innerHeight || document.documentElement.clientHeight) && r.right <= (window.innerWidth || document.documentElement.clientWidth))
  65. },
  66. getScroll = function() { // also Affix and scrollSpy uses it
  67. return {
  68. y: window.pageYOffset || document.documentElement.scrollTop,
  69. x: window.pageXOffset || document.documentElement.scrollLeft
  70. }
  71. },
  72. mouseHover = ('onmouseleave' in document) ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout'],
  73. tipPositions = /\b(top|bottom|left|top)+/;
  74. // Native Javascript for Bootstrap 3 | Collapse
  75. // by dnp_theme
  76. // COLLAPSE DEFINITION
  77. // ===================
  78. var Collapse = function(element, options) {
  79. options = options || {};
  80. this.btn = typeof element === 'object' ? element : document.querySelector(element);
  81. this.accordion = null;
  82. this.collapse = null;
  83. this.duration = 300; // default collapse transition duration
  84. this.options = {};
  85. this.options.duration = (isIE && isIE < 10) ? 0 : (options.duration || this.duration);
  86. var self = this;
  87. var getOuterHeight = function(el) {
  88. var s = el && (el.currentStyle || window.getComputedStyle(el)),
  89. // the getComputedStyle polyfill would do this for us, but we want to make sure it does
  90. btp = /px/.test(s.borderTopWidth) ? Math.round(s.borderTopWidth.replace('px', '')) : 0,
  91. mtp = /px/.test(s.marginTop) ? Math.round(s.marginTop.replace('px', '')) : 0,
  92. mbp = /px/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('px', '')) : 0,
  93. mte = /em/.test(s.marginTop) ? Math.round(s.marginTop.replace('em', '') * parseInt(s.fontSize)) : 0,
  94. mbe = /em/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('em', '') * parseInt(s.fontSize)) : 0;
  95. return el.clientHeight + parseInt(btp) + parseInt(mtp) + parseInt(mbp) + parseInt(mte) + parseInt(mbe); //we need an accurate margin value
  96. };
  97. this.toggle = function(e) {
  98. e.preventDefault();
  99. if (!/\bin/.test(self.collapse.className)) {
  100. self.open();
  101. } else {
  102. self.close();
  103. }
  104. };
  105. this.close = function() {
  106. this._close(this.collapse);
  107. addClass(this.btn, 'collapsed');
  108. };
  109. this.open = function() {
  110. this._open(this.collapse);
  111. removeClass(this.btn, 'collapsed');
  112. if (this.accordion !== null) {
  113. var active = this.accordion.querySelectorAll('.collapse.in'),
  114. al = active.length,
  115. i = 0;
  116. for (i; i < al; i++) {
  117. if (active[i] !== this.collapse) this._close(active[i]);
  118. }
  119. }
  120. };
  121. this._open = function(c) {
  122. this.removeEvent();
  123. addClass(c, 'in');
  124. c.setAttribute('aria-expanded', 'true');
  125. addClass(c, 'collapsing');
  126. setTimeout(function() {
  127. c.style.height = self.getMaxHeight(c) + 'px'
  128. c.style.overflowY = 'hidden';
  129. }, 0);
  130. setTimeout(function() {
  131. c.style.height = '';
  132. c.style.overflowY = '';
  133. removeClass(c, 'collapsing');
  134. self.addEvent();
  135. }, this.options.duration);
  136. };
  137. this._close = function(c) {
  138. this.removeEvent();
  139. c.setAttribute('aria-expanded', 'false');
  140. c.style.height = this.getMaxHeight(c) + 'px'
  141. setTimeout(function() {
  142. c.style.height = '0px';
  143. c.style.overflowY = 'hidden';
  144. addClass(c, 'collapsing');
  145. }, 0);
  146. setTimeout(function() {
  147. removeClass(c, 'collapsing');
  148. removeClass(c, 'in');
  149. c.style.overflowY = '';
  150. c.style.height = '';
  151. self.addEvent();
  152. }, this.options.duration);
  153. };
  154. this.getMaxHeight = function(l) { // get collapse trueHeight and border
  155. var h = 0;
  156. for (var k = 0, ll = l.children.length; k < ll; k++) {
  157. h += getOuterHeight(l.children[k]);
  158. }
  159. return h;
  160. };
  161. this.removeEvent = function() {
  162. this.btn.removeEventListener('click', this.toggle, false);
  163. };
  164. this.addEvent = function() {
  165. this.btn.addEventListener('click', this.toggle, false);
  166. };
  167. this.getTarget = function() {
  168. var t = this.btn,
  169. h = t.href && t.getAttribute('href').replace('#', ''),
  170. d = t.getAttribute('data-target') && (t.getAttribute('data-target')),
  171. id = h || (d && /#/.test(d)) && d.replace('#', ''),
  172. cl = (d && d.charAt(0) === '.') && d,
  173. //the navbar collapse trigger targets a class
  174. c = id && document.getElementById(id) || cl && document.querySelector(cl);
  175. return c;
  176. };
  177. // init
  178. this.addEvent();
  179. this.collapse = this.getTarget();
  180. this.accordion = this.btn.getAttribute('data-parent') && getClosest(this.btn, this.btn.getAttribute('data-parent'));
  181. };
  182. // COLLAPSE DATA API
  183. // =================
  184. var Collapses = document.querySelectorAll('[data-toggle="collapse"]');
  185. for (var o = 0, cll = Collapses.length; o < cll; o++) {
  186. var collapse = Collapses[o],
  187. options = {};
  188. options.duration = collapse.getAttribute('data-duration');
  189. new Collapse(collapse, options);
  190. }
  191. // Native Javascript for Bootstrap 3 | Tab
  192. // by dnp_theme
  193. // TAB DEFINITION
  194. // ===================
  195. var Tab = function( element,options ) {
  196. options = options || {};
  197. this.tab = typeof element === 'object' ? element : document.querySelector(element);
  198. this.tabs = this.tab.parentNode.parentNode;
  199. this.dropdown = this.tabs.querySelector('.dropdown');
  200. if ( /\bdropdown-menu/.test(this.tabs.className) ) {
  201. this.dropdown = this.tabs.parentNode;
  202. this.tabs = this.tabs.parentNode.parentNode;
  203. }
  204. this.options = options;
  205. // default tab transition duration
  206. this.duration = 150;
  207. this.options.duration = (isIE && isIE < 10) ? 0 : (options.duration || this.duration);
  208. var self = this;
  209. this.handle = function(e) {
  210. e = e || window.e; e.preventDefault();
  211. var next = e.target; //the tab we clicked is now the next tab
  212. var nextContent = document.getElementById(next.getAttribute('href').replace('#','')); //this is the actual object, the next tab content to activate
  213. // get current active tab and content
  214. var activeTab = self.getActiveTab();
  215. var activeContent = self.getActiveContent();
  216. if ( !/\bactive/.test(next.parentNode.className) ) {
  217. // toggle "active" class name
  218. removeClass(activeTab,'active');
  219. addClass(next.parentNode,'active');
  220. // handle dropdown menu "active" class name
  221. if ( self.dropdown ) {
  222. if ( !(/\bdropdown-menu/.test(self.tab.parentNode.parentNode.className)) ) {
  223. if (/\bactive/.test(self.dropdown.className)) removeClass(self.dropdown,'active');
  224. } else {
  225. if (!/\bactive/.test(self.dropdown.className)) addClass(self.dropdown,'active');
  226. }
  227. }
  228. //1. hide current active content first
  229. removeClass(activeContent,'in');
  230. setTimeout(function() {
  231. //2. toggle current active content from view
  232. removeClass(activeContent,'active');
  233. addClass(nextContent,'active');
  234. }, self.options.duration);
  235. setTimeout(function() {
  236. //3. show next active content
  237. addClass(nextContent,'in');
  238. }, self.options.duration*2);
  239. }
  240. };
  241. this.getActiveTab = function() {
  242. var activeTabs = this.tabs.querySelectorAll('.active');
  243. if ( activeTabs.length === 1 && !/\bdropdown/.test(activeTabs[0].className) ) {
  244. return activeTabs[0]
  245. } else if ( activeTabs.length > 1 ) {
  246. return activeTabs[activeTabs.length-1]
  247. }
  248. };
  249. this.getActiveContent = function() {
  250. var active = this.getActiveTab().getElementsByTagName('A')[0].getAttribute('href').replace('#','');
  251. return active && document.getElementById(active)
  252. };
  253. // init
  254. this.tab.addEventListener('click', this.handle, false);
  255. };
  256. // TAB DATA API
  257. // =================
  258. var Tabs = document.querySelectorAll("[data-toggle='tab'], [data-toggle='pill']");
  259. for ( var tb = 0, tbl = Tabs.length; tb<tbl; tb++ ) {
  260. var tab = Tabs[tb], options = {};
  261. options.duration = tab.getAttribute('data-duration') && tab.getAttribute('data-duration') || false;
  262. new Tab(tab,options);
  263. }
  264. return {
  265. Tab: Tab,
  266. Collapse: Collapse
  267. };
  268. }));