overscroll
Version:
1,900 lines (1,578 loc) • 158 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.overscroll = factory());
}(this, (function () { 'use strict';
function compose() {
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key];
}
if (funcs.length === 0) {
return function (arg) {
return arg;
};
}
if (funcs.length === 1) {
return funcs[0];
}
var last = funcs[funcs.length - 1];
var rest = funcs.slice(0, -1);
return function () {
return rest.reduceRight(function (composed, f) {
return f(composed);
}, last.apply(undefined, arguments));
};
}
/*!
* isobject <https://github.com/jonschlinkert/isobject>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
var isobject = function isObject(val) {
return val != null && typeof val === 'object' && Array.isArray(val) === false;
};
/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
function isObjectObject(o) {
return isobject(o) === true
&& Object.prototype.toString.call(o) === '[object Object]';
}
var isPlainObject = function isPlainObject(o) {
var ctor,prot;
if (isObjectObject(o) === false) return false;
// If has modified constructor
ctor = o.constructor;
if (typeof ctor !== 'function') return false;
// If has modified prototype
prot = ctor.prototype;
if (isObjectObject(prot) === false) return false;
// If constructor does not have an Object-specific method
if (prot.hasOwnProperty('isPrototypeOf') === false) {
return false;
}
// Most likely a plain Object
return true;
};
var toString = Object.prototype.toString;
var kindOf = function kindOf(val) {
if (val === void 0) return 'undefined';
if (val === null) return 'null';
var type = typeof val;
if (type === 'boolean') return 'boolean';
if (type === 'string') return 'string';
if (type === 'number') return 'number';
if (type === 'symbol') return 'symbol';
if (type === 'function') {
return isGeneratorFn(val) ? 'generatorfunction' : 'function';
}
if (isArray(val)) return 'array';
if (isBuffer(val)) return 'buffer';
if (isArguments(val)) return 'arguments';
if (isDate(val)) return 'date';
if (isError(val)) return 'error';
if (isRegexp(val)) return 'regexp';
switch (ctorName(val)) {
case 'Symbol': return 'symbol';
case 'Promise': return 'promise';
// Set, Map, WeakSet, WeakMap
case 'WeakMap': return 'weakmap';
case 'WeakSet': return 'weakset';
case 'Map': return 'map';
case 'Set': return 'set';
// 8-bit typed arrays
case 'Int8Array': return 'int8array';
case 'Uint8Array': return 'uint8array';
case 'Uint8ClampedArray': return 'uint8clampedarray';
// 16-bit typed arrays
case 'Int16Array': return 'int16array';
case 'Uint16Array': return 'uint16array';
// 32-bit typed arrays
case 'Int32Array': return 'int32array';
case 'Uint32Array': return 'uint32array';
case 'Float32Array': return 'float32array';
case 'Float64Array': return 'float64array';
}
if (isGeneratorObj(val)) {
return 'generator';
}
// Non-plain objects
type = toString.call(val);
switch (type) {
case '[object Object]': return 'object';
// iterators
case '[object Map Iterator]': return 'mapiterator';
case '[object Set Iterator]': return 'setiterator';
case '[object String Iterator]': return 'stringiterator';
case '[object Array Iterator]': return 'arrayiterator';
}
// other
return type.slice(8, -1).toLowerCase().replace(/\s/g, '');
};
function ctorName(val) {
return val.constructor ? val.constructor.name : null;
}
function isArray(val) {
if (Array.isArray) return Array.isArray(val);
return val instanceof Array;
}
function isError(val) {
return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number');
}
function isDate(val) {
if (val instanceof Date) return true;
return typeof val.toDateString === 'function'
&& typeof val.getDate === 'function'
&& typeof val.setDate === 'function';
}
function isRegexp(val) {
if (val instanceof RegExp) return true;
return typeof val.flags === 'string'
&& typeof val.ignoreCase === 'boolean'
&& typeof val.multiline === 'boolean'
&& typeof val.global === 'boolean';
}
function isGeneratorFn(name, val) {
return ctorName(name) === 'GeneratorFunction';
}
function isGeneratorObj(val) {
return typeof val.throw === 'function'
&& typeof val.return === 'function'
&& typeof val.next === 'function';
}
function isArguments(val) {
try {
if (typeof val.length === 'number' && typeof val.callee === 'function') {
return true;
}
} catch (err) {
if (err.message.indexOf('callee') !== -1) {
return true;
}
}
return false;
}
/**
* If you need to support Safari 5-7 (8-10 yr-old browser),
* take a look at https://github.com/feross/is-buffer
*/
function isBuffer(val) {
if (val.constructor && typeof val.constructor.isBuffer === 'function') {
return val.constructor.isBuffer(val);
}
return false;
}
var itis = {};
['Array', 'Number', 'Function', 'RegExp', 'Boolean', 'Date', 'Error', 'Arguments', 'Null', 'String'].forEach(function (name) {
itis[name] = function (v) {
return kindOf(v) === name.toLowerCase();
};
});
/**
* 2017-08-12 rainx
* If a variable is not equal to null or undefined, I think it is defined.
*/
var isDefined = (function (variable) {
return variable !== null && variable !== undefined;
});
/**
* 2017-08-12 rainx
* Be contrary to isDefined.
*/
var isUndefined = (function (variable) {
return variable === null || variable === undefined;
});
function isItClass(Cls) {
return function (obj) {
return obj instanceof Cls;
};
}
var isWindow = function (win) {
return win != null && win === win.window;
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
/**
* nodeType 说明 http://www.w3school.com.cn/jsref/prop_node_nodetype.asp
*
* 1 Element 代表元素
* Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference
* 2 Attr 代表属性
* Text, EntityReference
* 3 Text 代表元素或属性中的文本内容
* None
* 4 CDATASection 代表文档中的 CDATA 部分(不会由解析器解析的文本)
* None
* 5 EntityReference 代表实体引用
* Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
* 6 Entity 代表实体
* Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
* 7 ProcessingInstruction 代表处理指令
* None
* 8 Comment 代表注释
* None
* 9 Document 代表整个文档(DOM 树的根节点)
* Element, ProcessingInstruction, Comment, DocumentType
* 10 DocumentType 向为文档定义的实体提供接口
* None
* 11 DocumentFragment 代表轻量级的 Document 对象,能够容纳文档的某个部分
* Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
* 12 Notation 代表 DTD 中声明的符号
* None
*/
var isElement = (function (element) {
return (typeof element === 'undefined' ? 'undefined' : _typeof(element)) === 'object' && element.nodeType === 1;
});
var _typeof$1 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var isDocument = (function (element) {
return (typeof element === 'undefined' ? 'undefined' : _typeof$1(element)) === 'object' && element.nodeType === 9;
});
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
function getLength(obj) {
return isobject(obj) ? obj.length : undefined;
}
var isArrayLike = function (collection) {
var length = getLength(collection);
return itis.Number(length) && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// Number, Function, RegExp, Boolean, Date, Error, Arguments,
// PlainObject, Object, Array, ArrayLike, Element
var is = Object.assign(itis, {
Undefined: isUndefined,
Defined: isDefined,
Element: isElement,
Window: isWindow,
Document: isDocument,
PlainObject: isPlainObject,
Object: isobject,
ArrayLike: isArrayLike,
isItClass: isItClass
});
var actions = (function (scope) {
var target = scope.target,
win = scope.win,
overscroll = scope.overscroll,
isPageScroll = scope.isPageScroll,
resetState = scope.resetState,
getScroll = scope.getScroll,
getFromRange = scope.getFromRange,
getScrollSize = scope.getScrollSize,
getClientSize = scope.getClientSize;
function setScroll() {
var scrollLeft = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll.scrollLeft;
var scrollTop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : overscroll.scrollTop;
// 取出滚动区域大小和可视范围计算滚动位置
var scrollTopRange = overscroll.scrollTopRange,
scrollLeftRange = overscroll.scrollLeftRange;
var newScrollTop = scrollTopRange(scrollTop);
var newScrollLeft = scrollLeftRange(scrollLeft);
// 重置dom状态
resetState(newScrollTop, newScrollLeft);
// 缓存滚动条位置
overscroll.scrollLeft = newScrollLeft;
overscroll.scrollTop = newScrollTop;
if (isPageScroll) {
win.scrollTo(overscroll.scrollLeft, overscroll.scrollTop);
} else {
target.scrollTop = overscroll.scrollTop;
target.scrollLeft = overscroll.scrollLeft;
}
}
function resetCache() {
var _getScroll = getScroll(),
top = _getScroll.top,
left = _getScroll.left;
var _getScrollSize = getScrollSize(),
scrollWidth = _getScrollSize.width,
scrollHeight = _getScrollSize.height;
var _getClientSize = getClientSize(),
clientWidth = _getClientSize.width,
clientHeight = _getClientSize.height;
overscroll.scrollTop = top;
overscroll.scrollLeft = left;
overscroll.scrollWidth = scrollWidth;
overscroll.scrollHeight = scrollHeight;
overscroll.clientWidth = clientWidth;
overscroll.clientHeight = clientHeight;
overscroll.scrollTopRange = getFromRange(0, scrollHeight - clientHeight);
overscroll.scrollLeftRange = getFromRange(0, scrollWidth - clientWidth);
}
return {
setScroll: setScroll, resetCache: resetCache
};
});
var getOffset = function getOffset(_ref) {
var win = _ref.win,
body = _ref.body,
html = _ref.html;
return function (node) {
var box = node.getBoundingClientRect();
// < ie8 不支持 win.pageXOffset, 则使用 html.scrollLeft
return {
left: box.left + (win.pageXOffset || html.scrollLeft) - (html.clientLeft || body.clientLeft || 0),
top: box.top + (win.pageYOffset || html.scrollTop) - (html.clientTop || body.clientTop || 0)
};
};
};
var getPosition = function getPosition(scope) {
return function (node) {
// if ( node.parentNode === scope.body ) {
// return getOffset( scope )( node );
// }
var parent = node.parentNode;
var parentOffset = getOffset(scope)(parent);
var offset = getOffset(scope)(node);
return {
left: offset.left - parentOffset.left,
top: offset.top - parentOffset.top
};
};
};
var getFromRange = (function (min, max) {
return function (number) {
return Math.min(max, Math.max(min, number));
};
});
var getClientSize = (function (_ref) {
var html = _ref.html,
target = _ref.target,
isPageScroll = _ref.isPageScroll;
return function () {
var width = void 0;
var height = void 0;
if (isPageScroll) {
width = html.clientWidth;
height = html.clientHeight;
// width = win.innerWidth || html.clientWidth;
// height = win.innerHeight || html.clientHeight;
} else {
width = target.clientWidth;
height = target.clientHeight;
}
return { width: width, height: height };
};
});
var getScrollSize = (function (_ref) {
var body = _ref.body,
html = _ref.html,
target = _ref.target,
isPageScroll = _ref.isPageScroll;
return function () {
var width = void 0;
var height = void 0;
if (isPageScroll) {
width = Math.max(html.scrollWidth, body.scrollWidth);
height = Math.max(html.scrollHeight, body.scrollHeight);
} else {
width = target.scrollWidth;
height = target.scrollHeight;
}
return { width: width, height: height };
};
});
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
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;
};
}();
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 get$1 = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
var toConsumableArray = function (arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
} else {
return Array.from(arr);
}
};
var PIXEL_PATTERN = /margin|padding|width|height|max|min|offset/;
var removePixel = {
left: true,
top: true
};
var floatMap = {
cssFloat: 1,
styleFloat: 1,
float: 1
};
function getComputedStyle(node) {
return node.nodeType === 1 ? node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
}
function getStyleValue(node, type, value) {
type = type.toLowerCase();
if (value === 'auto') {
if (type === 'height') {
return node.offsetHeight;
}
if (type === 'width') {
return node.offsetWidth;
}
}
if (!(type in removePixel)) {
removePixel[type] = PIXEL_PATTERN.test(type);
}
return removePixel[type] ? parseFloat(value) || 0 : value;
}
function get(node, name) {
var length = arguments.length;
var style = getComputedStyle(node);
name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name;
return length === 1 ? style : getStyleValue(node, name, style[name] || node.style[name]);
}
function set(node, name, value) {
var length = arguments.length;
name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name;
if (length === 3) {
if (typeof value === 'number' && PIXEL_PATTERN.test(name)) {
value = value + 'px';
}
node.style[name] = value; // Number
return value;
}
if (is.PlainObject(name)) {
Object.entries(name).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
set(node, key, value);
});
}
// for ( const x in name ) {
// if ( name.hasOwnProperty( x )) {
// set( node, x, name[x]);
// }
// }
return getComputedStyle(node);
}
var hyphenateRE = /([a-z\d])([A-Z])/g;
function hyphenate(str) {
return str.replace(hyphenateRE, '$1-$2').toLowerCase();
}
// import camelCase from '../camelCase';
function firstCharToLowerCase(str) {
var firstChar = str.charAt(0);
if (/^[A-Z]$/.test(str.charAt(0))) {
return firstChar.toLowerCase() + str.slice(1);
}
return str;
}
var setData = function setData(dom, name) {
var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (dom.dataset) {
dom.dataset[firstCharToLowerCase(name)] = value;
} else {
dom.setAttribute('data-' + hyphenate(name), value);
}
};
var getData = function getData(dom, name) {
if (dom.dataset) {
return dom.dataset[firstCharToLowerCase(name)];
}
return dom.getAttribute('data-' + hyphenate(name));
};
var hasData = function hasData(dom, name) {
if (dom.dataset) {
return Object.keys(dom.dataset).includes(firstCharToLowerCase(name));
}
return dom.hasAttribute('data-' + hyphenate(name));
};
var removeData = function removeData(dom, name) {
if (dom.dataset) {
delete dom.dataset[firstCharToLowerCase(name)];
} else {
dom.removeAttribute('data-' + hyphenate(name));
}
};
var domData = { setData: setData, getData: getData, hasData: hasData, removeData: removeData };
// 实际情况下 body documentElement 不会都设置 overflow: scroll
// 推荐 html, body { height: 100% } body { overflow: auto; }
function compose$1(funcA, funcB) {
return function () {
return funcA(funcB.apply(undefined, arguments));
};
}
var scrollable = function scrollable(attr) {
return function (dom) {
return ['overlay', 'scroll', 'auto'].includes(get(dom, attr));
};
};
var hasScrollBar = (function (_ref) {
var body = _ref.body,
html = _ref.html,
target = _ref.target,
OVERSCROLLX = _ref.OVERSCROLLX,
OVERSCROLLY = _ref.OVERSCROLLY;
function hasScrollX(dom) {
return dom.scrollWidth > dom.clientWidth && (hasData(dom, OVERSCROLLX) || scrollable('overflow-x')(dom));
}
function hasScrollY(dom) {
return dom.scrollHeight > dom.clientHeight && (hasData(dom, OVERSCROLLY) || scrollable('overflow-y')(dom));
}
function scrollingElement() {
var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : target;
return dom === body ? html : dom;
}
function hasScroll(dom) {
return {
x: hasScrollX(dom),
y: hasScrollY(dom)
};
}
return {
hasScroll: compose$1(hasScroll, scrollingElement),
hasScrollX: compose$1(hasScrollX, scrollingElement),
hasScrollY: compose$1(hasScrollY, scrollingElement)
};
});
// 检测 parentNode 元素包含 childNode 元素
function contains(parentNode, childNode) {
// 标准浏览器支持compareDocumentPosition
if (parentNode.compareDocumentPosition) {
return !!(parentNode.compareDocumentPosition(childNode) & 16);
}
// IE支持contains
else if (parentNode.contains) {
return parentNode !== childNode && parentNode.contains(childNode);
}
return false;
}
function getDocument(dom) {
return is.Element(dom) ? dom.ownerDocument : document;
}
function getWindow(dom) {
var doc = is.Document(dom) ? dom : getDocument(dom);
return doc.defaultView || window;
}
function parentNode(dom, condition) {
var parent = dom.parentNode;
if (is.Function(condition)) {
return parent && condition(parent) === true ? parent : parent ? parentNode(parent, condition) : null;
}
return parent || getWindow(dom);
}
var domUtils = (function (scope) {
var target = scope.target,
NOBUBBLE = scope.NOBUBBLE;
var hasScrollBarFuncs = hasScrollBar(scope);
var hasScrollY = hasScrollBarFuncs.hasScrollY,
hasScrollX = hasScrollBarFuncs.hasScrollX;
function getNearestScrollable(dom) {
return target === dom || hasData(dom, NOBUBBLE) || hasScrollY(dom) || hasScrollX(dom) ? dom : parentNode(dom, function (dom) {
return target === dom || hasData(dom, NOBUBBLE) || hasScrollY(dom) || hasScrollX(dom);
});
}
return _extends({
domData: domData,
contains: contains,
getFromRange: getFromRange,
getWindow: getWindow,
getParent: parentNode,
getDocument: getDocument,
getNearestScrollable: getNearestScrollable,
getOffset: getOffset(scope),
getPosition: getPosition(scope),
getClientSize: getClientSize(scope),
getScrollSize: getScrollSize(scope)
}, hasScrollBarFuncs);
});
var domStates = (function (scope) {
var target = scope.target,
overscroll = scope.overscroll,
_scope$domData = scope.domData,
setData = _scope$domData.setData,
hasData = _scope$domData.hasData,
removeData = _scope$domData.removeData,
hasScrollY = scope.hasScrollY,
hasScrollX = scope.hasScrollX,
getNearestScrollable = scope.getNearestScrollable,
getParent = scope.getParent,
NOBUBBLE = scope.NOBUBBLE;
function handleState(states) {
return function (target) {
var state = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
if (!hasData(target, state)) {
states.filter(function (elemState) {
return state !== elemState && hasData(target, elemState);
}).forEach(function (state) {
return removeData(target, state);
});
if (state && states.includes(state)) {
setData(target, state);
}
}
};
}
// 滚动位置
var setPositionX = handleState(['scrollLeft', 'scrollRight', 'scrollX']);
var setPositionY = handleState(['scrollTop', 'scrollBottom', 'scrollY']);
// 动画状态
var setAnimationX = handleState(['scrollingX']);
var setAnimationY = handleState(['scrollingY']);
// 方向
var setDirectionX = handleState(['scrollingLeft', 'scrollingRight']);
var setDirectionY = handleState(['scrollingUp', 'scrollingDown']);
function animatingX() {
setAnimationX(target, 'scrollingX');
}
function animatingY() {
setAnimationY(target, 'scrollingY');
}
function scrollingUp() {
setDirectionY(target, 'scrollingUp');
}
function scrollingDown() {
setDirectionY(target, 'scrollingDown');
}
function scrollingLeft() {
setDirectionX(target, 'scrollingLeft');
}
function scrollingRight() {
setDirectionX(target, 'scrollingRight');
}
function scrollingStopX() {
setDirectionX(target);
setAnimationX(target);
}
function scrollingStopY() {
setDirectionY(target);
setAnimationY(target);
}
function isTop() {
var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll;
return dom.scrollTop < 1;
}
function isBottom() {
var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll;
return dom.scrollTop === dom.scrollHeight - dom.clientHeight;
}
function isLeft() {
var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll;
return dom.scrollLeft < 1;
}
function isRight() {
var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overscroll;
return dom.scrollLeft === dom.scrollWidth - dom.clientWidth;
}
function scrollableTo(dom) {
return {
top: hasScrollY(dom) && !isTop(dom),
left: hasScrollX(dom) && !isLeft(dom),
right: hasScrollX(dom) && !isRight(dom),
bottom: hasScrollY(dom) && !isBottom(dom)
};
}
function scrollable(dom) {
var childrenState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { top: false, left: false, right: false, bottom: false };
var scrollableDom = getNearestScrollable(dom);
var state = scrollableTo(scrollableDom);
if (target === scrollableDom) {
return {
top: state.top && !childrenState.top,
left: state.left && !childrenState.left,
right: state.right && !childrenState.right,
bottom: state.bottom && !childrenState.bottom
};
}
return scrollable(getParent(scrollableDom), {
top: hasData(scrollableDom, NOBUBBLE) || state.top || childrenState.top,
left: hasData(scrollableDom, NOBUBBLE) || state.left || childrenState.left,
right: hasData(scrollableDom, NOBUBBLE) || state.right || childrenState.right,
bottom: hasData(scrollableDom, NOBUBBLE) || state.bottom || childrenState.bottom
});
}
// 滚动状态设置完成后运行该函数
function resetState() {
var scrollTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var scrollLeft = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
// 位置判断 -----------------
// 纵向状态设置
if (isTop()) {
setPositionY(target, 'scrollTop');
} else if (isBottom()) {
setPositionY(target, 'scrollBottom');
} else {
setPositionY(target, 'scrollY');
}
// 横向状态设置
if (isLeft()) {
setPositionX(target, 'scrollLeft');
} else if (isRight()) {
setPositionX(target, 'scrollRight');
} else {
setPositionX(target, 'scrollX');
}
// 滚动状态判断 ----------------
if (scrollTop === 0 || overscroll.scrollTop === scrollTop) {
scrollingStopY();
} else if (overscroll.scrollTop > scrollTop) {
animatingY();
scrollingDown();
} else if (overscroll.scrollTop < scrollTop) {
animatingY();
scrollingUp();
}
if (scrollLeft === 0 || overscroll.scrollLeft === scrollLeft) {
scrollingStopX();
} else if (overscroll.scrollLeft > scrollLeft) {
animatingX();
scrollingRight();
} else if (overscroll.scrollLeft < scrollLeft) {
animatingX();
scrollingLeft();
}
}
return {
isTop: isTop,
isBottom: isBottom,
isLeft: isLeft,
isRight: isRight,
animatingX: animatingX,
animatingY: animatingY,
scrollingUp: scrollingUp,
scrollingDown: scrollingDown,
scrollingLeft: scrollingLeft,
scrollingRight: scrollingRight,
scrollingStopX: scrollingStopX,
scrollingStopY: scrollingStopY,
resetState: resetState,
scrollable: scrollable
};
});
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var hammer = createCommonjsModule(function (module) {
/*! Hammer.JS - v2.0.7 - 2016-04-22
* http://hammerjs.github.io/
*
* Copyright (c) 2016 Jorik Tangelder;
* Licensed under the MIT license */
(function(window, document, exportName, undefined) {
'use strict';
var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
var TEST_ELEMENT = document.createElement('div');
var TYPE_FUNCTION = 'function';
var round = Math.round;
var abs = Math.abs;
var now = Date.now;
/**
* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/
function setTimeoutContext(fn, timeout, context) {
return setTimeout(bindFn(fn, context), timeout);
}
/**
* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/
function invokeArrayArg(arg, fn, context) {
if (Array.isArray(arg)) {
each(arg, context[fn], context);
return true;
}
return false;
}
/**
* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/
function each(obj, iterator, context) {
var i;
if (!obj) {
return;
}
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj.length !== undefined) {
i = 0;
while (i < obj.length) {
iterator.call(context, obj[i], i, obj);
i++;
}
} else {
for (i in obj) {
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
}
}
}
/**
* wrap a method with a deprecation warning and stack trace
* @param {Function} method
* @param {String} name
* @param {String} message
* @returns {Function} A new function wrapping the supplied method.
*/
function deprecate(method, name, message) {
var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
return function() {
var e = new Error('get-stack-trace');
var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
var log = window.console && (window.console.warn || window.console.log);
if (log) {
log.call(window.console, deprecationMessage, stack);
}
return method.apply(this, arguments);
};
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} target
* @param {...Object} objects_to_assign
* @returns {Object} target
*/
var assign;
if (typeof Object.assign !== 'function') {
assign = function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
} else {
assign = Object.assign;
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge=false]
* @returns {Object} dest
*/
var extend = deprecate(function extend(dest, src, merge) {
var keys = Object.keys(src);
var i = 0;
while (i < keys.length) {
if (!merge || (merge && dest[keys[i]] === undefined)) {
dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}, 'extend', 'Use `assign`.');
/**
* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/
var merge = deprecate(function merge(dest, src) {
return extend(dest, src, true);
}, 'merge', 'Use `assign`.');
/**
* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/
function inherit(child, base, properties) {
var baseP = base.prototype,
childP;
childP = child.prototype = Object.create(baseP);
childP.constructor = child;
childP._super = baseP;
if (properties) {
assign(childP, properties);
}
}
/**
* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/
function bindFn(fn, context) {
return function boundFn() {
return fn.apply(context, arguments);
};
}
/**
* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/
function boolOrFn(val, args) {
if (typeof val == TYPE_FUNCTION) {
return val.apply(args ? args[0] || undefined : undefined, args);
}
return val;
}
/**
* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/
function ifUndefined(val1, val2) {
return (val1 === undefined) ? val2 : val1;
}
/**
* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function addEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
target.addEventListener(type, handler, false);
});
}
/**
* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function removeEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
target.removeEventListener(type, handler, false);
});
}
/**
* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/
function hasParent(node, parent) {
while (node) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
/**
* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/
function inStr(str, find) {
return str.indexOf(find) > -1;
}
/**
* split string on whitespace
* @param {String} str
* @returns {Array} words
*/
function splitStr(str) {
return str.trim().split(/\s+/g);
}
/**
* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {Boolean|Number} false when not found, or the index
*/
function inArray(src, find, findByKey) {
if (src.indexOf && !findByKey) {
return src.indexOf(find);
} else {
var i = 0;
while (i < src.length) {
if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
return i;
}
i++;
}
return -1;
}
}
/**
* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/
function toArray(obj) {
return Array.prototype.slice.call(obj, 0);
}
/**
* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/
function uniqueArray(src, key, sort) {
var results = [];
var values = [];
var i = 0;
while (i < src.length) {
var val = key ? src[i][key] : src[i];
if (inArray(values, val) < 0) {
results.push(src[i]);
}
values[i] = val;
i++;
}
if (sort) {
if (!key) {
results = results.sort();
} else {
results = results.sort(function sortUniqueArray(a, b) {
return a[key] > b[key];
});
}
}
return results;
}
/**
* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/
function prefixed(obj, property) {
var prefix, prop;
var camelProp = property[0].toUpperCase() + property.slice(1);
var i = 0;
while (i < VENDOR_PREFIXES.length) {
prefix = VENDOR_PREFIXES[i];
prop = (prefix) ? prefix + camelProp : property;
if (prop in obj) {
return prop;
}
i++;
}
return undefined;
}
/**
* get a unique id
* @returns {number} uniqueId
*/
var _uniqueId = 1;
function uniqueId() {
return _uniqueId++;
}
/**
* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/
function getWindowForElement(element) {
var doc = element.ownerDocument || element;
return (doc.defaultView || doc.parentWindow || window);
}
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
var SUPPORT_TOUCH = ('ontouchstart' in window);
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
var INPUT_TYPE_TOUCH = 'touch';
var INPUT_TYPE_PEN = 'pen';
var INPUT_TYPE_MOUSE = 'mouse';
var INPUT_TYPE_KINECT = 'kinect';
var COMPUTE_INTERVAL = 25;
var INPUT_START = 1;
var INPUT_MOVE = 2;
var INPUT_END = 4;
var INPUT_CANCEL = 8;
var DIRECTION_NONE = 1;
var DIRECTION_LEFT = 2;
var DIRECTION_RIGHT = 4;
var DIRECTION_UP = 8;
var DIRECTION_DOWN = 16;
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
var PROPS_XY = ['x', 'y'];
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
/**
* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/
function Input(manager, callback) {
var self = this;
this.manager = manager;
this.callback = callback;
this.element = manager.element;
this.target = manager.options.inputTarget;
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
// so when disabled the input events are completely bypassed.
this.domHandler = function(ev) {
if (boolOrFn(manager.options.enable, [manager])) {
self.handler(ev);
}
};
this.init();
}
Input.prototype = {
/**
* should handle the inputEvent data and trigger the callback
* @virtual
*/
handler: function() { },
/**
* bind the events
*/
init: function() {
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
},
/**
* unbind the events
*/
destroy: function() {
this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
};
/**
* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/
function createInputInstance(manager) {
var Type;
var inputClass = manager.options.inputClass;
if (inputClass) {
Type = inputClass;
} else if (SUPPORT_POINTER_EVENTS) {
Type = PointerEventInput;
} else if (SUPPORT_ONLY_TOUCH) {
Type = TouchInput;
} else if (!SUPPORT_TOUCH) {
Type = MouseInput;
} else {
Type = TouchMouseInput;
}
return new (Type)(manager, inputHandler);
}
/**
* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/
function inputHandler(manager, eventType, input) {
var pointersLen = input.pointers.length;
var changedPointersLen = input.changedPointers.length;
var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
input.isFirst = !!isFirst;
input.isFinal = !!isFinal;
if (isFirst) {
manager.session = {};
}
// source event is the normalized value of the domEvents
// like 'touchstart, mouseup, pointerdown'
input.eventType = eventType;
// compute scale, rotation etc
computeInputData(manager, input);
// emit secret event
manager.emit('hammer.input', input);
manager.recognize(input);
manager.session.prevInput = input;
}
/**
* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/
function computeInputData(manager, input) {
var session = manager.session;
var pointers = input.pointers;
var pointersLength = pointers.length;
// store the first input to calculate the distance and direction
if (!session.firstInput) {
session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches
if (pointersLength > 1 && !session.firstMultiple) {
session.firstMultiple = simpleCloneInputData(input);
} else if (pointersLength === 1) {
session.firstMultiple = false;
}
var firstInput = session.firstInput;
var firstMultiple = session.firstMultiple;
var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
var center = input.center = getCenter(pointers);
input.timeStamp = now();
input.deltaTime = input.timeStamp - firstInput.timeStamp;
input.angle = getAngle(offsetCenter, center);
input.distance = getDistance(offsetCenter, center);
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
input.overallVelocityX = overallVelocity.x;
input.overallVelocityY = overallVelocity.y;
input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
computeIntervalInputData(session, input);
// find the correct target
var target = manager.element;
if (hasParent(input.srcEvent.target, target)) {
target = input.srcEvent.target;
}
input.target = target;
}
function computeDeltaXY(session, input) {
var center = input.center;
var offset = session.offsetDelta || {};
var prevDelta = session.prevDelta || {};
var prevInput = session.prevInput || {};
if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
prevDelta = session.prevDelta = {
x: prevInput.deltaX || 0,
y: prevInput.deltaY || 0
};
offset = session.offsetDelta = {
x: center.x,
y: center.y
};
}
input.deltaX = prevDelta.x + (center.x - offset.x);
input.deltaY = prevDelta.y + (center.y - offset.y);
}
/**
* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/
function computeIntervalInputData(session, input) {
var last = session.lastInterval || input,
deltaTime = input.timeStamp - last.timeStamp,
velocity, velocityX, velocityY, direction;
if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
var deltaX = input.deltaX - last.deltaX;
var deltaY = input.deltaY - last.deltaY;
var v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
velocityY = v.y;
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
direction = getDirection(deltaX, deltaY);
session.lastInterval = input;
} else {
// use latest velocity info if it doesn't overtake a minimum period
velocity = last.velocity;
velocityX = last.velocityX;
velocityY = last.velocityY;
direction = last.direction;
}
input.velocity = velocity;
input.velocityX = velocityX;
input.velocityY = velocityY;
input.direction = direction;
}
/**
* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/
function simpleCloneInputData(input) {
// make a simple copy of the pointers because we will get a reference if we don't
// we only need clientXY for the calculations
var pointers = [];
var i = 0;
while (i < input.pointers.length) {
pointers[i] = {
clientX: round(input.pointers[i].clientX),
clientY: round(input.pointers[i].clientY)
};
i++;
}
return {
timeStamp: now(),
pointers: pointers,
center: getCenter(pointers),
deltaX: input.deltaX,
deltaY: input.deltaY
};
}
/**
* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/
function getCenter(pointers) {
var pointersLength = pointers.length;
// no need to loop when only one touch
if (pointersLength === 1) {
return {
x: round(pointers[0].clientX),
y: round(pointers[0].clientY)
};
}
var x = 0, y = 0, i = 0;
while (i < pointersLength) {
x += pointers[i].clientX;
y += pointers[i].clientY;
i++;
}
return {
x: round(x / pointersLength),
y: round(y / pointersLength)
};
}
/**
* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/
function getVelocity(deltaTime, x, y) {
return {
x: x / deltaTime || 0,
y: y / deltaTime || 0
};
}
/**
* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/
function getDirection(x, y) {
if (x === y) {
return DIRECTION_NONE;
}
if (abs(x) >= abs(y)) {
return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
/**
* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y key