UNPKG

apex4x

Version:

The Comprehensive ARIA Development Suite

4,267 lines 110 kB
/*@license
Tiny-Slider 2.9.4 https://github.com/ganlanyuan/tiny-slider
Author: William Lin
License: MIT (https://opensource.org/licenses/MIT)
Includes modifications by Bryan Garaventa to improve screen reader accessibility for use within the Apex 4X ARIA Carousel module. Changes are prefaced with the comment "4X MOD" within the source code.
*/

"use strict";

var win$1 = window;
var raf =
  win$1.requestAnimationFrame ||
  win$1.webkitRequestAnimationFrame ||
  win$1.mozRequestAnimationFrame ||
  win$1.msRequestAnimationFrame ||
  function (cb) {
    return setTimeout(cb, 16);
  };

var win = window;
var caf =
  win.cancelAnimationFrame ||
  win.mozCancelAnimationFrame ||
  function (id) {
    clearTimeout(id);
  };

function extend() {
  var obj,
    name,
    copy,
    target = arguments[0] || {},
    i = 1,
    length = arguments.length;

  for (; i < length; i++) {
    if ((obj = arguments[i]) !== null) {
      for (name in obj) {
        copy = obj[name];

        if (target === copy) {
          continue;
        } else if (copy !== undefined) {
          target[name] = copy;
        }
      }
    }
  }

  return target;
}

function checkStorageValue(value) {
  return ["true", "false"].indexOf(value) >= 0 ? JSON.parse(value) : value;
}

function setLocalStorage(storage, key, value, access) {
  if (access) {
    try {
      storage.setItem(key, value);
    } catch (e) {}
  }

  return value;
}

function getSlideId() {
  var id = window.tnsId;
  window.tnsId = !id ? 1 : id + 1;
  return "tns" + window.tnsId;
}

function getBody() {
  var doc = document,
    body = doc.body;

  if (!body) {
    body = doc.createElement("body");
    body.fake = true;
  }

  return body;
}

var docElement = document.documentElement;

function setFakeBody(body) {
  var docOverflow = "";

  if (body.fake) {
    docOverflow = docElement.style.overflow; //avoid crashing IE8, if background image is used

    body.style.background = ""; //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible

    body.style.overflow = docElement.style.overflow = "hidden";
    docElement.appendChild(body);
  }

  return docOverflow;
}

function resetFakeBody(body, docOverflow) {
  if (body.fake) {
    body.remove();
    docElement.style.overflow = docOverflow; // Trigger layout so kinetic scrolling isn't disabled in iOS6+
    // eslint-disable-next-line

    docElement.offsetHeight;
  }
}

// get css-calc
function calc() {
  var doc = document,
    body = getBody(),
    docOverflow = setFakeBody(body),
    div = doc.createElement("div"),
    result = false;
  body.appendChild(div);

  try {
    var str = "(10px * 10)",
      vals = ["calc" + str, "-moz-calc" + str, "-webkit-calc" + str],
      val;

    for (var i = 0; i < 3; i++) {
      val = vals[i];
      div.style.width = val;

      if (div.offsetWidth === 100) {
        result = val.replace(str, "");
        break;
      }
    }
  } catch (e) {}

  body.fake ? resetFakeBody(body, docOverflow) : div.remove();
  return result;
}

// get subpixel support value
function percentageLayout() {
  // check subpixel layout supporting
  var doc = document,
    body = getBody(),
    docOverflow = setFakeBody(body),
    wrapper = doc.createElement("div"),
    outer = doc.createElement("div"),
    str = "",
    count = 70,
    perPage = 3,
    supported = false;
  wrapper.className = "tns-t-subp2";
  outer.className = "tns-t-ct";

  for (var i = 0; i < count; i++) {
    str += "<div></div>";
  }

  outer.innerHTML = str;
  wrapper.appendChild(outer);
  body.appendChild(wrapper);
  supported =
    Math.abs(
      wrapper.getBoundingClientRect().left -
        outer.children[count - perPage].getBoundingClientRect().left,
    ) < 2;
  body.fake ? resetFakeBody(body, docOverflow) : wrapper.remove();
  return supported;
}

function mediaquerySupport() {
  if (window.matchMedia || window.msMatchMedia) {
    return true;
  }

  var doc = document,
    body = getBody(),
    docOverflow = setFakeBody(body),
    div = doc.createElement("div"),
    style = doc.createElement("style"),
    rule = "@media all and (min-width:1px){.tns-mq-test{position:absolute}}",
    position;
  style.type = "text/css";
  div.className = "tns-mq-test";
  body.appendChild(style);
  body.appendChild(div);

  if (style.styleSheet) {
    style.styleSheet.cssText = rule;
  } else {
    style.appendChild(doc.createTextNode(rule));
  }

  position = window.getComputedStyle
    ? window.getComputedStyle(div).position
    : div.currentStyle["position"];
  body.fake ? resetFakeBody(body, docOverflow) : div.remove();
  return position === "absolute";
}

// create and append style sheet
function createStyleSheet(media, nonce) {
  // Create the <style> tag
  var style = document.createElement("style"); // style.setAttribute("type", "text/css");
  // Add a media (and/or media query) here if you'd like!
  // style.setAttribute("media", "screen")
  // style.setAttribute("media", "only screen and (max-width : 1024px)")

  if (media) {
    style.setAttribute("media", media);
  } // Add nonce attribute for Content Security Policy

  if (nonce) {
    style.setAttribute("nonce", nonce);
  } // WebKit hack :(
  // style.appendChild(document.createTextNode(""));
  // Add the <style> element to the page

  document.querySelector("head").appendChild(style);
  return style.sheet ? style.sheet : style.styleSheet;
}

// cross browsers addRule method
function addCSSRule(sheet, selector, rules, index) {
  // return raf(function() {
  "insertRule" in sheet
    ? sheet.insertRule(selector + "{" + rules + "}", index)
    : sheet.addRule(selector, rules, index); // });
}

// cross browsers addRule method
function removeCSSRule(sheet, index) {
  // return raf(function() {
  "deleteRule" in sheet ? sheet.deleteRule(index) : sheet.removeRule(index); // });
}

function getCssRulesLength(sheet) {
  var rule = "insertRule" in sheet ? sheet.cssRules : sheet.rules;
  return rule.length;
}

function toDegree(y, x) {
  return Math.atan2(y, x) * (180 / Math.PI);
}

function getTouchDirection(angle, range) {
  var direction = false,
    gap = Math.abs(90 - Math.abs(angle));

  if (gap >= 90 - range) {
    direction = "horizontal";
  } else if (gap <= range) {
    direction = "vertical";
  }

  return direction;
}

// https://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/
function forEach(arr, callback, scope) {
  for (var i = 0, l = arr.length; i < l; i++) {
    callback.call(scope, arr[i], i);
  }
}

var classListSupport = "classList" in document.createElement("_");

var hasClass = classListSupport
  ? function (el, str) {
      return el.classList.contains(str);
    }
  : function (el, str) {
      return el.className.indexOf(str) >= 0;
    };

