Compare commits

...

6 Commits
0.3.3 ... 0.3.4

Author SHA1 Message Date
Hyunje Alex Jun
c6c087275c Release 0.3.4.
Patch notes
1. Fix the bug that scrollbars don't stop at the end.
2. Code refactoring.
2013-05-18 12:43:38 +09:00
Hyunje Alex Jun
4613c3daee Code refactoring. 2013-05-18 12:41:15 +09:00
Hyunje Alex Jun
667c61d2c3 Add jshint files. 2013-05-18 12:39:26 +09:00
Hyunje Alex Jun
4012ce9c8c Prevent scrollbar x from moving out of the container. 2013-05-18 12:21:20 +09:00
Hyunje Alex Jun
9734a18d01 Merge pull request #19 from EtnasSoft/master
Fixed bug with the Mouse Wheel and the right scroll bar.
2013-05-17 20:18:19 -07:00
etnassoft
4f49b0fa7f Fix bug with mouse wheel 2013-03-21 11:21:20 +01:00
6 changed files with 388 additions and 308 deletions

3
.jshintignore Normal file
View File

@@ -0,0 +1,3 @@
examples
min
src/jquery.mousewheel.js

66
.jshintrc Normal file
View File

@@ -0,0 +1,66 @@
{
// Settings
"passfail" : false, // Stop on first error.
"maxerr" : 100, // Maximum error before stopping.
// Predefined globals whom JSHint will ignore.
"browser" : true, // Standard browser globals e.g. `window`, `document`.
"node" : true,
"rhino" : false,
"couch" : false,
"wsh" : false, // Windows Scripting Host.
"jquery" : true,
"prototypejs" : false,
"mootools" : false,
"dojo" : false,
"predef" : [ // Custom globals.
"require",
"define"
],
// Development.
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
"devel" : true, // Allow developments statements e.g. `console.log();`.
// ECMAScript 5.
"es5" : true, // Allow ECMAScript 5 syntax.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').
// The Good Parts.
"asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
"laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : true, // Require {} for every new block or scope.
"eqeqeq" : true, // Require triple equals i.e. `===`.
"eqnull" : false, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : true, // Prohipit variable use before definition.
"loopfunc" : false, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
"scripturl" : true, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.
// Personal styling preferences.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : false, // Prohibit use of empty blocks.
"nonew" : true, // Prohibit use of constructors for side-effects.
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"white" : true, // Check against strict whitespace and indentation rules.
"indent" : 2 // Specify indentation spacing
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
"name": "perfect-scrollbar",
"title": "perfect-scrollbar",
"description": "Tiny but perfect jquery scrollbar plugin.",
"version": "0.3.3",
"version": "0.3.4",
"author": {
"name": "HyeonJe Jun",
"email": "noraesae@yuiazu.net",

View File

@@ -1,313 +1,324 @@
/* Copyright (c) 2012 HyeonJe Jun (http://github.com/noraesae)
* Licensed under the MIT License
*/
((function($) {
((function ($) {
// The default settings for the plugin
var defaultSettings = {
wheelSpeed: 10,
wheelPropagation: false
// The default settings for the plugin
var defaultSettings = {
wheelSpeed: 10,
wheelPropagation: false
};
$.fn.perfectScrollbar = function (suppliedSettings, option) {
// Use the default settings
var settings = $.extend(true, {}, defaultSettings);
if (typeof suppliedSettings === "object") {
// But over-ride any supplied
$.extend(true, settings, suppliedSettings);
} else {
// If no settings were supplied, then the first param must be the option
option = suppliedSettings;
}
if (option === 'update') {
if ($(this).data('perfect-scrollbar-update')) {
$(this).data('perfect-scrollbar-update')();
}
return $(this);
}
else if (option === 'destroy') {
if ($(this).data('perfect-scrollbar-destroy')) {
$(this).data('perfect-scrollbar-destroy')();
}
return $(this);
}
if ($(this).data('perfect-scrollbar')) {
// if there's already perfect-scrollbar
return $(this).data('perfect-scrollbar');
}
var $this = $(this).addClass('ps-container'),
$content = $(this).children(),
$scrollbarX = $("<div class='ps-scrollbar-x'></div>").appendTo($this),
$scrollbarY = $("<div class='ps-scrollbar-y'></div>").appendTo($this),
containerWidth,
containerHeight,
contentWidth,
contentHeight,
scrollbarXWidth,
scrollbarXLeft,
scrollbarXBottom = parseInt($scrollbarX.css('bottom'), 10),
scrollbarYHeight,
scrollbarYTop,
scrollbarYRight = parseInt($scrollbarY.css('right'), 10);
var updateContentScrollTop = function () {
var scrollTop = parseInt(scrollbarYTop * contentHeight / containerHeight, 10);
$this.scrollTop(scrollTop);
$scrollbarX.css({bottom: scrollbarXBottom - scrollTop});
};
$.fn.perfectScrollbar = function(suppliedSettings, option) {
// Use the default settings
var settings = $.extend( true, {}, defaultSettings );
if (typeof suppliedSettings === "object") {
// But over-ride any supplied
$.extend( true, settings, suppliedSettings );
} else {
// If no settings were supplied, then the first param must be the option
option = suppliedSettings;
}
if(option === 'update') {
if($(this).data('perfect_scrollbar_update')) {
$(this).data('perfect_scrollbar_update')();
}
return $(this);
}
else if(option === 'destroy') {
if($(this).data('perfect_scrollbar_destroy')) {
$(this).data('perfect_scrollbar_destroy')();
}
return $(this);
}
if($(this).data('perfect_scrollbar')) {
// if there's already perfect_scrollbar
return $(this).data('perfect_scrollbar');
}
var $this = $(this).addClass('ps-container'),
$content = $(this).children(),
$scrollbar_x = $("<div class='ps-scrollbar-x'></div>").appendTo($this),
$scrollbar_y = $("<div class='ps-scrollbar-y'></div>").appendTo($this),
container_width,
container_height,
content_width,
content_height,
scrollbar_x_width,
scrollbar_x_left,
scrollbar_x_bottom = parseInt($scrollbar_x.css('bottom'), 10),
scrollbar_y_height,
scrollbar_y_top,
scrollbar_y_right = parseInt($scrollbar_y.css('right'), 10);
var updateContentScrollTop = function() {
var scroll_top = parseInt(scrollbar_y_top * content_height / container_height, 10);
$this.scrollTop(scroll_top);
$scrollbar_x.css({bottom: scrollbar_x_bottom - scroll_top});
};
var updateContentScrollLeft = function() {
var scroll_left = parseInt(scrollbar_x_left * content_width / container_width, 10);
$this.scrollLeft(scroll_left);
$scrollbar_y.css({right: scrollbar_y_right - scroll_left});
};
var updateBarSizeAndPosition = function() {
container_width = $this.width();
container_height = $this.height();
content_width = $content.outerWidth(false);
content_height = $content.outerHeight(false);
if(container_width < content_width) {
scrollbar_x_width = parseInt(container_width * container_width / content_width, 10);
scrollbar_x_left = parseInt($this.scrollLeft() * container_width / content_width, 10);
}
else {
scrollbar_x_width = 0;
scrollbar_x_left = 0;
$this.scrollLeft(0);
}
if(container_height < content_height) {
scrollbar_y_height = parseInt(container_height * container_height / content_height, 10);
scrollbar_y_top = parseInt($this.scrollTop() * container_height / content_height, 10);
}
else {
scrollbar_y_height = 0;
scrollbar_y_left = 0;
$this.scrollTop(0);
}
$scrollbar_x.css({left: scrollbar_x_left + $this.scrollLeft(), bottom: scrollbar_x_bottom - $this.scrollTop(), width: scrollbar_x_width});
$scrollbar_y.css({top: scrollbar_y_top + $this.scrollTop(), right: scrollbar_y_right - $this.scrollLeft(), height: scrollbar_y_height});
};
var moveBarX = function(current_left, delta_x) {
var new_left = current_left + delta_x,
max_left = container_width - scrollbar_x_width;
if(new_left < 0) {
scrollbar_x_left = 0;
}
else if(new_left > max_left) {
scrollbar_x_left = max_left;
}
else {
scrollbar_x_left = new_left;
}
$scrollbar_x.css({left: scrollbar_x_left + $this.scrollLeft()});
};
var moveBarY = function(current_top, delta_y) {
var new_top = current_top + delta_y,
max_top = container_height - scrollbar_y_height;
if(new_top < 0) {
scrollbar_y_top = 0;
}
else if(new_top > max_top) {
scrollbar_y_top = max_top;
}
else {
scrollbar_y_top = new_top;
}
$scrollbar_y.css({top: scrollbar_y_top + $this.scrollTop()});
};
var bindMouseScrollXHandler = function() {
var current_left,
current_page_x;
$scrollbar_x.bind('mousedown.perfect-scroll', function(e) {
current_page_x = e.pageX;
current_left = $scrollbar_x.position().left;
$scrollbar_x.addClass('in-scrolling');
e.stopPropagation();
e.preventDefault();
});
$(document).bind('mousemove.perfect-scroll', function(e) {
if($scrollbar_x.hasClass('in-scrolling')) {
moveBarX(current_left, e.pageX - current_page_x);
updateContentScrollLeft();
e.stopPropagation();
e.preventDefault();
}
});
$(document).bind('mouseup.perfect-scroll', function(e) {
if($scrollbar_x.hasClass('in-scrolling')) {
$scrollbar_x.removeClass('in-scrolling');
}
});
};
var bindMouseScrollYHandler = function() {
var current_top,
current_page_y;
$scrollbar_y.bind('mousedown.perfect-scroll', function(e) {
current_page_y = e.pageY;
current_top = $scrollbar_y.position().top;
$scrollbar_y.addClass('in-scrolling');
e.stopPropagation();
e.preventDefault();
});
$(document).bind('mousemove.perfect-scroll', function(e) {
if($scrollbar_y.hasClass('in-scrolling')) {
moveBarY(current_top, e.pageY - current_page_y);
updateContentScrollTop();
e.stopPropagation();
e.preventDefault();
}
});
$(document).bind('mouseup.perfect-scroll', function(e) {
if($scrollbar_y.hasClass('in-scrolling')) {
$scrollbar_y.removeClass('in-scrolling');
}
});
};
// bind handlers
var bindMouseWheelHandler = function() {
var shouldPreventDefault = function(deltaX, deltaY) {
var scrollTop = $this.scrollTop();
if(scrollTop === 0 && deltaY > 0 && deltaX === 0) {
return !settings.wheelPropagation;
}
else if(scrollTop >= content_height - container_height && deltaY < 0 && deltaX === 0) {
return !settings.wheelPropagation;
}
var scrollLeft = $this.scrollLeft();
if(scrollLeft === 0 && deltaX < 0 && deltaY === 0) {
return !settings.wheelPropagation;
}
else if(scrollLeft >= content_width - container_width && deltaX > 0 && deltaY === 0) {
return !settings.wheelPropagation;
}
return true;
};
$this.mousewheel(function(e, delta, deltaX, deltaY) {
$this.scrollTop($this.scrollTop() - (deltaY * settings.wheelSpeed));
$this.scrollLeft($this.scrollLeft() + (deltaX * settings.wheelSpeed));
// update bar position
updateBarSizeAndPosition();
if(shouldPreventDefault(deltaX, deltaY)) {
e.preventDefault();
}
});
};
// bind mobile touch handler
var bindMobileTouchHandler = function() {
var applyTouchMove = function(difference_x, difference_y) {
$this.scrollTop($this.scrollTop() - difference_y);
$this.scrollLeft($this.scrollLeft() - difference_x);
// update bar position
updateBarSizeAndPosition();
};
var start_coords = {},
start_time = 0,
speed = {},
breaking_process = null;
$this.bind("touchstart.perfect-scroll", function(e) {
var touch = e.originalEvent.targetTouches[0];
start_coords.pageX = touch.pageX;
start_coords.pageY = touch.pageY;
start_time = (new Date()).getTime();
if (breaking_process !== null) {
clearInterval(breaking_process);
}
});
$this.bind("touchmove.perfect-scroll", function(e) {
var touch = e.originalEvent.targetTouches[0];
var current_coords = {};
current_coords.pageX = touch.pageX;
current_coords.pageY = touch.pageY;
var difference_x = current_coords.pageX - start_coords.pageX,
difference_y = current_coords.pageY - start_coords.pageY;
applyTouchMove(difference_x, difference_y);
start_coords = current_coords;
var current_time = (new Date()).getTime();
speed.x = difference_x / (current_time - start_time);
speed.y = difference_y / (current_time - start_time);
start_time = current_time;
e.preventDefault();
});
$this.bind("touchend.perfect-scroll", function(e) {
breaking_process = setInterval(function() {
if(Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
clearInterval(breaking_process);
return;
}
applyTouchMove(speed.x * 30, speed.y * 30);
speed.x *= 0.8;
speed.y *= 0.8;
}, 10);
});
};
var destroy = function() {
$scrollbar_x.remove();
$scrollbar_y.remove();
$this.unbind('mousewheel');
$this.unbind('touchstart.perfect-scroll');
$this.unbind('touchmove.perfect-scroll');
$this.unbind('touchend.perfect-scroll');
$(window).unbind('mousemove.perfect-scroll');
$(window).unbind('mouseup.perfect-scroll');
$this.data('perfect_scrollbar', null);
$this.data('perfect_scrollbar_update', null);
$this.data('perfect_scrollbar_destroy', null);
};
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var initialize = function() {
updateBarSizeAndPosition();
bindMouseScrollXHandler();
bindMouseScrollYHandler();
if(isMobile) bindMobileTouchHandler();
if($this.mousewheel) bindMouseWheelHandler();
$this.data('perfect_scrollbar', $this);
$this.data('perfect_scrollbar_update', updateBarSizeAndPosition);
$this.data('perfect_scrollbar_destroy', destroy);
};
// initialize
initialize();
return $this;
var updateContentScrollLeft = function () {
var scrollLeft = parseInt(scrollbarXLeft * contentWidth / containerWidth, 10);
$this.scrollLeft(scrollLeft);
$scrollbarY.css({right: scrollbarYRight - scrollLeft});
};
var updateBarSizeAndPosition = function () {
containerWidth = $this.width();
containerHeight = $this.height();
contentWidth = $content.outerWidth(false);
contentHeight = $content.outerHeight(false);
if (containerWidth < contentWidth) {
scrollbarXWidth = parseInt(containerWidth * containerWidth / contentWidth, 10);
scrollbarXLeft = parseInt($this.scrollLeft() * containerWidth / contentWidth, 10);
}
else {
scrollbarXWidth = 0;
scrollbarXLeft = 0;
$this.scrollLeft(0);
}
if (containerHeight < contentHeight) {
scrollbarYHeight = parseInt(containerHeight * containerHeight / contentHeight, 10);
scrollbarYTop = parseInt($this.scrollTop() * containerHeight / contentHeight, 10);
}
else {
scrollbarYHeight = 0;
scrollbarYTop = 0;
$this.scrollTop(0);
}
if (scrollbarYTop >= containerHeight - scrollbarYHeight) {
scrollbarYTop = containerHeight - scrollbarYHeight;
}
if (scrollbarXLeft >= containerWidth - scrollbarXWidth) {
scrollbarXLeft = containerWidth - scrollbarXWidth;
}
$scrollbarX.css({left: scrollbarXLeft + $this.scrollLeft(), bottom: scrollbarXBottom - $this.scrollTop(), width: scrollbarXWidth});
$scrollbarY.css({top: scrollbarYTop + $this.scrollTop(), right: scrollbarYRight - $this.scrollLeft(), height: scrollbarYHeight});
};
var moveBarX = function (currentLeft, deltaX) {
var newLeft = currentLeft + deltaX,
maxLeft = containerWidth - scrollbarXWidth;
if (newLeft < 0) {
scrollbarXLeft = 0;
}
else if (newLeft > maxLeft) {
scrollbarXLeft = maxLeft;
}
else {
scrollbarXLeft = newLeft;
}
$scrollbarX.css({left: scrollbarXLeft + $this.scrollLeft()});
};
var moveBarY = function (currentTop, deltaY) {
var newTop = currentTop + deltaY,
maxTop = containerHeight - scrollbarYHeight;
if (newTop < 0) {
scrollbarYTop = 0;
}
else if (newTop > maxTop) {
scrollbarYTop = maxTop;
}
else {
scrollbarYTop = newTop;
}
$scrollbarY.css({top: scrollbarYTop + $this.scrollTop()});
};
var bindMouseScrollXHandler = function () {
var currentLeft,
currentPageX;
$scrollbarX.bind('mousedown.perfect-scroll', function (e) {
currentPageX = e.pageX;
currentLeft = $scrollbarX.position().left;
$scrollbarX.addClass('in-scrolling');
e.stopPropagation();
e.preventDefault();
});
$(document).bind('mousemove.perfect-scroll', function (e) {
if ($scrollbarX.hasClass('in-scrolling')) {
moveBarX(currentLeft, e.pageX - currentPageX);
updateContentScrollLeft();
e.stopPropagation();
e.preventDefault();
}
});
$(document).bind('mouseup.perfect-scroll', function (e) {
if ($scrollbarX.hasClass('in-scrolling')) {
$scrollbarX.removeClass('in-scrolling');
}
});
};
var bindMouseScrollYHandler = function () {
var currentTop,
currentPageY;
$scrollbarY.bind('mousedown.perfect-scroll', function (e) {
currentPageY = e.pageY;
currentTop = $scrollbarY.position().top;
$scrollbarY.addClass('in-scrolling');
e.stopPropagation();
e.preventDefault();
});
$(document).bind('mousemove.perfect-scroll', function (e) {
if ($scrollbarY.hasClass('in-scrolling')) {
moveBarY(currentTop, e.pageY - currentPageY);
updateContentScrollTop();
e.stopPropagation();
e.preventDefault();
}
});
$(document).bind('mouseup.perfect-scroll', function (e) {
if ($scrollbarY.hasClass('in-scrolling')) {
$scrollbarY.removeClass('in-scrolling');
}
});
};
// bind handlers
var bindMouseWheelHandler = function () {
var shouldPreventDefault = function (deltaX, deltaY) {
var scrollTop = $this.scrollTop();
if (scrollTop === 0 && deltaY > 0 && deltaX === 0) {
return !settings.wheelPropagation;
}
else if (scrollTop >= contentHeight - containerHeight && deltaY < 0 && deltaX === 0) {
return !settings.wheelPropagation;
}
var scrollLeft = $this.scrollLeft();
if (scrollLeft === 0 && deltaX < 0 && deltaY === 0) {
return !settings.wheelPropagation;
}
else if (scrollLeft >= contentWidth - containerWidth && deltaX > 0 && deltaY === 0) {
return !settings.wheelPropagation;
}
return true;
};
$this.mousewheel(function (e, delta, deltaX, deltaY) {
$this.scrollTop($this.scrollTop() - (deltaY * settings.wheelSpeed));
$this.scrollLeft($this.scrollLeft() + (deltaX * settings.wheelSpeed));
// update bar position
updateBarSizeAndPosition();
if (shouldPreventDefault(deltaX, deltaY)) {
e.preventDefault();
}
});
};
// bind mobile touch handler
var bindMobileTouchHandler = function () {
var applyTouchMove = function (differenceX, differenceY) {
$this.scrollTop($this.scrollTop() - differenceY);
$this.scrollLeft($this.scrollLeft() - differenceX);
// update bar position
updateBarSizeAndPosition();
};
var startCoords = {},
startTime = 0,
speed = {},
breakingProcess = null;
$this.bind("touchstart.perfect-scroll", function (e) {
var touch = e.originalEvent.targetTouches[0];
startCoords.pageX = touch.pageX;
startCoords.pageY = touch.pageY;
startTime = (new Date()).getTime();
if (breakingProcess !== null) {
clearInterval(breakingProcess);
}
});
$this.bind("touchmove.perfect-scroll", function (e) {
var touch = e.originalEvent.targetTouches[0];
var currentCoords = {};
currentCoords.pageX = touch.pageX;
currentCoords.pageY = touch.pageY;
var differenceX = currentCoords.pageX - startCoords.pageX,
differenceY = currentCoords.pageY - startCoords.pageY;
applyTouchMove(differenceX, differenceY);
startCoords = currentCoords;
var currentTime = (new Date()).getTime();
speed.x = differenceX / (currentTime - startTime);
speed.y = differenceY / (currentTime - startTime);
startTime = currentTime;
e.preventDefault();
});
$this.bind("touchend.perfect-scroll", function (e) {
breakingProcess = setInterval(function () {
if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
clearInterval(breakingProcess);
return;
}
applyTouchMove(speed.x * 30, speed.y * 30);
speed.x *= 0.8;
speed.y *= 0.8;
}, 10);
});
};
var destroy = function () {
$scrollbarX.remove();
$scrollbarY.remove();
$this.unbind('mousewheel');
$this.unbind('touchstart.perfect-scroll');
$this.unbind('touchmove.perfect-scroll');
$this.unbind('touchend.perfect-scroll');
$(window).unbind('mousemove.perfect-scroll');
$(window).unbind('mouseup.perfect-scroll');
$this.data('perfect-scrollbar', null);
$this.data('perfect-scrollbar-update', null);
$this.data('perfect-scrollbar-destroy', null);
};
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var initialize = function () {
updateBarSizeAndPosition();
bindMouseScrollXHandler();
bindMouseScrollYHandler();
if (isMobile) {
bindMobileTouchHandler();
}
if ($this.mousewheel) {
bindMouseWheelHandler();
}
$this.data('perfect-scrollbar', $this);
$this.data('perfect-scrollbar-update', updateBarSizeAndPosition);
$this.data('perfect-scrollbar-destroy', destroy);
};
// initialize
initialize();
return $this;
};
})(jQuery));