/* -------------------------------------------------------------------- MaxImage 2.0 (Fullscreen Slideshow for use with jQuery Cycle Plugin) --------------------------------------------------------------------
Examples and documentation at: http://www.aaronvanderzwan.com/maximage/2.0/ Copyright (c) 2007-2012 Aaron Vanderzwan Dual licensed under the MIT and GPL licenses.
NOTES: This plugin is intended to simplify the creation of fullscreen background slideshows. It is intended to be used alongside the jQuery Cycle plugin: http://jquery.malsup.com/cycle/
If you simply need a fullscreen background image, please refer to the following document for ways to do this that are much more simple: http://css-tricks.com/perfect-full-page-background-image/
If you have any questions please contact Aaron Vanderzwan at http://www.aaronvanderzwan.com/blog/ Documentation at: http://blog.aaronvanderzwan.com/2012/07/maximage-2-0/
HISTORY: MaxImage 2.0 is a project first built as jQuery MaxImage Plugin (http://www.aaronvanderzwan.com/maximage/). Once CSS3 came along, the background-size:cover solved the problem MaxImage was intended to solve. However, fully customizable fullscreen slideshows is still fairly complex and I have not found any helpers for integrating with the jQuery Cycle Plugin. MaxCycle is intended to solve this problem.
TABLE OF CONTENTS: @Modern @setup @resize @preload @Old @setup @preload @onceloaded @maximage @windowresize @doneresizing @Cycle @setup @Adjust @center @fill @maxcover @maxcontain @Utils @browser_tests @construct_slide_object @sizes @modern_browser @debug
- /
/*!
* Maximage Version: 2.0.8 (16-Jan-2012) - http://www.aaronvanderzwan.com/maximage/2.0/ */
(function ($) { "use strict"; $.fn.maximage = function (settings, helperSettings) {
var config;
if (typeof settings == 'object' || settings === undefined) config = $.extend( $.fn.maximage.defaults, settings || {} ); if (typeof settings == 'string') config = $.fn.maximage.defaults;
/*jslint browser: true*/ $.Body = $('body'); $.Window = $(window); $.Scroll = $('html, body'); $.Events = { RESIZE: 'resize' };
this.each(function() { var $self = $(this), preload_count = 0, imageCache = [];
/* --------------------- */
// @Modern
/* MODERN BROWSER NOTES: Modern browsers have CSS3 background-size option so we setup the DOM to be the following structure for cycle plugin: div = cycle div = slide with background-size:cover div = slide with background-size:cover etc. */
var Modern = { setup: function(){ if($.Slides.length > 0){ var i, len = $.Slides.length;
// Setup images for(i=0; i < len; i++) { // Set our image var $img = $.Slides[i];
// Create a div with a background image so we can use CSS3's position cover (for modern browsers)
$self.append('}
// Begin our preload process (increments itself after load) Modern.preload(0);
// If using Cycle, this resets the height and width of each div to always fill the window; otherwise can be done with CSS Modern.resize(); } }, preload: function(n){ // Preload all of the images but never show them, just use their completion so we know that they are done // and so that the browser can cache them / fade them in smoothly
// Create new image object var $img = $('<img/>'); $img.load(function() { // Once the first image has completed loading, start the slideshow, etc. if(preload_count==0) { // Only start cycle after first image has loaded Cycle.setup();
// Run user defined onFirstImageLoaded() function config.onFirstImageLoaded(); }
// preload_count starts with 0, $.Slides.length starts with 1 if(preload_count==($.Slides.length-1)) { // If we have just loaded the final image, run the user defined function onImagesLoaded() config.onImagesLoaded( $self ); }else{ // Increment the counter preload_count++;
// Load the next image Modern.preload(preload_count); } });
// Set the src... this triggers begin of load $img[0].src = $.Slides[n].url;
// Push to external array to avoid cleanup by aggressive garbage collectors imageCache.push($img[0]); }, resize: function(){ // Cycle sets the height of each slide so when we resize our browser window this becomes a problem. // - the cycle option 'slideResize' has to be set to false otherwise it will trump our resize $.Window .bind($.Events.RESIZE, function(){ // Remove scrollbars so we can take propper measurements $.Scroll.addClass('mc-hide-scrolls');
// Set vars so we don't have to constantly check it $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Set container and slides height and width to match the window size $self .height($.Window.data('h')).width($.Window.data('w')) .children() .height($.Window.data('h')).width($.Window.data('w'));
// This is special noise for cycle (cycle has separate height and width for each slide) $self.children().each(function(){ this.cycleH = $.Window.data('h'); this.cycleW = $.Window.data('w'); });
// Put the scrollbars back to how they were $($.Scroll).removeClass('mc-hide-scrolls'); }); } }
/* --------------------- */
// @Old
/* OLD BROWSER NOTES: We setup the dom to be the following structure for cycle plugin on old browsers: div = cycle div = slide img = full screen size image div = slide img = full screen size image etc. */
var Old = { setup: function(){ var c, t, $div, j, slideLen = $.Slides.length;
// Clear container if($.BrowserTests.msie && !config.overrideMSIEStop){ // Stop IE from continually trying to preload images that we already removed document.execCommand("Stop", false); } $self.html();
$.Body.addClass('mc-old-browser');
if($.Slides.length > 0){ // Remove scrollbars so we can take propper measurements $.Scroll.addClass('mc-hide-scrolls');
// Cache our new dimensions $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Add our loading div to the DOM
$('body').append($("").attr("class", "mc-loader").css({'position':'absolute','left':'-9999px'}));// Loop through slides for(j = 0; j < slideLen; j++) { // Determine content (if container or image) if($.Slides[j].content.length == 0){ c = '<img src="' + $.Slides[j].url + '" />'; }else{ c = $.Slides[j].content; }
// Create Div
$div = $("// Add new container div to the DOM $self.append( $div );
// Account for slides without images if($('.mc-image-n' + j).children('img').length == 0){ }else{ // Add first image to loader to get that started $('div.mc-loader').append( $('.mc-image-n' + j).children('img').first().clone().addClass('not-loaded') ); } }
// Begin preloading Old.preload();
// Setup the resize function to listen for window changes Old.windowResize(); } }, preload: function(){ // Intervals to tell if an images have loaded var t = setInterval(function() { $('.mc-loader').children('img').each(function(i){ // Check if image is loaded var $img = $(this);
// Loop through not-loaded images if($img.hasClass('not-loaded')){ if( $img.height() > 0 ){ // Remove Dom notice $(this).removeClass('not-loaded');
// Set the dimensions var $img1 = $('div.mc-image-n' + i).children('img').first();
$img1 .data('h', $img.height()) .data('w', $img.width()) .data('ar', ($img.width() / $img.height()));
// Go on Old.onceLoaded(i) } } });
if( $('.not-loaded').length == 0){ // Remove our loader element because all of our images are now loaded $('.mc-loader').remove();
// Clear interval when all images are loaded clearInterval(t); } }, 1000); }, onceLoaded: function(m){ // Do maximage magic Old.maximage(m);
// Once the first image has completed loading, start the slideshow, etc. if(m == 0) { // If we changed the visibility before, make sure it is back on $self.css({'visibility':'visible'});
// Run user defined onFirstImageLoaded() function config.onFirstImageLoaded();
// After everything is done loading, clean up }else if(m == $.Slides.length - 1){ // Only start cycle after the first image has loaded Cycle.setup();
// Put the scrollbars back to how they were $($.Scroll).removeClass('mc-hide-scrolls');
// If we have just loaded the final image, run the user defined function onImagesLoaded() config.onImagesLoaded( $self );
if(config.debug) { debug(' - Final Maximage - ');debug($self); } } }, maximage: function(p){ // Cycle sets the height of each slide so when we resize our browser window this becomes a problem. // - the cycle option 'slideResize' has to be set to false otherwise it will trump our resize $('div.mc-image-n' + p) .height($.Window.data('h')) .width($.Window.data('w')) .children('img') .first() .each(function(){ Adjust.maxcover($(this)); }); }, windowResize: function(){ $.Window .bind($.Events.RESIZE, function(){ clearTimeout(this.id);
if($('.mc-image').length >= 1){ this.id = setTimeout(Old.doneResizing, 200); } }); }, doneResizing: function(){ // The final resize (on finish) // Remove scrollbars so we can take propper measurements $($.Scroll).addClass('mc-hide-scrolls');
// Cache our window's new dimensions $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Set the container's height and width $self.height($.Window.data('h')).width($.Window.data('w'))
// Set slide's height and width to match the window size $self.find('.mc-image').each(function(n){ Old.maximage(n); });
// Update cycle's ideas of what our slide's height and width should be var curr_opts = $self.data('cycle.opts'); if(curr_opts != undefined){ curr_opts.height = $.Window.data('h'); curr_opts.width = $.Window.data('w'); jQuery.each(curr_opts.elements, function(index, item) { item.cycleW = $.Window.data('w'); item.cycleH = $.Window.data('h'); }); }
// Put the scrollbars back to how they were $($.Scroll).removeClass('mc-hide-scrolls'); } }
/* --------------------- */
// @Cycle
var Cycle = { setup: function(){ var h,w;
$self.addClass('mc-cycle');
// Container sizes (if not set) $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Prefer CSS Transitions jQuery.easing.easeForCSSTransition = function(x, t, b, c, d, s) { return b+c; };
var cycleOptions = $.extend({ fit:1, containerResize:0, height:$.Window.data('h'), width:$.Window.data('w'), slideResize: false, easing: ($.BrowserTests.cssTransitions && config.cssTransitions ? 'easeForCSSTransition' : 'swing') }, config.cycleOptions);
$self.cycle( cycleOptions ); } }
/* --------------------- */
// @Adjust = Math to center and fill all elements
var Adjust = { center: function($item){ // Note: if alignment is 'left' or 'right' it can be controlled with CSS once verticalCenter // and horizontal center are set to false in the plugin options if(config.verticalCenter){ $item.css({marginTop:(($item.height() - $.Window.data('h'))/2) * -1}) } if(config.horizontalCenter){ $item.css({marginLeft:(($item.width() - $.Window.data('w'))/2) * -1}); } }, fill: function($item){ var $storageEl = $item.is('object') ? $item.parent().first() : $item;
if(typeof config.backgroundSize == 'function'){ // If someone wants to write their own fill() function, they can: example customBackgroundSize.html config.backgroundSize( $item ); }else if(config.backgroundSize == 'cover'){ if($.Window.data('w') / $.Window.data('h') < $storageEl.data('ar')){ $item .height($.Window.data('h')) .width(($.Window.data('h') * $storageEl.data('ar')).toFixed(0)); }else{ $item .height(($.Window.data('w') / $storageEl.data('ar')).toFixed(0)) .width($.Window.data('w')); } }else if(config.backgroundSize == 'contain'){ if($.Window.data('w') / $.Window.data('h') < $storageEl.data('ar')){ $item .height(($.Window.data('w') / $storageEl.data('ar')).toFixed(0)) .width($.Window.data('w')); }else{ $item .height($.Window.data('h')) .width(($.Window.data('h') * $storageEl.data('ar')).toFixed(0)); } }else{ debug('The backgroundSize option was not recognized for older browsers.'); } }, maxcover: function($item){ Adjust.fill($item); Adjust.center($item); }, maxcontain: function($item){ Adjust.fill($item); Adjust.center($item); } }
/* --------------------- */
// @Utils = General utilities for the plugin
var Utils = { browser_tests: function(){ var $div = $('<div />')[0], vendor = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'], p = 'transition', obj = { cssTransitions: false, cssBackgroundSize: ( "backgroundSize" in $div.style && config.cssBackgroundSize ), // Can override cssBackgroundSize in options html5Video: false, msie: false };
// Test for CSS Transitions if(config.cssTransitions){ if(typeof $div.style[p] == 'string') { obj.cssTransitions = true }
// Tests for vendor specific prop p = p.charAt(0).toUpperCase() + p.substr(1); for(var i=0; i<vendor.length; i++) { if(vendor[i] + p in $div.style) { obj.cssTransitions = true; } } }
// Check if we can play html5 videos if( !!document.createElement('video').canPlayType ) { obj.html5Video = true; }
// Check for MSIE since we lost $.browser in jQuery obj.msie = (Utils.msie() !== undefined);
if(config.debug) {
debug(' - Browser Test - ');debug(obj);
}
return obj; }, construct_slide_object: function(){ var obj = new Object(), arr = new Array(), temp = ;
$self.children().each(function(i){ var $img = $(this).is('img') ? $(this).clone() : $(this).find('img').first().clone();
// reset obj obj = {};
// set attributes to obj obj.url = $img.attr('src'); obj.title = $img.attr('title') != undefined ? $img.attr('title') : ; obj.alt = $img.attr('alt') != undefined ? $img.attr('alt') : ; obj.theclass = $img.attr('class') != undefined ? $img.attr('class') : ; obj.styles = $img.attr('style') != undefined ? $img.attr('style') : ; obj.orig = $img.clone(); obj.datahref = $img.attr('data-href') != undefined ? $img.attr('data-href') : ; obj.content = "";
// Setup content for within container if($(this).find('img').length > 0){ if($.BrowserTests.cssBackgroundSize){ $(this).find('img').first().remove(); } obj.content = $(this).html(); }
// Stop loading image so we can load them sequentiallyelse{ $img[0].src = "";
// Remove original object (only on nonIE. IE hangs if you remove an image during load) if($.BrowserTests.cssBackgroundSize){ $(this).remove(); }
// attach obj to arr arr.push(obj); });
if(config.debug) {
debug(' - Slide Object - ');debug(arr);
}
return arr;
},
msie: function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while ( div.innerHTML = , all[0] );
return v > 4 ? v : undef; }, sizes: function(){ var sizes = {h:0,w:0};
if(config.fillElement == "window"){ sizes.h = $.Window.height(); sizes.w = $.Window.width(); }else{ var $fillElement = $self.parents(config.fillElement).first();
// Height if($fillElement.height() == 0 || $fillElement.data('windowHeight') == true){ $fillElement.data('windowHeight',true); sizes.h = $.Window.height(); }else{ sizes.h = $fillElement.height(); }
// Width if($fillElement.width() == 0 || $fillElement.data('windowWidth') == true){ $fillElement.data('windowWidth',true); sizes.w = $.Window.width(); }else{ sizes.w = $fillElement.width(); } }
return sizes; } }
/* --------------------- */
// @Instantiation
// Helper Function // Run tests to see what our browser can handle $.BrowserTests = Utils.browser_tests();
if(typeof settings == 'string'){ // TODO: Resize object fallback for old browsers, If we are trying to size an HTML5 video and our browser doesn't support it if($.BrowserTests.html5Video || !$self.is('video')) { var to, $storageEl = $self.is('object') ? $self.parent().first() : $self; // Can't assign .data() to '<object>'
if( !$.Body.hasClass('mc-old-browser') ) $.Body.addClass('mc-old-browser');
// Cache our window's new dimensions $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Please include height and width attributes on your html elements $storageEl .data('h', $self.height()) .data('w', $self.width()) .data('ar', $self.width() / $self.height());
// We want to resize these elements with the window $.Window .bind($.Events.RESIZE, function(){ // Cache our window's new dimensions $.Window .data('h', Utils.sizes().h) .data('w', Utils.sizes().w);
// Limit resize runs to = $self.data('resizer'); clearTimeout(to); to = setTimeout( Adjust[settings]($self), 200 ); $self.data('resizer', to); });
// Initial run Adjust[settings]($self); } }else{ // Construct array of image objects for us to use $.Slides = Utils.construct_slide_object();
// If we are allowing background-size:cover run Modern if($.BrowserTests.cssBackgroundSize){ if(config.debug) debug(' - Using Modern - '); Modern.setup(); }else{ if(config.debug) debug(' - Using Old - '); Old.setup(); } } });
// private function for debugging function debug($obj) { if (window.console && window.console.log) { window.console.log($obj); } } }
// Default options $.fn.maximage.defaults = { debug: false, cssBackgroundSize: true, // Force run the functionality used for newer browsers cssTransitions: true, // Force run the functionality used for old browsers verticalCenter: true, // Only necessary for old browsers horizontalCenter: true, // Only necessary for old browsers scaleInterval: 20, // Only necessary for old browsers backgroundSize: 'cover', // Only necessary for old browsers (this can be function) fillElement: 'window', // Either 'window' or a CSS selector for a parent element overrideMSIEStop: false, // This gives the option to not 'stop' load for MSIE (stops coded background images from loading so we can preload)... // If setting this option to true, please beware of IE7/8 "Stack Overflow" error but if there are more than 13 slides // The description of the bug: http://blog.aaronvanderzwan.com/forums/topic/stack-overflow-in-ie-7-8/#post-33038 onFirstImageLoaded: function(){}, onImagesLoaded: function(){} } })(jQuery);