var _shared = { pullStartY: null, pullMoveY: null, handlers: [], styleEl: null, events: null, dist: 0, state: 'pending', timeout: null, distResisted: 0, supportsPassive: false, supportsPointerEvents: !!window.PointerEvent }; try { window.addEventListener('test', null, { get passive() { // eslint-disable-line getter-return _shared.supportsPassive = true; } }); } catch (e) {// do nothing } function setupDOM(handler) { if (!handler.ptrElement) { var ptr = document.createElement('div'); if (handler.mainElement !== document.body) { handler.mainElement.parentNode.insertBefore(ptr, handler.mainElement); } else { document.body.insertBefore(ptr, document.body.firstChild); } ptr.classList.add(((handler.classPrefix) + "ptr")); ptr.innerHTML = handler.getMarkup().replace(/__PREFIX__/g, handler.classPrefix); handler.ptrElement = ptr; if (typeof handler.onInit === 'function') { handler.onInit(handler); } // Add the css styles to the style node, and then // insert it into the dom if (!_shared.styleEl) { _shared.styleEl = document.createElement('style'); _shared.styleEl.setAttribute('id', 'pull-to-refresh-js-style'); document.head.appendChild(_shared.styleEl); } _shared.styleEl.textContent = handler.getStyles().replace(/__PREFIX__/g, handler.classPrefix).replace(/\s+/g, ' '); } return handler; } function onReset(handler) { handler.ptrElement.classList.remove(((handler.classPrefix) + "refresh")); handler.ptrElement.style[handler.cssProp] = '0px'; setTimeout(function () { // remove previous ptr-element from DOM if (handler.ptrElement && handler.ptrElement.parentNode) { handler.ptrElement.parentNode.removeChild(handler.ptrElement); handler.ptrElement = null; } // reset state _shared.state = 'pending'; }, handler.refreshTimeout); } function update(handler) { var iconEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "icon")); var textEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "text")); if (iconEl) { if (_shared.state === 'refreshing') { iconEl.innerHTML = handler.iconRefreshing; } else { iconEl.innerHTML = handler.iconArrow; } } if (textEl) { if (_shared.state === 'releasing') { textEl.innerHTML = handler.instructionsReleaseToRefresh; } if (_shared.state === 'pulling' || _shared.state === 'pending') { textEl.innerHTML = handler.instructionsPullToRefresh; } if (_shared.state === 'refreshing') { textEl.innerHTML = handler.instructionsRefreshing; } } } var _ptr = { setupDOM: setupDOM, onReset: onReset, update: update }; var screenY = function screenY(event) { if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { return event.screenY; } return event.touches[0].screenY; }; var _setupEvents = (function () { var _el; function _onTouchStart(e) { // here, we must pick a handler first, and then append their html/css on the DOM var target = _shared.handlers.filter(function (h) { return h.contains(e.target); })[0]; _shared.enable = !!target; if (target && _shared.state === 'pending') { _el = _ptr.setupDOM(target); if (target.shouldPullToRefresh()) { _shared.pullStartY = screenY(e); } clearTimeout(_shared.timeout); _ptr.update(target); } } function _onTouchMove(e) { if (!(_el && _el.ptrElement && _shared.enable)) { return; } if (!_shared.pullStartY) { if (_el.shouldPullToRefresh()) { _shared.pullStartY = screenY(e); } } else { _shared.pullMoveY = screenY(e); } if (_shared.state === 'refreshing') { if (e.cancelable && _el.shouldPullToRefresh() && _shared.pullStartY < _shared.pullMoveY) { e.preventDefault(); } return; } if (_shared.state === 'pending') { _el.ptrElement.classList.add(((_el.classPrefix) + "pull")); _shared.state = 'pulling'; _ptr.update(_el); } if (_shared.pullStartY && _shared.pullMoveY) { _shared.dist = _shared.pullMoveY - _shared.pullStartY; } _shared.distExtra = _shared.dist - _el.distIgnore; if (_shared.distExtra > 0) { if (e.cancelable) { e.preventDefault(); } _el.ptrElement.style[_el.cssProp] = (_shared.distResisted) + "px"; _shared.distResisted = _el.resistanceFunction(_shared.distExtra / _el.distThreshold) * Math.min(_el.distMax, _shared.distExtra); if (_shared.state === 'pulling' && _shared.distResisted > _el.distThreshold) { _el.ptrElement.classList.add(((_el.classPrefix) + "release")); _shared.state = 'releasing'; _ptr.update(_el); } if (_shared.state === 'releasing' && _shared.distResisted < _el.distThreshold) { _el.ptrElement.classList.remove(((_el.classPrefix) + "release")); _shared.state = 'pulling'; _ptr.update(_el); } } } function _onTouchEnd() { if (!(_el && _el.ptrElement && _shared.enable)) { return; } if (_shared.state === 'releasing' && _shared.distResisted > _el.distThreshold) { _shared.state = 'refreshing'; _el.ptrElement.style[_el.cssProp] = (_el.distReload) + "px"; _el.ptrElement.classList.add(((_el.classPrefix) + "refresh")); _shared.timeout = setTimeout(function () { var retval = _el.onRefresh(function () { return _ptr.onReset(_el); }); if (retval && typeof retval.then === 'function') { retval.then(function () { return _ptr.onReset(_el); }); } if (!retval && !_el.onRefresh.length) { _ptr.onReset(_el); } }, _el.refreshTimeout); } else { if (_shared.state === 'refreshing') { return; } _el.ptrElement.style[_el.cssProp] = '0px'; _shared.state = 'pending'; } _ptr.update(_el); _el.ptrElement.classList.remove(((_el.classPrefix) + "release")); _el.ptrElement.classList.remove(((_el.classPrefix) + "pull")); _shared.pullStartY = _shared.pullMoveY = null; _shared.dist = _shared.distResisted = 0; } function _onScroll() { if (_el) { _el.mainElement.classList.toggle(((_el.classPrefix) + "top"), _el.shouldPullToRefresh()); } } var _passiveSettings = _shared.supportsPassive ? { passive: _shared.passive || false } : undefined; if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { window.addEventListener('pointerup', _onTouchEnd); window.addEventListener('pointerdown', _onTouchStart); window.addEventListener('pointermove', _onTouchMove, _passiveSettings); } else { window.addEventListener('touchend', _onTouchEnd); window.addEventListener('touchstart', _onTouchStart); window.addEventListener('touchmove', _onTouchMove, _passiveSettings); } window.addEventListener('scroll', _onScroll); return { onTouchEnd: _onTouchEnd, onTouchStart: _onTouchStart, onTouchMove: _onTouchMove, onScroll: _onScroll, destroy: function destroy() { if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { window.removeEventListener('pointerdown', _onTouchStart); window.removeEventListener('pointerup', _onTouchEnd); window.removeEventListener('pointermove', _onTouchMove, _passiveSettings); } else { window.removeEventListener('touchstart', _onTouchStart); window.removeEventListener('touchend', _onTouchEnd); window.removeEventListener('touchmove', _onTouchMove, _passiveSettings); } window.removeEventListener('scroll', _onScroll); } }; }); var _ptrMarkup = "\n
\n
\n
\n
\n
\n
\n"; var _ptrStyles = "\n.__PREFIX__ptr {\n box-shadow: inset 0 -3px 5px rgba(0, 0, 0, 0.12);\n pointer-events: none;\n font-size: 0.85em;\n font-weight: bold;\n top: 0;\n height: 0;\n transition: height 0.3s, min-height 0.3s;\n text-align: center;\n width: 100%;\n overflow: hidden;\n display: flex;\n align-items: flex-end;\n align-content: stretch;\n}\n\n.__PREFIX__box {\n padding: 10px;\n flex-basis: 100%;\n}\n\n.__PREFIX__pull {\n transition: none;\n}\n\n.__PREFIX__text {\n margin-top: .33em;\n color: rgba(0, 0, 0, 0.3);\n}\n\n.__PREFIX__icon {\n color: rgba(0, 0, 0, 0.3);\n transition: transform .3s;\n}\n\n/*\nWhen at the top of the page, disable vertical overscroll so passive touch\nlisteners can take over.\n*/\n.__PREFIX__top {\n touch-action: pan-x pan-down pinch-zoom;\n}\n\n.__PREFIX__release {\n .__PREFIX__icon {\n transform: rotate(180deg);\n }\n}\n"; var _defaults = { distThreshold: 60, distMax: 80, distReload: 50, distIgnore: 0, mainElement: 'body', triggerElement: 'body', ptrElement: '.ptr', classPrefix: 'ptr--', cssProp: 'min-height', iconArrow: '⇣', iconRefreshing: '…', instructionsPullToRefresh: 'Pull down to refresh', instructionsReleaseToRefresh: 'Release to refresh', instructionsRefreshing: 'Refreshing', refreshTimeout: 500, getMarkup: function () { return _ptrMarkup; }, getStyles: function () { return _ptrStyles; }, onInit: function () {}, onRefresh: function () { return location.reload(); }, resistanceFunction: function (t) { return Math.min(1, t / 2.5); }, shouldPullToRefresh: function () { return !window.scrollY; } }; var _methods = ['mainElement', 'ptrElement', 'triggerElement']; var _setupHandler = (function (options) { var _handler = {}; // merge options with defaults Object.keys(_defaults).forEach(function (key) { _handler[key] = options[key] || _defaults[key]; }); // normalize timeout value, even if it is zero _handler.refreshTimeout = typeof options.refreshTimeout === 'number' ? options.refreshTimeout : _defaults.refreshTimeout; // normalize elements _methods.forEach(function (method) { if (typeof _handler[method] === 'string') { _handler[method] = document.querySelector(_handler[method]); } }); // attach events lazily if (!_shared.events) { _shared.events = _setupEvents(); } _handler.contains = function (target) { return _handler.triggerElement.contains(target); }; _handler.destroy = function () { // stop pending any pending callbacks clearTimeout(_shared.timeout); // remove handler from shared state var offset = _shared.handlers.indexOf(_handler); _shared.handlers.splice(offset, 1); }; return _handler; }); var index = { setPassiveMode: function setPassiveMode(isPassive) { _shared.passive = isPassive; }, setPointerEventsMode: function setPointerEventsMode(isEnabled) { _shared.pointerEventsEnabled = isEnabled; }, destroyAll: function destroyAll() { if (_shared.events) { _shared.events.destroy(); _shared.events = null; } _shared.handlers.forEach(function (h) { h.destroy(); }); }, init: function init(options) { if ( options === void 0 ) options = {}; var handler = _setupHandler(options); _shared.handlers.push(handler); return handler; }, // export utils for testing _: { setupHandler: _setupHandler, setupEvents: _setupEvents, setupDOM: _ptr.setupDOM, onReset: _ptr.onReset, update: _ptr.update } }; export default index;