1/*! 2 * jCarousel - Riding carousels with jQuery 3 * http://sorgalla.com/jcarousel/ 4 * 5 * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com) 6 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 7 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 8 * 9 * Built on top of the jQuery library 10 * http://jquery.com 11 * 12 * Inspired by the "Carousel Component" by Bill Scott 13 * http://billwscott.com/carousel/ 14 */ 15 16/*global window, jQuery */ 17(function($) { 18 // Default configuration properties. 19 var defaults = { 20 vertical: false, 21 rtl: false, 22 start: 1, 23 offset: 1, 24 size: null, 25 scroll: 3, 26 visible: null, 27 animation: 'normal', 28 easing: 'swing', 29 auto: 0, 30 wrap: null, 31 initCallback: null, 32 setupCallback: null, 33 reloadCallback: null, 34 itemLoadCallback: null, 35 itemFirstInCallback: null, 36 itemFirstOutCallback: null, 37 itemLastInCallback: null, 38 itemLastOutCallback: null, 39 itemVisibleInCallback: null, 40 itemVisibleOutCallback: null, 41 animationStepCallback: null, 42 buttonNextHTML: '<div></div>', 43 buttonPrevHTML: '<div></div>', 44 buttonNextEvent: 'click', 45 buttonPrevEvent: 'click', 46 buttonNextCallback: null, 47 buttonPrevCallback: null, 48 itemFallbackDimension: null 49 }, windowLoaded = false; 50 51 $(window).bind('load.jcarousel', function() { windowLoaded = true; }); 52 53 /** 54 * The jCarousel object. 55 * 56 * @constructor 57 * @class jcarousel 58 * @param e {HTMLElement} The element to create the carousel for. 59 * @param o {Object} A set of key/value pairs to set as configuration properties. 60 * @cat Plugins/jCarousel 61 */ 62 $.jcarousel = function(e, o) { 63 this.options = $.extend({}, defaults, o || {}); 64 65 this.locked = false; 66 this.autoStopped = false; 67 68 this.container = null; 69 this.clip = null; 70 this.list = null; 71 this.buttonNext = null; 72 this.buttonPrev = null; 73 this.buttonNextState = null; 74 this.buttonPrevState = null; 75 76 // Only set if not explicitly passed as option 77 if (!o || o.rtl === undefined) { 78 this.options.rtl = ($(e).attr('dir') || $('html').attr('dir') || '').toLowerCase() == 'rtl'; 79 } 80 81 this.wh = !this.options.vertical ? 'width' : 'height'; 82 this.lt = !this.options.vertical ? (this.options.rtl ? 'right' : 'left') : 'top'; 83 84 // Extract skin class 85 var skin = '', split = e.className.split(' '); 86 87 for (var i = 0; i < split.length; i++) { 88 if (split[i].indexOf('jcarousel-skin') != -1) { 89 $(e).removeClass(split[i]); 90 skin = split[i]; 91 break; 92 } 93 } 94 95 if (e.nodeName.toUpperCase() == 'UL' || e.nodeName.toUpperCase() == 'OL') { 96 this.list = $(e); 97 this.clip = this.list.parents('.jcarousel-clip'); 98 this.container = this.list.parents('.jcarousel-container'); 99 } else { 100 this.container = $(e); 101 this.list = this.container.find('ul,ol').eq(0); 102 this.clip = this.container.find('.jcarousel-clip'); 103 } 104 105 if (this.clip.size() === 0) { 106 this.clip = this.list.wrap('<div></div>').parent(); 107 } 108 109 if (this.container.size() === 0) { 110 this.container = this.clip.wrap('<div></div>').parent(); 111 } 112 113 if (skin !== '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1) { 114 this.container.wrap('<div class=" '+ skin + '"></div>'); 115 } 116 117 this.buttonPrev = $('.jcarousel-prev', this.container); 118 119 if (this.buttonPrev.size() === 0 && this.options.buttonPrevHTML !== null) { 120 this.buttonPrev = $(this.options.buttonPrevHTML).appendTo(this.container); 121 } 122 123 this.buttonPrev.addClass(this.className('jcarousel-prev')); 124 125 this.buttonNext = $('.jcarousel-next', this.container); 126 127 if (this.buttonNext.size() === 0 && this.options.buttonNextHTML !== null) { 128 this.buttonNext = $(this.options.buttonNextHTML).appendTo(this.container); 129 } 130 131 this.buttonNext.addClass(this.className('jcarousel-next')); 132 133 this.clip.addClass(this.className('jcarousel-clip')).css({ 134 position: 'relative' 135 }); 136 137 this.list.addClass(this.className('jcarousel-list')).css({ 138 overflow: 'hidden', 139 position: 'relative', 140 top: 0, 141 margin: 0, 142 padding: 0 143 }).css((this.options.rtl ? 'right' : 'left'), 0); 144 145 this.container.addClass(this.className('jcarousel-container')).css({ 146 position: 'relative' 147 }); 148 149 if (!this.options.vertical && this.options.rtl) {
150 this.container.addClass('jcarousel-direction-rtl').attr('dir', 'rtl'); 151 } 152 153 var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null; 154 var li = this.list.children('li'); 155 156 var self = this; 157 158 if (li.size() > 0) { 159 var wh = 0, j = this.options.offset; 160 li.each(function() { 161 self.format(this, j++); 162 wh += self.dimension(this, di); 163 }); 164 165 this.list.css(this.wh, (wh + 100) + 'px'); 166 167 // Only set if not explicitly passed as option 168 if (!o || o.size === undefined) { 169 this.options.size = li.size(); 170 } 171 } 172 173 this.container.show(); 174 this.buttonNext.show(); 175 this.buttonPrev.show(); 176 177 this.funcNext = function() { self.next(); }; 178 this.funcPrev = function() { self.prev(); }; 179 this.funcResize = function() { 180 if (self.resizeTimer) { 181 clearTimeout(self.resizeTimer); 182 } 183 184 self.resizeTimer = setTimeout(function() { 185 self.reload(); 186 }, 100); 187 }; 188 189 if (this.options.initCallback !== null) { 190 this.options.initCallback(this, 'init'); 191 } 192 193 /* SON-377: jcarousel issues in Chrome caused by this. */ 194 if (!windowLoaded && $.browser.safari && !window.chrome) { 195 this.buttons(false, false); 196 $(window).bind('load.jcarousel', function() { self.setup(); }); 197 } else { 198 this.setup(); 199 } 200 }; 201 202 // Create shortcut for internal use 203 var $jc = $.jcarousel; 204 205 $jc.fn = $jc.prototype = { 206 jcarousel: '0.2.8' 207 }; 208 209 $jc.fn.extend = $jc.extend = $.extend; 210 211 $jc.fn.extend({ 212 /** 213 * Setups the carousel. 214 * 215 * @method setup 216 * @return undefined 217 */ 218 setup: function() { 219 this.first = null; 220 this.last = null; 221 this.prevFirst = null; 222 this.prevLast = null; 223 this.animating = false; 224 this.timer = null; 225 this.resizeTimer = null; 226 this.tail = null; 227 this.inTail = false; 228 229 if (this.locked) { 230 return; 231 } 232 233 this.list.css(this.lt, this.pos(this.options.offset) + 'px'); 234 var p = this.pos(this.options.start, true); 235 this.prevFirst = this.prevLast = null; 236 this.animate(p, false); 237 238 $(window).unbind('resize.jcarousel', this.funcResize).bind('resize.jcarousel', this.funcResize); 239 240 if (this.options.setupCallback !== null) { 241 this.options.setupCallback(this); 242 } 243 }, 244 245 /** 246 * Clears the list and resets the carousel. 247 * 248 * @method reset 249 * @return undefined 250 */ 251 reset: function() { 252 this.list.empty(); 253 254 this.list.css(this.lt, '0px'); 255 this.list.css(this.wh, '10px'); 256 257 if (this.options.initCallback !== null) { 258 this.options.initCallback(this, 'reset'); 259 } 260 261 this.setup(); 262 }, 263 264 /** 265 * Reloads the carousel and adjusts positions. 266 * 267 * @method reload 268 * @return undefined 269 */ 270 reload: function() { 271 if (this.tail !== null && this.inTail) { 272 this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail); 273 } 274 275 this.tail = null; 276 this.inTail = false; 277 278 if (this.options.reloadCallback !== null) { 279 this.options.reloadCallback(this); 280 } 281 282 if (this.options.visible !== null) { 283 var self = this; 284 var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0; 285 this.list.children('li').each(function(i) { 286 wh += self.dimension(this, di); 287 if (i + 1 < self.first) { 288 lt = wh; 289 } 290 }); 291 292 this.list.css(this.wh, wh + 'px'); 293 this.list.css(this.lt, -lt + 'px'); 294 } 295 296 this.scroll(this.first, false); 297 }, 298 299 /** 300 * Locks the carousel. 301 * 302 * @method lock 303 * @return undefined 304 */ 305 lock: function() { 306 this.locked = true; 307 this.buttons(); 308 }, 309 310 /** 311 * Unlocks the carousel. 312 * 313 * @method unlock 314 * @return undefined 315 */ 316 unlock: function() { 317 this.locked = false;
318 this.buttons(); 319 }, 320 321 /** 322 * Sets the size of the carousel. 323 * 324 * @method size 325 * @return undefined 326 * @param s {Number} The size of the carousel. 327 */ 328 size: function(s) { 329 if (s !== undefined) { 330 this.options.size = s; 331 if (!this.locked) { 332 this.buttons(); 333 } 334 } 335 336 return this.options.size; 337 }, 338 339 /** 340 * Checks whether a list element exists for the given index (or index range). 341 * 342 * @method get 343 * @return bool 344 * @param i {Number} The index of the (first) element. 345 * @param i2 {Number} The index of the last element. 346 */ 347 has: function(i, i2) { 348 if (i2 === undefined || !i2) { 349 i2 = i; 350 } 351 352 if (this.options.size !== null && i2 > this.options.size) { 353 i2 = this.options.size; 354 } 355 356 for (var j = i; j <= i2; j++) { 357 var e = this.get(j); 358 if (!e.length || e.hasClass('jcarousel-item-placeholder')) { 359 return false; 360 } 361 } 362 363 return true; 364 }, 365 366 /** 367 * Returns a jQuery object with list element for the given index. 368 * 369 * @method get 370 * @return jQuery 371 * @param i {Number} The index of the element. 372 */ 373 get: function(i) { 374 return $('>.jcarousel-item-' + i, this.list); 375 }, 376 377 /** 378 * Adds an element for the given index to the list. 379 * If the element already exists, it updates the inner html. 380 * Returns the created element as jQuery object. 381 * 382 * @method add 383 * @return jQuery 384 * @param i {Number} The index of the element. 385 * @param s {String} The innerHTML of the element. 386 */ 387 add: function(i, s) { 388 var e = this.get(i), old = 0, n = $(s); 389 390 if (e.length === 0) { 391 var c, j = $jc.intval(i); 392 e = this.create(i); 393 while (true) { 394 c = this.get(--j); 395 if (j <= 0 || c.length) { 396 if (j <= 0) { 397 this.list.prepend(e); 398 } else { 399 c.after(e); 400 } 401 break; 402 } 403 } 404 } else { 405 old = this.dimension(e); 406 } 407 408 if (n.get(0).nodeName.toUpperCase() == 'LI') { 409 e.replaceWith(n); 410 e = n; 411 } else { 412 e.empty().append(s); 413 } 414 415 this.format(e.removeClass(this.className('jcarousel-item-placeholder')), i); 416 417 var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null; 418 var wh = this.dimension(e, di) - old; 419 420 if (i > 0 && i < this.first) { 421 this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px'); 422 } 423 424 this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px'); 425 426 return e; 427 }, 428 429 /** 430 * Removes an element for the given index from the list. 431 * 432 * @method remove 433 * @return undefined 434 * @param i {Number} The index of the element. 435 */ 436 remove: function(i) { 437 var e = this.get(i); 438 439 // Check if item exists and is not currently visible 440 if (!e.length || (i >= this.first && i <= this.last)) { 441 return; 442 } 443 444 var d = this.dimension(e); 445 446 if (i < this.first) { 447 this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px'); 448 } 449 450 e.remove(); 451 452 this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px'); 453 }, 454 455 /** 456 * Moves the carousel forwards. 457 * 458 * @method next 459 * @return undefined 460 */ 461 next: function() { 462 if (this.tail !== null && !this.inTail) { 463 this.scrollTail(false); 464 } else { 465 this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last') && this.options.size !== null && this.last == this.options.size) ? 1 : this.first + this.options.scroll); 466 } 467 }, 468 469 /** 470 * Moves the carousel backwards. 471 * 472 * @method prev 473 * @return undefined 474 */ 475 prev: function() { 476 if (this.tail !== null && this.inTail) { 477 this.scrollTail(true); 478 } else { 479 this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first') && this.options.size !== null && this.first == 1) ? this.options.size : this.first - this.options.scroll); 480 } 481 }, 482 483 /** 484 * Scrolls the tail of the carousel. 485 * 486 * @method scrollTail 487 * @return undefined 488 * @param b {Boolean} Whether scroll the tail back or forward. 489 */ 490 scrollTail: function(b) { 491 if (this.locked || this.animating || !this.tail) { 492 return; 493 } 494 495 this.pauseAuto(); 496 497 var pos = $jc.intval(this.list.css(this.lt)); 498 499 pos = !b ? pos - this.tail : pos + this.tail; 500 this.inTail = !b; 501 502 // Save for callbacks 503 this.prevFirst = this.first; 504 this.prevLast = this.last; 505 506 this.animate(pos); 507 }, 508 509 /** 510 * Scrolls the carousel to a certain position. 511 * 512 * @method scroll 513 * @return undefined 514 * @param i {Number} The index of the element to scoll to. 515 * @param a {Boolean} Flag indicating whether to perform animation. 516 */ 517 scroll: function(i, a) { 518 if (this.locked || this.animating) { 519 return; 520 } 521 522 this.pauseAuto(); 523 this.animate(this.pos(i), a); 524 }, 525 526 /** 527 * Prepares the carousel and return the position for a certian index. 528 * 529 * @method pos 530 * @return {Number} 531 * @param i {Number} The index of the element to scoll to. 532 * @param fv {Boolean} Whether to force last item to be visible. 533 */ 534 pos: function(i, fv) { 535 var pos = $jc.intval(this.list.css(this.lt)); 536 537 if (this.locked || this.animating) { 538 return pos; 539 } 540 541 if (this.options.wrap != 'circular') { 542 i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i); 543 } 544 545 var back = this.first > i; 546 547 // Create placeholders, new list width/height 548 // and new list position 549 var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first; 550 var c = back ? this.get(f) : this.get(this.last); 551 var j = back ? f : f - 1; 552 var e = null, l = 0, p = false, d = 0, g; 553 554 while (back ? --j >= i : ++j < i) { 555 e = this.get(j); 556 p = !e.length; 557 if (e.length === 0) { 558 e = this.create(j).addClass(this.className('jcarousel-item-placeholder')); 559 c[back ? 'before' : 'after' ](e); 560 561 if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) { 562 g = this.get(this.index(j)); 563 if (g.length) { 564 e = this.add(j, g.clone(true)); 565 } 566 } 567 } 568 569 c = e; 570 d = this.dimension(e); 571 572 if (p) { 573 l += d; 574 } 575 576 if (this.first !== null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size === null || j <= this.options.size)))) { 577 pos = back ? pos + d : pos - d; 578 } 579 } 580 581 // Calculate visible items 582 var clipping = this.clipping(), cache = [], visible = 0, v = 0; 583 c = this.get(i - 1); 584 j = i; 585 586 while (++visible) { 587 e = this.get(j); 588 p = !e.length; 589 if (e.length === 0) { 590 e = this.create(j).addClass(this.className('jcarousel-item-placeholder')); 591 // This should only happen on a next scroll 592 if (c.length === 0) { 593 this.list.prepend(e); 594 } else { 595 c[back ? 'before' : 'after' ](e); 596 } 597 598 if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) { 599 g = this.get(this.index(j)); 600 if (g.length) { 601 e = this.add(j, g.clone(true)); 602 } 603 } 604 } 605 606 c = e; 607 d = this.dimension(e); 608 if (d === 0) { 609 throw new Error('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...'); 610 } 611 612 if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size) { 613 cache.push(e); 614 } else if (p) { 615 l += d; 616 } 617 618 v += d; 619 620 if (v >= clipping) { 621 break; 622 } 623 624 j++; 625 } 626 627 // Remove out-of-range placeholders 628 for (var x = 0; x < cache.length; x++) { 629 cache[x].remove(); 630 } 631 632 // Resize list 633 if (l > 0) { 634 this.list.css(this.wh, this.dimension(this.list) + l + 'px'); 635 636 if (back) { 637 pos -= l; 638 this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px'); 639 } 640 } 641 642 // Calculate first and last item 643 var last = i + visible - 1; 644 if (this.options.wrap != 'circular' && this.options.size && last > this.options.size) { 645 last = this.options.size; 646 } 647 648 if (j > last) { 649 visible = 0; 650 j = last; 651 v = 0; 652 while (++visible) { 653 e = this.get(j--); 654 if (!e.length) { 655 break; 656 } 657 v += this.dimension(e); 658 if (v >= clipping) { 659 break; 660 } 661 } 662 } 663 664 var first = last - visible + 1; 665 if (this.options.wrap != 'circular' && first < 1) { 666 first = 1; 667 } 668 669 if (this.inTail && back) { 670 pos += this.tail; 671 this.inTail = false;
672 } 673 674 this.tail = null; 675 if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) { 676 var m = $jc.intval(this.get(last).css(!this.options.vertical ? 'marginRight' : 'marginBottom')); 677 if ((v - m) > clipping) { 678 this.tail = v - clipping - m; 679 } 680 } 681 682 if (fv && i === this.options.size && this.tail) { 683 pos -= this.tail; 684 this.inTail = true; 685 } 686 687 // Adjust position 688 while (i-- > first) { 689 pos += this.dimension(this.get(i)); 690 } 691 692 // Save visible item range 693 this.prevFirst = this.first; 694 this.prevLast = this.last; 695 this.first = first; 696 this.last = last; 697 698 return pos; 699 }, 700 701 /** 702 * Animates the carousel to a certain position. 703 * 704 * @method animate 705 * @return undefined 706 * @param p {Number} Position to scroll to. 707 * @param a {Boolean} Flag indicating whether to perform animation. 708 */ 709 animate: function(p, a) { 710 if (this.locked || this.animating) { 711 return; 712 } 713 714 this.animating = true; 715 716 var self = this; 717 var scrolled = function() { 718 self.animating = false; 719 720 if (p === 0) { 721 self.list.css(self.lt, 0); 722 } 723 724 if (!self.autoStopped && (self.options.wrap == 'circular' || self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size === null || self.last < self.options.size || (self.last == self.options.size && self.tail !== null && !self.inTail))) { 725 self.startAuto(); 726 } 727 728 self.buttons(); 729 self.notify('onAfterAnimation'); 730 731 // This function removes items which are appended automatically for circulation. 732 // This prevents the list from growing infinitely. 733 if (self.options.wrap == 'circular' && self.options.size !== null) { 734 for (var i = self.prevFirst; i <= self.prevLast; i++) { 735 if (i !== null && !(i >= self.first && i <= self.last) && (i < 1 || i > self.options.size)) { 736 self.remove(i); 737 } 738 } 739 } 740 }; 741 742 this.notify('onBeforeAnimation'); 743 744 // Animate 745 if (!this.options.animation || a === false) { 746 this.list.css(this.lt, p + 'px'); 747 scrolled(); 748 } else { 749 var o = !this.options.vertical ? (this.options.rtl ? {'right': p} : {'left': p}) : {'top': p}; 750 // Define animation settings. 751 var settings = { 752 duration: this.options.animation, 753 easing: this.options.easing, 754 complete: scrolled 755 }; 756 // If we have a step callback, specify it as well. 757 if ($.isFunction(this.options.animationStepCallback)) { 758 settings.step = this.options.animationStepCallback; 759 } 760 // Start the animation. 761 this.list.animate(o, settings); 762 } 763 }, 764 765 /** 766 * Starts autoscrolling. 767 * 768 * @method auto 769 * @return undefined 770 * @param s {Number} Seconds to periodically autoscroll the content. 771 */ 772 startAuto: function(s) { 773 if (s !== undefined) { 774 this.options.auto = s; 775 } 776 777 if (this.options.auto === 0) { 778 return this.stopAuto(); 779 } 780 781 if (this.timer !== null) { 782 return; 783 } 784 785 this.autoStopped = false; 786 787 var self = this; 788 this.timer = window.setTimeout(function() { self.next(); }, this.options.auto * 1000); 789 }, 790 791 /** 792 * Stops autoscrolling. 793 * 794 * @method stopAuto 795 * @return undefined 796 */ 797 stopAuto: function() { 798 this.pauseAuto(); 799 this.autoStopped = true; 800 }, 801 802 /** 803 * Pauses autoscrolling. 804 * 805 * @method pauseAuto 806 * @return undefined 807 */ 808 pauseAuto: function() { 809 if (this.timer === null) { 810 return; 811 } 812 813 window.clearTimeout(this.timer); 814 this.timer = null; 815 }, 816 817 /** 818 * Sets the states of the prev/next buttons. 819 * 820 * @method buttons 821 * @return undefined 822 */ 823 buttons: function(n, p) { 824 if (n == null) { 825 n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size === null || this.last < this.options.size); 826 if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size !== null && this.last >= this.options.size) { 827 n = this.tail !== null && !this.inTail; 828 } 829 } 830 831 if (p == null) { 832 p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1); 833 if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size !== null && this.first == 1) { 834 p = this.tail !== null && this.inTail; 835 } 836 } 837 838 var self = this; 839 840 if (this.buttonNext.size() > 0) { 841 this.buttonNext.unbind(this.options.buttonNextEvent + '.jcarousel', this.funcNext); 842 843 if (n) { 844 this.buttonNext.bind(this.options.buttonNextEvent + '.jcarousel', this.funcNext); 845 } 846 847 this.buttonNext[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true); 848 849 if (this.options.buttonNextCallback !== null && this.buttonNext.data('jcarou
849selstate') != n) { 850 this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); }).data('jcarouselstate', n); 851 } 852 } else { 853 if (this.options.buttonNextCallback !== null && this.buttonNextState != n) { 854 this.options.buttonNextCallback(self, null, n); 855 } 856 } 857 858 if (this.buttonPrev.size() > 0) { 859 this.buttonPrev.unbind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev); 860 861 if (p) { 862 this.buttonPrev.bind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev); 863 } 864 865 this.buttonPrev[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true); 866 867 if (this.options.buttonPrevCallback !== null && this.buttonPrev.data('jcarouselstate') != p) { 868 this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); }).data('jcarouselstate', p); 869 } 870 } else { 871 if (this.options.buttonPrevCallback !== null && this.buttonPrevState != p) { 872 this.options.buttonPrevCallback(self, null, p); 873 } 874 } 875 876 this.buttonNextState = n; 877 this.buttonPrevState = p; 878 }, 879 880 /** 881 * Notify callback of a specified event. 882 * 883 * @method notify 884 * @return undefined 885 * @param evt {String} The event name 886 */ 887 notify: function(evt) { 888 var state = this.prevFirst === null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev'); 889 890 // Load items 891 this.callback('itemLoadCallback', evt, state); 892 893 if (this.prevFirst !== this.first) { 894 this.callback('itemFirstInCallback', evt, state, this.first); 895 this.callback('itemFirstOutCallback', evt, state, this.prevFirst); 896 } 897 898 if (this.prevLast !== this.last) { 899 this.callback('itemLastInCallback', evt, state, this.last); 900 this.callback('itemLastOutCallback', evt, state, this.prevLast); 901 } 902 903 this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast); 904 this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last); 905 }, 906 907 callback: function(cb, evt, state, i1, i2, i3, i4) { 908 if (this.options[cb] == null || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation')) { 909 return; 910 } 911 912 var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb]; 913 914 if (!$.isFunction(callback)) { 915 return; 916 } 917 918 var self = this; 919 920 if (i1 === undefined) { 921 callback(self, state, evt); 922 } else if (i2 === undefined) { 923 this.get(i1).each(function() { callback(self, this, i1, state, evt); }); 924 } else { 925 var call = function(i) { 926 self.get(i).each(function() { callback(self, this, i, state, evt); }); 927 }; 928 for (var i = i1; i <= i2; i++) { 929 if (i !== null && !(i >= i3 && i <= i4)) { 930 call(i); 931 } 932 } 933 } 934 }, 935 936 create: function(i) { 937 return this.format('<li></li>', i); 938 }, 939 940 format: function(e, i) { 941 e = $(e); 942 var split = e.get(0).className.split(' '); 943 for (var j = 0; j < split.length; j++) { 944 if (split[j].indexOf('jcarousel-') != -1) { 945 e.removeClass(split[j]); 946 } 947 } 948 e.addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i)).css({ 949 'float': (this.options.rtl ? 'right' : 'left'), 950 'list-style': 'none' 951 }).attr('jcarouselindex', i); 952 return e; 953 }, 954 955 className: function(c) { 956 return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical'); 957 }, 958 959 dimension: function(e, d) { 960 var el = $(e); 961 962 if (d == null) { 963 return !this.options.vertical ? 964 (el.outerWidth(true) || $jc.intval(this.options.itemFallbackDimension)) : 965 (el.outerHeight(true) || $jc.intval(this.options.itemFallbackDimension)); 966 } else { 967 var w = !this.options.vertical ? 968 d - $jc.intval(el.css('marginLeft')) - $jc.intval(el.css('marginRight')) : 969 d - $jc.intval(el.css('marginTop')) - $jc.intval(el.css('marginBottom')); 970 971 $(el).css(this.wh, w + 'px'); 972 973 return this.dimension(el); 974 } 975 }, 976 977 clipping: function() { 978 return !this.options.vertical ? 979 this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) : 980 this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth')); 981 }, 982 983 index: function(i, s) { 984 if (s == null) { 985 s = this.options.size; 986 } 987 988 return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1; 989 } 990 }); 991 992 $jc.extend({ 993 /** 994 * Gets/Sets the global default configuration properties. 995 * 996 * @method defaults 997 * @return {Object} 998 * @param d {Object} A set of key/value pairs to set as configuration properties. 999 */ 1000 defaults: function(d) { 1001 return $.extend(defaults, d || {}); 1002 }, 1003 1004 intval: function(v) { 1005 v = parseInt(v, 10); 1006 return isNaN(v) ? 0 : v; 1007 }, 1008 1009 windowLoaded: function() { 1010 windowLoaded = true; 1011 } 1012 }); 1013 1014 /** 1015 * Creates a carousel for all matched elements. 1016 *
1017 * @example $("#mycarousel").jcarousel(); 1018 * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul> 1019 * @result 1020 * 1021 * <div class="jcarousel-skin-name"> 1022 * <div class="jcarousel-container"> 1023 * <div class="jcarousel-clip"> 1024 * <ul class="jcarousel-list"> 1025 * <li class="jcarousel-item-1">First item</li> 1026 * <li class="jcarousel-item-2">Second item</li> 1027 * </ul> 1028 * </div> 1029 * <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div> 1030 * <div class="jcarousel-next"></div> 1031 * </div> 1032 * </div> 1033 * 1034 * @method jcarousel 1035 * @return jQuery 1036 * @param o {Hash|String} A set of key/value pairs to set as configuration properties or a method name to call on a formerly created instance. 1037 */ 1038 $.fn.jcarousel = function(o) { 1039 if (typeof o == 'string') { 1040 var instance = $(this).data('jcarousel'), args = Array.prototype.slice.call(arguments, 1); 1041 return instance[o].apply(instance, args); 1042 } else { 1043 return this.each(function() { 1044 var instance = $(this).data('jcarousel'); 1045 if (instance) { 1046 if (o) { 1047 $.extend(instance.options, o); 1048 } 1049 instance.reload(); 1050 } else { 1051 $(this).data('jcarousel', new $jc(this, o)); 1052 } 1053 }); 1054 } 1055 }; 1056 1057})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.