bootstrap-native-v4.js 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009
  1. // Native Javascript for Bootstrap 4 v2.0.27 | © 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.Alert = bsn.Alert;
  13. root.Button = bsn.Button;
  14. root.Carousel = bsn.Carousel;
  15. root.Collapse = bsn.Collapse;
  16. root.Dropdown = bsn.Dropdown;
  17. root.Modal = bsn.Modal;
  18. root.Popover = bsn.Popover;
  19. root.ScrollSpy = bsn.ScrollSpy;
  20. root.Tab = bsn.Tab;
  21. root.Toast = bsn.Toast;
  22. root.Tooltip = bsn.Tooltip;
  23. }
  24. }(this, function () {
  25. /* Native Javascript for Bootstrap 4 | Internal Utility Functions
  26. ----------------------------------------------------------------*/
  27. "use strict";
  28. // globals
  29. var globalObject = typeof global !== 'undefined' ? global : this||window,
  30. DOC = document, HTML = DOC.documentElement, body = 'body', // allow the library to be used in <head>
  31. // Native Javascript for Bootstrap Global Object
  32. BSN = globalObject.BSN = {},
  33. supports = BSN.supports = [],
  34. // function toggle attributes
  35. dataToggle = 'data-toggle',
  36. dataDismiss = 'data-dismiss',
  37. dataSpy = 'data-spy',
  38. dataRide = 'data-ride',
  39. // components
  40. stringAlert = 'Alert',
  41. stringButton = 'Button',
  42. stringCarousel = 'Carousel',
  43. stringCollapse = 'Collapse',
  44. stringDropdown = 'Dropdown',
  45. stringModal = 'Modal',
  46. stringPopover = 'Popover',
  47. stringScrollSpy = 'ScrollSpy',
  48. stringTab = 'Tab',
  49. stringTooltip = 'Tooltip',
  50. stringToast = 'Toast',
  51. // options DATA API
  52. dataAutohide = 'data-autohide',
  53. databackdrop = 'data-backdrop',
  54. dataKeyboard = 'data-keyboard',
  55. dataTarget = 'data-target',
  56. dataInterval = 'data-interval',
  57. dataHeight = 'data-height',
  58. dataPause = 'data-pause',
  59. dataTitle = 'data-title',
  60. dataOriginalTitle = 'data-original-title',
  61. dataDismissible = 'data-dismissible',
  62. dataTrigger = 'data-trigger',
  63. dataAnimation = 'data-animation',
  64. dataContainer = 'data-container',
  65. dataPlacement = 'data-placement',
  66. dataDelay = 'data-delay',
  67. // option keys
  68. backdrop = 'backdrop', keyboard = 'keyboard', delay = 'delay',
  69. content = 'content', target = 'target', currentTarget = 'currentTarget',
  70. interval = 'interval', pause = 'pause', animation = 'animation',
  71. placement = 'placement', container = 'container',
  72. // box model
  73. offsetTop = 'offsetTop', offsetBottom = 'offsetBottom',
  74. offsetLeft = 'offsetLeft',
  75. scrollTop = 'scrollTop', scrollLeft = 'scrollLeft',
  76. clientWidth = 'clientWidth', clientHeight = 'clientHeight',
  77. offsetWidth = 'offsetWidth', offsetHeight = 'offsetHeight',
  78. innerWidth = 'innerWidth', innerHeight = 'innerHeight',
  79. scrollHeight = 'scrollHeight', scrollWidth = 'scrollWidth',
  80. height = 'height',
  81. // aria
  82. ariaExpanded = 'aria-expanded',
  83. ariaHidden = 'aria-hidden',
  84. ariaSelected = 'aria-selected',
  85. // event names
  86. clickEvent = 'click',
  87. focusEvent = 'focus',
  88. hoverEvent = 'hover',
  89. keydownEvent = 'keydown',
  90. keyupEvent = 'keyup',
  91. resizeEvent = 'resize', // passive
  92. scrollEvent = 'scroll', // passive
  93. mouseHover = ('onmouseleave' in DOC) ? [ 'mouseenter', 'mouseleave'] : [ 'mouseover', 'mouseout' ],
  94. // touch since 2.0.26
  95. touchEvents = { start: 'touchstart', end: 'touchend', move:'touchmove' }, // passive
  96. // originalEvents
  97. showEvent = 'show',
  98. shownEvent = 'shown',
  99. hideEvent = 'hide',
  100. hiddenEvent = 'hidden',
  101. closeEvent = 'close',
  102. closedEvent = 'closed',
  103. slidEvent = 'slid',
  104. slideEvent = 'slide',
  105. changeEvent = 'change',
  106. // other
  107. getAttribute = 'getAttribute',
  108. setAttribute = 'setAttribute',
  109. hasAttribute = 'hasAttribute',
  110. createElement = 'createElement',
  111. appendChild = 'appendChild',
  112. innerHTML = 'innerHTML',
  113. getElementsByTagName = 'getElementsByTagName',
  114. preventDefault = 'preventDefault',
  115. getBoundingClientRect = 'getBoundingClientRect',
  116. querySelectorAll = 'querySelectorAll',
  117. getElementsByCLASSNAME = 'getElementsByClassName',
  118. getComputedStyle = 'getComputedStyle',
  119. indexOf = 'indexOf',
  120. parentNode = 'parentNode',
  121. length = 'length',
  122. toLowerCase = 'toLowerCase',
  123. Transition = 'Transition',
  124. Duration = 'Duration',
  125. Webkit = 'Webkit',
  126. style = 'style',
  127. push = 'push',
  128. tabindex = 'tabindex',
  129. contains = 'contains',
  130. active = 'active',
  131. showClass = 'show',
  132. collapsing = 'collapsing',
  133. disabled = 'disabled',
  134. loading = 'loading',
  135. left = 'left',
  136. right = 'right',
  137. top = 'top',
  138. bottom = 'bottom',
  139. // tooltip / popover
  140. tipPositions = /\b(top|bottom|left|right)+/,
  141. // modal
  142. modalOverlay = 0,
  143. fixedTop = 'fixed-top',
  144. fixedBottom = 'fixed-bottom',
  145. // transitionEnd since 2.0.4
  146. supportTransitions = Webkit+Transition in HTML[style] || Transition[toLowerCase]() in HTML[style],
  147. transitionEndEvent = Webkit+Transition in HTML[style] ? Webkit[toLowerCase]()+Transition+'End' : Transition[toLowerCase]()+'end',
  148. transitionDuration = Webkit+Duration in HTML[style] ? Webkit[toLowerCase]()+Transition+Duration : Transition[toLowerCase]()+Duration,
  149. // set new focus element since 2.0.3
  150. setFocus = function(element){
  151. element.focus ? element.focus() : element.setActive();
  152. },
  153. // class manipulation, since 2.0.0 requires polyfill.js
  154. addClass = function(element,classNAME) {
  155. element.classList.add(classNAME);
  156. },
  157. removeClass = function(element,classNAME) {
  158. element.classList.remove(classNAME);
  159. },
  160. hasClass = function(element,classNAME){ // since 2.0.0
  161. return element.classList[contains](classNAME);
  162. },
  163. // selection methods
  164. getElementsByClassName = function(element,classNAME) { // returns Array
  165. return [].slice.call(element[getElementsByCLASSNAME]( classNAME ));
  166. },
  167. queryElement = function (selector, parent) {
  168. var lookUp = parent ? parent : DOC;
  169. return typeof selector === 'object' ? selector : lookUp.querySelector(selector);
  170. },
  171. getClosest = function (element, selector) { //element is the element and selector is for the closest parent element to find
  172. // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
  173. var firstChar = selector.charAt(0), selectorSubstring = selector.substr(1);
  174. if ( firstChar === '.' ) {// If selector is a class
  175. for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match
  176. if ( queryElement(selector,element[parentNode]) !== null && hasClass(element,selectorSubstring) ) { return element; }
  177. }
  178. } else if ( firstChar === '#' ) { // If selector is an ID
  179. for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match
  180. if ( element.id === selectorSubstring ) { return element; }
  181. }
  182. }
  183. return false;
  184. },
  185. // event attach jQuery style / trigger since 1.2.0
  186. on = function (element, event, handler, options) {
  187. options = options || false;
  188. element.addEventListener(event, handler, options);
  189. },
  190. off = function(element, event, handler, options) {
  191. options = options || false;
  192. element.removeEventListener(event, handler, options);
  193. },
  194. one = function (element, event, handler, options) { // one since 2.0.4
  195. on(element, event, function handlerWrapper(e){
  196. handler(e);
  197. off(element, event, handlerWrapper, options);
  198. }, options);
  199. },
  200. // determine support for passive events
  201. supportPassive = (function(){
  202. // Test via a getter in the options object to see if the passive property is accessed
  203. var result = false;
  204. try {
  205. var opts = Object.defineProperty({}, 'passive', {
  206. get: function() {
  207. result = true;
  208. }
  209. });
  210. one(globalObject, 'testPassive', null, opts);
  211. } catch (e) {}
  212. return result;
  213. }()),
  214. // event options
  215. // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
  216. passiveHandler = supportPassive ? { passive: true } : false,
  217. // transitions
  218. getTransitionDurationFromElement = function(element) {
  219. var duration = supportTransitions ? globalObject[getComputedStyle](element)[transitionDuration] : 0;
  220. duration = parseFloat(duration);
  221. duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0;
  222. return duration; // we take a short offset to make sure we fire on the next frame after animation
  223. },
  224. emulateTransitionEnd = function(element,handler){ // emulateTransitionEnd since 2.0.4
  225. var called = 0, duration = getTransitionDurationFromElement(element);
  226. duration ? one(element, transitionEndEvent, function(e){ !called && handler(e), called = 1; })
  227. : setTimeout(function() { !called && handler(), called = 1; }, 17);
  228. },
  229. bootstrapCustomEvent = function (eventName, componentName, related) {
  230. var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName);
  231. OriginalCustomEvent.relatedTarget = related;
  232. this.dispatchEvent(OriginalCustomEvent);
  233. },
  234. // tooltip / popover stuff
  235. getScroll = function() { // also Affix and ScrollSpy uses it
  236. return {
  237. y : globalObject.pageYOffset || HTML[scrollTop],
  238. x : globalObject.pageXOffset || HTML[scrollLeft]
  239. }
  240. },
  241. styleTip = function(link,element,position,parent) { // both popovers and tooltips (target,tooltip,placement,elementToAppendTo)
  242. var elementDimensions = { w : element[offsetWidth], h: element[offsetHeight] },
  243. windowWidth = (HTML[clientWidth] || DOC[body][clientWidth]),
  244. windowHeight = (HTML[clientHeight] || DOC[body][clientHeight]),
  245. rect = link[getBoundingClientRect](),
  246. scroll = parent === DOC[body] ? getScroll() : { x: parent[offsetLeft] + parent[scrollLeft], y: parent[offsetTop] + parent[scrollTop] },
  247. linkDimensions = { w: rect[right] - rect[left], h: rect[bottom] - rect[top] },
  248. isPopover = hasClass(element,'popover'),
  249. topPosition, leftPosition,
  250. arrow = queryElement('.arrow',element),
  251. arrowTop, arrowLeft, arrowWidth, arrowHeight,
  252. halfTopExceed = rect[top] + linkDimensions.h/2 - elementDimensions.h/2 < 0,
  253. halfLeftExceed = rect[left] + linkDimensions.w/2 - elementDimensions.w/2 < 0,
  254. halfRightExceed = rect[left] + elementDimensions.w/2 + linkDimensions.w/2 >= windowWidth,
  255. halfBottomExceed = rect[top] + elementDimensions.h/2 + linkDimensions.h/2 >= windowHeight,
  256. topExceed = rect[top] - elementDimensions.h < 0,
  257. leftExceed = rect[left] - elementDimensions.w < 0,
  258. bottomExceed = rect[top] + elementDimensions.h + linkDimensions.h >= windowHeight,
  259. rightExceed = rect[left] + elementDimensions.w + linkDimensions.w >= windowWidth;
  260. // recompute position
  261. position = (position === left || position === right) && leftExceed && rightExceed ? top : position; // first, when both left and right limits are exceeded, we fall back to top|bottom
  262. position = position === top && topExceed ? bottom : position;
  263. position = position === bottom && bottomExceed ? top : position;
  264. position = position === left && leftExceed ? right : position;
  265. position = position === right && rightExceed ? left : position;
  266. // update tooltip/popover class
  267. element.className[indexOf](position) === -1 && (element.className = element.className.replace(tipPositions,position));
  268. // we check the computed width & height and update here
  269. arrowWidth = arrow[offsetWidth]; arrowHeight = arrow[offsetHeight];
  270. // apply styling to tooltip or popover
  271. if ( position === left || position === right ) { // secondary|side positions
  272. if ( position === left ) { // LEFT
  273. leftPosition = rect[left] + scroll.x - elementDimensions.w - ( isPopover ? arrowWidth : 0 );
  274. } else { // RIGHT
  275. leftPosition = rect[left] + scroll.x + linkDimensions.w;
  276. }
  277. // adjust top and arrow
  278. if (halfTopExceed) {
  279. topPosition = rect[top] + scroll.y;
  280. arrowTop = linkDimensions.h/2 - arrowWidth;
  281. } else if (halfBottomExceed) {
  282. topPosition = rect[top] + scroll.y - elementDimensions.h + linkDimensions.h;
  283. arrowTop = elementDimensions.h - linkDimensions.h/2 - arrowWidth;
  284. } else {
  285. topPosition = rect[top] + scroll.y - elementDimensions.h/2 + linkDimensions.h/2;
  286. arrowTop = elementDimensions.h/2 - (isPopover ? arrowHeight*0.9 : arrowHeight/2);
  287. }
  288. } else if ( position === top || position === bottom ) { // primary|vertical positions
  289. if ( position === top) { // TOP
  290. topPosition = rect[top] + scroll.y - elementDimensions.h - ( isPopover ? arrowHeight : 0 );
  291. } else { // BOTTOM
  292. topPosition = rect[top] + scroll.y + linkDimensions.h;
  293. }
  294. // adjust left | right and also the arrow
  295. if (halfLeftExceed) {
  296. leftPosition = 0;
  297. arrowLeft = rect[left] + linkDimensions.w/2 - arrowWidth;
  298. } else if (halfRightExceed) {
  299. leftPosition = windowWidth - elementDimensions.w*1.01;
  300. arrowLeft = elementDimensions.w - ( windowWidth - rect[left] ) + linkDimensions.w/2 - arrowWidth/2;
  301. } else {
  302. leftPosition = rect[left] + scroll.x - elementDimensions.w/2 + linkDimensions.w/2;
  303. arrowLeft = elementDimensions.w/2 - ( isPopover ? arrowWidth : arrowWidth/2 );
  304. }
  305. }
  306. // apply style to tooltip/popover and its arrow
  307. element[style][top] = topPosition + 'px';
  308. element[style][left] = leftPosition + 'px';
  309. arrowTop && (arrow[style][top] = arrowTop + 'px');
  310. arrowLeft && (arrow[style][left] = arrowLeft + 'px');
  311. };
  312. BSN.version = '2.0.27';
  313. /* Native Javascript for Bootstrap 4 | Alert
  314. -------------------------------------------*/
  315. // ALERT DEFINITION
  316. // ================
  317. var Alert = function( element ) {
  318. // initialization element
  319. element = queryElement(element);
  320. // bind, target alert, duration and stuff
  321. var self = this, component = 'alert',
  322. alert = getClosest(element,'.'+component),
  323. triggerHandler = function(){ hasClass(alert,'fade') ? emulateTransitionEnd(alert,transitionEndHandler) : transitionEndHandler(); },
  324. // handlers
  325. clickHandler = function(e){
  326. alert = getClosest(e[target],'.'+component);
  327. element = queryElement('['+dataDismiss+'="'+component+'"]',alert);
  328. element && alert && (element === e[target] || element[contains](e[target])) && self.close();
  329. },
  330. transitionEndHandler = function(){
  331. bootstrapCustomEvent.call(alert, closedEvent, component);
  332. off(element, clickEvent, clickHandler); // detach it's listener
  333. alert[parentNode].removeChild(alert);
  334. };
  335. // public method
  336. this.close = function() {
  337. if ( alert && element && hasClass(alert,showClass) ) {
  338. bootstrapCustomEvent.call(alert, closeEvent, component);
  339. removeClass(alert,showClass);
  340. alert && triggerHandler();
  341. }
  342. };
  343. // init
  344. if ( !(stringAlert in element ) ) { // prevent adding event handlers twice
  345. on(element, clickEvent, clickHandler);
  346. }
  347. element[stringAlert] = self;
  348. };
  349. // ALERT DATA API
  350. // ==============
  351. supports[push]([stringAlert, Alert, '['+dataDismiss+'="alert"]']);
  352. /* Native Javascript for Bootstrap 4 | Button
  353. ---------------------------------------------*/
  354. // BUTTON DEFINITION
  355. // ===================
  356. var Button = function( element ) {
  357. // initialization element
  358. element = queryElement(element);
  359. // constant
  360. var toggled = false, // toggled makes sure to prevent triggering twice the change.bs.button events
  361. // strings
  362. component = 'button',
  363. checked = 'checked',
  364. LABEL = 'LABEL',
  365. INPUT = 'INPUT',
  366. // private methods
  367. keyHandler = function(e){
  368. var key = e.which || e.keyCode;
  369. key === 32 && e[target] === DOC.activeElement && toggle(e);
  370. },
  371. preventScroll = function(e){
  372. var key = e.which || e.keyCode;
  373. key === 32 && e[preventDefault]();
  374. },
  375. toggle = function(e) {
  376. var label = e[target].tagName === LABEL ? e[target] : e[target][parentNode].tagName === LABEL ? e[target][parentNode] : null; // the .btn label
  377. if ( !label ) return; //react if a label or its immediate child is clicked
  378. var labels = getElementsByClassName(label[parentNode],'btn'), // all the button group buttons
  379. input = label[getElementsByTagName](INPUT)[0];
  380. if ( !input ) return; // return if no input found
  381. // manage the dom manipulation
  382. if ( input.type === 'checkbox' ) { //checkboxes
  383. if ( !input[checked] ) {
  384. addClass(label,active);
  385. input[getAttribute](checked);
  386. input[setAttribute](checked,checked);
  387. input[checked] = true;
  388. } else {
  389. removeClass(label,active);
  390. input[getAttribute](checked);
  391. input.removeAttribute(checked);
  392. input[checked] = false;
  393. }
  394. if (!toggled) { // prevent triggering the event twice
  395. toggled = true;
  396. bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input
  397. bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group
  398. }
  399. }
  400. if ( input.type === 'radio' && !toggled ) { // radio buttons
  401. // don't trigger if already active (the OR condition is a hack to check if the buttons were selected with key press and NOT mouse click)
  402. if ( !input[checked] || (e.screenX === 0 && e.screenY == 0) ) {
  403. addClass(label,active);
  404. addClass(label,focusEvent);
  405. input[setAttribute](checked,checked);
  406. input[checked] = true;
  407. bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input
  408. bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group
  409. toggled = true;
  410. for (var i = 0, ll = labels[length]; i<ll; i++) {
  411. var otherLabel = labels[i], otherInput = otherLabel[getElementsByTagName](INPUT)[0];
  412. if ( otherLabel !== label && hasClass(otherLabel,active) ) {
  413. removeClass(otherLabel,active);
  414. otherInput.removeAttribute(checked);
  415. otherInput[checked] = false;
  416. bootstrapCustomEvent.call(otherInput, changeEvent, component); // trigger the change
  417. }
  418. }
  419. }
  420. }
  421. setTimeout( function() { toggled = false; }, 50 );
  422. },
  423. focusHandler = function(e) {
  424. addClass(e[target][parentNode],focusEvent);
  425. },
  426. blurHandler = function(e) {
  427. removeClass(e[target][parentNode],focusEvent);
  428. };
  429. // init
  430. if ( !( stringButton in element ) ) { // prevent adding event handlers twice
  431. on( element, clickEvent, toggle );
  432. on( element, keyupEvent, keyHandler ), on( element, keydownEvent, preventScroll );
  433. var allBtns = getElementsByClassName(element, 'btn');
  434. for (var i=0; i<allBtns.length; i++) {
  435. var input = allBtns[i][getElementsByTagName](INPUT)[0];
  436. on( input, focusEvent, focusHandler), on( input, 'blur', blurHandler);
  437. }
  438. }
  439. // activate items on load
  440. var labelsToACtivate = getElementsByClassName(element, 'btn'), lbll = labelsToACtivate[length];
  441. for (var i=0; i<lbll; i++) {
  442. !hasClass(labelsToACtivate[i],active) && queryElement('input:checked',labelsToACtivate[i])
  443. && addClass(labelsToACtivate[i],active);
  444. }
  445. element[stringButton] = this;
  446. };
  447. // BUTTON DATA API
  448. // =================
  449. supports[push]( [ stringButton, Button, '['+dataToggle+'="buttons"]' ] );
  450. /* Native Javascript for Bootstrap 4 | Carousel
  451. ----------------------------------------------*/
  452. // CAROUSEL DEFINITION
  453. // ===================
  454. var Carousel = function( element, options ) {
  455. // initialization element
  456. element = queryElement( element );
  457. // set options
  458. options = options || {};
  459. // DATA API
  460. var intervalAttribute = element[getAttribute](dataInterval),
  461. intervalOption = options[interval],
  462. intervalData = intervalAttribute === 'false' ? 0 : parseInt(intervalAttribute),
  463. pauseData = element[getAttribute](dataPause) === hoverEvent || false,
  464. keyboardData = element[getAttribute](dataKeyboard) === 'true' || false,
  465. // strings
  466. component = 'carousel',
  467. paused = 'paused',
  468. direction = 'direction',
  469. carouselItem = 'carousel-item',
  470. dataSlideTo = 'data-slide-to';
  471. this[keyboard] = options[keyboard] === true || keyboardData;
  472. this[pause] = (options[pause] === hoverEvent || pauseData) ? hoverEvent : false; // false / hover
  473. this[interval] = typeof intervalOption === 'number' ? intervalOption
  474. : intervalOption === false || intervalData === 0 || intervalData === false ? 0
  475. : isNaN(intervalData) ? 5000 // bootstrap carousel default interval
  476. : intervalData;
  477. // bind, event targets
  478. var self = this, index = element.index = 0, timer = element.timer = 0,
  479. isSliding = false, // isSliding prevents click event handlers when animation is running
  480. isTouch = false, startXPosition = null, currentXPosition = null, endXPosition = null, // touch and event coordinates
  481. slides = getElementsByClassName(element,carouselItem), total = slides[length],
  482. slideDirection = this[direction] = left,
  483. leftArrow = getElementsByClassName(element,component+'-control-prev')[0],
  484. rightArrow = getElementsByClassName(element,component+'-control-next')[0],
  485. indicator = queryElement( '.'+component+'-indicators', element ),
  486. indicators = indicator && indicator[getElementsByTagName]( "LI" ) || [];
  487. // invalidate when not enough items
  488. if (total < 2) { return; }
  489. // handlers
  490. var pauseHandler = function () {
  491. if ( self[interval] !==false && !hasClass(element,paused) ) {
  492. addClass(element,paused);
  493. !isSliding && ( clearInterval(timer), timer = null );
  494. }
  495. },
  496. resumeHandler = function() {
  497. if ( self[interval] !== false && hasClass(element,paused) ) {
  498. removeClass(element,paused);
  499. !isSliding && ( clearInterval(timer), timer = null );
  500. !isSliding && self.cycle();
  501. }
  502. },
  503. indicatorHandler = function(e) {
  504. e[preventDefault]();
  505. if (isSliding) return;
  506. var eventTarget = e[target]; // event target | the current active item
  507. if ( eventTarget && !hasClass(eventTarget,active) && eventTarget[getAttribute](dataSlideTo) ) {
  508. index = parseInt( eventTarget[getAttribute](dataSlideTo), 10 );
  509. } else { return false; }
  510. self.slideTo( index ); //Do the slide
  511. },
  512. controlsHandler = function (e) {
  513. e[preventDefault]();
  514. if (isSliding) return;
  515. var eventTarget = e.currentTarget || e.srcElement;
  516. if ( eventTarget === rightArrow ) {
  517. index++;
  518. } else if ( eventTarget === leftArrow ) {
  519. index--;
  520. }
  521. self.slideTo( index ); //Do the slide
  522. },
  523. keyHandler = function (e) {
  524. if (isSliding) return;
  525. switch (e.which) {
  526. case 39:
  527. index++;
  528. break;
  529. case 37:
  530. index--;
  531. break;
  532. default: return;
  533. }
  534. self.slideTo( index ); //Do the slide
  535. },
  536. // touch events
  537. toggleTouchEvents = function(toggle){
  538. toggle( element, touchEvents.move, touchMoveHandler, passiveHandler );
  539. toggle( element, touchEvents.end, touchEndHandler, passiveHandler );
  540. },
  541. touchDownHandler = function(e) {
  542. if ( isTouch ) { return; }
  543. startXPosition = parseInt(e.touches[0].pageX);
  544. if ( element.contains(e[target]) ) {
  545. isTouch = true;
  546. toggleTouchEvents(on);
  547. }
  548. },
  549. touchMoveHandler = function(e) {
  550. if ( !isTouch ) { e.preventDefault(); return; }
  551. currentXPosition = parseInt(e.touches[0].pageX);
  552. //cancel touch if more than one touches detected
  553. if ( e.type === 'touchmove' && e.touches[length] > 1 ) {
  554. e.preventDefault();
  555. return false;
  556. }
  557. },
  558. touchEndHandler = function(e) {
  559. if ( !isTouch || isSliding ) { return }
  560. endXPosition = currentXPosition || parseInt( e.touches[0].pageX );
  561. if ( isTouch ) {
  562. if ( (!element.contains(e[target]) || !element.contains(e.relatedTarget) ) && Math.abs(startXPosition - endXPosition) < 75 ) {
  563. return false;
  564. } else {
  565. if ( currentXPosition < startXPosition ) {
  566. index++;
  567. } else if ( currentXPosition > startXPosition ) {
  568. index--;
  569. }
  570. isTouch = false;
  571. self.slideTo(index);
  572. }
  573. toggleTouchEvents(off);
  574. }
  575. },
  576. // private methods
  577. isElementInScrollRange = function () {
  578. var rect = element[getBoundingClientRect](),
  579. viewportHeight = globalObject[innerHeight] || HTML[clientHeight]
  580. return rect[top] <= viewportHeight && rect[bottom] >= 0; // bottom && top
  581. },
  582. setActivePage = function( pageIndex ) { //indicators
  583. for ( var i = 0, icl = indicators[length]; i < icl; i++ ) {
  584. removeClass(indicators[i],active);
  585. }
  586. if (indicators[pageIndex]) addClass(indicators[pageIndex], active);
  587. };
  588. // public methods
  589. this.cycle = function() {
  590. if (timer) {
  591. clearInterval(timer);
  592. timer = null;
  593. }
  594. timer = setInterval(function() {
  595. isElementInScrollRange() && (index++, self.slideTo( index ) );
  596. }, this[interval]);
  597. };
  598. this.slideTo = function( next ) {
  599. if (isSliding) return; // when controled via methods, make sure to check again
  600. var activeItem = this.getActiveIndex(), // the current active
  601. orientation;
  602. // first return if we're on the same item #227
  603. if ( activeItem === next ) {
  604. return;
  605. // or determine slideDirection
  606. } else if ( (activeItem < next ) || (activeItem === 0 && next === total -1 ) ) {
  607. slideDirection = self[direction] = left; // next
  608. } else if ( (activeItem > next) || (activeItem === total - 1 && next === 0 ) ) {
  609. slideDirection = self[direction] = right; // prev
  610. }
  611. // find the right next index
  612. if ( next < 0 ) { next = total - 1; }
  613. else if ( next >= total ){ next = 0; }
  614. // update index
  615. index = next;
  616. orientation = slideDirection === left ? 'next' : 'prev'; //determine type
  617. bootstrapCustomEvent.call(element, slideEvent, component, slides[next]); // here we go with the slide
  618. isSliding = true;
  619. clearInterval(timer);
  620. timer = null;
  621. setActivePage( next );
  622. if ( supportTransitions && hasClass(element,'slide') ) {
  623. addClass(slides[next],carouselItem +'-'+ orientation);
  624. slides[next][offsetWidth];
  625. addClass(slides[next],carouselItem +'-'+ slideDirection);
  626. addClass(slides[activeItem],carouselItem +'-'+ slideDirection);
  627. emulateTransitionEnd(slides[next], function(e) {
  628. var timeout = e && e[target] !== slides[next] ? e.elapsedTime*1000+100 : 20;
  629. isSliding && setTimeout(function(){
  630. isSliding = false;
  631. addClass(slides[next],active);
  632. removeClass(slides[activeItem],active);
  633. removeClass(slides[next],carouselItem +'-'+ orientation);
  634. removeClass(slides[next],carouselItem +'-'+ slideDirection);
  635. removeClass(slides[activeItem],carouselItem +'-'+ slideDirection);
  636. bootstrapCustomEvent.call(element, slidEvent, component, slides[next]);
  637. if ( !DOC.hidden && self[interval] && !hasClass(element,paused) ) {
  638. self.cycle();
  639. }
  640. }, timeout);
  641. });
  642. } else {
  643. addClass(slides[next],active);
  644. slides[next][offsetWidth];
  645. removeClass(slides[activeItem],active);
  646. setTimeout(function() {
  647. isSliding = false;
  648. if ( self[interval] && !hasClass(element,paused) ) {
  649. self.cycle();
  650. }
  651. bootstrapCustomEvent.call(element, slidEvent, component, slides[next]);
  652. }, 100 );
  653. }
  654. };
  655. this.getActiveIndex = function () {
  656. return slides[indexOf](getElementsByClassName(element,carouselItem+' active')[0]) || 0;
  657. };
  658. // init
  659. if ( !(stringCarousel in element ) ) { // prevent adding event handlers twice
  660. if ( self[pause] && self[interval] ) {
  661. on( element, mouseHover[0], pauseHandler );
  662. on( element, mouseHover[1], resumeHandler );
  663. on( element, touchEvents.start, pauseHandler, passiveHandler );
  664. on( element, touchEvents.end, resumeHandler, passiveHandler );
  665. }
  666. slides[length] > 1 && on( element, touchEvents.start, touchDownHandler, passiveHandler );
  667. rightArrow && on( rightArrow, clickEvent, controlsHandler );
  668. leftArrow && on( leftArrow, clickEvent, controlsHandler );
  669. indicator && on( indicator, clickEvent, indicatorHandler );
  670. self[keyboard] && on( globalObject, keydownEvent, keyHandler );
  671. }
  672. if (self.getActiveIndex()<0) {
  673. slides[length] && addClass(slides[0],active);
  674. indicators[length] && setActivePage(0);
  675. }
  676. if ( self[interval] ){ self.cycle(); }
  677. element[stringCarousel] = self;
  678. };
  679. // CAROUSEL DATA API
  680. // =================
  681. supports[push]( [ stringCarousel, Carousel, '['+dataRide+'="carousel"]' ] );
  682. /* Native Javascript for Bootstrap 4 | Collapse
  683. -----------------------------------------------*/
  684. // COLLAPSE DEFINITION
  685. // ===================
  686. var Collapse = function( element, options ) {
  687. // initialization element
  688. element = queryElement(element);
  689. // set options
  690. options = options || {};
  691. // event targets and constants
  692. var accordion = null, collapse = null, self = this,
  693. accordionData = element[getAttribute]('data-parent'),
  694. activeCollapse, activeElement,
  695. // component strings
  696. component = 'collapse',
  697. collapsed = 'collapsed',
  698. isAnimating = 'isAnimating',
  699. // private methods
  700. openAction = function(collapseElement,toggle) {
  701. bootstrapCustomEvent.call(collapseElement, showEvent, component);
  702. collapseElement[isAnimating] = true;
  703. addClass(collapseElement,collapsing);
  704. removeClass(collapseElement,component);
  705. collapseElement[style][height] = collapseElement[scrollHeight] + 'px';
  706. emulateTransitionEnd(collapseElement, function() {
  707. collapseElement[isAnimating] = false;
  708. collapseElement[setAttribute](ariaExpanded,'true');
  709. toggle[setAttribute](ariaExpanded,'true');
  710. removeClass(collapseElement,collapsing);
  711. addClass(collapseElement, component);
  712. addClass(collapseElement,showClass);
  713. collapseElement[style][height] = '';
  714. bootstrapCustomEvent.call(collapseElement, shownEvent, component);
  715. });
  716. },
  717. closeAction = function(collapseElement,toggle) {
  718. bootstrapCustomEvent.call(collapseElement, hideEvent, component);
  719. collapseElement[isAnimating] = true;
  720. collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first
  721. removeClass(collapseElement,component);
  722. removeClass(collapseElement,showClass);
  723. addClass(collapseElement,collapsing);
  724. collapseElement[offsetWidth]; // force reflow to enable transition
  725. collapseElement[style][height] = '0px';
  726. emulateTransitionEnd(collapseElement, function() {
  727. collapseElement[isAnimating] = false;
  728. collapseElement[setAttribute](ariaExpanded,'false');
  729. toggle[setAttribute](ariaExpanded,'false');
  730. removeClass(collapseElement,collapsing);
  731. addClass(collapseElement,component);
  732. collapseElement[style][height] = '';
  733. bootstrapCustomEvent.call(collapseElement, hiddenEvent, component);
  734. });
  735. },
  736. getTarget = function() {
  737. var href = element.href && element[getAttribute]('href'),
  738. parent = element[getAttribute](dataTarget),
  739. id = href || ( parent && parent.charAt(0) === '#' ) && parent;
  740. return id && queryElement(id);
  741. };
  742. // public methods
  743. this.toggle = function(e) {
  744. e[preventDefault]();
  745. if (!hasClass(collapse,showClass)) { self.show(); }
  746. else { self.hide(); }
  747. };
  748. this.hide = function() {
  749. if ( collapse[isAnimating] ) return;
  750. closeAction(collapse,element);
  751. addClass(element,collapsed);
  752. };
  753. this.show = function() {
  754. if ( accordion ) {
  755. activeCollapse = queryElement('.'+component+'.'+showClass,accordion);
  756. activeElement = activeCollapse && (queryElement('['+dataTarget+'="#'+activeCollapse.id+'"]',accordion)
  757. || queryElement('[href="#'+activeCollapse.id+'"]',accordion) );
  758. }
  759. if ( !collapse[isAnimating] || activeCollapse && !activeCollapse[isAnimating] ) {
  760. if ( activeElement && activeCollapse !== collapse ) {
  761. closeAction(activeCollapse,activeElement);
  762. addClass(activeElement,collapsed);
  763. }
  764. openAction(collapse,element);
  765. removeClass(element,collapsed);
  766. }
  767. };
  768. // init
  769. if ( !(stringCollapse in element ) ) { // prevent adding event handlers twice
  770. on(element, clickEvent, self.toggle);
  771. }
  772. collapse = getTarget();
  773. collapse[isAnimating] = false; // when true it will prevent click handlers
  774. accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData);
  775. element[stringCollapse] = self;
  776. };
  777. // COLLAPSE DATA API
  778. // =================
  779. supports[push]( [ stringCollapse, Collapse, '['+dataToggle+'="collapse"]' ] );
  780. /* Native Javascript for Bootstrap 4 | Dropdown
  781. ----------------------------------------------*/
  782. // DROPDOWN DEFINITION
  783. // ===================
  784. var Dropdown = function( element, option ) {
  785. // initialization element
  786. element = queryElement(element);
  787. // set option
  788. this.persist = option === true || element[getAttribute]('data-persist') === 'true' || false;
  789. // constants, event targets, strings
  790. var self = this, children = 'children',
  791. parent = element[parentNode],
  792. component = 'dropdown', open = 'open',
  793. relatedTarget = null,
  794. menu = queryElement('.dropdown-menu', parent),
  795. menuItems = (function(){
  796. var set = menu[children], newSet = [];
  797. for ( var i=0; i<set[length]; i++ ){
  798. set[i][children][length] && (set[i][children][0].tagName === 'A' && newSet[push](set[i][children][0]));
  799. set[i].tagName === 'A' && newSet[push](set[i]);
  800. }
  801. return newSet;
  802. })(),
  803. // preventDefault on empty anchor links
  804. preventEmptyAnchor = function(anchor){
  805. (anchor.href && anchor.href.slice(-1) === '#' || anchor[parentNode] && anchor[parentNode].href
  806. && anchor[parentNode].href.slice(-1) === '#') && this[preventDefault]();
  807. },
  808. // toggle dismissible events
  809. toggleDismiss = function(){
  810. var type = element[open] ? on : off;
  811. type(DOC, clickEvent, dismissHandler);
  812. type(DOC, keydownEvent, preventScroll);
  813. type(DOC, keyupEvent, keyHandler);
  814. type(DOC, focusEvent, dismissHandler, true);
  815. },
  816. // handlers
  817. dismissHandler = function(e) {
  818. var eventTarget = e[target], hasData = eventTarget && (eventTarget[getAttribute](dataToggle)
  819. || eventTarget[parentNode] && getAttribute in eventTarget[parentNode]
  820. && eventTarget[parentNode][getAttribute](dataToggle));
  821. if ( e.type === focusEvent && (eventTarget === element || eventTarget === menu || menu[contains](eventTarget) ) ) {
  822. return;
  823. }
  824. if ( (eventTarget === menu || menu[contains](eventTarget)) && (self.persist || hasData) ) { return; }
  825. else {
  826. relatedTarget = eventTarget === element || element[contains](eventTarget) ? element : null;
  827. hide();
  828. }
  829. preventEmptyAnchor.call(e,eventTarget);
  830. },
  831. clickHandler = function(e) {
  832. relatedTarget = element;
  833. show();
  834. preventEmptyAnchor.call(e,e[target]);
  835. },
  836. preventScroll = function(e){
  837. var key = e.which || e.keyCode;
  838. if( key === 38 || key === 40 ) { e[preventDefault](); }
  839. },
  840. keyHandler = function(e){
  841. var key = e.which || e.keyCode,
  842. activeItem = DOC.activeElement,
  843. idx = menuItems[indexOf](activeItem),
  844. isSameElement = activeItem === element,
  845. isInsideMenu = menu[contains](activeItem),
  846. isMenuItem = activeItem[parentNode] === menu || activeItem[parentNode][parentNode] === menu;
  847. if ( isMenuItem ) { // navigate up | down
  848. idx = isSameElement ? 0
  849. : key === 38 ? (idx>1?idx-1:0)
  850. : key === 40 ? (idx<menuItems[length]-1?idx+1:idx) : idx;
  851. menuItems[idx] && setFocus(menuItems[idx]);
  852. }
  853. if ( (menuItems[length] && isMenuItem // menu has items
  854. || !menuItems[length] && (isInsideMenu || isSameElement) // menu might be a form
  855. || !isInsideMenu ) // or the focused element is not in the menu at all
  856. && element[open] && key === 27 // menu must be open
  857. ) {
  858. self.toggle();
  859. relatedTarget = null;
  860. }
  861. },
  862. // private methods
  863. show = function() {
  864. bootstrapCustomEvent.call(parent, showEvent, component, relatedTarget);
  865. addClass(menu,showClass);
  866. addClass(parent,showClass);
  867. element[setAttribute](ariaExpanded,true);
  868. bootstrapCustomEvent.call(parent, shownEvent, component, relatedTarget);
  869. element[open] = true;
  870. off(element, clickEvent, clickHandler);
  871. setTimeout(function(){
  872. setFocus( menu[getElementsByTagName]('INPUT')[0] || element ); // focus the first input item | element
  873. toggleDismiss();
  874. },1);
  875. },
  876. hide = function() {
  877. bootstrapCustomEvent.call(parent, hideEvent, component, relatedTarget);
  878. removeClass(menu,showClass);
  879. removeClass(parent,showClass);
  880. element[setAttribute](ariaExpanded,false);
  881. bootstrapCustomEvent.call(parent, hiddenEvent, component, relatedTarget);
  882. element[open] = false;
  883. toggleDismiss();
  884. setFocus(element);
  885. setTimeout(function(){ on(element, clickEvent, clickHandler); },1);
  886. };
  887. // set initial state to closed
  888. element[open] = false;
  889. // public methods
  890. this.toggle = function() {
  891. if (hasClass(parent,showClass) && element[open]) { hide(); }
  892. else { show(); }
  893. };
  894. // init
  895. if ( !(stringDropdown in element) ) { // prevent adding event handlers twice
  896. !tabindex in menu && menu[setAttribute](tabindex, '0'); // Fix onblur on Chrome | Safari
  897. on(element, clickEvent, clickHandler);
  898. }
  899. element[stringDropdown] = self;
  900. };
  901. // DROPDOWN DATA API
  902. // =================
  903. supports[push]( [stringDropdown, Dropdown, '['+dataToggle+'="dropdown"]'] );
  904. /* Native Javascript for Bootstrap 4 | Modal
  905. -------------------------------------------*/
  906. // MODAL DEFINITION
  907. // ===============
  908. var Modal = function(element, options) { // element can be the modal/triggering button
  909. // the modal (both JavaScript / DATA API init) / triggering button element (DATA API)
  910. element = queryElement(element);
  911. // strings
  912. var component = 'modal',
  913. staticString = 'static',
  914. modalTrigger = 'modalTrigger',
  915. paddingRight = 'paddingRight',
  916. modalBackdropString = 'modal-backdrop',
  917. isAnimating = 'isAnimating',
  918. // determine modal, triggering element
  919. btnCheck = element[getAttribute](dataTarget)||element[getAttribute]('href'),
  920. checkModal = queryElement( btnCheck ),
  921. modal = hasClass(element,component) ? element : checkModal;
  922. if ( hasClass(element, component) ) { element = null; } // modal is now independent of it's triggering element
  923. if ( !modal ) { return; } // invalidate
  924. // set options
  925. options = options || {};
  926. this[keyboard] = options[keyboard] === false || modal[getAttribute](dataKeyboard) === 'false' ? false : true;
  927. this[backdrop] = options[backdrop] === staticString || modal[getAttribute](databackdrop) === staticString ? staticString : true;
  928. this[backdrop] = options[backdrop] === false || modal[getAttribute](databackdrop) === 'false' ? false : this[backdrop];
  929. this[animation] = hasClass(modal, 'fade') ? true : false;
  930. this[content] = options[content]; // JavaScript only
  931. // set an initial state of the modal
  932. modal[isAnimating] = false;
  933. // bind, constants, event targets and other vars
  934. var self = this, relatedTarget = null,
  935. bodyIsOverflowing, scrollBarWidth, overlay, overlayDelay, modalTimer,
  936. // also find fixed-top / fixed-bottom items
  937. fixedItems = getElementsByClassName(HTML,fixedTop).concat(getElementsByClassName(HTML,fixedBottom)),
  938. // private methods
  939. getWindowWidth = function() {
  940. var htmlRect = HTML[getBoundingClientRect]();
  941. return globalObject[innerWidth] || (htmlRect[right] - Math.abs(htmlRect[left]));
  942. },
  943. setScrollbar = function () {
  944. var bodyStyle = globalObject[getComputedStyle](DOC[body]),
  945. bodyPad = parseInt((bodyStyle[paddingRight]), 10), itemPad;
  946. if (bodyIsOverflowing) {
  947. DOC[body][style][paddingRight] = (bodyPad + scrollBarWidth) + 'px';
  948. modal[style][paddingRight] = scrollBarWidth+'px';
  949. if (fixedItems[length]){
  950. for (var i = 0; i < fixedItems[length]; i++) {
  951. itemPad = globalObject[getComputedStyle](fixedItems[i])[paddingRight];
  952. fixedItems[i][style][paddingRight] = ( parseInt(itemPad) + scrollBarWidth) + 'px';
  953. }
  954. }
  955. }
  956. },
  957. resetScrollbar = function () {
  958. DOC[body][style][paddingRight] = '';
  959. modal[style][paddingRight] = '';
  960. if (fixedItems[length]){
  961. for (var i = 0; i < fixedItems[length]; i++) {
  962. fixedItems[i][style][paddingRight] = '';
  963. }
  964. }
  965. },
  966. measureScrollbar = function () { // thx walsh
  967. var scrollDiv = DOC[createElement]('div'), widthValue;
  968. scrollDiv.className = component+'-scrollbar-measure'; // this is here to stay
  969. DOC[body][appendChild](scrollDiv);
  970. widthValue = scrollDiv[offsetWidth] - scrollDiv[clientWidth];
  971. DOC[body].removeChild(scrollDiv);
  972. return widthValue;
  973. },
  974. checkScrollbar = function () {
  975. bodyIsOverflowing = DOC[body][clientWidth] < getWindowWidth();
  976. scrollBarWidth = measureScrollbar();
  977. },
  978. createOverlay = function() {
  979. var newOverlay = DOC[createElement]('div');
  980. overlay = queryElement('.'+modalBackdropString);
  981. if ( overlay === null ) {
  982. newOverlay[setAttribute]('class', modalBackdropString + (self[animation] ? ' fade' : ''));
  983. overlay = newOverlay;
  984. DOC[body][appendChild](overlay);
  985. }
  986. modalOverlay = 1;
  987. },
  988. removeOverlay = function() {
  989. overlay = queryElement('.'+modalBackdropString);
  990. if ( overlay && overlay !== null && typeof overlay === 'object' ) {
  991. modalOverlay = 0;
  992. DOC[body].removeChild(overlay); overlay = null;
  993. }
  994. },
  995. // triggers
  996. triggerShow = function() {
  997. setFocus(modal);
  998. modal[isAnimating] = false;
  999. bootstrapCustomEvent.call(modal, shownEvent, component, relatedTarget);
  1000. on(globalObject, resizeEvent, self.update, passiveHandler);
  1001. on(modal, clickEvent, dismissHandler);
  1002. on(DOC, keydownEvent, keyHandler);
  1003. },
  1004. triggerHide = function() {
  1005. modal[style].display = '';
  1006. element && (setFocus(element));
  1007. bootstrapCustomEvent.call(modal, hiddenEvent, component);
  1008. (function(){
  1009. if (!getElementsByClassName(DOC,component+' '+showClass)[0]) {
  1010. resetScrollbar();
  1011. removeClass(DOC[body],component+'-open');
  1012. overlay && hasClass(overlay,'fade') ? (removeClass(overlay,showClass), emulateTransitionEnd(overlay,removeOverlay))
  1013. : removeOverlay();
  1014. off(globalObject, resizeEvent, self.update, passiveHandler);
  1015. off(modal, clickEvent, dismissHandler);
  1016. off(DOC, keydownEvent, keyHandler);
  1017. }
  1018. }());
  1019. modal[isAnimating] = false;
  1020. },
  1021. // handlers
  1022. clickHandler = function(e) {
  1023. if ( modal[isAnimating] ) return;
  1024. var clickTarget = e[target];
  1025. clickTarget = clickTarget[hasAttribute](dataTarget) || clickTarget[hasAttribute]('href') ? clickTarget : clickTarget[parentNode];
  1026. if ( clickTarget === element && !hasClass(modal,showClass) ) {
  1027. modal[modalTrigger] = element;
  1028. relatedTarget = element;
  1029. self.show();
  1030. e[preventDefault]();
  1031. }
  1032. },
  1033. keyHandler = function(e) {
  1034. if ( modal[isAnimating] ) return;
  1035. if (self[keyboard] && e.which == 27 && hasClass(modal,showClass) ) {
  1036. self.hide();
  1037. }
  1038. },
  1039. dismissHandler = function(e) {
  1040. if ( modal[isAnimating] ) return;
  1041. var clickTarget = e[target];
  1042. if ( hasClass(modal,showClass) && ( clickTarget[parentNode][getAttribute](dataDismiss) === component
  1043. || clickTarget[getAttribute](dataDismiss) === component
  1044. || clickTarget === modal && self[backdrop] !== staticString ) ) {
  1045. self.hide(); relatedTarget = null;
  1046. e[preventDefault]();
  1047. }
  1048. };
  1049. // public methods
  1050. this.toggle = function() {
  1051. if ( hasClass(modal,showClass) ) {this.hide();} else {this.show();}
  1052. };
  1053. this.show = function() {
  1054. if ( hasClass(modal,showClass) || modal[isAnimating] ) {return}
  1055. clearTimeout(modalTimer);
  1056. modalTimer = setTimeout(function(){
  1057. modal[isAnimating] = true;
  1058. bootstrapCustomEvent.call(modal, showEvent, component, relatedTarget);
  1059. // we elegantly hide any opened modal
  1060. var currentOpen = getElementsByClassName(DOC,component+' '+showClass)[0];
  1061. if (currentOpen && currentOpen !== modal) {
  1062. modalTrigger in currentOpen && currentOpen[modalTrigger][stringModal].hide();
  1063. stringModal in currentOpen && currentOpen[stringModal].hide();
  1064. }
  1065. if ( self[backdrop] ) {
  1066. !modalOverlay && !overlay && createOverlay();
  1067. }
  1068. if ( overlay && !hasClass(overlay,showClass) ) {
  1069. overlay[offsetWidth]; // force reflow to enable trasition
  1070. overlayDelay = getTransitionDurationFromElement(overlay);
  1071. addClass(overlay, showClass);
  1072. }
  1073. setTimeout( function() {
  1074. modal[style].display = 'block';
  1075. checkScrollbar();
  1076. setScrollbar();
  1077. addClass(DOC[body],component+'-open');
  1078. addClass(modal,showClass);
  1079. modal[setAttribute](ariaHidden, false);
  1080. hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow();
  1081. }, supportTransitions && overlay && overlayDelay ? overlayDelay : 1);
  1082. },1);
  1083. };
  1084. this.hide = function() {
  1085. if ( modal[isAnimating] || !hasClass(modal,showClass) ) {return}
  1086. clearTimeout(modalTimer);
  1087. modalTimer = setTimeout(function(){
  1088. modal[isAnimating] = true;
  1089. bootstrapCustomEvent.call(modal, hideEvent, component);
  1090. overlay = queryElement('.'+modalBackdropString);
  1091. overlayDelay = overlay && getTransitionDurationFromElement(overlay);
  1092. removeClass(modal,showClass);
  1093. modal[setAttribute](ariaHidden, true);
  1094. setTimeout(function(){
  1095. hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerHide) : triggerHide();
  1096. }, supportTransitions && overlay && overlayDelay ? overlayDelay : 2);
  1097. },2)
  1098. };
  1099. this.setContent = function( content ) {
  1100. queryElement('.'+component+'-content',modal)[innerHTML] = content;
  1101. };
  1102. this.update = function() {
  1103. if (hasClass(modal,showClass)) {
  1104. checkScrollbar();
  1105. setScrollbar();
  1106. }
  1107. };
  1108. // init
  1109. // prevent adding event handlers over and over
  1110. // modal is independent of a triggering element
  1111. if ( !!element && !(stringModal in element) ) {
  1112. on(element, clickEvent, clickHandler);
  1113. }
  1114. if ( !!self[content] ) { self.setContent( self[content] ); }
  1115. if (element) { element[stringModal] = self; modal[modalTrigger] = element; }
  1116. else { modal[stringModal] = self; }
  1117. };
  1118. // DATA API
  1119. supports[push]( [ stringModal, Modal, '['+dataToggle+'="modal"]' ] );
  1120. /* Native Javascript for Bootstrap 4 | Popover
  1121. ----------------------------------------------*/
  1122. // POPOVER DEFINITION
  1123. // ==================
  1124. var Popover = function( element, options ) {
  1125. // initialization element
  1126. element = queryElement(element);
  1127. // set options
  1128. options = options || {};
  1129. // DATA API
  1130. var triggerData = element[getAttribute](dataTrigger), // click / hover / focus
  1131. animationData = element[getAttribute](dataAnimation), // true / false
  1132. placementData = element[getAttribute](dataPlacement),
  1133. dismissibleData = element[getAttribute](dataDismissible),
  1134. delayData = element[getAttribute](dataDelay),
  1135. containerData = element[getAttribute](dataContainer),
  1136. // internal strings
  1137. component = 'popover',
  1138. template = 'template',
  1139. trigger = 'trigger',
  1140. classString = 'class',
  1141. div = 'div',
  1142. fade = 'fade',
  1143. dataContent = 'data-content',
  1144. dismissible = 'dismissible',
  1145. closeBtn = '<button type="button" class="close">×</button>',
  1146. // check container
  1147. containerElement = queryElement(options[container]),
  1148. containerDataElement = queryElement(containerData),
  1149. // maybe the element is inside a modal
  1150. modal = getClosest(element,'.modal'),
  1151. // maybe the element is inside a fixed navbar
  1152. navbarFixedTop = getClosest(element,'.'+fixedTop),
  1153. navbarFixedBottom = getClosest(element,'.'+fixedBottom);
  1154. // set instance options
  1155. this[template] = options[template] ? options[template] : null; // JavaScript only
  1156. this[trigger] = options[trigger] ? options[trigger] : triggerData || hoverEvent;
  1157. this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade;
  1158. this[placement] = options[placement] ? options[placement] : placementData || top;
  1159. this[delay] = parseInt(options[delay] || delayData) || 200;
  1160. this[dismissible] = options[dismissible] || dismissibleData === 'true' ? true : false;
  1161. this[container] = containerElement ? containerElement
  1162. : containerDataElement ? containerDataElement
  1163. : navbarFixedTop ? navbarFixedTop
  1164. : navbarFixedBottom ? navbarFixedBottom
  1165. : modal ? modal : DOC[body];
  1166. // bind, content
  1167. var self = this,
  1168. titleString = options.title || element[getAttribute](dataTitle) || null,
  1169. contentString = options.content || element[getAttribute](dataContent) || null;
  1170. if ( !contentString && !this[template] ) return; // invalidate
  1171. // constants, vars
  1172. var popover = null, timer = 0, placementSetting = this[placement],
  1173. // handlers
  1174. dismissibleHandler = function(e) {
  1175. if (popover !== null && e[target] === queryElement('.close',popover)) {
  1176. self.hide();
  1177. }
  1178. },
  1179. // private methods
  1180. removePopover = function() {
  1181. self[container].removeChild(popover);
  1182. timer = null; popover = null;
  1183. },
  1184. createPopover = function() {
  1185. titleString = options.title || element[getAttribute](dataTitle);
  1186. contentString = options.content || element[getAttribute](dataContent);
  1187. // fixing https://github.com/thednp/bootstrap.native/issues/233
  1188. contentString = !!contentString ? contentString.trim() : null;
  1189. popover = DOC[createElement](div);
  1190. // popover arrow
  1191. var popoverArrow = DOC[createElement](div);
  1192. popoverArrow[setAttribute](classString,'arrow');
  1193. popover[appendChild](popoverArrow);
  1194. if ( contentString !== null && self[template] === null ) { //create the popover from data attributes
  1195. popover[setAttribute]('role','tooltip');
  1196. if (titleString !== null) {
  1197. var popoverTitle = DOC[createElement]('h3');
  1198. popoverTitle[setAttribute](classString,component+'-header');
  1199. popoverTitle[innerHTML] = self[dismissible] ? titleString + closeBtn : titleString;
  1200. popover[appendChild](popoverTitle);
  1201. }
  1202. //set popover content
  1203. var popoverContent = DOC[createElement](div);
  1204. popoverContent[setAttribute](classString,component+'-body');
  1205. popoverContent[innerHTML] = self[dismissible] && titleString === null ? contentString + closeBtn : contentString;
  1206. popover[appendChild](popoverContent);
  1207. } else { // or create the popover from template
  1208. var popoverTemplate = DOC[createElement](div);
  1209. self[template] = self[template].trim();
  1210. popoverTemplate[innerHTML] = self[template];
  1211. popover[innerHTML] = popoverTemplate.firstChild[innerHTML];
  1212. }
  1213. //append to the container
  1214. self[container][appendChild](popover);
  1215. popover[style].display = 'block';
  1216. popover[setAttribute](classString, component+ ' bs-' + component+'-'+placementSetting + ' ' + self[animation]);
  1217. },
  1218. showPopover = function () {
  1219. !hasClass(popover,showClass) && ( addClass(popover,showClass) );
  1220. },
  1221. updatePopover = function() {
  1222. styleTip(element, popover, placementSetting, self[container]);
  1223. },
  1224. // event toggle
  1225. dismissHandlerToggle = function(type){
  1226. if (clickEvent == self[trigger] || 'focus' == self[trigger]) {
  1227. !self[dismissible] && type( element, 'blur', self.hide );
  1228. }
  1229. self[dismissible] && type( DOC, clickEvent, dismissibleHandler );
  1230. type( globalObject, resizeEvent, self.hide, passiveHandler );
  1231. },
  1232. // triggers
  1233. showTrigger = function() {
  1234. dismissHandlerToggle(on);
  1235. bootstrapCustomEvent.call(element, shownEvent, component);
  1236. },
  1237. hideTrigger = function() {
  1238. dismissHandlerToggle(off);
  1239. removePopover();
  1240. bootstrapCustomEvent.call(element, hiddenEvent, component);
  1241. };
  1242. // public methods / handlers
  1243. this.toggle = function() {
  1244. if (popover === null) { self.show(); }
  1245. else { self.hide(); }
  1246. };
  1247. this.show = function() {
  1248. clearTimeout(timer);
  1249. timer = setTimeout( function() {
  1250. if (popover === null) {
  1251. placementSetting = self[placement]; // we reset placement in all cases
  1252. createPopover();
  1253. updatePopover();
  1254. showPopover();
  1255. bootstrapCustomEvent.call(element, showEvent, component);
  1256. !!self[animation] ? emulateTransitionEnd(popover, showTrigger) : showTrigger();
  1257. }
  1258. }, 20 );
  1259. };
  1260. this.hide = function() {
  1261. clearTimeout(timer);
  1262. timer = setTimeout( function() {
  1263. if (popover && popover !== null && hasClass(popover,showClass)) {
  1264. bootstrapCustomEvent.call(element, hideEvent, component);
  1265. removeClass(popover,showClass);
  1266. !!self[animation] ? emulateTransitionEnd(popover, hideTrigger) : hideTrigger();
  1267. }
  1268. }, self[delay] );
  1269. };
  1270. // init
  1271. if ( !(stringPopover in element) ) { // prevent adding event handlers twice
  1272. if (self[trigger] === hoverEvent) {
  1273. on( element, mouseHover[0], self.show );
  1274. if (!self[dismissible]) { on( element, mouseHover[1], self.hide ); }
  1275. } else if (clickEvent == self[trigger] || 'focus' == self[trigger]) {
  1276. on( element, self[trigger], self.toggle );
  1277. }
  1278. }
  1279. element[stringPopover] = self;
  1280. };
  1281. // POPOVER DATA API
  1282. // ================
  1283. supports[push]( [ stringPopover, Popover, '['+dataToggle+'="popover"]' ] );
  1284. /* Native Javascript for Bootstrap 4 | ScrollSpy
  1285. -----------------------------------------------*/
  1286. // SCROLLSPY DEFINITION
  1287. // ====================
  1288. var ScrollSpy = function(element, options) {
  1289. // initialization element, the element we spy on
  1290. element = queryElement(element);
  1291. // DATA API
  1292. var targetData = queryElement(element[getAttribute](dataTarget)),
  1293. offsetData = element[getAttribute]('data-offset');
  1294. // set options
  1295. options = options || {};
  1296. // invalidate
  1297. if ( !options[target] && !targetData ) { return; }
  1298. // event targets, constants
  1299. var self = this, spyTarget = options[target] && queryElement(options[target]) || targetData,
  1300. links = spyTarget && spyTarget[getElementsByTagName]('A'),
  1301. offset = parseInt(options['offset'] || offsetData) || 10,
  1302. items = [], targetItems = [], scrollOffset,
  1303. scrollTarget = element[offsetHeight] < element[scrollHeight] ? element : globalObject, // determine which is the real scrollTarget
  1304. isWindow = scrollTarget === globalObject;
  1305. // populate items and targets
  1306. for (var i=0, il=links[length]; i<il; i++) {
  1307. var href = links[i][getAttribute]('href'),
  1308. targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && queryElement(href);
  1309. if ( !!targetItem ) {
  1310. items[push](links[i]);
  1311. targetItems[push](targetItem);
  1312. }
  1313. }
  1314. // private methods
  1315. var updateItem = function(index) {
  1316. var item = items[index],
  1317. targetItem = targetItems[index], // the menu item targets this element
  1318. dropdown = item[parentNode][parentNode],
  1319. dropdownLink = hasClass(dropdown,'dropdown') && dropdown[getElementsByTagName]('A')[0],
  1320. targetRect = isWindow && targetItem[getBoundingClientRect](),
  1321. isActive = hasClass(item,active) || false,
  1322. topEdge = (isWindow ? targetRect[top] + scrollOffset : targetItem[offsetTop]) - offset,
  1323. bottomEdge = isWindow ? targetRect[bottom] + scrollOffset - offset : targetItems[index+1] ? targetItems[index+1][offsetTop] - offset : element[scrollHeight],
  1324. inside = scrollOffset >= topEdge && bottomEdge > scrollOffset;
  1325. if ( !isActive && inside ) {
  1326. if ( !hasClass(item,active) ) {
  1327. addClass(item,active);
  1328. if (dropdownLink && !hasClass(dropdownLink,active) ) {
  1329. addClass(dropdownLink,active);
  1330. }
  1331. bootstrapCustomEvent.call(element, 'activate', 'scrollspy', items[index]);
  1332. }
  1333. } else if ( !inside ) {
  1334. if ( hasClass(item,active) ) {
  1335. removeClass(item,active);
  1336. if (dropdownLink && hasClass(dropdownLink,active) && !getElementsByClassName(item[parentNode],active).length ) {
  1337. removeClass(dropdownLink,active);
  1338. }
  1339. }
  1340. } else if ( !inside && !isActive || isActive && inside ) {
  1341. return;
  1342. }
  1343. },
  1344. updateItems = function(){
  1345. scrollOffset = isWindow ? getScroll().y : element[scrollTop];
  1346. for (var index=0, itl=items[length]; index<itl; index++) {
  1347. updateItem(index)
  1348. }
  1349. };
  1350. // public method
  1351. this.refresh = function () {
  1352. updateItems();
  1353. }
  1354. // init
  1355. if ( !(stringScrollSpy in element) ) { // prevent adding event handlers twice
  1356. on( scrollTarget, scrollEvent, self.refresh, passiveHandler );
  1357. on( globalObject, resizeEvent, self.refresh, passiveHandler );
  1358. }
  1359. self.refresh();
  1360. element[stringScrollSpy] = self;
  1361. };
  1362. // SCROLLSPY DATA API
  1363. // ==================
  1364. supports[push]( [ stringScrollSpy, ScrollSpy, '['+dataSpy+'="scroll"]' ] );
  1365. /* Native Javascript for Bootstrap 4 | Tab
  1366. -----------------------------------------*/
  1367. // TAB DEFINITION
  1368. // ==============
  1369. var Tab = function( element, options ) {
  1370. // initialization element
  1371. element = queryElement(element);
  1372. // DATA API
  1373. var heightData = element[getAttribute](dataHeight),
  1374. // strings
  1375. component = 'tab', height = 'height', float = 'float', isAnimating = 'isAnimating';
  1376. // set options
  1377. options = options || {};
  1378. this[height] = supportTransitions ? (options[height] || heightData === 'true') : false;
  1379. // bind, event targets
  1380. var self = this, next,
  1381. tabs = getClosest(element,'.nav'),
  1382. tabsContentContainer = false,
  1383. dropdown = tabs && queryElement('.dropdown-toggle',tabs),
  1384. activeTab, activeContent, nextContent, containerHeight, equalContents, nextHeight,
  1385. // trigger
  1386. triggerEnd = function(){
  1387. tabsContentContainer[style][height] = '';
  1388. removeClass(tabsContentContainer,collapsing);
  1389. tabs[isAnimating] = false;
  1390. },
  1391. triggerShow = function() {
  1392. if (tabsContentContainer) { // height animation
  1393. if ( equalContents ) {
  1394. triggerEnd();
  1395. } else {
  1396. setTimeout(function(){ // enables height animation
  1397. tabsContentContainer[style][height] = nextHeight + 'px'; // height animation
  1398. tabsContentContainer[offsetWidth];
  1399. emulateTransitionEnd(tabsContentContainer, triggerEnd);
  1400. },50);
  1401. }
  1402. } else {
  1403. tabs[isAnimating] = false;
  1404. }
  1405. bootstrapCustomEvent.call(next, shownEvent, component, activeTab);
  1406. },
  1407. triggerHide = function() {
  1408. if (tabsContentContainer) {
  1409. activeContent[style][float] = left;
  1410. nextContent[style][float] = left;
  1411. containerHeight = activeContent[scrollHeight];
  1412. }
  1413. addClass(nextContent,active);
  1414. bootstrapCustomEvent.call(next, showEvent, component, activeTab);
  1415. removeClass(activeContent,active);
  1416. bootstrapCustomEvent.call(activeTab, hiddenEvent, component, next);
  1417. if (tabsContentContainer) {
  1418. nextHeight = nextContent[scrollHeight];
  1419. equalContents = nextHeight === containerHeight;
  1420. addClass(tabsContentContainer,collapsing);
  1421. tabsContentContainer[style][height] = containerHeight + 'px'; // height animation
  1422. tabsContentContainer[offsetHeight];
  1423. activeContent[style][float] = '';
  1424. nextContent[style][float] = '';
  1425. }
  1426. if ( hasClass(nextContent, 'fade') ) {
  1427. setTimeout(function(){
  1428. addClass(nextContent,showClass);
  1429. emulateTransitionEnd(nextContent,triggerShow);
  1430. },20);
  1431. } else { triggerShow(); }
  1432. };
  1433. if (!tabs) return; // invalidate
  1434. // set default animation state
  1435. tabs[isAnimating] = false;
  1436. // private methods
  1437. var getActiveTab = function() {
  1438. var activeTabs = getElementsByClassName(tabs,active), activeTab;
  1439. if ( activeTabs[length] === 1 && !hasClass(activeTabs[0][parentNode],'dropdown') ) {
  1440. activeTab = activeTabs[0];
  1441. } else if ( activeTabs[length] > 1 ) {
  1442. activeTab = activeTabs[activeTabs[length]-1];
  1443. }
  1444. return activeTab;
  1445. },
  1446. getActiveContent = function() {
  1447. return queryElement(getActiveTab()[getAttribute]('href'));
  1448. },
  1449. // handler
  1450. clickHandler = function(e) {
  1451. e[preventDefault]();
  1452. next = e[currentTarget];
  1453. !tabs[isAnimating] && !hasClass(next,active) && self.show();
  1454. };
  1455. // public method
  1456. this.show = function() { // the tab we clicked is now the next tab
  1457. next = next || element;
  1458. nextContent = queryElement(next[getAttribute]('href')); //this is the actual object, the next tab content to activate
  1459. activeTab = getActiveTab();
  1460. activeContent = getActiveContent();
  1461. tabs[isAnimating] = true;
  1462. removeClass(activeTab,active);
  1463. activeTab[setAttribute](ariaSelected,'false');
  1464. addClass(next,active);
  1465. next[setAttribute](ariaSelected,'true');
  1466. if ( dropdown ) {
  1467. if ( !hasClass(element[parentNode],'dropdown-menu') ) {
  1468. if (hasClass(dropdown,active)) removeClass(dropdown,active);
  1469. } else {
  1470. if (!hasClass(dropdown,active)) addClass(dropdown,active);
  1471. }
  1472. }
  1473. bootstrapCustomEvent.call(activeTab, hideEvent, component, next);
  1474. if (hasClass(activeContent, 'fade')) {
  1475. removeClass(activeContent,showClass);
  1476. emulateTransitionEnd(activeContent, triggerHide);
  1477. } else { triggerHide(); }
  1478. };
  1479. // init
  1480. if ( !(stringTab in element) ) { // prevent adding event handlers twice
  1481. on(element, clickEvent, clickHandler);
  1482. }
  1483. if (self[height]) { tabsContentContainer = getActiveContent()[parentNode]; }
  1484. element[stringTab] = self;
  1485. };
  1486. // TAB DATA API
  1487. // ============
  1488. supports[push]( [ stringTab, Tab, '['+dataToggle+'="tab"]' ] );
  1489. /* Native Javascript for Bootstrap 4 | Toast
  1490. ---------------------------------------------*/
  1491. // TOAST DEFINITION
  1492. // ==================
  1493. var Toast = function( element,options ) {
  1494. // initialization element
  1495. element = queryElement(element);
  1496. // set options
  1497. options = options || {};
  1498. // DATA API
  1499. var animationData = element[getAttribute](dataAnimation),
  1500. autohideData = element[getAttribute](dataAutohide),
  1501. delayData = element[getAttribute](dataDelay),
  1502. // strings
  1503. component = 'toast',
  1504. autohide = 'autohide',
  1505. animation = 'animation',
  1506. showing = 'showing',
  1507. hide = 'hide',
  1508. fade = 'fade';
  1509. // set instance options
  1510. this[animation] = options[animation] === false || animationData === 'false' ? 0 : 1; // true by default
  1511. this[autohide] = options[autohide] === false || autohideData === 'false' ? 0 : 1; // true by default
  1512. this[delay] = parseInt(options[delay] || delayData) || 500; // 500ms default
  1513. // bind,toast and timer
  1514. var self = this, timer = 0,
  1515. // get the toast element
  1516. toast = getClosest(element,'.toast');
  1517. // private methods
  1518. // animation complete
  1519. var showComplete = function() {
  1520. removeClass( toast, showing );
  1521. addClass( toast, showClass );
  1522. bootstrapCustomEvent.call(toast, shownEvent, component);
  1523. if (self[autohide]) { self.hide(); }
  1524. },
  1525. hideComplete = function() {
  1526. addClass( toast, hide );
  1527. bootstrapCustomEvent.call(toast, hiddenEvent, component);
  1528. },
  1529. close = function() {
  1530. removeClass( toast,showClass );
  1531. self[animation] ? emulateTransitionEnd(toast, hideComplete) : hideComplete();
  1532. },
  1533. disposeComplete = function(){
  1534. clearTimeout(timer); timer = null;
  1535. addClass( toast, hide );
  1536. off(element, clickEvent, self.hide);
  1537. element[stringToast] = null;
  1538. element = null;
  1539. toast = null;
  1540. };
  1541. // public methods
  1542. this.show = function() {
  1543. if (toast) {
  1544. bootstrapCustomEvent.call(toast, showEvent, component);
  1545. self[animation] && addClass( toast,fade );
  1546. removeClass( toast,hide );
  1547. addClass( toast,showing );
  1548. self[animation] ? emulateTransitionEnd(toast, showComplete) : showComplete();
  1549. }
  1550. };
  1551. this.hide = function(noTimer) {
  1552. if (toast && hasClass(toast,showClass)) {
  1553. bootstrapCustomEvent.call(toast, hideEvent, component);
  1554. if (noTimer) {
  1555. close();
  1556. } else {
  1557. timer = setTimeout( close, self[delay]);
  1558. }
  1559. }
  1560. };
  1561. this.dispose = function() {
  1562. if ( toast && hasClass(toast,showClass) ) {
  1563. removeClass( toast,showClass );
  1564. self[animation] ? emulateTransitionEnd(toast, disposeComplete) : disposeComplete();
  1565. }
  1566. };
  1567. // init
  1568. if ( !(stringToast in element) ) { // prevent adding event handlers twice
  1569. on(element, clickEvent, self.hide);
  1570. }
  1571. element[stringToast] = self;
  1572. };
  1573. // TOAST DATA API
  1574. // =================
  1575. supports[push]( [ stringToast, Toast, '['+dataDismiss+'="toast"]' ] );
  1576. /* Native Javascript for Bootstrap 4 | Tooltip
  1577. ---------------------------------------------*/
  1578. // TOOLTIP DEFINITION
  1579. // ==================
  1580. var Tooltip = function( element,options ) {
  1581. // initialization element
  1582. element = queryElement(element);
  1583. // set options
  1584. options = options || {};
  1585. // DATA API
  1586. var animationData = element[getAttribute](dataAnimation),
  1587. placementData = element[getAttribute](dataPlacement),
  1588. delayData = element[getAttribute](dataDelay),
  1589. containerData = element[getAttribute](dataContainer),
  1590. // strings
  1591. component = 'tooltip',
  1592. classString = 'class',
  1593. title = 'title',
  1594. fade = 'fade',
  1595. div = 'div',
  1596. // check container
  1597. containerElement = queryElement(options[container]),
  1598. containerDataElement = queryElement(containerData),
  1599. // maybe the element is inside a modal
  1600. modal = getClosest(element,'.modal'),
  1601. // maybe the element is inside a fixed navbar
  1602. navbarFixedTop = getClosest(element,'.'+fixedTop),
  1603. navbarFixedBottom = getClosest(element,'.'+fixedBottom);
  1604. // set instance options
  1605. this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade;
  1606. this[placement] = options[placement] ? options[placement] : placementData || top;
  1607. this[delay] = parseInt(options[delay] || delayData) || 200;
  1608. this[container] = containerElement ? containerElement
  1609. : containerDataElement ? containerDataElement
  1610. : navbarFixedTop ? navbarFixedTop
  1611. : navbarFixedBottom ? navbarFixedBottom
  1612. : modal ? modal : DOC[body];
  1613. // bind, event targets, title and constants
  1614. var self = this, timer = 0, placementSetting = this[placement], tooltip = null,
  1615. titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle);
  1616. if ( !titleString || titleString == "" ) return; // invalidate
  1617. // private methods
  1618. var removeToolTip = function() {
  1619. self[container].removeChild(tooltip);
  1620. tooltip = null; timer = null;
  1621. },
  1622. createToolTip = function() {
  1623. titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); // read the title again
  1624. if ( titleString && titleString !== "" ) { // invalidate, maybe markup changed
  1625. tooltip = DOC[createElement](div);
  1626. tooltip[setAttribute]('role',component);
  1627. tooltip[style][left] = '0';
  1628. tooltip[style][top] = '0';
  1629. // tooltip arrow
  1630. var tooltipArrow = DOC[createElement](div);
  1631. tooltipArrow[setAttribute](classString,'arrow');
  1632. tooltip[appendChild](tooltipArrow);
  1633. var tooltipInner = DOC[createElement](div);
  1634. tooltipInner[setAttribute](classString,component+'-inner');
  1635. tooltip[appendChild](tooltipInner);
  1636. tooltipInner[innerHTML] = titleString;
  1637. self[container][appendChild](tooltip);
  1638. tooltip[setAttribute](classString, component + ' bs-' + component+'-'+placementSetting + ' ' + self[animation]);
  1639. }
  1640. },
  1641. updateTooltip = function () {
  1642. styleTip(element, tooltip, placementSetting, self[container]);
  1643. },
  1644. showTooltip = function () {
  1645. !hasClass(tooltip,showClass) && ( addClass(tooltip,showClass) );
  1646. },
  1647. // triggers
  1648. showTrigger = function() {
  1649. on( globalObject, resizeEvent, self.hide, passiveHandler );
  1650. bootstrapCustomEvent.call(element, shownEvent, component);
  1651. },
  1652. hideTrigger = function() {
  1653. off( globalObject, resizeEvent, self.hide, passiveHandler );
  1654. removeToolTip();
  1655. bootstrapCustomEvent.call(element, hiddenEvent, component);
  1656. };
  1657. // public methods
  1658. this.show = function() {
  1659. clearTimeout(timer);
  1660. timer = setTimeout( function() {
  1661. if (tooltip === null) {
  1662. placementSetting = self[placement]; // we reset placement in all cases
  1663. // if(createToolTip() == false) return;
  1664. if(createToolTip() !== false) {
  1665. updateTooltip();
  1666. showTooltip();
  1667. bootstrapCustomEvent.call(element, showEvent, component);
  1668. !!self[animation] ? emulateTransitionEnd(tooltip, showTrigger) : showTrigger();
  1669. }
  1670. }
  1671. }, 20 );
  1672. };
  1673. this.hide = function() {
  1674. clearTimeout(timer);
  1675. timer = setTimeout( function() {
  1676. if (tooltip && hasClass(tooltip,showClass)) {
  1677. bootstrapCustomEvent.call(element, hideEvent, component);
  1678. removeClass(tooltip,showClass);
  1679. !!self[animation] ? emulateTransitionEnd(tooltip, hideTrigger) : hideTrigger();
  1680. }
  1681. }, self[delay]);
  1682. };
  1683. this.toggle = function() {
  1684. if (!tooltip) { self.show(); }
  1685. else { self.hide(); }
  1686. };
  1687. // init
  1688. if ( !(stringTooltip in element) ) { // prevent adding event handlers twice
  1689. element[setAttribute](dataOriginalTitle,titleString);
  1690. element.removeAttribute(title);
  1691. on(element, mouseHover[0], self.show);
  1692. on(element, mouseHover[1], self.hide);
  1693. }
  1694. element[stringTooltip] = self;
  1695. };
  1696. // TOOLTIP DATA API
  1697. // =================
  1698. supports[push]( [ stringTooltip, Tooltip, '['+dataToggle+'="tooltip"]' ] );
  1699. /* Native Javascript for Bootstrap | Initialize Data API
  1700. --------------------------------------------------------*/
  1701. var initializeDataAPI = function( constructor, collection ){
  1702. for (var i=0, l=collection[length]; i<l; i++) {
  1703. new constructor(collection[i]);
  1704. }
  1705. },
  1706. initCallback = BSN.initCallback = function(lookUp){
  1707. lookUp = lookUp || DOC;
  1708. for (var i=0, l=supports[length]; i<l; i++) {
  1709. initializeDataAPI( supports[i][1], lookUp[querySelectorAll] (supports[i][2]) );
  1710. }
  1711. };
  1712. // bulk initialize all components
  1713. DOC[body] ? initCallback() : on( DOC, 'DOMContentLoaded', function(){ initCallback(); } );
  1714. return {
  1715. Alert: Alert,
  1716. Button: Button,
  1717. Carousel: Carousel,
  1718. Collapse: Collapse,
  1719. Dropdown: Dropdown,
  1720. Modal: Modal,
  1721. Popover: Popover,
  1722. ScrollSpy: ScrollSpy,
  1723. Tab: Tab,
  1724. Toast: Toast,
  1725. Tooltip: Tooltip
  1726. };
  1727. }));