first commit
이 커밋은 다음에 포함됨:
@@ -0,0 +1,407 @@
|
||||
(function($, window, document) {
|
||||
var STATE_BEFORECHANGEOFFSET = 'beforeChangeOffset';
|
||||
var STATE_AFTERCHANGEOFFSET = 'afterChangeOffset';
|
||||
|
||||
var EVENT_PULLSTART = 'pullstart';
|
||||
var EVENT_PULLING = 'pulling';
|
||||
var EVENT_BEFORECHANGEOFFSET = STATE_BEFORECHANGEOFFSET;
|
||||
var EVENT_AFTERCHANGEOFFSET = STATE_AFTERCHANGEOFFSET;
|
||||
var EVENT_DRAGENDAFTERCHANGEOFFSET = 'dragEndAfterChangeOffset';
|
||||
|
||||
var CLASS_TRANSITIONING = $.className('transitioning');
|
||||
var CLASS_PULL_TOP_TIPS = $.className('pull-top-tips');
|
||||
var CLASS_PULL_BOTTOM_TIPS = $.className('pull-bottom-tips');
|
||||
var CLASS_PULL_LOADING = $.className('pull-loading');
|
||||
var CLASS_SCROLL = $.className('scroll');
|
||||
|
||||
var CLASS_PULL_TOP_ARROW = $.className('pull-loading') + ' ' + $.className('icon') + ' ' + $.className('icon-pulldown');
|
||||
var CLASS_PULL_TOP_ARROW_REVERSE = CLASS_PULL_TOP_ARROW + ' ' + $.className('reverse');
|
||||
var CLASS_PULL_TOP_SPINNER = $.className('pull-loading') + ' ' + $.className('spinner');
|
||||
var CLASS_HIDDEN = $.className('hidden');
|
||||
|
||||
var SELECTOR_PULL_LOADING = '.' + CLASS_PULL_LOADING;
|
||||
$.PullToRefresh = $.Class.extend({
|
||||
init: function(element, options) {
|
||||
this.element = element;
|
||||
this.options = $.extend(true, {
|
||||
down: {
|
||||
height: 75,
|
||||
callback: false,
|
||||
},
|
||||
up: {
|
||||
auto: false,
|
||||
offset: 100, //距离底部高度(到达该高度即触发)
|
||||
show: true,
|
||||
contentinit: '上拉显示更多',
|
||||
contentdown: '上拉显示更多',
|
||||
contentrefresh: '正在加载...',
|
||||
contentnomore: '没有更多数据了',
|
||||
callback: false
|
||||
},
|
||||
preventDefaultException: {
|
||||
tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
|
||||
}
|
||||
}, options);
|
||||
this.stopped = this.isNeedRefresh = this.isDragging = false;
|
||||
this.state = STATE_BEFORECHANGEOFFSET;
|
||||
this.isInScroll = this.element.classList.contains(CLASS_SCROLL);
|
||||
this.initPullUpTips();
|
||||
|
||||
this.initEvent();
|
||||
},
|
||||
_preventDefaultException: function(el, exceptions) {
|
||||
for (var i in exceptions) {
|
||||
if (exceptions[i].test(el[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
initEvent: function() {
|
||||
if ($.isFunction(this.options.down.callback)) {
|
||||
this.element.addEventListener($.EVENT_START, this);
|
||||
this.element.addEventListener('drag', this);
|
||||
this.element.addEventListener('dragend', this);
|
||||
}
|
||||
if (this.pullUpTips) {
|
||||
this.element.addEventListener('dragup', this);
|
||||
if (this.isInScroll) {
|
||||
this.element.addEventListener('scrollbottom', this);
|
||||
} else {
|
||||
window.addEventListener('scroll', this);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleEvent: function(e) {
|
||||
switch (e.type) {
|
||||
case $.EVENT_START:
|
||||
this.isInScroll && this._canPullDown() && e.target && !this._preventDefaultException(e.target, this.options.preventDefaultException) && e.preventDefault();
|
||||
break;
|
||||
case 'drag':
|
||||
this._drag(e);
|
||||
break;
|
||||
case 'dragend':
|
||||
this._dragend(e);
|
||||
break;
|
||||
case 'webkitTransitionEnd':
|
||||
this._transitionEnd(e);
|
||||
break;
|
||||
case 'dragup':
|
||||
case 'scroll':
|
||||
this._dragup(e);
|
||||
break;
|
||||
case 'scrollbottom':
|
||||
if (e.target === this.element) {
|
||||
this.pullUpLoading(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
initPullDownTips: function() {
|
||||
var self = this;
|
||||
if ($.isFunction(self.options.down.callback)) {
|
||||
self.pullDownTips = (function() {
|
||||
var element = document.querySelector('.' + CLASS_PULL_TOP_TIPS);
|
||||
if (element) {
|
||||
element.parentNode.removeChild(element);
|
||||
}
|
||||
if (!element) {
|
||||
element = document.createElement('div');
|
||||
element.classList.add(CLASS_PULL_TOP_TIPS);
|
||||
element.innerHTML = '<div class="mui-pull-top-wrapper"><span class="mui-pull-loading mui-icon mui-icon-pulldown"></span></div>';
|
||||
element.addEventListener('webkitTransitionEnd', self);
|
||||
}
|
||||
self.pullDownTipsIcon = element.querySelector(SELECTOR_PULL_LOADING);
|
||||
document.body.appendChild(element);
|
||||
return element;
|
||||
}());
|
||||
}
|
||||
},
|
||||
initPullUpTips: function() {
|
||||
var self = this;
|
||||
if ($.isFunction(self.options.up.callback)) {
|
||||
self.pullUpTips = (function() {
|
||||
var element = self.element.querySelector('.' + CLASS_PULL_BOTTOM_TIPS);
|
||||
if (!element) {
|
||||
element = document.createElement('div');
|
||||
element.classList.add(CLASS_PULL_BOTTOM_TIPS);
|
||||
if (!self.options.up.show) {
|
||||
element.classList.add(CLASS_HIDDEN);
|
||||
}
|
||||
element.innerHTML = '<div class="mui-pull-bottom-wrapper"><span class="mui-pull-loading">' + self.options.up.contentinit + '</span></div>';
|
||||
self.element.appendChild(element);
|
||||
}
|
||||
self.pullUpTipsIcon = element.querySelector(SELECTOR_PULL_LOADING);
|
||||
return element;
|
||||
}());
|
||||
}
|
||||
},
|
||||
_transitionEnd: function(e) {
|
||||
if (e.target === this.pullDownTips && this.removing) {
|
||||
this.removePullDownTips();
|
||||
}
|
||||
},
|
||||
_dragup: function(e) {
|
||||
var self = this;
|
||||
if (self.loading) {
|
||||
return;
|
||||
}
|
||||
if (e && e.detail && $.gestures.session.drag) {
|
||||
self.isDraggingUp = true;
|
||||
} else {
|
||||
if (!self.isDraggingUp) { //scroll event
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!self.isDragging) {
|
||||
if (self._canPullUp()) {
|
||||
self.pullUpLoading(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
_canPullUp: function() {
|
||||
if (this.removing) {
|
||||
return false;
|
||||
}
|
||||
if (this.isInScroll) {
|
||||
var scrollId = this.element.parentNode.getAttribute('data-scroll');
|
||||
if (scrollId) {
|
||||
var scrollApi = $.data[scrollId];
|
||||
return scrollApi.y === scrollApi.maxScrollY;
|
||||
}
|
||||
}
|
||||
return window.pageYOffset + window.innerHeight + this.options.up.offset >= document.documentElement.scrollHeight;
|
||||
},
|
||||
_canPullDown: function() {
|
||||
if (this.removing) {
|
||||
return false;
|
||||
}
|
||||
if (this.isInScroll) {
|
||||
var scrollId = this.element.parentNode.getAttribute('data-scroll');
|
||||
if (scrollId) {
|
||||
var scrollApi = $.data[scrollId];
|
||||
return scrollApi.y === 0;
|
||||
}
|
||||
}
|
||||
return document.body.scrollTop === 0;
|
||||
},
|
||||
_drag: function(e) {
|
||||
if (this.loading || this.stopped) {
|
||||
e.stopPropagation();
|
||||
e.detail.gesture.preventDefault();
|
||||
return;
|
||||
}
|
||||
var detail = e.detail;
|
||||
if (!this.isDragging) {
|
||||
if (detail.direction === 'down' && this._canPullDown()) {
|
||||
if (document.querySelector('.' + CLASS_PULL_TOP_TIPS)) {
|
||||
e.stopPropagation();
|
||||
e.detail.gesture.preventDefault();
|
||||
return;
|
||||
}
|
||||
this.isDragging = true;
|
||||
this.removing = false;
|
||||
this.startDeltaY = detail.deltaY;
|
||||
$.gestures.session.lockDirection = true; //锁定方向
|
||||
$.gestures.session.startDirection = detail.direction;
|
||||
this._pullStart(this.startDeltaY);
|
||||
}
|
||||
}
|
||||
if (this.isDragging) {
|
||||
e.stopPropagation();
|
||||
e.detail.gesture.preventDefault();
|
||||
var deltaY = detail.deltaY - this.startDeltaY;
|
||||
deltaY = Math.min(deltaY, 1.5 * this.options.down.height);
|
||||
this.deltaY = deltaY;
|
||||
this._pulling(deltaY);
|
||||
var state = deltaY > this.options.down.height ? STATE_AFTERCHANGEOFFSET : STATE_BEFORECHANGEOFFSET;
|
||||
if (this.state !== state) {
|
||||
this.state = state;
|
||||
if (this.state === STATE_AFTERCHANGEOFFSET) {
|
||||
this.removing = false;
|
||||
this.isNeedRefresh = true;
|
||||
} else {
|
||||
this.removing = true;
|
||||
this.isNeedRefresh = false;
|
||||
}
|
||||
this['_' + state](deltaY);
|
||||
}
|
||||
if ($.os.ios && parseFloat($.os.version) >= 8) {
|
||||
var clientY = detail.gesture.touches[0].clientY;
|
||||
if ((clientY + 10) > window.innerHeight || clientY < 10) {
|
||||
this._dragend(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_dragend: function(e) {
|
||||
var self = this;
|
||||
if (self.isDragging) {
|
||||
self.isDragging = false;
|
||||
self._dragEndAfterChangeOffset(self.isNeedRefresh);
|
||||
}
|
||||
if (self.isPullingUp) {
|
||||
if (self.pullingUpTimeout) {
|
||||
clearTimeout(self.pullingUpTimeout);
|
||||
}
|
||||
self.pullingUpTimeout = setTimeout(function() {
|
||||
self.isPullingUp = false;
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
_pullStart: function(startDeltaY) {
|
||||
this.pullStart(startDeltaY);
|
||||
$.trigger(this.element, EVENT_PULLSTART, {
|
||||
api: this,
|
||||
startDeltaY: startDeltaY
|
||||
});
|
||||
},
|
||||
_pulling: function(deltaY) {
|
||||
this.pulling(deltaY);
|
||||
$.trigger(this.element, EVENT_PULLING, {
|
||||
api: this,
|
||||
deltaY: deltaY
|
||||
});
|
||||
},
|
||||
_beforeChangeOffset: function(deltaY) {
|
||||
this.beforeChangeOffset(deltaY);
|
||||
$.trigger(this.element, EVENT_BEFORECHANGEOFFSET, {
|
||||
api: this,
|
||||
deltaY: deltaY
|
||||
});
|
||||
},
|
||||
_afterChangeOffset: function(deltaY) {
|
||||
this.afterChangeOffset(deltaY);
|
||||
$.trigger(this.element, EVENT_AFTERCHANGEOFFSET, {
|
||||
api: this,
|
||||
deltaY: deltaY
|
||||
});
|
||||
},
|
||||
_dragEndAfterChangeOffset: function(isNeedRefresh) {
|
||||
this.dragEndAfterChangeOffset(isNeedRefresh);
|
||||
$.trigger(this.element, EVENT_DRAGENDAFTERCHANGEOFFSET, {
|
||||
api: this,
|
||||
isNeedRefresh: isNeedRefresh
|
||||
});
|
||||
},
|
||||
removePullDownTips: function() {
|
||||
if (this.pullDownTips) {
|
||||
try {
|
||||
this.pullDownTips.parentNode && this.pullDownTips.parentNode.removeChild(this.pullDownTips);
|
||||
this.pullDownTips = null;
|
||||
this.removing = false;
|
||||
} catch (e) {}
|
||||
}
|
||||
},
|
||||
pullStart: function(startDeltaY) {
|
||||
this.initPullDownTips(startDeltaY);
|
||||
},
|
||||
pulling: function(deltaY) {
|
||||
this.pullDownTips.style.webkitTransform = 'translate3d(0,' + deltaY + 'px,0)';
|
||||
},
|
||||
beforeChangeOffset: function(deltaY) {
|
||||
this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW;
|
||||
},
|
||||
afterChangeOffset: function(deltaY) {
|
||||
this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW_REVERSE;
|
||||
},
|
||||
dragEndAfterChangeOffset: function(isNeedRefresh) {
|
||||
if (isNeedRefresh) {
|
||||
this.pullDownTipsIcon.className = CLASS_PULL_TOP_SPINNER;
|
||||
this.pullDownLoading();
|
||||
} else {
|
||||
this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW;
|
||||
this.endPullDownToRefresh();
|
||||
}
|
||||
},
|
||||
pullDownLoading: function() {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
if (!this.pullDownTips) {
|
||||
this.initPullDownTips();
|
||||
this.dragEndAfterChangeOffset(true);
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.pullDownTips.classList.add(CLASS_TRANSITIONING);
|
||||
this.pullDownTips.style.webkitTransform = 'translate3d(0,' + this.options.down.height + 'px,0)';
|
||||
this.options.down.callback.apply(this);
|
||||
},
|
||||
pullUpLoading: function(e) {
|
||||
if (this.loading || this.finished) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.isDraggingUp = false;
|
||||
this.pullUpTips.classList.remove(CLASS_HIDDEN);
|
||||
e && e.detail && e.detail.gesture && e.detail.gesture.preventDefault();
|
||||
this.pullUpTipsIcon.innerHTML = this.options.up.contentrefresh;
|
||||
this.options.up.callback.apply(this);
|
||||
},
|
||||
endPullDownToRefresh: function() {
|
||||
this.loading = false;
|
||||
this.pullUpTips && this.pullUpTips.classList.remove(CLASS_HIDDEN);
|
||||
this.pullDownTips.classList.add(CLASS_TRANSITIONING);
|
||||
this.pullDownTips.style.webkitTransform = 'translate3d(0,0,0)';
|
||||
if (this.deltaY <= 0) {
|
||||
this.removePullDownTips();
|
||||
} else {
|
||||
this.removing = true;
|
||||
}
|
||||
if (this.isInScroll) {
|
||||
$(this.element.parentNode).scroll().refresh();
|
||||
}
|
||||
},
|
||||
endPullUpToRefresh: function(finished) {
|
||||
if (finished) {
|
||||
this.finished = true;
|
||||
this.pullUpTipsIcon.innerHTML = this.options.up.contentnomore;
|
||||
this.element.removeEventListener('dragup', this);
|
||||
window.removeEventListener('scroll', this);
|
||||
} else {
|
||||
this.pullUpTipsIcon.innerHTML = this.options.up.contentdown;
|
||||
}
|
||||
this.loading = false;
|
||||
if (this.isInScroll) {
|
||||
$(this.element.parentNode).scroll().refresh();
|
||||
}
|
||||
},
|
||||
setStopped: function(stopped) {
|
||||
if (stopped != this.stopped) {
|
||||
this.stopped = stopped;
|
||||
this.pullUpTips && this.pullUpTips.classList[stopped ? 'add' : 'remove'](CLASS_HIDDEN);
|
||||
}
|
||||
},
|
||||
refresh: function(isReset) {
|
||||
if (isReset && this.finished && this.pullUpTipsIcon) {
|
||||
this.pullUpTipsIcon.innerHTML = this.options.up.contentdown;
|
||||
this.element.addEventListener('dragup', this);
|
||||
window.addEventListener('scroll', this);
|
||||
this.finished = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
$.fn.pullToRefresh = function(options) {
|
||||
var pullRefreshApis = [];
|
||||
options = options || {};
|
||||
this.each(function() {
|
||||
var self = this;
|
||||
var pullRefreshApi = null;
|
||||
var id = self.getAttribute('data-pullToRefresh');
|
||||
if (!id) {
|
||||
id = ++$.uuid;
|
||||
$.data[id] = pullRefreshApi = new $.PullToRefresh(self, options);
|
||||
self.setAttribute('data-pullToRefresh', id);
|
||||
} else {
|
||||
pullRefreshApi = $.data[id];
|
||||
}
|
||||
if (options.up && options.up.auto) { //如果设置了auto,则自动上拉一次
|
||||
pullRefreshApi.pullUpLoading();
|
||||
}
|
||||
pullRefreshApis.push(pullRefreshApi);
|
||||
});
|
||||
return pullRefreshApis.length === 1 ? pullRefreshApis[0] : pullRefreshApis;
|
||||
}
|
||||
})(mui, window, document);
|
||||
새 이슈에서 참조
사용자 차단