UNPKG

swup

Version:

Animated page transitions with css.

477 lines (386 loc) 17.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // helpers // modules var _delegate = require('delegate'); var _delegate2 = _interopRequireDefault(_delegate); var _Cache = require('./Cache'); var _Cache2 = _interopRequireDefault(_Cache); var _Link = require('./Link'); var _Link2 = _interopRequireDefault(_Link); var _transitionEnd = require('./transitionEnd'); var _transitionEnd2 = _interopRequireDefault(_transitionEnd); var _request = require('./modules/request'); var _request2 = _interopRequireDefault(_request); var _getDataFromHtml = require('./modules/getDataFromHtml'); var _getDataFromHtml2 = _interopRequireDefault(_getDataFromHtml); var _loadPage = require('./modules/loadPage'); var _loadPage2 = _interopRequireDefault(_loadPage); var _renderPage = require('./modules/renderPage'); var _renderPage2 = _interopRequireDefault(_renderPage); var _goBack = require('./modules/goBack'); var _goBack2 = _interopRequireDefault(_goBack); var _createState = require('./modules/createState'); var _createState2 = _interopRequireDefault(_createState); var _triggerEvent = require('./modules/triggerEvent'); var _triggerEvent2 = _interopRequireDefault(_triggerEvent); var _getUrl = require('./modules/getUrl'); var _getUrl2 = _interopRequireDefault(_getUrl); var _scrollTo = require('./modules/scrollTo'); var _scrollTo2 = _interopRequireDefault(_scrollTo); var _classify = require('./modules/classify'); var _classify2 = _interopRequireDefault(_classify); var _doScrolling = require('./modules/doScrolling'); var _doScrolling2 = _interopRequireDefault(_doScrolling); var _markSwupElements = require('./modules/markSwupElements'); var _markSwupElements2 = _interopRequireDefault(_markSwupElements); var _updateTransition = require('./modules/updateTransition'); var _updateTransition2 = _interopRequireDefault(_updateTransition); var _preloadPages = require('./modules/preloadPages'); var _preloadPages2 = _interopRequireDefault(_preloadPages); var _usePlugin = require('./modules/usePlugin'); var _usePlugin2 = _interopRequireDefault(_usePlugin); var _log = require('./modules/log'); var _log2 = _interopRequireDefault(_log); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Swup = function () { function Swup(setOptions) { _classCallCheck(this, Swup); // default options var defaults = { cache: true, animationSelector: '[class*="transition-"]', elements: ['#swup'], pageClassPrefix: '', debugMode: false, scroll: true, doScrollingRightAway: false, animateScroll: true, scrollFriction: .3, scrollAcceleration: .04, preload: true, support: true, plugins: [], skipPopStateHandling: function skipPopStateHandling(event) { if (event.state && event.state.source == "swup") { return false; } return true; }, LINK_SELECTOR: 'a[href^="' + window.location.origin + '"]:not([data-no-swup]), a[href^="/"]:not([data-no-swup]), a[href^="#"]:not([data-no-swup])', FORM_SELECTOR: 'form[data-swup-form]' /** * current transition object */ };this.transition = {}; var options = _extends({}, defaults, setOptions); /** * helper variables */ // mobile detection variable this.mobile = false; // id of element to scroll to after render this.scrollToElement = null; // promise used for preload, so no new loading of the same page starts while page is loading this.preloadPromise = null; // save options this.options = options; // plugins array this.plugins = []; /** * make modules accessible in instance */ this.getUrl = _getUrl2.default; this.cache = new _Cache2.default(); this.link = new _Link2.default(); this.transitionEndEvent = (0, _transitionEnd2.default)(); this.getDataFromHtml = _getDataFromHtml2.default; this.getPage = _request2.default; this.scrollTo = _scrollTo2.default; this.loadPage = _loadPage2.default; this.renderPage = _renderPage2.default; this.goBack = _goBack2.default; this.createState = _createState2.default; this.triggerEvent = _triggerEvent2.default; this.classify = _classify2.default; this.doScrolling = _doScrolling2.default; this.markSwupElements = _markSwupElements2.default; this.updateTransition = _updateTransition2.default; this.preloadPages = _preloadPages2.default; this.usePlugin = _usePlugin2.default; this.log = _log2.default; this.enable = this.enable; this.destroy = this.destroy; /** * detect mobile devices */ if (window.innerWidth <= 767) { this.mobile = true; } // attach instance to window in debug mode if (this.options.debugMode) { window.swup = this; } this.getUrl(); this.enable(); } _createClass(Swup, [{ key: 'enable', value: function enable() { var _this = this; /** * support check */ if (this.options.support) { // check pushState support if (!('pushState' in window.history)) { console.warn('pushState is not supported'); return; } // check transitionEnd support if ((0, _transitionEnd2.default)()) { this.transitionEndEvent = (0, _transitionEnd2.default)(); } else { console.warn('transitionEnd detection is not supported'); return; } // check Promise support if (typeof Promise === "undefined" || Promise.toString().indexOf("[native code]") === -1) { console.warn('Promise is not supported'); return; } } // variable to keep event listeners from "delegate" this.delegatedListeners = {}; /** * link click handler */ this.delegatedListeners.click = (0, _delegate2.default)(document, this.options.LINK_SELECTOR, 'click', this.linkClickHandler.bind(this)); /** * link mouseover handler (preload) */ this.delegatedListeners.mouseover = (0, _delegate2.default)(document.body, this.options.LINK_SELECTOR, 'mouseover', this.linkMouseoverHandler.bind(this)); /** * form submit handler */ this.delegatedListeners.formSubmit = (0, _delegate2.default)(document, this.options.FORM_SELECTOR, 'submit', this.formSubmitHandler.bind(this)); /** * popstate handler */ window.addEventListener('popstate', this.popStateHandler.bind(this)); /** * initial save to cache */ var page = this.getDataFromHtml(document.documentElement.outerHTML); page.url = this.currentUrl; if (this.options.cache) { this.cache.cacheUrl(page, this.options.debugMode); } /** * mark swup blocks in html */ this.markSwupElements(document.documentElement); /** * enable plugins from options */ this.options.plugins.forEach(function (item) { return _this.usePlugin(item); }); /** * modify initial history record */ window.history.replaceState(Object.assign({}, window.history.state, { url: window.location.href, random: Math.random(), source: "swup" }), document.title, window.location.href); /** * trigger enabled event */ this.triggerEvent('enabled'); document.documentElement.classList.add('swup-enabled'); /** * trigger page view event */ this.triggerEvent('pageView'); /** * preload pages if possible */ this.preloadPages(); } }, { key: 'destroy', value: function destroy() { // remove delegated listeners this.delegatedListeners.click.destroy(); this.delegatedListeners.mouseover.destroy(); // remove popstate listener window.removeEventListener('popstate', this.popStateHandler.bind(this)); // empty cache this.cache.empty(); // remove swup data atributes from blocks document.querySelectorAll('[data-swup]').forEach(function (element) { delete element.dataset.swup; }); this.triggerEvent('disabled'); document.documentElement.classList.remove('swup-enabled'); } }, { key: 'linkClickHandler', value: function linkClickHandler(event) { // no control key pressed if (!event.metaKey) { this.triggerEvent('clickLink'); var link = new _Link2.default(); event.preventDefault(); link.setPath(event.delegateTarget.href); if (link.getAddress() == this.currentUrl || link.getAddress() == '') { if (link.getHash() != '') { this.triggerEvent('samePageWithHash'); var element = document.querySelector(link.getHash()); if (element != null) { if (this.options.scroll) { var top = element.getBoundingClientRect().top + window.pageYOffset; this.scrollTo(document.body, top); } history.replaceState(undefined, undefined, link.getHash()); } else { console.warn('Element for offset not found (' + link.getHash() + ')'); } } else { this.triggerEvent('samePage'); if (this.options.scroll) { this.scrollTo(document.body, 0, 1); } } } else { if (link.getHash() != '') { this.scrollToElement = link.getHash(); } // custom class fro dynamic pages var swupClass = event.delegateTarget.dataset.swupClass; if (swupClass != null) { this.updateTransition(window.location.pathname, link.getAddress(), event.delegateTarget.dataset.swupClass); document.documentElement.classList.add('to-' + swupClass); } else { this.updateTransition(window.location.pathname, link.getAddress()); } this.loadPage({ url: link.getAddress() }, false); } } else { this.triggerEvent('openPageInNewTab'); } } }, { key: 'linkMouseoverHandler', value: function linkMouseoverHandler(event) { var _this2 = this; this.triggerEvent('hoverLink'); if (this.options.preload) { var link = new _Link2.default(); link.setPath(event.delegateTarget.href); if (link.getAddress() != this.currentUrl && !this.cache.exists(link.getAddress()) && this.preloadPromise == null) { this.preloadPromise = new Promise(function (resolve, reject) { _this2.getPage({ url: link.getAddress() }, function (response, request) { if (request.status === 500) { _this2.triggerEvent('serverError'); reject(link.getAddress()); return; } else { // get json data var page = _this2.getDataFromHtml(response); if (page != null) { page.url = link.getAddress(); _this2.cache.cacheUrl(page, _this2.options.debugMode); _this2.triggerEvent('pagePreloaded'); } else { reject(link.getAddress()); return; } } resolve(); _this2.preloadPromise = null; }); }); this.preloadPromise.route = link.getAddress(); } } } }, { key: 'formSubmitHandler', value: function formSubmitHandler(event) { // no control key pressed if (!event.metaKey) { this.triggerEvent('submitForm'); event.preventDefault(); var form = event.target; var formData = new FormData(form); var link = new _Link2.default(); link.setPath(form.action); if (link.getHash() != '') { this.scrollToElement = link.getHash(); } if (form.method.toLowerCase() != "get") { // remove page from cache this.cache.remove(link.getAddress()); // send data this.loadPage({ url: link.getAddress(), method: form.method, data: formData }); } else { // create base url var url = link.getAddress() || window.location.href; var inputs = form.querySelectorAll('input'); if (url.indexOf('?') == -1) { url += "?"; } else { url += "&"; } // add form data to url inputs.forEach(function (input) { if (input.type == "checkbox" || input.type == "radio") { if (input.checked) { url += encodeURIComponent(input.name) + "=" + encodeURIComponent(input.value) + "&"; } } else { url += encodeURIComponent(input.name) + "=" + encodeURIComponent(input.value) + "&"; } }); // remove last "&" url = url.slice(0, -1); // remove page from cache this.cache.remove(url); // send data this.loadPage({ url: url }); } } else { this.triggerEvent('openFormSubmitInNewTab'); } } }, { key: 'popStateHandler', value: function popStateHandler(event) { var link = new _Link2.default(); if (this.options.skipPopStateHandling(event)) return; link.setPath(event.state ? event.state.url : window.location.pathname); if (link.getHash() != '') { this.scrollToElement = link.getHash(); } else { event.preventDefault(); } this.triggerEvent('popState'); this.loadPage({ url: link.getAddress() }, event); } }]); return Swup; }(); exports.default = Swup;