var addClass = classListSupport
  ? function (el, str) {
      if (!hasClass(el, str)) {
        el.classList.add(str);
      }
    }
  : function (el, str) {
      if (!hasClass(el, str)) {
        el.className += " " + str;
      }
    };

var removeClass = classListSupport
  ? function (el, str) {
      if (hasClass(el, str)) {
        el.classList.remove(str);
      }
    }
  : function (el, str) {
      if (hasClass(el, str)) {
        el.className = el.className.replace(str, "");
      }
    };

function hasAttr(el, attr) {
  return el.hasAttribute(attr);
}

function getAttr(el, attr) {
  return el.getAttribute(attr);
}

function isNodeList(el) {
  // Only NodeList has the "item()" function
  return typeof el.item !== "undefined";
}

function setAttrs(els, attrs) {
  els = isNodeList(els) || els instanceof Array ? els : [els];

  if (Object.prototype.toString.call(attrs) !== "[object Object]") {
    return;
  }

  for (var i = els.length; i--; ) {
    for (var key in attrs) {
      els[i].setAttribute(key, attrs[key]);
    }
  }
}

function removeAttrs(els, attrs) {
  els = isNodeList(els) || els instanceof Array ? els : [els];
  attrs = attrs instanceof Array ? attrs : [attrs];
  var attrLength = attrs.length;

  for (var i = els.length; i--; ) {
    for (var j = attrLength; j--; ) {
      els[i].removeAttribute(attrs[j]);
    }
  }
}

function arrayFromNodeList(nl) {
  var arr = [];

  for (var i = 0, l = nl.length; i < l; i++) {
    arr.push(nl[i]);
  }

  return arr;
}

function hideElement(el, forceHide) {
  if (el.style.display !== "none") {
    el.style.display = "none";
  }
}

function showElement(el, forceHide) {
  if (el.style.display === "none") {
    el.style.display = "";
  }
}

function isVisible(el) {
  return window.getComputedStyle(el).display !== "none";
}

function whichProperty(props) {
  if (typeof props === "string") {
    var arr = [props],
      Props = props.charAt(0).toUpperCase() + props.substr(1),
      prefixes = ["Webkit", "Moz", "ms", "O"];
    prefixes.forEach(function (prefix) {
      if (prefix !== "ms" || props === "transform") {
        arr.push(prefix + Props);
      }
    });
    props = arr;
  }

  var el = document.createElement("fakeelement");
  props.length;

  for (var i = 0; i < props.length; i++) {
    var prop = props[i];

    if (el.style[prop] !== undefined) {
      return prop;
    }
  }

  return false; // explicit for ie9-
}

function has3DTransforms(tf) {
  if (!tf) {
    return false;
  }

  if (!window.getComputedStyle) {
    return false;
  }

  var doc = document,
    body = getBody(),
    docOverflow = setFakeBody(body),
    el = doc.createElement("p"),
    has3d,
    cssTF = tf.length > 9 ? "-" + tf.slice(0, -9).toLowerCase() + "-" : "";
  cssTF += "transform"; // Add it to the body to get the computed style

  body.insertBefore(el, null);
  el.style[tf] = "translate3d(1px,1px,1px)";
  has3d = window.getComputedStyle(el).getPropertyValue(cssTF);
  body.fake ? resetFakeBody(body, docOverflow) : el.remove();
  return has3d !== undefined && has3d.length > 0 && has3d !== "none";
}

// get transitionend, animationend based on transitionDuration
// @propin: string
// @propOut: string, first-letter uppercase
// Usage: getEndProperty('WebkitTransitionDuration', 'Transition') => webkitTransitionEnd
function getEndProperty(propIn, propOut) {
  var endProp = false;

  if (/^Webkit/.test(propIn)) {
    endProp = "webkit" + propOut + "End";
  } else if (/^O/.test(propIn)) {
    endProp = "o" + propOut + "End";
  } else if (propIn) {
    endProp = propOut.toLowerCase() + "end";
  }

  return endProp;
}

// Test via a getter in the options object to see if the passive property is accessed
var supportsPassive = false;

try {
  var opts = Object.defineProperty({}, "passive", {
    get: function () {
      supportsPassive = true;
    },
  });
  window.addEventListener("test", null, opts);
} catch (e) {}

var passiveOption = supportsPassive
  ? {
      passive: true,
    }
  : false;

function addEvents(el, obj, preventScrolling) {
  for (var prop in obj) {
    var option =
      ["touchstart", "touchmove"].indexOf(prop) >= 0 && !preventScrolling
        ? passiveOption
        : false;
    el.addEventListener(prop, obj[prop], option);
  }
}

function removeEvents(el, obj) {
  for (var prop in obj) {
    var option =
      ["touchstart", "touchmove"].indexOf(prop) >= 0 ? passiveOption : false;
    el.removeEventListener(prop, obj[prop], option);
  }
}

function Events() {
  return {
    topics: {},
    on: function (eventName, fn) {
      this.topics[eventName] = this.topics[eventName] || [];
      this.topics[eventName].push(fn);
    },
    off: function (eventName, fn) {
      if (this.topics[eventName]) {
        for (var i = 0; i < this.topics[eventName].length; i++) {
          if (this.topics[eventName][i] === fn) {
            this.topics[eventName].splice(i, 1);
            break;
          }
        }
      }
    },
    emit: function (eventName, data) {
      data.type = eventName;

      if (this.topics[eventName]) {
        this.topics[eventName].forEach(function (fn) {
          fn(data, eventName);
        });
      }
    },
  };
}

function jsTransform(element, attr, prefix, postfix, to, duration, callback) {
  var tick = Math.min(duration, 10),
    unit = to.indexOf("%") >= 0 ? "%" : "px",
    to = to.replace(unit, ""),
    from = Number(
      element.style[attr]
        .replace(prefix, "")
        .replace(postfix, "")
        .replace(unit, ""),
    ),
    positionTick = ((to - from) / duration) * tick;
  setTimeout(moveElement, tick);

  function moveElement() {
    duration -= tick;
    from += positionTick;
    element.style[attr] = prefix + from + unit + postfix;

    if (duration > 0) {
      setTimeout(moveElement, tick);
    } else {
      callback();
    }
  }
}

// Object.keys
if (!Object.keys) {
  Object.keys = function (object) {
    var keys = [];

    for (var name in object) {
      if (Object.prototype.hasOwnProperty.call(object, name)) {
        keys.push(name);
      }
    }

    return keys;
  };
} // ChildNode.remove

