1 2/** 3 * Slideshow JS Bundle 4 */ 5YUI.add('fl-event-move', function(Y) { 6 7/** 8 * Adds gesturemovevertical, gesturemoveverticalend, gesturemovehorizontal 9 * and gesturemovehorizontalend events. 10 * 11 * @module fl-event-move 12 */ 13var _eventBase = { 14 15 _isEndEvent: false, 16 17 on: function(node, subscriber, ce) 18 { 19 if(this.type.indexOf('end') > -1) { 20 this._isEndEvent = true; 21 } 22 23 subscriber._direction = this.type.replace('gesturemove', '').replace('end', ''); 24 25 if(window.navigator.msPointerEnabled) { 26 subscriber._startHandle = node.on('MSPointerDown', this._onStart, this, node, subscriber, ce); 27 subscriber._moveHandle = node.on('MSPointerMove', this._onMove, this, node, subscriber, ce); 28 subscriber._endHandle = node.on('MSPointerUp', this._onEnd, this, node, subscriber, ce); 29 } 30 else { 31 subscriber._startHandle = node.on('gesturemovestart', this._onStart, null, this, node, subscriber, ce); 32 subscriber._moveHandle = node.on('gesturemove', this._onMove, null, this, node, subscriber, ce); 33 subscriber._endHandle = node.on('gesturemoveend', this._onEnd, { standAlone: true }, this, node, subscriber, ce); 34 } 35 }, 36 37 detach: function(node, subscriber, ce) 38 { 39 subscriber._startHandle.detach(); 40 subscriber._startHandle = null; 41 subscriber._moveHandle.detach(); 42 subscriber._moveHandle = null; 43 subscriber._endHandle.detach(); 44 subscriber._endHandle = null; 45 }, 46 47 _onStart: function(e, node, subscriber, ce) 48 { 49 subscriber._doMove = null; 50 subscriber._startX = e.pageX; 51 subscriber._startY = e.pageY; 52 }, 53 54 _onMove: function(e, node, subscriber, ce) 55 { 56 if(this._checkDirection(e, subscriber)) { 57 subscriber._doMove = true; 58 } 59 else { 60 subscriber._doMove = false; 61 } 62 if(subscriber._doMove && !this._isEndEvent) { 63 ce.fire(e); 64 } 65 }, 66 67 _onEnd: function(e, node, subscriber, ce) 68 { 69 if(subscriber._doMove && this._isEndEvent) { 70 e.startPageX = subscriber._startX; 71 e.startPageY = subscriber._startY; 72 ce.fire(e); 73 } 74 75 subscriber._doMove = null; 76 }, 77 78 _checkDirection: function(e, subscriber) 79 { 80 var xDelta = Math.abs(subscriber._startX - e.pageX), 81 yDelta = Math.abs(subscriber._startY - e.pageY); 82 83 if(yDelta > xDelta && subscriber._startY > e.pageY && subscriber._direction == 'vertical') { 84 return true; 85 } 86 else if(yDelta > xDelta && subscriber._startY < e.pageY && subscriber._direction == 'vertical') { 87 return true; 88 } 89 else if(yDelta < xDelta && subscriber._startX > e.pageX && subscriber._direction == 'horizontal') { 90 return true; 91 } 92 else if(yDelta < xDelta && subscriber._startX < e.pageX && subscriber._direction == 'horizontal') { 93 return true; 94 } 95 96 return false; 97 } 98}; 99 100/** 101 * @event gesturemovevertical 102 * @param type {String} "gesturemovevertical" 103 * @param fn {Function} The method the event invokes. 104 * @param ctx {Object} Context for the method the event invokes. 105 */ 106Y.Event.define('gesturemovevertical', _eventBase); 107 108/** 109 * @event gesturemoveverticalend 110 * @param type {String} "gesturemoveverticalend" 111 * @param fn {Function} The method the event invokes. 112 * @param ctx {Object} Context for the method the event invokes. 113 */ 114Y.Event.define('gesturemoveverticalend', _eventBase); 115 116/** 117 * @event gesturemovehorizontal 118 * @param type {String} "gesturemovehorizontal" 119 * @param fn {Function} The method the event invokes. 120 * @param ctx {Object} Context for the method the event invokes. 121 */ 122Y.Event.define('gesturemovehorizontal', _eventBase); 123 124/** 125 * @event gesturemovehorizontalend 126 * @param type {String} "gesturemovehorizontalend" 127 * @param fn {Function} The method the event invokes. 128 * @param ctx {Object} Context for the method the event invokes. 129 */ 130Y.Event.define('gesturemovehorizontalend', _eventBase); 131 132 133}, '2.0.0' ,{requires:['event-move']}); 134 135 136YUI.add('fl-slideshow', function(Y) { 137 138/** 139 * @module fl-slideshow 140 */ 141 142/** 143 * Caption widget used in slideshows. 144 * 145 * @namespace FL 146 * @class SlideshowCaption 147 * @constructor 148 * @param config {Object} Configuration object 149 * @extends Widget 150 */ 151Y.namespace('FL').SlideshowCaption = Y.Base.create('fl-slideshow-caption', Y.Widget, [Y.WidgetChild], { 152 153 /** 154 * Flag for whether the text has been 155 * toggled or not. 156 * 157 * @property _textToggled 158 * @type Boolean 159 * @default false 160 * @protected 161 */ 162 _textToggled: false, 163 164 /** 165 * An anchor node used for the toggle link. 166 * 167 * @property _textToggleLink 168 * @type Object 169 * @default null 170 * @protected 171 */ 172 _textToggleLink: null, 173 174 /** 175 * @method renderUI 176 * @protected 177 */ 178 renderUI: function() 179 { 180 var root = this.get('root'), 181 bb = this.get('boundingBox'), 182 fallback = this.get('root').get('fallback'); 183 184 if(fallback) { 185 this._textToggleLink = Y.Node.create('<a role="button" tabindex="0"></a>'); 186 } 187 else { 188 this._textToggleLink = Y.Node.create('<button type="button"></button>'); 189 } 190 this._textToggleLink.addClass('fl-slideshow-caption-toggle' + (!fallback ? ' fl-content-ui-button' : '')); 191 this._textToggleLink.set('innerHTML', root.get('captionMoreLinkText')); 192 193 bb.appendChild(this._textToggleLink); 194 }, 195 196 /** 197 * @method bindUI 198 * @protected 199 */ 200 bindUI: function() 201 { 202 this.get('root').on('imageLoadComplete', Y.bind(this._setText, this)); 203 this._textToggleLink.on('click', Y.bind(this._toggleText, this)); 204 }, 205 206 /** 207 * Sets the caption text and displays the 208 * toggle link if necessary. 209 * 210 * @method _setText 211 * @protected 212 */ 213 214 _setText: function() 215 { 216 var root = this.get('root'), 217 text = root.imageInfo.caption, 218 textLength = root.get('captionTextLength'), 219 cb = this.get('contentBox'); 220 221 if(!root.imageInfo.caption || root.imageInfo.caption === '') { 222 cb.set('innerHTML', ''); 223 this._textToggleLink.setStyle('display', 'none'); 224 return; 225 } 226 else if(textLength > -1) { 227 if(!this._textToggled && textLength < text.length) { 228 text = this._shortenText(text); 229 this._textToggleLink.setStyle('display', 'inline-block'); 230 } 231 else if(this._textToggled && textLength < text.length) { 232 text = this._stripTags(text); 233 this._textToggleLink.setStyle('display', 'inline-block'); 234 } 235 else { 236 text = this._stripTags(text); 237 this._textToggleLink.setStyle('display', 'none'); 238 } 239 } 240 else { 241 text = this._stripTags(text); 242 } 243 244 cb.set('innerHTML', text); 245 }, 246 247 /** 248 * Shows or hides the full text when the 249 * toggle link is clicked. 250 * 251 * @method _toggleText 252 * @protected 253 */ 254 _toggleText: function() 255 { 256 var root = this.get('root'), 257 text = root.imageInfo.caption, 258 cb = this.get('contentBox'); 259 260 if(this._textToggled) { 261 text = this._shortenText(text); 262 this._textToggleLink.set('innerHTML', root.get('captionMoreLinkText')); 263 this._textToggled = false;
264 } 265 else { 266 text = this._stripTags(text); 267 this._textToggleLink.set('innerHTML', root.get('captionLessLinkText')); 268 this._textToggled = true; 269 } 270 271 cb.set('innerHTML', text); 272 }, 273 274 /** 275 * Strips out HTML tags from the caption text. 276 * 277 * @method _stripTags 278 * @param text {String} The text to strip HTML tags from. 279 * @param ignoreSettings {Boolean} If true, will strip tags even if 280 * the stripTags attribute is set to false. 281 * @protected 282 */ 283 _stripTags: function(text, ignoreSettings) 284 { 285 var root = this.get('root'), textDiv; 286 287 if(ignoreSettings || root.get('captionStripTags')) { 288 textDiv = document.createElement('div'); 289 textDiv.innerHTML = text; 290 text = textDiv.textContent || textDiv.innerText; 291 } 292 293 return text; 294 }, 295 296 /** 297 * Shortens the caption text to the length of 298 * the textLength attribute. 299 * 300 * @method _shortenText 301 * @protected 302 */ 303 _shortenText: function(text) 304 { 305 var root = this.get('root'); 306 307 text = this._stripTags(text, true).substring(0, root.get('captionTextLength')); 308 309 return Y.Lang.trim(text.substring(0, text.lastIndexOf(' '))) + ' ...'; 310 } 311 312}, { 313 314 /** 315 * Custom CSS class name for the widget. 316 * 317 * @property CSS_PREFIX
318 * @type String 319 * @protected 320 * @static 321 */ 322 CSS_PREFIX: 'fl-slideshow-caption', 323 324 /** 325 * Static property used to define the default attribute configuration of 326 * the Widget. 327 * 328 * @property ATTRS 329 * @type Object 330 * @protected 331 * @static 332 */ 333 ATTRS: { 334 335 } 336}); 337 338/** 339 * A widget for loading and transitioning between SlideshowImage 340 * instances. Each SlideshowImage instance is a child widget of 341 * SlideshowFrame. SlideshowFrame is a child widget of the main 342 * slideshow widget. 343 * 344 * @namespace FL 345 * @class SlideshowFrame 346 * @constructor 347 * @param config {Object} Configuration object 348 * @extends Widget 349 */ 350Y.namespace('FL').SlideshowFrame = Y.Base.create('fl-slideshow-frame', Y.Widget, [Y.WidgetParent, Y.WidgetChild], { 351 352 /** 353 * The imageInfo object used to load the active image. 354 * 355 * @property info 356 * @type Object 357 * @default null 358 * @protected 359 */ 360 _imageInfo: null, 361 362 /** 363 * The active FL.SlideshowImage instance in the frame. 364 * 365 * @property _activeImage 366 * @type FL.SlideshowImage 367 * @default null 368 * @protected 369 */ 370 _activeImage: null, 371 372 /** 373 * A FL.SlideshowImage instance used to load the 374 * next image and transition it into the frame. 375 * 376 * @property _nextImage 377 * @type FL.SlideshowImage 378 * @default null 379 * @protected 380 */ 381 _nextImage: null, 382 383 /** 384 * Used to store imageInfo if a load request is 385 * made while the frame is transitioning. If not null 386 * when the transition completes, a new image will 387 * be loaded using the imageInfo. 388 * 389 * @property _loadQueue 390 * @type Object 391 * @default false 392 * @protected 393 */ 394 _loadQueue: null, 395 396 /** 397 * An instance of FL.SlideshowTransition used for 398 * the current transition in progress. 399 * 400 * @property _transition 401 * @type FL.SlideshowTransition 402 * @default null 403 * @protected 404 */ 405 _transition: null, 406 407 /** 408 * A flag for whether the frame is currently transitioning or not. 409 * 410 * @property _transitioning 411 * @type Boolean 412 * @default false 413 * @protected 414 */ 415 _transitioning: false, 416 417 /** 418 * Flag for whether to resize when the current transition 419 * completes. Set to true when a resize request is made 420 * during a transition. 421 * 422 * @property _resizeAfterTransition 423 * @type Boolean 424 * @default false 425 * @protected 426 */ 427 _resizeAfterTransition: false, 428 429 /** 430 * Provides functionality for gesture based transitions 431 * between the active and next images. 432 * 433 * @property _gestures 434 * @type FL.SlideshowGestures 435 * @default null 436 * @protected 437 */ 438 _gestures: null, 439 440 /** 441 * Creates new instances of FL.SlideshowImage used in the frame. 442 * 443 * @method initializer 444 * @protected 445 */ 446 initializer: function() 447 { 448 var imageConfig = this.get('imageConfig'); 449 450 this._activeImage = new Y.FL.SlideshowImage(imageConfig); 451 this._nextImage = new Y.FL.SlideshowImage(imageConfig); 452 }, 453 454 /** 455 * Renders the FL.SlideshowImage instances used in the frame. 456 * 457 * @method renderUI 458 * @protected 459 */ 460 renderUI: function() 461 { 462 this.add(this._activeImage); 463 this.add(this._nextImage); 464 }, 465 466 /** 467 * @method bindUI 468 * @protected 469 */ 470 bindUI: function() 471 { 472 var activeBB = this._activeImage.get('boundingBox'), 473 nextBB = this._nextImage.get('boundingBox'), 474 transition = this.get('transition'); 475 476 if(('ontouchstart' in window || window.navigator.msPointerEnabled) && this.get('touchSupport')) { 477 478 this._gestures = new Y.FL.SlideshowGestures({ 479 direction: transition == 'slideVertical' ? 'vertical' : 'horizontal', 480 activeItem: activeBB, 481 nextItem: nextBB 482 }); 483 484 this._gestures.on('moveStart', this._gesturesMoveStart, this); 485 this._gestures.on('endComplete', this._gesturesEndComplete, this); 486 } 487 }, 488 489 /** 490 * Functional styles for the UI. 491 * 492 * @method syncUI 493 * @protected 494 */ 495 syncUI: function() 496 { 497 var activeBB = this._activeImage.get('boundingBox'), 498 nextBB = this._nextImage.get('boundingBox'), 499 cb = this.get('contentBox'); 500 501 activeBB.setStyle('position', 'absolute'); 502 activeBB.setStyle('top', '0px'); 503 activeBB.setStyle('left', '-9999px'); 504 505 nextBB.setStyle('position', 'absolute'); 506 nextBB.setStyle('top', '0px'); 507 nextBB.setStyle('left', '-9999px'); 508 509 cb.setStyle('position', 'relative');
510 cb.setStyle('overflow', 'hidden'); 511 }, 512 513 /** 514 * Checks whether the imageInfo should be loaded or queued. 515 * Initializes a new transition if loading is ok. 516 * 517 * @method load 518 * @param imageInfo {Object} The image info to load. 519 */ 520 load: function(imageInfo) 521 { 522 var activeInfo = this._activeImage._imageInfo; 523 524 if(this._transitioning) { 525 this._loadQueue = imageInfo; 526 return; 527 } 528 else if(activeInfo && activeInfo.largeURL == imageInfo.largeURL) { 529 return; 530 } 531 532 this._imageInfo = imageInfo; 533 this._transitionInit(imageInfo); 534 }, 535 536 /** 537 * Preloads the next image using the provided imageInfo. 538 * 539 * @method preload 540 * @param imageInfo {Object} The imageInfo to preload. 541 * @param width {Number} The width to preload. 542 * @param height {Number} The height to preload. 543 */ 544 preload: function(imageInfo, width, height) 545 { 546 this._imageInfo = imageInfo; 547 this._nextImage.preload(imageInfo, width, height); 548 }, 549 550 /** 551 * Unloads the active and next image instances. 552 * 553 * @method unload 554 */ 555 unload: function() 556 { 557 this._imageInfo = null; 558 this._loadQueue = null; 559 this._transitioning = false; 560 this._transition = null; 561 562 this._activeImage.detachAll(); 563 this._activeImage.unload(); 564 this._activeImage.get('boundingBox').setStyle('left', '-9999px'); 565 566 this._nextImage.detachAll(); 567 this._nextImage.unload(); 568 this._nextImage.get('boundingBox').setStyle('left', '-9999px'); 569 }, 570 571 /** 572 * Resizes the bounding box and active image. 573 * 574 * @method resize 575 * @param width {Number} The width value. 576 * @param height {Number} The height value. 577 */ 578 resize: function(width, height) 579 { 580 if(!width || !height) { 581 return; 582 } 583 584 var bb = this.get('boundingBox'), 585 padding = [ 586 parseInt(bb.getComputedStyle('paddingTop'), 10), 587 parseInt(bb.getComputedStyle('paddingRight'), 10), 588 parseInt(bb.getComputedStyle('paddingBottom'), 10), 589 parseInt(bb.getComputedStyle('paddingLeft'), 10) 590 ]; 591 592 width = width - padding[1] - padding[3]; 593 height = height - padding[0] - padding[2]; 594 595 this.set('width', width); 596 this.set('height', height); 597 598 if(this._transitioning) { 599 this._resizeAfterTransition = true; 600 } 601 else { 602 this._activeImage.resize(width, height); 603 this._nextImage.resize(width, height); 604 } 605 }, 606 607 /** 608 * Gets the current transition to use. 609 * 610 * @method _getTransition 611 * @protected 612 */ 613 _getTransition: function() 614 { 615 var root = this.get('root'), 616 lastIndex = root.albumInfo.images.length - 1, 617 direction = 'next', 618 transition = root.get('transition'); 619 620 if(root.lastImageIndex === null) { 621 direction = ''; 622 } 623 else if(root.imageIndex == lastIndex && root.lastImageIndex === 0) { 624 direction = 'prev'; 625 } 626 else if(root.imageIndex === 0 && root.lastImageIndex == lastIndex) { 627 direction = 'next'; 628 } 629 else if(root.lastImageIndex > root.imageIndex) { 630 direction = 'prev'; 631 } 632 else if(root.lastImageIndex < root.imageIndex) { 633 direction = 'next'; 634 } 635 636 if(direction == 'next') { 637 transition = transition.replace('slideHorizontal', 'slideLeft'); 638 transition = transition.replace('slideVertical', 'slideUp'); 639 } 640 else if(direction == 'prev') { 641 transition = transition.replace('slideHorizontal', 'slideRight'); 642 transition = transition.replace('slideVertical', 'slideDown'); 643 } 644 645 return transition; 646 }, 647 648 /** 649 * Fires the transitionInit event and loads the next image. 650 * The transition starts when the image's loadComplete 651 * event is fired. 652 * 653 * @method _transitionInit 654 * @param imageInfo {Object} The imageInfo to load before transitioning. 655 * @protected 656 */ 657 _transitionInit: function(imageInfo) 658 { 659 this._transitioning = true; 660 661 // Disable gestures if set. 662 if(this._gestures) { 663 this._gestures.disable(); 664 } 665 666 /** 667 * Fires when the next image is loading before a new transition. 668 * 669 * @event transitionInit 670 */ 671 this.fire('transitionInit'); 672 673 if(imageInfo) { 674 this._nextImage.once('loadComplete', this._transitionStart, this); 675 this._nextImage.load(imageInfo); 676 } 677 else { 678 this._transitionStart(); 679 } 680 }, 681 682 /** 683 * Fires the transitionStart event and starts the transition 684 * using a new instance of FL.SlideshowTransition. 685 * 686 * @method _transitionStart 687 * @protected 688 */ 689 _transitionStart: function() 690 { 691 var root = this.get('root'); 692 693 /** 694 * Fires when the next image has finished loading 695 * and a new transition starts. 696 * 697 * @event transitionStart 698 */ 699 this.fire('transitionStart'); 700 701 this._transition = new Y.FL.SlideshowTransition({ 702 itemIn: this._nextImage._imageInfo ? this._nextImage.get('boundingBox') : null, 703 itemOut: this._activeImage._imageInfo ? this._activeImage.get('boundingBox') : null, 704 type: this._getTransition(), 705 duration: root.get('transitionDuration'), 706 easing: root.get('transitionEasing'), 707 kenBurnsDuration: root.get('speed')/1000, 708 kenBurnsZoom: root.get('kenBurnsZoom') 709 }); 710 711 if(this._nextImage._imageInfo) { 712 this._nextImage.get('boundingBox').setStyle('left', '0px'); 713 } 714 715 this._transition.once('complete', this._transitionComplete, this); 716 this._transition.run(); 717 }, 718 719 /** 720 * Switches the next and active image variables, unloads the 721 * last image, fires the transitionComplete event and loads 722 * or resizes if appropriate. 723 * 724 * @method _transitionComplete 725 * @protected 726 */ 727 _transitionComplete: function() 728 { 729 var root = this.get('root'); 730 731 // Swap image container references. 732 this._swapImageRefs(); 733 734 /** 735 * Fired when the current transition completes. 736 * 737 * @event transitionComplete 738 */ 739 this.fire('transitionComplete'); 740 this._transition = null; 741 this._transitioning = false;
742 743 // Enable gestures if set. 744 if(this._gestures) { 745 if(root && root.albumInfo.images.length <= 1) { 746 this._gestures.disable(); 747 } 748 else { 749 this._gestures.enable(); 750 } 751 } 752 753 // Load from the queue? 754 if(this._loadQueue) { 755 this.load(this._loadQueue); 756 this._loadQueue = null; 757 } 758 // Resize the active image? 759 else if(this._resizeAfterTransition) { 760 this._resizeAfterTransition = false; 761 this._activeImage.resize(this.get('width'), this.get('height')); 762 this._nextImage.resize(this.get('width'), this.get('height')); 763 } 764 }, 765 766 /** 767 * @method _gesturesMoveStart 768 * @param e {Object} The event object. 769 * @protected 770 */ 771 _gesturesMoveStart: function(e) 772 { 773 var index = 0, 774 root = this.get('root'); 775 776 index = e.direction == 'next' ? root.imageIndex + 1 : root.imageIndex - 1; 777 index = index < 0 ? root.albumInfo.images.length - 1 : index; 778 index = index >= root.albumInfo.images.length ? 0 : index; 779 780 root.pause(); 781 root._hideLoadingImage(); 782 root._showLoadingImageWithDelay(); 783 784 Y.FL.SlideshowImageLoader.removeGroup(this._nextImage.get('loadGroup')); 785 786 this._nextImage.once('loadComplete', root._hideLoadingImage, root); 787 this._nextImage.load(root.albumInfo.images[index]); 788 }, 789 790 /** 791 * @method _gesturesEndComplete 792 * @protected 793 */ 794 _gesturesEndComplete: function() 795 { 796 var root = this.get('root'), 797 index = 0; 798 799 if(this._nextImage._imageInfo){ 800 index = this._nextImage._imageInfo.index; 801 this._swapImageRefs(); 802 this._imageInfo = root.albumInfo.images[index]; 803 root.loadImage(index); 804 } 805 }, 806 807 /** 808 * @method _swapImageRefs 809 * @protected 810 */ 811 _swapImageRefs: function() 812 { 813 var active = this._activeImage; 814 this._activeImage = this._nextImage; 815 this._nextImage = active; 816 817 if(this._nextImage._imageInfo) { 818 this._nextImage.unload(); 819 this._nextImage.get('boundingBox').setStyle('left', '-9999px'); 820 } 821 if(this._gestures) { 822 this._gestures.set('activeItem', this._activeImage.get('boundingBox')); 823 this._gestures.set('nextItem', this._nextImage.get('boundingBox')); 824 } 825 } 826 827}, { 828 829 /** 830 * Custom CSS class name for the widget. 831 832 * @property CSS_PREFIX
833 * @type String 834 * @protected 835 * @static 836 */ 837 CSS_PREFIX: 'fl-slideshow-frame', 838 839 /** 840 * Static property used to define the default attribute configuration of 841 * the Widget. 842 * 843 * @property ATTRS 844 * @type Object 845 * @protected 846 * @static 847 */ 848 ATTRS: { 849 850 /** 851 * The configuration object used to create new instances of 852 * FL.SlideshowImage. See the API docs for {@link FL.SlideshowImage} 853 * for a complete list of configuration attributes. 854 * 855 * @attribute imageConfig 856 * @type Object 857 * @default null 858 */ 859 imageConfig: { 860 value: null 861 }, 862 863 /** 864 * Whether to use touch gestures, when available, 865 * to transition between images or not. 866 * 867 * @attribute touchSupport 868 * @type Boolean 869 * @default false 870 */ 871 touchSupport: { 872 value: false 873 } 874 } 875}); 876 877/** 878 * A plugin for fullscreen slideshow functionality. 879 * 880 * @namespace FL 881 * @class SlideshowFullscreen 882 * @constructor 883 * @param config {Object} Configuration object 884 * @extends Plugin.Base 885 */ 886Y.namespace('FL').SlideshowFullscreen = Y.Base.create('fl-slideshow-fullscreen', Y.Plugin.Base, [], { 887 888 /** 889 * Flag for whether the slideshow is in 890 * fullscreen mode. 891 * 892 * @property active 893 * @type Boolean 894 * @default false 895 */ 896 active: false, 897 898 /** 899 * A div containing the close message. 900 * 901 * @property _closeMessage 902 * @type Node 903 * @default null 904 * @protected 905 */ 906 _closeMessage: null, 907 908 /** 909 * A timer for hiding the close message. 910 * 911 * @property _closeMessageTimer 912 * @type Object 913 * @default null 914 * @protected 915 */ 916 _closeMessageTimer: null, 917 918 /** 919 * The initial styles of the host's bounding box 920 * before entering fullscreen mode. 921 * 922 * @property _initialStyles 923 * @type Object 924 * @protected 925 */ 926 _initialStyles: { 927 position: 'static', 928 top: '0px', 929 left: '0px' 930 }, 931 932 /** 933 * @method initializer 934 * @protected 935 */ 936 initializer: function() 937 { 938 var host = this.get('host'), 939 bb = host.get('boundingBox'), 940 self = this; 941 942 bb.addClass('fl-fullscreen-enabled'); 943 944 if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { 945 document.addEventListener('fullscreenchange', function(){ self._osChange(); }, false); 946 document.addEventListener('mozfullscreenchange', function(){ self._osChange(); }, false); 947 document.addEventListener('webkitfullscreenchange', function(){ self._osChange(); }, false); 948 } 949 else { 950 this._renderCloseMessage(); 951 } 952 }, 953 954 /** 955 * Exits fullscreen if it is currently active 956 * otherwise it enters fullscreen. 957 * 958 * @method toggle 959 */ 960 toggle: function() 961 { 962 if(this.active) { 963 this.exit(); 964 } 965 else { 966 this.enter(); 967 } 968 }, 969 970 /** 971 * Enters OS fullscreen mode if supported, otherwise 972 * the slideshow takes over the browser window. 973 * 974 * @method enter 975 */ 976 enter: function() 977 { 978 if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { 979 this._osEnter(); 980 } 981 else { 982 this._browserEnter(); 983 } 984 }, 985 986 /** 987 * Exits fullscreen mode. 988 * 989 * @method exit 990 */ 991 exit: function() 992 { 993 if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { 994 this._osExit(); 995 } 996 else { 997 this._browserExit(); 998 } 999 }, 1000 1001 /** 1002 * Enters OS fullscreen mode. 1003 * 1004 * @method _osEnter 1005 * @protected 1006 */ 1007 _osEnter: function() 1008 { 1009 var bbNode = this.get('host').get('boundingBox')._node; 1010 1011 if(bbNode.webkitRequestFullScreen) { 1012 bbNode.webkitRequestFullScreen(); 1013 } 1014 else if(bbNode.mozRequestFullScreen) { 1015 bbNode.mozRequestFullScreen(); 1016 } 1017 else if(bbNode.requestFullScreen) { 1018 bbNode.requestFullScreen(); 1019 } 1020 }, 1021 1022 /** 1023 * Exits OS fullscreen mode. 1024 * 1025 * @method _osExit 1026 * @protected 1027 */ 1028 _osExit: function() 1029 { 1030 if(document.exitFullscreen) { 1031 document.exitFullscreen(); 1032 } 1033 else if(document.mozCancelFullScreen) { 1034 document.mozCancelFullScreen(); 1035 } 1036 else if(document.webkitCancelFullScreen) { 1037 document.webkitCancelFullScreen(); 1038 } 1039 }, 1040 1041 /** 1042 * Called when the OS fullscreenchange event fires and enters 1043 * or exits standard fullscreen mode which positions and 1044 * resizes the slideshow. 1045 * 1046 * @method _osChange 1047 * @protected 1048 */ 1049 _osChange: function() 1050 { 1051 var host = this.get('host'); 1052 1053 // Transitions break on Safari while entering and 1054 // exiting fullscreen. This fixes them! 1055 if(host.frame && host.frame._transitioning) { 1056 host.frame._transitionComplete(); 1057 } 1058 1059 if(this.active) { 1060 this._exit(); 1061 } 1062 else { 1063 this._enter(); 1064 } 1065 }, 1066 1067 /** 1068 * Enter browser fullscreen mode. 1069 * 1070 * @method _browserEnter 1071 * @protected 1072 */ 1073 _browserEnter: function() 1074 { 1075 var bb = this.get('host').get('boundingBox'); 1076 1077 this._initialStyles = { 1078 position: bb.getStyle('position'), 1079 top: bb.getStyle('top'), 1080 left: bb.getStyle('left'), 1081 zIndex: bb.getStyle('zIndex') 1082 }; 1083 1084 bb.setStyles({ 1085 position: 'fixed', 1086 top: '0px', 1087 left: '0px', 1088 zIndex: 10000 1089 }); 1090 1091 Y.Node.one('body').on('fl-fullscreen|keydown', Y.bind(this._onKey, this)); 1092 1093 this._showCloseMessage(); 1094 this._enter(); 1095 }, 1096 1097 /** 1098 * Exit browser fullscreen mode. 1099 * 1100 * @method _browserExit 1101 * @protected 1102 */ 1103 _browserExit: function() 1104 { 1105 var bb = this.get('host').get('boundingBox'); 1106 1107 bb.setStyles({ 1108 position: this._initialStyles.position, 1109 top: this._initialStyles.top, 1110 left: this._initialStyles.left, 1111 zIndex: this._initialStyles.zIndex 1112 }); 1113 1114 Y.Node.one('body').detach('fl-fullscreen|keydown'); 1115 1116 this._hideCloseMessage(); 1117 this._exit(); 1118 }, 1119 1120 /** 1121 * Enters fullscreen mode. 1122 * 1123 * @method _enter 1124 * @protected 1125 */ 1126 _enter: function() 1127 { 1128 var host = this.get('host'), 1129 bb = host.get('boundingBox'); 1130 1131 bb.addClass('fl-fullscreen-active'); 1132 1133 this.active = true; 1134 1135 host.resize(); 1136 }, 1137 1138 /** 1139 * Exits fullscreen mode. 1140 * 1141 * @method _exit 1142 * @protected 1143 */ 1144 _exit: function() 1145 { 1146 var host = this.get('host'), 1147 bb = host.get('boundingBox'); 1148 1149 bb.removeClass('fl-fullscreen-active'); 1150 1151 this.active = false;
1152 1153 host.resize(); 1154 }, 1155 1156 /** 1157 * Keyboard input for the esc button. 1158 * 1159 * @method _onKey 1160 * @protected 1161 */ 1162 _onKey: function(e) 1163 { 1164 if(e.keyCode == 27) { 1165 this.exit(); 1166 return false; 1167 } 1168 }, 1169 1170 /** 1171 * Creates the close message if one is 1172 * not already available in the document. 1173 * 1174 * @method _initCloseMessage 1175 * @protected 1176 */ 1177 _renderCloseMessage: function() 1178 { 1179 this._closeMessage = Y.Node.create('<div class="fl-fullscreen-close-message"></div>'); 1180 this._closeMessage.set('innerHTML', '<span>Press the "esc" button to exit fullscreen mode.</span>'); 1181 this._closeMessage.setStyle('display', 'none'); 1182 this.get('host').get('boundingBox').insert(this._closeMessage); 1183 }, 1184 1185 /** 1186 * Shows the close message. 1187 * 1188 * @method _showCloseMessage 1189 * @protected 1190 */ 1191 _showCloseMessage: function() 1192 { 1193 if(this._closeMessageTimer) { 1194 this._closeMessageTimer.cancel(); 1195 this._closeMessageTimer = null; 1196 } 1197 1198 this._closeMessage.show(true); 1199 this._closeMessageTimer = Y.later(4000, this, this._hideCloseMessage); 1200 }, 1201 1202 /** 1203 * Hides the close message. 1204 * 1205 * @method _hideCloseMessage 1206 * @protected 1207 */ 1208 _hideCloseMessage: function() 1209 { 1210 if(this._closeMessageTimer) { 1211 this._closeMessageTimer.cancel(); 1212 this._closeMessageTimer = null; 1213 } 1214 1215 this._closeMessage.hide(true); 1216 } 1217 1218}, { 1219 1220 /** 1221 * Namespace for the plugin. 1222 * 1223 * @property NS
1224 * @type String 1225 * @protected 1226 * @static 1227 */ 1228 NS: 'fullscreen', 1229 1230 OS_SUPPORT: (function(){ 1231 1232 var doc = document.documentElement; 1233 1234 return doc.webkitRequestFullScreen || doc.mozRequestFullScreen || doc.requestFullScreen; 1235 })() 1236}); 1237 1238/** 1239 * Provides functionality for gesture based transitions 1240 * between two slideshow components. 1241 * 1242 * @namespace FL 1243 * @class SlideshowGestures 1244 * @constructor 1245 * @param config {Object} Configuration object 1246 * @extends Base 1247 */ 1248Y.namespace('FL').SlideshowGestures = Y.Base.create('fl-slideshow-gestures', Y.Base, [], { 1249 1250 /** 1251 * The x coordinate for where a gesture event starts. 1252 * 1253 * @property _startX 1254 * @type Number 1255 * @default null 1256 * @protected 1257 */ 1258 _startX: null, 1259 1260 /** 1261 * The y coordinate for where a gesture event starts. 1262 * 1263 * @property _startY 1264 * @type Number 1265 * @default null 1266 * @protected 1267 */ 1268 _startY: null, 1269 1270 /** 1271 * A flag for whether a gesture is moving or not. 1272 * 1273 * @property _moving 1274 * @type Boolean 1275 * @default false 1276 * @protected 1277 */ 1278 _touchMoving: false, 1279 1280 /** 1281 * Whether the gesture is moving or not. 1282 * 1283 * @property _moving 1284 * @type Boolean 1285 * @default false 1286 * @protected 1287 */ 1288 _moving: false, 1289 1290 /** 1291 * The direction the current gesture event 1292 * is moving in (either next or prev). 1293 * 1294 * @property _movingDirection
1295 * @type String 1296 * @default null 1297 * @protected 1298 */ 1299 _movingDirection: null, 1300 1301 /** 1302 * A flag for whether a gesture gesture is currently 1303 * transitioning or not. 1304 * 1305 * @property _transitioning 1306 * @type Boolean 1307 * @default false 1308 * @protected 1309 */ 1310 _transitioning: false, 1311 1312 /** 1313 * @method initializer 1314 * @protected 1315 */ 1316 initializer: function() 1317 { 1318 this.enable(); 1319 }, 1320 1321 /** 1322 * @method enable 1323 */ 1324 enable: function() 1325 { 1326 var id = this.get('id'), 1327 direction = this.get('direction'), 1328 active = this.get('activeItem'), 1329 next = this.get('nextItem'); 1330 1331 active.on(id + '|gesturemovestart', Y.bind(this._onStart, this)); 1332 next.on(id + '|gesturemovestart', Y.bind(this._onStart, this)); 1333 next.on(id + '|transitionend', Y.bind(this._onEndComplete, this) ); 1334 next.on(id + '|oTransitionEnd', Y.bind(this._onEndComplete, this) ); 1335 next.on(id + '|webkitTransitionEnd', Y.bind(this._onEndComplete, this) ); 1336 1337 if(direction == 'horizontal') { 1338 active.on(id + '|gesturemovehorizontal', Y.bind(this._onMoveHorizontal, this)); 1339 active.on(id + '|gesturemovehorizontalend', Y.bind(this._onEndHorizontal, this)); 1340 next.on(id + '|gesturemovehorizontal', Y.bind(this._onMoveHorizontal, this)); 1341 next.on(id + '|gesturemovehorizontalend', Y.bind(this._onEndHorizontal, this)); 1342 } 1343 else { 1344 active.on(id + '|gesturemovevertical', Y.bind(this._onMoveVertical, this)); 1345 active.on(id + '|gesturemoveverticalend', Y.bind(this._onEndVertical, this)); 1346 next.on(id + '|gesturemovevertical', Y.bind(this._onMoveVertical, this)); 1347 next.on(id + '|gesturemoveverticalend', Y.bind(this._onEndVertical, this)); 1348 } 1349 }, 1350 1351 /** 1352 * @method disable 1353 */ 1354 disable: function() 1355 { 1356 var id = this.get('id'), 1357 active = this.get('activeItem'), 1358 next = this.get('nextItem'); 1359 1360 active.detach(id + '|*'); 1361 next.detach(id + '|*'); 1362 }, 1363 1364 /** 1365 * @method _onStart 1366 * @param e {Object} The event object. 1367 * @protected 1368 */ 1369 _onStart: function(e) 1370 { 1371 var direction = this.get('direction'); 1372 1373 if(this._transitioning) { 1374 this._onEndComplete(); 1375 } 1376 1377 if(direction == 'horizontal') { 1378 this._startX = e.pageX; 1379 } 1380 else { 1381 this._startY = e.pageY; 1382 } 1383 1384 /** 1385 * @event start 1386 */ 1387 this.fire('start'); 1388 }, 1389 1390 /** 1391 * @method _onMoveHorizontal 1392 * @param e {Object} The event object. 1393 * @protected 1394 */ 1395 _onMoveHorizontal: function(e) 1396 { 1397 var x = this._startX - e.pageX, 1398 active = this.get('activeItem'), 1399 next = this.get('nextItem'), 1400 width = parseInt(active.getComputedStyle('width'), 10), 1401 translate = x < 0 ? Math.abs(x) : -x, 1402 direction = x < 0 ? 'prev' : 'next'; 1403 1404 e.preventDefault(); 1405 1406 if(!this._moving || this._movingDirection != direction) { 1407 1408 active.setStyle('left', 0); 1409 1410 next.setStyles({ 1411 'opacity': 1, 1412 'left': x < 0 ? -width : width 1413 }); 1414 1415 this._moving = true; 1416 this._movingDirection = direction; 1417 1418 /** 1419 * @event moveStart 1420 */ 1421 this.fire('moveStart', { direction: direction }); 1422 } 1423 1424 active.setStyle('-webkit-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); 1425 active.setStyle('-ms-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); 1426 next.setStyle('-webkit-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); 1427 next.setStyle('-ms-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); 1428 1429 /** 1430 * @event move 1431 */ 1432 this.fire('move'); 1433 }, 1434 1435 /** 1436 * @method _onMoveVertical 1437 * @param e {Object} The event object. 1438 * @protected 1439 */ 1440 _onMoveVertical: function(e) 1441 { 1442 var y = this._startY - e.pageY, 1443 active = this.get('activeItem'), 1444 next = this.get('nextItem'), 1445 height = parseInt(active.getComputedStyle('height'), 10), 1446 translate = y < 0 ? Math.abs(y) : -y, 1447 direction = y < 0 ? 'prev' : 'next'; 1448 1449 e.preventDefault(); 1450 1451 if(!this._moving || this._movingDirection != direction) { 1452 1453 active.setStyle('top', 0); 1454 1455 next.setStyles({ 1456 'opacity': 1, 1457 'left' : 'auto', 1458 'top': y < 0 ? -height : height 1459 }); 1460 1461 this._moving = true; 1462 this._movingDirection = direction; 1463 1464 /** 1465 * @event moveStart 1466 */ 1467 this.fire('moveStart', { direction: direction }); 1468 } 1469 1470 active.setStyle('-webkit-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); 1471 active.setStyle('-ms-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); 1472 next.setStyle('-webkit-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); 1473 next.setStyle('-ms-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); 1474 1475 /** 1476 * @event move 1477 */ 1478 this.fire('move'); 1479 }, 1480 1481 /** 1482 * @method _onEndHorizontal 1483 * @param e {Object} The event object. 1484 * @protected 1485 */ 1486 _onEndHorizontal: function(e) 1487 { 1488 if(!this._moving) { 1489 return; 1490 } 1491 1492 var x = this._startX - e.pageX, 1493 active = this.get('activeItem'), 1494 next = this.get('nextItem'), 1495 width = parseInt(next.getComputedStyle('width'), 10), 1496 translate = x < 0 ? width : -width; 1497 1498 active.transition({ 1499 'transform': 'translate('+ translate +'px, 0px)' 1500 }); 1501 1502 next.transition({ 1503 'transform': 'translate('+ translate +'px, 0px)' 1504 }); 1505 1506 this._transitioning = true; 1507 1508 /** 1509 * @event end 1510 */ 1511 this.fire('end'); 1512 }, 1513 1514 /** 1515 * @method _onEndVertical 1516 * @param e {Object} The event object. 1517 * @protected 1518 */ 1519 _onEndVertical: function(e) 1520 { 1521 if(!this._moving) { 1522 return; 1523 } 1524 1525 var y = this._startY - e.pageY, 1526 active = this.get('activeItem'), 1527 next = this.get('nextItem'), 1528 height = parseInt(next.getComputedStyle('height'), 10), 1529 translate = y < 0 ? height : -height; 1530 1531 active.transition({ 1532 'transform': 'translate(0px, '+ translate +'px)' 1533 }); 1534 1535 next.transition({ 1536 'transform': 'translate(0px, '+ translate +'px)' 1537 }); 1538 1539 this._transitioning = true; 1540 1541 /** 1542 * @event end 1543 */ 1544 this.fire('end'); 1545 }, 1546 1547 /** 1548 * @method _onEndComplete 1549 * @protected 1550 */ 1551 _onEndComplete: function() 1552 { 1553 var direction = this.get('direction'), 1554 active = this.get('activeItem'), 1555 next = this.get('nextItem'); 1556 1557 active.setStyles({ 1558 'opacity': 0, 1559 '-webkit-transform': '', 1560 '-webkit-transition': '', 1561 '-ms-transform': '', 1562 '-ms-transition': '' 1563 }); 1564 1565 next.setStyles({ 1566 '-webkit-transform': '', 1567 '-webkit-transition': '', 1568 '-ms-transform': '', 1569 '-ms-transition': '' 1570 }); 1571 1572 if(direction == 'horizontal') { 1573 active.setStyle('left', '-9999px'); 1574 next.setStyle('left', '0px'); 1575 } 1576 else { 1577 active.setStyle('top', '-9999px'); 1578 next.setStyle('top', '0px'); 1579 } 1580 1581 this.set('activeItem', next); 1582 this.set('nextItem', active); 1583 this._moving = false;
1584 this._movingDirection = null; 1585 this._transitioning = false; 1586 1587 /** 1588 * @event endComplete 1589 */ 1590 this.fire('endComplete'); 1591 } 1592 1593}, { 1594 1595 /** 1596 * Static property used to define the default attribute configuration of 1597 * the Widget. 1598 * 1599 * @property ATTRS 1600 * @type Object 1601 * @protected 1602 * @static 1603 */ 1604 ATTRS: { 1605 1606 /** 1607 * The gesture direction to use. Possible values are 1608 * horizontal and vertical. 1609 * 1610 * @attribute direction 1611 * @type String 1612 * @default horizontal 1613 */ 1614 direction: { 1615 value: 'horizontal' 1616 }, 1617 1618 /** 1619 * The Node that is currently visible. 1620 * 1621 * @attribute activeItem 1622 * @type Node 1623 * @default null 1624 */ 1625 activeItem: { 1626 value: null 1627 }, 1628 1629 /** 1630 * The Node that will be transitioned in. 1631 * 1632 * @attribute nextItem 1633 * @type Node 1634 * @default null 1635 */ 1636 nextItem: { 1637 value: null 1638 } 1639 } 1640}); 1641 1642/** 1643 * A load queue for slideshow images. 1644 * 1645 * @namespace FL 1646 * @class SlideshowImageLoader 1647 * @static 1648 */ 1649Y.namespace('FL').SlideshowImageLoader = { 1650 1651 /** 1652 * Whether an image is being loaded or not. 1653 * 1654 * @property _loading 1655 * @type Boolean 1656 * @default false 1657 * @protected 1658 */ 1659 _loading: false, 1660 1661 /** 1662 * An node for loading the next image. 1663 * 1664 * @property _currentImage 1665 * @type Node 1666 * @default null 1667 * @protected 1668 */ 1669 _currentImage: null, 1670 1671 /** 1672 * An object containing the group, src and callback 1673 * for the current image that is being loaded. 1674 * 1675 * @property _currentImageData 1676 * @type Object 1677 * @default null 1678 * @protected 1679 */ 1680 _currentImageData: null, 1681 1682 /** 1683 * An array of image data objects that contain the group, 1684 * src and callback for each image that will be loaded. 1685 * 1686 * @property _queue 1687 * @type Array 1688 * @default [] 1689 * @protected 1690 */ 1691 _queue: [], 1692 1693 /** 1694 * Adds an image to the queue. 1695 * 1696 * @method add 1697 * @param group {String} The group this image is associated with. 1698 * Used to remove images in bulk. 1699 * @param src {String} The image url to load. 1700 * @param callback {Function} A function to call when the image 1701 * has finished loading. 1702 * @param bump {Boolean} If true, the image will be added to 1703 * the first position in the queue. 1704 */ 1705 add: function(group, src, callback, bump) 1706 { 1707 var imgData = { 1708 group : group, 1709 src : src, 1710 callback : callback 1711 }; 1712 1713 if(bump) { 1714 this._queue.unshift(imgData); 1715 } 1716 else { 1717 this._queue.push(imgData); 1718 } 1719 1720 if(!this._loading) { 1721 this._load(); 1722 } 1723 }, 1724 1725 /** 1726 * Removes a group of images from the queue. 1727 * 1728 * @method removeGroup 1729 * @param group {String} The group to remove. 1730 */ 1731 removeGroup: function(group) 1732 { 1733 var i = this._queue.length - 1; 1734 1735 for( ; i > -1 ; i--) { 1736 if(this._queue[i].group == group) { 1737 this._queue.splice(i, 1); 1738 } 1739 } 1740 1741 if(this._currentImageData && this._currentImageData.group == group) { 1742 this._currentImage.detachAll(); 1743 this._currentImage = null; 1744 this._currentImageData = null; 1745 1746 if(this._queue.length > 0) { 1747 this._load(); 1748 } 1749 else { 1750 this._loading = false; 1751 } 1752 } 1753 }, 1754 1755 /** 1756 * Loads the next image in the queue. 1757 * 1758 * @method _load 1759 * @protected 1760 */ 1761 _load: function() 1762 { 1763 this._loading = true; 1764 this._currentImageData = this._queue.shift(); 1765 this._currentImage = Y.Node.create('<img />'); 1766 this._currentImage.on('error', Y.bind(this._loadComplete, this)); 1767 this._currentImage.on('load', Y.bind(this._loadComplete, this)); 1768 this._currentImage.set('src', this._currentImageData.src); 1769 }, 1770 1771 /** 1772 * Calls the current image's callback function if set 1773 * and loads the next image if the queue is not empty. 1774 * 1775 * @method _loadComplete 1776 * @protected 1777 */ 1778 _loadComplete: function() 1779 { 1780 if(this._currentImageData.callback) { 1781 this._currentImageData.callback(this._currentImage); 1782 } 1783 1784 if(this._queue.length > 0) { 1785 this._load(); 1786 } 1787 else { 1788 this._loading = false; 1789 this._currentImage = null; 1790 this._currentImageData = null; 1791 } 1792 } 1793}; 1794 1795/** 1796 * Loads an image or video using the provided imageInfo object. 1797 * 1798 * @namespace FL 1799 * @class SlideshowImage 1800 * @constructor 1801 * @param config {Object} Configuration object 1802 * @extends Widget 1803 */ 1804Y.namespace('FL').SlideshowImage = Y.Base.create('fl-slideshow-image', Y.Widget, [Y.WidgetChild], { 1805 1806 /** 1807 * The imageInfo object used to load the image and 1808 * its various sizes. 1809 * 1810 * @property info 1811 * @type Object 1812 * @default null 1813 * @protected 1814 */ 1815 _imageInfo: null, 1816 1817 /** 1818 * A reference to the current image node in the bounding box. 1819 * 1820 * @property _image 1821 * @type Node 1822 * @default null 1823 * @protected 1824 */ 1825 _image: null, 1826 1827 /** 1828 * Whether or not new imageInfo is loading. 1829 * 1830 * @property _loading 1831 * @type Boolean 1832 * @default false 1833 * @protected 1834 */ 1835 _loading: false, 1836 1837 /** 1838 * The URL that is currently being loaded. 1839 * 1840 * @property _loadingURL 1841 * @type Boolean 1842 * @default null 1843 * @protected 1844 */ 1845 _loadingURL: null, 1846 1847 /** 1848 * An anchor node used for the video play button. 1849 * 1850 * @property _videoButton 1851 * @type Node 1852 * @default null 1853 * @protected 1854 */ 1855 _videoButton: null, 1856 1857 /** 1858 * A div node used to hold the video iframe. 1859 * 1860 * @property _videoBox 1861 * @type Node 1862 * @default null 1863 * @protected 1864 */ 1865 _videoBox: null, 1866 1867 /** 1868 * An iframe node used to render the video. 1869 * 1870 * @property _video 1871 * @type Node 1872 * @default null 1873 * @protected 1874 */ 1875 _video: null, 1876 1877 /** 1878 * The default content template for the image 1879 * inherited from Y.Widget. Set to null since 1880 * only the bounding box is needed. 1881 * 1882 * @property CONTENT_TEMPLATE
1883 * @type String 1884 * @default null 1885 * @protected 1886 */ 1887 CONTENT_TEMPLATE: null, 1888 1889 /** 1890 * Initial styling for the bounding box. 1891 * 1892 * @method syncUI 1893 * @protected 1894 */ 1895 syncUI: function() 1896 { 1897 var bb = this.get('boundingBox'); 1898 1899 if(this.get('crop')) { 1900 bb.setStyle('overflow', 'hidden'); 1901 bb.addClass('fl-slideshow-image-cropped'); 1902 } 1903 }, 1904 1905 /** 1906 * Sets the imageInfo object and 1907 * loads the appropriate image size. 1908 * 1909 * @method load 1910 * @param imageInfo {Object} The imageInfo object. 1911 */ 1912 load: function(imageInfo) 1913 { 1914 this._imageInfo = imageInfo; 1915 this._loading = true; 1916 this._load(); 1917 }, 1918 1919 /** 1920 * Sets the width and height of the bounding box and 1921 * preloads an image using the provided imageInfo object. 1922 * 1923 * @method preload 1924 * @param imageInfo {Object} The imageInfo to preload. 1925 * @param width {Number} The width to preload. 1926 * @param height {Number} The height to preload. 1927 */ 1928 preload: function(imageInfo, width, height) 1929 { 1930 var isVideo = this._isVideo(), 1931 loadVideos = this.get('loadVideos'), 1932 showVideoButton = this.get('showVideoButton'); 1933 1934 this.unload(); 1935 this.set('width', width); 1936 this.set('height', height); 1937 this._imageInfo = imageInfo; 1938 1939 if(!isVideo || !loadVideos || (isVideo && loadVideos && showVideoButton)) { 1940 Y.FL.SlideshowImageLoader.add( 1941 this.get('loadGroup'), 1942 this._getImageURL(), 1943 Y.bind(this._imagePreloaded, this), 1944 this.get('loadPriority') 1945 ); 1946 } 1947 }, 1948 1949 /** 1950 * Called when preloading completes. 1951 * 1952 * @method _imagePreloaded 1953 * @param img {Object} The image that was preloaded. 1954 * @protected 1955 */ 1956 _imagePreloaded: function(img) 1957 { 1958 this._image = img; 1959 }, 1960 1961 /** 1962 * Unloads the image if there is one loaded 1963 * and sets the imageInfo object to null. 1964 * 1965 * @method unload 1966 */ 1967 unload: function() 1968 { 1969 if(this._image) { 1970 this._image.remove(); 1971 this._image.detachAll(); 1972 this._image.set('src', ''); 1973 this._image = null; 1974 } 1975 if(this._video) { 1976 this._video.remove(); 1977 this._video = null; 1978 } 1979 if(this._videoButton) { 1980 this._videoButton.remove(); 1981 this._videoButton = null; 1982 } 1983 if(this._videoBox) { 1984 this._removeVideoBox(); 1985 } 1986 1987 this._imageInfo = null; 1988 this._loading = false; 1989 this._loadingURL = null; 1990 }, 1991 1992 /** 1993 * Resizes the bounding box and loads the 1994 * appropriate image size if necessary. 1995 * 1996 * @method resize 1997 * @param width {Number} The width value. 1998 * @param height {Number} The height value. 1999 */ 2000 resize: function(width, height) 2001 { 2002 var borderWidth = parseInt(this.get('boundingBox').getComputedStyle('borderTopWidth'), 10) * 2, 2003 bb = this.get('boundingBox'); 2004 2005 this.set('width', width - borderWidth); 2006 this.set('height', height - borderWidth); 2007 bb.setStyle('width', width - borderWidth + 'px'); 2008 bb.setStyle('height', height - borderWidth + 'px'); 2009 2010 if(this._videoButton) { 2011 this._positionVideoButton(); 2012 } 2013 if(this._videoBox) { 2014 this._loadVideo(); 2015 } 2016 if(!this._loading) { 2017 if(this._imageInfo) { 2018 this._load(); 2019 } 2020 if(this._image) { 2021 this._positionImage(); 2022 } 2023 } 2024 }, 2025 2026 /** 2027 * Loads (or reloads) the image or video. 2028 * 2029 * @method _load 2030 * @protected 2031 */ 2032 _load: function() 2033 { 2034 var loadVideos = this.get('loadVideos'), 2035 showVideoButton = this.get('showVideoButton'); 2036 2037 if(this._isVideo() && loadVideos && !showVideoButton && !('ontouchstart' in window)) { 2038 this._loadVideo(); 2039 } 2040 else { 2041 this._loadImage(); 2042 } 2043 }, 2044 2045 /** 2046 * Loads the appropriate image size if 2047 * it is not already loading. 2048 * 2049 * @method _loadImage 2050 * @protected 2051 */ 2052 _loadImage: function() 2053 { 2054 var url = this._getImageURL(), 2055 loadVideos = this.get('loadVideos'); 2056 2057 // Already loading. 2058 if(url == this._loadingURL) { 2059 return; 2060 } 2061 2062 // New URL to load. 2063 this._loadingURL = url; 2064 2065 // Load the new image. 2066 Y.FL.SlideshowImageLoader.add( 2067 this.get('loadGroup'), 2068 this._loadingURL, 2069 Y.bind(this._loadImageComplete, this), 2070 this.get('loadPriority') 2071 ); 2072 2073 // Initial load? 2074 if(this._loading) { 2075 2076 if(this._isVideo() && loadVideos) { 2077 this._insertVideoButton(); 2078 } 2079 2080 /** 2081 * Only fires when a new image is being 2082 * loaded, not a different size. 2083 * 2084 * @event loadStart 2085 */ 2086 this.fire('loadStart'); 2087 } 2088 }, 2089 2090 /** 2091 * Fires when the image has finished loading. 2092 * 2093 * @method _loadImageComplete 2094 * @protected 2095 */ 2096 _loadImageComplete: function(img) 2097 { 2098 var bb = this.get('boundingBox'), 2099 showVideoButton = this.get('showVideoButton'),
2100 showAria = this.get('root').get('bgslideshow') 2101 2102 this._image = img; 2103 this._image.setStyle('visibility', 'hidden'); 2104 this._image.addClass('fl-slideshow-image-img'); 2105 2106 if( showAria ) { 2107 this._image.set( 'aria-hidden', 'true') 2108 this._image.set( 'alt', "") 2109 } 2110 else { 2111 this._image.set( 'alt', this._imageInfo.alt ) 2112 } 2113 2114 // Remove load events. 2115 this._image.detachAll(); 2116 2117 // Remove previous videos. 2118 if(this._video && !showVideoButton) { 2119 this._video.remove(); 2120 this._video = null; 2121 } 2122 2123 // Remove the old image. 2124 bb.all('img').remove(); 2125 2126 // Append the new image. 2127 bb.append(this._image); 2128 2129 // Setup, scale and position the new image. 2130 this._setupImage(); 2131 this._resizeImage(); 2132 this._positionImage(); 2133 this._image.setStyle('visibility', 'visible'); 2134 2135 // Clear the loading url. 2136 this._loadingURL = null; 2137 2138 // Finish an initial load? 2139 if(this._loading) { 2140 2141 this._loading = false; 2142 2143 /** 2144 * Only fires when a new image is being 2145 * loaded, not a different size. 2146 * 2147 * @event loadComplete 2148 */ 2149 this.fire('loadComplete'); 2150 } 2151 }, 2152 2153 /** 2154 * UI setup for the new image. 2155 * 2156 * @method _setupImage 2157 * @protected 2158 */ 2159 _setupImage: function() 2160 { 2161 var bb = this.get('boundingBox'); 2162 2163 // IE interpolation 2164 if(typeof this._image._node.style.msInterpolationMode != 'undefined') { 2165 this._image._node.style.msInterpolationMode = 'bicubic'; 2166 } 2167 2168 // Protection 2169 if(this.get('protect')) { 2170 bb.delegate('contextmenu', this._protectImage, 'img'); 2171 bb.delegate('mousedown', this._protectImage, 'img'); 2172 } 2173 }, 2174 2175 /** 2176 * Fires on contextmenu or mousedown in attempt 2177 * to keep the image from being copied. 2178 * 2179 * @method _protectImage 2180 * @return {Boolean} Returns false to prevent the default event. 2181 * @protected 2182 */ 2183 _protectImage: function(e) 2184 { 2185 e.preventDefault(); 2186 return false; 2187 }, 2188 2189 /** 2190 * Resizes the image node. 2191 * 2192 * @method _resizeImage 2193 * @protected 2194 */ 2195 _resizeImage: function() 2196 { 2197 var borderWidth = parseInt(this._image.getComputedStyle('borderTopWidth'), 10) * 2, 2198 imageWidth = this._image.get('width'), 2199 imageHeight = this._image.get('height'), 2200 targetWidth = parseInt(this.get('boundingBox').getComputedStyle('width'), 10), 2201 targetHeight = parseInt(this.get('boundingBox').getComputedStyle('height'), 10), 2202 newWidth = 0, 2203 newHeight = 0, 2204 xScale = 0, 2205 yScale = 0, 2206 cropHorizontalsOnly = this.get('cropHorizontalsOnly'), 2207 isHorizontal = imageHeight > imageWidth, 2208 noCrop = false; 2209 2210 if(this._imageInfo && this.get('checkFilenamesForNoCrop')) { 2211 noCrop = this._imageInfo.filename.indexOf('nocrop') > -1; 2212 } 2213 2214 if(this.get('crop') && !(cropHorizontalsOnly && isHorizontal) && !noCrop) { 2215 newWidth = targetWidth; 2216 newHeight = Math.round(imageHeight * targetWidth/imageWidth); 2217 2218 if(newHeight < targetHeight) { 2219 newHeight = targetHeight; 2220 newWidth = Math.round(imageWidth * targetHeight/imageHeight); 2221 } 2222 } 2223 else { 2224 xScale = imageWidth/targetWidth; 2225 yScale = imageHeight/targetHeight; 2226 2227 if (yScale > xScale){ 2228 newWidth = Math.round(imageWidth * (1/yScale)); 2229 newHeight = Math.round(imageHeight * (1/yScale)); 2230 } 2231 else { 2232 newWidth = Math.round(imageWidth * (1/xScale)); 2233 newHeight = Math.round(imageHeight * (1/xScale)); 2234 } 2235 } 2236 2237 // Don't resize past the original size? 2238 if(!this.get('crop') && !this.get('upsize') && (newWidth > imageWidth || newHeight > imageHeight)) { 2239 newWidth = imageWidth; 2240 newHeight = imageHeight; 2241 } 2242 2243 // Compensate for borders. 2244 newWidth -= borderWidth; 2245 newHeight -= borderWidth; 2246 2247 // Resize the image. 2248 this._image.setStyle('width', newWidth + 'px'); 2249 this._image.setStyle('height', newHeight + 'px'); 2250 2251 // Constrain bounding box to image size. 2252 if(!this.get('crop') && this.get('constrainWidth')) { 2253 this.set('width', newWidth + 'px'); 2254 } 2255 if(!this.get('crop') && this.get('constrainHeight')) { 2256 this.set('height', newHeight + 'px'); 2257 } 2258 }, 2259 2260 /** 2261 * Positions the image within the bounding box. 2262 * 2263 * @method _positionImage 2264 * @protected 2265 */ 2266 _positionImage: function() 2267 { 2268 var pos = this.get('position').split(' '), 2269 x = pos[0] === '' ? 'center' : pos[0], 2270 y = pos[1] === '' ? 'center' : pos[1], 2271 newX = 0, 2272 newY = 0, 2273 bbWidth = parseInt(this.get('boundingBox').getComputedStyle('width'), 10), 2274 bbHeight = parseInt(this.get('boundingBox').getComputedStyle('height'), 10), 2275 borderWidth = parseInt(this._image.getComputedStyle('borderTopWidth'), 10) * 2, 2276 imageWidth = parseInt(this._image.getComputedStyle('width'), 10) + borderWidth, 2277 imageHeight = parseInt(this._image.getComputedStyle('height'), 10) + borderWidth; 2278 2279 if(isNaN(imageWidth) && isNaN(imageHeight)) { 2280 return; 2281 } 2282 if(x == 'left') { 2283 newX = 0; 2284 } 2285 if(x == 'center') { 2286 newX = (bbWidth - imageWidth)/2; 2287 } 2288 if(x == 'right') { 2289 newX = bbWidth - imageWidth; 2290 } 2291 2292 if(y == 'top') { 2293 newY = 0; 2294 } 2295 if(y == 'center') { 2296 newY = (bbHeight - imageHeight)/2; 2297 } 2298 if(y == 'bottom') { 2299 newY = bbHeight - imageHeight; 2300 } 2301 2302 this._image.setStyles({ 2303 'left': newX, 2304 'top': newY 2305 }); 2306 }, 2307 2308 /** 2309 * Gets the appropriate image url based 2310 * on the size of the bounding box. 2311 * 2312 * @method _getImageURL 2313 * @return {String} The url to load. 2314 * @protected 2315 */ 2316 _getImageURL: function() 2317 { 2318 var imageWidth = 0, 2319 imageHeight = 0, 2320 size = 0, 2321 targetWidth = this.get('width'), 2322 targetHeight = this.get('height'), 2323 useThumbSizes = this.get('useThumbSizes'), 2324 i = this._imageInfo, 2325 sizes = [ 2326 i.tinyURL || i.thumbURL || i.largeURL, 2327 i.thumbURL || i.largeURL, 2328 i.smallURL || i.largeURL, 2329 i.mediumURL || i.largeURL || i.smallURL, 2330 i.largeURL || i.mediumURL || i.smallURL, 2331 i.xlargeURL || i.largeURL || i.mediumURL || i.smallURL, 2332 i.x2largeURL || i.largeURL || i.mediumURL || i.smallURL, 2333 i.x3largeURL || i.x2largeURL || i.largeURL || i.mediumURL || i.smallURL 2334 ]; 2335 2336 // Width 2337 if(useThumbSizes && targetWidth <= 100) { 2338 imageWidth = 0; 2339 } 2340 else if(useThumbSizes && targetWidth <= 150) { 2341 imageWidth = 1; 2342 } 2343 else if(targetWidth <= 400) { 2344 imageWidth = 2; 2345 } 2346 else if(targetWidth >= 400 && targetWidth <= 600) { 2347 imageWidth = 3; 2348 } 2349 else if(targetWidth >= 600 && targetWidth <= 800) { 2350 imageWidth = 4; 2351 } 2352 else if(targetWidth >= 800 && targetWidth <= 1024) { 2353 imageWidth = 5; 2354 } 2355 else if(targetWidth >= 1024 && targetWidth <= 1280) { 2356 imageWidth = 6; 2357 } 2358 else { 2359 imageWidth = 7; 2360 } 2361 2362 // Height 2363 if(useThumbSizes && targetHeight <= 100) { 2364 imageHeight = 0; 2365 } 2366 else if(useThumbSizes && targetHeight <= 150) { 2367 imageHeight = 1; 2368 } 2369 else if(targetHeight <= 300) { 2370 imageHeight = 2; 2371 } 2372 else if(targetHeight >= 300 && targetHeight <= 450) { 2373 imageHeight = 3; 2374 } 2375 else if(targetHeight >= 450 && targetHeight <= 600) { 2376 imageHeight = 4; 2377 } 2378 else if(targetHeight >= 600 && targetHeight <= 768) { 2379 imageHeight = 5; 2380 } 2381 else if(targetHeight >= 768 && targetHeight <= 960) { 2382 imageHeight = 6; 2383 } 2384 else { 2385 imageHeight = 7; 2386 } 2387 2388 // Get the size number. 2389 size = Math.max(imageWidth, imageHeight); 2390 2391 return sizes[size]; 2392 }, 2393 2394 /** 2395 * Checks whether this is a video or not. 2396 * 2397 * @method _isVideo 2398 * @protected 2399 */ 2400 _isVideo: function() 2401 { 2402 if(!this._imageInfo) { 2403 return false;
2404 } 2405 else if(this._imageInfo.format == 'mp4' && this._imageInfo.sourceType == 'smugmug') { 2406 return true; 2407 } 2408 else if(this._imageInfo.iframe !== '') { 2409 return true; 2410 } 2411 2412 return false; 2413 }, 2414 2415 /** 2416 * @method _loadVideo 2417 * @protected 2418 */ 2419 _loadVideo: function() 2420 { 2421 var bb = this.get('boundingBox'), 2422 showVideoButton = this.get('showVideoButton'), 2423 autoPlay = showVideoButton ? true : false; 2424 2425 // Remove previous videos 2426 if(this._video) { 2427 this._video.remove(); 2428 this._video = null; 2429 } 2430 2431 // Get the video code 2432 if(this._imageInfo.format == 'mp4' && this._imageInfo.sourceType == 'smugmug') { 2433 this._video = this._getSmugMugVideoEmbed(this._imageInfo, autoPlay); 2434 } 2435 else if(this._imageInfo.iframe !== '') { 2436 this._video = this._getIframeVideoEmbed(this._imageInfo, autoPlay); 2437 } 2438 2439 // Insert the video 2440 if(this._videoBox) { 2441 this._videoBox.one('.fl-slideshow-video-wrap').insert(this._video); 2442 } 2443 else { 2444 bb.all('img').remove(); 2445 bb.append(this._video); 2446 } 2447 2448 // Finish an initial load? 2449 if(this._loading) { 2450 this._loading = false; 2451 this.fire('loadComplete'); 2452 } 2453 }, 2454 2455 /** 2456 * @method _insertVideoButton 2457 * @protected 2458 */ 2459 _insertVideoButton: function() 2460 { 2461 var bb = this.get('boundingBox'), 2462 event = 'ontouchstart' in window ? 'touchstart' : 'click', 2463 fallback = this.get('root').get('fallback'); 2464 2465 if(fallback) { 2466 this._videoButton = Y.Node.create('<a class="fl-slideshow-video-button" role="button" aria-label="insert video" tabindex="0"></a>'); 2467 } 2468 else { 2469 this._videoButton = Y.Node.create('<button class="fl-slideshow-video-button fl-content-ui-button" type="button" aria-label="insert video"></button>'); 2470 } 2471 this._videoButton.on(event, Y.bind(this._showVideoBox, this)); 2472 bb.insert(this._videoButton); 2473 this._positionVideoButton(); 2474 }, 2475 2476 /** 2477 * @method _positionVideoButton 2478 * @protected 2479 */ 2480 _positionVideoButton: function() 2481 { 2482 var bbWidth = this.get('width'), 2483 bbHeight = this.get('height'), 2484 buttonWidth = parseInt(this._videoButton.getStyle('width'), 10), 2485 buttonHeight = parseInt(this._videoButton.getStyle('height'), 10); 2486 2487 this._videoButton.setStyles({ 2488 left: (bbWidth - buttonWidth)/2, 2489 top: (bbHeight - buttonHeight)/2 2490 }); 2491 }, 2492 2493 /** 2494 * @method _showVideoBox 2495 * @protected 2496 */ 2497 _showVideoBox: function() 2498 { 2499 var root = this.get('root'), 2500 fallback = root.get('fallback'), 2501 wrap = Y.Node.create('<div class="fl-slideshow-video-wrap"></div>'), 2502 event = 'ontouchstart' in window ? 'touchstart' : 'click', 2503 tag = fallback ? 'a' : 'button', 2504 attrs = fallback ? 'role="button"' : 'type="button"', 2505 classes = fallback ? 'class="fl-slideshow-video-close"' : 'class="fl-slideshow-video-close fl-content-ui-button"', 2506 close = Y.Node.create('<' + [tag, attrs, classes].join(' ') + '></' + tag + '>'); 2507 2508 this._videoBox = Y.Node.create('<div class="fl-slideshow-video"></div>'); 2509 this._videoBox.setStyle('padding', root.get('boundingBox').getStyle('padding')); 2510 this._videoBox.insert(wrap); 2511 this._videoBox.insert(close); 2512 this._videoBox.on(event, Y.bind(this._removeVideoBox, this)); 2513 close.on(event, Y.bind(this._removeVideoBox, this)); 2514 2515 if(typeof YUI.Env.mods['sm-fonticon'] !== 'undefined') { 2516 close.addClass('sm-fonticon sm-fonticon-XCrossEncircled sm-button-skin-default sm-button-nochrome'); 2517 } 2518 2519 Y.one('body').insert(this._videoBox); 2520 this._loadVideo(); 2521 2522 Y.one('body').on('fl-slideshow-image|keydown', this._onKey, this); 2523 }, 2524 2525 /** 2526 * Get the embed code for a SmugMug video. 2527 * 2528 * @method _getSmugMugVideoEmbed 2529 * @param imageInfo {Object} The image info for the embed. 2530 * @param autoPlay {Boolean} Whether to auto play videos or not. 2531 * @protected 2532 */ 2533 _getSmugMugVideoEmbed: function(imageInfo, autoPlay) 2534 { 2535 var test = document.createElement('video'), 2536 width = 0, 2537 mp4 = '', 2538 vars = '', 2539 code = ''; 2540 2541 if(Y.UA.mobile !== null && !!test.canPlayType && test.canPlayType('video/mp4')) { 2542 width = this.get('width'); 2543 mp4 = 'https://www.smugmug.com/photos/' + imageInfo.id + '_' + imageInfo.key + '-' + width + '.mp4'; 2544 code += '<video width="100%" height="100%" poster="'+ this._getImageURL() +'" controls preload="none"'; 2545 2546 if(autoPlay) { 2547 code += ' autoplay'; 2548 } 2549 2550 code += '>'; 2551 code += '<source src="'+ mp4 +'" type="video/mp4" />'; 2552 code += '</video>'; 2553 } 2554 else { 2555 vars = 'imageId=' + imageInfo.id; 2556 vars += '&imageKey=' + imageInfo.key; 2557 vars += '&albumId=' + imageInfo.albumId; 2558 vars += '&albumKey=' + imageInfo.albumKey; 2559 vars += '&apiURL=https://api.smugmug.com/&hostLevel=live&isPro=true'; 2560 2561 if(autoPlay) { 2562 vars += '&autoPlay=true'; 2563 } 2564 else { 2565 vars += '&autoPlay=false'; 2566 } 2567 2568 code += '<object type="application/x-shockwave-flash" width="100%" height="100%" data="https://cdn.smugmug.com/img/ria/SmugPlayer/2012102601.swf">'; 2569 code += '<param name="movie" value="https://cdn.smugmug.com/img/ria/SmugPlayer/2012102601.swf">'; 2570 code += '<param name="allowFullScreen" value="true">'; 2571 code += '<param name="wmode" value="transparent">'; 2572 code += '<param name="flashVars" value="' + vars + '">'; 2573 code += '<embed src="https://cdn.smugmug.com/img/ria/SmugPlayer/2012102601.swf" flashvars="'+ vars +'" width="100%" height="100%" type="application/x-shockwave-flash" allowfullscreen="true" wmode="transparent">'; 2574 code += '</object>'; 2575 } 2576 2577 return Y.Node.create(code); 2578 }, 2579 2580 /** 2581 * Get the iframe video embed code. 2582 * 2583 * @method _getIframeVideoEmbed
2584 * @param imageInfo {Object} The image info for the embed. 2585 * @param autoPlay {Boolean} Whether to auto play videos or not. 2586 * @protected 2587 */ 2588 _getIframeVideoEmbed: function(imageInfo, autoPlay) 2589 { 2590 var code = '<iframe width="100%" height="100%" allowfullscreen ', 2591 url = imageInfo.iframe; 2592 2593 if(autoPlay) { 2594 url += url.indexOf('?') > -1 ? '&autoplay=1' : '?autoplay=1'; 2595 } 2596 2597 code += 'src="'+ url +'"></iframe>'; 2598 2599 return Y.Node.create(code); 2600 }, 2601 2602 /** 2603 * @method _removeVideoBox 2604 * @protected 2605 */ 2606 _removeVideoBox: function(e) 2607 { 2608 if(typeof e !== 'undefined' && e.target) { 2609 if(e.target.get('className').indexOf('fl-slideshow-video') < 0) { 2610 return; 2611 } 2612 } 2613 2614 if(this._videoBox !== null) { 2615 this._videoBox.remove(); 2616 this._videoBox = null; 2617 this._video = null; 2618 } 2619 2620 Y.one('body').detach('fl-slideshow-image|keydown', this._onKey); 2621 }, 2622 2623 /** 2624 * Keyboard input for the esc button. 2625 * 2626 * @method _onKey 2627 * @protected 2628 */ 2629 _onKey: function(e) 2630 { 2631 if(e.keyCode == 27) { 2632 this._removeVideoBox(); 2633 return false; 2634 } 2635 } 2636 2637}, { 2638 2639 /** 2640 * Custom CSS class name for the widget. 2641 2642 * @property CSS_PREFIX
2643 * @type String 2644 * @protected 2645 * @static 2646 */ 2647 CSS_PREFIX: 'fl-slideshow-image', 2648 2649 /** 2650 * Static property used to define the default attribute configuration of 2651 * the Widget. 2652 * 2653 * @property ATTRS 2654 * @type Object 2655 * @protected 2656 * @static 2657 */ 2658 ATTRS: { 2659 2660 /** 2661 * @attribute loadGroup 2662 * @type String 2663 * @default none 2664 */ 2665 loadGroup: { 2666 value: 'none' 2667 }, 2668 2669 /** 2670 * @attribute loadPriority 2671 * @type Boolean 2672 * @default false 2673 */ 2674 loadPriority: { 2675 value: false 2676 }, 2677 2678 /** 2679 * Whether to crop the image. 2680 * 2681 * @attribute crop 2682 * @type Boolean 2683 * @default false 2684 */ 2685 crop: { 2686 value: false 2687 }, 2688 2689 /** 2690 * Checks whether the filename has nocrop in it or not. 2691 * If it does, the image will not be cropped. 2692 * 2693 * @attribute checkFilenamesForNoCrop 2694 * @type Boolean 2695 * @default true 2696 */ 2697 checkFilenamesForNoCrop: { 2698 value: true 2699 }, 2700 2701 /** 2702 * Whether to only crop horizontal images or not. 2703 * 2704 * @attribute cropHorizontalsOnly 2705 * @type Boolean 2706 * @default false 2707 */ 2708 cropHorizontalsOnly: { 2709 value: false 2710 }, 2711 2712 /** 2713 * The x and y position of the image 2714 * within the bounding box. 2715 * 2716 * @attribute position
2717 * @type String 2718 * @default center center 2719 */ 2720 position: { 2721 value: 'center center' 2722 }, 2723 2724 /** 2725 * Whether to right click protect the image. 2726 * 2727 * @attribute protect 2728 * @type Boolean 2729 * @default true 2730 */ 2731 protect: { 2732 value: true 2733 }, 2734 2735 /** 2736 * Whether to resize the image past 2737 * its original width and height. 2738 * 2739 * @attribute upsize 2740 * @type Boolean 2741 * @default true 2742 */ 2743 upsize: { 2744 value: true 2745 }, 2746 2747 /** 2748 * Whether to load thumb sizes. Defaults 2749 * to false since thumb sizes are square. 2750 * 2751 * @attribute useThumbSizes 2752 * @type Boolean 2753 * @default false 2754 */ 2755 useThumbSizes: { 2756 value: false 2757 }, 2758 2759 /** 2760 * Whether to constrain the width of the 2761 * bounding box to the width of the image. 2762 * 2763 * @attribute constrainWidth 2764 * @type Boolean 2765 * @default false 2766 */ 2767 constrainWidth: { 2768 value: false 2769 }, 2770 2771 /** 2772 * Whether to constrain the height of the 2773 * bounding box to the height of the image. 2774 * 2775 * @attribute constrainHeight 2776 * @type Boolean 2777 * @default false 2778 */ 2779 constrainHeight: { 2780 value: false 2781 }, 2782 2783 /** 2784 * Whether to load videos or not. The poster 2785 * image will be loaded if set to false. 2786 * 2787 * @attribute loadVideos 2788 * @type Boolean 2789 * @default true 2790 */ 2791 loadVideos: { 2792 value: true 2793 }, 2794 2795 /** 2796 * Whether to show the video play button or not. 2797 * When clicked, videos will be displayed in a 2798 * lightbox instead of the slideshow itself. 2799 * 2800 * @attribute showVideoButton 2801 * @type Boolean 2802 * @default true 2803 */ 2804 showVideoButton: { 2805 value: true 2806 } 2807 } 2808}); 2809 2810/** 2811 * A plugin that turns the cursor into a prev or next arrow when 2812 * it is over the left or right side of the slideshow. 2813 * 2814 * @namespace FL 2815 * @class SlideshowMouseNav 2816 * @constructor 2817 * @param config {Object} Configuration object 2818 * @extends Plugin.Base 2819 */ 2820Y.namespace('FL').SlideshowMouseNav = Y.Base.create('fl-slideshow-mouse-nav', Y.Plugin.Base, [], { 2821 2822 /** 2823 * @method initializer 2824 * @protected 2825 */ 2826 initializer: function() 2827 { 2828 var trigger = this.get('trigger'); 2829 2830 trigger.on('click', this._triggerClick, this); 2831 trigger.on('mousemove', this._showArrow, this); 2832 trigger.on('mouseleave', this._hideArrow, this); 2833 }, 2834 2835 /** 2836 * @method _triggerClick 2837 * @protected 2838 */ 2839 _triggerClick: function(e) 2840 { 2841 var host = this.get('host'), 2842 trigger = this.get('trigger'), 2843 triggerWidth = parseInt(trigger.getStyle('width'), 10), 2844 triggerRegion = trigger.get('region'), 2845 layerX = e.pageX - triggerRegion.left + 5; 2846 2847 if(layerX >= triggerWidth/2) { 2848 host.nextImage(); 2849 } 2850 else { 2851 host.prevImage(); 2852 } 2853 }, 2854 2855 /** 2856 * @method _showArrow 2857 * @protected 2858 */ 2859 _showArrow: function(e) 2860 { 2861 var host = this.get('host'), 2862 trigger = this.get('trigger'), 2863 triggerWidth = parseInt(trigger.getStyle('width'), 10), 2864 triggerRegion = trigger.get('region'), 2865 layerX = e.pageX - triggerRegion.left + 5; 2866 2867 if(host.albumInfo !== null && host.albumInfo.images.length > 1) { 2868 if(layerX >= triggerWidth/2) { 2869 trigger.removeClass('fl-slideshow-mouse-nav-prev'); 2870 trigger.addClass('fl-slideshow-mouse-nav-next'); 2871 } 2872 else { 2873 trigger.removeClass('fl-slideshow-mouse-nav-next'); 2874 trigger.addClass('fl-slideshow-mouse-nav-prev'); 2875 } 2876 } 2877 }, 2878 2879 /** 2880 * @method _hideArrow 2881 * @protected 2882 */ 2883 _hideArrow: function() 2884 { 2885 var trigger = this.get('trigger'); 2886 2887 trigger.removeClass('fl-slideshow-mouse-nav-next'); 2888 trigger.removeClass('fl-slideshow-mouse-nav-prev'); 2889 } 2890 2891}, { 2892 2893 /** 2894 * Namespace for the plugin. 2895 * 2896 * @property NS
2897 * @type String 2898 * @protected 2899 * @static 2900 */ 2901 NS: 'mouseNav', 2902 2903 /** 2904 * Static property used to define the default attribute configuration of 2905 * the Plugin. 2906 * 2907 * @property ATTRS 2908 * @type Object 2909 * @protected 2910 * @static 2911 */ 2912 ATTRS: { 2913 2914 /** 2915 * A Node that triggers the arrows. 2916 * 2917 * @attribute trigger 2918 * @type Node 2919 * @default null 2920 */ 2921 trigger: { 2922 value: null 2923 } 2924 } 2925}); 2926 2927/** 2928 * Ken Burns effect for slideshow images. 2929 * 2930 * @namespace FL 2931 * @class SlideshowKenBurns 2932 * @constructor 2933 * @param config {Object} Configuration object 2934 * @extends Base 2935 */ 2936Y.namespace('FL').SlideshowKenBurns = Y.Base.create('fl-slideshow-ken-burns', Y.Base, [], { 2937 2938 /** 2939 * Runs the Ken Burns effect. 2940 * 2941 * @method run 2942 */ 2943 run: function() 2944 { 2945 var imageNode = null, 2946 transform = null; 2947 2948 if(Y.FL.Utils.cssSupport('transform')) { 2949 2950 // Image node 2951 imageNode = this.get('image').one('img'); 2952 2953 // Transform object 2954 transform = this._getTransform(); 2955 2956 // Apply the start transform 2957 imageNode.setStyles({ 2958 '-webkit-transform-origin': transform.origin, 2959 '-moz-transform-origin': transform.origin, 2960 '-ms-transform-origin': transform.origin, 2961 'transform-origin': transform.origin, 2962 'transform': transform.start 2963 }); 2964 2965 // Transition to the end transform 2966 imageNode.transition({ 2967 easing: 'ease-out', 2968 duration : this.get('duration'), 2969 'transform' : transform.end 2970 }); 2971 } 2972 }, 2973 2974 /** 2975 * @method _getTransform 2976 * @protected 2977 */ 2978 _getTransform: function() 2979 { 2980 var zoom = this.get('zoom'), 2981 image = this.get('image'), 2982 i = 0, 2983 zoomDirection = null, 2984 transform = null; 2985 2986 // Random zoom direction 2987 i = Math.floor(Math.random() * Y.FL.SlideshowKenBurns.ZOOM_DIRECTIONS.length); 2988 zoomDirection = Y.FL.SlideshowKenBurns.ZOOM_DIRECTIONS[i]; 2989 2990 // Random transform 2991 i = Math.floor(Math.random() * Y.FL.SlideshowKenBurns.TRANSFORMS.length); 2992 transform = Y.FL.SlideshowKenBurns.TRANSFORMS[i]; 2993 2994 // Get the start and end transforms 2995 if(!image.hasClass('fl-slideshow-image-cropped') && zoomDirection == 'in') { 2996 i = Math.floor(Math.random() * 2); 2997 transform.start = i === 0 ? 'scale(1) translate(100px, 0)' : 'scale(1) translate(-100px, 0)'; 2998 transform.end = 'scale(' + zoom + ') translate(0, 0)'; 2999 transform.origin = 'center center'; 3000 } 3001 else if(zoomDirection == 'out') { 3002 transform.start = 'scale(' + zoom + ') ' + transform.translate; 3003 transform.end = 'scale(1) translate(0, 0)'; 3004 } 3005 else { 3006 transform.start = 'scale(1) translate(0, 0)'; 3007 transform.end = 'scale(' + zoom + ') ' + transform.translate; 3008 } 3009 3010 return transform; 3011 } 3012 3013}, { 3014 3015 /** 3016 * Static property used to define the default attribute configuration of 3017 * the Widget. 3018 * 3019 * @property ATTRS 3020 * @type Object 3021 * @protected 3022 * @static 3023 */ 3024 ATTRS: { 3025 3026 /** 3027 * An instance of FL.Slideshow image to apply the 3028 * Ken Burns effect on. 3029 * 3030 * @attribute image 3031 * @type FL.Slideshow 3032 * @default null 3033 */ 3034 image: { 3035 value: null 3036 }, 3037 3038 /** 3039 * The amount to zoom the image. Zooming 3040 * in our out is done randomly by this class. 3041 * 3042 * @attribute scale 3043 * @type Number 3044 * @default 1.2 3045 */ 3046 zoom: { 3047 value: 1.2 3048 }, 3049 3050 /** 3051 * The duration of the effect in seconds. 3052 * 3053 * @attribute duration 3054 * @type Number 3055 * @default 2 3056 */ 3057 duration: { 3058 value: 2 3059 } 3060 }, 3061 3062 /** 3063 * The zoom directions that can be applied to an image. 3064 * 3065 * @property ZOOM_DIRECTIONS 3066 * @type Object 3067 * @readOnly 3068 * @protected 3069 * @static 3070 */ 3071 ZOOM_DIRECTIONS: [ 3072 'in', 3073 'out' 3074 ], 3075 3076 /** 3077 * The types of transforms that can be applied to an image. 3078 * 3079 * @property TRANSFORMS 3080 * @type Object 3081 * @readOnly 3082 * @protected 3083 * @static 3084 */ 3085 TRANSFORMS: [ 3086 { 3087 origin : 'left top', 3088 translate : 'translate(-30px, -15px)' 3089 },{ 3090 origin : 'left center', 3091 translate : 'translate(-30px, 0)' 3092 },{ 3093 origin : 'left bottom', 3094 translate : 'translate(-30px, 15px)' 3095 },{ 3096 origin : 'right top', 3097 translate : 'translate(30px, -15px)' 3098 },{ 3099 origin : 'right center', 3100 translate : 'translate(30px, 0)' 3101 },{ 3102 origin : 'right bottom', 3103 translate : 'translate(30px, 15px)' 3104 } 3105 ] 3106}); 3107 3108/** 3109 * Navigation buttons widget for controlling a slideshow instance 3110 * and its child widgets. 3111 * 3112 * @namespace FL 3113 * @class SlideshowNav 3114 * @constructor 3115 * @param config {Object} Configuration object 3116 * @extends Widget 3117 */ 3118Y.namespace('FL').SlideshowNav = Y.Base.create('fl-slideshow-nav', Y.Widget, [Y.WidgetChild], { 3119 3120 /**
3121 * An object containing the anchor nodes for all buttons. 3122 * 3123 * @property _buttons 3124 * @type Object 3125 * @default null 3126 * @protected 3127 */ 3128 _buttons: null, 3129 3130 /** 3131 * An div node containing the anchor nodes for the main buttons. 3132 * 3133 * @property _buttonsContainer 3134 * @type Object 3135 * @default null 3136 * @protected 3137 */ 3138 _buttonsContainer: null, 3139 3140 /** 3141 * An div node containing the anchor nodes for the left buttons. 3142 * 3143 * @property _buttonsLeftContainer 3144 * @type Object 3145 * @default null 3146 * @protected 3147 */ 3148 _buttonsLeftContainer: null, 3149 3150 /** 3151 * An div node containing the anchor nodes for the right buttons. 3152 * 3153 * @property _buttonsRightContainer 3154 * @type Object 3155 * @default null 3156 * @protected 3157 */ 3158 _buttonsRightContainer: null, 3159 3160 /** 3161 * Property map for rendering SmugMug font icons. 3162 * 3163 * @property _fontIcons 3164 * @type Object 3165 * @protected 3166 */ 3167 _fontIcons: { 3168 buy: 'Cart', 3169 caption: 'InfoEncircled', 3170 close: 'XCrossEncircled', 3171 fullscreen: 'ScreenExpand', 3172 next: 'ArrowRight', 3173 nextPage: 'ArrowRight', 3174 pause: 'PlayerPause', 3175 play: 'PlayerPlay', 3176 prev: 'ArrowLeft', 3177 prevPage: 'ArrowLeft', 3178 social: 'Heart', 3179 thumbs: 'ViewThumbGrid' 3180 }, 3181 3182 /** 3183 * The default content template for the nav 3184 * inherited from Y.Widget. Set to null since 3185 * only the bounding box is needed. 3186 * 3187 * @property CONTENT_TEMPLATE
3188 * @type String 3189 * @default null 3190 * @protected 3191 */ 3192 CONTENT_TEMPLATE: null, 3193 3194 /** 3195 * Renders the buttons. 3196 * 3197 * @method renderUI 3198 * @protected 3199 */ 3200 renderUI: function() 3201 { 3202 this._renderContainers(); 3203 this._renderButtons(); 3204 this._renderFontIcons(); 3205 }, 3206 3207 /** 3208 * Binds events to the root slideshow for each button. 3209 * 3210 * @method bindUI 3211 * @protected 3212 */ 3213 bindUI: function() 3214 { 3215 var root = this.get('root'), 3216 id = this.get('id'); 3217 3218 if(this._buttons.prev) { 3219 this._buttons.prev.on('click', root.prevImage, root); 3220 } 3221 if(this._buttons.next) { 3222 this._buttons.next.on('click', root.nextImage, root); 3223 } 3224 if(this._buttons.play) { 3225 this._buttons.play.on('click', this._playClicked, this); 3226 root.on(id + '|played', this._showPauseButton, this); 3227 root.on(id + '|paused', this._showPlayButton, this); 3228 3229 if(root._playing) { 3230 this._showPauseButton(); 3231 } 3232 else { 3233 this._showPlayButton(); 3234 } 3235 } 3236 if(this._buttons.buy) { 3237 3238 root.on(id + '|albumLoadComplete', this._updateBuy, this); 3239 3240 if(root.albumInfo !== null) { 3241 this._updateBuy(); 3242 } 3243 } 3244 if(this._buttons.count) { 3245 root.on(id + '|imageLoadComplete', this._updateCount, this); 3246 } 3247 if(this._buttons.thumbs) { 3248 this._buttons.thumbs.on('click', root._toggleThumbs, root); 3249 } 3250 if(this._buttons.caption) { 3251 root.on(id + '|imageLoadComplete', this._updateCaption, this); 3252 this._updateCaption(); 3253 } 3254 if(this._buttons.social) { 3255 this._buttons.social.on('click', root._toggleSocial, root); 3256 } 3257 if(this._buttons.fullscreen && root.fullscreen) { 3258 this._buttons.fullscreen.on('click', root.fullscreen.toggle, root.fullscreen); 3259 } 3260 if(this._buttons.close) { 3261 this._buttons.close.on('click', root.hide, root); 3262 } 3263 }, 3264 3265 /** 3266 * @method destructor 3267 * @protected 3268 */ 3269 destructor: function() 3270 { 3271 var root = this.get('root'), 3272 id = this.get('id'); 3273 3274 root.detach(id + '|*'); 3275 }, 3276 3277 /** 3278 * Renders the button left, right and main button containers. 3279 * 3280 * @method _renderContainers 3281 * @protected 3282 */ 3283 _renderContainers: function() 3284 { 3285 var cb = this.get('contentBox'), 3286 buttonsLeft = this.get('buttonsLeft'), 3287 buttonsRight = this.get('buttonsRight'); 3288 3289 this._buttonsContainer = Y.Node.create('<div></div>'); 3290 this._buttonsContainer.addClass('fl-slideshow-nav-buttons'); 3291 cb.appendChild(this._buttonsContainer); 3292 3293 if(buttonsLeft.length > 0) { 3294 this._buttonsLeftContainer = Y.Node.create('<div></div>'); 3295 this._buttonsLeftContainer.addClass('fl-slideshow-nav-buttons-left'); 3296 cb.appendChild(this._buttonsLeftContainer); 3297 } 3298 if(buttonsRight.length > 0) { 3299 this._buttonsRightContainer = Y.Node.create('<div></div>'); 3300 this._buttonsRightContainer.addClass('fl-slideshow-nav-buttons-right'); 3301 cb.appendChild(this._buttonsRightContainer); 3302 } 3303 }, 3304 3305 /** 3306 * Renders the buttons based on the buttons array 3307 * passed in the configuration object. 3308 * 3309 * @method _renderButtons 3310 * @protected 3311 */ 3312 _renderButtons: function() 3313 { 3314 var name = '', 3315 fallback = this.get('root').get('fallback'), 3316 tag = fallback ? 'a' : 'button', 3317 attrs = fallback ? 'role="button" tabindex="0"' : 'type="button"', 3318 i = 0, 3319 k = 0, 3320 b = [ 3321 { 3322 names: this.get('buttons'), 3323 container: this._buttonsContainer 3324 }, 3325 { 3326 names: this.get('buttonsLeft'), 3327 container: this._buttonsLeftContainer 3328 }, 3329 { 3330 names: this.get('buttonsRight'), 3331 container: this._buttonsRightContainer 3332 } 3333 ]; 3334 3335 this._buttons = {}; 3336 3337 for( ; i < b.length; i++) { 3338 for(k = 0; k < b[i].names.length; k++) { 3339 3340 name = b[i].names[k]; 3341 3342 if(name.indexOf('count') > -1) { 3343 this._buttons[name] = Y.Node.create('<span></span>'); 3344 this._updateCount(); 3345 } 3346 else { 3347 this._buttons[name] = Y.Node.create('<' + tag + ' ' + attrs + '></' + tag + '>'); 3348 } 3349 3350 if(name.indexOf('buy') > -1) { 3351 this._buttons[name].setStyle('display', 'none'); 3352 } 3353 3354 this._buttons[name].set('name', name); 3355 this._buttons[name].set('aria-label', name); 3356 this._buttons[name].addClass('fl-slideshow-nav-' + name + (!fallback ? ' fl-content-ui-button' : '')); 3357 b[i].container.appendChild(this._buttons[name]); 3358 } 3359 } 3360 }, 3361 3362 /** 3363 * Renders SmugMug font icons for each button. 3364 * 3365 * @method _renderFontIcons 3366 * @protected 3367 */ 3368 _renderFontIcons: function() 3369 { 3370 var name = null; 3371 3372 if(this.get('useFontIcons') && typeof YUI.Env.mods['sm-fonticon'] !== 'undefined') { 3373 for(name in this._buttons) { 3374 if(typeof this._buttons[name] !== 'undefined' && typeof this._fontIcons[name] !== 'undefined') { 3375 this._buttons[name].addClass('sm-fonticon-' + this._fontIcons[name]); 3376 this._buttons[name].addClass('sm-fonticon sm-button-skin-default sm-button-nochrome'); 3377 } 3378 else if(name.indexOf('count') > -1) { 3379 this._buttons[name].addClass('fonticons-enabled'); 3380 } 3381 } 3382 } 3383 }, 3384 3385 /** 3386 * Updates the image count. 3387 * 3388 * @method _updateCount 3389 * @protected 3390 */ 3391 _updateCount: function() 3392 { 3393 var html = '', 3394 countText = Y.FL.SlideshowNav.COUNT_TEXT, 3395 current = 1, 3396 total = 1; 3397 3398 if(this.get('root').albumInfo) { 3399 current = this.get('root').imageInfo.index + 1; 3400 total = this.get('root').albumInfo.images.length; 3401 } 3402 3403 html = countText.replace('{current}', current).replace('{total}', total); 3404 this._buttons.count.set('innerHTML', html); 3405 }, 3406 3407 /** 3408 * Shows the caption button if the current image 3409 * has a caption, hides it if the image does not 3410 * have a caption. 3411 * 3412 * @method _updateCaption 3413 * @protected 3414 */ 3415 _updateCaption: function() 3416 { 3417 var root = this.get('root'), 3418 imageInfo = root.imageInfo; 3419 3420 if(imageInfo && imageInfo.caption === '') { 3421 root.caption.slideshowOverlay.enable(); 3422 root.caption.slideshowOverlay.hide(); 3423 this._buttons.caption.detach('click'); 3424 this._buttons.caption.addClass('fl-slideshow-nav-caption-disabled'); 3425 } 3426 else { 3427 this._buttons.caption.on('click', root._toggleCaption, root); 3428 this._buttons.caption.removeClass('fl-slideshow-nav-caption-disabled'); 3429 } 3430 }, 3431 3432 /** 3433 * Checks if buying has been enabled for the current album. 3434 * 3435 * @method _updateBuy 3436 * @protected 3437 */ 3438 _updateBuy: function() 3439 { 3440 var sm = null, 3441 root = this.get('root'), 3442 rootSource = root.get('source')[root.albumIndex], 3443 albumIndex = root.albumIndex, 3444 source = root.get('source')[albumIndex]; 3445 3446 if(rootSource && rootSource.type == 'smugmug') { 3447 if(typeof root.albumInfo.printable !== 'undefined') { 3448 this._updateBuyComplete(); 3449 } 3450 else { 3451 sm = new Y.FL.SmugMugAPI(); 3452 sm.addParam('method', 'smugmug.albums.getInfo'); 3453 sm.addParam('AlbumID', source.id); 3454 sm.addParam('AlbumKey', source.key); 3455 sm.on('complete', this._updateBuyComplete, this); 3456 sm.request(); 3457 } 3458 } 3459 }, 3460 3461 /** 3462 * Shows the buy button and updates the buy url 3463 * if buying has been enabled. 3464 * 3465 * @method _updateBuyComplete 3466 * @param e {Object} The custom event object passed to this function. 3467 * @protected 3468 */ 3469 _updateBuyComplete: function(e) 3470 { 3471 var root = this.get('root'), 3472 printable = typeof e == 'undefined' ? root.albumInfo.printable : e.Album.Printable, 3473 link = root.albumInfo.link; 3474 3475 if(printable) { 3476 root.albumInfo.printable = true; 3477 this._buttons.buy.set('href', 'https://secure.smugmug.com/cart/batchadd/?url=' + encodeURIComponent(link)); 3478 this._buttons.buy.setStyle('display', 'inline-block'); 3479 } 3480 else { 3481 root.albumInfo.printable = false;
3482 this._buttons.buy.setStyle('display', 'none'); 3483 } 3484 3485 this.fire('resize'); 3486 }, 3487 3488 /** 3489 * Pauses the slideshow if it is playing and 3490 * plays the slideshow if it is paused. 3491 * 3492 * @method _playClicked 3493 * @protected 3494 */ 3495 _playClicked: function() 3496 { 3497 var root = this.get('root'); 3498 3499 if(root._playing) { 3500 root.pause(); 3501 } 3502 else { 3503 root.play(); 3504 } 3505 }, 3506 3507 /** 3508 * Toggles the button class for the play button 3509 * so pause is hidden and play is shown. 3510 * 3511 * @method _showPlayButton 3512 * @protected 3513 */ 3514 _showPlayButton: function() 3515 { 3516 this._buttons.play.removeClass('fl-slideshow-nav-pause'); 3517 this._buttons.play.addClass('fl-slideshow-nav-play'); 3518 3519 if(this.get('useFontIcons') && typeof YUI.Env.mods['sm-fonticon'] !== 'undefined') { 3520 this._buttons.play.removeClass('sm-fonticon-PlayerPause'); 3521 this._buttons.play.addClass('sm-fonticon-PlayerPlay'); 3522 } 3523 }, 3524 3525 /** 3526 * Toggles the button class for the play button 3527 * so pause is shown and play is hidden. 3528 * 3529 * @method _showPauseButton 3530 * @protected 3531 */ 3532 _showPauseButton: function() 3533 { 3534 this._buttons.play.removeClass('fl-slideshow-nav-play'); 3535 this._buttons.play.addClass('fl-slideshow-nav-pause'); 3536 3537 if(this.get('useFontIcons') && typeof YUI.Env.mods['sm-fonticon'] !== 'undefined') { 3538 this._buttons.play.removeClass('sm-fonticon-PlayerPlay'); 3539 this._buttons.play.addClass('sm-fonticon-PlayerPause'); 3540 } 3541 } 3542 3543}, { 3544 3545 /** 3546 * Custom CSS class name for the widget. 3547 * 3548 * @property CSS_PREFIX
3549 * @type String 3550 * @protected 3551 * @static 3552 */ 3553 CSS_PREFIX: 'fl-slideshow-nav', 3554 3555 /** 3556 * Static string used for displaying the image count. Use {current} 3557 * for the current image and {total} for the total number of images. 3558 * Those placeholders will be replaced when the count node is created. 3559 * 3560 * @property COUNT_TEXT 3561 * @type String 3562 * @protected 3563 * @static 3564 */ 3565 COUNT_TEXT: '{current} of {total}', 3566 3567 /** 3568 * Static property used to define the default attribute configuration of 3569 * the Widget. 3570 * 3571 * @property ATTRS 3572 * @type Object 3573 * @protected 3574 * @static 3575 */ 3576 ATTRS: { 3577 3578 /** 3579 * An array of button names that is used to render the main buttons. 3580 * 3581 * @attribute buttons 3582 * @type Array 3583 * @default [] 3584 * @writeOnce 3585 */ 3586 buttons: { 3587 value: [], 3588 writeOnce: true 3589 }, 3590 3591 /** 3592 * An array of button names that is used to render the left buttons. 3593 * 3594 * @attribute buttonsLeft 3595 * @type Array 3596 * @default [] 3597 * @writeOnce 3598 */ 3599 buttonsLeft: { 3600 value: [], 3601 writeOnce: true 3602 }, 3603 3604 /** 3605 * An array of button names that is used to render the right buttons. 3606 * 3607 * @attribute buttonsRight 3608 * @type Array 3609 * @default [] 3610 * @writeOnce 3611 */ 3612 buttonsRight: { 3613 value: [], 3614 writeOnce: true 3615 }, 3616 3617 /** 3618 * Whether to use font icons when available. 3619 * 3620 * @attribute useFontIcons 3621 * @type Boolean 3622 * @default true 3623 * @writeOnce 3624 */ 3625 useFontIcons: { 3626 value: true, 3627 writeOnce: true 3628 } 3629 } 3630}); 3631 3632/** 3633 * A plugin for overlaying widgets in a slideshow 3634 * with specialized show and hide functionality. 3635 * 3636 * @namespace FL 3637 * @class SlideshowOverlay 3638 * @constructor 3639 * @param config {Object} Configuration object 3640 * @extends Plugin.Base 3641 */ 3642Y.namespace('FL').SlideshowOverlay = Y.Base.create('fl-slideshow-overlay', Y.Plugin.Base, [], { 3643 3644 /** 3645 * Flag for whether the mouse has entered 3646 * the host's bounding box. 3647 * 3648 * @property _focus 3649 * @type Boolean 3650 * @default false 3651 * @protected 3652 */ 3653 _focus: false, 3654 3655 /** 3656 * Flag for whether the host's bounding box is visible. 3657 * 3658 * @property _visible 3659 * @type Boolean 3660 * @default true 3661 * @protected 3662 */ 3663 _visible: true, 3664 3665 /** 3666 * Flag for whether show and hide functionality 3667 * has been disabled. 3668 * 3669 * @property _disabled 3670 * @type Boolean 3671 * @default false 3672 * @protected 3673 */ 3674 _disabled: false, 3675 3676 /** 3677 * An object containing properties for the show transition. 3678 * 3679 * @property _showProps 3680 * @type Object 3681 * @protected 3682 */ 3683 _showProps: { 3684 duration: 0.5, 3685 easing: 'ease-out', 3686 opacity: 1 3687 }, 3688 3689 /** 3690 * An object containing properties for the hide transition. 3691 * 3692 * @property _hideProps 3693 * @type Object 3694 * @protected 3695 */ 3696 _hideProps: { 3697 duration: 0.5, 3698 easing: 'ease-out', 3699 opacity: 0 3700 }, 3701 3702 /** 3703 * A timer object for delaying the hide transition. 3704 * 3705 * @property _hideTimer 3706 * @type Object 3707 * @default null 3708 * @protected 3709 */ 3710 _hideTimer: null, 3711 3712 /** 3713 * @method initializer 3714 * @protected 3715 */ 3716 initializer: function() 3717 { 3718 var bb = this.get('host').get('boundingBox'); 3719 3720 this.afterHostEvent('render', this._initFocus); 3721 this.afterHostEvent('render', this._initVisibility); 3722 3723 if(this.get('closeButton')) { 3724 this._initCloseButton(); 3725 } 3726 3727 bb.addClass('fl-slideshow-overlay'); 3728 }, 3729 3730 /** 3731 * @method destructor 3732 * @protected 3733 */ 3734 destructor: function() 3735 { 3736 this._hideTimerCancel(); 3737 }, 3738 3739 /** 3740 * Binds the mouseenter and mouseleave events for setting focus. 3741 * 3742 * @method _initFocus 3743 * @protected 3744 */ 3745 _initFocus: function() 3746 { 3747 var bb = this.get('host').get('boundingBox'); 3748 bb.on('mouseenter', Y.bind(this._setFocusOnMouseenter, this)); 3749 bb.on('mouseleave', Y.bind(this._setFocusOnMouseleave, this)); 3750 }, 3751 3752 /** 3753 * Sets the initial visibility of the host's bounding box. 3754 * 3755 * @method _initVisibility 3756 * @protected 3757 */ 3758 _initVisibility: function() 3759 { 3760 var bb = this.get('host').get('boundingBox'), 3761 hideStyle = this.get('hideStyle'); 3762 3763 if(!this.get('visible')) { 3764 3765 if(hideStyle == 'display') { 3766 bb.setStyle('display', 'none'); 3767 } 3768 else if(hideStyle == 'left') { 3769 bb.setStyle('left', '-99999px'); 3770 } 3771 3772 bb.setStyle('opacity', '0'); 3773 this._visible = false;
3774 } 3775 }, 3776 3777 /** 3778 * Creates and inserts the close button. 3779 * 3780 * @method _initCloseButton 3781 * @protected 3782 */ 3783 _initCloseButton: function() 3784 { 3785 var bb = this.get('host').get('boundingBox'), 3786 fallback = this.get('host').get('root').get('fallback'), 3787 closeButton = null; 3788 3789 if(fallback) { 3790 closeButton = Y.Node.create('<a class="fl-slideshow-overlay-close" role="button" aria-label="close overlay" tabindex="0"></a>'); 3791 } 3792 else { 3793 closeButton = Y.Node.create('<button class="fl-slideshow-overlay-close fl-content-ui-button" type="button" aria-label="close overlay"></button>'); 3794 } 3795 closeButton.on('click', Y.bind(this._closeButtonClick, this)); 3796 3797 if(typeof YUI.Env.mods['sm-fonticon'] !== 'undefined') { 3798 closeButton.addClass('sm-fonticon sm-fonticon-XCrossEncircled sm-button-skin-default sm-button-nochrome'); 3799 } 3800 3801 bb.insert(closeButton); 3802 }, 3803 3804 /** 3805 * Hides the overlay when the close button is clicked. 3806 * 3807 * @method _closeButtonClick 3808 * @protected 3809 */ 3810 _closeButtonClick: function() 3811 { 3812 var bb = this.get('host').get('boundingBox'); 3813 bb.transition(this._hideProps, Y.bind(this._hideComplete, this)); 3814 }, 3815 3816 /** 3817 * Sets the focus flag to true. 3818 * 3819 * @method _setFocusOnMouseenter 3820 * @protected 3821 */ 3822 _setFocusOnMouseenter: function() 3823 { 3824 this._focus = true; 3825 }, 3826 3827 /** 3828 * Sets the focus flag to false. 3829 * 3830 * @method _setFocusOnMouseleave 3831 * @protected 3832 */ 3833 _setFocusOnMouseleave: function() 3834 { 3835 this._focus = false; 3836 }, 3837 3838 /** 3839 * Disables show and hide functionality. 3840 * 3841 * @method disable 3842 * @public 3843 */ 3844 disable: function() 3845 { 3846 this._disabled = true; 3847 }, 3848 3849 /** 3850 * Enables show and hide functionality. 3851 * 3852 * @method enable 3853 * @public 3854 */ 3855 enable: function() 3856 { 3857 this._disabled = false; 3858 }, 3859 3860 /** 3861 * Shows the host's bounding box with a fade in transition. 3862 * 3863 * @method show 3864 * @public 3865 */ 3866 show: function() 3867 { 3868 var bb = this.get('host').get('boundingBox'), 3869 hideStyle = this.get('hideStyle'); 3870 3871 if(this._disabled) { 3872 return; 3873 } 3874 3875 if(hideStyle == 'display') { 3876 bb.setStyle('display', 'block'); 3877 } 3878 else if(hideStyle == 'left') { 3879 bb.setStyle('left', 'auto'); 3880 } 3881 3882 bb.transition(this._showProps, Y.bind(this._showComplete, this)); 3883 3884 /** 3885 * @event hideStart 3886 */ 3887 this.fire('showStart'); 3888 }, 3889 3890 /** 3891 * @method _showComplete 3892 * @protected 3893 */ 3894 _showComplete: function() 3895 { 3896 this._visible = true; 3897 this.hideWithTimer(); 3898 3899 /** 3900 * @event showComplete 3901 */ 3902 this.fire('showComplete'); 3903 }, 3904 3905 /** 3906 * Hides the host's bounding box with a fade out transition. 3907 * 3908 * @method hide 3909 * @public 3910 */ 3911 hide: function() 3912 { 3913 if(this._focus || this._disabled) { 3914 return; 3915 } 3916 3917 var bb = this.get('host').get('boundingBox'); 3918 bb.transition(this._hideProps, Y.bind(this._hideComplete, this)); 3919 3920 /** 3921 * @event hideStart 3922 */ 3923 this.fire('hideStart'); 3924 }, 3925 3926 /** 3927 * Hides the host's bounding box with a fade out transition 3928 * after a timer completes. 3929 * 3930 * @method hideWithTimer 3931 * @public 3932 */ 3933 hideWithTimer: function() 3934 { 3935 this._hideTimerCancel(); 3936 this._hideTimer = Y.later(this.get('hideDelay'), this, this.hide); 3937 }, 3938 3939 /** 3940 * Cancels the hide timer. 3941 * 3942 * @method _hideTimerCancel 3943 * @protected 3944 */ 3945 _hideTimerCancel: function() 3946 { 3947 if(this._hideTimer) { 3948 this._hideTimer.cancel(); 3949 this._hideTimer = null; 3950 } 3951 }, 3952 3953 /** 3954 * @method _hideComplete 3955 * @protected 3956 */ 3957 _hideComplete: function() 3958 { 3959 var bb = this.get('host').get('boundingBox'), 3960 hideStyle = this.get('hideStyle'); 3961 3962 if(hideStyle == 'display') { 3963 bb.setStyle('display', 'none'); 3964 } 3965 else if(hideStyle == 'left') { 3966 bb.setStyle('left', '-99999px'); 3967 } 3968 3969 this._visible = false; 3970 3971 /** 3972 * @event hideComplete 3973 */ 3974 this.fire('hideComplete'); 3975 } 3976 3977}, { 3978 3979 /** 3980 * Namespace for the plugin. 3981 * 3982 * @property NS
3983 * @type String 3984 * @protected 3985 * @static 3986 */ 3987 NS: 'slideshowOverlay', 3988 3989 /** 3990 * Static property used to define the default attribute configuration of 3991 * the plugin. 3992 * 3993 * @property ATTRS 3994 * @type Object 3995 * @protected 3996 * @static 3997 */ 3998 ATTRS: { 3999 4000 /** 4001 * Whether to use the close button or not. 4002 * 4003 * @attribute closeButton 4004 * @type Boolean 4005 * @default false 4006 * @writeOnce 4007 */ 4008 closeButton: { 4009 value: false, 4010 writeOnce: true 4011 }, 4012 4013 /** 4014 * The time to wait before hiding the host's bounding box. 4015 * Measured in milliseconds. 4016 * 4017 * @attribute hideDelay 4018 * @type Number 4019 * @default 3000 4020 * @writeOnce 4021 */ 4022 hideDelay: { 4023 value: 3000, 4024 writeOnce: true 4025 }, 4026 4027 /** 4028 * The style to use for hiding the image. Possible 4029 * values are display and left. 4030 * 4031 * @attribute hideStyle 4032 * @type String 4033 * @default display 4034 * @writeOnce 4035 */ 4036 hideStyle: { 4037 value: 'display', 4038 writeOnce: true 4039 }, 4040 4041 /** 4042 * Sets the initial visibility of the host's bounding box. 4043 * 4044 * @attribute visible 4045 * @type Boolean 4046 * @default true 4047 * @writeOnce 4048 */ 4049 visible: { 4050 value: true, 4051 writeOnce: true 4052 } 4053 } 4054}); 4055 4056/** 4057 * Social buttons widget used in slideshows. 4058 * 4059 * @namespace FL 4060 * @class SlideshowSocial 4061 * @constructor 4062 * @param config {Object} Configuration object 4063 * @extends Widget 4064 */ 4065 4066Y.namespace('FL').SlideshowSocial = Y.Base.create('fl-slideshow-social', Y.Widget, [Y.WidgetChild], { 4067 4068 /** 4069 * An object containing the social button nodes. 4070 * 4071 * @property _buttons 4072 * @type Object 4073 * @default null 4074 * @protected 4075 */ 4076 _buttons: null, 4077 4078 /** 4079 * @method renderUI 4080 * @protected 4081 */ 4082 renderUI: function() 4083 { 4084 this._buttons = {}; 4085 }, 4086 4087 /** 4088 * @method bindUI 4089 * @protected 4090 */ 4091 bindUI: function() 4092 { 4093 var root = this.get('root'); 4094 4095 if(root.get('likeButtonEnabled')) { 4096 root.on('imageLoadComplete', Y.bind(this._updateLikeButton, this)); 4097 } 4098 if(root.get('tweetButtonEnabled')) { 4099 root.on('imageLoadComplete', Y.bind(this._updateTweetButton, this)); 4100 } 4101 if(root.get('pinterestButtonEnabled')) { 4102 root.on('imageLoadComplete', Y.bind(this._updatePinterestButton, this)); 4103 } 4104 }, 4105 4106 /** 4107 * @method _updateLikeButton 4108 * @protected 4109 */ 4110 _updateLikeButton: function() 4111 { 4112 var href = 'https://www.facebook.com/sharer/sharer.php?u=', 4113 svg = null, 4114 cb = this.get('contentBox'), 4115 root = this.get('root'), 4116 albumIndex = root.albumIndex, 4117 rootSource = root.get('source')[albumIndex], 4118 imageInfo = root.imageInfo; 4119 4120 if(this._buttons.like) { 4121 this._buttons.like.remove(); 4122 this._buttons.like = null; 4123 } 4124 4125 if(rootSource.type == 'smugmug') { 4126 href += encodeURIComponent('https://www.smugmug.com/services/graph/gallery/' + rootSource.id + '_' + rootSource.key + '/' + imageInfo.id + '_' + imageInfo.key); 4127 } 4128 else { 4129 href += encodeURIComponent(imageInfo.largeURL); 4130 } 4131 svg = '<svg viewBox="0 0 320 512" width="10" height="12" aria-hidden="true">'; 4132 svg += '<path fill="currentColor" d="M279.14 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z"/>'; 4133 svg += '</svg>'; 4134 4135 this._buttons.like = Y.Node.create('<a href="'+ href +'" class="fl-slideshow-social-facebook" target="_blank" rel="noopener noreferrer">' + svg + '<span>Share</span></a>'); 4136 4137 cb.appendChild(this._buttons.like); 4138 }, 4139 4140 /** 4141 * @method _updateTweetButton 4142 * @protected 4143 */ 4144 _updateTweetButton: function() 4145 { 4146 var href = 'https://x.com/intent/tweet?url=', 4147 svg = null, 4148 imageInfo = this.get('root').imageInfo, 4149 cb = this.get('contentBox'); 4150 4151 if(this._buttons.tweet) { 4152 this._buttons.tweet.remove(); 4153 this._buttons.tweet = null; 4154 } 4155 4156 href += encodeURIComponent(imageInfo.largeURL); 4157 svg = '<svg viewBox="0 0 24 24" width="12" height="12" aria-hidden="true">'; 4158 svg += '<path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>'; 4159 svg += '</svg>'; 4160 4161 this._buttons.tweet = Y.Node.create('<a href="'+ href +'" class="fl-slideshow-social-x" target="_blank" rel="noopener noreferrer">' + svg + '<span>Post</span></a>'); 4162 4163 cb.appendChild(this._buttons.tweet); 4164 }, 4165 4166 /** 4167 * @method _updatePinterestButton 4168 * @protected 4169 */ 4170 _updatePinterestButton: function() 4171 { 4172 var href = 'https://pinterest.com/pin/create/button/', 4173 svg = null, 4174 imageInfo = this.get('root').imageInfo, 4175 cb = this.get('contentBox'); 4176 4177 if(this._buttons.pin) { 4178 this._buttons.pin.remove(); 4179 this._buttons.pin = null; 4180 } 4181 4182 href += '?url=' + encodeURIComponent(window.location.href); 4183 href += '&media='+ encodeURIComponent(imageInfo.mediumURL); 4184 href += '&description='+ encodeURIComponent(imageInfo.caption); 4185 svg = '<svg viewBox="0 0 384 512" width="12" height="14" aria-hidden="true">';
4186 svg += '<path fill="currentColor" d="M204 6.5C101.4 6.5 0 74.9 0 185.6 0 256 39.6 296 63.6 296c9.9 0 15.6-27.6 15.6-35.4 0-9.3-23.7-29.1-23.7-67.8 0-80.4 61.2-137.4 140.4-137.4 68.1 0 118.5 38.7 118.5 109.8 0 53.1-21.3 152.7-90.3 152.7-24.9 0-46.2-18-46.2-43.8 0-37.8 26.4-74.4 26.4-113.4 0-66.2-93.9-54.2-93.9 25.8 0 16.8 2.1 35.4 9.6 50.7-13.8 59.4-42 147.9-42 209.1 0 18.9 2.7 37.5 4.5 56.4 3.4 3.8 1.7 3.4 6.9 1.5 50.4-69 48.6-82.5 71.4-172.8 12.3 23.4 44.1 36 69.3 36 106.2 0 153.9-103.5 153.9-196.8C384 71.3 298.2 6.5 204 6.5z"/>'; 4187 svg += '</svg>'; 4188 4189 this._buttons.pin = Y.Node.create('<a href="' + href + '" class="fl-slideshow-social-pinterest" target="_blank" rel="noopener noreferrer">' + svg + '<span>Pin</span></a>'); 4190 4191 cb.appendChild(this._buttons.pin); 4192 } 4193 4194}, { 4195 4196 /** 4197 * Custom CSS class name for the widget. 4198 * 4199 * @property CSS_PREFIX 4200 * @type String 4201 * @protected 4202 * @static 4203 */ 4204 CSS_PREFIX: 'fl-slideshow-social', 4205 4206 /** 4207 * Static property used to define the default attribute configuration of 4208 * the Widget. 4209 * 4210 * @property ATTRS 4211 * @type Object 4212 * @protected 4213 * @static 4214 */ 4215 ATTRS: { 4216 4217 } 4218}); 4219 4220/** 4221 * Creates a grid of FL.SlideshowImage instances. 4222 * 4223 * @namespace FL 4224 * @class SlideshowThumbs 4225 * @constructor 4226 * @param config {Object} Configuration object 4227 * @extends Widget 4228 */ 4229Y.namespace('FL').SlideshowThumbs = Y.Base.create('fl-slideshow-thumbs', Y.Widget, [Y.WidgetParent, Y.WidgetChild], { 4230 4231 /** 4232 * A div node used to hide the overflow when 4233 * transitioning between pages. 4234 * 4235 * @property _clipBox 4236 * @type Object 4237 * @default null 4238 * @protected 4239 */ 4240 _clipBox: null, 4241 4242 /** 4243 * A div node used to hold the pages. 4244 * 4245 * @property _pagesBox 4246 * @type Object 4247 * @default null 4248 * @protected 4249 */ 4250 _pagesBox: null, 4251 4252 /** 4253 * A reference to the active page div node. Holds a grid 4254 * of FL.SlideshowImage instances. 4255 * 4256 * @property _activePageBox 4257 * @type Object 4258 * @default null 4259 * @protected 4260 */ 4261 _activePageBox: null, 4262 4263 /** 4264 * The index of the active page of thumbs. 4265 * 4266 * @property _activePageIndex 4267 * @type Number 4268 * @default 0 4269 * @protected 4270 */ 4271 _activePageIndex: 0, 4272 4273 /** 4274 * A reference to the next page div node. Holds a grid 4275 * of FL.SlideshowImage instances. 4276 * 4277 * @property _nextPageBox 4278 * @type Object 4279 * @default null 4280 * @protected 4281 */ 4282 _nextPageBox: null, 4283 4284 /** 4285 * An array of FL.SlideshowImage instances in the active page. 4286 * 4287 * @property _activeImages 4288 * @type Array 4289 * @default null 4290 * @protected 4291 */ 4292 _activeImages: null, 4293 4294 /** 4295 * An array of FL.SlideshowImage instances used to 4296 * preload the next page of images. 4297 * 4298 * @property _nextImages 4299 * @type Array 4300 * @default null 4301 * @protected 4302 */ 4303 _nextImages: null, 4304 4305 /** 4306 * An array of FL.SlideshowImage instances used to 4307 * preload the previous page of images. 4308 * 4309 * @property _prevImages 4310 * @type Array 4311 * @default null 4312 * @protected 4313 */ 4314 _prevImages: null, 4315 4316 /** 4317 * An instance of FL.SlideshowNav used for the left nav. 4318 * 4319 * @property _leftNav 4320 * @type Object 4321 * @default null 4322 * @protected 4323 */ 4324 _leftNav: null, 4325 4326 /** 4327 * An instance of FL.SlideshowNav used for the right nav. 4328 * 4329 * @property _rightNav 4330 * @type Object 4331 * @default null 4332 * @protected 4333 */ 4334 _rightNav: null, 4335 4336 /** 4337 * An instance of FL.SlideshowNav used for the top nav. 4338 * 4339 * @property _topNav 4340 * @type Object 4341 * @default null 4342 * @protected 4343 */ 4344 _topNav: null, 4345 4346 /** 4347 * An instance of FL.SlideshowNav used for the bottom nav. 4348 * 4349 * @property _bottomNav 4350 * @type Object 4351 * @default null 4352 * @protected 4353 */ 4354 _bottomNav: null, 4355 4356 /** 4357 * Height of the bounding box. 4358 * 4359 * @property _bbHeight 4360 * @type Number 4361 * @default 0 4362 * @protected 4363 */ 4364 _bbHeight: 0, 4365 4366 /** 4367 * Width of the bounding box. 4368 * 4369 * @property _bbWidth 4370 * @type Number 4371 * @default 0 4372 * @protected 4373 */ 4374 _bbWidth: 0, 4375 4376 /** 4377 * Width of the content box. 4378 * 4379 * @property _cbWidth 4380 * @type Number 4381 * @default 0 4382 * @protected 4383 */ 4384 _cbWidth: 0, 4385 4386 /** 4387 * Left margin of the clip box. 4388 * 4389 * @property _clipBoxMarginLeft 4390 * @type Number 4391 * @default 0 4392 * @protected 4393 */ 4394 _clipBoxMarginLeft: 0, 4395 4396 /** 4397 * Top position of the clip box. 4398 * 4399 * @property _clipBoxTop 4400 * @type Number 4401 * @default 0 4402 * @protected 4403 */ 4404 _clipBoxTop: 0, 4405 4406 /** 4407 * The number of columns per page. 4408 * 4409 * @property _colsPerPage 4410 * @type Number 4411 * @default 0 4412 * @protected 4413 */ 4414 _colsPerPage: 0, 4415 4416 /** 4417 * The number of rows per page. 4418 * 4419 * @property _rowsPerPage 4420 * @type Number 4421 * @default 0 4422 * @protected 4423 */ 4424 _rowsPerPage: 0, 4425 4426 /** 4427 * The number of images per page. 4428 * 4429 * @property _imagesPerPage 4430 * @type Number 4431 * @default 0 4432 * @protected 4433 */ 4434 _imagesPerPage: 0, 4435 4436 /** 4437 * The number of pages. 4438 * 4439 * @property _numPages 4440 * @type Number 4441 * @default 0 4442 * @protected 4443 */ 4444 _numPages: 0, 4445 4446 /** 4447 * Height of the pages. 4448 * 4449 * @property _pageHeight 4450 * @type Number 4451 * @default 0 4452 * @protected 4453 */ 4454 _pageHeight: 0, 4455 4456 /** 4457 * Width of the pages. 4458 * 4459 * @property _pageWidth 4460 * @type Number 4461 * @default 0 4462 * @protected 4463 */ 4464 _pageWidth: 0, 4465 4466 /** 4467 * The horizontal spacing between thumbs. 4468 * 4469 * @property _horizontalSpacing 4470 * @type Number 4471 * @default 0 4472 * @protected 4473 */ 4474 _horizontalSpacing: 0, 4475 4476 /** 4477 * The vertical spacing between thumbs. 4478 * 4479 * @property _verticalSpacing 4480 * @type Number 4481 * @default 0 4482 * @protected 4483 */ 4484 _verticalSpacing: 0, 4485 4486 /** 4487 * Width of the left nav. 4488 * 4489 * @property _leftNavWidth 4490 * @type Number 4491 * @default 0 4492 * @protected 4493 */ 4494 _leftNavWidth: 0, 4495 4496 /** 4497 * Width of the right nav. 4498 * 4499 * @property _rightNavWidth 4500 * @type Number 4501 * @default 0 4502 * @protected 4503 */ 4504 _rightNavWidth: 0, 4505 4506 /** 4507 * An instance of FL.SlideshowTransition for the current transition. 4508 * 4509 * @property _transition 4510 * @type FL.SlideshowTransition 4511 * @default null 4512 * @protected 4513 */ 4514 _transition: null, 4515 4516 /** 4517 * Whether the pages are currently transitioning or not. 4518 * 4519 * @property _verticalSpacing 4520 * @type Boolean 4521 * @default false 4522 * @protected 4523 */ 4524 _transitioning: false, 4525 4526 /** 4527 * Direction of the current transition. 4528 * 4529 * @property _transitionDirection
4530 * @type String 4531 * @default next 4532 * @protected 4533 */ 4534 _transitionDirection: 'next', 4535 4536 /** 4537 * Provides functionality for gesture based transitions 4538 * between the active and next pages. 4539 * 4540 * @property _gestures 4541 * @type FL.SlideshowGestures 4542 * @default null 4543 * @protected 4544 */ 4545 _gestures: null, 4546 4547 /** 4548 * Initialize image vars. 4549 * 4550 * @method initializer 4551 * @protected 4552 */ 4553 initializer: function() 4554 { 4555 this._activeImages = []; 4556 this._nextImages = []; 4557 this._prevImages = []; 4558 }, 4559 4560 /** 4561 * Renders the UI boxes. 4562 * 4563 * @method renderUI 4564 * @protected 4565 */ 4566 renderUI: function() 4567 { 4568 this._renderBoxes(); 4569 this._renderNavs(); 4570 }, 4571 4572 /** 4573 * Binds the UI events. 4574 * 4575 * @method bindUI 4576 * @protected 4577 */ 4578 bindUI: function() 4579 { 4580 var root = this.get('root'), 4581 id = this.get('id'), 4582 transition = this.get('transition'); 4583 4584 root.on(id + '|albumLoadComplete', this._albumLoadComplete, this); 4585 4586 if('ontouchstart' in window && this.get('touchSupport')) { 4587 4588 this._gestures = new Y.FL.SlideshowGestures({ 4589 direction: transition == 'slideVertical' ? 'vertical' : 'horizontal', 4590 activeItem: this._activePageBox, 4591 nextItem: this._nextPageBox 4592 }); 4593 4594 this._gestures.on('moveStart', this._gesturesMoveStart, this); 4595 this._gestures.on('endComplete', this._gesturesEndComplete, this); 4596 } 4597 }, 4598 4599 /** 4600 * Syncs the UI boxes. 4601 * 4602 * @method syncUI 4603 * @protected 4604 */ 4605 syncUI: function() 4606 { 4607 this._syncBoxes(); 4608 this._syncNavs(); 4609 }, 4610 4611 /** 4612 * @method destructor 4613 * @protected 4614 */ 4615 destructor: function() 4616 { 4617 var root = this.get('root'), 4618 id = this.get('id'); 4619 4620 root.detach(id + '|*'); 4621 4622 Y.FL.SlideshowImageLoader.removeGroup('thumbs'); 4623 }, 4624 4625 /** 4626 * Unload all images. 4627 * 4628 * @method unload 4629 */ 4630 unload: function() 4631 { 4632 var root = this.get('root'), 4633 id = this.get('id'), 4634 i = 0; 4635 4636 root.detach(id + '|imageLoadComplete'); 4637 4638 Y.FL.SlideshowImageLoader.removeGroup('thumbs'); 4639 4640 for( ; i < this._activeImages.length; i++) { 4641 this._activeImages[i].unload(); 4642 } 4643 }, 4644 4645 /** 4646 * Resizes the UI boxes. 4647 * 4648 * @method resize 4649 */ 4650 resize: function() 4651 { 4652 this._setSizeInfo(); 4653 this._togglePageButtons(); 4654 this._resizeBoxes(); 4655 this._resizeNavs(); 4656 4657 if(this.get('root').albumInfo) { 4658 Y.FL.SlideshowImageLoader.removeGroup('thumbs'); 4659 this._renderActivePage(); 4660 this._preloadNextPage(); 4661 this._preloadPrevPage(); 4662 } 4663 4664 // Enable or disable gestures. 4665 if(this._gestures && this._numPages < 2) { 4666 this._gestures.disable(); 4667 } 4668 else if(this._gestures) { 4669 this._gestures.enable(); 4670 } 4671 }, 4672 4673 /** 4674 * Transitions to the previous page. 4675 * 4676 * @method prevPage 4677 * @protected 4678 */ 4679 prevPage: function() 4680 { 4681 if(this._transitioning) { 4682 return; 4683 } 4684 4685 this._transitionStart('prev'); 4686 }, 4687 4688 /** 4689 * Transitions to the next page. 4690 * 4691 * @method nextPage 4692 * @protected 4693 */ 4694 nextPage: function() 4695 { 4696 if(this._transitioning) { 4697 return; 4698 } 4699 4700 this._transitionStart('next'); 4701 }, 4702 4703 /** 4704 * Called when an album is loaded into the root slideshow widget. 4705 * 4706 * @method _albumLoadComplete 4707 * @protected 4708 */ 4709 _albumLoadComplete: function() 4710 { 4711 var root = this.get('root'), 4712 id = this.get('id'); 4713 4714 root.once(id + '|imageLoadComplete', this.resize, this); 4715 root.on(id + '|imageLoadComplete', this._imageLoadComplete, this); 4716 }, 4717 4718 /** 4719 * Called when an image is loaded into the root slideshow widget. 4720 * 4721 * @method _imageLoadComplete 4722 * @protected 4723 */ 4724 _imageLoadComplete: function() 4725 { 4726 var albumInfo = this.get('root').albumInfo, 4727 lastActive = Y.one('.fl-slideshow-image-active'), 4728 lastInfo = lastActive ? lastActive._imageInfo : null, 4729 nextActive = null, 4730 nextInfo = this.get('root').imageInfo; 4731 4732 this._setActiveImage(this._activeImages); 4733 4734 nextActive = Y.one('.fl-slideshow-image-active'); 4735 4736 if(lastActive && !nextActive) { 4737 if(nextInfo.index === 0 && lastInfo.index === albumInfo.images.length - 1) { 4738 this.nextPage(); 4739 } 4740 else if(lastInfo.index === 0 && nextInfo.index === albumInfo.images.length - 1) { 4741 this.prevPage(); 4742 } 4743 else if(lastInfo.index < nextInfo.index) { 4744 this.nextPage(); 4745 } 4746 else if(lastInfo.index > nextInfo.index) { 4747 this.prevPage(); 4748 } 4749 } 4750 }, 4751 4752 /** 4753 * Renders the boxes. 4754 * 4755 * @method _renderBoxes 4756 * @protected 4757 */ 4758 _renderBoxes: function() 4759 { 4760 // Clip box 4761 this._clipBox = Y.Node.create('<div></div>'); 4762 this._clipBox.addClass('fl-slideshow-thumbs-clip'); 4763 this.get('contentBox').insert(this._clipBox); 4764 4765 // Pages box 4766 this._pagesBox = Y.Node.create('<div></div>'); 4767 this._pagesBox.addClass('fl-slideshow-thumbs-pages'); 4768 this._clipBox.insert(this._pagesBox); 4769 4770 // Active page box 4771 this._activePageBox = Y.Node.create('<div></div>'); 4772 this._activePageBox.addClass('fl-slideshow-thumbs-page'); 4773 this._pagesBox.insert(this._activePageBox); 4774 4775 // Next page box 4776 this._nextPageBox = Y.Node.create('<div></div>'); 4777 this._nextPageBox.addClass('fl-slideshow-thumbs-page'); 4778 this._pagesBox.insert(this._nextPageBox); 4779 }, 4780 4781 /** 4782 * Syncs the boxes. 4783 * 4784 * @method _syncBoxes 4785 * @protected 4786 */ 4787 _syncBoxes: function() 4788 { 4789 // Active page box 4790 this._activePageBox.setStyle('left', '0'); 4791 4792 // Next page box 4793 this._nextPageBox.setStyle('left', '-9999px'); 4794 }, 4795 4796 /** 4797 * Resizes the boxes. 4798 * 4799 * @method _resizeBoxes 4800 * @protected 4801 */ 4802 _resizeBoxes: function() 4803 { 4804 this.set('width', this._bbWidth); 4805 this.set('height', this._bbHeight); 4806 4807 this.get('contentBox').setStyle('width', this._cbWidth + 'px'); 4808 4809 this._clipBox.setStyle('width', this._pageWidth + 'px'); 4810 this._clipBox.setStyle('height', this._pageHeight + 'px'); 4811 this._clipBox.setStyle('padding', this._verticalSpacing + 'px 0 0 ' + this._horizontalSpacing + 'px '); 4812 this._clipBox.setStyle('margin', '0 0 0 ' + this._clipBoxMarginLeft + 'px'); 4813 this._clipBox.setStyle('top', this._clipBoxTop); 4814 4815 this._pagesBox.setStyle('width', this._pageWidth + 'px'); 4816 this._pagesBox.setStyle('height', this._pageHeight + 'px'); 4817 4818 this._activePageBox.setStyle('width', this._pageWidth + 'px'); 4819 this._activePageBox.setStyle('height', this._pageHeight + 'px'); 4820 4821 this._nextPageBox.setStyle('width', this._pageWidth + 'px'); 4822 this._nextPageBox.setStyle('height', this._pageHeight + 'px'); 4823 }, 4824 4825 /** 4826 * Renders the active page of images. 4827 * 4828 * @method _renderActivePage 4829 * @protected 4830 */ 4831 _renderActivePage: function() 4832 { 4833 var i = 0, 4834 root = this.get('root'), 4835 imageIndex = this._imagesPerPage * this._activePageIndex, 4836 endIndex = imageIndex + this._imagesPerPage, 4837 images = root.albumInfo.images; 4838 4839 this._clearActiveImage(); 4840 4841 // Remove current images 4842 for( ; i < this._activeImages.length; i++) { 4843 this._activeImages[i].remove(); 4844 this._activeImages[i].unload(); 4845 this._activeImages[i].get('boundingBox')._imageInfo = null; 4846 this._activeImages[i].get('boundingBox').remove(); 4847 } 4848 4849 // Draw images 4850 for(i = 0; imageIndex < endIndex; imageIndex++) { 4851 4852 if(!images[imageIndex]) { 4853 break; 4854 } 4855 4856 this._renderImage(this._activeImages, i, this._activePageBox, images[imageIndex]); 4857 i++; 4858 } 4859 4860 this._setActiveImage(this._activeImages); 4861 }, 4862 4863 /** 4864 * Renders the next page of images. 4865 * 4866 * @method _renderNextPage 4867 * @protected 4868 */ 4869 _renderNextPage: function() 4870 { 4871 var i = 0, 4872 imageArray = this._transitionDirection == 'next' ? this._nextImages : this._prevImages; 4873 4874 this._nextPageBox.get('children').remove(); 4875 4876 for( ; i < imageArray.length; i++) { 4877 if(imageArray[i]._imageInfo) { 4878 this._renderImage(imageArray, i, this._nextPageBox, imageArray[i]._imageInfo); 4879 } 4880 else { 4881 break; 4882 } 4883 } 4884 4885 this._setActiveImage(imageArray); 4886 }, 4887 4888 /** 4889 * Preloads the next page of images. 4890 * 4891 * @method _preloadNextPage 4892 * @protected 4893 */ 4894 _preloadNextPage: function() 4895 { 4896 var pageIndex = this._activePageIndex + 1 >= this._numPages ? 0 : this._activePageIndex + 1; 4897 4898 this._preloadPage(pageIndex, this._nextImages); 4899 }, 4900 4901 /** 4902 * Preloads the previous page of images. 4903 * 4904 * @method _preloadPrevPage 4905 * @protected 4906 */ 4907 _preloadPrevPage: function() 4908 { 4909 var pageIndex = this._activePageIndex - 1 < 0 ? this._numPages - 1 : this._activePageIndex - 1; 4910 4911 this._preloadPage(pageIndex, this._prevImages); 4912 }, 4913 4914 /** 4915 * Preloads a page of images. 4916 * 4917 * @method _preloadPage 4918 * @param imageIndex {Number} The image index to start preloading from. 4919 * @param imageArray {Array} The array to store the preloaded images. 4920 * @protected 4921 */ 4922 _preloadPage: function(pageIndex, imageArray) 4923 { 4924 var i = 0, 4925 root = this.get('root'), 4926 images = root.albumInfo.images, 4927 imageIndex = pageIndex * this._imagesPerPage, 4928 endIndex = imageIndex + this._imagesPerPage, 4929 imageConfig = this.get('imageConfig'), 4930 width = imageConfig.width, 4931 height = imageConfig.height; 4932 4933 if(this._numPages > 1) { 4934 4935 // Unload existing images 4936 for( ; i < imageArray.length; i++) { 4937 imageArray[i].remove(); 4938 imageArray[i].unload(); 4939 } 4940 4941 // Preload the images 4942 for(i = 0; imageIndex < endIndex; imageIndex++) { 4943 4944 if(!images[imageIndex]) { 4945 continue; 4946 } 4947 4948 this._renderImage(imageArray, i); 4949 imageArray[i].preload(images[imageIndex], width, height); 4950 i++; 4951 } 4952 } 4953 }, 4954 4955 /** 4956 * Renders an image. 4957 * 4958 * @method _renderImage 4959 * @protected 4960 */ 4961 _renderImage: function(imageArray, i, page, imageInfo) 4962 { 4963 var imageBB = null, 4964 imageConfig = this.get('imageConfig'); 4965 4966 // Create the image? 4967 if(typeof imageArray[i] == 'undefined') { 4968 imageConfig.loadGroup = 'thumbs'; 4969 imageConfig.useThumbSizes = true; 4970 imageConfig.loadVideos = false;
4971 imageArray[i] = new Y.FL.SlideshowImage(imageConfig); 4972 imageBB = imageArray[i].get('boundingBox'); 4973 imageBB.on('click', this._imageClick, this); 4974 imageBB.on('mouseover', this._imageMouseover, this); 4975 imageBB.on('mouseout', this._imageMouseout, this); 4976 } 4977 4978 // Image bounding box 4979 imageBB = imageArray[i].get('boundingBox'); 4980 imageBB.setStyle('margin', '0 ' + this._horizontalSpacing + 'px ' + this._verticalSpacing + 'px 0'); 4981 4982 // Add the image to a page? 4983 if(page) { 4984 this._childrenContainer = page; 4985 this.add(imageArray[i]); 4986 imageArray[i].resize(imageConfig.width, imageConfig.height); 4987 } 4988 4989 // Load the image? 4990 if(imageInfo) { 4991 imageArray[i].load(imageInfo); 4992 imageBB._imageInfo = imageInfo; 4993 } 4994 }, 4995 4996 /** 4997 * Overrides the WidgetParent _uiAddChild method so _renderImage 4998 * will render to the appropriate page. 4999 * 5000 * @method _uiAddChild 5001 * @protected 5002 * @param child {Widget} The child Widget instance to render. 5003 * @param parentNode {Object} The Node under which the 5004 * child Widget is to be rendered. Set to the appropriate page 5005 * in the _renderImage method by setting _childrenContainer. 5006 */ 5007 _uiAddChild: function (child, parentNode) 5008 { 5009 child.render(parentNode); 5010 parentNode.appendChild(child.get('boundingBox')); 5011 }, 5012 5013 /** 5014 * Called when an image is clicked. 5015 * 5016 * @method _imageClick 5017 * @protected 5018 */ 5019 _imageClick: function(e) 5020 { 5021 var root = this.get('root'); 5022 5023 if(this.get('pauseOnClick')) { 5024 root.pause(); 5025 } 5026 5027 root.loadImage(e.currentTarget._imageInfo.index); 5028 5029 /** 5030 * Fires when an image is clicked. 5031 * 5032 * @event imageClick 5033 */ 5034 this.fire('imageClick'); 5035 }, 5036 5037 /** 5038 * Sets the active image. 5039 * 5040 * @method _setActiveImage 5041 * @param imageArray {Array} The image array to check for the active image. 5042 * @protected 5043 */ 5044 _setActiveImage: function(imageArray) 5045 { 5046 var i = 0; 5047 5048 this._clearActiveImage(); 5049 5050 for( ; i < imageArray.length; i++) { 5051 if(imageArray[i]._imageInfo) { 5052 if(imageArray[i]._imageInfo.index == this.get('root').imageInfo.index) { 5053 imageArray[i].get('boundingBox').addClass('fl-slideshow-image-active'); 5054 break; 5055 } 5056 } 5057 } 5058 }, 5059 5060 /** 5061 * Removes the class name 'fl-slideshow-image-active' 5062 * from the active image. 5063 * 5064 * @method _clearActiveImage 5065 * @protected 5066 */ 5067 _clearActiveImage: function() 5068 { 5069 var active = Y.one('.fl-slideshow-image-active'); 5070 5071 if(active) { 5072 active.removeClass('fl-slideshow-image-active'); 5073 } 5074 }, 5075 5076 /** 5077 * Gets the transition type. 5078 * 5079 * @method _getTransition 5080 * @protected 5081 */ 5082 _getTransition: function() 5083 { 5084 var transition = this.get('transition'); 5085 5086 if(transition == 'slideHorizontal' && this._transitionDirection == 'next') { 5087 return 'slideLeft'; 5088 } 5089 else if(transition == 'slideHorizontal' && this._transitionDirection == 'prev') { 5090 return 'slideRight'; 5091 } 5092 else if(transition == 'slideVertical' && this._transitionDirection == 'next') { 5093 return 'slideUp'; 5094 } 5095 else if(transition == 'slideVertical' && this._transitionDirection == 'prev') { 5096 return 'slideDown'; 5097 } 5098 5099 return transition; 5100 }, 5101 5102 /** 5103 * Starts the transition, moving in the provided direction. 5104 * 5105 * @method _transitionStart 5106 * @param direction {String} The direction to transition. 5107 * @protected 5108 */ 5109 _transitionStart: function(direction) 5110 { 5111 if(this._numPages > 1) { 5112 5113 Y.FL.SlideshowImageLoader.removeGroup('thumbs'); 5114 5115 this._transitionDirection = direction; 5116 this._transitioning = true; 5117 this._nextPageBox.setStyle('left', '0px'); 5118 this._renderNextPage(); 5119 5120 this._transition = new Y.FL.SlideshowTransition({ 5121 itemIn: this._nextPageBox, 5122 itemOut: this._activePageBox, 5123 type: this._getTransition(), 5124 duration: this.get('transitionDuration'), 5125 easing: this.get('transitionEasing') 5126 }); 5127 5128 this._transition.once('complete', this._transitionComplete, this); 5129 this._transition.run(); 5130 5131 // Disable gestures if set. 5132 if(this._gestures) { 5133 this._gestures.disable(); 5134 } 5135 } 5136 }, 5137 5138 /** 5139 * Transition cleanup called when the current transition ends. 5140 * 5141 * @method _transitionComplete 5142 * @protected 5143 */ 5144 _transitionComplete: function() 5145 { 5146 this._swapPageRefs(); 5147 this._transitioning = false;
5148 this._transitionDirection = ''; 5149 this._transition = null; 5150 5151 // Enable gestures if set. 5152 if(this._gestures) { 5153 this._gestures.enable(); 5154 } 5155 5156 /** 5157 * Fires when a page transition completes. 5158 * 5159 * @event transitionComplete 5160 */ 5161 this.fire('transitionComplete'); 5162 }, 5163 5164 /** 5165 * @method _gesturesMoveStart 5166 * @param e {Object} The event object. 5167 * @protected 5168 */ 5169 _gesturesMoveStart: function(e) 5170 { 5171 Y.FL.SlideshowImageLoader.removeGroup('thumbs'); 5172 5173 this._transitionDirection = e.direction; 5174 this._renderNextPage(); 5175 }, 5176 5177 /** 5178 * @method _gesturesEndComplete 5179 * @protected 5180 */ 5181 _gesturesEndComplete: function() 5182 { 5183 this._swapPageRefs(); 5184 this._transitionDirection = ''; 5185 this.fire('transitionComplete'); 5186 }, 5187 5188 /** 5189 * Swaps the active page and next page references when 5190 * a transition completes and sets the active page index. 5191 * 5192 * @method _swapPageRefs 5193 * @protected 5194 */ 5195 _swapPageRefs: function() 5196 { 5197 var lastBox = this._activePageBox, 5198 lastImages = this._activeImages; 5199 5200 this._activePageBox = this._nextPageBox; 5201 this._nextPageBox = lastBox; 5202 this._nextPageBox.setStyle('left', '-9999px'); 5203 5204 if(this._transitionDirection == 'next') { 5205 this._activeImages = this._nextImages; 5206 this._nextImages = lastImages; 5207 } 5208 else { 5209 this._activeImages = this._prevImages; 5210 this._prevImages = lastImages; 5211 } 5212 5213 // Active page index 5214 if(this._transitionDirection == 'next' && this._activePageIndex + 1 < this._numPages) { 5215 this._activePageIndex++; 5216 } 5217 else if(this._transitionDirection == 'next') { 5218 this._activePageIndex = 0; 5219 } 5220 else if(this._transitionDirection == 'prev' && this._activePageIndex - 1 > -1) { 5221 this._activePageIndex--; 5222 } 5223 else if(this._transitionDirection == 'prev') { 5224 this._activePageIndex = this._numPages - 1; 5225 } 5226 5227 // Swap gesture refs 5228 if(this._gestures) { 5229 this._gestures.set('activeItem', this._activePageBox); 5230 this._gestures.set('nextItem', this._nextPageBox); 5231 } 5232 5233 this._preloadNextPage(); 5234 this._preloadPrevPage(); 5235 }, 5236 5237 /** 5238 * Renders the enabled navs. 5239 * 5240 * @method _renderNavs 5241 * @protected 5242 */ 5243 _renderNavs: function() 5244 { 5245 var topNavButtons = this.get('topNavButtons'), 5246 rightNavButtons = this.get('rightNavButtons'), 5247 bottomNavButtons = this.get('bottomNavButtons'), 5248 leftNavButtons = this.get('leftNavButtons'); 5249 5250 if(this.get('topNavEnabled') && topNavButtons.length > 0) { 5251 this._topNav = new Y.FL.SlideshowNav({ buttons: topNavButtons }); 5252 this._topNav.get('boundingBox').addClass('fl-slideshow-thumbs-top-nav'); 5253 this.add(this._topNav); 5254 this._topNav.render(this.get('contentBox')); 5255 this._clipBox.insert(this._topNav.get('boundingBox'), 'before'); 5256 this._bindNavEvents(this._topNav); 5257 } 5258 if(this.get('rightNavEnabled') && rightNavButtons.length > 0) { 5259 this._rightNav = new Y.FL.SlideshowNav({ buttons: rightNavButtons }); 5260 this._rightNav.get('boundingBox').addClass('fl-slideshow-thumbs-right-nav'); 5261 this.add(this._rightNav); 5262 this._rightNav.render(this.get('contentBox')); 5263 this._bindNavEvents(this._rightNav); 5264 } 5265 if(this.get('bottomNavEnabled') && bottomNavButtons.length > 0) { 5266 this._bottomNav = new Y.FL.SlideshowNav({ buttons: bottomNavButtons }); 5267 this._bottomNav.get('boundingBox').addClass('fl-slideshow-thumbs-bottom-nav'); 5268 this.add(this._bottomNav); 5269 this._bottomNav.render(this.get('contentBox')); 5270 this._bindNavEvents(this._bottomNav); 5271 } 5272 if(this.get('leftNavEnabled') && leftNavButtons.length > 0) { 5273 this._leftNav = new Y.FL.SlideshowNav({ buttons: leftNavButtons }); 5274 this._leftNav.get('boundingBox').addClass('fl-slideshow-thumbs-left-nav'); 5275 this.add(this._leftNav); 5276 this._leftNav.render(this.get('contentBox')); 5277 this._bindNavEvents(this._leftNav); 5278 } 5279 }, 5280 5281 /** 5282 * Syncs the navs. 5283 * 5284 * @method _syncNavs 5285 * @protected 5286 */ 5287 _syncNavs: function() 5288 { 5289 var rightNavBB, bottomNavBB, leftNavBB; 5290 5291 if(this._rightNav) { 5292 rightNavBB = this._rightNav.get('boundingBox'); 5293 rightNavBB.setStyle('position', 'absolute'); 5294 rightNavBB.setStyle('top', '0px'); 5295 rightNavBB.setStyle('right', '0px'); 5296 } 5297 if(this._bottomNav) { 5298 bottomNavBB = this._bottomNav.get('boundingBox'); 5299 bottomNavBB.setStyle('position', 'absolute'); 5300 bottomNavBB.setStyle('bottom', '0px'); 5301 bottomNavBB.setStyle('width', '100%'); 5302 } 5303 if(this._leftNav) { 5304 leftNavBB = this._leftNav.get('boundingBox'); 5305 leftNavBB.setStyle('position', 'absolute'); 5306 leftNavBB.setStyle('top', '0px'); 5307 leftNavBB.setStyle('left', '0px'); 5308 } 5309 }, 5310 5311 /** 5312 * Resizes the navs. 5313 * 5314 * @method _resizeNavs 5315 * @protected 5316 */ 5317 _resizeNavs: function() 5318 { 5319 var rightNavBB, 5320 leftNavBB, 5321 marginTop; 5322 5323 if(this._rightNav) { 5324 rightNavBB = this._rightNav.get('boundingBox'); 5325 marginTop = this._bbHeight/2 - parseInt(rightNavBB.getComputedStyle('height'), 10)/2; 5326 rightNavBB.setStyle('marginTop', marginTop + 'px'); 5327 } 5328 if(this._leftNav) { 5329 leftNavBB = this._leftNav.get('boundingBox'); 5330 marginTop = this._bbHeight/2 - parseInt(leftNavBB.getComputedStyle('height'), 10)/2; 5331 leftNavBB.setStyle('marginTop', marginTop + 'px'); 5332 } 5333 }, 5334 5335 /** 5336 * Binds events to the provided nav. 5337 * 5338 * @method _bindNavEvents 5339 * @param nav {Object} The nav to bind to. 5340 * @protected 5341 */ 5342 _bindNavEvents: function(nav) 5343 { 5344 if(nav._buttons.prevPage) { 5345 nav._buttons.prevPage.on('click', this.prevPage, this); 5346 } 5347 if(nav._buttons.nextPage) { 5348 nav._buttons.nextPage.on('click', this.nextPage, this); 5349 } 5350 5351 nav.on('resize', this.resize, this); 5352 }, 5353 5354 /** 5355 * Hides the prev page and next page buttons 5356 * if there is only one page of thumbs. 5357 * 5358 * @method _togglePageButtons 5359 * @protected 5360 */ 5361 _togglePageButtons: function() 5362 { 5363 var buttons = this.get('boundingBox').all('.fl-slideshow-nav-prevPage, .fl-slideshow-nav-nextPage'), 5364 display = buttons.getStyle('display')[0]; 5365 5366 if(this._numPages == 1 && display == 'inline-block') { 5367 buttons.setStyle('display', 'none'); 5368 this._setSizeInfo(); 5369 } 5370 else if(this._numPages > 1 && display == 'none') { 5371 buttons.setStyle('display', 'inline-block'); 5372 this._setSizeInfo(); 5373 } 5374 }, 5375 5376 /** 5377 * Sets the size info used when resizing and loading pages. 5378 * 5379 * @method _setSizeInfo 5380 * @protected 5381 */ 5382 _setSizeInfo: function() 5383 { 5384 var root = this.get('root'), 5385 bb = this.get('boundingBox'), 5386 bbPosition = bb.getStyle('position'), 5387 bbLeftMargin = parseInt(bb.getStyle('marginLeft'), 10), 5388 bbRightMargin = parseInt(bb.getStyle('marginRight'), 10), 5389 bbTopMargin = parseInt(bb.getStyle('marginTop'), 10), 5390 bbBottomMargin = parseInt(bb.getStyle('marginBottom'), 10), 5391 bbLeftPadding = parseInt(bb.getStyle('paddingLeft'), 10), 5392 bbRightPadding = parseInt(bb.getStyle('paddingRight'), 10), 5393 bbTopPadding = parseInt(bb.getStyle('paddingTop'), 10), 5394 bbBottomPadding = parseInt(bb.getStyle('paddingBottom'), 10), 5395 parent = bb.get('parentNode'), 5396 parentWidth = parseInt(parent.getComputedStyle('width'), 10), 5397 parentHeight = parseInt(parent.getComputedStyle('height'), 10), 5398 bbWidth = parentWidth - bbLeftPadding - bbRightPadding - bbLeftMargin - bbRightMargin, 5399 bbHeight = parentHeight - bbTopPadding - bbBottomPadding - bbTopMargin - bbBottomMargin, 5400 cbWidth = bbWidth, 5401 pageWidth = bbWidth, 5402 pageHeight = bbHeight, 5403 columns = this.get('columns'), 5404 rows = this.get('rows'), 5405 imageConfig = this.get('imageConfig'), 5406 horizontalSpacing = this.get('horizontalSpacing'), 5407 verticalSpacing = this.get('verticalSpacing'), 5408 spaceEvenly = this.get('spaceEvenly'), 5409 centerSinglePage = this.get('centerSinglePage'), 5410 leftNavWidth = 0, 5411 rightNavWidth = 0, 5412 topNavHeight = 0, 5413 bottomNavHeight = 0, 5414 colsPerPage = columns, 5415 rowsPerPage = rows, 5416 imagesPerPage = 0, 5417 numPages = 1, 5418 clipBoxMarginLeft = 0, 5419 clipBoxTop = 0, 5420 availHorizSpace = 0, 5421 availVerticalSpace = 0; 5422 5423 // Position absolute causes some resizing bugs. 5424 bb.setStyle('position', 'relative'); 5425 5426 // Bounding box width 5427 if(!isNaN(columns)) { 5428 bbWidth = pageWidth = columns * (imageConfig.width + horizontalSpacing) + horizontalSpacing; 5429 } 5430
5431 // Bounding box height 5432 if(!isNaN(rows)) { 5433 bbHeight = pageHeight = rows * (imageConfig.height + verticalSpacing) + verticalSpacing; 5434 } 5435 5436 // Compensate for the navs 5437 if(this._leftNav) { 5438 5439 leftNavWidth = parseInt(this._leftNav.get('boundingBox').getComputedStyle('width'), 10); 5440 5441 if(isNaN(columns)) { 5442 pageWidth -= leftNavWidth; 5443 } 5444 else { 5445 bbWidth += leftNavWidth; 5446 } 5447 } 5448 if(this._rightNav) { 5449 5450 rightNavWidth = parseInt(this._rightNav.get('boundingBox').getComputedStyle('width'), 10); 5451 5452 if(isNaN(columns)) { 5453 pageWidth -= rightNavWidth; 5454 } 5455 else { 5456 bbWidth += rightNavWidth; 5457 } 5458 } 5459 if(this._topNav) { 5460 5461 topNavHeight = parseInt(this._topNav.get('boundingBox').getComputedStyle('height'), 10); 5462 5463 if(isNaN(rows)) { 5464 pageHeight -= topNavHeight; 5465 } 5466 else { 5467 bbHeight += topNavHeight; 5468 } 5469 } 5470 if(this._bottomNav) { 5471 5472 bottomNavHeight = parseInt(this._bottomNav.get('boundingBox').getComputedStyle('height'), 10); 5473 5474 if(isNaN(rows)) { 5475 pageHeight -= bottomNavHeight; 5476 } 5477 else { 5478 bbHeight += bottomNavHeight; 5479 } 5480 } 5481 5482 // Columns per page 5483 if(isNaN(columns)) { 5484 colsPerPage = Math.floor(pageWidth/(imageConfig.width + horizontalSpacing)); 5485 colsPerPage = colsPerPage < 1 ? 1 : colsPerPage; 5486 } 5487 5488 // Rows per page 5489 if(isNaN(rows)) { 5490 rowsPerPage = Math.floor(pageHeight/(imageConfig.height + verticalSpacing)); 5491 rowsPerPage = rowsPerPage < 1 ? 1 : rowsPerPage; 5492 } 5493 5494 // Images per page 5495 imagesPerPage = colsPerPage * rowsPerPage; 5496 5497 // Number of pages 5498 if(root.albumInfo) { 5499 numPages = Math.ceil(root.albumInfo.images.length/imagesPerPage); 5500 } 5501 5502 // Horizontal spacing 5503 if(isNaN(columns) && spaceEvenly) { 5504 horizontalSpacing = Math.floor((pageWidth - (imageConfig.width * colsPerPage))/(colsPerPage + 1)); 5505 } 5506 5507 // Vertical spacing 5508 if(isNaN(rows) && spaceEvenly) { 5509 verticalSpacing = Math.floor((pageHeight - (imageConfig.height * rowsPerPage))/(rowsPerPage + 1)); 5510 } 5511 5512 // Content container width 5513 if(root.albumInfo && centerSinglePage && numPages == 1 && rowsPerPage == 1) { 5514 5515 cbWidth = root.albumInfo.images.length * imageConfig.width; 5516 cbWidth += horizontalSpacing * (root.albumInfo.images.length + 1); 5517 5518 if(this._leftNav) { 5519 cbWidth += leftNavWidth; 5520 } 5521 if(this._rightNav) { 5522 cbWidth += rightNavWidth; 5523 } 5524 } 5525 else { 5526 cbWidth = bbWidth; 5527 } 5528 5529 // Final page width and height 5530 if(root.albumInfo && centerSinglePage && numPages == 1 && rowsPerPage == 1) { 5531 pageWidth = root.albumInfo.images.length * imageConfig.width; 5532 pageWidth += horizontalSpacing * root.albumInfo.images.length; 5533 } 5534 else { 5535 pageWidth = colsPerPage * (imageConfig.width + horizontalSpacing); 5536 } 5537 5538 pageHeight = rowsPerPage * (imageConfig.height + verticalSpacing); 5539 5540 // Clip box margin left 5541 if(numPages < 2) { 5542 clipBoxMarginLeft = leftNavWidth; 5543 } 5544 else { 5545 availHorizSpace = bbWidth; 5546 5547 if(this._rightNav) { 5548 availHorizSpace -= rightNavWidth; 5549 } 5550 if(this._leftNav) { 5551 availHorizSpace -= leftNavWidth; 5552 clipBoxMarginLeft = leftNavWidth + (availHorizSpace - pageWidth - horizontalSpacing)/2; 5553 } 5554 else { 5555 clipBoxMarginLeft = (availHorizSpace - pageWidth - horizontalSpacing)/2; 5556 } 5557 } 5558 5559 // Clip box margin top 5560 if(numPages > 1 && !spaceEvenly) { 5561 5562 availVerticalSpace = bbHeight; 5563 5564 if(this._topNav) { 5565 availVerticalSpace -= topNavHeight; 5566 } 5567 if(this._bottomNav) { 5568 availVerticalSpace -= bottomNavHeight; 5569 } 5570 5571 clipBoxTop = (availVerticalSpace - (verticalSpacing + pageHeight))/2; 5572 } 5573 5574 // Set the info 5575 this._bbHeight = bbHeight; 5576 this._bbWidth = bbWidth; 5577 this._cbWidth = cbWidth; 5578 this._clipBoxMarginLeft = clipBoxMarginLeft; 5579 this._clipBoxTop = clipBoxTop; 5580 this._colsPerPage = colsPerPage; 5581 this._rowsPerPage = rowsPerPage; 5582 this._imagesPerPage = imagesPerPage; 5583 this._numPages = numPages; 5584 this._pageHeight = pageHeight; 5585 this._pageWidth = pageWidth; 5586 this._leftNavWidth = leftNavWidth; 5587 this._rightNavWidth = rightNavWidth; 5588 this._horizontalSpacing = horizontalSpacing; 5589 this._verticalSpacing = verticalSpacing; 5590 this._activePageIndex = Math.floor(root.imageIndex/this._imagesPerPage); 5591 5592 // Set back to the initial position. 5593 bb.setStyle('position', bbPosition); 5594 } 5595 5596}, { 5597 5598 /** 5599 * Custom CSS class name for the widget. 5600 * 5601 * @property CSS_PREFIX
5602 * @type String 5603 * @protected 5604 * @static 5605 */ 5606 CSS_PREFIX: 'fl-slideshow-thumbs', 5607 5608 /** 5609 * Static property used to define the default attribute configuration of 5610 * the Widget. 5611 * 5612 * @property ATTRS 5613 * @type Object 5614 * @protected 5615 * @static 5616 */ 5617 ATTRS: { 5618 5619 /** 5620 * The number of thumbnail columns. If set to auto, the number of 5621 * columns will be calculated based on the width of the parent node. 5622 * 5623 * @attribute columns 5624 * @type String or Number 5625 * @default auto 5626 */ 5627 columns: { 5628 value: 'auto' 5629 }, 5630 5631 /** 5632 * The number of thumbnail rows. If set to auto, the number of 5633 * rows will be calculated based on the height of the parent node. 5634 * 5635 * @attribute rows 5636 * @type String or Number 5637 * @default auto 5638 */ 5639 rows: { 5640 value: 'auto' 5641 }, 5642 5643 /** 5644 * The horizontal spacing between thumbs. 5645 * 5646 * @attribute horizontalSpacing 5647 * @type Number 5648 * @default 15 5649 */ 5650 horizontalSpacing: { 5651 value: 15 5652 }, 5653 5654 /** 5655 * The vertical spacing between thumbs. 5656 * 5657 * @attribute verticalSpacing 5658 * @type Number 5659 * @default 15 5660 */ 5661 verticalSpacing: { 5662 value: 15 5663 }, 5664 5665 /** 5666 * Whether to space the thumbs evenly within a page. 5667 * 5668 * @attribute spaceEvenly 5669 * @type Boolean 5670 * @default true 5671 */ 5672 spaceEvenly: { 5673 value: true 5674 }, 5675 5676 /** 5677 * Whether to center single pages of thumbs. 5678 * 5679 * @attribute centerSinglePage 5680 * @type Boolean 5681 * @default false 5682 */ 5683 centerSinglePage: { 5684 value: true 5685 }, 5686 5687 /** 5688 * Whether to pause the parent slideshow when a thumb is clicked. 5689 * 5690 * @attribute pauseOnClick 5691 * @type Boolean 5692 * @default false 5693 */ 5694 pauseOnClick: { 5695 value: false 5696 }, 5697 5698 /** 5699 * The type of transition to use between pages. 5700 * 5701 * @attribute transition
5702 * @type String 5703 * @default slideHorizontal 5704 */ 5705 transition: { 5706 value: 'slideHorizontal' 5707 }, 5708 5709 /** 5710 * The duration of the transition between pages. 5711 * 5712 * @attribute transitionDuration 5713 * @type Number 5714 * @default 0.8 5715 */ 5716 transitionDuration: { 5717 value: 0.8 5718 }, 5719 5720 /** 5721 * The type of transition easing to use between pages. 5722 * 5723 * @attribute transitionEasing 5724 * @type String 5725 * @default ease-out 5726 */ 5727 transitionEasing: { 5728 value: 'ease-out' 5729 }, 5730 5731 /** 5732 * The configuration object used to create new instances of 5733 * FL.SlideshowImage. See the API docs for {@link FL.SlideshowImage} 5734 * for a complete list of configuration attributes. 5735 * 5736 * @attribute imageConfig 5737 * @type Object 5738 * @default {} 5739 */ 5740 imageConfig: { 5741 value: { 5742 crop: true, 5743 width: 50, 5744 height: 50 5745 } 5746 }, 5747 5748 /** 5749 * Whether to use the top nav or not. 5750 * 5751 * @attribute topNavEnabled 5752 * @type Boolean 5753 * @default false 5754 */ 5755 topNavEnabled: { 5756 value: false 5757 }, 5758 5759 /** 5760 * An array of button names used to render the top nav buttons. 5761 * 5762 * @attribute topNavButtons 5763 * @type Array 5764 * @default prevPage, nextPage 5765 */ 5766 topNavButtons: { 5767 value: ['prevPage', 'nextPage'] 5768 }, 5769 5770 /** 5771 * Whether to use the right nav or not. 5772 * 5773 * @attribute rightNavEnabled 5774 * @type Boolean 5775 * @default true 5776 */ 5777 rightNavEnabled: { 5778 value: true 5779 }, 5780 5781 /** 5782 * An array of button names used to render the right nav buttons. 5783 * 5784 * @attribute rightNavButtons 5785 * @type Array 5786 * @default nextPage 5787 */ 5788 rightNavButtons: { 5789 value: ['nextPage'] 5790 }, 5791 5792 /** 5793 * Whether to use the bottom nav or not. 5794 * 5795 * @attribute bottomNavEnabled 5796 * @type Boolean 5797 * @default false 5798 */ 5799 bottomNavEnabled: { 5800 value: false 5801 }, 5802 5803 /** 5804 * An array of button names used to render the bottom nav buttons. 5805 * 5806 * @attribute bottomNavButtons 5807 * @type Array 5808 * @default prevPage, nextPage 5809 */ 5810 bottomNavButtons:{ 5811 value: ['prevPage', 'nextPage'] 5812 }, 5813 5814 /** 5815 * Whether to use the left nav or not. 5816 * 5817 * @attribute leftNavEnabled 5818 * @type Boolean 5819 * @default true 5820 */ 5821 leftNavEnabled: { 5822 value: true 5823 }, 5824 5825 /** 5826 * An array of button names used to render the left nav buttons. 5827 * 5828 * @attribute leftNavButtons 5829 * @type Array 5830 * @default prevPage 5831 */ 5832 leftNavButtons:{ 5833 value: ['prevPage'] 5834 }, 5835 5836 /** 5837 * Whether to use touch gestures, when available, 5838 * to transition between pages or not. 5839 * 5840 * @attribute touchSupport 5841 * @type Boolean 5842 * @default false 5843 */ 5844 touchSupport: { 5845 value: false 5846 } 5847 } 5848}); 5849 5850/** 5851 * Provides functionality for transitions between slideshow components. 5852 * 5853 * @namespace FL 5854 * @class SlideshowTransition 5855 * @constructor 5856 * @param config {Object} Configuration object 5857 * @extends Base 5858 */ 5859Y.namespace('FL').SlideshowTransition = Y.Base.create('fl-slideshow-transition', Y.Base, [], { 5860 5861 /** 5862 * The transition function to use when run is called. 5863 * 5864 * @property _transitionFunction
5865 * @type String 5866 * @default _transitionFade 5867 * @protected 5868 */ 5869 _transitionFunction: '_transitionFade', 5870 5871 /** 5872 * The current transition type. 5873 * 5874 * @property _type 5875 * @type String 5876 * @default fade 5877 * @protected 5878 */ 5879 _type: 'fade', 5880 5881 /** 5882 * Parses the transition type and sets the _transitionFunction 5883 * used when run is called. 5884 * 5885 * @method initializer 5886 * @protected 5887 */ 5888 initializer: function() 5889 { 5890 var type = this.get('type'), 5891 typeArray = [], 5892 types = Y.FL.SlideshowTransition.TYPES, 5893 slideshowImageTypes = Y.FL.SlideshowTransition.SLIDESHOW_IMAGE_TYPES, 5894 isSlideshowImageTransition = Y.Array.indexOf(slideshowImageTypes, type) > -1, 5895 isSlideshowImage = this._isSlideshowImage(), 5896 itemIn = this.get('itemIn'), 5897 itemOut = this.get('itemOut'); 5898 5899 // Check for random transitions. 5900 if(type.indexOf(',') > -1) { 5901 typeArray = type.split(','); 5902 typeArray.sort(function() { return 0.5 - Math.random(); }); 5903 type = typeArray[0]; 5904 } 5905 5906 // Make sure we can run this transition, otherwise set a fallback. 5907 if(!isSlideshowImage && isSlideshowImageTransition) { 5908 type = 'fade'; 5909 } 5910 else if(isSlideshowImage) { 5911 if((itemIn && itemIn.one('img') === null) || (itemOut && itemOut.one('img') === null)) { 5912 type = 'none'; 5913 } 5914 else if(isSlideshowImageTransition) { 5915 if((Y.UA.gecko && Y.UA.gecko < 5) || Y.UA.opera > 0 || (Y.UA.ie > 0 && Y.UA.ie < 9)) { 5916 type = 'fade'; 5917 } 5918 } 5919 } 5920 5921 // Set the transition function and type. 5922 if(Y.FL.SlideshowTransition.TYPES[type]) { 5923 this._transitionFunction = types[type]; 5924 this._type = type; 5925 } 5926 5927 // Setup the items. 5928 this._setupItems(); 5929 }, 5930 5931 /** 5932 * Fires the start event and calls the transition function. 5933 * 5934 * @method run 5935 */ 5936 run: function() 5937 { 5938 /** 5939 * Fires when the transition starts. 5940 * 5941 * @event start 5942 */ 5943 this.fire('start'); 5944 5945 this[this._transitionFunction].call(this); 5946 }, 5947 5948 /** 5949 * Set initial styles for the items. 5950 * 5951 * @method _setupItems 5952 * @protected 5953 */ 5954 _setupItems: function() 5955 { 5956 var itemIn = this.get('itemIn'), 5957 itemOut = this.get('itemOut'); 5958 5959 if(itemIn) { 5960 itemIn.setStyle('zIndex', 2); 5961 itemIn.setStyle('opacity', 1); 5962 5963 if(Y.FL.Utils.cssSupport('transform')) { 5964 itemIn.setStyle('transform', 'translate(0, 0)'); 5965 } 5966 else { 5967 itemIn.setStyle('top', '0'); 5968 itemIn.setStyle('left', '0'); 5969 } 5970 } 5971 if(itemOut) { 5972 itemOut.setStyle('zIndex', 1); 5973 } 5974 }, 5975 5976 /** 5977 * Checks if the transition is being run 5978 * on an instance of FL.SlideshowImage or not. 5979 * 5980 * @method _isSlideshowImage 5981 * @protected 5982 */ 5983 _isSlideshowImage: function() 5984 { 5985 var itemIn = this.get('itemIn'), 5986 itemOut = this.get('itemOut'); 5987 5988 if(itemIn && itemIn.hasClass('fl-slideshow-image')) { 5989 return true; 5990 } 5991 else if(itemOut && itemOut.hasClass('fl-slideshow-image')) { 5992 return true; 5993 } 5994 5995 return false; 5996 }, 5997 5998 /** 5999 * Starts the transition using the provided property objects. 6000 * 6001 * @method _transitionStart 6002 * @param propsIn {Object} The properties to animate in. 6003 * @param propsOut {Object} The properties to animate out. 6004 * @protected 6005 */ 6006 _transitionStart: function(propsIn, propsOut) 6007 { 6008 var itemIn = this.get('itemIn'), 6009 itemOut = this.get('itemOut'), 6010 itemInCallback = Y.bind(this._transitionComplete, this), 6011 itemOutCallback = !itemIn ? itemInCallback : null, 6012 duration = this.get('duration'), 6013 easing = this.get('easing'); 6014 6015 if(itemIn) { 6016 propsIn.duration = propsIn.duration || duration; 6017 propsIn.easing = propsIn.easing || easing; 6018 itemIn.transition(propsIn); 6019 } 6020 if(itemOut) { 6021 propsOut.duration = propsOut.duration || duration; 6022 propsOut.easing = propsOut.easing || easing; 6023 itemOut.transition(propsOut); 6024 } 6025 6026 if(itemInCallback) { 6027 Y.later(propsIn.duration * 1000 + 100, null, itemInCallback); 6028 } 6029 else if(itemOutCallback) { 6030 Y.later(propsOut.duration * 1000 + 100, null, itemOutCallback); 6031 } 6032 }, 6033 6034 /** 6035 * Clean up method called when the transition completes. 6036 * 6037 * @method _transitionComplete 6038 * @protected 6039 */ 6040 _transitionComplete: function() 6041 { 6042 this._set('itemIn', null); 6043 this._set('itemOut', null); 6044 6045 /** 6046 * Fires when the transition completes. 6047 * 6048 * @event complete 6049 */ 6050 this.fire('complete'); 6051 }, 6052 6053 /** 6054 * No transition. 6055 * 6056 * @method _transitionNone 6057 * @protected 6058 */ 6059 _transitionNone: function() 6060 { 6061 var itemIn = this.get('itemIn'), 6062 itemOut = this.get('itemOut'); 6063 6064 if(itemIn) { 6065 itemIn.setStyle('opacity', 1); 6066 } 6067 if(itemOut) { 6068 itemOut.setStyle('opacity', 0); 6069 } 6070 6071 this._transitionComplete(); 6072 }, 6073 6074 /** 6075 * Fade transition. 6076 * 6077 * @method _transitionFade 6078 * @protected 6079 */ 6080 _transitionFade: function() 6081 { 6082 var itemIn = this.get('itemIn'); 6083 6084 if(itemIn) { 6085 itemIn.setStyle('opacity', 0); 6086 } 6087 6088 this._transitionStart({ opacity: 1 },{ opacity: 0 }); 6089 }, 6090 6091 /** 6092 * Slide left transition. 6093 * 6094 * @method _transitionSlideLeft 6095 * @protected 6096 */ 6097 _transitionSlideLeft: function() 6098 { 6099 if(Y.FL.Utils.cssSupport('transform')) { 6100 this._cssTransitionSlide({ 6101 inStart: 'translate(100%, 0)', 6102 inEnd: 'translate(0, 0)', 6103 outStart: 'translate(0, 0)', 6104 outEnd: 'translate(-100%, 0)' 6105 }); 6106 } 6107 else { 6108 this._jsTransitionSlide('left'); 6109 } 6110 }, 6111 6112 /** 6113 * Slide right transition. 6114 * 6115 * @method _transitionSlideRight 6116 * @protected 6117 */ 6118 _transitionSlideRight: function() 6119 { 6120 if(Y.FL.Utils.cssSupport('transform')) { 6121 this._cssTransitionSlide({ 6122 inStart: 'translate(-100%, 0)', 6123 inEnd: 'translate(0, 0)', 6124 outStart: 'translate(0, 0)', 6125 outEnd: 'translate(100%, 0)' 6126 }); 6127 } 6128 else { 6129 this._jsTransitionSlide('right'); 6130 } 6131 }, 6132 6133 /** 6134 * Slide up transition. 6135 * 6136 * @method _transitionSlideUp 6137 * @protected 6138 */ 6139 _transitionSlideUp: function() 6140 { 6141 if(Y.FL.Utils.cssSupport('transform')) { 6142 this._cssTransitionSlide({ 6143 inStart: 'translate(0, 100%)', 6144 inEnd: 'translate(0, 0)', 6145 outStart: 'translate(0, 0)', 6146 outEnd: 'translate(0, -100%)' 6147 }); 6148 } 6149 else { 6150 this._jsTransitionSlide('up'); 6151 } 6152 }, 6153 6154 /** 6155 * Slide down transition. 6156 * 6157 * @method _transitionSlideDown 6158 * @protected 6159 */ 6160 _transitionSlideDown: function() 6161 { 6162 if(Y.FL.Utils.cssSupport('transform')) { 6163 this._cssTransitionSlide({ 6164 inStart: 'translate(0, -100%)', 6165 inEnd: 'translate(0, 0)', 6166 outStart: 'translate(0, 0)', 6167 outEnd: 'translate(0, 100%)' 6168 }); 6169 } 6170 else { 6171 this._jsTransitionSlide('down'); 6172 } 6173 }, 6174 6175 /** 6176 * JavaScript slide transition. 6177 * 6178 * @method _jsTransitionSlide 6179 * @protected 6180 */ 6181 _jsTransitionSlide: function(direction) 6182 { 6183 var itemIn = this.get('itemIn'), 6184 itemOut = this.get('itemOut'), 6185 itemOutEnd = 0; 6186 6187 // Item Out 6188 if(itemOut && direction == 'left') { 6189 itemOutEnd = -parseInt(itemOut.getStyle('width'), 10); 6190 } 6191 if(itemOut && direction == 'right') { 6192 itemOutEnd = parseInt(itemOut.getStyle('width'), 10); 6193 } 6194 if(itemOut && direction == 'up') { 6195 itemOutEnd = -parseInt(itemOut.getStyle('height'), 10); 6196 } 6197 if(itemOut && direction == 'down') { 6198 itemOutEnd = parseInt(itemOut.getStyle('height'), 10); 6199 } 6200 6201 // Item In 6202 if(itemIn) { 6203 itemIn.setStyle('opacity', 1); 6204 } 6205 if(itemIn && direction == 'left') { 6206 itemIn.setStyle('left', itemIn.getStyle('width')); 6207 } 6208 if(itemIn && direction == 'right') { 6209 itemIn.setStyle('left', '-' + itemIn.getStyle('width')); 6210 } 6211 if(itemIn && direction == 'up') { 6212 itemIn.setStyle('top', itemIn.getStyle('height')); 6213 } 6214 if(itemIn && direction == 'down') { 6215 itemIn.setStyle('top', '-' + itemIn.getStyle('height')); 6216 } 6217 6218 // Transition Start 6219 if(direction == 'left' || direction == 'right') { 6220 this._transitionStart({ left: 0 },{ left: itemOutEnd }); 6221 } 6222 else { 6223 this._transitionStart({ top: 0 },{ top: itemOutEnd }); 6224 } 6225 }, 6226 6227 /** 6228 * CSS slide transition. 6229 *
6230 * @method _cssTransitionSlide 6231 * @protected 6232 */ 6233 _cssTransitionSlide: function(props) 6234 { 6235 var itemIn = this.get('itemIn'), 6236 itemOut = this.get('itemOut'), 6237 transformProp = Y.UA.chrome < 36 ? 'transform' : '-webkit-transform', 6238 inProps = {}, 6239 outProps = {}; 6240 6241 inProps[transformProp] = props.inEnd; 6242 outProps[transformProp] = props.outEnd; 6243 6244 if(itemIn) { 6245 itemIn.setStyle('transition', ''); 6246 itemIn.setStyle('opacity', 1); 6247 itemIn.setStyle(transformProp, props.inStart); 6248 } 6249 if(itemOut) { 6250 itemOut.setStyle('transition', ''); 6251 itemOut.setStyle(transformProp, props.outStart); 6252 } 6253 6254 this._transitionStart(inProps, outProps); 6255 }, 6256 6257 /** 6258 * Bars and blinds transition. 6259 * 6260 * @method _transitionBars 6261 * @protected 6262 */ 6263 _transitionBars: function() 6264 { 6265 // Hide the image until the slices have transitioned in. 6266 this.get('itemIn').one('.fl-slideshow-image-img').setStyle('opacity', 0); 6267 6268 var numBars = this.get('bars'), 6269 slices = this._renderSlices(1, numBars), 6270 duration = this.get('duration'), 6271 delay = 0, 6272 increment = 100, 6273 last = false, 6274 i = 0, 6275 clone = null, 6276 props = { 6277 duration: duration, 6278 opacity: 1 6279 }; 6280 6281 // barsRandom 6282 if(this._type == 'barsRandom') { 6283 slices = this._randomizeSlices(slices); 6284 } 6285 6286 // Transition the slices. 6287 for( ; i < slices.length; i++) { 6288 6289 // Make a clone of our transition properties. 6290 clone = Y.clone(props); 6291 6292 // blinds 6293 if(this._type == 'blinds') { 6294 clone.width = parseFloat(slices[i].getComputedStyle('width'), 10) + 'px'; 6295 slices[i].setStyle('width', '0px'); 6296 increment = 50; 6297 } 6298 6299 // Run the transition. 6300 last = i == slices.length - 1 ? true : false; 6301 Y.later(delay, this, this._transitionSlice, [slices[i], clone, last]); 6302 delay += increment; 6303 } 6304 6305 this._transitionSlicesFadeLast(delay); 6306 }, 6307 6308 /** 6309 * Boxes transition. 6310 * 6311 * @method _transitionBoxes 6312 * @protected 6313 */ 6314 _transitionBoxes: function() 6315 { 6316 // Hide the image until the slices have transitioned in. 6317 this.get('itemIn').one('.fl-slideshow-image-img').setStyle('opacity', 0); 6318 6319 var numCols = this.get('boxCols'), 6320 numRows = this.get('boxRows'), 6321 numSlices = numCols * numRows, 6322 multi = this._type != 'boxesRandom', 6323 slices = this._renderSlices(numRows, numCols, multi), 6324 duration = this.get('duration'), 6325 delay = 0, 6326 increment = 150, 6327 last = false, 6328 i = 0, 6329 row = 0, 6330 col = 0, 6331 startCol = -1, 6332 clone = null, 6333 props = { 6334 duration: duration, 6335 opacity: 1 6336 }; 6337 6338 // boxesRandom 6339 if(!multi) { 6340 6341 slices = this._randomizeSlices(slices); 6342 increment = 30; 6343 6344 for( ; i < slices.length; i++) { 6345 clone = Y.clone(props); 6346 last = i == slices.length - 1 ? true : false; 6347 Y.later(delay, this, this._transitionSlice, [slices[i], clone, last]); 6348 delay += increment; 6349 } 6350 } 6351 // boxes 6352 else { 6353 while(i < numSlices) { 6354 for(row = 0; row < numRows; row++) { 6355 if(row === 0) { 6356 startCol++; 6357 col = startCol; 6358 } 6359 if(col > -1 && col < numCols) { 6360 i++; 6361 clone = Y.clone(props); 6362 6363 // boxesGrow 6364 if(this._type == 'boxesGrow') { 6365 clone.height = parseFloat(slices[row][col].getComputedStyle('height'), 10) + 'px'; 6366 clone.width = parseFloat(slices[row][col].getComputedStyle('width'), 10) + 'px'; 6367 slices[row][col].setStyle('height', '0px'); 6368 slices[row][col].setStyle('width', '0px'); 6369 increment = 50; 6370 } 6371 6372 last = i == numSlices - 1 ? true : false;
6373 Y.later(delay, this, this._transitionSlice, [slices[row][col], clone, last]); 6374 } 6375 col--; 6376 } 6377 delay += increment; 6378 } 6379 } 6380 6381 this._transitionSlicesFadeLast(delay); 6382 }, 6383 6384 /** 6385 * Renders the divs for slice based transitions. 6386 * 6387 * @method _renderSlices 6388 * @protected 6389 */ 6390 _renderSlices: function(numRows, numCols, multidimensional) 6391 { 6392 var itemIn = this.get('itemIn'), 6393 itemHeight = parseFloat(itemIn.getComputedStyle('height'), 10), 6394 itemWidth = parseFloat(itemIn.getComputedStyle('width'), 10), 6395 img = itemIn.one('img'), 6396 imgSrc = img.get('src'), 6397 imgHeight = parseFloat(img.getComputedStyle('height'), 10), 6398 imgWidth = parseFloat(img.getComputedStyle('width'), 10), 6399 imgLeft = parseFloat(img.getComputedStyle('left'), 10), 6400 imgTop = parseFloat(img.getComputedStyle('top'), 10), 6401 col = 0, 6402 row = 0, 6403 sliceHeight = Math.round(itemHeight/numRows), 6404 sliceWidth = Math.round(itemWidth/numCols), 6405 slice = null, 6406 sliceImg = null, 6407 slices = []; 6408 6409 for( ; row < numRows; row++) { 6410 6411 if(typeof multidimensional !== 'undefined' && multidimensional) { 6412 slices[row] = []; 6413 } 6414 for(col = 0; col < numCols; col++) { 6415 6416 slice = Y.Node.create('<div class="fl-slideshow-transition-slice"></div>'); 6417 sliceImg = Y.Node.create('<img src="'+ imgSrc +'" />'); 6418 6419 slice.setStyles({ 6420 left: (sliceWidth * col) + 'px', 6421 top: (sliceHeight * row) + 'px', 6422 width: col == numCols - 1 ? (itemWidth - (sliceWidth * col)) + 'px' : sliceWidth + 'px', 6423 height: row == numRows - 1 ? (itemHeight - (sliceHeight * row)) + 'px' : sliceHeight + 'px', 6424 opacity: 0 6425 }); 6426 6427 sliceImg.setStyles({ 6428 height: imgHeight + 'px', 6429 width: imgWidth + 'px', 6430 top: imgTop - ((sliceHeight + (row * sliceHeight)) - sliceHeight) + 'px', 6431 left: imgLeft - ((sliceWidth + (col * sliceWidth)) - sliceWidth) + 'px' 6432 }); 6433 6434 slice.append(sliceImg); 6435 itemIn.append(slice); 6436 6437 if(typeof multidimensional !== 'undefined' && multidimensional) { 6438 slices[row].push(slice); 6439 } 6440 else { 6441 slices.push(slice); 6442 } 6443 } 6444 } 6445 6446 return slices; 6447 }, 6448 6449 /** 6450 * Fade the itemOut node. 6451 * 6452 * @method _transitionSlicesFadeLast 6453 * @protected 6454 */ 6455 _transitionSlicesFadeLast: function(delay) 6456 { 6457 var itemOut = this.get('itemOut'), 6458 width = parseInt(Y.one('body').get('winWidth'), 10), 6459 delay = (width > FLBuilderLayoutConfig.breakpoints.medium) ? delay/1000 : 0; 6460 6461 if(itemOut && !itemOut.hasClass('fl-slideshow-image-cropped')) { 6462 itemOut.transition({ 6463 duration: delay + this.get('duration'), 6464 opacity: 0 6465 }); 6466 } 6467 }, 6468 6469 /** 6470 * Transitions a single slice. 6471 * 6472 * @method _transitionSlice 6473 * @protected 6474 */ 6475 _transitionSlice: function(slice, props, last) 6476 { 6477 var callback = last ? Y.bind(this._transitionSlicesComplete, this) : null; 6478 6479 slice.transition(props, callback); 6480 }, 6481 6482 /** 6483 * Complete callback for slice based transitions. 6484 * 6485 * @method _transitionSlicesComplete 6486 * @protected 6487 */ 6488 _transitionSlicesComplete: function() 6489 { 6490 var itemIn = this.get('itemIn'); 6491 6492 itemIn.all('.fl-slideshow-transition-slice').remove(); 6493 itemIn.one('.fl-slideshow-image-img').setStyle('opacity', 1); 6494 this._transitionComplete(); 6495 }, 6496 6497 /** 6498 * Randomizes a slices array. 6499 * 6500 * @method _radomizeSlices 6501 * @protected 6502 */ 6503 _randomizeSlices: function(slices) 6504 { 6505 var i = slices.length, j, temp; 6506 6507 if(i === 0) { 6508 return; 6509 } 6510 while(--i) { 6511 j = Math.floor( Math.random() * ( i + 1 ) ); 6512 temp = slices[i]; 6513 slices[i] = slices[j]; 6514 slices[j] = temp; 6515 } 6516 6517 return slices; 6518 }, 6519 6520 _transitionKenBurns: function() 6521 { 6522 var kbDuration = this.get('kenBurnsDuration'), 6523 duration = this.get('duration'), 6524 itemIn = this.get('itemIn'), 6525 zoom = this.get('kenBurnsZoom'); 6526 6527 this._transitionFade(); 6528 6529 (new Y.FL.SlideshowKenBurns({ 6530 duration: kbDuration + duration + 4, 6531 image: itemIn, 6532 zoom: zoom 6533 })).run(); 6534 } 6535 6536}, { 6537 6538 /** 6539 * Static property used to define the default attribute configuration of 6540 * the Widget. 6541 * 6542 * @property ATTRS 6543 * @type Object 6544 * @protected 6545 * @static 6546 */ 6547 ATTRS: { 6548 6549 /** 6550 * The Node to transition in. 6551 * 6552 * @attribute itemIn 6553 * @type Node 6554 * @default null 6555 */ 6556 itemIn: { 6557 value: null 6558 }, 6559 6560 /** 6561 * The Node to transition out. 6562 * 6563 * @attribute itemOut 6564 * @type Node 6565 * @default null 6566 */ 6567 itemOut: { 6568 value: null 6569 }, 6570 6571 /** 6572 * The duration of the transition in seconds. 6573 * 6574 * @attribute duration 6575 * @type Number 6576 * @default 0.5 6577 */ 6578 duration: { 6579 value: 0.5 6580 }, 6581 6582 /** 6583 * The type of easing to use. 6584 * 6585 * @attribute easing
6586 * @type String 6587 * @default ease-out 6588 */ 6589 easing: { 6590 value: 'ease-out' 6591 }, 6592 6593 /** 6594 * The type of transition to use. 6595 * 6596 * @attribute type 6597 * @type String 6598 * @default fade 6599 */ 6600 type: { 6601 value: 'fade' 6602 }, 6603 6604 /** 6605 * The number of bars to use for 6606 * transitions such as blinds. 6607 * 6608 * @attribute bars 6609 * @type Number 6610 * @default 15 6611 */ 6612 bars: { 6613 value: 15 6614 }, 6615 6616 /** 6617 * The number of columns to use for 6618 * transitions such as boxes. 6619 * 6620 * @attribute boxCols 6621 * @type Number 6622 * @default 8 6623 */ 6624 boxCols: { 6625 value: 8 6626 }, 6627 6628 /** 6629 * The number of rows to use for 6630 * transitions such as boxes. 6631 * 6632 * @attribute boxRows 6633 * @type Number 6634 * @default 4 6635 */ 6636 boxRows: { 6637 value: 4 6638 }, 6639 6640 /** 6641 * The duration the ken burns effect will 6642 * last, measured in seconds. 6643 * 6644 * @attribute kenBurnsDuration 6645 * @type Number 6646 * @default 4 6647 */ 6648 kenBurnsDuration: { 6649 value: 4 6650 }, 6651 6652 /** 6653 * The amount of zoom to use for the Ken Burns effect. 6654 * 6655 * @attribute kenBurnsZoom 6656 * @type Number 6657 * @default 1.2 6658 */ 6659 kenBurnsZoom: { 6660 value: 1.2 6661 } 6662 }, 6663 6664 /** 6665 * The types of transitions and associated functions. 6666 * 6667 * @property TYPES 6668 * @type Object 6669 * @readOnly 6670 * @protected 6671 * @static 6672 */ 6673 TYPES: { 6674 fade: '_transitionFade', 6675 none: '_transitionNone', 6676 slideLeft: '_transitionSlideLeft', 6677 slideRight: '_transitionSlideRight', 6678 slideUp: '_transitionSlideUp', 6679 slideDown: '_transitionSlideDown', 6680 blinds: '_transitionBars', 6681 bars: '_transitionBars', 6682 barsRandom: '_transitionBars', 6683 boxes: '_transitionBoxes', 6684 boxesRandom: '_transitionBoxes', 6685 boxesGrow: '_transitionBoxes', 6686 kenBurns: '_transitionKenBurns' 6687 }, 6688 6689 /** 6690 * The types of transitions that can only be 6691 * run on FL.SlideshowImage widgets. 6692 * 6693 * @property SLIDESHOW_IMAGE_TYPES 6694 * @type Object 6695 * @readOnly 6696 * @protected 6697 * @static 6698 */ 6699 SLIDESHOW_IMAGE_TYPES: [ 6700 'blinds', 6701 'bars', 6702 'barsRandom', 6703 'boxes', 6704 'boxesRandom', 6705 'boxesGrow', 6706 'kenBurns' 6707 ] 6708}); 6709 6710/** 6711 * A highly configurable slideshow widget. 6712 * 6713 * @namespace FL 6714 * @class Slideshow 6715 * @constructor 6716 * @param config {Object} Configuration object 6717 * @extends FL.SlideshowBase 6718 */ 6719Y.namespace('FL').Slideshow = Y.Base.create('fl-slideshow', Y.FL.SlideshowBase, [], { 6720 6721 /** 6722 * A FL.SlideshowFrame instance used for the main image. 6723 * 6724 * @property frame 6725 * @type FL.SlideshowFrame 6726 * @default null 6727 */ 6728 frame: null, 6729 6730 /** 6731 * A FL.SlideshowNav instance used for the main nav. 6732 * 6733 * @property nav 6734 * @type FL.SlideshowNav 6735 * @default null 6736 */ 6737 nav: null, 6738 6739 /** 6740 * A FL.SlideshowNav instance used for the image nav's left button. 6741 * 6742 * @property imageNavLeft 6743 * @type FL.SlideshowNav 6744 * @default null 6745 */ 6746 imageNavLeft: null, 6747 6748 /** 6749 * A FL.SlideshowNav instance used for the image nav's right button. 6750 * 6751 * @property imageNavRight 6752 * @type FL.SlideshowNav 6753 * @default null 6754 */ 6755 imageNavRight: null, 6756 6757 /** 6758 * A FL.SlideshowThumbs instance used for the thumbnail grid. 6759 * 6760 * @property thumbs 6761 * @type FL.SlideshowThumbs 6762 * @default null 6763 */ 6764 thumbs: null, 6765 6766 /** 6767 * A FL.SlideshowThumbs instance used for the vertical thumbnail grid. 6768 * 6769 * @property verticalThumbs 6770 * @type FL.SlideshowThumbs 6771 * @default null 6772 */ 6773 verticalThumbs: null, 6774 6775 /** 6776 * A FL.SlideshowCaption instance. 6777 * 6778 * @property caption 6779 * @type FL.SlideshowCaption 6780 * @default null 6781 */ 6782 caption: null, 6783 6784 /** 6785 * A FL.SlideshowSocial instance. 6786 * 6787 * @property social 6788 * @type FL.SlideshowSocial 6789 * @default null 6790 */ 6791 social: null, 6792 6793 /** 6794 * A FL.SlideshowImage instance used to preload 6795 * the next image. 6796 * 6797 * @property _nextImagePreloader 6798 * @type FL.SlideshowImage 6799 * @default null 6800 * @protected 6801 */ 6802 _nextImagePreloader: null, 6803 6804 /** 6805 * An object that holds the initial nav settings 6806 * when the mini nav has been enabled for a responsive layout. 6807 * 6808 * @property _initialNavSettings 6809 * @type Object 6810 * @default null 6811 * @protected 6812 */ 6813 _initialNavSettings: null, 6814 6815 /** 6816 * Initializes the preloaders, nav buttons, fullscreen and captions. 6817 * 6818 * @method initializer 6819 * @protected 6820 */ 6821 initializer: function() 6822 { 6823 // Preloader config 6824 var imageConfig = { 6825 loadGroup: 'main-preload', 6826 crop: this.get('crop'), 6827 position: this.get('position'), 6828 protect: this.get('protect'), 6829 upsize: this.get('upsize') 6830 }; 6831 6832 // Preloader 6833 this._nextImagePreloader = new Y.FL.SlideshowImage(imageConfig); 6834 6835 // Nav buttons not needed for touch 6836 if(this._isMobile()) { 6837 this._removeNavButton('prevPage'); 6838 this._removeNavButton('nextPage'); 6839 this._removeNavButton('fullscreen'); 6840 } 6841 6842 // Fullscreen 6843 if(this._hasNavButton('fullscreen')) { 6844 if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { 6845 this.plug(Y.FL.SlideshowFullscreen); 6846 } 6847 else { 6848 this._removeNavButton('fullscreen'); 6849 } 6850 } 6851 }, 6852 6853 /** 6854 * Calls the FL.SlideshowBase superclass renderUI method 6855 * and renders the child widgets. 6856 * 6857 * @method renderUI 6858 * @protected 6859 */ 6860 renderUI: function() 6861 { 6862 Y.FL.Slideshow.superclass.renderUI.apply(this, arguments); 6863 6864 this._renderFrame(); 6865 this._renderVerticalThumbs(); 6866 this._renderNavAndThumbs(); 6867 this._renderImageNav(); 6868 this._renderMouseNav(); 6869 this._renderCaption(); 6870 this._renderSocial(); 6871 }, 6872 6873 /** 6874 * Calls the FL.SlideshowBase superclass bindUI method, binds 6875 * _resizeChildWidgets to fire after the resize method inherited 6876 * from FL.SlideshowBase, shows the loading image, binds overlay events 6877 * and binds an event to load an image into the frame. 6878 * 6879 * @method bindUI 6880 * @protected 6881 */ 6882 bindUI: function() 6883 { 6884 var ssBB = this.get('boundingBox'), 6885 frameBB = this.frame.get('boundingBox'), 6886 navOverlay = this.get('navOverlay'), 6887 navType = this.get('navType'), 6888 nav = this._getNav(), 6889 clickAction = this.get('clickAction'); 6890 6891 // Call superclass bindUI 6892 Y.FL.Slideshow.superclass.bindUI.apply(this, arguments); 6893 6894 // Resize child widgets after the superclass resize method. 6895 Y.Do.after(this._resizeChildWidgets, this, 'resize'); 6896 6897 // Loading events 6898 this.on('albumLoadStart', this._albumLoadStart, this); 6899 this.on('albumLoadComplete', this._albumLoadComplete, this); 6900 this.on('imageLoadComplete', this._loadFrame, this); 6901 6902 // Loading image 6903 if(this.get('loadingImageAlwaysEnabled')) { 6904 this.frame.on('transitionInit', Y.bind(this._showLoadingImageWithDelay, this)); 6905 this.frame.on('transitionStart', Y.bind(this._hideLoadingImage, this)); 6906 } 6907 6908 // Overlay events 6909 if(this.get('overlayHideOnMousemove')) { 6910 if(nav && navOverlay) { 6911 this.frame.once('transitionComplete', nav.slideshowOverlay.hideWithTimer, nav.slideshowOverlay); 6912 ssBB.on('mousemove', Y.bind(this._toggleNav, this)); 6913 } 6914 if(navType == 'buttons' || navType == 'thumbs' || navType == 'custom') { 6915 ssBB.on('mouseenter', Y.bind(this._checkOverlaysOnMouseenter, this)); 6916 ssBB.on('mouseleave', Y.bind(this._hideAllOverlays, this)); 6917 } 6918 } 6919 6920 ssBB.delegate('click', Y.bind(this._overlayCloseClick, this), '.fl-slideshow-overlay-close'); 6921 6922 // Click action 6923 if(clickAction == 'gallery' || clickAction == 'url') { 6924 frameBB.delegate('click', Y.bind(this._frameClick, this), '.fl-slideshow-image-img'); 6925 } 6926 }, 6927 6928 /** 6929 * Calls the FL.SlideshowBase superclass syncUI method 6930 * and makes the bounding box unselectable. 6931 * 6932 * @method syncUI 6933 * @protected 6934 */
6935 syncUI: function() 6936 { 6937 var bb = this.get('boundingBox'); 6938 6939 Y.FL.Slideshow.superclass.syncUI.apply(this, arguments); 6940 6941 bb._node.onselectstart = function() { return false; }; 6942 bb._node.unselectable = "on"; 6943 bb._node.style.MozUserSelect = "none"; 6944 6945 if(this.get('clickAction') != 'none') { 6946 this.frame.get('boundingBox').addClass('fl-click-action-enabled'); 6947 } 6948 }, 6949 6950 /** 6951 * Checks to see if the current device is mobile. 6952 * 6953 * @since 1.9.3 6954 * @access private 6955 * @method _isMobile 6956 * @return {Boolean} 6957 */ 6958 _isMobile: function() 6959 { 6960 return /Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi|webOS/i.test( navigator.userAgent ); 6961 }, 6962 6963 /** 6964 * Unload all slideshow images and pause 6965 * the slideshow. 6966 * 6967 * @method unload 6968 */ 6969 unload: function() 6970 { 6971 this.pause(); 6972 this.frame.unload(); 6973 6974 if(this.thumbs !== null) { 6975 this.thumbs.unload(); 6976 } 6977 }, 6978 6979 /** 6980 * @method _albumLoadStart 6981 * @protected 6982 */ 6983 _albumLoadStart: function() 6984 { 6985 this._showLoadingImage(); 6986 }, 6987 6988 /** 6989 * @method _albumLoadComplete 6990 * @protected 6991 */ 6992 _albumLoadComplete: function() 6993 { 6994 this.frame.once('transitionStart', Y.bind(this._hideLoadingImage, this)); 6995 }, 6996 6997 /** 6998 * Resizes all enabled child widgets. 6999 * 7000 * @method _resizeChildWidgets 7001 * @protected 7002 */ 7003 _resizeChildWidgets: function() 7004 { 7005 var bb = this.get('boundingBox'), 7006 cb = this.get('contentBox'), 7007 imageNavEnabled = this.get('imageNavEnabled'); 7008 7009 this._renderNavAndThumbs(); 7010 7011 if(this.get('verticalThumbsOverlay')) { 7012 this._resizeFrame(cb.get('offsetWidth'), bb.get('offsetHeight')); 7013 this._resizeVerticalThumbs(); 7014 } 7015 else { 7016 this._resizeVerticalThumbs(); 7017 this._resizeFrame(cb.get('offsetWidth'), bb.get('offsetHeight')); 7018 } 7019 7020 if(imageNavEnabled) { 7021 this._positionImageNav(); 7022 } 7023 7024 this._positionLoadingImage(); 7025 }, 7026 7027 /** 7028 * @method _renderVerticalThumbs 7029 * @protected 7030 */ 7031 _renderVerticalThumbs: function() 7032 { 7033 var threshold = this.get('responsiveThreshold'), 7034 ssBB = this.get('boundingBox'), 7035 bbWidth = ssBB.get('offsetWidth'), 7036 vtBB; 7037 7038 if(this.get('verticalThumbsEnabled') && bbWidth > threshold) { 7039 7040 this.verticalThumbs = new Y.FL.SlideshowThumbs(this._getVerticalThumbsConfig()); 7041 this.add(this.verticalThumbs); 7042 this.verticalThumbs.render(ssBB); 7043 7044 vtBB = this.verticalThumbs.get('boundingBox'); 7045 vtBB.addClass('fl-slideshow-vertical-thumbs'); 7046 vtBB.setStyle(this.get('verticalThumbsPosition'), 0); 7047 ssBB.append(vtBB); 7048 7049 if(this.get('verticalThumbsOverlay')) { 7050 this.verticalThumbs.plug(Y.FL.SlideshowOverlay, { 7051 hideDelay: this.get('overlayHideDelay'), 7052 hideStyle: 'left' 7053 }); 7054 7055 this.frame.get('boundingBox').append(vtBB); 7056 this.verticalThumbs.resize(); 7057 } 7058 else { 7059 this.verticalThumbs.resize(); 7060 this._adjustContentForVerticalThumbs(); 7061 } 7062 7063 this._bindVerticalThumbs(); 7064 } 7065 }, 7066 7067 /** 7068 * Prepares and returns the vertical thumbs config object. 7069 * 7070 * @method _getVerticalThumbsConfig 7071 * @protected 7072 * @returns Object 7073 */ 7074 _getVerticalThumbsConfig: function() 7075 { 7076 var attrs = this.getAttrs(), 7077 config = { 7078 columns: attrs.verticalThumbsColumns, 7079 rows: 'auto', 7080 centerSinglePage: false, 7081 horizontalSpacing: attrs.verticalThumbsHorizontalSpacing, 7082 verticalSpacing: attrs.verticalThumbsVerticalSpacing, 7083 spaceEvenly: attrs.verticalThumbsSpaceEvenly, 7084 rightNavEnabled: false, 7085 leftNavEnabled: false, 7086 topNavEnabled: attrs.verticalThumbsTopNavEnabled, 7087 topNavButtons: attrs.verticalThumbsTopNavButtons, 7088 bottomNavEnabled: attrs.verticalThumbsBottomNavEnabled, 7089 bottomNavButtons: attrs.verticalThumbsBottomNavButtons, 7090 pauseOnClick: attrs.verticalThumbsPauseOnClick, 7091 transition: attrs.verticalThumbsTransition, 7092 transitionDirection: attrs.verticalThumbsTransitionDirection, 7093 transitionEasing: attrs.verticalThumbsTransitionEasing, 7094 touchSupport: true, 7095 imageConfig: { 7096 crop: attrs.verticalThumbsImageCrop, 7097 width: attrs.verticalThumbsImageWidth, 7098 height: attrs.verticalThumbsImageHeight 7099 } 7100 }; 7101 7102 return config; 7103 }, 7104 7105 _bindVerticalThumbs: function() 7106 { 7107 var ssBB = this.get('boundingBox'), 7108 hideOnMouse = this.get('overlayHideOnMousemove'), 7109 vtOverlay = this.get('verticalThumbsOverlay'), 7110 vt = this.verticalThumbs; 7111 7112 if(vt && hideOnMouse && vtOverlay) { 7113 this.frame.once('transitionComplete', vt.slideshowOverlay.hideWithTimer, vt.slideshowOverlay); 7114 ssBB.on('mousemove', Y.bind(this._toggleVerticalThumbs, this)); 7115 ssBB.on('mouseenter', Y.bind(this._toggleVerticalThumbs, this)); 7116 } 7117 }, 7118 7119 /** 7120 * Resizes the vertical thumbs. 7121 * 7122 * @method _resizeVerticalThumbs 7123 * @protected 7124 */ 7125 _resizeVerticalThumbs: function() 7126 { 7127 var vtEnabled = this.get('verticalThumbsEnabled'), 7128 vtOverlay, 7129 threshold, 7130 ssBB, 7131 bbWidth, 7132 navOverlay, 7133 navType, 7134 nav, 7135 navBB; 7136 7137 if(vtEnabled) { 7138 vtOverlay = this.get('verticalThumbsOverlay'); 7139 threshold = this.get('responsiveThreshold'); 7140 ssBB = this.get('boundingBox'); 7141 bbWidth = ssBB.get('offsetWidth'); 7142 navOverlay = this.get('navOverlay'); 7143 navType = this.get('navType'); 7144 nav = this._getNav(); 7145 7146 if(this.verticalThumbs && bbWidth > threshold) { 7147 7148 this.verticalThumbs.get('boundingBox').setStyle('display', 'block'); 7149 this.verticalThumbs.resize(); 7150 7151 if(!vtOverlay) { 7152 this._adjustContentForVerticalThumbs(); 7153 } 7154 else if(nav && navOverlay) { 7155 7156 navBB = nav.get('boundingBox'); 7157 7158 if(navType == 'thumbs') { 7159 this._adjustOverlayForVerticalThumbs(navBB, true); 7160 this.thumbs.resize(); 7161 } 7162 else { 7163 this._adjustOverlayForVerticalThumbs(navBB); 7164 } 7165 } 7166 } 7167 else if(!this.verticalThumbs && bbWidth > threshold) { 7168 this._renderVerticalThumbs(); 7169 } 7170 else if(this.verticalThumbs && bbWidth <= threshold) { 7171 7172 this.verticalThumbs.get('boundingBox').setStyle('display', 'none'); 7173 7174 if(!vtOverlay) { 7175 this.get('contentBox').setStyles({ 7176 left: 'auto', 7177 position: 'relative', 7178 right: 'auto', 7179 width: 'auto' 7180 }); 7181 } 7182 } 7183 } 7184 }, 7185 7186 /** 7187 * Toggles the visibility of the vertical thumbs. 7188 * 7189 * @method _toggleVerticalThumbs 7190 * @protected 7191 */ 7192 _toggleVerticalThumbs: function() 7193 { 7194 if(this.verticalThumbs) { 7195 if(this.verticalThumbs.slideshowOverlay._visible) { 7196 this.verticalThumbs.slideshowOverlay.hideWithTimer(); 7197 } 7198 else { 7199 this.verticalThumbs.slideshowOverlay.show(); 7200 } 7201 } 7202 }, 7203 7204 /** 7205 * Adjusts the content position and width 7206 * for the vertical thumbs. 7207 * 7208 * @method _adjustContentForVerticalThumbs 7209 * @protected 7210 */ 7211 _adjustContentForVerticalThumbs: function() 7212 { 7213 var ssBB = this.get('boundingBox'), 7214 vtBB = this.verticalThumbs.get('boundingBox'), 7215 vtPos = this.get('verticalThumbsPosition'), 7216 ssCB = this.get('contentBox'), 7217 cbPos = vtPos == 'left' ? 'right' : 'left', 7218 cbWidth = ssBB.get('offsetWidth') - vtBB.get('offsetWidth'); 7219 7220 ssCB.setStyle('position', 'absolute'); 7221 ssCB.setStyle(cbPos, 0); 7222 ssCB.setStyle('width', cbWidth); 7223 }, 7224 7225 /** 7226 * Adjusts an overlay's position for the vertical 7227 * thumbs when they are overlaid as well. 7228 * 7229 * @method _adjustOverlayForVerticalThumbs 7230 * @protected 7231 */ 7232 _adjustOverlayForVerticalThumbs: function(node, useMargin) 7233 { 7234 var vtEnabled = this.get('verticalThumbsEnabled'), 7235 vtOverlay = this.get('verticalThumbsOverlay'), 7236 vtBB = null, 7237 vtPos = null, 7238 margin = typeof useMargin === 'undefined' ? '' : 'margin-', 7239 vtWidth = 0; 7240 7241 if(this.verticalThumbs && vtEnabled && vtOverlay) { 7242 vtBB = this.verticalThumbs.get('boundingBox'); 7243 vtWidth = vtBB.get('offsetWidth'); 7244 vtPos = this.get('verticalThumbsPosition'); 7245 7246 if(vtPos == 'left') { 7247 node.setStyle(margin + 'left', vtWidth + 'px'); 7248 } 7249 else { 7250 node.setStyle(margin + 'right', vtWidth + 'px'); 7251 } 7252 } 7253 }, 7254 7255 /** 7256 * Creates and renders a new instance of FL.SlideshowFrame 7257 * used for the main image. 7258 * 7259 * @method _renderFrame 7260 * @protected 7261 */ 7262 _renderFrame: function() 7263 { 7264 this.frame = new Y.FL.SlideshowFrame({ 7265 imageConfig: { 7266 loadGroup: 'main', 7267 loadPriority: true, 7268 crop: this.get('crop'),
7269 cropHorizontalsOnly: this.get('cropHorizontalsOnly'), 7270 position: this.get('position'), 7271 protect: this.get('protect'), 7272 upsize: this.get('upsize'), 7273 showVideoButton: this.get('navOverlay') 7274 }, 7275 touchSupport: this.get('touchSupport') 7276 }); 7277 7278 this.add(this.frame); 7279 this.frame.render(this.get('contentBox')); 7280 this.frame.get('boundingBox').addClass('fl-slideshow-main-image'); 7281 this._setPlayingTimerEvent(this.frame, 'transitionComplete'); 7282 this._loadingImageContainer = this.frame.get('contentBox'); 7283 }, 7284 7285 /** 7286 * Resizes the frame used for the main image. 7287 * 7288 * @method _resizeMainImage 7289 * @param width {Number} The width to resize to. 7290 * @param height {Number} The height to resize to. 7291 * @protected 7292 */ 7293 _resizeFrame: function(width, height) 7294 { 7295 var navOverlay = this.get('navOverlay'), 7296 nav = this._getNav(); 7297 7298 if(nav && !navOverlay) { 7299 height -= parseInt(nav.get('boundingBox').getComputedStyle('height'), 10); 7300 } 7301 7302 this.frame.resize(width, height); 7303 }, 7304 7305 /** 7306 * Called when the imageLoadComplete event fires. 7307 * Loads an image into the frame and preloads the next image. 7308 * 7309 * @method _loadFrame 7310 * @param e {Object} Event object containing the image info. 7311 * @protected 7312 */ 7313 _loadFrame: function(e) 7314 { 7315 var activeIndex = this.imageInfo.index, 7316 images = this.albumInfo.images, 7317 nextIndex = activeIndex + 1 >= images.length ? 0 : activeIndex + 1, 7318 width = this.frame.get('width'), 7319 height = this.frame.get('height'); 7320 7321 // Load the frame. 7322 this.frame.load(e.imageInfo); 7323 7324 // Remove main preload images from the load queue. 7325 Y.FL.SlideshowImageLoader.removeGroup('main-preload'); 7326 7327 // Preload the next image. 7328 this._nextImagePreloader.preload(images[nextIndex], width, height); 7329 }, 7330 7331 /** 7332 * Fired when the frame img tag is clicked. 7333 * 7334 * @method _frameClick 7335 * @protected 7336 */ 7337 _frameClick: function() 7338 { 7339 var clickAction = this.get('clickAction'), 7340 clickActionUrl = this.get('clickActionUrl'); 7341 7342 if(clickAction == 'url') { 7343 window.location.href = clickActionUrl; 7344 } 7345 else if(clickAction == 'gallery') { 7346 window.location.href = this.imageInfo.link; 7347 } 7348 }, 7349 7350 /** 7351 * Sets attributes to display a compact nav 7352 * for responsive layouts. 7353 * 7354 * @method _initMiniNav 7355 * @protected 7356 */ 7357 _initMiniNav: function() 7358 { 7359 var buttons = []; 7360 7361 if(this._hasNavButton('prev')) { 7362 buttons.push('prev'); 7363 } 7364 if(this._hasNavButton('thumbs') || this.get('navType') == 'thumbs') { 7365 buttons.push('thumbs'); 7366 } 7367 if(this._hasNavButton('caption')) { 7368 buttons.push('caption'); 7369 } 7370 if(this._hasNavButton('social')) { 7371 buttons.push('social'); 7372 } 7373 if(this._hasNavButton('buy')) { 7374 buttons.push('buy'); 7375 } 7376 if(this._hasNavButton('play')) { 7377 buttons.push('play'); 7378 } 7379 if(this._hasNavButton('fullscreen') && !('ontouchstart' in window)) { 7380 buttons.push('fullscreen'); 7381 } 7382 if(this._hasNavButton('next')) { 7383 buttons.push('next'); 7384 } 7385 7386 this._initialNavSettings = { 7387 buttons: this.get('navButtons'), 7388 buttonsLeft: this.get('navButtonsLeft'), 7389 buttonsRight: this.get('navButtonsRight'), 7390 type: this.get('navType') 7391 }; 7392 7393 this._set('navButtons', buttons); 7394 this._set('navButtonsLeft', []); 7395 this._set('navButtonsRight', []); 7396 this._set('navType', 'buttons'); 7397 }, 7398 7399 /** 7400 * Renders the nav and thumbs layout based on the 7401 * current window size. 7402 * 7403 * @method _renderNavAndThumbs 7404 * @protected 7405 */ 7406 _renderNavAndThumbs: function() 7407 { 7408 var navType = this.get('navType'), 7409 renderNav = false, 7410 bbWidth, 7411 threshold; 7412 7413 if(navType == 'buttons' || navType == 'thumbs') { 7414 bbWidth = this.get('boundingBox').get('offsetWidth'); 7415 threshold = this.get('responsiveThreshold'); 7416 7417 if(bbWidth <= threshold && this._initialNavSettings === null) { 7418 this._initMiniNav(); 7419 renderNav = true; 7420 } 7421 else if(bbWidth > threshold && this._initialNavSettings !== null) { 7422 this._set('navButtons', this._initialNavSettings.buttons); 7423 this._set('navButtonsLeft', this._initialNavSettings.buttonsLeft); 7424 this._set('navButtonsRight', this._initialNavSettings.buttonsRight); 7425 this._set('navType', this._initialNavSettings.type); 7426 this._initialNavSettings = null; 7427 renderNav = true; 7428 } 7429 7430 // Button nav 7431 if(renderNav || this.nav === null) { 7432 this._renderNav(); 7433 } 7434 7435 // Thumbs nav 7436 if(renderNav || this.thumbs === null) { 7437 this._renderThumbs(); 7438 } 7439 else if(this._thumbsEnabled()) { 7440 this._resizeThumbs(); 7441 } 7442 7443 // Caption 7444 if(renderNav && this.caption !== null) { 7445 this._syncCaption(); 7446 } 7447 7448 // Social 7449 if(renderNav && this.social !== null) { 7450 this._syncSocial(); 7451 } 7452 } 7453 }, 7454 7455 /** 7456 * Creates and renders a new instance of FL.SlideshowNav 7457 * used for the main nav. 7458 * 7459 * @method _renderNav 7460 * @protected 7461 */ 7462 _renderNav: function() 7463 { 7464 var frameBB = this.frame.get('boundingBox'), 7465 navBB = null, 7466 navOverlay = this.get('navOverlay'), 7467 navPosition = this.get('navPosition'); 7468 7469 // Destroy old instances 7470 this._destroyNav(); 7471 7472 // Create a new instance 7473 if(this.get('navType') == 'buttons') { 7474 7475 // Create the nav 7476 this.nav = new Y.FL.SlideshowNav({ 7477 buttons: this.get('navButtons'),
7478 buttonsLeft: this.get('navButtonsLeft'), 7479 buttonsRight: this.get('navButtonsRight') 7480 }); 7481 7482 // Add to widget parent and render 7483 this.add(this.nav); 7484 this.nav.render(this.get('contentBox')); 7485 navBB = this.nav.get('boundingBox'); 7486 7487 // Plug overlay? 7488 if(navOverlay) { 7489 this.nav.plug(Y.FL.SlideshowOverlay, { 7490 hideDelay: this.get('overlayHideDelay') 7491 }); 7492 7493 navBB.setStyle('position', 'absolute'); 7494 navBB.setStyle(navPosition, '0px'); 7495 } 7496 7497 // Insert 7498 if(navPosition == 'top') { 7499 frameBB.insert(navBB, 'before'); 7500 } 7501 else { 7502 frameBB.insert(navBB, 'after'); 7503 } 7504 7505 // CSS class name 7506 navBB.addClass('fl-slideshow-main-nav'); 7507 } 7508 }, 7509 7510 /** 7511 * Destroy the current nav instance. 7512 * 7513 * @method _destroyNav 7514 * @protected 7515 */ 7516 _destroyNav: function() 7517 { 7518 if(this.nav !== null) { 7519 if(this.nav.slideshowOverlay) { 7520 this.nav.slideshowOverlay.destroy(); 7521 } 7522 7523 this.nav.get('boundingBox').remove(); 7524 this.remove(this.nav); 7525 try { this.nav.destroy(true); } catch(e) {} 7526 this.nav = null; 7527 } 7528 }, 7529 7530 /** 7531 * Returns the nav object or null if navType is 7532 * set to none or custom. 7533 * 7534 * @method _getNav 7535 * @protected 7536 */ 7537 _getNav: function() 7538 { 7539 var navType = this.get('navType'); 7540 7541 if(navType == 'buttons') { 7542 return this.nav; 7543 } 7544 else if(navType == 'thumbs') { 7545 return this.thumbs; 7546 } 7547 else { 7548 return null; 7549 } 7550 }, 7551 7552 /** 7553 * Toggles the visibility of the nav or thumbs nav 7554 * if navOverlay is set to true. 7555 * 7556 * @method _toggleNav 7557 * @protected 7558 */ 7559 _toggleNav: function() 7560 { 7561 var nav = this._getNav(); 7562 7563 if(nav.slideshowOverlay) { 7564 if(nav.slideshowOverlay._visible) { 7565 nav.slideshowOverlay.hideWithTimer(); 7566 } 7567 else { 7568 nav.slideshowOverlay.show(); 7569 } 7570 } 7571 }, 7572 7573 /** 7574 * Creates and renders two instances of FL.SlideshowNav for the 7575 * prev and next button that will be overlaid on the main image. 7576 * 7577 * @method _renderImageNav 7578 * @protected 7579 */ 7580 _renderImageNav: function() 7581 { 7582 var ssBB; 7583 7584 if(this.get('imageNavEnabled')) { 7585 if(this._isMobile()) { 7586 this._set('imageNavEnabled', false); 7587 } 7588 else { 7589 ssBB = this.get('boundingBox'); 7590 7591 this.imageNavLeft = new Y.FL.SlideshowNav({ 7592 buttons: ['prev'], 7593 useFontIcons: false 7594 }); 7595 7596 this.imageNavRight = new Y.FL.SlideshowNav({ 7597 buttons: ['next'], 7598 useFontIcons: false 7599 }); 7600 7601 this.add(this.imageNavLeft); 7602 this.add(this.imageNavRight); 7603 7604 this.imageNavLeft.render(this.frame.get('boundingBox')); 7605 this.imageNavRight.render(this.frame.get('boundingBox')); 7606 7607 this.imageNavLeft.plug(Y.FL.SlideshowOverlay, { hideDelay: this.get('overlayHideDelay') }); 7608 this.imageNavRight.plug(Y.FL.SlideshowOverlay, { hideDelay: this.get('overlayHideDelay') }); 7609 7610 if(this.get('overlayHideOnMousemove')) { 7611 this.frame.once('transitionComplete', this.imageNavLeft.slideshowOverlay.hideWithTimer, this.imageNavLeft.slideshowOverlay); 7612 this.frame.once('transitionComplete', this.imageNavRight.slideshowOverlay.hideWithTimer, this.imageNavRight.slideshowOverlay); 7613 ssBB.on('mousemove', Y.bind(this._toggleImageNav, this)); 7614 ssBB.on('mouseenter', Y.bind(this._toggleImageNav, this)); 7615 } 7616 7617 this.imageNavLeft.get('boundingBox').addClass('fl-slideshow-image-nav-left'); 7618 this.imageNavRight.get('boundingBox').addClass('fl-slideshow-image-nav-right'); 7619 } 7620 } 7621 }, 7622 7623 /** 7624 * @method _positionImageNav 7625 * @protected 7626 */ 7627 _positionImageNav: function() 7628 { 7629 var leftBB = this.imageNavLeft.get('boundingBox'), 7630 rightBB = this.imageNavRight.get('boundingBox'), 7631 imageNavHeight = leftBB.get('offsetHeight'), 7632 frameHeight = this.frame.get('boundingBox').get('offsetHeight'), 7633 top = frameHeight/2 - imageNavHeight/2, 7634 styles = { 7635 top: top + 'px', 7636 display: 'block' 7637 }; 7638 7639 leftBB.setStyles(styles); 7640 rightBB.setStyles(styles); 7641 7642 this._adjustOverlayForVerticalThumbs(leftBB); 7643 this._adjustOverlayForVerticalThumbs(rightBB); 7644 }, 7645 7646 /** 7647 * Toggles the visibility of the image nav buttons. 7648 * 7649 * @method _toggleImageNav 7650 * @protected 7651 */ 7652 _toggleImageNav: function() 7653 { 7654 if(this.imageNavLeft.slideshowOverlay._visible) { 7655 this.imageNavLeft.slideshowOverlay.hideWithTimer(); 7656 } 7657 else { 7658 this.imageNavLeft.slideshowOverlay.show(); 7659 } 7660 7661 if(this.imageNavRight.slideshowOverlay._visible) { 7662 this.imageNavRight.slideshowOverlay.hideWithTimer(); 7663 } 7664 else { 7665 this.imageNavRight.slideshowOverlay.show(); 7666 } 7667 }, 7668 7669 /** 7670 * @method _renderMouseNav 7671 * @protected 7672 */ 7673 _renderMouseNav: function() 7674 { 7675 if(this.get('mouseNavEnabled') && !('ontouchstart' in window) && !window.navigator.msPointerEnabled) { 7676 this.plug(Y.FL.SlideshowMouseNav, { 7677 trigger: this.frame.get('boundingBox') 7678 }); 7679 } 7680 }, 7681 7682 /** 7683 * Checks whether the thumbs are enabled. 7684 * 7685 * @method _thumbsEnabled 7686 * @protected 7687 * @returns Boolean 7688 */ 7689 _thumbsEnabled: function() 7690 { 7691 var navType = this.get('navType'); 7692 7693 if(navType == 'thumbs') { 7694 return true; 7695 } 7696 if((navType == 'buttons' || navType == 'custom') && this._hasNavButton('thumbs')) { 7697 return true; 7698 } 7699 else { 7700 return false;
7701 } 7702 }, 7703 7704 /** 7705 * Creates and renders a new instance of FL.SlideshowThumbs. 7706 * 7707 * @method _renderThumbs 7708 * @protected 7709 */ 7710 _renderThumbs: function() 7711 { 7712 var frameBB, navOverlay, navPosition, navType; 7713 7714 // Destroy old instances 7715 this._destroyThumbs(); 7716 7717 // Create a new instance 7718 if(this._thumbsEnabled()) { 7719 frameBB = this.frame.get('boundingBox'); 7720 navOverlay = this.get('navOverlay'); 7721 navPosition = this.get('navPosition'); 7722 navType = this.get('navType'); 7723 7724 // Create the thumbs 7725 this.thumbs = new Y.FL.SlideshowThumbs(this._getThumbsConfig()); 7726 7727 // This breaks sometimes on SM Next. Try/catch bandaid for now. 7728 try { this.add(this.thumbs); } catch(e) {} 7729 7730 // Overlay setup 7731 if(navType == 'buttons' || navType == 'custom') { 7732 this.thumbs.plug(Y.FL.SlideshowOverlay, { 7733 hideDelay: this.get('overlayHideDelay'), 7734 hideStyle: 'left', 7735 visible: false 7736 }); 7737 } 7738 else if(navType == 'thumbs' && navOverlay) { 7739 this.thumbs.plug(Y.FL.SlideshowOverlay, { 7740 hideDelay: this.get('overlayHideDelay'), 7741 hideStyle: 'left' 7742 }); 7743 } 7744 7745 // Insert 7746 this.thumbs.render(this.get('contentBox')); 7747 7748 if(navPosition == 'top') { 7749 frameBB.insert(this.thumbs.get('boundingBox'), 'before'); 7750 } 7751 else { 7752 frameBB.insert(this.thumbs.get('boundingBox'), 'after'); 7753 } 7754 7755 // Hide overlay thumbs on click 7756 if(this.get('thumbsHideOnClick') && navType != 'thumbs') { 7757 this.thumbs.on('imageClick', Y.bind(this._hideThumbsOnImageClick, this)); 7758 } 7759 7760 this._syncThumbs(); 7761 } 7762 }, 7763 7764 /** 7765 * Destroy the current thumbs instance. 7766 * 7767 * @method _destroyThumbs 7768 * @protected 7769 */ 7770 _destroyThumbs: function() 7771 { 7772 if(this.thumbs !== null) { 7773 if(this.thumbs.slideshowOverlay) { 7774 this.thumbs.slideshowOverlay.destroy(); 7775 } 7776 7777 this.thumbs.get('boundingBox').remove(); 7778 this.remove(this.thumbs); 7779 try { this.thumbs.destroy(true); } catch(e) {} 7780 this.thumbs = null; 7781 } 7782 }, 7783 7784 /** 7785 * Syncs the thumbs UI styles. 7786 * 7787 * @method _syncThumbs 7788 * @protected 7789 */ 7790 _syncThumbs: function() 7791 { 7792 var thumbsBB = this.thumbs.get('boundingBox'), 7793 navOverlay = this.get('navOverlay'), 7794 navPosition = this.get('navPosition'), 7795 navType = this.get('navType'), 7796 paddingType = 'padding' + navPosition.charAt(0).toUpperCase() + navPosition.slice(1), 7797 navHeight = 0; 7798 7799 if(navType == 'buttons') { 7800 navHeight = parseInt(this.nav.get('boundingBox').getComputedStyle('height'), 10); 7801 thumbsBB.setStyle('position', 'absolute'); 7802 7803 if(navOverlay) { 7804 thumbsBB.setStyle(paddingType, navHeight + 'px'); 7805 thumbsBB.setStyle(navPosition, '0px'); 7806 } 7807 else { 7808 thumbsBB.setStyle(navPosition, navHeight + 'px'); 7809 } 7810 } 7811 if(navType == 'custom' || (navType == 'thumbs' && navOverlay)) { 7812 thumbsBB.setStyle('position', 'absolute'); 7813 thumbsBB.setStyle(navPosition, '0px'); 7814 } 7815 7816 this.thumbs.resize(); 7817 }, 7818 7819 /** 7820 * Prepares and returns the thumbs config object. 7821 * 7822 * @method _getThumbsConfig 7823 * @protected 7824 * @returns Object 7825 */ 7826 _getThumbsConfig: function() 7827 { 7828 var attrs = this.getAttrs(), 7829 navType = this.get('navType'), 7830 imageConfig = { 7831 crop: attrs.thumbsImageCrop, 7832 width: attrs.thumbsImageWidth, 7833 height: attrs.thumbsImageHeight 7834 }, 7835 config = { 7836 columns: 'auto', 7837 rows: 1, 7838 horizontalSpacing: attrs.thumbsHorizontalSpacing, 7839 verticalSpacing: attrs.thumbsVerticalSpacing, 7840 spaceEvenly: attrs.thumbsSpaceEvenly, 7841 centerSinglePage: attrs.thumbsCenterSinglePage, 7842 pauseOnClick: attrs.thumbsPauseOnClick, 7843 transition: attrs.thumbsTransition, 7844 transitionDirection: attrs.thumbsTransitionDirection, 7845 transitionEasing: attrs.thumbsTransitionEasing, 7846 leftNavButtons: attrs.navButtonsLeft, 7847 rightNavButtons: attrs.navButtonsRight, 7848 imageConfig: imageConfig, 7849 touchSupport: true 7850 }; 7851 7852 if(navType == 'buttons' || navType == 'custom') { 7853 if('ontouchstart' in window) { 7854 config.leftNavEnabled = false; 7855 config.rightNavEnabled = false; 7856 } 7857 else { 7858 config.centerSinglePage = false;
7859 config.leftNavButtons = ['prevPage']; 7860 config.rightNavButtons = ['nextPage']; 7861 } 7862 } 7863 7864 return config; 7865 }, 7866 7867 /** 7868 * Resizes the thumbs. 7869 * 7870 * @method _resizeThumbs 7871 * @protected 7872 */ 7873 _resizeThumbs: function() 7874 { 7875 if(this.thumbs) { 7876 this.thumbs.resize(); 7877 } 7878 }, 7879 7880 /** 7881 * Shows or hides the thumbs. 7882 * 7883 * @method _toggleThumbs 7884 * @protected 7885 */ 7886 _toggleThumbs: function() 7887 { 7888 this._toggleOverlay(this.thumbs.slideshowOverlay); 7889 }, 7890 7891 /** 7892 * Hides the thumbs when a thumb image is clicked. 7893 * 7894 * @method _hideThumbsOnImageClick 7895 * @protected 7896 */ 7897 _hideThumbsOnImageClick: function() 7898 { 7899 if(this.thumbs.slideshowOverlay) { 7900 this.thumbs.slideshowOverlay._focus = false; 7901 this.thumbs.slideshowOverlay.enable(); 7902 this.thumbs.slideshowOverlay.hide(); 7903 7904 if(this.nav && this.nav.slideshowOverlay) { 7905 this.nav.slideshowOverlay.enable(); 7906 } 7907 } 7908 }, 7909 7910 /** 7911 * Creates and renders a new instance of FL.SlideshowCaption. 7912 * 7913 * @method _renderCaption 7914 * @protected 7915 */ 7916 _renderCaption: function() 7917 { 7918 if(this._hasNavButton('caption')) { 7919 7920 this.caption = new Y.FL.SlideshowCaption({ 7921 lessLinkText: this.get('captionLessLinkText'), 7922 moreLinkText: this.get('captionMoreLinkText'), 7923 textLength: this.get('captionTextLength'), 7924 stripTags: this.get('captionStripTags') 7925 }); 7926 7927 this.add(this.caption); 7928 7929 this.caption.plug(Y.FL.SlideshowOverlay, { 7930 hideDelay: this.get('overlayHideDelay'), 7931 visible: false, 7932 closeButton: true 7933 }); 7934 7935 this._syncCaption(); 7936 } 7937 }, 7938 7939 /** 7940 * Syncs the caption UI styles. 7941 * 7942 * @method _syncCaption 7943 * @protected 7944 */ 7945 _syncCaption: function() 7946 { 7947 var captionBB = this.caption.get('boundingBox'), 7948 navOverlay = this.get('navOverlay'), 7949 navPosition = this.get('navPosition'), 7950 nav = this._getNav(), 7951 paddingType = 'padding' + navPosition.charAt(0).toUpperCase() + navPosition.slice(1), 7952 navHeight = 0; 7953 7954 captionBB.setStyle('position', 'absolute'); 7955 7956 if(nav) { 7957 navHeight = parseInt(nav.get('boundingBox').getComputedStyle('height'), 10); 7958 } 7959 7960 if(nav && navOverlay) { 7961 captionBB.setStyle(paddingType, navHeight + 'px'); 7962 captionBB.setStyle(navPosition, '0px'); 7963 } 7964 else { 7965 captionBB.setStyle(navPosition, navHeight + 'px'); 7966 } 7967 }, 7968 7969 /** 7970 * Shows or hides the caption. 7971 * 7972 * @method _toggleCaption 7973 * @protected 7974 */ 7975 _toggleCaption: function() 7976 { 7977 this._toggleOverlay(this.caption.slideshowOverlay); 7978 }, 7979 7980 /** 7981 * Creates and renders a new instance of FL.SlideshowSocial. 7982 * 7983 * @method _renderSocial 7984 * @protected 7985 */ 7986 _renderSocial: function() 7987 { 7988 if(this._hasNavButton('social')) { 7989 this.social = new Y.FL.SlideshowSocial(); 7990 this.add(this.social); 7991 7992 this.social.plug(Y.FL.SlideshowOverlay, { 7993 hideDelay: this.get('overlayHideDelay'), 7994 visible: false, 7995 closeButton: true 7996 }); 7997 7998 this._syncSocial(); 7999 } 8000 }, 8001 8002 /** 8003 * Syncs the social UI styles. 8004 * 8005 * @method _syncSocial 8006 * @protected 8007 */ 8008 _syncSocial: function() 8009 { 8010 var socialBB = this.social.get('boundingBox'), 8011 navOverlay = this.get('navOverlay'), 8012 navPosition = this.get('navPosition'), 8013 nav = this._getNav(), 8014 paddingType = 'padding' + navPosition.charAt(0).toUpperCase() + navPosition.slice(1), 8015 navHeight = 0; 8016 8017 socialBB.setStyle('position', 'absolute'); 8018 8019 if(nav) { 8020 navHeight = parseInt(nav.get('boundingBox').getComputedStyle('height'), 10); 8021 } 8022 if(nav && navOverlay) { 8023 socialBB.setStyle(paddingType, navHeight + 'px'); 8024 socialBB.setStyle(navPosition, '0px'); 8025 } 8026 else { 8027 socialBB.setStyle(navPosition, navHeight + 'px'); 8028 } 8029 }, 8030 8031 /** 8032 * Shows or hides the social buttons. 8033 * 8034 * @method _toggleSocial 8035 * @protected 8036 */ 8037 _toggleSocial: function() 8038 { 8039 this._toggleOverlay(this.social.slideshowOverlay); 8040 }, 8041 8042 /** 8043 * Shows or hides an overlaid widget based 8044 * on its current visibility. 8045 * 8046 * @method _toggleOverlay 8047 * @param overlay {Object} The overlay to toggle. 8048 * @protected 8049 */ 8050 _toggleOverlay: function(overlay) 8051 { 8052 var navType = this.get('navType'), 8053 nav = this._getNav(); 8054 8055 if(overlay._visible) { 8056 if(nav && nav.slideshowOverlay) { 8057 nav.slideshowOverlay.enable(); 8058 } 8059 overlay.enable(); 8060 overlay.hide(); 8061 } 8062 else { 8063 if(nav && nav.slideshowOverlay) { 8064 nav.slideshowOverlay.disable(); 8065 } 8066 overlay.show(); 8067 overlay.disable(); 8068 } 8069 8070 if(this.thumbs && navType != 'thumbs' && this.thumbs.slideshowOverlay !== overlay) { 8071 this.thumbs.slideshowOverlay.enable(); 8072 this.thumbs.slideshowOverlay.hide(); 8073 } 8074 if(this.caption && this.caption.slideshowOverlay !== overlay) { 8075 this.caption.slideshowOverlay.enable(); 8076 this.caption.slideshowOverlay.hide(); 8077 } 8078 if(this.social && this.social.slideshowOverlay !== overlay) { 8079 this.social.slideshowOverlay.enable(); 8080 this.social.slideshowOverlay.hide(); 8081 } 8082 }, 8083 8084 /** 8085 * Called when an overlay's close button is clicked. 8086 * 8087 * @method _overlayCloseClick 8088 * @protected 8089 */ 8090 _overlayCloseClick: function() 8091 { 8092 if(this.nav && this.nav.slideshowOverlay) { 8093 this.nav.slideshowOverlay.enable(); 8094 } 8095 if(this.thumbs && this.thumbs.slideshowOverlay) { 8096 this.thumbs.slideshowOverlay.enable(); 8097 } 8098 if(this.caption) { 8099 this.caption.slideshowOverlay.enable(); 8100 } 8101 if(this.social) { 8102 this.social.slideshowOverlay.enable(); 8103 } 8104 if(this.imageNavLeft) { 8105 this.imageNavLeft.slideshowOverlay.enable(); 8106 this.imageNavRight.slideshowOverlay.enable(); 8107 } 8108 }, 8109 8110 /** 8111 * Hides all overlaid widgets. 8112 * 8113 * @method _hideAllOverlays 8114 * @protected 8115 */ 8116 _hideAllOverlays: function() 8117 { 8118 if(this.nav && this.nav.slideshowOverlay && this.nav.slideshowOverlay._visible) { 8119 this.nav.slideshowOverlay.enable(); 8120 this.nav.slideshowOverlay.hideWithTimer(); 8121 } 8122 if(this.thumbs && this.thumbs.slideshowOverlay && this.thumbs.slideshowOverlay._visible) { 8123 this.thumbs.slideshowOverlay.enable(); 8124 this.thumbs.slideshowOverlay.hideWithTimer(); 8125 } 8126 if(this.caption && this.caption.slideshowOverlay._visible) { 8127 this.caption.slideshowOverlay.enable(); 8128 this.caption.slideshowOverlay.hideWithTimer(); 8129 } 8130 if(this.social && this.social.slideshowOverlay._visible) { 8131 this.social.slideshowOverlay.enable(); 8132 this.social.slideshowOverlay.hideWithTimer(); 8133 } 8134 if(this.imageNavLeft) { 8135 this.imageNavLeft.slideshowOverlay.enable(); 8136 this.imageNavLeft.slideshowOverlay.hideWithTimer(); 8137 this.imageNavRight.slideshowOverlay.enable(); 8138 this.imageNavRight.slideshowOverlay.hideWithTimer(); 8139 } 8140 }, 8141 8142 /** 8143 * Checks if overlays are still visible when the mouse enters 8144 * the bounding box. If they are, overlay functionality is disabled 8145 * until the overlays are closed by a button or the mouse leaves 8146 * the bounding box. If only the nav overlay is visible, this 8147 * function does nothing. 8148 *
8149 * @method _checkOverlaysOnMouseenter 8150 * @protected 8151 */ 8152 _checkOverlaysOnMouseenter: function() 8153 { 8154 var navType = this.get('navType'), 8155 navOverlay = this.get('navOverlay'), 8156 nav = this._getNav(), 8157 overlayVisible = false; 8158 8159 if(this.thumbs && navType != 'thumbs' && this.thumbs.slideshowOverlay._visible) { 8160 overlayVisible = true; 8161 this.thumbs.slideshowOverlay.disable(); 8162 } 8163 else if(this.caption && this.caption.slideshowOverlay._visible) { 8164 overlayVisible = true; 8165 this.caption.slideshowOverlay.disable(); 8166 } 8167 else if(this.social && this.social.slideshowOverlay._visible) { 8168 overlayVisible = true; 8169 this.social.slideshowOverlay.disable(); 8170 } 8171 8172 if(nav && overlayVisible && navOverlay) { 8173 nav.slideshowOverlay.disable(); 8174 } 8175 }, 8176 8177 /** 8178 * Checks whether a nav button is set or not. 8179 * 8180 * @method _hasNavButton 8181 * @protected 8182 * @param button {String} The button to look for. 8183 * @returns Boolean 8184 */ 8185 _hasNavButton: function(button) 8186 { 8187 var navType = this.get('navType'); 8188 8189 if(navType == 'buttons' || navType == 'thumbs' || navType == 'custom') { 8190 if(Y.Array.indexOf(this.get('navButtons'), button) > -1) { 8191 return true; 8192 } 8193 else if(Y.Array.indexOf(this.get('navButtonsLeft'), button) > -1) { 8194 return true; 8195 } 8196 else if(Y.Array.indexOf(this.get('navButtonsRight'), button) > -1) { 8197 return true; 8198 } 8199 else { 8200 return false; 8201 } 8202 } 8203 else { 8204 return false; 8205 } 8206 }, 8207 8208 /** 8209 * @method _removeNavButton 8210 * @param button {String} The name of the button to remove. 8211 * @protected 8212 */ 8213 _removeNavButton: function(button) 8214 { 8215 var buttons = this.get('navButtons'), 8216 buttonsLeft = this.get('navButtonsLeft'), 8217 buttonsRight = this.get('navButtonsRight'), 8218 vtTopNavButtons = this.get('verticalThumbsTopNavButtons'), 8219 vtBottomNavButtons = this.get('verticalThumbsBottomNavButtons'); 8220 8221 if(Y.Array.indexOf(buttons, button) > -1) { 8222 buttons.splice(Y.Array.indexOf(buttons, button), 1); 8223 } 8224 if(Y.Array.indexOf(buttonsLeft, button) > -1) { 8225 buttonsLeft.splice(Y.Array.indexOf(buttonsLeft, button), 1); 8226 } 8227 if(Y.Array.indexOf(buttonsRight, button) > -1) { 8228 buttonsRight.splice(Y.Array.indexOf(buttonsRight, button), 1); 8229 } 8230 if(Y.Array.indexOf(vtTopNavButtons, button) > -1) { 8231 vtTopNavButtons.splice(Y.Array.indexOf(vtTopNavButtons, button), 1); 8232 } 8233 if(Y.Array.indexOf(vtBottomNavButtons, button) > -1) { 8234 vtBottomNavButtons.splice(Y.Array.indexOf(vtBottomNavButtons, button), 1); 8235 } 8236 8237 this._set('navButtons', buttons); 8238 this._set('navButtonsLeft', buttonsLeft); 8239 this._set('navButtonsRight', buttonsRight); 8240 this._set('verticalThumbsTopNavButtons', vtTopNavButtons); 8241 this._set('verticalThumbsBottomNavButtons', vtBottomNavButtons); 8242 } 8243 8244}, { 8245 8246 /** 8247 * Custom CSS class name for the widget. 8248 * 8249 * @property CSS_PREFIX
8250 * @type String 8251 * @protected 8252 * @static 8253 */ 8254 CSS_PREFIX: 'fl-slideshow', 8255 8256 /** 8257 * Static property used to define the default attribute configuration of 8258 * the Widget. 8259 * 8260 * @property ATTRS 8261 * @type Object 8262 * @protected 8263 * @static 8264 */ 8265 ATTRS: { 8266 8267 /** 8268 * What should happen when the main image is clicked. 8269 * Options are none, gallery and url. If url is chosen, 8270 * clickActionUrl must be set. 8271 * 8272 * @attribute clickAction 8273 * @type String 8274 * @default none 8275 */ 8276 clickAction: { 8277 value: 'none' 8278 }, 8279 8280 /** 8281 * The redirect url to use when clickAction is set to url. 8282 * 8283 * @attribute clickActionUrl 8284 * @type String 8285 * @default none 8286 */ 8287 clickActionUrl: { 8288 value: '' 8289 }, 8290 8291 /** 8292 * Whether to crop the main image. 8293 * 8294 * @attribute crop 8295 * @type Boolean 8296 * @default false 8297 */ 8298 crop: { 8299 value: false 8300 }, 8301 8302 /** 8303 * Whether to only crop horizontal images or not. 8304 * 8305 * @attribute cropHorizontalsOnly 8306 * @type Boolean 8307 * @default false 8308 */ 8309 cropHorizontalsOnly: { 8310 value: false 8311 }, 8312 8313 /** 8314 * Whether to always use the loading image between images 8315 * or to only use it between albums. 8316 * 8317 * @attribute loadingImageAlwaysEnabled 8318 * @type Boolean 8319 * @default true 8320 */ 8321 loadingImageAlwaysEnabled: { 8322 value: true 8323 }, 8324 8325 /** 8326 * The x and y position of the main image 8327 * within the bounding box. 8328 * 8329 * @attribute position
8330 * @type String 8331 * @default center center 8332 */ 8333 position: { 8334 value: 'center center' 8335 }, 8336 8337 /** 8338 * Whether to right click protect the main image. 8339 * 8340 * @attribute protect 8341 * @type Boolean 8342 * @default true 8343 */ 8344 protect: { 8345 value: true 8346 }, 8347 8348 /** 8349 * Whether to resize the main image past 8350 * its original width and height. 8351 * 8352 * @attribute upsize 8353 * @type Boolean 8354 * @default true 8355 */ 8356 upsize: { 8357 value: true 8358 }, 8359 8360 /** 8361 * The type of transition to use. Possible values are 8362 * none, fade, slideHorizontal and slideVertical. The 8363 * value can also be a common separated string of transitions 8364 * that will be randomly chosen for each image. 8365 * 8366 * @attribute transition 8367 * @type String 8368 * @default fade 8369 */ 8370 transition: { 8371 value: 'fade' 8372 }, 8373 8374 /** 8375 * The duration of the transition, measured in seconds. 8376 * 8377 * @attribute transitionDuration 8378 * @type Number 8379 * @default 1 8380 */ 8381 transitionDuration: { 8382 value: 1 8383 }, 8384 8385 /** 8386 * The type of transition easing to use. 8387 * 8388 * @attribute transitionEasing
8389 * @type String 8390 * @default ease-out 8391 */ 8392 transitionEasing: { 8393 value: 'ease-out' 8394 }, 8395 8396 /** 8397 * The amount of zoom to use for the Ken Burns effect. 8398 * 8399 * @attribute kenBurnsZoom 8400 * @type Number 8401 * @default 1.2 8402 */ 8403 kenBurnsZoom: { 8404 value: 1.2 8405 }, 8406 8407 /** 8408 * The type of navigation to use. Possible values are 8409 * buttons, thumbs, custom and none. 8410 * 8411 * @attribute navType 8412 * @type String 8413 * @default none 8414 */ 8415 navType: { 8416 value: 'none' 8417 }, 8418 8419 /** 8420 * The position of the main nav. Possible values are top and bottom. 8421 * 8422 * @attribute navPosition 8423 * @type String 8424 * @default bottom 8425 */ 8426 navPosition: { 8427 value: 'bottom' 8428 }, 8429 8430 /** 8431 * Whether to overlay the nav on top of the main image. 8432 * 8433 * @attribute navOverlay 8434 * @type Boolean 8435 * @default false 8436 */ 8437 navOverlay: { 8438 value: false 8439 }, 8440 8441 /** 8442 * An array of button names used to render the main nav's buttons. 8443 * 8444 * @attribute navButtons 8445 * @type Array 8446 * @default [] 8447 */ 8448 navButtons: { 8449 value: [] 8450 }, 8451 8452 /** 8453 * An array of button names used to render the main nav's left buttons. 8454 * 8455 * @attribute navButtonsLeft 8456 * @type Array 8457 * @default [] 8458 */ 8459 navButtonsLeft: { 8460 value: [] 8461 }, 8462 8463 /** 8464 * An array of button names used to render the main nav's right buttons. 8465 * 8466 * @attribute navButtonsRight 8467 * @type Array 8468 * @default [] 8469 */ 8470 navButtonsRight: { 8471 value: [] 8472 }, 8473 8474 /** 8475 * Whether to hide the overlays when the mouse moves or not. 8476 * 8477 * @attribute overlayHideOnMousemove
8478 * @type String 8479 * @default mouseover 8480 */ 8481 overlayHideOnMousemove: { 8482 value: true 8483 }, 8484 8485 /** 8486 * How long to wait before hiding the overlays. 8487 * Measured in milliseconds. 8488 * 8489 * @attribute overlayHideDelay 8490 * @type Number 8491 * @default false 8492 */ 8493 overlayHideDelay: { 8494 value: 3000 8495 }, 8496 8497 /** 8498 * Whether to use the image nav or not. If true, a prev 8499 * and next button will be overlaid on the main image. 8500 * 8501 * @attribute imageNavEnabled 8502 * @type Boolean 8503 * @default false 8504 */ 8505 imageNavEnabled: { 8506 value: false 8507 }, 8508 8509 /** 8510 * Whether to use the mouse nav or not. If true, the cursor 8511 * will turn into a prev or next button when over the slideshow. 8512 * 8513 * @attribute mouseNavEnabled 8514 * @type Boolean 8515 * @default false 8516 */ 8517 mouseNavEnabled: { 8518 value: false 8519 }, 8520 8521 /** 8522 * Whether to hide the thumbs when clicking on a thumbnail 8523 * image or not. Thumbs always hide navType is set to buttons. 8524 * 8525 * @attribute thumbsHideOnClick 8526 * @type Boolean 8527 * @default false 8528 */ 8529 thumbsHideOnClick: { 8530 value: true 8531 }, 8532 8533 /** 8534 * The horizontal spacing between thumbs. 8535 * 8536 * @attribute thumbsHorizontalSpacing 8537 * @type Number 8538 * @default 15 8539 */ 8540 thumbsHorizontalSpacing: { 8541 value: 15 8542 }, 8543 8544 /** 8545 * The vertical spacing between thumbs. 8546 * 8547 * @attribute thumbsVerticalSpacing 8548 * @type Number 8549 * @default 15 8550 */ 8551 thumbsVerticalSpacing: { 8552 value: 15 8553 }, 8554 8555 /** 8556 * Whether to space the thumbs evenly within a page. 8557 * 8558 * @attribute thumbsSpaceEvenly 8559 * @type Boolean 8560 * @default true 8561 */ 8562 thumbsSpaceEvenly: { 8563 value: true 8564 }, 8565 8566 /** 8567 * Whether to center single pages of thumbs. 8568 * 8569 * @attribute thumbsCenterSinglePage 8570 * @type Boolean 8571 * @default false 8572 */ 8573 thumbsCenterSinglePage: { 8574 value: true 8575 }, 8576 8577 /** 8578 * Whether to pause the slideshow when a thumb is clicked. 8579 * 8580 * @attribute thumbsPauseOnClick 8581 * @type Boolean 8582 * @default false 8583 */ 8584 thumbsPauseOnClick: { 8585 value: false 8586 }, 8587 8588 /** 8589 * The type of transition to use between pages of thumbs. 8590 * 8591 * @attribute thumbsTransition
8592 * @type String 8593 * @default slideHorizontal 8594 */ 8595 thumbsTransition: { 8596 value: 'slideHorizontal' 8597 }, 8598 8599 /** 8600 * The duration of the transition between pages of thumbs. 8601 * 8602 * @attribute thumbsTransitionDuration 8603 * @type Number 8604 * @default 0.8 8605 */ 8606 thumbsTransitionDuration: { 8607 value: 0.8 8608 }, 8609 8610 /** 8611 * The type of transition easing to use between pages of thumbs. 8612 * 8613 * @attribute thumbsTransitionEasing 8614 * @type String 8615 * @default ease-out 8616 */ 8617 thumbsTransitionEasing: { 8618 value: 'ease-out' 8619 }, 8620 8621 /** 8622 * Whether to crop the thumbnails. 8623 * 8624 * @attribute thumbsImageCrop 8625 * @type Boolean 8626 * @default true 8627 */ 8628 thumbsImageCrop: { 8629 value: true 8630 }, 8631 8632 /** 8633 * The width of each thumbnail. 8634 * 8635 * @attribute thumbsImageWidth 8636 * @type Number 8637 * @default 50 8638 */ 8639 thumbsImageWidth: { 8640 value: 50 8641 }, 8642 8643 /** 8644 * The height of each thumbnail. 8645 * 8646 * @attribute thumbsImageHeight 8647 * @type Number 8648 * @default 50 8649 */ 8650 thumbsImageHeight: { 8651 value: 50 8652 }, 8653 8654 /** 8655 * The text to use for the "read less" toggle link. 8656 * 8657 * @attribute captionLessLinkText
8658 * @type String 8659 * @default Read Less 8660 */ 8661 captionLessLinkText: { 8662 value: 'Read Less' 8663 }, 8664 8665 /** 8666 * The text to use for the "read more" toggle link. 8667 * 8668 * @attribute captionMoreLinkText 8669 * @type String 8670 * @default Read More 8671 */ 8672 captionMoreLinkText: { 8673 value: 'Read More' 8674 }, 8675 8676 /** 8677 * The length of the caption to show. If greater than -1, 8678 * the text will be truncated and a read more link will 8679 * be displayed. If set to -1, the entire caption will be shown. 8680 * 8681 * @attribute captionTextLength 8682 * @type Number 8683 * @default 200 8684 */ 8685 captionTextLength: { 8686 value: 200 8687 }, 8688 8689 /** 8690 * Whether to strip out HTML tags in the caption 8691 * text or not. 8692 * 8693 * @attribute captionStripTags 8694 * @type Boolean 8695 * @default false 8696 */ 8697 captionStripTags: { 8698 value: false 8699 }, 8700 8701 /** 8702 * Whether to use the vertical thumbs or not. 8703 * 8704 * @attribute verticalThumbsEnabled 8705 * @type Boolean 8706 * @default false 8707 */ 8708 verticalThumbsEnabled: { 8709 value: false 8710 }, 8711 8712 /** 8713 * Position of the vertical thumbs. Possible values 8714 * are either left or right. 8715 * 8716 * @attribute verticalThumbsPosition
8717 * @type String 8718 * @default left 8719 */ 8720 verticalThumbsPosition: { 8721 value: 'left' 8722 }, 8723 8724 /** 8725 * Whether to overlay the vertical thumbs 8726 * on the main image or not. 8727 * 8728 * @attribute verticalThumbsOverlay 8729 * @type Boolean 8730 * @default false 8731 */ 8732 verticalThumbsOverlay: { 8733 value: false 8734 }, 8735 8736 /** 8737 * The number of columns for the vertical thumbs. 8738 * 8739 * @attribute verticalThumbsColumns 8740 * @type Number 8741 * @default 1 8742 */ 8743 verticalThumbsColumns: { 8744 value: 1 8745 }, 8746 8747 /** 8748 * Whether to use the vertical thumbs top nav or not. 8749 * 8750 * @attribute verticalThumbsTopNavEnabled 8751 * @type Boolean 8752 * @default false 8753 */ 8754 verticalThumbsTopNavEnabled: { 8755 value: false 8756 }, 8757 8758 /** 8759 * An array of button names used to render 8760 * the vertical thumbs top nav buttons. 8761 * 8762 * @attribute verticalThumbsTopNavButtons 8763 * @type Array 8764 * @default prevPage, nextPage 8765 */ 8766 verticalThumbsTopNavButtons: { 8767 value: ['prevPage', 'nextPage'] 8768 }, 8769 8770 /** 8771 * Whether to use the vertical thumbs bottom nav or not. 8772 * 8773 * @attribute verticalThumbsBottomNavEnabled 8774 * @type Boolean 8775 * @default false 8776 */ 8777 verticalThumbsBottomNavEnabled: { 8778 value: true 8779 }, 8780 8781 /** 8782 * An array of button names used to render 8783 * the vertical thumbs top nav buttons. 8784 * 8785 * @attribute verticalThumbsBottomNavButtons 8786 * @type Array 8787 * @default prevPage, nextPage 8788 */ 8789 verticalThumbsBottomNavButtons: { 8790 value: ['prevPage', 'nextPage'] 8791 }, 8792 8793 /** 8794 * The horizontal spacing between vertical thumbs. 8795 * 8796 * @attribute verticalThumbsHorizontalSpacing 8797 * @type Number 8798 * @default 15 8799 */ 8800 verticalThumbsHorizontalSpacing: { 8801 value: 15 8802 }, 8803 8804 /** 8805 * The vertical spacing between vertical thumbs. 8806 * 8807 * @attribute verticalThumbsVerticalSpacing 8808 * @type Number 8809 * @default 15 8810 */ 8811 verticalThumbsVerticalSpacing: { 8812 value: 15 8813 }, 8814 8815 /** 8816 * Whether to space the vertical thumbs evenly within a page. 8817 * 8818 * @attribute verticalThumbsSpaceEvenly 8819 * @type Boolean 8820 * @default false 8821 */ 8822 verticalThumbsSpaceEvenly: { 8823 value: false 8824 }, 8825 8826 /** 8827 * Whether to pause the slideshow when a vertical thumb is clicked. 8828 * 8829 * @attribute verticalThumbsPauseOnClick 8830 * @type Boolean 8831 * @default false 8832 */ 8833 verticalThumbsPauseOnClick: { 8834 value: false 8835 }, 8836 8837 /** 8838 * Whether to crop the vertical thumbs or not. 8839 * 8840 * @attribute verticalThumbsImageCrop 8841 * @type Boolean 8842 * @default true 8843 */ 8844 verticalThumbsImageCrop: { 8845 value: true 8846 }, 8847 8848 /** 8849 * The width of each vertical thumbnail. 8850 * 8851 * @attribute verticalThumbsImageWidth 8852 * @type Number 8853 * @default 75 8854 */ 8855 verticalThumbsImageWidth: { 8856 value: 75 8857 }, 8858 8859 /** 8860 * The height of each vertical thumbnail. 8861 * 8862 * @attribute verticalThumbsImageHeight 8863 * @type Number 8864 * @default 75 8865 */ 8866 verticalThumbsImageHeight: { 8867 value: 75 8868 }, 8869 8870 /** 8871 * The type of transition to use between pages of vertical thumbs. 8872 * 8873 * @attribute verticalThumbsTransition
8874 * @type String 8875 * @default slideVertical 8876 */ 8877 verticalThumbsTransition: { 8878 value: 'slideVertical' 8879 }, 8880 8881 /** 8882 * The duration of the transition between pages of vertical thumbs. 8883 * 8884 * @attribute verticalThumbsTransitionDuration 8885 * @type Number 8886 * @default 0.8 8887 */ 8888 verticalThumbsTransitionDuration: { 8889 value: 0.8 8890 }, 8891 8892 /** 8893 * The type of transition easing to use between pages of vertical thumbs. 8894 * 8895 * @attribute verticalThumbsTransitionEasing 8896 * @type String 8897 * @default ease-out 8898 */ 8899 verticalThumbsTransitionEasing: { 8900 value: 'ease-out' 8901 }, 8902 8903 /** 8904 * Whether to use the Facebook like button or not. 8905 * 8906 * @attribute likeButtonEnabled 8907 * @type Boolean 8908 * @default true 8909 */ 8910 likeButtonEnabled: { 8911 value: true 8912 }, 8913 8914 /** 8915 * Whether to use the Pinterest button or not. 8916 * 8917 * @attribute pinterestButtonEnabled 8918 * @type Boolean 8919 * @default true 8920 */ 8921 pinterestButtonEnabled: { 8922 value: true 8923 }, 8924 8925 /** 8926 * Whether to use the Tweet button or not. 8927 * 8928 * @attribute tweetButtonEnabled 8929 * @type Boolean 8930 * @default true 8931 */ 8932 tweetButtonEnabled: { 8933 value: true 8934 }, 8935 8936 /** 8937 * Whether to use touch gestures, when available, 8938 * to transition between images or not. 8939 * 8940 * @attribute touchSupport 8941 * @type Boolean 8942 * @default true 8943 */ 8944 touchSupport: { 8945 value: true 8946 }, 8947 8948 /** 8949 * Whether to fallback to link tags or use button tags. 8950 * 8951 * @attribute fallback 8952 * @type Boolean 8953 * @default false 8954 */ 8955 fallback: { 8956 value: false 8957 }, 8958 } 8959}); 8960 8961 8962}, '2.0.0' ,{requires:['anim', 'event-mouseenter', 'plugin', 'transition', 'fl-event-move', 'fl-slideshow-css', 'fl-slideshow-base', 'fl-utils', 'sm-fonticon']}); 8963 8964 8965YUI.add('fl-slideshow-album-loader', function(Y) { 8966 8967/** 8968 * @module fl-slideshow-album-loader 8969 */ 8970 8971/** 8972 * Loads slideshow albums using a provided source object. 8973 * 8974 * @namespace FL 8975 * @class SlideshowAlbumLoader 8976 * @constructor 8977 * @param config {Object} Configuration object 8978 * @extends Base 8979 */ 8980Y.namespace('FL').SlideshowAlbumLoader = Y.Base.create('fl-slideshow-album-loader', Y.Base, [], { 8981 8982 /** 8983 * The source object used for loading. 8984 * 8985 * @property _source 8986 * @type Object 8987 * @default null 8988 * @protected 8989 */ 8990 _source: null, 8991 8992 /** 8993 * Loads slideshow album data using the provided source object. 8994 * 8995 * @method load 8996 * @param source {Object} The source object to use for loading. 8997 */ 8998 load: function(source) 8999 { 9000 this._source = source; 9001 9002 /** 9003 * Fires before a new load request is made. 9004 * 9005 * @event start 9006 */ 9007 this.fire('start'); 9008 9009 this[Y.FL.SlideshowAlbumLoader.TYPES[source.type]].call(this); 9010 }, 9011 9012 /** 9013 * Called when a source type completes loading 9014 * and fires the complete event. 9015 * 9016 * @method _loadComplete 9017 * @param o {Object} Passed to complete event subscribers. 9018 * @protected 9019 */ 9020 _loadComplete: function(o) 9021 { 9022 o = this._randomize(o); 9023 9024 /** 9025 * Fires after a new load request is made. 9026 * 9027 * @event complete 9028 */ 9029 this.fire('complete', o); 9030 }, 9031 9032 /** 9033 * Randomizes images in an album. 9034 * 9035 * @method _randomize 9036 * @param album {Object} The album to randomize. 9037 * @protected 9038 */ 9039 _randomize: function(o) 9040 { 9041 var i; 9042 9043 if(this.get('randomize')) { 9044 o.albumInfo.images.sort(function() { return 0.5 - Math.random(); }); 9045 9046 for(i = 0; i < o.albumInfo.images.length; i++) { 9047 o.albumInfo.images[i].index = i; 9048 } 9049 } 9050 9051 return o; 9052 }, 9053 9054 /** 9055 * Loads slideshow album data from SmugMug. 9056 * 9057 * @method _loadSmugMug 9058 * @protected 9059 */ 9060 _loadSmugMug: function() 9061 { 9062 var sm = new Y.FL.SmugMugAPI(); 9063 9064 sm.on('complete', this._loadSmugMugSuccess, this); 9065 sm.addParam('method', 'smugmug.images.get'); 9066 sm.addParam('AlbumID', this._source.id); 9067 sm.addParam('AlbumKey', this._source.key); 9068 sm.addParam('Extras', 'Caption,Format,FileName'); 9069 9070 // Gallery password 9071 if(this._source.password) { 9072 sm.addParam('Password', this._source.password); 9073 } 9074 9075 // Site-wide password 9076 if(this._source.sp) { 9077 sm.addParam('SitePassword', this._source.sp); 9078 } 9079 9080 sm.request(); 9081 }, 9082 9083 /** 9084 * Processes slideshow album data loaded from SmugMug. 9085 * 9086 * @method _loadSmugMugSuccess 9087 * @param e {Object} The custom event object passed to this function. 9088 * @protected 9089 */ 9090 _loadSmugMugSuccess: function(e) 9091 { 9092 var images = e.Album.Images, 9093 album = {}, 9094 proxy = typeof this._source.proxy !== 'undefined' ? this._source.proxy : '', 9095 buyBase = '', 9096 baseURL = '', 9097 ext = '', 9098 format = '', 9099 i = 0, 9100 temp = null, 9101 iframe = null; 9102 9103 album.index = this._source.index; 9104 album.id = e.Album.id; 9105 album.key = e.Album.Key; 9106 album.link = e.Album.URL; 9107 album.title = this._source.title ? this._source.title : ''; 9108 album.images = []; 9109 buyBase = album.link.replace('https://', '').split('/').shift(); 9110 buyBase = 'https://' + buyBase + '/buy/' + e.Album.id + '_' + e.Album.Key + '/'; 9111 9112 for(i = 0; i < images.length; i++) 9113 { 9114 baseURL = proxy + e.Album.URL + '/' + images[i].id + '_' + images[i].Key; 9115 format = images[i].Format.toLowerCase(); 9116 ext = format == 'mp4' ? '.jpg' : '.' + format; 9117 9118 album.images[i] = {}; 9119 album.images[i].index = i; 9120 album.images[i].sourceType = 'smugmug'; 9121 album.images[i].albumId = e.Album.id; 9122 album.images[i].albumKey = e.Album.Key; 9123 album.images[i].id = images[i].id; 9124 album.images[i].key = images[i].Key; 9125 album.images[i].filename = images[i].FileName; 9126 album.images[i].format = format;
9127 album.images[i].caption = images[i].Caption || ''; 9128 album.images[i].link = e.Album.URL + '#' + images[i].id + '_' + images[i].Key; 9129 album.images[i].tinyURL = baseURL + '-Ti' + ext; 9130 album.images[i].thumbURL = baseURL + '-Th' + ext; 9131 album.images[i].smallURL = baseURL + '-S' + ext; 9132 album.images[i].mediumURL = baseURL + '-M' + ext; 9133 album.images[i].largeURL = baseURL + '-L' + ext; 9134 album.images[i].xlargeURL = baseURL + '-XL' + ext; 9135 album.images[i].x2largeURL = baseURL + '-X2' + ext; 9136 album.images[i].x3largeURL = baseURL + '-X3' + ext; 9137 album.images[i].buyURL = buyBase + images[i].id + '_' + images[i].Key; 9138 album.images[i].iframe = ''; 9139 9140 if(album.images[i].caption.indexOf('iframe')) { 9141 temp = Y.Node.create('<div>'+ album.images[i].caption +'</div>'); 9142 iframe = temp.one('iframe'); 9143 9144 if(iframe) { 9145 album.images[i].iframe = iframe.getAttribute('src'); 9146 album.images[i].caption = album.images[i].caption.replace(/<iframe.*>.*<\/iframe>/gi, ''); 9147 } 9148 } 9149 } 9150 9151 this._loadComplete({ 'albumInfo': album }); 9152 }, 9153 9154 /** 9155 * Loads slideshow album data from an array of urls. 9156 * 9157 * NOTE: You must have a large URL. 9158 * 9159 * @method _loadUrls 9160 * @protected 9161 */ 9162 _loadUrls: function() 9163 { 9164 var album = {}, 9165 i = 0; 9166 9167 album.index = this._source.index; 9168 album.title = this._source.title ? this._source.title : ''; 9169 album.images = []; 9170 9171 for( ; i < this._source.urls.length; i++) 9172 { 9173 album.images[i] = {}; 9174 album.images[i].index = i; 9175 album.images[i].sourceType = 'urls'; 9176 album.images[i].filename = this._source.urls[i].largeURL.split('/').pop(); 9177 album.images[i].format = ''; 9178 album.images[i].caption = this._source.urls[i].caption || ''; 9179 album.images[i].alt = this._source.urls[i].alt || ''; 9180 album.images[i].link = this._source.urls[i].largeURL; 9181 album.images[i].thumbURL = this._source.urls[i].thumbURL || this._source.urls[i].largeURL; 9182 album.images[i].smallURL = this._source.urls[i].smallURL || this._source.urls[i].largeURL; 9183 album.images[i].mediumURL = this._source.urls[i].mediumURL || this._source.urls[i].largeURL; 9184 album.images[i].largeURL = this._source.urls[i].largeURL; // Must have a large URL 9185 album.images[i].xlargeURL = this._source.urls[i].xlargeURL || this._source.urls[i].largeURL; 9186 album.images[i].x2largeURL = this._source.urls[i].x2largeURL || this._source.urls[i].largeURL; 9187 album.images[i].x3largeURL = this._source.urls[i].x3largeURL || this._source.urls[i].largeURL; 9188 album.images[i].buyURL = this._source.urls[i].buyURL || ''; 9189 album.images[i].iframe = this._source.urls[i].iframe || ''; 9190 } 9191 9192 this._loadComplete({ 'albumInfo': album }); 9193 } 9194 9195}, { 9196 9197 /** 9198 * Static property used to define the default attribute configuration of 9199 * the Widget. 9200 * 9201 * @property ATTRS 9202 * @type Object 9203 * @protected 9204 * @static 9205 */ 9206 ATTRS: { 9207 9208 /** 9209 * If true, the images will be randomized after loading. 9210 * 9211 * @attribute randomize 9212 * @type Boolean 9213 * @default false 9214 */ 9215 randomize: { 9216 value: false 9217 } 9218 }, 9219 9220 /** 9221 * The types of source data that can be loaded 9222 * and associated functions. 9223 * 9224 * @property TYPES 9225 * @type Object 9226 * @readOnly 9227 * @protected 9228 * @static 9229 */ 9230 TYPES: { 9231 'smugmug': '_loadSmugMug', 9232 'flickr': '_loadFlickr', 9233 'picasa': '_loadPicasa', 9234 'urls': '_loadUrls', 9235 'html': '_loadHtml' 9236 } 9237}); 9238 9239 9240}, '2.0.0' ,{requires:['base', 'fl-smugmug-api']}); 9241 9242 9243YUI.add('fl-slideshow-base', function(Y) { 9244 9245/** 9246 * @module fl-slideshow-base 9247 */ 9248 9249/** 9250 * The base class that gets extended when creating new 9251 * slideshow widgets. Manages loading, playing, and resizing. 9252 * <p> 9253 * While SlideshowBase can be instantiated, it is only meant to 9254 * be extended and does not display any images. 9255 * 9256 * @namespace FL 9257 * @class SlideshowBase 9258 * @constructor 9259 * @param config {Object} Configuration object 9260 * @extends Widget 9261 */ 9262Y.namespace('FL').SlideshowBase = Y.Base.create('fl-slideshow-base', Y.Widget, [Y.WidgetParent], { 9263 9264 /** 9265 * FL.SlideshowAlbumLoader instance used to load albums. 9266 * 9267 * @property _loader 9268 * @type FL.SlideshowAlbumLoader 9269 * @default null 9270 * @protected 9271 */ 9272 _albumLoader: null, 9273 9274 /** 9275 * An array of albums loaded from the source attribute. 9276 * Each album is an array of objects containing image info. 9277 * 9278 * @property albums 9279 * @type Array 9280 * @default [] 9281 */ 9282 albums: [], 9283 9284 /** 9285 * Info for the active album. 9286 * 9287 * @property albumInfo 9288 * @type Object 9289 * @default null 9290 */ 9291 albumInfo: null, 9292 9293 /** 9294 * A number that represents the index of the active 9295 * album in the albums array. 9296 * 9297 * @property albumIndex 9298 * @type Number 9299 * @default null 9300 */ 9301 albumIndex: null, 9302 9303 /** 9304 * Info for the active image. 9305 * 9306 * @property imageInfo 9307 * @type Object 9308 * @default null 9309 */ 9310 imageInfo: null, 9311 9312 /** 9313 * A number that represents the index of the active 9314 * image in the albumInfo array.
9315 * 9316 * @property imageIndex 9317 * @type Number 9318 * @default null 9319 */ 9320 imageIndex: null, 9321 9322 /** 9323 * A number that represents the index of the last 9324 * image that was loaded in the albumInfo array. 9325 * 9326 * @property lastImageIndex 9327 * @type Number 9328 * @default null 9329 */ 9330 lastImageIndex: null, 9331 9332 /** 9333 * Timer for the delay before resizing if one is set. 9334 * 9335 * @property _resizeTimer 9336 * @type Object 9337 * @default null 9338 * @protected 9339 */ 9340 _resizeTimer: null, 9341 9342 /** 9343 * Flag for whether the slideshow is currently playing or not. 9344 * 9345 * @property playing 9346 * @type Boolean 9347 * @default false 9348 * @protected 9349 */ 9350 _playing: false, 9351 9352 /** 9353 * Timer for the break in between images when 9354 * the slideshow is playing. 9355 * 9356 * @property _playingTimer 9357 * @type Object 9358 * @default null 9359 * @protected 9360 */ 9361 _playingTimer: null, 9362 9363 /** 9364 * If set, the slideshow will only auto start when 9365 * this event is fired. 9366 * 9367 * @property _playingTimerEvent 9368 * @type Object 9369 * @default null 9370 * @protected 9371 */ 9372 _playingTimerEvent: null, 9373 9374 /** 9375 * An instance of FL.Spinner that is shown and hidden 9376 * using _showLoadingImage and _hideLoadingImage when 9377 * a loading activity occurs. 9378 * 9379 * @property _loadingImage 9380 * @type FL.Spinner 9381 * @default null 9382 * @protected 9383 */ 9384 _loadingImage: null, 9385 9386 /** 9387 * An div node that wraps the loading image. 9388 * 9389 * @property _loadingImageWrap 9390 * @type Node 9391 * @default null 9392 * @protected 9393 */ 9394 _loadingImageWrap: null, 9395 9396 /** 9397 * Whether the loading image is visible or not. 9398 * 9399 * @property _loadingImageVisible 9400 * @type Boolean 9401 * @default false 9402 * @protected 9403 */ 9404 _loadingImageVisible: false, 9405 9406 /** 9407 * A timer to delay the display of the loading image. 9408 * 9409 * @property _loadingImageTimer 9410 * @type Object 9411 * @default null 9412 * @protected 9413 */ 9414 _loadingImageTimer: null, 9415 9416 /** 9417 * The container to insert the loading image into. If 9418 * no container is set, the loading image will be inserted 9419 * into the widget's bounding box. 9420 * 9421 * @property _loadingImageContainer 9422 * @type Object 9423 * @default null 9424 * @protected 9425 */ 9426 _loadingImageContainer: null, 9427 9428 /** 9429 * The initial height of the slideshow. Used to resize 9430 * back to the starting height when exiting stretchy. 9431 * 9432 * @property _initialHeight 9433 * @type Number 9434 * @default null 9435 * @protected 9436 */ 9437 _initialHeight: null, 9438 9439 /** 9440 * The initial width of the slideshow. Used to resize 9441 * back to the starting width when exiting stretchy. 9442 * 9443 * @property _initialWidth 9444 * @type Number 9445 * @default null 9446 * @protected 9447 */ 9448 _initialWidth: null, 9449 9450 /** 9451 * @method initializer 9452 * @protected 9453 */ 9454 initializer: function() 9455 { 9456 // Loader 9457 this._albumLoader = new Y.FL.SlideshowAlbumLoader({ 9458 randomize: this.get('randomize') 9459 }); 9460 }, 9461 9462 /** 9463 * @method renderUI 9464 * @protected 9465 */ 9466 renderUI: function() 9467 { 9468 this._renderLoadingImage(); 9469 }, 9470 9471 /** 9472 * @method bindUI 9473 * @protected 9474 */ 9475 bindUI: function() 9476 { 9477 // Album load complete 9478 this._albumLoader.on('complete', this._loadAlbumComplete, this); 9479 9480 // Resize Events 9481 Y.one(window).on('fl-slideshow-base|resize', this._delayResize, this); 9482 Y.one(window).on('fl-slideshow-base|orientationchange', this._delayResize, this); 9483 9484 // Key Events 9485 Y.Node.one('body').on('keydown', Y.bind(this._onKey, this)); 9486 }, 9487 9488 /** 9489 * @method syncUI 9490 * @protected 9491 */ 9492 syncUI: function() 9493 { 9494 this.get('boundingBox').addClass('fl-slideshow-' + this.get('color')); 9495 9496 this.resize(); 9497 9498 if(this.get('loadOnRender')) { 9499 this.loadAlbum(this.get('defaultAlbum'), this.get('defaultImage')); 9500 } 9501 }, 9502 9503 /** 9504 * Add album data to the source object. 9505 * 9506 * @method addAlbum 9507 * @protected 9508 */ 9509 addAlbum: function(data) 9510 { 9511 var source = this.get('source'), 9512 i = source.length; 9513 9514 source[i] = data; 9515 source[i].index = i; 9516 9517 this.set('source', source); 9518 }, 9519 9520 /** 9521 * Loads an album from the source array with the provided albumIndex. 9522 * If no albumIndex is provided, the first album in the array will be loaded. 9523 * An image to load can also be specified using imageIndex. 9524 * 9525 * @method loadAlbum 9526 * @param albumIndex {Number} The album index to load from the source array. 9527 * @param imageIndex {Number} The image index to load from the album array. 9528 */ 9529 loadAlbum: function(albumIndex, imageIndex) 9530 { 9531 var source = this.get('source'), 9532 loadImageIndex = typeof imageIndex == 'undefined' ? 0 : imageIndex; 9533 9534 // Reset internal image indexes. 9535 this.imageIndex = null; 9536 this.lastImageIndex = null; 9537 9538 /** 9539 * Fires before a new album request is made. 9540 * 9541 * @event albumLoadStart 9542 */ 9543 this.fire('albumLoadStart'); 9544 9545 // Load an image after the album. 9546 this.once('albumLoadComplete', Y.bind(this.loadImage, this, loadImageIndex)); 9547 9548 // Load data passed from another slideshow instance. 9549 if(source[albumIndex] && source[albumIndex].type == 'album-data') { 9550 this.albums[albumIndex] = source[albumIndex].data; 9551 this._loadAlbumComplete({albumInfo: this.albums[albumIndex]}); 9552 } 9553 // Load the album from the albums array. 9554 else if(source[albumIndex] && this.albums[albumIndex]) { 9555 this._loadAlbumComplete({albumInfo: this.albums[albumIndex]}); 9556 } 9557 // Load the album using the album loader. 9558 else { 9559 this._albumLoader.load(source[albumIndex] || source[0]); 9560 } 9561 }, 9562 9563 /** 9564 * Processes the loaded album and fires the albumLoadComplete event. 9565 * 9566 * @method _loadAlbumComplete 9567 * @param o {Object} The custom event object passed to this method. 9568 * @protected 9569 */ 9570 _loadAlbumComplete: function(o) 9571 { 9572 this.albums[o.albumInfo.index] = o.albumInfo; 9573 this.albumInfo = o.albumInfo; 9574 this.albumIndex = o.albumInfo.index; 9575 9576 /** 9577 * Fires after a new album request is made. 9578 * 9579 * @event albumLoadComplete 9580 */ 9581 this.fire('albumLoadComplete'); 9582 9583 // Auto Play 9584 if(this.get('autoPlay')) { 9585 this._playingTimerStart(); 9586 this.fire('played'); 9587 this._playing = true; 9588 } 9589 }, 9590 9591 /** 9592 * Sets the active image index and fires the imageLoadComplete event. 9593 * 9594 * @method loadImage 9595 * @param index {Number} The image index to load. 9596 */ 9597 loadImage: function(index) 9598 { 9599 if(this._playing) { 9600 this._playingTimerStart(); 9601 } 9602 9603 index = index < 0 ? this.albumInfo.images.length - 1 : index; 9604 index = index >= this.albumInfo.images.length ? 0 : index; 9605 9606 this.lastImageIndex = this.imageIndex; 9607 this.imageIndex = index; 9608 this.imageInfo = this.albumInfo.images[index]; 9609 9610 /** 9611 * Fires after a new image index is set. 9612 * 9613 * @event imageLoadComplete 9614 */ 9615 this.fire('imageLoadComplete', { 'imageInfo': this.imageInfo }); 9616 }, 9617 9618 /** 9619 * Loads the previous image. 9620 * 9621 * @method prevImage 9622 */ 9623 prevImage: function() 9624 { 9625 if(this.get('pauseOnNextOrPrev')) { 9626 this.pause(); 9627 } 9628 9629 this.loadImage(this.imageIndex - 1); 9630 9631 /** 9632 * Fires when the previous image is loaded. 9633 * 9634 * @event prevImage 9635 */ 9636 this.fire('prevImage'); 9637 }, 9638 9639 /** 9640 * Loads the next image. 9641 * 9642 * @method nextImage 9643 */ 9644 nextImage: function() 9645 { 9646 if(this.get('pauseOnNextOrPrev')) { 9647 this.pause(); 9648 } 9649 9650 this.loadImage(this.imageIndex + 1); 9651 9652 /** 9653 * Fires when the next image is loaded. 9654 * 9655 * @event nextImage 9656 */ 9657 this.fire('nextImage'); 9658 }, 9659 9660 /** 9661 * Keyboard navigation for the next and prev images. 9662 * 9663 * @method _onKey 9664 * @protected 9665 */ 9666 _onKey: function(e) 9667 { 9668 switch(e.keyCode) { 9669 9670 case 37: 9671 this.prevImage(); 9672 break; 9673 9674 case 39: 9675 this.nextImage(); 9676 break; 9677 } 9678 }, 9679 9680 /** 9681 * Resizes the slideshow using either the 9682 * stretchy or standard functions. 9683 * 9684 * @method resize 9685 */ 9686 resize: function() 9687 { 9688 var stretchy = this.get('stretchy'), 9689 stretchyType = this.get('stretchyType'), 9690 width = parseInt(Y.one('body').get('winWidth'), 10), 9691 threshold = this.get('responsiveThreshold'); 9692 9693 // Stretchy resize to the window only if the parent width is greater 9694 // than the responsive threshold and stretchyType is set to window. 9695 if(width > threshold && stretchy && stretchyType == 'window') { 9696 this._stretchyWindowResize(); 9697 } 9698 9699 // Ratio resize if the parent width is less than the responsive 9700 // threshold or if stretchyType is set to ratio. 9701 else if((width <= threshold) || (stretchy && stretchyType == 'ratio')) { 9702 this._stretchyRatioResize(); 9703 } 9704 9705 // Do a standard resize based on the height and 9706 // width passed to the constructor function. 9707 else { 9708 this._standardResize(); 9709 } 9710 9711 /** 9712 * Fires when the slideshow is resized. 9713 * 9714 * @event resize 9715 */ 9716 this.fire('resize'); 9717 }, 9718 9719 /** 9720 * @method _standardResize 9721 * @protected 9722 */ 9723 _standardResize: function() 9724 { 9725 var stretchy = this.get('stretchy'), 9726 stretchyType = this.get('stretchyType'), 9727 bb = this.get('boundingBox'), 9728 parent = bb.get('parentNode'), 9729 parentHeight = parseInt(parent.getComputedStyle('height'), 10), 9730 parentWidth = parseInt(parent.getComputedStyle('width'), 10), 9731 height = this.get('height'), 9732 width = this.get('width'); 9733
9734 // Window resize if we are in fullscreen. 9735 if(bb.hasClass('fl-fullscreen-active')) { 9736 this._stretchyWindowResize(); 9737 return; 9738 } 9739 9740 // Resize to the width and height of the parent. 9741 else if(stretchy && stretchyType == 'contain') { 9742 bb.setStyle('height', parentHeight + 'px'); 9743 bb.setStyle('width', parentWidth + 'px'); 9744 } 9745 9746 // Ratio resize if we don't have a height defined. 9747 else if(!Y.Lang.isNumber(height)) { 9748 this._stretchyRatioResize(); 9749 return; 9750 } 9751 9752 // Resize to the defined width and height. 9753 else { 9754 9755 bb.setStyle('height', height + 'px'); 9756 9757 if(width) { 9758 bb.setStyle('width', width + 'px'); 9759 } 9760 else { 9761 bb.setStyle('width', parentWidth + 'px'); 9762 } 9763 } 9764 }, 9765 9766 /** 9767 * Resizes to the height of the window, compensating 9768 * for any padding. 9769 * 9770 * @method _stretchyWindowResize 9771 * @protected 9772 */ 9773 _stretchyWindowResize: function() 9774 { 9775 var bb = this.get('boundingBox'), 9776 verticalSpace = this.get('stretchyVerticalSpace'), 9777 paddingTop = parseInt(bb.getStyle('paddingTop'), 10), 9778 paddingBottom = parseInt(bb.getStyle('paddingBottom'), 10), 9779 height = parseInt(Y.one('body').get('winHeight'), 10), 9780 width = ''; 9781 9782 // Set the vertical space to 0 and width to the 9783 // window's width if we are in fullscreen mode. 9784 if(bb.hasClass('fl-fullscreen-active')) { 9785 verticalSpace = 0; 9786 width = parseInt(Y.one('body').get('winWidth'), 10) + 'px'; 9787 } 9788 9789 height = (height - paddingTop - paddingBottom - verticalSpace) + 'px'; 9790 9791 bb.setStyle('height', height); 9792 bb.setStyle('width', width); 9793 }, 9794 9795 /** 9796 * Resizes the height by multiplying the width and stretchyRatio value. 9797 * 9798 * @method _stretchyRatioResize 9799 * @protected 9800 */ 9801 _stretchyRatioResize: function() 9802 { 9803 var bb = this.get('boundingBox'), 9804 parent = bb.get('parentNode'), 9805 verticalSpace = 0, 9806 stretchyRatio = this.get('stretchyRatio'), 9807 paddingTop = parseInt(bb.getStyle('paddingTop'), 10), 9808 paddingBottom = parseInt(bb.getStyle('paddingBottom'), 10), 9809 computedWidth = parseInt(parent.getComputedStyle('width'), 10), 9810 winHeight = parseInt(Y.one('body').get('winHeight'), 10), 9811 winWidth = parseInt(Y.one('body').get('winWidth'), 10), 9812 height = computedWidth * stretchyRatio, 9813 width = ''; 9814 9815 // Use the window's height and width if we are in fullscreen mode. 9816 if(bb.hasClass('fl-fullscreen-active')) { 9817 height = winHeight; 9818 width = winWidth; 9819 } 9820 9821 height = (height - paddingTop - paddingBottom - verticalSpace) + 'px'; 9822 9823 bb.setStyle('height', height); 9824 bb.setStyle('width', width); 9825 }, 9826 9827 /** 9828 * Resizes the slideshow after the resize timer completes. 9829 * 9830 * @method _delayResize 9831 * @protected 9832 */ 9833 _delayResize: function() 9834 { 9835 if(this._resizeTimer) { 9836 this._resizeTimer.cancel(); 9837 } 9838 9839 this._resizeTimer = Y.later(300, this, this.resize); 9840 }, 9841 9842 /** 9843 * Starts a new playing timer and fires the played event. 9844 * 9845 * @method play 9846 */ 9847 play: function() 9848 { 9849 this._playingTimer = Y.later(this.get('speed'), this, this._playingTimerComplete); 9850 9851 /** 9852 * Fires when the playing timer starts. 9853 * 9854 * @event played 9855 */ 9856 this.fire('played'); 9857 this._playing = true; 9858 }, 9859 9860 /** 9861 * Cancels the current playing timer and fires the paused event. 9862 * 9863 * @method pause 9864 */ 9865 pause: function() 9866 { 9867 this._playingTimerCancel(); 9868 9869 /** 9870 * Fires when the playing timer is canceled. 9871 * 9872 * @event paused 9873 */ 9874 this.fire('paused'); 9875 this._playing = false; 9876 }, 9877 9878 /** 9879 * A new playing timer will start when this event is fired. 9880 *
9881 * @method _setPlayingTimerEvent 9882 * @param obj {Object} The event's host object. 9883 * @param e {String} The event to fire on the host object. 9884 * @protected 9885 */ 9886 _setPlayingTimerEvent: function(obj, e) 9887 { 9888 this._playingTimerEvent = { 9889 'obj': obj, 9890 'e': e 9891 }; 9892 }, 9893 9894 /** 9895 * Cancels the playing timer if it is running and starts a new one. 9896 * The next image is loaded when the timer completes. 9897 * 9898 * @method _playingTimerStart 9899 * @protected 9900 */ 9901 _playingTimerStart: function(e) 9902 { 9903 this._playingTimerCancel(); 9904 9905 if(!e && this._playingTimerEvent !== null) { 9906 this._playingTimerEvent.obj.once('fl-slideshow-base|' + this._playingTimerEvent.e, Y.bind(this._playingTimerStart, this)); 9907 } 9908 else { 9909 this._playingTimer = Y.later(this.get('speed'), this, this._playingTimerComplete); 9910 } 9911 }, 9912 9913 /** 9914 * Fires when the playing timer completes, starts a 9915 * new timer and loads the next image. 9916 * 9917 * @method _playingTimerComplete 9918 * @protected 9919 */ 9920 _playingTimerComplete: function() 9921 { 9922 this.loadImage(this.imageIndex + 1); 9923 9924 /** 9925 * Fires when the playing timer completes. 9926 * 9927 * @event albumLoadStart 9928 */ 9929 this.fire('playingTimerComplete'); 9930 }, 9931 9932 /** 9933 * Cancels the playing timer. 9934 * 9935 * @method _playingTimerCancel 9936 * @protected 9937 */ 9938 _playingTimerCancel: function() 9939 { 9940 if(this._playingTimer) { 9941 this._playingTimer.cancel(); 9942 } 9943 if(this._playingTimerEvent) { 9944 this._playingTimerEvent.obj.detach('fl-slideshow-base|' + this._playingTimerEvent.e); 9945 } 9946 }, 9947 9948 /** 9949 * Creates the loading image. 9950 * 9951 * @method _renderLoadingImage 9952 * @protected 9953 */ 9954 _renderLoadingImage: function() 9955 { 9956 var defaults = { 9957 lines: 11, // The number of lines to draw 9958 length: 6, // The length of each line 9959 width: 2, // The line thickness 9960 radius: 7, // The radius of the inner circle 9961 color: '', // #rbg or #rrggbb 9962 speed: 1, // Rounds per second 9963 trail: 60, // Afterglow percentage 9964 shadow: false // Whether to render a shadow 9965 }, 9966 settings = Y.merge(defaults, this.get('loadingImageSettings')); 9967 9968 if(this.get('loadingImageEnabled')) { 9969 9970 // Loading image 9971 if(settings.color === '') { 9972 settings.color = this._colorToHex(Y.one('body').getStyle('color')); 9973 } 9974 9975 this._loadingImage = new Y.FL.Spinner(settings); 9976 9977 // Loading image wrap 9978 this._loadingImageWrap = Y.Node.create('<div class="fl-loading-image"></div>'); 9979 9980 this._loadingImageWrap.setStyles({ 9981 position : 'absolute', 9982 'z-index' : '1000' 9983 }); 9984 } 9985 }, 9986 9987 /** 9988 * Inserts the loading image. 9989 * 9990 * @method _showLoadingImage 9991 * @protected 9992 */ 9993 _showLoadingImage: function() 9994 { 9995 if(this._loadingImage && !this._loadingImageVisible) { 9996 9997 this._loadingImageVisible = true; 9998 this._loadingImage.spin(); 9999 this._loadingImageWrap.insert(this._loadingImage.el); 10000 10001 if(this._loadingImageContainer !== null) { 10002 this._loadingImageContainer.insert(this._loadingImageWrap); 10003 } 10004 else { 10005 this.get('contentBox').insert(this._loadingImageWrap); 10006 } 10007 10008 this._positionLoadingImage(); 10009 } 10010 }, 10011 10012 /** 10013 * Inserts the loading image div node after 10014 * a timer completes. 10015 * 10016 * @method _showLoadingImageWithDelay 10017 * @protected 10018 */ 10019 _showLoadingImageWithDelay: function() 10020 { 10021 if(this._loadingImage) { 10022 this._loadingImageTimer = Y.later(1000, this, this._showLoadingImage); 10023 } 10024 }, 10025 10026 /** 10027 * Removes the loading image div node. 10028 * 10029 * @method _hideLoadingImage 10030 * @protected 10031 */ 10032 _hideLoadingImage: function() 10033 { 10034 if(this._loadingImageTimer) { 10035 this._loadingImageTimer.cancel(); 10036 this._loadingImageTimer = null; 10037 } 10038 if(this._loadingImage && this._loadingImageVisible) { 10039 this._loadingImageVisible = false; 10040 this._loadingImage.stop(); 10041 this._loadingImageWrap.remove(); 10042 } 10043 }, 10044 10045 /** 10046 * Centers the loading image in the content box. 10047 * 10048 * @method _positionLoadingImage 10049 * @protected 10050 */ 10051 _positionLoadingImage: function() 10052 { 10053 if(this._loadingImage && this._loadingImageVisible) { 10054 10055 var wrap = this._loadingImageWrap, 10056 wrapHeight = parseInt(wrap.getComputedStyle('height'), 10), 10057 wrapWidth = parseInt(wrap.getComputedStyle('width'), 10), 10058 parent = wrap.get('parentNode'), 10059 parentHeight = parseInt(parent.getComputedStyle('height'), 10), 10060 parentWidth = parseInt(parent.getComputedStyle('width'), 10), 10061 left = (parentWidth - wrapWidth)/2, 10062 top = (parentHeight - wrapHeight)/2; 10063 10064 wrap.setStyles({ 10065 left : left + 'px', 10066 top : top + 'px' 10067 }); 10068 10069 Y.one(this._loadingImage.el).setStyles({ 10070 left : '50%', 10071 top : '50%' 10072 }); 10073 } 10074 }, 10075 10076 /** 10077 * Convert RGB color value to a hex value. 10078 *
10079 * @method _colorToHex 10080 * @protected 10081 */ 10082 _colorToHex: function(color) 10083 { 10084 var digits, red, green, blue, rgb; 10085 10086 if(color.substr(0, 1) === '#') { 10087 return color; 10088 } 10089 10090 digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color); 10091 10092 if ( null === digits ) { 10093 return '#000'; 10094 } 10095 10096 red = parseInt(digits[2], 10); 10097 green = parseInt(digits[3], 10); 10098 blue = parseInt(digits[4], 10); 10099 rgb = blue | (green << 8) | (red << 16); 10100 rgb = rgb.toString(16); 10101 10102 if(rgb === '0') { 10103 rgb = '000'; 10104 } 10105 10106 return digits[1] + '#' + rgb; 10107 } 10108 10109}, { 10110 10111 /** 10112 * Custom CSS class name for the widget. 10113 * 10114 * @property CSS_PREFIX 10115 * @type String 10116 * @protected 10117 * @static 10118 */ 10119 CSS_PREFIX: 'fl-slideshow-base', 10120 10121 /** 10122 * Static property used to define the default attribute configuration of 10123 * the Widget. 10124 * 10125 * @property ATTRS 10126 * @type Object 10127 * @protected 10128 * @static 10129 */ 10130 ATTRS: { 10131 10132 /** 10133 * Used to create the color class that gets added to the bounding box 10134 * when the widget is rendered. The color class is used to create new 10135 * CSS color themes. The default CSS provided includes dark and light themes. 10136 * 10137 * @attribute color
10138 * @type String 10139 * @default dark 10140 * @writeOnce 10141 */ 10142 color: { 10143 value: 'dark', 10144 writeOnce: true 10145 }, 10146 10147 /** 10148 * An array of source objects used to load albums. Each object must have 10149 * a type property and can have a title property as well. 10150 * <p> 10151 * In addition to those properties, each object has additional required 10152 * properties specific to its type. The types currently supported are 10153 * smugmug and urls with planned support for flickr and picasa. 10154 * See the user guide for information on loading different types. 10155 * 10156 * @attribute source 10157 * @type Array 10158 * @default [] 10159 * @writeOnce 10160 */ 10161 source: { 10162 value: [], 10163 setter: function(source) { 10164 10165 if(source.constructor == Object) { 10166 source = [source]; 10167 } 10168 10169 for(var i = 0; i < source.length; i++) { 10170 source[i].index = i; 10171 } 10172 10173 return source; 10174 } 10175 }, 10176 10177 /** 10178 * The default album index to load. 10179 * 10180 * @attribute defaultAlbum 10181 * @type Number 10182 * @default 0 10183 */ 10184 defaultAlbum: { 10185 value: 0 10186 }, 10187 10188 /** 10189 * The default image index to load. 10190 * 10191 * @attribute defaultImage 10192 * @type Number 10193 * @default 0 10194 */ 10195 defaultImage: { 10196 value: 0 10197 }, 10198 10199 /** 10200 * If true, the slideshow will be loaded after rendering. 10201 * 10202 * @attribute loadOnRender 10203 * @type Boolean 10204 * @default true 10205 */ 10206 loadOnRender: { 10207 value: true 10208 }, 10209 10210 /** 10211 * If true, the slideshow will start playing after loading. 10212 * 10213 * @attribute autoPlay 10214 * @type Boolean 10215 * @default true 10216 */ 10217 autoPlay: { 10218 value: true 10219 }, 10220 10221 /** 10222 * Whether to pause when the next or previous image is loaded 10223 * using nextImage or prevImage. The slideshow will not be paused 10224 * if the next or previous image is loaded using loadImage as is the 10225 * case when the slideshow is playing. 10226 * 10227 * @attribute pauseOnNextOrPrev 10228 * @type Boolean 10229 * @default true 10230 */ 10231 pauseOnNextOrPrev: { 10232 value: true 10233 }, 10234 10235 /** 10236 * If true, the images will be randomized after loading. 10237 * 10238 * @attribute randomize 10239 * @type Boolean 10240 * @default false 10241 */ 10242 randomize: { 10243 value: false 10244 }, 10245 10246 /** 10247 * The time between images when playing, measured in milliseconds. 10248 * 10249 * @attribute speed 10250 * @type Number 10251 * @default 4000 10252 */ 10253 speed: { 10254 value: 4000 10255 }, 10256 10257 /** 10258 * The minimum width of the parent node at which 10259 * responsive features are enabled. Set to 0 to 10260 * disable responsive features as they are enabled 10261 * whether stretchy is set to true or not. 10262 * 10263 * @attribute responsiveThreshold 10264 * @type Number 10265 * @default 600 10266 */ 10267 responsiveThreshold: { 10268 value: 600 10269 }, 10270 10271 /** 10272 * Whether stretchy resizing should be enabled. 10273 * 10274 * @attribute stretchy 10275 * @type Boolean 10276 * @default false 10277 */ 10278 stretchy: { 10279 value: false 10280 }, 10281 10282 /** 10283 * The type of stretchy logic to use. Possible values are 10284 * window and ratio. Both types resize the width of the 10285 * slideshow to the width of its parent node. With window, the 10286 * height of the slideshow is resized to the height of the window. 10287 * With ratio, the height of the slideshow is resized based 10288 * on the ratio set with stretchyRatio or the height of the window 10289 * if the ratio height is greater than the window height. 10290 * 10291 * @attribute stretchyType
10292 * @type String 10293 * @default ratio 10294 */ 10295 stretchyType: { 10296 value: 'ratio' 10297 }, 10298 10299 /** 10300 * The number of pixels to subtract from the height of 10301 * the slideshow when stretchy is set to true. 10302 * 10303 * @attribute stretchyVerticalSpace 10304 * @type Number 10305 * @default 0 10306 */ 10307 stretchyVerticalSpace: { 10308 value: 0 10309 }, 10310 10311 /** 10312 * Used to calculate the height of the slideshow when stretchyType 10313 * is set to ratio. 10314 * 10315 * @attribute stretchyRatio 10316 * @type Number 10317 * @default 0.7 10318 */ 10319 stretchyRatio: { 10320 value: 0.7 10321 }, 10322 10323 /** 10324 * Whether to use the loading image or not. 10325 * 10326 * @attribute loadingImageEnabled 10327 * @type Boolean 10328 * @default true 10329 */ 10330 loadingImageEnabled: { 10331 value: true 10332 }, 10333 10334 /** 10335 * Property object for setting up the spin.js loading image. 10336 * For a complete list of properties see: 10337 * http://effinroot.eiremedia.netdna-cdn.com/repo/plugins/misc/spin.js/index.html 10338 * 10339 * @attribute loadingImageSettings 10340 * @type Object 10341 */ 10342 loadingImageSettings: { 10343 value: {} 10344 } 10345 } 10346}); 10347 10348 10349}, '2.0.0' ,{requires:['node', 'base', 'widget', 'widget-parent', 'widget-child', 'fl-slideshow-album-loader', 'fl-spinner']}); 10350 10351 10352YUI.add('fl-smugmug-api', function(Y) { 10353 10354/** 10355 * @module fl-smugmug-api 10356 */ 10357 10358/** 10359 * SmugMug API wrapper. 10360 * 10361 * NOTE: Only anonymous logins are currently supported. 10362 * 10363 * @namespace FL 10364 * @class SmugMugAPI 10365 * @constructor 10366 * @param config {Object} Configuration object 10367 * @extends Base 10368 */ 10369Y.namespace('FL').SmugMugAPI = Y.Base.create('fl-smugmug-api', Y.Base, [], { 10370 10371 /** 10372 * ID for the current session. 10373 * 10374 * @property _sessionID
10375 * @type String 10376 * @default null 10377 * @protected 10378 */ 10379 _sessionID: null, 10380 10381 /** 10382 * URL with parameters for the next API request. 10383 * Reset after each request. 10384 * 10385 * @property _requestURL 10386 * @type String 10387 * @default null 10388 * @protected 10389 */ 10390 _requestURL: null, 10391 10392 /** 10393 * Lifecycle method. Initializes the request url. 10394 * 10395 * @method initializer 10396 * @protected 10397 */ 10398 initializer: function() 10399 { 10400 this._resetRequestURL(); 10401 }, 10402 10403 /** 10404 * Adds a key/value pair to the request url. 10405 * 10406 * @method addParam 10407 * @param key {String} The name of the parameter (example: key=val). 10408 * @param val {String} The value of the parameter (example: key=val). 10409 */ 10410 addParam: function(key, val) 10411 { 10412 this._requestURL = this._requestURL + '&' + key + '=' + val; 10413 }, 10414 10415 /** 10416 * Requests an anonymous login session. 10417 * 10418 * @method loginAnon 10419 */ 10420 loginAnon: function() 10421 { 10422 this.addParam('method', 'smugmug.login.anonymously'); 10423 this.once('complete', this._loginAnonComplete); 10424 this.request(); 10425 }, 10426 10427 /** 10428 * Anonymous login success handler. 10429 * 10430 * @method _loginAnonComplete 10431 * @param data {Object} A jsonp data object. 10432 * @protected 10433 */ 10434 _loginAnonComplete: function(data) 10435 { 10436 if(data.Login) { 10437 this._sessionID = data.Login.Session.id; 10438 } 10439 }, 10440 10441 /** 10442 * Sends an API request using the request url. 10443 * 10444 * @method request 10445 */ 10446 request: function() 10447 { 10448 this.addParam('Callback', '{callback}'); 10449 10450 Y.jsonp(this._requestURL, { 10451 on: { 10452 success: this._requestComplete, 10453 timeout: function(){} // TODO: Handle timeouts 10454 }, 10455 context: this, 10456 timeout: 60000, 10457 args: [] 10458 }); 10459 }, 10460 10461 /** 10462 * API request complete handler. 10463 * 10464 * @method _requestComplete 10465 * @param data {Object} A jsonp data object. 10466 * @protected 10467 */ 10468 _requestComplete: function(data) 10469 { 10470 this._resetRequestURL(); 10471 10472 /** 10473 * Fires when a request is complete. 10474 * 10475 * @event complete 10476 */ 10477 this.fire('complete', data); 10478 }, 10479 10480 /** 10481 * Clears all parameters on the request url except 10482 * the API key and session ID. 10483 * 10484 * @method _resetRequestURL 10485 * @protected 10486 */ 10487 _resetRequestURL: function() 10488 { 10489 this._requestURL = this.get('apiURL') + '?APIKey=' + this.get('apiKey'); 10490 10491 if(this._sessionID) { 10492 this.addParam('SessionID', this._sessionID); 10493 } 10494 } 10495 10496}, { 10497 10498 /** 10499 * Static property used to define the default attribute configuration of 10500 * the Widget. 10501 * 10502 * @property ATTRS 10503 * @type Object 10504 * @protected 10505 * @static 10506 */ 10507 ATTRS: { 10508 10509 /** 10510 * SmugMug API url to use for requests. 10511 * 10512 * @attribute apiUrl
10513 * @type String 10514 * @default https://api.smugmug.com/services/api/json/1.3.0/ 10515 */ 10516 apiURL: { 10517 value: 'https://api.smugmug.com/services/api/json/1.3.0/' 10518 }, 10519 10520 /** 10521 * SmugMug API key. 10522 * 10523 * @attribute apiKey 10524 * @type String 10525 * @default 7w6kuU5Ee6KSgRRExf2KLgppdkez9JD2 10526 */ 10527 apiKey: { 10528 value: '7w6kuU5Ee6KSgRRExf2KLgppdkez9JD2' 10529 } 10530 } 10531}); 10532 10533 10534}, '2.0.0' ,{requires:['base', 'jsonp']}); 10535 10536 10537YUI.add('fl-spinner', function(Y) { 10538 10539(function(window,document,undefined){var width="width",length="length",radius="radius",lines="lines",trail="trail",color="color",opacity="opacity",speed="speed",shadow="shadow",style="style",height="height",left="left",top="top",px="px",childNodes="childNodes",firstChild="firstChild",parentNode="parentNode",position="position",relative="relative",absolute="absolute",animation="animation",transform="transform",Origin="Origin",Timeout="Timeout",coord="coord",black="#000",styleSheets=style+"Sheets",prefixes="webkit0Moz0ms0O".split(0),animations={},useCssAnimations;function eachPair(args,it){var end=~~((args[length]-1)/2);for(var i=1;i<=end;i++){it(args[i*2-1],args[i*2])}}function createEl(tag){var el=document.createElement(tag||"div");eachPair(arguments,function(prop,val){el[prop]=val});return el}function ins(parent,child1,child2){if(child2&&!child2[parentNode]){ins(parent,child2)}parent.insertBefore(child1,child2||null);return parent}ins(document.getElementsByTagName("head")[0],createEl(style));var sheet=document[styleSheets][document[styleSheets][length]-1];function addAnimation(to,end){var name=[opacity,end,~~(to*100)].join("-"),dest="{"+opacity+":"+to+"}",i;if(!animations[name]){for(i=0;i<prefixes[length];i++){try{sheet.insertRule("@"+(prefixes[i]&&"-"+prefixes[i].toLowerCase()+"-"||"")+"keyframes "+name+"{0%{"+opacity+":1}"+end+"%"+dest+"to"+dest+"}",sheet.cssRules[length])}catch(err){}}animations[name]=1}return name}function vendor(el,prop){var s=el[style],pp,i;if(s[prop]!==undefined){return prop}prop=prop.charAt(0).toUpperCase()+prop.slice(1);for(i=0;i<prefixes[length];i++){pp=prefixes[i]+prop;if(s[pp]!==undefined){return pp}}}function css(el){eachPair(arguments,function(n,val){el[style][vendor(el,n)||n]=val});return el}function defaults(obj){eachPair(arguments,function(prop,val){if(obj[prop]===undefined){obj[prop]=val}});return obj}var Spinner=function Spinner(o){this.opts=defaults(o||{},lines,12,trail,100,length,7,width,5,radius,10,color,black,opacity,1/4,speed,1)},proto=Spinner.prototype={spin:function(target){var self=this,el=self.el=self[lines](self.opts);if(target){ins(target,css(el,left,~~(target.offsetWidth/2)+px,top,~~(target.offsetHeight/2)+px),target[firstChild])}if(!useCssAnimations){var o=self.opts,i=0,f=20/o[speed],ostep=(1-o[opacity])/(f*o[trail]/100),astep=f/o[lines];(function anim(){i++;for(var s=o[lines];s;s--){var alpha=Math.max(1-(i+s*astep)%f*ostep,o[opacity]);self[opacity](el,o[lines]-s,alpha,o)}self[Timeout]=self.el&&window["set"+Timeout](anim,50)})()}return self},stop:function(){var self=this,el=self.el;window["clear"+Timeout](self[Timeout]);if(el&&el[parentNode]){el[parentNode].removeChild(el)}self.el=undefined;return self}};proto[lines]=function(o){var el=css(createEl(),position,relative),animationName=addAnimation(o[opacity],o[trail]),i=0,seg;function fill(color,shadow){return css(createEl(),position,absolute,width,(o[length]+o[width])+px,height,o[width]+px,"background",color,"boxShadow",shadow,transform+Origin,left,transform,"rotate("+~~(360/o[lines]*i)+"deg) translate("+o[radius]+px+",0)","borderRadius","100em")}for(;i<o[lines];i++){seg=css(createEl(),position,absolute,top,1+~(o[width]/2)+px,transform,"translate3d(0,0,0)",animation,animationName+" "+1/o[speed]+"s linear infinite "+(1/o[lines]/o[speed]*i-1/o[speed])+"s");if(o[shadow]){ins(seg,css(fill(black,"0 0 4px "+black),top,2+px))}ins(el,ins(seg,fill(o[color],"0 0 1px rgba(0,0,0,.1)")))}return el};proto[opacity]=function(el,i,val){el[childNodes][i][style][opacity]=val};var behavior="behavior",URL_VML="url(#default#VML)",tag="group0roundrect0fill0stroke".split(0);(function(){var s=css(createEl(tag[0]),behavior,URL_VML),i;if(!vendor(s,transform)&&s.adj){for(i=0;i<tag[length];i++){sheet.addRule(tag[i],behavior+":"+URL_VML)}proto[lines]=function(){var o=this.opts,r=o[length]+o[width],s=2*r;function grp(){return css(createEl(tag[0],coord+"size",s+" "+s,coord+Origin,-r+" "+-r),width,s,height,s)}var g=grp(),margin=~(o[length]+o[radius]+o[width])+px,i;function seg(i,dx,filter){ins(g,ins(css(grp(),"rotation",360/o[lines]*i+"deg",left,~~dx),ins(css(createEl(tag[1],"arcsize",1),width,r,height,o[width],left,o[radius],top,-o[width]/2,"filter",filter),createEl(tag[2],color,o[color],op
10539acity,o[opacity]),createEl(tag[3],opacity,0))))}if(o[shadow]){for(i=1;i<=o[lines];i++){seg(i,-2,"progid:DXImage"+transform+".Microsoft.Blur(pixel"+radius+"=2,make"+shadow+"=1,"+shadow+opacity+"=.3)")}}for(i=1;i<=o[lines];i++){seg(i)}return ins(css(createEl(),"margin",margin+" 0 0 "+margin,position,relative),g)};proto[opacity]=function(el,i,val,o){o=o[shadow]&&o[lines]||0;el[firstChild][childNodes][i+o][firstChild][firstChild][opacity]=val}}else{useCssAnimations=vendor(s,animation)}})();Y.namespace('FL').Spinner=Spinner})(window,document); 10540 10541 10542}, '2.0.0' ); 10543 10544 10545YUI.add('fl-utils', function(Y) { 10546 10547/** 10548 * @module fl-utils 10549 */ 10550 10551/** 10552 * General helper functions for all FastLine modules. 10553 * 10554 * @namespace FL 10555 * @class Utils 10556 * @constructor 10557 * @static 10558 */ 10559Y.namespace('FL').Utils = { 10560 10561 /** 10562 * Checks for support of the provided CSS property. 10563 * Method adapted from: https://gist.github.com/556448 10564 * 10565 * @method cssSupport 10566 * @param p {String} The property to check. 10567 * @returns Boolean 10568 */ 10569 cssSupport: function(p) 10570 { 10571 var b = document.body || document.documentElement, 10572 s = b.style, 10573 v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms', 'Icab'], 10574 i = 0; 10575 10576 // Transform not working well in these browsers 10577 if(p == 'transform' && Y.UA.gecko && Y.UA.gecko < 4) { return false; } 10578 if(p == 'transform' && Y.UA.opera > 0) { return false; } 10579 if(p == 'transform' && Y.UA.ie > 0 && Y.UA.ie < 10) { return false; } 10580 if(p == 'transform' && navigator.userAgent.match(/Trident/)) { return false; } 10581 10582 // No css support detected 10583 if(typeof s == 'undefined') { return false; } 10584 10585 // Tests for standard prop 10586 if(typeof s[p] == 'string') { return true; } 10587 10588 // Tests for vendor specific prop 10589 p = p.charAt(0).toUpperCase() + p.substr(1); 10590 10591 for( ; i < v.length; i++) { 10592 if(typeof s[v[i] + p] == 'string') { return true; } 10593 } 10594 } 10595}; 10596 10597 10598}, '2.0.0' );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.