3215 lines
109 KiB
JavaScript
3215 lines
109 KiB
JavaScript
|
var tns = (function (){
|
||
|
var win = window;
|
||
|
|
||
|
var raf = win.requestAnimationFrame
|
||
|
|| win.webkitRequestAnimationFrame
|
||
|
|| win.mozRequestAnimationFrame
|
||
|
|| win.msRequestAnimationFrame
|
||
|
|| function(cb) { return setTimeout(cb, 16); };
|
||
|
|
||
|
var win$1 = window;
|
||
|
|
||
|
var caf = win$1.cancelAnimationFrame
|
||
|
|| win$1.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'),
|
||
|
len = 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,
|
||
|
running;
|
||
|
|
||
|
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,
|
||
|
displayIndex = getCurrentSlide(),
|
||
|
indexMin = 0,
|
||
|
indexMax = !autoWidth ? getIndexMax() : null,
|
||
|
// resize
|
||
|
resizeTimer,
|
||
|
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,
|
||
|
disX,
|
||
|
disY,
|
||
|
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();
|
||
|
if (width) {
|
||
|
return width;
|
||
|
} else if (el.parentNode.parentNode !== null) {
|
||
|
getClientWidth(el.parentNode);
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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',
|
||
|
hasGutter = 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) {
|
||
|
var initIndex = !carousel ? 0 : cloneCount;
|
||
|
// customized nav
|
||
|
// 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 = 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 = 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.3',
|
||
|
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));
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
return tns;
|
||
|
})();
|