mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-03-17 14:30:28 +00:00
802 lines
19 KiB
JavaScript
802 lines
19 KiB
JavaScript
/*
|
|
* jQuery Mobile Framework : scrollview plugin
|
|
* Copyright (c) 2010 Adobe Systems Incorporated - Kin Blas (jblas@adobe.com)
|
|
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
|
* Note: Code is in draft form and is subject to change
|
|
*/
|
|
(function($,window,document,undefined){
|
|
|
|
jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, {
|
|
options: {
|
|
fps: 60, // Frames per second in msecs.
|
|
direction: null, // "x", "y", or null for both.
|
|
|
|
scrollDuration: 2000, // Duration of the scrolling animation in msecs.
|
|
overshootDuration: 250, // Duration of the overshoot animation in msecs.
|
|
snapbackDuration: 500, // Duration of the snapback animation in msecs.
|
|
|
|
moveThreshold: 10, // User must move this many pixels in any direction to trigger a scroll.
|
|
moveIntervalThreshold: 150, // Time between mousemoves must not exceed this threshold.
|
|
|
|
scrollMethod: "translate", // "translate", "position", "scroll"
|
|
|
|
startEventName: "scrollstart",
|
|
updateEventName: "scrollupdate",
|
|
stopEventName: "scrollstop",
|
|
|
|
eventType: $.support.touch ? "touch" : "mouse",
|
|
|
|
showScrollBars: true,
|
|
|
|
pagingEnabled: false,
|
|
delayedClickSelector: "a,input,textarea,select,button,.ui-btn",
|
|
delayedClickEnabled: false
|
|
},
|
|
|
|
_makePositioned: function($ele)
|
|
{
|
|
if ($ele.css("position") == "static")
|
|
$ele.css("position", "relative");
|
|
},
|
|
|
|
_create: function()
|
|
{
|
|
this._$clip = $(this.element).addClass("ui-scrollview-clip");
|
|
var $child = this._$clip.children();
|
|
if ($child.length > 1) {
|
|
$child = this._$clip.wrapInner("<div></div>").children();
|
|
}
|
|
this._$view = $child.addClass("ui-scrollview-view");
|
|
|
|
this._$clip.css("overflow", this.options.scrollMethod === "scroll" ? "scroll" : "hidden");
|
|
this._makePositioned(this._$clip);
|
|
|
|
this._$view.css("overflow", "hidden");
|
|
|
|
// Turn off our faux scrollbars if we are using native scrolling
|
|
// to position the view.
|
|
|
|
this.options.showScrollBars = this.options.scrollMethod === "scroll" ? false : this.options.showScrollBars;
|
|
|
|
// We really don't need this if we are using a translate transformation
|
|
// for scrolling. We set it just in case the user wants to switch methods
|
|
// on the fly.
|
|
|
|
this._makePositioned(this._$view);
|
|
this._$view.css({ left: 0, top: 0 });
|
|
|
|
this._sx = 0;
|
|
this._sy = 0;
|
|
|
|
var direction = this.options.direction;
|
|
this._hTracker = (direction !== "y") ? new MomentumTracker(this.options) : null;
|
|
this._vTracker = (direction !== "x") ? new MomentumTracker(this.options) : null;
|
|
|
|
this._timerInterval = 1000/this.options.fps;
|
|
this._timerID = 0;
|
|
|
|
var self = this;
|
|
this._timerCB = function(){ self._handleMomentumScroll(); };
|
|
|
|
this._addBehaviors();
|
|
},
|
|
|
|
_startMScroll: function(speedX, speedY)
|
|
{
|
|
this._stopMScroll();
|
|
this._showScrollBars();
|
|
|
|
var keepGoing = false;
|
|
var duration = this.options.scrollDuration;
|
|
|
|
this._$clip.trigger(this.options.startEventName);
|
|
|
|
var ht = this._hTracker;
|
|
if (ht)
|
|
{
|
|
var c = this._$clip.width();
|
|
var v = this._$view.width();
|
|
ht.start(this._sx, speedX, duration, (v > c) ? -(v - c) : 0, 0);
|
|
keepGoing = !ht.done();
|
|
}
|
|
|
|
var vt = this._vTracker;
|
|
if (vt)
|
|
{
|
|
var c = this._$clip.height();
|
|
var v = this._$view.height();
|
|
vt.start(this._sy, speedY, duration, (v > c) ? -(v - c) : 0, 0);
|
|
keepGoing = keepGoing || !vt.done();
|
|
}
|
|
|
|
if (keepGoing)
|
|
this._timerID = setTimeout(this._timerCB, this._timerInterval);
|
|
else
|
|
this._stopMScroll();
|
|
},
|
|
|
|
_stopMScroll: function()
|
|
{
|
|
if (this._timerID)
|
|
{
|
|
this._$clip.trigger(this.options.stopEventName);
|
|
clearTimeout(this._timerID);
|
|
}
|
|
this._timerID = 0;
|
|
|
|
if (this._vTracker)
|
|
this._vTracker.reset();
|
|
|
|
if (this._hTracker)
|
|
this._hTracker.reset();
|
|
|
|
this._hideScrollBars();
|
|
},
|
|
|
|
_handleMomentumScroll: function()
|
|
{
|
|
var keepGoing = false;
|
|
var v = this._$view;
|
|
|
|
var x = 0, y = 0;
|
|
|
|
var vt = this._vTracker;
|
|
if (vt)
|
|
{
|
|
vt.update();
|
|
y = vt.getPosition();
|
|
keepGoing = !vt.done();
|
|
}
|
|
|
|
var ht = this._hTracker;
|
|
if (ht)
|
|
{
|
|
ht.update();
|
|
x = ht.getPosition();
|
|
keepGoing = keepGoing || !ht.done();
|
|
}
|
|
|
|
this._setScrollPosition(x, y);
|
|
this._$clip.trigger(this.options.updateEventName, [ { x: x, y: y } ]);
|
|
|
|
if (keepGoing)
|
|
this._timerID = setTimeout(this._timerCB, this._timerInterval);
|
|
else
|
|
this._stopMScroll();
|
|
},
|
|
|
|
_setScrollPosition: function(x, y)
|
|
{
|
|
this._sx = x;
|
|
this._sy = y;
|
|
|
|
var $v = this._$view;
|
|
|
|
var sm = this.options.scrollMethod;
|
|
|
|
switch (sm)
|
|
{
|
|
case "translate":
|
|
setElementTransform($v, x + "px", y + "px");
|
|
break;
|
|
case "position":
|
|
$v.css({left: x + "px", top: y + "px"});
|
|
break;
|
|
case "scroll":
|
|
var c = this._$clip[0];
|
|
c.scrollLeft = -x;
|
|
c.scrollTop = -y;
|
|
break;
|
|
}
|
|
|
|
var $vsb = this._$vScrollBar;
|
|
var $hsb = this._$hScrollBar;
|
|
|
|
if ($vsb)
|
|
{
|
|
var $sbt = $vsb.find(".ui-scrollbar-thumb");
|
|
if (sm === "translate")
|
|
setElementTransform($sbt, "0px", -y/$v.height() * $sbt.parent().height() + "px");
|
|
else
|
|
$sbt.css("top", -y/$v.height()*100 + "%");
|
|
}
|
|
|
|
if ($hsb)
|
|
{
|
|
var $sbt = $hsb.find(".ui-scrollbar-thumb");
|
|
if (sm === "translate")
|
|
setElementTransform($sbt, -x/$v.width() * $sbt.parent().width() + "px", "0px");
|
|
else
|
|
$sbt.css("left", -x/$v.width()*100 + "%");
|
|
}
|
|
},
|
|
|
|
scrollTo: function(x, y, duration)
|
|
{
|
|
this._stopMScroll();
|
|
if (!duration)
|
|
return this._setScrollPosition(x, y);
|
|
|
|
x = -x;
|
|
y = -y;
|
|
|
|
var self = this;
|
|
var start = getCurrentTime();
|
|
var efunc = $.easing["easeOutQuad"];
|
|
var sx = this._sx;
|
|
var sy = this._sy;
|
|
var dx = x - sx;
|
|
var dy = y - sy;
|
|
var tfunc = function(){
|
|
var elapsed = getCurrentTime() - start;
|
|
if (elapsed >= duration)
|
|
{
|
|
self._timerID = 0;
|
|
self._setScrollPosition(x, y);
|
|
}
|
|
else
|
|
{
|
|
var ec = efunc(elapsed/duration, elapsed, 0, 1, duration);
|
|
self._setScrollPosition(sx + (dx * ec), sy + (dy * ec));
|
|
self._timerID = setTimeout(tfunc, self._timerInterval);
|
|
}
|
|
};
|
|
|
|
this._timerID = setTimeout(tfunc, this._timerInterval);
|
|
},
|
|
|
|
getScrollPosition: function()
|
|
{
|
|
return { x: -this._sx, y: -this._sy };
|
|
},
|
|
|
|
_getScrollHierarchy: function()
|
|
{
|
|
var svh = [];
|
|
this._$clip.parents(".ui-scrollview-clip").each(function(){
|
|
var d = $(this).jqmData("scrollview");
|
|
if (d) svh.unshift(d);
|
|
});
|
|
return svh;
|
|
},
|
|
|
|
_getAncestorByDirection: function(dir)
|
|
{
|
|
var svh = this._getScrollHierarchy();
|
|
var n = svh.length;
|
|
while (0 < n--)
|
|
{
|
|
var sv = svh[n];
|
|
var svdir = sv.options.direction;
|
|
|
|
if (!svdir || svdir == dir)
|
|
return sv;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
_handleDragStart: function(e, ex, ey)
|
|
{
|
|
// Stop any scrolling of elements in our parent hierarcy.
|
|
$.each(this._getScrollHierarchy(),function(i,sv){ sv._stopMScroll(); });
|
|
this._stopMScroll();
|
|
|
|
var c = this._$clip;
|
|
var v = this._$view;
|
|
|
|
if (this.options.delayedClickEnabled) {
|
|
this._$clickEle = $(e.target).closest(this.options.delayedClickSelector);
|
|
}
|
|
this._lastX = ex;
|
|
this._lastY = ey;
|
|
this._doSnapBackX = false;
|
|
this._doSnapBackY = false;
|
|
this._speedX = 0;
|
|
this._speedY = 0;
|
|
this._directionLock = "";
|
|
this._didDrag = false;
|
|
|
|
if (this._hTracker)
|
|
{
|
|
var cw = parseInt(c.css("width"), 10);
|
|
var vw = parseInt(v.css("width"), 10);
|
|
this._maxX = cw - vw;
|
|
if (this._maxX > 0) this._maxX = 0;
|
|
if (this._$hScrollBar)
|
|
this._$hScrollBar.find(".ui-scrollbar-thumb").css("width", (cw >= vw ? "100%" : Math.floor(cw/vw*100)+ "%"));
|
|
}
|
|
|
|
if (this._vTracker)
|
|
{
|
|
var ch = parseInt(c.css("height"), 10);
|
|
var vh = parseInt(v.css("height"), 10);
|
|
this._maxY = ch - vh;
|
|
if (this._maxY > 0) this._maxY = 0;
|
|
if (this._$vScrollBar)
|
|
this._$vScrollBar.find(".ui-scrollbar-thumb").css("height", (ch >= vh ? "100%" : Math.floor(ch/vh*100)+ "%"));
|
|
}
|
|
|
|
var svdir = this.options.direction;
|
|
|
|
this._pageDelta = 0;
|
|
this._pageSize = 0;
|
|
this._pagePos = 0;
|
|
|
|
if (this.options.pagingEnabled && (svdir === "x" || svdir === "y"))
|
|
{
|
|
this._pageSize = svdir === "x" ? cw : ch;
|
|
this._pagePos = svdir === "x" ? this._sx : this._sy;
|
|
this._pagePos -= this._pagePos % this._pageSize;
|
|
}
|
|
this._lastMove = 0;
|
|
this._enableTracking();
|
|
|
|
// If we're using mouse events, we need to prevent the default
|
|
// behavior to suppress accidental selection of text, etc. We
|
|
// can't do this on touch devices because it will disable the
|
|
// generation of "click" events.
|
|
//
|
|
// XXX: We should test if this has an effect on links! - kin
|
|
|
|
if (this.options.eventType == "mouse" || this.options.delayedClickEnabled)
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
},
|
|
|
|
_propagateDragMove: function(sv, e, ex, ey, dir)
|
|
{
|
|
this._hideScrollBars();
|
|
this._disableTracking();
|
|
sv._handleDragStart(e,ex,ey);
|
|
sv._directionLock = dir;
|
|
sv._didDrag = this._didDrag;
|
|
},
|
|
|
|
_handleDragMove: function(e, ex, ey)
|
|
{
|
|
this._lastMove = getCurrentTime();
|
|
|
|
var v = this._$view;
|
|
|
|
var dx = ex - this._lastX;
|
|
var dy = ey - this._lastY;
|
|
var svdir = this.options.direction;
|
|
|
|
if (!this._directionLock)
|
|
{
|
|
var x = Math.abs(dx);
|
|
var y = Math.abs(dy);
|
|
var mt = this.options.moveThreshold;
|
|
|
|
if (x < mt && y < mt) {
|
|
return false;
|
|
}
|
|
|
|
var dir = null;
|
|
var r = 0;
|
|
if (x < y && (x/y) < 0.5) {
|
|
dir = "y";
|
|
}
|
|
else if (x > y && (y/x) < 0.5) {
|
|
dir = "x";
|
|
}
|
|
|
|
if (svdir && dir && svdir != dir)
|
|
{
|
|
// This scrollview can't handle the direction the user
|
|
// is attempting to scroll. Find an ancestor scrollview
|
|
// that can handle the request.
|
|
|
|
var sv = this._getAncestorByDirection(dir);
|
|
if (sv)
|
|
{
|
|
this._propagateDragMove(sv, e, ex, ey, dir);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this._directionLock = svdir ? svdir : (dir ? dir : "none");
|
|
}
|
|
|
|
var newX = this._sx;
|
|
var newY = this._sy;
|
|
|
|
if (this._directionLock !== "y" && this._hTracker)
|
|
{
|
|
var x = this._sx;
|
|
this._speedX = dx;
|
|
newX = x + dx;
|
|
|
|
// Simulate resistance.
|
|
|
|
this._doSnapBackX = false;
|
|
if (newX > 0 || newX < this._maxX)
|
|
{
|
|
if (this._directionLock === "x")
|
|
{
|
|
var sv = this._getAncestorByDirection("x");
|
|
if (sv)
|
|
{
|
|
this._setScrollPosition(newX > 0 ? 0 : this._maxX, newY);
|
|
this._propagateDragMove(sv, e, ex, ey, dir);
|
|
return false;
|
|
}
|
|
}
|
|
newX = x + (dx/2);
|
|
this._doSnapBackX = true;
|
|
}
|
|
}
|
|
|
|
if (this._directionLock !== "x" && this._vTracker)
|
|
{
|
|
var y = this._sy;
|
|
this._speedY = dy;
|
|
newY = y + dy;
|
|
|
|
// Simulate resistance.
|
|
|
|
this._doSnapBackY = false;
|
|
if (newY > 0 || newY < this._maxY)
|
|
{
|
|
if (this._directionLock === "y")
|
|
{
|
|
var sv = this._getAncestorByDirection("y");
|
|
if (sv)
|
|
{
|
|
this._setScrollPosition(newX, newY > 0 ? 0 : this._maxY);
|
|
this._propagateDragMove(sv, e, ex, ey, dir);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
newY = y + (dy/2);
|
|
this._doSnapBackY = true;
|
|
}
|
|
|
|
}
|
|
|
|
if (this.options.pagingEnabled && (svdir === "x" || svdir === "y"))
|
|
{
|
|
if (this._doSnapBackX || this._doSnapBackY)
|
|
this._pageDelta = 0;
|
|
else
|
|
{
|
|
var opos = this._pagePos;
|
|
var cpos = svdir === "x" ? newX : newY;
|
|
var delta = svdir === "x" ? dx : dy;
|
|
|
|
this._pageDelta = (opos > cpos && delta < 0) ? this._pageSize : ((opos < cpos && delta > 0) ? -this._pageSize : 0);
|
|
}
|
|
}
|
|
|
|
this._didDrag = true;
|
|
this._lastX = ex;
|
|
this._lastY = ey;
|
|
|
|
this._setScrollPosition(newX, newY);
|
|
|
|
this._showScrollBars();
|
|
|
|
// Call preventDefault() to prevent touch devices from
|
|
// scrolling the main window.
|
|
|
|
// e.preventDefault();
|
|
|
|
return false;
|
|
},
|
|
|
|
_handleDragStop: function(e)
|
|
{
|
|
var l = this._lastMove;
|
|
var t = getCurrentTime();
|
|
var doScroll = l && (t - l) <= this.options.moveIntervalThreshold;
|
|
|
|
var sx = (this._hTracker && this._speedX && doScroll) ? this._speedX : (this._doSnapBackX ? 1 : 0);
|
|
var sy = (this._vTracker && this._speedY && doScroll) ? this._speedY : (this._doSnapBackY ? 1 : 0);
|
|
|
|
var svdir = this.options.direction;
|
|
if (this.options.pagingEnabled && (svdir === "x" || svdir === "y") && !this._doSnapBackX && !this._doSnapBackY)
|
|
{
|
|
var x = this._sx;
|
|
var y = this._sy;
|
|
if (svdir === "x")
|
|
x = -this._pagePos + this._pageDelta;
|
|
else
|
|
y = -this._pagePos + this._pageDelta;
|
|
|
|
this.scrollTo(x, y, this.options.snapbackDuration);
|
|
}
|
|
else if (sx || sy)
|
|
this._startMScroll(sx, sy);
|
|
else
|
|
this._hideScrollBars();
|
|
|
|
this._disableTracking();
|
|
|
|
if (!this._didDrag && this.options.delayedClickEnabled && this._$clickEle.length) {
|
|
this._$clickEle
|
|
.trigger("mousedown")
|
|
//.trigger("focus")
|
|
.trigger("mouseup")
|
|
.trigger("click");
|
|
}
|
|
|
|
// If a view scrolled, then we need to absorb
|
|
// the event so that links etc, underneath our
|
|
// cursor/finger don't fire.
|
|
|
|
return this._didDrag ? false : undefined;
|
|
},
|
|
|
|
_enableTracking: function()
|
|
{
|
|
$(document).bind(this._dragMoveEvt, this._dragMoveCB);
|
|
$(document).bind(this._dragStopEvt, this._dragStopCB);
|
|
},
|
|
|
|
_disableTracking: function()
|
|
{
|
|
$(document).unbind(this._dragMoveEvt, this._dragMoveCB);
|
|
$(document).unbind(this._dragStopEvt, this._dragStopCB);
|
|
},
|
|
|
|
_showScrollBars: function()
|
|
{
|
|
var vclass = "ui-scrollbar-visible";
|
|
if (this._$vScrollBar) this._$vScrollBar.addClass(vclass);
|
|
if (this._$hScrollBar) this._$hScrollBar.addClass(vclass);
|
|
},
|
|
|
|
_hideScrollBars: function()
|
|
{
|
|
var vclass = "ui-scrollbar-visible";
|
|
if (this._$vScrollBar) this._$vScrollBar.removeClass(vclass);
|
|
if (this._$hScrollBar) this._$hScrollBar.removeClass(vclass);
|
|
},
|
|
|
|
_addBehaviors: function()
|
|
{
|
|
var self = this;
|
|
if (this.options.eventType === "mouse")
|
|
{
|
|
this._dragStartEvt = "mousedown";
|
|
this._dragStartCB = function(e){ return self._handleDragStart(e, e.clientX, e.clientY); };
|
|
|
|
this._dragMoveEvt = "mousemove";
|
|
this._dragMoveCB = function(e){ return self._handleDragMove(e, e.clientX, e.clientY); };
|
|
|
|
this._dragStopEvt = "mouseup";
|
|
this._dragStopCB = function(e){ return self._handleDragStop(e); };
|
|
}
|
|
else // "touch"
|
|
{
|
|
this._dragStartEvt = "touchstart";
|
|
this._dragStartCB = function(e)
|
|
{
|
|
var t = e.originalEvent.targetTouches[0];
|
|
return self._handleDragStart(e, t.pageX, t.pageY);
|
|
};
|
|
|
|
this._dragMoveEvt = "touchmove";
|
|
this._dragMoveCB = function(e)
|
|
{
|
|
var t = e.originalEvent.targetTouches[0];
|
|
return self._handleDragMove(e, t.pageX, t.pageY);
|
|
};
|
|
|
|
this._dragStopEvt = "touchend";
|
|
this._dragStopCB = function(e){ return self._handleDragStop(e); };
|
|
}
|
|
|
|
this._$view.bind(this._dragStartEvt, this._dragStartCB);
|
|
|
|
if (this.options.showScrollBars)
|
|
{
|
|
var $c = this._$clip;
|
|
var prefix = "<div class=\"ui-scrollbar ui-scrollbar-";
|
|
var suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
|
|
if (this._vTracker)
|
|
{
|
|
$c.append(prefix + "y" + suffix);
|
|
this._$vScrollBar = $c.children(".ui-scrollbar-y");
|
|
}
|
|
if (this._hTracker)
|
|
{
|
|
$c.append(prefix + "x" + suffix);
|
|
this._$hScrollBar = $c.children(".ui-scrollbar-x");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
function setElementTransform($ele, x, y)
|
|
{
|
|
var v = "translate3d(" + x + "," + y + ", 0px)";
|
|
$ele.css({
|
|
"-moz-transform": v,
|
|
"-webkit-transform": v,
|
|
"transform": v
|
|
});
|
|
}
|
|
|
|
|
|
function MomentumTracker(options)
|
|
{
|
|
this.options = $.extend({}, options);
|
|
this.easing = "easeOutQuad";
|
|
this.reset();
|
|
}
|
|
|
|
var tstates = {
|
|
scrolling: 0,
|
|
overshot: 1,
|
|
snapback: 2,
|
|
done: 3
|
|
};
|
|
|
|
function getCurrentTime() { return (new Date()).getTime(); }
|
|
|
|
$.extend(MomentumTracker.prototype, {
|
|
start: function(pos, speed, duration, minPos, maxPos)
|
|
{
|
|
this.state = (speed != 0) ? ((pos < minPos || pos > maxPos) ? tstates.snapback : tstates.scrolling) : tstates.done;
|
|
this.pos = pos;
|
|
this.speed = speed;
|
|
this.duration = (this.state == tstates.snapback) ? this.options.snapbackDuration : duration;
|
|
this.minPos = minPos;
|
|
this.maxPos = maxPos;
|
|
|
|
this.fromPos = (this.state == tstates.snapback) ? this.pos : 0;
|
|
this.toPos = (this.state == tstates.snapback) ? ((this.pos < this.minPos) ? this.minPos : this.maxPos) : 0;
|
|
|
|
this.startTime = getCurrentTime();
|
|
},
|
|
|
|
reset: function()
|
|
{
|
|
this.state = tstates.done;
|
|
this.pos = 0;
|
|
this.speed = 0;
|
|
this.minPos = 0;
|
|
this.maxPos = 0;
|
|
this.duration = 0;
|
|
},
|
|
|
|
update: function()
|
|
{
|
|
var state = this.state;
|
|
if (state == tstates.done)
|
|
return this.pos;
|
|
|
|
var duration = this.duration;
|
|
var elapsed = getCurrentTime() - this.startTime;
|
|
elapsed = elapsed > duration ? duration : elapsed;
|
|
|
|
if (state == tstates.scrolling || state == tstates.overshot)
|
|
{
|
|
var dx = this.speed * (1 - $.easing[this.easing](elapsed/duration, elapsed, 0, 1, duration));
|
|
|
|
var x = this.pos + dx;
|
|
|
|
var didOverShoot = (state == tstates.scrolling) && (x < this.minPos || x > this.maxPos);
|
|
if (didOverShoot)
|
|
x = (x < this.minPos) ? this.minPos : this.maxPos;
|
|
|
|
this.pos = x;
|
|
|
|
if (state == tstates.overshot)
|
|
{
|
|
if (elapsed >= duration)
|
|
{
|
|
this.state = tstates.snapback;
|
|
this.fromPos = this.pos;
|
|
this.toPos = (x < this.minPos) ? this.minPos : this.maxPos;
|
|
this.duration = this.options.snapbackDuration;
|
|
this.startTime = getCurrentTime();
|
|
elapsed = 0;
|
|
}
|
|
}
|
|
else if (state == tstates.scrolling)
|
|
{
|
|
if (didOverShoot)
|
|
{
|
|
this.state = tstates.overshot;
|
|
this.speed = dx / 2;
|
|
this.duration = this.options.overshootDuration;
|
|
this.startTime = getCurrentTime();
|
|
}
|
|
else if (elapsed >= duration)
|
|
this.state = tstates.done;
|
|
}
|
|
}
|
|
else if (state == tstates.snapback)
|
|
{
|
|
if (elapsed >= duration)
|
|
{
|
|
this.pos = this.toPos;
|
|
this.state = tstates.done;
|
|
}
|
|
else
|
|
this.pos = this.fromPos + ((this.toPos - this.fromPos) * $.easing[this.easing](elapsed/duration, elapsed, 0, 1, duration));
|
|
}
|
|
|
|
return this.pos;
|
|
},
|
|
|
|
done: function() { return this.state == tstates.done; },
|
|
getPosition: function(){ return this.pos; }
|
|
});
|
|
|
|
jQuery.widget( "mobile.scrolllistview", jQuery.mobile.scrollview, {
|
|
options: {
|
|
direction: "y"
|
|
},
|
|
|
|
_create: function() {
|
|
$.mobile.scrollview.prototype._create.call(this);
|
|
|
|
// Cache the dividers so we don't have to search for them everytime the
|
|
// view is scrolled.
|
|
//
|
|
// XXX: Note that we need to update this cache if we ever support lists
|
|
// that can dynamically update their content.
|
|
|
|
this._$dividers = this._$view.find(":jqmData(role='list-divider')");
|
|
this._lastDivider = null;
|
|
},
|
|
|
|
_setScrollPosition: function(x, y)
|
|
{
|
|
// Let the view scroll like it normally does.
|
|
|
|
$.mobile.scrollview.prototype._setScrollPosition.call(this, x, y);
|
|
|
|
y = -y;
|
|
|
|
// Find the dividers for the list.
|
|
|
|
var $divs = this._$dividers;
|
|
var cnt = $divs.length;
|
|
var d = null;
|
|
var dy = 0;
|
|
var nd = null;
|
|
|
|
for (var i = 0; i < cnt; i++)
|
|
{
|
|
nd = $divs.get(i);
|
|
var t = nd.offsetTop;
|
|
if (y >= t)
|
|
{
|
|
d = nd;
|
|
dy = t;
|
|
}
|
|
else if (d)
|
|
break;
|
|
}
|
|
|
|
// If we found a divider to move position it at the top of the
|
|
// clip view.
|
|
|
|
if (d)
|
|
{
|
|
var h = d.offsetHeight;
|
|
var mxy = (d != nd) ? nd.offsetTop : (this._$view.get(0).offsetHeight);
|
|
if (y + h >= mxy)
|
|
y = (mxy - h) - dy;
|
|
else
|
|
y = y - dy;
|
|
|
|
// XXX: Need to convert this over to using $().css() and supporting the non-transform case.
|
|
|
|
var ld = this._lastDivider;
|
|
if (ld && d != ld) {
|
|
setElementTransform($(ld), 0, 0);
|
|
}
|
|
setElementTransform($(d), 0, y + "px");
|
|
this._lastDivider = d;
|
|
|
|
}
|
|
}
|
|
});
|
|
|
|
})(jQuery,window,document); // End Component
|