/** * Slideshow JS Bundle */ YUI.add('fl-event-move', function(Y) { /** * Adds gesturemovevertical, gesturemoveverticalend, gesturemovehorizontal * and gesturemovehorizontalend events. * * @module fl-event-move */ var _eventBase = { _isEndEvent: false, on: function(node, subscriber, ce) { if(this.type.indexOf('end') > -1) { this._isEndEvent = true; } subscriber._direction = this.type.replace('gesturemove', '').replace('end', ''); if(window.navigator.msPointerEnabled) { subscriber._startHandle = node.on('MSPointerDown', this._onStart, this, node, subscriber, ce); subscriber._moveHandle = node.on('MSPointerMove', this._onMove, this, node, subscriber, ce); subscriber._endHandle = node.on('MSPointerUp', this._onEnd, this, node, subscriber, ce); } else { subscriber._startHandle = node.on('gesturemovestart', this._onStart, null, this, node, subscriber, ce); subscriber._moveHandle = node.on('gesturemove', this._onMove, null, this, node, subscriber, ce); subscriber._endHandle = node.on('gesturemoveend', this._onEnd, { standAlone: true }, this, node, subscriber, ce); } }, detach: function(node, subscriber, ce) { subscriber._startHandle.detach(); subscriber._startHandle = null; subscriber._moveHandle.detach(); subscriber._moveHandle = null; subscriber._endHandle.detach(); subscriber._endHandle = null; }, _onStart: function(e, node, subscriber, ce) { subscriber._doMove = null; subscriber._startX = e.pageX; subscriber._startY = e.pageY; }, _onMove: function(e, node, subscriber, ce) { if(this._checkDirection(e, subscriber)) { subscriber._doMove = true; } else { subscriber._doMove = false; } if(subscriber._doMove && !this._isEndEvent) { ce.fire(e); } }, _onEnd: function(e, node, subscriber, ce) { if(subscriber._doMove && this._isEndEvent) { e.startPageX = subscriber._startX; e.startPageY = subscriber._startY; ce.fire(e); } subscriber._doMove = null; }, _checkDirection: function(e, subscriber) { var xDelta = Math.abs(subscriber._startX - e.pageX), yDelta = Math.abs(subscriber._startY - e.pageY); if(yDelta > xDelta && subscriber._startY > e.pageY && subscriber._direction == 'vertical') { return true; } else if(yDelta > xDelta && subscriber._startY < e.pageY && subscriber._direction == 'vertical') { return true; } else if(yDelta < xDelta && subscriber._startX > e.pageX && subscriber._direction == 'horizontal') { return true; } else if(yDelta < xDelta && subscriber._startX < e.pageX && subscriber._direction == 'horizontal') { return true; } return false; } }; /** * @event gesturemovevertical * @param type {String} "gesturemovevertical" * @param fn {Function} The method the event invokes. * @param ctx {Object} Context for the method the event invokes. */ Y.Event.define('gesturemovevertical', _eventBase); /** * @event gesturemoveverticalend * @param type {String} "gesturemoveverticalend" * @param fn {Function} The method the event invokes. * @param ctx {Object} Context for the method the event invokes. */ Y.Event.define('gesturemoveverticalend', _eventBase); /** * @event gesturemovehorizontal * @param type {String} "gesturemovehorizontal" * @param fn {Function} The method the event invokes. * @param ctx {Object} Context for the method the event invokes. */ Y.Event.define('gesturemovehorizontal', _eventBase); /** * @event gesturemovehorizontalend * @param type {String} "gesturemovehorizontalend" * @param fn {Function} The method the event invokes. * @param ctx {Object} Context for the method the event invokes. */ Y.Event.define('gesturemovehorizontalend', _eventBase); }, '2.0.0' ,{requires:['event-move']}); YUI.add('fl-slideshow', function(Y) { /** * @module fl-slideshow */ /** * Caption widget used in slideshows. * * @namespace FL * @class SlideshowCaption * @constructor * @param config {Object} Configuration object * @extends Widget */ Y.namespace('FL').SlideshowCaption = Y.Base.create('fl-slideshow-caption', Y.Widget, [Y.WidgetChild], { /** * Flag for whether the text has been * toggled or not. * * @property _textToggled * @type Boolean * @default false * @protected */ _textToggled: false, /** * An anchor node used for the toggle link. * * @property _textToggleLink * @type Object * @default null * @protected */ _textToggleLink: null, /** * @method renderUI * @protected */ renderUI: function() { var root = this.get('root'), bb = this.get('boundingBox'); this._textToggleLink = Y.Node.create(''); this._textToggleLink.addClass('fl-slideshow-caption-toggle'); this._textToggleLink.set('innerHTML', root.get('captionMoreLinkText')); bb.appendChild(this._textToggleLink); }, /** * @method bindUI * @protected */ bindUI: function() { this.get('root').on('imageLoadComplete', Y.bind(this._setText, this)); this._textToggleLink.on('click', Y.bind(this._toggleText, this)); }, /** * Sets the caption text and displays the * toggle link if necessary. * * @method _setText * @protected */ _setText: function() { var root = this.get('root'), text = root.imageInfo.caption, textLength = root.get('captionTextLength'), cb = this.get('contentBox'); if(!root.imageInfo.caption || root.imageInfo.caption === '') { cb.set('innerHTML', ''); this._textToggleLink.setStyle('display', 'none'); return; } else if(textLength > -1) { if(!this._textToggled && textLength < text.length) { text = this._shortenText(text); this._textToggleLink.setStyle('display', 'inline-block'); } else if(this._textToggled && textLength < text.length) { text = this._stripTags(text); this._textToggleLink.setStyle('display', 'inline-block'); } else { text = this._stripTags(text); this._textToggleLink.setStyle('display', 'none'); } } else { text = this._stripTags(text); } cb.set('innerHTML', text); }, /** * Shows or hides the full text when the * toggle link is clicked. * * @method _toggleText * @protected */ _toggleText: function() { var root = this.get('root'), text = root.imageInfo.caption, cb = this.get('contentBox'); if(this._textToggled) { text = this._shortenText(text); this._textToggleLink.set('innerHTML', root.get('captionMoreLinkText')); this._textToggled = false; } else { text = this._stripTags(text); this._textToggleLink.set('innerHTML', root.get('captionLessLinkText')); this._textToggled = true; } cb.set('innerHTML', text); }, /** * Strips out HTML tags from the caption text. * * @method _stripTags * @param text {String} The text to strip HTML tags from. * @param ignoreSettings {Boolean} If true, will strip tags even if * the stripTags attribute is set to false. * @protected */ _stripTags: function(text, ignoreSettings) { var root = this.get('root'), textDiv; if(ignoreSettings || root.get('captionStripTags')) { textDiv = document.createElement('div'); textDiv.innerHTML = text; text = textDiv.textContent || textDiv.innerText; } return text; }, /** * Shortens the caption text to the length of * the textLength attribute. * * @method _shortenText * @protected */ _shortenText: function(text) { var root = this.get('root'); text = this._stripTags(text, true).substring(0, root.get('captionTextLength')); return Y.Lang.trim(text.substring(0, text.lastIndexOf(' '))) + ' ...'; } }, { /** * Custom CSS class name for the widget. * * @property CSS_PREFIX * @type String * @protected * @static */ CSS_PREFIX: 'fl-slideshow-caption', /** * Static property used to define the default attribute configuration of * the Widget. * * @property ATTRS * @type Object * @protected * @static */ ATTRS: { } }); /** * A widget for loading and transitioning between SlideshowImage * instances. Each SlideshowImage instance is a child widget of * SlideshowFrame. SlideshowFrame is a child widget of the main * slideshow widget. * * @namespace FL * @class SlideshowFrame * @constructor * @param config {Object} Configuration object * @extends Widget */ Y.namespace('FL').SlideshowFrame = Y.Base.create('fl-slideshow-frame', Y.Widget, [Y.WidgetParent, Y.WidgetChild], { /** * The imageInfo object used to load the active image. * * @property info * @type Object * @default null * @protected */ _imageInfo: null, /** * The active FL.SlideshowImage instance in the frame. * * @property _activeImage * @type FL.SlideshowImage * @default null * @protected */ _activeImage: null, /** * A FL.SlideshowImage instance used to load the * next image and transition it into the frame. * * @property _nextImage * @type FL.SlideshowImage * @default null * @protected */ _nextImage: null, /** * Used to store imageInfo if a load request is * made while the frame is transitioning. If not null * when the transition completes, a new image will * be loaded using the imageInfo. * * @property _loadQueue * @type Object * @default false * @protected */ _loadQueue: null, /** * An instance of FL.SlideshowTransition used for * the current transition in progress. * * @property _transition * @type FL.SlideshowTransition * @default null * @protected */ _transition: null, /** * A flag for whether the frame is currently transitioning or not. * * @property _transitioning * @type Boolean * @default false * @protected */ _transitioning: false, /** * Flag for whether to resize when the current transition * completes. Set to true when a resize request is made * during a transition. * * @property _resizeAfterTransition * @type Boolean * @default false * @protected */ _resizeAfterTransition: false, /** * Provides functionality for gesture based transitions * between the active and next images. * * @property _gestures * @type FL.SlideshowGestures * @default null * @protected */ _gestures: null, /** * Creates new instances of FL.SlideshowImage used in the frame. * * @method initializer * @protected */ initializer: function() { var imageConfig = this.get('imageConfig'); this._activeImage = new Y.FL.SlideshowImage(imageConfig); this._nextImage = new Y.FL.SlideshowImage(imageConfig); }, /** * Renders the FL.SlideshowImage instances used in the frame. * * @method renderUI * @protected */ renderUI: function() { this.add(this._activeImage); this.add(this._nextImage); }, /** * @method bindUI * @protected */ bindUI: function() { var activeBB = this._activeImage.get('boundingBox'), nextBB = this._nextImage.get('boundingBox'), transition = this.get('transition'); if(('ontouchstart' in window || window.navigator.msPointerEnabled) && this.get('touchSupport')) { this._gestures = new Y.FL.SlideshowGestures({ direction: transition == 'slideVertical' ? 'vertical' : 'horizontal', activeItem: activeBB, nextItem: nextBB }); this._gestures.on('moveStart', this._gesturesMoveStart, this); this._gestures.on('endComplete', this._gesturesEndComplete, this); } }, /** * Functional styles for the UI. * * @method syncUI * @protected */ syncUI: function() { var activeBB = this._activeImage.get('boundingBox'), nextBB = this._nextImage.get('boundingBox'), cb = this.get('contentBox'); activeBB.setStyle('position', 'absolute'); activeBB.setStyle('top', '0px'); activeBB.setStyle('left', '-9999px'); nextBB.setStyle('position', 'absolute'); nextBB.setStyle('top', '0px'); nextBB.setStyle('left', '-9999px'); cb.setStyle('position', 'relative'); cb.setStyle('overflow', 'hidden'); }, /** * Checks whether the imageInfo should be loaded or queued. * Initializes a new transition if loading is ok. * * @method load * @param imageInfo {Object} The image info to load. */ load: function(imageInfo) { var activeInfo = this._activeImage._imageInfo; if(this._transitioning) { this._loadQueue = imageInfo; return; } else if(activeInfo && activeInfo.largeURL == imageInfo.largeURL) { return; } this._imageInfo = imageInfo; this._transitionInit(imageInfo); }, /** * Preloads the next image using the provided imageInfo. * * @method preload * @param imageInfo {Object} The imageInfo to preload. * @param width {Number} The width to preload. * @param height {Number} The height to preload. */ preload: function(imageInfo, width, height) { this._imageInfo = imageInfo; this._nextImage.preload(imageInfo, width, height); }, /** * Unloads the active and next image instances. * * @method unload */ unload: function() { this._imageInfo = null; this._loadQueue = null; this._transitioning = false; this._transition = null; this._activeImage.detachAll(); this._activeImage.unload(); this._activeImage.get('boundingBox').setStyle('left', '-9999px'); this._nextImage.detachAll(); this._nextImage.unload(); this._nextImage.get('boundingBox').setStyle('left', '-9999px'); }, /** * Resizes the bounding box and active image. * * @method resize * @param width {Number} The width value. * @param height {Number} The height value. */ resize: function(width, height) { if(!width || !height) { return; } var bb = this.get('boundingBox'), padding = [ parseInt(bb.getComputedStyle('paddingTop'), 10), parseInt(bb.getComputedStyle('paddingRight'), 10), parseInt(bb.getComputedStyle('paddingBottom'), 10), parseInt(bb.getComputedStyle('paddingLeft'), 10) ]; width = width - padding[1] - padding[3]; height = height - padding[0] - padding[2]; this.set('width', width); this.set('height', height); if(this._transitioning) { this._resizeAfterTransition = true; } else { this._activeImage.resize(width, height); this._nextImage.resize(width, height); } }, /** * Gets the current transition to use. * * @method _getTransition * @protected */ _getTransition: function() { var root = this.get('root'), lastIndex = root.albumInfo.images.length - 1, direction = 'next', transition = root.get('transition'); if(root.lastImageIndex === null) { direction = ''; } else if(root.imageIndex == lastIndex && root.lastImageIndex === 0) { direction = 'prev'; } else if(root.imageIndex === 0 && root.lastImageIndex == lastIndex) { direction = 'next'; } else if(root.lastImageIndex > root.imageIndex) { direction = 'prev'; } else if(root.lastImageIndex < root.imageIndex) { direction = 'next'; } if(direction == 'next') { transition = transition.replace('slideHorizontal', 'slideLeft'); transition = transition.replace('slideVertical', 'slideUp'); } else if(direction == 'prev') { transition = transition.replace('slideHorizontal', 'slideRight'); transition = transition.replace('slideVertical', 'slideDown'); } return transition; }, /** * Fires the transitionInit event and loads the next image. * The transition starts when the image's loadComplete * event is fired. * * @method _transitionInit * @param imageInfo {Object} The imageInfo to load before transitioning. * @protected */ _transitionInit: function(imageInfo) { this._transitioning = true; // Disable gestures if set. if(this._gestures) { this._gestures.disable(); } /** * Fires when the next image is loading before a new transition. * * @event transitionInit */ this.fire('transitionInit'); if(imageInfo) { this._nextImage.once('loadComplete', this._transitionStart, this); this._nextImage.load(imageInfo); } else { this._transitionStart(); } }, /** * Fires the transitionStart event and starts the transition * using a new instance of FL.SlideshowTransition. * * @method _transitionStart * @protected */ _transitionStart: function() { var root = this.get('root'); /** * Fires when the next image has finished loading * and a new transition starts. * * @event transitionStart */ this.fire('transitionStart'); this._transition = new Y.FL.SlideshowTransition({ itemIn: this._nextImage._imageInfo ? this._nextImage.get('boundingBox') : null, itemOut: this._activeImage._imageInfo ? this._activeImage.get('boundingBox') : null, type: this._getTransition(), duration: root.get('transitionDuration'), easing: root.get('transitionEasing'), kenBurnsDuration: root.get('speed')/1000, kenBurnsZoom: root.get('kenBurnsZoom') }); if(this._nextImage._imageInfo) { this._nextImage.get('boundingBox').setStyle('left', '0px'); } this._transition.once('complete', this._transitionComplete, this); this._transition.run(); }, /** * Switches the next and active image variables, unloads the * last image, fires the transitionComplete event and loads * or resizes if appropriate. * * @method _transitionComplete * @protected */ _transitionComplete: function() { var root = this.get('root'); // Swap image container references. this._swapImageRefs(); /** * Fired when the current transition completes. * * @event transitionComplete */ this.fire('transitionComplete'); this._transition = null; this._transitioning = false; // Enable gestures if set. if(this._gestures) { if(root && root.albumInfo.images.length <= 1) { this._gestures.disable(); } else { this._gestures.enable(); } } // Load from the queue? if(this._loadQueue) { this.load(this._loadQueue); this._loadQueue = null; } // Resize the active image? else if(this._resizeAfterTransition) { this._resizeAfterTransition = false; this._activeImage.resize(this.get('width'), this.get('height')); this._nextImage.resize(this.get('width'), this.get('height')); } }, /** * @method _gesturesMoveStart * @param e {Object} The event object. * @protected */ _gesturesMoveStart: function(e) { var index = 0, root = this.get('root'); index = e.direction == 'next' ? root.imageIndex + 1 : root.imageIndex - 1; index = index < 0 ? root.albumInfo.images.length - 1 : index; index = index >= root.albumInfo.images.length ? 0 : index; root.pause(); root._hideLoadingImage(); root._showLoadingImageWithDelay(); Y.FL.SlideshowImageLoader.removeGroup(this._nextImage.get('loadGroup')); this._nextImage.once('loadComplete', root._hideLoadingImage, root); this._nextImage.load(root.albumInfo.images[index]); }, /** * @method _gesturesEndComplete * @protected */ _gesturesEndComplete: function() { var root = this.get('root'), index = 0; if(this._nextImage._imageInfo){ index = this._nextImage._imageInfo.index; this._swapImageRefs(); this._imageInfo = root.albumInfo.images[index]; root.loadImage(index); } }, /** * @method _swapImageRefs * @protected */ _swapImageRefs: function() { var active = this._activeImage; this._activeImage = this._nextImage; this._nextImage = active; if(this._nextImage._imageInfo) { this._nextImage.unload(); this._nextImage.get('boundingBox').setStyle('left', '-9999px'); } if(this._gestures) { this._gestures.set('activeItem', this._activeImage.get('boundingBox')); this._gestures.set('nextItem', this._nextImage.get('boundingBox')); } } }, { /** * Custom CSS class name for the widget. * @property CSS_PREFIX * @type String * @protected * @static */ CSS_PREFIX: 'fl-slideshow-frame', /** * Static property used to define the default attribute configuration of * the Widget. * * @property ATTRS * @type Object * @protected * @static */ ATTRS: { /** * The configuration object used to create new instances of * FL.SlideshowImage. See the API docs for {@link FL.SlideshowImage} * for a complete list of configuration attributes. * * @attribute imageConfig * @type Object * @default null */ imageConfig: { value: null }, /** * Whether to use touch gestures, when available, * to transition between images or not. * * @attribute touchSupport * @type Boolean * @default false */ touchSupport: { value: false } } }); /** * A plugin for fullscreen slideshow functionality. * * @namespace FL * @class SlideshowFullscreen * @constructor * @param config {Object} Configuration object * @extends Plugin.Base */ Y.namespace('FL').SlideshowFullscreen = Y.Base.create('fl-slideshow-fullscreen', Y.Plugin.Base, [], { /** * Flag for whether the slideshow is in * fullscreen mode. * * @property active * @type Boolean * @default false */ active: false, /** * A div containing the close message. * * @property _closeMessage * @type Node * @default null * @protected */ _closeMessage: null, /** * A timer for hiding the close message. * * @property _closeMessageTimer * @type Object * @default null * @protected */ _closeMessageTimer: null, /** * The initial styles of the host's bounding box * before entering fullscreen mode. * * @property _initialStyles * @type Object * @protected */ _initialStyles: { position: 'static', top: '0px', left: '0px' }, /** * @method initializer * @protected */ initializer: function() { var host = this.get('host'), bb = host.get('boundingBox'), self = this; bb.addClass('fl-fullscreen-enabled'); if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { document.addEventListener('fullscreenchange', function(){ self._osChange(); }, false); document.addEventListener('mozfullscreenchange', function(){ self._osChange(); }, false); document.addEventListener('webkitfullscreenchange', function(){ self._osChange(); }, false); } else { this._renderCloseMessage(); } }, /** * Exits fullscreen if it is currently active * otherwise it enters fullscreen. * * @method toggle */ toggle: function() { if(this.active) { this.exit(); } else { this.enter(); } }, /** * Enters OS fullscreen mode if supported, otherwise * the slideshow takes over the browser window. * * @method enter */ enter: function() { if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { this._osEnter(); } else { this._browserEnter(); } }, /** * Exits fullscreen mode. * * @method exit */ exit: function() { if(Y.FL.SlideshowFullscreen.OS_SUPPORT) { this._osExit(); } else { this._browserExit(); } }, /** * Enters OS fullscreen mode. * * @method _osEnter * @protected */ _osEnter: function() { var bbNode = this.get('host').get('boundingBox')._node; if(bbNode.webkitRequestFullScreen) { bbNode.webkitRequestFullScreen(); } else if(bbNode.mozRequestFullScreen) { bbNode.mozRequestFullScreen(); } else if(bbNode.requestFullScreen) { bbNode.requestFullScreen(); } }, /** * Exits OS fullscreen mode. * * @method _osExit * @protected */ _osExit: function() { if(document.exitFullscreen) { document.exitFullscreen(); } else if(document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if(document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } }, /** * Called when the OS fullscreenchange event fires and enters * or exits standard fullscreen mode which positions and * resizes the slideshow. * * @method _osChange * @protected */ _osChange: function() { var host = this.get('host'); // Transitions break on Safari while entering and // exiting fullscreen. This fixes them! if(host.frame && host.frame._transitioning) { host.frame._transitionComplete(); } if(this.active) { this._exit(); } else { this._enter(); } }, /** * Enter browser fullscreen mode. * * @method _browserEnter * @protected */ _browserEnter: function() { var bb = this.get('host').get('boundingBox'); this._initialStyles = { position: bb.getStyle('position'), top: bb.getStyle('top'), left: bb.getStyle('left'), zIndex: bb.getStyle('zIndex') }; bb.setStyles({ position: 'fixed', top: '0px', left: '0px', zIndex: 10000 }); Y.Node.one('body').on('fl-fullscreen|keydown', Y.bind(this._onKey, this)); this._showCloseMessage(); this._enter(); }, /** * Exit browser fullscreen mode. * * @method _browserExit * @protected */ _browserExit: function() { var bb = this.get('host').get('boundingBox'); bb.setStyles({ position: this._initialStyles.position, top: this._initialStyles.top, left: this._initialStyles.left, zIndex: this._initialStyles.zIndex }); Y.Node.one('body').detach('fl-fullscreen|keydown'); this._hideCloseMessage(); this._exit(); }, /** * Enters fullscreen mode. * * @method _enter * @protected */ _enter: function() { var host = this.get('host'), bb = host.get('boundingBox'); bb.addClass('fl-fullscreen-active'); this.active = true; host.resize(); }, /** * Exits fullscreen mode. * * @method _exit * @protected */ _exit: function() { var host = this.get('host'), bb = host.get('boundingBox'); bb.removeClass('fl-fullscreen-active'); this.active = false; host.resize(); }, /** * Keyboard input for the esc button. * * @method _onKey * @protected */ _onKey: function(e) { if(e.keyCode == 27) { this.exit(); return false; } }, /** * Creates the close message if one is * not already available in the document. * * @method _initCloseMessage * @protected */ _renderCloseMessage: function() { this._closeMessage = Y.Node.create('
'); this._closeMessage.set('innerHTML', 'Press the "esc" button to exit fullscreen mode.'); this._closeMessage.setStyle('display', 'none'); this.get('host').get('boundingBox').insert(this._closeMessage); }, /** * Shows the close message. * * @method _showCloseMessage * @protected */ _showCloseMessage: function() { if(this._closeMessageTimer) { this._closeMessageTimer.cancel(); this._closeMessageTimer = null; } this._closeMessage.show(true); this._closeMessageTimer = Y.later(4000, this, this._hideCloseMessage); }, /** * Hides the close message. * * @method _hideCloseMessage * @protected */ _hideCloseMessage: function() { if(this._closeMessageTimer) { this._closeMessageTimer.cancel(); this._closeMessageTimer = null; } this._closeMessage.hide(true); } }, { /** * Namespace for the plugin. * * @property NS * @type String * @protected * @static */ NS: 'fullscreen', OS_SUPPORT: (function(){ var doc = document.documentElement; return doc.webkitRequestFullScreen || doc.mozRequestFullScreen || doc.requestFullScreen; })() }); /** * Provides functionality for gesture based transitions * between two slideshow components. * * @namespace FL * @class SlideshowGestures * @constructor * @param config {Object} Configuration object * @extends Base */ Y.namespace('FL').SlideshowGestures = Y.Base.create('fl-slideshow-gestures', Y.Base, [], { /** * The x coordinate for where a gesture event starts. * * @property _startX * @type Number * @default null * @protected */ _startX: null, /** * The y coordinate for where a gesture event starts. * * @property _startY * @type Number * @default null * @protected */ _startY: null, /** * A flag for whether a gesture is moving or not. * * @property _moving * @type Boolean * @default false * @protected */ _touchMoving: false, /** * Whether the gesture is moving or not. * * @property _moving * @type Boolean * @default false * @protected */ _moving: false, /** * The direction the current gesture event * is moving in (either next or prev). * * @property _movingDirection * @type String * @default null * @protected */ _movingDirection: null, /** * A flag for whether a gesture gesture is currently * transitioning or not. * * @property _transitioning * @type Boolean * @default false * @protected */ _transitioning: false, /** * @method initializer * @protected */ initializer: function() { this.enable(); }, /** * @method enable */ enable: function() { var id = this.get('id'), direction = this.get('direction'), active = this.get('activeItem'), next = this.get('nextItem'); active.on(id + '|gesturemovestart', Y.bind(this._onStart, this)); next.on(id + '|gesturemovestart', Y.bind(this._onStart, this)); next.on(id + '|transitionend', Y.bind(this._onEndComplete, this) ); next.on(id + '|oTransitionEnd', Y.bind(this._onEndComplete, this) ); next.on(id + '|webkitTransitionEnd', Y.bind(this._onEndComplete, this) ); if(direction == 'horizontal') { active.on(id + '|gesturemovehorizontal', Y.bind(this._onMoveHorizontal, this)); active.on(id + '|gesturemovehorizontalend', Y.bind(this._onEndHorizontal, this)); next.on(id + '|gesturemovehorizontal', Y.bind(this._onMoveHorizontal, this)); next.on(id + '|gesturemovehorizontalend', Y.bind(this._onEndHorizontal, this)); } else { active.on(id + '|gesturemovevertical', Y.bind(this._onMoveVertical, this)); active.on(id + '|gesturemoveverticalend', Y.bind(this._onEndVertical, this)); next.on(id + '|gesturemovevertical', Y.bind(this._onMoveVertical, this)); next.on(id + '|gesturemoveverticalend', Y.bind(this._onEndVertical, this)); } }, /** * @method disable */ disable: function() { var id = this.get('id'), active = this.get('activeItem'), next = this.get('nextItem'); active.detach(id + '|*'); next.detach(id + '|*'); }, /** * @method _onStart * @param e {Object} The event object. * @protected */ _onStart: function(e) { var direction = this.get('direction'); if(this._transitioning) { this._onEndComplete(); } if(direction == 'horizontal') { this._startX = e.pageX; } else { this._startY = e.pageY; } /** * @event start */ this.fire('start'); }, /** * @method _onMoveHorizontal * @param e {Object} The event object. * @protected */ _onMoveHorizontal: function(e) { var x = this._startX - e.pageX, active = this.get('activeItem'), next = this.get('nextItem'), width = parseInt(active.getComputedStyle('width'), 10), translate = x < 0 ? Math.abs(x) : -x, direction = x < 0 ? 'prev' : 'next'; e.preventDefault(); if(!this._moving || this._movingDirection != direction) { active.setStyle('left', 0); next.setStyles({ 'opacity': 1, 'left': x < 0 ? -width : width }); this._moving = true; this._movingDirection = direction; /** * @event moveStart */ this.fire('moveStart', { direction: direction }); } active.setStyle('-webkit-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); active.setStyle('-ms-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); next.setStyle('-webkit-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); next.setStyle('-ms-transform', 'translate('+ translate +'px, 0px) translateZ(0px)'); /** * @event move */ this.fire('move'); }, /** * @method _onMoveVertical * @param e {Object} The event object. * @protected */ _onMoveVertical: function(e) { var y = this._startY - e.pageY, active = this.get('activeItem'), next = this.get('nextItem'), height = parseInt(active.getComputedStyle('height'), 10), translate = y < 0 ? Math.abs(y) : -y, direction = y < 0 ? 'prev' : 'next'; e.preventDefault(); if(!this._moving || this._movingDirection != direction) { active.setStyle('top', 0); next.setStyles({ 'opacity': 1, 'left' : 'auto', 'top': y < 0 ? -height : height }); this._moving = true; this._movingDirection = direction; /** * @event moveStart */ this.fire('moveStart', { direction: direction }); } active.setStyle('-webkit-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); active.setStyle('-ms-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); next.setStyle('-webkit-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); next.setStyle('-ms-transform', 'translate(0px, '+ translate +'px) translateZ(0px)'); /** * @event move */ this.fire('move'); }, /** * @method _onEndHorizontal * @param e {Object} The event object. * @protected */ _onEndHorizontal: function(e) { if(!this._moving) { return; } var x = this._startX - e.pageX, active = this.get('activeItem'), next = this.get('nextItem'), width = parseInt(next.getComputedStyle('width'), 10), translate = x < 0 ? width : -width; active.transition({ 'transform': 'translate('+ translate +'px, 0px)' }); next.transition({ 'transform': 'translate('+ translate +'px, 0px)' }); this._transitioning = true; /** * @event end */ this.fire('end'); }, /** * @method _onEndVertical * @param e {Object} The event object. * @protected */ _onEndVertical: function(e) { if(!this._moving) { return; } var y = this._startY - e.pageY, active = this.get('activeItem'), next = this.get('nextItem'), height = parseInt(next.getComputedStyle('height'), 10), translate = y < 0 ? height : -height; active.transition({ 'transform': 'translate(0px, '+ translate +'px)' }); next.transition({ 'transform': 'translate(0px, '+ translate +'px)' }); this._transitioning = true; /** * @event end */ this.fire('end'); }, /** * @method _onEndComplete * @protected */ _onEndComplete: function() { var direction = this.get('direction'), active = this.get('activeItem'), next = this.get('nextItem'); active.setStyles({ 'opacity': 0, '-webkit-transform': '', '-webkit-transition': '', '-ms-transform': '', '-ms-transition': '' }); next.setStyles({ '-webkit-transform': '', '-webkit-transition': '', '-ms-transform': '', '-ms-transition': '' }); if(direction == 'horizontal') { active.setStyle('left', '-9999px'); next.setStyle('left', '0px'); } else { active.setStyle('top', '-9999px'); next.setStyle('top', '0px'); } this.set('activeItem', next); this.set('nextItem', active); this._moving = false; this._movingDirection = null; this._transitioning = false; /** * @event endComplete */ this.fire('endComplete'); } }, { /** * Static property used to define the default attribute configuration of * the Widget. * * @property ATTRS * @type Object * @protected * @static */ ATTRS: { /** * The gesture direction to use. Possible values are * horizontal and vertical. * * @attribute direction * @type String * @default horizontal */ direction: { value: 'horizontal' }, /** * The Node that is currently visible. * * @attribute activeItem * @type Node * @default null */ activeItem: { value: null }, /** * The Node that will be transitioned in. * * @attribute nextItem * @type Node * @default null */ nextItem: { value: null } } }); /** * A load queue for slideshow images. * * @namespace FL * @class SlideshowImageLoader * @static */ Y.namespace('FL').SlideshowImageLoader = { /** * Whether an image is being loaded or not. * * @property _loading * @type Boolean * @default false * @protected */ _loading: false, /** * An node for loading the next image. * * @property _currentImage * @type Node * @default null * @protected */ _currentImage: null, /** * An object containing the group, src and callback * for the current image that is being loaded. * * @property _currentImageData * @type Object * @default null * @protected */ _currentImageData: null, /** * An array of image data objects that contain the group, * src and callback for each image that will be loaded. * * @property _queue * @type Array * @default [] * @protected */ _queue: [], /** * Adds an image to the queue. * * @method add * @param group {String} The group this image is associated with. * Used to remove images in bulk. * @param src {String} The image url to load. * @param callback {Function} A function to call when the image * has finished loading. * @param bump {Boolean} If true, the image will be added to * the first position in the queue. */ add: function(group, src, callback, bump) { var imgData = { group : group, src : src, callback : callback }; if(bump) { this._queue.unshift(imgData); } else { this._queue.push(imgData); } if(!this._loading) { this._load(); } }, /** * Removes a group of images from the queue. * * @method removeGroup * @param group {String} The group to remove. */ removeGroup: function(group) { var i = this._queue.length - 1; for( ; i > -1 ; i--) { if(this._queue[i].group == group) { this._queue.splice(i, 1); } } if(this._currentImageData && this._currentImageData.group == group) { this._currentImage.detachAll(); this._currentImage = null; this._currentImageData = null; if(this._queue.length > 0) { this._load(); } else { this._loading = false; } } }, /** * Loads the next image in the queue. * * @method _load * @protected */ _load: function() { this._loading = true; this._currentImageData = this._queue.shift(); this._currentImage = Y.Node.create('
');
cb.appendChild(this._buttons.pin);
}
}, {
/**
* Custom CSS class name for the widget.
*
* @property CSS_PREFIX
* @type String
* @protected
* @static
*/
CSS_PREFIX: 'fl-slideshow-social',
/**
* Static property used to define the default attribute configuration of
* the Widget.
*
* @property ATTRS
* @type Object
* @protected
* @static
*/
ATTRS: {
}
});
/**
* Creates a grid of FL.SlideshowImage instances.
*
* @namespace FL
* @class SlideshowThumbs
* @constructor
* @param config {Object} Configuration object
* @extends Widget
*/
Y.namespace('FL').SlideshowThumbs = Y.Base.create('fl-slideshow-thumbs', Y.Widget, [Y.WidgetParent, Y.WidgetChild], {
/**
* A div node used to hide the overflow when
* transitioning between pages.
*
* @property _clipBox
* @type Object
* @default null
* @protected
*/
_clipBox: null,
/**
* A div node used to hold the pages.
*
* @property _pagesBox
* @type Object
* @default null
* @protected
*/
_pagesBox: null,
/**
* A reference to the active page div node. Holds a grid
* of FL.SlideshowImage instances.
*
* @property _activePageBox
* @type Object
* @default null
* @protected
*/
_activePageBox: null,
/**
* The index of the active page of thumbs.
*
* @property _activePageIndex
* @type Number
* @default 0
* @protected
*/
_activePageIndex: 0,
/**
* A reference to the next page div node. Holds a grid
* of FL.SlideshowImage instances.
*
* @property _nextPageBox
* @type Object
* @default null
* @protected
*/
_nextPageBox: null,
/**
* An array of FL.SlideshowImage instances in the active page.
*
* @property _activeImages
* @type Array
* @default null
* @protected
*/
_activeImages: null,
/**
* An array of FL.SlideshowImage instances used to
* preload the next page of images.
*
* @property _nextImages
* @type Array
* @default null
* @protected
*/
_nextImages: null,
/**
* An array of FL.SlideshowImage instances used to
* preload the previous page of images.
*
* @property _prevImages
* @type Array
* @default null
* @protected
*/
_prevImages: null,
/**
* An instance of FL.SlideshowNav used for the left nav.
*
* @property _leftNav
* @type Object
* @default null
* @protected
*/
_leftNav: null,
/**
* An instance of FL.SlideshowNav used for the right nav.
*
* @property _rightNav
* @type Object
* @default null
* @protected
*/
_rightNav: null,
/**
* An instance of FL.SlideshowNav used for the top nav.
*
* @property _topNav
* @type Object
* @default null
* @protected
*/
_topNav: null,
/**
* An instance of FL.SlideshowNav used for the bottom nav.
*
* @property _bottomNav
* @type Object
* @default null
* @protected
*/
_bottomNav: null,
/**
* Height of the bounding box.
*
* @property _bbHeight
* @type Number
* @default 0
* @protected
*/
_bbHeight: 0,
/**
* Width of the bounding box.
*
* @property _bbWidth
* @type Number
* @default 0
* @protected
*/
_bbWidth: 0,
/**
* Width of the content box.
*
* @property _cbWidth
* @type Number
* @default 0
* @protected
*/
_cbWidth: 0,
/**
* Left margin of the clip box.
*
* @property _clipBoxMarginLeft
* @type Number
* @default 0
* @protected
*/
_clipBoxMarginLeft: 0,
/**
* Top position of the clip box.
*
* @property _clipBoxTop
* @type Number
* @default 0
* @protected
*/
_clipBoxTop: 0,
/**
* The number of columns per page.
*
* @property _colsPerPage
* @type Number
* @default 0
* @protected
*/
_colsPerPage: 0,
/**
* The number of rows per page.
*
* @property _rowsPerPage
* @type Number
* @default 0
* @protected
*/
_rowsPerPage: 0,
/**
* The number of images per page.
*
* @property _imagesPerPage
* @type Number
* @default 0
* @protected
*/
_imagesPerPage: 0,
/**
* The number of pages.
*
* @property _numPages
* @type Number
* @default 0
* @protected
*/
_numPages: 0,
/**
* Height of the pages.
*
* @property _pageHeight
* @type Number
* @default 0
* @protected
*/
_pageHeight: 0,
/**
* Width of the pages.
*
* @property _pageWidth
* @type Number
* @default 0
* @protected
*/
_pageWidth: 0,
/**
* The horizontal spacing between thumbs.
*
* @property _horizontalSpacing
* @type Number
* @default 0
* @protected
*/
_horizontalSpacing: 0,
/**
* The vertical spacing between thumbs.
*
* @property _verticalSpacing
* @type Number
* @default 0
* @protected
*/
_verticalSpacing: 0,
/**
* Width of the left nav.
*
* @property _leftNavWidth
* @type Number
* @default 0
* @protected
*/
_leftNavWidth: 0,
/**
* Width of the right nav.
*
* @property _rightNavWidth
* @type Number
* @default 0
* @protected
*/
_rightNavWidth: 0,
/**
* An instance of FL.SlideshowTransition for the current transition.
*
* @property _transition
* @type FL.SlideshowTransition
* @default null
* @protected
*/
_transition: null,
/**
* Whether the pages are currently transitioning or not.
*
* @property _verticalSpacing
* @type Boolean
* @default false
* @protected
*/
_transitioning: false,
/**
* Direction of the current transition.
*
* @property _transitionDirection
* @type String
* @default next
* @protected
*/
_transitionDirection: 'next',
/**
* Provides functionality for gesture based transitions
* between the active and next pages.
*
* @property _gestures
* @type FL.SlideshowGestures
* @default null
* @protected
*/
_gestures: null,
/**
* Initialize image vars.
*
* @method initializer
* @protected
*/
initializer: function()
{
this._activeImages = [];
this._nextImages = [];
this._prevImages = [];
},
/**
* Renders the UI boxes.
*
* @method renderUI
* @protected
*/
renderUI: function()
{
this._renderBoxes();
this._renderNavs();
},
/**
* Binds the UI events.
*
* @method bindUI
* @protected
*/
bindUI: function()
{
var root = this.get('root'),
id = this.get('id'),
transition = this.get('transition');
root.on(id + '|albumLoadComplete', this._albumLoadComplete, this);
if('ontouchstart' in window && this.get('touchSupport')) {
this._gestures = new Y.FL.SlideshowGestures({
direction: transition == 'slideVertical' ? 'vertical' : 'horizontal',
activeItem: this._activePageBox,
nextItem: this._nextPageBox
});
this._gestures.on('moveStart', this._gesturesMoveStart, this);
this._gestures.on('endComplete', this._gesturesEndComplete, this);
}
},
/**
* Syncs the UI boxes.
*
* @method syncUI
* @protected
*/
syncUI: function()
{
this._syncBoxes();
this._syncNavs();
},
/**
* @method destructor
* @protected
*/
destructor: function()
{
var root = this.get('root'),
id = this.get('id');
root.detach(id + '|*');
Y.FL.SlideshowImageLoader.removeGroup('thumbs');
},
/**
* Unload all images.
*
* @method unload
*/
unload: function()
{
var root = this.get('root'),
id = this.get('id'),
i = 0;
root.detach(id + '|imageLoadComplete');
Y.FL.SlideshowImageLoader.removeGroup('thumbs');
for( ; i < this._activeImages.length; i++) {
this._activeImages[i].unload();
}
},
/**
* Resizes the UI boxes.
*
* @method resize
*/
resize: function()
{
this._setSizeInfo();
this._togglePageButtons();
this._resizeBoxes();
this._resizeNavs();
if(this.get('root').albumInfo) {
Y.FL.SlideshowImageLoader.removeGroup('thumbs');
this._renderActivePage();
this._preloadNextPage();
this._preloadPrevPage();
}
// Enable or disable gestures.
if(this._gestures && this._numPages < 2) {
this._gestures.disable();
}
else if(this._gestures) {
this._gestures.enable();
}
},
/**
* Transitions to the previous page.
*
* @method prevPage
* @protected
*/
prevPage: function()
{
if(this._transitioning) {
return;
}
this._transitionStart('prev');
},
/**
* Transitions to the next page.
*
* @method nextPage
* @protected
*/
nextPage: function()
{
if(this._transitioning) {
return;
}
this._transitionStart('next');
},
/**
* Called when an album is loaded into the root slideshow widget.
*
* @method _albumLoadComplete
* @protected
*/
_albumLoadComplete: function()
{
var root = this.get('root'),
id = this.get('id');
root.once(id + '|imageLoadComplete', this.resize, this);
root.on(id + '|imageLoadComplete', this._imageLoadComplete, this);
},
/**
* Called when an image is loaded into the root slideshow widget.
*
* @method _imageLoadComplete
* @protected
*/
_imageLoadComplete: function()
{
var albumInfo = this.get('root').albumInfo,
lastActive = Y.one('.fl-slideshow-image-active'),
lastInfo = lastActive ? lastActive._imageInfo : null,
nextActive = null,
nextInfo = this.get('root').imageInfo;
this._setActiveImage(this._activeImages);
nextActive = Y.one('.fl-slideshow-image-active');
if(lastActive && !nextActive) {
if(nextInfo.index === 0 && lastInfo.index === albumInfo.images.length - 1) {
this.nextPage();
}
else if(lastInfo.index === 0 && nextInfo.index === albumInfo.images.length - 1) {
this.prevPage();
}
else if(lastInfo.index < nextInfo.index) {
this.nextPage();
}
else if(lastInfo.index > nextInfo.index) {
this.prevPage();
}
}
},
/**
* Renders the boxes.
*
* @method _renderBoxes
* @protected
*/
_renderBoxes: function()
{
// Clip box
this._clipBox = Y.Node.create('');
this._clipBox.addClass('fl-slideshow-thumbs-clip');
this.get('contentBox').insert(this._clipBox);
// Pages box
this._pagesBox = Y.Node.create('');
this._pagesBox.addClass('fl-slideshow-thumbs-pages');
this._clipBox.insert(this._pagesBox);
// Active page box
this._activePageBox = Y.Node.create('');
this._activePageBox.addClass('fl-slideshow-thumbs-page');
this._pagesBox.insert(this._activePageBox);
// Next page box
this._nextPageBox = Y.Node.create('');
this._nextPageBox.addClass('fl-slideshow-thumbs-page');
this._pagesBox.insert(this._nextPageBox);
},
/**
* Syncs the boxes.
*
* @method _syncBoxes
* @protected
*/
_syncBoxes: function()
{
// Active page box
this._activePageBox.setStyle('left', '0');
// Next page box
this._nextPageBox.setStyle('left', '-9999px');
},
/**
* Resizes the boxes.
*
* @method _resizeBoxes
* @protected
*/
_resizeBoxes: function()
{
this.set('width', this._bbWidth);
this.set('height', this._bbHeight);
this.get('contentBox').setStyle('width', this._cbWidth + 'px');
this._clipBox.setStyle('width', this._pageWidth + 'px');
this._clipBox.setStyle('height', this._pageHeight + 'px');
this._clipBox.setStyle('padding', this._verticalSpacing + 'px 0 0 ' + this._horizontalSpacing + 'px ');
this._clipBox.setStyle('margin', '0 0 0 ' + this._clipBoxMarginLeft + 'px');
this._clipBox.setStyle('top', this._clipBoxTop);
this._pagesBox.setStyle('width', this._pageWidth + 'px');
this._pagesBox.setStyle('height', this._pageHeight + 'px');
this._activePageBox.setStyle('width', this._pageWidth + 'px');
this._activePageBox.setStyle('height', this._pageHeight + 'px');
this._nextPageBox.setStyle('width', this._pageWidth + 'px');
this._nextPageBox.setStyle('height', this._pageHeight + 'px');
},
/**
* Renders the active page of images.
*
* @method _renderActivePage
* @protected
*/
_renderActivePage: function()
{
var i = 0,
root = this.get('root'),
imageIndex = this._imagesPerPage * this._activePageIndex,
endIndex = imageIndex + this._imagesPerPage,
images = root.albumInfo.images;
this._clearActiveImage();
// Remove current images
for( ; i < this._activeImages.length; i++) {
this._activeImages[i].remove();
this._activeImages[i].unload();
this._activeImages[i].get('boundingBox')._imageInfo = null;
this._activeImages[i].get('boundingBox').remove();
}
// Draw images
for(i = 0; imageIndex < endIndex; imageIndex++) {
if(!images[imageIndex]) {
break;
}
this._renderImage(this._activeImages, i, this._activePageBox, images[imageIndex]);
i++;
}
this._setActiveImage(this._activeImages);
},
/**
* Renders the next page of images.
*
* @method _renderNextPage
* @protected
*/
_renderNextPage: function()
{
var i = 0,
imageArray = this._transitionDirection == 'next' ? this._nextImages : this._prevImages;
this._nextPageBox.get('children').remove();
for( ; i < imageArray.length; i++) {
if(imageArray[i]._imageInfo) {
this._renderImage(imageArray, i, this._nextPageBox, imageArray[i]._imageInfo);
}
else {
break;
}
}
this._setActiveImage(imageArray);
},
/**
* Preloads the next page of images.
*
* @method _preloadNextPage
* @protected
*/
_preloadNextPage: function()
{
var pageIndex = this._activePageIndex + 1 >= this._numPages ? 0 : this._activePageIndex + 1;
this._preloadPage(pageIndex, this._nextImages);
},
/**
* Preloads the previous page of images.
*
* @method _preloadPrevPage
* @protected
*/
_preloadPrevPage: function()
{
var pageIndex = this._activePageIndex - 1 < 0 ? this._numPages - 1 : this._activePageIndex - 1;
this._preloadPage(pageIndex, this._prevImages);
},
/**
* Preloads a page of images.
*
* @method _preloadPage
* @param imageIndex {Number} The image index to start preloading from.
* @param imageArray {Array} The array to store the preloaded images.
* @protected
*/
_preloadPage: function(pageIndex, imageArray)
{
var i = 0,
root = this.get('root'),
images = root.albumInfo.images,
imageIndex = pageIndex * this._imagesPerPage,
endIndex = imageIndex + this._imagesPerPage,
imageConfig = this.get('imageConfig'),
width = imageConfig.width,
height = imageConfig.height;
if(this._numPages > 1) {
// Unload existing images
for( ; i < imageArray.length; i++) {
imageArray[i].remove();
imageArray[i].unload();
}
// Preload the images
for(i = 0; imageIndex < endIndex; imageIndex++) {
if(!images[imageIndex]) {
continue;
}
this._renderImage(imageArray, i);
imageArray[i].preload(images[imageIndex], width, height);
i++;
}
}
},
/**
* Renders an image.
*
* @method _renderImage
* @protected
*/
_renderImage: function(imageArray, i, page, imageInfo)
{
var imageBB = null,
imageConfig = this.get('imageConfig');
// Create the image?
if(typeof imageArray[i] == 'undefined') {
imageConfig.loadGroup = 'thumbs';
imageConfig.useThumbSizes = true;
imageConfig.loadVideos = false;
imageArray[i] = new Y.FL.SlideshowImage(imageConfig);
imageBB = imageArray[i].get('boundingBox');
imageBB.on('click', this._imageClick, this);
imageBB.on('mouseover', this._imageMouseover, this);
imageBB.on('mouseout', this._imageMouseout, this);
}
// Image bounding box
imageBB = imageArray[i].get('boundingBox');
imageBB.setStyle('margin', '0 ' + this._horizontalSpacing + 'px ' + this._verticalSpacing + 'px 0');
// Add the image to a page?
if(page) {
this._childrenContainer = page;
this.add(imageArray[i]);
imageArray[i].resize(imageConfig.width, imageConfig.height);
}
// Load the image?
if(imageInfo) {
imageArray[i].load(imageInfo);
imageBB._imageInfo = imageInfo;
}
},
/**
* Overrides the WidgetParent _uiAddChild method so _renderImage
* will render to the appropriate page.
*
* @method _uiAddChild
* @protected
* @param child {Widget} The child Widget instance to render.
* @param parentNode {Object} The Node under which the
* child Widget is to be rendered. Set to the appropriate page
* in the _renderImage method by setting _childrenContainer.
*/
_uiAddChild: function (child, parentNode)
{
child.render(parentNode);
parentNode.appendChild(child.get('boundingBox'));
},
/**
* Called when an image is clicked.
*
* @method _imageClick
* @protected
*/
_imageClick: function(e)
{
var root = this.get('root');
if(this.get('pauseOnClick')) {
root.pause();
}
root.loadImage(e.currentTarget._imageInfo.index);
/**
* Fires when an image is clicked.
*
* @event imageClick
*/
this.fire('imageClick');
},
/**
* Sets the active image.
*
* @method _setActiveImage
* @param imageArray {Array} The image array to check for the active image.
* @protected
*/
_setActiveImage: function(imageArray)
{
var i = 0;
this._clearActiveImage();
for( ; i < imageArray.length; i++) {
if(imageArray[i]._imageInfo) {
if(imageArray[i]._imageInfo.index == this.get('root').imageInfo.index) {
imageArray[i].get('boundingBox').addClass('fl-slideshow-image-active');
break;
}
}
}
},
/**
* Removes the class name 'fl-slideshow-image-active'
* from the active image.
*
* @method _clearActiveImage
* @protected
*/
_clearActiveImage: function()
{
var active = Y.one('.fl-slideshow-image-active');
if(active) {
active.removeClass('fl-slideshow-image-active');
}
},
/**
* Gets the transition type.
*
* @method _getTransition
* @protected
*/
_getTransition: function()
{
var transition = this.get('transition');
if(transition == 'slideHorizontal' && this._transitionDirection == 'next') {
return 'slideLeft';
}
else if(transition == 'slideHorizontal' && this._transitionDirection == 'prev') {
return 'slideRight';
}
else if(transition == 'slideVertical' && this._transitionDirection == 'next') {
return 'slideUp';
}
else if(transition == 'slideVertical' && this._transitionDirection == 'prev') {
return 'slideDown';
}
return transition;
},
/**
* Starts the transition, moving in the provided direction.
*
* @method _transitionStart
* @param direction {String} The direction to transition.
* @protected
*/
_transitionStart: function(direction)
{
if(this._numPages > 1) {
Y.FL.SlideshowImageLoader.removeGroup('thumbs');
this._transitionDirection = direction;
this._transitioning = true;
this._nextPageBox.setStyle('left', '0px');
this._renderNextPage();
this._transition = new Y.FL.SlideshowTransition({
itemIn: this._nextPageBox,
itemOut: this._activePageBox,
type: this._getTransition(),
duration: this.get('transitionDuration'),
easing: this.get('transitionEasing')
});
this._transition.once('complete', this._transitionComplete, this);
this._transition.run();
// Disable gestures if set.
if(this._gestures) {
this._gestures.disable();
}
}
},
/**
* Transition cleanup called when the current transition ends.
*
* @method _transitionComplete
* @protected
*/
_transitionComplete: function()
{
this._swapPageRefs();
this._transitioning = false;
this._transitionDirection = '';
this._transition = null;
// Enable gestures if set.
if(this._gestures) {
this._gestures.enable();
}
/**
* Fires when a page transition completes.
*
* @event transitionComplete
*/
this.fire('transitionComplete');
},
/**
* @method _gesturesMoveStart
* @param e {Object} The event object.
* @protected
*/
_gesturesMoveStart: function(e)
{
Y.FL.SlideshowImageLoader.removeGroup('thumbs');
this._transitionDirection = e.direction;
this._renderNextPage();
},
/**
* @method _gesturesEndComplete
* @protected
*/
_gesturesEndComplete: function()
{
this._swapPageRefs();
this._transitionDirection = '';
this.fire('transitionComplete');
},
/**
* Swaps the active page and next page references when
* a transition completes and sets the active page index.
*
* @method _swapPageRefs
* @protected
*/
_swapPageRefs: function()
{
var lastBox = this._activePageBox,
lastImages = this._activeImages;
this._activePageBox = this._nextPageBox;
this._nextPageBox = lastBox;
this._nextPageBox.setStyle('left', '-9999px');
if(this._transitionDirection == 'next') {
this._activeImages = this._nextImages;
this._nextImages = lastImages;
}
else {
this._activeImages = this._prevImages;
this._prevImages = lastImages;
}
// Active page index
if(this._transitionDirection == 'next' && this._activePageIndex + 1 < this._numPages) {
this._activePageIndex++;
}
else if(this._transitionDirection == 'next') {
this._activePageIndex = 0;
}
else if(this._transitionDirection == 'prev' && this._activePageIndex - 1 > -1) {
this._activePageIndex--;
}
else if(this._transitionDirection == 'prev') {
this._activePageIndex = this._numPages - 1;
}
// Swap gesture refs
if(this._gestures) {
this._gestures.set('activeItem', this._activePageBox);
this._gestures.set('nextItem', this._nextPageBox);
}
this._preloadNextPage();
this._preloadPrevPage();
},
/**
* Renders the enabled navs.
*
* @method _renderNavs
* @protected
*/
_renderNavs: function()
{
var topNavButtons = this.get('topNavButtons'),
rightNavButtons = this.get('rightNavButtons'),
bottomNavButtons = this.get('bottomNavButtons'),
leftNavButtons = this.get('leftNavButtons');
if(this.get('topNavEnabled') && topNavButtons.length > 0) {
this._topNav = new Y.FL.SlideshowNav({ buttons: topNavButtons });
this._topNav.get('boundingBox').addClass('fl-slideshow-thumbs-top-nav');
this.add(this._topNav);
this._topNav.render(this.get('contentBox'));
this._clipBox.insert(this._topNav.get('boundingBox'), 'before');
this._bindNavEvents(this._topNav);
}
if(this.get('rightNavEnabled') && rightNavButtons.length > 0) {
this._rightNav = new Y.FL.SlideshowNav({ buttons: rightNavButtons });
this._rightNav.get('boundingBox').addClass('fl-slideshow-thumbs-right-nav');
this.add(this._rightNav);
this._rightNav.render(this.get('contentBox'));
this._bindNavEvents(this._rightNav);
}
if(this.get('bottomNavEnabled') && bottomNavButtons.length > 0) {
this._bottomNav = new Y.FL.SlideshowNav({ buttons: bottomNavButtons });
this._bottomNav.get('boundingBox').addClass('fl-slideshow-thumbs-bottom-nav');
this.add(this._bottomNav);
this._bottomNav.render(this.get('contentBox'));
this._bindNavEvents(this._bottomNav);
}
if(this.get('leftNavEnabled') && leftNavButtons.length > 0) {
this._leftNav = new Y.FL.SlideshowNav({ buttons: leftNavButtons });
this._leftNav.get('boundingBox').addClass('fl-slideshow-thumbs-left-nav');
this.add(this._leftNav);
this._leftNav.render(this.get('contentBox'));
this._bindNavEvents(this._leftNav);
}
},
/**
* Syncs the navs.
*
* @method _syncNavs
* @protected
*/
_syncNavs: function()
{
var rightNavBB, bottomNavBB, leftNavBB;
if(this._rightNav) {
rightNavBB = this._rightNav.get('boundingBox');
rightNavBB.setStyle('position', 'absolute');
rightNavBB.setStyle('top', '0px');
rightNavBB.setStyle('right', '0px');
}
if(this._bottomNav) {
bottomNavBB = this._bottomNav.get('boundingBox');
bottomNavBB.setStyle('position', 'absolute');
bottomNavBB.setStyle('bottom', '0px');
bottomNavBB.setStyle('width', '100%');
}
if(this._leftNav) {
leftNavBB = this._leftNav.get('boundingBox');
leftNavBB.setStyle('position', 'absolute');
leftNavBB.setStyle('top', '0px');
leftNavBB.setStyle('left', '0px');
}
},
/**
* Resizes the navs.
*
* @method _resizeNavs
* @protected
*/
_resizeNavs: function()
{
var rightNavBB,
leftNavBB,
marginTop;
if(this._rightNav) {
rightNavBB = this._rightNav.get('boundingBox');
marginTop = this._bbHeight/2 - parseInt(rightNavBB.getComputedStyle('height'), 10)/2;
rightNavBB.setStyle('marginTop', marginTop + 'px');
}
if(this._leftNav) {
leftNavBB = this._leftNav.get('boundingBox');
marginTop = this._bbHeight/2 - parseInt(leftNavBB.getComputedStyle('height'), 10)/2;
leftNavBB.setStyle('marginTop', marginTop + 'px');
}
},
/**
* Binds events to the provided nav.
*
* @method _bindNavEvents
* @param nav {Object} The nav to bind to.
* @protected
*/
_bindNavEvents: function(nav)
{
if(nav._buttons.prevPage) {
nav._buttons.prevPage.on('click', this.prevPage, this);
}
if(nav._buttons.nextPage) {
nav._buttons.nextPage.on('click', this.nextPage, this);
}
nav.on('resize', this.resize, this);
},
/**
* Hides the prev page and next page buttons
* if there is only one page of thumbs.
*
* @method _togglePageButtons
* @protected
*/
_togglePageButtons: function()
{
var buttons = this.get('boundingBox').all('.fl-slideshow-nav-prevPage, .fl-slideshow-nav-nextPage'),
display = buttons.getStyle('display')[0];
if(this._numPages == 1 && display == 'inline-block') {
buttons.setStyle('display', 'none');
this._setSizeInfo();
}
else if(this._numPages > 1 && display == 'none') {
buttons.setStyle('display', 'inline-block');
this._setSizeInfo();
}
},
/**
* Sets the size info used when resizing and loading pages.
*
* @method _setSizeInfo
* @protected
*/
_setSizeInfo: function()
{
var root = this.get('root'),
bb = this.get('boundingBox'),
bbPosition = bb.getStyle('position'),
bbLeftMargin = parseInt(bb.getStyle('marginLeft'), 10),
bbRightMargin = parseInt(bb.getStyle('marginRight'), 10),
bbTopMargin = parseInt(bb.getStyle('marginTop'), 10),
bbBottomMargin = parseInt(bb.getStyle('marginBottom'), 10),
bbLeftPadding = parseInt(bb.getStyle('paddingLeft'), 10),
bbRightPadding = parseInt(bb.getStyle('paddingRight'), 10),
bbTopPadding = parseInt(bb.getStyle('paddingTop'), 10),
bbBottomPadding = parseInt(bb.getStyle('paddingBottom'), 10),
parent = bb.get('parentNode'),
parentWidth = parseInt(parent.getComputedStyle('width'), 10),
parentHeight = parseInt(parent.getComputedStyle('height'), 10),
bbWidth = parentWidth - bbLeftPadding - bbRightPadding - bbLeftMargin - bbRightMargin,
bbHeight = parentHeight - bbTopPadding - bbBottomPadding - bbTopMargin - bbBottomMargin,
cbWidth = bbWidth,
pageWidth = bbWidth,
pageHeight = bbHeight,
columns = this.get('columns'),
rows = this.get('rows'),
imageConfig = this.get('imageConfig'),
horizontalSpacing = this.get('horizontalSpacing'),
verticalSpacing = this.get('verticalSpacing'),
spaceEvenly = this.get('spaceEvenly'),
centerSinglePage = this.get('centerSinglePage'),
leftNavWidth = 0,
rightNavWidth = 0,
topNavHeight = 0,
bottomNavHeight = 0,
colsPerPage = columns,
rowsPerPage = rows,
imagesPerPage = 0,
numPages = 1,
clipBoxMarginLeft = 0,
clipBoxTop = 0,
availHorizSpace = 0,
availVerticalSpace = 0;
// Position absolute causes some resizing bugs.
bb.setStyle('position', 'relative');
// Bounding box width
if(!isNaN(columns)) {
bbWidth = pageWidth = columns * (imageConfig.width + horizontalSpacing) + horizontalSpacing;
}
// Bounding box height
if(!isNaN(rows)) {
bbHeight = pageHeight = rows * (imageConfig.height + verticalSpacing) + verticalSpacing;
}
// Compensate for the navs
if(this._leftNav) {
leftNavWidth = parseInt(this._leftNav.get('boundingBox').getComputedStyle('width'), 10);
if(isNaN(columns)) {
pageWidth -= leftNavWidth;
}
else {
bbWidth += leftNavWidth;
}
}
if(this._rightNav) {
rightNavWidth = parseInt(this._rightNav.get('boundingBox').getComputedStyle('width'), 10);
if(isNaN(columns)) {
pageWidth -= rightNavWidth;
}
else {
bbWidth += rightNavWidth;
}
}
if(this._topNav) {
topNavHeight = parseInt(this._topNav.get('boundingBox').getComputedStyle('height'), 10);
if(isNaN(rows)) {
pageHeight -= topNavHeight;
}
else {
bbHeight += topNavHeight;
}
}
if(this._bottomNav) {
bottomNavHeight = parseInt(this._bottomNav.get('boundingBox').getComputedStyle('height'), 10);
if(isNaN(rows)) {
pageHeight -= bottomNavHeight;
}
else {
bbHeight += bottomNavHeight;
}
}
// Columns per page
if(isNaN(columns)) {
colsPerPage = Math.floor(pageWidth/(imageConfig.width + horizontalSpacing));
colsPerPage = colsPerPage < 1 ? 1 : colsPerPage;
}
// Rows per page
if(isNaN(rows)) {
rowsPerPage = Math.floor(pageHeight/(imageConfig.height + verticalSpacing));
rowsPerPage = rowsPerPage < 1 ? 1 : rowsPerPage;
}
// Images per page
imagesPerPage = colsPerPage * rowsPerPage;
// Number of pages
if(root.albumInfo) {
numPages = Math.ceil(root.albumInfo.images.length/imagesPerPage);
}
// Horizontal spacing
if(isNaN(columns) && spaceEvenly) {
horizontalSpacing = Math.floor((pageWidth - (imageConfig.width * colsPerPage))/(colsPerPage + 1));
}
// Vertical spacing
if(isNaN(rows) && spaceEvenly) {
verticalSpacing = Math.floor((pageHeight - (imageConfig.height * rowsPerPage))/(rowsPerPage + 1));
}
// Content container width
if(root.albumInfo && centerSinglePage && numPages == 1 && rowsPerPage == 1) {
cbWidth = root.albumInfo.images.length * imageConfig.width;
cbWidth += horizontalSpacing * (root.albumInfo.images.length + 1);
if(this._leftNav) {
cbWidth += leftNavWidth;
}
if(this._rightNav) {
cbWidth += rightNavWidth;
}
}
else {
cbWidth = bbWidth;
}
// Final page width and height
if(root.albumInfo && centerSinglePage && numPages == 1 && rowsPerPage == 1) {
pageWidth = root.albumInfo.images.length * imageConfig.width;
pageWidth += horizontalSpacing * root.albumInfo.images.length;
}
else {
pageWidth = colsPerPage * (imageConfig.width + horizontalSpacing);
}
pageHeight = rowsPerPage * (imageConfig.height + verticalSpacing);
// Clip box margin left
if(numPages < 2) {
clipBoxMarginLeft = leftNavWidth;
}
else {
availHorizSpace = bbWidth;
if(this._rightNav) {
availHorizSpace -= rightNavWidth;
}
if(this._leftNav) {
availHorizSpace -= leftNavWidth;
clipBoxMarginLeft = leftNavWidth + (availHorizSpace - pageWidth - horizontalSpacing)/2;
}
else {
clipBoxMarginLeft = (availHorizSpace - pageWidth - horizontalSpacing)/2;
}
}
// Clip box margin top
if(numPages > 1 && !spaceEvenly) {
availVerticalSpace = bbHeight;
if(this._topNav) {
availVerticalSpace -= topNavHeight;
}
if(this._bottomNav) {
availVerticalSpace -= bottomNavHeight;
}
clipBoxTop = (availVerticalSpace - (verticalSpacing + pageHeight))/2;
}
// Set the info
this._bbHeight = bbHeight;
this._bbWidth = bbWidth;
this._cbWidth = cbWidth;
this._clipBoxMarginLeft = clipBoxMarginLeft;
this._clipBoxTop = clipBoxTop;
this._colsPerPage = colsPerPage;
this._rowsPerPage = rowsPerPage;
this._imagesPerPage = imagesPerPage;
this._numPages = numPages;
this._pageHeight = pageHeight;
this._pageWidth = pageWidth;
this._leftNavWidth = leftNavWidth;
this._rightNavWidth = rightNavWidth;
this._horizontalSpacing = horizontalSpacing;
this._verticalSpacing = verticalSpacing;
this._activePageIndex = Math.floor(root.imageIndex/this._imagesPerPage);
// Set back to the initial position.
bb.setStyle('position', bbPosition);
}
}, {
/**
* Custom CSS class name for the widget.
*
* @property CSS_PREFIX
* @type String
* @protected
* @static
*/
CSS_PREFIX: 'fl-slideshow-thumbs',
/**
* Static property used to define the default attribute configuration of
* the Widget.
*
* @property ATTRS
* @type Object
* @protected
* @static
*/
ATTRS: {
/**
* The number of thumbnail columns. If set to auto, the number of
* columns will be calculated based on the width of the parent node.
*
* @attribute columns
* @type String or Number
* @default auto
*/
columns: {
value: 'auto'
},
/**
* The number of thumbnail rows. If set to auto, the number of
* rows will be calculated based on the height of the parent node.
*
* @attribute rows
* @type String or Number
* @default auto
*/
rows: {
value: 'auto'
},
/**
* The horizontal spacing between thumbs.
*
* @attribute horizontalSpacing
* @type Number
* @default 15
*/
horizontalSpacing: {
value: 15
},
/**
* The vertical spacing between thumbs.
*
* @attribute verticalSpacing
* @type Number
* @default 15
*/
verticalSpacing: {
value: 15
},
/**
* Whether to space the thumbs evenly within a page.
*
* @attribute spaceEvenly
* @type Boolean
* @default true
*/
spaceEvenly: {
value: true
},
/**
* Whether to center single pages of thumbs.
*
* @attribute centerSinglePage
* @type Boolean
* @default false
*/
centerSinglePage: {
value: true
},
/**
* Whether to pause the parent slideshow when a thumb is clicked.
*
* @attribute pauseOnClick
* @type Boolean
* @default false
*/
pauseOnClick: {
value: false
},
/**
* The type of transition to use between pages.
*
* @attribute transition
* @type String
* @default slideHorizontal
*/
transition: {
value: 'slideHorizontal'
},
/**
* The duration of the transition between pages.
*
* @attribute transitionDuration
* @type Number
* @default 0.8
*/
transitionDuration: {
value: 0.8
},
/**
* The type of transition easing to use between pages.
*
* @attribute transitionEasing
* @type String
* @default ease-out
*/
transitionEasing: {
value: 'ease-out'
},
/**
* The configuration object used to create new instances of
* FL.SlideshowImage. See the API docs for {@link FL.SlideshowImage}
* for a complete list of configuration attributes.
*
* @attribute imageConfig
* @type Object
* @default {}
*/
imageConfig: {
value: {
crop: true,
width: 50,
height: 50
}
},
/**
* Whether to use the top nav or not.
*
* @attribute topNavEnabled
* @type Boolean
* @default false
*/
topNavEnabled: {
value: false
},
/**
* An array of button names used to render the top nav buttons.
*
* @attribute topNavButtons
* @type Array
* @default prevPage, nextPage
*/
topNavButtons: {
value: ['prevPage', 'nextPage']
},
/**
* Whether to use the right nav or not.
*
* @attribute rightNavEnabled
* @type Boolean
* @default true
*/
rightNavEnabled: {
value: true
},
/**
* An array of button names used to render the right nav buttons.
*
* @attribute rightNavButtons
* @type Array
* @default nextPage
*/
rightNavButtons: {
value: ['nextPage']
},
/**
* Whether to use the bottom nav or not.
*
* @attribute bottomNavEnabled
* @type Boolean
* @default false
*/
bottomNavEnabled: {
value: false
},
/**
* An array of button names used to render the bottom nav buttons.
*
* @attribute bottomNavButtons
* @type Array
* @default prevPage, nextPage
*/
bottomNavButtons:{
value: ['prevPage', 'nextPage']
},
/**
* Whether to use the left nav or not.
*
* @attribute leftNavEnabled
* @type Boolean
* @default true
*/
leftNavEnabled: {
value: true
},
/**
* An array of button names used to render the left nav buttons.
*
* @attribute leftNavButtons
* @type Array
* @default prevPage
*/
leftNavButtons:{
value: ['prevPage']
},
/**
* Whether to use touch gestures, when available,
* to transition between pages or not.
*
* @attribute touchSupport
* @type Boolean
* @default false
*/
touchSupport: {
value: false
}
}
});
/**
* Provides functionality for transitions between slideshow components.
*
* @namespace FL
* @class SlideshowTransition
* @constructor
* @param config {Object} Configuration object
* @extends Base
*/
Y.namespace('FL').SlideshowTransition = Y.Base.create('fl-slideshow-transition', Y.Base, [], {
/**
* The transition function to use when run is called.
*
* @property _transitionFunction
* @type String
* @default _transitionFade
* @protected
*/
_transitionFunction: '_transitionFade',
/**
* The current transition type.
*
* @property _type
* @type String
* @default fade
* @protected
*/
_type: 'fade',
/**
* Parses the transition type and sets the _transitionFunction
* used when run is called.
*
* @method initializer
* @protected
*/
initializer: function()
{
var type = this.get('type'),
typeArray = [],
types = Y.FL.SlideshowTransition.TYPES,
slideshowImageTypes = Y.FL.SlideshowTransition.SLIDESHOW_IMAGE_TYPES,
isSlideshowImageTransition = Y.Array.indexOf(slideshowImageTypes, type) > -1,
isSlideshowImage = this._isSlideshowImage(),
itemIn = this.get('itemIn'),
itemOut = this.get('itemOut');
// Check for random transitions.
if(type.indexOf(',') > -1) {
typeArray = type.split(',');
typeArray.sort(function() { return 0.5 - Math.random(); });
type = typeArray[0];
}
// Make sure we can run this transition, otherwise set a fallback.
if(!isSlideshowImage && isSlideshowImageTransition) {
type = 'fade';
}
else if(isSlideshowImage) {
if((itemIn && itemIn.one('img') === null) || (itemOut && itemOut.one('img') === null)) {
type = 'none';
}
else if(isSlideshowImageTransition) {
if((Y.UA.gecko && Y.UA.gecko < 5) || Y.UA.opera > 0 || (Y.UA.ie > 0 && Y.UA.ie < 9)) {
type = 'fade';
}
}
}
// Set the transition function and type.
if(Y.FL.SlideshowTransition.TYPES[type]) {
this._transitionFunction = types[type];
this._type = type;
}
// Setup the items.
this._setupItems();
},
/**
* Fires the start event and calls the transition function.
*
* @method run
*/
run: function()
{
/**
* Fires when the transition starts.
*
* @event start
*/
this.fire('start');
this[this._transitionFunction].call(this);
},
/**
* Set initial styles for the items.
*
* @method _setupItems
* @protected
*/
_setupItems: function()
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut');
if(itemIn) {
itemIn.setStyle('zIndex', 2);
itemIn.setStyle('opacity', 1);
if(Y.FL.Utils.cssSupport('transform')) {
itemIn.setStyle('transform', 'translate(0, 0)');
}
else {
itemIn.setStyle('top', '0');
itemIn.setStyle('left', '0');
}
}
if(itemOut) {
itemOut.setStyle('zIndex', 1);
}
},
/**
* Checks if the transition is being run
* on an instance of FL.SlideshowImage or not.
*
* @method _isSlideshowImage
* @protected
*/
_isSlideshowImage: function()
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut');
if(itemIn && itemIn.hasClass('fl-slideshow-image')) {
return true;
}
else if(itemOut && itemOut.hasClass('fl-slideshow-image')) {
return true;
}
return false;
},
/**
* Starts the transtion using the provided property objects.
*
* @method _transitionStart
* @param propsIn {Object} The properties to animate in.
* @param propsOut {Object} The properties to animate out.
* @protected
*/
_transitionStart: function(propsIn, propsOut)
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut'),
itemInCallback = Y.bind(this._transitionComplete, this),
itemOutCallback = !itemIn ? itemInCallback : null,
duration = this.get('duration'),
easing = this.get('easing');
if(itemIn) {
propsIn.duration = propsIn.duration || duration;
propsIn.easing = propsIn.easing || easing;
itemIn.transition(propsIn);
}
if(itemOut) {
propsOut.duration = propsOut.duration || duration;
propsOut.easing = propsOut.easing || easing;
itemOut.transition(propsOut);
}
if(itemInCallback) {
Y.later(propsIn.duration * 1000 + 100, null, itemInCallback);
}
else if(itemOutCallback) {
Y.later(propsOut.duration * 1000 + 100, null, itemOutCallback);
}
},
/**
* Clean up method called when the transition completes.
*
* @method _transitionComplete
* @protected
*/
_transitionComplete: function()
{
this._set('itemIn', null);
this._set('itemOut', null);
/**
* Fires when the transition completes.
*
* @event complete
*/
this.fire('complete');
},
/**
* No transition.
*
* @method _transitionNone
* @protected
*/
_transitionNone: function()
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut');
if(itemIn) {
itemIn.setStyle('opacity', 1);
}
if(itemOut) {
itemOut.setStyle('opacity', 0);
}
this._transitionComplete();
},
/**
* Fade transition.
*
* @method _transitionFade
* @protected
*/
_transitionFade: function()
{
var itemIn = this.get('itemIn');
if(itemIn) {
itemIn.setStyle('opacity', 0);
}
this._transitionStart({ opacity: 1 },{ opacity: 0 });
},
/**
* Slide left transition.
*
* @method _transitionSlideLeft
* @protected
*/
_transitionSlideLeft: function()
{
if(Y.FL.Utils.cssSupport('transform')) {
this._cssTransitionSlide({
inStart: 'translate(100%, 0)',
inEnd: 'translate(0, 0)',
outStart: 'translate(0, 0)',
outEnd: 'translate(-100%, 0)'
});
}
else {
this._jsTransitionSlide('left');
}
},
/**
* Slide right transition.
*
* @method _transitionSlideRight
* @protected
*/
_transitionSlideRight: function()
{
if(Y.FL.Utils.cssSupport('transform')) {
this._cssTransitionSlide({
inStart: 'translate(-100%, 0)',
inEnd: 'translate(0, 0)',
outStart: 'translate(0, 0)',
outEnd: 'translate(100%, 0)'
});
}
else {
this._jsTransitionSlide('right');
}
},
/**
* Slide up transition.
*
* @method _transitionSlideUp
* @protected
*/
_transitionSlideUp: function()
{
if(Y.FL.Utils.cssSupport('transform')) {
this._cssTransitionSlide({
inStart: 'translate(0, 100%)',
inEnd: 'translate(0, 0)',
outStart: 'translate(0, 0)',
outEnd: 'translate(0, -100%)'
});
}
else {
this._jsTransitionSlide('up');
}
},
/**
* Slide down transition.
*
* @method _transitionSlideDown
* @protected
*/
_transitionSlideDown: function()
{
if(Y.FL.Utils.cssSupport('transform')) {
this._cssTransitionSlide({
inStart: 'translate(0, -100%)',
inEnd: 'translate(0, 0)',
outStart: 'translate(0, 0)',
outEnd: 'translate(0, 100%)'
});
}
else {
this._jsTransitionSlide('down');
}
},
/**
* JavaScript slide transition.
*
* @method _jsTransitionSlide
* @protected
*/
_jsTransitionSlide: function(direction)
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut'),
itemOutEnd = 0;
// Item Out
if(itemOut && direction == 'left') {
itemOutEnd = -parseInt(itemOut.getStyle('width'), 10);
}
if(itemOut && direction == 'right') {
itemOutEnd = parseInt(itemOut.getStyle('width'), 10);
}
if(itemOut && direction == 'up') {
itemOutEnd = -parseInt(itemOut.getStyle('height'), 10);
}
if(itemOut && direction == 'down') {
itemOutEnd = parseInt(itemOut.getStyle('height'), 10);
}
// Item In
if(itemIn) {
itemIn.setStyle('opacity', 1);
}
if(itemIn && direction == 'left') {
itemIn.setStyle('left', itemIn.getStyle('width'));
}
if(itemIn && direction == 'right') {
itemIn.setStyle('left', '-' + itemIn.getStyle('width'));
}
if(itemIn && direction == 'up') {
itemIn.setStyle('top', itemIn.getStyle('height'));
}
if(itemIn && direction == 'down') {
itemIn.setStyle('top', '-' + itemIn.getStyle('height'));
}
// Transition Start
if(direction == 'left' || direction == 'right') {
this._transitionStart({ left: 0 },{ left: itemOutEnd });
}
else {
this._transitionStart({ top: 0 },{ top: itemOutEnd });
}
},
/**
* CSS slide transition.
*
* @method _cssTransitionSlide
* @protected
*/
_cssTransitionSlide: function(props)
{
var itemIn = this.get('itemIn'),
itemOut = this.get('itemOut'),
transformProp = Y.UA.chrome < 36 ? 'transform' : '-webkit-transform',
inProps = {},
outProps = {};
inProps[transformProp] = props.inEnd;
outProps[transformProp] = props.outEnd;
if(itemIn) {
itemIn.setStyle('transition', '');
itemIn.setStyle('opacity', 1);
itemIn.setStyle(transformProp, props.inStart);
}
if(itemOut) {
itemOut.setStyle('transition', '');
itemOut.setStyle(transformProp, props.outStart);
}
this._transitionStart(inProps, outProps);
},
/**
* Bars and blinds transition.
*
* @method _transitionBars
* @protected
*/
_transitionBars: function()
{
// Hide the image until the slices have transitioned in.
this.get('itemIn').one('.fl-slideshow-image-img').setStyle('opacity', 0);
var numBars = this.get('bars'),
slices = this._renderSlices(1, numBars),
duration = this.get('duration'),
delay = 0,
increment = 100,
last = false,
i = 0,
clone = null,
props = {
duration: duration,
opacity: 1
};
// barsRandom
if(this._type == 'barsRandom') {
slices = this._randomizeSlices(slices);
}
// Transition the slices.
for( ; i < slices.length; i++) {
// Make a clone of our transition properties.
clone = Y.clone(props);
// blinds
if(this._type == 'blinds') {
clone.width = parseFloat(slices[i].getComputedStyle('width'), 10) + 'px';
slices[i].setStyle('width', '0px');
increment = 50;
}
// Run the transition.
last = i == slices.length - 1 ? true : false;
Y.later(delay, this, this._transitionSlice, [slices[i], clone, last]);
delay += increment;
}
this._transitionSlicesFadeLast(delay);
},
/**
* Boxes transition.
*
* @method _transitionBoxes
* @protected
*/
_transitionBoxes: function()
{
// Hide the image until the slices have transitioned in.
this.get('itemIn').one('.fl-slideshow-image-img').setStyle('opacity', 0);
var numCols = this.get('boxCols'),
numRows = this.get('boxRows'),
numSlices = numCols * numRows,
multi = this._type != 'boxesRandom',
slices = this._renderSlices(numRows, numCols, multi),
duration = this.get('duration'),
delay = 0,
increment = 150,
last = false,
i = 0,
row = 0,
col = 0,
startCol = -1,
clone = null,
props = {
duration: duration,
opacity: 1
};
// boxesRandom
if(!multi) {
slices = this._randomizeSlices(slices);
increment = 30;
for( ; i < slices.length; i++) {
clone = Y.clone(props);
last = i == slices.length - 1 ? true : false;
Y.later(delay, this, this._transitionSlice, [slices[i], clone, last]);
delay += increment;
}
}
// boxes
else {
while(i < numSlices) {
for(row = 0; row < numRows; row++) {
if(row === 0) {
startCol++;
col = startCol;
}
if(col > -1 && col < numCols) {
i++;
clone = Y.clone(props);
// boxesGrow
if(this._type == 'boxesGrow') {
clone.height = parseFloat(slices[row][col].getComputedStyle('height'), 10) + 'px';
clone.width = parseFloat(slices[row][col].getComputedStyle('width'), 10) + 'px';
slices[row][col].setStyle('height', '0px');
slices[row][col].setStyle('width', '0px');
increment = 50;
}
last = i == numSlices - 1 ? true : false;
Y.later(delay, this, this._transitionSlice, [slices[row][col], clone, last]);
}
col--;
}
delay += increment;
}
}
this._transitionSlicesFadeLast(delay);
},
/**
* Renders the divs for slice based transitions.
*
* @method _renderSlices
* @protected
*/
_renderSlices: function(numRows, numCols, multidimensional)
{
var itemIn = this.get('itemIn'),
itemHeight = parseFloat(itemIn.getComputedStyle('height'), 10),
itemWidth = parseFloat(itemIn.getComputedStyle('width'), 10),
img = itemIn.one('img'),
imgSrc = img.get('src'),
imgHeight = parseFloat(img.getComputedStyle('height'), 10),
imgWidth = parseFloat(img.getComputedStyle('width'), 10),
imgLeft = parseFloat(img.getComputedStyle('left'), 10),
imgTop = parseFloat(img.getComputedStyle('top'), 10),
col = 0,
row = 0,
sliceHeight = Math.round(itemHeight/numRows),
sliceWidth = Math.round(itemWidth/numCols),
slice = null,
sliceImg = null,
slices = [];
for( ; row < numRows; row++) {
if(typeof multidimensional !== 'undefined' && multidimensional) {
slices[row] = [];
}
for(col = 0; col < numCols; col++) {
slice = Y.Node.create('');
sliceImg = Y.Node.create('* While SlideshowBase can be instantiated, it is only meant to * be extended and does not display any images. * * @namespace FL * @class SlideshowBase * @constructor * @param config {Object} Configuration object * @extends Widget */ Y.namespace('FL').SlideshowBase = Y.Base.create('fl-slideshow-base', Y.Widget, [Y.WidgetParent], { /** * FL.SlideshowAlbumLoader instance used to load albums. * * @property _loader * @type FL.SlideshowAlbumLoader * @default null * @protected */ _albumLoader: null, /** * An array of albums loaded from the source attribute. * Each album is an array of objects containing image info. * * @property albums * @type Array * @default [] */ albums: [], /** * Info for the active album. * * @property albumInfo * @type Object * @default null */ albumInfo: null, /** * A number that represents the index of the active * album in the albums array. * * @property albumIndex * @type Number * @default null */ albumIndex: null, /** * Info for the active image. * * @property imageInfo * @type Object * @default null */ imageInfo: null, /** * A number that represents the index of the active * image in the albumInfo array. * * @property imageIndex * @type Number * @default null */ imageIndex: null, /** * A number that represents the index of the last * image that was loaded in the albumInfo array. * * @property lastImageIndex * @type Number * @default null */ lastImageIndex: null, /** * Timer for the delay before resizing if one is set. * * @property _resizeTimer * @type Object * @default null * @protected */ _resizeTimer: null, /** * Flag for whether the slideshow is currently playing or not. * * @property playing * @type Boolean * @default false * @protected */ _playing: false, /** * Timer for the break in between images when * the slideshow is playing. * * @property _playingTimer * @type Object * @default null * @protected */ _playingTimer: null, /** * If set, the slideshow will only auto start when * this event is fired. * * @property _playingTimerEvent * @type Object * @default null * @protected */ _playingTimerEvent: null, /** * An instance of FL.Spinner that is shown and hidden * using _showLoadingImage and _hideLoadingImage when * a loading activity occurs. * * @property _loadingImage * @type FL.Spinner * @default null * @protected */ _loadingImage: null, /** * An div node that wraps the loading image. * * @property _loadingImageWrap * @type Node * @default null * @protected */ _loadingImageWrap: null, /** * Whether the loading image is visible or not. * * @property _loadingImageVisible * @type Boolean * @default false * @protected */ _loadingImageVisible: false, /** * A timer to delay the display of the loading image. * * @property _loadingImageTimer * @type Object * @default null * @protected */ _loadingImageTimer: null, /** * The container to insert the loading image into. If * no container is set, the loading image will be inserted * into the widget's bounding box. * * @property _loadingImageContainer * @type Object * @default null * @protected */ _loadingImageContainer: null, /** * The intial height of the slideshow. Used to resize * back to the starting height when exiting stretchy. * * @property _initialHeight * @type Number * @default null * @protected */ _initialHeight: null, /** * The intial width of the slideshow. Used to resize * back to the starting width when exiting stretchy. * * @property _initialWidth * @type Number * @default null * @protected */ _initialWidth: null, /** * @method initializer * @protected */ initializer: function() { // Loader this._albumLoader = new Y.FL.SlideshowAlbumLoader({ randomize: this.get('randomize') }); }, /** * @method renderUI * @protected */ renderUI: function() { this._renderLoadingImage(); }, /** * @method bindUI * @protected */ bindUI: function() { // Album load complete this._albumLoader.on('complete', this._loadAlbumComplete, this); // Resize Events Y.one(window).on('fl-slideshow-base|resize', this._delayResize, this); Y.one(window).on('fl-slideshow-base|orientationchange', this._delayResize, this); // Key Events Y.Node.one('body').on('keydown', Y.bind(this._onKey, this)); }, /** * @method syncUI * @protected */ syncUI: function() { this.get('boundingBox').addClass('fl-slideshow-' + this.get('color')); this.resize(); if(this.get('loadOnRender')) { this.loadAlbum(this.get('defaultAlbum'), this.get('defaultImage')); } }, /** * Add album data to the source object. * * @method addAlbum * @protected */ addAlbum: function(data) { var source = this.get('source'), i = source.length; source[i] = data; source[i].index = i; this.set('source', source); }, /** * Loads an album from the source array with the provided albumIndex. * If no albumIndex is provided, the first album in the array will be loaded. * An image to load can also be specified using imageIndex. * * @method loadAlbum * @param albumIndex {Number} The album index to load from the source array. * @param imageIndex {Number} The image index to load from the album array. */ loadAlbum: function(albumIndex, imageIndex) { var source = this.get('source'), loadImageIndex = typeof imageIndex == 'undefined' ? 0 : imageIndex; // Reset internal image indexes. this.imageIndex = null; this.lastImageIndex = null; /** * Fires before a new album request is made. * * @event albumLoadStart */ this.fire('albumLoadStart'); // Load an image after the album. this.once('albumLoadComplete', Y.bind(this.loadImage, this, loadImageIndex)); // Load data passed from another slideshow instance. if(source[albumIndex] && source[albumIndex].type == 'album-data') { this.albums[albumIndex] = source[albumIndex].data; this._loadAlbumComplete({albumInfo: this.albums[albumIndex]}); } // Load the album from the albums array. else if(source[albumIndex] && this.albums[albumIndex]) { this._loadAlbumComplete({albumInfo: this.albums[albumIndex]}); } // Load the album using the album loader. else { this._albumLoader.load(source[albumIndex] || source[0]); } }, /** * Processes the loaded album and fires the albumLoadComplete event. * * @method _loadAlbumComplete * @param o {Object} The custom event object passed to this method. * @protected */ _loadAlbumComplete: function(o) { this.albums[o.albumInfo.index] = o.albumInfo; this.albumInfo = o.albumInfo; this.albumIndex = o.albumInfo.index; /** * Fires after a new album request is made. * * @event albumLoadComplete */ this.fire('albumLoadComplete'); // Auto Play if(this.get('autoPlay')) { this._playingTimerStart(); this.fire('played'); this._playing = true; } }, /** * Sets the active image index and fires the imageLoadComplete event. * * @method loadImage * @param index {Number} The image index to load. */ loadImage: function(index) { if(this._playing) { this._playingTimerStart(); } index = index < 0 ? this.albumInfo.images.length - 1 : index; index = index >= this.albumInfo.images.length ? 0 : index; this.lastImageIndex = this.imageIndex; this.imageIndex = index; this.imageInfo = this.albumInfo.images[index]; /** * Fires after a new image index is set. * * @event imageLoadComplete */ this.fire('imageLoadComplete', { 'imageInfo': this.imageInfo }); }, /** * Loads the previous image. * * @method prevImage */ prevImage: function() { if(this.get('pauseOnNextOrPrev')) { this.pause(); } this.loadImage(this.imageIndex - 1); /** * Fires when the previous image is loaded. * * @event prevImage */ this.fire('prevImage'); }, /** * Loads the next image. * * @method nextImage */ nextImage: function() { if(this.get('pauseOnNextOrPrev')) { this.pause(); } this.loadImage(this.imageIndex + 1); /** * Fires when the next image is loaded. * * @event nextImage */ this.fire('nextImage'); }, /** * Keyboard navigation for the next and prev images. * * @method _onKey * @protected */ _onKey: function(e) { switch(e.keyCode) { case 37: this.prevImage(); break; case 39: this.nextImage(); break; } }, /** * Resizes the slideshow using either the * stretchy or standard functions. * * @method resize */ resize: function() { var stretchy = this.get('stretchy'), stretchyType = this.get('stretchyType'), width = parseInt(Y.one('body').get('winWidth'), 10), threshold = this.get('responsiveThreshold'); // Stretchy resize to the window only if the parent width is greater // than the responsive threshold and stretchyType is set to window. if(width > threshold && stretchy && stretchyType == 'window') { this._stretchyWindowResize(); } // Ratio resize if the parent width is less than the responsive // threshold or if stretchyType is set to ratio. else if((width <= threshold) || (stretchy && stretchyType == 'ratio')) { this._stretchyRatioResize(); } // Do a standard resize based on the height and // width passed to the constructor function. else { this._standardResize(); } /** * Fires when the slideshow is resized. * * @event resize */ this.fire('resize'); }, /** * @method _standardResize * @protected */ _standardResize: function() { var stretchy = this.get('stretchy'), stretchyType = this.get('stretchyType'), bb = this.get('boundingBox'), parent = bb.get('parentNode'), parentHeight = parseInt(parent.getComputedStyle('height'), 10), parentWidth = parseInt(parent.getComputedStyle('width'), 10), height = this.get('height'), width = this.get('width'); // Window resize if we are in fullscreen. if(bb.hasClass('fl-fullscreen-active')) { this._stretchyWindowResize(); return; } // Resize to the width and height of the parent. else if(stretchy && stretchyType == 'contain') { bb.setStyle('height', parentHeight + 'px'); bb.setStyle('width', parentWidth + 'px'); } // Ratio resize if we don't have a height defined. else if(!Y.Lang.isNumber(height)) { this._stretchyRatioResize(); return; } // Resize to the defined width and height. else { bb.setStyle('height', height + 'px'); if(width) { bb.setStyle('width', width + 'px'); } else { bb.setStyle('width', parentWidth + 'px'); } } }, /** * Resizes to the height of the window, compensating * for any padding. * * @method _stretchyWindowResize * @protected */ _stretchyWindowResize: function() { var bb = this.get('boundingBox'), verticalSpace = this.get('stretchyVerticalSpace'), paddingTop = parseInt(bb.getStyle('paddingTop'), 10), paddingBottom = parseInt(bb.getStyle('paddingBottom'), 10), height = parseInt(Y.one('body').get('winHeight'), 10), width = ''; // Set the vertical space to 0 and width to the // window's width if we are in fullscreen mode. if(bb.hasClass('fl-fullscreen-active')) { verticalSpace = 0; width = parseInt(Y.one('body').get('winWidth'), 10) + 'px'; } height = (height - paddingTop - paddingBottom - verticalSpace) + 'px'; bb.setStyle('height', height); bb.setStyle('width', width); }, /** * Resizes the height by multiplying the width and stretchyRatio value. * * @method _stretchyRatioResize * @protected */ _stretchyRatioResize: function() { var bb = this.get('boundingBox'), parent = bb.get('parentNode'), verticalSpace = 0, stretchyRatio = this.get('stretchyRatio'), paddingTop = parseInt(bb.getStyle('paddingTop'), 10), paddingBottom = parseInt(bb.getStyle('paddingBottom'), 10), computedWidth = parseInt(parent.getComputedStyle('width'), 10), winHeight = parseInt(Y.one('body').get('winHeight'), 10), winWidth = parseInt(Y.one('body').get('winWidth'), 10), height = computedWidth * stretchyRatio, width = ''; // Use the window's height and width if we are in fullscreen mode. if(bb.hasClass('fl-fullscreen-active')) { height = winHeight; width = winWidth; } height = (height - paddingTop - paddingBottom - verticalSpace) + 'px'; bb.setStyle('height', height); bb.setStyle('width', width); }, /** * Resizes the slideshow after the resize timer completes. * * @method _delayResize * @protected */ _delayResize: function() { if(this._resizeTimer) { this._resizeTimer.cancel(); } this._resizeTimer = Y.later(300, this, this.resize); }, /** * Starts a new playing timer and fires the played event. * * @method play */ play: function() { this._playingTimer = Y.later(this.get('speed'), this, this._playingTimerComplete); /** * Fires when the playing timer starts. * * @event played */ this.fire('played'); this._playing = true; }, /** * Cancels the current playing timer and fires the paused event. * * @method pause */ pause: function() { this._playingTimerCancel(); /** * Fires when the playing timer is canceled. * * @event paused */ this.fire('paused'); this._playing = false; }, /** * A new playing timer will start when this event is fired. * * @method _setPlayingTimerEvent * @param obj {Object} The event's host object. * @param e {String} The event to fire on the host object. * @protected */ _setPlayingTimerEvent: function(obj, e) { this._playingTimerEvent = { 'obj': obj, 'e': e }; }, /** * Cancels the playing timer if it is running and starts a new one. * The next image is loaded when the timer completes. * * @method _playingTimerStart * @protected */ _playingTimerStart: function(e) { this._playingTimerCancel(); if(!e && this._playingTimerEvent !== null) { this._playingTimerEvent.obj.once('fl-slideshow-base|' + this._playingTimerEvent.e, Y.bind(this._playingTimerStart, this)); } else { this._playingTimer = Y.later(this.get('speed'), this, this._playingTimerComplete); } }, /** * Fires when the playing timer completes, starts a * new timer and loads the next image. * * @method _playingTimerComplete * @protected */ _playingTimerComplete: function() { this.loadImage(this.imageIndex + 1); /** * Fires when the playing timer completes. * * @event albumLoadStart */ this.fire('playingTimerComplete'); }, /** * Cancels the playing timer. * * @method _playingTimerCancel * @protected */ _playingTimerCancel: function() { if(this._playingTimer) { this._playingTimer.cancel(); } if(this._playingTimerEvent) { this._playingTimerEvent.obj.detach('fl-slideshow-base|' + this._playingTimerEvent.e); } }, /** * Creates the loading image. * * @method _renderLoadingImage * @protected */ _renderLoadingImage: function() { var defaults = { lines: 11, // The number of lines to draw length: 6, // The length of each line width: 2, // The line thickness radius: 7, // The radius of the inner circle color: '', // #rbg or #rrggbb speed: 1, // Rounds per second trail: 60, // Afterglow percentage shadow: false // Whether to render a shadow }, settings = Y.merge(defaults, this.get('loadingImageSettings')); if(this.get('loadingImageEnabled')) { // Loading image if(settings.color === '') { settings.color = this._colorToHex(Y.one('body').getStyle('color')); } this._loadingImage = new Y.FL.Spinner(settings); // Loading image wrap this._loadingImageWrap = Y.Node.create('
'); this._loadingImageWrap.setStyles({ position : 'absolute', 'z-index' : '1000' }); } }, /** * Inserts the loading image. * * @method _showLoadingImage * @protected */ _showLoadingImage: function() { if(this._loadingImage && !this._loadingImageVisible) { this._loadingImageVisible = true; this._loadingImage.spin(); this._loadingImageWrap.insert(this._loadingImage.el); if(this._loadingImageContainer !== null) { this._loadingImageContainer.insert(this._loadingImageWrap); } else { this.get('contentBox').insert(this._loadingImageWrap); } this._positionLoadingImage(); } }, /** * Inserts the loading image div node after * a timer completes. * * @method _showLoadingImageWithDelay * @protected */ _showLoadingImageWithDelay: function() { if(this._loadingImage) { this._loadingImageTimer = Y.later(1000, this, this._showLoadingImage); } }, /** * Removes the loading image div node. * * @method _hideLoadingImage * @protected */ _hideLoadingImage: function() { if(this._loadingImageTimer) { this._loadingImageTimer.cancel(); this._loadingImageTimer = null; } if(this._loadingImage && this._loadingImageVisible) { this._loadingImageVisible = false; this._loadingImage.stop(); this._loadingImageWrap.remove(); } }, /** * Centers the loading image in the content box. * * @method _positionLoadingImage * @protected */ _positionLoadingImage: function() { if(this._loadingImage && this._loadingImageVisible) { var wrap = this._loadingImageWrap, wrapHeight = parseInt(wrap.getComputedStyle('height'), 10), wrapWidth = parseInt(wrap.getComputedStyle('width'), 10), parent = wrap.get('parentNode'), parentHeight = parseInt(parent.getComputedStyle('height'), 10), parentWidth = parseInt(parent.getComputedStyle('width'), 10), left = (parentWidth - wrapWidth)/2, top = (parentHeight - wrapHeight)/2; wrap.setStyles({ left : left + 'px', top : top + 'px' }); Y.one(this._loadingImage.el).setStyles({ left : '50%', top : '50%' }); } }, /** * Convert RGB color value to a hex value. * * @method _colorToHex * @protected */ _colorToHex: function(color) { var digits, red, green, blue, rgb; if(color.substr(0, 1) === '#') { return color; } digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color); if ( null === digits ) { return '#000'; } red = parseInt(digits[2], 10); green = parseInt(digits[3], 10); blue = parseInt(digits[4], 10); rgb = blue | (green << 8) | (red << 16); rgb = rgb.toString(16); if(rgb === '0') { rgb = '000'; } return digits[1] + '#' + rgb; } }, { /** * Custom CSS class name for the widget. * * @property CSS_PREFIX * @type String * @protected * @static */ CSS_PREFIX: 'fl-slideshow-base', /** * Static property used to define the default attribute configuration of * the Widget. * * @property ATTRS * @type Object * @protected * @static */ ATTRS: { /** * Used to create the color class that gets added to the bounding box * when the widget is rendered. The color class is used to create new * CSS color themes. The default CSS provided includes dark and light themes. * * @attribute color * @type String * @default dark * @writeOnce */ color: { value: 'dark', writeOnce: true }, /** * An array of source objects used to load albums. Each object must have * a type property and can have a title property as well. *
* In addition to those properties, each object has additional required
* properties specific to its type. The types currently supported are
* smugmug and urls with planned support for flickr and picasa.
* See the user guide for information on loading different types.
*
* @attribute source
* @type Array
* @default []
* @writeOnce
*/
source: {
value: [],
setter: function(source) {
if(source.constructor == Object) {
source = [source];
}
for(var i = 0; i < source.length; i++) {
source[i].index = i;
}
return source;
}
},
/**
* The default album index to load.
*
* @attribute defaultAlbum
* @type Number
* @default 0
*/
defaultAlbum: {
value: 0
},
/**
* The default image index to load.
*
* @attribute defaultImage
* @type Number
* @default 0
*/
defaultImage: {
value: 0
},
/**
* If true, the slideshow will be loaded after rendering.
*
* @attribute loadOnRender
* @type Boolean
* @default true
*/
loadOnRender: {
value: true
},
/**
* If true, the slideshow will start playing after loading.
*
* @attribute autoPlay
* @type Boolean
* @default true
*/
autoPlay: {
value: true
},
/**
* Whether to pause when the next or previous image is loaded
* using nextImage or prevImage. The slideshow will not be paused
* if the next or previous image is loaded using loadImage as is the
* case when the slideshow is playing.
*
* @attribute pauseOnNextOrPrev
* @type Boolean
* @default true
*/
pauseOnNextOrPrev: {
value: true
},
/**
* If true, the images will be randomized after loading.
*
* @attribute randomize
* @type Boolean
* @default false
*/
randomize: {
value: false
},
/**
* The time between images when playing, measured in milliseconds.
*
* @attribute speed
* @type Number
* @default 4000
*/
speed: {
value: 4000
},
/**
* The minimum width of the parent node at which
* responsive features are enabled. Set to 0 to
* disable responsive features as they are enabled
* whether stretchy is set to true or not.
*
* @attribute responsiveThreshold
* @type Number
* @default 600
*/
responsiveThreshold: {
value: 600
},
/**
* Whether stretchy resizing should be enabled.
*
* @attribute stretchy
* @type Boolean
* @default false
*/
stretchy: {
value: false
},
/**
* The type of stretchy logic to use. Possible values are
* window and ratio. Both types resize the width of the
* slideshow to the width of its parent node. With window, the
* height of the slideshow is resized to the height of the window.
* With ratio, the height of the slideshow is resized based
* on the ratio set with stretchyRatio or the height of the window
* if the ratio height is greater than the window height.
*
* @attribute stretchyType
* @type String
* @default ratio
*/
stretchyType: {
value: 'ratio'
},
/**
* The number of pixels to subtract from the height of
* the slideshow when stretchy is set to true.
*
* @attribute stretchyVerticalSpace
* @type Number
* @default 0
*/
stretchyVerticalSpace: {
value: 0
},
/**
* Used to calculate the height of the slideshow when stretchyType
* is set to ratio.
*
* @attribute stretchyRatio
* @type Number
* @default 0.7
*/
stretchyRatio: {
value: 0.7
},
/**
* Whether to use the loading image or not.
*
* @attribute loadingImageEnabled
* @type Boolean
* @default true
*/
loadingImageEnabled: {
value: true
},
/**
* Property object for setting up the spin.js loading image.
* For a complete list of properties see:
* http://effinroot.eiremedia.netdna-cdn.com/repo/plugins/misc/spin.js/index.html
*
* @attribute loadingImageSettings
* @type Object
*/
loadingImageSettings: {
value: {}
}
}
});
}, '2.0.0' ,{requires:['node', 'base', 'widget', 'widget-parent', 'widget-child', 'fl-slideshow-album-loader', 'fl-spinner']});
YUI.add('fl-smugmug-api', function(Y) {
/**
* @module fl-smugmug-api
*/
/**
* SmugMug API wrapper.
*
* NOTE: Only anonymous logins are currently supported.
*
* @namespace FL
* @class SmugMugAPI
* @constructor
* @param config {Object} Configuration object
* @extends Base
*/
Y.namespace('FL').SmugMugAPI = Y.Base.create('fl-smugmug-api', Y.Base, [], {
/**
* ID for the current session.
*
* @property _sessionID
* @type String
* @default null
* @protected
*/
_sessionID: null,
/**
* URL with parameters for the next API request.
* Reset after each request.
*
* @property _requestURL
* @type String
* @default null
* @protected
*/
_requestURL: null,
/**
* Lifecycle method. Initializes the request url.
*
* @method initializer
* @protected
*/
initializer: function()
{
this._resetRequestURL();
},
/**
* Adds a key/value pair to the request url.
*
* @method addParam
* @param key {String} The name of the parameter (example: key=val).
* @param val {String} The value of the parameter (example: key=val).
*/
addParam: function(key, val)
{
this._requestURL = this._requestURL + '&' + key + '=' + val;
},
/**
* Requests an anonymous login session.
*
* @method loginAnon
*/
loginAnon: function()
{
this.addParam('method', 'smugmug.login.anonymously');
this.once('complete', this._loginAnonComplete);
this.request();
},
/**
* Anonymous login success handler.
*
* @method _loginAnonComplete
* @param data {Object} A jsonp data object.
* @protected
*/
_loginAnonComplete: function(data)
{
if(data.Login) {
this._sessionID = data.Login.Session.id;
}
},
/**
* Sends an API request using the request url.
*
* @method request
*/
request: function()
{
this.addParam('Callback', '{callback}');
Y.jsonp(this._requestURL, {
on: {
success: this._requestComplete,
timeout: function(){} // TODO: Handle timeouts
},
context: this,
timeout: 60000,
args: []
});
},
/**
* API request complete handler.
*
* @method _requestComplete
* @param data {Object} A jsonp data object.
* @protected
*/
_requestComplete: function(data)
{
this._resetRequestURL();
/**
* Fires when a request is complete.
*
* @event complete
*/
this.fire('complete', data);
},
/**
* Clears all parameters on the request url except
* the API key and session ID.
*
* @method _resetRequestURL
* @protected
*/
_resetRequestURL: function()
{
this._requestURL = this.get('apiURL') + '?APIKey=' + this.get('apiKey');
if(this._sessionID) {
this.addParam('SessionID', this._sessionID);
}
}
}, {
/**
* Static property used to define the default attribute configuration of
* the Widget.
*
* @property ATTRS
* @type Object
* @protected
* @static
*/
ATTRS: {
/**
* SmugMug API url to use for requests.
*
* @attribute apiUrl
* @type String
* @default https://api.smugmug.com/services/api/json/1.3.0/
*/
apiURL: {
value: 'https://api.smugmug.com/services/api/json/1.3.0/'
},
/**
* SmugMug API key.
*
* @attribute apiKey
* @type String
* @default 7w6kuU5Ee6KSgRRExf2KLgppdkez9JD2
*/
apiKey: {
value: '7w6kuU5Ee6KSgRRExf2KLgppdkez9JD2'
}
}
});
}, '2.0.0' ,{requires:['base', 'jsonp']});
YUI.add('fl-spinner', function(Y) {
(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