1/* 2 * Antiscroll: cross-browser native OS X Lion scrollbars 3 * https://github.com/Automattic/antiscroll 4 * v0.9 5 */ 6 7(function ($) { 8 9 /** 10 * Augment jQuery prototype. 11 */ 12 13 $.fn.antiscroll = function (options) { 14 return this.each(function () { 15 if ($(this).data('antiscroll')) { 16 $(this).data('antiscroll').destroy(); 17 } 18 19 $(this).data('antiscroll', new $.Antiscroll(this, options)); 20 }); 21 }; 22 23 /** 24 * Expose constructor. 25 */ 26 27 $.Antiscroll = Antiscroll; 28 29 /** 30 * Antiscroll pane constructor. 31 * 32 * @param {Element|jQuery} main pane 33 * @parma {Object} options 34 * @api public 35 */ 36 37 function Antiscroll (el, opts) { 38 this.el = $(el); 39 this.options = opts || {}; 40 41 this.x = (false !== this.options.x) || this.options.forceHorizontal; 42 this.y = (false !== this.options.y) || this.options.forceVertical; 43 this.autoHide = false !== this.options.autoHide; 44 this.padding = undefined == this.options.padding ? 2 : this.options.padding; 45 46 this.inner = this.el.find('.antiscroll-inner'); 47 this.inner.css({ 48 'width': '+=' + (this.y ? scrollbarSize() : 0) 49 , 'height': '+=' + (this.x ? scrollbarSize() : 0) 50 }); 51 52 this.refresh(); 53 }; 54 55 /** 56 * refresh scrollbars 57 * 58 * @api public 59 */ 60 61 Antiscroll.prototype.refresh = function() { 62 var needHScroll = this.inner.get(0).scrollWidth > this.el.width() + (this.y ? scrollbarSize() : 0), 63 needVScroll = this.inner.get(0).scrollHeight > this.el.height() + (this.x ? scrollbarSize() : 0); 64 65 if (this.x) { 66 if (!this.horizontal && needHScroll) { 67 this.horizontal = new Scrollbar.Horizontal(this); 68 } else if (this.horizontal && !needHScroll) { 69 this.horizontal.destroy(); 70 this.horizontal = null; 71 } else if (this.horizontal) { 72 this.horizontal.update(); 73 } 74 } 75 76 if (this.y) { 77 if (!this.vertical && needVScroll) { 78 this.vertical = new Scrollbar.Vertical(this); 79 } else if (this.vertical && !needVScroll) { 80 this.vertical.destroy(); 81 this.vertical = null; 82 } else if (this.vertical) { 83 this.vertical.update(); 84 } 85 } 86 }; 87 88 /** 89 * Cleans up. 90 * 91 * @return {Antiscroll} for chaining 92 * @api public 93 */ 94 95 Antiscroll.prototype.destroy = function () { 96 if (this.horizontal) { 97 this.horizontal.destroy(); 98 this.horizontal = null 99 } 100 if (this.vertical) { 101 this.vertical.destroy(); 102 this.vertical = null 103 } 104 return this; 105 }; 106 107 /** 108 * Rebuild Antiscroll. 109 * 110 * @return {Antiscroll} for chaining 111 * @api public 112 */ 113 114 Antiscroll.prototype.rebuild = function () { 115 this.destroy(); 116 this.inner.attr('style', ''); 117 Antiscroll.call(this, this.el, this.options); 118 return this; 119 }; 120 121 /** 122 * Scrollbar constructor. 123 * 124 * @param {Element|jQuery} element 125 * @api public 126 */ 127 128 function Scrollbar (pane) { 129 this.pane = pane; 130 this.pane.el.append(this.el); 131 this.innerEl = this.pane.inner.get(0); 132 133 this.dragging = false; 134 this.enter = false; 135 this.shown = false; 136 137 // hovering 138 this.pane.el.mouseenter($.proxy(this, 'mouseenter')); 139 this.pane.el.mouseleave($.proxy(this, 'mouseleave')); 140 141 // dragging 142 this.el.mousedown($.proxy(this, 'mousedown')); 143 144 // scrolling 145 this.innerPaneScrollListener = $.proxy(this, 'scroll'); 146 this.pane.inner.scroll(this.innerPaneScrollListener); 147 148 // wheel -optional- 149 this.innerPaneMouseWheelListener = $.proxy(this, 'mousewheel'); 150 this.pane.inner.bind('mousewheel', this.innerPaneMouseWheelListener); 151 152 // show 153 var initialDisplay = this.pane.options.initialDisplay; 154 155 if (initialDisplay !== false) { 156 this.show(); 157 if (this.pane.autoHide) { 158 this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000); 159 } 160 } 161 }; 162 163 /** 164 * Cleans up. 165 * 166 * @return {Scrollbar} for chaining 167 * @api public 168 */ 169 170 Scrollbar.prototype.destroy = function () { 171 this.el.remove(); 172 this.pane.inner.unbind('scroll', this.innerPaneScrollListener); 173 this.pane.inner.unbind('mousewheel', this.innerPaneMouseWheelListener); 174 return this; 175 }; 176 177 /** 178 * Called upon mouseenter. 179 * 180 * @api private 181 */ 182 183 Scrollbar.prototype.mouseenter = function () { 184 this.enter = true; 185 this.show(); 186 }; 187 188 /** 189 * Called upon mouseleave. 190 * 191 * @api private 192 */ 193 194 Scrollbar.prototype.mouseleave = function () { 195 this.enter = false;
196 197 if (!this.dragging) { 198 if (this.pane.autoHide) { 199 this.hide(); 200 } 201 } 202 }; 203 204 /** 205 * Called upon wrap scroll. 206 * 207 * @api private 208 */ 209 210 Scrollbar.prototype.scroll = function () { 211 if (!this.shown) { 212 this.show(); 213 if (!this.enter && !this.dragging) { 214 if (this.pane.autoHide) { 215 this.hiding = setTimeout($.proxy(this, 'hide'), 1500); 216 } 217 } 218 } 219 220 this.update(); 221 }; 222 223 /** 224 * Called upon scrollbar mousedown. 225 * 226 * @api private 227 */ 228 229 Scrollbar.prototype.mousedown = function (ev) { 230 ev.preventDefault(); 231 232 this.dragging = true; 233 234 this.startPageY = ev.pageY - parseInt(this.el.css('top'), 10); 235 this.startPageX = ev.pageX - parseInt(this.el.css('left'), 10); 236 237 // prevent crazy selections on IE 238 this.el[0].ownerDocument.onselectstart = function () { return false; }; 239 240 var pane = this.pane, 241 move = $.proxy(this, 'mousemove'), 242 self = this 243 244 $(this.el[0].ownerDocument) 245 .mousemove(move) 246 .mouseup(function () { 247 self.dragging = false; 248 this.onselectstart = null; 249 250 $(this).unbind('mousemove', move); 251 252 if (!self.enter) { 253 self.hide(); 254 } 255 }); 256 }; 257 258 /** 259 * Show scrollbar. 260 * 261 * @api private 262 */ 263 264 Scrollbar.prototype.show = function (duration) { 265 if (!this.shown && this.update()) { 266 this.el.addClass('antiscroll-scrollbar-shown'); 267 if (this.hiding) { 268 clearTimeout(this.hiding); 269 this.hiding = null; 270 } 271 this.shown = true; 272 } 273 }; 274 275 /** 276 * Hide scrollbar. 277 * 278 * @api private 279 */ 280 281 Scrollbar.prototype.hide = function () { 282 if (this.pane.autoHide !== false && this.shown) { 283 // check for dragging 284 this.el.removeClass('antiscroll-scrollbar-shown'); 285 this.shown = false; 286 } 287 }; 288 289 /** 290 * Horizontal scrollbar constructor 291 * 292 * @api private 293 */ 294 295 Scrollbar.Horizontal = function (pane) { 296 this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-horizontal"/>', pane.el); 297 Scrollbar.call(this, pane); 298 }; 299 300 /** 301 * Inherits from Scrollbar. 302 */ 303 304 inherits(Scrollbar.Horizontal, Scrollbar); 305 306 /** 307 * Updates size/position of scrollbar. 308 * 309 * @api private 310 */ 311 312 Scrollbar.Horizontal.prototype.update = function () { 313 var paneWidth = this.pane.el.width(), 314 trackWidth = paneWidth - this.pane.padding * 2, 315 innerEl = this.pane.inner.get(0) 316 317 this.el 318 .css('width', trackWidth * paneWidth / innerEl.scrollWidth) 319 .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth); 320 321 return paneWidth < innerEl.scrollWidth; 322 }; 323 324 /** 325 * Called upon drag. 326 * 327 * @api private 328 */ 329 330 Scrollbar.Horizontal.prototype.mousemove = function (ev) { 331 var trackWidth = this.pane.el.width() - this.pane.padding * 2, 332 pos = ev.pageX - this.startPageX, 333 barWidth = this.el.width(), 334 innerEl = this.pane.inner.get(0) 335 336 // minimum top is 0, maximum is the track height 337 var y = Math.min(Math.max(pos, 0), trackWidth - barWidth); 338 339 innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) 340 * y / (trackWidth - barWidth); 341 }; 342 343 /** 344 * Called upon container mousewheel. 345 * 346 * @api private 347 */ 348 349 Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { 350 if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || 351 (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) 352 == this.innerEl.scrollWidth))) { 353 ev.preventDefault(); 354 return false; 355 } 356 }; 357 358 /** 359 * Vertical scrollbar constructor 360 * 361 * @api private 362 */ 363 364 Scrollbar.Vertical = function (pane) { 365 this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-vertical"/>', pane.el); 366 Scrollbar.call(this, pane); 367 }; 368 369 /** 370 * Inherits from Scrollbar. 371 */ 372 373 inherits(Scrollbar.Vertical, Scrollbar); 374 375 /** 376 * Updates size/position of scrollbar. 377 * 378 * @api private 379 */ 380 381 Scrollbar.Vertical.prototype.update = function () { 382 var paneHeight = this.pane.el.height(), 383 trackHeight = paneHeight - this.pane.padding * 2, 384 innerEl = this.innerEl; 385 386 var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight; 387 scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight; 388 389 var topPos = trackHeight * innerEl.scrollTop / innerEl.scrollHeight; 390 391 if((topPos + scrollbarHeight) > trackHeight) { 392 var diff = (topPos + scrollbarHeight) - trackHeight; 393 topPos = topPos - diff - 3; 394 } 395 396 this.el 397 .css('height', scrollbarHeight) 398 .css('top', topPos); 399 400 return paneHeight < innerEl.scrollHeight; 401 }; 402 403 /** 404 * Called upon drag. 405 * 406 * @api private 407 */ 408 409 Scrollbar.Vertical.prototype.mousemove = function (ev) { 410 var paneHeight = this.pane.el.height(), 411 trackHeight = paneHeight - this.pane.padding * 2, 412 pos = ev.pageY - this.startPageY, 413 barHeight = this.el.height(), 414 innerEl = this.innerEl 415 416 // minimum top is 0, maximum is the track height 417 var y = Math.min(Math.max(pos, 0), trackHeight - barHeight); 418 419 innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) 420 * y / (trackHeight - barHeight); 421 }; 422 423 /**
424 * Called upon container mousewheel. 425 * 426 * @api private 427 */ 428 429 Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { 430 if ((y > 0 && 0 == this.innerEl.scrollTop) || 431 (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) 432 == this.innerEl.scrollHeight))) { 433 ev.preventDefault(); 434 return false; 435 } 436 }; 437 438 /** 439 * Cross-browser inheritance. 440 * 441 * @param {Function} constructor 442 * @param {Function} constructor we inherit from 443 * @api private 444 */ 445 446 function inherits (ctorA, ctorB) { 447 function f() {}; 448 f.prototype = ctorB.prototype; 449 ctorA.prototype = new f; 450 }; 451 452 /** 453 * Scrollbar size detection. 454 */ 455 456 var size; 457 458 function scrollbarSize () { 459 if (size === undefined) { 460 var div = $( 461 '<div class="antiscroll-inner" style="width:50px;height:50px;overflow-y:scroll;' 462 + 'position:absolute;top:-200px;left:-200px;"><div style="height:100px;width:100%"/>' 463 + '</div>' 464 ); 465 466 $('body').append(div); 467 var w1 = $(div).innerWidth(); 468 var w2 = $('div', div).innerWidth(); 469 $(div).remove(); 470 471 size = w1 - w2; 472 } 473 474 return size; 475 }; 476 477})(jQuery);
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.