[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('yui2-carousel', function(Y) { 2 var YAHOO = Y.YUI2; 3 /* 4 Copyright (c) 2011, Yahoo! Inc. All rights reserved. 5 Code licensed under the BSD License: 6 http://developer.yahoo.com/yui/license.html 7 version: 2.9.0 8 */ 9 /** 10 * The Carousel module provides a widget for browsing among a set of like 11 * objects represented pictorially. 12 * 13 * @module carousel 14 * @requires yahoo, dom, event, element 15 * @optional animation 16 * @namespace YAHOO.widget 17 * @title Carousel Widget 18 */ 19 (function () { 20 21 var WidgetName = "Carousel"; // forward declaration 22 23 /** 24 * The Carousel widget. 25 * 26 * @class Carousel 27 * @extends YAHOO.util.Element 28 * @constructor 29 * @param el {HTMLElement | String} The HTML element that represents the 30 * the container that houses the Carousel. 31 * @param cfg {Object} (optional) The configuration values 32 */ 33 YAHOO.widget.Carousel = function (el, cfg) { 34 35 YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg); 36 }; 37 38 /* 39 * Private variables of the Carousel component 40 */ 41 42 /* Some abbreviations to avoid lengthy typing and lookups. */ 43 var 44 Carousel = YAHOO.widget.Carousel, 45 Dom = YAHOO.util.Dom, 46 Event = YAHOO.util.Event, 47 JS = YAHOO.lang, 48 49 /** 50 * The internal table of Carousel instances. 51 * @private 52 * @static 53 */ 54 instances = {}, 55 syncUiOnItemInsert = true, 56 57 /* 58 * Custom events of the Carousel component 59 */ 60 61 /** 62 * @event afterScroll 63 * @description Fires when the Carousel has scrolled to the previous or 64 * next page. Passes back the index of the first and last visible items in 65 * the Carousel. See 66 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 67 * for more information on listening for this event. 68 * @type YAHOO.util.CustomEvent 69 */ 70 afterScrollEvent = "afterScroll", 71 72 /** 73 * @event allItemsRemovedEvent 74 * @description Fires when all items have been removed from the Carousel. 75 * See 76 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 77 * for more information on listening for this event. 78 * @type YAHOO.util.CustomEvent 79 */ 80 allItemsRemovedEvent = "allItemsRemoved", 81 82 /** 83 * @event beforeHide 84 * @description Fires before the Carousel is hidden. See 85 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 86 * for more information on listening for this event. 87 * @type YAHOO.util.CustomEvent 88 */ 89 beforeHideEvent = "beforeHide", 90 91 /** 92 * @event beforePageChange 93 * @description Fires when the Carousel is about to scroll to the previous 94 * or next page. Passes back the page number of the current page. Note 95 * that the first page number is zero. See 96 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 97 * for more information on listening for this event. 98 * @type YAHOO.util.CustomEvent 99 */ 100 beforePageChangeEvent = "beforePageChange", 101 102 /** 103 * @event beforeScroll 104 * @description Fires when the Carousel is about to scroll to the previous 105 * or next page. Passes back the index of the first and last visible items 106 * in the Carousel and the direction (backward/forward) of the scroll. See 107 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 108 * for more information on listening for this event. 109 * @type YAHOO.util.CustomEvent 110 */ 111 beforeScrollEvent = "beforeScroll", 112 113 /** 114 * @event beforeShow 115 * @description Fires when the Carousel is about to be shown. See 116 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 117 * for more information on listening for this event. 118 * @type YAHOO.util.CustomEvent 119 */ 120 beforeShowEvent = "beforeShow", 121 122 /** 123 * @event blur 124 * @description Fires when the Carousel loses focus. See 125 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 126 * for more information on listening for this event. 127 * @type YAHOO.util.CustomEvent 128 */ 129 blurEvent = "blur", 130 131 /** 132 * @event focus 133 * @description Fires when the Carousel gains focus. See 134 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 135 * for more information on listening for this event. 136 * @type YAHOO.util.CustomEvent 137 */ 138 focusEvent = "focus", 139 140 /** 141 * @event hide 142 * @description Fires when the Carousel is hidden. See 143 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 144 * for more information on listening for this event. 145 * @type YAHOO.util.CustomEvent 146 */ 147 hideEvent = "hide", 148 149 /** 150 * @event itemAdded 151 * @description Fires when an item has been added to the Carousel. Passes 152 * back the content of the item that would be added, the index at which the 153 * item would be added, and the event itself. See 154 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 155 * for more information on listening for this event. 156 * @type YAHOO.util.CustomEvent 157 */ 158 itemAddedEvent = "itemAdded", 159 160 /** 161 * @event itemRemoved 162 * @description Fires when an item has been removed from the Carousel. 163 * Passes back the content of the item that would be removed, the index 164 * from which the item would be removed, and the event itself. See 165 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 166 * for more information on listening for this event. 167 * @type YAHOO.util.CustomEvent 168 */ 169 itemRemovedEvent = "itemRemoved", 170 171 /** 172 * @event itemReplaced 173 * @description Fires when an item has been replaced in the Carousel. 174 * Passes back the content of the item that was replaced, the content 175 * of the new item, the index where the replacement occurred, and the event 176 * itself. See 177 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 178 * for more information on listening for this event. 179 * @type YAHOO.util.CustomEvent 180 */ 181 itemReplacedEvent = "itemReplaced", 182 183 /** 184 * @event itemSelected 185 * @description Fires when an item has been selected in the Carousel. 186 * Passes back the index of the selected item in the Carousel. Note, that 187 * the index begins from zero. See 188 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 189 * for more information on listening for this event. 190 * @type YAHOO.util.CustomEvent 191 */ 192 itemSelectedEvent = "itemSelected", 193 194 /** 195 * @event loadItems 196 * @description Fires when the Carousel needs more items to be loaded for 197 * displaying them. Passes back the first and last visible items in the 198 * Carousel, and the number of items needed to be loaded. See 199 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 200 * for more information on listening for this event. 201 * @type YAHOO.util.CustomEvent 202 */ 203 loadItemsEvent = "loadItems", 204 205 /** 206 * @event navigationStateChange 207 * @description Fires when the state of either one of the navigation 208 * buttons are changed from enabled to disabled or vice versa. Passes back 209 * the state (true/false) of the previous and next buttons. The value true 210 * signifies the button is enabled, false signifies disabled. See 211 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 212 * for more information on listening for this event. 213 * @type YAHOO.util.CustomEvent 214 */ 215 navigationStateChangeEvent = "navigationStateChange", 216 217 /** 218 * @event pageChange 219 * @description Fires after the Carousel has scrolled to the previous or 220 * next page. Passes back the page number of the current page. Note 221 * that the first page number is zero. See 222 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 223 * for more information on listening for this event. 224 * @type YAHOO.util.CustomEvent 225 */ 226 pageChangeEvent = "pageChange", 227 228 /* 229 * Internal event. 230 * @event render 231 * @description Fires when the Carousel is rendered. See 232 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 233 * for more information on listening for this event. 234 * @type YAHOO.util.CustomEvent 235 */ 236 renderEvent = "render", 237 238 /** 239 * @event show 240 * @description Fires when the Carousel is shown. See 241 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 242 * for more information on listening for this event. 243 * @type YAHOO.util.CustomEvent 244 */ 245 showEvent = "show", 246 247 /** 248 * @event startAutoPlay 249 * @description Fires when the auto play has started in the Carousel. See 250 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 251 * for more information on listening for this event. 252 * @type YAHOO.util.CustomEvent 253 */ 254 startAutoPlayEvent = "startAutoPlay", 255 256 /** 257 * @event stopAutoPlay 258 * @description Fires when the auto play has been stopped in the Carousel. 259 * See 260 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 261 * for more information on listening for this event. 262 * @type YAHOO.util.CustomEvent 263 */ 264 stopAutoPlayEvent = "stopAutoPlay", 265 266 /* 267 * Internal event. 268 * @event uiUpdateEvent 269 * @description Fires when the UI has been updated. 270 * See 271 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 272 * for more information on listening for this event. 273 * @type YAHOO.util.CustomEvent 274 */ 275 uiUpdateEvent = "uiUpdate"; 276 277 /* 278 * Private helper functions used by the Carousel component 279 */ 280 281 /** 282 * Set multiple styles on one element. 283 * @method setStyles 284 * @param el {HTMLElement} The element to set styles on 285 * @param style {Object} top:"10px", left:"0px", etc. 286 * @private 287 */ 288 function setStyles(el, styles) { 289 var which; 290 291 for (which in styles) { 292 if (styles.hasOwnProperty(which)) { 293 Dom.setStyle(el, which, styles[which]); 294 } 295 } 296 } 297 298 /** 299 * Create an element, set its class name and optionally install the element 300 * to its parent. 301 * @method createElement 302 * @param el {String} The element to be created 303 * @param attrs {Object} Configuration of parent, class and id attributes. 304 * If the content is specified, it is inserted after creation of the 305 * element. The content can also be an HTML element in which case it would 306 * be appended as a child node of the created element. 307 * @private 308 */ 309 function createElement(el, attrs) { 310 var newEl = document.createElement(el); 311 312 attrs = attrs || {}; 313 if (attrs.className) { 314 Dom.addClass(newEl, attrs.className); 315 } 316 317 if (attrs.styles) { 318 setStyles(newEl, attrs.styles); 319 } 320 321 if (attrs.parent) { 322 attrs.parent.appendChild(newEl); 323 } 324 325 if (attrs.id) { 326 newEl.setAttribute("id", attrs.id); 327 } 328 329 if (attrs.content) { 330 if (attrs.content.nodeName) { 331 newEl.appendChild(attrs.content); 332 } else { 333 newEl.innerHTML = attrs.content; 334 } 335 } 336 337 return newEl; 338 } 339 340 /** 341 * Get the computed style of an element. 342 * 343 * @method getStyle 344 * @param el {HTMLElement} The element for which the style needs to be 345 * returned. 346 * @param style {String} The style attribute 347 * @param type {String} "int", "float", etc. (defaults to int) 348 * @private 349 */ 350 function getStyle(el, style, type) { 351 var value; 352 353 if (!el) { 354 return 0; 355 } 356 357 function getStyleIntVal(el, style) { 358 var val; 359 360 /* 361 * XXX: Safari calculates incorrect marginRight for an element 362 * which has its parent element style set to overflow: hidden 363 * https://bugs.webkit.org/show_bug.cgi?id=13343 364 * Let us assume marginLeft == marginRight 365 * 366 * Seems like IE9 also has this issue! 367 */ 368 if (style == "marginRight" && (YAHOO.env.ua.webkit || 369 (YAHOO.env.ua.ie && YAHOO.env.ua.ie >= 9))) { 370 val = parseInt(Dom.getStyle(el, "marginLeft"), 10); 371 } else { 372 val = parseInt(Dom.getStyle(el, style), 10); 373 } 374 375 return JS.isNumber(val) ? val : 0; 376 } 377 378 function getStyleFloatVal(el, style) { 379 var val; 380 381 /* 382 * XXX: Safari calculates incorrect marginRight for an element 383 * which has its parent element style set to overflow: hidden 384 * https://bugs.webkit.org/show_bug.cgi?id=13343 385 * Let us assume marginLeft == marginRight 386 */ 387 if (style == "marginRight" && YAHOO.env.ua.webkit) { 388 val = parseFloat(Dom.getStyle(el, "marginLeft")); 389 } else { 390 val = parseFloat(Dom.getStyle(el, style)); 391 } 392 393 return JS.isNumber(val) ? val : 0; 394 } 395 396 if (typeof type == "undefined") { 397 type = "int"; 398 } 399 400 switch (style) { 401 case "height": 402 value = el.offsetHeight; 403 if (value > 0) { 404 value += getStyleIntVal(el, "marginTop") + 405 getStyleIntVal(el, "marginBottom"); 406 } else { 407 value = getStyleFloatVal(el, "height") + 408 getStyleIntVal(el, "marginTop") + 409 getStyleIntVal(el, "marginBottom") + 410 getStyleIntVal(el, "borderTopWidth") + 411 getStyleIntVal(el, "borderBottomWidth") + 412 getStyleIntVal(el, "paddingTop") + 413 getStyleIntVal(el, "paddingBottom"); 414 } 415 break; 416 case "width": 417 value = el.offsetWidth; 418 if (value > 0) { 419 value += getStyleIntVal(el, "marginLeft") + 420 getStyleIntVal(el, "marginRight"); 421 } else { 422 value = getStyleFloatVal(el, "width") + 423 getStyleIntVal(el, "marginLeft") + 424 getStyleIntVal(el, "marginRight") + 425 getStyleIntVal(el, "borderLeftWidth") + 426 getStyleIntVal(el, "borderRightWidth") + 427 getStyleIntVal(el, "paddingLeft") + 428 getStyleIntVal(el, "paddingRight"); 429 } 430 break; 431 default: 432 if (type == "int") { 433 value = getStyleIntVal(el, style); 434 } else if (type == "float") { 435 value = getStyleFloatVal(el, style); 436 } else { 437 value = Dom.getStyle(el, style); 438 } 439 break; 440 } 441 442 return value; 443 } 444 445 /** 446 * Compute and return the height or width of a single Carousel item 447 * depending upon the orientation. 448 * 449 * @method getCarouselItemSize 450 * @param which {String} "height" or "width" to be returned. If this is 451 * passed explicitly, the calculated size is not cached. 452 * @private 453 */ 454 function getCarouselItemSize(which) { 455 var carousel = this, 456 child, 457 item, 458 size = 0, 459 vertical = false; 460 461 if (carousel._itemAttrCache[which]) { 462 return carousel._itemAttrCache[which]; 463 } 464 465 if (carousel._itemsTable.numItems === 0) { 466 return 0; 467 } 468 469 // get first loaded item 470 item = carousel._findClosestSibling(-1); 471 472 if (JS.isUndefined(item)) { 473 return 0; 474 } 475 476 child = Dom.get(item.id); 477 478 if (typeof which == "undefined") { 479 vertical = carousel.get("isVertical"); 480 } else { 481 vertical = which == "height"; 482 } 483 484 if (vertical) { 485 size = getStyle(child, "height"); 486 } else { 487 size = getStyle(child, "width"); 488 } 489 490 if (size) { 491 carousel._itemAttrCache[which] = size; 492 } 493 494 return size; 495 } 496 497 /** 498 * Return the size of a part of the item (reveal). 499 * 500 * @method getRevealSize 501 * @private 502 */ 503 function getRevealSize() { 504 var carousel = this, isVertical, sz; 505 506 isVertical = carousel.get("isVertical"); 507 sz = getCarouselItemSize.call(carousel, 508 isVertical ? "height" : "width"); 509 return (sz * carousel.get("revealAmount") / 100); 510 } 511 512 /** 513 * Compute and return the position of a Carousel item based on its 514 * position. 515 * 516 * @method getCarouselItemPosition 517 * @param position {Number} The position of the Carousel item. 518 * @private 519 */ 520 function getCarouselItemPosition(pos) { 521 var carousel = this, 522 itemsPerRow = carousel._cols, 523 itemsPerCol = carousel._rows, 524 page, 525 sz, 526 isVertical, 527 itemsCol, 528 itemsRow, 529 sentinel, 530 top, 531 left, 532 rsz, 533 delta, 534 styles = {}, 535 itemsTable = carousel._itemsTable; 536 537 isVertical = carousel.get("isVertical"); 538 sz = getCarouselItemSize.call(carousel, 539 isVertical ? "height" : "width"); 540 rsz = getRevealSize.call(carousel); 541 542 if (itemsPerCol) { 543 page = this.getPageForItem(pos); 544 if (isVertical) { 545 itemsRow = Math.floor(pos/itemsPerRow); 546 delta = itemsRow; 547 top = delta * sz; 548 styles.top = (top + rsz) + "px"; 549 550 sz = getCarouselItemSize.call(carousel, "width"); 551 552 itemsCol = pos % itemsPerRow; 553 delta = itemsCol; 554 left = delta * sz; 555 styles.left = left + "px"; 556 } else { 557 itemsCol = pos % itemsPerRow; 558 sentinel = (page - 1) * itemsPerRow; 559 delta = itemsCol + sentinel; 560 left = delta * sz; 561 styles.left = (left + rsz) + "px"; 562 563 sz = getCarouselItemSize.call(carousel, "height"); 564 565 itemsRow = Math.floor(pos/itemsPerRow); 566 sentinel = (page - 1) * itemsPerCol; 567 delta = itemsRow - sentinel; 568 top = delta * sz; 569 570 styles.top = top + "px"; 571 } 572 } else { 573 if (isVertical) { 574 styles.left = 0; 575 styles.top = ((pos * sz) + rsz) + "px"; 576 } else { 577 styles.top = 0; 578 styles.left = ((pos * sz) + rsz) + "px"; 579 } 580 } 581 582 return styles; 583 } 584 585 /** 586 * Return the index of the first item in the view port for displaying item 587 * in "pos". 588 * 589 * @method getFirstVisibleForPosition 590 * @param pos {Number} The position of the item to be displayed 591 * @private 592 */ 593 function getFirstVisibleForPosition(pos) { 594 var num = this.get("numVisible"); 595 return Math.floor(pos / num) * num; 596 } 597 598 /** 599 * Return the scrolling offset size given the number of elements to 600 * scroll. 601 * 602 * @method getScrollOffset 603 * @param delta {Number} The delta number of elements to scroll by. 604 * @private 605 */ 606 function getScrollOffset(delta) { 607 var carousel = this, 608 itemSize = 0, 609 size = 0, 610 attr = carousel.get("isVertical") ? "height" : "width"; 611 612 itemSize = getCarouselItemSize.call(carousel, attr); 613 614 size = itemSize * delta; 615 616 return size; 617 } 618 619 /** 620 * Scroll the Carousel by a page backward. 621 * 622 * @method scrollPageBackward 623 * @param {Event} ev The event object 624 * @param {Object} obj The context object 625 * @private 626 */ 627 function scrollPageBackward(ev, obj) { 628 obj.scrollPageBackward(); 629 Event.preventDefault(ev); 630 } 631 632 /** 633 * Scroll the Carousel by a page forward. 634 * 635 * @method scrollPageForward 636 * @param {Event} ev The event object 637 * @param {Object} obj The context object 638 * @private 639 */ 640 function scrollPageForward(ev, obj) { 641 obj.scrollPageForward(); 642 Event.preventDefault(ev); 643 } 644 645 /** 646 * Set the selected item. 647 * 648 * @method setItemSelection 649 * @param {Number} newpos The index of the new position 650 * @param {Number} oldpos The index of the previous position 651 * @private 652 */ 653 function setItemSelection(newpos, oldpos) { 654 var carousel = this, 655 cssClass = carousel.CLASSES, 656 el, 657 firstItem = carousel._firstItem, 658 numItems = carousel.get("numItems"), 659 numVisible = carousel.get("numVisible"), 660 position = oldpos, 661 sentinel = firstItem + numVisible - 1; 662 663 if (position >= 0 && position < numItems) { 664 if (!JS.isUndefined(carousel._itemsTable.items[position])) { 665 el = Dom.get(carousel._itemsTable.items[position].id); 666 if (el) { 667 Dom.removeClass(el, cssClass.SELECTED_ITEM); 668 } 669 } 670 } 671 672 if (JS.isNumber(newpos)) { 673 newpos = parseInt(newpos, 10); 674 newpos = JS.isNumber(newpos) ? newpos : 0; 675 } else { 676 newpos = firstItem; 677 } 678 679 if (JS.isUndefined(carousel._itemsTable.items[newpos])) { 680 newpos = getFirstVisibleForPosition.call(carousel, newpos); 681 carousel.scrollTo(newpos); // still loading the item 682 } 683 684 if (!JS.isUndefined(carousel._itemsTable.items[newpos])) { 685 el = Dom.get(carousel._itemsTable.items[newpos].id); 686 if (el) { 687 Dom.addClass(el, cssClass.SELECTED_ITEM); 688 } 689 } 690 691 if (newpos < firstItem || newpos > sentinel) { // out of focus 692 newpos = getFirstVisibleForPosition.call(carousel, newpos); 693 carousel.scrollTo(newpos); 694 } 695 } 696 697 /** 698 * Show or hide navigation. 699 * 700 * @method showNavigation 701 * @private 702 */ 703 function showNavigation(hide) { 704 var carousel = this, 705 cfg = carousel.get("navigation"); 706 707 if (JS.isUndefined(cfg)) { 708 return; // can't do anything 709 } 710 711 if (JS.isUndefined(hide)) { 712 // show the navigation 713 if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) && 714 !JS.isUndefined(cfg.prev[0])) { 715 Dom.setStyle(cfg.prev[0], "visibility", "visible"); 716 } 717 if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) && 718 !JS.isUndefined(cfg.next[0])) { 719 Dom.setStyle(cfg.next[0], "visibility", "visible"); 720 } 721 if (!JS.isUndefined(carousel._pages) && 722 !JS.isUndefined(carousel._pages.el)) { 723 Dom.setStyle(carousel._pages.el, "visibility", "visible"); 724 } 725 } else { 726 // hide the navigation 727 if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) && 728 !JS.isUndefined(cfg.prev[0])) { 729 Dom.setStyle(cfg.prev[0], "visibility", "hidden"); 730 } 731 if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) && 732 !JS.isUndefined(cfg.next[0])) { 733 Dom.setStyle(cfg.next[0], "visibility", "hidden"); 734 } 735 if (!JS.isUndefined(carousel._pages) && 736 !JS.isUndefined(carousel._pages.el)) { 737 Dom.setStyle(carousel._pages.el, "visibility", "hidden"); 738 } 739 } 740 } 741 742 /** 743 * Fire custom events for enabling/disabling navigation elements. 744 * 745 * @method syncNavigation 746 * @private 747 */ 748 function syncNavigation() { 749 var attach = false, 750 carousel = this, 751 cssClass = carousel.CLASSES, 752 i, 753 navigation, 754 sentinel; 755 756 // Don't do anything if the Carousel is not rendered 757 if (!carousel._hasRendered) { 758 return; 759 } 760 761 navigation = carousel.get("navigation"); 762 sentinel = carousel._firstItem + carousel.get("numVisible"); 763 764 if (navigation.prev) { 765 if (carousel.get("numItems") === 0 || carousel._firstItem === 0) { 766 if (carousel.get("numItems") === 0 || 767 !carousel.get("isCircular")) { 768 Event.removeListener(navigation.prev, "click", 769 scrollPageBackward); 770 Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); 771 for (i = 0; i < carousel._navBtns.prev.length; i++) { 772 carousel._navBtns.prev[i].setAttribute("disabled", 773 "true"); 774 } 775 carousel._prevEnabled = false; 776 } else { 777 attach = !carousel._prevEnabled; 778 } 779 } else { 780 attach = !carousel._prevEnabled; 781 } 782 783 if (attach) { 784 Event.on(navigation.prev, "click", scrollPageBackward, 785 carousel); 786 Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); 787 for (i = 0; i < carousel._navBtns.prev.length; i++) { 788 carousel._navBtns.prev[i].removeAttribute("disabled"); 789 } 790 carousel._prevEnabled = true; 791 } 792 } 793 794 attach = false; 795 if (navigation.next) { 796 if (sentinel >= carousel.get("numItems")) { 797 if (!carousel.get("isCircular")) { 798 Event.removeListener(navigation.next, "click", 799 scrollPageForward); 800 Dom.addClass(navigation.next, cssClass.DISABLED); 801 for (i = 0; i < carousel._navBtns.next.length; i++) { 802 carousel._navBtns.next[i].setAttribute("disabled", 803 "true"); 804 } 805 carousel._nextEnabled = false; 806 } else { 807 attach = !carousel._nextEnabled; 808 } 809 } else { 810 attach = !carousel._nextEnabled; 811 } 812 813 if (attach) { 814 Event.on(navigation.next, "click", scrollPageForward, 815 carousel); 816 Dom.removeClass(navigation.next, cssClass.DISABLED); 817 for (i = 0; i < carousel._navBtns.next.length; i++) { 818 carousel._navBtns.next[i].removeAttribute("disabled"); 819 } 820 carousel._nextEnabled = true; 821 } 822 } 823 824 carousel.fireEvent(navigationStateChangeEvent, 825 { next: carousel._nextEnabled, prev: carousel._prevEnabled }); 826 } 827 828 /** 829 * Synchronize and redraw the Pager UI if necessary. 830 * 831 * @method syncPagerUi 832 * @private 833 */ 834 function syncPagerUi(page) { 835 var carousel = this, numPages, numVisible; 836 837 // Don't do anything if the Carousel is not rendered 838 if (!carousel._hasRendered) { 839 return; 840 } 841 842 numVisible = carousel.get("numVisible"); 843 844 if (!JS.isNumber(page)) { 845 page = Math.floor(carousel.get("selectedItem") / numVisible); 846 } 847 848 numPages = Math.ceil(carousel.get("numItems") / numVisible); 849 850 carousel._pages.num = numPages; 851 carousel._pages.cur = page; 852 853 if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) { 854 carousel._updatePagerMenu(); 855 } else { 856 carousel._updatePagerButtons(); 857 } 858 } 859 860 /** 861 * Get full dimensions of an element. 862 * 863 * @method getDimensions 864 * @param {Object} el The element to get the dimensions of 865 * @param {String} which Get the height or width of an element 866 * @private 867 */ 868 function getDimensions(el, which) { 869 switch (which) { 870 case 'height': 871 return getStyle(el, "marginTop") + 872 getStyle(el, "marginBottom") + 873 getStyle(el, "paddingTop") + 874 getStyle(el, "paddingBottom") + 875 getStyle(el, "borderTopWidth") + 876 getStyle(el, "borderBottomWidth"); 877 case 'width': 878 return getStyle(el, "marginLeft") + 879 getStyle(el, "marginRight") + 880 getStyle(el, "paddingLeft") + 881 getStyle(el, "paddingRight") + 882 getStyle(el, "borderLeftWidth") + 883 getStyle(el, "borderRightWidth"); 884 default: 885 break; 886 } 887 888 return getStyle(el, which); 889 } 890 891 /** 892 * Handle UI update. 893 * Call the appropriate methods on events fired when an item is added, or 894 * removed for synchronizing the DOM. 895 * 896 * @method syncUi 897 * @param {Object} o The item that needs to be added or removed 898 * @private 899 */ 900 function syncUi(o) { 901 var carousel = this; 902 903 if (!JS.isObject(o)) { 904 return; 905 } 906 907 switch (o.ev) { 908 case itemAddedEvent: 909 carousel._syncUiForItemAdd(o); 910 break; 911 case itemRemovedEvent: 912 carousel._syncUiForItemRemove(o); 913 break; 914 case itemReplacedEvent: 915 carousel._syncUiForItemReplace(o); 916 break; 917 case loadItemsEvent: 918 carousel._syncUiForLazyLoading(o); 919 break; 920 } 921 922 carousel.fireEvent(uiUpdateEvent); 923 } 924 925 /** 926 * Update the state variables after scrolling the Carousel view port. 927 * 928 * @method updateStateAfterScroll 929 * @param {Integer} item The index to which the Carousel has scrolled to. 930 * @param {Integer} sentinel The last element in the view port. 931 * @private 932 */ 933 function updateStateAfterScroll(item, sentinel) { 934 var carousel = this, 935 page = carousel.get("currentPage"), 936 newPage, 937 numPerPage = carousel.get("numVisible"); 938 939 newPage = parseInt(carousel._firstItem / numPerPage, 10); 940 if (newPage != page) { 941 carousel.setAttributeConfig("currentPage", { value: newPage }); 942 carousel.fireEvent(pageChangeEvent, newPage); 943 } 944 945 if (carousel.get("selectOnScroll")) { 946 if (carousel.get("selectedItem") != carousel._selectedItem) { 947 carousel.set("selectedItem", carousel._selectedItem); 948 } 949 } 950 951 clearTimeout(carousel._autoPlayTimer); 952 delete carousel._autoPlayTimer; 953 if (carousel.isAutoPlayOn()) { 954 carousel.startAutoPlay(); 955 } 956 957 carousel.fireEvent(afterScrollEvent, 958 { first: carousel._firstItem, 959 last: sentinel }, 960 carousel); 961 } 962 963 /* 964 * Static members and methods of the Carousel component 965 */ 966 967 /** 968 * Return the appropriate Carousel object based on the id associated with 969 * the Carousel element or false if none match. 970 * @method getById 971 * @public 972 * @static 973 */ 974 Carousel.getById = function (id) { 975 return instances[id] ? instances[id].object : false; 976 }; 977 978 YAHOO.extend(Carousel, YAHOO.util.Element, { 979 980 /* 981 * Internal variables used within the Carousel component 982 */ 983 984 /** 985 * Number of rows for a multirow carousel. 986 * 987 * @property _rows 988 * @private 989 */ 990 _rows: null, 991 992 /** 993 * Number of cols for a multirow carousel. 994 * 995 * @property _cols 996 * @private 997 */ 998 _cols: null, 999 1000 /** 1001 * The Animation object. 1002 * 1003 * @property _animObj 1004 * @private 1005 */ 1006 _animObj: null, 1007 1008 /** 1009 * The Carousel element. 1010 * 1011 * @property _carouselEl 1012 * @private 1013 */ 1014 _carouselEl: null, 1015 1016 /** 1017 * The Carousel clipping container element. 1018 * 1019 * @property _clipEl 1020 * @private 1021 */ 1022 _clipEl: null, 1023 1024 /** 1025 * The current first index of the Carousel. 1026 * 1027 * @property _firstItem 1028 * @private 1029 */ 1030 _firstItem: 0, 1031 1032 /** 1033 * Does the Carousel element have focus? 1034 * 1035 * @property _hasFocus 1036 * @private 1037 */ 1038 _hasFocus: false, 1039 1040 /** 1041 * Is the Carousel rendered already? 1042 * 1043 * @property _hasRendered 1044 * @private 1045 */ 1046 _hasRendered: false, 1047 1048 /** 1049 * Is the animation still in progress? 1050 * 1051 * @property _isAnimationInProgress 1052 * @private 1053 */ 1054 _isAnimationInProgress: false, 1055 1056 /** 1057 * Is the auto-scrolling of Carousel in progress? 1058 * 1059 * @property _isAutoPlayInProgress 1060 * @private 1061 */ 1062 _isAutoPlayInProgress: false, 1063 1064 /** 1065 * The table of items in the Carousel. 1066 * The numItems is the number of items in the Carousel, items being the 1067 * array of items in the Carousel. The size is the size of a single 1068 * item in the Carousel. It is cached here for efficiency (to avoid 1069 * computing the size multiple times). 1070 * 1071 * @property _itemsTable 1072 * @private 1073 */ 1074 _itemsTable: null, 1075 1076 /** 1077 * The Carousel navigation buttons. 1078 * 1079 * @property _navBtns 1080 * @private 1081 */ 1082 _navBtns: null, 1083 1084 /** 1085 * The Carousel navigation. 1086 * 1087 * @property _navEl 1088 * @private 1089 */ 1090 _navEl: null, 1091 1092 /** 1093 * Status of the next navigation item. 1094 * 1095 * @property _nextEnabled 1096 * @private 1097 */ 1098 _nextEnabled: true, 1099 1100 /** 1101 * The Carousel pages structure. 1102 * This is an object of the total number of pages and the current page. 1103 * 1104 * @property _pages 1105 * @private 1106 */ 1107 _pages: null, 1108 1109 /** 1110 * The Carousel pagination structure. 1111 * 1112 * @property _pagination 1113 * @private 1114 */ 1115 _pagination: null, 1116 1117 /** 1118 * Status of the previous navigation item. 1119 * 1120 * @property _prevEnabled 1121 * @private 1122 */ 1123 _prevEnabled: true, 1124 1125 /** 1126 * Whether the Carousel size needs to be recomputed or not? 1127 * 1128 * @property _recomputeSize 1129 * @private 1130 */ 1131 _recomputeSize: true, 1132 1133 /** 1134 * Cache the Carousel item attributes. 1135 * 1136 * @property _itemAttrCache 1137 * @private 1138 */ 1139 _itemAttrCache: null, 1140 1141 /* 1142 * CSS classes used by the Carousel component 1143 */ 1144 1145 CLASSES: { 1146 1147 /** 1148 * The class name of the Carousel navigation buttons. 1149 * 1150 * @property BUTTON 1151 * @default "yui-carousel-button" 1152 */ 1153 BUTTON: "yui-carousel-button", 1154 1155 /** 1156 * The class name of the Carousel element. 1157 * 1158 * @property CAROUSEL 1159 * @default "yui-carousel" 1160 */ 1161 CAROUSEL: "yui-carousel", 1162 1163 /** 1164 * The class name of the container of the items in the Carousel. 1165 * 1166 * @property CAROUSEL_EL 1167 * @default "yui-carousel-element" 1168 */ 1169 CAROUSEL_EL: "yui-carousel-element", 1170 1171 /** 1172 * The class name of the Carousel's container element. 1173 * 1174 * @property CONTAINER 1175 * @default "yui-carousel-container" 1176 */ 1177 CONTAINER: "yui-carousel-container", 1178 1179 /** 1180 * The class name of the Carousel's container element. 1181 * 1182 * @property CONTENT 1183 * @default "yui-carousel-content" 1184 */ 1185 CONTENT: "yui-carousel-content", 1186 1187 /** 1188 * The class name of a disabled navigation button. 1189 * 1190 * @property DISABLED 1191 * @default "yui-carousel-button-disabled" 1192 */ 1193 DISABLED: "yui-carousel-button-disabled", 1194 1195 /** 1196 * The class name of the first Carousel navigation button. 1197 * 1198 * @property FIRST_NAV 1199 * @default " yui-carousel-first-button" 1200 */ 1201 FIRST_NAV: " yui-carousel-first-button", 1202 1203 /** 1204 * The class name of a first disabled navigation button. 1205 * 1206 * @property FIRST_NAV_DISABLED 1207 * @default "yui-carousel-first-button-disabled" 1208 */ 1209 FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled", 1210 1211 /** 1212 * The class name of a first page element. 1213 * 1214 * @property FIRST_PAGE 1215 * @default "yui-carousel-nav-first-page" 1216 */ 1217 FIRST_PAGE: "yui-carousel-nav-first-page", 1218 1219 /** 1220 * The class name of the Carousel navigation button that has focus. 1221 * 1222 * @property FOCUSSED_BUTTON 1223 * @default "yui-carousel-button-focus" 1224 */ 1225 FOCUSSED_BUTTON: "yui-carousel-button-focus", 1226 1227 /** 1228 * The class name of a horizontally oriented Carousel. 1229 * 1230 * @property HORIZONTAL 1231 * @default "yui-carousel-horizontal" 1232 */ 1233 HORIZONTAL: "yui-carousel-horizontal", 1234 1235 /** 1236 * The element to be used as the progress indicator when the item 1237 * is still being loaded. 1238 * 1239 * @property ITEM_LOADING 1240 * @default The progress indicator (spinner) image CSS class 1241 */ 1242 ITEM_LOADING: "yui-carousel-item-loading", 1243 1244 /** 1245 * The class name that will be set if the Carousel adjusts itself 1246 * for a minimum width. 1247 * 1248 * @property MIN_WIDTH 1249 * @default "yui-carousel-min-width" 1250 */ 1251 MIN_WIDTH: "yui-carousel-min-width", 1252 1253 /** 1254 * The navigation element container class name. 1255 * 1256 * @property NAVIGATION 1257 * @default "yui-carousel-nav" 1258 */ 1259 NAVIGATION: "yui-carousel-nav", 1260 1261 /** 1262 * The class name of the next Carousel navigation button. 1263 * 1264 * @property NEXT_NAV 1265 * @default " yui-carousel-next-button" 1266 */ 1267 NEXT_NAV: " yui-carousel-next-button", 1268 1269 /** 1270 * The class name of the next navigation link. This variable is 1271 * not only used for styling, but also for identifying the link 1272 * within the Carousel container. 1273 * 1274 * @property NEXT_PAGE 1275 * @default "yui-carousel-next" 1276 */ 1277 NEXT_PAGE: "yui-carousel-next", 1278 1279 /** 1280 * The class name for the navigation container for prev/next. 1281 * 1282 * @property NAV_CONTAINER 1283 * @default "yui-carousel-buttons" 1284 */ 1285 NAV_CONTAINER: "yui-carousel-buttons", 1286 1287 /** 1288 * The class name for an item in the pager UL or dropdown menu. 1289 * 1290 * @property PAGER_ITEM 1291 * @default "yui-carousel-pager-item" 1292 */ 1293 PAGER_ITEM: "yui-carousel-pager-item", 1294 1295 /** 1296 * The class name for the pagination container 1297 * 1298 * @property PAGINATION 1299 * @default "yui-carousel-pagination" 1300 */ 1301 PAGINATION: "yui-carousel-pagination", 1302 1303 /** 1304 * The class name of the focussed page navigation. This class is 1305 * specifically used for the ugly focus handling in Opera. 1306 * 1307 * @property PAGE_FOCUS 1308 * @default "yui-carousel-nav-page-focus" 1309 */ 1310 PAGE_FOCUS: "yui-carousel-nav-page-focus", 1311 1312 /** 1313 * The class name of the previous navigation link. This variable 1314 * is not only used for styling, but also for identifying the link 1315 * within the Carousel container. 1316 * 1317 * @property PREV_PAGE 1318 * @default "yui-carousel-prev" 1319 */ 1320 PREV_PAGE: "yui-carousel-prev", 1321 1322 /** 1323 * The class name of the item. 1324 * 1325 * @property ITEM 1326 * @default "yui-carousel-item" 1327 */ 1328 ITEM: "yui-carousel-item", 1329 1330 /** 1331 * The class name of the selected item. 1332 * 1333 * @property SELECTED_ITEM 1334 * @default "yui-carousel-item-selected" 1335 */ 1336 SELECTED_ITEM: "yui-carousel-item-selected", 1337 1338 /** 1339 * The class name of the selected paging navigation. 1340 * 1341 * @property SELECTED_NAV 1342 * @default "yui-carousel-nav-page-selected" 1343 */ 1344 SELECTED_NAV: "yui-carousel-nav-page-selected", 1345 1346 /** 1347 * The class name of a vertically oriented Carousel. 1348 * 1349 * @property VERTICAL 1350 * @default "yui-carousel-vertical" 1351 */ 1352 VERTICAL: "yui-carousel-vertical", 1353 1354 /** 1355 * The class name of a multirow Carousel. 1356 * 1357 * @property MULTI_ROW 1358 * @default "yui-carousel-multi-row" 1359 */ 1360 MULTI_ROW: "yui-carousel-multi-row", 1361 1362 /** 1363 * The class name of a row in a multirow Carousel. 1364 * 1365 * @property ROW 1366 * @default "yui-carousel-new-row" 1367 */ 1368 ROW: "yui-carousel-row", 1369 1370 /** 1371 * The class name of a vertical Carousel's container element. 1372 * 1373 * @property VERTICAL_CONTAINER 1374 * @default "yui-carousel-vertical-container" 1375 */ 1376 VERTICAL_CONTAINER: "yui-carousel-vertical-container", 1377 1378 /** 1379 * The class name of a visible Carousel. 1380 * 1381 * @property VISIBLE 1382 * @default "yui-carousel-visible" 1383 */ 1384 VISIBLE: "yui-carousel-visible" 1385 1386 }, 1387 1388 /* 1389 * Configuration attributes for configuring the Carousel component 1390 */ 1391 1392 CONFIG: { 1393 1394 /** 1395 * The offset of the first visible item in the Carousel. 1396 * 1397 * @property FIRST_VISIBLE 1398 * @default 0 1399 */ 1400 FIRST_VISIBLE: 0, 1401 1402 /** 1403 * The minimum width of the horizontal Carousel container to support 1404 * the navigation buttons. 1405 * 1406 * @property HORZ_MIN_WIDTH 1407 * @default 180 1408 */ 1409 HORZ_MIN_WIDTH: 180, 1410 1411 /** 1412 * The maximum number of pager buttons allowed beyond which the UI 1413 * of the pager would be a drop-down of pages instead of buttons. 1414 * 1415 * @property MAX_PAGER_BUTTONS 1416 * @default 5 1417 */ 1418 MAX_PAGER_BUTTONS: 5, 1419 1420 /** 1421 * The minimum width of the vertical Carousel container to support 1422 * the navigation buttons. 1423 * 1424 * @property VERT_MIN_WIDTH 1425 * @default 155 1426 */ 1427 VERT_MIN_WIDTH: 115, 1428 1429 /** 1430 * The number of visible items in the Carousel. 1431 * 1432 * @property NUM_VISIBLE 1433 * @default 3 1434 */ 1435 NUM_VISIBLE: 3 1436 1437 }, 1438 1439 /* 1440 * Internationalizable strings in the Carousel component 1441 */ 1442 1443 STRINGS: { 1444 1445 /** 1446 * The content to be used as the progress indicator when the item 1447 * is still being loaded. Inserted into DOM with innerHTML. 1448 * 1449 * @property ITEM_LOADING_CONTENT 1450 * @type HTML 1451 * @default "Loading" 1452 */ 1453 ITEM_LOADING_CONTENT: "Loading", 1454 1455 /** 1456 * The next navigation button name/text. Inserted into DOM with innerHTML. 1457 * 1458 * @property NEXT_BUTTON_TEXT 1459 * @type HTML 1460 * @default "Next Page" 1461 */ 1462 NEXT_BUTTON_TEXT: "Next Page", 1463 1464 /** 1465 * The prefix text for the pager in case the UI is a drop-down. 1466 * Inserted into DOM with innerHTML. 1467 * 1468 * @property PAGER_PREFIX_TEXT 1469 * @type HTML 1470 * @default "Go to page " 1471 */ 1472 PAGER_PREFIX_TEXT: "Go to page ", 1473 1474 /** 1475 * The previous navigation button name/text. Inserted into DOM with innerHTML. 1476 * 1477 * @property PREVIOUS_BUTTON_TEXT 1478 * @type HTML 1479 * @default "Previous Page" 1480 */ 1481 PREVIOUS_BUTTON_TEXT: "Previous Page" 1482 1483 }, 1484 1485 /* 1486 * Public methods of the Carousel component 1487 */ 1488 1489 /** 1490 * Insert or append an item to the Carousel. 1491 * E.g. if Object: ({content:"Your Content", id:"", className:""}, index) 1492 * 1493 * @method addItem 1494 * @public 1495 * @param item {HTML | Object | HTMLElement} The item to be appended 1496 * to the Carousel. If the parameter is a string, it is assumed to be 1497 * the HTML content of the newly created item. If the parameter is an 1498 * object, it is assumed to supply the content and an optional class 1499 * and an optional id of the newly created item. 1500 * @param index {Number} optional The position to where in the list 1501 * (starts from zero). 1502 * @return {Boolean} Return true on success, false otherwise 1503 */ 1504 addItem: function (item, index) { 1505 var carousel = this, 1506 className, 1507 content, 1508 elId, 1509 replaceItems = 0, 1510 newIndex, // Add newIndex as workaround for undefined pos 1511 numItems = carousel.get("numItems"); 1512 1513 if (!item) { 1514 return false; 1515 } 1516 1517 if (JS.isString(item) || item.nodeName) { 1518 content = item.nodeName ? item.innerHTML : item; 1519 } else if (JS.isObject(item)) { 1520 content = item.content; 1521 } else { 1522 return false; 1523 } 1524 1525 className = carousel.CLASSES.ITEM + 1526 (item.className ? " " + item.className : ""); 1527 elId = item.id ? item.id : Dom.generateId(); 1528 1529 if (JS.isUndefined(index)) { 1530 carousel._itemsTable.items.push({ 1531 item : content, 1532 className : className, 1533 id : elId 1534 }); 1535 // Add newIndex as workaround for undefined pos 1536 newIndex = carousel._itemsTable.items.length - 1; 1537 } else { 1538 if (index < 0 || index > numItems) { 1539 return false; 1540 } 1541 1542 // make sure we splice into the correct position 1543 if (!carousel._itemsTable.items[index]) { 1544 carousel._itemsTable.items[index] = undefined; 1545 replaceItems = 1; 1546 } 1547 1548 carousel._itemsTable.items.splice(index, replaceItems, { 1549 item : content, 1550 className : className, 1551 id : elId 1552 }); 1553 } 1554 carousel._itemsTable.numItems++; 1555 1556 if (numItems < carousel._itemsTable.items.length) { 1557 carousel.set("numItems", carousel._itemsTable.items.length); 1558 } 1559 1560 // Add newPos as workaround for undefined pos 1561 carousel.fireEvent(itemAddedEvent, 1562 { pos: index, ev: itemAddedEvent, newPos: newIndex }); 1563 1564 return true; 1565 }, 1566 1567 /** 1568 * Insert or append multiple items to the Carousel. 1569 * 1570 * @method addItems 1571 * @public 1572 * @param items {Array} An array containing an array of new items each linked to the 1573 * index where the insertion should take place. 1574 * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]] 1575 * NOTE: An item at index must already exist. 1576 * @return {Boolean} Return true on success, false otherwise 1577 */ 1578 addItems: function (items) { 1579 var i, n, rv = true; 1580 1581 if (!JS.isArray(items)) { 1582 return false; 1583 } 1584 1585 syncUiOnItemInsert = false; 1586 for (i = 0, n = items.length; i < n; i++) { 1587 if (this.addItem(items[i][0], items[i][1]) === false) { 1588 rv = false; 1589 } 1590 } 1591 syncUiOnItemInsert = true; 1592 1593 this._syncUiItems(); 1594 1595 return rv; 1596 }, 1597 1598 /** 1599 * Remove focus from the Carousel. 1600 * 1601 * @method blur 1602 * @public 1603 */ 1604 blur: function () { 1605 this._carouselEl.blur(); 1606 this.fireEvent(blurEvent); 1607 }, 1608 1609 /** 1610 * Clears the items from Carousel. 1611 * 1612 * @method clearItems 1613 * @public 1614 */ 1615 clearItems: function () { 1616 var carousel = this, n = carousel.get("numItems"); 1617 1618 while (n > 0) { 1619 if (!carousel.removeItem(0)) { 1620 } 1621 /* 1622 For dynamic loading, the numItems may be much larger than 1623 the actual number of items in the table. So, set the 1624 numItems to zero, and break out of the loop if the table 1625 is already empty. 1626 */ 1627 if (carousel._itemsTable.numItems === 0) { 1628 carousel.set("numItems", 0); 1629 break; 1630 } 1631 n--; 1632 } 1633 1634 carousel.fireEvent(allItemsRemovedEvent); 1635 }, 1636 1637 /** 1638 * Set focus on the Carousel. 1639 * 1640 * @method focus 1641 * @public 1642 */ 1643 focus: function () { 1644 var carousel = this, 1645 first, 1646 focusEl, 1647 isSelectionInvisible, 1648 itemsTable, 1649 last, 1650 numVisible, 1651 selectOnScroll, 1652 selected, 1653 selItem; 1654 1655 // Don't do anything if the Carousel is not rendered 1656 if (!carousel._hasRendered) { 1657 return; 1658 } 1659 1660 if (carousel.isAnimating()) { 1661 // this messes up real bad! 1662 return; 1663 } 1664 1665 selItem = carousel.get("selectedItem"); 1666 numVisible = carousel.get("numVisible"); 1667 selectOnScroll = carousel.get("selectOnScroll"); 1668 selected = (selItem >= 0) ? 1669 carousel.getItem(selItem) : null; 1670 first = carousel.get("firstVisible"); 1671 last = first + numVisible - 1; 1672 isSelectionInvisible = (selItem < first || selItem > last); 1673 focusEl = (selected && selected.id) ? 1674 Dom.get(selected.id) : null; 1675 itemsTable = carousel._itemsTable; 1676 1677 if (!selectOnScroll && isSelectionInvisible) { 1678 focusEl = (itemsTable && itemsTable.items && 1679 itemsTable.items[first]) ? 1680 Dom.get(itemsTable.items[first].id) : null; 1681 } 1682 1683 if (focusEl) { 1684 try { 1685 focusEl.focus(); 1686 } catch (ex) { 1687 // ignore focus errors 1688 } 1689 } 1690 1691 carousel.fireEvent(focusEvent); 1692 }, 1693 1694 /** 1695 * Hide the Carousel. 1696 * 1697 * @method hide 1698 * @public 1699 */ 1700 hide: function () { 1701 var carousel = this; 1702 1703 if (carousel.fireEvent(beforeHideEvent) !== false) { 1704 carousel.removeClass(carousel.CLASSES.VISIBLE); 1705 showNavigation.call(carousel, false); 1706 carousel.fireEvent(hideEvent); 1707 } 1708 }, 1709 1710 /** 1711 * Initialize the Carousel. 1712 * 1713 * @method init 1714 * @public 1715 * @param el {HTMLElement | String} The html element that represents 1716 * the Carousel container. 1717 * @param attrs {Object} The set of configuration attributes for 1718 * creating the Carousel. 1719 */ 1720 init: function (el, attrs) { 1721 var carousel = this, 1722 elId = el, // save for a rainy day 1723 parse = false, 1724 selected; 1725 1726 if (!el) { 1727 return; 1728 } 1729 1730 carousel._hasRendered = false; 1731 carousel._navBtns = { prev: [], next: [] }; 1732 carousel._pages = { el: null, num: 0, cur: 0 }; 1733 carousel._pagination = {}; 1734 carousel._itemAttrCache = {}; 1735 1736 carousel._itemsTable = { loading: {}, numItems: 0, 1737 items: [], size: 0 }; 1738 1739 1740 if (JS.isString(el)) { 1741 el = Dom.get(el); 1742 } else if (!el.nodeName) { 1743 return; 1744 } 1745 1746 Carousel.superclass.init.call(carousel, el, attrs); 1747 1748 // check if we're starting somewhere in the middle 1749 selected = carousel.get("selectedItem"); 1750 if(selected > 0){ 1751 carousel.set("firstVisible",getFirstVisibleForPosition.call(carousel,selected)); 1752 } 1753 1754 if (el) { 1755 if (!el.id) { // in case the HTML element is passed 1756 el.setAttribute("id", Dom.generateId()); 1757 } 1758 parse = carousel._parseCarousel(el); 1759 if (!parse) { 1760 carousel._createCarousel(elId); 1761 } 1762 } else { 1763 el = carousel._createCarousel(elId); 1764 } 1765 elId = el.id; 1766 1767 carousel.initEvents(); 1768 1769 if (parse) { 1770 carousel._parseCarouselItems(); 1771 } 1772 1773 // add the selected class 1774 if(selected > 0){ 1775 setItemSelection.call(carousel,selected,0); 1776 } 1777 1778 if (!attrs || typeof attrs.isVertical == "undefined") { 1779 carousel.set("isVertical", false); 1780 } 1781 1782 carousel._parseCarouselNavigation(el); 1783 carousel._navEl = carousel._setupCarouselNavigation(); 1784 1785 instances[elId] = { object: carousel }; 1786 carousel._loadItems(Math.min(carousel.get("firstVisible")+carousel.get("numVisible"),carousel.get("numItems"))-1); 1787 }, 1788 1789 /** 1790 * Initialize the configuration attributes used to create the Carousel. 1791 * 1792 * @method initAttributes 1793 * @public 1794 * @param attrs {Object} The set of configuration attributes for 1795 * creating the Carousel. 1796 */ 1797 initAttributes: function (attrs) { 1798 var carousel = this; 1799 1800 attrs = attrs || {}; 1801 Carousel.superclass.initAttributes.call(carousel, attrs); 1802 1803 /** 1804 * @attribute carouselEl 1805 * @description The type of the Carousel element. 1806 * @default OL 1807 * @type Boolean 1808 */ 1809 carousel.setAttributeConfig("carouselEl", { 1810 validator : JS.isString, 1811 value : attrs.carouselEl || "OL" 1812 }); 1813 1814 /** 1815 * @attribute carouselItemEl 1816 * @description The type of the list of items within the Carousel. 1817 * @default LI 1818 * @type Boolean 1819 */ 1820 carousel.setAttributeConfig("carouselItemEl", { 1821 validator : JS.isString, 1822 value : attrs.carouselItemEl || "LI" 1823 }); 1824 1825 /** 1826 * @attribute currentPage 1827 * @description The current page number (read-only.) 1828 * @type Number 1829 */ 1830 carousel.setAttributeConfig("currentPage", { 1831 readOnly : true, 1832 value : 0 1833 }); 1834 1835 /** 1836 * @attribute firstVisible 1837 * @description The index to start the Carousel from (indexes begin 1838 * from zero) 1839 * @default 0 1840 * @type Number 1841 */ 1842 carousel.setAttributeConfig("firstVisible", { 1843 method : carousel._setFirstVisible, 1844 validator : carousel._validateFirstVisible, 1845 value : 1846 attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE 1847 }); 1848 1849 /** 1850 * @attribute selectOnScroll 1851 * @description Set this to true to automatically set focus to 1852 * follow scrolling in the Carousel. 1853 * @default true 1854 * @type Boolean 1855 */ 1856 carousel.setAttributeConfig("selectOnScroll", { 1857 validator : JS.isBoolean, 1858 value : attrs.selectOnScroll || true 1859 }); 1860 1861 /** 1862 * @attribute numVisible 1863 * @description The number of visible items in the Carousel's 1864 * viewport. 1865 * @default 3 1866 * @type Number 1867 */ 1868 carousel.setAttributeConfig("numVisible", { 1869 setter : carousel._numVisibleSetter, 1870 method : carousel._setNumVisible, 1871 validator : carousel._validateNumVisible, 1872 value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE 1873 }); 1874 1875 /** 1876 * @attribute numItems 1877 * @description The number of items in the Carousel. 1878 * @type Number 1879 */ 1880 carousel.setAttributeConfig("numItems", { 1881 method : carousel._setNumItems, 1882 validator : carousel._validateNumItems, 1883 value : carousel._itemsTable.numItems 1884 }); 1885 1886 /** 1887 * @attribute scrollIncrement 1888 * @description The number of items to scroll by for arrow keys. 1889 * @default 1 1890 * @type Number 1891 */ 1892 carousel.setAttributeConfig("scrollIncrement", { 1893 validator : carousel._validateScrollIncrement, 1894 value : attrs.scrollIncrement || 1 1895 }); 1896 1897 /** 1898 * @attribute selectedItem 1899 * @description The index of the selected item. 1900 * @type Number 1901 */ 1902 carousel.setAttributeConfig("selectedItem", { 1903 setter : carousel._selectedItemSetter, 1904 method : carousel._setSelectedItem, 1905 validator : JS.isNumber, 1906 value : -1 1907 }); 1908 1909 /** 1910 * @attribute revealAmount 1911 * @description The percentage of the item to be revealed on each 1912 * side of the Carousel (before and after the first and last item 1913 * in the Carousel's viewport.) 1914 * @default 0 1915 * @type Number 1916 */ 1917 carousel.setAttributeConfig("revealAmount", { 1918 method : carousel._setRevealAmount, 1919 validator : carousel._validateRevealAmount, 1920 value : attrs.revealAmount || 0 1921 }); 1922 1923 /** 1924 * @attribute isCircular 1925 * @description Set this to true to wrap scrolling of the contents 1926 * in the Carousel. 1927 * @default false 1928 * @type Boolean 1929 */ 1930 carousel.setAttributeConfig("isCircular", { 1931 validator : JS.isBoolean, 1932 value : attrs.isCircular || false 1933 }); 1934 1935 /** 1936 * @attribute isVertical 1937 * @description True if the orientation of the Carousel is vertical 1938 * @default false 1939 * @type Boolean 1940 */ 1941 carousel.setAttributeConfig("isVertical", { 1942 method : carousel._setOrientation, 1943 validator : JS.isBoolean, 1944 value : attrs.isVertical || false 1945 }); 1946 1947 /** 1948 * @attribute navigation 1949 * @description The set of navigation controls for Carousel 1950 * @default <br> 1951 * { prev: null, // the previous navigation element<br> 1952 * next: null } // the next navigation element 1953 * @type Object 1954 */ 1955 carousel.setAttributeConfig("navigation", { 1956 method : carousel._setNavigation, 1957 validator : carousel._validateNavigation, 1958 value : 1959 attrs.navigation || {prev: null,next: null,page: null} 1960 }); 1961 1962 /** 1963 * @attribute animation 1964 * @description The optional animation attributes for the Carousel. 1965 * @default <br> 1966 * { speed: 0, // the animation speed (in seconds)<br> 1967 * effect: null } // the animation effect (like 1968 * YAHOO.util.Easing.easeOut) 1969 * @type Object 1970 */ 1971 carousel.setAttributeConfig("animation", { 1972 validator : carousel._validateAnimation, 1973 value : attrs.animation || { speed: 0, effect: null } 1974 }); 1975 1976 /** 1977 * @attribute autoPlay 1978 * @description Set this to time in milli-seconds to have the 1979 * Carousel automatically scroll the contents. 1980 * @type Number 1981 * @deprecated Use autoPlayInterval instead. 1982 */ 1983 carousel.setAttributeConfig("autoPlay", { 1984 validator : JS.isNumber, 1985 value : attrs.autoPlay || 0 1986 }); 1987 1988 /** 1989 * @attribute autoPlayInterval 1990 * @description The delay in milli-seconds for scrolling the 1991 * Carousel during auto-play. 1992 * Note: The startAutoPlay() method needs to be invoked to trigger 1993 * automatic scrolling of Carousel. 1994 * @type Number 1995 */ 1996 carousel.setAttributeConfig("autoPlayInterval", { 1997 validator : JS.isNumber, 1998 value : attrs.autoPlayInterval || 0 1999 }); 2000 2001 /** 2002 * @attribute numPages 2003 * @description The number of pages in the carousel. 2004 * @type Number 2005 */ 2006 carousel.setAttributeConfig("numPages", { 2007 readOnly : true, 2008 getter : carousel._getNumPages 2009 }); 2010 2011 /** 2012 * @attribute lastVisible 2013 * @description The last item visible in the carousel. 2014 * @type Number 2015 */ 2016 carousel.setAttributeConfig("lastVisible", { 2017 readOnly : true, 2018 getter : carousel._getLastVisible 2019 }); 2020 }, 2021 2022 /** 2023 * Initialize and bind the event handlers. 2024 * 2025 * @method initEvents 2026 * @public 2027 */ 2028 initEvents: function () { 2029 var carousel = this, 2030 cssClass = carousel.CLASSES, 2031 focussedLi; 2032 2033 carousel.on("keydown", carousel._keyboardEventHandler); 2034 2035 carousel.on(afterScrollEvent, syncNavigation); 2036 2037 carousel.on(itemAddedEvent, syncUi); 2038 2039 carousel.on(itemRemovedEvent, syncUi); 2040 2041 carousel.on(itemReplacedEvent, syncUi); 2042 2043 carousel.on(itemSelectedEvent, carousel._focusHandler); 2044 2045 carousel.on(loadItemsEvent, syncUi); 2046 2047 carousel.on(allItemsRemovedEvent, function (ev) { 2048 carousel.scrollTo(0); 2049 syncNavigation.call(carousel); 2050 syncPagerUi.call(carousel); 2051 }); 2052 2053 carousel.on(pageChangeEvent, syncPagerUi, carousel); 2054 2055 carousel.on(renderEvent, function (ev) { 2056 if (carousel.get("selectedItem") === null || 2057 carousel.get("selectedItem") <= 0) { //in either case 2058 carousel.set("selectedItem", carousel.get("firstVisible")); 2059 } 2060 syncNavigation.call(carousel, ev); 2061 syncPagerUi.call(carousel, ev); 2062 carousel._setClipContainerSize(); 2063 carousel.show(); 2064 }); 2065 2066 carousel.on("selectedItemChange", function (ev) { 2067 setItemSelection.call(carousel, ev.newValue, ev.prevValue); 2068 if (ev.newValue >= 0) { 2069 carousel._updateTabIndex( 2070 carousel.getElementForItem(ev.newValue)); 2071 } 2072 carousel.fireEvent(itemSelectedEvent, ev.newValue); 2073 }); 2074 2075 carousel.on(uiUpdateEvent, function (ev) { 2076 syncNavigation.call(carousel, ev); 2077 syncPagerUi.call(carousel, ev); 2078 }); 2079 2080 carousel.on("firstVisibleChange", function (ev) { 2081 if (!carousel.get("selectOnScroll")) { 2082 if (ev.newValue >= 0) { 2083 carousel._updateTabIndex( 2084 carousel.getElementForItem(ev.newValue)); 2085 } 2086 } 2087 }); 2088 2089 // Handle item selection on mouse click 2090 carousel.on("click", function (ev) { 2091 if (carousel.isAutoPlayOn()) { 2092 carousel.stopAutoPlay(); 2093 } 2094 carousel._itemClickHandler(ev); 2095 carousel._pagerClickHandler(ev); 2096 }); 2097 2098 // Restore the focus on the navigation buttons 2099 2100 Event.onFocus(carousel.get("element"), function (ev, obj) { 2101 var target = Event.getTarget(ev); 2102 2103 if (target && target.nodeName.toUpperCase() == "A" && 2104 Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) { 2105 if (focussedLi) { 2106 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS); 2107 } 2108 focussedLi = target.parentNode; 2109 Dom.addClass(focussedLi, cssClass.PAGE_FOCUS); 2110 } else { 2111 if (focussedLi) { 2112 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS); 2113 } 2114 } 2115 2116 obj._hasFocus = true; 2117 obj._updateNavButtons(Event.getTarget(ev), true); 2118 }, carousel); 2119 2120 Event.onBlur(carousel.get("element"), function (ev, obj) { 2121 obj._hasFocus = false; 2122 obj._updateNavButtons(Event.getTarget(ev), false); 2123 }, carousel); 2124 }, 2125 2126 /** 2127 * Return true if the Carousel is still animating, or false otherwise. 2128 * 2129 * @method isAnimating 2130 * @return {Boolean} Return true if animation is still in progress, or 2131 * false otherwise. 2132 * @public 2133 */ 2134 isAnimating: function () { 2135 return this._isAnimationInProgress; 2136 }, 2137 2138 /** 2139 * Return true if the auto-scrolling of Carousel is "on", or false 2140 * otherwise. 2141 * 2142 * @method isAutoPlayOn 2143 * @return {Boolean} Return true if autoPlay is "on", or false 2144 * otherwise. 2145 * @public 2146 */ 2147 isAutoPlayOn: function () { 2148 return this._isAutoPlayInProgress; 2149 }, 2150 2151 /** 2152 * Return the carouselItemEl at index or null if the index is not 2153 * found. 2154 * 2155 * @method getElementForItem 2156 * @param index {Number} The index of the item to be returned 2157 * @return {Element} Return the item at index or null if not found 2158 * @public 2159 */ 2160 getElementForItem: function (index) { 2161 var carousel = this; 2162 2163 if (index < 0 || index >= carousel.get("numItems")) { 2164 return null; 2165 } 2166 2167 if (carousel._itemsTable.items[index]) { 2168 return Dom.get(carousel._itemsTable.items[index].id); 2169 } 2170 2171 return null; 2172 }, 2173 2174 /** 2175 * Return the carouselItemEl for all items in the Carousel. 2176 * 2177 * @method getElementForItems 2178 * @return {Array} Return all the items 2179 * @public 2180 */ 2181 getElementForItems: function () { 2182 var carousel = this, els = [], i; 2183 2184 for (i = 0; i < carousel._itemsTable.numItems; i++) { 2185 els.push(carousel.getElementForItem(i)); 2186 } 2187 2188 return els; 2189 }, 2190 2191 /** 2192 * Return the item at index or null if the index is not found. 2193 * 2194 * @method getItem 2195 * @param index {Number} The index of the item to be returned 2196 * @return {Object} Return the item at index or null if not found 2197 * @public 2198 */ 2199 getItem: function (index) { 2200 var carousel = this; 2201 2202 if (index < 0 || index >= carousel.get("numItems")) { 2203 return null; 2204 } 2205 2206 if (carousel._itemsTable.items.length > index) { 2207 if (!JS.isUndefined(carousel._itemsTable.items[index])) { 2208 return carousel._itemsTable.items[index]; 2209 } 2210 } 2211 2212 return null; 2213 }, 2214 2215 /** 2216 * Return all items as an array. 2217 * 2218 * @method getItems 2219 * @return {Array} Return all items in the Carousel 2220 * @public 2221 */ 2222 getItems: function () { 2223 return this._itemsTable.items; 2224 }, 2225 2226 /** 2227 * Return all loading items as an array. 2228 * 2229 * @method getLoadingItems 2230 * @return {Array} Return all items that are loading in the Carousel. 2231 * @public 2232 */ 2233 getLoadingItems: function () { 2234 return this._itemsTable.loading; 2235 }, 2236 2237 /** 2238 * For a multirow carousel, return the number of rows specified by user. 2239 * 2240 * @method getItems 2241 * @return {Number} Number of rows 2242 * @public 2243 */ 2244 getRows: function () { 2245 return this._rows; 2246 }, 2247 2248 /** 2249 * For a multirow carousel, return the number of cols specified by user. 2250 * 2251 * @method getItems 2252 * @return {Array} Return all items in the Carousel 2253 * @public 2254 */ 2255 getCols: function () { 2256 return this._cols; 2257 }, 2258 2259 /** 2260 * Return the position of the Carousel item that has the id "id", or -1 2261 * if the id is not found. 2262 * 2263 * @method getItemPositionById 2264 * @param index {Number} The index of the item to be returned 2265 * @public 2266 */ 2267 getItemPositionById: function (id) { 2268 var carousel = this, 2269 n = carousel.get("numItems"), 2270 i = 0, 2271 items = carousel._itemsTable.items, 2272 item; 2273 2274 while (i < n) { 2275 item = items[i] || {}; 2276 if(item.id == id) { 2277 return i; 2278 } 2279 i++; 2280 } 2281 2282 return -1; 2283 }, 2284 2285 /** 2286 * Return all visible items as an array. 2287 * 2288 * @method getVisibleItems 2289 * @return {Array} The array of visible items 2290 * @public 2291 */ 2292 getVisibleItems: function () { 2293 var carousel = this, 2294 i = carousel.get("firstVisible"), 2295 n = i + carousel.get("numVisible"), 2296 r = []; 2297 2298 while (i < n) { 2299 r.push(carousel.getElementForItem(i)); 2300 i++; 2301 } 2302 2303 return r; 2304 }, 2305 2306 /** 2307 * Remove an item at index from the Carousel. 2308 * 2309 * @method removeItem 2310 * @public 2311 * @param index {Number} The position to where in the list (starts from 2312 * zero). 2313 * @return {Boolean} Return true on success, false otherwise 2314 */ 2315 removeItem: function (index) { 2316 var carousel = this, 2317 itemsTable = carousel._itemsTable, 2318 item, 2319 num = carousel.get("numItems"); 2320 2321 if (index < 0 || index >= num) { 2322 return false; 2323 } 2324 2325 item = itemsTable.items.splice(index, 1); 2326 if (item && item.length == 1) { 2327 if(itemsTable.numItems){ 2328 itemsTable.numItems--; 2329 } 2330 2331 carousel.set("numItems", num - 1); 2332 2333 carousel.fireEvent(itemRemovedEvent, 2334 { item: item[0], pos: index, ev: itemRemovedEvent }); 2335 return true; 2336 } 2337 2338 return false; 2339 }, 2340 2341 /** 2342 * Replace an item at index witin Carousel. 2343 * 2344 * @method replaceItem 2345 * @public 2346 * @param item {HTML | Object | HTMLElement} The item to be appended 2347 * to the Carousel. If the parameter is a string, it is assumed to be 2348 * the HTML content of the newly created item. If the parameter is an 2349 * object, it is assumed to supply the content and an optional class 2350 * and an optional id of the newly created item. 2351 * @param index {Number} The position to where in the list (starts from 2352 * zero). 2353 * @return {Boolean} Return true on success, false otherwise 2354 */ 2355 replaceItem: function (item, index) { 2356 var carousel = this, 2357 className, 2358 content, 2359 elId, 2360 numItems = carousel.get("numItems"), 2361 oel, 2362 el = item; 2363 2364 if (!item) { 2365 return false; 2366 } 2367 2368 if (JS.isString(item) || item.nodeName) { 2369 content = item.nodeName ? item.innerHTML : item; 2370 } else if (JS.isObject(item)) { 2371 content = item.content; 2372 } else { 2373 return false; 2374 } 2375 2376 if (JS.isUndefined(index)) { 2377 return false; 2378 } else { 2379 if (index < 0 || index >= numItems) { 2380 return false; 2381 } 2382 2383 oel = carousel._itemsTable.items[index]; 2384 if(!oel){ 2385 oel = carousel._itemsTable.loading[index]; 2386 carousel._itemsTable.items[index] = undefined; 2387 } 2388 2389 elId = oel.id || Dom.generateId(); 2390 carousel._itemsTable.items.splice(index, 1, { 2391 item : content, 2392 className : carousel.CLASSES.ITEM + (item.className ? " " + item.className : ""), 2393 id : elId 2394 }); 2395 2396 el = carousel._itemsTable.items[index]; 2397 } 2398 carousel.fireEvent(itemReplacedEvent, 2399 { newItem: el, oldItem: oel, pos: index, ev: itemReplacedEvent }); 2400 2401 return true; 2402 }, 2403 2404 /** 2405 * Replace multiple items at specified indexes. 2406 * NOTE: item at index must already exist. 2407 * 2408 * @method replaceItems 2409 * @public 2410 * @param items {Array} An array containing an array of replacement items each linked to the 2411 * index where the substitution should take place. 2412 * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]] 2413 * @return {Boolean} Return true on success, false otherwise 2414 */ 2415 replaceItems: function (items) { 2416 var i, n, rv = true; 2417 2418 if (!JS.isArray(items)) { 2419 return false; 2420 } 2421 2422 syncUiOnItemInsert = false; 2423 for (i = 0, n = items.length; i < n; i++) { 2424 if (this.replaceItem(items[i][0], items[i][1]) === false) { 2425 rv = false; 2426 } 2427 } 2428 syncUiOnItemInsert = true; 2429 2430 this._syncUiItems(); 2431 2432 return rv; 2433 }, 2434 2435 /** 2436 * Render the Carousel. 2437 * 2438 * @method render 2439 * @public 2440 * @param appendTo {HTMLElement | String} The element to which the 2441 * Carousel should be appended prior to rendering. 2442 * @return {Boolean} Status of the operation 2443 */ 2444 render: function (appendTo) { 2445 var carousel = this, 2446 cssClass = carousel.CLASSES, 2447 rows = carousel._rows; 2448 2449 carousel.addClass(cssClass.CAROUSEL); 2450 2451 if (!carousel._clipEl) { 2452 carousel._clipEl = carousel._createCarouselClip(); 2453 carousel._clipEl.appendChild(carousel._carouselEl); 2454 } 2455 2456 if (appendTo) { 2457 carousel.appendChild(carousel._clipEl); 2458 carousel.appendTo(appendTo); 2459 } else { 2460 if (!Dom.inDocument(carousel.get("element"))) { 2461 return false; 2462 } 2463 carousel.appendChild(carousel._clipEl); 2464 } 2465 2466 if (rows) { 2467 Dom.addClass(carousel._clipEl, cssClass.MULTI_ROW); 2468 } 2469 2470 if (carousel.get("isVertical")) { 2471 carousel.addClass(cssClass.VERTICAL); 2472 } else { 2473 carousel.addClass(cssClass.HORIZONTAL); 2474 } 2475 2476 if (carousel.get("numItems") < 1) { 2477 return false; 2478 } 2479 2480 carousel._refreshUi(); 2481 2482 return true; 2483 }, 2484 2485 /** 2486 * Scroll the Carousel by an item backward. 2487 * 2488 * @method scrollBackward 2489 * @public 2490 */ 2491 scrollBackward: function () { 2492 var carousel = this; 2493 carousel.scrollTo(carousel._firstItem - 2494 carousel.get("scrollIncrement")); 2495 }, 2496 2497 /** 2498 * Scroll the Carousel by an item forward. 2499 * 2500 * @method scrollForward 2501 * @public 2502 */ 2503 scrollForward: function () { 2504 var carousel = this; 2505 carousel.scrollTo(carousel._firstItem + 2506 carousel.get("scrollIncrement")); 2507 }, 2508 2509 /** 2510 * Scroll the Carousel by a page backward. 2511 * 2512 * @method scrollPageBackward 2513 * @public 2514 */ 2515 scrollPageBackward: function () { 2516 var carousel = this, 2517 isVertical = carousel.get("isVertical"), 2518 cols = carousel._cols, 2519 firstVisible = carousel.get("firstVisible"), 2520 item = firstVisible - carousel.get("numVisible"); 2521 2522 if (item < 0) { 2523 // Only account for multi-row when scrolling backwards from 2524 // item 0 2525 if (cols) { 2526 item = firstVisible - cols; 2527 } 2528 } 2529 2530 carousel.scrollTo(item); 2531 }, 2532 2533 /** 2534 * Scroll the Carousel by a page forward. 2535 * 2536 * @method scrollPageForward 2537 * @public 2538 */ 2539 scrollPageForward: function () { 2540 var carousel = this, 2541 item = carousel._firstItem + carousel.get("numVisible"); 2542 2543 if (item > carousel.get("numItems")) { 2544 item = 0; 2545 } 2546 2547 if (carousel.get("selectOnScroll")) { 2548 carousel._selectedItem = carousel._getSelectedItem(item); 2549 } 2550 2551 carousel.scrollTo(item); 2552 }, 2553 2554 /** 2555 * Scroll the Carousel to make the item the first visible item. 2556 * 2557 * @method scrollTo 2558 * @public 2559 * @param item Number The index of the element to position at. 2560 * @param dontSelect Boolean True if select should be avoided 2561 */ 2562 scrollTo: function (item, dontSelect) { 2563 var carousel = this, animate, animCfg, isCircular, isVertical, 2564 delta, direction, firstItem, lastItem, itemsPerRow, 2565 itemsPerCol, numItems, numPerPage, offset, page, rv, sentinel, 2566 index, stopAutoScroll, 2567 itemsTable = carousel._itemsTable; 2568 2569 if (itemsTable.numItems === 0 || item == carousel._firstItem || 2570 carousel.isAnimating()) { 2571 return; // nothing to do! 2572 } 2573 2574 animCfg = carousel.get("animation"); 2575 isCircular = carousel.get("isCircular"); 2576 isVertical = carousel.get("isVertical"); 2577 itemsPerRow = carousel._cols; 2578 itemsPerCol = carousel._rows; 2579 firstItem = carousel._firstItem; 2580 numItems = carousel.get("numItems"); 2581 numPerPage = carousel.get("numVisible"); 2582 page = carousel.get("currentPage"); 2583 2584 stopAutoScroll = function () { 2585 if (carousel.isAutoPlayOn()) { 2586 carousel.stopAutoPlay(); 2587 } 2588 }; 2589 2590 if (item < 0) { 2591 if (isCircular) { 2592 // Normalize the offset so that it doesn't scroll to a 2593 // different index when number of items is not a factor of 2594 // the number of visible items 2595 if (numItems % numPerPage !== 0) { 2596 item = numItems + (numItems%numPerPage) - numPerPage-1; 2597 } else { 2598 item = numItems + item; 2599 } 2600 } else { 2601 stopAutoScroll.call(carousel); 2602 return; 2603 } 2604 } else if (numItems > 0 && item > numItems - 1) { 2605 2606 if (carousel.get("isCircular")) { 2607 item = numItems - item; 2608 } else { 2609 stopAutoScroll.call(carousel); 2610 return; 2611 } 2612 } 2613 2614 if (isNaN(item)) { 2615 return; 2616 } 2617 2618 direction = (carousel._firstItem > item) ? "backward" : "forward"; 2619 2620 sentinel = firstItem + numPerPage; 2621 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel; 2622 rv = carousel.fireEvent(beforeScrollEvent, 2623 { dir: direction, first: firstItem, last: sentinel }); 2624 if (rv === false) { // scrolling is prevented 2625 return; 2626 } 2627 2628 carousel.fireEvent(beforePageChangeEvent, { page: page }); 2629 2630 // call loaditems to check if we have all the items to display 2631 lastItem = item + numPerPage - 1; 2632 carousel._loadItems(lastItem > numItems-1 ? numItems-1 : lastItem); 2633 2634 // Calculate the delta relative to the first item, the delta is 2635 // always negative. 2636 delta = 0 - item; 2637 2638 if (itemsPerCol) { 2639 // offset calculations for multirow Carousel 2640 if (isVertical) { 2641 delta = parseInt(delta / itemsPerRow, 10); 2642 } else { 2643 delta = parseInt(delta / itemsPerCol, 10); 2644 } 2645 } 2646 2647 carousel._firstItem = item; 2648 carousel.set("firstVisible", item); 2649 2650 if (!dontSelect && carousel.get("selectOnScroll")) { 2651 carousel._selectedItem = item; 2652 } 2653 2654 2655 sentinel = item + numPerPage; 2656 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel; 2657 2658 offset = getScrollOffset.call(carousel, delta); 2659 2660 animate = animCfg.speed > 0; 2661 2662 if (animate) { 2663 carousel._animateAndSetCarouselOffset(offset, item, sentinel, 2664 dontSelect); 2665 } else { 2666 carousel._setCarouselOffset(offset); 2667 updateStateAfterScroll.call(carousel, item, sentinel); 2668 } 2669 }, 2670 2671 /** 2672 * Get the page an item is on within carousel. 2673 * 2674 * @method getPageForItem 2675 * @public 2676 * @param index {Number} Index of item 2677 * @return {Number} Page item is on 2678 */ 2679 getPageForItem : function(item) { 2680 return Math.ceil( 2681 (item+1) / parseInt(this.get("numVisible"),10) 2682 ); 2683 }, 2684 2685 /** 2686 * Get the first visible item's index on any given page. 2687 * 2688 * @method getFirstVisibleOnpage 2689 * @public 2690 * @param page {Number} Page 2691 * @return {Number} First item's index 2692 */ 2693 getFirstVisibleOnPage : function(page) { 2694 return (page - 1) * this.get("numVisible"); 2695 }, 2696 2697 /** 2698 * Select the previous item in the Carousel. 2699 * 2700 * @method selectPreviousItem 2701 * @public 2702 */ 2703 selectPreviousItem: function () { 2704 var carousel = this, 2705 newpos = 0, 2706 selected = carousel.get("selectedItem"); 2707 2708 if (selected == carousel._firstItem) { 2709 newpos = selected - carousel.get("numVisible"); 2710 carousel._selectedItem = carousel._getSelectedItem(selected-1); 2711 // since we have selected the item already 2712 carousel.scrollTo(newpos, true); 2713 } else { 2714 newpos = carousel.get("selectedItem") - 2715 carousel.get("scrollIncrement"); 2716 carousel.set("selectedItem",carousel._getSelectedItem(newpos)); 2717 } 2718 }, 2719 2720 /** 2721 * Select the next item in the Carousel. 2722 * 2723 * @method selectNextItem 2724 * @public 2725 */ 2726 selectNextItem: function () { 2727 var carousel = this, newpos = 0; 2728 2729 newpos = carousel.get("selectedItem") + 2730 carousel.get("scrollIncrement"); 2731 carousel.set("selectedItem", carousel._getSelectedItem(newpos)); 2732 }, 2733 2734 /** 2735 * Display the Carousel. 2736 * 2737 * @method show 2738 * @public 2739 */ 2740 show: function () { 2741 var carousel = this, 2742 cssClass = carousel.CLASSES; 2743 2744 if (carousel.fireEvent(beforeShowEvent) !== false) { 2745 carousel.addClass(cssClass.VISIBLE); 2746 showNavigation.call(carousel); 2747 carousel.fireEvent(showEvent); 2748 } 2749 }, 2750 2751 /** 2752 * Start auto-playing the Carousel. 2753 * 2754 * @method startAutoPlay 2755 * @public 2756 */ 2757 startAutoPlay: function () { 2758 var carousel = this, timer; 2759 2760 if (JS.isUndefined(carousel._autoPlayTimer)) { 2761 timer = carousel.get("autoPlayInterval"); 2762 if (timer <= 0) { 2763 return; 2764 } 2765 carousel._isAutoPlayInProgress = true; 2766 carousel.fireEvent(startAutoPlayEvent); 2767 carousel._autoPlayTimer = setTimeout(function () { 2768 carousel._autoScroll(); 2769 }, timer); 2770 } 2771 }, 2772 2773 /** 2774 * Stop auto-playing the Carousel. 2775 * 2776 * @method stopAutoPlay 2777 * @public 2778 */ 2779 stopAutoPlay: function () { 2780 var carousel = this; 2781 2782 if (!JS.isUndefined(carousel._autoPlayTimer)) { 2783 clearTimeout(carousel._autoPlayTimer); 2784 delete carousel._autoPlayTimer; 2785 carousel._isAutoPlayInProgress = false; 2786 carousel.fireEvent(stopAutoPlayEvent); 2787 } 2788 }, 2789 2790 /** 2791 * Update interface's pagination data within a registered template. 2792 * 2793 * @method updatePagination 2794 * @public 2795 */ 2796 updatePagination: function () { 2797 var carousel = this, 2798 pagination = carousel._pagination; 2799 if(!pagination.el){ return false; } 2800 2801 var numItems = carousel.get('numItems'), 2802 numVisible = carousel.get('numVisible'), 2803 firstVisible = carousel.get('firstVisible')+1, 2804 currentPage = carousel.get('currentPage')+1, 2805 numPages = carousel.get('numPages'), 2806 replacements = { 2807 'numVisible' : numVisible, 2808 'numPages' : numPages, 2809 'numItems' : numItems, 2810 'selectedItem' : carousel.get('selectedItem')+1, 2811 'currentPage' : currentPage, 2812 'firstVisible' : firstVisible, 2813 'lastVisible' : carousel.get("lastVisible")+1 2814 }, 2815 cb = pagination.callback || {}, 2816 scope = cb.scope && cb.obj ? cb.obj : carousel; 2817 2818 pagination.el.innerHTML = JS.isFunction(cb.fn) ? cb.fn.apply(scope, [pagination.template, replacements]) : YAHOO.lang.substitute(pagination.template, replacements); 2819 }, 2820 2821 /** 2822 * Register carousels pagination template, append to interface, and populate. 2823 * 2824 * @method registerPagination 2825 * @param template {String} Pagination template as passed to lang.substitute 2826 * @public 2827 */ 2828 registerPagination: function (tpl, pos, cb) { 2829 var carousel = this; 2830 2831 carousel._pagination.template = tpl; 2832 carousel._pagination.callback = cb || {}; 2833 2834 if(!carousel._pagination.el){ 2835 carousel._pagination.el = createElement('DIV', {className:carousel.CLASSES.PAGINATION}); 2836 2837 if(pos == "before"){ 2838 carousel._navEl.insertBefore(carousel._pagination.el, carousel._navEl.firstChild); 2839 } else { 2840 carousel._navEl.appendChild(carousel._pagination.el); 2841 } 2842 2843 carousel.on('itemSelected', carousel.updatePagination); 2844 carousel.on('pageChange', carousel.updatePagination); 2845 } 2846 2847 carousel.updatePagination(); 2848 }, 2849 2850 /** 2851 * Return the string representation of the Carousel. 2852 * 2853 * @method toString 2854 * @public 2855 * @return {String} 2856 */ 2857 toString: function () { 2858 return WidgetName + (this.get ? " (#" + this.get("id") + ")" : ""); 2859 }, 2860 2861 /* 2862 * Protected methods of the Carousel component 2863 */ 2864 2865 /** 2866 * Set the Carousel offset to the passed offset after animating. 2867 * 2868 * @method _animateAndSetCarouselOffset 2869 * @param {Integer} offset The offset to which the Carousel has to be 2870 * scrolled to. 2871 * @param {Integer} item The index to which the Carousel will scroll. 2872 * @param {Integer} sentinel The last element in the view port. 2873 * @protected 2874 */ 2875 _animateAndSetCarouselOffset: function (offset, item, sentinel) { 2876 var carousel = this, 2877 animCfg = carousel.get("animation"), 2878 animObj = null; 2879 2880 if (carousel.get("isVertical")) { 2881 animObj = new YAHOO.util.Motion(carousel._carouselEl, 2882 { top: { to: offset } }, 2883 animCfg.speed, animCfg.effect); 2884 } else { 2885 animObj = new YAHOO.util.Motion(carousel._carouselEl, 2886 { left: { to: offset } }, 2887 animCfg.speed, animCfg.effect); 2888 } 2889 2890 carousel._isAnimationInProgress = true; 2891 animObj.onComplete.subscribe(carousel._animationCompleteHandler, 2892 { scope: carousel, item: item, 2893 last: sentinel }); 2894 animObj.animate(); 2895 }, 2896 2897 /** 2898 * Handle the animation complete event. 2899 * 2900 * @method _animationCompleteHandler 2901 * @param {Event} ev The event. 2902 * @param {Array} p The event parameters. 2903 * @param {Object} o The object that has the state of the Carousel 2904 * @protected 2905 */ 2906 _animationCompleteHandler: function (ev, p, o) { 2907 o.scope._isAnimationInProgress = false; 2908 updateStateAfterScroll.call(o.scope, o.item, o.last); 2909 }, 2910 2911 /** 2912 * Automatically scroll the contents of the Carousel. 2913 * @method _autoScroll 2914 * @protected 2915 */ 2916 _autoScroll: function() { 2917 var carousel = this, 2918 currIndex = carousel._firstItem, 2919 index; 2920 2921 if (currIndex >= carousel.get("numItems") - 1) { 2922 if (carousel.get("isCircular")) { 2923 index = 0; 2924 } else { 2925 carousel.stopAutoPlay(); 2926 } 2927 } else { 2928 index = currIndex + carousel.get("numVisible"); 2929 } 2930 2931 carousel._selectedItem = carousel._getSelectedItem(index); 2932 carousel.scrollTo.call(carousel, index); 2933 }, 2934 2935 /** 2936 * Create the Carousel. 2937 * 2938 * @method createCarousel 2939 * @param elId {String} The id of the element to be created 2940 * @protected 2941 */ 2942 _createCarousel: function (elId) { 2943 var carousel = this, 2944 cssClass = carousel.CLASSES, 2945 el = Dom.get(elId); 2946 2947 if (!el) { 2948 el = createElement("DIV", { 2949 className : cssClass.CAROUSEL, 2950 id : elId 2951 }); 2952 } 2953 2954 if (!carousel._carouselEl) { 2955 carousel._carouselEl=createElement(carousel.get("carouselEl"), 2956 { className: cssClass.CAROUSEL_EL }); 2957 } 2958 2959 return el; 2960 }, 2961 2962 /** 2963 * Create the Carousel clip container. 2964 * 2965 * @method createCarouselClip 2966 * @protected 2967 */ 2968 _createCarouselClip: function () { 2969 return createElement("DIV", { className: this.CLASSES.CONTENT }); 2970 }, 2971 2972 /** 2973 * Create the Carousel item. 2974 * 2975 * @method createCarouselItem 2976 * @param obj {Object} The attributes of the element to be created 2977 * @protected 2978 */ 2979 _createCarouselItem: function (obj) { 2980 var attr, carousel = this; 2981 2982 return createElement(carousel.get("carouselItemEl"), { 2983 className : obj.className, 2984 styles : {}, 2985 content : obj.content, 2986 id : obj.id 2987 }); 2988 }, 2989 2990 /** 2991 * Return a valid item for a possibly out of bounds index considering 2992 * the isCircular property. 2993 * 2994 * @method _getValidIndex 2995 * @param index {Number} The index of the item to be returned 2996 * @return {Object} Return a valid item index 2997 * @protected 2998 */ 2999 _getValidIndex: function (index) { 3000 var carousel = this, 3001 isCircular = carousel.get("isCircular"), 3002 numItems = carousel.get("numItems"), 3003 numVisible = carousel.get("numVisible"), 3004 sentinel = numItems - 1; 3005 3006 if (index < 0) { 3007 index = isCircular ? 3008 Math.ceil(numItems/numVisible)*numVisible + index : 0; 3009 } else if (index > sentinel) { 3010 index = isCircular ? 0 : sentinel; 3011 } 3012 3013 return index; 3014 }, 3015 3016 /** 3017 * Get the value for the selected item. 3018 * 3019 * @method _getSelectedItem 3020 * @param val {Number} The new value for "selected" item 3021 * @return {Number} The new value that would be set 3022 * @protected 3023 */ 3024 _getSelectedItem: function (val) { 3025 var carousel = this, 3026 isCircular = carousel.get("isCircular"), 3027 numItems = carousel.get("numItems"), 3028 sentinel = numItems - 1; 3029 3030 if (val < 0) { 3031 if (isCircular) { 3032 val = numItems + val; 3033 } else { 3034 val = carousel.get("selectedItem"); 3035 } 3036 } else if (val > sentinel) { 3037 if (isCircular) { 3038 val = val - numItems; 3039 } else { 3040 val = carousel.get("selectedItem"); 3041 } 3042 } 3043 return val; 3044 }, 3045 3046 /** 3047 * The "focus" handler for a Carousel. 3048 * 3049 * @method _focusHandler 3050 * @param {Event} ev The event object 3051 * @protected 3052 */ 3053 _focusHandler: function() { 3054 var carousel = this; 3055 if (carousel._hasFocus) { 3056 carousel.focus(); 3057 } 3058 }, 3059 3060 /** 3061 * The "click" handler for the item. 3062 * 3063 * @method _itemClickHandler 3064 * @param {Event} ev The event object 3065 * @protected 3066 */ 3067 _itemClickHandler: function (ev) { 3068 var carousel = this, 3069 carouselItem = carousel.get("carouselItemEl"), 3070 container = carousel.get("element"), 3071 el, 3072 item, 3073 target = Event.getTarget(ev), 3074 tag = target.tagName.toUpperCase(); 3075 3076 if(tag === "INPUT" || 3077 tag === "SELECT" || 3078 tag === "TEXTAREA") { 3079 return; 3080 } 3081 3082 while (target && target != container && 3083 target.id != carousel._carouselEl) { 3084 el = target.nodeName; 3085 if (el.toUpperCase() == carouselItem) { 3086 break; 3087 } 3088 target = target.parentNode; 3089 } 3090 3091 if ((item = carousel.getItemPositionById(target.id)) >= 0) { 3092 carousel.set("selectedItem", carousel._getSelectedItem(item)); 3093 carousel.focus(); 3094 } 3095 }, 3096 3097 /** 3098 * The keyboard event handler for Carousel. 3099 * 3100 * @method _keyboardEventHandler 3101 * @param ev {Event} The event that is being handled. 3102 * @protected 3103 */ 3104 _keyboardEventHandler: function (ev) { 3105 var carousel = this, 3106 key = Event.getCharCode(ev), 3107 target = Event.getTarget(ev), 3108 prevent = false; 3109 3110 // do not mess while animation is in progress or naving via select 3111 if (carousel.isAnimating() || target.tagName.toUpperCase() === "SELECT") { 3112 return; 3113 } 3114 3115 switch (key) { 3116 case 0x25: // left arrow 3117 case 0x26: // up arrow 3118 carousel.selectPreviousItem(); 3119 prevent = true; 3120 break; 3121 case 0x27: // right arrow 3122 case 0x28: // down arrow 3123 carousel.selectNextItem(); 3124 prevent = true; 3125 break; 3126 case 0x21: // page-up 3127 carousel.scrollPageBackward(); 3128 prevent = true; 3129 break; 3130 case 0x22: // page-down 3131 carousel.scrollPageForward(); 3132 prevent = true; 3133 break; 3134 } 3135 3136 if (prevent) { 3137 if (carousel.isAutoPlayOn()) { 3138 carousel.stopAutoPlay(); 3139 } 3140 Event.preventDefault(ev); 3141 } 3142 }, 3143 3144 /** 3145 * The load the required set of items that are needed for display. 3146 * 3147 * @method _loadItems 3148 * @protected 3149 */ 3150 _loadItems: function(last) { 3151 var carousel = this, 3152 numItems = carousel.get("numItems"), 3153 numVisible = carousel.get("numVisible"), 3154 reveal = carousel.get("revealAmount"), 3155 first = carousel._itemsTable.items.length, 3156 lastVisible = carousel.get("lastVisible"); 3157 3158 // adjust if going backwards 3159 if(first > last && last+1 >= numVisible){ 3160 // need to get first a bit differently for the last page 3161 first = last % numVisible || last == lastVisible ? last - last % numVisible : last - numVisible + 1; 3162 } 3163 3164 if(reveal && last < numItems - 1){ last++; } 3165 3166 if (last >= first && (!carousel.getItem(first) || !carousel.getItem(last))) { 3167 carousel.fireEvent(loadItemsEvent, { 3168 ev: loadItemsEvent, first: first, last: last, 3169 num: last - first + 1 3170 }); 3171 } 3172 3173 }, 3174 3175 /** 3176 * The "onchange" handler for select box pagination. 3177 * 3178 * @method _pagerChangeHandler 3179 * @param {Event} ev The event object 3180 * @protected 3181 */ 3182 _pagerChangeHandler: function (ev) { 3183 var carousel = this, 3184 target = Event.getTarget(ev), 3185 page = target.value, 3186 item; 3187 3188 if (page) { 3189 item = carousel.getFirstVisibleOnPage(page); 3190 carousel._selectedItem = item; 3191 carousel.scrollTo(item); 3192 carousel.focus(); 3193 } 3194 }, 3195 /** 3196 * The "click" handler for anchor pagination. 3197 * 3198 * @method _pagerClickHandler 3199 * @param {Event} ev The event object 3200 * @protected 3201 */ 3202 _pagerClickHandler: function (ev) { 3203 var carousel = this, 3204 css = carousel.CLASSES, 3205 target = Event.getTarget(ev), 3206 elNode = target.nodeName.toUpperCase(), 3207 val, 3208 stringIndex, 3209 page, 3210 item; 3211 3212 if (Dom.hasClass(target, css.PAGER_ITEM) || Dom.hasClass(target.parentNode, css.PAGER_ITEM)) { 3213 if (elNode == "EM") { 3214 target = target.parentNode;// item is an em and not an anchor (when text is visible) 3215 } 3216 val = target.href; 3217 stringIndex = val.lastIndexOf("#"); 3218 page = parseInt(val.substring(stringIndex+1), 10); 3219 if (page != -1) { 3220 item = carousel.getFirstVisibleOnPage(page); 3221 carousel._selectedItem = item; 3222 carousel.scrollTo(item); 3223 carousel.focus(); 3224 } 3225 Event.preventDefault(ev); 3226 } 3227 }, 3228 3229 /** 3230 * Find the Carousel within a container. The Carousel is identified by 3231 * the first element that matches the carousel element tag or the 3232 * element that has the Carousel class. 3233 * 3234 * @method parseCarousel 3235 * @param parent {HTMLElement} The parent element to look under 3236 * @return {Boolean} True if Carousel is found, false otherwise 3237 * @protected 3238 */ 3239 _parseCarousel: function (parent) { 3240 var carousel = this, child, cssClass, domEl, found, node; 3241 3242 cssClass = carousel.CLASSES; 3243 domEl = carousel.get("carouselEl"); 3244 found = false; 3245 3246 for (child = parent.firstChild; child; child = child.nextSibling) { 3247 if (child.nodeType == 1) { 3248 node = child.nodeName; 3249 if (node.toUpperCase() == domEl) { 3250 carousel._carouselEl = child; 3251 Dom.addClass(carousel._carouselEl, 3252 carousel.CLASSES.CAROUSEL_EL); 3253 found = true; 3254 } 3255 } 3256 } 3257 3258 return found; 3259 }, 3260 3261 /** 3262 * Find the items within the Carousel and add them to the items table. 3263 * A Carousel item is identified by elements that matches the carousel 3264 * item element tag. 3265 * 3266 * @method parseCarouselItems 3267 * @protected 3268 */ 3269 _parseCarouselItems: function () { 3270 var carousel = this, 3271 cssClass = carousel.CLASSES, 3272 i=0, 3273 rows, 3274 child, 3275 domItemEl, 3276 elId, 3277 node, 3278 index = carousel.get("firstVisible"), 3279 parent = carousel._carouselEl; 3280 3281 rows = carousel._rows; 3282 domItemEl = carousel.get("carouselItemEl"); 3283 3284 for (child = parent.firstChild; child; child = child.nextSibling) { 3285 if (child.nodeType == 1) { 3286 node = child.nodeName; 3287 if (node.toUpperCase() == domItemEl) { 3288 if (child.id) { 3289 elId = child.id; 3290 } else { 3291 elId = Dom.generateId(); 3292 child.setAttribute("id", elId); 3293 Dom.addClass(child, carousel.CLASSES.ITEM); 3294 } 3295 carousel.addItem(child,index); 3296 index++; 3297 } 3298 } 3299 } 3300 }, 3301 3302 /** 3303 * Find the Carousel navigation within a container. The navigation 3304 * elements need to match the carousel navigation class names. 3305 * 3306 * @method parseCarouselNavigation 3307 * @param parent {HTMLElement} The parent element to look under 3308 * @return {Boolean} True if at least one is found, false otherwise 3309 * @protected 3310 */ 3311 _parseCarouselNavigation: function (parent) { 3312 var carousel = this, 3313 cfg, 3314 cssClass = carousel.CLASSES, 3315 el, 3316 i, 3317 j, 3318 nav, 3319 rv = false; 3320 3321 nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent); 3322 if (nav.length > 0) { 3323 for (i in nav) { 3324 if (nav.hasOwnProperty(i)) { 3325 el = nav[i]; 3326 if (el.nodeName == "INPUT" || 3327 el.nodeName == "BUTTON" || 3328 el.nodeName == "A") {// Anchor support in Nav (for SEO) 3329 carousel._navBtns.prev.push(el); 3330 } else { 3331 j = el.getElementsByTagName("INPUT"); 3332 if (JS.isArray(j) && j.length > 0) { 3333 carousel._navBtns.prev.push(j[0]); 3334 } else { 3335 j = el.getElementsByTagName("BUTTON"); 3336 if (JS.isArray(j) && j.length > 0) { 3337 carousel._navBtns.prev.push(j[0]); 3338 } 3339 } 3340 } 3341 } 3342 } 3343 cfg = { prev: nav }; 3344 } 3345 3346 nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent); 3347 if (nav.length > 0) { 3348 for (i in nav) { 3349 if (nav.hasOwnProperty(i)) { 3350 el = nav[i]; 3351 if (el.nodeName == "INPUT" || 3352 el.nodeName == "BUTTON" || 3353 el.nodeName == "A") {// Anchor support in Nav (for SEO) 3354 carousel._navBtns.next.push(el); 3355 } else { 3356 j = el.getElementsByTagName("INPUT"); 3357 if (JS.isArray(j) && j.length > 0) { 3358 carousel._navBtns.next.push(j[0]); 3359 } else { 3360 j = el.getElementsByTagName("BUTTON"); 3361 if (JS.isArray(j) && j.length > 0) { 3362 carousel._navBtns.next.push(j[0]); 3363 } 3364 } 3365 } 3366 } 3367 } 3368 if (cfg) { 3369 cfg.next = nav; 3370 } else { 3371 cfg = { next: nav }; 3372 } 3373 } 3374 3375 if (cfg) { 3376 carousel.set("navigation", cfg); 3377 rv = true; 3378 } 3379 3380 return rv; 3381 }, 3382 3383 /** 3384 * Refresh the widget UI if it is not already rendered, on first item 3385 * addition. 3386 * 3387 * @method _refreshUi 3388 * @protected 3389 */ 3390 _refreshUi: function () { 3391 var carousel = this, 3392 isVertical = carousel.get("isVertical"), 3393 firstVisible = carousel.get("firstVisible"), 3394 i, item, n, rsz, sz; 3395 3396 if (carousel._itemsTable.numItems < 1) { 3397 return; 3398 } 3399 3400 sz = getCarouselItemSize.call(carousel, 3401 isVertical ? "height" : "width"); 3402 // This fixes the widget to auto-adjust height/width for absolute 3403 // positioned children. 3404 item = carousel._itemsTable.items[firstVisible].id; 3405 3406 sz = isVertical ? getStyle(item, "width") : 3407 getStyle(item, "height"); 3408 3409 Dom.setStyle(carousel._carouselEl, 3410 isVertical ? "width" : "height", sz + "px"); 3411 3412 // Set the rendered state appropriately. 3413 carousel._hasRendered = true; 3414 carousel.fireEvent(renderEvent); 3415 }, 3416 3417 /** 3418 * Set the Carousel offset to the passed offset. 3419 * 3420 * @method _setCarouselOffset 3421 * @protected 3422 */ 3423 _setCarouselOffset: function (offset) { 3424 var carousel = this, which; 3425 3426 which = carousel.get("isVertical") ? "top" : "left"; 3427 Dom.setStyle(carousel._carouselEl, which, offset + "px"); 3428 }, 3429 3430 /** 3431 * Setup/Create the Carousel navigation element (if needed). 3432 * 3433 * @method _setupCarouselNavigation 3434 * @protected 3435 */ 3436 _setupCarouselNavigation: function () { 3437 var carousel = this, 3438 btn, cfg, cssClass, nav, navContainer, nextButton, prevButton; 3439 3440 cssClass = carousel.CLASSES; 3441 3442 // TODO: can the _navBtns be tested against instead? 3443 navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION, 3444 "DIV", carousel.get("element")); 3445 3446 if (navContainer.length === 0) { 3447 navContainer = createElement("DIV", 3448 { className: cssClass.NAVIGATION }); 3449 carousel.insertBefore(navContainer, 3450 Dom.getFirstChild(carousel.get("element"))); 3451 } else { 3452 navContainer = navContainer[0]; 3453 } 3454 3455 carousel._pages.el = createElement("UL"); 3456 navContainer.appendChild(carousel._pages.el); 3457 3458 nav = carousel.get("navigation"); 3459 if (JS.isString(nav.prev) || JS.isArray(nav.prev)) { 3460 if (JS.isString(nav.prev)) { 3461 nav.prev = [nav.prev]; 3462 } 3463 for (btn in nav.prev) { 3464 if (nav.prev.hasOwnProperty(btn)) { 3465 carousel._navBtns.prev.push(Dom.get(nav.prev[btn])); 3466 } 3467 } 3468 } else { 3469 // TODO: separate method for creating a navigation button 3470 prevButton = createElement("SPAN", 3471 { className: cssClass.BUTTON + cssClass.FIRST_NAV }); 3472 // XXX: for IE 6.x 3473 Dom.setStyle(prevButton, "visibility", "visible"); 3474 btn = Dom.generateId(); 3475 prevButton.innerHTML = "<button type=\"button\" " + 3476 "id=\"" + btn + "\" name=\"" + 3477 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">" + 3478 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>"; 3479 navContainer.appendChild(prevButton); 3480 btn = Dom.get(btn); 3481 carousel._navBtns.prev = [btn]; 3482 cfg = { prev: [prevButton] }; 3483 } 3484 3485 if (JS.isString(nav.next) || JS.isArray(nav.next)) { 3486 if (JS.isString(nav.next)) { 3487 nav.next = [nav.next]; 3488 } 3489 for (btn in nav.next) { 3490 if (nav.next.hasOwnProperty(btn)) { 3491 carousel._navBtns.next.push(Dom.get(nav.next[btn])); 3492 } 3493 } 3494 } else { 3495 // TODO: separate method for creating a navigation button 3496 nextButton = createElement("SPAN", 3497 { className: cssClass.BUTTON + cssClass.NEXT_NAV }); 3498 // XXX: for IE 6.x 3499 Dom.setStyle(nextButton, "visibility", "visible"); 3500 btn = Dom.generateId(); 3501 nextButton.innerHTML = "<button type=\"button\" " + 3502 "id=\"" + btn + "\" name=\"" + 3503 carousel.STRINGS.NEXT_BUTTON_TEXT + "\">" + 3504 carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>"; 3505 navContainer.appendChild(nextButton); 3506 btn = Dom.get(btn); 3507 carousel._navBtns.next = [btn]; 3508 if (cfg) { 3509 cfg.next = [nextButton]; 3510 } else { 3511 cfg = { next: [nextButton] }; 3512 } 3513 } 3514 3515 if (cfg) { 3516 carousel.set("navigation", cfg); 3517 } 3518 3519 return navContainer; 3520 }, 3521 3522 /** 3523 * Set the clip container size (based on the new numVisible value). 3524 * 3525 * @method _setClipContainerSize 3526 * @param clip {HTMLElement} The clip container element. 3527 * @param num {Number} optional The number of items per page. 3528 * @protected 3529 */ 3530 _setClipContainerSize: function (clip, num) { 3531 var carousel = this, 3532 isVertical = carousel.get("isVertical"), 3533 rows = carousel._rows, 3534 cols = carousel._cols, 3535 reveal = carousel.get("revealAmount"), 3536 itemHeight = getCarouselItemSize.call(carousel, "height"), 3537 itemWidth = getCarouselItemSize.call(carousel, "width"), 3538 containerHeight, 3539 containerWidth; 3540 3541 carousel._recomputeSize = (containerHeight === 0); // bleh! 3542 if (carousel._recomputeSize) { 3543 carousel._hasRendered = false; 3544 return; // no use going further, bail out! 3545 } 3546 3547 clip = clip || carousel._clipEl; 3548 3549 if (rows) { 3550 containerHeight = itemHeight * rows; 3551 containerWidth = itemWidth * cols; 3552 } else { 3553 num = num || carousel.get("numVisible"); 3554 if (isVertical) { 3555 containerHeight = itemHeight * num; 3556 } else { 3557 containerWidth = itemWidth * num; 3558 } 3559 } 3560 3561 reveal = getRevealSize.call(carousel); 3562 if (isVertical) { 3563 containerHeight += (reveal * 2); 3564 } else { 3565 containerWidth += (reveal * 2); 3566 } 3567 3568 if (isVertical) { 3569 containerHeight += getDimensions(carousel._carouselEl,"height"); 3570 Dom.setStyle(clip, "height", containerHeight + "px"); 3571 // For multi-row Carousel 3572 if (cols) { 3573 containerWidth += getDimensions(carousel._carouselEl, 3574 "width"); 3575 Dom.setStyle(clip, "width", containerWidth + (0) + "px"); 3576 } 3577 } else { 3578 containerWidth += getDimensions(carousel._carouselEl, "width"); 3579 Dom.setStyle(clip, "width", containerWidth + "px"); 3580 // For multi-row Carousel 3581 if (rows) { 3582 containerHeight += getDimensions(carousel._carouselEl, 3583 "height"); 3584 Dom.setStyle(clip, "height", containerHeight + "px"); 3585 } 3586 } 3587 3588 if (clip) { 3589 carousel._setContainerSize(clip); // adjust the container size 3590 } 3591 }, 3592 3593 /** 3594 * Set the container size. 3595 * 3596 * @method _setContainerSize 3597 * @param clip {HTMLElement} The clip container element. 3598 * @param attr {String} Either set the height or width. 3599 * @protected 3600 */ 3601 _setContainerSize: function (clip, attr) { 3602 var carousel = this, 3603 config = carousel.CONFIG, 3604 cssClass = carousel.CLASSES, 3605 isVertical, 3606 rows, 3607 cols, 3608 size; 3609 3610 isVertical = carousel.get("isVertical"); 3611 rows = carousel._rows; 3612 cols = carousel._cols; 3613 clip = clip || carousel._clipEl; 3614 attr = attr || (isVertical ? "height" : "width"); 3615 size = parseFloat(Dom.getStyle(clip, attr), 10); 3616 3617 size = JS.isNumber(size) ? size : 0; 3618 3619 if (isVertical) { 3620 size += getDimensions(carousel._carouselEl, "height") + 3621 getStyle(carousel._navEl, "height"); 3622 } else { 3623 size += getDimensions(carousel._carouselEl, "width"); 3624 } 3625 3626 if (!isVertical) { 3627 if (size < config.HORZ_MIN_WIDTH) { 3628 size = config.HORZ_MIN_WIDTH; 3629 carousel.addClass(cssClass.MIN_WIDTH); 3630 } 3631 } 3632 carousel.setStyle(attr, size + "px"); 3633 3634 // Additionally the width of the container should be set for 3635 // the vertical Carousel 3636 if (isVertical) { 3637 size = getCarouselItemSize.call(carousel, "width"); 3638 if(cols) { 3639 size = size * cols; 3640 } 3641 // Bug fix for vertical carousel (goes in conjunction with 3642 // .yui-carousel-element {... 3200px removed from styles), and 3643 // allows for multirows in IEs). 3644 Dom.setStyle(carousel._carouselEl, "width", size + "px"); 3645 if (size < config.VERT_MIN_WIDTH) { 3646 size = config.VERT_MIN_WIDTH; 3647 // set a min width on vertical carousel, don't see why this 3648 // shouldn't always be set... 3649 carousel.addClass(cssClass.MIN_WIDTH); 3650 } 3651 carousel.setStyle("width", size + "px"); 3652 } else { 3653 /* 3654 * Fix for automatically computing the height and width in IE. 3655 * Many thanks to ErisDS for the fix. 3656 * For more information visit, 3657 * http://erisds.co.uk/code/yui2-javascript-carousel-an-update-about-version-2-8 3658 */ 3659 size = getCarouselItemSize.call(carousel, "height"); 3660 if (rows) { 3661 size = size * rows; 3662 } 3663 Dom.setStyle(carousel._carouselEl, "height", size + "px"); 3664 } 3665 }, 3666 3667 /** 3668 * Set the value for the Carousel's first visible item. 3669 * 3670 * @method _setFirstVisible 3671 * @param val {Number} The new value for firstVisible 3672 * @return {Number} The new value that would be set 3673 * @protected 3674 */ 3675 _setFirstVisible: function (val) { 3676 var carousel = this; 3677 3678 if (val >= 0 && val < carousel.get("numItems")) { 3679 carousel.scrollTo(val); 3680 } else { 3681 val = carousel.get("firstVisible"); 3682 } 3683 return val; 3684 }, 3685 3686 /** 3687 * Set the value for the Carousel's navigation. 3688 * 3689 * @method _setNavigation 3690 * @param cfg {Object} The navigation configuration 3691 * @return {Object} The new value that would be set 3692 * @protected 3693 */ 3694 _setNavigation: function (cfg) { 3695 var carousel = this; 3696 3697 if (cfg.prev) { 3698 Event.on(cfg.prev, "click", scrollPageBackward, carousel); 3699 } 3700 if (cfg.next) { 3701 Event.on(cfg.next, "click", scrollPageForward, carousel); 3702 } 3703 }, 3704 3705 /** 3706 * Clip the container size every time numVisible is set. 3707 * 3708 * @method _setNumVisible 3709 * @param val {Number} The new value for numVisible 3710 * @return {Number} The new value that would be set 3711 * @protected 3712 */ 3713 _setNumVisible: function (val) { // TODO: _setNumVisible should just be reserved for setting numVisible. 3714 var carousel = this; 3715 3716 carousel._setClipContainerSize(carousel._clipEl, val); 3717 }, 3718 3719 /** 3720 * Set the value for the number of visible items in the Carousel. 3721 * 3722 * @method _numVisibleSetter 3723 * @param val {Number} The new value for numVisible 3724 * @return {Number} The new value that would be set 3725 * @protected 3726 */ 3727 _numVisibleSetter: function (val) { 3728 var carousel = this, 3729 numVisible = val; 3730 3731 if(JS.isArray(val)) { 3732 carousel._cols = val[0]; 3733 carousel._rows = val[1]; 3734 numVisible = val[0] * val[1]; 3735 } 3736 return numVisible; 3737 }, 3738 3739 /** 3740 * Set the value for selectedItem. 3741 * 3742 * @method _selectedItemSetter 3743 * @param val {Number} The new value for selectedItem 3744 * @return {Number} The new value that would be set 3745 * @protected 3746 */ 3747 _selectedItemSetter: function (val) { 3748 var carousel = this; 3749 return (val < carousel.get("numItems")) ? val : 0; 3750 }, 3751 3752 /** 3753 * Set the number of items in the Carousel. 3754 * Warning: Setting this to a lower number than the current removes 3755 * items from the end. 3756 * 3757 * @method _setNumItems 3758 * @param val {Number} The new value for numItems 3759 * @return {Number} The new value that would be set 3760 * @protected 3761 */ 3762 _setNumItems: function (val) { 3763 var carousel = this, 3764 num = carousel._itemsTable.numItems; 3765 3766 if (JS.isArray(carousel._itemsTable.items)) { 3767 if (carousel._itemsTable.items.length != num) { // out of sync 3768 num = carousel._itemsTable.items.length; 3769 carousel._itemsTable.numItems = num; 3770 } 3771 } 3772 3773 if (val < num) { 3774 while (num > val) { 3775 carousel.removeItem(num - 1); 3776 num--; 3777 } 3778 } 3779 3780 return val; 3781 }, 3782 3783 /** 3784 * Set the orientation of the Carousel. 3785 * 3786 * @method _setOrientation 3787 * @param val {Boolean} The new value for isVertical 3788 * @return {Boolean} The new value that would be set 3789 * @protected 3790 */ 3791 _setOrientation: function (val) { 3792 var carousel = this, 3793 cssClass = carousel.CLASSES; 3794 3795 if (val) { 3796 carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL); 3797 } else { 3798 carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL); 3799 } 3800 /* 3801 The _itemAttrCache need not be emptied since the cache is for 3802 DOM attributes that do not change; not the Carousel dimensions. 3803 */ 3804 3805 return val; 3806 }, 3807 3808 /** 3809 * Set the value for the reveal amount percentage in the Carousel. 3810 * 3811 * @method _setRevealAmount 3812 * @param val {Number} The new value for revealAmount 3813 * @return {Number} The new value that would be set 3814 * @protected 3815 */ 3816 _setRevealAmount: function (val) { 3817 var carousel = this; 3818 3819 if (val >= 0 && val <= 100) { 3820 val = parseInt(val, 10); 3821 val = JS.isNumber(val) ? val : 0; 3822 carousel._setClipContainerSize(); 3823 } else { 3824 val = carousel.get("revealAmount"); 3825 } 3826 return val; 3827 }, 3828 3829 /** 3830 * Set the value for the selected item. 3831 * 3832 * @method _setSelectedItem 3833 * @param val {Number} The new value for "selected" item 3834 * @protected 3835 */ 3836 _setSelectedItem: function (val) { 3837 this._selectedItem = val; 3838 }, 3839 3840 /** 3841 * Get the total number of pages. 3842 * 3843 * @method _getNumPages 3844 * @protected 3845 */ 3846 _getNumPages: function () { 3847 return Math.ceil( 3848 parseInt(this.get("numItems"),10) / parseInt(this.get("numVisible"),10) 3849 ); 3850 }, 3851 3852 /** 3853 * Get the last visible item. 3854 * 3855 * @method _getLastVisible 3856 * @protected 3857 */ 3858 _getLastVisible: function () { 3859 var carousel = this; 3860 return carousel.get("currentPage") + 1 == carousel.get("numPages") ? 3861 carousel.get("numItems") - 1: 3862 carousel.get("firstVisible") + carousel.get("numVisible") - 1; 3863 }, 3864 3865 /** 3866 * Synchronize and redraw the UI after an item is added. 3867 * 3868 * @method _syncUiForItemAdd 3869 * @protected 3870 */ 3871 _syncUiForItemAdd: function (obj) { 3872 var attr, 3873 carousel = this, 3874 carouselEl = carousel._carouselEl, 3875 el, 3876 item, 3877 itemsTable = carousel._itemsTable, 3878 oel, 3879 pos, 3880 sibling, 3881 styles; 3882 3883 pos = JS.isUndefined(obj.pos) ? 3884 obj.newPos || itemsTable.numItems - 1 : obj.pos; 3885 3886 if (!oel) { 3887 item = itemsTable.items[pos] || {}; 3888 el = carousel._createCarouselItem({ 3889 className : item.className, 3890 styles : item.styles, 3891 content : item.item, 3892 id : item.id, 3893 pos : pos 3894 }); 3895 if (JS.isUndefined(obj.pos)) { 3896 if (!JS.isUndefined(itemsTable.loading[pos])) { 3897 oel = itemsTable.loading[pos]; 3898 // if oel is null, it is a problem ... 3899 } 3900 if (oel) { 3901 // replace the node 3902 carouselEl.replaceChild(el, oel); 3903 // ... and remove the item from the data structure 3904 delete itemsTable.loading[pos]; 3905 } else { 3906 carouselEl.appendChild(el); 3907 } 3908 } else { 3909 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) { 3910 sibling = Dom.get(itemsTable.items[obj.pos + 1].id); 3911 } 3912 if (sibling) { 3913 carouselEl.insertBefore(el, sibling); 3914 } else { 3915 } 3916 } 3917 } else { 3918 if (JS.isUndefined(obj.pos)) { 3919 if (!Dom.isAncestor(carousel._carouselEl, oel)) { 3920 carouselEl.appendChild(oel); 3921 } 3922 } else { 3923 if (!Dom.isAncestor(carouselEl, oel)) { 3924 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) { 3925 carouselEl.insertBefore(oel, 3926 Dom.get(itemsTable.items[obj.pos + 1].id)); 3927 } 3928 } 3929 } 3930 } 3931 3932 if (!carousel._hasRendered) { 3933 carousel._refreshUi(); 3934 } 3935 3936 if (carousel.get("selectedItem") < 0) { 3937 carousel.set("selectedItem", carousel.get("firstVisible")); 3938 } 3939 3940 carousel._syncUiItems(); 3941 }, 3942 3943 /** 3944 * Synchronize and redraw the UI after an item is replaced. 3945 * 3946 * @method _syncUiForItemReplace 3947 * @protected 3948 */ 3949 _syncUiForItemReplace: function (o) { 3950 var carousel = this, 3951 carouselEl = carousel._carouselEl, 3952 itemsTable = carousel._itemsTable, 3953 pos = o.pos, 3954 item = o.newItem, 3955 oel = o.oldItem, 3956 el; 3957 3958 el = carousel._createCarouselItem({ 3959 className : item.className, 3960 styles : item.styles, 3961 content : item.item, 3962 id : oel.id 3963 }); 3964 3965 // replace the current item's attributes 3966 if ((oel = Dom.get(oel.id))) { // testing assignment 3967 oel.className = item.className; 3968 oel.styles = item.styles; 3969 oel.innerHTML = item.item; 3970 3971 itemsTable.items[pos] = el; 3972 3973 if (itemsTable.loading[pos]) { 3974 itemsTable.numItems++; 3975 delete itemsTable.loading[pos]; 3976 } 3977 } 3978 // TODO: should we add the item if oel is undefined? 3979 3980 // sync shouldn't be necessary since we're replacing items that are already positioned 3981 //carousel._syncUiItems(); 3982 }, 3983 3984 /** 3985 * Synchronize and redraw the UI after an item is removed. 3986 * 3987 * @method _syncUiForItemRemove 3988 * @protected 3989 */ 3990 _syncUiForItemRemove: function (obj) { 3991 var carousel = this, 3992 carouselEl = carousel._carouselEl, 3993 el, item, num, pos; 3994 3995 num = carousel.get("numItems"); 3996 item = obj.item; 3997 pos = obj.pos; 3998 3999 if (item && (el = Dom.get(item.id))) { 4000 if (el && Dom.isAncestor(carouselEl, el)) { 4001 Event.purgeElement(el, true); 4002 carouselEl.removeChild(el); 4003 } 4004 4005 // nothing is done w/ pos after this, should we remove it? 4006 if (carousel.get("selectedItem") == pos) { 4007 pos = pos >= num ? num - 1 : pos; 4008 } 4009 } else { 4010 } 4011 4012 carousel._syncUiItems(); 4013 }, 4014 4015 /** 4016 * Find the closest sibling to insert before 4017 * 4018 * @method _findClosestSibling 4019 * @protected 4020 */ 4021 _findClosestSibling: function (pos) { 4022 var carousel = this, 4023 itemsTable = carousel._itemsTable, 4024 len = itemsTable.items.length, 4025 j = pos, 4026 sibling; 4027 4028 // attempt to find the next closest sibling 4029 while (j<len && !sibling) { 4030 sibling = itemsTable.items[++j]; 4031 } 4032 4033 return sibling; 4034 }, 4035 4036 /** 4037 * Synchronize the items table for lazy loading. 4038 * 4039 * @method _syncUiForLazyLoading 4040 * @protected 4041 */ 4042 _syncUiForLazyLoading: function (obj) { 4043 var carousel = this, 4044 carouselEl = carousel._carouselEl, 4045 itemsTable = carousel._itemsTable, 4046 len = itemsTable.items.length, 4047 sibling = carousel._findClosestSibling(obj.last), 4048 last = obj.last, 4049 // only add DOM nodes for the currently visible items 4050 // this eliminates uneccessary performance overhead 4051 // but still allows loading styles to be applied to the items 4052 first = last - carousel.get("numVisible") + 1, 4053 el, 4054 j; 4055 4056 for (var i = first; i <= last; i++) { 4057 if(!itemsTable.loading[i] && !itemsTable.items[i]){ 4058 el = carousel._createCarouselItem({ 4059 className : carousel.CLASSES.ITEM + " " + carousel.CLASSES.ITEM_LOADING, 4060 content : carousel.STRINGS.ITEM_LOADING_CONTENT, 4061 id : Dom.generateId() 4062 }); 4063 if (el) { 4064 if (sibling) { 4065 sibling = Dom.get(sibling.id); 4066 if (sibling) { 4067 carouselEl.insertBefore(el, sibling); 4068 } else { 4069 } 4070 } else { 4071 carouselEl.appendChild(el); 4072 } 4073 } 4074 itemsTable.loading[i] = el; 4075 } 4076 } 4077 4078 carousel._syncUiItems(); 4079 }, 4080 4081 /** 4082 * Redraw the UI for item positioning. 4083 * 4084 * @method _syncUiItems 4085 * @protected 4086 */ 4087 _syncUiItems: function () { 4088 4089 if(!syncUiOnItemInsert) { 4090 return; 4091 } 4092 4093 var attr, 4094 carousel = this, 4095 numItems = carousel.get("numItems"), 4096 i, 4097 itemsTable = carousel._itemsTable, 4098 items = itemsTable.items, 4099 loading = itemsTable.loading, 4100 item, 4101 styles, 4102 updateStyles = false; 4103 4104 for (i = 0; i < numItems; i++) { 4105 item = items[i] || loading[i]; 4106 4107 if (item && item.id) { 4108 styles = getCarouselItemPosition.call(carousel, i); 4109 item.styles = item.styles || {}; 4110 4111 for (attr in styles) { 4112 if(item.styles[attr] !== styles[attr]) 4113 { 4114 updateStyles = true; 4115 item.styles[attr] = styles[attr]; 4116 } 4117 } 4118 if(updateStyles) 4119 { 4120 setStyles(Dom.get(item.id), styles); 4121 } 4122 updateStyles = false; 4123 } 4124 } 4125 }, 4126 4127 /** 4128 * Set the correct class for the navigation buttons. 4129 * 4130 * @method _updateNavButtons 4131 * @param el {Object} The target button 4132 * @param setFocus {Boolean} True to set focus ring, false otherwise. 4133 * @protected 4134 */ 4135 _updateNavButtons: function (el, setFocus) { 4136 var children, 4137 cssClass = this.CLASSES, 4138 grandParent, 4139 parent = el.parentNode; 4140 4141 if (!parent) { 4142 return; 4143 } 4144 grandParent = parent.parentNode; 4145 4146 if (el.nodeName.toUpperCase() == "BUTTON" && 4147 Dom.hasClass(parent, cssClass.BUTTON)) { 4148 if (setFocus) { 4149 if (grandParent) { 4150 children = Dom.getChildren(grandParent); 4151 if (children) { 4152 Dom.removeClass(children, cssClass.FOCUSSED_BUTTON); 4153 } 4154 } 4155 Dom.addClass(parent, cssClass.FOCUSSED_BUTTON); 4156 } else { 4157 Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON); 4158 } 4159 } 4160 }, 4161 4162 /** 4163 * Update the UI for the pager buttons based on the current page and 4164 * the number of pages. 4165 * 4166 * @method _updatePagerButtons 4167 * @protected 4168 */ 4169 _updatePagerButtons: function () { 4170 4171 if(!syncUiOnItemInsert) { 4172 return; 4173 } 4174 4175 var carousel = this, 4176 css = carousel.CLASSES, 4177 cur = carousel._pages.cur, // current page 4178 el, 4179 html, 4180 i, 4181 item, 4182 n = carousel.get("numVisible"), 4183 num = carousel._pages.num, // total pages 4184 pager = carousel._pages.el; // the pager container element 4185 4186 if (num === 0 || !pager) { 4187 return; // don't do anything if number of pages is 0 4188 } 4189 4190 // Hide the pager before redrawing it 4191 Dom.setStyle(pager, "visibility", "hidden"); 4192 4193 // Remove all nodes from the pager 4194 while (pager.firstChild) { 4195 pager.removeChild(pager.firstChild); 4196 } 4197 4198 for (i = 0; i < num; i++) { 4199 4200 el = document.createElement("LI"); 4201 4202 if (i === 0) { 4203 Dom.addClass(el, css.FIRST_PAGE); 4204 } 4205 if (i == cur) { 4206 Dom.addClass(el, css.SELECTED_NAV); 4207 } 4208 4209 html = "<a class=" + css.PAGER_ITEM + " href=\"#" + (i+1) + "\" tabindex=\"0\"><em>" + 4210 carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) + 4211 "</em></a>"; 4212 el.innerHTML = html; 4213 4214 pager.appendChild(el); 4215 } 4216 4217 // Show the pager now 4218 Dom.setStyle(pager, "visibility", "visible"); 4219 }, 4220 4221 /** 4222 * Update the UI for the pager menu based on the current page and 4223 * the number of pages. If the number of pages is greater than 4224 * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop 4225 * down menu instead of a set of buttons. 4226 * 4227 * @method _updatePagerMenu 4228 * @protected 4229 */ 4230 _updatePagerMenu: function () { 4231 var carousel = this, 4232 css = carousel.CLASSES, 4233 cur = carousel._pages.cur, // current page 4234 el, 4235 i, 4236 item, 4237 n = carousel.get("numVisible"), 4238 num = carousel._pages.num, // total pages 4239 pager = carousel._pages.el, // the pager container element 4240 sel; 4241 4242 if (num === 0 || !pager) { 4243 return;// don't do anything if number of pages is 0 4244 } 4245 4246 sel = document.createElement("SELECT"); 4247 4248 4249 if (!sel) { 4250 return; 4251 } 4252 4253 // Hide the pager before redrawing it 4254 Dom.setStyle(pager, "visibility", "hidden"); 4255 4256 // Remove all nodes from the pager 4257 while (pager.firstChild) { 4258 pager.removeChild(pager.firstChild); 4259 } 4260 4261 for (i = 0; i < num; i++) { 4262 4263 el = document.createElement("OPTION"); 4264 el.value = i+1; 4265 el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1); 4266 4267 if (i == cur) { 4268 el.setAttribute("selected", "selected"); 4269 } 4270 4271 sel.appendChild(el); 4272 } 4273 4274 el = document.createElement("FORM"); 4275 if (!el) { 4276 } else { 4277 el.appendChild(sel); 4278 pager.appendChild(el); 4279 } 4280 4281 // Show the pager now 4282 Event.addListener(sel, "change", carousel._pagerChangeHandler, this, true); 4283 Dom.setStyle(pager, "visibility", "visible"); 4284 }, 4285 4286 /** 4287 * Set the correct tab index for the Carousel items. 4288 * 4289 * @method _updateTabIndex 4290 * @param el {Object} The element to be focussed 4291 * @protected 4292 */ 4293 _updateTabIndex: function (el) { 4294 var carousel = this; 4295 4296 if (el) { 4297 if (carousel._focusableItemEl) { 4298 carousel._focusableItemEl.tabIndex = -1; 4299 } 4300 carousel._focusableItemEl = el; 4301 el.tabIndex = 0; 4302 } 4303 }, 4304 4305 /** 4306 * Validate animation parameters. 4307 * 4308 * @method _validateAnimation 4309 * @param cfg {Object} The animation configuration 4310 * @return {Boolean} The status of the validation 4311 * @protected 4312 */ 4313 _validateAnimation: function (cfg) { 4314 var rv = true; 4315 4316 if (JS.isObject(cfg)) { 4317 if (cfg.speed) { 4318 rv = rv && JS.isNumber(cfg.speed); 4319 } 4320 if (cfg.effect) { 4321 rv = rv && JS.isFunction(cfg.effect); 4322 } else if (!JS.isUndefined(YAHOO.util.Easing)) { 4323 cfg.effect = YAHOO.util.Easing.easeOut; 4324 } 4325 } else { 4326 rv = false; 4327 } 4328 4329 return rv; 4330 }, 4331 4332 /** 4333 * Validate the firstVisible value. 4334 * 4335 * @method _validateFirstVisible 4336 * @param val {Number} The first visible value 4337 * @return {Boolean} The status of the validation 4338 * @protected 4339 */ 4340 _validateFirstVisible: function (val) { 4341 var carousel = this, numItems = carousel.get("numItems"); 4342 4343 if (JS.isNumber(val)) { 4344 if (numItems === 0 && val == numItems) { 4345 return true; 4346 } else { 4347 return (val >= 0 && val < numItems); 4348 } 4349 } 4350 4351 return false; 4352 }, 4353 4354 /** 4355 * Validate and navigation parameters. 4356 * 4357 * @method _validateNavigation 4358 * @param cfg {Object} The navigation configuration 4359 * @return {Boolean} The status of the validation 4360 * @protected 4361 */ 4362 _validateNavigation : function (cfg) { 4363 var i; 4364 4365 if (!JS.isObject(cfg)) { 4366 return false; 4367 } 4368 4369 if (cfg.prev) { 4370 if (!JS.isArray(cfg.prev)) { 4371 return false; 4372 } 4373 for (i in cfg.prev) { 4374 if (cfg.prev.hasOwnProperty(i)) { 4375 if (!JS.isString(cfg.prev[i].nodeName)) { 4376 return false; 4377 } 4378 } 4379 } 4380 } 4381 4382 if (cfg.next) { 4383 if (!JS.isArray(cfg.next)) { 4384 return false; 4385 } 4386 for (i in cfg.next) { 4387 if (cfg.next.hasOwnProperty(i)) { 4388 if (!JS.isString(cfg.next[i].nodeName)) { 4389 return false; 4390 } 4391 } 4392 } 4393 } 4394 4395 return true; 4396 }, 4397 4398 /** 4399 * Validate the numItems value. 4400 * 4401 * @method _validateNumItems 4402 * @param val {Number} The numItems value 4403 * @return {Boolean} The status of the validation 4404 * @protected 4405 */ 4406 _validateNumItems: function (val) { 4407 return JS.isNumber(val) && (val >= 0); 4408 }, 4409 4410 /** 4411 * Validate the numVisible value. 4412 * 4413 * @method _validateNumVisible 4414 * @param val {Number} The numVisible value 4415 * @return {Boolean} The status of the validation 4416 * @protected 4417 */ 4418 _validateNumVisible: function (val) { 4419 var rv = false; 4420 4421 if (JS.isNumber(val)) { 4422 rv = val > 0 && val <= this.get("numItems"); 4423 } else if (JS.isArray(val)) { 4424 if (JS.isNumber(val[0]) && JS.isNumber(val[1])) { 4425 rv = val[0] * val[1] > 0 && val.length == 2; 4426 } 4427 } 4428 4429 return rv; 4430 }, 4431 4432 /** 4433 * Validate the revealAmount value. 4434 * 4435 * @method _validateRevealAmount 4436 * @param val {Number} The revealAmount value 4437 * @return {Boolean} The status of the validation 4438 * @protected 4439 */ 4440 _validateRevealAmount: function (val) { 4441 var rv = false; 4442 4443 if (JS.isNumber(val)) { 4444 rv = val >= 0 && val < 100; 4445 } 4446 4447 return rv; 4448 }, 4449 4450 /** 4451 * Validate the scrollIncrement value. 4452 * 4453 * @method _validateScrollIncrement 4454 * @param val {Number} The scrollIncrement value 4455 * @return {Boolean} The status of the validation 4456 * @protected 4457 */ 4458 _validateScrollIncrement: function (val) { 4459 var rv = false; 4460 4461 if (JS.isNumber(val)) { 4462 rv = (val > 0 && val < this.get("numItems")); 4463 } 4464 4465 return rv; 4466 } 4467 4468 }); 4469 4470 })(); 4471 /* 4472 ;; Local variables: ** 4473 ;; mode: js2 ** 4474 ;; indent-tabs-mode: nil ** 4475 ;; End: ** 4476 */ 4477 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"}); 4478 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"}); 4479 4480 }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-element", "yui2-skin-sam-carousel"], "optional": ["yui2-animation"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |