/** * Created by IntelliJ IDEA. * User: mcanas * Date: Oct 11, 2010 * Time: 1:38:22 PM * To change this template use File | Settings | File Templates. */ (function($){ // Define the public methods var methods = { /* The initializer method * Called automatically when no registered methods are passed to jSelect(); * */ init : function( options ) { return this.each(function() { var $this = $(this), props = $this.data('properties'); // If the object has not been initialized... if(!props) { // Set the default settings values var settings = { autoPlay: false, axis : 'horizontal', collapse : false, delay : 3, easing : 'swing', looping : false, playReversed : false, speed : 1 }; // Replace the default settings with the user settings if( options ) { $.extend(settings, options) } // Grab the DOM components var $screen = $this.hasClass('jShow_screen') ? $this : $('.jShow_screen', $this); // The screen is the viewable area of the jShow slider var $reel = $('.jShow_reel', $screen); // The reel is the object that will be animated if(settings.looping) { $reel.append($reel.html()); } var $frames = $.map($('.jShow_frame', $reel), function(obj) { return $(obj); }); // A js array of jquery objects containing each frame var nav = { // jShow navigational elements next : $('.jShow_nav_next', $this), // the next frame button play : $('.jShow_nav_play', $this), // the play button, which will be toggled to represent the pause button prev : $('.jShow_nav_prev', $this), // the previous frame button to : $.map($('.jShow_nav_to', $this), function(obj) { return $(obj); }) // the skip to frame buttons. The frame to skip to is determined by their order. }; // Collect all of the properties, settings and DOM components into one namespace $this.data('properties', { animating : false, axial : { dimension : ((settings.axis == 'vertical') ? 'outerHeight' : 'outerWidth'), orientation : ((settings.axis == 'vertical') ? ((settings.playReversed) ? 'bottom' : 'top') : ((settings.playReversed) ? 'right' : 'left')), position : ((settings.axis == 'vertical') ? 'top' : 'left') }, frames : $frames, frameCount : (settings.looping) ? ($frames.length/2) : $frames.length, index : { current: 0, selected: 0 }, interval : 0, nav : nav, paused : true, reel : $reel, screen : $screen, settings : settings, timer : 0 }); props = $this.data('properties'); var reelHeight = 0, reelWidth = 0; // Arrange the frames as necessary, based on the axis setting $.each($frames, function(i, $frame){ $frame.data('properties', { index : { relative : ((i >= $frames.length/2 && settings.looping) ? i - ($frames.length/2) : i), absolute : i } }); if(settings.axis == 'vertical') { reelHeight += $frame[props.axial.dimension]() + parseInt($frame.css('margin-top')) + parseInt($frame.css('margin-bottom')); } else { if(settings.axis == 'horizontal') { $frame.css('float', 'left'); } reelWidth += $frame[props.axial.dimension]() + parseInt($frame.css('margin-left')) + parseInt($frame.css('margin-right')); } }); // Get the reel width or height for the opposite dimension if(settings.axis == 'vertical') { reelWidth = $frames[0].outerWidth() + parseInt($frames[0].css('margin-left')) + parseInt($frames[0].css('margin-right')); } else { reelHeight = $frames[0].outerHeight() + parseInt($frames[0].css('margin-top')) + parseInt($frames[0].css('margin-bottom')); } // Resize the reel $reel.css({ height:reelHeight, width:reelWidth }); // Set a position attribute for each frame $.each($frames, function(i, $frame) { $frame.attr(props.axial.orientation, $frame.position()[props.axial.position]); }); // Rearrange frames if the playReversed setting is true if(settings.playReversed) { //$reel.empty(); $.each($frames, function() { $reel.prepend(this); }); $reel.css(props.axial.orientation, 0); } // Attach the event handler for the skip to frame buttons if(props.nav.to.length > 0) { $.each(props.nav.to, function(i, navTo) { navTo.bind('click', function(e) { e.preventDefault(); if(!props.paused) pause.call($this); skipToFrame.call($this, i, true, 'to'); }); }); } // Attach the event handler for the next frame button if(props.nav.next.length > 0) { $.each(props.nav.next, function(i, obj) { $(obj).bind('click', function(e) { e.preventDefault(); if(!props.paused) pause.call($this); nextFrame.call($this); }); }); } // Attach the event handler for the next frame button if(props.nav.prev.length > 0) { $.each(props.nav.prev, function(i, obj) { $(obj).bind('click', function(e) { e.preventDefault(); if(!props.paused) pause.call($this); prevFrame.call($this); }); }); } // Attach the event handler for the play/pause button if(props.nav.play.length > 0) { $.each(props.nav.play, function(i, obj) { $(obj).bind('click', function(e) { e.preventDefault(); if(props.paused) play.call($this); else pause.call($this); }); }); } if(settings.delay > 0 && settings.autoPlay) { play.call($this); } // If the show delay is greater than 0 and autoPlay is true, start the slide show else { props.nav.play.addClass('paused'); } // Otherwise set the play button to pause } }); }, next : function() { // Next frame public function nextFrame.call(this); }, pause : function() { // Pause slide show public function pause.call(this); }, play : function() { // Play slide show public function play.call(this); }, previous : function() { // Next frame public function prevFrame.call(this); }, skipTo : function(i, animate, direction) { // Skip to frame public function direction = (direction === undefined || direction === null ) ? 'to' : direction; animate = (animate === undefined || animate === null || animate !== false); skipToFrame.call(this, i, animate, direction); } }; // Next frame private function function nextFrame() { var props = this.data('properties'); var selectedIndex = (props.index.current == (props.frames.length-1)) ? 0 : props.index.current + 1; skipToFrame.call(this, selectedIndex, true, 'next'); } // Pause slide show private function function pause() { var props = this.data('properties'); window.clearInterval(props.interval); props.paused = true; props.nav.play.addClass('paused'); } // Play slide show private function function play() { var $this = this, props = $this.data('properties'); window.clearInterval(props.interval); props.interval = window.setInterval(function() { nextFrame.call($this); }, props.settings.delay * 1000); props.paused = false; props.nav.play.removeClass('paused'); } // Previous frame private function function prevFrame() { var props = this.data('properties'); var selectedIndex = (props.index.current == 0) ? props.frames.length - 1 : props.index.current - 1; skipToFrame.call(this, selectedIndex, true, 'previous'); } // Skip to frame private function, used for frame by frame movement function skipToFrame(selectedIndex, animate, direction) { // Grab the properties var $this = this, props = $this.data('properties'); // If the slider is animating, exit if(props.animating || props.index.current == selectedIndex) { return false; } // Stop the slide show if it is playing var wasPlaying = false; if(props.interval > 0) { wasPlaying = true; pause.call($this); } var frameMoveCount = 0, moveTo = 0; var reelPosition = parseInt(props.reel.css(props.axial.orientation)); var loopStartPos = 0; var loopEndPos = props.reel[props.axial.dimension]() - props.screen[props.axial.dimension](); var $currentFrame = props.frames[props.index.current]; var $selectedFrame = props.frames[selectedIndex]; // Handle movement and index scenarios related to the looping functionality if(props.settings.looping) { if(props.index.current >= (props.frameCount) && direction === 'to') { props.index.selected = selectedIndex + (props.frameCount); } else if(direction === 'next' && $selectedFrame.attr(props.axial.orientation) > loopEndPos)// (Math.abs(reelPosition) + $selectedFrame[props.axial.dimension]()) > loopEndPos) { // If you're on the last frame, handle the next event moveTo = props.frames[$currentFrame.data('properties').index.relative].attr(props.axial.orientation) * -1; props.reel.css(props.axial.orientation, moveTo); props.index.current = $currentFrame.data('properties').index.relative; props.index.selected = $selectedFrame.data('properties').index.relative; } else if(direction === 'previous' && $selectedFrame.attr(props.axial.orientation) < loopStartPos)// (Math.abs(reelPosition) - $selectedFrame[props.axial.dimension]() < loopStartPos)) { // If you're on the first frame, handle the previous event moveTo = props.frames[props.frameCount].attr(props.axial.orientation) * -1; props.reel.css(props.axial.orientation, moveTo); props.index.current = props.frames[props.frameCount].data('properties').index.absolute; props.index.selected = props.index.current-1; } else { props.index.selected = selectedIndex; } } else { props.index.selected = selectedIndex; } // Set the active Skip To nav element if(props.nav.to.length > 0) { props.nav.to[props.frames[props.index.current].data('properties').index.relative].removeClass('active'); props.nav.to[props.frames[props.index.selected].data('properties').index.relative].addClass('active'); } // Calculate the number of frames to move by frameMoveCount = props.index.selected - props.index.current; // If collapse is set, and the move count is greater than 1... if(props.settings.collapse && Math.abs(frameMoveCount) > 1) { // Collapse the intermediate frames $.each(props.frames, function(j, $frame) { if(frameMoveCount > 0 && j > props.index.current && j < props.index.selected) { $frame.addClass('collapsed'); } else if(frameMoveCount < 0 && j < props.index.current && j > props.index.selected) { props.reel.css(props.axial.position, (props.reel.position()[props.axial.position] + $frame[props.axial.dimension]())); $frame.addClass('collapsed'); } }); } // Calculate the final coordinate for the reel moveTo = props.frames[props.index.selected].attr(props.axial.orientation) * -1; // Set the jquery animation object settings based on the jShow axis value var anim = {}; anim[props.axial.orientation] = moveTo; if(animate) { // Set animating boolean props.animating = true; // Fire the animation start event $this.trigger('animationstart', props.frames[props.index.selected].data('properties').index.relative); // Start animating props.reel.animate(anim, props.settings.speed*1000, props.settings.easing, function() { // When the animation is finished, uncollapse the collapsed frames if(props.settings.collapse && Math.abs(frameMoveCount) > 1) { $('.collapsed', props.reel).each(function() { var $frame = $(this); $frame.removeClass('collapsed'); if(frameMoveCount > 0) { props.reel.css(props.axial.position, (props.reel.position()[props.axial.position] - $frame[props.axial.dimension]())); } }); } // We're not animating anymore props.animating = false; // Restart the slide show if it was in progress if(wasPlaying) { play.call($this); } // Fire the animation end event $this.trigger('animationend', props.frames[props.index.selected].data('properties').index.relative); }); } else { props.reel.css(anim); } // Set the current index to the selected index props.index.current = props.index.selected; return true; } $.fn.jShow = function( method ) { if(methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if(typeof method === 'object' || !method) { return methods.init.apply(this, arguments); } else { $.error('Method '+method+' does not exist on jQuery.jShow'); } }; })(jQuery);