swup
Version:
Animated page transitions with css.
477 lines (386 loc) • 17.4 kB
JavaScript
'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;