1 var osmplayer = osmplayer || {}; 2 (function(exports) { 3 /*! 4 * iScroll Lite base on iScroll v4.1.6 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org 5 * Released under MIT license, http://cubiq.org/license 6 */ 7 8 (function(){ 9 var m = Math, 10 mround = function (r) { return r >> 0; }, 11 vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : 12 (/firefox/i).test(navigator.userAgent) ? 'Moz' : 13 'opera' in window ? 'O' : '', 14 15 // Browser capabilities 16 isAndroid = (/android/gi).test(navigator.appVersion), 17 isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), 18 isPlaybook = (/playbook/gi).test(navigator.appVersion), 19 isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), 20 21 has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), 22 hasTouch = 'ontouchstart' in window && !isTouchPad, 23 hasTransform = vendor + 'Transform' in document.documentElement.style, 24 hasTransitionEnd = isIDevice || isPlaybook, 25 26 nextFrame = (function() { 27 return window.requestAnimationFrame 28 || window.webkitRequestAnimationFrame 29 || window.mozRequestAnimationFrame 30 || window.oRequestAnimationFrame 31 || window.msRequestAnimationFrame 32 || function(callback) { return setTimeout(callback, 17); } 33 })(), 34 cancelFrame = (function () { 35 return window.cancelRequestAnimationFrame 36 || window.webkitCancelAnimationFrame 37 || window.webkitCancelRequestAnimationFrame 38 || window.mozCancelRequestAnimationFrame 39 || window.oCancelRequestAnimationFrame 40 || window.msCancelRequestAnimationFrame 41 || clearTimeout 42 })(), 43 44 // Events 45 RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize', 46 START_EV = hasTouch ? 'touchstart' : 'mousedown', 47 MOVE_EV = hasTouch ? 'touchmove' : 'mousemove', 48 END_EV = hasTouch ? 'touchend' : 'mouseup', 49 CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup', 50 51 // Helpers 52 trnOpen = 'translate' + (has3d ? '3d(' : '('), 53 trnClose = has3d ? ',0)' : ')', 54 55 // Constructor 56 iScroll = function (el, options) { 57 var that = this, 58 doc = document, 59 i; 60 61 that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); 62 that.wrapper.style.overflow = 'hidden'; 63 that.scroller = that.wrapper.children[0]; 64 65 // Default options 66 that.options = { 67 hScroll: true, 68 vScroll: true, 69 x: 0, 70 y: 0, 71 bounce: true, 72 bounceLock: false, 73 momentum: true, 74 lockDirection: true, 75 useTransform: true, 76 useTransition: false, 77 78 // Events 79 onRefresh: null, 80 onBeforeScrollStart: function (e) { e.preventDefault(); }, 81 onScrollStart: null, 82 onBeforeScrollMove: null, 83 onScrollMove: null, 84 onBeforeScrollEnd: null, 85 onScrollEnd: null, 86 onTouchEnd: null, 87 onDestroy: null 88 }; 89 90 // User defined options 91 for (i in options) that.options[i] = options[i]; 92 93 // Set starting position 94 that.x = that.options.x; 95 that.y = that.options.y; 96 97 // Normalize options 98 that.options.useTransform = hasTransform ? that.options.useTransform : false; 99 that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar; 100 that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar; 101 that.options.useTransition = hasTransitionEnd && that.options.useTransition; 102 103 // Set some default styles 104 that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left'; 105 that.scroller.style[vendor + 'TransitionDuration'] = '0'; 106 that.scroller.style[vendor + 'TransformOrigin'] = '0 0'; 107 if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)'; 108 109 if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose; 110 else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; 111 112 that.refresh(); 113 114 that._bind(RESIZE_EV, window); 115 that._bind(START_EV); 116 if (!hasTouch) that._bind('mouseout', that.wrapper); 117 }; 118 119 // Prototype 120 iScroll.prototype = { 121 enabled: true, 122 x: 0, 123 y: 0, 124 steps: [], 125 scale: 1, 126 127 handleEvent: function (e) { 128 var that = this; 129 switch(e.type) { 130 case START_EV: 131 if (!hasTouch && e.button !== 0) return; 132 that._start(e); 133 break; 134 case MOVE_EV: that._move(e); break; 135 case END_EV: 136 case CANCEL_EV: that._end(e); break; 137 case RESIZE_EV: that._resize(); break; 138 case 'mouseout': that._mouseout(e); break; 139 case 'webkitTransitionEnd': that._transitionEnd(e); break; 140 } 141 }, 142 143 _resize: function () { 144 this.refresh(); 145 }, 146 147 _pos: function (x, y) { 148 x = this.hScroll ? x : 0; 149 y = this.vScroll ? y : 0; 150 151 if (this.options.useTransform) { 152 this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')'; 153 } else { 154 x = mround(x); 155 y = mround(y); 156 this.scroller.style.left = x + 'px'; 157 this.scroller.style.top = y + 'px'; 158 } 159 160 this.x = x; 161 this.y = y; 162 }, 163 164 _start: function (e) { 165 var that = this, 166 point = hasTouch ? e.touches[0] : e, 167 matrix, x, y; 168 169 if (!that.enabled) return; 170 171 if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e); 172 173 if (that.options.useTransition) that._transitionTime(0); 174 175 that.moved = false; 176 that.animating = false; 177 that.zoomed = false; 178 that.distX = 0; 179 that.distY = 0; 180 that.absDistX = 0; 181 that.absDistY = 0; 182 that.dirX = 0; 183 that.dirY = 0; 184 185 if (that.options.momentum) { 186 if (that.options.useTransform) { 187 // Very lame general purpose alternative to CSSMatrix 188 matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(','); 189 x = matrix[4] * 1; 190 y = matrix[5] * 1; 191 } else { 192 x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; 193 y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; 194 } 195 196 if (x != that.x || y != that.y) { 197 if (that.options.useTransition) that._unbind('webkitTransitionEnd'); 198 else cancelFrame(that.aniTime); 199 that.steps = []; 200 that._pos(x, y); 201 } 202 } 203 204 that.startX = that.x; 205 that.startY = that.y; 206 that.pointX = point.pageX; 207 that.pointY = point.pageY; 208 209 that.startTime = e.timeStamp || Date.now(); 210 211 if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); 212 213 that._bind(MOVE_EV); 214 that._bind(END_EV); 215 that._bind(CANCEL_EV); 216 }, 217 218 _move: function (e) { 219 var that = this, 220 point = hasTouch ? e.touches[0] : e, 221 deltaX = point.pageX - that.pointX, 222 deltaY = point.pageY - that.pointY, 223 newX = that.x + deltaX, 224 newY = that.y + deltaY, 225 timestamp = e.timeStamp || Date.now(); 226 227 if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e); 228 229 that.pointX = point.pageX; 230 that.pointY = point.pageY; 231 232 // Slow down if outside of the boundaries 233 if (newX > 0 || newX < that.maxScrollX) { 234 newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX; 235 } 236 if (newY > 0 || newY < that.maxScrollY) { 237 newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= 0 || that.maxScrollY >= 0 ? 0 : that.maxScrollY; 238 } 239 240 that.distX += deltaX; 241 that.distY += deltaY; 242 that.absDistX = m.abs(that.distX); 243 that.absDistY = m.abs(that.distY); 244 245 if (that.absDistX < 6 && that.absDistY < 6) { 246 return; 247 } 248 249 // Lock direction 250 if (that.options.lockDirection) { 251 if (that.absDistX > that.absDistY + 5) { 252 newY = that.y; 253 deltaY = 0; 254 } else if (that.absDistY > that.absDistX + 5) { 255 newX = that.x; 256 deltaX = 0; 257 } 258 } 259 260 that.moved = true; 261 that._pos(newX, newY); 262 that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; 263 that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; 264 265 if (timestamp - that.startTime > 300) { 266 that.startTime = timestamp; 267 that.startX = that.x; 268 that.startY = that.y; 269 } 270 271 if (that.options.onScrollMove) that.options.onScrollMove.call(that, e); 272 }, 273 274 _end: function (e) { 275 if (hasTouch && e.touches.length != 0) return; 276 277 var that = this, 278 point = hasTouch ? e.changedTouches[0] : e, 279 target, ev, 280 momentumX = { dist:0, time:0 }, 281 momentumY = { dist:0, time:0 }, 282 duration = (e.timeStamp || Date.now()) - that.startTime, 283 newPosX = that.x, 284 newPosY = that.y, 285 newDuration; 286 287 that._unbind(MOVE_EV); 288 that._unbind(END_EV); 289 that._unbind(CANCEL_EV); 290 291 if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); 292 293 if (!that.moved) { 294 if (hasTouch) { 295 // Find the last touched element 296 target = point.target; 297 while (target.nodeType != 1) target = target.parentNode; 298 299 if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { 300 ev = document.createEvent('MouseEvents'); 301 ev.initMouseEvent('click', true, true, e.view, 1, 302 point.screenX, point.screenY, point.clientX, point.clientY, 303 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 304 0, null); 305 ev._fake = true; 306 target.dispatchEvent(ev); 307 } 308 } 309 310 that._resetPos(200); 311 312 if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); 313 return; 314 } 315 316 if (duration < 300 && that.options.momentum) { 317 momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX; 318 momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y : 0), that.options.bounce ? that.wrapperH : 0) : momentumY; 319 320 newPosX = that.x + momentumX.dist; 321 newPosY = that.y + momentumY.dist; 322 323 if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 }; 324 if ((that.y > 0 && newPosY > 0) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 }; 325 } 326 327 if (momentumX.dist || momentumY.dist) { 328 newDuration = m.max(m.max(momentumX.time, momentumY.time), 10); 329 330 that.scrollTo(mround(newPosX), mround(newPosY), newDuration); 331 332 if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); 333 return; 334 } 335 336 that._resetPos(200); 337 if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); 338 }, 339 340 _resetPos: function (time) { 341 var that = this, 342 resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x, 343 resetY = that.y >= 0 || that.maxScrollY > 0 ? 0 : that.y < that.maxScrollY ? that.maxScrollY : that.y; 344 345 if (resetX == that.x && resetY == that.y) { 346 if (that.moved) { 347 if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end 348 that.moved = false; 349 } 350 351 return; 352 } 353 354 that.scrollTo(resetX, resetY, time || 0); 355 }, 356 357 _mouseout: function (e) { 358 var t = e.relatedTarget; 359 360 if (!t) { 361 this._end(e); 362 return; 363 } 364 365 while (t = t.parentNode) if (t == this.wrapper) return; 366 367 this._end(e); 368 }, 369 370 _transitionEnd: function (e) { 371 var that = this; 372 373 if (e.target != that.scroller) return; 374 375 that._unbind('webkitTransitionEnd'); 376 377 that._startAni(); 378 }, 379 380 /** 381 * 382 * Utilities 383 * 384 */ 385 _startAni: function () { 386 var that = this, 387 startX = that.x, startY = that.y, 388 startTime = Date.now(), 389 step, easeOut, 390 animate; 391 392 if (that.animating) return; 393 394 if (!that.steps.length) { 395 that._resetPos(400); 396 return; 397 } 398 399 step = that.steps.shift(); 400 401 if (step.x == startX && step.y == startY) step.time = 0; 402 403 that.animating = true; 404 that.moved = true; 405 406 if (that.options.useTransition) { 407 that._transitionTime(step.time); 408 that._pos(step.x, step.y); 409 that.animating = false; 410 if (step.time) that._bind('webkitTransitionEnd'); 411 else that._resetPos(0); 412 return; 413 } 414 415 animate = function () { 416 var now = Date.now(), 417 newX, newY; 418 419 if (now >= startTime + step.time) { 420 that._pos(step.x, step.y); 421 that.animating = false; 422 if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end 423 that._startAni(); 424 return; 425 } 426 427 now = (now - startTime) / step.time - 1; 428 easeOut = m.sqrt(1 - now * now); 429 newX = (step.x - startX) * easeOut + startX; 430 newY = (step.y - startY) * easeOut + startY; 431 that._pos(newX, newY); 432 if (that.animating) that.aniTime = nextFrame(animate); 433 }; 434 435 animate(); 436 }, 437 438 _transitionTime: function (time) { 439 this.scroller.style[vendor + 'TransitionDuration'] = time + 'ms'; 440 }, 441 442 _momentum: function (dist, time, maxDistUpper, maxDistLower, size) { 443 var deceleration = 0.0006, 444 speed = m.abs(dist) / time, 445 newDist = (speed * speed) / (2 * deceleration), 446 newTime = 0, outsideDist = 0; 447 448 // Proportinally reduce speed if we are outside of the boundaries 449 if (dist > 0 && newDist > maxDistUpper) { 450 outsideDist = size / (6 / (newDist / speed * deceleration)); 451 maxDistUpper = maxDistUpper + outsideDist; 452 speed = speed * maxDistUpper / newDist; 453 newDist = maxDistUpper; 454 } else if (dist < 0 && newDist > maxDistLower) { 455 outsideDist = size / (6 / (newDist / speed * deceleration)); 456 maxDistLower = maxDistLower + outsideDist; 457 speed = speed * maxDistLower / newDist; 458 newDist = maxDistLower; 459 } 460 461 newDist = newDist * (dist < 0 ? -1 : 1); 462 newTime = speed / deceleration; 463 464 return { dist: newDist, time: mround(newTime) }; 465 }, 466 467 _offset: function (el) { 468 var left = -el.offsetLeft, 469 top = -el.offsetTop; 470 471 while (el = el.offsetParent) { 472 left -= el.offsetLeft; 473 top -= el.offsetTop; 474 } 475 476 return { left: left, top: top }; 477 }, 478 479 _bind: function (type, el, bubble) { 480 (el || this.scroller).addEventListener(type, this, !!bubble); 481 }, 482 483 _unbind: function (type, el, bubble) { 484 (el || this.scroller).removeEventListener(type, this, !!bubble); 485 }, 486 487 488 /** 489 * 490 * Public methods 491 * 492 */ 493 destroy: function () { 494 var that = this; 495 496 that.scroller.style[vendor + 'Transform'] = ''; 497 498 // Remove the event listeners 499 that._unbind(RESIZE_EV, window); 500 that._unbind(START_EV); 501 that._unbind(MOVE_EV); 502 that._unbind(END_EV); 503 that._unbind(CANCEL_EV); 504 that._unbind('mouseout', that.wrapper); 505 if (that.options.useTransition) that._unbind('webkitTransitionEnd'); 506 507 if (that.options.onDestroy) that.options.onDestroy.call(that); 508 }, 509 510 refresh: function () { 511 var that = this, 512 offset; 513 514 that.wrapperW = that.wrapper.clientWidth; 515 that.wrapperH = that.wrapper.clientHeight; 516 517 that.scrollerW = that.scroller.offsetWidth; 518 that.scrollerH = that.scroller.offsetHeight; 519 that.maxScrollX = that.wrapperW - that.scrollerW; 520 that.maxScrollY = that.wrapperH - that.scrollerH; 521 that.dirX = 0; 522 that.dirY = 0; 523 524 that.hScroll = that.options.hScroll && that.maxScrollX < 0; 525 that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH); 526 527 offset = that._offset(that.wrapper); 528 that.wrapperOffsetLeft = -offset.left; 529 that.wrapperOffsetTop = -offset.top; 530 531 532 that.scroller.style[vendor + 'TransitionDuration'] = '0'; 533 534 that._resetPos(200); 535 }, 536 537 scrollTo: function (x, y, time, relative) { 538 var that = this, 539 step = x, 540 i, l; 541 542 that.stop(); 543 544 if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; 545 546 for (i=0, l=step.length; i<l; i++) { 547 if (step[i].relative) { step[i].x = that.x - step[i].x; step[i].y = that.y - step[i].y; } 548 that.steps.push({ x: step[i].x, y: step[i].y, time: step[i].time || 0 }); 549 } 550 551 that._startAni(); 552 }, 553 554 scrollToElement: function (el, time) { 555 var that = this, pos; 556 el = el.nodeType ? el : that.scroller.querySelector(el); 557 if (!el) return; 558 559 pos = that._offset(el); 560 pos.left += that.wrapperOffsetLeft; 561 pos.top += that.wrapperOffsetTop; 562 563 pos.left = pos.left > 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left; 564 pos.top = pos.top > 0 ? 0 : pos.top < that.maxScrollY ? that.maxScrollY : pos.top; 565 time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; 566 567 that.scrollTo(pos.left, pos.top, time); 568 }, 569 570 disable: function () { 571 this.stop(); 572 this._resetPos(0); 573 this.enabled = false; 574 575 // If disabled after touchstart we make sure that there are no left over events 576 this._unbind(MOVE_EV); 577 this._unbind(END_EV); 578 this._unbind(CANCEL_EV); 579 }, 580 581 enable: function () { 582 this.enabled = true; 583 }, 584 585 stop: function () { 586 cancelFrame(this.aniTime); 587 this.steps = []; 588 this.moved = false; 589 this.animating = false; 590 } 591 }; 592 593 if (typeof exports !== 'undefined') exports.iScroll = iScroll; 594 else window.iScroll = iScroll; 595 596 })(); 597 })(osmplayer); 598 /** 599 * @constructor 600 * @extends minplayer.display 601 * @class This class creates the playlist functionality for the minplayer. 602 * 603 * @param {object} context The jQuery context. 604 * @param {object} options This components options. 605 */ 606 osmplayer.playlist = function(context, options) { 607 608 // Derive from display 609 minplayer.display.call(this, 'playlist', context, options); 610 }; 611 612 /** Derive from minplayer.display. */ 613 osmplayer.playlist.prototype = new minplayer.display(); 614 615 /** Reset the constructor. */ 616 osmplayer.playlist.prototype.constructor = osmplayer.playlist; 617 618 /** 619 * Returns the default options for this plugin. 620 * 621 * @param {object} options The default options for this plugin. 622 */ 623 osmplayer.playlist.prototype.defaultOptions = function(options) { 624 options.vertical = true; 625 options.playlist = ''; 626 options.pageLimit = 10; 627 options.autoNext = true; 628 options.shuffle = false; 629 options.loop = false; 630 options.hysteresis = 40; 631 options.scrollSpeed = 20; 632 options.scrollMode = 'auto'; 633 minplayer.display.prototype.defaultOptions.call(this, options); 634 }; 635 636 /** 637 * @see minplayer.plugin#construct 638 */ 639 osmplayer.playlist.prototype.construct = function() { 640 641 /** The nodes within this playlist. */ 642 this.nodes = []; 643 644 // Current page. 645 this.page = -1; 646 647 // The total amount of nodes. 648 this.totalItems = 0; 649 650 // The current loaded item index. 651 this.currentItem = -1; 652 653 // The play playqueue. 654 this.playqueue = []; 655 656 // The playqueue position. 657 this.playqueuepos = 0; 658 659 // The current playlist. 660 this.playlist = this.options.playlist; 661 662 // Create the scroll bar. 663 this.scroll = null; 664 665 // Create our orientation variable. 666 this.orient = { 667 pos: this.options.vertical ? 'y' : 'x', 668 pagePos: this.options.vertical ? 'pageY' : 'pageX', 669 offset: this.options.vertical ? 'top' : 'left', 670 wrapperSize: this.options.vertical ? 'wrapperH' : 'wrapperW', 671 minScroll: this.options.vertical ? 'minScrollY' : 'minScrollX', 672 maxScroll: this.options.vertical ? 'maxScrollY' : 'maxScrollX', 673 size: this.options.vertical ? 'height' : 'width' 674 }; 675 676 // Create the pager. 677 this.pager = this.create('pager', 'osmplayer'); 678 this.pager.ubind(this.uuid + ':nextPage', (function(playlist) { 679 return function(event) { 680 playlist.nextPage(); 681 }; 682 })(this)); 683 this.pager.ubind(this.uuid + ':prevPage', (function(playlist) { 684 return function(event) { 685 playlist.prevPage(); 686 }; 687 })(this)); 688 689 // Call the minplayer plugin constructor. 690 minplayer.display.prototype.construct.call(this); 691 692 // Load the "next" item. 693 this.hasPlaylist = this.next(); 694 695 // Say that we are ready. 696 this.ready(); 697 }; 698 699 /** 700 * @see minplayer.plugin.onAdded 701 */ 702 osmplayer.playlist.prototype.onAdded = function(plugin) { 703 704 // Get the media. 705 if (this.options.autoNext) { 706 707 // Get the player from this plugin. 708 plugin.get('player', (function(playlist) { 709 return function(player) { 710 player.ubind(playlist.uuid + ':player_ended', function(event) { 711 if (playlist.hasPlaylist) { 712 if (typeof player.options.originalAutoPlay == 'undefined') { 713 player.options.originalAutoPlay = player.options.autoplay; 714 } 715 player.options.autoplay = true; 716 playlist.next(); 717 } 718 }); 719 }; 720 })(this)); 721 } 722 }; 723 724 /** 725 * Wrapper around the scroll scrollTo method. 726 * 727 * @param {number} pos The position you would like to set the list. 728 * @param {boolean} relative If this is a relative position change. 729 */ 730 osmplayer.playlist.prototype.scrollTo = function(pos, relative) { 731 if (this.scroll) { 732 this.scroll.options.hideScrollbar = false; 733 if (this.options.vertical) { 734 this.scroll.scrollTo(0, pos, 0, relative); 735 } 736 else { 737 this.scroll.scrollTo(pos, 0, 0, relative); 738 } 739 this.scroll.options.hideScrollbar = true; 740 } 741 }; 742 743 /** 744 * Refresh the scrollbar. 745 */ 746 osmplayer.playlist.prototype.refreshScroll = function() { 747 748 // Make sure that our window has the addEventListener to keep IE happy. 749 if (!window.addEventListener) { 750 setTimeout((function(playlist) { 751 return function() { 752 playlist.refreshScroll.call(playlist); 753 }; 754 })(this), 200); 755 return; 756 } 757 758 // Check the size of the playlist. 759 var list = this.elements.list; 760 var scroll = this.elements.scroll; 761 762 // Destroy the scroll bar first. 763 if (this.scroll) { 764 this.scroll.scrollTo(0, 0); 765 this.scroll.destroy(); 766 this.scroll = null; 767 this.elements.list 768 .unbind('mousemove') 769 .unbind('mouseenter') 770 .unbind('mouseleave'); 771 } 772 773 // Need to force the width of the list. 774 if (!this.options.vertical) { 775 var listSize = 0; 776 jQuery.each(this.elements.list.children(), function() { 777 listSize += jQuery(this).outerWidth(); 778 }); 779 this.elements.list.width(listSize); 780 } 781 782 // Check to see if we should add a scroll bar functionality. 783 if ((list.length > 0) && 784 (scroll.length > 0) && 785 (list[this.orient.size]() > scroll[this.orient.size]())) { 786 787 // Setup the osmplayer.iScroll component. 788 this.scroll = new osmplayer.iScroll(this.elements.scroll.eq(0)[0], { 789 hScroll: !this.options.vertical, 790 hScrollbar: !this.options.vertical, 791 vScroll: this.options.vertical, 792 vScrollbar: this.options.vertical, 793 hideScrollbar: (this.options.scrollMode !== 'none') 794 }); 795 796 // Use autoScroll for non-touch devices. 797 if ((this.options.scrollMode == 'auto') && !minplayer.hasTouch) { 798 799 // Bind to the mouse events for autoscrolling. 800 this.elements.list.bind('mousemove', (function(playlist) { 801 return function(event) { 802 event.preventDefault(); 803 var offset = playlist.display.offset()[playlist.orient.offset]; 804 playlist.mousePos = event[playlist.orient.pagePos]; 805 playlist.mousePos -= offset; 806 }; 807 })(this)).bind('mouseenter', (function(playlist) { 808 return function(event) { 809 event.preventDefault(); 810 playlist.scrolling = true; 811 var setScroll = function() { 812 if (playlist.scrolling) { 813 var scrollSize = playlist.scroll[playlist.orient.wrapperSize]; 814 var scrollMid = (scrollSize / 2); 815 var delta = playlist.mousePos - scrollMid; 816 if (Math.abs(delta) > playlist.options.hysteresis) { 817 var hyst = playlist.options.hysteresis; 818 hyst *= (delta > 0) ? -1 : 0; 819 delta = (playlist.options.scrollSpeed * (delta + hyst)); 820 delta /= scrollMid; 821 var pos = playlist.scroll[playlist.orient.pos] - delta; 822 var min = playlist.scroll[playlist.orient.minScroll] || 0; 823 var max = playlist.scroll[playlist.orient.maxScroll]; 824 if (pos >= min) { 825 playlist.scrollTo(min); 826 } 827 else if (pos <= max) { 828 playlist.scrollTo(max); 829 } 830 else { 831 playlist.scrollTo(delta, true); 832 } 833 } 834 835 // Set timeout to try again. 836 setTimeout(setScroll, 30); 837 } 838 }; 839 setScroll(); 840 }; 841 })(this)).bind('mouseleave', (function(playlist) { 842 return function(event) { 843 event.preventDefault(); 844 playlist.scrolling = false; 845 }; 846 })(this)); 847 } 848 849 this.scroll.refresh(); 850 this.scroll.scrollTo(0, 0, 200); 851 } 852 }; 853 854 /** 855 * Adds a new node to the playlist. 856 * 857 * @param {object} node The node that you would like to add to the playlist. 858 */ 859 osmplayer.playlist.prototype.addNode = function(node) { 860 861 // Get the current index for this node. 862 var index = this.nodes.length; 863 864 // Create the teaser object. 865 var teaser = this.create('teaser', 'osmplayer', this.elements.list); 866 867 // Set the node for this teaser. 868 teaser.setNode(node); 869 870 // Bind to when it loads. 871 teaser.ubind(this.uuid + ':nodeLoad', (function(playlist) { 872 return function(event, data) { 873 playlist.loadItem(index, true); 874 }; 875 })(this)); 876 877 // Add this to our nodes array. 878 this.nodes.push(teaser); 879 }; 880 881 /** 882 * Sets the playlist. 883 * 884 * @param {object} playlist The playlist object. 885 * @param {integer} loadIndex The index of the item to load. 886 */ 887 osmplayer.playlist.prototype.set = function(playlist, loadIndex) { 888 889 // Check to make sure the playlist is an object. 890 if (typeof playlist !== 'object') { 891 this.trigger('error', 'Playlist must be an object to set'); 892 return; 893 } 894 895 // Check to make sure the playlist has correct format. 896 if (!playlist.hasOwnProperty('total_rows')) { 897 this.trigger('error', 'Unknown playlist format.'); 898 return; 899 } 900 901 // Make sure the playlist has some rows. 902 if (playlist.total_rows && playlist.nodes.length) { 903 904 // Set the total rows. 905 this.totalItems = playlist.total_rows; 906 this.currentItem = 0; 907 908 // Show or hide the next page if there is or is not a next page. 909 if ((((this.page + 1) * this.options.pageLimit) >= this.totalItems) || 910 (this.totalItems == playlist.nodes.length)) { 911 this.pager.nextPage.hide(); 912 } 913 else { 914 this.pager.nextPage.show(); 915 } 916 917 var teaser = null; 918 var numNodes = playlist.nodes.length; 919 this.elements.list.empty(); 920 this.nodes = []; 921 922 // Iterate through all the nodes. 923 for (var index = 0; index < numNodes; index++) { 924 925 // Add this node to the playlist. 926 this.addNode(playlist.nodes[index]); 927 928 // If the index is equal to the loadIndex. 929 if (loadIndex === index) { 930 this.loadItem(index); 931 } 932 } 933 934 // Refresh the sizes. 935 this.refreshScroll(); 936 937 // Trigger that the playlist has loaded. 938 this.trigger('playlistLoad', playlist); 939 } 940 941 // Show that we are no longer busy. 942 if (this.elements.playlist_busy) { 943 this.elements.playlist_busy.hide(); 944 } 945 }; 946 947 /** 948 * Stores the current playlist state in the playqueue. 949 */ 950 osmplayer.playlist.prototype.setQueue = function() { 951 952 // Add this item to the playqueue. 953 this.playqueue.push({ 954 page: this.page, 955 item: this.currentItem 956 }); 957 958 // Store the current playqueue position. 959 this.playqueuepos = this.playqueue.length; 960 }; 961 962 /** 963 * Loads the next item. 964 * 965 * @return {boolean} TRUE if loaded, FALSE if not. 966 */ 967 osmplayer.playlist.prototype.next = function() { 968 var item = 0, page = this.page; 969 970 // See if we are at the front of the playqueue. 971 if (this.playqueuepos >= this.playqueue.length) { 972 973 // If this is shuffle, then load a random item. 974 if (this.options.shuffle) { 975 item = Math.floor(Math.random() * this.totalItems); 976 page = Math.floor(item / this.options.pageLimit); 977 item = item % this.options.pageLimit; 978 return this.load(page, item); 979 } 980 else { 981 982 // Otherwise, increment the current item by one. 983 item = (this.currentItem + 1); 984 if (item >= this.nodes.length) { 985 return this.load(page + 1, 0); 986 } 987 else { 988 return this.loadItem(item); 989 } 990 } 991 } 992 else { 993 994 // Load the next item in the playqueue. 995 this.playqueuepos = this.playqueuepos + 1; 996 var currentQueue = this.playqueue[this.playqueuepos]; 997 return this.load(currentQueue.page, currentQueue.item); 998 } 999 }; 1000 1001 /** 1002 * Loads the previous item. 1003 * 1004 * @return {boolean} TRUE if loaded, FALSE if not. 1005 */ 1006 osmplayer.playlist.prototype.prev = function() { 1007 1008 // Move back into the playqueue. 1009 this.playqueuepos = this.playqueuepos - 1; 1010 this.playqueuepos = (this.playqueuepos < 0) ? 0 : this.playqueuepos; 1011 var currentQueue = this.playqueue[this.playqueuepos]; 1012 if (currentQueue) { 1013 return this.load(currentQueue.page, currentQueue.item); 1014 } 1015 return false; 1016 }; 1017 1018 /** 1019 * Loads a playlist node. 1020 * 1021 * @param {number} index The index of the item you would like to load. 1022 * @return {boolean} TRUE if loaded, FALSE if not. 1023 */ 1024 osmplayer.playlist.prototype.loadItem = function(index, autoplay) { 1025 if (index < this.nodes.length) { 1026 this.setQueue(); 1027 1028 // Get the teaser at the current index and deselect it. 1029 var teaser = this.nodes[this.currentItem]; 1030 teaser.select(false); 1031 this.currentItem = index; 1032 1033 // Get the new teaser and select it. 1034 teaser = this.nodes[index]; 1035 teaser.select(true); 1036 teaser.node.autoplay = !!autoplay; 1037 this.trigger('nodeLoad', teaser.node); 1038 return true; 1039 } 1040 1041 return false; 1042 }; 1043 1044 /** 1045 * Loads the next page. 1046 * 1047 * @param {integer} loadIndex The index of the item to load. 1048 * @return {boolean} TRUE if loaded, FALSE if not. 1049 */ 1050 osmplayer.playlist.prototype.nextPage = function(loadIndex) { 1051 return this.load(this.page + 1, loadIndex); 1052 }; 1053 1054 /** 1055 * Loads the previous page. 1056 * 1057 * @param {integer} loadIndex The index of the item to load. 1058 * @return {boolean} TRUE if loaded, FALSE if not. 1059 */ 1060 osmplayer.playlist.prototype.prevPage = function(loadIndex) { 1061 return this.load(this.page - 1, loadIndex); 1062 }; 1063 1064 /** 1065 * Loads a playlist. 1066 * 1067 * @param {integer} page The page to load. 1068 * @param {integer} loadIndex The index of the item to load. 1069 * @return {boolean} TRUE if loaded, FALSE if not. 1070 */ 1071 osmplayer.playlist.prototype.load = function(page, loadIndex) { 1072 1073 // If the playlist and pages are the same, then no need to load. 1074 if ((this.playlist == this.options.playlist) && (page == this.page)) { 1075 return this.loadItem(loadIndex); 1076 } 1077 1078 // Set the new playlist. 1079 this.playlist = this.options.playlist; 1080 1081 // Return if there aren't any playlists to play. 1082 if (!this.playlist) { 1083 return false; 1084 } 1085 1086 // Determine if we need to loop. 1087 var maxPages = Math.floor(this.totalItems / this.options.pageLimit); 1088 if (page > maxPages) { 1089 if (this.options.loop) { 1090 page = 0; 1091 loadIndex = 0; 1092 } 1093 else { 1094 return false; 1095 } 1096 } 1097 1098 // Say that we are busy. 1099 if (this.elements.playlist_busy) { 1100 this.elements.playlist_busy.show(); 1101 } 1102 1103 // Normalize the page. 1104 page = page || 0; 1105 page = (page < 0) ? 0 : page; 1106 1107 // Set the queue. 1108 this.setQueue(); 1109 1110 // Set the new page. 1111 this.page = page; 1112 1113 // Hide or show the page based on if we are on the first page. 1114 if (this.page === 0) { 1115 this.pager.prevPage.hide(); 1116 } 1117 else { 1118 this.pager.prevPage.show(); 1119 } 1120 1121 // If the playlist is an object, then go ahead and set it. 1122 if (typeof this.playlist == 'object') { 1123 this.set(this.playlist, loadIndex); 1124 if (this.playlist.endpoint) { 1125 this.playlist = this.options.playlist = this.playlist.endpoint; 1126 } 1127 return true; 1128 } 1129 1130 // Get the highest priority parser. 1131 var parser = osmplayer.parser['default']; 1132 for (var name in osmplayer.parser) { 1133 if (osmplayer.parser.hasOwnProperty(name)) { 1134 if (osmplayer.parser[name].valid(this.playlist)) { 1135 if (osmplayer.parser[name].priority > parser.priority) { 1136 parser = osmplayer.parser[name]; 1137 } 1138 } 1139 } 1140 } 1141 1142 // The start index. 1143 var start = this.page * this.options.pageLimit; 1144 1145 // Get the feed from the parser. 1146 var feed = parser.getFeed( 1147 this.playlist, 1148 start, 1149 this.options.pageLimit 1150 ); 1151 1152 // Build our request. 1153 var request = { 1154 type: 'GET', 1155 url: feed, 1156 success: (function(playlist) { 1157 return function(data) { 1158 playlist.set(parser.parse(data), loadIndex); 1159 }; 1160 })(this), 1161 error: (function(playlist) { 1162 return function(XMLHttpRequest, textStatus, errorThrown) { 1163 if (playlist.elements.playlist_busy) { 1164 playlist.elements.playlist_busy.hide(); 1165 } 1166 playlist.trigger('error', textStatus); 1167 }; 1168 })(this) 1169 }; 1170 1171 // Set the data if applicable. 1172 var dataType = parser.getType(); 1173 if (dataType) { 1174 request.dataType = dataType; 1175 } 1176 1177 // Perform an ajax callback. 1178 jQuery.ajax(request); 1179 1180 // Return that we did something. 1181 return true; 1182 }; 1183