1(function ($) { 2 $.widget("ui.rcarousel", { 3 _create: function() { 4 var data, 5 $root = $( this.element ), 6 _self = this, 7 options = this.options; 8 9 // if options were default there should be no problem 10 // check if user set options before init: $('element').rcarousel({with: "foo", visible: 3}); 11 // in above example exception will be thrown because 'with' should be a number! 12 this._checkOptionsValidity( this.options ); 13 14 // for every carousel create a data object and keeps it in the element 15 this._createDataObject(); 16 data = $root.data( "data" ); 17 18 // create wrapper inside root element; this is needed for animating 19 $root 20 .addClass(Â "ui-carousel"Â ) 21 .children() 22 .wrapAll(Â "<div class='wrapper'></div>" ); 23 24 // save all children of root element in âpathsâ array 25 this._saveElements(); 26 27 // make pages using paginate algorithm 28 this._generatePages(); 29 30 this._loadElements(); 31 32 this._setCarouselWidth(); 33 this._setCarouselHeight(); 34 35 // handle default event handlers 36 $( options.navigation.next ).click( 37 function( event ) { 38 _self.next(); 39 event.preventDefault(); 40 } 41 ); 42 43 $( options.navigation.prev ).click( 44 function( event ) { 45 _self.prev(); 46 event.preventDefault(); 47 } 48 ); 49 50 data.navigation.next = $( options.navigation.next ); 51 data.navigation.prev = $( options.navigation.prev ); 52 53 // stop on hover feature 54 $root.hover( 55 function() { 56 if ( options.auto.enabled ) { 57 clearInterval( data.interval ); 58 data.hoveredOver = true; 59 } 60 }, 61 function() { 62 if ( options.auto.enabled ) { 63 data.hoveredOver = false; 64 _self._autoMode( options.auto.direction ); 65 } 66 } 67 ); 68 69 this._setStep(); 70 71 // if auto mode is enabled run it 72 if ( options.auto.enabled ) { 73 this._autoMode( options.auto.direction ); 74 } 75 76 // broadcast event 77 this._trigger( "start" ); 78 }, 79 80 _addElement: function( jQueryElement, direction )Â { 81 var $root = $( this.element ), 82 $content = $root.find(Â "div.wrapper"Â ), 83 options = this.options; 84 85 jQueryElement 86 .width( options.width ) 87 .height( options.height ); 88 89 if ( options.orientation === "horizontal" ) { 90 $( jQueryElement ).css( "marginRight", options.margin ); 91 } else { 92 $( jQueryElement ).css({ 93 marginBottom: options.margin, 94 "float": "none" 95 }); 96 } 97 98 if ( direction === "prev" ) { 99 100 // clone event handlers and data as well 101 $content.prepend( jQueryElement.clone(true, true) ); 102 } else { 103 $content.append( jQueryElement.clone(true, true) ); 104 } 105 }, 106 107 append: function( jqElements ) { 108 var $root = $( this.element ), 109 data = $root.data( "data"Â ); 110 111 // add new elements 112 jqElements.each( 113 function( i, el )Â { 114 data.paths.push( $(el) ); 115 } 116 ); 117 118 data.oldPage = data.pages[data.oldPageIndex].slice(0); 119 data.appended = true; 120 121 // rebuild pages 122 this._generatePages(); 123 }, 124 125 _autoMode: function( direction ) { 126 var options = this.options, 127 data = $( this.element ).data(Â "data"Â ); 128 129 if ( direction === "next" ) { 130 data.interval = setTimeout( $.proxy(this.next, this), options.auto.interval ); 131 } else { 132 data.interval = setTimeout( $.proxy(this.prev, this), options.auto.interval ); 133 } 134 }, 135 136 _checkOptionsValidity: function( options ) { 137 var i, 138 self = this, 139 _correctSteps = ""; 140 141 // for every element in options object check its validity 142 $.each(options, 143 function( key, value ) { 144 145 switch ( key ) { 146 case "visible": 147 // visible should be a positive integer 148 if ( !value || typeof value !== "number" || value <= 0 || (Math.ceil(value) - value > 0) ) { 149 throw new Error( "visible should be defined as a positive integer" ); 150 } 151 break; 152 153 case "step": 154 if ( !value || typeof value !== "number" || value <= 0 || (Math.ceil(value) - value > 0) ) { 155 throw new Error( "step should be defined as a positive integer" ); 156 } else if ( value > self.options.visible ) { 157 // for example for visible: 3 the following array of values for 'step' is valid 158 // 3 <= step >= 1 by 1 ==> [1,2,3] 159 // output correct values 160 for ( i = 1; i <= Math.floor(options.visible); i++ ) { 161 _correctSteps += ( i < Math.floor(value) ) ? i + ", " : i; 162 } 163 164 throw new Error( "Only following step values are correct: " + _correctSteps ); 165 } 166 break; 167 168 case "width": 169 // width & height is defined by default so you can omit them to some extent
170 if ( !value || typeof value !== "number" || value <= 0 || Math.ceil(value) - value > 0 ) { 171 throw new Error( "width should be defined as a positive integer" ); 172 } 173 break; 174 175 case "height": 176 if ( !value || typeof value !== "number" || value <= 0 || Math.ceil(value) - value > 0 ) { 177 throw new Error("height should be defined as a positive integer"); 178 } 179 break; 180 181 case "speed": 182 if ( !value && value !== 0 ) { 183 throw new Error("speed should be defined as a number or a string"); 184 } 185 186 if ( typeof value === "number" && value < 0 ) { 187 throw new Error( "speed should be a positive number" ); 188 } else if ( typeof value === "string" && !(value === "slow" || value === "normal" || value === "fast") ) { 189 throw new Error( 'Only "slow", "normal" and "fast" values are valid' ); 190 } 191 break; 192 193 case "navigation": 194 if ( !value || $.isPlainObject(value) === false ) { 195 throw new Error( "navigation should be defined as an object with at least one of the properties: 'prev' or 'next' in it"); 196 } 197 198 if ( value.prev && typeof value.prev !== "string" ) { 199 throw new Error( "navigation.prev should be defined as a string and point to '.class' or '#id' of an element" ); 200 } 201 202 if ( value.next && typeof value.next !== "string" ) { 203 throw new Error(" navigation.next should be defined as a string and point to '.class' or '#id' of an element" ); 204 } 205 break; 206 207 case "auto": 208 if ( typeof value.direction !== "string" ) { 209 throw new Error( "direction should be defined as a string" ); 210 } 211 212 if ( !(value.direction === "next" || value.direction === "prev") ) { 213 throw new Error( "direction: only 'right' and 'left' values are valid" ); 214 } 215 216 if ( isNaN(value.interval) || typeof value.interval !== "number" || value.interval < 0 || Math.ceil(value.interval) - value.interval > 0 ) { 217 throw new Error( "interval should be a positive number" ); 218 } 219 break; 220 221 case "margin": 222 if ( isNaN(value) || typeof value !== "number" || value < 0 || Math.ceil(value) - value > 0 ) { 223 throw new Error( "margin should be a positive number" ); 224 } 225 break; 226 } 227 } 228 ); 229 }, 230 231 _createDataObject: function() { 232 var $root = $( this.element ); 233 234 $root.data("data", 235 { 236 paths: [], 237 pathsLen: 0, 238 pages: [], 239 lastPage: [], 240 oldPageIndex: 0, 241 pageIndex: 0, 242 navigation: {}, 243 animated: false, 244 appended: false, 245 hoveredOver: false 246 } 247 ); 248 }, 249 250 _generatePages: function() { 251 var self = this, 252 options = this.options, 253 data = $( this.element ).data( "data" ), 254 _visible = options.visible, 255 _pathsLen = data.paths.length; 256 257 // having 10 elements: A, B, C, D, E, F, G, H, I, J the algorithm 258 // creates 3 pages for âvisible: 5â and âstep: 4â: 259 // [ABCDE], [EFGHI], [FGHIJ] 260 261 function _init() { 262 // init creates the last page [FGHIJ] and remembers it 263 264 data.pages = []; 265 data.lastPage = []; 266 data.pages[0] = []; 267 268 // init last page 269 for ( var i = _pathsLen - 1; i >= _pathsLen - _visible; i-- ) { 270 data.lastPage.unshift( data.paths[i] ); 271 } 272 273 // and first page 274 for ( var i = 0; i < _visible; i++ ) { 275 data.pages[0][data.pages[0].length] = data.paths[i]; 276 } 277 } 278 279 function _islastPage( page ) { 280 var _isLast = false; 281 282 for ( var i = 0; i < data.lastPage.length; i++ ) { 283 if ( data.lastPage[i].get(0) === page[i].get(0) ) { 284 _isLast = true; 285 } else { 286 _isLast = false; 287 break; 288 } 289 } 290 291 return _isLast; 292 } 293 294 function _append( start, end, atIndex ) { 295 var _index = atIndex || data.pages.length; 296 297 if ( !atIndex ) { 298 data.pages[_index] = []; 299 } 300 301 for ( var i = start; i < end; i++ ) { 302 data.pages[_index].push( data.paths[i] ); 303 } 304 return _index; 305 } 306 307 function _paginate() { 308 var _isBeginning = true, 309 _complement = false, 310 _start = options.step, 311 _end, _index, _oldFirstEl, _oldLastEl; 312 313 // continue until you reach the last page 314 // we start from the 2nd page (1st page has been already initiated) 315 while ( !_islastPage(data.pages[data.pages.length - 1]) || _isBeginning ) { 316 _isBeginning = false;
317 318 _end = _start + _visible; 319 320 // we cannot exceed _pathsLen 321 if ( _end > _pathsLen ) { 322 _end = _pathsLen; 323 } 324 325 // when we run ouf of elements (_end - _start < _visible) we must add the difference at the begining 326 // in our example the 3rd page is [FGHIJ] and J element is added in the second step 327 // first we add [FGHI] as old elements 328 // we must assure that we have always âvisibleâ (5 in our example) elements 329 if ( _end - _start < _visible ) { 330 _complement = true; 331 } else { 332 _complement = false; 333 } 334 335 if ( _complement ) { 336 337 // first add old elemets; for 3rd page it adds [FGHIâ¦] 338 // remember the page we add to (_index) 339 _oldFirstEl = _start - ( _visible - (_end - _start) ); 340 _oldLastEl = _oldFirstEl + ( _visible - (_end - _start) ); 341 _index = _append( _oldFirstEl, _oldLastEl ); 342 343 // then add new elements; for 3th page it is J element: 344 // [fghiJ] 345 _append( _start, _end, _index ); 346 347 } else { 348 349 // normal pages like [ABCDE], [EFGHI] 350 _append( _start, _end ); 351 352 // next step 353 _start += options.step; 354 } 355 } 356 } 357 358 // go! 359 _init(); 360 _paginate(); 361 }, 362 363 getCurrentPage: function() { 364 var data = $( this.element ).data( "data" ); 365 return data.pageIndex + 1; 366 }, 367 368 getTotalPages: function() { 369 var data = $( this.element ).data( "data" ); 370 return data.pages.length; 371 }, 372 373 goToPage: function( page ) { 374 var _by, 375 data = $( this.element ).data( "data" ); 376 377 if ( !data.animated && page !== data.pageIndex ) { 378 data.animated = true; 379 380 if ( page > data.pages.length - 1 ) { 381 page = data.pages.length - 1; 382 } else if ( page < 0 ) { 383 page = 0; 384 } 385 386 data.pageIndex = page; 387 _by = page - data.oldPageIndex; 388 389 if ( _by >= 0 ) { 390 //move by n elements from current index 391 this._goToNextPage( _by ); 392 } else { 393 this._goToPrevPage( _by ); 394 } 395 396 data.oldPageIndex = page; 397 } 398 }, 399 400 _loadElements: function(elements, direction) { 401 var options = this.options, 402 data = $( this.element ).data( "data" ), 403 _dir = direction || "next", 404 _elem = elements || data.pages[options.startAtPage], 405 _start = 0, 406 _end = _elem.length; 407 408 if ( _dir === "next" ) { 409 for ( var i = _start; i < _end; i++ ) { 410 this._addElement( _elem[i], _dir ); 411 } 412 } else { 413 for ( var i = _end - 1; i >= _start; i-- ) { 414 this._addElement( _elem[i], _dir ); 415 } 416 } 417 }, 418 419 _goToPrevPage: function( by ) { 420 var _page, _oldPage, _dist, _index, _animOpts, $lastEl, _unique, _pos, _theSame, 421 $root = $( this.element ), 422 self = this, 423 options = this.options, 424 data = $( this.element ).data( "data" ); 425 426 // pick pages 427 if ( data.appended ) { 428 _oldPage = data.oldPage; 429 } else { 430 _oldPage = data.pages[data.oldPageIndex]; 431 } 432 433 _index = data.oldPageIndex + by; 434 _page = data.pages[_index].slice( 0 ); 435 436 // For example, the first time widget was initiated there were 5 437 // elements: A, B, C, D, E and 3 pages for visible 2 and step 2: 438 // AB, CD, DE. Then a user loaded next 5 elements so there were 439 // 10 already: A, B, C, D, E, F, G, H, I, J and 5 pages: 440 // AB, CD, EF, GH and IJ. If the other elemets were loaded when 441 // CD page was shown (from 5 elements) â_theSameâ is true because 442 // we compare the same 443 // pages, that is, the 2nd page from 5 elements and the 2nd from 444 // 10 elements. Thus what we do next is to decrement the index and 445 // loads the first page from 10 elements. 446 $( _page ).each( 447 function( i, el ) { 448 if ( el.get(0) === $(_oldPage[i]).get(0) ) { 449 _theSame = true; 450 } else { 451 _theSame = false; 452 } 453 } 454 ); 455 456 if ( data.appended && _theSame ) { 457 if ( data.pageIndex === 0 ) { 458 _index = data.pageIndex = data.pages.length - 1; 459 } else { 460 _index = --data.pageIndex; 461 } 462 463 _page = data.pages[_index].slice( 0 ); 464 } 465 466 // check if last element from _page appears in _oldPage 467 // for [ABCDFGHIJ] elements there are 3 pages for âvisibleâ = 6 and 468 // âstepâ = 2: [ABCDEF], [CDEFGH] and [EFGHIJ]; going from the 3rd 469 // to the 2nd page we only loads 2 elements: [CD] because all 470 // remaining were loaded already 471 $lastEl = _page[_page.length - 1].get( 0 ); 472 for ( var i = _oldPage.length - 1; i >= 0; i-- ) { 473 if ( $lastEl === $(_oldPage[i]).get(0) ) { 474 _unique = false;
475 _pos = i; 476 break; 477 } else { 478 _unique = true; 479 } 480 } 481 482 if ( !_unique ) { 483 while ( _pos >= 0 ) { 484 if ( _page[_page.length - 1].get(0) === _oldPage[_pos].get(0) ) { 485 // this element is unique 486 _page.pop(); 487 } 488 --_pos; 489 } 490 } 491 492 // load new elements 493 self._loadElements( _page, "prev" ); 494 495 // calculate the distance 496 _dist = options.width * _page.length + ( options.margin * _page.length ); 497 498 if (options.orientation === "horizontal") { 499 _animOpts = {scrollLeft: 0}; 500 $root.scrollLeft( _dist ); 501 } else { 502 _animOpts = {scrollTop: 0}; 503 $root.scrollTop( _dist ); 504 } 505 506 $root 507 .animate(_animOpts, options.speed, function () { 508 self._removeOldElements( "last", _page.length ); 509 data.animated = false; 510 511 if ( !data.hoveredOver && options.auto.enabled ) { 512 // if autoMode is on and you change page manually 513 clearInterval( data.interval ); 514 515 self._autoMode( options.auto.direction ); 516 } 517 518 // scrolling is finished, send an event 519 self._trigger("pageLoaded", null, {page: _index}); 520 }); 521 522 // reset to deafult 523 data.appended = false; 524 }, 525 526 _goToNextPage: function( by ) { 527 var _page, _oldPage, _dist, _index, _animOpts, $firstEl, _unique, _pos, _theSame, 528 $root = $( this.element ), 529 options = this.options, 530 data = $root.data( "data" ), 531 self = this; 532 533 // pick pages 534 if ( data.appended )Â { 535 _oldPage = data.oldPage; 536 } else { 537 _oldPage = data.pages[data.oldPageIndex]; 538 } 539 540 _index = data.oldPageIndex + by; 541 _page = data.pages[_index].slice( 0 ); 542 543 // For example, the first time widget was initiated there were 5 544 // elements: A, B, C, D, E and 2 pages for visible 4 and step 3: 545 // ABCD and BCDE. Then a user loaded next 5 elements so there were 546 // 10 already: A, B, C, D, E, F, G, H, I, J and 3 pages: 547 // ABCD, DEFG and GHIJ. If the other elemets were loaded when 548 // ABCD page was shown (from 5 elements) â_theSameâ is true because 549 // we compare the same 550 // pages, that is, the first pages from 5 elements and the first from 551 // 10 elements. Thus what we do next is to increment the index and 552 // loads the second page from 10 elements. 553 $( _page ).each( 554 function( i, el )Â { 555 if ( el.get(0) === $(_oldPage[i]).get(0) )Â { 556 _theSame = true; 557 } else { 558 _theSame = false; 559 } 560 } 561 ); 562 563 if ( data.appended && _theSame )Â { 564 _page = data.pages[++data.pageIndex].slice( 0 ); 565 } 566 567 // check if 1st element from _page appears in _oldPage 568 // for [ABCDFGHIJ] elements there are 3 pages for âvisibleâ = 6 and 569 // âstepâ = 2: [ABCDEF], [CDEFGH] and [EFGHIJ]; going from the 2nd 570 // to the 3rd page we only loads 2 elements: [IJ] because all 571 // remaining were loaded already 572 $firstEl = _page[0].get( 0 ); 573 for ( var i = 0; i < _page.length; i++) { 574 if ( $firstEl === $(_oldPage[i]).get(0) ) { 575 _unique = false; 576 _pos = i; 577 break; 578 } else { 579 _unique = true; 580 } 581 } 582 583 if ( !_unique ) { 584 while ( _pos < _oldPage.length ) { 585 if ( _page[0].get(0) === _oldPage[_pos].get(0) ) { 586 _page.shift(); 587 } 588 ++_pos; 589 } 590 } 591 592 // load new elements 593 this._loadElements( _page, "next" ); 594 595 // calculate the distance 596 _dist = options.width * _page.length + ( options.margin * _page.length ); 597 598 if ( options.orientation === "horizontal" )Â { 599 _animOpts = {scrollLeft: "+=" + _dist}; 600 } else { 601 _animOpts = {scrollTop: "+=" + _dist}; 602 } 603 604 $root 605 .animate(_animOpts, options.speed, function() { 606 self._removeOldElements( "first", _page.length ); 607 608 if ( options.orientation === "horizontal" ) { 609 $root.scrollLeft( 0 ); 610 } else { 611 $root.scrollTop( 0 ); 612 } 613 614 data.animated = false; 615 616 if ( !data.hoveredOver && options.auto.enabled ) { 617 // if autoMode is on and you change page manually 618 clearInterval( data.interval ); 619 620 self._autoMode( options.auto.direction ); 621 } 622 623 // scrolling is finished, send an event 624 self._trigger( "pageLoaded", null, {page: _index}); 625 626 }); 627 628 // reset to deafult 629 data.appended = false;
630 }, 631 632 next: function() { 633 var options = this.options, 634 data = $( this.element ).data( "data" ); 635 636 if ( !data.animated ) { 637 data.animated = true; 638 639 if ( !data.appended )Â { 640 ++data.pageIndex; 641 } 642 643 if ( data.pageIndex > data.pages.length - 1 ) { 644 data.pageIndex = 0; 645 } 646 647 // move by one element from current index 648 this._goToNextPage( data.pageIndex - data.oldPageIndex ); 649 data.oldPageIndex = data.pageIndex; 650 } 651 }, 652 653 prev: function() { 654 var options = this.options, 655 data = $( this.element ).data( "data" ); 656 657 if ( !data.animated ) { 658 data.animated = true; 659 660 if ( !data.appended )Â { 661 --data.pageIndex; 662 } 663 664 if ( data.pageIndex < 0 ) { 665 data.pageIndex = data.pages.length - 1; 666 } 667 668 // move left by one element from current index 669 this._goToPrevPage( data.pageIndex - data.oldPageIndex ); 670 data.oldPageIndex = data.pageIndex; 671 } 672 }, 673 674 _removeOldElements: function(position, length) { 675 // remove 'step' elements 676 var $root = $( this.element ); 677 678 for ( var i = 0; i < length; i++ ) { 679 if ( position === "first" ) { 680 $root 681 .find(Â "div.wrapper"Â ) 682 .children() 683 .first() 684 .remove(); 685 } else { 686 $root 687 .find(Â "div.wrapper"Â ) 688 .children() 689 .last() 690 .remove(); 691 } 692 } 693 }, 694 695 _saveElements: function()Â { 696 var $el, 697 $root = $( this.element ), 698 $elements = $root.find( "div.wrapper"Â ).children(), 699 data = $root.data( "data" ); 700 701 $elements.each( 702 function( i, el ) { 703 $el = $( el ); 704 705 // keep elementâs data and events 706 data.paths.push( $el.clone(true, true) ); 707 $el.remove(); 708 } 709 ); 710 }, 711 712 _setOption: function( key, value ) { 713 var _newOptions, 714 options = this.options, 715 data = $( this.element ).data( "data" ); 716 717 switch (key) { 718 case "speed": 719 this._checkOptionsValidity({speed: value}); 720 options.speed = value; 721 $.Widget.prototype._setOption.apply( this, arguments ); 722 break; 723 724 case "auto": 725 _newOptions = $.extend( options.auto, value ); 726 this._checkOptionsValidity({auto: _newOptions}); 727 728 if ( options.auto.enabled ) { 729 this._autoMode( options.auto.direction ); 730 } 731 } 732 733 }, 734 _setStep: function(s) { 735 // calculate a step 736 var _step, 737 options = this.options, 738 data = $( this.element ).data( "data" ); 739 740 _step = s || options.step; 741 742 options.step = _step; 743 data.step = options.width * _step; 744 }, 745 746 _setCarouselHeight: function() { 747 var _newHeight, 748 $root = $( this.element ), 749 data = $( this.element ).data( "data" ), 750 options = this.options; 751 752 if ( options.orientation === "vertical" ) { 753 _newHeight = options.visible * options.height + options.margin * (options.visible - 1); 754 } else { 755 _newHeight = options.height; 756 } 757 758 $root.height(_newHeight); 759 }, 760 761 _setCarouselWidth: function() { 762 var _newWidth, 763 $root = $( this.element ), 764 options = this.options, 765 data = $( this.element ).data( "data" ); 766 767 if ( options.orientation === "horizontal" ) { 768 _newWidth = options.visible * options.width + options.margin * (options.visible - 1); 769 } else { 770 _newWidth = options.width; 771 } 772 773 // set carousel width and disable overflow: auto 774 $root.css({ 775 width: _newWidth, 776 overflow: "hidden" 777 }); 778 }, 779 780 options: { 781 visible: 3, 782 step: 3, 783 width: 100, 784 height: 100, 785 speed: 1000, 786 margin: 0, 787 orientation: "horizontal", 788 auto: { 789 enabled: false, 790 direction: "next", 791 interval: 5000 792 }, 793 startAtPage: 0, 794 navigation: { 795 next: "#ui-carousel-next", 796 prev: "#ui-carousel-prev" 797 } 798 } 799 }); 800}(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.