Skip to content

Instantly share code, notes, and snippets.

@miya2000
Created June 5, 2010 09:01
Show Gist options
  • Select an option

  • Save miya2000/426464 to your computer and use it in GitHub Desktop.

Select an option

Save miya2000/426464 to your computer and use it in GitHub Desktop.
if (window.top != window.self) {
// たぶん BackCompat じゃないと動かない。
if (this.scrollable.isRoot()) {
document.body.style.height = '100%';
document.body.style.overflow = 'hidden';
}
detach.push(subscribe(window, 'focus', bind(this, function() {
var left = -parseInt(document.documentElement.style.marginLeft, 10) || 0;
var top = -parseInt(document.documentElement.style.marginTop, 10) || 0;
document.documentElement.style.marginLeft = '';
document.documentElement.style.marginTop = '';
document.body.style.height = '';
document.body.style.overflow = '';
this.scrollable.scrollTo(left, top);
})));
detach.push(subscribe(window, 'blur', bind(this, function() {
var left = this.scrollable.getScrollLeft();
var top = this.scrollable.getScrollTop();
document.body.style.height = '100%';
document.body.style.overflow = 'hidden';
document.documentElement.style.marginLeft = -left + 'px';
document.documentElement.style.marginTop = -top + 'px';
})));
}
var Browser = {
Opera: !!window.opera,
Opera_version: window.opera ? parseFloat(window.opera.version()) : null,
Webkit: (navigator.userAgent.indexOf(' AppleWebKit/') >= 0)
};
/// class Scrollable - scrollable element wrapper.
function Scrollable(element) {
if (!(this instanceof Scrollable)) return new Scrollable(element);
this.element = element;
this.scrollElement = element;
if (this.isRoot() && Browser.Webkit) {
this.scrollElement = document.body;
}
this.scroller = {
'1': new Smooth(this.scrollElement, 'scrollLeft'),
'2': new Smooth(this.scrollElement, 'scrollTop')
};
this.scroller[1].easing = this.scroller[2].easing = Smooth.easings.easeOutSine;
this.scroller[1].duration = this.scroller[2].duration = function(change, sliding) {
var abs = (change ^ (change >> 31)) - (change >> 31); // Math.abs(change);
if (abs < 120) return this.baseDuration * (abs / 120) | 0;
return this.baseDuration;
};
if (this.isRoot() && Browser.Webkit) {
this.scroller[1].onprogress = this.scroller[2].onprogress = function() {
Scrollable.fn.dispatchScrollEvent(document);
};
}
}
Scrollable.HORIZONTAL_AXIS = 1; // compatible MouseScrollEvent(Gecko).
Scrollable.VERTICAL_AXIS = 2;
var Sfn = Scrollable.fn = (function() {
//[[navico notation]]
return {
getRoot: getRoot,
isRoot: isRoot,
dispatchScrollEvent: dispatchScrollEvent,
getClientWidth: getClientWidth,
getClientHeight: getClientHeight,
getScrollWidth: getScrollWidth,
getScrollHeight: getScrollHeight,
getScrollLeft: getScrollLeft,
getScrollTop: getScrollTop,
getScrollElement: getScrollElement,
isScrollable: isScrollable,
isScrollableNode: isScrollableNode,
scrollDirectTo: scrollDirectTo,
scrollDirectBy: scrollDirectBy,
scrollTo: scrollTo,
scrollBy: scrollBy
};
function getRoot() {
return Scrollable.ROOT || (Scrollable.ROOT = (document.compatMode == 'BackCompat') ? document.body : document.documentElement);
}
function isRoot(ele) {
return ele === getRoot();
}
function dispatchScrollEvent(target) {
var ev = document.createEvent('HTMLEvents');
ev.initEvent('scroll', true, false);
target.dispatchEvent(ev);
}
function getClientWidth(viewElement) {
return viewElement.clientWidth;
}
function getClientHeight(viewElement) {
return viewElement.clientHeight;
}
function getScrollWidth(scrollElement) {
if (Browser.Opera) {
var style = document.defaultView.getComputedStyle(scrollElement, '');
var pad = 0;
if (Browser.Opera_version <= 10.10) {
pad -= parseInt(style.borderLeftWidth, 10);
}
else {
pad += parseInt(style.paddingLeft, 10);
}
return scrollElement.scrollWidth + pad;
}
else {
return scrollElement.scrollWidth;
}
}
function getScrollHeight(scrollElement) {
if (Browser.Opera && !isRoot(scrollElement)) {
var style = document.defaultView.getComputedStyle(scrollElement, '');
var pad = 0;
pad += parseInt(style.paddingTop, 10);
return scrollElement.scrollHeight + pad;
}
else {
return scrollElement.scrollHeight;
}
}
function getScrollLeft(scrollElement) {
return scrollElement.scrollLeft;
}
function getScrollTop(scrollElement) {
return scrollElement.scrollTop;
}
function getScrollElement(ele) {
if (isRoot(ele) && Browser.Webkit) return document.body;
return ele;
}
function isScrollable(ele, h1v2orBoth, u1d2orBoth) {
var scr = getScrollElement(ele);
if (h1v2orBoth == 1) {
if (u1d2orBoth == 1) return getScrollLeft(scr) > 0;
if (u1d2orBoth == 2) return getScrollLeft(scr) < getScrollWidth(scr) - getClientWidth(ele);
return getScrollWidth(scr) > getClientWidth(ele);
}
if (h1v2orBoth == 2) {
if (u1d2orBoth == 1) return getScrollTop(scr) > 0;
if (u1d2orBoth == 2) return getScrollTop(scr) < getScrollHeight(scr) - getClientHeight(ele);
return getScrollHeight(scr) > getClientHeight(ele);
}
return (getScrollWidth(scr) > getClientWidth(ele)) && (getScrollHeight(scr) > getClientHeight(ele));
}
function isScrollableNode(ele, h1v2) {
if (!ele || ele.nodeType != 1) return false;
if (isRoot(ele)) return true;
if (ele.nodeName == 'TEXTAREA') return true;
var style = document.defaultView.getComputedStyle(ele, '');
if (style.display != 'block') return false;
if (typeof style.overflowY == 'undefined') {
return /^(auto|scroll)$/.test(style.overflow);
}
else {
if (h1v2 == 1) return /^(auto|scroll)$/.test(style.overflowX);
if (h1v2 == 2) return /^(auto|scroll)$/.test(style.overflowY);
return /^(auto|scroll)$/.test(style.overflow);
}
}
function scrollDirectTo(ele, h1v2, to) {
var dest = to, max, scr = getScrollElement(ele);
if (h1v2 == 1) max = getScrollWidth(scr) - getClientWidth(ele);
if (h1v2 == 2) max = getScrollHeight(scr) - getClientHeight(ele);
if (dest < 0 ) dest = 0;
if (dest > max) dest = max;
if (h1v2 == 1) scr.scrollLeft = dest;
if (h1v2 == 2) scr.scrollTop = dest;
if (isRoot(ele) && Browser.Webkit) {
dispatchScrollEvent(document);
}
}
function scrollDirectBy(ele, h1v2, delta) {
Sfn.scrollDirectTo(ele, h1v2, (h1v2 == 1 ? getScrollLeft(ele) : getScrollTop(ele)) + delta);
}
function scrollTo(ele, x, y) {
if (x != null) Sfn.scrollDirectTo(ele, 1, x);
if (y != null) Sfn.scrollDirectTo(ele, 2, y);
}
function scrollBy(ele, dx, dy) {
if (dx != null) Sfn.scrollDirectBy(ele, 1, dx);
if (dy != null) Sfn.scrollDirectBy(ele, 2, dy);
}
})();
(function(member) { member.apply(Scrollable.prototype); })(function() {
this.isRoot = function() { return Sfn.isRoot(this.element); };
this.getClientWidth = function() { return Sfn.getClientWidth(this.element); };
this.getClientHeight = function() { return Sfn.getClientHeight(this.element); };
this.getScrollWidth = function() { return Sfn.getScrollWidth(this.scrollElement); };
this.getScrollHeight = function() { return Sfn.getScrollHeight(this.scrollElement); };
this.getScrollLeft = function() { return Sfn.getScrollLeft(this.scrollElement); };
this.getScrollTop = function() { return Sfn.getScrollTop(this.scrollElement); };
this.isScrollable = function(h1v2, u1d2) { return Sfn.isScrollable(this.element, h1v2, u1d2); };
this.isScrollableNode = function() { return Sfn.isScrollableNode(this.element); };
this.scrollDirectTo = function scrollDirectTo(h1v2, to, smooth) {
if (!smooth) {
Sfn.scrollDirectTo(this.element, h1v2, to);
return;
}
var dest = to, max;
if (h1v2 == 1) max = this.getScrollWidth() - this.getClientWidth();
if (h1v2 == 2) max = this.getScrollHeight() - this.getClientHeight();
if (dest < 0 ) dest = 0;
if (dest > max) dest = max;
this.scroller[h1v2].go(dest);
};
this.scrollDirectBy = function scrollDirectBy(h1v2, delta, smooth) {
var scroller = this.scroller[h1v2];
var from = scroller.isActive ? scroller.dest : scroller.current();
this.scrollDirectTo(h1v2, from + delta, smooth);
};
this.scrollTo = function scrollTo(x, y, smooth) {
if (x != null) this.scrollDirectTo(1, x, smooth);
if (y != null) this.scrollDirectTo(2, y, smooth);
};
this.scrollBy = function scrollTo(dx, dy, smooth) {
if (dx != null) this.scrollDirectBy(1, dx, smooth);
if (dy != null) this.scrollDirectBy(2, dy, smooth);
};
});
/// class Smooth - simple smooth animation.
function Smooth(target, property, baseDuration, delay, initialRatio) {
this.target = target;
this.property = property;
this.baseDuration = baseDuration || 180;
this.delay = delay || 16;
this.initialRatio = initialRatio || 0;
}
Smooth.easings = {
easeNone : function easeNone(t, b, c, d) { return c*t/d + b; },
easeInSine : function easeInSine(t, b, c, d) { return -c * Math.cos(t/d *(Math.PI/2)) + c + b; },
easeOutSine : function easeOutSine(t, b, c, d) { return c * Math.sin(t/d *(Math.PI/2)) + b; },
easeInExpo : function easeInExpo(t, b, c, d) { return(t==0) ? b : c * Math.pow(2, 10 *(t/d - 1)) + b - c * 0.001; },
easeOutExpo : function easeOutExpo(t, b, c, d) { return(t==d) ? b+c : c * 1.001 *(-Math.pow(2, -10 * t/d) + 1) + b; }
};
(function(member) { member.apply(Smooth.prototype) })(function() {
this.easing = Smooth.easings.easeOutExpo;
this.duration = function(change, sliding) { return this.baseDuration; };
this.go = function(/*from, */dest) {
var target = this.target, prop = this.property, from;
if (arguments.length > 1) {
from = arguments[0];
dest = arguments[1];
}
else {
from = target[prop];
}
if (this.isActive) {
if (this.dest == dest) return;
this.dest = dest;
this.activity.slide(dest);
return;
}
var begin = parseFloat(from) || 0, end = parseFloat(dest), change = end - begin;
if (this.initialRatio) begin += change * this.initialRatio;
var m = /[0-9]*\.?([0-9]*)(.*)/.exec(dest), scale = (m ? m[1].length : 0), unit = (m ? m[2] : null);
var power = Math.pow(10, scale);
var start = new Date(), duration = this.duration(change, false);
this.from = from; this.dest = dest; this.isActive = true;
this.activity = {
slide: slide,
anim: anim,
dispose: dispose
};
var self = this;
function slide(dest) {
var cur = parseFloat(self.current());
if (!isNaN(cur)) begin = cur;
end = parseFloat(dest);
change = end - begin;
start = new Date();
duration = self.duration(change, true);
}
function anim() {
var finished = false;
try {
var time = new Date() - start, next;
if (time >= duration) {
next = self.dest;
finished = true;
}
else {
next = (self.easing(time, begin, change, duration) * power + 0.5 | 0) / power; // not strict round at negative value.
if (unit) next = next + unit;
if (next == self.dest) finished = true;
}
target[prop] = next;
}
catch(e) { self.clear(); throw e; }
if (finished) {
self.clear();
if (self.onfinish) self.onfinish();
}
else {
if (self.onprogress) self.onprogress();
}
}
var tid = setInterval(anim, this.delay);
function dispose() { if (tid) clearInterval(tid); tid = null; }
if (arguments.length > 1 || duration <= 0) anim(); // call first if "from" set or "duration" isn't set.
};
this.current = function() {
return this.target[this.property];
};
this.clear = function() {
this.isActive = false;
if (this.activity != null) this.activity.dispose();
this.activity = null;
};
this.restore = function() {
if (this.from != null) this.target[this.property] = this.from;
this.clear();
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment