1 /** The osmplayer namespace. */ 2 var osmplayer = osmplayer || {}; 3 4 /** 5 * @constructor 6 * @extends minplayer.display 7 * @class This class provides the scroll functionality for the playlists. 8 * 9 * We can calculate how the scrollbar controls the playlist using the 10 * following diagram / equations. 11 * ___ ____________ 12 * | | |\ 13 * | | list | \ 14 * | | |y \ 15 * | | | \ 16 * | |____________| \ _ _____ 17 * | | |\ | | | 18 * | | | \ | | | 19 * | | | \ | |x | 20 * | | | \ | | | 21 * | | | \|_|_ | 22 * | | | | | | | 23 * l | window | | | h w 24 * | | | |_|_| | 25 * | | | /| | | 26 * | | | / | | | 27 * | | | / v| | | 28 * | | | / | | | 29 * | |____________|/ |_|____| 30 * | | | / 31 * | | | / 32 * | | | / 33 * | | | / 34 * |__|____________|/ 35 * 36 * l - The list height. 37 * h - Handle Bar height. 38 * w - Window height. 39 * x - The distance from top of window to the top of the handle. 40 * y - The disatnce from the top of the list to the top of the window. 41 * v - The distance from bottom of window to the bottom of the handle. 42 * 43 * jQuery UI provides "v". We already know "l", "h", "w". We can then 44 * calculate the relationship between the scroll bar handle position to the 45 * list position using the following equations. 46 * 47 * x = (w - (v + h)) 48 * y = ((l - w)/(w - h)) * x 49 * 50 * -- or -- 51 * 52 * y = ((l - w)/(w - h)) * (w - (v + h)) 53 * 54 * We can statically calculate the ((l - w)/(w - h)) as a ratio and use 55 * that to speed up calculations as follows. 56 * 57 * ratio = ((l - w)/(w - h)); 58 * 59 * So, our translation equations are as follows... 60 * 61 * y = ratio * (w - (v + h)) 62 * v = w - (h + (y / ratio)) 63 * 64 * 65 * @param {object} context The jQuery context. 66 * @param {object} options This components options. 67 */ 68 osmplayer.scroll = function(context, options) { 69 70 // Derive from display 71 minplayer.display.call(this, 'scroll', context, options); 72 }; 73 74 /** Derive from minplayer.display. */ 75 osmplayer.scroll.prototype = new minplayer.display(); 76 77 /** Reset the constructor. */ 78 osmplayer.scroll.prototype.constructor = osmplayer.scroll; 79 80 /** 81 * @see minplayer.plugin#construct 82 */ 83 osmplayer.scroll.prototype.construct = function() { 84 85 // Make sure we provide default options... 86 this.options = jQuery.extend({ 87 vertical: true, 88 hysteresis: 40, 89 scrollSpeed: 20, 90 scrollMode: 'auto' 91 }, this.options); 92 93 // Call the minplayer plugin constructor. 94 minplayer.display.prototype.construct.call(this); 95 96 // Make this component orientation agnostic. 97 this.pos = this.options.vertical ? 'pageY' : 'pageX'; 98 this.offset = this.options.vertical ? 'top' : 'left'; 99 this.margin = this.options.vertical ? 'marginTop' : 'marginLeft'; 100 this.size = this.options.vertical ? 'height' : 'width'; 101 this.outer = this.options.vertical ? 'outerHeight' : 'outerWidth'; 102 103 this.getMousePos = function(event) { 104 return (event[this.pos] - this.display.offset()[this.offset]); 105 }; 106 this.getPos = function(handlePos) { 107 if (this.options.vertical) { 108 return this.ratio * (this.scrollSize - (handlePos + this.handleSize)); 109 } 110 else { 111 return this.ratio * handlePos; 112 } 113 }; 114 this.getHandlePos = function(pos) { 115 if (this.options.vertical) { 116 return this.scrollSize - (this.handleSize + (pos / this.ratio)); 117 } 118 else { 119 return (pos / this.ratio); 120 } 121 }; 122 123 // If they have a scroll bar. 124 if (this.elements.scroll) { 125 126 // Get the values of our variables. 127 var scroll = this.elements.scroll; 128 this.handleSize = 0; 129 this.scrollTop = 0; 130 this.mousePos = 0; 131 132 // Refresh the scroll. 133 this.refresh(); 134 135 // Create the scroll bar slider control. 136 this.scroll = scroll.slider({ 137 orientation: this.options.vertical ? 'vertical' : 'horizontal', 138 max: this.scrollSize, 139 create: (function(scroll, vertical) { 140 return function(event, ui) { 141 var handle = jQuery('.ui-slider-handle', event.target); 142 scroll.handleSize = handle[scroll.outer](); 143 scroll.scrollTop = (scroll.scrollSize - scroll.handleSize); 144 var initValue = vertical ? scroll.scrollTop : 0; 145 jQuery(this).slider('option', 'value', initValue); 146 }; 147 })(this, this.options.vertical), 148 slide: (function(scroll, vertical) { 149 return function(event, ui) { 150 // Get the new position. 151 var pos = scroll.getPos(ui.value); 152 153 // Ensure it doesn't go over the limits. 154 if (vertical && (pos < 0)) { 155 scroll.scroll.slider('option', 'value', scroll.scrollTop); 156 return false; 157 } 158 else if (!vertical && (ui.value > scroll.scrollTop)) { 159 scroll.scroll.slider('option', 'value', scroll.scrollTop); 160 return false; 161 } 162 163 // Set our list position. 164 scroll.elements.list.css(scroll.margin, -pos + 'px'); 165 return true; 166 }; 167 })(this, this.options.vertical) 168 }); 169 170 // If they wish to have auto scroll mode. 171 if (this.options.scrollMode == 'auto') { 172 173 // Bind to the mouse events. 174 this.elements.list.bind('mousemove', (function(scroll) { 175 176 // Return our event function. 177 return function(event) { 178 event.preventDefault(); 179 scroll.mousePos = event[scroll.pos]; 180 scroll.mousePos -= scroll.display.offset()[scroll.offset]; 181 }; 182 183 })(this)).bind('mouseenter', (function(scroll) { 184 185 // Return our event function. 186 return function(event) { 187 event.preventDefault(); 188 scroll.scrolling = true; 189 setTimeout(function setScroll() { 190 if (scroll.scrolling) { 191 192 // Get the delta. 193 var delta = scroll.mousePos - scroll.scrollMid; 194 195 // Determine if we are within our hysteresis. 196 if (Math.abs(delta) > scroll.options.hysteresis) { 197 198 // Get the hysteresis and delta. 199 var hyst = scroll.options.hysteresis; 200 hyst *= (delta > 0) ? -1 : 0; 201 delta = (scroll.options.scrollSpeed * (delta + hyst)); 202 delta /= scroll.scrollMid; 203 204 // Get the scroll position. 205 var pos = scroll.elements.list.css(scroll.margin); 206 pos = parseFloat(pos) - delta; 207 pos = (pos > 0) ? 0 : pos; 208 209 // Get the maximum top position. 210 var top = -scroll.listSize + scroll.scrollSize; 211 pos = (pos < top) ? top : pos; 212 213 // Set the new scroll position. 214 scroll.elements.list.css(scroll.margin, pos + 'px'); 215 216 // Set the scroll position. 217 pos = scroll.getHandlePos(-pos); 218 scroll.scroll.slider('option', 'value', pos); 219 } 220 221 // Set timeout to try again. 222 setTimeout(setScroll, 20); 223 } 224 }, 20); 225 }; 226 227 })(this)).bind('mouseleave', (function(scroll) { 228 229 // Return our event function. 230 return function(event) { 231 event.preventDefault(); 232 scroll.scrolling = false; 233 }; 234 235 })(this)); 236 } 237 } 238 }; 239 240 /** 241 * Refreshes the scroll list. 242 */ 243 osmplayer.scroll.prototype.refresh = function() { 244 245 // The list size. 246 if (this.options.vertical) { 247 this.listSize = this.elements.list.height(); 248 } 249 else { 250 this.listSize = 0; 251 jQuery.each(this.elements.list.children(), (function(scroll) { 252 return function() { 253 scroll.listSize += jQuery(this)[scroll.outer](); 254 }; 255 })(this)); 256 257 // Set the list size. 258 this.elements.list[this.size](this.listSize); 259 } 260 261 // Refresh the list. 262 this.onResize(); 263 264 // Set the scroll position. 265 if (this.scroll) { 266 this.elements.list.css(this.margin, '0px'); 267 this.scroll.slider('option', 'value', this.getHandlePos(0)); 268 } 269 }; 270 271 /** 272 * Refresh all the variables that may change. 273 */ 274 osmplayer.scroll.prototype.onResize = function() { 275 this.scrollSize = this.elements.scroll[this.size](); 276 this.scrollMid = this.scrollSize / 2; 277 this.scrollTop = (this.scrollSize - this.handleSize); 278 this.ratio = (this.listSize - this.scrollSize); 279 this.ratio /= (this.scrollSize - this.handleSize); 280 if (this.scroll) { 281 this.scroll.slider('option', 'max', this.scrollSize); 282 } 283 }; 284