0
0
mirror of https://github.com/salesagility/SuiteCRM.git synced 2024-11-24 08:36:48 +00:00
salesagility_SuiteCRM/jssource/src_files/include/javascript/iscroll.js
2013-09-23 20:30:44 +01:00

725 lines
19 KiB
JavaScript
Executable File

/**
*
* Find more about the scrolling function at
* http://cubiq.org/iscroll
*
* Copyright (c) 2010 Matteo Spinelli, http://cubiq.org/
* Released under MIT license
* http://cubiq.org/dropbox/mit-license.txt
*
* Version 3.7.1 - Last updated: 2010.10.08
*
*/
(function(){
function iScroll (el, options) {
var that = this, i;
that.element = typeof el == 'object' ? el : document.getElementById(el);
that.wrapper = that.element.parentNode;
that.element.style.webkitTransitionProperty = '-webkit-transform';
that.element.style.webkitTransitionTimingFunction = 'cubic-bezier(0,0,0.25,1)';
that.element.style.webkitTransitionDuration = '0';
that.element.style.webkitTransform = translateOpen + '0,0' + translateClose;
// Default options
that.options = {
bounce: has3d,
momentum: has3d,
checkDOMChanges: true,
topOnDOMChanges: false,
hScrollbar: has3d,
vScrollbar: has3d,
fadeScrollbar: isIthing || !isTouch,
shrinkScrollbar: isIthing || !isTouch,
desktopCompatibility: false,
overflow: 'auto',
snap: false,
bounceLock: false,
scrollbarColor: 'rgba(0,0,0,0.5)',
onScrollEnd: function () {}
};
// User defined options
if (typeof options == 'object') {
for (i in options) {
that.options[i] = options[i];
}
}
if (that.options.desktopCompatibility) {
that.options.overflow = 'hidden';
}
that.onScrollEnd = that.options.onScrollEnd;
delete that.options.onScrollEnd;
that.wrapper.style.overflow = that.options.overflow;
that.refresh();
window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);
if (isTouch || that.options.desktopCompatibility) {
that.element.addEventListener(START_EVENT, that, false);
that.element.addEventListener(MOVE_EVENT, that, false);
that.element.addEventListener(END_EVENT, that, false);
}
if (that.options.checkDOMChanges) {
that.element.addEventListener('DOMSubtreeModified', that, false);
}
}
iScroll.prototype = {
x: 0,
y: 0,
enabled: true,
handleEvent: function (e) {
var that = this;
switch (e.type) {
case START_EVENT:
that.touchStart(e);
break;
case MOVE_EVENT:
that.touchMove(e);
break;
case END_EVENT:
that.touchEnd(e);
break;
case 'webkitTransitionEnd':
that.transitionEnd();
break;
case 'orientationchange':
case 'resize':
that.refresh();
break;
case 'DOMSubtreeModified':
that.onDOMModified(e);
break;
}
},
onDOMModified: function (e) {
var that = this;
// (Hopefully) execute onDOMModified only once
if (e.target.parentNode != that.element) {
return;
}
setTimeout(function () { that.refresh(); }, 0);
if (that.options.topOnDOMChanges && (that.x!=0 || that.y!=0)) {
that.scrollTo(0,0,'0');
}
},
refresh: function () {
var that = this,
resetX = that.x, resetY = that.y,
snap;
that.scrollWidth = that.wrapper.clientWidth;
that.scrollHeight = that.wrapper.clientHeight;
that.scrollerWidth = that.element.offsetWidth;
that.scrollerHeight = that.element.offsetHeight;
that.maxScrollX = that.scrollWidth - that.scrollerWidth;
that.maxScrollY = that.scrollHeight - that.scrollerHeight;
that.directionX = 0;
that.directionY = 0;
if (that.scrollX) {
if (that.maxScrollX >= 0) {
resetX = 0;
} else if (that.x < that.maxScrollX) {
resetX = that.maxScrollX;
}
}
if (that.scrollY) {
if (that.maxScrollY >= 0) {
resetY = 0;
} else if (that.y < that.maxScrollY) {
resetY = that.maxScrollY;
}
}
// Snap
if (that.options.snap) {
that.maxPageX = -Math.floor(that.maxScrollX/that.scrollWidth);
that.maxPageY = -Math.floor(that.maxScrollY/that.scrollHeight);
snap = that.snap(resetX, resetY);
resetX = snap.x;
resetY = snap.y;
}
if (resetX!=that.x || resetY!=that.y) {
that.setTransitionTime('0');
that.setPosition(resetX, resetY, true);
}
that.scrollX = that.scrollerWidth > that.scrollWidth;
that.scrollY = !that.options.bounceLock && !that.scrollX || that.scrollerHeight > that.scrollHeight;
// Update horizontal scrollbar
if (that.options.hScrollbar && that.scrollX) {
that.scrollBarX = that.scrollBarX || new scrollbar('horizontal', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar, that.options.scrollbarColor);
that.scrollBarX.init(that.scrollWidth, that.scrollerWidth);
} else if (that.scrollBarX) {
that.scrollBarX = that.scrollBarX.remove();
}
// Update vertical scrollbar
if (that.options.vScrollbar && that.scrollY && that.scrollerHeight > that.scrollHeight) {
that.scrollBarY = that.scrollBarY || new scrollbar('vertical', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar, that.options.scrollbarColor);
that.scrollBarY.init(that.scrollHeight, that.scrollerHeight);
} else if (that.scrollBarY) {
that.scrollBarY = that.scrollBarY.remove();
}
},
setPosition: function (x, y, hideScrollBars) {
var that = this;
that.x = x;
that.y = y;
that.element.style.webkitTransform = translateOpen + that.x + 'px,' + that.y + 'px' + translateClose;
// Move the scrollbars
if (!hideScrollBars) {
if (that.scrollBarX) {
that.scrollBarX.setPosition(that.x);
}
if (that.scrollBarY) {
that.scrollBarY.setPosition(that.y);
}
}
},
setTransitionTime: function(time) {
var that = this;
time = time || '0';
that.element.style.webkitTransitionDuration = time;
if (that.scrollBarX) {
that.scrollBarX.bar.style.webkitTransitionDuration = time;
that.scrollBarX.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
}
if (that.scrollBarY) {
that.scrollBarY.bar.style.webkitTransitionDuration = time;
that.scrollBarY.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
}
},
touchStart: function(e) {
var that = this,
matrix;
if (!that.enabled) {
return;
}
e.preventDefault();
e.stopPropagation();
that.scrolling = true; // This is probably not needed, but may be useful if iScroll is used in conjuction with other frameworks
that.moved = false;
that.distX = 0;
that.distY = 0;
that.setTransitionTime('0');
// Check if the scroller is really where it should be
if (that.options.momentum || that.options.snap) {
matrix = new WebKitCSSMatrix(window.getComputedStyle(that.element).webkitTransform);
if (matrix.e != that.x || matrix.f != that.y) {
document.removeEventListener('webkitTransitionEnd', that, false);
that.setPosition(matrix.e, matrix.f);
that.moved = true;
}
}
that.touchStartX = isTouch ? e.changedTouches[0].pageX : e.pageX;
that.scrollStartX = that.x;
that.touchStartY = isTouch ? e.changedTouches[0].pageY : e.pageY;
that.scrollStartY = that.y;
that.scrollStartTime = e.timeStamp;
that.directionX = 0;
that.directionY = 0;
},
touchMove: function(e) {
if (!this.scrolling) {
return;
}
var that = this,
pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
leftDelta = that.scrollX ? pageX - that.touchStartX : 0,
topDelta = that.scrollY ? pageY - that.touchStartY : 0,
newX = that.x + leftDelta,
newY = that.y + topDelta;
//e.preventDefault();
e.stopPropagation(); // Stopping propagation just saves some cpu cycles (I presume)
that.touchStartX = pageX;
that.touchStartY = pageY;
// Slow down if outside of the boundaries
if (newX >= 0 || newX < that.maxScrollX) {
newX = that.options.bounce ? Math.round(that.x + leftDelta / 3) : (newX >= 0 || that.maxScrollX>=0) ? 0 : that.maxScrollX;
}
if (newY >= 0 || newY < that.maxScrollY) {
newY = that.options.bounce ? Math.round(that.y + topDelta / 3) : (newY >= 0 || that.maxScrollY>=0) ? 0 : that.maxScrollY;
}
if (that.distX + that.distY > 5) { // 5 pixels threshold
// Lock scroll direction
if (that.distX-3 > that.distY) {
newY = that.y;
topDelta = 0;
} else if (that.distY-3 > that.distX) {
newX = that.x;
leftDelta = 0;
}
that.setPosition(newX, newY);
that.moved = true;
that.directionX = leftDelta > 0 ? -1 : 1;
that.directionY = topDelta > 0 ? -1 : 1;
} else {
that.distX+= Math.abs(leftDelta);
that.distY+= Math.abs(topDelta);
// that.dist+= Math.abs(leftDelta) + Math.abs(topDelta);
}
},
touchEnd: function(e) {
if (!this.scrolling) {
return;
}
var that = this,
time = e.timeStamp - that.scrollStartTime,
point = isTouch ? e.changedTouches[0] : e,
target, ev,
momentumX, momentumY,
newDuration = 0,
newPositionX = that.x, newPositionY = that.y,
snap;
that.scrolling = false;
if (!that.moved) {
that.resetPosition();
if (isTouch) {
// Find the last touched element
target = point.target;
while (target.nodeType != 1) {
target = target.parentNode;
}
// Create the fake event
ev = document.createEvent('MouseEvents');
ev.initMouseEvent('click', true, true, e.view, 1,
point.screenX, point.screenY, point.clientX, point.clientY,
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
0, null);
ev._fake = true;
target.dispatchEvent(ev);
}
return;
}
if (!that.options.snap && time > 250) { // Prevent slingshot effect
that.resetPosition();
return;
}
if (that.options.momentum) {
momentumX = that.scrollX === true
? that.momentum(that.x - that.scrollStartX,
time,
that.options.bounce ? -that.x + that.scrollWidth/5 : -that.x,
that.options.bounce ? that.x + that.scrollerWidth - that.scrollWidth + that.scrollWidth/5 : that.x + that.scrollerWidth - that.scrollWidth)
: { dist: 0, time: 0 };
momentumY = that.scrollY === true
? that.momentum(that.y - that.scrollStartY,
time,
that.options.bounce ? -that.y + that.scrollHeight/5 : -that.y,
that.options.bounce ? (that.maxScrollY < 0 ? that.y + that.scrollerHeight - that.scrollHeight : 0) + that.scrollHeight/5 : that.y + that.scrollerHeight - that.scrollHeight)
: { dist: 0, time: 0 };
newDuration = Math.max(Math.max(momentumX.time, momentumY.time), 1); // The minimum animation length must be 1ms
newPositionX = that.x + momentumX.dist;
newPositionY = that.y + momentumY.dist;
}
if (that.options.snap) {
snap = that.snap(newPositionX, newPositionY);
newPositionX = snap.x;
newPositionY = snap.y;
newDuration = Math.max(snap.time, newDuration);
}
that.scrollTo(newPositionX, newPositionY, newDuration + 'ms');
},
transitionEnd: function () {
var that = this;
document.removeEventListener('webkitTransitionEnd', that, false);
that.resetPosition();
},
resetPosition: function () {
var that = this,
resetX = that.x,
resetY = that.y;
if (that.x >= 0) {
resetX = 0;
} else if (that.x < that.maxScrollX) {
resetX = that.maxScrollX;
}
if (that.y >= 0 || that.maxScrollY > 0) {
resetY = 0;
} else if (that.y < that.maxScrollY) {
resetY = that.maxScrollY;
}
if (resetX != that.x || resetY != that.y) {
that.scrollTo(resetX, resetY);
} else {
if (that.moved) {
that.onScrollEnd(); // Execute custom code on scroll end
that.moved = false;
}
// Hide the scrollbars
if (that.scrollBarX) {
that.scrollBarX.hide();
}
if (that.scrollBarY) {
that.scrollBarY.hide();
}
}
},
snap: function (x, y) {
var that = this, time;
if (that.directionX > 0) {
x = Math.floor(x/that.scrollWidth);
} else if (that.directionX < 0) {
x = Math.ceil(x/that.scrollWidth);
} else {
x = Math.round(x/that.scrollWidth);
}
that.pageX = -x;
x = x * that.scrollWidth;
if (x > 0) {
x = that.pageX = 0;
} else if (x < that.maxScrollX) {
that.pageX = that.maxPageX;
x = that.maxScrollX;
}
if (that.directionY > 0) {
y = Math.floor(y/that.scrollHeight);
} else if (that.directionY < 0) {
y = Math.ceil(y/that.scrollHeight);
} else {
y = Math.round(y/that.scrollHeight);
}
that.pageY = -y;
y = y * that.scrollHeight;
if (y > 0) {
y = that.pageY = 0;
} else if (y < that.maxScrollY) {
that.pageY = that.maxPageY;
y = that.maxScrollY;
}
// Snap with constant speed (proportional duration)
time = Math.round(Math.max(
Math.abs(that.x - x) / that.scrollWidth * 500,
Math.abs(that.y - y) / that.scrollHeight * 500
));
return { x: x, y: y, time: time };
},
scrollTo: function (destX, destY, runtime) {
var that = this;
if (that.x == destX && that.y == destY) {
that.resetPosition();
return;
}
that.moved = true;
that.setTransitionTime(runtime || '350ms');
that.setPosition(destX, destY);
if (runtime==='0' || runtime=='0s' || runtime=='0ms') {
that.resetPosition();
} else {
document.addEventListener('webkitTransitionEnd', that, false); // At the end of the transition check if we are still inside of the boundaries
}
},
scrollToPage: function (pageX, pageY, runtime) {
var that = this, snap;
if (!that.options.snap) {
that.pageX = -Math.round(that.x / that.scrollWidth);
that.pageY = -Math.round(that.y / that.scrollHeight);
}
if (pageX == 'next') {
pageX = ++that.pageX;
} else if (pageX == 'prev') {
pageX = --that.pageX;
}
if (pageY == 'next') {
pageY = ++that.pageY;
} else if (pageY == 'prev') {
pageY = --that.pageY;
}
pageX = -pageX*that.scrollWidth;
pageY = -pageY*that.scrollHeight;
snap = that.snap(pageX, pageY);
pageX = snap.x;
pageY = snap.y;
that.scrollTo(pageX, pageY, runtime || '500ms');
},
scrollToElement: function (el, runtime) {
el = typeof el == 'object' ? el : this.element.querySelector(el);
if (!el) {
return;
}
var that = this,
x = that.scrollX ? -el.offsetLeft : 0,
y = that.scrollY ? -el.offsetTop : 0;
if (x >= 0) {
x = 0;
} else if (x < that.maxScrollX) {
x = that.maxScrollX;
}
if (y >= 0) {
y = 0;
} else if (y < that.maxScrollY) {
y = that.maxScrollY;
}
that.scrollTo(x, y, runtime);
},
momentum: function (dist, time, maxDistUpper, maxDistLower) {
var friction = 2.5,
deceleration = 1.2,
speed = Math.abs(dist) / time * 1000,
newDist = speed * speed / friction / 1000,
newTime = 0;
// Proportinally reduce speed if we are outside of the boundaries
if (dist > 0 && newDist > maxDistUpper) {
speed = speed * maxDistUpper / newDist / friction;
newDist = maxDistUpper;
} else if (dist < 0 && newDist > maxDistLower) {
speed = speed * maxDistLower / newDist / friction;
newDist = maxDistLower;
}
newDist = newDist * (dist < 0 ? -1 : 1);
newTime = speed / deceleration;
return { dist: Math.round(newDist), time: Math.round(newTime) };
},
destroy: function (full) {
var that = this;
window.removeEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);
that.element.removeEventListener(START_EVENT, that, false);
that.element.removeEventListener(MOVE_EVENT, that, false);
that.element.removeEventListener(END_EVENT, that, false);
document.removeEventListener('webkitTransitionEnd', that, false);
if (that.options.checkDOMChanges) {
that.element.removeEventListener('DOMSubtreeModified', that, false);
}
if (that.scrollBarX) {
that.scrollBarX = that.scrollBarX.remove();
}
if (that.scrollBarY) {
that.scrollBarY = that.scrollBarY.remove();
}
if (full) {
that.wrapper.parentNode.removeChild(that.wrapper);
}
return null;
}
};
function scrollbar (dir, wrapper, fade, shrink, color) {
var that = this,
doc = document;
that.dir = dir;
that.fade = fade;
that.shrink = shrink;
that.uid = ++uid;
// Create main scrollbar
that.bar = doc.createElement('div');
that.bar.style.cssText = 'position:absolute;top:0;left:0;-webkit-transition-timing-function:cubic-bezier(0,0,0.25,1);pointer-events:none;-webkit-transition-duration:0;-webkit-transition-delay:0;-webkit-transition-property:-webkit-transform;z-index:10;background:' + color + ';' +
'-webkit-transform:' + translateOpen + '0,0' + translateClose + ';' +
(dir == 'horizontal' ? '-webkit-border-radius:3px 2px;min-width:6px;min-height:5px' : '-webkit-border-radius:2px 3px;min-width:5px;min-height:6px');
// Create scrollbar wrapper
that.wrapper = doc.createElement('div');
that.wrapper.style.cssText = '-webkit-mask:-webkit-canvas(scrollbar' + that.uid + that.dir + ');position:absolute;z-index:10;pointer-events:none;overflow:hidden;opacity:0;-webkit-transition-duration:' + (fade ? '300ms' : '0') + ';-webkit-transition-delay:0;-webkit-transition-property:opacity;' +
(that.dir == 'horizontal' ? 'bottom:2px;left:2px;right:7px;height:5px' : 'top:2px;right:2px;bottom:7px;width:5px;');
// Add scrollbar to the DOM
that.wrapper.appendChild(that.bar);
wrapper.appendChild(that.wrapper);
}
scrollbar.prototype = {
init: function (scroll, size) {
var that = this,
doc = document,
pi = Math.PI,
ctx;
// Create scrollbar mask
if (that.dir == 'horizontal') {
if (that.maxSize != that.wrapper.offsetWidth) {
that.maxSize = that.wrapper.offsetWidth;
ctx = doc.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, that.maxSize, 5);
ctx.fillStyle = "rgb(0,0,0)";
ctx.beginPath();
ctx.arc(2.5, 2.5, 2.5, pi/2, -pi/2, false);
ctx.lineTo(that.maxSize-2.5, 0);
ctx.arc(that.maxSize-2.5, 2.5, 2.5, -pi/2, pi/2, false);
ctx.closePath();
ctx.fill();
}
} else {
if (that.maxSize != that.wrapper.offsetHeight) {
that.maxSize = that.wrapper.offsetHeight;
ctx = doc.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, 5, that.maxSize);
ctx.fillStyle = "rgb(0,0,0)";
ctx.beginPath();
ctx.arc(2.5, 2.5, 2.5, pi, 0, false);
ctx.lineTo(5, that.maxSize-2.5);
ctx.arc(2.5, that.maxSize-2.5, 2.5, 0, pi, false);
ctx.closePath();
ctx.fill();
}
}
that.size = Math.max(Math.round(that.maxSize * that.maxSize / size), 6);
that.maxScroll = that.maxSize - that.size;
that.toWrapperProp = that.maxScroll / (scroll - size);
that.bar.style[that.dir == 'horizontal' ? 'width' : 'height'] = that.size + 'px';
},
setPosition: function (pos) {
var that = this;
if (that.wrapper.style.opacity != '1') {
that.show();
}
pos = Math.round(that.toWrapperProp * pos);
if (pos < 0) {
pos = that.shrink ? pos + pos*3 : 0;
if (that.size + pos < 7) {
pos = -that.size + 6;
}
} else if (pos > that.maxScroll) {
pos = that.shrink ? pos + (pos-that.maxScroll)*3 : that.maxScroll;
if (that.size + that.maxScroll - pos < 7) {
pos = that.size + that.maxScroll - 6;
}
}
pos = that.dir == 'horizontal'
? translateOpen + pos + 'px,0' + translateClose
: translateOpen + '0,' + pos + 'px' + translateClose;
that.bar.style.webkitTransform = pos;
},
show: function () {
if (has3d) {
this.wrapper.style.webkitTransitionDelay = '0';
}
this.wrapper.style.opacity = '1';
},
hide: function () {
if (has3d) {
this.wrapper.style.webkitTransitionDelay = '350ms';
}
this.wrapper.style.opacity = '0';
},
remove: function () {
this.wrapper.parentNode.removeChild(this.wrapper);
return null;
}
};
// Is translate3d compatible?
var has3d = ('WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix()),
// Device sniffing
isIthing = (/iphone|ipad/gi).test(navigator.appVersion),
isTouch = ('ontouchstart' in window),
// Event sniffing
START_EVENT = isTouch ? 'touchstart' : 'mousedown',
MOVE_EVENT = isTouch ? 'touchmove' : 'mousemove',
END_EVENT = isTouch ? 'touchend' : 'mouseup',
// Translate3d helper
translateOpen = 'translate' + (has3d ? '3d(' : '('),
translateClose = has3d ? ',0)' : ')',
// Unique ID
uid = 0;
// Expose iScroll to the world
window.iScroll = iScroll;
})();