1/** 2 * jquery.scrollbox.js 3 * 4 * @version 0.2.1 5 * @author Invis1ble 6 * @copyright (c) 2013 Invis1ble <[email protected]> 7 * @license MIT http://www.opensource.org/licenses/mit-license.php 8 */ 9!function ($, w, d, undef) { 10 11 'use strict'; 12 13 var name = 'scrollbox', 14 Scrollbox = function ($element, options) { 15 var o = this.options = $.extend({}, $.fn[name].defaults, options); 16 17 this.$element = $element; 18 this.$rail = $(o.templates.rail); 19 this.$bar = $(o.templates.bar); 20 21 this.init = o.init || this.init; 22 this.addListeners = o.addListeners || this.addListeners; 23 this.removeListeners = o.removeListeners || this.removeListeners; 24 this.scroll = o.scroll || this.scroll; 25 this.jump = o.jump || this.jump; 26 this.update = o.update || this.update; 27 this.destroy = o.destroy || this.destroy; 28 29 this._isOver = this._isCaptured = this._isShown = false; 30 this._prevY = this._scrolledTo = 0; 31 this._isReachTriggered = {top: false, bottom: false}; 32 this._scrollHeight = undef; 33 34 this._listeners = {}; 35 36 this.init(); 37 }; 38 39 Scrollbox.prototype = { 40 41 constructor: Scrollbox, 42 43 init: function () { 44 var options = this.options; 45 46 this.$wrapper = this.$element
47 .trigger('init.' + name) 48 .css('overflow', 'hidden') 49 .wrap(options.templates.wrapper).parent() 50 .append(this.$rail) 51 .append(this.$bar); 52 53 this._updateBarHeight(); 54 options.start != 'top' && this.jump(options.start); 55 this._isShown && this.addListeners(); 56 }, 57 58 addListeners: function () { 59 this._listeners.wheel = $.proxy(this._wheel, this); 60 61 this.$wrapper.on({ 62 mouseenter: $.proxy(this._enter, this), 63 mouseleave: $.proxy(this._leave, this) 64 }); 65 66 if (w.addEventListener) { 67 w.addEventListener('DOMMouseScroll', this._listeners.wheel, false); 68 w.addEventListener('mousewheel', this._listeners.wheel, false); 69 } 70 else 71 d.attachEvent('onmousewheel', this._listeners.wheel); 72 73 this.$bar.on('mousedown', $.proxy(this._capture, this)); 74 75 $(d).on({ 76 mouseup: $.proxy(this._release, this), 77 mousemove: $.proxy(this._move, this) 78 }); 79 }, 80 81 removeListeners: function () { 82 this.$wrapper.off({ 83 mouseenter: this._enter, 84 mouseleave: this._leave 85 }); 86 87 if (w.removeEventListener) { 88 w.removeEventListener('DOMMouseScroll', this._listeners.wheel, false); 89 w.removeEventListener('mousewheel', this._listeners.wheel, false); 90 } 91 else 92 d.detachEvent('onmousewheel', this._listeners.wheel); 93 94 this.$bar.off('mousedown', this._capture); 95 96 $(d).off({ 97 mouseup: this._release, 98 mousemove: this._move 99 }); 100 }, 101 102 _enter: function (e) { 103 var event = $.Event('enter.' + name); 104 105 this.$element.trigger(event); 106 107 if (!event.isDefaultPrevented()) { 108 e.preventDefault(); 109 this._isOver = true; 110 } 111 }, 112 113 _leave: function (e) { 114 var event = $.Event('leave.' + name); 115 116 this.$element.trigger(event); 117 118 if (!event.isDefaultPrevented()) { 119 e.preventDefault(); 120 this._isOver = false; 121 } 122 }, 123 124 _wheel: function (e) { 125 if (!this._isOver) return; 126 var e = e || w.event, 127 event = $.Event('wheel.' + name); 128 129 this.$element.trigger(event); 130 131 if (!event.isDefaultPrevented()) { 132 if (e.preventDefault) 133 e.preventDefault(); 134 else 135 e.returnValue = false; 136 137 this.scroll((e.detail ? e.detail / 3 : - e.wheelDelta / 120) * this.options.sensitivity); 138 } 139 }, 140 141 _capture: function (e) { 142 if (e.which == 1) { 143 e.preventDefault(); 144 this.$element.trigger('dragstart.' + name); 145 this._isCaptured = true; 146 this._prevY = e.pageY; 147 } 148 }, 149 150 _move: function (e) { 151 if (!this._isCaptured) return; 152 this.$element.trigger('drag.' + name); 153 e.preventDefault(); 154 this.scroll((e.pageY - this._prevY) / this._getRatio()); 155 this._prevY = e.pageY; 156 }, 157 158 _release: function (e) { 159 if (!this._isCaptured || e.which != 1) return; 160 this.$element.trigger('dragstop.' + name); 161 e.preventDefault(); 162 this._isCaptured = false; 163 }, 164 165 scroll: function (delta) { 166 var max = this._getScrollHeight() - this.$element.outerHeight(), 167 scrollTo = this._scrolledTo + delta, 168 options = this.options, 169 position; 170 171 this.$element.trigger('scroll.' + name); 172 173 if (scrollTo >= max) 174 this._scrolledTo = max; 175 else if (scrollTo <= 0) 176 this._scrolledTo = 0; 177 else 178 this._scrolledTo = scrollTo; 179 180 this.$element.scrollTop(this._scrolledTo); 181 this._updateBarPosition(); 182 183 if (!this._isReachTriggered.bottom && this._scrolledTo + options.buffer >= max) { 184 position = 'bottom'; 185 } 186 else if (!this._isReachTriggered.top && this._scrolledTo - options.buffer <= 0) { 187 position = 'top'; 188 } 189 190 if (position) { 191 this.$element.trigger($.Event('reach.' + name, {position: position})); 192 this._isReachTriggered[position] = true; 193 } 194 }, 195 196 jump: function (y) { 197 if (y === 'top') 198 y = 0; 199 else if (y === 'bottom') 200 y = this._getScrollHeight() - this.$element.height(); 201 202 this.scroll(y - this._scrolledTo); 203 }, 204 205 update: function () { 206 var isShown = this._isShown; 207 208 this._scrollHeight = undef; 209 this._isReachTriggered.top = this._isReachTriggered.bottom = false;
210 this._updateBarHeight(); 211 212 if (this._isShown) { 213 this._updateBarPosition(); 214 isShown || this.addListeners(); 215 } 216 }, 217 218 _getRatio: function () { 219 return this.$element.outerHeight() / this._getScrollHeight(); 220 }, 221 222 _getScrollHeight: function () { 223 // opera bug workaround 224 if (this._scrollHeight === undef) 225 this._scrollHeight = this.$element[0].scrollHeight; 226 227 return this._scrollHeight; 228 }, 229 230 _updateBarHeight: function () { 231 var ratio = this._getRatio(); 232 233 if (ratio != 1) { 234 this.$bar.height(this.$element.outerHeight() * ratio); 235 236 if (!this._isShown) { 237 this.$bar.addClass(name + '-bar-in'); 238 this.$rail.addClass(name + '-rail-in'); 239 this._isShown = true; 240 } 241 } 242 else if (this._isShown) { 243 this.removeListeners(); 244 this.$bar.removeClass(name + '-bar-in'); 245 this.$rail.removeClass(name + '-rail-in'); 246 this._isShown = false; 247 } 248 }, 249 250 _updateBarPosition: function () { 251 var h = this.$element.outerHeight(); 252 this.$bar.css('top', (h - this.$bar.outerHeight()) * (this._scrolledTo / (this._getScrollHeight() - h))); 253 }, 254 255 destroy: function () { 256 this.$wrapper 257 .off('.' + name) 258 .find('*').off('.' + name); 259 260 this.removeListeners(); 261 262 this.$element 263 .unwrap() 264 .removeData(name); 265 266 this.$rail.remove(); 267 this.$bar.remove(); 268 269 this.$bar = this.$rail = this.$element = this.$wrapper = null; 270 } 271 272 }; 273 274 $.fn[name] = function (option) { 275 var args = Array.prototype.slice.call(arguments, 1); 276 return this.each(function () { 277 var $this = $(this), 278 data = $this.data(name), 279 options = typeof option == 'object' && option; 280 281 if (!data) $this.data(name, (data = new Scrollbox($this, options))); 282 typeof option == 'string' && data[option].apply(data, args); 283 }); 284 }; 285 286 $.fn[name].constructor = Scrollbox; 287 288 $.fn[name].defaults = { 289 buffer: 0, 290 sensitivity: 20, 291 start: 'top', 292 templates: { 293 bar: '<div class="' + name + '-bar"></div>', 294 rail: '<div class="' + name + '-rail"></div>', 295 wrapper: '<div class="' + name + '-wrapper"></div>' 296 } 297 }; 298 299}(jQuery, window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.