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