User:Emma/common.js: Difference between revisions
Jump to navigation
Jump to search
Want an adless experience? Log in or Create an account.
mNo edit summary |
(Test script) |
||
Line 1: | Line 1: | ||
/* | |||
Class: Scroller | |||
Adds a scrollbar to a specific div. The scrollbar is implemented using a Script.aculo.us slider. | |||
The class reparents the original div, creates a slider and ties the reparented div to the slider, | |||
setting any properties necessary on the divs to make it all work. The scrollbar can be styled using | |||
CSS. The track of the scrollbar has class 'scroll-track', 'scroll-track-top' and 'scroll-track-bot', | |||
the thumb has class 'scroll-handle', 'scroll-handle-top' and 'scroll-handle-bot'. | |||
properties: | |||
myIndex - an integer used to generate a unique ID for use in, for example, div ids. | |||
outerBox - the div that holds the scrollpane + scrollbar | |||
innerBox - the div that holds the scrollpane | |||
innerHeight - the height of the inner box. | |||
viewportHeight - the height of the view onto the scrolled div. | |||
track - a div that holds the script.aculo.us slider (the scrollbar) | |||
trackHeight - the height of the slider | |||
handle - the div for the 'thumb' of the scrollbar | |||
handleHeight - the height of the thumb | |||
slider - the script.aculo.us slider itself | |||
ieDecreaseBy - a fudge factor used when calculating the width of innerBox | |||
*/ | |||
var Scroller = Class.create(); | |||
/* | |||
$('. | property: Scroller.ids | ||
}); | A cache of Scrollers indexed by the ID of the original div. | ||
*/ | |||
$('. | Scroller.ids = new Object(); | ||
}); | |||
/* | |||
property: Scroller.i | |||
A unique ID generator. | |||
*/ | |||
Scroller.i = 0; | |||
Scroller.prototype = { | |||
/* | |||
constructor: initialize | |||
Wrap the passed div in a scrollpane. | |||
parameters: | |||
el - the div to add a scrollbar to. | |||
*/ | |||
initialize: function(el) { | |||
this.outerBox = el; | |||
this.decorate(); | |||
}, | |||
/* | |||
function: decorate | |||
create the necessary elements to implement the scrollbar and wire up events. | |||
*/ | |||
decorate: function() { | |||
$(this.outerBox).makePositioned(); // Fix IE | |||
// Seed a unique ID | |||
Scroller.i = Scroller.i + 1; | |||
this.myIndex = Scroller.i; | |||
//wrap the existing content in an intermediate inner box | |||
this.innerBox = document.createElement("DIV"); | |||
this.innerBox.className="scroll-innerBox"; | |||
$(this.innerBox).makePositioned(); // Fix IE | |||
this.innerBox.style.cssFloat=this.innerBox.style.styleFloat='left'; // Need the scrollbar to appear next to the scrollpane | |||
this.innerBox.id="scroll-innerBox-"+Scroller.i; | |||
this.innerBox.style.top = "0px"; | |||
//Transfer the contents of Outer Box to Inner Box | |||
while (this.outerBox.hasChildNodes()) { | |||
this.innerBox.appendChild(this.outerBox.firstChild); | |||
} | |||
this.innerBox.style.overflow="hidden"; | |||
//turn off scrolling on the outer div | |||
this.outerBox.style.overflow="hidden"; | |||
// create a track | |||
this.track=document.createElement('div'); | |||
this.track.className="scroll-track"; | |||
$(this.track).makePositioned(); | |||
this.track.style.cssFloat=this.track.style.styleFloat='left'; | |||
this.track.id="scroll-track-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.track.appendChild(document.createComment('')); | |||
// Create the top button | |||
this.tracktop=document.createElement('div'); | |||
this.tracktop.className="scroll-track-top"; | |||
$(this.tracktop).makePositioned(); | |||
this.tracktop.style.cssFloat=this.tracktop.style.styleFloat='left'; | |||
this.tracktop.id="scroll-track-top-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.tracktop.appendChild(document.createComment('')); | |||
// Create the bottom button | |||
this.trackbot=document.createElement('div'); | |||
this.trackbot.className="scroll-track-bot"; | |||
$(this.trackbot).makePositioned(); | |||
this.trackbot.style.cssFloat=this.trackbot.style.styleFloat='left'; | |||
this.trackbot.id="scroll-track-bot-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.trackbot.appendChild(document.createComment('')); | |||
// Create the handle | |||
this.handle=document.createElement('div'); | |||
this.handle.className="scroll-handle-container"; | |||
this.handle.id="scroll-handle-container"+Scroller.i; | |||
// Create the handle middle | |||
this.handle_middle=document.createElement('div'); | |||
this.handle_middle.className="scroll-handle"; | |||
$(this.handle_middle).makePositioned(); | |||
this.handle_middle.id="scroll-handle-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.handle_middle.appendChild(document.createComment('')); | |||
// Create the handle top cap | |||
this.handletop=document.createElement('div'); | |||
this.handletop.className="scroll-handle-top"; | |||
$(this.handletop).makePositioned(); | |||
this.handletop.id="scroll-handle-top-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.handletop.appendChild(document.createComment('')); | |||
// Create the handle bottom cap | |||
this.handlebot=document.createElement('div'); | |||
this.handlebot.className="scroll-handle-bot"; | |||
$(this.handlebot).makePositioned(); | |||
this.handlebot.id="scroll-handle-bot-"+Scroller.i; | |||
// Fix IE line-height bug. Sigh. | |||
this.handlebot.appendChild(document.createComment('')); | |||
this.track.hide(); | |||
this.tracktop.hide(); | |||
this.trackbot.hide(); | |||
this.outerBox.appendChild(this.innerBox); | |||
this.outerBox.appendChild(this.tracktop); | |||
this.handle.appendChild(this.handletop); | |||
this.handle.appendChild(this.handle_middle); | |||
this.handle.appendChild(this.handlebot); | |||
this.track.appendChild(this.handle); | |||
this.outerBox.appendChild(this.track); | |||
this.outerBox.appendChild(this.trackbot); | |||
this.slider = new Control.Slider($(this.handle).id, $(this.track).id, {axis:'vertical', | |||
minimum: 0, | |||
maximum: $(this.outerBox).clientHeight}); | |||
this.slider.options.onSlide = this.slider.options.onChange = this.onChange.bind(this); | |||
setTimeout(this.resetScrollbar.bind(this, false), 10); | |||
this.domMouseCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider); | |||
this.mouseWheelCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider); | |||
this.trackTopCB = this.tracktopEvent.bindAsEventListener(this, this.slider); | |||
this.trackBotCB = this.trackbotEvent.bindAsEventListener(this, this.slider); | |||
//Events control | |||
$(this.outerBox).observe('DOMMouseScroll', this.domMouseCB); // Mozilla | |||
$(this.outerBox).observe('mousewheel', this.mouseWheelCB);// IE/Opera | |||
$(this.tracktop).observe('mousedown', this.trackTopCB); | |||
$(this.trackbot).observe('mousedown', this.trackBotCB); | |||
}, | |||
release: function() { | |||
$(this.outerBox).stopObserving('DOMMouseScroll', this.domMouseCB); | |||
$(this.outerBox).stopObserving('mousewheel', this.mouseWheelCB);// IE/Opera | |||
$(this.tracktop).stopObserving('mousedown', this.trackTopCB); | |||
$(this.trackbot).stopObserving('mousedown', this.trackBotCB); | |||
}, | |||
/* | |||
function: resetScrollbar | |||
Re-calculate the geometry of the scrollbar. Typically called from an event handler. | |||
args: | |||
repeat - if true, set timer to re-calculate to fix IE bug on resize window. | |||
*/ | |||
resetScrollbar: function(repeat) { | |||
this.track.hide(); | |||
this.tracktop.hide(); | |||
this.trackbot.hide(); | |||
this.enableScroll = false; | |||
this.innerHeight = $(this.outerBox).clientHeight; | |||
this.innerBox.style.height = this.innerHeight + "px"; | |||
var newWidth = $(this.outerBox).clientWidth; | |||
var tth = Element.getStyle(this.tracktop,"height"); | |||
if (tth) | |||
tth = tth.replace("px",""); | |||
else | |||
tth = 0; | |||
var hth = Element.getStyle(this.handletop,"height"); | |||
if (hth) | |||
hth = hth.replace("px",""); | |||
else | |||
hth = 0; | |||
if (this.innerHeight < this.innerBox.scrollHeight) { | |||
this.viewportHeight = this.innerHeight - tth*2; | |||
this.slider.trackLength = this.viewportHeight; | |||
this.track.style.height = this.viewportHeight + "px"; | |||
this.handleHeight = Math.round(this.viewportHeight * this.innerHeight / this.innerBox.scrollHeight); | |||
if(this.handleHeight < (hth*2)) | |||
this.handleHeight = (hth*2); | |||
if (this.handleHeight < 10) | |||
this.handleHeight = 10; | |||
this.handle.style.height = this.handleHeight + "px"; | |||
this.handle_middle.style.height = this.handleHeight - hth*2 + "px"; | |||
this.handletop.style.height = hth + "px"; | |||
this.slider.handleLength = this.handleHeight; | |||
this.track.style.display = 'inline'; | |||
this.tracktop.style.display = 'inline'; | |||
this.trackbot.style.display = 'inline'; | |||
this.ieDecreaseBy = 1; // Firefox seems to have an off-by one error, so allow for it. | |||
if (this.outerBox.currentStyle) { | |||
var borderWidth = this.outerBox.currentStyle["borderWidth"].replace("px",""); | |||
if(!isNaN(borderWidth)) { | |||
this.ieDecreaseBy = (borderWidth) * 2; | |||
} | |||
} | |||
newWidth = ($(this.outerBox).clientWidth - $(this.track).clientWidth - this.ieDecreaseBy); | |||
this.enableScroll = true; | |||
} | |||
//Set the width of of the scrollpane (aka innerBox). | |||
this.innerBox.style.width = newWidth + "px"; | |||
//Fix IE resize event Bug | |||
if(repeat) { | |||
setTimeout(this.resetScrollbar.bind(this, false), 10); | |||
} | |||
}, | |||
//Mouse wheel code from http://adomas.org/javascript-mouse-wheel/ | |||
MouseWheelEvent: function(event, slider) { | |||
var delta = 0; | |||
if (!event) //For IE. | |||
event = window.event; | |||
if (event.wheelDelta) { //IE/Opera. | |||
delta = event.wheelDelta / 120; | |||
/*if (window.opera) //In Opera 9, delta differs in sign as compared to IE | |||
delta = -delta; But it isn't necessary with Opera v9.51*/ | |||
} else if (event.detail) { //Mozilla case | |||
delta = -event.detail / 3; | |||
} | |||
if (delta) | |||
slider.setValueBy(-delta / 10); | |||
Event.stop(event); | |||
}, | |||
trackbotEvent: function(event, slider) { | |||
if (Event.isLeftClick(event)) { | |||
slider.setValueBy(0.2); | |||
Event.stop(event); | |||
} | |||
}, | |||
tracktopEvent: function(event, slider) { | |||
if (Event.isLeftClick(event)) { | |||
slider.setValueBy(-0.2); | |||
Event.stop(event); | |||
} | |||
}, | |||
/* | |||
function: onChange | |||
Called when the script.aculo.us slider has changed (i.e. when it has been dragged). Scroll the inner box. | |||
args: | |||
val - not used. | |||
*/ | |||
onChange: function(val) { | |||
if(this.enableScroll) | |||
this.innerBox.scrollTop = Math.round (val * (this.innerBox.scrollHeight-this.innerBox.offsetHeight)); | |||
} | |||
} | |||
/* | |||
function: Scroller.setAll | |||
Search for divs of the class 'makeScroll' and wrap them in a Scroller. | |||
*/ | |||
Scroller.setAll = function () { | |||
$$('.makeScroll').each(function(item) { | |||
Scroller.ids[item.id] = new Scroller(item); | |||
}); | |||
} | |||
/* | |||
function: Scroller.reset | |||
If the passed element has class 'makeScroll', wrap it in a Scroller. | |||
*/ | |||
Scroller.reset = function (body_id) { | |||
if ($(body_id).className.match(new RegExp("(^|\\s)makeScroll(\\s|$)"))) { | |||
if (Scroller.ids[body_id]) | |||
Scroller.ids[body_id].release(); | |||
Scroller.ids[body_id] = new Scroller($(body_id)); | |||
} | |||
} | |||
/* | |||
property: Scroller.updateAll | |||
Reset all of the scrollbars. | |||
*/ | |||
Scroller.updateAll = function () { | |||
$H(Scroller.ids).each(function(pair) { | |||
Scroller.ids[pair.key].resetScrollbar(true); | |||
}); | |||
} | |||
/* | |||
Hook up some global event handlers. | |||
*/ | |||
Event.observe(window, "load", Scroller.setAll); | |||
Event.observe(window, "resize", Scroller.updateAll); |
Revision as of 01:56, February 3, 2012
/* Class: Scroller Adds a scrollbar to a specific div. The scrollbar is implemented using a Script.aculo.us slider. The class reparents the original div, creates a slider and ties the reparented div to the slider, setting any properties necessary on the divs to make it all work. The scrollbar can be styled using CSS. The track of the scrollbar has class 'scroll-track', 'scroll-track-top' and 'scroll-track-bot', the thumb has class 'scroll-handle', 'scroll-handle-top' and 'scroll-handle-bot'. properties: myIndex - an integer used to generate a unique ID for use in, for example, div ids. outerBox - the div that holds the scrollpane + scrollbar innerBox - the div that holds the scrollpane innerHeight - the height of the inner box. viewportHeight - the height of the view onto the scrolled div. track - a div that holds the script.aculo.us slider (the scrollbar) trackHeight - the height of the slider handle - the div for the 'thumb' of the scrollbar handleHeight - the height of the thumb slider - the script.aculo.us slider itself ieDecreaseBy - a fudge factor used when calculating the width of innerBox */ var Scroller = Class.create(); /* property: Scroller.ids A cache of Scrollers indexed by the ID of the original div. */ Scroller.ids = new Object(); /* property: Scroller.i A unique ID generator. */ Scroller.i = 0; Scroller.prototype = { /* constructor: initialize Wrap the passed div in a scrollpane. parameters: el - the div to add a scrollbar to. */ initialize: function(el) { this.outerBox = el; this.decorate(); }, /* function: decorate create the necessary elements to implement the scrollbar and wire up events. */ decorate: function() { $(this.outerBox).makePositioned(); // Fix IE // Seed a unique ID Scroller.i = Scroller.i + 1; this.myIndex = Scroller.i; //wrap the existing content in an intermediate inner box this.innerBox = document.createElement("DIV"); this.innerBox.className="scroll-innerBox"; $(this.innerBox).makePositioned(); // Fix IE this.innerBox.style.cssFloat=this.innerBox.style.styleFloat='left'; // Need the scrollbar to appear next to the scrollpane this.innerBox.id="scroll-innerBox-"+Scroller.i; this.innerBox.style.top = "0px"; //Transfer the contents of Outer Box to Inner Box while (this.outerBox.hasChildNodes()) { this.innerBox.appendChild(this.outerBox.firstChild); } this.innerBox.style.overflow="hidden"; //turn off scrolling on the outer div this.outerBox.style.overflow="hidden"; // create a track this.track=document.createElement('div'); this.track.className="scroll-track"; $(this.track).makePositioned(); this.track.style.cssFloat=this.track.style.styleFloat='left'; this.track.id="scroll-track-"+Scroller.i; // Fix IE line-height bug. Sigh. this.track.appendChild(document.createComment('')); // Create the top button this.tracktop=document.createElement('div'); this.tracktop.className="scroll-track-top"; $(this.tracktop).makePositioned(); this.tracktop.style.cssFloat=this.tracktop.style.styleFloat='left'; this.tracktop.id="scroll-track-top-"+Scroller.i; // Fix IE line-height bug. Sigh. this.tracktop.appendChild(document.createComment('')); // Create the bottom button this.trackbot=document.createElement('div'); this.trackbot.className="scroll-track-bot"; $(this.trackbot).makePositioned(); this.trackbot.style.cssFloat=this.trackbot.style.styleFloat='left'; this.trackbot.id="scroll-track-bot-"+Scroller.i; // Fix IE line-height bug. Sigh. this.trackbot.appendChild(document.createComment('')); // Create the handle this.handle=document.createElement('div'); this.handle.className="scroll-handle-container"; this.handle.id="scroll-handle-container"+Scroller.i; // Create the handle middle this.handle_middle=document.createElement('div'); this.handle_middle.className="scroll-handle"; $(this.handle_middle).makePositioned(); this.handle_middle.id="scroll-handle-"+Scroller.i; // Fix IE line-height bug. Sigh. this.handle_middle.appendChild(document.createComment('')); // Create the handle top cap this.handletop=document.createElement('div'); this.handletop.className="scroll-handle-top"; $(this.handletop).makePositioned(); this.handletop.id="scroll-handle-top-"+Scroller.i; // Fix IE line-height bug. Sigh. this.handletop.appendChild(document.createComment('')); // Create the handle bottom cap this.handlebot=document.createElement('div'); this.handlebot.className="scroll-handle-bot"; $(this.handlebot).makePositioned(); this.handlebot.id="scroll-handle-bot-"+Scroller.i; // Fix IE line-height bug. Sigh. this.handlebot.appendChild(document.createComment('')); this.track.hide(); this.tracktop.hide(); this.trackbot.hide(); this.outerBox.appendChild(this.innerBox); this.outerBox.appendChild(this.tracktop); this.handle.appendChild(this.handletop); this.handle.appendChild(this.handle_middle); this.handle.appendChild(this.handlebot); this.track.appendChild(this.handle); this.outerBox.appendChild(this.track); this.outerBox.appendChild(this.trackbot); this.slider = new Control.Slider($(this.handle).id, $(this.track).id, {axis:'vertical', minimum: 0, maximum: $(this.outerBox).clientHeight}); this.slider.options.onSlide = this.slider.options.onChange = this.onChange.bind(this); setTimeout(this.resetScrollbar.bind(this, false), 10); this.domMouseCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider); this.mouseWheelCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider); this.trackTopCB = this.tracktopEvent.bindAsEventListener(this, this.slider); this.trackBotCB = this.trackbotEvent.bindAsEventListener(this, this.slider); //Events control $(this.outerBox).observe('DOMMouseScroll', this.domMouseCB); // Mozilla $(this.outerBox).observe('mousewheel', this.mouseWheelCB);// IE/Opera $(this.tracktop).observe('mousedown', this.trackTopCB); $(this.trackbot).observe('mousedown', this.trackBotCB); }, release: function() { $(this.outerBox).stopObserving('DOMMouseScroll', this.domMouseCB); $(this.outerBox).stopObserving('mousewheel', this.mouseWheelCB);// IE/Opera $(this.tracktop).stopObserving('mousedown', this.trackTopCB); $(this.trackbot).stopObserving('mousedown', this.trackBotCB); }, /* function: resetScrollbar Re-calculate the geometry of the scrollbar. Typically called from an event handler. args: repeat - if true, set timer to re-calculate to fix IE bug on resize window. */ resetScrollbar: function(repeat) { this.track.hide(); this.tracktop.hide(); this.trackbot.hide(); this.enableScroll = false; this.innerHeight = $(this.outerBox).clientHeight; this.innerBox.style.height = this.innerHeight + "px"; var newWidth = $(this.outerBox).clientWidth; var tth = Element.getStyle(this.tracktop,"height"); if (tth) tth = tth.replace("px",""); else tth = 0; var hth = Element.getStyle(this.handletop,"height"); if (hth) hth = hth.replace("px",""); else hth = 0; if (this.innerHeight < this.innerBox.scrollHeight) { this.viewportHeight = this.innerHeight - tth*2; this.slider.trackLength = this.viewportHeight; this.track.style.height = this.viewportHeight + "px"; this.handleHeight = Math.round(this.viewportHeight * this.innerHeight / this.innerBox.scrollHeight); if(this.handleHeight < (hth*2)) this.handleHeight = (hth*2); if (this.handleHeight < 10) this.handleHeight = 10; this.handle.style.height = this.handleHeight + "px"; this.handle_middle.style.height = this.handleHeight - hth*2 + "px"; this.handletop.style.height = hth + "px"; this.slider.handleLength = this.handleHeight; this.track.style.display = 'inline'; this.tracktop.style.display = 'inline'; this.trackbot.style.display = 'inline'; this.ieDecreaseBy = 1; // Firefox seems to have an off-by one error, so allow for it. if (this.outerBox.currentStyle) { var borderWidth = this.outerBox.currentStyle["borderWidth"].replace("px",""); if(!isNaN(borderWidth)) { this.ieDecreaseBy = (borderWidth) * 2; } } newWidth = ($(this.outerBox).clientWidth - $(this.track).clientWidth - this.ieDecreaseBy); this.enableScroll = true; } //Set the width of of the scrollpane (aka innerBox). this.innerBox.style.width = newWidth + "px"; //Fix IE resize event Bug if(repeat) { setTimeout(this.resetScrollbar.bind(this, false), 10); } }, //Mouse wheel code from http://adomas.org/javascript-mouse-wheel/ MouseWheelEvent: function(event, slider) { var delta = 0; if (!event) //For IE. event = window.event; if (event.wheelDelta) { //IE/Opera. delta = event.wheelDelta / 120; /*if (window.opera) //In Opera 9, delta differs in sign as compared to IE delta = -delta; But it isn't necessary with Opera v9.51*/ } else if (event.detail) { //Mozilla case delta = -event.detail / 3; } if (delta) slider.setValueBy(-delta / 10); Event.stop(event); }, trackbotEvent: function(event, slider) { if (Event.isLeftClick(event)) { slider.setValueBy(0.2); Event.stop(event); } }, tracktopEvent: function(event, slider) { if (Event.isLeftClick(event)) { slider.setValueBy(-0.2); Event.stop(event); } }, /* function: onChange Called when the script.aculo.us slider has changed (i.e. when it has been dragged). Scroll the inner box. args: val - not used. */ onChange: function(val) { if(this.enableScroll) this.innerBox.scrollTop = Math.round (val * (this.innerBox.scrollHeight-this.innerBox.offsetHeight)); } } /* function: Scroller.setAll Search for divs of the class 'makeScroll' and wrap them in a Scroller. */ Scroller.setAll = function () { $$('.makeScroll').each(function(item) { Scroller.ids[item.id] = new Scroller(item); }); } /* function: Scroller.reset If the passed element has class 'makeScroll', wrap it in a Scroller. */ Scroller.reset = function (body_id) { if ($(body_id).className.match(new RegExp("(^|\\s)makeScroll(\\s|$)"))) { if (Scroller.ids[body_id]) Scroller.ids[body_id].release(); Scroller.ids[body_id] = new Scroller($(body_id)); } } /* property: Scroller.updateAll Reset all of the scrollbars. */ Scroller.updateAll = function () { $H(Scroller.ids).each(function(pair) { Scroller.ids[pair.key].resetScrollbar(true); }); } /* Hook up some global event handlers. */ Event.observe(window, "load", Scroller.setAll); Event.observe(window, "resize", Scroller.updateAll);