'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var win = window; var caf = win.cancelAnimationFrame || win.mozCancelAnimationFrame || function (id) { clearTimeout(id); }; function extend() { var obj, name, copy, target = arguments[0] || {}, i = 1, length = arguments.length; for (; i < length; i++) { if ((obj = arguments[i]) !== null) { for (name in obj) { copy = obj[name]; if (target === copy) { continue; } else if (copy !== undefined) { target[name] = copy; } } } } return target; } function checkStorageValue(value) { return ['true', 'false'].indexOf(value) >= 0 ? JSON.parse(value) : value; } function setLocalStorage(storage, key, value, access) { if (access) { try { storage.setItem(key, value); } catch (e) {} } return value; } function getSlideId() { var id = window.tnsId; window.tnsId = !id ? 1 : id + 1; return 'tns' + window.tnsId; } function getBody() { var doc = document, body = doc.body; if (!body) { body = doc.createElement('body'); body.fake = true; } return body; } var docElement = document.documentElement; function setFakeBody(body) { var docOverflow = ''; if (body.fake) { docOverflow = docElement.style.overflow; //avoid crashing IE8, if background image is used body.style.background = ''; //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible body.style.overflow = docElement.style.overflow = 'hidden'; docElement.appendChild(body); } return docOverflow; } function resetFakeBody(body, docOverflow) { if (body.fake) { body.remove(); docElement.style.overflow = docOverflow; // Trigger layout so kinetic scrolling isn't disabled in iOS6+ // eslint-disable-next-line docElement.offsetHeight; } } // get css-calc function calc() { var doc = document, body = getBody(), docOverflow = setFakeBody(body), div = doc.createElement('div'), result = false; body.appendChild(div); try { var str = '(10px * 10)', vals = ['calc' + str, '-moz-calc' + str, '-webkit-calc' + str], val; for (var i = 0; i < 3; i++) { val = vals[i]; div.style.width = val; if (div.offsetWidth === 100) { result = val.replace(str, ''); break; } } } catch (e) {} body.fake ? resetFakeBody(body, docOverflow) : div.remove(); return result; } // get subpixel support value function percentageLayout() { // check subpixel layout supporting var doc = document, body = getBody(), docOverflow = setFakeBody(body), wrapper = doc.createElement('div'), outer = doc.createElement('div'), str = '', count = 70, perPage = 3, supported = false; wrapper.className = "tns-t-subp2"; outer.className = "tns-t-ct"; for (var i = 0; i < count; i++) { str += '<div></div>'; } outer.innerHTML = str; wrapper.appendChild(outer); body.appendChild(wrapper); supported = Math.abs(wrapper.getBoundingClientRect().left - outer.children[count - perPage].getBoundingClientRect().left) < 2; body.fake ? resetFakeBody(body, docOverflow) : wrapper.remove(); return supported; } function mediaquerySupport() { if (window.matchMedia || window.msMatchMedia) { return true; } var doc = document, body = getBody(), docOverflow = setFakeBody(body), div = doc.createElement('div'), style = doc.createElement('style'), rule = '@media all and (min-width:1px){.tns-mq-test{position:absolute}}', position; style.type = 'text/css'; div.className = 'tns-mq-test'; body.appendChild(style); body.appendChild(div); if (style.styleSheet) { style.styleSheet.cssText = rule; } else { style.appendChild(doc.createTextNode(rule)); } position = window.getComputedStyle ? window.getComputedStyle(div).position : div.currentStyle['position']; body.fake ? resetFakeBody(body, docOverflow) : div.remove(); return position === "absolute"; } // create and append style sheet function createStyleSheet(media, nonce) { // Create the <style> tag var style = document.createElement("style"); // style.setAttribute("type", "text/css"); // Add a media (and/or media query) here if you'd like! // style.setAttribute("media", "screen") // style.setAttribute("media", "only screen and (max-width : 1024px)") if (media) { style.setAttribute("media", media); } // Add nonce attribute for Content Security Policy if (nonce) { style.setAttribute("nonce", nonce); } // WebKit hack :( // style.appendChild(document.createTextNode("")); // Add the <style> element to the page document.querySelector('head').appendChild(style); return style.sheet ? style.sheet : style.styleSheet; } // cross browsers addRule method function addCSSRule(sheet, selector, rules, index) { // return raf(function() { 'insertRule' in sheet ? sheet.insertRule(selector + '{' + rules + '}', index) : sheet.addRule(selector, rules, index); // }); } // cross browsers addRule method function removeCSSRule(sheet, index) { // return raf(function() { 'deleteRule' in sheet ? sheet.deleteRule(index) : sheet.removeRule(index); // }); } function getCssRulesLength(sheet) { var rule = 'insertRule' in sheet ? sheet.cssRules : sheet.rules; return rule.length; } function toDegree(y, x) { return Math.atan2(y, x) * (180 / Math.PI); } function getTouchDirection(angle, range) { var direction = false, gap = Math.abs(90 - Math.abs(angle)); if (gap >= 90 - range) { direction = 'horizontal'; } else if (gap <= range) { direction = 'vertical'; } return direction; } // https://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/ function forEach(arr, callback, scope) { for (var i = 0, l = arr.length; i < l; i++) { callback.call(scope, arr[i], i); } } var classListSupport = ('classList' in document.createElement('_')); var hasClass = classListSupport ? function (el, str) { return el.classList.contains(str); } : function (el, str) { return el.className.indexOf(str) >= 0; }; var addClass = classListSupport ? function (el, str) { if (!hasClass(el, str)) { el.classList.add(str); } } : function (el, str) { if (!hasClass(el, str)) { el.className += ' ' + str; } }; var removeClass = classListSupport ? function (el, str) { if (hasClass(el, str)) { el.classList.remove(str); } } : function (el, str) { if (hasClass(el, str)) { el.className = el.className.replace(str, ''); } }; function hasAttr(el, attr) { return el.hasAttribute(attr); } function getAttr(el, attr) { return el.getAttribute(attr); } function isNodeList(el) { // Only NodeList has the "item()" function return typeof el.item !== "undefined"; } function setAttrs(els, attrs) { els = isNodeList(els) || els instanceof Array ? els : [els]; if (Object.prototype.toString.call(attrs) !== '[object Object]') { return; } for (var i = els.length; i--;) { for (var key in attrs) { els[i].setAttribute(key, attrs[key]); } } } function removeAttrs(els, attrs) { els = isNodeList(els) || els instanceof Array ? els : [els]; attrs = attrs instanceof Array ? attrs : [attrs]; var attrLength = attrs.length; for (var i = els.length; i--;) { for (var j = attrLength; j--;) { els[i].removeAttribute(attrs[j]); } } } function arrayFromNodeList(nl) { var arr = []; for (var i = 0, l = nl.length; i < l; i++) { arr.push(nl[i]); } return arr; } function hideElement(el, forceHide) { if (el.style.display !== 'none') { el.style.display = 'none'; } } function showElement(el, forceHide) { if (el.style.display === 'none') { el.style.display = ''; } } function isVisible(el) { return window.getComputedStyle(el).display !== 'none'; } function whichProperty(props) { if (typeof props === 'string') { var arr = [props], Props = props.charAt(0).toUpperCase() + props.substr(1), prefixes = ['Webkit', 'Moz', 'ms', 'O']; prefixes.forEach(function (prefix) { if (prefix !== 'ms' || props === 'transform') { arr.push(prefix + Props); } }); props = arr; } var el = document.createElement('fakeelement'); props.length; for (var i = 0; i < props.length; i++) { var prop = props[i]; if (el.style[prop] !== undefined) { return prop; } } return false; // explicit for ie9- } function has3DTransforms(tf) { if (!tf) { return false; } if (!window.getComputedStyle) { return false; } var doc = document, body = getBody(), docOverflow = setFakeBody(body), el = doc.createElement('p'), has3d, cssTF = tf.length > 9 ? '-' + tf.slice(0, -9).toLowerCase() + '-' : ''; cssTF += 'transform'; // Add it to the body to get the computed style body.insertBefore(el, null); el.style[tf] = 'translate3d(1px,1px,1px)'; has3d = window.getComputedStyle(el).getPropertyValue(cssTF); body.fake ? resetFakeBody(body, docOverflow) : el.remove(); return has3d !== undefined && has3d.length > 0 && has3d !== "none"; } // get transitionend, animationend based on transitionDuration // @propin: string // @propOut: string, first-letter uppercase // Usage: getEndProperty('WebkitTransitionDuration', 'Transition') => webkitTransitionEnd function getEndProperty(propIn, propOut) { var endProp = false; if (/^Webkit/.test(propIn)) { endProp = 'webkit' + propOut + 'End'; } else if (/^O/.test(propIn)) { endProp = 'o' + propOut + 'End'; } else if (propIn) { endProp = propOut.toLowerCase() + 'end'; } return endProp; } // Test via a getter in the options object to see if the passive property is accessed var supportsPassive = false; try { var opts = Object.defineProperty({}, 'passive', { get: function () { supportsPassive = true; } }); window.addEventListener("test", null, opts); } catch (e) {} var passiveOption = supportsPassive ? { passive: true } : false; function addEvents(el, obj, preventScrolling) { for (var prop in obj) { var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 && !preventScrolling ? passiveOption : false; el.addEventListener(prop, obj[prop], option); } } function removeEvents(el, obj) { for (var prop in obj) { var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 ? passiveOption : false; el.removeEventListener(prop, obj[prop], option); } } function Events() { return { topics: {}, on: function (eventName, fn) { this.topics[eventName] = this.topics[eventName] || []; this.topics[eventName].push(fn); }, off: function (eventName, fn) { if (this.topics[eventName]) { for (var i = 0; i < this.topics[eventName].length; i++) { if (this.topics[eventName][i] === fn) { this.topics[eventName].splice(i, 1); break; } } } }, emit: function (eventName, data) { data.type = eventName; if (this.topics[eventName]) { this.topics[eventName].forEach(function (fn) { fn(data, eventName); }); } } }; } function jsTransform(element, attr, prefix, postfix, to, duration, callback) { var tick = Math.min(duration, 10), unit = to.indexOf('%') >= 0 ? '%' : 'px', to = to.replace(unit, ''), from = Number(element.style[attr].replace(prefix, '').replace(postfix, '').replace(unit, '')), positionTick = (to - from) / duration * tick; setTimeout(moveElement, tick); function moveElement() { duration -= tick; from += positionTick; element.style[attr] = prefix + from + unit + postfix; if (duration > 0) { setTimeout(moveElement, tick); } else { callback(); } } } // Object.keys if (!Object.keys) { Object.keys = function (object) { var keys = []; for (var name in object) { if (Object.prototype.hasOwnProperty.call(object, name)) { keys.push(name); } } return keys; }; } // ChildNode.remove if (!("remove" in Element.prototype)) { Element.prototype.remove = function () { if (this.parentNode) { this.parentNode.removeChild(this); } }; } var tns = function (options) { options = extend({ container: '.slider', mode: 'carousel', axis: 'horizontal', items: 1, gutter: 0, edgePadding: 0, fixedWidth: false, autoWidth: false, viewportMax: false, slideBy: 1, center: false, controls: true, controlsPosition: 'top', controlsText: ['prev', 'next'], controlsContainer: false, prevButton: false, nextButton: false, nav: true, navPosition: 'top', navContainer: false, navAsThumbnails: false, arrowKeys: false, speed: 300, autoplay: false, autoplayPosition: 'top', autoplayTimeout: 5000, autoplayDirection: 'forward', autoplayText: ['start', 'stop'], autoplayHoverPause: false, autoplayButton: false, autoplayButtonOutput: true, autoplayResetOnVisibility: true, animateIn: 'tns-fadeIn', animateOut: 'tns-fadeOut', animateNormal: 'tns-normal', animateDelay: false, loop: true, rewind: false, autoHeight: false, responsive: false, lazyload: false, lazyloadSelector: '.tns-lazy-img', touch: true, mouseDrag: false, swipeAngle: 15, nested: false, preventActionWhenRunning: false, preventScrollOnTouch: false, freezable: true, onInit: false, useLocalStorage: true, nonce: false }, options || {}); var doc = document, win = window, KEYS = { ENTER: 13, SPACE: 32, LEFT: 37, RIGHT: 39 }, tnsStorage = {}, localStorageAccess = options.useLocalStorage; if (localStorageAccess) { // check browser version and local storage access var browserInfo = navigator.userAgent; var uid = new Date(); try { tnsStorage = win.localStorage; if (tnsStorage) { tnsStorage.setItem(uid, uid); localStorageAccess = tnsStorage.getItem(uid) == uid; tnsStorage.removeItem(uid); } else { localStorageAccess = false; } if (!localStorageAccess) { tnsStorage = {}; } } catch (e) { localStorageAccess = false; } if (localStorageAccess) { // remove storage when browser version changes if (tnsStorage['tnsApp'] && tnsStorage['tnsApp'] !== browserInfo) { ['tC', 'tPL', 'tMQ', 'tTf', 't3D', 'tTDu', 'tTDe', 'tADu', 'tADe', 'tTE', 'tAE'].forEach(function (item) { tnsStorage.removeItem(item); }); } // update browserInfo localStorage['tnsApp'] = browserInfo; } } var CALC = tnsStorage['tC'] ? checkStorageValue(tnsStorage['tC']) : setLocalStorage(tnsStorage, 'tC', calc(), localStorageAccess), PERCENTAGELAYOUT = tnsStorage['tPL'] ? checkStorageValue(tnsStorage['tPL']) : setLocalStorage(tnsStorage, 'tPL', percentageLayout(), localStorageAccess), CSSMQ = tnsStorage['tMQ'] ? checkStorageValue(tnsStorage['tMQ']) : setLocalStorage(tnsStorage, 'tMQ', mediaquerySupport(), localStorageAccess), TRANSFORM = tnsStorage['tTf'] ? checkStorageValue(tnsStorage['tTf']) : setLocalStorage(tnsStorage, 'tTf', whichProperty('transform'), localStorageAccess), HAS3DTRANSFORMS = tnsStorage['t3D'] ? checkStorageValue(tnsStorage['t3D']) : setLocalStorage(tnsStorage, 't3D', has3DTransforms(TRANSFORM), localStorageAccess), TRANSITIONDURATION = tnsStorage['tTDu'] ? checkStorageValue(tnsStorage['tTDu']) : setLocalStorage(tnsStorage, 'tTDu', whichProperty('transitionDuration'), localStorageAccess), TRANSITIONDELAY = tnsStorage['tTDe'] ? checkStorageValue(tnsStorage['tTDe']) : setLocalStorage(tnsStorage, 'tTDe', whichProperty('transitionDelay'), localStorageAccess), ANIMATIONDURATION = tnsStorage['tADu'] ? checkStorageValue(tnsStorage['tADu']) : setLocalStorage(tnsStorage, 'tADu', whichProperty('animationDuration'), localStorageAccess), ANIMATIONDELAY = tnsStorage['tADe'] ? checkStorageValue(tnsStorage['tADe']) : setLocalStorage(tnsStorage, 'tADe', whichProperty('animationDelay'), localStorageAccess), TRANSITIONEND = tnsStorage['tTE'] ? checkStorageValue(tnsStorage['tTE']) : setLocalStorage(tnsStorage, 'tTE', getEndProperty(TRANSITIONDURATION, 'Transition'), localStorageAccess), ANIMATIONEND = tnsStorage['tAE'] ? checkStorageValue(tnsStorage['tAE']) : setLocalStorage(tnsStorage, 'tAE', getEndProperty(ANIMATIONDURATION, 'Animation'), localStorageAccess); // get element nodes from selectors var supportConsoleWarn = win.console && typeof win.console.warn === "function", tnsList = ['container', 'controlsContainer', 'prevButton', 'nextButton', 'navContainer', 'autoplayButton'], optionsElements = {}; tnsList.forEach(function (item) { if (typeof options[item] === 'string') { var str = options[item], el = doc.querySelector(str); optionsElements[item] = str; if (el && el.nodeName) { options[item] = el; } else { if (supportConsoleWarn) { console.warn('Can\'t find', options[item]); } return; } } }); // make sure at least 1 slide if (options.container.children.length < 1) { if (supportConsoleWarn) { console.warn('No slides found in', options.container); } return; } // update options var responsive = options.responsive, nested = options.nested, carousel = options.mode === 'carousel' ? true : false; if (responsive) { // apply responsive[0] to options and remove it if (0 in responsive) { options = extend(options, responsive[0]); delete responsive[0]; } var responsiveTem = {}; for (var key in responsive) { var val = responsive[key]; // update responsive // from: 300: 2 // to: // 300: { // items: 2 // } val = typeof val === 'number' ? { items: val } : val; responsiveTem[key] = val; } responsive = responsiveTem; responsiveTem = null; } // update options function updateOptions(obj) { for (var key in obj) { if (!carousel) { if (key === 'slideBy') { obj[key] = 'page'; } if (key === 'edgePadding') { obj[key] = false; } if (key === 'autoHeight') { obj[key] = false; } } // update responsive options if (key === 'responsive') { updateOptions(obj[key]); } } } if (!carousel) { updateOptions(options); } // === define and set variables === if (!carousel) { options.axis = 'horizontal'; options.slideBy = 'page'; options.edgePadding = false; var animateIn = options.animateIn, animateOut = options.animateOut, animateDelay = options.animateDelay, animateNormal = options.animateNormal; } var horizontal = options.axis === 'horizontal' ? true : false, outerWrapper = doc.createElement('div'), innerWrapper = doc.createElement('div'), middleWrapper, container = options.container, containerParent = container.parentNode, containerHTML = container.outerHTML, slideItems = container.children, slideCount = slideItems.length, breakpointZone, windowWidth = getWindowWidth(), isOn = false; if (responsive) { setBreakpointZone(); } if (carousel) { container.className += ' tns-vpfix'; } // fixedWidth: viewport > rightBoundary > indexMax var autoWidth = options.autoWidth, fixedWidth = getOption('fixedWidth'), edgePadding = getOption('edgePadding'), gutter = getOption('gutter'), viewport = getViewportWidth(), center = getOption('center'), items = !autoWidth ? Math.floor(getOption('items')) : 1, slideBy = getOption('slideBy'), viewportMax = options.viewportMax || options.fixedWidthViewportWidth, arrowKeys = getOption('arrowKeys'), speed = getOption('speed'), rewind = options.rewind, loop = rewind ? false : options.loop, autoHeight = getOption('autoHeight'), controls = getOption('controls'), controlsText = getOption('controlsText'), nav = getOption('nav'), touch = getOption('touch'), mouseDrag = getOption('mouseDrag'), autoplay = getOption('autoplay'), autoplayTimeout = getOption('autoplayTimeout'), autoplayText = getOption('autoplayText'), autoplayHoverPause = getOption('autoplayHoverPause'), autoplayResetOnVisibility = getOption('autoplayResetOnVisibility'), sheet = createStyleSheet(null, getOption('nonce')), lazyload = options.lazyload, lazyloadSelector = options.lazyloadSelector, slidePositions, // collection of slide positions slideItemsOut = [], cloneCount = loop ? getCloneCountForLoop() : 0, slideCountNew = !carousel ? slideCount + cloneCount : slideCount + cloneCount * 2, hasRightDeadZone = (fixedWidth || autoWidth) && !loop ? true : false, rightBoundary = fixedWidth ? getRightBoundary() : null, updateIndexBeforeTransform = !carousel || !loop ? true : false, // transform transformAttr = horizontal ? 'left' : 'top', transformPrefix = '', transformPostfix = '', // index getIndexMax = function () { if (fixedWidth) { return function () { return center && !loop ? slideCount - 1 : Math.ceil(-rightBoundary / (fixedWidth + gutter)); }; } else if (autoWidth) { return function () { for (var i = 0; i < slideCountNew; i++) { if (slidePositions[i] >= -rightBoundary) { return i; } } }; } else { return function () { if (center && carousel && !loop) { return slideCount - 1; } else { return loop || carousel ? Math.max(0, slideCountNew - Math.ceil(items)) : slideCountNew - 1; } }; } }(), index = getStartIndex(getOption('startIndex')), indexCached = index; getCurrentSlide(); var indexMin = 0, indexMax = !autoWidth ? getIndexMax() : null, preventActionWhenRunning = options.preventActionWhenRunning, swipeAngle = options.swipeAngle, moveDirectionExpected = swipeAngle ? '?' : true, running = false, onInit = options.onInit, events = new Events(), // id, class newContainerClasses = ' tns-slider tns-' + options.mode, slideId = container.id || getSlideId(), disable = getOption('disable'), disabled = false, freezable = options.freezable, freeze = freezable && !autoWidth ? getFreeze() : false, frozen = false, controlsEvents = { 'click': onControlsClick, 'keydown': onControlsKeydown }, navEvents = { 'click': onNavClick, 'keydown': onNavKeydown }, hoverEvents = { 'mouseover': mouseoverPause, 'mouseout': mouseoutRestart }, visibilityEvent = { 'visibilitychange': onVisibilityChange }, docmentKeydownEvent = { 'keydown': onDocumentKeydown }, touchEvents = { 'touchstart': onPanStart, 'touchmove': onPanMove, 'touchend': onPanEnd, 'touchcancel': onPanEnd }, dragEvents = { 'mousedown': onPanStart, 'mousemove': onPanMove, 'mouseup': onPanEnd, 'mouseleave': onPanEnd }, hasControls = hasOption('controls'), hasNav = hasOption('nav'), navAsThumbnails = autoWidth ? true : options.navAsThumbnails, hasAutoplay = hasOption('autoplay'), hasTouch = hasOption('touch'), hasMouseDrag = hasOption('mouseDrag'), slideActiveClass = 'tns-slide-active', slideClonedClass = 'tns-slide-cloned', imgCompleteClass = 'tns-complete', imgEvents = { 'load': onImgLoaded, 'error': onImgFailed }, imgsComplete, liveregionCurrent, preventScroll = options.preventScrollOnTouch === 'force' ? true : false; // controls if (hasControls) { var controlsContainer = options.controlsContainer, controlsContainerHTML = options.controlsContainer ? options.controlsContainer.outerHTML : '', prevButton = options.prevButton, nextButton = options.nextButton, prevButtonHTML = options.prevButton ? options.prevButton.outerHTML : '', nextButtonHTML = options.nextButton ? options.nextButton.outerHTML : '', prevIsButton, nextIsButton; } // nav if (hasNav) { var navContainer = options.navContainer, navContainerHTML = options.navContainer ? options.navContainer.outerHTML : '', navItems, pages = autoWidth ? slideCount : getPages(), pagesCached = 0, navClicked = -1, navCurrentIndex = getCurrentNavIndex(), navCurrentIndexCached = navCurrentIndex, navActiveClass = 'tns-nav-active', navStr = 'Carousel Page ', navStrCurrent = ' (Current Slide)'; } // autoplay if (hasAutoplay) { var autoplayDirection = options.autoplayDirection === 'forward' ? 1 : -1, autoplayButton = options.autoplayButton, autoplayButtonHTML = options.autoplayButton ? options.autoplayButton.outerHTML : '', autoplayHtmlStrings = ['<span class=\'tns-visually-hidden\'>', ' animation</span>'], autoplayTimer, animating, autoplayHoverPaused, autoplayUserPaused, autoplayVisibilityPaused; } if (hasTouch || hasMouseDrag) { var initPosition = {}, lastPosition = {}, translateInit, panStart = false, rafIndex, getDist = horizontal ? function (a, b) { return a.x - b.x; } : function (a, b) { return a.y - b.y; }; } // disable slider when slidecount <= items if (!autoWidth) { resetVariblesWhenDisable(disable || freeze); } if (TRANSFORM) { transformAttr = TRANSFORM; transformPrefix = 'translate'; if (HAS3DTRANSFORMS) { transformPrefix += horizontal ? '3d(' : '3d(0px, '; transformPostfix = horizontal ? ', 0px, 0px)' : ', 0px)'; } else { transformPrefix += horizontal ? 'X(' : 'Y('; transformPostfix = ')'; } } if (carousel) { container.className = container.className.replace('tns-vpfix', ''); } initStructure(); initSheet(); initSliderTransform(); // === COMMON FUNCTIONS === // function resetVariblesWhenDisable(condition) { if (condition) { controls = nav = touch = mouseDrag = arrowKeys = autoplay = autoplayHoverPause = autoplayResetOnVisibility = false; } } function getCurrentSlide() { var tem = carousel ? index - cloneCount : index; while (tem < 0) { tem += slideCount; } return tem % slideCount + 1; } function getStartIndex(ind) { ind = ind ? Math.max(0, Math.min(loop ? slideCount - 1 : slideCount - items, ind)) : 0; return carousel ? ind + cloneCount : ind; } function getAbsIndex(i) { if (i == null) { i = index; } if (carousel) { i -= cloneCount; } while (i < 0) { i += slideCount; } return Math.floor(i % slideCount); } function getCurrentNavIndex() { var absIndex = getAbsIndex(), result; result = navAsThumbnails ? absIndex : fixedWidth || autoWidth ? Math.ceil((absIndex + 1) * pages / slideCount - 1) : Math.floor(absIndex / items); // set active nav to the last one when reaches the right edge if (!loop && carousel && index === indexMax) { result = pages - 1; } return result; } function getItemsMax() { // fixedWidth or autoWidth while viewportMax is not available if (autoWidth || fixedWidth && !viewportMax) { return slideCount - 1; // most cases } else { var str = fixedWidth ? 'fixedWidth' : 'items', arr = []; if (fixedWidth || options[str] < slideCount) { arr.push(options[str]); } if (responsive) { for (var bp in responsive) { var tem = responsive[bp][str]; if (tem && (fixedWidth || tem < slideCount)) { arr.push(tem); } } } if (!arr.length) { arr.push(0); } return Math.ceil(fixedWidth ? viewportMax / Math.min.apply(null, arr) : Math.max.apply(null, arr)); } } function getCloneCountForLoop() { var itemsMax = getItemsMax(), result = carousel ? Math.ceil((itemsMax * 5 - slideCount) / 2) : itemsMax * 4 - slideCount; result = Math.max(itemsMax, result); return hasOption('edgePadding') ? result + 1 : result; } function getWindowWidth() { return win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth; } function getInsertPosition(pos) { return pos === 'top' ? 'afterbegin' : 'beforeend'; } function getClientWidth(el) { if (el == null) { return; } var div = doc.createElement('div'), rect, width; el.appendChild(div); rect = div.getBoundingClientRect(); width = rect.right - rect.left; div.remove(); return width || getClientWidth(el.parentNode); } function getViewportWidth() { var gap = edgePadding ? edgePadding * 2 - gutter : 0; return getClientWidth(containerParent) - gap; } function hasOption(item) { if (options[item]) { return true; } else { if (responsive) { for (var bp in responsive) { if (responsive[bp][item]) { return true; } } } return false; } } // get option: // fixed width: viewport, fixedWidth, gutter => items // others: window width => all variables // all: items => slideBy function getOption(item, ww) { if (ww == null) { ww = windowWidth; } if (item === 'items' && fixedWidth) { return Math.floor((viewport + gutter) / (fixedWidth + gutter)) || 1; } else { var result = options[item]; if (responsive) { for (var bp in responsive) { // bp: convert string to number if (ww >= parseInt(bp)) { if (item in responsive[bp]) { result = responsive[bp][item]; } } } } if (item === 'slideBy' && result === 'page') { result = getOption('items'); } if (!carousel && (item === 'slideBy' || item === 'items')) { result = Math.floor(result); } return result; } } function getSlideMarginLeft(i) { return CALC ? CALC + '(' + i * 100 + '% / ' + slideCountNew + ')' : i * 100 / slideCountNew + '%'; } function getInnerWrapperStyles(edgePaddingTem, gutterTem, fixedWidthTem, speedTem, autoHeightBP) { var str = ''; if (edgePaddingTem !== undefined) { var gap = edgePaddingTem; if (gutterTem) { gap -= gutterTem; } str = horizontal ? 'margin: 0 ' + gap + 'px 0 ' + edgePaddingTem + 'px;' : 'margin: ' + edgePaddingTem + 'px 0 ' + gap + 'px 0;'; } else if (gutterTem && !fixedWidthTem) { var gutterTemUnit = '-' + gutterTem + 'px', dir = horizontal ? gutterTemUnit + ' 0 0' : '0 ' + gutterTemUnit + ' 0'; str = 'margin: 0 ' + dir + ';'; } if (!carousel && autoHeightBP && TRANSITIONDURATION && speedTem) { str += getTransitionDurationStyle(speedTem); } return str; } function getContainerWidth(fixedWidthTem, gutterTem, itemsTem) { if (fixedWidthTem) { return (fixedWidthTem + gutterTem) * slideCountNew + 'px'; } else { return CALC ? CALC + '(' + slideCountNew * 100 + '% / ' + itemsTem + ')' : slideCountNew * 100 / itemsTem + '%'; } } function getSlideWidthStyle(fixedWidthTem, gutterTem, itemsTem) { var width; if (fixedWidthTem) { width = fixedWidthTem + gutterTem + 'px'; } else { if (!carousel) { itemsTem = Math.floor(itemsTem); } var dividend = carousel ? slideCountNew : itemsTem; width = CALC ? CALC + '(100% / ' + dividend + ')' : 100 / dividend + '%'; } width = 'width:' + width; // inner slider: overwrite outer slider styles return nested !== 'inner' ? width + ';' : width + ' !important;'; } function getSlideGutterStyle(gutterTem) { var str = ''; // gutter maybe interger || 0 // so can't use 'if (gutter)' if (gutterTem !== false) { var prop = horizontal ? 'padding-' : 'margin-', dir = horizontal ? 'right' : 'bottom'; str = prop + dir + ': ' + gutterTem + 'px;'; } return str; } function getCSSPrefix(name, num) { var prefix = name.substring(0, name.length - num).toLowerCase(); if (prefix) { prefix = '-' + prefix + '-'; } return prefix; } function getTransitionDurationStyle(speed) { return getCSSPrefix(TRANSITIONDURATION, 18) + 'transition-duration:' + speed / 1000 + 's;'; } function getAnimationDurationStyle(speed) { return getCSSPrefix(ANIMATIONDURATION, 17) + 'animation-duration:' + speed / 1000 + 's;'; } function initStructure() { var classOuter = 'tns-outer', classInner = 'tns-inner'; hasOption('gutter'); outerWrapper.className = classOuter; innerWrapper.className = classInner; outerWrapper.id = slideId + '-ow'; innerWrapper.id = slideId + '-iw'; // set container properties if (container.id === '') { container.id = slideId; } newContainerClasses += PERCENTAGELAYOUT || autoWidth ? ' tns-subpixel' : ' tns-no-subpixel'; newContainerClasses += CALC ? ' tns-calc' : ' tns-no-calc'; if (autoWidth) { newContainerClasses += ' tns-autowidth'; } newContainerClasses += ' tns-' + options.axis; container.className += newContainerClasses; // add constrain layer for carousel if (carousel) { middleWrapper = doc.createElement('div'); middleWrapper.id = slideId + '-mw'; middleWrapper.className = 'tns-ovh'; outerWrapper.appendChild(middleWrapper); middleWrapper.appendChild(innerWrapper); } else { outerWrapper.appendChild(innerWrapper); } if (autoHeight) { var wp = middleWrapper ? middleWrapper : innerWrapper; wp.className += ' tns-ah'; } containerParent.insertBefore(outerWrapper, container); innerWrapper.appendChild(container); // add id, class, aria attributes // before clone slides forEach(slideItems, function (item, i) { addClass(item, 'tns-item'); if (!item.id) { item.id = slideId + '-item' + i; } if (!carousel && animateNormal) { addClass(item, animateNormal); } setAttrs(item, { 'aria-hidden': 'true', 'tabindex': '-1' }); }); // ## clone slides // carousel: n + slides + n // gallery: slides + n if (cloneCount) { var fragmentBefore = doc.createDocumentFragment(), fragmentAfter = doc.createDocumentFragment(); for (var j = cloneCount; j--;) { var num = j % slideCount, cloneFirst = slideItems[num].cloneNode(true); addClass(cloneFirst, slideClonedClass); removeAttrs(cloneFirst, 'id'); fragmentAfter.insertBefore(cloneFirst, fragmentAfter.firstChild); if (carousel) { var cloneLast = slideItems[slideCount - 1 - num].cloneNode(true); addClass(cloneLast, slideClonedClass); removeAttrs(cloneLast, 'id'); fragmentBefore.appendChild(cloneLast); } } container.insertBefore(fragmentBefore, container.firstChild); container.appendChild(fragmentAfter); slideItems = container.children; } } function initSliderTransform() { // ## images loaded/failed if (hasOption('autoHeight') || autoWidth || !horizontal) { var imgs = container.querySelectorAll('img'); // add img load event listener forEach(imgs, function (img) { var src = img.src; if (!lazyload) { // not data img if (src && src.indexOf('data:image') < 0) { img.src = ''; addEvents(img, imgEvents); addClass(img, 'loading'); img.src = src; // data img } else { imgLoaded(img); } } }); // set imgsComplete raf(function () { imgsLoadedCheck(arrayFromNodeList(imgs), function () { imgsComplete = true; }); }); // reset imgs for auto height: check visible imgs only if (hasOption('autoHeight')) { imgs = getImageArray(index, Math.min(index + items - 1, slideCountNew - 1)); } lazyload ? initSliderTransformStyleCheck() : raf(function () { imgsLoadedCheck(arrayFromNodeList(imgs), initSliderTransformStyleCheck); }); } else { // set container transform property if (carousel) { doContainerTransformSilent(); } // update slider tools and events initTools(); initEvents(); } } function initSliderTransformStyleCheck() { if (autoWidth && slideCount > 1) { // check styles application var num = loop ? index : slideCount - 1; (function stylesApplicationCheck() { var left = slideItems[num].getBoundingClientRect().left; var right = slideItems[num - 1].getBoundingClientRect().right; Math.abs(left - right) <= 1 ? initSliderTransformCore() : setTimeout(function () { stylesApplicationCheck(); }, 16); })(); } else { initSliderTransformCore(); } } function initSliderTransformCore() { // run Fn()s which are rely on image loading if (!horizontal || autoWidth) { setSlidePositions(); if (autoWidth) { rightBoundary = getRightBoundary(); if (freezable) { freeze = getFreeze(); } indexMax = getIndexMax(); // <= slidePositions, rightBoundary <= resetVariblesWhenDisable(disable || freeze); } else { updateContentWrapperHeight(); } } // set container transform property if (carousel) { doContainerTransformSilent(); } // update slider tools and events initTools(); initEvents(); } function initSheet() { // gallery: // set animation classes and left value for gallery slider if (!carousel) { for (var i = index, l = index + Math.min(slideCount, items); i < l; i++) { var item = slideItems[i]; item.style.left = (i - index) * 100 / items + '%'; addClass(item, animateIn); removeClass(item, animateNormal); } } // #### LAYOUT // ## INLINE-BLOCK VS FLOAT // ## PercentageLayout: // slides: inline-block // remove blank space between slides by set font-size: 0 // ## Non PercentageLayout: // slides: float // margin-right: -100% // margin-left: ~ // Resource: https://docs.google.com/spreadsheets/d/147up245wwTXeQYve3BRSAD4oVcvQmuGsFteJOeA5xNQ/edit?usp=sharing if (horizontal) { if (PERCENTAGELAYOUT || autoWidth) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', 'font-size:' + win.getComputedStyle(slideItems[0]).fontSize + ';', getCssRulesLength(sheet)); addCSSRule(sheet, '#' + slideId, 'font-size:0;', getCssRulesLength(sheet)); } else if (carousel) { forEach(slideItems, function (slide, i) { slide.style.marginLeft = getSlideMarginLeft(i); }); } } // ## BASIC STYLES if (CSSMQ) { // middle wrapper style if (TRANSITIONDURATION) { var str = middleWrapper && options.autoHeight ? getTransitionDurationStyle(options.speed) : ''; addCSSRule(sheet, '#' + slideId + '-mw', str, getCssRulesLength(sheet)); } // inner wrapper styles str = getInnerWrapperStyles(options.edgePadding, options.gutter, options.fixedWidth, options.speed, options.autoHeight); addCSSRule(sheet, '#' + slideId + '-iw', str, getCssRulesLength(sheet)); // container styles if (carousel) { str = horizontal && !autoWidth ? 'width:' + getContainerWidth(options.fixedWidth, options.gutter, options.items) + ';' : ''; if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); } addCSSRule(sheet, '#' + slideId, str, getCssRulesLength(sheet)); } // slide styles str = horizontal && !autoWidth ? getSlideWidthStyle(options.fixedWidth, options.gutter, options.items) : ''; if (options.gutter) { str += getSlideGutterStyle(options.gutter); } // set gallery items transition-duration if (!carousel) { if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); } if (ANIMATIONDURATION) { str += getAnimationDurationStyle(speed); } } if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); } // non CSS mediaqueries: IE8 // ## update inner wrapper, container, slides if needed // set inline styles for inner wrapper & container // insert stylesheet (one line) for slides only (since slides are many) } else { // middle wrapper styles update_carousel_transition_duration(); // inner wrapper styles innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, autoHeight); // container styles if (carousel && horizontal && !autoWidth) { container.style.width = getContainerWidth(fixedWidth, gutter, items); } // slide styles var str = horizontal && !autoWidth ? getSlideWidthStyle(fixedWidth, gutter, items) : ''; if (gutter) { str += getSlideGutterStyle(gutter); } // append to the last line if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); } } // ## MEDIAQUERIES if (responsive && CSSMQ) { for (var bp in responsive) { // bp: convert string to number bp = parseInt(bp); var opts = responsive[bp], str = '', middleWrapperStr = '', innerWrapperStr = '', containerStr = '', slideStr = '', itemsBP = !autoWidth ? getOption('items', bp) : null, fixedWidthBP = getOption('fixedWidth', bp), speedBP = getOption('speed', bp), edgePaddingBP = getOption('edgePadding', bp), autoHeightBP = getOption('autoHeight', bp), gutterBP = getOption('gutter', bp); // middle wrapper string if (TRANSITIONDURATION && middleWrapper && getOption('autoHeight', bp) && 'speed' in opts) { middleWrapperStr = '#' + slideId + '-mw{' + getTransitionDurationStyle(speedBP) + '}'; } // inner wrapper string if ('edgePadding' in opts || 'gutter' in opts) { innerWrapperStr = '#' + slideId + '-iw{' + getInnerWrapperStyles(edgePaddingBP, gutterBP, fixedWidthBP, speedBP, autoHeightBP) + '}'; } // container string if (carousel && horizontal && !autoWidth && ('fixedWidth' in opts || 'items' in opts || fixedWidth && 'gutter' in opts)) { containerStr = 'width:' + getContainerWidth(fixedWidthBP, gutterBP, itemsBP) + ';'; } if (TRANSITIONDURATION && 'speed' in opts) { containerStr += getTransitionDurationStyle(speedBP); } if (containerStr) { containerStr = '#' + slideId + '{' + containerStr + '}'; } // slide string if ('fixedWidth' in opts || fixedWidth && 'gutter' in opts || !carousel && 'items' in opts) { slideStr += getSlideWidthStyle(fixedWidthBP, gutterBP, itemsBP); } if ('gutter' in opts) { slideStr += getSlideGutterStyle(gutterBP); } // set gallery items transition-duration if (!carousel && 'speed' in opts) { if (TRANSITIONDURATION) { slideStr += getTransitionDurationStyle(speedBP); } if (ANIMATIONDURATION) { slideStr += getAnimationDurationStyle(speedBP); } } if (slideStr) { slideStr = '#' + slideId + ' > .tns-item{' + slideStr + '}'; } // add up str = middleWrapperStr + innerWrapperStr + containerStr + slideStr; if (str) { sheet.insertRule('@media (min-width: ' + bp / 16 + 'em) {' + str + '}', sheet.cssRules.length); } } } } function initTools() { // == slides == updateSlideStatus(); // == live region == outerWrapper.insertAdjacentHTML('afterbegin', '<div class="tns-liveregion tns-visually-hidden" aria-live="polite" aria-atomic="true">slide <span class="current">' + getLiveRegionStr() + '</span> of ' + slideCount + '</div>'); liveregionCurrent = outerWrapper.querySelector('.tns-liveregion .current'); // == autoplayInit == if (hasAutoplay) { var txt = autoplay ? 'stop' : 'start'; if (autoplayButton) { setAttrs(autoplayButton, { 'data-action': txt }); } else if (options.autoplayButtonOutput) { outerWrapper.insertAdjacentHTML(getInsertPosition(options.autoplayPosition), '<button type="button" data-action="' + txt + '">' + autoplayHtmlStrings[0] + txt + autoplayHtmlStrings[1] + autoplayText[0] + '</button>'); autoplayButton = outerWrapper.querySelector('[data-action]'); } // add event if (autoplayButton) { addEvents(autoplayButton, { 'click': toggleAutoplay }); } if (autoplay) { startAutoplay(); if (autoplayHoverPause) { addEvents(container, hoverEvents); } if (autoplayResetOnVisibility) { addEvents(container, visibilityEvent); } } } // == navInit == if (hasNav) { // will not hide the navs in case they're thumbnails if (navContainer) { setAttrs(navContainer, { 'aria-label': 'Carousel Pagination' }); navItems = navContainer.children; forEach(navItems, function (item, i) { setAttrs(item, { 'data-nav': i, 'tabindex': '-1', 'aria-label': navStr + (i + 1), 'aria-controls': slideId }); }); // generated nav } else { var navHtml = '', hiddenStr = navAsThumbnails ? '' : 'style="display:none"'; for (var i = 0; i < slideCount; i++) { // hide nav items by default navHtml += '<button type="button" data-nav="' + i + '" tabindex="-1" aria-controls="' + slideId + '" ' + hiddenStr + ' aria-label="' + navStr + (i + 1) + '"></button>'; } navHtml = '<div class="tns-nav" aria-label="Carousel Pagination">' + navHtml + '</div>'; outerWrapper.insertAdjacentHTML(getInsertPosition(options.navPosition), navHtml); navContainer = outerWrapper.querySelector('.tns-nav'); navItems = navContainer.children; } updateNavVisibility(); // add transition if (TRANSITIONDURATION) { var prefix = TRANSITIONDURATION.substring(0, TRANSITIONDURATION.length - 18).toLowerCase(), str = 'transition: all ' + speed / 1000 + 's'; if (prefix) { str = '-' + prefix + '-' + str; } addCSSRule(sheet, '[aria-controls^=' + slideId + '-item]', str, getCssRulesLength(sheet)); } setAttrs(navItems[navCurrentIndex], { 'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent }); removeAttrs(navItems[navCurrentIndex], 'tabindex'); addClass(navItems[navCurrentIndex], navActiveClass); // add events addEvents(navContainer, navEvents); } // == controlsInit == if (hasControls) { if (!controlsContainer && (!prevButton || !nextButton)) { outerWrapper.insertAdjacentHTML(getInsertPosition(options.controlsPosition), '<div class="tns-controls" aria-label="Carousel Navigation" tabindex="0"><button type="button" data-controls="prev" tabindex="-1" aria-controls="' + slideId + '">' + controlsText[0] + '</button><button type="button" data-controls="next" tabindex="-1" aria-controls="' + slideId + '">' + controlsText[1] + '</button></div>'); controlsContainer = outerWrapper.querySelector('.tns-controls'); } if (!prevButton || !nextButton) { prevButton = controlsContainer.children[0]; nextButton = controlsContainer.children[1]; } if (options.controlsContainer) { setAttrs(controlsContainer, { 'aria-label': 'Carousel Navigation', 'tabindex': '0' }); } if (options.controlsContainer || options.prevButton && options.nextButton) { setAttrs([prevButton, nextButton], { 'aria-controls': slideId, 'tabindex': '-1' }); } if (options.controlsContainer || options.prevButton && options.nextButton) { setAttrs(prevButton, { 'data-controls': 'prev' }); setAttrs(nextButton, { 'data-controls': 'next' }); } prevIsButton = isButton(prevButton); nextIsButton = isButton(nextButton); updateControlsStatus(); // add events if (controlsContainer) { addEvents(controlsContainer, controlsEvents); } else { addEvents(prevButton, controlsEvents); addEvents(nextButton, controlsEvents); } } // hide tools if needed disableUI(); } function initEvents() { // add events if (carousel && TRANSITIONEND) { var eve = {}; eve[TRANSITIONEND] = onTransitionEnd; addEvents(container, eve); } if (touch) { addEvents(container, touchEvents, options.preventScrollOnTouch); } if (mouseDrag) { addEvents(container, dragEvents); } if (arrowKeys) { addEvents(doc, docmentKeydownEvent); } if (nested === 'inner') { events.on('outerResized', function () { resizeTasks(); events.emit('innerLoaded', info()); }); } else if (responsive || fixedWidth || autoWidth || autoHeight || !horizontal) { addEvents(win, { 'resize': onResize }); } if (autoHeight) { if (nested === 'outer') { events.on('innerLoaded', doAutoHeight); } else if (!disable) { doAutoHeight(); } } doLazyLoad(); if (disable) { disableSlider(); } else if (freeze) { freezeSlider(); } events.on('indexChanged', additionalUpdates); if (nested === 'inner') { events.emit('innerLoaded', info()); } if (typeof onInit === 'function') { onInit(info()); } isOn = true; } function destroy() { // sheet sheet.disabled = true; if (sheet.ownerNode) { sheet.ownerNode.remove(); } // remove win event listeners removeEvents(win, { 'resize': onResize }); // arrowKeys, controls, nav if (arrowKeys) { removeEvents(doc, docmentKeydownEvent); } if (controlsContainer) { removeEvents(controlsContainer, controlsEvents); } if (navContainer) { removeEvents(navContainer, navEvents); } // autoplay removeEvents(container, hoverEvents); removeEvents(container, visibilityEvent); if (autoplayButton) { removeEvents(autoplayButton, { 'click': toggleAutoplay }); } if (autoplay) { clearInterval(autoplayTimer); } // container if (carousel && TRANSITIONEND) { var eve = {}; eve[TRANSITIONEND] = onTransitionEnd; removeEvents(container, eve); } if (touch) { removeEvents(container, touchEvents); } if (mouseDrag) { removeEvents(container, dragEvents); } // cache Object values in options && reset HTML var htmlList = [containerHTML, controlsContainerHTML, prevButtonHTML, nextButtonHTML, navContainerHTML, autoplayButtonHTML]; tnsList.forEach(function (item, i) { var el = item === 'container' ? outerWrapper : options[item]; if (typeof el === 'object' && el) { var prevEl = el.previousElementSibling ? el.previousElementSibling : false, parentEl = el.parentNode; el.outerHTML = htmlList[i]; options[item] = prevEl ? prevEl.nextElementSibling : parentEl.firstElementChild; } }); // reset variables tnsList = animateIn = animateOut = animateDelay = animateNormal = horizontal = outerWrapper = innerWrapper = container = containerParent = containerHTML = slideItems = slideCount = breakpointZone = windowWidth = autoWidth = fixedWidth = edgePadding = gutter = viewport = items = slideBy = viewportMax = arrowKeys = speed = rewind = loop = autoHeight = sheet = lazyload = slidePositions = slideItemsOut = cloneCount = slideCountNew = hasRightDeadZone = rightBoundary = updateIndexBeforeTransform = transformAttr = transformPrefix = transformPostfix = getIndexMax = index = indexCached = indexMin = indexMax = swipeAngle = moveDirectionExpected = running = onInit = events = newContainerClasses = slideId = disable = disabled = freezable = freeze = frozen = controlsEvents = navEvents = hoverEvents = visibilityEvent = docmentKeydownEvent = touchEvents = dragEvents = hasControls = hasNav = navAsThumbnails = hasAutoplay = hasTouch = hasMouseDrag = slideActiveClass = imgCompleteClass = imgEvents = imgsComplete = controls = controlsText = controlsContainer = controlsContainerHTML = prevButton = nextButton = prevIsButton = nextIsButton = nav = navContainer = navContainerHTML = navItems = pages = pagesCached = navClicked = navCurrentIndex = navCurrentIndexCached = navActiveClass = navStr = navStrCurrent = autoplay = autoplayTimeout = autoplayDirection = autoplayText = autoplayHoverPause = autoplayButton = autoplayButtonHTML = autoplayResetOnVisibility = autoplayHtmlStrings = autoplayTimer = animating = autoplayHoverPaused = autoplayUserPaused = autoplayVisibilityPaused = initPosition = lastPosition = translateInit = panStart = rafIndex = getDist = touch = mouseDrag = null; // check variables // [animateIn, animateOut, animateDelay, animateNormal, horizontal, outerWrapper, innerWrapper, container, containerParent, containerHTML, slideItems, slideCount, breakpointZone, windowWidth, autoWidth, fixedWidth, edgePadding, gutter, viewport, items, slideBy, viewportMax, arrowKeys, speed, rewind, loop, autoHeight, sheet, lazyload, slidePositions, slideItemsOut, cloneCount, slideCountNew, hasRightDeadZone, rightBoundary, updateIndexBeforeTransform, transformAttr, transformPrefix, transformPostfix, getIndexMax, index, indexCached, indexMin, indexMax, resizeTimer, swipeAngle, moveDirectionExpected, running, onInit, events, newContainerClasses, slideId, disable, disabled, freezable, freeze, frozen, controlsEvents, navEvents, hoverEvents, visibilityEvent, docmentKeydownEvent, touchEvents, dragEvents, hasControls, hasNav, navAsThumbnails, hasAutoplay, hasTouch, hasMouseDrag, slideActiveClass, imgCompleteClass, imgEvents, imgsComplete, controls, controlsText, controlsContainer, controlsContainerHTML, prevButton, nextButton, prevIsButton, nextIsButton, nav, navContainer, navContainerHTML, navItems, pages, pagesCached, navClicked, navCurrentIndex, navCurrentIndexCached, navActiveClass, navStr, navStrCurrent, autoplay, autoplayTimeout, autoplayDirection, autoplayText, autoplayHoverPause, autoplayButton, autoplayButtonHTML, autoplayResetOnVisibility, autoplayHtmlStrings, autoplayTimer, animating, autoplayHoverPaused, autoplayUserPaused, autoplayVisibilityPaused, initPosition, lastPosition, translateInit, disX, disY, panStart, rafIndex, getDist, touch, mouseDrag ].forEach(function(item) { if (item !== null) { console.log(item); } }); for (var a in this) { if (a !== 'rebuild') { this[a] = null; } } isOn = false; } // === ON RESIZE === // responsive || fixedWidth || autoWidth || !horizontal function onResize(e) { raf(function () { resizeTasks(getEvent(e)); }); } function resizeTasks(e) { if (!isOn) { return; } if (nested === 'outer') { events.emit('outerResized', info(e)); } windowWidth = getWindowWidth(); var bpChanged, breakpointZoneTem = breakpointZone, needContainerTransform = false; if (responsive) { setBreakpointZone(); bpChanged = breakpointZoneTem !== breakpointZone; // if (hasRightDeadZone) { needContainerTransform = true; } // *? if (bpChanged) { events.emit('newBreakpointStart', info(e)); } } var indChanged, itemsChanged, itemsTem = items, disableTem = disable, freezeTem = freeze, arrowKeysTem = arrowKeys, controlsTem = controls, navTem = nav, touchTem = touch, mouseDragTem = mouseDrag, autoplayTem = autoplay, autoplayHoverPauseTem = autoplayHoverPause, autoplayResetOnVisibilityTem = autoplayResetOnVisibility, indexTem = index; if (bpChanged) { var fixedWidthTem = fixedWidth, autoHeightTem = autoHeight, controlsTextTem = controlsText, centerTem = center, autoplayTextTem = autoplayText; if (!CSSMQ) { var gutterTem = gutter, edgePaddingTem = edgePadding; } } // get option: // fixed width: viewport, fixedWidth, gutter => items // others: window width => all variables // all: items => slideBy arrowKeys = getOption('arrowKeys'); controls = getOption('controls'); nav = getOption('nav'); touch = getOption('touch'); center = getOption('center'); mouseDrag = getOption('mouseDrag'); autoplay = getOption('autoplay'); autoplayHoverPause = getOption('autoplayHoverPause'); autoplayResetOnVisibility = getOption('autoplayResetOnVisibility'); if (bpChanged) { disable = getOption('disable'); fixedWidth = getOption('fixedWidth'); speed = getOption('speed'); autoHeight = getOption('autoHeight'); controlsText = getOption('controlsText'); autoplayText = getOption('autoplayText'); autoplayTimeout = getOption('autoplayTimeout'); if (!CSSMQ) { edgePadding = getOption('edgePadding'); gutter = getOption('gutter'); } } // update options resetVariblesWhenDisable(disable); viewport = getViewportWidth(); // <= edgePadding, gutter if ((!horizontal || autoWidth) && !disable) { setSlidePositions(); if (!horizontal) { updateContentWrapperHeight(); // <= setSlidePositions needContainerTransform = true; } } if (fixedWidth || autoWidth) { rightBoundary = getRightBoundary(); // autoWidth: <= viewport, slidePositions, gutter // fixedWidth: <= viewport, fixedWidth, gutter indexMax = getIndexMax(); // autoWidth: <= rightBoundary, slidePositions // fixedWidth: <= rightBoundary, fixedWidth, gutter } if (bpChanged || fixedWidth) { items = getOption('items'); slideBy = getOption('slideBy'); itemsChanged = items !== itemsTem; if (itemsChanged) { if (!fixedWidth && !autoWidth) { indexMax = getIndexMax(); } // <= items // check index before transform in case // slider reach the right edge then items become bigger updateIndex(); } } if (bpChanged) { if (disable !== disableTem) { if (disable) { disableSlider(); } else { enableSlider(); // <= slidePositions, rightBoundary, indexMax } } } if (freezable && (bpChanged || fixedWidth || autoWidth)) { freeze = getFreeze(); // <= autoWidth: slidePositions, gutter, viewport, rightBoundary // <= fixedWidth: fixedWidth, gutter, rightBoundary // <= others: items if (freeze !== freezeTem) { if (freeze) { doContainerTransform(getContainerTransformValue(getStartIndex(0))); freezeSlider(); } else { unfreezeSlider(); needContainerTransform = true; } } } resetVariblesWhenDisable(disable || freeze); // controls, nav, touch, mouseDrag, arrowKeys, autoplay, autoplayHoverPause, autoplayResetOnVisibility if (!autoplay) { autoplayHoverPause = autoplayResetOnVisibility = false; } if (arrowKeys !== arrowKeysTem) { arrowKeys ? addEvents(doc, docmentKeydownEvent) : removeEvents(doc, docmentKeydownEvent); } if (controls !== controlsTem) { if (controls) { if (controlsContainer) { showElement(controlsContainer); } else { if (prevButton) { showElement(prevButton); } if (nextButton) { showElement(nextButton); } } } else { if (controlsContainer) { hideElement(controlsContainer); } else { if (prevButton) { hideElement(prevButton); } if (nextButton) { hideElement(nextButton); } } } } if (nav !== navTem) { if (nav) { showElement(navContainer); updateNavVisibility(); } else { hideElement(navContainer); } } if (touch !== touchTem) { touch ? addEvents(container, touchEvents, options.preventScrollOnTouch) : removeEvents(container, touchEvents); } if (mouseDrag !== mouseDragTem) { mouseDrag ? addEvents(container, dragEvents) : removeEvents(container, dragEvents); } if (autoplay !== autoplayTem) { if (autoplay) { if (autoplayButton) { showElement(autoplayButton); } if (!animating && !autoplayUserPaused) { startAutoplay(); } } else { if (autoplayButton) { hideElement(autoplayButton); } if (animating) { stopAutoplay(); } } } if (autoplayHoverPause !== autoplayHoverPauseTem) { autoplayHoverPause ? addEvents(container, hoverEvents) : removeEvents(container, hoverEvents); } if (autoplayResetOnVisibility !== autoplayResetOnVisibilityTem) { autoplayResetOnVisibility ? addEvents(doc, visibilityEvent) : removeEvents(doc, visibilityEvent); } if (bpChanged) { if (fixedWidth !== fixedWidthTem || center !== centerTem) { needContainerTransform = true; } if (autoHeight !== autoHeightTem) { if (!autoHeight) { innerWrapper.style.height = ''; } } if (controls && controlsText !== controlsTextTem) { prevButton.innerHTML = controlsText[0]; nextButton.innerHTML = controlsText[1]; } if (autoplayButton && autoplayText !== autoplayTextTem) { var i = autoplay ? 1 : 0, html = autoplayButton.innerHTML, len = html.length - autoplayTextTem[i].length; if (html.substring(len) === autoplayTextTem[i]) { autoplayButton.innerHTML = html.substring(0, len) + autoplayText[i]; } } } else { if (center && (fixedWidth || autoWidth)) { needContainerTransform = true; } } if (itemsChanged || fixedWidth && !autoWidth) { pages = getPages(); updateNavVisibility(); } indChanged = index !== indexTem; if (indChanged) { events.emit('indexChanged', info()); needContainerTransform = true; } else if (itemsChanged) { if (!indChanged) { additionalUpdates(); } } else if (fixedWidth || autoWidth) { doLazyLoad(); updateSlideStatus(); updateLiveRegion(); } if (itemsChanged && !carousel) { updateGallerySlidePositions(); } if (!disable && !freeze) { // non-mediaqueries: IE8 if (bpChanged && !CSSMQ) { // middle wrapper styles // inner wrapper styles if (edgePadding !== edgePaddingTem || gutter !== gutterTem) { innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, speed, autoHeight); } if (horizontal) { // container styles if (carousel) { container.style.width = getContainerWidth(fixedWidth, gutter, items); } // slide styles var str = getSlideWidthStyle(fixedWidth, gutter, items) + getSlideGutterStyle(gutter); // remove the last line and // add new styles removeCSSRule(sheet, getCssRulesLength(sheet) - 1); addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); } } // auto height if (autoHeight) { doAutoHeight(); } if (needContainerTransform) { doContainerTransformSilent(); indexCached = index; } } if (bpChanged) { events.emit('newBreakpointEnd', info(e)); } } // === INITIALIZATION FUNCTIONS === // function getFreeze() { if (!fixedWidth && !autoWidth) { var a = center ? items - (items - 1) / 2 : items; return slideCount <= a; } var width = fixedWidth ? (fixedWidth + gutter) * slideCount : slidePositions[slideCount], vp = edgePadding ? viewport + edgePadding * 2 : viewport + gutter; if (center) { vp -= fixedWidth ? (viewport - fixedWidth) / 2 : (viewport - (slidePositions[index + 1] - slidePositions[index] - gutter)) / 2; } return width <= vp; } function setBreakpointZone() { breakpointZone = 0; for (var bp in responsive) { bp = parseInt(bp); // convert string to number if (windowWidth >= bp) { breakpointZone = bp; } } } // (slideBy, indexMin, indexMax) => index var updateIndex = function () { return loop ? carousel ? // loop + carousel function () { var leftEdge = indexMin, rightEdge = indexMax; leftEdge += slideBy; rightEdge -= slideBy; // adjust edges when has edge paddings // or fixed-width slider with extra space on the right side if (edgePadding) { leftEdge += 1; rightEdge -= 1; } else if (fixedWidth) { if ((viewport + gutter) % (fixedWidth + gutter)) { rightEdge -= 1; } } if (cloneCount) { if (index > rightEdge) { index -= slideCount; } else if (index < leftEdge) { index += slideCount; } } } : // loop + gallery function () { if (index > indexMax) { while (index >= indexMin + slideCount) { index -= slideCount; } } else if (index < indexMin) { while (index <= indexMax - slideCount) { index += slideCount; } } } : // non-loop function () { index = Math.max(indexMin, Math.min(indexMax, index)); }; }(); function disableUI() { if (!autoplay && autoplayButton) { hideElement(autoplayButton); } if (!nav && navContainer) { hideElement(navContainer); } if (!controls) { if (controlsContainer) { hideElement(controlsContainer); } else { if (prevButton) { hideElement(prevButton); } if (nextButton) { hideElement(nextButton); } } } } function enableUI() { if (autoplay && autoplayButton) { showElement(autoplayButton); } if (nav && navContainer) { showElement(navContainer); } if (controls) { if (controlsContainer) { showElement(controlsContainer); } else { if (prevButton) { showElement(prevButton); } if (nextButton) { showElement(nextButton); } } } } function freezeSlider() { if (frozen) { return; } // remove edge padding from inner wrapper if (edgePadding) { innerWrapper.style.margin = '0px'; } // add class tns-transparent to cloned slides if (cloneCount) { var str = 'tns-transparent'; for (var i = cloneCount; i--;) { if (carousel) { addClass(slideItems[i], str); } addClass(slideItems[slideCountNew - i - 1], str); } } // update tools disableUI(); frozen = true; } function unfreezeSlider() { if (!frozen) { return; } // restore edge padding for inner wrapper // for mordern browsers if (edgePadding && CSSMQ) { innerWrapper.style.margin = ''; } // remove class tns-transparent to cloned slides if (cloneCount) { var str = 'tns-transparent'; for (var i = cloneCount; i--;) { if (carousel) { removeClass(slideItems[i], str); } removeClass(slideItems[slideCountNew - i - 1], str); } } // update tools enableUI(); frozen = false; } function disableSlider() { if (disabled) { return; } sheet.disabled = true; container.className = container.className.replace(newContainerClasses.substring(1), ''); removeAttrs(container, ['style']); if (loop) { for (var j = cloneCount; j--;) { if (carousel) { hideElement(slideItems[j]); } hideElement(slideItems[slideCountNew - j - 1]); } } // vertical slider if (!horizontal || !carousel) { removeAttrs(innerWrapper, ['style']); } // gallery if (!carousel) { for (var i = index, l = index + slideCount; i < l; i++) { var item = slideItems[i]; removeAttrs(item, ['style']); removeClass(item, animateIn); removeClass(item, animateNormal); } } // update tools disableUI(); disabled = true; } function enableSlider() { if (!disabled) { return; } sheet.disabled = false; container.className += newContainerClasses; doContainerTransformSilent(); if (loop) { for (var j = cloneCount; j--;) { if (carousel) { showElement(slideItems[j]); } showElement(slideItems[slideCountNew - j - 1]); } } // gallery if (!carousel) { for (var i = index, l = index + slideCount; i < l; i++) { var item = slideItems[i], classN = i < index + items ? animateIn : animateNormal; item.style.left = (i - index) * 100 / items + '%'; addClass(item, classN); } } // update tools enableUI(); disabled = false; } function updateLiveRegion() { var str = getLiveRegionStr(); if (liveregionCurrent.innerHTML !== str) { liveregionCurrent.innerHTML = str; } } function getLiveRegionStr() { var arr = getVisibleSlideRange(), start = arr[0] + 1, end = arr[1] + 1; return start === end ? start + '' : start + ' to ' + end; } function getVisibleSlideRange(val) { if (val == null) { val = getContainerTransformValue(); } var start = index, end, rangestart, rangeend; // get range start, range end for autoWidth and fixedWidth if (center || edgePadding) { if (autoWidth || fixedWidth) { rangestart = -(parseFloat(val) + edgePadding); rangeend = rangestart + viewport + edgePadding * 2; } } else { if (autoWidth) { rangestart = slidePositions[index]; rangeend = rangestart + viewport; } } // get start, end // - check auto width if (autoWidth) { slidePositions.forEach(function (point, i) { if (i < slideCountNew) { if ((center || edgePadding) && point <= rangestart + 0.5) { start = i; } if (rangeend - point >= 0.5) { end = i; } } }); // - check percentage width, fixed width } else { if (fixedWidth) { var cell = fixedWidth + gutter; if (center || edgePadding) { start = Math.floor(rangestart / cell); end = Math.ceil(rangeend / cell - 1); } else { end = start + Math.ceil(viewport / cell) - 1; } } else { if (center || edgePadding) { var a = items - 1; if (center) { start -= a / 2; end = index + a / 2; } else { end = index + a; } if (edgePadding) { var b = edgePadding * items / viewport; start -= b; end += b; } start = Math.floor(start); end = Math.ceil(end); } else { end = start + items - 1; } } start = Math.max(start, 0); end = Math.min(end, slideCountNew - 1); } return [start, end]; } function doLazyLoad() { if (lazyload && !disable) { var arg = getVisibleSlideRange(); arg.push(lazyloadSelector); getImageArray.apply(null, arg).forEach(function (img) { if (!hasClass(img, imgCompleteClass)) { // stop propagation transitionend event to container var eve = {}; eve[TRANSITIONEND] = function (e) { e.stopPropagation(); }; addEvents(img, eve); addEvents(img, imgEvents); // update src img.src = getAttr(img, 'data-src'); // update srcset var srcset = getAttr(img, 'data-srcset'); if (srcset) { img.srcset = srcset; } addClass(img, 'loading'); } }); } } function onImgLoaded(e) { imgLoaded(getTarget(e)); } function onImgFailed(e) { imgFailed(getTarget(e)); } function imgLoaded(img) { addClass(img, 'loaded'); imgCompleted(img); } function imgFailed(img) { addClass(img, 'failed'); imgCompleted(img); } function imgCompleted(img) { addClass(img, imgCompleteClass); removeClass(img, 'loading'); removeEvents(img, imgEvents); } function getImageArray(start, end, imgSelector) { var imgs = []; if (!imgSelector) { imgSelector = 'img'; } while (start <= end) { forEach(slideItems[start].querySelectorAll(imgSelector), function (img) { imgs.push(img); }); start++; } return imgs; } // check if all visible images are loaded // and update container height if it's done function doAutoHeight() { var imgs = getImageArray.apply(null, getVisibleSlideRange()); raf(function () { imgsLoadedCheck(imgs, updateInnerWrapperHeight); }); } function imgsLoadedCheck(imgs, cb) { // execute callback function if all images are complete if (imgsComplete) { return cb(); } // check image classes imgs.forEach(function (img, index) { if (!lazyload && img.complete) { imgCompleted(img); } // Check image.complete if (hasClass(img, imgCompleteClass)) { imgs.splice(index, 1); } }); // execute callback function if selected images are all complete if (!imgs.length) { return cb(); } // otherwise execute this functiona again raf(function () { imgsLoadedCheck(imgs, cb); }); } function additionalUpdates() { doLazyLoad(); updateSlideStatus(); updateLiveRegion(); updateControlsStatus(); updateNavStatus(); } function update_carousel_transition_duration() { if (carousel && autoHeight) { middleWrapper.style[TRANSITIONDURATION] = speed / 1000 + 's'; } } function getMaxSlideHeight(slideStart, slideRange) { var heights = []; for (var i = slideStart, l = Math.min(slideStart + slideRange, slideCountNew); i < l; i++) { heights.push(slideItems[i].offsetHeight); } return Math.max.apply(null, heights); } // update inner wrapper height // 1. get the max-height of the visible slides // 2. set transitionDuration to speed // 3. update inner wrapper height to max-height // 4. set transitionDuration to 0s after transition done function updateInnerWrapperHeight() { var maxHeight = autoHeight ? getMaxSlideHeight(index, items) : getMaxSlideHeight(cloneCount, slideCount), wp = middleWrapper ? middleWrapper : innerWrapper; if (wp.style.height !== maxHeight) { wp.style.height = maxHeight + 'px'; } } // get the distance from the top edge of the first slide to each slide // (init) => slidePositions function setSlidePositions() { slidePositions = [0]; var attr = horizontal ? 'left' : 'top', attr2 = horizontal ? 'right' : 'bottom', base = slideItems[0].getBoundingClientRect()[attr]; forEach(slideItems, function (item, i) { // skip the first slide if (i) { slidePositions.push(item.getBoundingClientRect()[attr] - base); } // add the end edge if (i === slideCountNew - 1) { slidePositions.push(item.getBoundingClientRect()[attr2] - base); } }); } // update slide function updateSlideStatus() { var range = getVisibleSlideRange(), start = range[0], end = range[1]; forEach(slideItems, function (item, i) { // show slides if (i >= start && i <= end) { if (hasAttr(item, 'aria-hidden')) { removeAttrs(item, ['aria-hidden', 'tabindex']); addClass(item, slideActiveClass); } // hide slides } else { if (!hasAttr(item, 'aria-hidden')) { setAttrs(item, { 'aria-hidden': 'true', 'tabindex': '-1' }); removeClass(item, slideActiveClass); } } }); } // gallery: update slide position function updateGallerySlidePositions() { var l = index + Math.min(slideCount, items); for (var i = slideCountNew; i--;) { var item = slideItems[i]; if (i >= index && i < l) { // add transitions to visible slides when adjusting their positions addClass(item, 'tns-moving'); item.style.left = (i - index) * 100 / items + '%'; addClass(item, animateIn); removeClass(item, animateNormal); } else if (item.style.left) { item.style.left = ''; addClass(item, animateNormal); removeClass(item, animateIn); } // remove outlet animation removeClass(item, animateOut); } // removing '.tns-moving' setTimeout(function () { forEach(slideItems, function (el) { removeClass(el, 'tns-moving'); }); }, 300); } // set tabindex on Nav function updateNavStatus() { // get current nav if (nav) { navCurrentIndex = navClicked >= 0 ? navClicked : getCurrentNavIndex(); navClicked = -1; if (navCurrentIndex !== navCurrentIndexCached) { var navPrev = navItems[navCurrentIndexCached], navCurrent = navItems[navCurrentIndex]; setAttrs(navPrev, { 'tabindex': '-1', 'aria-label': navStr + (navCurrentIndexCached + 1) }); removeClass(navPrev, navActiveClass); setAttrs(navCurrent, { 'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent }); removeAttrs(navCurrent, 'tabindex'); addClass(navCurrent, navActiveClass); navCurrentIndexCached = navCurrentIndex; } } } function getLowerCaseNodeName(el) { return el.nodeName.toLowerCase(); } function isButton(el) { return getLowerCaseNodeName(el) === 'button'; } function isAriaDisabled(el) { return el.getAttribute('aria-disabled') === 'true'; } function disEnableElement(isButton, el, val) { if (isButton) { el.disabled = val; } else { el.setAttribute('aria-disabled', val.toString()); } } // set 'disabled' to true on controls when reach the edges function updateControlsStatus() { if (!controls || rewind || loop) { return; } var prevDisabled = prevIsButton ? prevButton.disabled : isAriaDisabled(prevButton), nextDisabled = nextIsButton ? nextButton.disabled : isAriaDisabled(nextButton), disablePrev = index <= indexMin ? true : false, disableNext = !rewind && index >= indexMax ? true : false; if (disablePrev && !prevDisabled) { disEnableElement(prevIsButton, prevButton, true); } if (!disablePrev && prevDisabled) { disEnableElement(prevIsButton, prevButton, false); } if (disableNext && !nextDisabled) { disEnableElement(nextIsButton, nextButton, true); } if (!disableNext && nextDisabled) { disEnableElement(nextIsButton, nextButton, false); } } // set duration function resetDuration(el, str) { if (TRANSITIONDURATION) { el.style[TRANSITIONDURATION] = str; } } function getSliderWidth() { return fixedWidth ? (fixedWidth + gutter) * slideCountNew : slidePositions[slideCountNew]; } function getCenterGap(num) { if (num == null) { num = index; } var gap = edgePadding ? gutter : 0; return autoWidth ? (viewport - gap - (slidePositions[num + 1] - slidePositions[num] - gutter)) / 2 : fixedWidth ? (viewport - fixedWidth) / 2 : (items - 1) / 2; } function getRightBoundary() { var gap = edgePadding ? gutter : 0, result = viewport + gap - getSliderWidth(); if (center && !loop) { result = fixedWidth ? -(fixedWidth + gutter) * (slideCountNew - 1) - getCenterGap() : getCenterGap(slideCountNew - 1) - slidePositions[slideCountNew - 1]; } if (result > 0) { result = 0; } return result; } function getContainerTransformValue(num) { if (num == null) { num = index; } var val; if (horizontal && !autoWidth) { if (fixedWidth) { val = -(fixedWidth + gutter) * num; if (center) { val += getCenterGap(); } } else { var denominator = TRANSFORM ? slideCountNew : items; if (center) { num -= getCenterGap(); } val = -num * 100 / denominator; } } else { val = -slidePositions[num]; if (center && autoWidth) { val += getCenterGap(); } } if (hasRightDeadZone) { val = Math.max(val, rightBoundary); } val += horizontal && !autoWidth && !fixedWidth ? '%' : 'px'; return val; } function doContainerTransformSilent(val) { resetDuration(container, '0s'); doContainerTransform(val); } function doContainerTransform(val) { if (val == null) { val = getContainerTransformValue(); } container.style[transformAttr] = transformPrefix + val + transformPostfix; } function animateSlide(number, classOut, classIn, isOut) { var l = number + items; if (!loop) { l = Math.min(l, slideCountNew); } for (var i = number; i < l; i++) { var item = slideItems[i]; // set item positions if (!isOut) { item.style.left = (i - index) * 100 / items + '%'; } if (animateDelay && TRANSITIONDELAY) { item.style[TRANSITIONDELAY] = item.style[ANIMATIONDELAY] = animateDelay * (i - number) / 1000 + 's'; } removeClass(item, classOut); addClass(item, classIn); if (isOut) { slideItemsOut.push(item); } } } // make transfer after click/drag: // 1. change 'transform' property for mordern browsers // 2. change 'left' property for legacy browsers var transformCore = function () { return carousel ? function () { resetDuration(container, ''); if (TRANSITIONDURATION || !speed) { // for morden browsers with non-zero duration or // zero duration for all browsers doContainerTransform(); // run fallback function manually // when duration is 0 / container is hidden if (!speed || !isVisible(container)) { onTransitionEnd(); } } else { // for old browser with non-zero duration jsTransform(container, transformAttr, transformPrefix, transformPostfix, getContainerTransformValue(), speed, onTransitionEnd); } if (!horizontal) { updateContentWrapperHeight(); } } : function () { slideItemsOut = []; var eve = {}; eve[TRANSITIONEND] = eve[ANIMATIONEND] = onTransitionEnd; removeEvents(slideItems[indexCached], eve); addEvents(slideItems[index], eve); animateSlide(indexCached, animateIn, animateOut, true); animateSlide(index, animateNormal, animateIn); // run fallback function manually // when transition or animation not supported / duration is 0 if (!TRANSITIONEND || !ANIMATIONEND || !speed || !isVisible(container)) { onTransitionEnd(); } }; }(); function render(e, sliderMoved) { if (updateIndexBeforeTransform) { updateIndex(); } // render when slider was moved (touch or drag) even though index may not change if (index !== indexCached || sliderMoved) { // events events.emit('indexChanged', info()); events.emit('transitionStart', info()); if (autoHeight) { doAutoHeight(); } // pause autoplay when click or keydown from user if (animating && e && ['click', 'keydown'].indexOf(e.type) >= 0) { stopAutoplay(); } running = true; transformCore(); } } /* * Transfer prefixed properties to the same format * CSS: -Webkit-Transform => webkittransform * JS: WebkitTransform => webkittransform * @param {string} str - property * */ function strTrans(str) { return str.toLowerCase().replace(/-/g, ''); } // AFTER TRANSFORM // Things need to be done after a transfer: // 1. check index // 2. add classes to visible slide // 3. disable controls buttons when reach the first/last slide in non-loop slider // 4. update nav status // 5. lazyload images // 6. update container height function onTransitionEnd(event) { // check running on gallery mode // make sure trantionend/animationend events run only once if (carousel || running) { events.emit('transitionEnd', info(event)); if (!carousel && slideItemsOut.length > 0) { for (var i = 0; i < slideItemsOut.length; i++) { var item = slideItemsOut[i]; // set item positions item.style.left = ''; if (ANIMATIONDELAY && TRANSITIONDELAY) { item.style[ANIMATIONDELAY] = ''; item.style[TRANSITIONDELAY] = ''; } removeClass(item, animateOut); addClass(item, animateNormal); } } /* update slides, nav, controls after checking ... * => legacy browsers who don't support 'event' * have to check event first, otherwise event.target will cause an error * => or 'gallery' mode: * + event target is slide item * => or 'carousel' mode: * + event target is container, * + event.property is the same with transform attribute */ if (!event || !carousel && event.target.parentNode === container || event.target === container && strTrans(event.propertyName) === strTrans(transformAttr)) { if (!updateIndexBeforeTransform) { var indexTem = index; updateIndex(); if (index !== indexTem) { events.emit('indexChanged', info()); doContainerTransformSilent(); } } if (nested === 'inner') { events.emit('innerLoaded', info()); } running = false; indexCached = index; } } } // # ACTIONS function goTo(targetIndex, e) { if (freeze) { return; } // prev slideBy if (targetIndex === 'prev') { onControlsClick(e, -1); // next slideBy } else if (targetIndex === 'next') { onControlsClick(e, 1); // go to exact slide } else { if (running) { if (preventActionWhenRunning) { return; } else { onTransitionEnd(); } } var absIndex = getAbsIndex(), indexGap = 0; if (targetIndex === 'first') { indexGap = -absIndex; } else if (targetIndex === 'last') { indexGap = carousel ? slideCount - items - absIndex : slideCount - 1 - absIndex; } else { if (typeof targetIndex !== 'number') { targetIndex = parseInt(targetIndex); } if (!isNaN(targetIndex)) { // from directly called goTo function if (!e) { targetIndex = Math.max(0, Math.min(slideCount - 1, targetIndex)); } indexGap = targetIndex - absIndex; } } // gallery: make sure new page won't overlap with current page if (!carousel && indexGap && Math.abs(indexGap) < items) { var factor = indexGap > 0 ? 1 : -1; indexGap += index + indexGap - slideCount >= indexMin ? slideCount * factor : slideCount * 2 * factor * -1; } index += indexGap; // make sure index is in range if (carousel && loop) { if (index < indexMin) { index += slideCount; } if (index > indexMax) { index -= slideCount; } } // if index is changed, start rendering if (getAbsIndex(index) !== getAbsIndex(indexCached)) { render(e); } } } // on controls click function onControlsClick(e, dir) { if (running) { if (preventActionWhenRunning) { return; } else { onTransitionEnd(); } } var passEventObject; if (!dir) { e = getEvent(e); var target = getTarget(e); while (target !== controlsContainer && [prevButton, nextButton].indexOf(target) < 0) { target = target.parentNode; } var targetIn = [prevButton, nextButton].indexOf(target); if (targetIn >= 0) { passEventObject = true; dir = targetIn === 0 ? -1 : 1; } } if (rewind) { if (index === indexMin && dir === -1) { goTo('last', e); return; } else if (index === indexMax && dir === 1) { goTo('first', e); return; } } if (dir) { index += slideBy * dir; if (autoWidth) { index = Math.floor(index); } // pass e when click control buttons or keydown render(passEventObject || e && e.type === 'keydown' ? e : null); } } // on nav click function onNavClick(e) { if (running) { if (preventActionWhenRunning) { return; } else { onTransitionEnd(); } } e = getEvent(e); var target = getTarget(e), navIndex; // find the clicked nav item while (target !== navContainer && !hasAttr(target, 'data-nav')) { target = target.parentNode; } if (hasAttr(target, 'data-nav')) { var navIndex = navClicked = Number(getAttr(target, 'data-nav')), targetIndexBase = fixedWidth || autoWidth ? navIndex * slideCount / pages : navIndex * items, targetIndex = navAsThumbnails ? navIndex : Math.min(Math.ceil(targetIndexBase), slideCount - 1); goTo(targetIndex, e); if (navCurrentIndex === navIndex) { if (animating) { stopAutoplay(); } navClicked = -1; // reset navClicked } } } // autoplay functions function setAutoplayTimer() { autoplayTimer = setInterval(function () { onControlsClick(null, autoplayDirection); }, autoplayTimeout); animating = true; } function stopAutoplayTimer() { clearInterval(autoplayTimer); animating = false; } function updateAutoplayButton(action, txt) { setAttrs(autoplayButton, { 'data-action': action }); autoplayButton.innerHTML = autoplayHtmlStrings[0] + action + autoplayHtmlStrings[1] + txt; } function startAutoplay() { setAutoplayTimer(); if (autoplayButton) { updateAutoplayButton('stop', autoplayText[1]); } } function stopAutoplay() { stopAutoplayTimer(); if (autoplayButton) { updateAutoplayButton('start', autoplayText[0]); } } // programaitcally play/pause the slider function play() { if (autoplay && !animating) { startAutoplay(); autoplayUserPaused = false; } } function pause() { if (animating) { stopAutoplay(); autoplayUserPaused = true; } } function toggleAutoplay() { if (animating) { stopAutoplay(); autoplayUserPaused = true; } else { startAutoplay(); autoplayUserPaused = false; } } function onVisibilityChange() { if (doc.hidden) { if (animating) { stopAutoplayTimer(); autoplayVisibilityPaused = true; } } else if (autoplayVisibilityPaused) { setAutoplayTimer(); autoplayVisibilityPaused = false; } } function mouseoverPause() { if (animating) { stopAutoplayTimer(); autoplayHoverPaused = true; } } function mouseoutRestart() { if (autoplayHoverPaused) { setAutoplayTimer(); autoplayHoverPaused = false; } } // keydown events on document function onDocumentKeydown(e) { e = getEvent(e); var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode); if (keyIndex >= 0) { onControlsClick(e, keyIndex === 0 ? -1 : 1); } } // on key control function onControlsKeydown(e) { e = getEvent(e); var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode); if (keyIndex >= 0) { if (keyIndex === 0) { if (!prevButton.disabled) { onControlsClick(e, -1); } } else if (!nextButton.disabled) { onControlsClick(e, 1); } } } // set focus function setFocus(el) { el.focus(); } // on key nav function onNavKeydown(e) { e = getEvent(e); var curElement = doc.activeElement; if (!hasAttr(curElement, 'data-nav')) { return; } // var code = e.keyCode, var keyIndex = [KEYS.LEFT, KEYS.RIGHT, KEYS.ENTER, KEYS.SPACE].indexOf(e.keyCode), navIndex = Number(getAttr(curElement, 'data-nav')); if (keyIndex >= 0) { if (keyIndex === 0) { if (navIndex > 0) { setFocus(navItems[navIndex - 1]); } } else if (keyIndex === 1) { if (navIndex < pages - 1) { setFocus(navItems[navIndex + 1]); } } else { navClicked = navIndex; goTo(navIndex, e); } } } function getEvent(e) { e = e || win.event; return isTouchEvent(e) ? e.changedTouches[0] : e; } function getTarget(e) { return e.target || win.event.srcElement; } function isTouchEvent(e) { return e.type.indexOf('touch') >= 0; } function preventDefaultBehavior(e) { e.preventDefault ? e.preventDefault() : e.returnValue = false; } function getMoveDirectionExpected() { return getTouchDirection(toDegree(lastPosition.y - initPosition.y, lastPosition.x - initPosition.x), swipeAngle) === options.axis; } function onPanStart(e) { if (running) { if (preventActionWhenRunning) { return; } else { onTransitionEnd(); } } if (autoplay && animating) { stopAutoplayTimer(); } panStart = true; if (rafIndex) { caf(rafIndex); rafIndex = null; } var $ = getEvent(e); events.emit(isTouchEvent(e) ? 'touchStart' : 'dragStart', info(e)); if (!isTouchEvent(e) && ['img', 'a'].indexOf(getLowerCaseNodeName(getTarget(e))) >= 0) { preventDefaultBehavior(e); } lastPosition.x = initPosition.x = $.clientX; lastPosition.y = initPosition.y = $.clientY; if (carousel) { translateInit = parseFloat(container.style[transformAttr].replace(transformPrefix, '')); resetDuration(container, '0s'); } } function onPanMove(e) { if (panStart) { var $ = getEvent(e); lastPosition.x = $.clientX; lastPosition.y = $.clientY; if (carousel) { if (!rafIndex) { rafIndex = raf(function () { panUpdate(e); }); } } else { if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); } if (moveDirectionExpected) { preventScroll = true; } } if ((typeof e.cancelable !== 'boolean' || e.cancelable) && preventScroll) { e.preventDefault(); } } } function panUpdate(e) { if (!moveDirectionExpected) { panStart = false; return; } caf(rafIndex); if (panStart) { rafIndex = raf(function () { panUpdate(e); }); } if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); } if (moveDirectionExpected) { if (!preventScroll && isTouchEvent(e)) { preventScroll = true; } try { if (e.type) { events.emit(isTouchEvent(e) ? 'touchMove' : 'dragMove', info(e)); } } catch (err) {} var x = translateInit, dist = getDist(lastPosition, initPosition); if (!horizontal || fixedWidth || autoWidth) { x += dist; x += 'px'; } else { var percentageX = TRANSFORM ? dist * items * 100 / ((viewport + gutter) * slideCountNew) : dist * 100 / (viewport + gutter); x += percentageX; x += '%'; } container.style[transformAttr] = transformPrefix + x + transformPostfix; } } function onPanEnd(e) { if (panStart) { if (rafIndex) { caf(rafIndex); rafIndex = null; } if (carousel) { resetDuration(container, ''); } panStart = false; var $ = getEvent(e); lastPosition.x = $.clientX; lastPosition.y = $.clientY; var dist = getDist(lastPosition, initPosition); if (Math.abs(dist)) { // drag vs click if (!isTouchEvent(e)) { // prevent "click" var target = getTarget(e); addEvents(target, { 'click': function preventClick(e) { preventDefaultBehavior(e); removeEvents(target, { 'click': preventClick }); } }); } if (carousel) { rafIndex = raf(function () { if (horizontal && !autoWidth) { var indexMoved = -dist * items / (viewport + gutter); indexMoved = dist > 0 ? Math.floor(indexMoved) : Math.ceil(indexMoved); index += indexMoved; } else { var moved = -(translateInit + dist); if (moved <= 0) { index = indexMin; } else if (moved >= slidePositions[slideCountNew - 1]) { index = indexMax; } else { var i = 0; while (i < slideCountNew && moved >= slidePositions[i]) { index = i; if (moved > slidePositions[i] && dist < 0) { index += 1; } i++; } } } render(e, dist); events.emit(isTouchEvent(e) ? 'touchEnd' : 'dragEnd', info(e)); }); } else { if (moveDirectionExpected) { onControlsClick(e, dist > 0 ? -1 : 1); } } } } // reset if (options.preventScrollOnTouch === 'auto') { preventScroll = false; } if (swipeAngle) { moveDirectionExpected = '?'; } if (autoplay && !animating) { setAutoplayTimer(); } } // === RESIZE FUNCTIONS === // // (slidePositions, index, items) => vertical_conentWrapper.height function updateContentWrapperHeight() { var wp = middleWrapper ? middleWrapper : innerWrapper; wp.style.height = slidePositions[index + items] - slidePositions[index] + 'px'; } function getPages() { var rough = fixedWidth ? (fixedWidth + gutter) * slideCount / viewport : slideCount / items; return Math.min(Math.ceil(rough), slideCount); } /* * 1. update visible nav items list * 2. add "hidden" attributes to previous visible nav items * 3. remove "hidden" attrubutes to new visible nav items */ function updateNavVisibility() { if (!nav || navAsThumbnails) { return; } if (pages !== pagesCached) { var min = pagesCached, max = pages, fn = showElement; if (pagesCached > pages) { min = pages; max = pagesCached; fn = hideElement; } while (min < max) { fn(navItems[min]); min++; } // cache pages pagesCached = pages; } } function info(e) { return { container: container, slideItems: slideItems, navContainer: navContainer, navItems: navItems, controlsContainer: controlsContainer, hasControls: hasControls, prevButton: prevButton, nextButton: nextButton, items: items, slideBy: slideBy, cloneCount: cloneCount, slideCount: slideCount, slideCountNew: slideCountNew, index: index, indexCached: indexCached, displayIndex: getCurrentSlide(), navCurrentIndex: navCurrentIndex, navCurrentIndexCached: navCurrentIndexCached, pages: pages, pagesCached: pagesCached, sheet: sheet, isOn: isOn, event: e || {} }; } return { version: '2.9.4', getInfo: info, events: events, goTo: goTo, play: play, pause: pause, isOn: isOn, updateSliderHeight: updateInnerWrapperHeight, refresh: initSliderTransform, destroy: destroy, rebuild: function () { return tns(extend(options, optionsElements)); } }; }; exports.tns = tns;