// Native Javascript for Bootstrap 4 v2.0.27 | © dnp_theme | MIT-License (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD support: define([], factory); } else if (typeof module === 'object' && module.exports) { // CommonJS-like: module.exports = factory(); } else { // Browser globals (root is window) var bsn = factory(); root.Alert = bsn.Alert; root.Button = bsn.Button; root.Carousel = bsn.Carousel; root.Collapse = bsn.Collapse; root.Dropdown = bsn.Dropdown; root.Modal = bsn.Modal; root.Popover = bsn.Popover; root.ScrollSpy = bsn.ScrollSpy; root.Tab = bsn.Tab; root.Toast = bsn.Toast; root.Tooltip = bsn.Tooltip; } }(this, function () { /* Native Javascript for Bootstrap 4 | Internal Utility Functions ----------------------------------------------------------------*/ "use strict"; // globals var globalObject = typeof global !== 'undefined' ? global : this||window, DOC = document, HTML = DOC.documentElement, body = 'body', // allow the library to be used in // Native Javascript for Bootstrap Global Object BSN = globalObject.BSN = {}, supports = BSN.supports = [], // function toggle attributes dataToggle = 'data-toggle', dataDismiss = 'data-dismiss', dataSpy = 'data-spy', dataRide = 'data-ride', // components stringAlert = 'Alert', stringButton = 'Button', stringCarousel = 'Carousel', stringCollapse = 'Collapse', stringDropdown = 'Dropdown', stringModal = 'Modal', stringPopover = 'Popover', stringScrollSpy = 'ScrollSpy', stringTab = 'Tab', stringTooltip = 'Tooltip', stringToast = 'Toast', // options DATA API dataAutohide = 'data-autohide', databackdrop = 'data-backdrop', dataKeyboard = 'data-keyboard', dataTarget = 'data-target', dataInterval = 'data-interval', dataHeight = 'data-height', dataPause = 'data-pause', dataTitle = 'data-title', dataOriginalTitle = 'data-original-title', dataDismissible = 'data-dismissible', dataTrigger = 'data-trigger', dataAnimation = 'data-animation', dataContainer = 'data-container', dataPlacement = 'data-placement', dataDelay = 'data-delay', // option keys backdrop = 'backdrop', keyboard = 'keyboard', delay = 'delay', content = 'content', target = 'target', currentTarget = 'currentTarget', interval = 'interval', pause = 'pause', animation = 'animation', placement = 'placement', container = 'container', // box model offsetTop = 'offsetTop', offsetBottom = 'offsetBottom', offsetLeft = 'offsetLeft', scrollTop = 'scrollTop', scrollLeft = 'scrollLeft', clientWidth = 'clientWidth', clientHeight = 'clientHeight', offsetWidth = 'offsetWidth', offsetHeight = 'offsetHeight', innerWidth = 'innerWidth', innerHeight = 'innerHeight', scrollHeight = 'scrollHeight', scrollWidth = 'scrollWidth', height = 'height', // aria ariaExpanded = 'aria-expanded', ariaHidden = 'aria-hidden', ariaSelected = 'aria-selected', // event names clickEvent = 'click', focusEvent = 'focus', hoverEvent = 'hover', keydownEvent = 'keydown', keyupEvent = 'keyup', resizeEvent = 'resize', // passive scrollEvent = 'scroll', // passive mouseHover = ('onmouseleave' in DOC) ? [ 'mouseenter', 'mouseleave'] : [ 'mouseover', 'mouseout' ], // touch since 2.0.26 touchEvents = { start: 'touchstart', end: 'touchend', move:'touchmove' }, // passive // originalEvents showEvent = 'show', shownEvent = 'shown', hideEvent = 'hide', hiddenEvent = 'hidden', closeEvent = 'close', closedEvent = 'closed', slidEvent = 'slid', slideEvent = 'slide', changeEvent = 'change', // other getAttribute = 'getAttribute', setAttribute = 'setAttribute', hasAttribute = 'hasAttribute', createElement = 'createElement', appendChild = 'appendChild', innerHTML = 'innerHTML', getElementsByTagName = 'getElementsByTagName', preventDefault = 'preventDefault', getBoundingClientRect = 'getBoundingClientRect', querySelectorAll = 'querySelectorAll', getElementsByCLASSNAME = 'getElementsByClassName', getComputedStyle = 'getComputedStyle', indexOf = 'indexOf', parentNode = 'parentNode', length = 'length', toLowerCase = 'toLowerCase', Transition = 'Transition', Duration = 'Duration', Webkit = 'Webkit', style = 'style', push = 'push', tabindex = 'tabindex', contains = 'contains', active = 'active', showClass = 'show', collapsing = 'collapsing', disabled = 'disabled', loading = 'loading', left = 'left', right = 'right', top = 'top', bottom = 'bottom', // tooltip / popover tipPositions = /\b(top|bottom|left|right)+/, // modal modalOverlay = 0, fixedTop = 'fixed-top', fixedBottom = 'fixed-bottom', // transitionEnd since 2.0.4 supportTransitions = Webkit+Transition in HTML[style] || Transition[toLowerCase]() in HTML[style], transitionEndEvent = Webkit+Transition in HTML[style] ? Webkit[toLowerCase]()+Transition+'End' : Transition[toLowerCase]()+'end', transitionDuration = Webkit+Duration in HTML[style] ? Webkit[toLowerCase]()+Transition+Duration : Transition[toLowerCase]()+Duration, // set new focus element since 2.0.3 setFocus = function(element){ element.focus ? element.focus() : element.setActive(); }, // class manipulation, since 2.0.0 requires polyfill.js addClass = function(element,classNAME) { element.classList.add(classNAME); }, removeClass = function(element,classNAME) { element.classList.remove(classNAME); }, hasClass = function(element,classNAME){ // since 2.0.0 return element.classList[contains](classNAME); }, // selection methods getElementsByClassName = function(element,classNAME) { // returns Array return [].slice.call(element[getElementsByCLASSNAME]( classNAME )); }, queryElement = function (selector, parent) { var lookUp = parent ? parent : DOC; return typeof selector === 'object' ? selector : lookUp.querySelector(selector); }, getClosest = function (element, selector) { //element is the element and selector is for the closest parent element to find // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/ var firstChar = selector.charAt(0), selectorSubstring = selector.substr(1); if ( firstChar === '.' ) {// If selector is a class for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match if ( queryElement(selector,element[parentNode]) !== null && hasClass(element,selectorSubstring) ) { return element; } } } else if ( firstChar === '#' ) { // If selector is an ID for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match if ( element.id === selectorSubstring ) { return element; } } } return false; }, // event attach jQuery style / trigger since 1.2.0 on = function (element, event, handler, options) { options = options || false; element.addEventListener(event, handler, options); }, off = function(element, event, handler, options) { options = options || false; element.removeEventListener(event, handler, options); }, one = function (element, event, handler, options) { // one since 2.0.4 on(element, event, function handlerWrapper(e){ handler(e); off(element, event, handlerWrapper, options); }, options); }, // determine support for passive events supportPassive = (function(){ // Test via a getter in the options object to see if the passive property is accessed var result = false; try { var opts = Object.defineProperty({}, 'passive', { get: function() { result = true; } }); one(globalObject, 'testPassive', null, opts); } catch (e) {} return result; }()), // event options // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection passiveHandler = supportPassive ? { passive: true } : false, // transitions getTransitionDurationFromElement = function(element) { var duration = supportTransitions ? globalObject[getComputedStyle](element)[transitionDuration] : 0; duration = parseFloat(duration); duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0; return duration; // we take a short offset to make sure we fire on the next frame after animation }, emulateTransitionEnd = function(element,handler){ // emulateTransitionEnd since 2.0.4 var called = 0, duration = getTransitionDurationFromElement(element); duration ? one(element, transitionEndEvent, function(e){ !called && handler(e), called = 1; }) : setTimeout(function() { !called && handler(), called = 1; }, 17); }, bootstrapCustomEvent = function (eventName, componentName, related) { var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName); OriginalCustomEvent.relatedTarget = related; this.dispatchEvent(OriginalCustomEvent); }, // tooltip / popover stuff getScroll = function() { // also Affix and ScrollSpy uses it return { y : globalObject.pageYOffset || HTML[scrollTop], x : globalObject.pageXOffset || HTML[scrollLeft] } }, styleTip = function(link,element,position,parent) { // both popovers and tooltips (target,tooltip,placement,elementToAppendTo) var elementDimensions = { w : element[offsetWidth], h: element[offsetHeight] }, windowWidth = (HTML[clientWidth] || DOC[body][clientWidth]), windowHeight = (HTML[clientHeight] || DOC[body][clientHeight]), rect = link[getBoundingClientRect](), scroll = parent === DOC[body] ? getScroll() : { x: parent[offsetLeft] + parent[scrollLeft], y: parent[offsetTop] + parent[scrollTop] }, linkDimensions = { w: rect[right] - rect[left], h: rect[bottom] - rect[top] }, isPopover = hasClass(element,'popover'), topPosition, leftPosition, arrow = queryElement('.arrow',element), arrowTop, arrowLeft, arrowWidth, arrowHeight, halfTopExceed = rect[top] + linkDimensions.h/2 - elementDimensions.h/2 < 0, halfLeftExceed = rect[left] + linkDimensions.w/2 - elementDimensions.w/2 < 0, halfRightExceed = rect[left] + elementDimensions.w/2 + linkDimensions.w/2 >= windowWidth, halfBottomExceed = rect[top] + elementDimensions.h/2 + linkDimensions.h/2 >= windowHeight, topExceed = rect[top] - elementDimensions.h < 0, leftExceed = rect[left] - elementDimensions.w < 0, bottomExceed = rect[top] + elementDimensions.h + linkDimensions.h >= windowHeight, rightExceed = rect[left] + elementDimensions.w + linkDimensions.w >= windowWidth; // recompute position position = (position === left || position === right) && leftExceed && rightExceed ? top : position; // first, when both left and right limits are exceeded, we fall back to top|bottom position = position === top && topExceed ? bottom : position; position = position === bottom && bottomExceed ? top : position; position = position === left && leftExceed ? right : position; position = position === right && rightExceed ? left : position; // update tooltip/popover class element.className[indexOf](position) === -1 && (element.className = element.className.replace(tipPositions,position)); // we check the computed width & height and update here arrowWidth = arrow[offsetWidth]; arrowHeight = arrow[offsetHeight]; // apply styling to tooltip or popover if ( position === left || position === right ) { // secondary|side positions if ( position === left ) { // LEFT leftPosition = rect[left] + scroll.x - elementDimensions.w - ( isPopover ? arrowWidth : 0 ); } else { // RIGHT leftPosition = rect[left] + scroll.x + linkDimensions.w; } // adjust top and arrow if (halfTopExceed) { topPosition = rect[top] + scroll.y; arrowTop = linkDimensions.h/2 - arrowWidth; } else if (halfBottomExceed) { topPosition = rect[top] + scroll.y - elementDimensions.h + linkDimensions.h; arrowTop = elementDimensions.h - linkDimensions.h/2 - arrowWidth; } else { topPosition = rect[top] + scroll.y - elementDimensions.h/2 + linkDimensions.h/2; arrowTop = elementDimensions.h/2 - (isPopover ? arrowHeight*0.9 : arrowHeight/2); } } else if ( position === top || position === bottom ) { // primary|vertical positions if ( position === top) { // TOP topPosition = rect[top] + scroll.y - elementDimensions.h - ( isPopover ? arrowHeight : 0 ); } else { // BOTTOM topPosition = rect[top] + scroll.y + linkDimensions.h; } // adjust left | right and also the arrow if (halfLeftExceed) { leftPosition = 0; arrowLeft = rect[left] + linkDimensions.w/2 - arrowWidth; } else if (halfRightExceed) { leftPosition = windowWidth - elementDimensions.w*1.01; arrowLeft = elementDimensions.w - ( windowWidth - rect[left] ) + linkDimensions.w/2 - arrowWidth/2; } else { leftPosition = rect[left] + scroll.x - elementDimensions.w/2 + linkDimensions.w/2; arrowLeft = elementDimensions.w/2 - ( isPopover ? arrowWidth : arrowWidth/2 ); } } // apply style to tooltip/popover and its arrow element[style][top] = topPosition + 'px'; element[style][left] = leftPosition + 'px'; arrowTop && (arrow[style][top] = arrowTop + 'px'); arrowLeft && (arrow[style][left] = arrowLeft + 'px'); }; BSN.version = '2.0.27'; /* Native Javascript for Bootstrap 4 | Alert -------------------------------------------*/ // ALERT DEFINITION // ================ var Alert = function( element ) { // initialization element element = queryElement(element); // bind, target alert, duration and stuff var self = this, component = 'alert', alert = getClosest(element,'.'+component), triggerHandler = function(){ hasClass(alert,'fade') ? emulateTransitionEnd(alert,transitionEndHandler) : transitionEndHandler(); }, // handlers clickHandler = function(e){ alert = getClosest(e[target],'.'+component); element = queryElement('['+dataDismiss+'="'+component+'"]',alert); element && alert && (element === e[target] || element[contains](e[target])) && self.close(); }, transitionEndHandler = function(){ bootstrapCustomEvent.call(alert, closedEvent, component); off(element, clickEvent, clickHandler); // detach it's listener alert[parentNode].removeChild(alert); }; // public method this.close = function() { if ( alert && element && hasClass(alert,showClass) ) { bootstrapCustomEvent.call(alert, closeEvent, component); removeClass(alert,showClass); alert && triggerHandler(); } }; // init if ( !(stringAlert in element ) ) { // prevent adding event handlers twice on(element, clickEvent, clickHandler); } element[stringAlert] = self; }; // ALERT DATA API // ============== supports[push]([stringAlert, Alert, '['+dataDismiss+'="alert"]']); /* Native Javascript for Bootstrap 4 | Button ---------------------------------------------*/ // BUTTON DEFINITION // =================== var Button = function( element ) { // initialization element element = queryElement(element); // constant var toggled = false, // toggled makes sure to prevent triggering twice the change.bs.button events // strings component = 'button', checked = 'checked', LABEL = 'LABEL', INPUT = 'INPUT', // private methods keyHandler = function(e){ var key = e.which || e.keyCode; key === 32 && e[target] === DOC.activeElement && toggle(e); }, preventScroll = function(e){ var key = e.which || e.keyCode; key === 32 && e[preventDefault](); }, toggle = function(e) { var label = e[target].tagName === LABEL ? e[target] : e[target][parentNode].tagName === LABEL ? e[target][parentNode] : null; // the .btn label if ( !label ) return; //react if a label or its immediate child is clicked var labels = getElementsByClassName(label[parentNode],'btn'), // all the button group buttons input = label[getElementsByTagName](INPUT)[0]; if ( !input ) return; // return if no input found // manage the dom manipulation if ( input.type === 'checkbox' ) { //checkboxes if ( !input[checked] ) { addClass(label,active); input[getAttribute](checked); input[setAttribute](checked,checked); input[checked] = true; } else { removeClass(label,active); input[getAttribute](checked); input.removeAttribute(checked); input[checked] = false; } if (!toggled) { // prevent triggering the event twice toggled = true; bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group } } if ( input.type === 'radio' && !toggled ) { // radio buttons // 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) if ( !input[checked] || (e.screenX === 0 && e.screenY == 0) ) { addClass(label,active); addClass(label,focusEvent); input[setAttribute](checked,checked); input[checked] = true; bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group toggled = true; for (var i = 0, ll = labels[length]; i 1 ) { e.preventDefault(); return false; } }, touchEndHandler = function(e) { if ( !isTouch || isSliding ) { return } endXPosition = currentXPosition || parseInt( e.touches[0].pageX ); if ( isTouch ) { if ( (!element.contains(e[target]) || !element.contains(e.relatedTarget) ) && Math.abs(startXPosition - endXPosition) < 75 ) { return false; } else { if ( currentXPosition < startXPosition ) { index++; } else if ( currentXPosition > startXPosition ) { index--; } isTouch = false; self.slideTo(index); } toggleTouchEvents(off); } }, // private methods isElementInScrollRange = function () { var rect = element[getBoundingClientRect](), viewportHeight = globalObject[innerHeight] || HTML[clientHeight] return rect[top] <= viewportHeight && rect[bottom] >= 0; // bottom && top }, setActivePage = function( pageIndex ) { //indicators for ( var i = 0, icl = indicators[length]; i < icl; i++ ) { removeClass(indicators[i],active); } if (indicators[pageIndex]) addClass(indicators[pageIndex], active); }; // public methods this.cycle = function() { if (timer) { clearInterval(timer); timer = null; } timer = setInterval(function() { isElementInScrollRange() && (index++, self.slideTo( index ) ); }, this[interval]); }; this.slideTo = function( next ) { if (isSliding) return; // when controled via methods, make sure to check again var activeItem = this.getActiveIndex(), // the current active orientation; // first return if we're on the same item #227 if ( activeItem === next ) { return; // or determine slideDirection } else if ( (activeItem < next ) || (activeItem === 0 && next === total -1 ) ) { slideDirection = self[direction] = left; // next } else if ( (activeItem > next) || (activeItem === total - 1 && next === 0 ) ) { slideDirection = self[direction] = right; // prev } // find the right next index if ( next < 0 ) { next = total - 1; } else if ( next >= total ){ next = 0; } // update index index = next; orientation = slideDirection === left ? 'next' : 'prev'; //determine type bootstrapCustomEvent.call(element, slideEvent, component, slides[next]); // here we go with the slide isSliding = true; clearInterval(timer); timer = null; setActivePage( next ); if ( supportTransitions && hasClass(element,'slide') ) { addClass(slides[next],carouselItem +'-'+ orientation); slides[next][offsetWidth]; addClass(slides[next],carouselItem +'-'+ slideDirection); addClass(slides[activeItem],carouselItem +'-'+ slideDirection); emulateTransitionEnd(slides[next], function(e) { var timeout = e && e[target] !== slides[next] ? e.elapsedTime*1000+100 : 20; isSliding && setTimeout(function(){ isSliding = false; addClass(slides[next],active); removeClass(slides[activeItem],active); removeClass(slides[next],carouselItem +'-'+ orientation); removeClass(slides[next],carouselItem +'-'+ slideDirection); removeClass(slides[activeItem],carouselItem +'-'+ slideDirection); bootstrapCustomEvent.call(element, slidEvent, component, slides[next]); if ( !DOC.hidden && self[interval] && !hasClass(element,paused) ) { self.cycle(); } }, timeout); }); } else { addClass(slides[next],active); slides[next][offsetWidth]; removeClass(slides[activeItem],active); setTimeout(function() { isSliding = false; if ( self[interval] && !hasClass(element,paused) ) { self.cycle(); } bootstrapCustomEvent.call(element, slidEvent, component, slides[next]); }, 100 ); } }; this.getActiveIndex = function () { return slides[indexOf](getElementsByClassName(element,carouselItem+' active')[0]) || 0; }; // init if ( !(stringCarousel in element ) ) { // prevent adding event handlers twice if ( self[pause] && self[interval] ) { on( element, mouseHover[0], pauseHandler ); on( element, mouseHover[1], resumeHandler ); on( element, touchEvents.start, pauseHandler, passiveHandler ); on( element, touchEvents.end, resumeHandler, passiveHandler ); } slides[length] > 1 && on( element, touchEvents.start, touchDownHandler, passiveHandler ); rightArrow && on( rightArrow, clickEvent, controlsHandler ); leftArrow && on( leftArrow, clickEvent, controlsHandler ); indicator && on( indicator, clickEvent, indicatorHandler ); self[keyboard] && on( globalObject, keydownEvent, keyHandler ); } if (self.getActiveIndex()<0) { slides[length] && addClass(slides[0],active); indicators[length] && setActivePage(0); } if ( self[interval] ){ self.cycle(); } element[stringCarousel] = self; }; // CAROUSEL DATA API // ================= supports[push]( [ stringCarousel, Carousel, '['+dataRide+'="carousel"]' ] ); /* Native Javascript for Bootstrap 4 | Collapse -----------------------------------------------*/ // COLLAPSE DEFINITION // =================== var Collapse = function( element, options ) { // initialization element element = queryElement(element); // set options options = options || {}; // event targets and constants var accordion = null, collapse = null, self = this, accordionData = element[getAttribute]('data-parent'), activeCollapse, activeElement, // component strings component = 'collapse', collapsed = 'collapsed', isAnimating = 'isAnimating', // private methods openAction = function(collapseElement,toggle) { bootstrapCustomEvent.call(collapseElement, showEvent, component); collapseElement[isAnimating] = true; addClass(collapseElement,collapsing); removeClass(collapseElement,component); collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; emulateTransitionEnd(collapseElement, function() { collapseElement[isAnimating] = false; collapseElement[setAttribute](ariaExpanded,'true'); toggle[setAttribute](ariaExpanded,'true'); removeClass(collapseElement,collapsing); addClass(collapseElement, component); addClass(collapseElement,showClass); collapseElement[style][height] = ''; bootstrapCustomEvent.call(collapseElement, shownEvent, component); }); }, closeAction = function(collapseElement,toggle) { bootstrapCustomEvent.call(collapseElement, hideEvent, component); collapseElement[isAnimating] = true; collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first removeClass(collapseElement,component); removeClass(collapseElement,showClass); addClass(collapseElement,collapsing); collapseElement[offsetWidth]; // force reflow to enable transition collapseElement[style][height] = '0px'; emulateTransitionEnd(collapseElement, function() { collapseElement[isAnimating] = false; collapseElement[setAttribute](ariaExpanded,'false'); toggle[setAttribute](ariaExpanded,'false'); removeClass(collapseElement,collapsing); addClass(collapseElement,component); collapseElement[style][height] = ''; bootstrapCustomEvent.call(collapseElement, hiddenEvent, component); }); }, getTarget = function() { var href = element.href && element[getAttribute]('href'), parent = element[getAttribute](dataTarget), id = href || ( parent && parent.charAt(0) === '#' ) && parent; return id && queryElement(id); }; // public methods this.toggle = function(e) { e[preventDefault](); if (!hasClass(collapse,showClass)) { self.show(); } else { self.hide(); } }; this.hide = function() { if ( collapse[isAnimating] ) return; closeAction(collapse,element); addClass(element,collapsed); }; this.show = function() { if ( accordion ) { activeCollapse = queryElement('.'+component+'.'+showClass,accordion); activeElement = activeCollapse && (queryElement('['+dataTarget+'="#'+activeCollapse.id+'"]',accordion) || queryElement('[href="#'+activeCollapse.id+'"]',accordion) ); } if ( !collapse[isAnimating] || activeCollapse && !activeCollapse[isAnimating] ) { if ( activeElement && activeCollapse !== collapse ) { closeAction(activeCollapse,activeElement); addClass(activeElement,collapsed); } openAction(collapse,element); removeClass(element,collapsed); } }; // init if ( !(stringCollapse in element ) ) { // prevent adding event handlers twice on(element, clickEvent, self.toggle); } collapse = getTarget(); collapse[isAnimating] = false; // when true it will prevent click handlers accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData); element[stringCollapse] = self; }; // COLLAPSE DATA API // ================= supports[push]( [ stringCollapse, Collapse, '['+dataToggle+'="collapse"]' ] ); /* Native Javascript for Bootstrap 4 | Dropdown ----------------------------------------------*/ // DROPDOWN DEFINITION // =================== var Dropdown = function( element, option ) { // initialization element element = queryElement(element); // set option this.persist = option === true || element[getAttribute]('data-persist') === 'true' || false; // constants, event targets, strings var self = this, children = 'children', parent = element[parentNode], component = 'dropdown', open = 'open', relatedTarget = null, menu = queryElement('.dropdown-menu', parent), menuItems = (function(){ var set = menu[children], newSet = []; for ( var i=0; i1?idx-1:0) : key === 40 ? (idx= topEdge && bottomEdge > scrollOffset; if ( !isActive && inside ) { if ( !hasClass(item,active) ) { addClass(item,active); if (dropdownLink && !hasClass(dropdownLink,active) ) { addClass(dropdownLink,active); } bootstrapCustomEvent.call(element, 'activate', 'scrollspy', items[index]); } } else if ( !inside ) { if ( hasClass(item,active) ) { removeClass(item,active); if (dropdownLink && hasClass(dropdownLink,active) && !getElementsByClassName(item[parentNode],active).length ) { removeClass(dropdownLink,active); } } } else if ( !inside && !isActive || isActive && inside ) { return; } }, updateItems = function(){ scrollOffset = isWindow ? getScroll().y : element[scrollTop]; for (var index=0, itl=items[length]; index 1 ) { activeTab = activeTabs[activeTabs[length]-1]; } return activeTab; }, getActiveContent = function() { return queryElement(getActiveTab()[getAttribute]('href')); }, // handler clickHandler = function(e) { e[preventDefault](); next = e[currentTarget]; !tabs[isAnimating] && !hasClass(next,active) && self.show(); }; // public method this.show = function() { // the tab we clicked is now the next tab next = next || element; nextContent = queryElement(next[getAttribute]('href')); //this is the actual object, the next tab content to activate activeTab = getActiveTab(); activeContent = getActiveContent(); tabs[isAnimating] = true; removeClass(activeTab,active); activeTab[setAttribute](ariaSelected,'false'); addClass(next,active); next[setAttribute](ariaSelected,'true'); if ( dropdown ) { if ( !hasClass(element[parentNode],'dropdown-menu') ) { if (hasClass(dropdown,active)) removeClass(dropdown,active); } else { if (!hasClass(dropdown,active)) addClass(dropdown,active); } } bootstrapCustomEvent.call(activeTab, hideEvent, component, next); if (hasClass(activeContent, 'fade')) { removeClass(activeContent,showClass); emulateTransitionEnd(activeContent, triggerHide); } else { triggerHide(); } }; // init if ( !(stringTab in element) ) { // prevent adding event handlers twice on(element, clickEvent, clickHandler); } if (self[height]) { tabsContentContainer = getActiveContent()[parentNode]; } element[stringTab] = self; }; // TAB DATA API // ============ supports[push]( [ stringTab, Tab, '['+dataToggle+'="tab"]' ] ); /* Native Javascript for Bootstrap 4 | Toast ---------------------------------------------*/ // TOAST DEFINITION // ================== var Toast = function( element,options ) { // initialization element element = queryElement(element); // set options options = options || {}; // DATA API var animationData = element[getAttribute](dataAnimation), autohideData = element[getAttribute](dataAutohide), delayData = element[getAttribute](dataDelay), // strings component = 'toast', autohide = 'autohide', animation = 'animation', showing = 'showing', hide = 'hide', fade = 'fade'; // set instance options this[animation] = options[animation] === false || animationData === 'false' ? 0 : 1; // true by default this[autohide] = options[autohide] === false || autohideData === 'false' ? 0 : 1; // true by default this[delay] = parseInt(options[delay] || delayData) || 500; // 500ms default // bind,toast and timer var self = this, timer = 0, // get the toast element toast = getClosest(element,'.toast'); // private methods // animation complete var showComplete = function() { removeClass( toast, showing ); addClass( toast, showClass ); bootstrapCustomEvent.call(toast, shownEvent, component); if (self[autohide]) { self.hide(); } }, hideComplete = function() { addClass( toast, hide ); bootstrapCustomEvent.call(toast, hiddenEvent, component); }, close = function() { removeClass( toast,showClass ); self[animation] ? emulateTransitionEnd(toast, hideComplete) : hideComplete(); }, disposeComplete = function(){ clearTimeout(timer); timer = null; addClass( toast, hide ); off(element, clickEvent, self.hide); element[stringToast] = null; element = null; toast = null; }; // public methods this.show = function() { if (toast) { bootstrapCustomEvent.call(toast, showEvent, component); self[animation] && addClass( toast,fade ); removeClass( toast,hide ); addClass( toast,showing ); self[animation] ? emulateTransitionEnd(toast, showComplete) : showComplete(); } }; this.hide = function(noTimer) { if (toast && hasClass(toast,showClass)) { bootstrapCustomEvent.call(toast, hideEvent, component); if (noTimer) { close(); } else { timer = setTimeout( close, self[delay]); } } }; this.dispose = function() { if ( toast && hasClass(toast,showClass) ) { removeClass( toast,showClass ); self[animation] ? emulateTransitionEnd(toast, disposeComplete) : disposeComplete(); } }; // init if ( !(stringToast in element) ) { // prevent adding event handlers twice on(element, clickEvent, self.hide); } element[stringToast] = self; }; // TOAST DATA API // ================= supports[push]( [ stringToast, Toast, '['+dataDismiss+'="toast"]' ] ); /* Native Javascript for Bootstrap 4 | Tooltip ---------------------------------------------*/ // TOOLTIP DEFINITION // ================== var Tooltip = function( element,options ) { // initialization element element = queryElement(element); // set options options = options || {}; // DATA API var animationData = element[getAttribute](dataAnimation), placementData = element[getAttribute](dataPlacement), delayData = element[getAttribute](dataDelay), containerData = element[getAttribute](dataContainer), // strings component = 'tooltip', classString = 'class', title = 'title', fade = 'fade', div = 'div', // check container containerElement = queryElement(options[container]), containerDataElement = queryElement(containerData), // maybe the element is inside a modal modal = getClosest(element,'.modal'), // maybe the element is inside a fixed navbar navbarFixedTop = getClosest(element,'.'+fixedTop), navbarFixedBottom = getClosest(element,'.'+fixedBottom); // set instance options this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade; this[placement] = options[placement] ? options[placement] : placementData || top; this[delay] = parseInt(options[delay] || delayData) || 200; this[container] = containerElement ? containerElement : containerDataElement ? containerDataElement : navbarFixedTop ? navbarFixedTop : navbarFixedBottom ? navbarFixedBottom : modal ? modal : DOC[body]; // bind, event targets, title and constants var self = this, timer = 0, placementSetting = this[placement], tooltip = null, titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); if ( !titleString || titleString == "" ) return; // invalidate // private methods var removeToolTip = function() { self[container].removeChild(tooltip); tooltip = null; timer = null; }, createToolTip = function() { titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); // read the title again if ( titleString && titleString !== "" ) { // invalidate, maybe markup changed tooltip = DOC[createElement](div); tooltip[setAttribute]('role',component); tooltip[style][left] = '0'; tooltip[style][top] = '0'; // tooltip arrow var tooltipArrow = DOC[createElement](div); tooltipArrow[setAttribute](classString,'arrow'); tooltip[appendChild](tooltipArrow); var tooltipInner = DOC[createElement](div); tooltipInner[setAttribute](classString,component+'-inner'); tooltip[appendChild](tooltipInner); tooltipInner[innerHTML] = titleString; self[container][appendChild](tooltip); tooltip[setAttribute](classString, component + ' bs-' + component+'-'+placementSetting + ' ' + self[animation]); } }, updateTooltip = function () { styleTip(element, tooltip, placementSetting, self[container]); }, showTooltip = function () { !hasClass(tooltip,showClass) && ( addClass(tooltip,showClass) ); }, // triggers showTrigger = function() { on( globalObject, resizeEvent, self.hide, passiveHandler ); bootstrapCustomEvent.call(element, shownEvent, component); }, hideTrigger = function() { off( globalObject, resizeEvent, self.hide, passiveHandler ); removeToolTip(); bootstrapCustomEvent.call(element, hiddenEvent, component); }; // public methods this.show = function() { clearTimeout(timer); timer = setTimeout( function() { if (tooltip === null) { placementSetting = self[placement]; // we reset placement in all cases // if(createToolTip() == false) return; if(createToolTip() !== false) { updateTooltip(); showTooltip(); bootstrapCustomEvent.call(element, showEvent, component); !!self[animation] ? emulateTransitionEnd(tooltip, showTrigger) : showTrigger(); } } }, 20 ); }; this.hide = function() { clearTimeout(timer); timer = setTimeout( function() { if (tooltip && hasClass(tooltip,showClass)) { bootstrapCustomEvent.call(element, hideEvent, component); removeClass(tooltip,showClass); !!self[animation] ? emulateTransitionEnd(tooltip, hideTrigger) : hideTrigger(); } }, self[delay]); }; this.toggle = function() { if (!tooltip) { self.show(); } else { self.hide(); } }; // init if ( !(stringTooltip in element) ) { // prevent adding event handlers twice element[setAttribute](dataOriginalTitle,titleString); element.removeAttribute(title); on(element, mouseHover[0], self.show); on(element, mouseHover[1], self.hide); } element[stringTooltip] = self; }; // TOOLTIP DATA API // ================= supports[push]( [ stringTooltip, Tooltip, '['+dataToggle+'="tooltip"]' ] ); /* Native Javascript for Bootstrap | Initialize Data API --------------------------------------------------------*/ var initializeDataAPI = function( constructor, collection ){ for (var i=0, l=collection[length]; i