/* * jQuery SmoothDivScroll 1.1 * * Copyright (c) 2010 Thomas Kahn * Licensed under the GPL license. * * http://www.maaki.com/thomas/SmoothDivScroll/ * * Depends: * jquery.ui.widget.js * */ (function($) { $.widget("thomaskahn.smoothDivScroll", { // Default options options: { scrollingHotSpotLeft: "div.scrollingHotSpotLeft", scrollingHotSpotRight: "div.scrollingHotSpotRight", scrollableArea : "div.scrollableArea", scrollWrapper: "div.scrollWrapper", hiddenOnStart: false, ajaxContentURL: "", countOnlyClass: "", scrollStep: 15, scrollInterval: 10, mouseDownSpeedBooster: 3, autoScroll: "", autoScrollDirection: "right", autoScrollStep: 5, autoScrollInterval: 10, visibleHotSpots: "", hotSpotsVisibleTime: 5, startAtElementId: "" }, _create: function() { // Set variables var self = this, o = this.options, el = this.element; el.data("scrollWrapper", el.find(o.scrollWrapper)); el.data("scrollingHotSpotRight", el.find(o.scrollingHotSpotRight)); el.data("scrollingHotSpotLeft", el.find(o.scrollingHotSpotLeft)); el.data("scrollableArea", el.find(o.scrollableArea)); el.data("speedBooster", 1); el.data("motherElementOffset", el.offset().left); el.data("scrollXPos", 0); el.data("hotSpotWidth", el.find(o.scrollingHotSpotLeft).width()); el.data("scrollableAreaWidth", 0); el.data("startingPosition", 0); el.data("rightScrollInterval", null); el.data("leftScrollInterval", null); el.data("autoScrollInterval", null); el.data("hideHotSpotBackgroundsInterval", null); el.data("previousScrollLeft", 0); el.data("pingPongDirection", "right"); el.data("getNextElementWidth", true); el.data("swapAt", null); el.data("startAtElementHasNotPassed", true); el.data("swappedElement", null); el.data("originalElements", el.data("scrollableArea").children(o.countOnlyClass)); el.data("visible",true); el.data("initialAjaxContentLoaded", false); el.data("enabled", true); // Set the starting position of the scrollable area. // If no startAtElementId is set, the starting position // will be the default value (zero) el.data("scrollWrapper").scrollLeft(el.data("startingPosition")); // If the user wants to have visible hotspots, here is where it's taken care of if(o.autoScroll !== "always") { switch(o.visibleHotSpots) { case "always": self.showHotSpotBackgrounds(); break; case "onstart": self.showHotSpotBackgrounds(); el.data("hideHotSpotBackgroundsInterval", setTimeout(function(){ self.hideHotSpotBackgrounds("slow"); },(o.hotSpotsVisibleTime * 1000))); break; default: break; } } /***************************************** SET UP EVENTS FOR SCROLLING RIGHT *****************************************/ // Check the mouse X position and calculate the relative X position inside the right hotspot el.data("scrollingHotSpotRight").bind("mousemove", function(e){ var x = e.pageX - (this.offsetLeft + el.data("motherElementOffset")); el.data("scrollXPos", Math.round((x/el.data("hotSpotWidth")) * o.scrollStep)); if(el.data("scrollXPos") === Infinity) { el.data("scrollXPos", 0); } }); // mouseover right hotspot - scrolling el.data("scrollingHotSpotRight").bind("mouseover", function(){ // Clear autoscrolling, if it should only run on start if((o.autoScroll === "onstart" && el.data("autoScrollInterval") !== null)) { clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); self._trigger("autoScrollIntervalStopped"); } // Start the scrolling interval el.data("rightScrollInterval", setInterval(function(){ if(el.data("scrollXPos") > 0 && el.data("enabled")) { el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (el.data("scrollXPos") * el.data("speedBooster"))); self._showHideHotSpots(); } }, o.scrollInterval)); // Callback self._trigger("mouseOverRightHotSpot"); }); // mouseout right hotspot el.data("scrollingHotSpotRight").bind("mouseout", function(){ clearInterval(el.data("rightScrollInterval")); el.data("scrollXPos", 0); }); // mousedown right hotspot (add scrolling speed booster) el.data("scrollingHotSpotRight").bind("mousedown", function(){ el.data("speedBooster", o.mouseDownSpeedBooster); }); // mouseup anywhere (stop boosting the scrolling speed) $("body").bind("mouseup", function(){ el.data("speedBooster", 1); }); /***************************************** SET UP EVENTS FOR SCROLLING LEFT *****************************************/ // Check the mouse X position and calculate the relative X position inside the left hotspot el.data("scrollingHotSpotLeft").bind("mousemove", function(e){ var x = el.data("scrollingHotSpotLeft").innerWidth() - (e.pageX - el.data("motherElementOffset")); el.data("scrollXPos", Math.round((x/el.data("hotSpotWidth")) * o.scrollStep)); if(el.data("scrollXPos") === Infinity) { el.data("scrollXPos", 0); } }); // mouseover left hotspot el.data("scrollingHotSpotLeft").bind("mouseover", function(){ // Clear autoscrolling, if it should only run on start if((o.autoScroll === "onstart" && el.data("autoScrollInterval") !== null)) { clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); self._trigger("autoScrollIntervalStopped"); } el.data("leftScrollInterval", setInterval(function(){ if(el.data("scrollXPos") > 0 && el.data("enabled")) { el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (el.data("scrollXPos")*el.data("speedBooster"))); self._showHideHotSpots(); } }, o.scrollInterval)); // Callback self._trigger("mouseOverLeftHotSpot"); }); // mouseout left hotspot el.data("scrollingHotSpotLeft").bind("mouseout", function(){ clearInterval(el.data("leftScrollInterval")); el.data("scrollXPos", 0); }); // mousedown left hotspot (add scrolling speed booster) el.data("scrollingHotSpotLeft").bind("mousedown", function(){ el.data("speedBooster", o.mouseDownSpeedBooster); }); /***************************************** SET UP EVENT FOR RESIZING THE BROWSER WINDOW *****************************************/ $(window).bind("resize", function(){ // If the scrollable area is not hidden on start, show/hide the hotspots if(!(o.hiddenOnStart)) { self._showHideHotSpots(); } self._trigger("windowResized"); }); /***************************************** FETCHING AJAX CONTENT ON INITIALIZATION *****************************************/ // If there's an ajaxContentURL in the options, // fetch the content if(o.ajaxContentURL.length > 0) { self.replaceContent(o.ajaxContentURL); } else { self.recalculateScrollableArea(); } // Should it be hidden on start? if(o.hiddenOnStart) { self.hide(); } /***************************************** AUTOSCROLLING *****************************************/ // If the user has set the option autoScroll, the scollable area will // start scrolling automatically. If the content is fetched using AJAX // the autoscroll is not started here but in recalculateScrollableArea. // Otherwise recalculateScrollableArea won't have the time to calculate // the width of the scrollable area before the autoscrolling starts. if((o.autoScroll.length > 0) && !(o.hiddenOnStart) && (o.ajaxContentURL.length <= 0)) { self.startAutoScroll(); } }, /********************************************************** Hotspot functions **********************************************************/ showHotSpotBackgrounds: function(fadeSpeed) { // Alter the CSS (SmoothDivScroll.css) if you want to customize // the look'n'feel of the visible hotspots var self = this, el = this.element; // Fade in the hotspot backgrounds if(fadeSpeed !== undefined) { // Before the fade-in starts, we need to make sure the opacity // is zero el.data("scrollingHotSpotLeft").css("opacity","1");//0.0 el.data("scrollingHotSpotRight").css("opacity","1");//0.0 el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible"); el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible"); // Fade in the left hotspot //el.data("scrollingHotSpotLeft").fadeTo(fadeSpeed, 1);//0.35 // Fade in the right hotspot //el.data("scrollingHotSpotRight").fadeTo(fadeSpeed, 1);//0.35 } // Don't fade, just show them else { // The left hotspot el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible"); el.data("scrollingHotSpotLeft").removeAttr("style"); // The right hotspot el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible"); el.data("scrollingHotSpotRight").removeAttr("style"); } self._showHideHotSpots(); }, hideHotSpotBackgrounds: function(fadeSpeed) { var el = this.element; // Fade out the hotspot backgrounds if(fadeSpeed !== undefined) { // Fade out the left hotspot el.data("scrollingHotSpotLeft").fadeTo(fadeSpeed, 1, function(){//0.0 el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible"); }); // Fade out the right hotspot el.data("scrollingHotSpotRight").fadeTo(fadeSpeed, 1,function(){//0.0 el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible"); }); } // Don't fade, just hide them else { el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible"); el.data("scrollingHotSpotLeft").removeAttr("style"); el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible"); el.data("scrollingHotSpotRight").removeAttr("style"); } }, // Function for showing and hiding hotspots depending on the // offset of the scrolling _showHideHotSpots: function() { var self = this, el = this.element, o = this.options; // If autoscrolling is set to always, there should be no hotspots if(o.autoScroll !== "always") { // If the scrollable area is shorter than the scroll wrapper, both hotspots // should be hidden if(el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth())) { //el.data("scrollingHotSpotLeft").hide(); //el.data("scrollingHotSpotRight").hide(); el.data("scrollingHotSpotLeft").addClass('active'); el.data("scrollingHotSpotRight").addClass('active'); } // When you can't scroll further left the left scroll hotspot should be hidden // and the right hotspot visible. else if (el.data("scrollWrapper").scrollLeft() === 0) { //el.data("scrollingHotSpotLeft").hide(); //el.data("scrollingHotSpotRight").show(); el.data("scrollingHotSpotLeft").addClass('active'); el.data("scrollingHotSpotRight").removeClass('active'); // Callback self._trigger("scrollLeftLimitReached"); // Clear interval clearInterval(el.data("leftScrollInterval")); el.data("leftScrollInterval", null); } // When you can't scroll further right // the right scroll hotspot should be hidden // and the left hotspot visible else if (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth() + el.data("scrollWrapper").scrollLeft())) { //el.data("scrollingHotSpotLeft").show(); //el.data("scrollingHotSpotRight").hide(); el.data("scrollingHotSpotLeft").removeClass('active'); el.data("scrollingHotSpotRight").addClass('active'); // Callback self._trigger("scrollRightLimitReached"); // Clear interval clearInterval(el.data("rightScrollInterval")); el.data("rightScrollInterval", null); } // If you are somewhere in the middle of your // scrolling, both hotspots should be visible else { //el.data("scrollingHotSpotLeft").show(); //el.data("scrollingHotSpotRight").show(); el.data("scrollingHotSpotLeft").removeClass('active'); el.data("scrollingHotSpotRight").removeClass('active'); } } else { //el.data("scrollingHotSpotLeft").hide(); //el.data("scrollingHotSpotRight").hide(); el.data("scrollingHotSpotLeft").addClass('active'); el.data("scrollingHotSpotRight").addClass('active'); } }, /********************************************************** Moving to a certain element **********************************************************/ moveToElement: function(moveTo, elementNumber) { var self = this, el = this.element, o = this.options, tempScrollableAreaWidth = 0, foundStartAtElement = false; switch(moveTo) { case "first": el.data("scrollXPos", 0); self._trigger("movedToFirstElement"); break; case "start": // Check to see where the start-at element is at the moment. // This can vary if endlessloop is used for autoscroll since it // swaps elements around. el.data("scrollableArea").children(o.countOnlyClass).each(function() { if( (o.startAtElementId.length > 0) && (($(this).attr("id")) === o.startAtElementId) ) { el.data("startingPosition", tempScrollableAreaWidth); foundStartAtElement = true; } tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true); }); el.data("scrollXPos", el.data("startingPosition")); self._trigger("movedToStartElement"); break; case "last": el.data("scrollXPos", el.data("scrollableAreaWidth")); self._trigger("movedToLastElement"); break; case "number": if(!(isNaN(elementNumber))) { // Get the total width of all preceding elements el.data("scrollableArea").children(o.countOnlyClass).each(function(index) { if(index === (elementNumber-1)) { el.data("scrollXPos", tempScrollableAreaWidth); } tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true); }); } self._trigger("movedToElementNumber", null, {"elementNumber": elementNumber}); break; default: break; } el.data("scrollWrapper").scrollLeft(el.data("scrollXPos")); self._showHideHotSpots(); }, /********************************************************** Adding or replacing content **********************************************************/ addContent: function(ajaxContentURL, addWhere) { var self = this, el = this.element; $.get(ajaxContentURL, function(data){ // Add the loaded content first or last in the scrollable area if(addWhere === "first") { el.data("scrollableArea").children(":first").before(data); } else { el.data("scrollableArea").children(":last").after(data); } // Recalculate the total width of the elements inside the scrollable area self.recalculateScrollableArea(); // Determine which hotspots to show self._showHideHotSpots(); }); }, replaceContent: function(ajaxContentURL) { var self = this, el = this.element; el.data("scrollableArea").load(ajaxContentURL, function(){ // Recalculate the total width of the elements inside the scrollable area self.recalculateScrollableArea(); self.moveToElement("first"); self._showHideHotSpots(); el.data("startingPosition", 0); }); }, /********************************************************** Recalculate the scrollable area **********************************************************/ recalculateScrollableArea: function() { var tempScrollableAreaWidth = 0, foundStartAtElement = false, o = this.options, el = this.element, self = this; // Add up the total width of all the items inside the scrollable area el.data("scrollableArea").children(o.countOnlyClass).each(function() { // Check to see if the current element in the loop is the one where the scrolling should start if( (o.startAtElementId.length > 0) && (($(this).attr("id")) === o.startAtElementId) ) { el.data("startingPosition", tempScrollableAreaWidth); foundStartAtElement = true; } tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true); }); // If the element with the ID specified by startAtElementId // is not found, reset it if (!(foundStartAtElement)) { el.data("startAtElementId", ""); } // Set the width of the scrollable area el.data("scrollableAreaWidth", tempScrollableAreaWidth); el.data("scrollableArea").width(el.data("scrollableAreaWidth")); // If the content of the scrollable area is fetched using AJAX // during initialization, it needs to be done here. After it has // been loaded a flag variable is set to indicate that the content // has been loaded already and shouldn if(!(el.data("initialAjaxContentLoaded"))) { if((o.autoScroll.length > 0) && !(o.hiddenOnStart) && (o.ajaxContentURL.length > 0)) { self.startAutoScroll(); el.data("initialAjaxContentLoaded", true); } } }, /********************************************************** Stopping, starting and doing the autoscrolling **********************************************************/ stopAutoScroll: function() { var self = this, el = this.element; clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); // Check to see which hotspots should be active // in the position where the scroller has stopped self._showHideHotSpots(); self._trigger("autoScrollStopped"); }, startAutoScroll: function() { var self = this, el = this.element, o = this.options; self._showHideHotSpots(); // Stop any running interval clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); // Callback self._trigger("autoScrollStarted"); // Start interval el.data("autoScrollInterval", setInterval(function(){ // If the scroller is not visible or // if the scrollable area is shorter than the scroll wrapper // any running autoscroll interval should stop. if(!(el.data("visible")) || (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth()))) { // Stop any running interval clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); } else { // Store the old scrollLeft value to see if the scrolling has reached the end el.data("previousScrollLeft", el.data("scrollWrapper").scrollLeft()); switch(o.autoScrollDirection) { case "right": el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollStep); if(el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) { self._trigger("autoScrollRightLimitReached"); clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); self._trigger("autoScrollIntervalStopped"); } break; case "left": el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollStep); if(el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) { self._trigger("autoScrollLeftLimitReached"); clearInterval(el.data("autoScrollInterval")); el.data("autoScrollInterval", null); self._trigger("autoScrollIntervalStopped"); } break; case "backandforth": if(el.data("pingPongDirection") === "right") { el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (o.autoScrollStep)); } else { el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (o.autoScrollStep)); } // If the scrollLeft hasnt't changed it means that the scrolling has reached // the end and the direction should be switched if(el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) { if(el.data("pingPongDirection") === "right") { el.data("pingPongDirection", "left"); self._trigger("autoScrollRightLimitReached"); } else { el.data("pingPongDirection", "right"); self._trigger("autoScrollLeftLimitReached"); } } break; case "endlessloopright": // Get the width of the first element. When it has scrolled out of view, // the element swapping should be executed. A true/false variable is used // as a flag variable so the swapAt value doesn't have to be recalculated // in each loop. if(el.data("getNextElementWidth")) { if((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) { el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true)); el.data("startAtElementHasNotPassed", false); } else { el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true)); } el.data("getNextElementWidth", false); } // Do the autoscrolling el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollStep); // Check to see if the swap should be done if(el.data("swapAt") <= el.data("scrollWrapper").scrollLeft()) { el.data("swappedElement", el.data("scrollableArea").children(":first").detach()); el.data("scrollableArea").append(el.data("swappedElement")); el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - el.data("swappedElement").outerWidth(true)); el.data("getNextElementWidth", true); } break; case "endlessloopleft": // Get the width of the first element. When it has scrolled out of view, // the element swapping should be executed. A true/false variable is used // as a flag variable so the swapAt value doesn't have to be recalculated // in each loop. if(el.data("getNextElementWidth")) { if((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) { el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true)); el.data("startAtElementHasNotPassed", false); } else { el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true)); } el.data("getNextElementWidth", false); } // Do the autoscrolling el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollStep); // Check to see if the swap should be done if(el.data("scrollWrapper").scrollLeft() === 0) { el.data("swappedElement", el.data("scrollableArea").children(":last").detach()); el.data("scrollableArea").prepend(el.data("swappedElement")); el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + el.data("swappedElement").outerWidth(true)); el.data("getNextElementWidth", true); } break; default: break; } } }, o.autoScrollInterval)); }, restoreOriginalElements: function() { var self = this, el = this.element; // Restore the original content of the scrollable area el.data("scrollableArea").html(el.data("originalElements")); self.recalculateScrollableArea(); self.moveToElement("first"); }, show: function(){ var el = this.element; el.data("visible", true); el.show(); }, hide: function(){ var el = this.element; el.data("visible", false); el.hide(); }, enable: function(){ var el = this.element; // Set enabled to true el.data("enabled", true); }, disable: function(){ var el = this.element; // Clear all running intervals clearInterval(el.data("autoScrollInterval")); clearInterval(el.data("rightScrollInterval")); clearInterval(el.data("leftScrollInterval")); clearInterval(el.data("hideHotSpotBackgroundsInterval")); // Set enabled to false el.data("enabled", false); }, destroy: function() { var el = this.element; // Clear all running intervals clearInterval(el.data("autoScrollInterval")); clearInterval(el.data("rightScrollInterval")); clearInterval(el.data("leftScrollInterval")); clearInterval(el.data("hideHotSpotBackgroundsInterval")); // Remove all element specific events el.data("scrollingHotSpotRight").unbind("mouseover"); el.data("scrollingHotSpotRight").unbind("mouseout"); el.data("scrollingHotSpotRight").unbind("mousedown"); el.data("scrollingHotSpotLeft").unbind("mouseover"); el.data("scrollingHotSpotLeft").unbind("mouseout"); el.data("scrollingHotSpotLeft").unbind("mousedown"); // Restore the original content of the scrollable area el.data("scrollableArea").html(el.data("originalElements")); // Remove the width of the scrollable area el.data("scrollableArea").removeAttr("style"); el.data("scrollingHotSpotRight").removeAttr("style"); el.data("scrollingHotSpotLeft").removeAttr("style"); el.data("scrollWrapper").scrollLeft(0); el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible"); el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible"); el.data("scrollingHotSpotRight").hide(); el.data("scrollingHotSpotLeft").hide(); // Call the base destroy function $.Widget.prototype.destroy.apply(this, arguments); } }); })(jQuery);