6920 lines
211 KiB
JavaScript
6920 lines
211 KiB
JavaScript
/**
|
|
* Galleria v1.4.7 2016-09-17
|
|
* http://galleria.io
|
|
*
|
|
* Copyright (c) 2010 - 2016 worse is better UG
|
|
* Licensed under the MIT license
|
|
* https://raw.github.com/worseisbetter/galleria/master/LICENSE
|
|
*
|
|
*/
|
|
|
|
(function( $, window, Galleria, undef ) {
|
|
|
|
/*global jQuery, navigator, Image, module, define */
|
|
|
|
// some references
|
|
var doc = window.document,
|
|
$doc = $( doc ),
|
|
$win = $( window ),
|
|
|
|
// native prototypes
|
|
protoArray = Array.prototype,
|
|
|
|
// internal constants
|
|
VERSION = 1.47,
|
|
DEBUG = true,
|
|
TIMEOUT = 30000,
|
|
DUMMY = false,
|
|
NAV = navigator.userAgent.toLowerCase(),
|
|
HASH = window.location.hash.replace(/#\//, ''),
|
|
PROT = window.location.protocol == "file:" ? "http:" : window.location.protocol,
|
|
M = Math,
|
|
F = function(){},
|
|
FALSE = function() { return false; },
|
|
IE = (function() {
|
|
|
|
var v = 3,
|
|
div = doc.createElement( 'div' ),
|
|
all = div.getElementsByTagName( 'i' );
|
|
|
|
do {
|
|
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
|
|
} while ( all[0] );
|
|
|
|
return v > 4 ? v : doc.documentMode || undef;
|
|
|
|
}() ),
|
|
DOM = function() {
|
|
return {
|
|
html: doc.documentElement,
|
|
body: doc.body,
|
|
head: doc.getElementsByTagName('head')[0],
|
|
title: doc.title
|
|
};
|
|
},
|
|
IFRAME = window.parent !== window.self,
|
|
|
|
// list of Galleria events
|
|
_eventlist = 'data ready thumbnail loadstart loadfinish image play pause progress ' +
|
|
'fullscreen_enter fullscreen_exit idle_enter idle_exit rescale ' +
|
|
'lightbox_open lightbox_close lightbox_image',
|
|
|
|
_events = (function() {
|
|
|
|
var evs = [];
|
|
|
|
$.each( _eventlist.split(' '), function( i, ev ) {
|
|
evs.push( ev );
|
|
|
|
// legacy events
|
|
if ( /_/.test( ev ) ) {
|
|
evs.push( ev.replace( /_/g, '' ) );
|
|
}
|
|
});
|
|
|
|
return evs;
|
|
|
|
}()),
|
|
|
|
// legacy options
|
|
// allows the old my_setting syntax and converts it to camel case
|
|
|
|
_legacyOptions = function( options ) {
|
|
|
|
var n;
|
|
|
|
if ( typeof options !== 'object' ) {
|
|
|
|
// return whatever it was...
|
|
return options;
|
|
}
|
|
|
|
$.each( options, function( key, value ) {
|
|
if ( /^[a-z]+_/.test( key ) ) {
|
|
n = '';
|
|
$.each( key.split('_'), function( i, k ) {
|
|
n += i > 0 ? k.substr( 0, 1 ).toUpperCase() + k.substr( 1 ) : k;
|
|
});
|
|
options[ n ] = value;
|
|
delete options[ key ];
|
|
}
|
|
});
|
|
|
|
return options;
|
|
},
|
|
|
|
_patchEvent = function( type ) {
|
|
|
|
// allow 'image' instead of Galleria.IMAGE
|
|
if ( $.inArray( type, _events ) > -1 ) {
|
|
return Galleria[ type.toUpperCase() ];
|
|
}
|
|
|
|
return type;
|
|
},
|
|
|
|
// video providers
|
|
_video = {
|
|
youtube: {
|
|
reg: /https?:\/\/(?:[a-zA_Z]{2,3}.)?(?:youtube\.com\/watch\?)((?:[\w\d\-\_\=]+&(?:amp;)?)*v(?:<[A-Z]+>)?=([0-9a-zA-Z\-\_]+))/i,
|
|
embed: function() {
|
|
return PROT + '//www.youtube.com/embed/' + this.id;
|
|
},
|
|
get_thumb: function( data ) {
|
|
return PROT + '//img.youtube.com/vi/'+this.id+'/default.jpg';
|
|
},
|
|
get_image: function( data ) {
|
|
return PROT + '//img.youtube.com/vi/'+this.id+'/hqdefault.jpg'; }
|
|
},
|
|
vimeo: {
|
|
reg: /https?:\/\/(?:www\.)?(vimeo\.com)\/(?:hd#)?([0-9]+)/i,
|
|
embed: function() {
|
|
return PROT + '//player.vimeo.com/video/' + this.id;
|
|
},
|
|
getUrl: function() {
|
|
return PROT + '//vimeo.com/api/v2/video/' + this.id + '.json?callback=?';
|
|
},
|
|
get_thumb: function( data ) {
|
|
return data[0].thumbnail_medium;
|
|
},
|
|
get_image: function( data ) {
|
|
return data[0].thumbnail_large;
|
|
}
|
|
},
|
|
dailymotion: {
|
|
reg: /https?:\/\/(?:www\.)?(dailymotion\.com)\/video\/([^_]+)/,
|
|
embed: function() {
|
|
return PROT + '//www.dailymotion.com/embed/video/' + this.id;
|
|
},
|
|
getUrl: function() {
|
|
return 'https://api.dailymotion.com/video/' + this.id + '?fields=thumbnail_240_url,thumbnail_720_url&callback=?';
|
|
},
|
|
get_thumb: function( data ) {
|
|
return data.thumbnail_240_url;
|
|
},
|
|
get_image: function( data ) {
|
|
return data.thumbnail_720_url;
|
|
}
|
|
},
|
|
_inst: []
|
|
},
|
|
Video = function( type, id ) {
|
|
|
|
for( var i=0; i<_video._inst.length; i++ ) {
|
|
if ( _video._inst[i].id === id && _video._inst[i].type == type ) {
|
|
return _video._inst[i];
|
|
}
|
|
}
|
|
|
|
this.type = type;
|
|
this.id = id;
|
|
this.readys = [];
|
|
|
|
_video._inst.push(this);
|
|
|
|
var self = this;
|
|
|
|
$.extend( this, _video[type] );
|
|
|
|
_videoThumbs = function(data) {
|
|
self.data = data;
|
|
$.each( self.readys, function( i, fn ) {
|
|
fn( self.data );
|
|
});
|
|
self.readys = [];
|
|
};
|
|
|
|
if ( this.hasOwnProperty('getUrl') ) {
|
|
$.getJSON( this.getUrl(), _videoThumbs);
|
|
} else {
|
|
window.setTimeout(_videoThumbs, 400);
|
|
}
|
|
|
|
this.getMedia = function( type, callback, fail ) {
|
|
fail = fail || F;
|
|
var self = this;
|
|
var success = function( data ) {
|
|
callback( self['get_'+type]( data ) );
|
|
};
|
|
try {
|
|
if ( self.data ) {
|
|
success( self.data );
|
|
} else {
|
|
self.readys.push( success );
|
|
}
|
|
} catch(e) {
|
|
fail();
|
|
}
|
|
};
|
|
},
|
|
|
|
// utility for testing the video URL and getting the video ID
|
|
_videoTest = function( url ) {
|
|
var match;
|
|
for ( var v in _video ) {
|
|
match = url && _video[v].reg && url.match( _video[v].reg );
|
|
if( match && match.length ) {
|
|
return {
|
|
id: match[2],
|
|
provider: v
|
|
};
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// native fullscreen handler
|
|
_nativeFullscreen = {
|
|
|
|
support: (function() {
|
|
var html = DOM().html;
|
|
return !IFRAME && ( html.requestFullscreen || html.msRequestFullscreen || html.mozRequestFullScreen || html.webkitRequestFullScreen );
|
|
}()),
|
|
|
|
callback: F,
|
|
|
|
enter: function( instance, callback, elem ) {
|
|
|
|
this.instance = instance;
|
|
|
|
this.callback = callback || F;
|
|
|
|
elem = elem || DOM().html;
|
|
if ( elem.requestFullscreen ) {
|
|
elem.requestFullscreen();
|
|
}
|
|
else if ( elem.msRequestFullscreen ) {
|
|
elem.msRequestFullscreen();
|
|
}
|
|
else if ( elem.mozRequestFullScreen ) {
|
|
elem.mozRequestFullScreen();
|
|
}
|
|
else if ( elem.webkitRequestFullScreen ) {
|
|
elem.webkitRequestFullScreen();
|
|
}
|
|
},
|
|
|
|
exit: function( callback ) {
|
|
|
|
this.callback = callback || F;
|
|
|
|
if ( doc.exitFullscreen ) {
|
|
doc.exitFullscreen();
|
|
}
|
|
else if ( doc.msExitFullscreen ) {
|
|
doc.msExitFullscreen();
|
|
}
|
|
else if ( doc.mozCancelFullScreen ) {
|
|
doc.mozCancelFullScreen();
|
|
}
|
|
else if ( doc.webkitCancelFullScreen ) {
|
|
doc.webkitCancelFullScreen();
|
|
}
|
|
},
|
|
|
|
instance: null,
|
|
|
|
listen: function() {
|
|
|
|
if ( !this.support ) {
|
|
return;
|
|
}
|
|
|
|
var handler = function() {
|
|
|
|
if ( !_nativeFullscreen.instance ) {
|
|
return;
|
|
}
|
|
var fs = _nativeFullscreen.instance._fullscreen;
|
|
|
|
if ( doc.fullscreen || doc.mozFullScreen || doc.webkitIsFullScreen || ( doc.msFullscreenElement && doc.msFullscreenElement !== null ) ) {
|
|
fs._enter( _nativeFullscreen.callback );
|
|
} else {
|
|
fs._exit( _nativeFullscreen.callback );
|
|
}
|
|
};
|
|
doc.addEventListener( 'fullscreenchange', handler, false );
|
|
doc.addEventListener( 'MSFullscreenChange', handler, false );
|
|
doc.addEventListener( 'mozfullscreenchange', handler, false );
|
|
doc.addEventListener( 'webkitfullscreenchange', handler, false );
|
|
}
|
|
},
|
|
|
|
// the internal gallery holder
|
|
_galleries = [],
|
|
|
|
// the internal instance holder
|
|
_instances = [],
|
|
|
|
// flag for errors
|
|
_hasError = false,
|
|
|
|
// canvas holder
|
|
_canvas = false,
|
|
|
|
// instance pool, holds the galleries until themeLoad is triggered
|
|
_pool = [],
|
|
|
|
// Run galleries from theme trigger
|
|
_loadedThemes = [],
|
|
_themeLoad = function( theme ) {
|
|
|
|
_loadedThemes.push(theme);
|
|
|
|
// run the instances we have in the pool
|
|
// and apply the last theme if not specified
|
|
$.each( _pool, function( i, instance ) {
|
|
if ( instance._options.theme == theme.name || (!instance._initialized && !instance._options.theme) ) {
|
|
instance.theme = theme;
|
|
instance._init.call( instance );
|
|
}
|
|
});
|
|
},
|
|
|
|
// the Utils singleton
|
|
Utils = (function() {
|
|
|
|
return {
|
|
|
|
// legacy support for clearTimer
|
|
clearTimer: function( id ) {
|
|
$.each( Galleria.get(), function() {
|
|
this.clearTimer( id );
|
|
});
|
|
},
|
|
|
|
// legacy support for addTimer
|
|
addTimer: function( id ) {
|
|
$.each( Galleria.get(), function() {
|
|
this.addTimer( id );
|
|
});
|
|
},
|
|
|
|
array : function( obj ) {
|
|
return protoArray.slice.call(obj, 0);
|
|
},
|
|
|
|
create : function( className, nodeName ) {
|
|
nodeName = nodeName || 'div';
|
|
var elem = doc.createElement( nodeName );
|
|
elem.className = className;
|
|
return elem;
|
|
},
|
|
|
|
removeFromArray : function( arr, elem ) {
|
|
$.each(arr, function(i, el) {
|
|
if ( el == elem ) {
|
|
arr.splice(i, 1);
|
|
return false;
|
|
}
|
|
});
|
|
return arr;
|
|
},
|
|
|
|
getScriptPath : function( src ) {
|
|
|
|
// the currently executing script is always the last
|
|
src = src || $('script:last').attr('src');
|
|
var slices = src.split('/');
|
|
|
|
if (slices.length == 1) {
|
|
return '';
|
|
}
|
|
|
|
slices.pop();
|
|
|
|
return slices.join('/') + '/';
|
|
},
|
|
|
|
// CSS3 transitions, added in 1.2.4
|
|
animate : (function() {
|
|
|
|
// detect transition
|
|
var transition = (function( style ) {
|
|
var props = 'transition WebkitTransition MozTransition OTransition'.split(' '),
|
|
i;
|
|
|
|
// disable css3 animations in opera until stable
|
|
if ( window.opera ) {
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; props[i]; i++ ) {
|
|
if ( typeof style[ props[ i ] ] !== 'undefined' ) {
|
|
return props[ i ];
|
|
}
|
|
}
|
|
return false;
|
|
}(( doc.body || doc.documentElement).style ));
|
|
|
|
// map transitionend event
|
|
var endEvent = {
|
|
MozTransition: 'transitionend',
|
|
OTransition: 'oTransitionEnd',
|
|
WebkitTransition: 'webkitTransitionEnd',
|
|
transition: 'transitionend'
|
|
}[ transition ];
|
|
|
|
// map bezier easing conversions
|
|
var easings = {
|
|
_default: [0.25, 0.1, 0.25, 1],
|
|
galleria: [0.645, 0.045, 0.355, 1],
|
|
galleriaIn: [0.55, 0.085, 0.68, 0.53],
|
|
galleriaOut: [0.25, 0.46, 0.45, 0.94],
|
|
ease: [0.25, 0, 0.25, 1],
|
|
linear: [0.25, 0.25, 0.75, 0.75],
|
|
'ease-in': [0.42, 0, 1, 1],
|
|
'ease-out': [0, 0, 0.58, 1],
|
|
'ease-in-out': [0.42, 0, 0.58, 1]
|
|
};
|
|
|
|
// function for setting transition css for all browsers
|
|
var setStyle = function( elem, value, suffix ) {
|
|
var css = {};
|
|
suffix = suffix || 'transition';
|
|
$.each( 'webkit moz ms o'.split(' '), function() {
|
|
css[ '-' + this + '-' + suffix ] = value;
|
|
});
|
|
elem.css( css );
|
|
};
|
|
|
|
// clear styles
|
|
var clearStyle = function( elem ) {
|
|
setStyle( elem, 'none', 'transition' );
|
|
if ( Galleria.WEBKIT && Galleria.TOUCH ) {
|
|
setStyle( elem, 'translate3d(0,0,0)', 'transform' );
|
|
if ( elem.data('revert') ) {
|
|
elem.css( elem.data('revert') );
|
|
elem.data('revert', null);
|
|
}
|
|
}
|
|
};
|
|
|
|
// various variables
|
|
var change, strings, easing, syntax, revert, form, css;
|
|
|
|
// the actual animation method
|
|
return function( elem, to, options ) {
|
|
|
|
// extend defaults
|
|
options = $.extend({
|
|
duration: 400,
|
|
complete: F,
|
|
stop: false
|
|
}, options);
|
|
|
|
// cache jQuery instance
|
|
elem = $( elem );
|
|
|
|
if ( !options.duration ) {
|
|
elem.css( to );
|
|
options.complete.call( elem[0] );
|
|
return;
|
|
}
|
|
|
|
// fallback to jQuery's animate if transition is not supported
|
|
if ( !transition ) {
|
|
elem.animate(to, options);
|
|
return;
|
|
}
|
|
|
|
// stop
|
|
if ( options.stop ) {
|
|
// clear the animation
|
|
elem.off( endEvent );
|
|
clearStyle( elem );
|
|
}
|
|
|
|
// see if there is a change
|
|
change = false;
|
|
$.each( to, function( key, val ) {
|
|
css = elem.css( key );
|
|
if ( Utils.parseValue( css ) != Utils.parseValue( val ) ) {
|
|
change = true;
|
|
}
|
|
// also add computed styles for FF
|
|
elem.css( key, css );
|
|
});
|
|
if ( !change ) {
|
|
window.setTimeout( function() {
|
|
options.complete.call( elem[0] );
|
|
}, options.duration );
|
|
return;
|
|
}
|
|
|
|
// the css strings to be applied
|
|
strings = [];
|
|
|
|
// the easing bezier
|
|
easing = options.easing in easings ? easings[ options.easing ] : easings._default;
|
|
|
|
// the syntax
|
|
syntax = ' ' + options.duration + 'ms' + ' cubic-bezier(' + easing.join(',') + ')';
|
|
|
|
// add a tiny timeout so that the browsers catches any css changes before animating
|
|
window.setTimeout( (function(elem, endEvent, to, syntax) {
|
|
return function() {
|
|
|
|
// attach the end event
|
|
elem.one(endEvent, (function( elem ) {
|
|
return function() {
|
|
|
|
// clear the animation
|
|
clearStyle(elem);
|
|
|
|
// run the complete method
|
|
options.complete.call(elem[0]);
|
|
};
|
|
}( elem )));
|
|
|
|
// do the webkit translate3d for better performance on iOS
|
|
if( Galleria.WEBKIT && Galleria.TOUCH ) {
|
|
|
|
revert = {};
|
|
form = [0,0,0];
|
|
|
|
$.each( ['left', 'top'], function(i, m) {
|
|
if ( m in to ) {
|
|
form[ i ] = ( Utils.parseValue( to[ m ] ) - Utils.parseValue(elem.css( m )) ) + 'px';
|
|
revert[ m ] = to[ m ];
|
|
delete to[ m ];
|
|
}
|
|
});
|
|
|
|
if ( form[0] || form[1]) {
|
|
|
|
elem.data('revert', revert);
|
|
|
|
strings.push('-webkit-transform' + syntax);
|
|
|
|
// 3d animate
|
|
setStyle( elem, 'translate3d(' + form.join(',') + ')', 'transform');
|
|
}
|
|
}
|
|
|
|
// push the animation props
|
|
$.each(to, function( p, val ) {
|
|
strings.push(p + syntax);
|
|
});
|
|
|
|
// set the animation styles
|
|
setStyle( elem, strings.join(',') );
|
|
|
|
// animate
|
|
elem.css( to );
|
|
|
|
};
|
|
}(elem, endEvent, to, syntax)), 2);
|
|
};
|
|
}()),
|
|
|
|
removeAlpha : function( elem ) {
|
|
if ( elem instanceof jQuery ) {
|
|
elem = elem[0];
|
|
}
|
|
if ( IE < 9 && elem ) {
|
|
|
|
var style = elem.style,
|
|
currentStyle = elem.currentStyle,
|
|
filter = currentStyle && currentStyle.filter || style.filter || "";
|
|
|
|
if ( /alpha/.test( filter ) ) {
|
|
style.filter = filter.replace( /alpha\([^)]*\)/i, '' );
|
|
}
|
|
}
|
|
},
|
|
|
|
forceStyles : function( elem, styles ) {
|
|
elem = $(elem);
|
|
if ( elem.attr( 'style' ) ) {
|
|
elem.data( 'styles', elem.attr( 'style' ) ).removeAttr( 'style' );
|
|
}
|
|
elem.css( styles );
|
|
},
|
|
|
|
revertStyles : function() {
|
|
$.each( Utils.array( arguments ), function( i, elem ) {
|
|
|
|
elem = $( elem );
|
|
elem.removeAttr( 'style' );
|
|
|
|
elem.attr('style',''); // "fixes" webkit bug
|
|
|
|
if ( elem.data( 'styles' ) ) {
|
|
elem.attr( 'style', elem.data('styles') ).data( 'styles', null );
|
|
}
|
|
});
|
|
},
|
|
|
|
moveOut : function( elem ) {
|
|
Utils.forceStyles( elem, {
|
|
position: 'absolute',
|
|
left: -10000
|
|
});
|
|
},
|
|
|
|
moveIn : function() {
|
|
Utils.revertStyles.apply( Utils, Utils.array( arguments ) );
|
|
},
|
|
|
|
hide : function( elem, speed, callback ) {
|
|
|
|
callback = callback || F;
|
|
|
|
var $elem = $(elem);
|
|
elem = $elem[0];
|
|
|
|
// save the value if not exist
|
|
if (! $elem.data('opacity') ) {
|
|
$elem.data('opacity', $elem.css('opacity') );
|
|
}
|
|
|
|
// always hide
|
|
var style = { opacity: 0 };
|
|
|
|
if (speed) {
|
|
|
|
var complete = IE < 9 && elem ? function() {
|
|
Utils.removeAlpha( elem );
|
|
elem.style.visibility = 'hidden';
|
|
callback.call( elem );
|
|
} : callback;
|
|
|
|
Utils.animate( elem, style, {
|
|
duration: speed,
|
|
complete: complete,
|
|
stop: true
|
|
});
|
|
} else {
|
|
if ( IE < 9 && elem ) {
|
|
Utils.removeAlpha( elem );
|
|
elem.style.visibility = 'hidden';
|
|
} else {
|
|
$elem.css( style );
|
|
}
|
|
}
|
|
},
|
|
|
|
show : function( elem, speed, callback ) {
|
|
|
|
callback = callback || F;
|
|
|
|
var $elem = $(elem);
|
|
elem = $elem[0];
|
|
|
|
// bring back saved opacity
|
|
var saved = parseFloat( $elem.data('opacity') ) || 1,
|
|
style = { opacity: saved };
|
|
|
|
// animate or toggle
|
|
if (speed) {
|
|
|
|
if ( IE < 9 ) {
|
|
$elem.css('opacity', 0);
|
|
elem.style.visibility = 'visible';
|
|
}
|
|
|
|
var complete = IE < 9 && elem ? function() {
|
|
if ( style.opacity == 1 ) {
|
|
Utils.removeAlpha( elem );
|
|
}
|
|
callback.call( elem );
|
|
} : callback;
|
|
|
|
Utils.animate( elem, style, {
|
|
duration: speed,
|
|
complete: complete,
|
|
stop: true
|
|
});
|
|
} else {
|
|
if ( IE < 9 && style.opacity == 1 && elem ) {
|
|
Utils.removeAlpha( elem );
|
|
elem.style.visibility = 'visible';
|
|
} else {
|
|
$elem.css( style );
|
|
}
|
|
}
|
|
},
|
|
|
|
wait : function(options) {
|
|
|
|
Galleria._waiters = Galleria._waiters || [];
|
|
|
|
options = $.extend({
|
|
until : FALSE,
|
|
success : F,
|
|
error : function() { Galleria.raise('Could not complete wait function.'); },
|
|
timeout: 3000
|
|
}, options);
|
|
|
|
var start = Utils.timestamp(),
|
|
elapsed,
|
|
now,
|
|
tid,
|
|
fn = function() {
|
|
now = Utils.timestamp();
|
|
elapsed = now - start;
|
|
Utils.removeFromArray( Galleria._waiters, tid );
|
|
if ( options.until( elapsed ) ) {
|
|
options.success();
|
|
return false;
|
|
}
|
|
if (typeof options.timeout == 'number' && now >= start + options.timeout) {
|
|
options.error();
|
|
return false;
|
|
}
|
|
Galleria._waiters.push( tid = window.setTimeout(fn, 10) );
|
|
};
|
|
Galleria._waiters.push( tid = window.setTimeout(fn, 10) );
|
|
},
|
|
|
|
toggleQuality : function( img, force ) {
|
|
|
|
if ( ( IE !== 7 && IE !== 8 ) || !img || img.nodeName.toUpperCase() != 'IMG' ) {
|
|
return;
|
|
}
|
|
|
|
if ( typeof force === 'undefined' ) {
|
|
force = img.style.msInterpolationMode === 'nearest-neighbor';
|
|
}
|
|
|
|
img.style.msInterpolationMode = force ? 'bicubic' : 'nearest-neighbor';
|
|
},
|
|
|
|
insertStyleTag : function( styles, id ) {
|
|
|
|
if ( id && $( '#'+id ).length ) {
|
|
return;
|
|
}
|
|
|
|
var style = doc.createElement( 'style' );
|
|
if ( id ) {
|
|
style.id = id;
|
|
}
|
|
|
|
DOM().head.appendChild( style );
|
|
|
|
if ( style.styleSheet ) { // IE
|
|
style.styleSheet.cssText = styles;
|
|
} else {
|
|
var cssText = doc.createTextNode( styles );
|
|
style.appendChild( cssText );
|
|
}
|
|
},
|
|
|
|
// a loadscript method that works for local scripts
|
|
loadScript: function( url, callback ) {
|
|
|
|
var done = false,
|
|
script = $('<scr'+'ipt>').attr({
|
|
src: url,
|
|
async: true
|
|
}).get(0);
|
|
|
|
// Attach handlers for all browsers
|
|
script.onload = script.onreadystatechange = function() {
|
|
if ( !done && (!this.readyState ||
|
|
this.readyState === 'loaded' || this.readyState === 'complete') ) {
|
|
|
|
done = true;
|
|
|
|
// Handle memory leak in IE
|
|
script.onload = script.onreadystatechange = null;
|
|
|
|
if (typeof callback === 'function') {
|
|
callback.call( this, this );
|
|
}
|
|
}
|
|
};
|
|
|
|
DOM().head.appendChild( script );
|
|
},
|
|
|
|
// parse anything into a number
|
|
parseValue: function( val ) {
|
|
if (typeof val === 'number') {
|
|
return val;
|
|
} else if (typeof val === 'string') {
|
|
var arr = val.match(/\-?\d|\./g);
|
|
return arr && arr.constructor === Array ? arr.join('')*1 : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
},
|
|
|
|
// timestamp abstraction
|
|
timestamp: function() {
|
|
return new Date().getTime();
|
|
},
|
|
|
|
loadCSS : function( href, id, callback ) {
|
|
|
|
var link,
|
|
length;
|
|
|
|
// look for manual css
|
|
$('link[rel=stylesheet]').each(function() {
|
|
if ( new RegExp( href ).test( this.href ) ) {
|
|
link = this;
|
|
return false;
|
|
}
|
|
});
|
|
|
|
if ( typeof id === 'function' ) {
|
|
callback = id;
|
|
id = undef;
|
|
}
|
|
|
|
callback = callback || F; // dirty
|
|
|
|
// if already present, return
|
|
if ( link ) {
|
|
callback.call( link, link );
|
|
return link;
|
|
}
|
|
|
|
// save the length of stylesheets to check against
|
|
length = doc.styleSheets.length;
|
|
|
|
// check for existing id
|
|
if( $( '#' + id ).length ) {
|
|
|
|
$( '#' + id ).attr( 'href', href );
|
|
length--;
|
|
|
|
} else {
|
|
link = $( '<link>' ).attr({
|
|
rel: 'stylesheet',
|
|
href: href,
|
|
id: id
|
|
}).get(0);
|
|
|
|
var styles = $('link[rel="stylesheet"], style');
|
|
if ( styles.length ) {
|
|
styles.get(0).parentNode.insertBefore( link, styles[0] );
|
|
} else {
|
|
DOM().head.appendChild( link );
|
|
}
|
|
|
|
if ( IE && length >= 31 ) {
|
|
Galleria.raise( 'You have reached the browser stylesheet limit (31)', true );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( typeof callback === 'function' ) {
|
|
|
|
// First check for dummy element (new in 1.2.8)
|
|
var $loader = $('<s>').attr( 'id', 'galleria-loader' ).hide().appendTo( DOM().body );
|
|
|
|
Utils.wait({
|
|
until: function() {
|
|
return $loader.height() > 0;
|
|
},
|
|
success: function() {
|
|
$loader.remove();
|
|
callback.call( link, link );
|
|
},
|
|
error: function() {
|
|
$loader.remove();
|
|
|
|
// If failed, tell the dev to download the latest theme
|
|
Galleria.raise( 'Theme CSS could not load after 20 sec. ' + ( Galleria.QUIRK ?
|
|
'Your browser is in Quirks Mode, please add a correct doctype.' :
|
|
'Please download the latest theme at http://galleria.io/customer/.' ), true );
|
|
},
|
|
timeout: 5000
|
|
});
|
|
}
|
|
return link;
|
|
}
|
|
};
|
|
}()),
|
|
|
|
// play icon
|
|
_playIcon = function( container ) {
|
|
|
|
var css = '.galleria-videoicon{width:60px;height:60px;position:absolute;top:50%;left:50%;z-index:1;' +
|
|
'margin:-30px 0 0 -30px;cursor:pointer;background:#000;background:rgba(0,0,0,.8);border-radius:3px;-webkit-transition:all 150ms}' +
|
|
'.galleria-videoicon i{width:0px;height:0px;border-style:solid;border-width:10px 0 10px 16px;display:block;' +
|
|
'border-color:transparent transparent transparent #ffffff;margin:20px 0 0 22px}.galleria-image:hover .galleria-videoicon{background:#000}';
|
|
|
|
Utils.insertStyleTag( css, 'galleria-videoicon' );
|
|
|
|
return $( Utils.create( 'galleria-videoicon' ) ).html( '<i></i>' ).appendTo( container )
|
|
.click( function() { $( this ).siblings( 'img' ).mouseup(); });
|
|
},
|
|
|
|
// the transitions holder
|
|
_transitions = (function() {
|
|
|
|
var _slide = function(params, complete, fade, door) {
|
|
|
|
var easing = this.getOptions('easing'),
|
|
distance = this.getStageWidth(),
|
|
from = { left: distance * ( params.rewind ? -1 : 1 ) },
|
|
to = { left: 0 };
|
|
|
|
if ( fade ) {
|
|
from.opacity = 0;
|
|
to.opacity = 1;
|
|
} else {
|
|
from.opacity = 1;
|
|
}
|
|
|
|
$(params.next).css(from);
|
|
|
|
Utils.animate(params.next, to, {
|
|
duration: params.speed,
|
|
complete: (function( elems ) {
|
|
return function() {
|
|
complete();
|
|
elems.css({
|
|
left: 0
|
|
});
|
|
};
|
|
}( $( params.next ).add( params.prev ) )),
|
|
queue: false,
|
|
easing: easing
|
|
});
|
|
|
|
if (door) {
|
|
params.rewind = !params.rewind;
|
|
}
|
|
|
|
if (params.prev) {
|
|
|
|
from = { left: 0 };
|
|
to = { left: distance * ( params.rewind ? 1 : -1 ) };
|
|
|
|
if ( fade ) {
|
|
from.opacity = 1;
|
|
to.opacity = 0;
|
|
}
|
|
|
|
$(params.prev).css(from);
|
|
Utils.animate(params.prev, to, {
|
|
duration: params.speed,
|
|
queue: false,
|
|
easing: easing,
|
|
complete: function() {
|
|
$(this).css('opacity', 0);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
return {
|
|
|
|
active: false,
|
|
|
|
init: function( effect, params, complete ) {
|
|
if ( _transitions.effects.hasOwnProperty( effect ) ) {
|
|
_transitions.effects[ effect ].call( this, params, complete );
|
|
}
|
|
},
|
|
|
|
effects: {
|
|
|
|
fade: function(params, complete) {
|
|
$(params.next).css({
|
|
opacity: 0,
|
|
left: 0
|
|
});
|
|
Utils.animate(params.next, {
|
|
opacity: 1
|
|
},{
|
|
duration: params.speed,
|
|
complete: complete
|
|
});
|
|
if (params.prev) {
|
|
$(params.prev).css('opacity',1).show();
|
|
Utils.animate(params.prev, {
|
|
opacity: 0
|
|
},{
|
|
duration: params.speed
|
|
});
|
|
}
|
|
},
|
|
|
|
flash: function(params, complete) {
|
|
$(params.next).css({
|
|
opacity: 0,
|
|
left: 0
|
|
});
|
|
if (params.prev) {
|
|
Utils.animate( params.prev, {
|
|
opacity: 0
|
|
},{
|
|
duration: params.speed/2,
|
|
complete: function() {
|
|
Utils.animate( params.next, {
|
|
opacity:1
|
|
},{
|
|
duration: params.speed,
|
|
complete: complete
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
Utils.animate( params.next, {
|
|
opacity: 1
|
|
},{
|
|
duration: params.speed,
|
|
complete: complete
|
|
});
|
|
}
|
|
},
|
|
|
|
pulse: function(params, complete) {
|
|
if (params.prev) {
|
|
$(params.prev).hide();
|
|
}
|
|
$(params.next).css({
|
|
opacity: 0,
|
|
left: 0
|
|
}).show();
|
|
Utils.animate(params.next, {
|
|
opacity:1
|
|
},{
|
|
duration: params.speed,
|
|
complete: complete
|
|
});
|
|
},
|
|
|
|
slide: function(params, complete) {
|
|
_slide.apply( this, Utils.array( arguments ) );
|
|
},
|
|
|
|
fadeslide: function(params, complete) {
|
|
_slide.apply( this, Utils.array( arguments ).concat( [true] ) );
|
|
},
|
|
|
|
doorslide: function(params, complete) {
|
|
_slide.apply( this, Utils.array( arguments ).concat( [false, true] ) );
|
|
}
|
|
}
|
|
};
|
|
}());
|
|
|
|
// listen to fullscreen
|
|
_nativeFullscreen.listen();
|
|
|
|
// create special click:fast event for fast touch interaction
|
|
$.event.special['click:fast'] = {
|
|
propagate: true,
|
|
add: function(handleObj) {
|
|
|
|
var getCoords = function(e) {
|
|
if ( e.touches && e.touches.length ) {
|
|
var touch = e.touches[0];
|
|
return {
|
|
x: touch.pageX,
|
|
y: touch.pageY
|
|
};
|
|
}
|
|
};
|
|
|
|
var def = {
|
|
touched: false,
|
|
touchdown: false,
|
|
coords: { x:0, y:0 },
|
|
evObj: {}
|
|
};
|
|
|
|
$(this).data({
|
|
clickstate: def,
|
|
timer: 0
|
|
}).on('touchstart.fast', function(e) {
|
|
window.clearTimeout($(this).data('timer'));
|
|
$(this).data('clickstate', {
|
|
touched: true,
|
|
touchdown: true,
|
|
coords: getCoords(e.originalEvent),
|
|
evObj: e
|
|
});
|
|
}).on('touchmove.fast', function(e) {
|
|
var coords = getCoords(e.originalEvent),
|
|
state = $(this).data('clickstate'),
|
|
distance = Math.max(
|
|
Math.abs(state.coords.x - coords.x),
|
|
Math.abs(state.coords.y - coords.y)
|
|
);
|
|
if ( distance > 6 ) {
|
|
$(this).data('clickstate', $.extend(state, {
|
|
touchdown: false
|
|
}));
|
|
}
|
|
}).on('touchend.fast', function(e) {
|
|
var $this = $(this),
|
|
state = $this.data('clickstate');
|
|
if(state.touchdown) {
|
|
handleObj.handler.call(this, e);
|
|
}
|
|
$this.data('timer', window.setTimeout(function() {
|
|
$this.data('clickstate', def);
|
|
}, 400));
|
|
}).on('click.fast', function(e) {
|
|
var state = $(this).data('clickstate');
|
|
if ( state.touched ) {
|
|
return false;
|
|
}
|
|
$(this).data('clickstate', def);
|
|
handleObj.handler.call(this, e);
|
|
});
|
|
},
|
|
remove: function() {
|
|
$(this).off('touchstart.fast touchmove.fast touchend.fast click.fast');
|
|
}
|
|
};
|
|
|
|
// trigger resize on orientationchange (IOS7)
|
|
$win.on( 'orientationchange', function() {
|
|
$(this).resize();
|
|
});
|
|
|
|
/**
|
|
The main Galleria class
|
|
|
|
@class
|
|
@constructor
|
|
|
|
@example var gallery = new Galleria();
|
|
|
|
@author http://wib.io
|
|
|
|
@requires jQuery
|
|
|
|
*/
|
|
|
|
Galleria = function() {
|
|
|
|
var self = this;
|
|
|
|
// internal options
|
|
this._options = {};
|
|
|
|
// flag for controlling play/pause
|
|
this._playing = false;
|
|
|
|
// internal interval for slideshow
|
|
this._playtime = 5000;
|
|
|
|
// internal variable for the currently active image
|
|
this._active = null;
|
|
|
|
// the internal queue, arrayified
|
|
this._queue = { length: 0 };
|
|
|
|
// the internal data array
|
|
this._data = [];
|
|
|
|
// the internal dom collection
|
|
this._dom = {};
|
|
|
|
// the internal thumbnails array
|
|
this._thumbnails = [];
|
|
|
|
// the internal layers array
|
|
this._layers = [];
|
|
|
|
// internal init flag
|
|
this._initialized = false;
|
|
|
|
// internal firstrun flag
|
|
this._firstrun = false;
|
|
|
|
// global stagewidth/height
|
|
this._stageWidth = 0;
|
|
this._stageHeight = 0;
|
|
|
|
// target holder
|
|
this._target = undef;
|
|
|
|
// bind hashes
|
|
this._binds = [];
|
|
|
|
// instance id
|
|
this._id = parseInt(M.random()*10000, 10);
|
|
|
|
// add some elements
|
|
var divs = 'container stage images image-nav image-nav-left image-nav-right ' +
|
|
'info info-text info-title info-description ' +
|
|
'thumbnails thumbnails-list thumbnails-container thumb-nav-left thumb-nav-right ' +
|
|
'loader counter tooltip',
|
|
spans = 'current total';
|
|
|
|
$.each( divs.split(' '), function( i, elemId ) {
|
|
self._dom[ elemId ] = Utils.create( 'galleria-' + elemId );
|
|
});
|
|
|
|
$.each( spans.split(' '), function( i, elemId ) {
|
|
self._dom[ elemId ] = Utils.create( 'galleria-' + elemId, 'span' );
|
|
});
|
|
|
|
// the internal keyboard object
|
|
// keeps reference of the keybinds and provides helper methods for binding keys
|
|
var keyboard = this._keyboard = {
|
|
|
|
keys : {
|
|
'UP': 38,
|
|
'DOWN': 40,
|
|
'LEFT': 37,
|
|
'RIGHT': 39,
|
|
'RETURN': 13,
|
|
'ESCAPE': 27,
|
|
'BACKSPACE': 8,
|
|
'SPACE': 32
|
|
},
|
|
|
|
map : {},
|
|
|
|
bound: false,
|
|
|
|
press: function(e) {
|
|
var key = e.keyCode || e.which;
|
|
if ( key in keyboard.map && typeof keyboard.map[key] === 'function' ) {
|
|
keyboard.map[key].call(self, e);
|
|
}
|
|
},
|
|
|
|
attach: function(map) {
|
|
|
|
var key, up;
|
|
|
|
for( key in map ) {
|
|
if ( map.hasOwnProperty( key ) ) {
|
|
up = key.toUpperCase();
|
|
if ( up in keyboard.keys ) {
|
|
keyboard.map[ keyboard.keys[up] ] = map[key];
|
|
} else {
|
|
keyboard.map[ up ] = map[key];
|
|
}
|
|
}
|
|
}
|
|
if ( !keyboard.bound ) {
|
|
keyboard.bound = true;
|
|
$doc.on('keydown', keyboard.press);
|
|
}
|
|
},
|
|
|
|
detach: function() {
|
|
keyboard.bound = false;
|
|
keyboard.map = {};
|
|
$doc.off('keydown', keyboard.press);
|
|
}
|
|
};
|
|
|
|
// internal controls for keeping track of active / inactive images
|
|
var controls = this._controls = {
|
|
|
|
0: undef,
|
|
|
|
1: undef,
|
|
|
|
active : 0,
|
|
|
|
swap : function() {
|
|
controls.active = controls.active ? 0 : 1;
|
|
},
|
|
|
|
getActive : function() {
|
|
return self._options.swipe ? controls.slides[ self._active ] : controls[ controls.active ];
|
|
},
|
|
|
|
getNext : function() {
|
|
return self._options.swipe ? controls.slides[ self.getNext( self._active ) ] : controls[ 1 - controls.active ];
|
|
},
|
|
|
|
slides : [],
|
|
|
|
frames: [],
|
|
|
|
layers: []
|
|
};
|
|
|
|
// internal carousel object
|
|
var carousel = this._carousel = {
|
|
|
|
// shortcuts
|
|
next: self.$('thumb-nav-right'),
|
|
prev: self.$('thumb-nav-left'),
|
|
|
|
// cache the width
|
|
width: 0,
|
|
|
|
// track the current position
|
|
current: 0,
|
|
|
|
// cache max value
|
|
max: 0,
|
|
|
|
// save all hooks for each width in an array
|
|
hooks: [],
|
|
|
|
// update the carousel
|
|
// you can run this method anytime, f.ex on window.resize
|
|
update: function() {
|
|
var w = 0,
|
|
h = 0,
|
|
hooks = [0];
|
|
|
|
$.each( self._thumbnails, function( i, thumb ) {
|
|
if ( thumb.ready ) {
|
|
w += thumb.outerWidth || $( thumb.container ).outerWidth( true );
|
|
// Due to a bug in jquery, outerwidth() returns the floor of the actual outerwidth,
|
|
// if the browser is zoom to a value other than 100%. height() returns the floating point value.
|
|
var containerWidth = $( thumb.container).width();
|
|
w += containerWidth - M.floor(containerWidth);
|
|
|
|
hooks[ i+1 ] = w;
|
|
h = M.max( h, thumb.outerHeight || $( thumb.container).outerHeight( true ) );
|
|
}
|
|
});
|
|
|
|
self.$( 'thumbnails' ).css({
|
|
width: w,
|
|
height: h
|
|
});
|
|
|
|
carousel.max = w;
|
|
carousel.hooks = hooks;
|
|
carousel.width = self.$( 'thumbnails-list' ).width();
|
|
carousel.setClasses();
|
|
|
|
self.$( 'thumbnails-container' ).toggleClass( 'galleria-carousel', w > carousel.width );
|
|
|
|
// one extra calculation
|
|
carousel.width = self.$( 'thumbnails-list' ).width();
|
|
|
|
// todo: fix so the carousel moves to the left
|
|
},
|
|
|
|
bindControls: function() {
|
|
|
|
var i;
|
|
|
|
carousel.next.on( 'click:fast', function(e) {
|
|
e.preventDefault();
|
|
|
|
if ( self._options.carouselSteps === 'auto' ) {
|
|
|
|
for ( i = carousel.current; i < carousel.hooks.length; i++ ) {
|
|
if ( carousel.hooks[i] - carousel.hooks[ carousel.current ] > carousel.width ) {
|
|
carousel.set(i - 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
carousel.set( carousel.current + self._options.carouselSteps);
|
|
}
|
|
});
|
|
|
|
carousel.prev.on( 'click:fast', function(e) {
|
|
e.preventDefault();
|
|
|
|
if ( self._options.carouselSteps === 'auto' ) {
|
|
|
|
for ( i = carousel.current; i >= 0; i-- ) {
|
|
if ( carousel.hooks[ carousel.current ] - carousel.hooks[i] > carousel.width ) {
|
|
carousel.set( i + 2 );
|
|
break;
|
|
} else if ( i === 0 ) {
|
|
carousel.set( 0 );
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
carousel.set( carousel.current - self._options.carouselSteps );
|
|
}
|
|
});
|
|
},
|
|
|
|
// calculate and set positions
|
|
set: function( i ) {
|
|
i = M.max( i, 0 );
|
|
while ( carousel.hooks[i - 1] + carousel.width >= carousel.max && i >= 0 ) {
|
|
i--;
|
|
}
|
|
carousel.current = i;
|
|
carousel.animate();
|
|
},
|
|
|
|
// get the last position
|
|
getLast: function(i) {
|
|
return ( i || carousel.current ) - 1;
|
|
},
|
|
|
|
// follow the active image
|
|
follow: function(i) {
|
|
|
|
//don't follow if position fits
|
|
if ( i === 0 || i === carousel.hooks.length - 2 ) {
|
|
carousel.set( i );
|
|
return;
|
|
}
|
|
|
|
// calculate last position
|
|
var last = carousel.current;
|
|
while( carousel.hooks[last] - carousel.hooks[ carousel.current ] <
|
|
carousel.width && last <= carousel.hooks.length ) {
|
|
last ++;
|
|
}
|
|
|
|
// set position
|
|
if ( i - 1 < carousel.current ) {
|
|
carousel.set( i - 1 );
|
|
} else if ( i + 2 > last) {
|
|
carousel.set( i - last + carousel.current + 2 );
|
|
}
|
|
},
|
|
|
|
// helper for setting disabled classes
|
|
setClasses: function() {
|
|
carousel.prev.toggleClass( 'disabled', !carousel.current );
|
|
carousel.next.toggleClass( 'disabled', carousel.hooks[ carousel.current ] + carousel.width >= carousel.max );
|
|
},
|
|
|
|
// the animation method
|
|
animate: function(to) {
|
|
carousel.setClasses();
|
|
var num = carousel.hooks[ carousel.current ] * -1;
|
|
|
|
if ( isNaN( num ) ) {
|
|
return;
|
|
}
|
|
|
|
// FF 24 bug
|
|
self.$( 'thumbnails' ).css('left', function() {
|
|
return $(this).css('left');
|
|
});
|
|
|
|
Utils.animate(self.get( 'thumbnails' ), {
|
|
left: num
|
|
},{
|
|
duration: self._options.carouselSpeed,
|
|
easing: self._options.easing,
|
|
queue: false
|
|
});
|
|
}
|
|
};
|
|
|
|
// tooltip control
|
|
// added in 1.2
|
|
var tooltip = this._tooltip = {
|
|
|
|
initialized : false,
|
|
|
|
open: false,
|
|
|
|
timer: 'tooltip' + self._id,
|
|
|
|
swapTimer: 'swap' + self._id,
|
|
|
|
init: function() {
|
|
|
|
tooltip.initialized = true;
|
|
|
|
var css = '.galleria-tooltip{padding:3px 8px;max-width:50%;background:#ffe;color:#000;z-index:3;position:absolute;font-size:11px;line-height:1.3;' +
|
|
'opacity:0;box-shadow:0 0 2px rgba(0,0,0,.4);-moz-box-shadow:0 0 2px rgba(0,0,0,.4);-webkit-box-shadow:0 0 2px rgba(0,0,0,.4);}';
|
|
|
|
Utils.insertStyleTag( css, 'galleria-tooltip' );
|
|
|
|
self.$( 'tooltip' ).css({
|
|
opacity: 0.8,
|
|
visibility: 'visible',
|
|
display: 'none'
|
|
});
|
|
|
|
},
|
|
|
|
// move handler
|
|
move: function( e ) {
|
|
var mouseX = self.getMousePosition(e).x,
|
|
mouseY = self.getMousePosition(e).y,
|
|
$elem = self.$( 'tooltip' ),
|
|
x = mouseX,
|
|
y = mouseY,
|
|
height = $elem.outerHeight( true ) + 1,
|
|
width = $elem.outerWidth( true ),
|
|
limitY = height + 15;
|
|
|
|
var maxX = self.$( 'container' ).width() - width - 2,
|
|
maxY = self.$( 'container' ).height() - height - 2;
|
|
|
|
if ( !isNaN(x) && !isNaN(y) ) {
|
|
|
|
x += 10;
|
|
y -= ( height+8 );
|
|
|
|
x = M.max( 0, M.min( maxX, x ) );
|
|
y = M.max( 0, M.min( maxY, y ) );
|
|
|
|
if( mouseY < limitY ) {
|
|
y = limitY;
|
|
}
|
|
|
|
$elem.css({ left: x, top: y });
|
|
}
|
|
},
|
|
|
|
// bind elements to the tooltip
|
|
// you can bind multiple elementIDs using { elemID : function } or { elemID : string }
|
|
// you can also bind single DOM elements using bind(elem, string)
|
|
bind: function( elem, value ) {
|
|
|
|
// todo: revise if alternative tooltip is needed for mobile devices
|
|
if (Galleria.TOUCH) {
|
|
return;
|
|
}
|
|
|
|
if (! tooltip.initialized ) {
|
|
tooltip.init();
|
|
}
|
|
|
|
var mouseout = function() {
|
|
self.$( 'container' ).off( 'mousemove', tooltip.move );
|
|
self.clearTimer( tooltip.timer );
|
|
|
|
self.$( 'tooltip' ).stop().animate({
|
|
opacity: 0
|
|
}, 200, function() {
|
|
|
|
self.$( 'tooltip' ).hide();
|
|
|
|
self.addTimer( tooltip.swapTimer, function() {
|
|
tooltip.open = false;
|
|
}, 1000);
|
|
});
|
|
};
|
|
|
|
var hover = function( elem, value) {
|
|
|
|
tooltip.define( elem, value );
|
|
|
|
$( elem ).hover(function() {
|
|
|
|
self.clearTimer( tooltip.swapTimer );
|
|
self.$('container').off( 'mousemove', tooltip.move ).on( 'mousemove', tooltip.move ).trigger( 'mousemove' );
|
|
tooltip.show( elem );
|
|
|
|
self.addTimer( tooltip.timer, function() {
|
|
self.$( 'tooltip' ).stop().show().animate({
|
|
opacity: 1
|
|
});
|
|
tooltip.open = true;
|
|
|
|
}, tooltip.open ? 0 : 500);
|
|
|
|
}, mouseout).click(mouseout);
|
|
};
|
|
|
|
if ( typeof value === 'string' ) {
|
|
hover( ( elem in self._dom ? self.get( elem ) : elem ), value );
|
|
} else {
|
|
// asume elemID here
|
|
$.each( elem, function( elemID, val ) {
|
|
hover( self.get(elemID), val );
|
|
});
|
|
}
|
|
},
|
|
|
|
show: function( elem ) {
|
|
|
|
elem = $( elem in self._dom ? self.get(elem) : elem );
|
|
|
|
var text = elem.data( 'tt' ),
|
|
mouseup = function( e ) {
|
|
|
|
// attach a tiny settimeout to make sure the new tooltip is filled
|
|
window.setTimeout( (function( ev ) {
|
|
return function() {
|
|
tooltip.move( ev );
|
|
};
|
|
}( e )), 10);
|
|
|
|
elem.off( 'mouseup', mouseup );
|
|
|
|
};
|
|
|
|
text = typeof text === 'function' ? text() : text;
|
|
|
|
if ( ! text ) {
|
|
return;
|
|
}
|
|
|
|
self.$( 'tooltip' ).html( text.replace(/\s/, ' ') );
|
|
|
|
// trigger mousemove on mouseup in case of click
|
|
elem.on( 'mouseup', mouseup );
|
|
},
|
|
|
|
define: function( elem, value ) {
|
|
|
|
// we store functions, not strings
|
|
if (typeof value !== 'function') {
|
|
var s = value;
|
|
value = function() {
|
|
return s;
|
|
};
|
|
}
|
|
|
|
elem = $( elem in self._dom ? self.get(elem) : elem ).data('tt', value);
|
|
|
|
tooltip.show( elem );
|
|
|
|
}
|
|
};
|
|
|
|
// internal fullscreen control
|
|
var fullscreen = this._fullscreen = {
|
|
|
|
scrolled: 0,
|
|
|
|
crop: undef,
|
|
|
|
active: false,
|
|
|
|
prev: $(),
|
|
|
|
beforeEnter: function(fn){ fn(); },
|
|
beforeExit: function(fn){ fn(); },
|
|
|
|
keymap: self._keyboard.map,
|
|
|
|
parseCallback: function( callback, enter ) {
|
|
|
|
return _transitions.active ? function() {
|
|
if ( typeof callback == 'function' ) {
|
|
callback.call(self);
|
|
}
|
|
var active = self._controls.getActive(),
|
|
next = self._controls.getNext();
|
|
|
|
self._scaleImage( next );
|
|
self._scaleImage( active );
|
|
|
|
if ( enter && self._options.trueFullscreen ) {
|
|
// Firefox bug, revise later
|
|
$( active.container ).add( next.container ).trigger( 'transitionend' );
|
|
}
|
|
|
|
} : callback;
|
|
|
|
},
|
|
|
|
enter: function( callback ) {
|
|
|
|
fullscreen.beforeEnter(function() {
|
|
|
|
callback = fullscreen.parseCallback( callback, true );
|
|
|
|
if ( self._options.trueFullscreen && _nativeFullscreen.support ) {
|
|
|
|
// do some stuff prior animation for wmoother transitions
|
|
|
|
fullscreen.active = true;
|
|
|
|
Utils.forceStyles( self.get('container'), {
|
|
width: '100%',
|
|
height: '100%'
|
|
});
|
|
|
|
self.rescale();
|
|
|
|
if ( Galleria.MAC ) {
|
|
if ( !( Galleria.SAFARI && /version\/[1-5]/.test(NAV)) ) {
|
|
self.$('container').css('opacity', 0).addClass('fullscreen');
|
|
window.setTimeout(function() {
|
|
fullscreen.scale();
|
|
self.$('container').css('opacity', 1);
|
|
}, 50);
|
|
} else {
|
|
self.$('stage').css('opacity', 0);
|
|
window.setTimeout(function() {
|
|
fullscreen.scale();
|
|
self.$('stage').css('opacity', 1);
|
|
},4);
|
|
}
|
|
} else {
|
|
self.$('container').addClass('fullscreen');
|
|
}
|
|
|
|
$win.resize( fullscreen.scale );
|
|
|
|
_nativeFullscreen.enter( self, callback, self.get('container') );
|
|
|
|
} else {
|
|
|
|
fullscreen.scrolled = $win.scrollTop();
|
|
if( !Galleria.TOUCH ) {
|
|
window.scrollTo(0, 0);
|
|
}
|
|
|
|
fullscreen._enter( callback );
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
_enter: function( callback ) {
|
|
|
|
fullscreen.active = true;
|
|
|
|
if ( IFRAME ) {
|
|
|
|
fullscreen.iframe = (function() {
|
|
|
|
var elem,
|
|
refer = doc.referrer,
|
|
test = doc.createElement('a'),
|
|
loc = window.location;
|
|
|
|
test.href = refer;
|
|
|
|
if( test.protocol != loc.protocol ||
|
|
test.hostname != loc.hostname ||
|
|
test.port != loc.port ) {
|
|
Galleria.raise('Parent fullscreen not available. Iframe protocol, domains and ports must match.');
|
|
return false;
|
|
}
|
|
|
|
fullscreen.pd = window.parent.document;
|
|
|
|
$( fullscreen.pd ).find('iframe').each(function() {
|
|
var idoc = this.contentDocument || this.contentWindow.document;
|
|
if ( idoc === doc ) {
|
|
elem = this;
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return elem;
|
|
}());
|
|
|
|
}
|
|
|
|
// hide the image until rescale is complete
|
|
Utils.hide( self.getActiveImage() );
|
|
|
|
if ( IFRAME && fullscreen.iframe ) {
|
|
fullscreen.iframe.scrolled = $( window.parent ).scrollTop();
|
|
window.parent.scrollTo(0, 0);
|
|
}
|
|
|
|
var data = self.getData(),
|
|
options = self._options,
|
|
inBrowser = !self._options.trueFullscreen || !_nativeFullscreen.support,
|
|
htmlbody = {
|
|
height: '100%',
|
|
overflow: 'hidden',
|
|
margin:0,
|
|
padding:0
|
|
};
|
|
|
|
if (inBrowser) {
|
|
|
|
self.$('container').addClass('fullscreen');
|
|
fullscreen.prev = self.$('container').prev();
|
|
|
|
if ( !fullscreen.prev.length ) {
|
|
fullscreen.parent = self.$( 'container' ).parent();
|
|
}
|
|
|
|
// move
|
|
self.$( 'container' ).appendTo( 'body' );
|
|
|
|
// begin styleforce
|
|
|
|
Utils.forceStyles(self.get('container'), {
|
|
position: Galleria.TOUCH ? 'absolute' : 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: '100%',
|
|
zIndex: 10000
|
|
});
|
|
Utils.forceStyles( DOM().html, htmlbody );
|
|
Utils.forceStyles( DOM().body, htmlbody );
|
|
}
|
|
|
|
if ( IFRAME && fullscreen.iframe ) {
|
|
Utils.forceStyles( fullscreen.pd.documentElement, htmlbody );
|
|
Utils.forceStyles( fullscreen.pd.body, htmlbody );
|
|
Utils.forceStyles( fullscreen.iframe, $.extend( htmlbody, {
|
|
width: '100%',
|
|
height: '100%',
|
|
top: 0,
|
|
left: 0,
|
|
position: 'fixed',
|
|
zIndex: 10000,
|
|
border: 'none'
|
|
}));
|
|
}
|
|
|
|
// temporarily attach some keys
|
|
// save the old ones first in a cloned object
|
|
fullscreen.keymap = $.extend({}, self._keyboard.map);
|
|
|
|
self.attachKeyboard({
|
|
escape: self.exitFullscreen,
|
|
right: self.next,
|
|
left: self.prev
|
|
});
|
|
|
|
// temporarily save the crop
|
|
fullscreen.crop = options.imageCrop;
|
|
|
|
// set fullscreen options
|
|
if ( options.fullscreenCrop != undef ) {
|
|
options.imageCrop = options.fullscreenCrop;
|
|
}
|
|
|
|
// swap to big image if it's different from the display image
|
|
if ( data && data.big && data.image !== data.big ) {
|
|
var big = new Galleria.Picture(),
|
|
cached = big.isCached( data.big ),
|
|
index = self.getIndex(),
|
|
thumb = self._thumbnails[ index ];
|
|
|
|
self.trigger( {
|
|
type: Galleria.LOADSTART,
|
|
cached: cached,
|
|
rewind: false,
|
|
index: index,
|
|
imageTarget: self.getActiveImage(),
|
|
thumbTarget: thumb,
|
|
galleriaData: data
|
|
});
|
|
|
|
big.load( data.big, function( big ) {
|
|
self._scaleImage( big, {
|
|
complete: function( big ) {
|
|
self.trigger({
|
|
type: Galleria.LOADFINISH,
|
|
cached: cached,
|
|
index: index,
|
|
rewind: false,
|
|
imageTarget: big.image,
|
|
thumbTarget: thumb
|
|
});
|
|
var image = self._controls.getActive().image;
|
|
if ( image ) {
|
|
$( image ).width( big.image.width ).height( big.image.height )
|
|
.attr( 'style', $( big.image ).attr('style') )
|
|
.attr( 'src', big.image.src );
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
var n = self.getNext(index),
|
|
p = new Galleria.Picture(),
|
|
ndata = self.getData( n );
|
|
p.preload( self.isFullscreen() && ndata.big ? ndata.big : ndata.image );
|
|
}
|
|
|
|
// init the first rescale and attach callbacks
|
|
|
|
self.rescale(function() {
|
|
|
|
self.addTimer(false, function() {
|
|
// show the image after 50 ms
|
|
if ( inBrowser ) {
|
|
Utils.show( self.getActiveImage() );
|
|
}
|
|
|
|
if (typeof callback === 'function') {
|
|
callback.call( self );
|
|
}
|
|
self.rescale();
|
|
|
|
}, 100);
|
|
|
|
self.trigger( Galleria.FULLSCREEN_ENTER );
|
|
});
|
|
|
|
if ( !inBrowser ) {
|
|
Utils.show( self.getActiveImage() );
|
|
} else {
|
|
$win.resize( fullscreen.scale );
|
|
}
|
|
|
|
},
|
|
|
|
scale : function() {
|
|
self.rescale();
|
|
},
|
|
|
|
exit: function( callback ) {
|
|
|
|
fullscreen.beforeExit(function() {
|
|
|
|
callback = fullscreen.parseCallback( callback );
|
|
|
|
if ( self._options.trueFullscreen && _nativeFullscreen.support ) {
|
|
_nativeFullscreen.exit( callback );
|
|
} else {
|
|
fullscreen._exit( callback );
|
|
}
|
|
});
|
|
},
|
|
|
|
_exit: function( callback ) {
|
|
|
|
fullscreen.active = false;
|
|
|
|
var inBrowser = !self._options.trueFullscreen || !_nativeFullscreen.support,
|
|
$container = self.$( 'container' ).removeClass( 'fullscreen' );
|
|
|
|
// move back
|
|
if ( fullscreen.parent ) {
|
|
fullscreen.parent.prepend( $container );
|
|
} else {
|
|
$container.insertAfter( fullscreen.prev );
|
|
}
|
|
|
|
if ( inBrowser ) {
|
|
Utils.hide( self.getActiveImage() );
|
|
|
|
// revert all styles
|
|
Utils.revertStyles( self.get('container'), DOM().html, DOM().body );
|
|
|
|
// scroll back
|
|
if( !Galleria.TOUCH ) {
|
|
window.scrollTo(0, fullscreen.scrolled);
|
|
}
|
|
|
|
// reload iframe src manually
|
|
var frame = self._controls.frames[ self._controls.active ];
|
|
if ( frame && frame.image ) {
|
|
frame.image.src = frame.image.src;
|
|
}
|
|
}
|
|
|
|
if ( IFRAME && fullscreen.iframe ) {
|
|
Utils.revertStyles( fullscreen.pd.documentElement, fullscreen.pd.body, fullscreen.iframe );
|
|
if ( fullscreen.iframe.scrolled ) {
|
|
window.parent.scrollTo(0, fullscreen.iframe.scrolled );
|
|
}
|
|
}
|
|
|
|
// detach all keyboard events and apply the old keymap
|
|
self.detachKeyboard();
|
|
self.attachKeyboard( fullscreen.keymap );
|
|
|
|
// bring back cached options
|
|
self._options.imageCrop = fullscreen.crop;
|
|
|
|
// return to original image
|
|
var big = self.getData().big,
|
|
image = self._controls.getActive().image;
|
|
|
|
if ( !self.getData().iframe && image && big && big == image.src ) {
|
|
|
|
window.setTimeout(function(src) {
|
|
return function() {
|
|
image.src = src;
|
|
};
|
|
}( self.getData().image ), 1 );
|
|
|
|
}
|
|
|
|
self.rescale(function() {
|
|
self.addTimer(false, function() {
|
|
|
|
// show the image after 50 ms
|
|
if ( inBrowser ) {
|
|
Utils.show( self.getActiveImage() );
|
|
}
|
|
|
|
if ( typeof callback === 'function' ) {
|
|
callback.call( self );
|
|
}
|
|
|
|
$win.trigger( 'resize' );
|
|
|
|
}, 50);
|
|
self.trigger( Galleria.FULLSCREEN_EXIT );
|
|
});
|
|
|
|
$win.off('resize', fullscreen.scale);
|
|
}
|
|
};
|
|
|
|
// the internal idle object for controlling idle states
|
|
var idle = this._idle = {
|
|
|
|
trunk: [],
|
|
|
|
bound: false,
|
|
|
|
active: false,
|
|
|
|
add: function(elem, to, from, hide) {
|
|
if ( !elem || Galleria.TOUCH ) {
|
|
return;
|
|
}
|
|
if (!idle.bound) {
|
|
idle.addEvent();
|
|
}
|
|
elem = $(elem);
|
|
|
|
if ( typeof from == 'boolean' ) {
|
|
hide = from;
|
|
from = {};
|
|
}
|
|
|
|
from = from || {};
|
|
|
|
var extract = {},
|
|
style;
|
|
|
|
for ( style in to ) {
|
|
if ( to.hasOwnProperty( style ) ) {
|
|
extract[ style ] = elem.css( style );
|
|
}
|
|
}
|
|
|
|
elem.data('idle', {
|
|
from: $.extend( extract, from ),
|
|
to: to,
|
|
complete: true,
|
|
busy: false
|
|
});
|
|
|
|
if ( !hide ) {
|
|
idle.addTimer();
|
|
} else {
|
|
elem.css( to );
|
|
}
|
|
idle.trunk.push(elem);
|
|
},
|
|
|
|
remove: function(elem) {
|
|
|
|
elem = $(elem);
|
|
|
|
$.each(idle.trunk, function(i, el) {
|
|
if ( el && el.length && !el.not(elem).length ) {
|
|
elem.css( elem.data( 'idle' ).from );
|
|
idle.trunk.splice(i, 1);
|
|
}
|
|
});
|
|
|
|
if (!idle.trunk.length) {
|
|
idle.removeEvent();
|
|
self.clearTimer( idle.timer );
|
|
}
|
|
},
|
|
|
|
addEvent : function() {
|
|
idle.bound = true;
|
|
self.$('container').on( 'mousemove click', idle.showAll );
|
|
if ( self._options.idleMode == 'hover' ) {
|
|
self.$('container').on( 'mouseleave', idle.hide );
|
|
}
|
|
},
|
|
|
|
removeEvent : function() {
|
|
idle.bound = false;
|
|
self.$('container').on( 'mousemove click', idle.showAll );
|
|
if ( self._options.idleMode == 'hover' ) {
|
|
self.$('container').off( 'mouseleave', idle.hide );
|
|
}
|
|
},
|
|
|
|
addTimer : function() {
|
|
if( self._options.idleMode == 'hover' ) {
|
|
return;
|
|
}
|
|
self.addTimer( 'idle', function() {
|
|
idle.hide();
|
|
}, self._options.idleTime );
|
|
},
|
|
|
|
hide : function() {
|
|
|
|
if ( !self._options.idleMode || self.getIndex() === false ) {
|
|
return;
|
|
}
|
|
|
|
self.trigger( Galleria.IDLE_ENTER );
|
|
|
|
var len = idle.trunk.length;
|
|
|
|
$.each( idle.trunk, function(i, elem) {
|
|
|
|
var data = elem.data('idle');
|
|
|
|
if (! data) {
|
|
return;
|
|
}
|
|
|
|
elem.data('idle').complete = false;
|
|
|
|
Utils.animate( elem, data.to, {
|
|
duration: self._options.idleSpeed,
|
|
complete: function() {
|
|
if ( i == len-1 ) {
|
|
idle.active = false;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
showAll : function() {
|
|
|
|
self.clearTimer( 'idle' );
|
|
|
|
$.each( idle.trunk, function( i, elem ) {
|
|
idle.show( elem );
|
|
});
|
|
},
|
|
|
|
show: function(elem) {
|
|
|
|
var data = elem.data('idle');
|
|
|
|
if ( !idle.active || ( !data.busy && !data.complete ) ) {
|
|
|
|
data.busy = true;
|
|
|
|
self.trigger( Galleria.IDLE_EXIT );
|
|
|
|
self.clearTimer( 'idle' );
|
|
|
|
Utils.animate( elem, data.from, {
|
|
duration: self._options.idleSpeed/2,
|
|
complete: function() {
|
|
idle.active = true;
|
|
$(elem).data('idle').busy = false;
|
|
$(elem).data('idle').complete = true;
|
|
}
|
|
});
|
|
|
|
}
|
|
idle.addTimer();
|
|
}
|
|
};
|
|
|
|
// internal lightbox object
|
|
// creates a predesigned lightbox for simple popups of images in galleria
|
|
var lightbox = this._lightbox = {
|
|
|
|
width : 0,
|
|
|
|
height : 0,
|
|
|
|
initialized : false,
|
|
|
|
active : null,
|
|
|
|
image : null,
|
|
|
|
elems : {},
|
|
|
|
keymap: false,
|
|
|
|
init : function() {
|
|
|
|
if ( lightbox.initialized ) {
|
|
return;
|
|
}
|
|
lightbox.initialized = true;
|
|
|
|
// create some elements to work with
|
|
var elems = 'overlay box content shadow title info close prevholder prev nextholder next counter image',
|
|
el = {},
|
|
op = self._options,
|
|
css = '',
|
|
abs = 'position:absolute;',
|
|
prefix = 'lightbox-',
|
|
cssMap = {
|
|
overlay: 'position:fixed;display:none;opacity:'+op.overlayOpacity+';filter:alpha(opacity='+(op.overlayOpacity*100)+
|
|
');top:0;left:0;width:100%;height:100%;background:'+op.overlayBackground+';z-index:99990',
|
|
box: 'position:fixed;display:none;width:400px;height:400px;top:50%;left:50%;margin-top:-200px;margin-left:-200px;z-index:99991',
|
|
shadow: abs+'background:#000;width:100%;height:100%;',
|
|
content: abs+'background-color:#fff;top:10px;left:10px;right:10px;bottom:10px;overflow:hidden',
|
|
info: abs+'bottom:10px;left:10px;right:10px;color:#444;font:11px/13px arial,sans-serif;height:13px',
|
|
close: abs+'top:10px;right:10px;height:20px;width:20px;background:#fff;text-align:center;cursor:pointer;color:#444;font:16px/22px arial,sans-serif;z-index:99999',
|
|
image: abs+'top:10px;left:10px;right:10px;bottom:30px;overflow:hidden;display:block;',
|
|
prevholder: abs+'width:50%;top:0;bottom:40px;cursor:pointer;',
|
|
nextholder: abs+'width:50%;top:0;bottom:40px;right:-1px;cursor:pointer;',
|
|
prev: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;left:20px;display:none;text-align:center;color:#000;font:bold 16px/36px arial,sans-serif',
|
|
next: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;right:20px;left:auto;display:none;font:bold 16px/36px arial,sans-serif;text-align:center;color:#000',
|
|
title: 'float:left',
|
|
counter: 'float:right;margin-left:8px;'
|
|
},
|
|
hover = function(elem) {
|
|
return elem.hover(
|
|
function() { $(this).css( 'color', '#bbb' ); },
|
|
function() { $(this).css( 'color', '#444' ); }
|
|
);
|
|
},
|
|
appends = {};
|
|
|
|
// fix for navigation hovers transparent background event "feature"
|
|
var exs = '';
|
|
if ( IE > 7 ) {
|
|
exs = IE < 9 ? 'background:#000;filter:alpha(opacity=0);' : 'background:rgba(0,0,0,0);';
|
|
} else {
|
|
exs = 'z-index:99999';
|
|
}
|
|
|
|
cssMap.nextholder += exs;
|
|
cssMap.prevholder += exs;
|
|
|
|
// create and insert CSS
|
|
$.each(cssMap, function( key, value ) {
|
|
css += '.galleria-'+prefix+key+'{'+value+'}';
|
|
});
|
|
|
|
css += '.galleria-'+prefix+'box.iframe .galleria-'+prefix+'prevholder,'+
|
|
'.galleria-'+prefix+'box.iframe .galleria-'+prefix+'nextholder{'+
|
|
'width:100px;height:100px;top:50%;margin-top:-70px}';
|
|
|
|
Utils.insertStyleTag( css, 'galleria-lightbox' );
|
|
|
|
// create the elements
|
|
$.each(elems.split(' '), function( i, elemId ) {
|
|
self.addElement( 'lightbox-' + elemId );
|
|
el[ elemId ] = lightbox.elems[ elemId ] = self.get( 'lightbox-' + elemId );
|
|
});
|
|
|
|
// initiate the image
|
|
lightbox.image = new Galleria.Picture();
|
|
|
|
// append the elements
|
|
$.each({
|
|
box: 'shadow content close prevholder nextholder',
|
|
info: 'title counter',
|
|
content: 'info image',
|
|
prevholder: 'prev',
|
|
nextholder: 'next'
|
|
}, function( key, val ) {
|
|
var arr = [];
|
|
$.each( val.split(' '), function( i, prop ) {
|
|
arr.push( prefix + prop );
|
|
});
|
|
appends[ prefix+key ] = arr;
|
|
});
|
|
|
|
self.append( appends );
|
|
|
|
$( el.image ).append( lightbox.image.container );
|
|
|
|
$( DOM().body ).append( el.overlay, el.box );
|
|
|
|
// add the prev/next nav and bind some controls
|
|
|
|
hover( $( el.close ).on( 'click:fast', lightbox.hide ).html('×') );
|
|
|
|
$.each( ['Prev','Next'], function(i, dir) {
|
|
|
|
var $d = $( el[ dir.toLowerCase() ] ).html( /v/.test( dir ) ? '‹ ' : ' ›' ),
|
|
$e = $( el[ dir.toLowerCase()+'holder'] );
|
|
|
|
$e.on( 'click:fast', function() {
|
|
lightbox[ 'show' + dir ]();
|
|
});
|
|
|
|
// IE7 and touch devices will simply show the nav
|
|
if ( IE < 8 || Galleria.TOUCH ) {
|
|
$d.show();
|
|
return;
|
|
}
|
|
|
|
$e.hover( function() {
|
|
$d.show();
|
|
}, function(e) {
|
|
$d.stop().fadeOut( 200 );
|
|
});
|
|
|
|
});
|
|
$( el.overlay ).on( 'click:fast', lightbox.hide );
|
|
|
|
// the lightbox animation is slow on ipad
|
|
if ( Galleria.IPAD ) {
|
|
self._options.lightboxTransitionSpeed = 0;
|
|
}
|
|
|
|
},
|
|
|
|
rescale: function(event) {
|
|
|
|
// calculate
|
|
var width = M.min( $win.width()-40, lightbox.width ),
|
|
height = M.min( $win.height()-60, lightbox.height ),
|
|
ratio = M.min( width / lightbox.width, height / lightbox.height ),
|
|
destWidth = M.round( lightbox.width * ratio ) + 40,
|
|
destHeight = M.round( lightbox.height * ratio ) + 60,
|
|
to = {
|
|
width: destWidth,
|
|
height: destHeight,
|
|
'margin-top': M.ceil( destHeight / 2 ) *- 1,
|
|
'margin-left': M.ceil( destWidth / 2 ) *- 1
|
|
};
|
|
|
|
// if rescale event, don't animate
|
|
if ( event ) {
|
|
$( lightbox.elems.box ).css( to );
|
|
} else {
|
|
$( lightbox.elems.box ).animate( to, {
|
|
duration: self._options.lightboxTransitionSpeed,
|
|
easing: self._options.easing,
|
|
complete: function() {
|
|
var image = lightbox.image,
|
|
speed = self._options.lightboxFadeSpeed;
|
|
|
|
self.trigger({
|
|
type: Galleria.LIGHTBOX_IMAGE,
|
|
imageTarget: image.image
|
|
});
|
|
|
|
$( image.container ).show();
|
|
|
|
$( image.image ).animate({ opacity: 1 }, speed);
|
|
Utils.show( lightbox.elems.info, speed );
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
hide: function() {
|
|
|
|
// remove the image
|
|
lightbox.image.image = null;
|
|
|
|
$win.off('resize', lightbox.rescale);
|
|
|
|
$( lightbox.elems.box ).hide().find( 'iframe' ).remove();
|
|
|
|
Utils.hide( lightbox.elems.info );
|
|
|
|
self.detachKeyboard();
|
|
self.attachKeyboard( lightbox.keymap );
|
|
|
|
lightbox.keymap = false;
|
|
|
|
Utils.hide( lightbox.elems.overlay, 200, function() {
|
|
$( this ).hide().css( 'opacity', self._options.overlayOpacity );
|
|
self.trigger( Galleria.LIGHTBOX_CLOSE );
|
|
});
|
|
},
|
|
|
|
showNext: function() {
|
|
lightbox.show( self.getNext( lightbox.active ) );
|
|
},
|
|
|
|
showPrev: function() {
|
|
lightbox.show( self.getPrev( lightbox.active ) );
|
|
},
|
|
|
|
show: function(index) {
|
|
|
|
lightbox.active = index = typeof index === 'number' ? index : self.getIndex() || 0;
|
|
|
|
if ( !lightbox.initialized ) {
|
|
lightbox.init();
|
|
}
|
|
|
|
// trigger the event
|
|
self.trigger( Galleria.LIGHTBOX_OPEN );
|
|
|
|
// temporarily attach some keys
|
|
// save the old ones first in a cloned object
|
|
if ( !lightbox.keymap ) {
|
|
|
|
lightbox.keymap = $.extend({}, self._keyboard.map);
|
|
|
|
self.attachKeyboard({
|
|
escape: lightbox.hide,
|
|
right: lightbox.showNext,
|
|
left: lightbox.showPrev
|
|
});
|
|
}
|
|
|
|
$win.off('resize', lightbox.rescale );
|
|
|
|
var data = self.getData(index),
|
|
total = self.getDataLength(),
|
|
n = self.getNext( index ),
|
|
ndata, p, i;
|
|
|
|
Utils.hide( lightbox.elems.info );
|
|
|
|
try {
|
|
for ( i = self._options.preload; i > 0; i-- ) {
|
|
p = new Galleria.Picture();
|
|
ndata = self.getData( n );
|
|
p.preload( ndata.big ? ndata.big : ndata.image );
|
|
n = self.getNext( n );
|
|
}
|
|
} catch(e) {}
|
|
|
|
lightbox.image.isIframe = ( data.iframe && !data.image );
|
|
|
|
$( lightbox.elems.box ).toggleClass( 'iframe', lightbox.image.isIframe );
|
|
|
|
$( lightbox.image.container ).find( '.galleria-videoicon' ).remove();
|
|
|
|
lightbox.image.load( data.big || data.image || data.iframe, function( image ) {
|
|
|
|
if ( image.isIframe ) {
|
|
|
|
var cw = $(window).width(),
|
|
ch = $(window).height();
|
|
|
|
if ( image.video && self._options.maxVideoSize ) {
|
|
var r = M.min( self._options.maxVideoSize/cw, self._options.maxVideoSize/ch );
|
|
if ( r < 1 ) {
|
|
cw *= r;
|
|
ch *= r;
|
|
}
|
|
}
|
|
lightbox.width = cw;
|
|
lightbox.height = ch;
|
|
|
|
} else {
|
|
lightbox.width = image.original.width;
|
|
lightbox.height = image.original.height;
|
|
}
|
|
|
|
$( image.image ).css({
|
|
width: image.isIframe ? '100%' : '100.1%',
|
|
height: image.isIframe ? '100%' : '100.1%',
|
|
top: 0,
|
|
bottom: 0,
|
|
zIndex: 99998,
|
|
opacity: 0,
|
|
visibility: 'visible'
|
|
}).parent().height('100%');
|
|
|
|
lightbox.elems.title.innerHTML = data.title || '';
|
|
lightbox.elems.counter.innerHTML = (index + 1) + ' / ' + total;
|
|
$win.resize( lightbox.rescale );
|
|
lightbox.rescale();
|
|
|
|
if( data.image && data.iframe ) {
|
|
|
|
$( lightbox.elems.box ).addClass('iframe');
|
|
|
|
if ( data.video ) {
|
|
var $icon = _playIcon( image.container ).hide();
|
|
window.setTimeout(function() {
|
|
$icon.fadeIn(200);
|
|
}, 200);
|
|
}
|
|
|
|
$( image.image ).css( 'cursor', 'pointer' ).mouseup((function(data, image) {
|
|
return function(e) {
|
|
$( lightbox.image.container ).find( '.galleria-videoicon' ).remove();
|
|
e.preventDefault();
|
|
image.isIframe = true;
|
|
image.load( data.iframe + ( data.video ? '&autoplay=1' : '' ), {
|
|
width: '100%',
|
|
height: IE < 8 ? $( lightbox.image.container ).height() : '100%'
|
|
});
|
|
};
|
|
}(data, image)));
|
|
}
|
|
});
|
|
|
|
$( lightbox.elems.overlay ).show().css( 'visibility', 'visible' );
|
|
$( lightbox.elems.box ).show();
|
|
}
|
|
};
|
|
|
|
// the internal timeouts object
|
|
// provides helper methods for controlling timeouts
|
|
|
|
var _timer = this._timer = {
|
|
|
|
trunk: {},
|
|
|
|
add: function( id, fn, delay, loop ) {
|
|
id = id || new Date().getTime();
|
|
loop = loop || false;
|
|
this.clear( id );
|
|
if ( loop ) {
|
|
var old = fn;
|
|
fn = function() {
|
|
old();
|
|
_timer.add( id, fn, delay );
|
|
};
|
|
}
|
|
this.trunk[ id ] = window.setTimeout( fn, delay );
|
|
},
|
|
|
|
clear: function( id ) {
|
|
|
|
var del = function( i ) {
|
|
window.clearTimeout( this.trunk[ i ] );
|
|
delete this.trunk[ i ];
|
|
}, i;
|
|
|
|
if ( !!id && id in this.trunk ) {
|
|
del.call( this, id );
|
|
|
|
} else if ( typeof id === 'undefined' ) {
|
|
for ( i in this.trunk ) {
|
|
if ( this.trunk.hasOwnProperty( i ) ) {
|
|
del.call( this, i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
return this;
|
|
};
|
|
|
|
// end Galleria constructor
|
|
|
|
Galleria.prototype = {
|
|
|
|
// bring back the constructor reference
|
|
|
|
constructor: Galleria,
|
|
|
|
/**
|
|
Use this function to initialize the gallery and start loading.
|
|
Should only be called once per instance.
|
|
|
|
@param {HTMLElement} target The target element
|
|
@param {Object} options The gallery options
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
init: function( target, options ) {
|
|
|
|
options = _legacyOptions( options );
|
|
|
|
// save the original ingredients
|
|
this._original = {
|
|
target: target,
|
|
options: options,
|
|
data: null
|
|
};
|
|
|
|
// save the target here
|
|
this._target = this._dom.target = target.nodeName ? target : $( target ).get(0);
|
|
|
|
// save the original content for destruction
|
|
this._original.html = this._target.innerHTML;
|
|
|
|
// push the instance
|
|
_instances.push( this );
|
|
|
|
// raise error if no target is detected
|
|
if ( !this._target ) {
|
|
Galleria.raise('Target not found', true);
|
|
return;
|
|
}
|
|
|
|
// apply options
|
|
this._options = {
|
|
autoplay: false,
|
|
carousel: true,
|
|
carouselFollow: true, // legacy, deprecate at 1.3
|
|
carouselSpeed: 400,
|
|
carouselSteps: 'auto',
|
|
clicknext: false,
|
|
dailymotion: {
|
|
foreground: '%23EEEEEE',
|
|
highlight: '%235BCEC5',
|
|
background: '%23222222',
|
|
logo: 0,
|
|
hideInfos: 1
|
|
},
|
|
dataConfig : function( elem ) { return {}; },
|
|
dataSelector: 'img',
|
|
dataSort: false,
|
|
dataSource: this._target,
|
|
debug: undef,
|
|
dummy: undef, // 1.2.5
|
|
easing: 'galleria',
|
|
extend: function(options) {},
|
|
fullscreenCrop: undef, // 1.2.5
|
|
fullscreenDoubleTap: true, // 1.2.4 toggles fullscreen on double-tap for touch devices
|
|
fullscreenTransition: undef, // 1.2.6
|
|
height: 0,
|
|
idleMode: true, // 1.2.4 toggles idleMode
|
|
idleTime: 3000,
|
|
idleSpeed: 200,
|
|
imageCrop: false,
|
|
imageMargin: 0,
|
|
imagePan: false,
|
|
imagePanSmoothness: 12,
|
|
imagePosition: '50%',
|
|
imageTimeout: undef, // 1.2.5
|
|
initialTransition: undef, // 1.2.4, replaces transitionInitial
|
|
keepSource: false,
|
|
layerFollow: true, // 1.2.5
|
|
lightbox: false, // 1.2.3
|
|
lightboxFadeSpeed: 200,
|
|
lightboxTransitionSpeed: 200,
|
|
linkSourceImages: true,
|
|
maxScaleRatio: undef,
|
|
maxVideoSize: undef, // 1.2.9
|
|
minScaleRatio: undef, // deprecated in 1.2.9
|
|
overlayOpacity: 0.85,
|
|
overlayBackground: '#0b0b0b',
|
|
pauseOnInteraction: true,
|
|
popupLinks: false,
|
|
preload: 2,
|
|
queue: true,
|
|
responsive: true,
|
|
show: 0,
|
|
showInfo: true,
|
|
showCounter: true,
|
|
showImagenav: true,
|
|
swipe: 'auto', // 1.2.4 -> revised in 1.3 -> changed type in 1.3.5
|
|
theme: null,
|
|
thumbCrop: true,
|
|
thumbEventType: 'click:fast',
|
|
thumbMargin: 0,
|
|
thumbQuality: 'auto',
|
|
thumbDisplayOrder: true, // 1.2.8
|
|
thumbPosition: '50%', // 1.3
|
|
thumbnails: true,
|
|
touchTransition: undef, // 1.2.6
|
|
transition: 'fade',
|
|
transitionInitial: undef, // legacy, deprecate in 1.3. Use initialTransition instead.
|
|
transitionSpeed: 400,
|
|
trueFullscreen: true, // 1.2.7
|
|
useCanvas: false, // 1.2.4
|
|
variation: '', // 1.3.2
|
|
videoPoster: true, // 1.3
|
|
vimeo: {
|
|
title: 0,
|
|
byline: 0,
|
|
portrait: 0,
|
|
color: 'aaaaaa'
|
|
},
|
|
wait: 5000, // 1.2.7
|
|
width: 'auto',
|
|
youtube: {
|
|
modestbranding: 1,
|
|
autohide: 1,
|
|
color: 'white',
|
|
hd: 1,
|
|
rel: 0,
|
|
showinfo: 0
|
|
}
|
|
};
|
|
|
|
// legacy support for transitionInitial
|
|
this._options.initialTransition = this._options.initialTransition || this._options.transitionInitial;
|
|
|
|
if ( options ) {
|
|
|
|
// turn off debug
|
|
if ( options.debug === false ) {
|
|
DEBUG = false;
|
|
}
|
|
|
|
// set timeout
|
|
if ( typeof options.imageTimeout === 'number' ) {
|
|
TIMEOUT = options.imageTimeout;
|
|
}
|
|
|
|
// set dummy
|
|
if ( typeof options.dummy === 'string' ) {
|
|
DUMMY = options.dummy;
|
|
}
|
|
|
|
// set theme
|
|
if ( typeof options.theme == 'string' ) {
|
|
this._options.theme = options.theme;
|
|
}
|
|
}
|
|
|
|
// hide all content
|
|
$( this._target ).children().hide();
|
|
|
|
// Warn for quirks mode
|
|
if ( Galleria.QUIRK ) {
|
|
Galleria.raise('Your page is in Quirks mode, Galleria may not render correctly. Please validate your HTML and add a correct doctype.');
|
|
}
|
|
|
|
// now we just have to wait for the theme...
|
|
// first check if it has already loaded
|
|
if ( _loadedThemes.length ) {
|
|
if ( this._options.theme ) {
|
|
for ( var i=0; i<_loadedThemes.length; i++ ) {
|
|
if( this._options.theme === _loadedThemes[i].name ) {
|
|
this.theme = _loadedThemes[i];
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// if no theme sepcified, apply the first loaded theme
|
|
this.theme = _loadedThemes[0];
|
|
}
|
|
}
|
|
|
|
if ( typeof this.theme == 'object' ) {
|
|
this._init();
|
|
} else {
|
|
// if no theme is loaded yet, push the instance into a pool and run it when the theme is ready
|
|
_pool.push( this );
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// this method should only be called once per instance
|
|
// for manipulation of data, use the .load method
|
|
|
|
_init: function() {
|
|
|
|
var self = this,
|
|
options = this._options;
|
|
|
|
if ( this._initialized ) {
|
|
Galleria.raise( 'Init failed: Gallery instance already initialized.' );
|
|
return this;
|
|
}
|
|
|
|
this._initialized = true;
|
|
|
|
if ( !this.theme ) {
|
|
Galleria.raise( 'Init failed: No theme found.', true );
|
|
return this;
|
|
}
|
|
|
|
// merge the theme & caller options
|
|
$.extend( true, options, this.theme.defaults, this._original.options, Galleria.configure.options );
|
|
|
|
// internally we use boolean for swipe
|
|
options.swipe = (function(s) {
|
|
|
|
if ( s == 'enforced' ) { return true; }
|
|
|
|
// legacy patch
|
|
if( s === false || s == 'disabled' ) { return false; }
|
|
|
|
return !!Galleria.TOUCH;
|
|
|
|
}( options.swipe ));
|
|
|
|
// disable options that arent compatible with swipe
|
|
if ( options.swipe ) {
|
|
options.clicknext = false;
|
|
options.imagePan = false;
|
|
}
|
|
|
|
// check for canvas support
|
|
(function( can ) {
|
|
if ( !( 'getContext' in can ) ) {
|
|
can = null;
|
|
return;
|
|
}
|
|
_canvas = _canvas || {
|
|
elem: can,
|
|
context: can.getContext( '2d' ),
|
|
cache: {},
|
|
length: 0
|
|
};
|
|
}( doc.createElement( 'canvas' ) ) );
|
|
|
|
// bind the gallery to run when data is ready
|
|
this.bind( Galleria.DATA, function() {
|
|
|
|
// remove big if total pixels are less than 1024 (most phones)
|
|
if ( window.screen && window.screen.width && Array.prototype.forEach ) {
|
|
|
|
this._data.forEach(function(data) {
|
|
|
|
var density = 'devicePixelRatio' in window ? window.devicePixelRatio : 1,
|
|
m = M.max( window.screen.width, window.screen.height );
|
|
|
|
if ( m*density < 1024 ) {
|
|
data.big = data.image;
|
|
}
|
|
});
|
|
}
|
|
|
|
// save the new data
|
|
this._original.data = this._data;
|
|
|
|
// lets show the counter here
|
|
this.get('total').innerHTML = this.getDataLength();
|
|
|
|
// cache the container
|
|
var $container = this.$( 'container' );
|
|
|
|
// set ratio if height is < 2
|
|
if ( self._options.height < 2 ) {
|
|
self._userRatio = self._ratio = self._options.height;
|
|
}
|
|
|
|
// the gallery is ready, let's just wait for the css
|
|
var num = { width: 0, height: 0 };
|
|
var testHeight = function() {
|
|
return self.$( 'stage' ).height();
|
|
};
|
|
|
|
// check container and thumbnail height
|
|
Utils.wait({
|
|
until: function() {
|
|
|
|
// keep trying to get the value
|
|
num = self._getWH();
|
|
$container.width( num.width ).height( num.height );
|
|
return testHeight() && num.width && num.height > 50;
|
|
|
|
},
|
|
success: function() {
|
|
|
|
self._width = num.width;
|
|
self._height = num.height;
|
|
self._ratio = self._ratio || num.height/num.width;
|
|
|
|
// for some strange reason, webkit needs a single setTimeout to play ball
|
|
if ( Galleria.WEBKIT ) {
|
|
window.setTimeout( function() {
|
|
self._run();
|
|
}, 1);
|
|
} else {
|
|
self._run();
|
|
}
|
|
},
|
|
error: function() {
|
|
|
|
// Height was probably not set, raise hard errors
|
|
|
|
if ( testHeight() ) {
|
|
Galleria.raise('Could not extract sufficient width/height of the gallery container. Traced measures: width:' + num.width + 'px, height: ' + num.height + 'px.', true);
|
|
} else {
|
|
Galleria.raise('Could not extract a stage height from the CSS. Traced height: ' + testHeight() + 'px.', true);
|
|
}
|
|
},
|
|
timeout: typeof this._options.wait == 'number' ? this._options.wait : false
|
|
});
|
|
});
|
|
|
|
// build the gallery frame
|
|
this.append({
|
|
'info-text' :
|
|
['info-title', 'info-description'],
|
|
'info' :
|
|
['info-text'],
|
|
'image-nav' :
|
|
['image-nav-right', 'image-nav-left'],
|
|
'stage' :
|
|
['images', 'loader', 'counter', 'image-nav'],
|
|
'thumbnails-list' :
|
|
['thumbnails'],
|
|
'thumbnails-container' :
|
|
['thumb-nav-left', 'thumbnails-list', 'thumb-nav-right'],
|
|
'container' :
|
|
['stage', 'thumbnails-container', 'info', 'tooltip']
|
|
});
|
|
|
|
Utils.hide( this.$( 'counter' ).append(
|
|
this.get( 'current' ),
|
|
doc.createTextNode(' / '),
|
|
this.get( 'total' )
|
|
) );
|
|
|
|
this.setCounter('–');
|
|
|
|
Utils.hide( self.get('tooltip') );
|
|
|
|
// add a notouch class on the container to prevent unwanted :hovers on touch devices
|
|
this.$( 'container' ).addClass([
|
|
( Galleria.TOUCH ? 'touch' : 'notouch' ),
|
|
this._options.variation,
|
|
'galleria-theme-'+this.theme.name
|
|
].join(' '));
|
|
|
|
// add images to the controls
|
|
if ( !this._options.swipe ) {
|
|
$.each( new Array(2), function( i ) {
|
|
|
|
// create a new Picture instance
|
|
var image = new Galleria.Picture();
|
|
|
|
// apply some styles, create & prepend overlay
|
|
$( image.container ).css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0
|
|
}).prepend( self._layers[i] = $( Utils.create('galleria-layer') ).css({
|
|
position: 'absolute',
|
|
top:0, left:0, right:0, bottom:0,
|
|
zIndex:2
|
|
})[0] );
|
|
|
|
// append the image
|
|
self.$( 'images' ).append( image.container );
|
|
|
|
// reload the controls
|
|
self._controls[i] = image;
|
|
|
|
// build a frame
|
|
var frame = new Galleria.Picture();
|
|
frame.isIframe = true;
|
|
|
|
$( frame.container ).attr('class', 'galleria-frame').css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
zIndex: 4,
|
|
background: '#000',
|
|
display: 'none'
|
|
}).appendTo( image.container );
|
|
|
|
self._controls.frames[i] = frame;
|
|
|
|
});
|
|
}
|
|
|
|
// some forced generic styling
|
|
this.$( 'images' ).css({
|
|
position: 'relative',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: '100%'
|
|
});
|
|
|
|
if ( options.swipe ) {
|
|
this.$( 'images' ).css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
width: 0,
|
|
height: '100%'
|
|
});
|
|
this.finger = new Galleria.Finger(this.get('stage'), {
|
|
onchange: function(page) {
|
|
self.pause().show(page);
|
|
},
|
|
oncomplete: function(page) {
|
|
|
|
var index = M.max( 0, M.min( parseInt( page, 10 ), self.getDataLength() - 1 ) ),
|
|
data = self.getData(index);
|
|
|
|
$( self._thumbnails[ index ].container )
|
|
.addClass( 'active' )
|
|
.siblings( '.active' )
|
|
.removeClass( 'active' );
|
|
|
|
if ( !data ) {
|
|
return;
|
|
}
|
|
|
|
// remove video iframes
|
|
self.$( 'images' ).find( '.galleria-frame' ).css('opacity', 0).hide().find( 'iframe' ).remove();
|
|
|
|
if ( self._options.carousel && self._options.carouselFollow ) {
|
|
self._carousel.follow( index );
|
|
}
|
|
}
|
|
});
|
|
this.bind( Galleria.RESCALE, function() {
|
|
this.finger.setup();
|
|
});
|
|
this.$('stage').on('click', function(e) {
|
|
var data = self.getData();
|
|
if ( !data ) {
|
|
return;
|
|
}
|
|
if ( data.iframe ) {
|
|
|
|
if ( self.isPlaying() ) {
|
|
self.pause();
|
|
}
|
|
var frame = self._controls.frames[ self._active ],
|
|
w = self._stageWidth,
|
|
h = self._stageHeight;
|
|
|
|
if ( $( frame.container ).find( 'iframe' ).length ) {
|
|
return;
|
|
}
|
|
|
|
$( frame.container ).css({
|
|
width: w,
|
|
height: h,
|
|
opacity: 0
|
|
}).show().animate({
|
|
opacity: 1
|
|
}, 200);
|
|
|
|
window.setTimeout(function() {
|
|
frame.load( data.iframe + ( data.video ? '&autoplay=1' : '' ), {
|
|
width: w,
|
|
height: h
|
|
}, function( frame ) {
|
|
self.$( 'container' ).addClass( 'videoplay' );
|
|
frame.scale({
|
|
width: self._stageWidth,
|
|
height: self._stageHeight,
|
|
iframelimit: data.video ? self._options.maxVideoSize : undef
|
|
});
|
|
});
|
|
}, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
if ( data.link ) {
|
|
if ( self._options.popupLinks ) {
|
|
var win = window.open( data.link, '_blank' );
|
|
} else {
|
|
window.location.href = data.link;
|
|
}
|
|
return;
|
|
}
|
|
});
|
|
this.bind( Galleria.IMAGE, function(e) {
|
|
|
|
self.setCounter( e.index );
|
|
self.setInfo( e.index );
|
|
|
|
var next = this.getNext(),
|
|
prev = this.getPrev();
|
|
|
|
var preloads = [prev,next];
|
|
preloads.push(this.getNext(next), this.getPrev(prev), self._controls.slides.length-1);
|
|
|
|
var filtered = [];
|
|
|
|
$.each(preloads, function(i, val) {
|
|
if ( $.inArray(val, filtered) == -1 ) {
|
|
filtered.push(val);
|
|
}
|
|
});
|
|
|
|
$.each(filtered, function(i, loadme) {
|
|
var d = self.getData(loadme),
|
|
img = self._controls.slides[loadme],
|
|
src = self.isFullscreen() && d.big ? d.big : ( d.image || d.iframe );
|
|
|
|
if ( d.iframe && !d.image ) {
|
|
img.isIframe = true;
|
|
}
|
|
|
|
if ( !img.ready ) {
|
|
self._controls.slides[loadme].load(src, function(img) {
|
|
if ( !img.isIframe ) {
|
|
$(img.image).css('visibility', 'hidden');
|
|
}
|
|
self._scaleImage(img, {
|
|
complete: function(img) {
|
|
if ( !img.isIframe ) {
|
|
$(img.image).css({
|
|
opacity: 0,
|
|
visibility: 'visible'
|
|
}).animate({
|
|
opacity: 1
|
|
}, 200);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
this.$( 'thumbnails, thumbnails-list' ).css({
|
|
overflow: 'hidden',
|
|
position: 'relative'
|
|
});
|
|
|
|
// bind image navigation arrows
|
|
this.$( 'image-nav-right, image-nav-left' ).on( 'click:fast', function(e) {
|
|
|
|
// pause if options is set
|
|
if ( options.pauseOnInteraction ) {
|
|
self.pause();
|
|
}
|
|
|
|
// navigate
|
|
var fn = /right/.test( this.className ) ? 'next' : 'prev';
|
|
self[ fn ]();
|
|
|
|
}).on('click', function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
// tune the clicknext option
|
|
if ( options.clicknext || options.swipe ) {
|
|
e.stopPropagation();
|
|
}
|
|
});
|
|
|
|
// hide controls if chosen to
|
|
$.each( ['info','counter','image-nav'], function( i, el ) {
|
|
if ( options[ 'show' + el.substr(0,1).toUpperCase() + el.substr(1).replace(/-/,'') ] === false ) {
|
|
Utils.moveOut( self.get( el.toLowerCase() ) );
|
|
}
|
|
});
|
|
|
|
// load up target content
|
|
this.load();
|
|
|
|
// now it's usually safe to remove the content
|
|
// IE will never stop loading if we remove it, so let's keep it hidden for IE (it's usually fast enough anyway)
|
|
if ( !options.keepSource && !IE ) {
|
|
this._target.innerHTML = '';
|
|
}
|
|
|
|
// re-append the errors, if they happened before clearing
|
|
if ( this.get( 'errors' ) ) {
|
|
this.appendChild( 'target', 'errors' );
|
|
}
|
|
|
|
// append the gallery frame
|
|
this.appendChild( 'target', 'container' );
|
|
|
|
// parse the carousel on each thumb load
|
|
if ( options.carousel ) {
|
|
var count = 0,
|
|
show = options.show;
|
|
this.bind( Galleria.THUMBNAIL, function() {
|
|
this.updateCarousel();
|
|
if ( ++count == this.getDataLength() && typeof show == 'number' && show > 0 ) {
|
|
this._carousel.follow( show );
|
|
}
|
|
});
|
|
}
|
|
|
|
// bind window resize for responsiveness
|
|
if ( options.responsive ) {
|
|
$win.on( 'resize', function() {
|
|
if ( !self.isFullscreen() ) {
|
|
self.resize();
|
|
}
|
|
});
|
|
}
|
|
|
|
// double-tap/click fullscreen toggle
|
|
|
|
if ( options.fullscreenDoubleTap ) {
|
|
|
|
this.$( 'stage' ).on( 'touchstart', (function() {
|
|
var last, cx, cy, lx, ly, now,
|
|
getData = function(e) {
|
|
return e.originalEvent.touches ? e.originalEvent.touches[0] : e;
|
|
};
|
|
self.$( 'stage' ).on('touchmove', function() {
|
|
last = 0;
|
|
});
|
|
return function(e) {
|
|
if( /(-left|-right)/.test(e.target.className) ) {
|
|
return;
|
|
}
|
|
now = Utils.timestamp();
|
|
cx = getData(e).pageX;
|
|
cy = getData(e).pageY;
|
|
if ( e.originalEvent.touches.length < 2 && ( now - last < 300 ) && ( cx - lx < 20) && ( cy - ly < 20) ) {
|
|
self.toggleFullscreen();
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
last = now;
|
|
lx = cx;
|
|
ly = cy;
|
|
};
|
|
}()));
|
|
}
|
|
|
|
// bind the ons
|
|
$.each( Galleria.on.binds, function(i, bind) {
|
|
// check if already bound
|
|
if ( $.inArray( bind.hash, self._binds ) == -1 ) {
|
|
self.bind( bind.type, bind.callback );
|
|
}
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
addTimer : function() {
|
|
this._timer.add.apply( this._timer, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
clearTimer : function() {
|
|
this._timer.clear.apply( this._timer, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
// parse width & height from CSS or options
|
|
|
|
_getWH : function() {
|
|
|
|
var $container = this.$( 'container' ),
|
|
$target = this.$( 'target' ),
|
|
self = this,
|
|
num = {},
|
|
arr;
|
|
|
|
$.each(['width', 'height'], function( i, m ) {
|
|
|
|
// first check if options is set
|
|
if ( self._options[ m ] && typeof self._options[ m ] === 'number') {
|
|
num[ m ] = self._options[ m ];
|
|
} else {
|
|
|
|
arr = [
|
|
Utils.parseValue( $container.css( m ) ), // the container css height
|
|
Utils.parseValue( $target.css( m ) ), // the target css height
|
|
$container[ m ](), // the container jQuery method
|
|
$target[ m ]() // the target jQuery method
|
|
];
|
|
|
|
// if first time, include the min-width & min-height
|
|
if ( !self[ '_'+m ] ) {
|
|
arr.splice(arr.length,
|
|
Utils.parseValue( $container.css( 'min-'+m ) ),
|
|
Utils.parseValue( $target.css( 'min-'+m ) )
|
|
);
|
|
}
|
|
|
|
// else extract the measures from different sources and grab the highest value
|
|
num[ m ] = M.max.apply( M, arr );
|
|
}
|
|
});
|
|
|
|
// allow setting a height ratio instead of exact value
|
|
// useful when doing responsive galleries
|
|
|
|
if ( self._userRatio ) {
|
|
num.height = num.width * self._userRatio;
|
|
}
|
|
|
|
return num;
|
|
},
|
|
|
|
// Creates the thumbnails and carousel
|
|
// can be used at any time, f.ex when the data object is manipulated
|
|
// push is an optional argument with pushed images
|
|
|
|
_createThumbnails : function( push ) {
|
|
|
|
this.get( 'total' ).innerHTML = this.getDataLength();
|
|
|
|
var src,
|
|
thumb,
|
|
data,
|
|
|
|
$container,
|
|
|
|
self = this,
|
|
o = this._options,
|
|
|
|
i = push ? this._data.length - push.length : 0,
|
|
chunk = i,
|
|
|
|
thumbchunk = [],
|
|
loadindex = 0,
|
|
|
|
gif = IE < 8 ? 'http://upload.wikimedia.org/wikipedia/commons/c/c0/Blank.gif' :
|
|
'data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D',
|
|
|
|
// get previously active thumbnail, if exists
|
|
active = (function() {
|
|
var a = self.$('thumbnails').find('.active');
|
|
if ( !a.length ) {
|
|
return false;
|
|
}
|
|
return a.find('img').attr('src');
|
|
}()),
|
|
|
|
// cache the thumbnail option
|
|
optval = typeof o.thumbnails === 'string' ? o.thumbnails.toLowerCase() : null,
|
|
|
|
// move some data into the instance
|
|
// for some reason, jQuery cant handle css(property) when zooming in FF, breaking the gallery
|
|
// so we resort to getComputedStyle for browsers who support it
|
|
getStyle = function( prop ) {
|
|
return doc.defaultView && doc.defaultView.getComputedStyle ?
|
|
doc.defaultView.getComputedStyle( thumb.container, null )[ prop ] :
|
|
$container.css( prop );
|
|
},
|
|
|
|
fake = function(image, index, container) {
|
|
return function() {
|
|
$( container ).append( image );
|
|
self.trigger({
|
|
type: Galleria.THUMBNAIL,
|
|
thumbTarget: image,
|
|
index: index,
|
|
galleriaData: self.getData( index )
|
|
});
|
|
};
|
|
},
|
|
|
|
onThumbEvent = function( e ) {
|
|
|
|
// pause if option is set
|
|
if ( o.pauseOnInteraction ) {
|
|
self.pause();
|
|
}
|
|
|
|
// extract the index from the data
|
|
var index = $( e.currentTarget ).data( 'index' );
|
|
if ( self.getIndex() !== index ) {
|
|
self.show( index );
|
|
}
|
|
|
|
e.preventDefault();
|
|
},
|
|
|
|
thumbComplete = function( thumb, callback ) {
|
|
|
|
$( thumb.container ).css( 'visibility', 'visible' );
|
|
self.trigger({
|
|
type: Galleria.THUMBNAIL,
|
|
thumbTarget: thumb.image,
|
|
index: thumb.data.order,
|
|
galleriaData: self.getData( thumb.data.order )
|
|
});
|
|
|
|
if ( typeof callback == 'function' ) {
|
|
callback.call( self, thumb );
|
|
}
|
|
},
|
|
|
|
onThumbLoad = function( thumb, callback ) {
|
|
|
|
// scale when ready
|
|
thumb.scale({
|
|
width: thumb.data.width,
|
|
height: thumb.data.height,
|
|
crop: o.thumbCrop,
|
|
margin: o.thumbMargin,
|
|
canvas: o.useCanvas,
|
|
position: o.thumbPosition,
|
|
complete: function( thumb ) {
|
|
|
|
// shrink thumbnails to fit
|
|
var top = ['left', 'top'],
|
|
arr = ['Width', 'Height'],
|
|
m,
|
|
css,
|
|
data = self.getData( thumb.index );
|
|
|
|
// calculate shrinked positions
|
|
$.each(arr, function( i, measure ) {
|
|
m = measure.toLowerCase();
|
|
if ( (o.thumbCrop !== true || o.thumbCrop === m ) ) {
|
|
css = {};
|
|
css[ m ] = thumb[ m ];
|
|
$( thumb.container ).css( css );
|
|
css = {};
|
|
css[ top[ i ] ] = 0;
|
|
$( thumb.image ).css( css );
|
|
}
|
|
|
|
// cache outer measures
|
|
thumb[ 'outer' + measure ] = $( thumb.container )[ 'outer' + measure ]( true );
|
|
});
|
|
|
|
// set high quality if downscale is moderate
|
|
Utils.toggleQuality( thumb.image,
|
|
o.thumbQuality === true ||
|
|
( o.thumbQuality === 'auto' && thumb.original.width < thumb.width * 3 )
|
|
);
|
|
|
|
if ( o.thumbDisplayOrder && !thumb.lazy ) {
|
|
|
|
$.each( thumbchunk, function( i, th ) {
|
|
if ( i === loadindex && th.ready && !th.displayed ) {
|
|
|
|
loadindex++;
|
|
th.displayed = true;
|
|
|
|
thumbComplete( th, callback );
|
|
|
|
return;
|
|
}
|
|
});
|
|
} else {
|
|
thumbComplete( thumb, callback );
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
if ( !push ) {
|
|
this._thumbnails = [];
|
|
this.$( 'thumbnails' ).empty();
|
|
}
|
|
|
|
// loop through data and create thumbnails
|
|
for( ; this._data[ i ]; i++ ) {
|
|
|
|
data = this._data[ i ];
|
|
|
|
// get source from thumb or image
|
|
src = data.thumb || data.image;
|
|
|
|
if ( ( o.thumbnails === true || optval == 'lazy' ) && ( data.thumb || data.image ) ) {
|
|
|
|
// add a new Picture instance
|
|
thumb = new Galleria.Picture(i);
|
|
|
|
// save the index
|
|
thumb.index = i;
|
|
|
|
// flag displayed
|
|
thumb.displayed = false;
|
|
|
|
// flag lazy
|
|
thumb.lazy = false;
|
|
|
|
// flag video
|
|
thumb.video = false;
|
|
|
|
// append the thumbnail
|
|
this.$( 'thumbnails' ).append( thumb.container );
|
|
|
|
// cache the container
|
|
$container = $( thumb.container );
|
|
|
|
// hide it
|
|
$container.css( 'visibility', 'hidden' );
|
|
|
|
thumb.data = {
|
|
width : Utils.parseValue( getStyle( 'width' ) ),
|
|
height : Utils.parseValue( getStyle( 'height' ) ),
|
|
order : i,
|
|
src : src
|
|
};
|
|
|
|
// grab & reset size for smoother thumbnail loads
|
|
if ( o.thumbCrop !== true ) {
|
|
$container.css( { width: 'auto', height: 'auto' } );
|
|
} else {
|
|
$container.css( { width: thumb.data.width, height: thumb.data.height } );
|
|
}
|
|
|
|
// load the thumbnail
|
|
if ( optval == 'lazy' ) {
|
|
|
|
$container.addClass( 'lazy' );
|
|
|
|
thumb.lazy = true;
|
|
|
|
thumb.load( gif, {
|
|
height: thumb.data.height,
|
|
width: thumb.data.width
|
|
});
|
|
|
|
} else {
|
|
thumb.load( src, onThumbLoad );
|
|
}
|
|
|
|
// preload all images here
|
|
if ( o.preload === 'all' ) {
|
|
thumb.preload( data.image );
|
|
}
|
|
|
|
// create empty spans if thumbnails is set to 'empty'
|
|
} else if ( ( data.iframe && optval !== null ) || optval === 'empty' || optval === 'numbers' ) {
|
|
thumb = {
|
|
container: Utils.create( 'galleria-image' ),
|
|
image: Utils.create( 'img', 'span' ),
|
|
ready: true,
|
|
data: {
|
|
order: i
|
|
}
|
|
};
|
|
|
|
// create numbered thumbnails
|
|
if ( optval === 'numbers' ) {
|
|
$( thumb.image ).text( i + 1 );
|
|
}
|
|
|
|
if ( data.iframe ) {
|
|
$( thumb.image ).addClass( 'iframe' );
|
|
}
|
|
|
|
this.$( 'thumbnails' ).append( thumb.container );
|
|
|
|
// we need to "fake" a loading delay before we append and trigger
|
|
// 50+ should be enough
|
|
|
|
window.setTimeout( ( fake )( thumb.image, i, thumb.container ), 50 + ( i*20 ) );
|
|
|
|
// create null object to silent errors
|
|
} else {
|
|
thumb = {
|
|
container: null,
|
|
image: null
|
|
};
|
|
}
|
|
|
|
// add events for thumbnails
|
|
// you can control the event type using thumb_event_type
|
|
// we'll add the same event to the source if it's kept
|
|
|
|
$( thumb.container ).add( o.keepSource && o.linkSourceImages ? data.original : null )
|
|
.data('index', i).on( o.thumbEventType, onThumbEvent )
|
|
.data('thumbload', onThumbLoad);
|
|
|
|
if (active === src) {
|
|
$( thumb.container ).addClass( 'active' );
|
|
}
|
|
|
|
this._thumbnails.push( thumb );
|
|
}
|
|
|
|
thumbchunk = this._thumbnails.slice( chunk );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Lazy-loads thumbnails.
|
|
You can call this method to load lazy thumbnails at run time
|
|
|
|
@param {Array|Number} index Index or array of indexes of thumbnails to be loaded
|
|
@param {Function} complete Callback that is called when all lazy thumbnails have been loaded
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
lazyLoad: function( index, complete ) {
|
|
|
|
var arr = index.constructor == Array ? index : [ index ],
|
|
self = this,
|
|
loaded = 0;
|
|
|
|
$.each( arr, function(i, ind) {
|
|
|
|
if ( ind > self._thumbnails.length - 1 ) {
|
|
return;
|
|
}
|
|
|
|
var thumb = self._thumbnails[ ind ],
|
|
data = thumb.data,
|
|
callback = function() {
|
|
if ( ++loaded == arr.length && typeof complete == 'function' ) {
|
|
complete.call( self );
|
|
}
|
|
},
|
|
thumbload = $( thumb.container ).data( 'thumbload' );
|
|
if ( thumb.video ) {
|
|
thumbload.call( self, thumb, callback );
|
|
} else {
|
|
thumb.load( data.src , function( thumb ) {
|
|
thumbload.call( self, thumb, callback );
|
|
});
|
|
}
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
/**
|
|
Lazy-loads thumbnails in chunks.
|
|
This method automatcally chops up the loading process of many thumbnails into chunks
|
|
|
|
@param {Number} size Size of each chunk to be loaded
|
|
@param {Number} [delay] Delay between each loads
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
lazyLoadChunks: function( size, delay ) {
|
|
|
|
var len = this.getDataLength(),
|
|
i = 0,
|
|
n = 0,
|
|
arr = [],
|
|
temp = [],
|
|
self = this;
|
|
|
|
delay = delay || 0;
|
|
|
|
for( ; i<len; i++ ) {
|
|
temp.push(i);
|
|
if ( ++n == size || i == len-1 ) {
|
|
arr.push( temp );
|
|
n = 0;
|
|
temp = [];
|
|
}
|
|
}
|
|
|
|
var init = function( wait ) {
|
|
var a = arr.shift();
|
|
if ( a ) {
|
|
window.setTimeout(function() {
|
|
self.lazyLoad(a, function() {
|
|
init( true );
|
|
});
|
|
}, ( delay && wait ) ? delay : 0 );
|
|
}
|
|
};
|
|
|
|
init( false );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
// the internal _run method should be called after loading data into galleria
|
|
// makes sure the gallery has proper measurements before postrun & ready
|
|
_run : function() {
|
|
|
|
var self = this;
|
|
|
|
self._createThumbnails();
|
|
|
|
// make sure we have a stageHeight && stageWidth
|
|
|
|
Utils.wait({
|
|
|
|
timeout: 10000,
|
|
|
|
until: function() {
|
|
|
|
// Opera crap
|
|
if ( Galleria.OPERA ) {
|
|
self.$( 'stage' ).css( 'display', 'inline-block' );
|
|
}
|
|
|
|
self._stageWidth = self.$( 'stage' ).width();
|
|
self._stageHeight = self.$( 'stage' ).height();
|
|
|
|
return( self._stageWidth &&
|
|
self._stageHeight > 50 ); // what is an acceptable height?
|
|
},
|
|
|
|
success: function() {
|
|
|
|
// save the instance
|
|
_galleries.push( self );
|
|
|
|
// postrun some stuff after the gallery is ready
|
|
|
|
// create the touch slider
|
|
if ( self._options.swipe ) {
|
|
|
|
var $images = self.$( 'images' ).width( self.getDataLength() * self._stageWidth );
|
|
$.each( new Array( self.getDataLength() ), function(i) {
|
|
|
|
var image = new Galleria.Picture(),
|
|
data = self.getData(i);
|
|
|
|
$( image.container ).css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: self._stageWidth*i
|
|
}).prepend( self._layers[i] = $( Utils.create('galleria-layer') ).css({
|
|
position: 'absolute',
|
|
top:0, left:0, right:0, bottom:0,
|
|
zIndex:2
|
|
})[0] ).appendTo( $images );
|
|
|
|
if( data.video ) {
|
|
_playIcon( image.container );
|
|
}
|
|
|
|
self._controls.slides.push(image);
|
|
|
|
var frame = new Galleria.Picture();
|
|
frame.isIframe = true;
|
|
|
|
$( frame.container ).attr('class', 'galleria-frame').css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
zIndex: 4,
|
|
background: '#000',
|
|
display: 'none'
|
|
}).appendTo( image.container );
|
|
|
|
self._controls.frames.push(frame);
|
|
});
|
|
|
|
self.finger.setup();
|
|
}
|
|
|
|
// show counter
|
|
Utils.show( self.get('counter') );
|
|
|
|
// bind carousel nav
|
|
if ( self._options.carousel ) {
|
|
self._carousel.bindControls();
|
|
}
|
|
|
|
// start autoplay
|
|
if ( self._options.autoplay ) {
|
|
|
|
self.pause();
|
|
|
|
if ( typeof self._options.autoplay === 'number' ) {
|
|
self._playtime = self._options.autoplay;
|
|
}
|
|
|
|
self._playing = true;
|
|
}
|
|
// if second load, just do the show and return
|
|
if ( self._firstrun ) {
|
|
|
|
if ( self._options.autoplay ) {
|
|
self.trigger( Galleria.PLAY );
|
|
}
|
|
|
|
if ( typeof self._options.show === 'number' ) {
|
|
self.show( self._options.show );
|
|
}
|
|
return;
|
|
}
|
|
|
|
self._firstrun = true;
|
|
|
|
// initialize the History plugin
|
|
if ( Galleria.History ) {
|
|
|
|
// bind the show method
|
|
Galleria.History.change(function( value ) {
|
|
|
|
// if ID is NaN, the user pressed back from the first image
|
|
// return to previous address
|
|
if ( isNaN( value ) ) {
|
|
window.history.go(-1);
|
|
|
|
// else show the image
|
|
} else {
|
|
self.show( value, undef, true );
|
|
}
|
|
});
|
|
}
|
|
|
|
self.trigger( Galleria.READY );
|
|
|
|
// call the theme init method
|
|
self.theme.init.call( self, self._options );
|
|
|
|
// Trigger Galleria.ready
|
|
$.each( Galleria.ready.callbacks, function(i ,fn) {
|
|
if ( typeof fn == 'function' ) {
|
|
fn.call( self, self._options );
|
|
}
|
|
});
|
|
|
|
// call the extend option
|
|
self._options.extend.call( self, self._options );
|
|
|
|
// show the initial image
|
|
// first test for permalinks in history
|
|
if ( /^[0-9]{1,4}$/.test( HASH ) && Galleria.History ) {
|
|
self.show( HASH, undef, true );
|
|
|
|
} else if( self._data[ self._options.show ] ) {
|
|
self.show( self._options.show );
|
|
}
|
|
|
|
// play trigger
|
|
if ( self._options.autoplay ) {
|
|
self.trigger( Galleria.PLAY );
|
|
}
|
|
},
|
|
|
|
error: function() {
|
|
Galleria.raise('Stage width or height is too small to show the gallery. Traced measures: width:' + self._stageWidth + 'px, height: ' + self._stageHeight + 'px.', true);
|
|
}
|
|
|
|
});
|
|
},
|
|
|
|
/**
|
|
Loads data into the gallery.
|
|
You can call this method on an existing gallery to reload the gallery with new data.
|
|
|
|
@param {Array|string} [source] Optional JSON array of data or selector of where to find data in the document.
|
|
Defaults to the Galleria target or dataSource option.
|
|
|
|
@param {string} [selector] Optional element selector of what elements to parse.
|
|
Defaults to 'img'.
|
|
|
|
@param {Function} [config] Optional function to modify the data extraction proceedure from the selector.
|
|
See the dataConfig option for more information.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
load : function( source, selector, config ) {
|
|
|
|
var self = this,
|
|
o = this._options;
|
|
|
|
// empty the data array
|
|
this._data = [];
|
|
|
|
// empty the thumbnails
|
|
this._thumbnails = [];
|
|
this.$('thumbnails').empty();
|
|
|
|
// shorten the arguments
|
|
if ( typeof selector === 'function' ) {
|
|
config = selector;
|
|
selector = null;
|
|
}
|
|
|
|
// use the source set by target
|
|
source = source || o.dataSource;
|
|
|
|
// use selector set by option
|
|
selector = selector || o.dataSelector;
|
|
|
|
// use the dataConfig set by option
|
|
config = config || o.dataConfig;
|
|
|
|
// if source is a true object, make it into an array
|
|
if( $.isPlainObject( source ) ) {
|
|
source = [source];
|
|
}
|
|
|
|
// check if the data is an array already
|
|
if ( $.isArray( source ) ) {
|
|
if ( this.validate( source ) ) {
|
|
this._data = source;
|
|
} else {
|
|
Galleria.raise( 'Load failed: JSON Array not valid.' );
|
|
}
|
|
} else {
|
|
|
|
// add .video and .iframe to the selector (1.2.7)
|
|
selector += ',.video,.iframe';
|
|
|
|
// loop through images and set data
|
|
$( source ).find( selector ).each( function( i, elem ) {
|
|
|
|
elem = $( elem );
|
|
var data = {},
|
|
parent = elem.parent(),
|
|
href = parent.attr( 'href' ),
|
|
rel = parent.attr( 'rel' );
|
|
|
|
if( href && ( elem[0].nodeName == 'IMG' || elem.hasClass('video') ) && _videoTest( href ) ) {
|
|
data.video = href;
|
|
} else if( href && elem.hasClass('iframe') ) {
|
|
data.iframe = href;
|
|
} else {
|
|
data.image = data.big = href;
|
|
}
|
|
|
|
if ( rel ) {
|
|
data.big = rel;
|
|
}
|
|
|
|
// alternative extraction from HTML5 data attribute, added in 1.2.7
|
|
$.each( 'big title description link layer image'.split(' '), function( i, val ) {
|
|
if ( elem.data(val) ) {
|
|
data[ val ] = elem.data(val).toString();
|
|
}
|
|
});
|
|
|
|
if ( !data.big ) {
|
|
data.big = data.image;
|
|
}
|
|
|
|
// mix default extractions with the hrefs and config
|
|
// and push it into the data array
|
|
self._data.push( $.extend({
|
|
|
|
title: elem.attr('title') || '',
|
|
thumb: elem.attr('src'),
|
|
image: elem.attr('src'),
|
|
big: elem.attr('src'),
|
|
description: elem.attr('alt') || '',
|
|
link: elem.attr('longdesc'),
|
|
original: elem.get(0) // saved as a reference
|
|
|
|
}, data, config( elem ) ) );
|
|
|
|
});
|
|
}
|
|
|
|
if ( typeof o.dataSort == 'function' ) {
|
|
protoArray.sort.call( this._data, o.dataSort );
|
|
} else if ( o.dataSort == 'random' ) {
|
|
this._data.sort( function() {
|
|
return M.round(M.random())-0.5;
|
|
});
|
|
}
|
|
|
|
// trigger the DATA event and return
|
|
if ( this.getDataLength() ) {
|
|
this._parseData( function() {
|
|
this.trigger( Galleria.DATA );
|
|
} );
|
|
}
|
|
return this;
|
|
|
|
},
|
|
|
|
// make sure the data works properly
|
|
_parseData : function( callback ) {
|
|
|
|
var self = this,
|
|
current,
|
|
ready = false,
|
|
onload = function() {
|
|
var complete = true;
|
|
$.each( self._data, function( i, data ) {
|
|
if ( data.loading ) {
|
|
complete = false;
|
|
return false;
|
|
}
|
|
});
|
|
if ( complete && !ready ) {
|
|
ready = true;
|
|
callback.call( self );
|
|
}
|
|
};
|
|
|
|
$.each( this._data, function( i, data ) {
|
|
|
|
current = self._data[ i ];
|
|
|
|
// copy image as thumb if no thumb exists
|
|
if ( 'thumb' in data === false ) {
|
|
current.thumb = data.image;
|
|
}
|
|
// copy image as big image if no biggie exists
|
|
if ( !data.big ) {
|
|
current.big = data.image;
|
|
}
|
|
// parse video
|
|
if ( 'video' in data ) {
|
|
var result = _videoTest( data.video );
|
|
|
|
if ( result ) {
|
|
current.iframe = new Video(result.provider, result.id ).embed() + (function() {
|
|
|
|
// add options
|
|
if ( typeof self._options[ result.provider ] == 'object' ) {
|
|
var str = '?', arr = [];
|
|
$.each( self._options[ result.provider ], function( key, val ) {
|
|
arr.push( key + '=' + val );
|
|
});
|
|
|
|
// small youtube specifics, perhaps move to _video later
|
|
if ( result.provider == 'youtube' ) {
|
|
arr = ['wmode=opaque'].concat(arr);
|
|
}
|
|
return str + arr.join('&');
|
|
}
|
|
return '';
|
|
}());
|
|
|
|
// pre-fetch video providers media
|
|
|
|
if( !current.thumb || !current.image ) {
|
|
$.each( ['thumb', 'image'], function( i, type ) {
|
|
if ( type == 'image' && !self._options.videoPoster ) {
|
|
current.image = undef;
|
|
return;
|
|
}
|
|
var video = new Video( result.provider, result.id );
|
|
if ( !current[ type ] ) {
|
|
current.loading = true;
|
|
video.getMedia( type, (function(current, type) {
|
|
return function(src) {
|
|
current[ type ] = src;
|
|
if ( type == 'image' && !current.big ) {
|
|
current.big = current.image;
|
|
}
|
|
delete current.loading;
|
|
onload();
|
|
};
|
|
}( current, type )));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
onload();
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Destroy the Galleria instance and recover the original content
|
|
|
|
@example this.destroy();
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
destroy : function() {
|
|
this.$( 'target' ).data( 'galleria', null );
|
|
this.$( 'container' ).off( 'galleria' );
|
|
this.get( 'target' ).innerHTML = this._original.html;
|
|
this.clearTimer();
|
|
Utils.removeFromArray( _instances, this );
|
|
Utils.removeFromArray( _galleries, this );
|
|
if ( Galleria._waiters.length ) {
|
|
$.each( Galleria._waiters, function( i, w ) {
|
|
if ( w ) window.clearTimeout( w );
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Adds and/or removes images from the gallery
|
|
Works just like Array.splice
|
|
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
|
|
|
|
@example this.splice( 2, 4 ); // removes 4 images after the second image
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
splice : function() {
|
|
var self = this,
|
|
args = Utils.array( arguments );
|
|
window.setTimeout(function() {
|
|
protoArray.splice.apply( self._data, args );
|
|
self._parseData( function() {
|
|
self._createThumbnails();
|
|
});
|
|
},2);
|
|
return self;
|
|
},
|
|
|
|
/**
|
|
Append images to the gallery
|
|
Works just like Array.push
|
|
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push
|
|
|
|
@example this.push({ image: 'image1.jpg' }); // appends the image to the gallery
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
push : function() {
|
|
var self = this,
|
|
args = Utils.array( arguments );
|
|
|
|
if ( args.length == 1 && args[0].constructor == Array ) {
|
|
args = args[0];
|
|
}
|
|
|
|
window.setTimeout(function() {
|
|
protoArray.push.apply( self._data, args );
|
|
self._parseData( function() {
|
|
self._createThumbnails( args );
|
|
});
|
|
}, 2);
|
|
return self;
|
|
},
|
|
|
|
_getActive : function() {
|
|
return this._controls.getActive();
|
|
},
|
|
|
|
validate : function( data ) {
|
|
// todo: validate a custom data array
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
Bind any event to Galleria
|
|
|
|
@param {string} type The Event type to listen for
|
|
@param {Function} fn The function to execute when the event is triggered
|
|
|
|
@example this.bind( 'image', function() { Galleria.log('image shown') });
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
bind : function(type, fn) {
|
|
|
|
// allow 'image' instead of Galleria.IMAGE
|
|
type = _patchEvent( type );
|
|
|
|
this.$( 'container' ).on( type, this.proxy(fn) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Unbind any event to Galleria
|
|
|
|
@param {string} type The Event type to forget
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
unbind : function(type) {
|
|
|
|
type = _patchEvent( type );
|
|
|
|
this.$( 'container' ).off( type );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Manually trigger a Galleria event
|
|
|
|
@param {string} type The Event to trigger
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
trigger : function( type ) {
|
|
|
|
type = typeof type === 'object' ?
|
|
$.extend( type, { scope: this } ) :
|
|
{ type: _patchEvent( type ), scope: this };
|
|
|
|
this.$( 'container' ).trigger( type );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Assign an "idle state" to any element.
|
|
The idle state will be applied after a certain amount of idle time
|
|
Useful to hide f.ex navigation when the gallery is inactive
|
|
|
|
@param {HTMLElement|string} elem The Dom node or selector to apply the idle state to
|
|
@param {Object} styles the CSS styles to apply when in idle mode
|
|
@param {Object} [from] the CSS styles to apply when in normal
|
|
@param {Boolean} [hide] set to true if you want to hide it first
|
|
|
|
@example addIdleState( this.get('image-nav'), { opacity: 0 });
|
|
@example addIdleState( '.galleria-image-nav', { top: -200 }, true);
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
addIdleState: function( elem, styles, from, hide ) {
|
|
this._idle.add.apply( this._idle, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Removes any idle state previously set using addIdleState()
|
|
|
|
@param {HTMLElement|string} elem The Dom node or selector to remove the idle state from.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
removeIdleState: function( elem ) {
|
|
this._idle.remove.apply( this._idle, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Force Galleria to enter idle mode.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
enterIdleMode: function() {
|
|
this._idle.hide();
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Force Galleria to exit idle mode.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
exitIdleMode: function() {
|
|
this._idle.showAll();
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Enter FullScreen mode
|
|
|
|
@param {Function} callback the function to be executed when the fullscreen mode is fully applied.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
enterFullscreen: function( callback ) {
|
|
this._fullscreen.enter.apply( this, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Exits FullScreen mode
|
|
|
|
@param {Function} callback the function to be executed when the fullscreen mode is fully applied.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
exitFullscreen: function( callback ) {
|
|
this._fullscreen.exit.apply( this, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Toggle FullScreen mode
|
|
|
|
@param {Function} callback the function to be executed when the fullscreen mode is fully applied or removed.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
toggleFullscreen: function( callback ) {
|
|
this._fullscreen[ this.isFullscreen() ? 'exit' : 'enter'].apply( this, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Adds a tooltip to any element.
|
|
You can also call this method with an object as argument with elemID:value pairs to apply tooltips to (see examples)
|
|
|
|
@param {HTMLElement} elem The DOM Node to attach the event to
|
|
@param {string|Function} value The tooltip message. Can also be a function that returns a string.
|
|
|
|
@example this.bindTooltip( this.get('thumbnails'), 'My thumbnails');
|
|
@example this.bindTooltip( this.get('thumbnails'), function() { return 'My thumbs' });
|
|
@example this.bindTooltip( { image_nav: 'Navigation' });
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
bindTooltip: function( elem, value ) {
|
|
this._tooltip.bind.apply( this._tooltip, Utils.array(arguments) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Note: this method is deprecated. Use refreshTooltip() instead.
|
|
|
|
Redefine a tooltip.
|
|
Use this if you want to re-apply a tooltip value to an already bound tooltip element.
|
|
|
|
@param {HTMLElement} elem The DOM Node to attach the event to
|
|
@param {string|Function} value The tooltip message. Can also be a function that returns a string.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
defineTooltip: function( elem, value ) {
|
|
this._tooltip.define.apply( this._tooltip, Utils.array(arguments) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Refresh a tooltip value.
|
|
Use this if you want to change the tooltip value at runtime, f.ex if you have a play/pause toggle.
|
|
|
|
@param {HTMLElement} elem The DOM Node that has a tooltip that should be refreshed
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
refreshTooltip: function( elem ) {
|
|
this._tooltip.show.apply( this._tooltip, Utils.array(arguments) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Open a pre-designed lightbox with the currently active image.
|
|
You can control some visuals using gallery options.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
openLightbox: function() {
|
|
this._lightbox.show.apply( this._lightbox, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Close the lightbox.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
closeLightbox: function() {
|
|
this._lightbox.hide.apply( this._lightbox, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Check if a variation exists
|
|
|
|
@returns {Boolean} If the variation has been applied
|
|
*/
|
|
|
|
hasVariation: function( variation ) {
|
|
return $.inArray( variation, this._options.variation.split(/\s+/) ) > -1;
|
|
},
|
|
|
|
/**
|
|
Get the currently active image element.
|
|
|
|
@returns {HTMLElement} The image element
|
|
*/
|
|
|
|
getActiveImage: function() {
|
|
var active = this._getActive();
|
|
return active ? active.image : undef;
|
|
},
|
|
|
|
/**
|
|
Get the currently active thumbnail element.
|
|
|
|
@returns {HTMLElement} The thumbnail element
|
|
*/
|
|
|
|
getActiveThumb: function() {
|
|
return this._thumbnails[ this._active ].image || undef;
|
|
},
|
|
|
|
/**
|
|
Get the mouse position relative to the gallery container
|
|
|
|
@param e The mouse event
|
|
|
|
@example
|
|
|
|
var gallery = this;
|
|
$(document).mousemove(function(e) {
|
|
console.log( gallery.getMousePosition(e).x );
|
|
});
|
|
|
|
@returns {Object} Object with x & y of the relative mouse postion
|
|
*/
|
|
|
|
getMousePosition : function(e) {
|
|
return {
|
|
x: e.pageX - this.$( 'container' ).offset().left,
|
|
y: e.pageY - this.$( 'container' ).offset().top
|
|
};
|
|
},
|
|
|
|
/**
|
|
Adds a panning effect to the image
|
|
|
|
@param [img] The optional image element. If not specified it takes the currently active image
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
addPan : function( img ) {
|
|
|
|
if ( this._options.imageCrop === false ) {
|
|
return;
|
|
}
|
|
|
|
img = $( img || this.getActiveImage() );
|
|
|
|
// define some variables and methods
|
|
var self = this,
|
|
x = img.width() / 2,
|
|
y = img.height() / 2,
|
|
destX = parseInt( img.css( 'left' ), 10 ),
|
|
destY = parseInt( img.css( 'top' ), 10 ),
|
|
curX = destX || 0,
|
|
curY = destY || 0,
|
|
distX = 0,
|
|
distY = 0,
|
|
active = false,
|
|
ts = Utils.timestamp(),
|
|
cache = 0,
|
|
move = 0,
|
|
|
|
// positions the image
|
|
position = function( dist, cur, pos ) {
|
|
if ( dist > 0 ) {
|
|
move = M.round( M.max( dist * -1, M.min( 0, cur ) ) );
|
|
if ( cache !== move ) {
|
|
|
|
cache = move;
|
|
|
|
if ( IE === 8 ) { // scroll is faster for IE
|
|
img.parent()[ 'scroll' + pos ]( move * -1 );
|
|
} else {
|
|
var css = {};
|
|
css[ pos.toLowerCase() ] = move;
|
|
img.css(css);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// calculates mouse position after 50ms
|
|
calculate = function(e) {
|
|
if (Utils.timestamp() - ts < 50) {
|
|
return;
|
|
}
|
|
active = true;
|
|
x = self.getMousePosition(e).x;
|
|
y = self.getMousePosition(e).y;
|
|
},
|
|
|
|
// the main loop to check
|
|
loop = function(e) {
|
|
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
distX = img.width() - self._stageWidth;
|
|
distY = img.height() - self._stageHeight;
|
|
destX = x / self._stageWidth * distX * -1;
|
|
destY = y / self._stageHeight * distY * -1;
|
|
curX += ( destX - curX ) / self._options.imagePanSmoothness;
|
|
curY += ( destY - curY ) / self._options.imagePanSmoothness;
|
|
|
|
position( distY, curY, 'Top' );
|
|
position( distX, curX, 'Left' );
|
|
|
|
};
|
|
|
|
// we need to use scroll in IE8 to speed things up
|
|
if ( IE === 8 ) {
|
|
|
|
img.parent().scrollTop( curY * -1 ).scrollLeft( curX * -1 );
|
|
img.css({
|
|
top: 0,
|
|
left: 0
|
|
});
|
|
|
|
}
|
|
|
|
// unbind and bind event
|
|
this.$( 'stage' ).off( 'mousemove', calculate ).on( 'mousemove', calculate );
|
|
|
|
// loop the loop
|
|
this.addTimer( 'pan' + self._id, loop, 50, true);
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Brings the scope into any callback
|
|
|
|
@param fn The callback to bring the scope into
|
|
@param [scope] Optional scope to bring
|
|
|
|
@example $('#fullscreen').click( this.proxy(function() { this.enterFullscreen(); }) )
|
|
|
|
@returns {Function} Return the callback with the gallery scope
|
|
*/
|
|
|
|
proxy : function( fn, scope ) {
|
|
if ( typeof fn !== 'function' ) {
|
|
return F;
|
|
}
|
|
scope = scope || this;
|
|
return function() {
|
|
return fn.apply( scope, Utils.array( arguments ) );
|
|
};
|
|
},
|
|
|
|
/**
|
|
Tells you the theme name of the gallery
|
|
|
|
@returns {String} theme name
|
|
*/
|
|
|
|
getThemeName : function() {
|
|
return this.theme.name;
|
|
},
|
|
|
|
/**
|
|
Removes the panning effect set by addPan()
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
removePan: function() {
|
|
|
|
// todo: doublecheck IE8
|
|
|
|
this.$( 'stage' ).off( 'mousemove' );
|
|
|
|
this.clearTimer( 'pan' + this._id );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Adds an element to the Galleria DOM array.
|
|
When you add an element here, you can access it using element ID in many API calls
|
|
|
|
@param {string} id The element ID you wish to use. You can add many elements by adding more arguments.
|
|
|
|
@example addElement('mybutton');
|
|
@example addElement('mybutton','mylink');
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
addElement : function( id ) {
|
|
|
|
var dom = this._dom;
|
|
|
|
$.each( Utils.array(arguments), function( i, blueprint ) {
|
|
dom[ blueprint ] = Utils.create( 'galleria-' + blueprint );
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Attach keyboard events to Galleria
|
|
|
|
@param {Object} map The map object of events.
|
|
Possible keys are 'UP', 'DOWN', 'LEFT', 'RIGHT', 'RETURN', 'ESCAPE', 'BACKSPACE', and 'SPACE'.
|
|
|
|
@example
|
|
|
|
this.attachKeyboard({
|
|
right: this.next,
|
|
left: this.prev,
|
|
up: function() {
|
|
console.log( 'up key pressed' )
|
|
}
|
|
});
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
attachKeyboard : function( map ) {
|
|
this._keyboard.attach.apply( this._keyboard, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Detach all keyboard events to Galleria
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
detachKeyboard : function() {
|
|
this._keyboard.detach.apply( this._keyboard, Utils.array( arguments ) );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Fast helper for appending galleria elements that you added using addElement()
|
|
|
|
@param {string} parentID The parent element ID where the element will be appended
|
|
@param {string} childID the element ID that should be appended
|
|
|
|
@example this.addElement('myElement');
|
|
this.appendChild( 'info', 'myElement' );
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
appendChild : function( parentID, childID ) {
|
|
this.$( parentID ).append( this.get( childID ) || childID );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Fast helper for prepending galleria elements that you added using addElement()
|
|
|
|
@param {string} parentID The parent element ID where the element will be prepended
|
|
@param {string} childID the element ID that should be prepended
|
|
|
|
@example
|
|
|
|
this.addElement('myElement');
|
|
this.prependChild( 'info', 'myElement' );
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
prependChild : function( parentID, childID ) {
|
|
this.$( parentID ).prepend( this.get( childID ) || childID );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Remove an element by blueprint
|
|
|
|
@param {string} elemID The element to be removed.
|
|
You can remove multiple elements by adding arguments.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
remove : function( elemID ) {
|
|
this.$( Utils.array( arguments ).join(',') ).remove();
|
|
return this;
|
|
},
|
|
|
|
// a fast helper for building dom structures
|
|
// leave this out of the API for now
|
|
|
|
append : function( data ) {
|
|
var i, j;
|
|
for( i in data ) {
|
|
if ( data.hasOwnProperty( i ) ) {
|
|
if ( data[i].constructor === Array ) {
|
|
for( j = 0; data[i][j]; j++ ) {
|
|
this.appendChild( i, data[i][j] );
|
|
}
|
|
} else {
|
|
this.appendChild( i, data[i] );
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// an internal helper for scaling according to options
|
|
_scaleImage : function( image, options ) {
|
|
|
|
image = image || this._controls.getActive();
|
|
|
|
// janpub (JH) fix:
|
|
// image might be unselected yet
|
|
// e.g. when external logics rescales the gallery on window resize events
|
|
if( !image ) {
|
|
return;
|
|
}
|
|
|
|
var complete,
|
|
|
|
scaleLayer = function( img ) {
|
|
$( img.container ).children(':first').css({
|
|
top: M.max(0, Utils.parseValue( img.image.style.top )),
|
|
left: M.max(0, Utils.parseValue( img.image.style.left )),
|
|
width: Utils.parseValue( img.image.width ),
|
|
height: Utils.parseValue( img.image.height )
|
|
});
|
|
};
|
|
|
|
options = $.extend({
|
|
width: this._stageWidth,
|
|
height: this._stageHeight,
|
|
crop: this._options.imageCrop,
|
|
max: this._options.maxScaleRatio,
|
|
min: this._options.minScaleRatio,
|
|
margin: this._options.imageMargin,
|
|
position: this._options.imagePosition,
|
|
iframelimit: this._options.maxVideoSize
|
|
}, options );
|
|
|
|
if ( this._options.layerFollow && this._options.imageCrop !== true ) {
|
|
|
|
if ( typeof options.complete == 'function' ) {
|
|
complete = options.complete;
|
|
options.complete = function() {
|
|
complete.call( image, image );
|
|
scaleLayer( image );
|
|
};
|
|
} else {
|
|
options.complete = scaleLayer;
|
|
}
|
|
|
|
} else {
|
|
$( image.container ).children(':first').css({ top: 0, left: 0 });
|
|
}
|
|
|
|
image.scale( options );
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Updates the carousel,
|
|
useful if you resize the gallery and want to re-check if the carousel nav is needed.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
updateCarousel : function() {
|
|
this._carousel.update();
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Resize the entire gallery container
|
|
|
|
@param {Object} [measures] Optional object with width/height specified
|
|
@param {Function} [complete] The callback to be called when the scaling is complete
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
resize : function( measures, complete ) {
|
|
|
|
if ( typeof measures == 'function' ) {
|
|
complete = measures;
|
|
measures = undef;
|
|
}
|
|
|
|
measures = $.extend( { width:0, height:0 }, measures );
|
|
|
|
var self = this,
|
|
$container = this.$( 'container' );
|
|
|
|
$.each( measures, function( m, val ) {
|
|
if ( !val ) {
|
|
$container[ m ]( 'auto' );
|
|
measures[ m ] = self._getWH()[ m ];
|
|
}
|
|
});
|
|
|
|
$.each( measures, function( m, val ) {
|
|
$container[ m ]( val );
|
|
});
|
|
|
|
return this.rescale( complete );
|
|
|
|
},
|
|
|
|
/**
|
|
Rescales the gallery
|
|
|
|
@param {number} width The target width
|
|
@param {number} height The target height
|
|
@param {Function} complete The callback to be called when the scaling is complete
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
rescale : function( width, height, complete ) {
|
|
|
|
var self = this;
|
|
|
|
// allow rescale(fn)
|
|
if ( typeof width === 'function' ) {
|
|
complete = width;
|
|
width = undef;
|
|
}
|
|
|
|
var scale = function() {
|
|
|
|
// set stagewidth
|
|
self._stageWidth = width || self.$( 'stage' ).width();
|
|
self._stageHeight = height || self.$( 'stage' ).height();
|
|
|
|
if ( self._options.swipe ) {
|
|
$.each( self._controls.slides, function(i, img) {
|
|
self._scaleImage( img );
|
|
$( img.container ).css('left', self._stageWidth * i);
|
|
});
|
|
self.$('images').css('width', self._stageWidth * self.getDataLength());
|
|
} else {
|
|
// scale the active image
|
|
self._scaleImage();
|
|
}
|
|
|
|
if ( self._options.carousel ) {
|
|
self.updateCarousel();
|
|
}
|
|
|
|
var frame = self._controls.frames[ self._controls.active ];
|
|
|
|
if (frame) {
|
|
self._controls.frames[ self._controls.active ].scale({
|
|
width: self._stageWidth,
|
|
height: self._stageHeight,
|
|
iframelimit: self._options.maxVideoSize
|
|
});
|
|
}
|
|
|
|
self.trigger( Galleria.RESCALE );
|
|
|
|
if ( typeof complete === 'function' ) {
|
|
complete.call( self );
|
|
}
|
|
};
|
|
|
|
scale.call( self );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Refreshes the gallery.
|
|
Useful if you change image options at runtime and want to apply the changes to the active image.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
refreshImage : function() {
|
|
this._scaleImage();
|
|
if ( this._options.imagePan ) {
|
|
this.addPan();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_preload: function() {
|
|
if ( this._options.preload ) {
|
|
var p, i,
|
|
n = this.getNext(),
|
|
ndata;
|
|
try {
|
|
for ( i = this._options.preload; i > 0; i-- ) {
|
|
p = new Galleria.Picture();
|
|
ndata = this.getData( n );
|
|
p.preload( this.isFullscreen() && ndata.big ? ndata.big : ndata.image );
|
|
n = this.getNext( n );
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
},
|
|
|
|
/**
|
|
Shows an image by index
|
|
|
|
@param {number|boolean} index The index to show
|
|
@param {Boolean} rewind A boolean that should be true if you want the transition to go back
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
show : function( index, rewind, _history ) {
|
|
|
|
var swipe = this._options.swipe;
|
|
|
|
// do nothing queue is long || index is false || queue is false and transition is in progress
|
|
if ( !swipe &&
|
|
( this._queue.length > 3 || index === false || ( !this._options.queue && this._queue.stalled ) ) ) {
|
|
return;
|
|
}
|
|
|
|
index = M.max( 0, M.min( parseInt( index, 10 ), this.getDataLength() - 1 ) );
|
|
|
|
rewind = typeof rewind !== 'undefined' ? !!rewind : index < this.getIndex();
|
|
|
|
_history = _history || false;
|
|
|
|
// do the history thing and return
|
|
if ( !_history && Galleria.History ) {
|
|
Galleria.History.set( index.toString() );
|
|
return;
|
|
}
|
|
|
|
if ( this.finger && index !== this._active ) {
|
|
this.finger.to = -( index*this.finger.width );
|
|
this.finger.index = index;
|
|
}
|
|
this._active = index;
|
|
|
|
// we do things a bit simpler in swipe:
|
|
if ( swipe ) {
|
|
|
|
var data = this.getData(index),
|
|
self = this;
|
|
if ( !data ) {
|
|
return;
|
|
}
|
|
|
|
var src = this.isFullscreen() && data.big ? data.big : ( data.image || data.iframe ),
|
|
image = this._controls.slides[index],
|
|
cached = image.isCached( src ),
|
|
thumb = this._thumbnails[ index ];
|
|
|
|
var evObj = {
|
|
cached: cached,
|
|
index: index,
|
|
rewind: rewind,
|
|
imageTarget: image.image,
|
|
thumbTarget: thumb.image,
|
|
galleriaData: data
|
|
};
|
|
|
|
this.trigger($.extend(evObj, {
|
|
type: Galleria.LOADSTART
|
|
}));
|
|
|
|
self.$('container').removeClass( 'videoplay' );
|
|
|
|
var complete = function() {
|
|
|
|
self._layers[index].innerHTML = self.getData().layer || '';
|
|
|
|
self.trigger($.extend(evObj, {
|
|
type: Galleria.LOADFINISH
|
|
}));
|
|
self._playCheck();
|
|
};
|
|
|
|
self._preload();
|
|
|
|
window.setTimeout(function() {
|
|
|
|
// load if not ready
|
|
if ( !image.ready || $(image.image).attr('src') != src ) {
|
|
if ( data.iframe && !data.image ) {
|
|
image.isIframe = true;
|
|
}
|
|
image.load(src, function(image) {
|
|
evObj.imageTarget = image.image;
|
|
self._scaleImage(image, complete).trigger($.extend(evObj, {
|
|
type: Galleria.IMAGE
|
|
}));
|
|
complete();
|
|
});
|
|
} else {
|
|
self.trigger($.extend(evObj, {
|
|
type: Galleria.IMAGE
|
|
}));
|
|
complete();
|
|
}
|
|
}, 100);
|
|
|
|
} else {
|
|
protoArray.push.call( this._queue, {
|
|
index : index,
|
|
rewind : rewind
|
|
});
|
|
if ( !this._queue.stalled ) {
|
|
this._show();
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// the internal _show method does the actual showing
|
|
_show : function() {
|
|
|
|
// shortcuts
|
|
var self = this,
|
|
queue = this._queue[ 0 ],
|
|
data = this.getData( queue.index );
|
|
|
|
if ( !data ) {
|
|
return;
|
|
}
|
|
|
|
var src = this.isFullscreen() && data.big ? data.big : ( data.image || data.iframe ),
|
|
active = this._controls.getActive(),
|
|
next = this._controls.getNext(),
|
|
cached = next.isCached( src ),
|
|
thumb = this._thumbnails[ queue.index ],
|
|
mousetrigger = function() {
|
|
$( next.image ).trigger( 'mouseup' );
|
|
};
|
|
|
|
self.$('container').toggleClass('iframe', !!data.isIframe).removeClass( 'videoplay' );
|
|
|
|
// to be fired when loading & transition is complete:
|
|
var complete = (function( data, next, active, queue, thumb ) {
|
|
|
|
return function() {
|
|
|
|
var win;
|
|
|
|
_transitions.active = false;
|
|
|
|
// optimize quality
|
|
Utils.toggleQuality( next.image, self._options.imageQuality );
|
|
|
|
// remove old layer
|
|
self._layers[ self._controls.active ].innerHTML = '';
|
|
|
|
// swap
|
|
$( active.container ).css({
|
|
zIndex: 0,
|
|
opacity: 0
|
|
}).show();
|
|
|
|
$( active.container ).find( 'iframe, .galleria-videoicon' ).remove();
|
|
$( self._controls.frames[ self._controls.active ].container ).hide();
|
|
|
|
$( next.container ).css({
|
|
zIndex: 1,
|
|
left: 0,
|
|
top: 0
|
|
}).show();
|
|
|
|
self._controls.swap();
|
|
|
|
// add pan according to option
|
|
if ( self._options.imagePan ) {
|
|
self.addPan( next.image );
|
|
}
|
|
|
|
// make the image clickable
|
|
// order of precedence: iframe, link, lightbox, clicknext
|
|
if ( ( data.iframe && data.image ) || data.link || self._options.lightbox || self._options.clicknext ) {
|
|
|
|
$( next.image ).css({
|
|
cursor: 'pointer'
|
|
}).on( 'mouseup', function( e ) {
|
|
|
|
// non-left click
|
|
if ( typeof e.which == 'number' && e.which > 1 ) {
|
|
return;
|
|
}
|
|
|
|
// iframe / video
|
|
if ( data.iframe ) {
|
|
|
|
if ( self.isPlaying() ) {
|
|
self.pause();
|
|
}
|
|
var frame = self._controls.frames[ self._controls.active ],
|
|
w = self._stageWidth,
|
|
h = self._stageHeight;
|
|
|
|
$( frame.container ).css({
|
|
width: w,
|
|
height: h,
|
|
opacity: 0
|
|
}).show().animate({
|
|
opacity: 1
|
|
}, 200);
|
|
|
|
window.setTimeout(function() {
|
|
frame.load( data.iframe + ( data.video ? '&autoplay=1' : '' ), {
|
|
width: w,
|
|
height: h
|
|
}, function( frame ) {
|
|
self.$( 'container' ).addClass( 'videoplay' );
|
|
frame.scale({
|
|
width: self._stageWidth,
|
|
height: self._stageHeight,
|
|
iframelimit: data.video ? self._options.maxVideoSize : undef
|
|
});
|
|
});
|
|
}, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
// clicknext
|
|
if ( self._options.clicknext && !Galleria.TOUCH ) {
|
|
if ( self._options.pauseOnInteraction ) {
|
|
self.pause();
|
|
}
|
|
self.next();
|
|
return;
|
|
}
|
|
|
|
// popup link
|
|
if ( data.link ) {
|
|
if ( self._options.popupLinks ) {
|
|
win = window.open( data.link, '_blank' );
|
|
} else {
|
|
window.location.href = data.link;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( self._options.lightbox ) {
|
|
self.openLightbox();
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
// check if we are playing
|
|
self._playCheck();
|
|
|
|
// trigger IMAGE event
|
|
self.trigger({
|
|
type: Galleria.IMAGE,
|
|
index: queue.index,
|
|
imageTarget: next.image,
|
|
thumbTarget: thumb.image,
|
|
galleriaData: data
|
|
});
|
|
|
|
// remove the queued image
|
|
protoArray.shift.call( self._queue );
|
|
|
|
// remove stalled
|
|
self._queue.stalled = false;
|
|
|
|
// if we still have images in the queue, show it
|
|
if ( self._queue.length ) {
|
|
self._show();
|
|
}
|
|
|
|
};
|
|
}( data, next, active, queue, thumb ));
|
|
|
|
// let the carousel follow
|
|
if ( this._options.carousel && this._options.carouselFollow ) {
|
|
this._carousel.follow( queue.index );
|
|
}
|
|
|
|
// preload images
|
|
self._preload();
|
|
|
|
// show the next image, just in case
|
|
Utils.show( next.container );
|
|
|
|
next.isIframe = data.iframe && !data.image;
|
|
|
|
// add active classes
|
|
$( self._thumbnails[ queue.index ].container )
|
|
.addClass( 'active' )
|
|
.siblings( '.active' )
|
|
.removeClass( 'active' );
|
|
|
|
// trigger the LOADSTART event
|
|
self.trigger( {
|
|
type: Galleria.LOADSTART,
|
|
cached: cached,
|
|
index: queue.index,
|
|
rewind: queue.rewind,
|
|
imageTarget: next.image,
|
|
thumbTarget: thumb.image,
|
|
galleriaData: data
|
|
});
|
|
|
|
// stall the queue
|
|
self._queue.stalled = true;
|
|
|
|
// begin loading the next image
|
|
next.load( src, function( next ) {
|
|
|
|
// add layer HTML
|
|
var layer = $( self._layers[ 1-self._controls.active ] ).html( data.layer || '' ).hide();
|
|
|
|
self._scaleImage( next, {
|
|
|
|
complete: function( next ) {
|
|
|
|
// toggle low quality for IE
|
|
if ( 'image' in active ) {
|
|
Utils.toggleQuality( active.image, false );
|
|
}
|
|
Utils.toggleQuality( next.image, false );
|
|
|
|
// remove the image panning, if applied
|
|
// TODO: rethink if this is necessary
|
|
self.removePan();
|
|
|
|
// set the captions and counter
|
|
self.setInfo( queue.index );
|
|
self.setCounter( queue.index );
|
|
|
|
// show the layer now
|
|
if ( data.layer ) {
|
|
layer.show();
|
|
// inherit click events set on image
|
|
if ( ( data.iframe && data.image ) || data.link || self._options.lightbox || self._options.clicknext ) {
|
|
layer.css( 'cursor', 'pointer' ).off( 'mouseup' ).mouseup( mousetrigger );
|
|
}
|
|
}
|
|
|
|
// add play icon
|
|
if( data.video && data.image ) {
|
|
_playIcon( next.container );
|
|
}
|
|
|
|
var transition = self._options.transition;
|
|
|
|
// can JavaScript loop through objects in order? yes.
|
|
$.each({
|
|
initial: active.image === null,
|
|
touch: Galleria.TOUCH,
|
|
fullscreen: self.isFullscreen()
|
|
}, function( type, arg ) {
|
|
if ( arg && self._options[ type + 'Transition' ] !== undef ) {
|
|
transition = self._options[ type + 'Transition' ];
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// validate the transition
|
|
if ( transition in _transitions.effects === false ) {
|
|
complete();
|
|
} else {
|
|
var params = {
|
|
prev: active.container,
|
|
next: next.container,
|
|
rewind: queue.rewind,
|
|
speed: self._options.transitionSpeed || 400
|
|
};
|
|
|
|
_transitions.active = true;
|
|
|
|
// call the transition function and send some stuff
|
|
_transitions.init.call( self, transition, params, complete );
|
|
|
|
}
|
|
|
|
// trigger the LOADFINISH event
|
|
self.trigger({
|
|
type: Galleria.LOADFINISH,
|
|
cached: cached,
|
|
index: queue.index,
|
|
rewind: queue.rewind,
|
|
imageTarget: next.image,
|
|
thumbTarget: self._thumbnails[ queue.index ].image,
|
|
galleriaData: self.getData( queue.index )
|
|
});
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
Gets the next index
|
|
|
|
@param {number} [base] Optional starting point
|
|
|
|
@returns {number} the next index, or the first if you are at the first (looping)
|
|
*/
|
|
|
|
getNext : function( base ) {
|
|
base = typeof base === 'number' ? base : this.getIndex();
|
|
return base === this.getDataLength() - 1 ? 0 : base + 1;
|
|
},
|
|
|
|
/**
|
|
Gets the previous index
|
|
|
|
@param {number} [base] Optional starting point
|
|
|
|
@returns {number} the previous index, or the last if you are at the first (looping)
|
|
*/
|
|
|
|
getPrev : function( base ) {
|
|
base = typeof base === 'number' ? base : this.getIndex();
|
|
return base === 0 ? this.getDataLength() - 1 : base - 1;
|
|
},
|
|
|
|
/**
|
|
Shows the next image in line
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
next : function() {
|
|
if ( this.getDataLength() > 1 ) {
|
|
this.show( this.getNext(), false );
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Shows the previous image in line
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
prev : function() {
|
|
if ( this.getDataLength() > 1 ) {
|
|
this.show( this.getPrev(), true );
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Retrieve a DOM element by element ID
|
|
|
|
@param {string} elemId The delement ID to fetch
|
|
|
|
@returns {HTMLElement} The elements DOM node or null if not found.
|
|
*/
|
|
|
|
get : function( elemId ) {
|
|
return elemId in this._dom ? this._dom[ elemId ] : null;
|
|
},
|
|
|
|
/**
|
|
Retrieve a data object
|
|
|
|
@param {number} index The data index to retrieve.
|
|
If no index specified it will take the currently active image
|
|
|
|
@returns {Object} The data object
|
|
*/
|
|
|
|
getData : function( index ) {
|
|
return index in this._data ?
|
|
this._data[ index ] : this._data[ this._active ];
|
|
},
|
|
|
|
/**
|
|
Retrieve the number of data items
|
|
|
|
@returns {number} The data length
|
|
*/
|
|
getDataLength : function() {
|
|
return this._data.length;
|
|
},
|
|
|
|
/**
|
|
Retrieve the currently active index
|
|
|
|
@returns {number|boolean} The active index or false if none found
|
|
*/
|
|
|
|
getIndex : function() {
|
|
return typeof this._active === 'number' ? this._active : false;
|
|
},
|
|
|
|
/**
|
|
Retrieve the stage height
|
|
|
|
@returns {number} The stage height
|
|
*/
|
|
|
|
getStageHeight : function() {
|
|
return this._stageHeight;
|
|
},
|
|
|
|
/**
|
|
Retrieve the stage width
|
|
|
|
@returns {number} The stage width
|
|
*/
|
|
|
|
getStageWidth : function() {
|
|
return this._stageWidth;
|
|
},
|
|
|
|
/**
|
|
Retrieve the option
|
|
|
|
@param {string} key The option key to retrieve. If no key specified it will return all options in an object.
|
|
|
|
@returns option or options
|
|
*/
|
|
|
|
getOptions : function( key ) {
|
|
return typeof key === 'undefined' ? this._options : this._options[ key ];
|
|
},
|
|
|
|
/**
|
|
Set options to the instance.
|
|
You can set options using a key & value argument or a single object argument (see examples)
|
|
|
|
@param {string} key The option key
|
|
@param {string} value the the options value
|
|
|
|
@example setOptions( 'autoplay', true )
|
|
@example setOptions({ autoplay: true });
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
setOptions : function( key, value ) {
|
|
if ( typeof key === 'object' ) {
|
|
$.extend( this._options, key );
|
|
} else {
|
|
this._options[ key ] = value;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Starts playing the slideshow
|
|
|
|
@param {number} delay Sets the slideshow interval in milliseconds.
|
|
If you set it once, you can just call play() and get the same interval the next time.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
play : function( delay ) {
|
|
|
|
this._playing = true;
|
|
|
|
this._playtime = delay || this._playtime;
|
|
|
|
this._playCheck();
|
|
|
|
this.trigger( Galleria.PLAY );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Stops the slideshow if currently playing
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
pause : function() {
|
|
|
|
this._playing = false;
|
|
|
|
this.trigger( Galleria.PAUSE );
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Toggle between play and pause events.
|
|
|
|
@param {number} delay Sets the slideshow interval in milliseconds.
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
playToggle : function( delay ) {
|
|
return ( this._playing ) ? this.pause() : this.play( delay );
|
|
},
|
|
|
|
/**
|
|
Checks if the gallery is currently playing
|
|
|
|
@returns {Boolean}
|
|
*/
|
|
|
|
isPlaying : function() {
|
|
return this._playing;
|
|
},
|
|
|
|
/**
|
|
Checks if the gallery is currently in fullscreen mode
|
|
|
|
@returns {Boolean}
|
|
*/
|
|
|
|
isFullscreen : function() {
|
|
return this._fullscreen.active;
|
|
},
|
|
|
|
_playCheck : function() {
|
|
var self = this,
|
|
played = 0,
|
|
interval = 20,
|
|
now = Utils.timestamp(),
|
|
timer_id = 'play' + this._id;
|
|
|
|
if ( this._playing ) {
|
|
|
|
this.clearTimer( timer_id );
|
|
|
|
var fn = function() {
|
|
|
|
played = Utils.timestamp() - now;
|
|
if ( played >= self._playtime && self._playing ) {
|
|
self.clearTimer( timer_id );
|
|
self.next();
|
|
return;
|
|
}
|
|
if ( self._playing ) {
|
|
|
|
// trigger the PROGRESS event
|
|
self.trigger({
|
|
type: Galleria.PROGRESS,
|
|
percent: M.ceil( played / self._playtime * 100 ),
|
|
seconds: M.floor( played / 1000 ),
|
|
milliseconds: played
|
|
});
|
|
|
|
self.addTimer( timer_id, fn, interval );
|
|
}
|
|
};
|
|
self.addTimer( timer_id, fn, interval );
|
|
}
|
|
},
|
|
|
|
/**
|
|
Modify the slideshow delay
|
|
|
|
@param {number} delay the number of milliseconds between slides,
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
setPlaytime: function( delay ) {
|
|
this._playtime = delay;
|
|
return this;
|
|
},
|
|
|
|
setIndex: function( val ) {
|
|
this._active = val;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Manually modify the counter
|
|
|
|
@param {number} [index] Optional data index to fectch,
|
|
if no index found it assumes the currently active index
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
setCounter: function( index ) {
|
|
|
|
if ( typeof index === 'number' ) {
|
|
index++;
|
|
} else if ( typeof index === 'undefined' ) {
|
|
index = this.getIndex()+1;
|
|
}
|
|
|
|
this.get( 'current' ).innerHTML = index;
|
|
|
|
if ( IE ) { // weird IE bug
|
|
|
|
var count = this.$( 'counter' ),
|
|
opacity = count.css( 'opacity' );
|
|
|
|
if ( parseInt( opacity, 10 ) === 1) {
|
|
Utils.removeAlpha( count[0] );
|
|
} else {
|
|
this.$( 'counter' ).css( 'opacity', opacity );
|
|
}
|
|
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Manually set captions
|
|
|
|
@param {number} [index] Optional data index to fectch and apply as caption,
|
|
if no index found it assumes the currently active index
|
|
|
|
@returns Instance
|
|
*/
|
|
|
|
setInfo : function( index ) {
|
|
|
|
var self = this,
|
|
data = this.getData( index );
|
|
|
|
$.each( ['title','description'], function( i, type ) {
|
|
|
|
var elem = self.$( 'info-' + type );
|
|
|
|
if ( !!data[type] ) {
|
|
elem[ data[ type ].length ? 'show' : 'hide' ]().html( data[ type ] );
|
|
} else {
|
|
elem.empty().hide();
|
|
}
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
Checks if the data contains any captions
|
|
|
|
@param {number} [index] Optional data index to fectch,
|
|
if no index found it assumes the currently active index.
|
|
|
|
@returns {boolean}
|
|
*/
|
|
|
|
hasInfo : function( index ) {
|
|
|
|
var check = 'title description'.split(' '),
|
|
i;
|
|
|
|
for ( i = 0; check[i]; i++ ) {
|
|
if ( !!this.getData( index )[ check[i] ] ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
},
|
|
|
|
jQuery : function( str ) {
|
|
|
|
var self = this,
|
|
ret = [];
|
|
|
|
$.each( str.split(','), function( i, elemId ) {
|
|
elemId = $.trim( elemId );
|
|
|
|
if ( self.get( elemId ) ) {
|
|
ret.push( elemId );
|
|
}
|
|
});
|
|
|
|
var jQ = $( self.get( ret.shift() ) );
|
|
|
|
$.each( ret, function( i, elemId ) {
|
|
jQ = jQ.add( self.get( elemId ) );
|
|
});
|
|
|
|
return jQ;
|
|
|
|
},
|
|
|
|
/**
|
|
Converts element IDs into a jQuery collection
|
|
You can call for multiple IDs separated with commas.
|
|
|
|
@param {string} str One or more element IDs (comma-separated)
|
|
|
|
@returns jQuery
|
|
|
|
@example this.$('info,container').hide();
|
|
*/
|
|
|
|
$ : function( str ) {
|
|
return this.jQuery.apply( this, Utils.array( arguments ) );
|
|
}
|
|
|
|
};
|
|
|
|
// End of Galleria prototype
|
|
|
|
// Add events as static variables
|
|
$.each( _events, function( i, ev ) {
|
|
|
|
// legacy events
|
|
var type = /_/.test( ev ) ? ev.replace( /_/g, '' ) : ev;
|
|
|
|
Galleria[ ev.toUpperCase() ] = 'galleria.'+type;
|
|
|
|
} );
|
|
|
|
$.extend( Galleria, {
|
|
|
|
// Browser helpers
|
|
IE9: IE === 9,
|
|
IE8: IE === 8,
|
|
IE7: IE === 7,
|
|
IE6: IE === 6,
|
|
IE: IE,
|
|
WEBKIT: /webkit/.test( NAV ),
|
|
CHROME: /chrome/.test( NAV ),
|
|
SAFARI: /safari/.test( NAV ) && !(/chrome/.test( NAV )),
|
|
QUIRK: ( IE && doc.compatMode && doc.compatMode === "BackCompat" ),
|
|
MAC: /mac/.test( navigator.platform.toLowerCase() ),
|
|
OPERA: !!window.opera,
|
|
IPHONE: /iphone/.test( NAV ),
|
|
IPAD: /ipad/.test( NAV ),
|
|
ANDROID: /android/.test( NAV ),
|
|
TOUCH: ('ontouchstart' in doc)
|
|
|
|
});
|
|
|
|
// Galleria static methods
|
|
|
|
/**
|
|
Adds a theme that you can use for your Gallery
|
|
|
|
@param {Object} theme Object that should contain all your theme settings.
|
|
<ul>
|
|
<li>name - name of the theme</li>
|
|
<li>author - name of the author</li>
|
|
<li>css - css file name (not path)</li>
|
|
<li>defaults - default options to apply, including theme-specific options</li>
|
|
<li>init - the init function</li>
|
|
</ul>
|
|
|
|
@returns {Object} theme
|
|
*/
|
|
|
|
Galleria.addTheme = function( theme ) {
|
|
|
|
// make sure we have a name
|
|
if ( !theme.name ) {
|
|
Galleria.raise('No theme name specified');
|
|
}
|
|
|
|
if ( typeof theme.defaults !== 'object' ) {
|
|
theme.defaults = {};
|
|
} else {
|
|
theme.defaults = _legacyOptions( theme.defaults );
|
|
}
|
|
|
|
var css = false,
|
|
reg, reg2;
|
|
|
|
if ( typeof theme.css === 'string' ) {
|
|
|
|
// look for manually added CSS
|
|
$('link').each(function( i, link ) {
|
|
reg = new RegExp( theme.css );
|
|
if ( reg.test( link.href ) ) {
|
|
|
|
// we found the css
|
|
css = true;
|
|
|
|
// the themeload trigger
|
|
_themeLoad( theme );
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// else look for the absolute path and load the CSS dynamic
|
|
if ( !css ) {
|
|
|
|
|
|
$(function() {
|
|
// Try to determine the css-path from the theme script.
|
|
// In IE8/9, the script-dom-element seems to be not present
|
|
// at once, if galleria itself is inserted into the dom
|
|
// dynamically. We therefore try multiple times before raising
|
|
// an error.
|
|
var retryCount = 0;
|
|
var tryLoadCss = function() {
|
|
$('script').each(function (i, script) {
|
|
// look for the theme script
|
|
reg = new RegExp('galleria\\.' + theme.name.toLowerCase() + '\\.');
|
|
reg2 = new RegExp('galleria\\.io\\/theme\\/' + theme.name.toLowerCase() + '\\/(\\d*\\.*)?(\\d*\\.*)?(\\d*\\/)?js');
|
|
if (reg.test(script.src) || reg2.test(script.src)) {
|
|
// we have a match
|
|
css = script.src.replace(/[^\/]*$/, '') + theme.css;
|
|
|
|
window.setTimeout(function () {
|
|
Utils.loadCSS(css, 'galleria-theme-'+theme.name, function () {
|
|
|
|
// run galleries with this theme
|
|
_themeLoad(theme);
|
|
|
|
});
|
|
}, 1);
|
|
}
|
|
});
|
|
if (!css) {
|
|
if (retryCount++ > 5) {
|
|
Galleria.raise('No theme CSS loaded');
|
|
} else {
|
|
window.setTimeout(tryLoadCss, 500);
|
|
}
|
|
}
|
|
};
|
|
tryLoadCss();
|
|
});
|
|
}
|
|
|
|
} else {
|
|
|
|
// pass
|
|
_themeLoad( theme );
|
|
}
|
|
return theme;
|
|
};
|
|
|
|
/**
|
|
loadTheme loads a theme js file and attaches a load event to Galleria
|
|
|
|
@param {string} src The relative path to the theme source file
|
|
|
|
@param {Object} [options] Optional options you want to apply
|
|
|
|
@returns Galleria
|
|
*/
|
|
|
|
Galleria.loadTheme = function( src, options ) {
|
|
|
|
// Don't load if theme is already loaded
|
|
if( $('script').filter(function() { return $(this).attr('src') == src; }).length ) {
|
|
return;
|
|
}
|
|
|
|
var loaded = false,
|
|
err;
|
|
|
|
// start listening for the timeout onload
|
|
$( window ).load( function() {
|
|
if ( !loaded ) {
|
|
// give it another 20 seconds
|
|
err = window.setTimeout(function() {
|
|
if ( !loaded ) {
|
|
Galleria.raise( "Galleria had problems loading theme at " + src + ". Please check theme path or load manually.", true );
|
|
}
|
|
}, 20000);
|
|
}
|
|
});
|
|
|
|
// load the theme
|
|
Utils.loadScript( src, function() {
|
|
loaded = true;
|
|
window.clearTimeout( err );
|
|
});
|
|
|
|
return Galleria;
|
|
};
|
|
|
|
/**
|
|
Retrieves a Galleria instance.
|
|
|
|
@param {number} [index] Optional index to retrieve.
|
|
If no index is supplied, the method will return all instances in an array.
|
|
|
|
@returns Instance or Array of instances
|
|
*/
|
|
|
|
Galleria.get = function( index ) {
|
|
if ( !!_instances[ index ] ) {
|
|
return _instances[ index ];
|
|
} else if ( typeof index !== 'number' ) {
|
|
return _instances;
|
|
} else {
|
|
Galleria.raise('Gallery index ' + index + ' not found');
|
|
}
|
|
};
|
|
|
|
/**
|
|
|
|
Configure Galleria options via a static function.
|
|
The options will be applied to all instances
|
|
|
|
@param {string|object} key The options to apply or a key
|
|
|
|
@param [value] If key is a string, this is the value
|
|
|
|
@returns Galleria
|
|
|
|
*/
|
|
|
|
Galleria.configure = function( key, value ) {
|
|
|
|
var opts = {};
|
|
|
|
if( typeof key == 'string' && value ) {
|
|
opts[key] = value;
|
|
key = opts;
|
|
} else {
|
|
$.extend( opts, key );
|
|
}
|
|
|
|
Galleria.configure.options = opts;
|
|
|
|
$.each( Galleria.get(), function(i, instance) {
|
|
instance.setOptions( opts );
|
|
});
|
|
|
|
return Galleria;
|
|
};
|
|
|
|
Galleria.configure.options = {};
|
|
|
|
/**
|
|
|
|
Bind a Galleria event to the gallery
|
|
|
|
@param {string} type A string representing the galleria event
|
|
|
|
@param {function} callback The function that should run when the event is triggered
|
|
|
|
@returns Galleria
|
|
|
|
*/
|
|
|
|
Galleria.on = function( type, callback ) {
|
|
if ( !type ) {
|
|
return;
|
|
}
|
|
|
|
callback = callback || F;
|
|
|
|
// hash the bind
|
|
var hash = type + callback.toString().replace(/\s/g,'') + Utils.timestamp();
|
|
|
|
// for existing instances
|
|
$.each( Galleria.get(), function(i, instance) {
|
|
instance._binds.push( hash );
|
|
instance.bind( type, callback );
|
|
});
|
|
|
|
// for future instances
|
|
Galleria.on.binds.push({
|
|
type: type,
|
|
callback: callback,
|
|
hash: hash
|
|
});
|
|
|
|
return Galleria;
|
|
};
|
|
|
|
Galleria.on.binds = [];
|
|
|
|
/**
|
|
|
|
Run Galleria
|
|
Alias for $(selector).galleria(options)
|
|
|
|
@param {string} selector A selector of element(s) to intialize galleria to
|
|
|
|
@param {object} options The options to apply
|
|
|
|
@returns Galleria
|
|
|
|
*/
|
|
|
|
Galleria.run = function( selector, options ) {
|
|
if ( $.isFunction( options ) ) {
|
|
options = { extend: options };
|
|
}
|
|
$( selector || '#galleria' ).galleria( options );
|
|
return Galleria;
|
|
};
|
|
|
|
/**
|
|
Creates a transition to be used in your gallery
|
|
|
|
@param {string} name The name of the transition that you will use as an option
|
|
|
|
@param {Function} fn The function to be executed in the transition.
|
|
The function contains two arguments, params and complete.
|
|
Use the params Object to integrate the transition, and then call complete when you are done.
|
|
|
|
@returns Galleria
|
|
|
|
*/
|
|
|
|
Galleria.addTransition = function( name, fn ) {
|
|
_transitions.effects[name] = fn;
|
|
return Galleria;
|
|
};
|
|
|
|
/**
|
|
The Galleria utilites
|
|
*/
|
|
|
|
Galleria.utils = Utils;
|
|
|
|
/**
|
|
A helper metod for cross-browser logging.
|
|
It uses the console log if available otherwise it falls back to alert
|
|
|
|
@example Galleria.log("hello", document.body, [1,2,3]);
|
|
*/
|
|
|
|
Galleria.log = function() {
|
|
var args = Utils.array( arguments );
|
|
if( 'console' in window && 'log' in window.console ) {
|
|
try {
|
|
return window.console.log.apply( window.console, args );
|
|
} catch( e ) {
|
|
$.each( args, function() {
|
|
window.console.log(this);
|
|
});
|
|
}
|
|
} else {
|
|
return window.alert( args.join('<br>') );
|
|
}
|
|
};
|
|
|
|
/**
|
|
A ready method for adding callbacks when a gallery is ready
|
|
Each method is call before the extend option for every instance
|
|
|
|
@param {function} callback The function to call
|
|
|
|
@returns Galleria
|
|
*/
|
|
|
|
Galleria.ready = function( fn ) {
|
|
if ( typeof fn != 'function' ) {
|
|
return Galleria;
|
|
}
|
|
$.each( _galleries, function( i, gallery ) {
|
|
fn.call( gallery, gallery._options );
|
|
});
|
|
Galleria.ready.callbacks.push( fn );
|
|
return Galleria;
|
|
};
|
|
|
|
Galleria.ready.callbacks = [];
|
|
|
|
/**
|
|
Method for raising errors
|
|
|
|
@param {string} msg The message to throw
|
|
|
|
@param {boolean} [fatal] Set this to true to override debug settings and display a fatal error
|
|
*/
|
|
|
|
Galleria.raise = function( msg, fatal ) {
|
|
|
|
var type = fatal ? 'Fatal error' : 'Error',
|
|
|
|
css = {
|
|
color: '#fff',
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
zIndex: 100000
|
|
},
|
|
|
|
echo = function( msg ) {
|
|
|
|
var html = '<div style="padding:4px;margin:0 0 2px;background:#' +
|
|
( fatal ? '811' : '222' ) + ';">' +
|
|
( fatal ? '<strong>' + type + ': </strong>' : '' ) +
|
|
msg + '</div>';
|
|
|
|
$.each( _instances, function() {
|
|
|
|
var cont = this.$( 'errors' ),
|
|
target = this.$( 'target' );
|
|
|
|
if ( !cont.length ) {
|
|
|
|
target.css( 'position', 'relative' );
|
|
|
|
cont = this.addElement( 'errors' ).appendChild( 'target', 'errors' ).$( 'errors' ).css(css);
|
|
}
|
|
cont.append( html );
|
|
|
|
});
|
|
|
|
if ( !_instances.length ) {
|
|
$('<div>').css( $.extend( css, { position: 'fixed' } ) ).append( html ).appendTo( DOM().body );
|
|
}
|
|
};
|
|
|
|
// if debug is on, display errors and throw exception if fatal
|
|
if ( DEBUG ) {
|
|
echo( msg );
|
|
if ( fatal ) {
|
|
throw new Error(type + ': ' + msg);
|
|
}
|
|
|
|
// else just echo a silent generic error if fatal
|
|
} else if ( fatal ) {
|
|
if ( _hasError ) {
|
|
return;
|
|
}
|
|
_hasError = true;
|
|
fatal = false;
|
|
echo( 'Gallery could not load.' );
|
|
}
|
|
};
|
|
|
|
// Add the version
|
|
Galleria.version = VERSION;
|
|
|
|
Galleria.getLoadedThemes = function() {
|
|
return $.map(_loadedThemes, function(theme) {
|
|
return theme.name;
|
|
});
|
|
};
|
|
|
|
/**
|
|
A method for checking what version of Galleria the user has installed and throws a readable error if the user needs to upgrade.
|
|
Useful when building plugins that requires a certain version to function.
|
|
|
|
@param {number} version The minimum version required
|
|
|
|
@param {string} [msg] Optional message to display. If not specified, Galleria will throw a generic error.
|
|
|
|
@returns Galleria
|
|
*/
|
|
|
|
Galleria.requires = function( version, msg ) {
|
|
msg = msg || 'You need to upgrade Galleria to version ' + version + ' to use one or more components.';
|
|
if ( Galleria.version < version ) {
|
|
Galleria.raise(msg, true);
|
|
}
|
|
return Galleria;
|
|
};
|
|
|
|
/**
|
|
Adds preload, cache, scale and crop functionality
|
|
|
|
@constructor
|
|
|
|
@requires jQuery
|
|
|
|
@param {number} [id] Optional id to keep track of instances
|
|
*/
|
|
|
|
Galleria.Picture = function( id ) {
|
|
|
|
// save the id
|
|
this.id = id || null;
|
|
|
|
// the image should be null until loaded
|
|
this.image = null;
|
|
|
|
// Create a new container
|
|
this.container = Utils.create('galleria-image');
|
|
|
|
// add container styles
|
|
$( this.container ).css({
|
|
overflow: 'hidden',
|
|
position: 'relative' // for IE Standards mode
|
|
});
|
|
|
|
// saves the original measurements
|
|
this.original = {
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
|
|
// flag when the image is ready
|
|
this.ready = false;
|
|
|
|
// flag for iframe Picture
|
|
this.isIframe = false;
|
|
|
|
};
|
|
|
|
Galleria.Picture.prototype = {
|
|
|
|
// the inherited cache object
|
|
cache: {},
|
|
|
|
// show the image on stage
|
|
show: function() {
|
|
Utils.show( this.image );
|
|
},
|
|
|
|
// hide the image
|
|
hide: function() {
|
|
Utils.moveOut( this.image );
|
|
},
|
|
|
|
clear: function() {
|
|
this.image = null;
|
|
},
|
|
|
|
/**
|
|
Checks if an image is in cache
|
|
|
|
@param {string} src The image source path, ex '/path/to/img.jpg'
|
|
|
|
@returns {boolean}
|
|
*/
|
|
|
|
isCached: function( src ) {
|
|
return !!this.cache[src];
|
|
},
|
|
|
|
/**
|
|
Preloads an image into the cache
|
|
|
|
@param {string} src The image source path, ex '/path/to/img.jpg'
|
|
|
|
@returns Galleria.Picture
|
|
*/
|
|
|
|
preload: function( src ) {
|
|
$( new Image() ).load((function(src, cache) {
|
|
return function() {
|
|
cache[ src ] = src;
|
|
};
|
|
}( src, this.cache ))).attr( 'src', src );
|
|
},
|
|
|
|
/**
|
|
Loads an image and call the callback when ready.
|
|
Will also add the image to cache.
|
|
|
|
@param {string} src The image source path, ex '/path/to/img.jpg'
|
|
@param {Object} [size] The forced size of the image, defined as an object { width: xx, height:xx }
|
|
@param {Function} callback The function to be executed when the image is loaded & scaled
|
|
|
|
@returns The image container (jQuery object)
|
|
*/
|
|
|
|
load: function(src, size, callback) {
|
|
|
|
if ( typeof size == 'function' ) {
|
|
callback = size;
|
|
size = null;
|
|
}
|
|
|
|
if( this.isIframe ) {
|
|
var id = 'if'+new Date().getTime();
|
|
|
|
var iframe = this.image = $('<iframe>', {
|
|
src: src,
|
|
frameborder: 0,
|
|
id: id,
|
|
allowfullscreen: true,
|
|
css: { visibility: 'hidden' }
|
|
})[0];
|
|
|
|
if ( size ) {
|
|
$( iframe ).css( size );
|
|
}
|
|
|
|
$( this.container ).find( 'iframe,img' ).remove();
|
|
|
|
this.container.appendChild( this.image );
|
|
|
|
$('#'+id).load( (function( self, callback ) {
|
|
return function() {
|
|
window.setTimeout(function() {
|
|
$( self.image ).css( 'visibility', 'visible' );
|
|
if( typeof callback == 'function' ) {
|
|
callback.call( self, self );
|
|
}
|
|
}, 10);
|
|
};
|
|
}( this, callback )));
|
|
|
|
return this.container;
|
|
}
|
|
|
|
this.image = new Image();
|
|
|
|
// IE8 opacity inherit bug
|
|
if ( Galleria.IE8 ) {
|
|
$( this.image ).css( 'filter', 'inherit' );
|
|
}
|
|
|
|
// FF shaking images bug:
|
|
// http://support.galleria.io/discussions/problems/12245-shaking-photos
|
|
if ( !Galleria.IE && !Galleria.CHROME && !Galleria.SAFARI ) {
|
|
$( this.image ).css( 'image-rendering', 'optimizequality' );
|
|
}
|
|
|
|
var reload = false,
|
|
resort = false,
|
|
|
|
// some jquery cache
|
|
$container = $( this.container ),
|
|
$image = $( this.image ),
|
|
|
|
onerror = function() {
|
|
if ( !reload ) {
|
|
reload = true;
|
|
// reload the image with a timestamp
|
|
window.setTimeout((function(image, src) {
|
|
return function() {
|
|
image.attr('src', src + (src.indexOf('?') > -1 ? '&' : '?') + Utils.timestamp() );
|
|
};
|
|
}( $(this), src )), 50);
|
|
} else {
|
|
// apply the dummy image if it exists
|
|
if ( DUMMY ) {
|
|
$( this ).attr( 'src', DUMMY );
|
|
} else {
|
|
Galleria.raise('Image not found: ' + src);
|
|
}
|
|
}
|
|
},
|
|
|
|
// the onload method
|
|
onload = (function( self, callback, src ) {
|
|
|
|
return function() {
|
|
|
|
var complete = function() {
|
|
|
|
$( this ).off( 'load' );
|
|
|
|
// save the original size
|
|
self.original = size || {
|
|
height: this.height,
|
|
width: this.width
|
|
};
|
|
|
|
// translate3d if needed
|
|
if ( Galleria.HAS3D ) {
|
|
this.style.MozTransform = this.style.webkitTransform = 'translate3d(0,0,0)';
|
|
}
|
|
|
|
$container.append( this );
|
|
|
|
self.cache[ src ] = src; // will override old cache
|
|
|
|
if (typeof callback == 'function' ) {
|
|
window.setTimeout(function() {
|
|
callback.call( self, self );
|
|
},1);
|
|
}
|
|
};
|
|
|
|
// Delay the callback to "fix" the Adblock Bug
|
|
// http://code.google.com/p/adblockforchrome/issues/detail?id=3701
|
|
if ( ( !this.width || !this.height ) ) {
|
|
(function( img ) {
|
|
Utils.wait({
|
|
until: function() {
|
|
return img.width && img.height;
|
|
},
|
|
success: function() {
|
|
complete.call( img );
|
|
},
|
|
error: function() {
|
|
if ( !resort ) {
|
|
$(new Image()).load( onload ).attr( 'src', img.src );
|
|
resort = true;
|
|
} else {
|
|
Galleria.raise('Could not extract width/height from image: ' + img.src +
|
|
'. Traced measures: width:' + img.width + 'px, height: ' + img.height + 'px.');
|
|
}
|
|
},
|
|
timeout: 100
|
|
});
|
|
}( this ));
|
|
} else {
|
|
complete.call( this );
|
|
}
|
|
};
|
|
}( this, callback, src ));
|
|
|
|
// remove any previous images
|
|
$container.find( 'iframe,img' ).remove();
|
|
|
|
// append the image
|
|
$image.css( 'display', 'block');
|
|
|
|
// hide it for now
|
|
Utils.hide( this.image );
|
|
|
|
// remove any max/min scaling
|
|
$.each('minWidth minHeight maxWidth maxHeight'.split(' '), function(i, prop) {
|
|
$image.css(prop, (/min/.test(prop) ? '0' : 'none'));
|
|
});
|
|
|
|
// begin load and insert in cache when done
|
|
$image.load( onload ).on( 'error', onerror ).attr( 'src', src );
|
|
|
|
// return the container
|
|
return this.container;
|
|
},
|
|
|
|
/**
|
|
Scales and crops the image
|
|
|
|
@param {Object} options The method takes an object with a number of options:
|
|
|
|
<ul>
|
|
<li>width - width of the container</li>
|
|
<li>height - height of the container</li>
|
|
<li>min - minimum scale ratio</li>
|
|
<li>max - maximum scale ratio</li>
|
|
<li>margin - distance in pixels from the image border to the container</li>
|
|
<li>complete - a callback that fires when scaling is complete</li>
|
|
<li>position - positions the image, works like the css background-image property.</li>
|
|
<li>crop - defines how to crop. Can be true, false, 'width' or 'height'</li>
|
|
<li>canvas - set to true to try a canvas-based rescale</li>
|
|
</ul>
|
|
|
|
@returns The image container object (jQuery)
|
|
*/
|
|
|
|
scale: function( options ) {
|
|
|
|
var self = this;
|
|
|
|
// extend some defaults
|
|
options = $.extend({
|
|
width: 0,
|
|
height: 0,
|
|
min: undef,
|
|
max: undef,
|
|
margin: 0,
|
|
complete: F,
|
|
position: 'center',
|
|
crop: false,
|
|
canvas: false,
|
|
iframelimit: undef
|
|
}, options);
|
|
|
|
if( this.isIframe ) {
|
|
|
|
var cw = options.width,
|
|
ch = options.height,
|
|
nw, nh;
|
|
if ( options.iframelimit ) {
|
|
var r = M.min( options.iframelimit/cw, options.iframelimit/ch );
|
|
if ( r < 1 ) {
|
|
nw = cw * r;
|
|
nh = ch * r;
|
|
|
|
$( this.image ).css({
|
|
top: ch/2-nh/2,
|
|
left: cw/2-nw/2,
|
|
position: 'absolute'
|
|
});
|
|
} else {
|
|
$( this.image ).css({
|
|
top: 0,
|
|
left: 0
|
|
});
|
|
}
|
|
}
|
|
$( this.image ).width( nw || cw ).height( nh || ch ).removeAttr( 'width' ).removeAttr( 'height' );
|
|
$( this.container ).width( cw ).height( ch );
|
|
options.complete.call(self, self);
|
|
try {
|
|
if( this.image.contentWindow ) {
|
|
$( this.image.contentWindow ).trigger('resize');
|
|
}
|
|
} catch(e) {}
|
|
return this.container;
|
|
|
|
}
|
|
|
|
// return the element if no image found
|
|
if (!this.image) {
|
|
return this.container;
|
|
}
|
|
|
|
// store locale variables
|
|
var width,
|
|
height,
|
|
$container = $( self.container ),
|
|
data;
|
|
|
|
// wait for the width/height
|
|
Utils.wait({
|
|
until: function() {
|
|
width = options.width ||
|
|
$container.width() ||
|
|
Utils.parseValue( $container.css('width') );
|
|
|
|
height = options.height ||
|
|
$container.height() ||
|
|
Utils.parseValue( $container.css('height') );
|
|
|
|
return width && height;
|
|
},
|
|
success: function() {
|
|
|
|
// calculate some cropping
|
|
var newWidth = ( width - options.margin * 2 ) / self.original.width,
|
|
newHeight = ( height - options.margin * 2 ) / self.original.height,
|
|
min = M.min( newWidth, newHeight ),
|
|
max = M.max( newWidth, newHeight ),
|
|
cropMap = {
|
|
'true' : max,
|
|
'width' : newWidth,
|
|
'height': newHeight,
|
|
'false' : min,
|
|
'landscape': self.original.width > self.original.height ? max : min,
|
|
'portrait': self.original.width < self.original.height ? max : min
|
|
},
|
|
ratio = cropMap[ options.crop.toString() ],
|
|
canvasKey = '';
|
|
|
|
// allow maxScaleRatio
|
|
if ( options.max ) {
|
|
ratio = M.min( options.max, ratio );
|
|
}
|
|
|
|
// allow minScaleRatio
|
|
if ( options.min ) {
|
|
ratio = M.max( options.min, ratio );
|
|
}
|
|
|
|
$.each( ['width','height'], function( i, m ) {
|
|
$( self.image )[ m ]( self[ m ] = self.image[ m ] = M.round( self.original[ m ] * ratio ) );
|
|
});
|
|
|
|
$( self.container ).width( width ).height( height );
|
|
|
|
if ( options.canvas && _canvas ) {
|
|
|
|
_canvas.elem.width = self.width;
|
|
_canvas.elem.height = self.height;
|
|
|
|
canvasKey = self.image.src + ':' + self.width + 'x' + self.height;
|
|
|
|
self.image.src = _canvas.cache[ canvasKey ] || (function( key ) {
|
|
|
|
_canvas.context.drawImage(self.image, 0, 0, self.original.width*ratio, self.original.height*ratio);
|
|
|
|
try {
|
|
|
|
data = _canvas.elem.toDataURL();
|
|
_canvas.length += data.length;
|
|
_canvas.cache[ key ] = data;
|
|
return data;
|
|
|
|
} catch( e ) {
|
|
return self.image.src;
|
|
}
|
|
|
|
}( canvasKey ) );
|
|
|
|
}
|
|
|
|
// calculate image_position
|
|
var pos = {},
|
|
mix = {},
|
|
getPosition = function(value, measure, margin) {
|
|
var result = 0;
|
|
if (/\%/.test(value)) {
|
|
var flt = parseInt( value, 10 ) / 100,
|
|
m = self.image[ measure ] || $( self.image )[ measure ]();
|
|
|
|
result = M.ceil( m * -1 * flt + margin * flt );
|
|
} else {
|
|
result = Utils.parseValue( value );
|
|
}
|
|
return result;
|
|
},
|
|
positionMap = {
|
|
'top': { top: 0 },
|
|
'left': { left: 0 },
|
|
'right': { left: '100%' },
|
|
'bottom': { top: '100%' }
|
|
};
|
|
|
|
$.each( options.position.toLowerCase().split(' '), function( i, value ) {
|
|
if ( value === 'center' ) {
|
|
value = '50%';
|
|
}
|
|
pos[i ? 'top' : 'left'] = value;
|
|
});
|
|
|
|
$.each( pos, function( i, value ) {
|
|
if ( positionMap.hasOwnProperty( value ) ) {
|
|
$.extend( mix, positionMap[ value ] );
|
|
}
|
|
});
|
|
|
|
pos = pos.top ? $.extend( pos, mix ) : mix;
|
|
|
|
pos = $.extend({
|
|
top: '50%',
|
|
left: '50%'
|
|
}, pos);
|
|
|
|
// apply position
|
|
$( self.image ).css({
|
|
position : 'absolute',
|
|
top : getPosition(pos.top, 'height', height),
|
|
left : getPosition(pos.left, 'width', width)
|
|
});
|
|
|
|
// show the image
|
|
self.show();
|
|
|
|
// flag ready and call the callback
|
|
self.ready = true;
|
|
options.complete.call( self, self );
|
|
|
|
},
|
|
error: function() {
|
|
Galleria.raise('Could not scale image: '+self.image.src);
|
|
},
|
|
timeout: 1000
|
|
});
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// our own easings
|
|
$.extend( $.easing, {
|
|
|
|
galleria: function (_, t, b, c, d) {
|
|
if ((t/=d/2) < 1) {
|
|
return c/2*t*t*t + b;
|
|
}
|
|
return c/2*((t-=2)*t*t + 2) + b;
|
|
},
|
|
|
|
galleriaIn: function (_, t, b, c, d) {
|
|
return c*(t/=d)*t + b;
|
|
},
|
|
|
|
galleriaOut: function (_, t, b, c, d) {
|
|
return -c *(t/=d)*(t-2) + b;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// Forked version of Ainos Finger.js for native-style touch
|
|
|
|
Galleria.Finger = (function() {
|
|
|
|
var abs = M.abs;
|
|
|
|
// test for translate3d support
|
|
var has3d = Galleria.HAS3D = (function() {
|
|
|
|
var el = doc.createElement('p'),
|
|
has3d,
|
|
t = ['webkit','O','ms','Moz',''],
|
|
s,
|
|
i=0,
|
|
a = 'transform';
|
|
|
|
DOM().html.insertBefore(el, null);
|
|
|
|
for (; t[i]; i++) {
|
|
s = t[i] ? t[i]+'Transform' : a;
|
|
if (el.style[s] !== undefined) {
|
|
el.style[s] = "translate3d(1px,1px,1px)";
|
|
has3d = $(el).css(t[i] ? '-'+t[i].toLowerCase()+'-'+a : a);
|
|
}
|
|
}
|
|
|
|
DOM().html.removeChild(el);
|
|
return (has3d !== undefined && has3d.length > 0 && has3d !== "none");
|
|
}());
|
|
|
|
// request animation shim
|
|
var requestFrame = (function(){
|
|
var r = 'RequestAnimationFrame';
|
|
return window.requestAnimationFrame ||
|
|
window['webkit'+r] ||
|
|
window['moz'+r] ||
|
|
window['o'+r] ||
|
|
window['ms'+r] ||
|
|
function( callback ) {
|
|
window.setTimeout(callback, 1000 / 60);
|
|
};
|
|
}());
|
|
|
|
var Finger = function(elem, options) {
|
|
|
|
// default options
|
|
this.config = {
|
|
start: 0,
|
|
duration: 500,
|
|
onchange: function() {},
|
|
oncomplete: function() {},
|
|
easing: function(x,t,b,c,d) {
|
|
return -c * ((t=t/d-1)*t*t*t - 1) + b; // easeOutQuart
|
|
}
|
|
};
|
|
|
|
this.easeout = function (x, t, b, c, d) {
|
|
return c*((t=t/d-1)*t*t*t*t + 1) + b;
|
|
};
|
|
|
|
if ( !elem.children.length ) {
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
|
|
// extend options
|
|
$.extend(this.config, options);
|
|
|
|
this.elem = elem;
|
|
this.child = elem.children[0];
|
|
this.to = this.pos = 0;
|
|
this.touching = false;
|
|
this.start = {};
|
|
this.index = this.config.start;
|
|
this.anim = 0;
|
|
this.easing = this.config.easing;
|
|
|
|
if ( !has3d ) {
|
|
this.child.style.position = 'absolute';
|
|
this.elem.style.position = 'relative';
|
|
}
|
|
|
|
// Bind event handlers to context
|
|
$.each(['ontouchstart','ontouchmove','ontouchend','setup'], function(i, fn) {
|
|
self[fn] = (function(caller) {
|
|
return function() {
|
|
caller.apply( self, arguments );
|
|
};
|
|
}(self[fn]));
|
|
});
|
|
|
|
// the physical animator
|
|
this.setX = function() {
|
|
|
|
var style = self.child.style;
|
|
|
|
if (!has3d) {
|
|
// this is actually faster than CSS3 translate
|
|
style.left = self.pos+'px';
|
|
return;
|
|
}
|
|
style.MozTransform = style.webkitTransform = style.transform = 'translate3d(' + self.pos + 'px,0,0)';
|
|
return;
|
|
};
|
|
|
|
// bind events
|
|
$(elem).on('touchstart', this.ontouchstart);
|
|
$(window).on('resize', this.setup);
|
|
$(window).on('orientationchange', this.setup);
|
|
|
|
// set up width
|
|
this.setup();
|
|
|
|
// start the animations
|
|
(function animloop(){
|
|
requestFrame(animloop);
|
|
self.loop.call( self );
|
|
}());
|
|
|
|
};
|
|
|
|
Finger.prototype = {
|
|
|
|
constructor: Finger,
|
|
|
|
setup: function() {
|
|
this.width = $( this.elem ).width();
|
|
this.length = M.ceil( $(this.child).width() / this.width );
|
|
if ( this.index !== 0 ) {
|
|
this.index = M.max(0, M.min( this.index, this.length-1 ) );
|
|
this.pos = this.to = -this.width*this.index;
|
|
}
|
|
},
|
|
|
|
setPosition: function(pos) {
|
|
this.pos = pos;
|
|
this.to = pos;
|
|
},
|
|
|
|
ontouchstart: function(e) {
|
|
|
|
var touch = e.originalEvent.touches;
|
|
|
|
this.start = {
|
|
pageX: touch[0].pageX,
|
|
pageY: touch[0].pageY,
|
|
time: +new Date()
|
|
};
|
|
|
|
this.isScrolling = null;
|
|
this.touching = true;
|
|
this.deltaX = 0;
|
|
|
|
$doc.on('touchmove', this.ontouchmove);
|
|
$doc.on('touchend', this.ontouchend);
|
|
},
|
|
|
|
ontouchmove: function(e) {
|
|
|
|
var touch = e.originalEvent.touches;
|
|
|
|
// ensure swiping with one touch and not pinching
|
|
if( touch && touch.length > 1 || e.scale && e.scale !== 1 ) {
|
|
return;
|
|
}
|
|
|
|
this.deltaX = touch[0].pageX - this.start.pageX;
|
|
|
|
// determine if scrolling test has run - one time test
|
|
if ( this.isScrolling === null ) {
|
|
this.isScrolling = !!(
|
|
this.isScrolling ||
|
|
M.abs(this.deltaX) < M.abs(touch[0].pageY - this.start.pageY)
|
|
);
|
|
}
|
|
|
|
// if user is not trying to scroll vertically
|
|
if (!this.isScrolling) {
|
|
|
|
// prevent native scrolling
|
|
e.preventDefault();
|
|
|
|
// increase resistance if first or last slide
|
|
this.deltaX /= ( (!this.index && this.deltaX > 0 || this.index == this.length - 1 && this.deltaX < 0 ) ?
|
|
( M.abs(this.deltaX) / this.width + 1.8 ) : 1 );
|
|
this.to = this.deltaX - this.index * this.width;
|
|
}
|
|
e.stopPropagation();
|
|
},
|
|
|
|
ontouchend: function(e) {
|
|
|
|
this.touching = false;
|
|
|
|
// determine if slide attempt triggers next/prev slide
|
|
var isValidSlide = +new Date() - this.start.time < 250 &&
|
|
M.abs(this.deltaX) > 40 ||
|
|
M.abs(this.deltaX) > this.width/2,
|
|
|
|
isPastBounds = !this.index && this.deltaX > 0 ||
|
|
this.index == this.length - 1 && this.deltaX < 0;
|
|
|
|
// if not scrolling vertically
|
|
if ( !this.isScrolling ) {
|
|
this.show( this.index + ( isValidSlide && !isPastBounds ? (this.deltaX < 0 ? 1 : -1) : 0 ) );
|
|
}
|
|
|
|
$doc.off('touchmove', this.ontouchmove);
|
|
$doc.off('touchend', this.ontouchend);
|
|
},
|
|
|
|
show: function( index ) {
|
|
if ( index != this.index ) {
|
|
this.config.onchange.call(this, index);
|
|
} else {
|
|
this.to = -( index*this.width );
|
|
}
|
|
},
|
|
|
|
moveTo: function( index ) {
|
|
if ( index != this.index ) {
|
|
this.pos = this.to = -( index*this.width );
|
|
this.index = index;
|
|
}
|
|
},
|
|
|
|
loop: function() {
|
|
|
|
var distance = this.to - this.pos,
|
|
factor = 1;
|
|
|
|
if ( this.width && distance ) {
|
|
factor = M.max(0.5, M.min(1.5, M.abs(distance / this.width) ) );
|
|
}
|
|
|
|
// if distance is short or the user is touching, do a 1-1 animation
|
|
if ( this.touching || M.abs(distance) <= 1 ) {
|
|
this.pos = this.to;
|
|
distance = 0;
|
|
if ( this.anim && !this.touching ) {
|
|
this.config.oncomplete( this.index );
|
|
}
|
|
this.anim = 0;
|
|
this.easing = this.config.easing;
|
|
} else {
|
|
if ( !this.anim ) {
|
|
// save animation parameters
|
|
this.anim = { start: this.pos, time: +new Date(), distance: distance, factor: factor, destination: this.to };
|
|
}
|
|
// check if to has changed or time has run out
|
|
var elapsed = +new Date() - this.anim.time;
|
|
var duration = this.config.duration*this.anim.factor;
|
|
|
|
if ( elapsed > duration || this.anim.destination != this.to ) {
|
|
this.anim = 0;
|
|
this.easing = this.easeout;
|
|
return;
|
|
}
|
|
// apply easing
|
|
this.pos = this.easing(
|
|
null,
|
|
elapsed,
|
|
this.anim.start,
|
|
this.anim.distance,
|
|
duration
|
|
);
|
|
}
|
|
this.setX();
|
|
}
|
|
};
|
|
|
|
return Finger;
|
|
|
|
}());
|
|
|
|
// the plugin initializer
|
|
$.fn.galleria = function( options ) {
|
|
|
|
var selector = this.selector;
|
|
|
|
// try domReady if element not found
|
|
if ( !$(this).length ) {
|
|
|
|
$(function() {
|
|
if ( $( selector ).length ) {
|
|
|
|
// if found on domReady, go ahead
|
|
$( selector ).galleria( options );
|
|
|
|
} else {
|
|
|
|
// if not, try fetching the element for 5 secs, then raise a warning.
|
|
Galleria.utils.wait({
|
|
until: function() {
|
|
return $( selector ).length;
|
|
},
|
|
success: function() {
|
|
$( selector ).galleria( options );
|
|
},
|
|
error: function() {
|
|
Galleria.raise('Init failed: Galleria could not find the element "'+selector+'".');
|
|
},
|
|
timeout: 5000
|
|
});
|
|
|
|
}
|
|
});
|
|
return this;
|
|
}
|
|
|
|
return this.each(function() {
|
|
|
|
// destroy previous instance and prepare for new load
|
|
if ( $.data(this, 'galleria') ) {
|
|
$.data( this, 'galleria' ).destroy();
|
|
$( this ).find( '*' ).hide();
|
|
}
|
|
|
|
// load the new gallery
|
|
$.data( this, 'galleria', new Galleria().init( this, options ) );
|
|
});
|
|
|
|
};
|
|
|
|
// export as AMD or CommonJS
|
|
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
|
|
module.exports = Galleria;
|
|
} else {
|
|
window.Galleria = Galleria;
|
|
if ( typeof define === "function" && define.amd ) {
|
|
define( "galleria", ['jquery'], function() { return Galleria; } );
|
|
}
|
|
}
|
|
|
|
// phew
|
|
|
|
}( jQuery, this ) );
|