core.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. /* eslint no-param-reassign: "off" */
  2. import { getDocument } from 'ssr-window';
  3. import $ from '../shared/dom.js';
  4. import { extend, now, deleteProps } from '../shared/utils.js';
  5. import { getSupport } from '../shared/get-support.js';
  6. import { getDevice } from '../shared/get-device.js';
  7. import { getBrowser } from '../shared/get-browser.js';
  8. import Resize from './modules/resize/resize.js';
  9. import Observer from './modules/observer/observer.js';
  10. import eventsEmitter from './events-emitter.js';
  11. import update from './update/index.js';
  12. import translate from './translate/index.js';
  13. import transition from './transition/index.js';
  14. import slide from './slide/index.js';
  15. import loop from './loop/index.js';
  16. import grabCursor from './grab-cursor/index.js';
  17. import events from './events/index.js';
  18. import breakpoints from './breakpoints/index.js';
  19. import classes from './classes/index.js';
  20. import images from './images/index.js';
  21. import checkOverflow from './check-overflow/index.js';
  22. import defaults from './defaults.js';
  23. import moduleExtendParams from './moduleExtendParams.js';
  24. const prototypes = {
  25. eventsEmitter,
  26. update,
  27. translate,
  28. transition,
  29. slide,
  30. loop,
  31. grabCursor,
  32. events,
  33. breakpoints,
  34. checkOverflow,
  35. classes,
  36. images
  37. };
  38. const extendedDefaults = {};
  39. class Swiper {
  40. constructor() {
  41. let el;
  42. let params;
  43. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  44. args[_key] = arguments[_key];
  45. }
  46. if (args.length === 1 && args[0].constructor && Object.prototype.toString.call(args[0]).slice(8, -1) === 'Object') {
  47. params = args[0];
  48. } else {
  49. [el, params] = args;
  50. }
  51. if (!params) params = {};
  52. params = extend({}, params);
  53. if (el && !params.el) params.el = el;
  54. if (params.el && $(params.el).length > 1) {
  55. const swipers = [];
  56. $(params.el).each(containerEl => {
  57. const newParams = extend({}, params, {
  58. el: containerEl
  59. });
  60. swipers.push(new Swiper(newParams));
  61. });
  62. return swipers;
  63. } // Swiper Instance
  64. const swiper = this;
  65. swiper.__swiper__ = true;
  66. swiper.support = getSupport();
  67. swiper.device = getDevice({
  68. userAgent: params.userAgent
  69. });
  70. swiper.browser = getBrowser();
  71. swiper.eventsListeners = {};
  72. swiper.eventsAnyListeners = [];
  73. swiper.modules = [...swiper.__modules__];
  74. if (params.modules && Array.isArray(params.modules)) {
  75. swiper.modules.push(...params.modules);
  76. }
  77. const allModulesParams = {};
  78. swiper.modules.forEach(mod => {
  79. mod({
  80. swiper,
  81. extendParams: moduleExtendParams(params, allModulesParams),
  82. on: swiper.on.bind(swiper),
  83. once: swiper.once.bind(swiper),
  84. off: swiper.off.bind(swiper),
  85. emit: swiper.emit.bind(swiper)
  86. });
  87. }); // Extend defaults with modules params
  88. const swiperParams = extend({}, defaults, allModulesParams); // Extend defaults with passed params
  89. swiper.params = extend({}, swiperParams, extendedDefaults, params);
  90. swiper.originalParams = extend({}, swiper.params);
  91. swiper.passedParams = extend({}, params); // add event listeners
  92. if (swiper.params && swiper.params.on) {
  93. Object.keys(swiper.params.on).forEach(eventName => {
  94. swiper.on(eventName, swiper.params.on[eventName]);
  95. });
  96. }
  97. if (swiper.params && swiper.params.onAny) {
  98. swiper.onAny(swiper.params.onAny);
  99. } // Save Dom lib
  100. swiper.$ = $; // Extend Swiper
  101. Object.assign(swiper, {
  102. enabled: swiper.params.enabled,
  103. el,
  104. // Classes
  105. classNames: [],
  106. // Slides
  107. slides: $(),
  108. slidesGrid: [],
  109. snapGrid: [],
  110. slidesSizesGrid: [],
  111. // isDirection
  112. isHorizontal() {
  113. return swiper.params.direction === 'horizontal';
  114. },
  115. isVertical() {
  116. return swiper.params.direction === 'vertical';
  117. },
  118. // Indexes
  119. activeIndex: 0,
  120. realIndex: 0,
  121. //
  122. isBeginning: true,
  123. isEnd: false,
  124. // Props
  125. translate: 0,
  126. previousTranslate: 0,
  127. progress: 0,
  128. velocity: 0,
  129. animating: false,
  130. // Locks
  131. allowSlideNext: swiper.params.allowSlideNext,
  132. allowSlidePrev: swiper.params.allowSlidePrev,
  133. // Touch Events
  134. touchEvents: function touchEvents() {
  135. const touch = ['touchstart', 'touchmove', 'touchend', 'touchcancel'];
  136. const desktop = ['pointerdown', 'pointermove', 'pointerup'];
  137. swiper.touchEventsTouch = {
  138. start: touch[0],
  139. move: touch[1],
  140. end: touch[2],
  141. cancel: touch[3]
  142. };
  143. swiper.touchEventsDesktop = {
  144. start: desktop[0],
  145. move: desktop[1],
  146. end: desktop[2]
  147. };
  148. return swiper.support.touch || !swiper.params.simulateTouch ? swiper.touchEventsTouch : swiper.touchEventsDesktop;
  149. }(),
  150. touchEventsData: {
  151. isTouched: undefined,
  152. isMoved: undefined,
  153. allowTouchCallbacks: undefined,
  154. touchStartTime: undefined,
  155. isScrolling: undefined,
  156. currentTranslate: undefined,
  157. startTranslate: undefined,
  158. allowThresholdMove: undefined,
  159. // Form elements to match
  160. focusableElements: swiper.params.focusableElements,
  161. // Last click time
  162. lastClickTime: now(),
  163. clickTimeout: undefined,
  164. // Velocities
  165. velocities: [],
  166. allowMomentumBounce: undefined,
  167. isTouchEvent: undefined,
  168. startMoving: undefined
  169. },
  170. // Clicks
  171. allowClick: true,
  172. // Touches
  173. allowTouchMove: swiper.params.allowTouchMove,
  174. touches: {
  175. startX: 0,
  176. startY: 0,
  177. currentX: 0,
  178. currentY: 0,
  179. diff: 0
  180. },
  181. // Images
  182. imagesToLoad: [],
  183. imagesLoaded: 0
  184. });
  185. swiper.emit('_swiper'); // Init
  186. if (swiper.params.init) {
  187. swiper.init();
  188. } // Return app instance
  189. return swiper;
  190. }
  191. enable() {
  192. const swiper = this;
  193. if (swiper.enabled) return;
  194. swiper.enabled = true;
  195. if (swiper.params.grabCursor) {
  196. swiper.setGrabCursor();
  197. }
  198. swiper.emit('enable');
  199. }
  200. disable() {
  201. const swiper = this;
  202. if (!swiper.enabled) return;
  203. swiper.enabled = false;
  204. if (swiper.params.grabCursor) {
  205. swiper.unsetGrabCursor();
  206. }
  207. swiper.emit('disable');
  208. }
  209. setProgress(progress, speed) {
  210. const swiper = this;
  211. progress = Math.min(Math.max(progress, 0), 1);
  212. const min = swiper.minTranslate();
  213. const max = swiper.maxTranslate();
  214. const current = (max - min) * progress + min;
  215. swiper.translateTo(current, typeof speed === 'undefined' ? 0 : speed);
  216. swiper.updateActiveIndex();
  217. swiper.updateSlidesClasses();
  218. }
  219. emitContainerClasses() {
  220. const swiper = this;
  221. if (!swiper.params._emitClasses || !swiper.el) return;
  222. const cls = swiper.el.className.split(' ').filter(className => {
  223. return className.indexOf('swiper') === 0 || className.indexOf(swiper.params.containerModifierClass) === 0;
  224. });
  225. swiper.emit('_containerClasses', cls.join(' '));
  226. }
  227. getSlideClasses(slideEl) {
  228. const swiper = this;
  229. return slideEl.className.split(' ').filter(className => {
  230. return className.indexOf('swiper-slide') === 0 || className.indexOf(swiper.params.slideClass) === 0;
  231. }).join(' ');
  232. }
  233. emitSlidesClasses() {
  234. const swiper = this;
  235. if (!swiper.params._emitClasses || !swiper.el) return;
  236. const updates = [];
  237. swiper.slides.each(slideEl => {
  238. const classNames = swiper.getSlideClasses(slideEl);
  239. updates.push({
  240. slideEl,
  241. classNames
  242. });
  243. swiper.emit('_slideClass', slideEl, classNames);
  244. });
  245. swiper.emit('_slideClasses', updates);
  246. }
  247. slidesPerViewDynamic(view, exact) {
  248. if (view === void 0) {
  249. view = 'current';
  250. }
  251. if (exact === void 0) {
  252. exact = false;
  253. }
  254. const swiper = this;
  255. const {
  256. params,
  257. slides,
  258. slidesGrid,
  259. slidesSizesGrid,
  260. size: swiperSize,
  261. activeIndex
  262. } = swiper;
  263. let spv = 1;
  264. if (params.centeredSlides) {
  265. let slideSize = slides[activeIndex].swiperSlideSize;
  266. let breakLoop;
  267. for (let i = activeIndex + 1; i < slides.length; i += 1) {
  268. if (slides[i] && !breakLoop) {
  269. slideSize += slides[i].swiperSlideSize;
  270. spv += 1;
  271. if (slideSize > swiperSize) breakLoop = true;
  272. }
  273. }
  274. for (let i = activeIndex - 1; i >= 0; i -= 1) {
  275. if (slides[i] && !breakLoop) {
  276. slideSize += slides[i].swiperSlideSize;
  277. spv += 1;
  278. if (slideSize > swiperSize) breakLoop = true;
  279. }
  280. }
  281. } else {
  282. // eslint-disable-next-line
  283. if (view === 'current') {
  284. for (let i = activeIndex + 1; i < slides.length; i += 1) {
  285. const slideInView = exact ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize;
  286. if (slideInView) {
  287. spv += 1;
  288. }
  289. }
  290. } else {
  291. // previous
  292. for (let i = activeIndex - 1; i >= 0; i -= 1) {
  293. const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize;
  294. if (slideInView) {
  295. spv += 1;
  296. }
  297. }
  298. }
  299. }
  300. return spv;
  301. }
  302. update() {
  303. const swiper = this;
  304. if (!swiper || swiper.destroyed) return;
  305. const {
  306. snapGrid,
  307. params
  308. } = swiper; // Breakpoints
  309. if (params.breakpoints) {
  310. swiper.setBreakpoint();
  311. }
  312. swiper.updateSize();
  313. swiper.updateSlides();
  314. swiper.updateProgress();
  315. swiper.updateSlidesClasses();
  316. function setTranslate() {
  317. const translateValue = swiper.rtlTranslate ? swiper.translate * -1 : swiper.translate;
  318. const newTranslate = Math.min(Math.max(translateValue, swiper.maxTranslate()), swiper.minTranslate());
  319. swiper.setTranslate(newTranslate);
  320. swiper.updateActiveIndex();
  321. swiper.updateSlidesClasses();
  322. }
  323. let translated;
  324. if (swiper.params.freeMode && swiper.params.freeMode.enabled) {
  325. setTranslate();
  326. if (swiper.params.autoHeight) {
  327. swiper.updateAutoHeight();
  328. }
  329. } else {
  330. if ((swiper.params.slidesPerView === 'auto' || swiper.params.slidesPerView > 1) && swiper.isEnd && !swiper.params.centeredSlides) {
  331. translated = swiper.slideTo(swiper.slides.length - 1, 0, false, true);
  332. } else {
  333. translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
  334. }
  335. if (!translated) {
  336. setTranslate();
  337. }
  338. }
  339. if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
  340. swiper.checkOverflow();
  341. }
  342. swiper.emit('update');
  343. }
  344. changeDirection(newDirection, needUpdate) {
  345. if (needUpdate === void 0) {
  346. needUpdate = true;
  347. }
  348. const swiper = this;
  349. const currentDirection = swiper.params.direction;
  350. if (!newDirection) {
  351. // eslint-disable-next-line
  352. newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
  353. }
  354. if (newDirection === currentDirection || newDirection !== 'horizontal' && newDirection !== 'vertical') {
  355. return swiper;
  356. }
  357. swiper.$el.removeClass(`${swiper.params.containerModifierClass}${currentDirection}`).addClass(`${swiper.params.containerModifierClass}${newDirection}`);
  358. swiper.emitContainerClasses();
  359. swiper.params.direction = newDirection;
  360. swiper.slides.each(slideEl => {
  361. if (newDirection === 'vertical') {
  362. slideEl.style.width = '';
  363. } else {
  364. slideEl.style.height = '';
  365. }
  366. });
  367. swiper.emit('changeDirection');
  368. if (needUpdate) swiper.update();
  369. return swiper;
  370. }
  371. mount(el) {
  372. const swiper = this;
  373. if (swiper.mounted) return true; // Find el
  374. const $el = $(el || swiper.params.el);
  375. el = $el[0];
  376. if (!el) {
  377. return false;
  378. }
  379. el.swiper = swiper;
  380. const getWrapperSelector = () => {
  381. return `.${(swiper.params.wrapperClass || '').trim().split(' ').join('.')}`;
  382. };
  383. const getWrapper = () => {
  384. if (el && el.shadowRoot && el.shadowRoot.querySelector) {
  385. const res = $(el.shadowRoot.querySelector(getWrapperSelector())); // Children needs to return slot items
  386. res.children = options => $el.children(options);
  387. return res;
  388. }
  389. return $el.children(getWrapperSelector());
  390. }; // Find Wrapper
  391. let $wrapperEl = getWrapper();
  392. if ($wrapperEl.length === 0 && swiper.params.createElements) {
  393. const document = getDocument();
  394. const wrapper = document.createElement('div');
  395. $wrapperEl = $(wrapper);
  396. wrapper.className = swiper.params.wrapperClass;
  397. $el.append(wrapper);
  398. $el.children(`.${swiper.params.slideClass}`).each(slideEl => {
  399. $wrapperEl.append(slideEl);
  400. });
  401. }
  402. Object.assign(swiper, {
  403. $el,
  404. el,
  405. $wrapperEl,
  406. wrapperEl: $wrapperEl[0],
  407. mounted: true,
  408. // RTL
  409. rtl: el.dir.toLowerCase() === 'rtl' || $el.css('direction') === 'rtl',
  410. rtlTranslate: swiper.params.direction === 'horizontal' && (el.dir.toLowerCase() === 'rtl' || $el.css('direction') === 'rtl'),
  411. wrongRTL: $wrapperEl.css('display') === '-webkit-box'
  412. });
  413. return true;
  414. }
  415. init(el) {
  416. const swiper = this;
  417. if (swiper.initialized) return swiper;
  418. const mounted = swiper.mount(el);
  419. if (mounted === false) return swiper;
  420. swiper.emit('beforeInit'); // Set breakpoint
  421. if (swiper.params.breakpoints) {
  422. swiper.setBreakpoint();
  423. } // Add Classes
  424. swiper.addClasses(); // Create loop
  425. if (swiper.params.loop) {
  426. swiper.loopCreate();
  427. } // Update size
  428. swiper.updateSize(); // Update slides
  429. swiper.updateSlides();
  430. if (swiper.params.watchOverflow) {
  431. swiper.checkOverflow();
  432. } // Set Grab Cursor
  433. if (swiper.params.grabCursor && swiper.enabled) {
  434. swiper.setGrabCursor();
  435. }
  436. if (swiper.params.preloadImages) {
  437. swiper.preloadImages();
  438. } // Slide To Initial Slide
  439. if (swiper.params.loop) {
  440. swiper.slideTo(swiper.params.initialSlide + swiper.loopedSlides, 0, swiper.params.runCallbacksOnInit, false, true);
  441. } else {
  442. swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
  443. } // Attach events
  444. swiper.attachEvents(); // Init Flag
  445. swiper.initialized = true; // Emit
  446. swiper.emit('init');
  447. swiper.emit('afterInit');
  448. return swiper;
  449. }
  450. destroy(deleteInstance, cleanStyles) {
  451. if (deleteInstance === void 0) {
  452. deleteInstance = true;
  453. }
  454. if (cleanStyles === void 0) {
  455. cleanStyles = true;
  456. }
  457. const swiper = this;
  458. const {
  459. params,
  460. $el,
  461. $wrapperEl,
  462. slides
  463. } = swiper;
  464. if (typeof swiper.params === 'undefined' || swiper.destroyed) {
  465. return null;
  466. }
  467. swiper.emit('beforeDestroy'); // Init Flag
  468. swiper.initialized = false; // Detach events
  469. swiper.detachEvents(); // Destroy loop
  470. if (params.loop) {
  471. swiper.loopDestroy();
  472. } // Cleanup styles
  473. if (cleanStyles) {
  474. swiper.removeClasses();
  475. $el.removeAttr('style');
  476. $wrapperEl.removeAttr('style');
  477. if (slides && slides.length) {
  478. slides.removeClass([params.slideVisibleClass, params.slideActiveClass, params.slideNextClass, params.slidePrevClass].join(' ')).removeAttr('style').removeAttr('data-swiper-slide-index');
  479. }
  480. }
  481. swiper.emit('destroy'); // Detach emitter events
  482. Object.keys(swiper.eventsListeners).forEach(eventName => {
  483. swiper.off(eventName);
  484. });
  485. if (deleteInstance !== false) {
  486. swiper.$el[0].swiper = null;
  487. deleteProps(swiper);
  488. }
  489. swiper.destroyed = true;
  490. return null;
  491. }
  492. static extendDefaults(newDefaults) {
  493. extend(extendedDefaults, newDefaults);
  494. }
  495. static get extendedDefaults() {
  496. return extendedDefaults;
  497. }
  498. static get defaults() {
  499. return defaults;
  500. }
  501. static installModule(mod) {
  502. if (!Swiper.prototype.__modules__) Swiper.prototype.__modules__ = [];
  503. const modules = Swiper.prototype.__modules__;
  504. if (typeof mod === 'function' && modules.indexOf(mod) < 0) {
  505. modules.push(mod);
  506. }
  507. }
  508. static use(module) {
  509. if (Array.isArray(module)) {
  510. module.forEach(m => Swiper.installModule(m));
  511. return Swiper;
  512. }
  513. Swiper.installModule(module);
  514. return Swiper;
  515. }
  516. }
  517. Object.keys(prototypes).forEach(prototypeGroup => {
  518. Object.keys(prototypes[prototypeGroup]).forEach(protoMethod => {
  519. Swiper.prototype[protoMethod] = prototypes[prototypeGroup][protoMethod];
  520. });
  521. });
  522. Swiper.use([Resize, Observer]);
  523. export default Swiper;