if (!("remove" in Element.prototype)) {
  Element.prototype.remove = function () {
    if (this.parentNode) {
      this.parentNode.removeChild(this);
    }
  };
}
var tns = function (options) {
  options = extend(
    {
      container: ".slider",
      mode: "carousel",
      axis: "horizontal",
      items: 1,
      gutter: 0,
      edgePadding: 0,
      fixedWidth: false,
      autoWidth: false,
      viewportMax: false,
      slideBy: 1,
      center: false,
      controls: true,
      controlsPosition: "top",
      controlsText: ["prev", "next"],
      controlsContainer: false,
      prevButton: false,
      nextButton: false,
      nav: true,
      navPosition: "top",
      navContainer: false,
      navAsThumbnails: false,
      arrowKeys: false,
      speed: 300,
      autoplay: false,
      autoplayPosition: "top",
      autoplayTimeout: 5000,
      autoplayDirection: "forward",
      autoplayText: ["start", "stop"],
      // 4X MOD
      autoplayHoverPause: true,
      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;

  // 4X MOD
  $A.loop(
    slideItems,
    function (i, o) {
      $A.setAttr(o, {
        role: "region",
        "aria-roledescription": "" + (i + 1),
        "aria-label": "Slide",
      });
    },
    "array",
  );

  if (responsive) {
    setBreakpointZone();
  }

  if (carousel) {
    container.className += " tns-vpfix";
  } // fixedWidth: viewport > rightBoundary > indexMax

  var autoWidth = options.autoWidth,
    fixedWidth = getOption("fixedWidth"),
    edgePadding = getOption("edgePadding"),
    gutter = getOption("gutter"),
    viewport = getViewportWidth(),
    center = getOption("center"),
    items = !autoWidth ? Math.floor(getOption("items")) : 1,
    slideBy = getOption("slideBy"),
    viewportMax = options.viewportMax || options.fixedWidthViewportWidth,
    arrowKeys = getOption("arrowKeys"),
    speed = getOption("speed"),
    rewind = options.rewind,
    loop = rewind ? false : options.loop,
    autoHeight = getOption("autoHeight"),
    controls = getOption("controls"),
    controlsText = getOption("controlsText"),
    nav = getOption("nav"),
    touch = getOption("touch"),
    mouseDrag = getOption("mouseDrag"),
    autoplay = getOption("autoplay"),
    autoplayTimeout = getOption("autoplayTimeout"),
    autoplayText = getOption("autoplayText"),
    autoplayHoverPause = getOption("autoplayHoverPause"),
    autoplayResetOnVisibility = getOption("autoplayResetOnVisibility"),
    sheet = createStyleSheet(null, getOption("nonce")),
    lazyload = options.lazyload,
    lazyloadSelector = options.lazyloadSelector,
    slidePositions,
    // collection of slide positions
    slideItemsOut = [],
    cloneCount = loop ? getCloneCountForLoop() : 0,
    slideCountNew = !carousel
      ? slideCount + cloneCount
      : slideCount + cloneCount * 2,
    hasRightDeadZone = (fixedWidth || autoWidth) && !loop ? true : false,
    rightBoundary = fixedWidth ? getRightBoundary() : null,
    updateIndexBeforeTransform = !carousel || !loop ? true : false,
    // transform
    transformAttr = horizontal ? "left" : "top",
    transformPrefix = "",
    transformPostfix = "",
    // index
    getIndexMax = (function () {
      if (fixedWidth) {
        return function () {
          return center && !loop
            ? slideCount - 1
            : Math.ceil(-rightBoundary / (fixedWidth + gutter));
        };
      } else if (autoWidth) {
        return function () {
          for (var i = 0; i < slideCountNew; i++) {
            if (slidePositions[i] >= -rightBoundary) {
              return i;
            }
          }
        };
      } else {
        return function () {
          if (center && carousel && !loop) {
            return slideCount - 1;
          } else {
            return loop || carousel
              ? Math.max(0, slideCountNew - Math.ceil(items))
              : slideCountNew - 1;
          }
        };
      }
    })(),
    index = getStartIndex(getOption("startIndex")),
    indexCached = index;
  getCurrentSlide();
  var indexMin = 0,
    indexMax = !autoWidth ? getIndexMax() : null,
    preventActionWhenRunning = options.preventActionWhenRunning,
    swipeAngle = options.swipeAngle,
    moveDirectionExpected = swipeAngle ? "?" : true,
    running = false,
    onInit = options.onInit,
    events = new Events(),
    // id, class
    newContainerClasses = " tns-slider tns-" + options.mode,
    slideId = container.id || getSlideId(),
    disable = getOption("disable"),
    disabled = false,
    freezable = options.freezable,
    freeze = freezable && !autoWidth ? getFreeze() : false,
    frozen = false,
    controlsEvents = {
      click: onControlsClick,
      keydown: onControlsKeydown,
    },
    navEvents = {
      click: onNavClick,
      keydown: onNavKeydown,
    },
    // 4X MOD
    hoverEvents = {
      mouseenter: mouseoverPause,
      mouseleave: mouseoutRestart,
    },
    // 4X MOD
    focusEvents = {
      focusin: focusPause,
    },
    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 = "Page ";
  } // 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,
      // 4X MOD
      autoplayFocusPaused,
      autoplayVisibilityPaused;
  }

  if (hasTouch || hasMouseDrag) {
    var initPosition = {},
      lastPosition = {},
      translateInit,
      panStart = false,
      rafIndex,
      getDist = horizontal
        ? function (a, b) {
            return a.x - b.x;
          }
        : function (a, b) {
            return a.y - b.y;
          };
  } // disable slider when slidecount <= items

  if (!autoWidth) {
    resetVariblesWhenDisable(disable || freeze);
  }

  if (TRANSFORM) {
    transformAttr = TRANSFORM;
    transformPrefix = "translate";

    if (HAS3DTRANSFORMS) {
      transformPrefix += horizontal ? "3d(" : "3d(0px, ";
      transformPostfix = horizontal ? ", 0px, 0px)" : ", 0px)";
    } else {
      transformPrefix += horizontal ? "X(" : "Y(";
      transformPostfix = ")";
    }
  }

  if (carousel) {
    container.className = container.className.replace("tns-vpfix", "");
  }

  initStructure();
  initSheet();
  initSliderTransform(); // === COMMON FUNCTIONS === //

  function resetVariblesWhenDisable(condition) {
    if (condition) {
      controls =
        nav =
        touch =
        mouseDrag =
        arrowKeys =
        autoplay =
        autoplayHoverPause =
        autoplayResetOnVisibility =
          false;
    }
  }

  function getCurrentSlide() {
    var tem = carousel ? index - cloneCount : index;

    while (tem < 0) {
      tem += slideCount;
    }

    return (tem % slideCount) + 1;
  }

  function getStartIndex(ind) {
    ind = ind
      ? Math.max(0, Math.min(loop ? slideCount - 1 : slideCount - items, ind))
      : 0;
    return carousel ? ind + cloneCount : ind;
  }

  function getAbsIndex(i) {
    if (i == null) {
      i = index;
    }

    if (carousel) {
      i -= cloneCount;
    }

    while (i < 0) {
      i += slideCount;
    }

    return Math.floor(i % slideCount);
  }

  function getCurrentNavIndex() {
    var absIndex = getAbsIndex(),
      result;
    result = navAsThumbnails
      ? absIndex
      : fixedWidth || autoWidth
        ? Math.ceil(((absIndex + 1) * pages) / slideCount - 1)
        : Math.floor(absIndex / items); // set active nav to the last one when reaches the right edge

    if (!loop && carousel && index === indexMax) {
      result = pages - 1;
    }

    return result;
  }

  function getItemsMax() {
    // fixedWidth or autoWidth while viewportMax is not available
    if (autoWidth || (fixedWidth && !viewportMax)) {
      return slideCount - 1; // most cases
    } else {
      var str = fixedWidth ? "fixedWidth" : "items",
        arr = [];

      if (fixedWidth || options[str] < slideCount) {
        arr.push(options[str]);
      }

      if (responsive) {
        for (var bp in responsive) {
          var tem = responsive[bp][str];

          if (tem && (fixedWidth || tem < slideCount)) {
            arr.push(tem);
          }
        }
      }

      if (!arr.length) {
        arr.push(0);
      }

      return Math.ceil(
        fixedWidth
          ? viewportMax / Math.min.apply(null, arr)
          : Math.max.apply(null, arr),
      );
    }
  }

  function getCloneCountForLoop() {
    var itemsMax = getItemsMax(),
      result = carousel
        ? Math.ceil((itemsMax * 5 - slideCount) / 2)
        : itemsMax * 4 - slideCount;
    result = Math.max(itemsMax, result);
    return hasOption("edgePadding") ? result + 1 : result;
  }

  function getWindowWidth() {
    return (
      win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth
    );
  }

  function getInsertPosition(pos) {
    return pos === "top" ? "afterbegin" : "beforeend";
  }

  function getClientWidth(el) {
    if (el == null) {
      return;
    }

    var div = doc.createElement("div"),
      rect,
      width;
    el.appendChild(div);
    rect = div.getBoundingClientRect();
    width = rect.right - rect.left;
    div.remove();
    return width || getClientWidth(el.parentNode);
  }

  function getViewportWidth() {
    var gap = edgePadding ? edgePadding * 2 - gutter : 0;
    return getClientWidth(containerParent) - gap;
  }

  function hasOption(item) {
    if (options[item]) {
      return true;
    } else {
      if (responsive) {
        for (var bp in responsive) {
          if (responsive[bp][item]) {
            return true;
          }
        }
      }

      return false;
    }
  } // get option:
  // fixed width: viewport, fixedWidth, gutter => items
  // others: window width => all variables
  // all: items => slideBy

  function getOption(item, ww) {
    if (ww == null) {
      ww = windowWidth;
    }

    if (item === "items" && fixedWidth) {
      return Math.floor((viewport + gutter) / (fixedWidth + gutter)) || 1;
    } else {
      var result = options[item];

      if (responsive) {
        for (var bp in responsive) {
          // bp: convert string to number
          if (ww >= parseInt(bp)) {
            if (item in responsive[bp]) {
              result = responsive[bp][item];
            }
          }
        }
      }

      if (item === "slideBy" && result === "page") {
        result = getOption("items");
      }

      if (!carousel && (item === "slideBy" || item === "items")) {
        result = Math.floor(result);
      }

      return result;
    }
  }

  function getSlideMarginLeft(i) {
    return CALC
      ? CALC + "(" + i * 100 + "% / " + slideCountNew + ")"
      : (i * 100) / slideCountNew + "%";
  }

  function getInnerWrapperStyles(
    edgePaddingTem,
    gutterTem,
    fixedWidthTem,
    speedTem,
    autoHeightBP,
  ) {
    var str = "";

    if (edgePaddingTem !== undefined) {
      var gap = edgePaddingTem;

      if (gutterTem) {
        gap -= gutterTem;
      }

      str = horizontal
        ? "margin: 0 " + gap + "px 0 " + edgePaddingTem + "px;"
        : "margin: " + edgePaddingTem + "px 0 " + gap + "px 0;";
    } else if (gutterTem && !fixedWidthTem) {
      var gutterTemUnit = "-" + gutterTem + "px",
        dir = horizontal ? gutterTemUnit + " 0 0" : "0 " + gutterTemUnit + " 0";
      str = "margin: 0 " + dir + ";";
    }

    if (!carousel && autoHeightBP && TRANSITIONDURATION && speedTem) {
      str += getTransitionDurationStyle(speedTem);
    }

    return str;
  }

  function getContainerWidth(fixedWidthTem, gutterTem, itemsTem) {
    if (fixedWidthTem) {
      return (fixedWidthTem + gutterTem) * slideCountNew + "px";
    } else {
      return CALC
        ? CALC + "(" + slideCountNew * 100 + "% / " + itemsTem + ")"
        : (slideCountNew * 100) / itemsTem + "%";
    }
  }

  function getSlideWidthStyle(fixedWidthTem, gutterTem, itemsTem) {
    var width;

    if (fixedWidthTem) {
      width = fixedWidthTem + gutterTem + "px";
    } else {
      if (!carousel) {
        itemsTem = Math.floor(itemsTem);
      }

      var dividend = carousel ? slideCountNew : itemsTem;
      width = CALC ? CALC + "(100% / " + dividend + ")" : 100 / dividend + "%";
    }

    width = "width:" + width; // inner slider: overwrite outer slider styles

    return nested !== "inner" ? width + ";" : width + " !important;";
  }

  function getSlideGutterStyle(gutterTem) {
    var str = ""; // gutter maybe interger || 0
    // so can't use 'if (gutter)'

    if (gutterTem !== false) {
      var prop = horizontal ? "padding-" : "margin-",
        dir = horizontal ? "right" : "bottom";
      str = prop + dir + ": " + gutterTem + "px;";
    }

    return str;
  }

  function getCSSPrefix(name, num) {
    var prefix = name.substring(0, name.length - num).toLowerCase();

    if (prefix) {
      prefix = "-" + prefix + "-";
    }

    return prefix;
  }

  function getTransitionDurationStyle(speed) {
    return (
      getCSSPrefix(TRANSITIONDURATION, 18) +
      "transition-duration:" +
      speed / 1000 +
      "s;"
    );
  }

  function getAnimationDurationStyle(speed) {
    return (
      getCSSPrefix(ANIMATIONDURATION, 17) +
      "animation-duration:" +
      speed / 1000 +
      "s;"
    );
  }

  function initStructure() {
    var classOuter = "tns-outer",
      classInner = "tns-inner";
    hasOption("gutter");
    outerWrapper.className = classOuter;
    innerWrapper.className = classInner;
    outerWrapper.id = slideId + "-ow";
    innerWrapper.id = slideId + "-iw"; // set container properties

    if (container.id === "") {
      container.id = slideId;
    }

    newContainerClasses +=
      PERCENTAGELAYOUT || autoWidth ? " tns-subpixel" : " tns-no-subpixel";
    newContainerClasses += CALC ? " tns-calc" : " tns-no-calc";

    if (autoWidth) {
      newContainerClasses += " tns-autowidth";
    }

    newContainerClasses += " tns-" + options.axis;
    container.className += newContainerClasses; // add constrain layer for carousel

    if (carousel) {
      middleWrapper = doc.createElement("div");
      middleWrapper.id = slideId + "-mw";
      middleWrapper.className = "tns-ovh";
      outerWrapper.appendChild(middleWrapper);
      middleWrapper.appendChild(innerWrapper);
    } else {
      outerWrapper.appendChild(innerWrapper);
    }

    if (autoHeight) {
      var wp = middleWrapper ? middleWrapper : innerWrapper;
      wp.className += " tns-ah";
    }

    containerParent.insertBefore(outerWrapper, container);
    innerWrapper.appendChild(container); // add id, class, aria attributes
    // before clone slides

    forEach(slideItems, function (item, i) {
      addClass(item, "tns-item");

      if (!item.id) {
        item.id = slideId + "-item" + i;
      }

      if (!carousel && animateNormal) {
        addClass(item, animateNormal);
      }

      setAttrs(item, {
        "aria-hidden": "true",
        tabindex: "-1",
      });
    }); // ## clone slides
    // carousel: n + slides + n
    // gallery:      slides + n

    if (cloneCount) {
      var fragmentBefore = doc.createDocumentFragment(),
        fragmentAfter = doc.createDocumentFragment();

      for (var j = cloneCount; j--; ) {
        var num = j % slideCount,
          cloneFirst = slideItems[num].cloneNode(true);
        addClass(cloneFirst, slideClonedClass);
        removeAttrs(cloneFirst, "id");
        fragmentAfter.insertBefore(cloneFirst, fragmentAfter.firstChild);

        if (carousel) {
          var cloneLast = slideItems[slideCount - 1 - num].cloneNode(true);
          addClass(cloneLast, slideClonedClass);
          removeAttrs(cloneLast, "id");
          fragmentBefore.appendChild(cloneLast);
        }
      }

      container.insertBefore(fragmentBefore, container.firstChild);
      container.appendChild(fragmentAfter);
      slideItems = container.children;
    }
  }

  function initSliderTransform() {
    // ## images loaded/failed
    if (hasOption("autoHeight") || autoWidth || !horizontal) {
      var imgs = container.querySelectorAll("img"); // add img load event listener

      forEach(imgs, function (img) {
        var src = img.src;

        if (!lazyload) {
          // not data img
          if (src && src.indexOf("data:image") < 0) {
            img.src = "";
            addEvents(img, imgEvents);
            addClass(img, "loading");
            img.src = src; // data img
          } else {
            imgLoaded(img);
          }
        }
      }); // set imgsComplete

      raf(function () {
        imgsLoadedCheck(arrayFromNodeList(imgs), function () {
          imgsComplete = true;
        });
      }); // reset imgs for auto height: check visible imgs only

      if (hasOption("autoHeight")) {
        imgs = getImageArray(
          index,
          Math.min(index + items - 1, slideCountNew - 1),
        );
      }

      lazyload
        ? initSliderTransformStyleCheck()
        : raf(function () {
            imgsLoadedCheck(
              arrayFromNodeList(imgs),
              initSliderTransformStyleCheck,
            );
          });
    } else {
      // set container transform property
      if (carousel) {
        doContainerTransformSilent();
      } // update slider tools and events

      initTools();
      initEvents();
    }
  }

  function initSliderTransformStyleCheck() {
    if (autoWidth && slideCount > 1) {
      // check styles application
      var num = loop ? index : slideCount - 1;

      (function stylesApplicationCheck() {
        var left = slideItems[num].getBoundingClientRect().left;
        var right = slideItems[num - 1].getBoundingClientRect().right;
        Math.abs(left - right) <= 1
          ? initSliderTransformCore()
          : setTimeout(function () {
              stylesApplicationCheck();
            }, 16);
      })();
    } else {
      initSliderTransformCore();
    }
  }

  function initSliderTransformCore() {
    // run Fn()s which are rely on image loading
    if (!horizontal || autoWidth) {
      setSlidePositions();

      if (autoWidth) {
        rightBoundary = getRightBoundary();

        if (freezable) {
          freeze = getFreeze();
        }

        indexMax = getIndexMax(); // <= slidePositions, rightBoundary <=

        resetVariblesWhenDisable(disable || freeze);
      } else {
        updateContentWrapperHeight();
      }
    } // set container transform property

    if (carousel) {
      doContainerTransformSilent();
    } // update slider tools and events

    initTools();
    initEvents();
  }

  function initSheet() {
    // gallery:
    // set animation classes and left value for gallery slider
    if (!carousel) {
      for (var i = index, l = index + Math.min(slideCount, items); i < l; i++) {
        var item = slideItems[i];
        item.style.left = ((i - index) * 100) / items + "%";
        addClass(item, animateIn);
        removeClass(item, animateNormal);
      }
    } // #### LAYOUT
    // ## INLINE-BLOCK VS FLOAT
    // ## PercentageLayout:
    // slides: inline-block
    // remove blank space between slides by set font-size: 0
    // ## Non PercentageLayout:
    // slides: float
    //         margin-right: -100%
    //         margin-left: ~
    // Resource: https://docs.google.com/spreadsheets/d/147up245wwTXeQYve3BRSAD4oVcvQmuGsFteJOeA5xNQ/edit?usp=sharing

    if (horizontal) {
      if (PERCENTAGELAYOUT || autoWidth) {
        addCSSRule(
          sheet,
          "#" + slideId + " > .tns-item",
          "font-size:" + win.getComputedStyle(slideItems[0]).fontSize + ";",
          getCssRulesLength(sheet),
        );
        addCSSRule(
          sheet,
          "#" + slideId,
          "font-size:0;",
          getCssRulesLength(sheet),
        );
      } else if (carousel) {
        forEach(slideItems, function (slide, i) {
          slide.style.marginLeft = getSlideMarginLeft(i);
        });
      }
    } // ## BASIC STYLES

    if (CSSMQ) {
      // middle wrapper style
      if (TRANSITIONDURATION) {
        var str =
          middleWrapper && options.autoHeight
            ? getTransitionDurationStyle(options.speed)
            : "";
        addCSSRule(sheet, "#" + slideId + "-mw", str, getCssRulesLength(sheet));
      } // inner wrapper styles

      str = getInnerWrapperStyles(
        options.edgePadding,
        options.gutter,
        options.fixedWidth,
        options.speed,
        options.autoHeight,
      );
      addCSSRule(sheet, "#" + slideId + "-iw", str, getCssRulesLength(sheet)); // container styles

      if (carousel) {
        str =
          horizontal && !autoWidth
            ? "width:" +
              getContainerWidth(
                options.fixedWidth,
                options.gutter,
                options.items,
              ) +
              ";"
            : "";

        if (TRANSITIONDURATION) {
          str += getTransitionDurationStyle(speed);
        }

        addCSSRule(sheet, "#" + slideId, str, getCssRulesLength(sheet));
      } // slide styles

      str =
        horizontal && !autoWidth
          ? getSlideWidthStyle(
              options.fixedWidth,
              options.gutter,
              options.items,
            )
          : "";

      if (options.gutter) {
        str += getSlideGutterStyle(options.gutter);
      } // set gallery items transition-duration

      if (!carousel) {
        if (TRANSITIONDURATION) {
          str += getTransitionDurationStyle(speed);
        }

        if (ANIMATIONDURATION) {
          str += getAnimationDurationStyle(speed);
        }
      }

      if (str) {
        addCSSRule(
          sheet,
          "#" + slideId + " > .tns-item",
          str,
          getCssRulesLength(sheet),
        );
      } // non CSS mediaqueries: IE8
      // ## update inner wrapper, container, slides if needed
      // set inline styles for inner wrapper & container
      // insert stylesheet (one line) for slides only (since slides are many)
    } else {
      // middle wrapper styles
      update_carousel_transition_duration(); // inner wrapper styles

      innerWrapper.style.cssText = getInnerWrapperStyles(
        edgePadding,
        gutter,
        fixedWidth,
        autoHeight,
      ); // container styles

      if (carousel && horizontal && !autoWidth) {
        container.style.width = getContainerWidth(fixedWidth, gutter, items);
      } // slide styles

      var str =
        horizontal && !autoWidth
          ? getSlideWidthStyle(fixedWidth, gutter, items)
          : "";

      if (gutter) {
        str += getSlideGutterStyle(gutter);
      } // append to the last line

      if (str) {
        addCSSRule(
          sheet,
          "#" + slideId + " > .tns-item",
          str,
          getCssRulesLength(sheet),
        );
      }
    } // ## MEDIAQUERIES

    if (responsive && CSSMQ) {
      for (var bp in responsive) {
        // bp: convert string to number
        bp = parseInt(bp);
        var opts = responsive[bp],
          str = "",
          middleWrapperStr = "",
          innerWrapperStr = "",
          containerStr = "",
          slideStr = "",
          itemsBP = !autoWidth ? getOption("items", bp) : null,
          fixedWidthBP = getOption("fixedWidth", bp),
          speedBP = getOption("speed", bp),
          edgePaddingBP = getOption("edgePadding", bp),
          autoHeightBP = getOption("autoHeight", bp),
          gutterBP = getOption("gutter", bp); // middle wrapper string

        if (
          TRANSITIONDURATION &&
          middleWrapper &&
          getOption("autoHeight", bp) &&
          "speed" in opts
        ) {
          middleWrapperStr =
            "#" + slideId + "-mw{" + getTransitionDurationStyle(speedBP) + "}";
        } // inner wrapper string

        if ("edgePadding" in opts || "gutter" in opts) {
          innerWrapperStr =
            "#" +
            slideId +
            "-iw{" +
            getInnerWrapperStyles(
              edgePaddingBP,
              gutterBP,
              fixedWidthBP,
              speedBP,
              autoHeightBP,
            ) +
            "}";
        } // container string

        if (
          carousel &&
          horizontal &&
          !autoWidth &&
          ("fixedWidth" in opts ||
            "items" in opts ||
            (fixedWidth && "gutter" in opts))
        ) {
          containerStr =
            "width:" + getContainerWidth(fixedWidthBP, gutterBP, itemsBP) + ";";
        }

        if (TRANSITIONDURATION && "speed" in opts) {
          containerStr += getTransitionDurationStyle(speedBP);
        }

        if (containerStr) {
          containerStr = "#" + slideId + "{" + containerStr + "}";
        } // slide string

        if (
          "fixedWidth" in opts ||
          (fixedWidth && "gutter" in opts) ||
          (!carousel && "items" in opts)
        ) {
          slideStr += getSlideWidthStyle(fixedWidthBP, gutterBP, itemsBP);
        }

        if ("gutter" in opts) {
          slideStr += getSlideGutterStyle(gutterBP);
        } // set gallery items transition-duration

        if (!carousel && "speed" in opts) {
          if (TRANSITIONDURATION) {
            slideStr += getTransitionDurationStyle(speedBP);
          }

          if (ANIMATIONDURATION) {
            slideStr += getAnimationDurationStyle(speedBP);
          }
        }

        if (slideStr) {
          slideStr = "#" + slideId + " > .tns-item{" + slideStr + "}";
        } // add up

        str = middleWrapperStr + innerWrapperStr + containerStr + slideStr;

        if (str) {
          sheet.insertRule(
            "@media (min-width: " + bp / 16 + "em) {" + str + "}",
            sheet.cssRules.length,
          );
        }
      }
    }
  }

  function initTools() {
    // == slides ==
    updateSlideStatus();

    // == live region ==
    // 4X MOD
    outerWrapper.parentNode.setAttribute("role", "region");
    outerWrapper.parentNode.setAttribute("aria-label", "Carousel");
    outerWrapper.insertAdjacentHTML(
      "afterbegin",
      '<div aria-hidden="true" style="position: absolute; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); padding: 0; border: 0; height: 1px; width: 1px; overflow: hidden; white-space: nowrap;" class="tns-liveregion tns-visually-hidden">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) {
          // 4X MOD
          addEvents(outerWrapper.parentNode, focusEvents);
          addEvents(outerWrapper.parentNode, hoverEvents);
        }

        if (autoplayResetOnVisibility) {
          addEvents(container, visibilityEvent);
        }
      }
    } // == navInit ==

    if (hasNav) {
      // will not hide the navs in case they're thumbnails

      if (navContainer) {
        // 4X MOD
        setAttrs(navContainer, {
          role: "toolbar",
          "aria-label": "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>';
        }

        // 4X MOD
        navHtml =
          '<div class="tns-nav" role="toolbar" aria-label="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], {
        // 4X MOD
        "aria-label": navStr + (navCurrentIndex + 1),
        "aria-current": "true",
      });
      removeAttrs(navItems[navCurrentIndex], "tabindex");
      addClass(navItems[navCurrentIndex], navActiveClass); // add events

      addEvents(navContainer, navEvents);
    } // == controlsInit ==

    if (hasControls) {
      if (!controlsContainer && (!prevButton || !nextButton)) {
        // 4X MOD
        outerWrapper.insertAdjacentHTML(
          getInsertPosition(options.controlsPosition),
          '<div class="tns-controls"><button type="button" data-controls="prev" aria-controls="' +
            slideId +
            '">' +
            controlsText[0] +
            '</button><button type="button" data-controls="next" aria-controls="' +
            slideId +
            '">' +
            controlsText[1] +
            "</button></div>",
        );
        controlsContainer = outerWrapper.querySelector(".tns-controls");
      }

      if (!prevButton || !nextButton) {
        prevButton = controlsContainer.children[0];
        nextButton = controlsContainer.children[1];
      }

      // 4X MOD
      /*
      if (options.controlsContainer) {
        setAttrs(controlsContainer, {
          "aria-label": "Carousel Navigation",
          tabindex: "0"
        });
      }
*/

      if (
        options.controlsContainer ||
        (options.prevButton && options.nextButton)
      ) {
        setAttrs([prevButton, nextButton], {
          "aria-controls": slideId,
          // 4X MOD
          // 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

    // 4X MOD
    removeEvents(outerWrapper.parentNode, 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

    // 4X MOD
    tnsList =
      animateIn =
      animateOut =
      animateDelay =
      animateNormal =
      horizontal =
      outerWrapper =
      innerWrapper =
      container =
      containerParent =
      containerHTML =
      slideItems =
      slideCount =
      breakpointZone =
      windowWidth =
      autoWidth =
      fixedWidth =
      edgePadding =
      gutter =
      viewport =
      items =
      slideBy =
      viewportMax =
      arrowKeys =
      speed =
      rewind =
      loop =
      autoHeight =
      sheet =
      lazyload =
      slidePositions =
      slideItemsOut =
      cloneCount =
      slideCountNew =
      hasRightDeadZone =
      rightBoundary =
      updateIndexBeforeTransform =
      transformAttr =
      transformPrefix =
      transformPostfix =
      getIndexMax =
      index =
      indexCached =
      indexMin =
      indexMax =
      swipeAngle =
      moveDirectionExpected =
      running =
      onInit =
      events =
      newContainerClasses =
      slideId =
      disable =
      disabled =
      freezable =
      freeze =
      frozen =
      controlsEvents =
      navEvents =
      hoverEvents =
      visibilityEvent =
      docmentKeydownEvent =
      touchEvents =
      dragEvents =
      hasControls =
      hasNav =
      navAsThumbnails =
      hasAutoplay =
      hasTouch =
      hasMouseDrag =
      slideActiveClass =
      imgCompleteClass =
      imgEvents =
      imgsComplete =
      controls =
      controlsText =
      controlsContainer =
      controlsContainerHTML =
      prevButton =
      nextButton =
      prevIsButton =
      nextIsButton =
      nav =
      navContainer =
      navContainerHTML =
      navItems =
      pages =
      pagesCached =
      navClicked =
      navCurrentIndex =
      navCurrentIndexCached =
      navActiveClass =
      navStr =
      autoplay =
      autoplayTimeout =
      autoplayDirection =
      autoplayText =
      autoplayHoverPause =
      autoplayButton =
      autoplayButtonHTML =
      autoplayResetOnVisibility =
      autoplayHtmlStrings =
      autoplayTimer =
      animating =
      autoplayHoverPaused =
      autoplayUserPaused =
      autoplayFocusPaused =
      autoplayVisibilityPaused =
      initPosition =
      lastPosition =
      translateInit =
      panStart =
      rafIndex =
      getDist =
      touch =
      mouseDrag =
        null; // check variables
    // [animateIn, animateOut, animateDelay, animateNormal, horizontal, outerWrapper, innerWrapper, container, containerParent, containerHTML, slideItems, slideCount, breakpointZone, windowWidth, autoWidth, fixedWidth, edgePadding, gutter, viewport, items, slideBy, viewportMax, arrowKeys, speed, rewind, loop, autoHeight, sheet, lazyload, slidePositions, slideItemsOut, cloneCount, slideCountNew, hasRightDeadZone, rightBoundary, updateIndexBeforeTransform, transformAttr, transformPrefix, transformPostfix, getIndexMax, index, indexCached, indexMin, indexMax, resizeTimer, swipeAngle, moveDirectionExpected, running, onInit, events, newContainerClasses, slideId, disable, disabled, freezable, freeze, frozen, controlsEvents, navEvents, hoverEvents, visibilityEvent, docmentKeydownEvent, touchEvents, dragEvents, hasControls, hasNav, navAsThumbnails, hasAutoplay, hasTouch, hasMouseDrag, slideActiveClass, imgCompleteClass, imgEvents, imgsComplete, controls, controlsText, controlsContainer, controlsContainerHTML, prevButton, nextButton, prevIsButton, nextIsButton, nav, navContainer, navContainerHTML, navItems, pages, pagesCached, navClicked, navCurrentIndex, navCurrentIndexCached, navActiveClass, navStr, 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) {
      // 4X MOD
      autoplayHoverPause
        ? addEvents(outerWrapper.parentNode, hoverEvents)
        : removeEvents(outerWrapper.parentNode, 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();

    // 4X MOD
    if (!str) return;

    if (liveregionCurrent.innerHTML !== str) {
      liveregionCurrent.innerHTML = str;
    }
  }

  function getLiveRegionStr() {
    var arr = getVisibleSlideRange(),
      // 4X MOD
      start =
        arr[0] +
        1 -
        (getPages() === slideCount
          ? 0
          : arr[0] === slideCount + cloneCount
            ? slideCount + cloneCount
            : cloneCount),
      end =
        arr[1] +
        1 -
        (getPages() === slideCount
          ? 0
          : arr[1] === slideCount + cloneCount
            ? slideCount + cloneCount
            : cloneCount);
    if (start === end && end > slideCount) return "1";
    if (start < 1 || end > slideCount) return "";
    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);
          // 4X MOD
          if (
            !autoplay ||
            (autoplay &&
              autoplayUserPaused &&
              autoplayFocusPaused &&
              !animating)
          )
            $A.announce(item);
        } // 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),
          // 4X MOD
          "aria-current": "false",
        });
        removeClass(navPrev, navActiveClass);
        setAttrs(navCurrent, {
          // 4X MOD
          "aria-label": navStr + (navCurrentIndex + 1),
          "aria-current": "true",
        });
        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]);
    }

    // 4X MOD
    autoplayFocusPaused = false;
    autoplayUserPaused = false;
  }

  function stopAutoplay() {
    stopAutoplayTimer();

    if (autoplayButton) {
      updateAutoplayButton("start", autoplayText[0]);
    }

    // 4X MOD
    autoplayFocusPaused = true;
    autoplayUserPaused = true;
  } // 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;
    }
  }

  // 4X MOD
  function focusPause() {
    if (animating) {
      stopAutoplay();
      autoplayFocusPaused = true;
    }
  }

  function mouseoverPause() {
    if (animating) {
      stopAutoplayTimer();
      autoplayHoverPaused = true;
    }
  }

  function mouseoutRestart() {
    if (autoplayHoverPaused) {
      setAutoplayTimer();
      autoplayHoverPaused = false;
    }
  } // keydown events on document

  function onDocumentKeydown(e) {
    e = getEvent(e);
    var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);

    if (keyIndex >= 0) {
      onControlsClick(e, keyIndex === 0 ? -1 : 1);
    }
  } // on key control

  function onControlsKeydown(e) {
    e = getEvent(e);
    var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);

    if (keyIndex >= 0) {
      if (keyIndex === 0) {
        if (!prevButton.disabled) {
          onControlsClick(e, -1);
        }
      } else if (!nextButton.disabled) {
        onControlsClick(e, 1);
      }
    }
  } // set focus

  function setFocus(el) {
    el.focus();
  } // on key nav

  function onNavKeydown(e) {
    e = getEvent(e);
    var curElement = doc.activeElement;

    if (!hasAttr(curElement, "data-nav")) {
      return;
    } // var code = e.keyCode,

    var keyIndex = [KEYS.LEFT, KEYS.RIGHT, KEYS.ENTER, KEYS.SPACE].indexOf(
        e.keyCode,
      ),
      navIndex = Number(getAttr(curElement, "data-nav"));

    if (keyIndex >= 0) {
      if (keyIndex === 0) {
        if (navIndex > 0) {
          setFocus(navItems[navIndex - 1]);
        }
      } else if (keyIndex === 1) {
        if (navIndex < pages - 1) {
          setFocus(navItems[navIndex + 1]);
        }
      } else {
        navClicked = navIndex;
        goTo(navIndex, e);
      }
    }
  }

  function getEvent(e) {
    e = e || win.event;
    return isTouchEvent(e) ? e.changedTouches[0] : e;
  }

  function getTarget(e) {
    return e.target || win.event.srcElement;
  }

  function isTouchEvent(e) {
    return e.type.indexOf("touch") >= 0;
  }

  function preventDefaultBehavior(e) {
    e.preventDefault ? e.preventDefault() : (e.returnValue = false);
  }

  function getMoveDirectionExpected() {
    return (
      getTouchDirection(
        toDegree(
          lastPosition.y - initPosition.y,
          lastPosition.x - initPosition.x,
        ),
        swipeAngle,
      ) === options.axis
    );
  }

  function onPanStart(e) {
    if (running) {
      if (preventActionWhenRunning) {
        return;
      } else {
        onTransitionEnd();
      }
    }

    if (autoplay && animating) {
      stopAutoplayTimer();
    }

    panStart = true;

    if (rafIndex) {
      caf(rafIndex);
      rafIndex = null;
    }

    var $ = getEvent(e);
    events.emit(isTouchEvent(e) ? "touchStart" : "dragStart", info(e));

    if (
      !isTouchEvent(e) &&
      ["img", "a"].indexOf(getLowerCaseNodeName(getTarget(e))) >= 0
    ) {
      preventDefaultBehavior(e);
    }

    lastPosition.x = initPosition.x = $.clientX;
    lastPosition.y = initPosition.y = $.clientY;

    if (carousel) {
      translateInit = parseFloat(
        container.style[transformAttr].replace(transformPrefix, ""),
      );
      resetDuration(container, "0s");
    }
  }

  function onPanMove(e) {
    if (panStart) {
      var $ = getEvent(e);
      lastPosition.x = $.clientX;
      lastPosition.y = $.clientY;

      if (carousel) {
        if (!rafIndex) {
          rafIndex = raf(function () {
            panUpdate(e);
          });
        }
      } else {
        if (moveDirectionExpected === "?") {
          moveDirectionExpected = getMoveDirectionExpected();
        }

        if (moveDirectionExpected) {
          preventScroll = true;
        }
      }

      if (
        (typeof e.cancelable !== "boolean" || e.cancelable) &&
        preventScroll
      ) {
        e.preventDefault();
      }
    }
  }

  function panUpdate(e) {
    if (!moveDirectionExpected) {
      panStart = false;
      return;
    }

    caf(rafIndex);

    if (panStart) {
      rafIndex = raf(function () {
        panUpdate(e);
      });
    }

    if (moveDirectionExpected === "?") {
      moveDirectionExpected = getMoveDirectionExpected();
    }

    if (moveDirectionExpected) {
      if (!preventScroll && isTouchEvent(e)) {
        preventScroll = true;
      }

      try {
        if (e.type) {
          events.emit(isTouchEvent(e) ? "touchMove" : "dragMove", info(e));
        }
      } catch (err) {}

      var x = translateInit,
        dist = getDist(lastPosition, initPosition);

      if (!horizontal || fixedWidth || autoWidth) {
        x += dist;
        x += "px";
      } else {
        var percentageX = TRANSFORM
          ? (dist * items * 100) / ((viewport + gutter) * slideCountNew)
          : (dist * 100) / (viewport + gutter);
        x += percentageX;
        x += "%";
      }

      container.style[transformAttr] = transformPrefix + x + transformPostfix;
    }
  }

  function onPanEnd(e) {
    if (panStart) {
      if (rafIndex) {
        caf(rafIndex);
        rafIndex = null;
      }

      if (carousel) {
        resetDuration(container, "");
      }

      panStart = false;
      var $ = getEvent(e);
      lastPosition.x = $.clientX;
      lastPosition.y = $.clientY;
      var dist = getDist(lastPosition, initPosition);

      if (Math.abs(dist)) {
        // drag vs click
        if (!isTouchEvent(e)) {
          // prevent "click"
          var target = getTarget(e);
          addEvents(target, {
            click: function preventClick(e) {
              preventDefaultBehavior(e);
              removeEvents(target, {
                click: preventClick,
              });
            },
          });
        }

        if (carousel) {
          rafIndex = raf(function () {
            if (horizontal && !autoWidth) {
              var indexMoved = (-dist * items) / (viewport + gutter);
              indexMoved =
                dist > 0 ? Math.floor(indexMoved) : Math.ceil(indexMoved);
              index += indexMoved;
            } else {
              var moved = -(translateInit + dist);

              if (moved <= 0) {
                index = indexMin;
              } else if (moved >= slidePositions[slideCountNew - 1]) {
                index = indexMax;
              } else {
                var i = 0;

                while (i < slideCountNew && moved >= slidePositions[i]) {
                  index = i;

                  if (moved > slidePositions[i] && dist < 0) {
                    index += 1;
                  }

                  i++;
                }
              }
            }

            render(e, dist);
            events.emit(isTouchEvent(e) ? "touchEnd" : "dragEnd", info(e));
          });
        } else {
          if (moveDirectionExpected) {
            onControlsClick(e, dist > 0 ? -1 : 1);
          }
        }
      }
    } // reset

    if (options.preventScrollOnTouch === "auto") {
      preventScroll = false;
    }

    if (swipeAngle) {
      moveDirectionExpected = "?";
    }

    if (autoplay && !animating) {
      setAutoplayTimer();
    }
  } // === RESIZE FUNCTIONS === //
  // (slidePositions, index, items) => vertical_conentWrapper.height

  function updateContentWrapperHeight() {
    var wp = middleWrapper ? middleWrapper : innerWrapper;
    wp.style.height =
      slidePositions[index + items] - slidePositions[index] + "px";
  }

  function getPages() {
    var rough = fixedWidth
      ? ((fixedWidth + gutter) * slideCount) / viewport
      : slideCount / items;
    return Math.min(Math.ceil(rough), slideCount);
  }
  /*
   * 1. update visible nav items list
   * 2. add "hidden" attributes to previous visible nav items
   * 3. remove "hidden" attrubutes to new visible nav items
   */

  function updateNavVisibility() {
    if (!nav || navAsThumbnails) {
      return;
    }

    if (pages !== pagesCached) {
      var min = pagesCached,
        max = pages,
        fn = showElement;

      if (pagesCached > pages) {
        min = pages;
        max = pagesCached;
        fn = hideElement;
      }

      while (min < max) {
        fn(navItems[min]);
        min++;
      } // cache pages

      pagesCached = pages;
    }
  }

  function info(e) {
    return {
      container: container,
      slideItems: slideItems,
      navContainer: navContainer,
      navItems: navItems,
      controlsContainer: controlsContainer,
      hasControls: hasControls,
      prevButton: prevButton,
      nextButton: nextButton,
      items: items,
      slideBy: slideBy,
      cloneCount: cloneCount,
      slideCount: slideCount,
      slideCountNew: slideCountNew,
      index: index,
      indexCached: indexCached,
      displayIndex: getCurrentSlide(),
      navCurrentIndex: navCurrentIndex,
      navCurrentIndexCached: navCurrentIndexCached,
      pages: pages,
      pagesCached: pagesCached,
      sheet: sheet,
      isOn: isOn,
      event: e || {},
    };
  }

  return {
    version: "2.9.4",
    getInfo: info,
    events: events,
    goTo: goTo,
    play: play,
    pause: pause,
    isOn: isOn,
    updateSliderHeight: updateInnerWrapperHeight,
    refresh: initSliderTransform,
    destroy: destroy,
    rebuild: function () {
      return tns(extend(options, optionsElements));
    },
  };
};

$A.extend({
  tns: tns,
});