2016-10-16 08:53:19 +00:00
/ * *
2016-10-16 09:52:29 +00:00
* Galleria v1 . 4.7 2016 - 09 - 17
2016-10-16 08:53:19 +00:00
* http : //galleria.io
*
2016-10-16 09:52:29 +00:00
* Copyright ( c ) 2010 - 2016 worse is better UG
2016-10-16 08:53:19 +00:00
* Licensed under the MIT license
2016-10-16 09:52:29 +00:00
* https : //raw.github.com/worseisbetter/galleria/master/LICENSE
2016-10-16 08:53:19 +00:00
*
* /
( 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
2016-10-16 09:52:29 +00:00
VERSION = 1.47 ,
2016-10-16 08:53:19 +00:00
DEBUG = true ,
TIMEOUT = 30000 ,
DUMMY = false ,
NAV = navigator . userAgent . toLowerCase ( ) ,
HASH = window . location . hash . replace ( /#\// , '' ) ,
2016-10-16 09:52:29 +00:00
PROT = window . location . protocol == "file:" ? "http:" : window . location . protocol ,
2016-10-16 08:53:19 +00:00
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 ( ) {
2016-10-16 09:52:29 +00:00
return PROT + '//www.youtube.com/embed/' + this . id ;
2016-10-16 08:53:19 +00:00
} ,
2016-10-16 09:52:29 +00:00
get _thumb : function ( data ) {
return PROT + '//img.youtube.com/vi/' + this . id + '/default.jpg' ;
2016-10-16 08:53:19 +00:00
} ,
2016-10-16 09:52:29 +00:00
get _image : function ( data ) {
return PROT + '//img.youtube.com/vi/' + this . id + '/hqdefault.jpg' ; }
2016-10-16 08:53:19 +00:00
} ,
vimeo : {
reg : /https?:\/\/(?:www\.)?(vimeo\.com)\/(?:hd#)?([0-9]+)/i ,
embed : function ( ) {
2016-10-16 09:52:29 +00:00
return PROT + '//player.vimeo.com/video/' + this . id ;
2016-10-16 08:53:19 +00:00
} ,
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 ] ) ;
2016-10-16 09:52:29 +00:00
_videoThumbs = function ( data ) {
2016-10-16 08:53:19 +00:00
self . data = data ;
$ . each ( self . readys , function ( i , fn ) {
fn ( self . data ) ;
} ) ;
self . readys = [ ] ;
2016-10-16 09:52:29 +00:00
} ;
if ( this . hasOwnProperty ( 'getUrl' ) ) {
$ . getJSON ( this . getUrl ( ) , _videoThumbs ) ;
} else {
window . setTimeout ( _videoThumbs , 400 ) ;
}
2016-10-16 08:53:19 +00:00
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 ( ) {
2016-10-16 09:52:29 +00:00
return $loader . height ( ) > 0 ;
2016-10-16 08:53:19 +00:00
} ,
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 ( ) ;
2016-10-16 09:52:29 +00:00
@ author http : //wib.io
2016-10-16 08:53:19 +00:00
@ 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 < / l i >
< li > author - name of the author < / l i >
< li > css - css file name ( not path ) < / l i >
< li > defaults - default options to apply , including theme - specific options < / l i >
< li > init - the init function < / l i >
< / u l >
@ 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 ,
2016-10-16 09:52:29 +00:00
reg , reg2 ;
2016-10-16 08:53:19 +00:00
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 ( ) + '\\.' ) ;
2016-10-16 09:52:29 +00:00
reg2 = new RegExp ( 'galleria\\.io\\/theme\\/' + theme . name . toLowerCase ( ) + '\\/(\\d*\\.*)?(\\d*\\.*)?(\\d*\\/)?js' ) ;
if ( reg . test ( script . src ) || reg2 . test ( script . src ) ) {
2016-10-16 08:53:19 +00:00
// 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 < / l i >
< li > height - height of the container < / l i >
< li > min - minimum scale ratio < / l i >
< li > max - maximum scale ratio < / l i >
< li > margin - distance in pixels from the image border to the container < / l i >
< li > complete - a callback that fires when scaling is complete < / l i >
< li > position - positions the image , works like the css background - image property . < / l i >
< li > crop - defines how to crop . Can be true , false , 'width' or 'height' < / l i >
< li > canvas - set to true to try a canvas - based rescale < / l i >
< / u l >
@ 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
2016-10-16 09:52:29 +00:00
} ( jQuery , this ) ) ;