Telegram Web, preconfigured for usage in I2P. http://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

929 lines
24 KiB

/*! nanoScrollerJS - v0.8.0 - 2014
11 years ago
* http://jamesflorentino.github.com/nanoScrollerJS/
* Copyright (c) 2014 James Florentino; Licensed MIT */
11 years ago
(function($, window, document) {
"use strict";
var BROWSER_IS_IE7, BROWSER_SCROLLBAR_WIDTH, DOMSCROLL, DOWN, DRAG, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEMOVE, MOUSEUP, MOUSEWHEEL, NanoScroll, PANEDOWN, RESIZE, SCROLL, SCROLLBAR, TOUCHMOVE, UP, WHEEL, cAF, defaults, getBrowserScrollbarWidth, hasTransform, isFFWithBuggyScrollbar, rAF, transform, _elementStyle, _prefixStyle, _vendor;
defaults = {
11 years ago
/**
a classname for the pane element.
@property paneClass
@type String
@default 'nano-pane'
*/
paneClass: 'nano-pane',
11 years ago
/**
a classname for the slider element.
@property sliderClass
@type String
@default 'nano-slider'
*/
sliderClass: 'nano-slider',
11 years ago
/**
a classname for the content element.
@property contentClass
@type String
@default 'nano-content'
*/
contentClass: 'nano-content',
11 years ago
/**
a setting to enable native scrolling in iOS devices.
@property iOSNativeScrolling
@type Boolean
@default false
*/
11 years ago
iOSNativeScrolling: false,
11 years ago
/**
a setting to prevent the rest of the page being
scrolled when user scrolls the `.content` element.
@property preventPageScrolling
@type Boolean
@default false
*/
11 years ago
preventPageScrolling: false,
11 years ago
/**
a setting to disable binding to the resize event.
@property disableResize
@type Boolean
@default false
*/
11 years ago
disableResize: false,
11 years ago
/**
a setting to make the scrollbar always visible.
@property alwaysVisible
@type Boolean
@default false
*/
11 years ago
alwaysVisible: false,
11 years ago
/**
a default timeout for the `flash()` method.
@property flashDelay
@type Number
@default 1500
*/
11 years ago
flashDelay: 1500,
11 years ago
/**
a minimum height for the `.slider` element.
@property sliderMinHeight
@type Number
@default 20
*/
11 years ago
sliderMinHeight: 20,
11 years ago
/**
a maximum height for the `.slider` element.
@property sliderMaxHeight
@type Number
@default null
*/
11 years ago
sliderMaxHeight: null,
11 years ago
/**
an alternate document context.
@property documentContext
@type Document
@default null
*/
11 years ago
documentContext: null,
11 years ago
/**
an alternate window context.
@property windowContext
@type Window
@default null
*/
11 years ago
windowContext: null
};
11 years ago
/**
@property SCROLLBAR
@type String
@static
@final
@private
*/
11 years ago
SCROLLBAR = 'scrollbar';
11 years ago
/**
@property SCROLL
@type String
@static
@final
@private
*/
11 years ago
SCROLL = 'scroll';
11 years ago
/**
@property MOUSEDOWN
@type String
@final
@private
*/
11 years ago
MOUSEDOWN = 'mousedown';
11 years ago
/**
@property MOUSEMOVE
@type String
@static
@final
@private
*/
11 years ago
MOUSEMOVE = 'mousemove';
11 years ago
/**
@property MOUSEWHEEL
@type String
@final
@private
*/
11 years ago
MOUSEWHEEL = 'mousewheel';
11 years ago
/**
@property MOUSEUP
@type String
@static
@final
@private
*/
11 years ago
MOUSEUP = 'mouseup';
11 years ago
/**
@property RESIZE
@type String
@final
@private
*/
11 years ago
RESIZE = 'resize';
11 years ago
/**
@property DRAG
@type String
@static
@final
@private
*/
11 years ago
DRAG = 'drag';
11 years ago
/**
@property UP
@type String
@static
@final
@private
*/
11 years ago
UP = 'up';
11 years ago
/**
@property PANEDOWN
@type String
@static
@final
@private
*/
11 years ago
PANEDOWN = 'panedown';
11 years ago
/**
@property DOMSCROLL
@type String
@static
@final
@private
*/
11 years ago
DOMSCROLL = 'DOMMouseScroll';
11 years ago
/**
@property DOWN
@type String
@static
@final
@private
*/
11 years ago
DOWN = 'down';
11 years ago
/**
@property WHEEL
@type String
@static
@final
@private
*/
11 years ago
WHEEL = 'wheel';
11 years ago
/**
@property KEYDOWN
@type String
@static
@final
@private
*/
11 years ago
KEYDOWN = 'keydown';
11 years ago
/**
@property KEYUP
@type String
@static
@final
@private
*/
11 years ago
KEYUP = 'keyup';
11 years ago
/**
@property TOUCHMOVE
@type String
@static
@final
@private
*/
11 years ago
TOUCHMOVE = 'touchmove';
11 years ago
/**
@property BROWSER_IS_IE7
@type Boolean
@static
@final
@private
*/
11 years ago
BROWSER_IS_IE7 = window.navigator.appName === 'Microsoft Internet Explorer' && /msie 7./i.test(window.navigator.appVersion) && window.ActiveXObject;
11 years ago
/**
@property BROWSER_SCROLLBAR_WIDTH
@type Number
@static
@default null
@private
*/
11 years ago
BROWSER_SCROLLBAR_WIDTH = null;
rAF = window.requestAnimationFrame;
cAF = window.cancelAnimationFrame;
_elementStyle = document.createElement('div').style;
_vendor = (function() {
var i, transform, vendor, vendors, _i, _len;
vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'];
for (i = _i = 0, _len = vendors.length; _i < _len; i = ++_i) {
vendor = vendors[i];
transform = vendors[i] + 'ransform';
if (transform in _elementStyle) {
return vendors[i].substr(0, vendors[i].length - 1);
}
}
return false;
})();
_prefixStyle = function(style) {
if (_vendor === false) {
return false;
}
if (_vendor === '') {
return style;
}
return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
};
transform = _prefixStyle('transform');
hasTransform = transform !== false;
11 years ago
/**
Returns browser's native scrollbar width
@method getBrowserScrollbarWidth
@return {Number} the scrollbar width in pixels
@static
@private
*/
11 years ago
getBrowserScrollbarWidth = function() {
var outer, outerStyle, scrollbarWidth;
outer = document.createElement('div');
outerStyle = outer.style;
outerStyle.position = 'absolute';
outerStyle.width = '100px';
outerStyle.height = '100px';
outerStyle.overflow = SCROLL;
outerStyle.top = '-9999px';
document.body.appendChild(outer);
scrollbarWidth = outer.offsetWidth - outer.clientWidth;
document.body.removeChild(outer);
return scrollbarWidth;
};
isFFWithBuggyScrollbar = function() {
var isOSXFF, ua, version;
ua = window.navigator.userAgent;
isOSXFF = /(?=.+Mac OS X)(?=.+Firefox)/.test(ua);
if (!isOSXFF) {
return false;
}
version = /Firefox\/\d{2}\./.exec(ua);
if (version) {
version = version[0].replace(/\D+/g, '');
}
return isOSXFF && +version > 23;
};
11 years ago
/**
@class NanoScroll
@param element {HTMLElement|Node} the main element
@param options {Object} nanoScroller's options
@constructor
*/
11 years ago
NanoScroll = (function() {
function NanoScroll(el, options) {
this.el = el;
this.options = options;
BROWSER_SCROLLBAR_WIDTH || (BROWSER_SCROLLBAR_WIDTH = getBrowserScrollbarWidth());
this.$el = $(this.el);
this.doc = $(this.options.documentContext || document);
this.win = $(this.options.windowContext || window);
this.$content = this.$el.children("." + options.contentClass);
this.$content.attr('tabindex', this.options.tabIndex || 0);
this.content = this.$content[0];
this.previousPosition = 0;
if (this.options.iOSNativeScrolling && (this.el.style.WebkitOverflowScrolling != null || navigator.userAgent.match(/mobi.+Gecko/i))) {
11 years ago
this.nativeScrolling();
} else {
this.generate();
}
this.createEvents();
this.addEvents();
this.reset();
}
11 years ago
/**
Prevents the rest of the page being scrolled
when user scrolls the `.nano-content` element.
11 years ago
@method preventScrolling
@param event {Event}
@param direction {String} Scroll direction (up or down)
@private
*/
11 years ago
NanoScroll.prototype.preventScrolling = function(e, direction) {
if (!this.isActive) {
return;
}
if (e.type === DOMSCROLL) {
if (direction === DOWN && e.originalEvent.detail > 0 || direction === UP && e.originalEvent.detail < 0) {
e.preventDefault();
}
} else if (e.type === MOUSEWHEEL) {
if (!e.originalEvent || !e.originalEvent.wheelDelta) {
return;
}
if (direction === DOWN && e.originalEvent.wheelDelta < 0 || direction === UP && e.originalEvent.wheelDelta > 0) {
e.preventDefault();
}
}
};
11 years ago
/**
Enable iOS native scrolling
@method nativeScrolling
@private
*/
11 years ago
NanoScroll.prototype.nativeScrolling = function() {
this.$content.css({
WebkitOverflowScrolling: 'touch'
});
this.iOSNativeScrolling = true;
this.isActive = true;
};
11 years ago
/**
Updates those nanoScroller properties that
are related to current scrollbar position.
@method updateScrollValues
@private
*/
11 years ago
NanoScroll.prototype.updateScrollValues = function() {
var content, direction;
11 years ago
content = this.content;
this.maxScrollTop = content.scrollHeight - content.clientHeight;
this.prevScrollTop = this.contentScrollTop || 0;
this.contentScrollTop = content.scrollTop;
direction = this.contentScrollTop > this.previousPosition ? "down" : this.contentScrollTop < this.previousPosition ? "up" : "same";
this.previousPosition = this.contentScrollTop;
if (direction !== "same") {
this.$el.trigger('update', {
position: this.contentScrollTop,
maximum: this.maxScrollTop,
direction: direction
});
}
11 years ago
if (!this.iOSNativeScrolling) {
this.maxSliderTop = this.paneHeight - this.sliderHeight;
this.sliderTop = this.maxScrollTop === 0 ? 0 : this.contentScrollTop * this.maxSliderTop / this.maxScrollTop;
}
};
11 years ago
/**
Updates CSS styles for current scroll position.
Uses CSS 2d transfroms and `window.requestAnimationFrame` if available.
@method setOnScrollStyles
@private
*/
11 years ago
NanoScroll.prototype.setOnScrollStyles = function() {
var cssValue;
11 years ago
if (hasTransform) {
cssValue = {};
cssValue[transform] = "translate(0, " + this.sliderTop + "px)";
} else {
cssValue = {
top: this.sliderTop
};
}
if (rAF) {
if (!this.scrollRAF) {
this.scrollRAF = rAF((function(_this) {
return function() {
_this.scrollRAF = null;
_this.slider.css(cssValue);
};
})(this));
11 years ago
}
} else {
this.slider.css(cssValue);
}
};
11 years ago
/**
Creates event related methods
@method createEvents
@private
*/
11 years ago
NanoScroll.prototype.createEvents = function() {
this.events = {
down: (function(_this) {
return function(e) {
_this.isBeingDragged = true;
_this.offsetY = e.pageY - _this.slider.offset().top;
_this.pane.addClass('active');
_this.doc.bind(MOUSEMOVE, _this.events[DRAG]).bind(MOUSEUP, _this.events[UP]);
return false;
};
})(this),
drag: (function(_this) {
return function(e) {
_this.sliderY = e.pageY - _this.$el.offset().top - _this.offsetY;
_this.scroll();
if (_this.contentScrollTop >= _this.maxScrollTop && _this.prevScrollTop !== _this.maxScrollTop) {
11 years ago
_this.$el.trigger('scrollend');
} else if (_this.contentScrollTop === 0 && _this.prevScrollTop !== 0) {
_this.$el.trigger('scrolltop');
11 years ago
}
return false;
};
})(this),
up: (function(_this) {
return function(e) {
_this.isBeingDragged = false;
_this.pane.removeClass('active');
_this.doc.unbind(MOUSEMOVE, _this.events[DRAG]).unbind(MOUSEUP, _this.events[UP]);
return false;
};
})(this),
resize: (function(_this) {
return function(e) {
_this.reset();
};
})(this),
panedown: (function(_this) {
return function(e) {
_this.sliderY = (e.offsetY || e.originalEvent.layerY) - (_this.sliderHeight * 0.5);
_this.scroll();
_this.events.down(e);
return false;
};
})(this),
scroll: (function(_this) {
return function(e) {
_this.updateScrollValues();
if (_this.isBeingDragged) {
return;
11 years ago
}
if (!_this.iOSNativeScrolling) {
_this.sliderY = _this.sliderTop;
_this.setOnScrollStyles();
11 years ago
}
if (e == null) {
return;
}
if (_this.contentScrollTop >= _this.maxScrollTop) {
if (_this.options.preventPageScrolling) {
_this.preventScrolling(e, DOWN);
}
if (_this.prevScrollTop !== _this.maxScrollTop) {
_this.$el.trigger('scrollend');
}
} else if (_this.contentScrollTop === 0) {
if (_this.options.preventPageScrolling) {
_this.preventScrolling(e, UP);
}
if (_this.prevScrollTop !== 0) {
_this.$el.trigger('scrolltop');
}
}
};
})(this),
wheel: (function(_this) {
return function(e) {
var delta;
if (e == null) {
return;
}
delta = e.delta || e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail || (e.originalEvent && -e.originalEvent.detail);
if (delta) {
_this.sliderY += -delta / 3;
}
_this.scroll();
return false;
};
})(this)
11 years ago
};
};
11 years ago
/**
Adds event listeners with jQuery.
@method addEvents
@private
*/
11 years ago
NanoScroll.prototype.addEvents = function() {
var events;
this.removeEvents();
events = this.events;
if (!this.options.disableResize) {
this.win.bind(RESIZE, events[RESIZE]);
}
if (!this.iOSNativeScrolling) {
this.slider.bind(MOUSEDOWN, events[DOWN]);
this.pane.bind(MOUSEDOWN, events[PANEDOWN]).bind("" + MOUSEWHEEL + " " + DOMSCROLL, events[WHEEL]);
}
this.$content.bind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
};
11 years ago
/**
Removes event listeners with jQuery.
@method removeEvents
@private
*/
11 years ago
NanoScroll.prototype.removeEvents = function() {
var events;
events = this.events;
this.win.unbind(RESIZE, events[RESIZE]);
if (!this.iOSNativeScrolling) {
this.slider.unbind();
this.pane.unbind();
}
this.$content.unbind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
};
11 years ago
/**
Generates nanoScroller's scrollbar and elements for it.
@method generate
@chainable
@private
*/
11 years ago
NanoScroll.prototype.generate = function() {
var contentClass, cssRule, currentPadding, options, pane, paneClass, sliderClass;
11 years ago
options = this.options;
paneClass = options.paneClass, sliderClass = options.sliderClass, contentClass = options.contentClass;
if (!(pane = this.$el.children("." + paneClass)).length && !pane.children("." + sliderClass).length) {
11 years ago
this.$el.append("<div class=\"" + paneClass + "\"><div class=\"" + sliderClass + "\" /></div>");
}
this.pane = this.$el.children("." + paneClass);
this.slider = this.pane.find("." + sliderClass);
if (BROWSER_SCROLLBAR_WIDTH === 0 && isFFWithBuggyScrollbar()) {
currentPadding = window.getComputedStyle(this.content, null).getPropertyValue('padding-right').replace(/\D+/g, '');
cssRule = {
right: -14,
paddingRight: +currentPadding + 14
};
} else if (BROWSER_SCROLLBAR_WIDTH) {
cssRule = {
right: -BROWSER_SCROLLBAR_WIDTH
};
this.$el.addClass('has-scrollbar');
}
if (cssRule != null) {
this.$content.css(cssRule);
}
return this;
};
11 years ago
/**
@method restore
@private
*/
11 years ago
NanoScroll.prototype.restore = function() {
this.stopped = false;
if (!this.iOSNativeScrolling) {
this.pane.show();
}
this.addEvents();
};
11 years ago
/**
Resets nanoScroller's scrollbar.
@method reset
@chainable
@example
$(".nano").nanoScroller();
*/
11 years ago
NanoScroll.prototype.reset = function() {
var content, contentHeight, contentPosition, contentStyle, contentStyleOverflowY, paneBottom, paneHeight, paneOuterHeight, paneTop, parentMaxHeight, right, sliderHeight;
11 years ago
if (this.iOSNativeScrolling) {
this.contentHeight = this.content.scrollHeight;
return;
}
if (!this.$el.find("." + this.options.paneClass).length) {
this.generate().stop();
}
if (this.stopped) {
this.restore();
}
content = this.content;
contentStyle = content.style;
contentStyleOverflowY = contentStyle.overflowY;
if (BROWSER_IS_IE7) {
this.$content.css({
height: this.$content.height()
});
}
contentHeight = content.scrollHeight + BROWSER_SCROLLBAR_WIDTH;
parentMaxHeight = parseInt(this.$el.css("max-height"), 10);
if (parentMaxHeight > 0) {
this.$el.height("");
this.$el.height(content.scrollHeight > parentMaxHeight ? parentMaxHeight : content.scrollHeight);
}
paneHeight = this.pane.outerHeight(false);
paneTop = parseInt(this.pane.css('top'), 10);
paneBottom = parseInt(this.pane.css('bottom'), 10);
paneOuterHeight = paneHeight + paneTop + paneBottom;
sliderHeight = Math.round(paneOuterHeight / contentHeight * paneOuterHeight);
if (sliderHeight < this.options.sliderMinHeight) {
sliderHeight = this.options.sliderMinHeight;
} else if ((this.options.sliderMaxHeight != null) && sliderHeight > this.options.sliderMaxHeight) {
sliderHeight = this.options.sliderMaxHeight;
}
if (contentStyleOverflowY === SCROLL && contentStyle.overflowX !== SCROLL) {
sliderHeight += BROWSER_SCROLLBAR_WIDTH;
}
this.maxSliderTop = paneOuterHeight - sliderHeight;
this.contentHeight = contentHeight;
this.paneHeight = paneHeight;
this.paneOuterHeight = paneOuterHeight;
this.sliderHeight = sliderHeight;
this.slider.height(sliderHeight);
this.events.scroll();
this.pane.show();
this.isActive = true;
if ((content.scrollHeight === content.clientHeight) || (this.pane.outerHeight(true) >= content.scrollHeight && contentStyleOverflowY !== SCROLL)) {
this.pane.hide();
this.isActive = false;
} else if (this.el.clientHeight === content.scrollHeight && contentStyleOverflowY === SCROLL) {
this.slider.hide();
} else {
this.slider.show();
}
this.pane.css({
opacity: (this.options.alwaysVisible ? 1 : ''),
visibility: (this.options.alwaysVisible ? 'visible' : '')
});
contentPosition = this.$content.css('position');
if (contentPosition === 'static' || contentPosition === 'relative') {
right = parseInt(this.$content.css('right'), 10);
if (right) {
this.$content.css({
right: '',
marginRight: right
});
}
}
11 years ago
return this;
};
11 years ago
/**
@method scroll
@private
@example
$(".nano").nanoScroller({ scroll: 'top' });
*/
11 years ago
NanoScroll.prototype.scroll = function() {
if (!this.isActive) {
return;
}
this.sliderY = Math.max(0, this.sliderY);
this.sliderY = Math.min(this.maxSliderTop, this.sliderY);
this.$content.scrollTop((this.paneHeight - this.contentHeight + BROWSER_SCROLLBAR_WIDTH) * this.sliderY / this.maxSliderTop * -1);
if (!this.iOSNativeScrolling) {
this.updateScrollValues();
this.setOnScrollStyles();
}
return this;
};
11 years ago
/**
Scroll at the bottom with an offset value
@method scrollBottom
@param offsetY {Number}
@chainable
@example
$(".nano").nanoScroller({ scrollBottom: value });
*/
11 years ago
NanoScroll.prototype.scrollBottom = function(offsetY) {
if (!this.isActive) {
return;
}
this.$content.scrollTop(this.contentHeight - this.$content.height() - offsetY).trigger(MOUSEWHEEL);
this.stop().restore();
11 years ago
return this;
};
11 years ago
/**
Scroll at the top with an offset value
@method scrollTop
@param offsetY {Number}
@chainable
@example
$(".nano").nanoScroller({ scrollTop: value });
*/
11 years ago
NanoScroll.prototype.scrollTop = function(offsetY) {
if (!this.isActive) {
return;
}
this.$content.scrollTop(+offsetY).trigger(MOUSEWHEEL);
this.stop().restore();
11 years ago
return this;
};
11 years ago
/**
Scroll to an element
@method scrollTo
@param node {Node} A node to scroll to.
@chainable
@example
$(".nano").nanoScroller({ scrollTo: $('#a_node') });
*/
11 years ago
NanoScroll.prototype.scrollTo = function(node) {
if (!this.isActive) {
return;
}
this.scrollTop(this.$el.find(node).get(0).offsetTop);
11 years ago
return this;
};
11 years ago
/**
To stop the operation.
This option will tell the plugin to disable all event bindings and hide the gadget scrollbar from the UI.
@method stop
@chainable
@example
$(".nano").nanoScroller({ stop: true });
*/
11 years ago
NanoScroll.prototype.stop = function() {
if (cAF && this.scrollRAF) {
11 years ago
cAF(this.scrollRAF);
this.scrollRAF = null;
11 years ago
}
this.stopped = true;
this.removeEvents();
if (!this.iOSNativeScrolling) {
this.pane.hide();
}
return this;
};
11 years ago
/**
Destroys nanoScroller and restores browser's native scrollbar.
@method destroy
@chainable
@example
$(".nano").nanoScroller({ destroy: true });
*/
11 years ago
NanoScroll.prototype.destroy = function() {
if (!this.stopped) {
this.stop();
}
if (!this.iOSNativeScrolling && this.pane.length) {
this.pane.remove();
}
if (BROWSER_IS_IE7) {
this.$content.height('');
}
this.$content.removeAttr('tabindex');
if (this.$el.hasClass('has-scrollbar')) {
this.$el.removeClass('has-scrollbar');
this.$content.css({
right: ''
});
}
return this;
};
11 years ago
/**
To flash the scrollbar gadget for an amount of time defined in plugin settings (defaults to 1,5s).
Useful if you want to show the user (e.g. on pageload) that there is more content waiting for him.
@method flash
@chainable
@example
$(".nano").nanoScroller({ flash: true });
*/
11 years ago
NanoScroll.prototype.flash = function() {
if (this.iOSNativeScrolling) {
return;
}
if (!this.isActive) {
return;
}
this.reset();
this.pane.addClass('flashed');
setTimeout((function(_this) {
return function() {
_this.pane.removeClass('flashed');
};
})(this), this.options.flashDelay);
11 years ago
return this;
};
return NanoScroll;
})();
$.fn.nanoScroller = function(settings) {
return this.each(function() {
var options, scrollbar;
if (!(scrollbar = this.nanoscroller)) {
options = $.extend({}, defaults, settings);
this.nanoscroller = scrollbar = new NanoScroll(this, options);
}
if (settings && typeof settings === "object") {
$.extend(scrollbar.options, settings);
if (settings.scrollBottom != null) {
return scrollbar.scrollBottom(settings.scrollBottom);
}
if (settings.scrollTop != null) {
return scrollbar.scrollTop(settings.scrollTop);
}
if (settings.scrollTo) {
return scrollbar.scrollTo(settings.scrollTo);
}
if (settings.scroll === 'bottom') {
return scrollbar.scrollBottom(0);
}
if (settings.scroll === 'top') {
return scrollbar.scrollTop(0);
}
if (settings.scroll && settings.scroll instanceof $) {
return scrollbar.scrollTo(settings.scroll);
}
if (settings.stop) {
return scrollbar.stop();
}
if (settings.destroy) {
return scrollbar.destroy();
}
if (settings.flash) {
return scrollbar.flash();
}
}
return scrollbar.reset();
});
};
$.fn.nanoScroller.Constructor = NanoScroll;
})(jQuery, window, document);