Created
April 6, 2013 08:12
-
-
Save myndzi/5325367 to your computer and use it in GitHub Desktop.
LearnBoost antiscroll modified to compile under closure's advanced mode. Remember to pass it a jQuery externs file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==ClosureCompiler== | |
// @compilation_level ADVANCED_OPTIMIZATIONS | |
// @output_file_name default.js | |
// ==/ClosureCompiler== | |
// ADD YOUR CODE HERE | |
(function ($) { | |
/** | |
* Augment jQuery prototype. | |
*/ | |
$.fn['antiscroll'] = function (options) { | |
return this.each(function () { | |
if ($(this).data('$.A')) { | |
$(this).data('$.A').destroy(); | |
} | |
$(this).data('$.A', new $.Antiscroll(this, options)); | |
}); | |
}; | |
/** | |
* Expose constructor. | |
*/ | |
$.Antiscroll = Antiscroll; | |
/** | |
* Antiscroll pane constructor. | |
* | |
* @constructor | |
* @param {Element|jQuery} el | |
* @param {Object} opts | |
*/ | |
function Antiscroll(el, opts) { | |
this.el = $(el); | |
this.options = opts || {}; | |
this.x = (false !== this.options['x']) || this.options['forceHorizontal']; | |
this.y = (false !== this.options['y']) || this.options['forceVertical']; | |
this.autoHide = false !== this.options['autoHide']; | |
this.padding = undefined == this.options['padding'] ? 2 : this.options['padding']; | |
/** @type {jQuery} */ | |
this.inner = this.el.find('.antiscroll-inner'); | |
this.inner.css({ | |
'width': '+=' + (this.y ? scrollbarSize() : 0), | |
'height': '+=' + (this.x ? scrollbarSize() : 0) | |
}); | |
var cssMap = {}; | |
if (this.x) cssMap.width = '+=' + scrollbarSize(); | |
if (this.y) cssMap.height = '+=' + scrollbarSize(); | |
this.inner.css(cssMap); | |
this.refresh(); | |
}; | |
/** | |
* refresh scrollbars | |
* | |
*/ | |
Antiscroll.prototype.refresh = function () { | |
var needHScroll = this.inner.get(0).scrollWidth > this.el.width() + (this.y ? scrollbarSize() : 0), | |
needVScroll = this.inner.get(0).scrollHeight > this.el.height() + (this.x ? scrollbarSize() : 0); | |
if (this.x) { | |
if (!this.horizontal && needHScroll) { | |
this.horizontal = new Scrollbar.Horizontal(this); | |
} | |
else if (this.horizontal && !needHScroll) { | |
this.horizontal.destroy(); | |
this.horizontal = null; | |
} | |
else if (this.horizontal) { | |
this.horizontal.update(); | |
} | |
} | |
if (this.y) { | |
if (!this.vertical && needVScroll) { | |
this.vertical = new Scrollbar.Vertical(this); | |
} | |
else if (this.vertical && !needVScroll) { | |
this.vertical.destroy(); | |
this.vertical = null; | |
} | |
else if (this.vertical) { | |
this.vertical.update(); | |
} | |
} | |
}; | |
/** | |
* Cleans up. | |
* | |
* @return {Antiscroll} for chaining | |
*/ | |
Antiscroll.prototype.destroy = function () { | |
if (this.horizontal) { | |
this.horizontal.destroy(); | |
this.horizontal = null | |
} | |
if (this.vertical) { | |
this.vertical.destroy(); | |
this.vertical = null | |
} | |
return this; | |
}; | |
/** | |
* Rebuild Antiscroll. | |
* | |
* @return {Antiscroll} for chaining | |
*/ | |
Antiscroll.prototype.rebuild = function () { | |
this.destroy(); | |
this.inner.attr('style', ''); | |
Antiscroll.call(this, this.el, this.options); | |
return this; | |
}; | |
/** | |
* Scrollbar constructor. | |
* | |
* @constructor | |
* @this {Scrollbar.Horizontal|Scrollbar.Vertical} | |
* @param {Antiscroll} pane | |
*/ | |
function Scrollbar(pane) { | |
this.pane = pane; | |
this.pane.el.append(this.el); | |
this.innerEl = this.pane.inner.get(0); | |
this.dragging = false; | |
this.enter = false; | |
this.shown = false; | |
// hovering | |
// mouseenter = z | |
this.pane.el.mouseenter($.proxy(this, 'z')); | |
// mouseleave = y | |
this.pane.el.mouseleave($.proxy(this, 'y')); | |
// dragging | |
// mousedown = x | |
this.el.mousedown($.proxy(this, 'x')); | |
// scrolling | |
// scroll = u | |
this.innerPaneScrollListener = $.proxy(this, 'u'); | |
this.pane.inner.scroll(this.innerPaneScrollListener); | |
// wheel -optional- | |
// mousewheel = w | |
this.innerPaneMouseWheelListener = $.proxy(this, 'w'); | |
this.pane.inner.bind('mousewheel', this.innerPaneMouseWheelListener); | |
// show | |
var initialDisplay = this.pane.options['initialDisplay']; | |
if (initialDisplay !== false) { | |
this.show(); | |
if (this.pane.autoHide) { | |
this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); | |
} | |
} | |
}; | |
/** | |
* Cleans up. | |
* | |
* @return {Scrollbar} for chaining | |
*/ | |
Scrollbar.prototype.destroy = function () { | |
this.el.remove(); | |
this.pane.inner.unbind('scroll', this.innerPaneScrollListener); | |
this.pane.inner.unbind('mousewheel', this.innerPaneMouseWheelListener); | |
return this; | |
}; | |
/** | |
* Called upon mouseenter. | |
* | |
*/ | |
Scrollbar.prototype['z'] = function () { | |
this.enter = true; | |
this.show(); | |
}; | |
/** | |
* Called upon mouseleave. | |
* | |
*/ | |
Scrollbar.prototype['y'] = function () { | |
this.enter = false; | |
if (!this.dragging) { | |
if (this.pane.autoHide) { | |
this.hide(); | |
} | |
} | |
}; | |
/** | |
* Called upon wrap scroll. | |
* | |
*/ | |
Scrollbar.prototype['u'] = function () { | |
if (!this.shown) { | |
this.show(); | |
if (!this.enter && !this.dragging) { | |
if (this.pane.autoHide) { | |
this.hiding = setTimeout($.proxy(this, 'hide'), 1500); | |
} | |
} | |
} | |
this.update(); | |
}; | |
/** | |
* Called upon scrollbar mousedown. | |
* | |
*/ | |
Scrollbar.prototype['x'] = function (ev) { | |
ev.preventDefault(); | |
this.dragging = true; | |
this.startPageY = ev.pageY - parseInt(this.el.css('top'), 10); | |
this.startPageX = ev.pageX - parseInt(this.el.css('left'), 10); | |
// prevent crazy selections on IE | |
this.el[0].ownerDocument.onselectstart = function () { | |
return false; | |
}; | |
// mousemove = v | |
var pane = this.pane, | |
move = $.proxy(this, 'v'), | |
self = this | |
$(this.el[0].ownerDocument) | |
.mousemove(move) | |
.mouseup(function () { | |
self.dragging = false; | |
this.onselectstart = null; | |
$(this).unbind('mousemove', move); | |
if (!self.enter) { | |
self.hide(); | |
} | |
}); | |
}; | |
/** | |
* Show scrollbar. | |
* @param {number=} duration | |
* | |
*/ | |
Scrollbar.prototype.show = function (duration) { | |
if (!this.shown && this.update()) { | |
this.el.addClass('antiscroll-scrollbar-shown'); | |
if (this.hiding) { | |
clearTimeout(this.hiding); | |
this.hiding = null; | |
} | |
this.shown = true; | |
} | |
}; | |
/** | |
* Hide scrollbar. | |
* | |
*/ | |
Scrollbar.prototype.hide = function () { | |
if (this.pane.autoHide !== false && this.shown) { | |
// check for dragging | |
this.el.removeClass('antiscroll-scrollbar-shown'); | |
this.shown = false; | |
} | |
}; | |
/** | |
* Horizontal scrollbar constructor | |
* | |
* @constructor | |
* @extends Scrollbar | |
*/ | |
Scrollbar.Horizontal = function (pane) { | |
this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-horizontal">'); | |
Scrollbar.call(this, pane); | |
}; | |
/** | |
* Inherits from Scrollbar. | |
*/ | |
inherits(Scrollbar.Horizontal, Scrollbar); | |
/** | |
* Updates size/position of scrollbar. | |
* | |
*/ | |
Scrollbar.Horizontal.prototype.update = function () { | |
var paneWidth = this.pane.el.width(), | |
trackWidth = paneWidth - this.pane.padding * 2, | |
innerEl = this.pane.inner.get(0) | |
this.el | |
.css('width', trackWidth * paneWidth / innerEl.scrollWidth) | |
.css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth); | |
return paneWidth < innerEl.scrollWidth; | |
}; | |
/** | |
* Called upon drag. | |
* | |
*/ | |
Scrollbar.Horizontal.prototype['v'] = function (ev) { | |
var trackWidth = this.pane.el.width() - this.pane.padding * 2, | |
pos = ev.pageX - this.startPageX, | |
barWidth = this.el.width(), | |
innerEl = this.pane.inner.get(0) | |
// minimum top is 0, maximum is the track height | |
var y = Math.min(Math.max(pos, 0), trackWidth - barWidth); | |
innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) * y / (trackWidth - barWidth); | |
}; | |
/** | |
* Called upon container mousewheel. | |
* | |
*/ | |
Scrollbar.Horizontal.prototype['w'] = function (ev, delta, x, y) { | |
if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || | |
(x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) == this.innerEl.scrollWidth))) { | |
ev.preventDefault(); | |
return false; | |
} | |
}; | |
/** | |
* Vertical scrollbar constructor | |
* | |
* @constructor | |
* @extends Scrollbar | |
*/ | |
Scrollbar.Vertical = function (pane) { | |
this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-vertical">'); | |
Scrollbar.call(this, pane); | |
}; | |
/** | |
* Inherits from Scrollbar. | |
*/ | |
inherits(Scrollbar.Vertical, Scrollbar); | |
/** | |
* Updates size/position of scrollbar. | |
* | |
*/ | |
Scrollbar.Vertical.prototype.update = function () { | |
var paneHeight = this.pane.el.height(), | |
trackHeight = paneHeight - this.pane.padding * 2, | |
innerEl = this.innerEl; | |
var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight; | |
scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight; | |
var topPos = trackHeight * innerEl.scrollTop / innerEl.scrollHeight; | |
if ((topPos + scrollbarHeight) > trackHeight) { | |
var diff = (topPos + scrollbarHeight) - trackHeight; | |
topPos = topPos - diff - 3; | |
} | |
this.el | |
.css('height', scrollbarHeight) | |
.css('top', topPos); | |
return paneHeight < innerEl.scrollHeight; | |
}; | |
/** | |
* Called upon drag. | |
*/ | |
Scrollbar.Vertical.prototype['v'] = function (ev) { | |
var paneHeight = this.pane.el.height(), | |
trackHeight = paneHeight - this.pane.padding * 2, | |
pos = ev.pageY - this.startPageY, | |
barHeight = this.el.height(), | |
innerEl = this.innerEl | |
// minimum top is 0, maximum is the track height | |
var y = Math.min(Math.max(pos, 0), trackHeight - barHeight); | |
innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) * y / (trackHeight - barHeight); | |
}; | |
/** | |
* Called upon container mousewheel. | |
* | |
*/ | |
Scrollbar.Vertical.prototype['w'] = function (ev, delta, x, y) { | |
if ((y > 0 && 0 == this.innerEl.scrollTop) || | |
(y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) == this.innerEl.scrollHeight))) { | |
ev.preventDefault(); | |
return false; | |
} | |
}; | |
/** | |
* Cross-browser inheritance. | |
* | |
* @param {Function} ctorA | |
* @param {Function} ctorB | |
*/ | |
function inherits(ctorA, ctorB) { | |
/** @constructor */ | |
function f() {}; | |
f.prototype = ctorB.prototype; | |
ctorA.prototype = new f; | |
}; | |
var size; | |
/** | |
* Scrollbar size detection. | |
* @return {number} | |
*/ | |
function scrollbarSize() { | |
if (size === undefined) { | |
var div = $( | |
'<div class="antiscroll-inner" style="width:50px;height:50px;overflow-y:scroll;' + 'position:absolute;top:-200px;left:-200px;"><div style="height:100px;width:100%">' + '</div>'); | |
$('body').append(div); | |
var w1 = $(div).innerWidth(); | |
var w2 = $('div', div).innerWidth(); | |
$(div).remove(); | |
size = w1 - w2; | |
} | |
return size; | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment