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