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 var _ = this; 266 setTimeout(function(){ 267 if (!_.shown && _.update()) { 268 _.el.addClass('antiscroll-scrollbar-shown'); 269 if (_.hiding) { 270 clearTimeout(_.hiding); 271 _.hiding = null; 272 } 273 _.shown = true; 274 } 275 }, 100); 276 }; 277 278 /** 279 * Hide scrollbar. 280 * 281 * @api private 282 */ 283 284 Scrollbar.prototype.hide = function () { 285 var _ = this; 286 setTimeout(function(){ 287 if (_.pane.autoHide !== false && _.shown) { 288 // check for dragging 289 _.el.removeClass('antiscroll-scrollbar-shown'); 290 _.shown = false; 291 } 292 }, 100); 293 }; 294 295 /** 296 * Horizontal scrollbar constructor 297 * 298 * @api private 299 */ 300 301 Scrollbar.Horizontal = function (pane) { 302 this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-horizontal"/>', pane.el); 303 Scrollbar.call(this, pane); 304 }; 305 306 /** 307 * Inherits from Scrollbar. 308 */ 309 310 inherits(Scrollbar.Horizontal, Scrollbar); 311 312 /** 313 * Updates size/position of scrollbar. 314 * 315 * @api private 316 */ 317 318 Scrollbar.Horizontal.prototype.update = function () { 319 var paneWidth = this.pane.el.width(), 320 trackWidth = paneWidth - this.pane.padding * 2, 321 innerEl = this.pane.inner.get(0) 322 323 this.el 324 .css('width', trackWidth * paneWidth / innerEl.scrollWidth) 325 .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth); 326 327 return paneWidth < innerEl.scrollWidth; 328 }; 329 330 /** 331 * Called upon drag. 332 * 333 * @api private 334 */ 335 336 Scrollbar.Horizontal.prototype.mousemove = function (ev) { 337 var trackWidth = this.pane.el.width() - this.pane.padding * 2, 338 pos = ev.pageX - this.startPageX, 339 barWidth = this.el.width(), 340 innerEl = this.pane.inner.get(0) 341 342 // minimum top is 0, maximum is the track height 343 var y = Math.min(Math.max(pos, 0), trackWidth - barWidth); 344 345 innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width()) 346 * y / (trackWidth - barWidth); 347 }; 348 349 /** 350 * Called upon container mousewheel. 351 * 352 * @api private 353 */ 354 355 Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) { 356 if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) || 357 (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width()) 358 == this.innerEl.scrollWidth))) { 359 ev.preventDefault(); 360 return false; 361 } 362 }; 363 364 /** 365 * Vertical scrollbar constructor 366 * 367 * @api private 368 */ 369 370 Scrollbar.Vertical = function (pane) { 371 this.el = $('<div class="antiscroll-scrollbar antiscroll-scrollbar-vertical"/>', pane.el); 372 Scrollbar.call(this, pane); 373 }; 374 375 /** 376 * Inherits from Scrollbar. 377 */ 378 379 inherits(Scrollbar.Vertical, Scrollbar); 380 381 /** 382 * Updates size/position of scrollbar. 383 * 384 * @api private 385 */ 386 387 Scrollbar.Vertical.prototype.update = function () { 388 var paneHeight = this.pane.el.height(), 389 trackHeight = paneHeight - this.pane.padding * 2, 390 innerEl = this.innerEl; 391 392 var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight; 393 scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight; 394 395 var topPos = trackHeight * innerEl.scrollTop / innerEl.scrollHeight; 396 397 if((topPos + scrollbarHeight) > trackHeight) { 398 var diff = (topPos + scrollbarHeight) - trackHeight; 399 topPos = topPos - diff - 3; 400 } 401 402 this.el 403 .css('height', scrollbarHeight) 404 .css('top', topPos); 405 406 return paneHeight < innerEl.scrollHeight; 407 }; 408 409 /** 410 * Called upon drag. 411 * 412 * @api private 413 */ 414 415 Scrollbar.Vertical.prototype.mousemove = function (ev) { 416 var paneHeight = this.pane.el.height(), 417 trackHeight = paneHeight - this.pane.padding * 2, 418 pos = ev.pageY - this.startPageY, 419 barHeight = this.el.height(), 420 innerEl = this.innerEl 421 422 // minimum top is 0, maximum is the track height 423 var y = Math.min(Math.max(pos, 0), trackHeight - barHeight); 424 425 innerEl.scrollTop = (innerEl.scrollHeight - paneHeight) 426 * y / (trackHeight - barHeight); 427 }; 428 429 /**
430 * Called upon container mousewheel. 431 * 432 * @api private 433 */ 434 435 Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) { 436 if ((y > 0 && 0 == this.innerEl.scrollTop) || 437 (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height()) 438 == this.innerEl.scrollHeight))) { 439 ev.preventDefault(); 440 return false; 441 } 442 }; 443 444 /** 445 * Cross-browser inheritance. 446 * 447 * @param {Function} constructor 448 * @param {Function} constructor we inherit from 449 * @api private 450 */ 451 452 function inherits (ctorA, ctorB) { 453 function f() {}; 454 f.prototype = ctorB.prototype; 455 ctorA.prototype = new f; 456 }; 457 458 /** 459 * Scrollbar size detection. 460 */ 461 462 var size; 463 464 function scrollbarSize () { 465 if (size === undefined) { 466 var div = $( 467 '<div class="antiscroll-inner" style="width:50px;height:50px;overflow-y:scroll;' 468 + 'position:absolute;top:-200px;left:-200px;"><div style="height:100px;width:100%"/>' 469 + '</div>' 470 ); 471 472 $('body').append(div); 473 var w1 = $(div).innerWidth(); 474 var w2 = $('div', div).innerWidth(); 475 $(div).remove(); 476 477 size = w1 - w2; 478 } 479 480 return size; 481 }; 482 483})(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.