text: /**
  
   * Galleria v 1.2.9 2013-01-18
  
   * http://galleria.io
  
   *
  
   * Licensed under the MIT license
  
   * https://raw.github.com/aino/galleria/master/LICENSE
  
   *
  
   */
  
  
  
  (function( $ ) {
  
  
  
  /*global jQuery, navigator, Galleria:true, Image */
  
  
  
  // some references
  
  var undef,
  
      window = this,
  
      doc    = window.document,
  
      $doc   = $( doc ),
  
      $win   = $( window ),
  
  
  
  // native prototypes
  
      protoArray = Array.prototype,
  
  
  
  // internal constants
  
      VERSION = 1.29,
  
      DEBUG = true,
  
      TIMEOUT = 30000,
  
      DUMMY = false,
  
      NAV = navigator.userAgent.toLowerCase(),
  
      HASH = window.location.hash.replace(/#\//, ''),
  
      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 : 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;(?:amp;)?)*v(?:&lt;[A-Z]+&gt;)?=([0-9a-zA-Z\-\_]+))/i,
  
              embed: function(id) {
  
                  return 'http://www.youtube.com/embed/'+id;
  
              },
  
              getThumb: function( id, success, fail ) {
  
                  fail = fail || F;
  
                  $.getJSON(window.location.protocol+'//gdata.youtube.com/feeds/api/videos/' + id + '?v=2&alt=json-in-script&callback=?', function(data) {
  
                      try {
  
                          success( data.entry.media$group.media$thumbnail[0].url );
  
                      } catch(e) {
  
                          fail();
  
                      }
  
                  }).error(fail);
  
              }
  
          },
  
          vimeo: {
  
              reg: /https?:\/\/(?:www\.)?(vimeo\.com)\/(?:hd#)?([0-9]+)/i,
  
              embed: function(id) {
  
                  return 'http://player.vimeo.com/video/'+id;
  
              },
  
              getThumb: function( id, success, fail ) {
  
                  fail = fail || F;
  
                  $.getJSON('http://vimeo.com/api/v2/video/' + id + '.json?callback=?', function(data) {
  
                      try {
  
                          success( data[0].thumbnail_medium );
  
                      } catch(e) {
  
                          fail();
  
                      }
  
                  }).error(fail);
  
              }
  
          },
  
          dailymotion: {
  
              reg: /https?:\/\/(?:www\.)?(dailymotion\.com)\/video\/([^_]+)/,
  
              embed: function(id) {
  
                  return 'http://www.dailymotion.com/embed/video/'+id;
  
              },
  
              getThumb: function( id, success, fail ) {
  
                  fail = fail || F;
  
                  $.getJSON('https://api.dailymotion.com/video/'+id+'?fields=thumbnail_medium_url&callback=?', function(data) {
  
                      try {
  
                          success( data.thumbnail_medium_url );
  
                      } catch(e) {
  
                          fail();
  
                      }
  
                  }).error(fail);
  
              }
  
          }
  
      },
  
  
  
      // utility for testing the video URL and getting the video ID
  
      _videoTest = function( url ) {
  
          var match;
  
          for ( var v in _video ) {
  
              match = url && 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.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.mozRequestFullScreen ) {
  
                  elem.mozRequestFullScreen();
  
              }
  
              else if ( elem.webkitRequestFullScreen ) {
  
                  elem.webkitRequestFullScreen();
  
              }
  
          },
  
  
  
          exit: function( callback ) {
  
  
  
              this.callback = callback || F;
  
  
  
              if ( doc.exitFullscreen ) {
  
                  doc.exitFullscreen();
  
              }
  
              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 ) {
  
                      fs._enter( _nativeFullscreen.callback );
  
                  } else {
  
                      fs._exit( _nativeFullscreen.callback );
  
                  }
  
              };
  
              doc.addEventListener( 'fullscreenchange', 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 = [],
  
  
  
      // themeLoad trigger
  
      _themeLoad = function( theme ) {
  
  
  
          Galleria.theme = theme;
  
  
  
          // run the instances we have in the pool
  
          $.each( _pool, function( i, instance ) {
  
              if ( !instance._initialized ) {
  
                  instance._init.call( instance );
  
              }
  
          });
  
  
  
          _pool = [];
  
      },
  
  
  
      // 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.unbind( 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 ( 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 ) );
  
              },
  
  
  
              elem : function( elem ) {
  
                  if (elem instanceof $) {
  
                      return {
  
                          $: elem,
  
                          dom: elem[0]
  
                      };
  
                  } else {
  
                      return {
  
                          $: $(elem),
  
                          dom: elem
  
                      };
  
                  }
  
              },
  
  
  
              hide : function( elem, speed, callback ) {
  
  
  
                  callback = callback || F;
  
  
  
                  var el = Utils.elem( elem ),
  
                      $elem = el.$;
  
  
  
                  elem = el.dom;
  
  
  
                  // 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 el = Utils.elem( elem ),
  
                      $elem = el.$;
  
  
  
                  elem = el.dom;
  
  
  
                  // 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 );
  
                      }
  
                  }
  
              },
  
  
  
  
  
              // enhanced click for mobile devices
  
              // we bind a touchend and hijack any click event in the bubble
  
              // then we execute the click directly and save it in a separate data object for later
  
              optimizeTouch: (function() {
  
  
  
                  var node,
  
                      evs,
  
                      fakes,
  
                      travel,
  
                      evt = {},
  
                      handler = function( e ) {
  
                          e.preventDefault();
  
                          evt = $.extend({}, e, true);
  
                      },
  
                      attach = function() {
  
                          this.evt = evt;
  
                      },
  
                      fake = function() {
  
                          this.handler.call(node, this.evt);
  
                      };
  
  
  
                  return function( elem ) {
  
  
  
                      $(elem).bind('touchend', function( e ) {
  
  
  
                          node = e.target;
  
                          travel = true;
  
  
  
                          while( node.parentNode && node != e.currentTarget && travel ) {
  
  
  
                              evs =   $(node).data('events');
  
                              fakes = $(node).data('fakes');
  
  
  
                              if (evs && 'click' in evs) {
  
  
  
                                  travel = false;
  
                                  e.preventDefault();
  
  
  
                                  // fake the click and save the event object
  
                                  $(node).click(handler).click();
  
  
  
                                  // remove the faked click
  
                                  evs.click.pop();
  
  
  
                                  // attach the faked event
  
                                  $.each( evs.click, attach);
  
  
  
                                  // save the faked clicks in a new data object
  
                                  $(node).data('fakes', evs.click);
  
  
  
                                  // remove all clicks
  
                                  delete evs.click;
  
  
  
                              } else if ( fakes ) {
  
  
  
                                  travel = false;
  
                                  e.preventDefault();
  
  
  
                                  // fake all clicks
  
                                  $.each( fakes, fake );
  
                              }
  
  
  
                              // bubble
  
                              node = node.parentNode;
  
                          }
  
                      });
  
                  };
  
              }()),
  
  
  
              wait : function(options) {
  
                  options = $.extend({
  
                      until : FALSE,
  
                      success : F,
  
                      error : function() { Galleria.raise('Could not complete wait function.'); },
  
                      timeout: 3000
  
                  }, options);
  
  
  
                  var start = Utils.timestamp(),
  
                      elapsed,
  
                      now,
  
                      fn = function() {
  
                          now = Utils.timestamp();
  
                          elapsed = now - start;
  
                          if ( options.until( elapsed ) ) {
  
                              options.success();
  
                              return false;
  
                          }
  
  
  
                          if (typeof options.timeout == 'number' && now >= start + options.timeout) {
  
                              options.error();
  
                              return false;
  
                          }
  
                          window.setTimeout(fn, 10);
  
                      };
  
  
  
                  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 ) {
  
  
  
                          // IE has a limit of 31 stylesheets in one document
  
                          if( length >= 31 ) {
  
                              Galleria.raise( 'You have reached the browser stylesheet limit (31)', true );
  
                              return;
  
                          }
  
                      }
  
                  }
  
  
  
                  if ( typeof callback === 'function' ) {
  
  
  
                      // First check for dummy element (new in 1.2.8)
  
                      var $loader = $('<s>').attr( 'id', 'galleria-loader' ).hide().appendTo( DOM().body );
  
  
  
                      Utils.wait({
  
                          until: function() {
  
                              return $loader.height() == 1;
  
                          },
  
                          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;
  
              }
  
          };
  
      }()),
  
  
  
      // 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] ) );
  
                  }
  
              }
  
          };
  
      }());
  
  
  
  _nativeFullscreen.listen();
  
  
  
  /**
  
      The main Galleria class
  
  
  
      @class
  
      @constructor
  
  
  
      @example var gallery = new Galleria();
  
  
  
      @author http://aino.se
  
  
  
      @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(Math.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.bind('keydown', keyboard.press);
  
              }
  
          },
  
  
  
          detach: function() {
  
              keyboard.bound = false;
  
              keyboard.map = {};
  
              $doc.unbind('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 controls[ controls.active ];
  
          },
  
  
  
          getNext : function() {
  
              return controls[ 1 - controls.active ];
  
          }
  
      };
  
  
  
      // 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 );
  
                      hooks[ i+1 ] = w;
  
                      h = Math.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.bind( 'click', 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.bind( 'click', 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 = Math.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;
  
              }
  
  
  
              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 = Math.max( 0, Math.min( maxX, x ) );
  
                  y = Math.max( 0, Math.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' ).unbind( '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').unbind( 'mousemove', tooltip.move ).bind( '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.unbind( 'mouseup', mouseup );
  
  
  
                  };
  
  
  
              text = typeof text === 'function' ? text() : text;
  
  
  
              if ( ! text ) {
  
                  return;
  
              }
  
  
  
              self.$( 'tooltip' ).html( text.replace(/\s/, '&#160;') );
  
  
  
              // trigger mousemove on mouseup in case of click
  
              elem.bind( '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,
  
  
  
          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 ) {
  
  
  
              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.WEBKIT ) {
  
                          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();
  
                  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');
  
  
  
                  // begin styleforce
  
  
  
                  Utils.forceStyles(self.get('container'), {
  
                      position: '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 );
  
                              }
  
                          }
  
                      });
  
                  });
  
              }
  
  
  
              // 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 );
  
                      }
  
  
  
                  }, 100);
  
  
  
                  self.trigger( Galleria.FULLSCREEN_ENTER );
  
              });
  
  
  
              if ( !inBrowser ) {
  
                  Utils.show( self.getActiveImage() );
  
              } else {
  
                  $win.resize( fullscreen.scale );
  
              }
  
  
  
          },
  
  
  
          scale : function() {
  
              self.rescale();
  
          },
  
  
  
          exit: function( callback ) {
  
  
  
              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;
  
  
  
              self.$('container').removeClass( 'fullscreen' );
  
  
  
              if ( inBrowser ) {
  
                  Utils.hide( self.getActiveImage() );
  
  
  
                  // revert all styles
  
                  Utils.revertStyles( self.get('container'), DOM().html, DOM().body );
  
  
  
                  // scroll back
  
                  window.scrollTo(0, fullscreen.scrolled);
  
              }
  
  
  
              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.unbind('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) {
  
                  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').bind( 'mousemove click', idle.showAll );
  
              if ( self._options.idleMode == 'hover' ) {
  
                  self.$('container').bind( 'mouseleave', idle.hide );
  
              }
  
          },
  
  
  
          removeEvent : function() {
  
              idle.bound = false;
  
              self.$('container').bind( 'mousemove click', idle.showAll );
  
              if ( self._options.idleMode == 'hover' ) {
  
                  self.$('container').unbind( '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 || self.getData().iframe ) {
  
                  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() {
  
  
  
              // trigger the event
  
              self.trigger( Galleria.LIGHTBOX_OPEN );
  
  
  
              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 = {};
  
  
  
              // IE8 fix for IE's transparent background event "feature"
  
              if ( IE && IE > 7 ) {
  
                  cssMap.nextholder += 'background:#000;filter:alpha(opacity=0);';
  
                  cssMap.prevholder += 'background:#000;filter:alpha(opacity=0);';
  
              }
  
  
  
              // 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 );
  
  
  
              Utils.optimizeTouch( el.box );
  
  
  
              // add the prev/next nav and bind some controls
  
  
  
              hover( $( el.close ).bind( 'click', lightbox.hide ).html('&#215;') );
  
  
  
              $.each( ['Prev','Next'], function(i, dir) {
  
  
  
                  var $d = $( el[ dir.toLowerCase() ] ).html( /v/.test( dir ) ? '&#8249;&#160;' : '&#160;&#8250;' ),
  
                      $e = $( el[ dir.toLowerCase()+'holder'] );
  
  
  
                  $e.bind( 'click', 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 ).bind( 'click', lightbox.hide );
  
  
  
              // the lightbox animation is slow on ipad
  
              if ( Galleria.IPAD ) {
  
                  self._options.lightboxTransitionSpeed = 0;
  
              }
  
  
  
          },
  
  
  
          rescale: function(event) {
  
  
  
              // calculate
  
               var width = Math.min( $win.width()-40, lightbox.width ),
  
                  height = Math.min( $win.height()-60, lightbox.height ),
  
                  ratio = Math.min( width / lightbox.width, height / lightbox.height ),
  
                  destWidth = Math.round( lightbox.width * ratio ) + 40,
  
                  destHeight = Math.round( lightbox.height * ratio ) + 60,
  
                  to = {
  
                      width: destWidth,
  
                      height: destHeight,
  
                      'margin-top': Math.ceil( destHeight / 2 ) *- 1,
  
                      'margin-left': Math.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.unbind('resize', lightbox.rescale);
  
  
  
              $( lightbox.elems.box ).hide();
  
  
  
              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();
  
              }
  
  
  
              // 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.unbind('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( 'big' in ndata ? ndata.big : ndata.image );
  
                      n = self.getNext( n );
  
                  }
  
              } catch(e) {}
  
  
  
              lightbox.image.isIframe = !!data.iframe;
  
  
  
              $(lightbox.elems.box).toggleClass( 'iframe', !!data.iframe );
  
  
  
              lightbox.image.load( data.iframe || data.big || data.image, function( image ) {
  
  
  
                  if ( image.isIframe ) {
  
  
  
                      var cw = $(window).width(),
  
                          ch = $(window).height();
  
  
  
                      if ( self._options.maxVideoSize ) {
  
                          var r = Math.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,
  
                      zIndex: 99998,
  
                      opacity: 0,
  
                      visibility: 'visible'
  
                  });
  
  
  
                  lightbox.elems.title.innerHTML = data.title || '';
  
                  lightbox.elems.counter.innerHTML = (index + 1) + ' / ' + total;
  
                  $win.resize( lightbox.rescale );
  
                  lightbox.rescale();
  
              });
  
  
  
              $( 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 ) {
  
  
  
          var self = this;
  
  
  
          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: true, // 1.2.4
  
              thumbCrop: true,
  
              thumbEventType: 'click',
  
              thumbFit: true, // legacy, deprecate at 1.3
  
              thumbMargin: 0,
  
              thumbQuality: 'auto',
  
              thumbDisplayOrder: true, // 1.2.8
  
              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
  
              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;
  
  
  
          // turn off debug
  
          if ( options && options.debug === false ) {
  
              DEBUG = false;
  
          }
  
  
  
          // set timeout
  
          if ( options && typeof options.imageTimeout === 'number' ) {
  
              TIMEOUT = options.imageTimeout;
  
          }
  
  
  
          // set dummy
  
          if ( options && typeof options.dummy === 'string' ) {
  
              DUMMY = options.dummy;
  
          }
  
  
  
          // 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...
  
          if ( typeof Galleria.theme === 'object' ) {
  
              this._init();
  
          } else {
  
              // push the instance into the 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 ( !Galleria.theme ) {
  
              Galleria.raise( 'Init failed: No theme found.', true );
  
              return this;
  
          }
  
  
  
          // merge the theme & caller options
  
          $.extend( true, options, Galleria.theme.defaults, this._original.options, Galleria.configure.options );
  
  
  
          // 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() {
  
  
  
              // 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('&#8211;');
  
  
  
          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' );
  
  
  
          // add images to the controls
  
          $.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;
  
  
  
          });
  
  
  
          // some forced generic styling
  
          this.$( 'images' ).css({
  
              position: 'relative',
  
              top: 0,
  
              left: 0,
  
              width: '100%',
  
              height: '100%'
  
          });
  
  
  
          this.$( 'thumbnails, thumbnails-list' ).css({
  
              overflow: 'hidden',
  
              position: 'relative'
  
          });
  
  
  
          // bind image navigation arrows
  
          this.$( 'image-nav-right, image-nav-left' ).bind( 'click', function(e) {
  
  
  
              // tune the clicknext option
  
              if ( options.clicknext ) {
  
                  e.stopPropagation();
  
              }
  
  
  
              // pause if options is set
  
              if ( options.pauseOnInteraction ) {
  
                  self.pause();
  
              }
  
  
  
              // navigate
  
              var fn = /right/.test( this.className ) ? 'next' : 'prev';
  
              self[ fn ]();
  
  
  
          });
  
  
  
          // 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.bind( 'resize', function() {
  
                  if ( !self.isFullscreen() ) {
  
                      self.resize();
  
                  }
  
              });
  
          }
  
  
  
          // bind swipe gesture
  
          if ( options.swipe ) {
  
  
  
              (function( images ) {
  
  
  
                  var swipeStart = [0,0],
  
                      swipeStop = [0,0],
  
                      limitX = 30,
  
                      limitY = 100,
  
                      multi = false,
  
                      tid = 0,
  
                      data,
  
                      ev = {
  
                          start: 'touchstart',
  
                          move: 'touchmove',
  
                          stop: 'touchend'
  
                      },
  
                      getData = function(e) {
  
                          return e.originalEvent.touches ? e.originalEvent.touches[0] : e;
  
                      },
  
                      moveHandler = function( e ) {
  
  
  
                          if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) {
  
                              return;
  
                          }
  
  
  
                          data = getData( e );
  
                          swipeStop = [ data.pageX, data.pageY ];
  
  
  
                          if ( !swipeStart[0] ) {
  
                              swipeStart = swipeStop;
  
                          }
  
  
  
                          if ( Math.abs( swipeStart[0] - swipeStop[0] ) > 10 ) {
  
                              e.preventDefault();
  
                          }
  
                      },
  
                      upHandler = function( e ) {
  
  
  
                          images.unbind( ev.move, moveHandler );
  
  
  
                          // if multitouch (possibly zooming), abort
  
                          if ( ( e.originalEvent.touches && e.originalEvent.touches.length ) || multi ) {
  
                              multi = !multi;
  
                              return;
  
                          }
  
  
  
                          if ( Utils.timestamp() - tid < 1000 &&
  
                               Math.abs( swipeStart[0] - swipeStop[0] ) > limitX &&
  
                               Math.abs( swipeStart[1] - swipeStop[1] ) < limitY ) {
  
  
  
                              e.preventDefault();
  
                              self[ swipeStart[0] > swipeStop[0] ? 'next' : 'prev' ]();
  
                          }
  
  
  
                          swipeStart = swipeStop = [0,0];
  
                      };
  
  
  
                  images.bind(ev.start, function(e) {
  
  
  
                      if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) {
  
                          return;
  
                      }
  
  
  
                      data = getData(e);
  
                      tid = Utils.timestamp();
  
                      swipeStart = swipeStop = [ data.pageX, data.pageY ];
  
                      images.bind(ev.move, moveHandler ).one(ev.stop, upHandler);
  
  
  
                  });
  
  
  
              }( self.$( 'images' ) ));
  
  
  
              // double-tap/click fullscreen toggle
  
  
  
              if ( options.fullscreenDoubleTap ) {
  
  
  
                  this.$( 'stage' ).bind( 'touchstart', (function() {
  
                      var last, cx, cy, lx, ly, now,
  
                          getData = function(e) {
  
                              return e.originalEvent.touches ? e.originalEvent.touches[0] : e;
  
                          };
  
                      return function(e) {
  
                          now = Galleria.utils.timestamp();
  
                          cx = getData(e).pageX;
  
                          cy = getData(e).pageY;
  
                          if ( ( now - last < 500 ) && ( cx - lx < 20) && ( cy - ly < 20) ) {
  
                              self.toggleFullscreen();
  
                              e.preventDefault();
  
                              self.$( 'stage' ).unbind( 'touchend', arguments.callee );
  
                              return;
  
                          }
  
                          last = now;
  
                          lx = cx;
  
                          ly = cy;
  
                      };
  
                  }()));
  
              }
  
  
  
          }
  
  
  
          // optimize touch for container
  
          Utils.optimizeTouch( this.get( 'container' ) );
  
  
  
          // 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 ] = Math.max.apply( Math, 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,
  
              special,
  
  
  
              $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,
  
                      complete: function( thumb ) {
  
  
  
                          // shrink thumbnails to fit
  
                          var top = ['left', 'top'],
  
                              arr = ['Width', 'Height'],
  
                              m,
  
                              css,
  
                              data = self.getData( thumb.index ),
  
                              special = data.thumb.split(':');
  
  
  
                          // calculate shrinked positions
  
                          $.each(arr, function( i, measure ) {
  
                              m = measure.toLowerCase();
  
                              if ( (o.thumbCrop !== true || o.thumbCrop === m ) && o.thumbFit ) {
  
                                  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 )
  
                          );
  
  
  
                          // get "special" thumbs from provider
  
                          if( data.iframe && special.length == 2 && special[0] in _video ) {
  
  
  
                              _video[ special[0] ].getThumb( special[1], (function(img) {
  
                                  return function(src) {
  
                                      img.src = src;
  
                                      thumbComplete( thumb, callback );
  
                                  };
  
                              }( thumb.image ) ));
  
  
  
                          } else 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.thumbFit && o.thumbCrop !== true ) {
  
                      $container.css( { width: 'auto', height: 'auto' } );
  
                  } else {
  
                      $container.css( { width: thumb.data.width, height: thumb.data.height } );
  
                  }
  
  
  
                  // load the thumbnail
  
                  special = src.split(':');
  
  
  
                  if ( special.length == 2 && special[0] in _video ) {
  
  
  
                      thumb.video = true;
  
                      thumb.ready = true;
  
  
  
                      thumb.load( gif, {
  
                          height: thumb.data.height,
  
                          width: thumb.data.height*1.25
  
                      }, onThumbLoad);
  
  
  
                  } else 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 === 'empty' || optval === 'numbers' ) {
  
  
  
                  thumb = {
  
                      container:  Utils.create( 'galleria-image' ),
  
                      image: Utils.create( 'img', 'span' ),
  
                      ready: true
  
                  };
  
  
  
                  // 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).bind( 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,
  
              thumbnails = this.$( 'thumbnails' ).children().filter(function() {
  
                  return $(this).data('lazy-src');
  
              }),
  
              loaded = 0;
  
  
  
          $.each( arr, function(i, ind) {
  
  
  
              if ( ind > self._thumbnails.length - 1 ) {
  
                  return;
  
              }
  
  
  
              var thumb = self._thumbnails[ ind ],
  
                  data = thumb.data,
  
                  special = data.src.split(':'),
  
                  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
  
  
  
                  // 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
  
                  Galleria.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( /^function Object/.test( source.constructor ) ) {
  
              source = [source];
  
          }
  
  
  
          // check if the data is an array already
  
          if ( source.constructor === Array ) {
  
              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'.split(' '), function( i, val ) {
  
                      if ( elem.data(val) ) {
  
                          data[ val ] = elem.data(val);
  
                      }
  
                  });
  
  
  
                  // 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 Math.round(Math.random())-0.5;
  
              });
  
          }
  
  
  
  
  
  
  
          // trigger the DATA event and return
  
          if ( this.getDataLength() ) {
  
              this._parseData().trigger( Galleria.DATA );
  
          }
  
          return this;
  
  
  
      },
  
  
  
      // make sure the data works properly
  
      _parseData : function() {
  
  
  
          var self = this,
  
              current;
  
  
  
          $.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 ( !'big' in data ) {
  
                  current.big = data.image;
  
              }
  
              // parse video
  
              if ( 'video' in data ) {
  
                  var result = _videoTest( data.video );
  
  
  
                  if ( result ) {
  
                      current.iframe = _video[ result.provider ].embed( result.id ) + (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 '';
  
                      }());
  
                      delete current.video;
  
                      if( !('thumb' in current) || !current.thumb ) {
  
                          current.thumb = result.provider+':'+result.id;
  
                      }
  
                  }
  
              }
  
          });
  
  
  
          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' ).unbind( 'galleria' );
  
          this.get( 'target' ).innerHTML = this._original.html;
  
          this.clearTimer();
  
          Utils.removeFromArray( _instances, this );
  
          Utils.removeFromArray( _galleries, this );
  
          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()._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()._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' ).bind( 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' ).unbind( 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;
  
      },
  
  
  
      /**
  
          Get the currently active image element.
  
  
  
          @returns {HTMLElement} The image element
  
      */
  
  
  
      getActiveImage: function() {
  
          return this._getActive().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 = Math.round( Math.max( dist * -1, Math.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' ).unbind( 'mousemove', calculate ).bind( '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 ) );
  
          };
  
      },
  
  
  
      /**
  
          Removes the panning effect set by addPan()
  
  
  
          @returns Instance
  
      */
  
  
  
      removePan: function() {
  
  
  
          // todo: doublecheck IE8
  
  
  
          this.$( 'stage' ).unbind( '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 self = this,
  
  
  
              complete,
  
  
  
              scaleLayer = function( img ) {
  
                  $( img.container ).children(':first').css({
  
                      top: Math.max(0, Utils.parseValue( img.image.style.top )),
  
                      left: Math.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();
  
  
  
              // scale the active image
  
              self._scaleImage();
  
  
  
              if ( self._options.carousel ) {
  
                  self.updateCarousel();
  
              }
  
  
  
              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;
  
      },
  
  
  
      /**
  
          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 ) {
  
  
  
          // do nothing queue is long || index is false || queue is false and transition is in progress
  
          if ( this._queue.length > 3 || index === false || ( !this._options.queue && this._queue.stalled ) ) {
  
              return;
  
          }
  
  
  
          index = Math.max( 0, Math.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;
  
          }
  
  
  
          this._active = index;
  
  
  
          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 = data.iframe || ( this.isFullscreen() && 'big' in data ? data.big : data.image ), // use big image if fullscreen mode
  
              active = this._controls.getActive(),
  
              next = this._controls.getNext(),
  
              cached = next.isCached( src ),
  
              thumb = this._thumbnails[ queue.index ],
  
              mousetrigger = function() {
  
                  $( next.image ).trigger( 'mouseup' );
  
              };
  
  
  
          // 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();
  
  
  
                  if( active.isIframe ) {
  
                      $( active.container ).find( 'iframe' ).remove();
  
                  }
  
  
  
                  self.$('container').toggleClass('iframe', !!data.iframe);
  
  
  
                  $( 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 link or add lightbox
  
                  // link takes precedence over lightbox if both are detected
  
                  if ( data.link || self._options.lightbox || self._options.clicknext ) {
  
  
  
                      $( next.image ).css({
  
                          cursor: 'pointer'
  
                      }).bind( 'mouseup', function( e ) {
  
  
  
                          // non-left click
  
                          if ( typeof e.which == 'number' && e.which > 1 ) {
  
                              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
  
          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 = self.getData( n );
  
                      p.preload( this.isFullscreen() && 'big' in ndata ? ndata.big : ndata.image );
  
                      n = self.getNext( n );
  
                  }
  
              } catch(e) {}
  
          }
  
  
  
          // show the next image, just in case
  
          Utils.show( next.container );
  
  
  
          next.isIframe = !!data.iframe;
  
  
  
          // 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.link || self._options.lightbox || self._options.clicknext ) {
  
                              layer.css( 'cursor', 'pointer' ).unbind( 'mouseup' ).mouseup( mousetrigger );
  
                          }
  
                      }
  
  
  
                      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:      Math.ceil( played / self._playtime * 100 ),
  
                          seconds:      Math.floor( played / 1000 ),
  
                          milliseconds: played
  
                      });
  
  
  
                      self.addTimer( timer_id, fn, interval );
  
                  }
  
              };
  
              self.addTimer( timer_id, fn, interval );
  
          }
  
      },
  
  
  
      /**
  
          Modify the slideshow delay
  
  
  
          @param {number} delay the number of milliseconds between slides,
  
  
  
          @returns Instance
  
      */
  
  
  
      setPlaytime: function( delay ) {
  
          this._playtime = delay;
  
          return this;
  
      },
  
  
  
      setIndex: function( val ) {
  
          this._active = val;
  
          return this;
  
      },
  
  
  
      /**
  
          Manually modify the counter
  
  
  
          @param {number} [index] Optional data index to fectch,
  
          if no index found it assumes the currently active index
  
  
  
          @returns Instance
  
      */
  
  
  
      setCounter: function( index ) {
  
  
  
          if ( typeof index === 'number' ) {
  
              index++;
  
          } else if ( typeof index === 'undefined' ) {
  
              index = this.getIndex()+1;
  
          }
  
  
  
          this.get( 'current' ).innerHTML = index;
  
  
  
          if ( IE ) { // weird IE bug
  
  
  
              var count = this.$( 'counter' ),
  
                  opacity = count.css( 'opacity' );
  
  
  
              if ( parseInt( opacity, 10 ) === 1) {
  
                  Utils.removeAlpha( count[0] );
  
              } else {
  
                  this.$( 'counter' ).css( 'opacity', opacity );
  
              }
  
  
  
          }
  
  
  
          return this;
  
      },
  
  
  
      /**
  
          Manually set captions
  
  
  
          @param {number} [index] Optional data index to fectch and apply as caption,
  
          if no index found it assumes the currently active index
  
  
  
          @returns Instance
  
      */
  
  
  
      setInfo : function( index ) {
  
  
  
          var self = this,
  
              data = this.getData( index );
  
  
  
          $.each( ['title','description'], function( i, type ) {
  
  
  
              var elem = self.$( 'info-' + type );
  
  
  
              if ( !!data[type] ) {
  
                  elem[ data[ type ].length ? 'show' : 'hide' ]().html( data[ type ] );
  
              } else {
  
                 elem.empty().hide();
  
              }
  
          });
  
  
  
          return this;
  
      },
  
  
  
      /**
  
          Checks if the data contains any captions
  
  
  
          @param {number} [index] Optional data index to fectch,
  
          if no index found it assumes the currently active index.
  
  
  
          @returns {boolean}
  
      */
  
  
  
      hasInfo : function( index ) {
  
  
  
          var check = 'title description'.split(' '),
  
              i;
  
  
  
          for ( i = 0; check[i]; i++ ) {
  
              if ( !!this.getData( index )[ check[i] ] ) {
  
                  return true;
  
              }
  
          }
  
          return false;
  
  
  
      },
  
  
  
      jQuery : function( str ) {
  
  
  
          var self = this,
  
              ret = [];
  
  
  
          $.each( str.split(','), function( i, elemId ) {
  
              elemId = $.trim( elemId );
  
  
  
              if ( self.get( elemId ) ) {
  
                  ret.push( elemId );
  
              }
  
          });
  
  
  
          var jQ = $( self.get( ret.shift() ) );
  
  
  
          $.each( ret, function( i, elemId ) {
  
              jQ = jQ.add( self.get( elemId ) );
  
          });
  
  
  
          return jQ;
  
  
  
      },
  
  
  
      /**
  
          Converts element IDs into a jQuery collection
  
          You can call for multiple IDs separated with commas.
  
  
  
          @param {string} str One or more element IDs (comma-separated)
  
  
  
          @returns jQuery
  
  
  
          @example this.$('info,container').hide();
  
      */
  
  
  
      $ : function( str ) {
  
          return this.jQuery.apply( this, Utils.array( arguments ) );
  
      }
  
  
  
  };
  
  
  
  // End of Galleria prototype
  
  
  
  // Add events as static variables
  
  $.each( _events, function( i, ev ) {
  
  
  
      // legacy events
  
      var type = /_/.test( ev ) ? ev.replace( /_/g, '' ) : ev;
  
  
  
      Galleria[ ev.toUpperCase() ] = 'galleria.'+type;
  
  
  
  } );
  
  
  
  $.extend( Galleria, {
  
  
  
      // Browser helpers
  
      IE9:     IE === 9,
  
      IE8:     IE === 8,
  
      IE7:     IE === 7,
  
      IE6:     IE === 6,
  
      IE:      IE,
  
      WEBKIT:  /webkit/.test( NAV ),
  
      CHROME:  /chrome/.test( NAV ),
  
      SAFARI:  /safari/.test( NAV ) && !(/chrome/.test( NAV )),
  
      QUIRK:   ( IE && doc.compatMode && doc.compatMode === "BackCompat" ),
  
      MAC:     /mac/.test( navigator.platform.toLowerCase() ),
  
      OPERA:   !!window.opera,
  
      IPHONE:  /iphone/.test( NAV ),
  
      IPAD:    /ipad/.test( NAV ),
  
      ANDROID: /android/.test( NAV ),
  
      TOUCH:   ('ontouchstart' in doc)
  
  
  
  });
  
  
  
  // Galleria static methods
  
  
  
  /**
  
      Adds a theme that you can use for your Gallery
  
  
  
      @param {Object} theme Object that should contain all your theme settings.
  
      <ul>
  
          <li>name - name of the theme</li>
  
          <li>author - name of the author</li>
  
          <li>css - css file name (not path)</li>
  
          <li>defaults - default options to apply, including theme-specific options</li>
  
          <li>init - the init function</li>
  
      </ul>
  
  
  
      @returns {Object} theme
  
  */
  
  
  
  Galleria.addTheme = function( theme ) {
  
  
  
      // make sure we have a name
  
      if ( !theme.name ) {
  
          Galleria.raise('No theme name specified');
  
      }
  
  
  
      if ( typeof theme.defaults !== 'object' ) {
  
          theme.defaults = {};
  
      } else {
  
          theme.defaults = _legacyOptions( theme.defaults );
  
      }
  
  
  
      var css = false,
  
          reg;
  
  
  
      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 ) {
  
  
  
              $('script').each(function( i, script ) {
  
  
  
                  // look for the theme script
  
                  reg = new RegExp( 'galleria\\.' + theme.name.toLowerCase() + '\\.' );
  
                  if( reg.test( script.src )) {
  
  
  
                      // we have a match
  
                      css = script.src.replace(/[^\/]*$/, '') + theme.css;
  
  
  
                      window.setTimeout(function() {
  
                          Utils.loadCSS( css, 'galleria-theme', function() {
  
  
  
                              // the themeload trigger
  
                              _themeLoad( theme );
  
  
  
                          });
  
                      }, 1);
  
  
  
                  }
  
              });
  
          }
  
  
  
          if ( !css ) {
  
              Galleria.raise('No theme CSS loaded');
  
          }
  
      } 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.theme ) {
  
                      Galleria.raise( "Galleria had problems loading theme at " + src + ". Please check theme path or load manually.", true );
  
                  }
  
              }, 20000);
  
          }
  
      });
  
  
  
      // first clear the current theme, if exists
  
      Galleria.unloadTheme();
  
  
  
      // load the theme
  
      Utils.loadScript( src, function() {
  
          loaded = true;
  
          window.clearTimeout( err );
  
      });
  
  
  
      return Galleria;
  
  };
  
  
  
  /**
  
      unloadTheme unloads the Galleria theme and prepares for a new theme
  
  
  
      @returns Galleria
  
  */
  
  
  
  Galleria.unloadTheme = function() {
  
  
  
      if ( typeof Galleria.theme == 'object' ) {
  
  
  
          $('script').each(function( i, script ) {
  
  
  
              if( new RegExp( 'galleria\\.' + Galleria.theme.name + '\\.' ).test( script.src ) ) {
  
                  $( script ).remove();
  
              }
  
          });
  
  
  
          Galleria.theme = undef;
  
      }
  
  
  
      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',
  
  
  
          self = this,
  
  
  
          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;
  
  
  
  /**
  
      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();
  
  
  
              this.image = $('<iframe>', {
  
                  src: src,
  
                  frameborder: 0,
  
                  id: id,
  
                  allowfullscreen: true,
  
                  css: { visibility: 'hidden' }
  
              })[0];
  
  
  
              $( 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' );
  
          }
  
  
  
          var i = 0,
  
              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 + '?' + 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 ).unbind( 'load' );
  
  
  
                          // save the original size
  
                          self.original = size || {
  
                              height: this.height,
  
                              width: this.width
  
                          };
  
  
  
                          self.container.appendChild( 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 ) ) {
  
                          window.setTimeout( (function( img ) {
  
                              return function() {
  
                                  if ( img.width && img.height ) {
  
                                      complete.call( img );
  
                                  } else {
  
                                      // last resort, this should never happen but just in case it does...
  
                                      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.');
  
                                      }
  
                                  }
  
                              };
  
                          }( this )), 2);
  
                      } 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 ).error( onerror ).attr( 'src', src );
  
  
  
          // return the container
  
          return this.container;
  
      },
  
  
  
      /**
  
          Scales and crops the image
  
  
  
          @param {Object} options The method takes an object with a number of options:
  
  
  
          <ul>
  
              <li>width - width of the container</li>
  
              <li>height - height of the container</li>
  
              <li>min - minimum scale ratio</li>
  
              <li>max - maximum scale ratio</li>
  
              <li>margin - distance in pixels from the image border to the container</li>
  
              <li>complete - a callback that fires when scaling is complete</li>
  
              <li>position - positions the image, works like the css background-image property.</li>
  
              <li>crop - defines how to crop. Can be true, false, 'width' or 'height'</li>
  
              <li>canvas - set to true to try a canvas-based rescale</li>
  
          </ul>
  
  
  
          @returns The image container object (jQuery)
  
      */
  
  
  
      scale: function( options ) {
  
  
  
          var self = this;
  
  
  
          // extend some defaults
  
          options = $.extend({
  
              width: 0,
  
              height: 0,
  
              min: undef,
  
              max: undef,
  
              margin: 0,
  
              complete: F,
  
              position: 'center',
  
              crop: false,
  
              canvas: false,
  
              iframelimit: undef
  
          }, options);
  
  
  
          if( this.isIframe ) {
  
  
  
              var cw = options.width, 
  
                  ch = options.height,
  
                  nw, nh;
  
              if ( options.iframelimit ) {
  
                  var r = Math.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 = Math.min( newWidth, newHeight ),
  
                      max = Math.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 = Math.min( options.max, ratio );
  
                  }
  
  
  
                  // allow minScaleRatio
  
                  if ( options.min ) {
  
                      ratio = Math.max( options.min, ratio );
  
                  }
  
  
  
                  $.each( ['width','height'], function( i, m ) {
  
                      $( self.image )[ m ]( self[ m ] = self.image[ m ] = Math.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 = Math.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;
  
      }
  
  
  
  });
  
  
  
  // 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 ) );
  
      });
  
  
  
  };
  
  
  
  // phew
  
  
  
  }( jQuery ) );
  
  