Team:SYSU-China/temp/galleria.js

From 2011.igem.org

/*!

* Galleria v 1.1.9 2010-07-10
* http://galleria.aino.se
*
* Copyright (c) 2010, Aino
* Licensed under the MIT license.
*/

(function() {

var initializing = false,

   fnTest = /xyz/.test(function(){xyz;}) ? /\b__super\b/ : /.*/,
   Class = function(){},
   window = this;

Class.extend = function(prop) {

   var __super = this.prototype;
   initializing = true;
   var proto = new this();
   initializing = false;
   for (var name in prop) {
       if (name) {
           proto[name] = typeof prop[name] == "function" && 
               typeof __super[name] == "function" && fnTest.test(prop[name]) ? 
               (function(name, fn) { 
                   return function() { 
                       var tmp = this.__super; 
                       this.__super = __super[name]; 
                       var ret = fn.apply(this, arguments);       
                       this.__super = tmp; 
                       return ret; 
                   }; 
               })(name, prop[name]) : prop[name]; 
       } 
   }
   function Class() {
       if ( !initializing && this.__constructor ) {
           this.__constructor.apply(this, arguments);
       }
   }
   Class.prototype = proto;
   Class.constructor = Class;
   Class.extend = arguments.callee;
   return Class;

};

var Base = Class.extend({

   loop : function( elem, fn) {
       var scope = this;
       if (typeof elem == 'number') {
           elem = new Array(elem);
       }
       jQuery.each(elem, function() {
           fn.call(scope, arguments[1], arguments[0]);
       });
       return elem;
   },
   create : function( elem, className ) {
       elem = elem || 'div';
       var el = document.createElement(elem);
       if (className) {
           el.className = className;
       }
       return el;
   },
   getElements : function( selector ) {
       var elems = {};
       this.loop( jQuery(selector), this.proxy(function( elem ) {
           this.push(elem, elems);
       }));
       return elems;
   },
   setStyle : function( elem, css ) {
       jQuery(elem).css(css);
       return this;
   },
   getStyle : function( elem, styleProp, parse ) {
       var val = jQuery(elem).css(styleProp);
       return parse ? this.parseValue( val ) : val;
   },
   cssText : function( string ) {
       var style = document.createElement('style');
       this.getElements('head')[0].appendChild(style);
       if (style.styleSheet) { // IE
           style.styleSheet.cssText = string;
       } else {
           var cssText = document.createTextNode(string);
           style.appendChild(cssText);
       }
       return this;
   },
   touch : function(el) {
       var sibling = el.nextSibling;
       if ( sibling ) {
         sibling.parentNode.removeChild(el);
         sibling.parentNode.insertBefore(el,sibling);
       } else {
         sibling = el.parentNode;
         sibling.removeChild(el);
         sibling.appendChild(el);
       }
   },
   loadCSS : function(href, callback) {
       var exists = this.getElements('link[href="'+href+'"]').length;
       if (exists) {
           callback.call(null);
           return exists[0];
       }
       var link = this.create('link');
       link.rel = 'stylesheet';
       link.href = href;
       
       if (typeof callback == 'function') {
           // a new css check method, still experimental...
           this.wait(function() {
               return !!document.body;
           }, function() {
               var testElem = this.create('div', 'galleria-container galleria-stage');
               this.moveOut(testElem);
               document.body.appendChild(testElem);
               var getStyles = this.proxy(function() {
                   var str = ;
                   var props;
                   if (document.defaultView && document.defaultView.getComputedStyle) {
                       props = document.defaultView.getComputedStyle(testElem, "");
                       this.loop(props, function(prop) {
                           str += prop + props.getPropertyValue(prop);
                       });
                   } else if (testElem.currentStyle) { // IE
                       props = testElem.currentStyle;
                       this.loop(props, function(val, prop) {
                           str += prop + val;
                       });
                   }
                   return str;
               });
               var current = getStyles();
               this.wait(function() {
                   return getStyles() !== current;
               }, function() {
                   document.body.removeChild(testElem);
                   callback.call(link);
               }, function() {
                   G.raise('Could not confirm theme CSS');
               }, 2000);
           });
       }
       window.setTimeout(this.proxy(function() {
           var styles = this.getElements('link[rel="stylesheet"],style');
           if (styles.length) {
               styles[0].parentNode.insertBefore(link, styles[0]);
           } else {
               this.getElements('head')[0].appendChild(link);
           }
           // IE needs a manual touch to re-order the cascade
           if (G.IE) {
               this.loop(styles, function(el) {
                   this.touch(el);
               })
           }
       }), 2);
       return link;
   },
   moveOut : function( elem ) {
       return this.setStyle(elem, {
           position: 'absolute',
           left: '-10000px',
           display: 'block'
       });
   },
   moveIn : function( elem ) {
       return this.setStyle(elem, {
           left: '0'
       }); 
   },
   reveal : function( elem ) {
       return jQuery( elem ).show();
   },
   hide : function( elem ) {
       return jQuery( elem ).hide();
   },
   mix : function() {
       return jQuery.extend.apply(null, arguments);
   },
   proxy : function( fn, scope ) {
       if ( typeof fn !== 'function' ) {
           return function() {};
       }
       scope = scope || this;
       return function() {
           return fn.apply( scope, Array.prototype.slice.call(arguments) );
       };
   },
   listen : function( elem, type, fn ) {
       jQuery(elem).bind( type, fn );
   },
   forget : function( elem, type ) {
       jQuery(elem).unbind(type);
   },
   dispatch : function( elem, type ) {
       jQuery(elem).trigger(type);
   },
   clone : function( elem, keepEvents ) {
       keepEvents = keepEvents || false;
       return jQuery(elem).clone(keepEvents)[0];
   },
   removeAttr : function( elem, attributes ) {
       this.loop( attributes.split(' '), function(attr) {
           jQuery(elem).removeAttr(attr);
       });
   },
   push : function( elem, obj ) {
       if (typeof obj.length == 'undefined') {
           obj.length = 0;
       }
       Array.prototype.push.call( obj, elem );
       return elem;
   },
   width : function( elem, outer ) {
       return this.meassure(elem, outer, 'Width');
   },
   height : function( elem, outer ) {
       return this.meassure(elem, outer, 'Height');
   },
   meassure : function(el, outer, meassure) {
       var elem = jQuery( el );
       var ret = outer ? elem['outer'+meassure](true) : elem[meassure.toLowerCase()]();
       // fix quirks mode
       if (G.QUIRK) {
           var which = meassure == "Width" ? [ "left", "right" ] : [ "top", "bottom" ];
           this.loop(which, function(s) {
               ret += elem.css('border-' + s + '-width').replace(/[^\d]/g,) * 1;
               ret += elem.css('padding-' + s).replace(/[^\d]/g,) * 1;
           });
       }
       return ret;
   },
   toggleClass : function( elem, className, arg ) {
       if (typeof arg !== 'undefined') {
           var fn = arg ? 'addClass' : 'removeClass';
           jQuery(elem)[fn](className);
           return this;
       }
       jQuery(elem).toggleClass(className);
       return this;
   },
   hideAll : function( el ) {
       jQuery(el).find('*').hide();
   },
   animate : function( el, options ) {
       var elem = jQuery(el);
       if (!elem.length) {
           return;
       }
       if (options.from) {
           elem.css(from);
       }
       elem.animate(options.to, {
           duration: options.duration || 400,
           complete: options.complete || function(){}
       });
   },
   wait : function(fn, callback, err, max) {
       fn = this.proxy(fn);
       callback = this.proxy(callback);
       err = this.proxy(err);
       var ts = new Date().getTime() + (max || 3000);
       window.setTimeout(function() {
           if (fn()) {
               callback();
               return false;
           }
           if (new Date().getTime() >= ts) {
               err();
               callback();
               return false;
           }
           window.setTimeout(arguments.callee, 2);
       }, 2);
       return this;
   },
   loadScript: function(url, callback) {
      var script = document.createElement('script');
      script.src = url;
      script.async = true; // HTML5
      var done = false;
      var scope = this;
      // Attach handlers for all browsers
      script.onload = script.onreadystatechange = function() {
          if ( !done && (!this.readyState ||
              this.readyState == "loaded" || this.readyState == "complete") ) {
              done = true;
              
              if (typeof callback == 'function') {
                  callback.call(scope, this);
              }
              // Handle memory leak in IE
              script.onload = script.onreadystatechange = null;
          }
      };
      var s = document.getElementsByTagName('script')[0];
      s.parentNode.insertBefore(script, s);
      
      return this;
   },
   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;
       }
   }

});

var Picture = Base.extend({

   __constructor : function(order) {
       this.image = null;
       this.elem = this.create('div', 'galleria-image');
       this.setStyle( this.elem, {
           overflow: 'hidden',
           position: 'relative' // for IE Standards mode
       } );
       this.order = order;
       this.orig = { w:0, h:0, r:1 };
   },
   
   cache: {},
   
   ready: false,
   outerWidth: 0,
   
   add: function(src) {
       if (this.cache[src]) {
           return this.cache[src];
       }
       var image = new Image();
       image.src = src;
       this.setStyle(image, {display: 'block'});
       if (image.complete && image.width) {
           this.cache[src] = image;
           return image;
       }
       image.onload = (function(scope) {
           return function() {
               scope.cache[src] = image;
           };
       })(this);
       return image;
   },
   
   isCached: function(src) {
       return this.cache[src] ? this.cache[src].complete : false;
   },
   
   make: function(src) {
       var i = this.cache[src] || this.add(src);
       return this.clone(i);
   },
   
   load: function(src, callback) {
       callback = this.proxy( callback );
       this.elem.innerHTML = ;
       this.image = this.make( src );
       this.moveOut( this.image );
       this.elem.appendChild( this.image );
       this.wait(function() {
           return (this.image.complete && this.image.width);
       }, function() {
           this.orig = {
               h: this.image.height,
               w: this.image.width
           };
           callback( {target: this.image, scope: this} );
       }, function() {
           G.raise('image not loaded in 10 seconds: '+ src);
       }, 10000);
       return this;
   },
   
   scale: function(options) {
       var o = this.mix({
           width: 0,
           height: 0,
           min: undefined,
           max: undefined,
           margin: 0,
           complete: function(){},
           position: 'center',
           crop: false
       }, options);
       if (!this.image) {
           return this;
       }
       var width,height;
       this.wait(function() {
           width  = o.width || this.width(this.elem);
           height = o.height || this.height(this.elem);
           return width && height;
       }, function() {
           var nw = (width - o.margin*2) / this.orig.w;
           var nh = (height- o.margin*2) / this.orig.h;
           var rmap = {
               'true': Math.max(nw,nh),
               'width': nw,
               'height': nh,
               'false': Math.min(nw,nh)
           }
           var ratio = rmap[o.crop.toString()];
           if (o.max) {
               ratio = Math.min(o.max, ratio);
           }
           if (o.min) {
               ratio = Math.max(o.min, ratio);
           }
           this.setStyle(this.elem, {
               width: width,
               height: height
           });
           this.image.width = Math.ceil(this.orig.w * ratio);
           this.image.height = Math.ceil(this.orig.h * ratio);
           
           var getPosition = this.proxy(function(value, img, m) {
               var result = 0;
               if (/\%/.test(value)) {
                   var pos = parseInt(value) / 100;
                   result = Math.ceil(this.image[img] * -1 * pos + m * pos - o.margin);
               } else {
                   result = parseInt(value) + o.margin;
               }
               return result;
           });
           
           var map = {
               'top': { top: 0 },
               'left': { left: 0 },
               'right': { left: '100%' },
               'bottom': { top: '100%' }
           }
           
           var pos = {};
           var mix = {};
           
           this.loop(o.position.toLowerCase().split(' '), function(p, i) {
               if (p == 'center') {
                   p = '50%';
               }
               pos[i ? 'top' : 'left'] = p;
           });
           this.loop(pos, function(val, key) {
               if (map.hasOwnProperty(val)) {
                   mix = this.mix(mix, map[val]);
               }
           });
           
           pos = pos.top ? this.mix(pos, mix) : mix;
           
           pos = this.mix({
               top: '50%',
               left: '50%'
           }, pos);
           
           this.setStyle(this.image, {
               position : 'relative',
               top :  getPosition(pos.top, 'height', height),
               left : getPosition(pos.left, 'width', width)
           });
           this.ready = true;
           o.complete.call(this);
       });
       return this;
   }

});

var tID; // the private timeout handler

var G = window.Galleria = Base.extend({

   __constructor : function(options) {
       this.theme = undefined;
       this.options = options;
       this.playing = false;
       this.playtime = 3000;
       this.active = null;
       this.queue = {};
       this.data = {};
       this.dom = {};
       this.controls = {
           active : 0,
           swap : function() {
               this.active = this.active ? 0 : 1;
           },
           getActive : function() {
               return this[this.active];
           },
           getNext : function() {
               return this[Math.abs(this.active - 1)];
           }
       };
       this.thumbnails = { width: 0 };
       this.stageWidth = 0;
       this.stageHeight = 0;
       
       var elems = 'container stage images image-nav image-nav-left image-nav-right ' + 
                   'info info-link info-text info-title info-description info-author info-close ' +
                   'thumbnails thumbnails-list thumbnails-container thumb-nav-left thumb-nav-right ' +
                   'loader counter';
       elems = elems.split(' ');
       
       this.loop(elems, function(blueprint) {
           this.dom[ blueprint ] = this.create('div', 'galleria-' + blueprint);
       });
   },
   
   init: function() {
       if (typeof this.options.target === 'undefined' ) {
           G.raise('No target.');
       }
       
       this.options = this.mix(G.theme.defaults, this.options);
       this.options = this.mix({
           autoplay: false,
           carousel: true,
           carousel_follow: true,
           carousel_speed: 400,
           carousel_steps: 'auto',
           data_config : function( elem ) { return {}; },
           data_image_selector: 'img',
           data_source: this.options.target,
           data_type: 'auto',
           debug: false,
           extend: function(options) {},
           height: 'auto',
           image_crop: false,
           image_margin: 0,
           image_position: '50%',
           keep_source: false,
           link_source_images: true,
           max_scale_ratio: undefined,
           min_scale_ratio: undefined,
           on_image: function(img,thumb) {},
           popup_links: false,
           preload: 2,
           queue: true,
           show: 0,
           thumb_crop: true,
           thumb_margin: 0,
           thumb_quality: 'auto',
           thumb_fit: true,
           thumbnails: true,
           transition: G.transitions.fade,
           transition_speed: 400
       }, this.options);
       
       var o = this.options;
       
       this.target = this.dom.target = this.getElements(o.target)[0];
       if (!this.target) {
            G.raise('Target not found.');
       }
       
       this.bind(G.DATA, function() {
           this.run();
       });
       
       this.bind(G.LOADFINISH, function(e) {
            o.on_image.call(this, e.imageTarget, e.thumbTarget);
       });
       
       this.bind(G.READY, function() {
           if (G.History) {
               G.History.change(this.proxy(function(e) {
                   var val = parseInt(e.value.replace(/\//,));
                   if (isNaN(val)) {
                       window.history.go(-1);
                   } else {
                       this.show(val, undefined, true);
                   }
               }));
           }
           G.theme.init.call(this, o);
           o.extend.call(this, o);
           
           if (/^[0-9]{1,4}$/.test(hash) && G.History) {
               this.show(hash, undefined, true);
           } else if (typeof o.show == 'number') {
               this.show(o.show);
           }
           
           if (o.autoplay) {
               if (typeof o.autoplay == 'number') {
                   this.play(o.autoplay);
               } else {
                   this.play();
               }
           }
       });
       this.load();
       return this;
   },
   
   bind : function(type, fn) {
       this.listen( this.get('container'), type, this.proxy(fn) );
       return this;
   },
   
   trigger : function( type ) {
       type = typeof type == 'object' ? 
           this.mix( type, { scope: this } ) : 
           { type: type, scope: this };
       this.dispatch( this.get('container'), type );
       return this;
   },
   run : function() {
       var o = this.options;
       if (!this.data.length) {
           G.raise('Data is empty.');
       }
       if (!o.keep_source && !Galleria.IE) {
           this.target.innerHTML = ;
       }
       this.loop(2, function() {
           var image = new Picture();
           this.setStyle( image.elem, {
               position: 'absolute',
               top: 0,
               left: 0
           });
           this.setStyle(this.get( 'images' ), {
               position: 'relative',
               top: 0,
               left: 0,
               width: '100%',
               height: '100%'
           });
           this.get( 'images' ).appendChild( image.elem );
           this.push(image, this.controls);
       }, this);
       
       if (o.carousel) {
           // try the carousel on each thumb load
           this.bind(G.THUMBNAIL, this.parseCarousel);
       }
       
       this.build();
       this.target.appendChild(this.get('container'));
       
       var w = 0;
       var h = 0;
       
       for( var i=0; this.data[i]; i++ ) {
           var thumb;
           if (o.thumbnails === true) {
               thumb = new Picture(i);
               var src = this.data[i].thumb || this.data[i].image;
               
               this.get( 'thumbnails' ).appendChild( thumb.elem );
               
               w = this.getStyle(thumb.elem, 'width', true);
               h = this.getStyle(thumb.elem, 'height', true);
               
               // grab & reset size for smoother thumbnail loads
               if (o.thumb_fit && o.thum_crop !== true) {
                   this.setStyle(thumb.elem, { width:0, height: 0});
               }
               
               thumb.load(src, this.proxy(function(e) {
                   var orig = e.target.width;
                   e.scope.scale({
                       width: w,
                       height: h,
                       crop: o.thumb_crop,
                       margin: o.thumb_margin,
                       complete: this.proxy(function() {
                           // shrink thumbnails to fit
                           var top = ['left', 'top'];
                           var arr = ['Height', 'Width'];
                           this.loop(arr, function(m,i) {
                               if ((!o.thumb_crop || o.thumb_crop == m.toLowerCase()) && o.thumb_fit) {
                                   var css = {};
                                   var opp = arr[Math.abs(i-1)].toLowerCase();
                                   css[opp] = e.target[opp];
                                   this.setStyle(e.target.parentNode, css);
                                   var css = {};
                                   css[top[i]] = 0;
                                   this.setStyle(e.target, css);
                               }
                               e.scope['outer'+m] = this[m.toLowerCase()](e.target.parentNode, true);
                           });
                           // set high quality if downscale is moderate
                           this.toggleQuality(e.target, o.thumb_quality === true || ( o.thumb_quality == 'auto' && orig < e.target.width * 3 ));
                           this.trigger({
                               type: G.THUMBNAIL,
                               thumbTarget: e.target,
                               thumbOrder: e.scope.order
                           });
                       })
                   });
               }));
               if (o.preload == 'all') {
                   thumb.add(this.data[i].image);
               }
           } else if (o.thumbnails == 'empty') {
               thumb = {
                   elem:  this.create('div','galleria-image'),
                   image: this.create('span','img')
               };
               thumb.elem.appendChild(thumb.image);
               this.get( 'thumbnails' ).appendChild( thumb.elem );
           } else {
               thumb = {
                   elem: false,
                   image: false
               }
           }
           var activate = this.proxy(function(e) {
               this.pause();
               e.preventDefault();
               var ind = e.currentTarget.rel;
               if (this.active !== ind) {
                   this.show( ind );
               }
           });
           if (o.thumbnails !== false) {
               thumb.elem.rel = i;
               this.listen(thumb.elem, 'click', activate);
           }
           if (o.link_source_images && o.keep_source && this.data[i].elem) {
               this.data[i].elem.rel = i;
               this.listen(this.data[i].elem, 'click', activate);
           }
           this.push(thumb, this.thumbnails );
       }
       this.setStyle( this.get('thumbnails'), { opacity: 0 } );
       
       if (o.height && o.height != 'auto') {
           this.setStyle( this.get('container'), { height: o.height })
       }
       
       this.wait(function() {
           // the most sensitive piece of code in Galleria, we need to have all the meassurements right to continue
           var cssHeight = this.getStyle( this.get( 'container' ), 'height', true );
           this.stageWidth = this.width(this.get( 'stage' ));
           this.stageHeight = this.height( this.get( 'stage' ));
           if (!this.stageHeight && o.height == 'auto') {
               // no height detected for sure, set reasonable ratio (16/9)
               this.setStyle( this.get( 'container' ),  { 
                   height: Math.round( this.stageWidth*9/16 ) 
               } );
               this.stageHeight = this.height( this.get( 'stage' ));
           }
           return this.stageHeight && this.stageWidth;
       }, function() {
           this.listen(this.get('image-nav-right'), 'click', this.proxy(function() {
               this.pause();
               this.next();
           }));
           this.listen(this.get('image-nav-left'), 'click', this.proxy(function() {
               this.pause();
               this.prev();
           }));
           this.setStyle( this.get('thumbnails'), { opacity: 1 } );
           this.trigger( G.READY );
       }, function() {
           G.raise('Galleria could not load properly. Make sure stage has a height and width.');
       }, 5000);
   },
   
   parseCarousel : function(e) {
       var w = 0;
       var h = 0;
       var hooks = [0];
       this.loop(this.thumbnails, function(thumb,i) {
           if (thumb.ready) {
               w += thumb.outerWidth || this.width(thumb.elem, true);
               hooks[i+1] = w;
               h = Math.max(h, thumb.image.height)
           }
       });
       this.toggleClass(this.get('thumbnails-container'), 'galleria-carousel', w > this.stageWidth);
       this.setStyle(this.get('thumbnails-list'), {
           overflow:'hidden',
           position: 'relative' // for IE Standards mode
       });
       this.setStyle(this.get('thumbnails'), {
           width: w,
           height: h,
           position: 'relative',
           overflow: 'hidden'
       });
       if (!this.carousel) {
           this.initCarousel();
       }
       this.carousel.max = w;
       this.carousel.hooks = hooks;
       this.carousel.width = this.width(this.get('thumbnails-list'));
       this.carousel.setClasses();
   },
   
   initCarousel : function() {
       var c = this.carousel = {
           right: this.get('thumb-nav-right'),
           left: this.get('thumb-nav-left'),
           update: this.proxy(function() {
               this.parseCarousel();
               // todo: fix so the carousel moves to the left
           }),
           width: 0,
           current: 0,
           set: function(i) {
               i = Math.max(i,0);
               while (c.hooks[i-1] + c.width > c.max && i >= 0) {
                   i--;
               }
               c.current = i;
               c.animate();
           },
           hooks: [],
           getLast: function(i) {
               i = i || c.current
               
               return i-1;
           },
           follow: function(i) {
               if (i == 0 || i == c.hooks.length-2) {
                   c.set(i);
                   return;
               }
               var last = c.current;
               while(c.hooks[last] - c.hooks[c.current] < c.width && last<= c.hooks.length) {
                   last++;
               }
               if (i-1 < c.current) {
                   c.set(i-1)
               } else if (i+2 > last) {
                   c.set(i - last + c.current + 2)
               }
           },
           max: 0,
           setClasses: this.proxy(function() {
               this.toggleClass( c.left, 'disabled', !c.current );
               this.toggleClass( c.right, 'disabled', c.hooks[c.current] + c.width > c.max );
           }),
           animate: this.proxy(function(to) {
               c.setClasses();
               this.animate( this.get('thumbnails'), {
                   to: { left: c.hooks[c.current] * -1 },
                   duration: this.options.carousel_speed,
                   easing: 'galleria',
                   queue: false
               });
           })
       };
       this.listen(c.right, 'click', this.proxy(function(e) {
           if (this.options.carousel_steps == 'auto') {
               for (var i = c.current; i<c.hooks.length; i++) {
                   if (c.hooks[i] - c.hooks[c.current] > c.width) {
                       c.set(i-2);
                       break;
                   }
               }
           } else {
               c.set(c.current + this.options.carousel_steps);
           }
       }));
       this.listen(c.left, 'click', this.proxy(function(e) {
           if (this.options.carousel_steps == 'auto') {
               for (var i = c.current; i>=0; i--) {
                   if (c.hooks[c.current] - c.hooks[i] > c.width) {
                       c.set(i+2);
                       break;
                   } else if (i == 0) {
                       c.set(0);
                       break;
                   }
               }
           } else {
               c.set(c.current - this.options.carousel_steps);
           }
       }));
   },
   addElement : function() {
       this.loop(arguments, function(b) {
           this.dom[b] = this.create('div', 'galleria-' + b );
       });
       return this;
   },
   getDimensions: function(i) {
       return {
           w: i.width,
           h: i.height,
           cw: this.stageWidth,
           ch: this.stageHeight,
           top: (this.stageHeight - i.height) / 2,
           left: (this.stageWidth - i.width) / 2
       };
   },
   attachKeyboard : function(map) {
       jQuery(document).bind('keydown', {map: map, scope: this}, this.keyNav);
       return this;
   },
   detachKeyboard : function() {
       jQuery(document).unbind('keydown', this.keyNav);
       return this;
   },
   keyNav : function(e) {
       var key = e.keyCode || e.which;
       var map = e.data.map;
       var scope = e.data.scope;
       var keymap = {
           UP: 38,
           DOWN: 40,
           LEFT: 37,
           RIGHT: 39,
           RETURN: 13,
           ESCAPE: 27,
           BACKSPACE: 8
       };
       for( var i in map ) {
           var k = i.toUpperCase();
           if ( keymap[k] ) {
               map[keymap[k]] = map[i];
           }
       }
       if (typeof map[key] == 'function') {
           map[key].call(scope, e);
       }
   },
   build : function() {
       this.append({
           'info-text' :
               ['info-title', 'info-description', 'info-author'],
           'info' : 
               ['info-link', 'info-text', 'info-close'],
           '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']
       });
   },
   
   appendChild : function(parent, child) {
       try {
           this.get(parent).appendChild(this.get(child));
       } catch(e) {}
   },
   
   append : function(data) {
       for( var i in data) {
           if (data[i].constructor == Array) {
               for(var j=0; data[i][j]; j++) {
                   this.appendChild(i, data[i][j]);
               }
           } else {
               this.appendChild(i, data[i]);
           }
       }
       return this;
   },
   
   rescale : function(width, height) {
       
       var o = this.options;
       
       var check = this.proxy(function() {
           this.stageWidth = width || this.width(this.get('stage'));
           this.stageHeight = height || this.height(this.get('stage'));
           return this.stageWidth && this.stageHeight;
       });
       if ( G.WEBKIT ) {
           this.wait(check);// wekit is too fast
       } else {
           check.call(this); 
       }
       this.controls.getActive().scale({
           width: this.stageWidth, 
           height: this.stageHeight, 
           crop: o.image_crop, 
           max: o.max_scale_ratio,
           min: o.min_scale_ratio,
           margin: o.image_margin,
           position: o.image_position
       });
       if (this.carousel) {
           this.carousel.update();
       }
       
   },
   
   show : function(index, rewind, history) {
       if (!this.options.queue && this.queue.stalled) {
           return;
       }
       rewind = typeof rewind != 'undefined' ? !!rewind : index < this.active;
       history = history || false;
       index = Math.max(0, Math.min(parseInt(index), this.data.length - 1));
       if (!history && G.History) {
           G.History.value(index.toString());
           return;
       }
       this.active = index;
       this.push([index,rewind], this.queue);
       if (!this.queue.stalled) {
           this.showImage();
       }
       return this;
   },
   
   showImage : function() {
       var o = this.options;
       var args = this.queue[0];
       var index = args[0];
       var rewind = !!args[1];
       if (o.carousel && this.carousel && o.carousel_follow) {
           this.carousel.follow(index);
       }
       
       var src = this.getData(index).image;
       var active = this.controls.getActive();
       var next = this.controls.getNext();
       var cached = next.isCached(src);
       var complete = this.proxy(function() {
           this.queue.stalled = false;
           this.toggleQuality(next.image, o.image_quality);
           this.setStyle( active.elem, { zIndex : 0 } );
           this.setStyle( next.elem, { zIndex : 1 } );
           this.controls.swap();
           this.moveOut( active.image );
           if (this.getData( index ).link) {
               this.setStyle( next.image, { cursor: 'pointer' } );
               this.listen( next.image, 'click', this.proxy(function() {
                   if (o.popup_links) {
                       var win = window.open(this.getData( index ).link, '_blank');
                   } else {
                       window.location.href = this.getData( index ).link;
                   }
               }));
           }
           Array.prototype.shift.call( this.queue );
           if (this.queue.length) {
               this.showImage();
           }
           this.playCheck();
       });
       if (typeof o.preload == 'number' && o.preload > 0) {
           var p,n = this.getNext();
           try {
               for (var i = o.preload; i>0; i--) {
                   p = new Picture();
                   p.add(this.getData(n).image);
                   n = this.getNext(n);
               }
           } catch(e) {}
       }
       this.trigger( {
           type: G.LOADSTART,
           cached: cached,
           imageTarget: next.image,
           thumbTarget: this.thumbnails[index].image
       } );
       next.load( src, this.proxy(function(e) {
           next.scale({
               width: this.stageWidth, 
               height: this.stageHeight, 
               crop: o.image_crop, 
               max: o.max_scale_ratio, 
               min: o.min_scale_ratio,
               margin: o.image_margin,
               position: o.image_position,
               complete: this.proxy(function() {
                   if (active.image) {
                       this.toggleQuality(active.image, false);
                   }
                   this.toggleQuality(next.image, false);
                   this.trigger({
                       type: G.LOADFINISH,
                       cached: cached,
                       imageTarget: next.image,
                       thumbTarget: this.thumbnails[index].image
                   });
                   this.queue.stalled = true;
                   var transition = G.transitions[o.transition] || o.transition;
                   if (typeof transition == 'function') {
                       transition.call(this, {
                           prev: active.image,
                           next: next.image,
                           rewind: rewind,
                           speed: o.transition_speed || 400
                       }, complete );
                   } else {
                       complete();
                   }
               })
           });
           this.setInfo(index);
           this.get('counter').innerHTML = '' + (index+1) + 
               ' / ' + this.thumbnails.length + '';
       }));
   },
   
   getNext : function(base) {
       base = base || this.active;
       return base == this.data.length - 1 ? 0 : base + 1;
   },
   
   getPrev : function(base) {
       base = base || this.active;
       return base === 0 ? this.data.length - 1 : base - 1;
   },
   
   next : function() {
       if (this.data.length > 1) {
           this.show(this.getNext(), false);
       }
       return this;
   },
   
   prev : function() {
       if (this.data.length > 1) {
           this.show(this.getPrev(), true);
       }
       return this;
   },
   
   get : function( elem ) {
       return this.dom[ elem ] || false;
   },
   
   getData : function( index ) {
       return this.data[index] || this.data[this.active];
   },
   
   play : function(delay) {
       this.playing = true;
       this.playtime = delay || this.playtime;
       this.playCheck();
       return this;
   },
   
   pause : function() {
       this.playing = false;
       return this;
   },
   
   playCheck : function() {
       if (this.playing) {
           window.clearInterval(tID);
           tID = window.setTimeout(this.proxy(function() {
               if (this.playing) {
                   this.next();
               }
           }), this.playtime);
       }
   },
   
   setActive: function(val) {
       this.active = val;
       return this;
   },
   
   setInfo : function(index) {
       var data = this.getData(index);
       var set = this.proxy(function() {
           this.loop(arguments, function(type) {
               var elem = this.get('info-'+type);
               var fn = data[type] && data[type].length ? 'reveal' : 'hide';
               this[fn](elem);
               elem.innerHTML = data[type];
           });
       });
       set('title','description','author');
       return this;
   },
   
   hasInfo : function(index) {
       var d = this.getData(index);
       var check = 'title description author'.split(' ');
       for ( var i=0; check[i]; i++ ) {
           if ( d[ check[i] ] && d[ check[i] ].length ) {
               return true;
           }
       }
       return false;
   },
   
   getDataObject : function(o) {
       var obj = {
           image: ,
           thumb: ,
           title: ,
           description: ,
           author: ,
           link: 
       };
       return o ? this.mix(obj,o) : obj;
   },
   
   jQuery : function( str ) {
       var ret = [];
       this.loop(str.split(','), this.proxy(function(elem) {
           elem = elem.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
           if (this.get(elem)) {
               ret.push(elem);
           }
       }));
       var jQ = jQuery(this.get(ret.shift()));
       this.loop(ret, this.proxy(function(elem) {
           jQ = jQ.add(this.get(elem));
       }));
       return jQ;
   },
   
   $ : function( str ) {
       return this.jQuery( str );
   },
   
   toggleQuality : function(img, force) {
       if (!G.IE7 || typeof img == 'undefined' || !img) {
           return this;
       }
       if (typeof force === 'undefined') {
           force = img.style.msInterpolationMode == 'nearest-neighbor';
       }
       img.style.msInterpolationMode = force ? 'bicubic' : 'nearest-neighbor';
       return this;
   },
   
   load : function() {
       var loaded = 0;
       var o = this.options;
       if (
           (o.data_type == 'auto' && 
               typeof o.data_source == 'object' && 
               !(o.data_source instanceof jQuery) && 
               !o.data_source.tagName
           ) || o.data_type == 'json' || o.data_source.constructor == Array ) {
           this.data = o.data_source;
           this.trigger( G.DATA );
           
       } else { // assume selector
           var images = jQuery(o.data_source).find(o.data_image_selector);
           var getData = this.proxy(function( elem ) {
               var i,j,anchor = elem.parentNode;
               if (anchor && anchor.nodeName == 'A') {
                   if (anchor.href.match(/\.(png|gif|jpg)/i)) {
                       i = anchor.href;
                   } else {
                       j = anchor.href;
                   }
               }
               var obj = this.getDataObject({
                   title: elem.title,
                   thumb: elem.src,
                   image: i || elem.src,
                   description: elem.alt,
                   link: j || elem.getAttribute('longdesc'),
                   elem: elem
               });
               return this.mix(obj, o.data_config( elem ) );
           });
           
           this.loop(images, function( elem ) {
               loaded++;
               this.push( getData( elem ), this.data );
               if (!o.keep_source && !Galleria.IE) {
                   elem.parentNode.removeChild(elem);
               }
               if ( loaded == images.length ) {
                   this.trigger( G.DATA );
               }
           });
       }
   }

});

G.log = function() {

   try { 
       console.log.apply( console, Array.prototype.slice.call(arguments) ); 
   } catch(e) {
       try {
           opera.postError.apply( opera, arguments ); 
       } catch(er) { 
             alert( Array.prototype.join.call( arguments, " " ) ); 
       } 
   }

};

var nav = navigator.userAgent.toLowerCase(); var hash = window.location.hash.replace(/#\//,);

G.DATA = 'data'; G.READY = 'ready'; G.THUMBNAIL = 'thumbnail'; G.LOADSTART = 'loadstart'; G.LOADFINISH = 'loadfinish'; G.THEMELOAD = 'themeload'; G.IE7 = !!(window.XMLHttpRequest && document.expando); G.IE6 = (!window.XMLHttpRequest); G.IE = !!(G.IE6 || G.IE7); G.WEBKIT = /webkit/.test( nav ); G.SAFARI = /safari/.test( nav ); G.CHROME = /chrome/.test( nav ); G.QUIRK = (G.IE && document.compatMode && document.compatMode == "BackCompat"); G.MAC = /mac/.test(navigator.platform.toLowerCase());

G.themes = {}; G.themes.create = G.addTheme = function(obj) {

   var theme = {};
   var orig = ['name','author','version','defaults','init'];
   var proto = G.prototype;
   proto.loop(orig, function(val) {
       if (!obj[ val ]) {
           G.raise(val+' not specified in theme.');
       }
       if (val != 'name' && val != 'init') {
           theme[val] = obj[val];
       }
   });
   theme.init = obj.init;
   
   if (obj.css) {
       var css;
       proto.loop(proto.getElements('script'), function(el) {
           var reg = new RegExp('galleria.' + obj.name.toLowerCase() + '.js');
           if(reg.test(el.src)) {
               css = el.src.replace(/[^\/]*$/, "") + obj.css;
               proto.loadCSS(css, function() {
                   G.theme = theme;
                   jQuery(document).trigger( G.THEMELOAD );
               });
           }
       });
       if (!css) {
           G.raise('No theme CSS loaded');
       }
   }
   return theme;

};

G.raise = function(msg) {

   if ( G.debug ) {
       throw new Error( msg );
   }

};

G.loadTheme = function(src) {

   G.prototype.loadScript(src);

};

jQuery.easing.galleria = function (x, t, b, c, d) {

   if ((t/=d/2) < 1) { 
       return c/2*t*t*t*t + b;
   }
   return -c/2 * ((t-=2)*t*t*t - 2) + b;

};

G.transitions = {

   add: function(name, fn) {
       if (name != arguments.callee.name ) {
           this[name] = fn;
       }
   },
   fade: function(params, complete) {
       jQuery(params.next).show().css('opacity',0).animate({
           opacity: 1
       }, params.speed, complete);
       if (params.prev) {
           jQuery(params.prev).css('opacity',1).animate({
               opacity: 0
           }, params.speed);
       }
   },
   flash: function(params, complete) {
       jQuery(params.next).css('opacity',0);
       if (params.prev) {
           jQuery(params.prev).animate({
               opacity: 0
           }, (params.speed/2), function() {
               jQuery(params.next).animate({
                   opacity: 1
               }, params.speed, complete);
           });
       } else {
           jQuery(params.next).animate({
               opacity: 1
           }, params.speed, complete);
       }
   },
   slide: function(params, complete) {
       var image = jQuery(params.next).parent();
       var images =  this.$('images');
       var width = this.stageWidth;
       image.css({
           left: width * ( params.rewind ? -1 : 1 )
       });
       images.animate({
           left: width * ( params.rewind ? 1 : -1 )
       }, {
           duration: params.speed,
           queue: false,
           easing: 'galleria',
           complete: function() {
               images.css('left',0);
               image.css('left',0);
               complete();
           }
       });
   },
   fadeslide: function(params, complete) {
       if (params.prev) {
           jQuery(params.prev).css({
               opacity: 1,
               left: 0
           }).animate({
               opacity: 0,
               left: 50 * ( params.rewind ? 1 : -1 )
           },{
               duration: params.speed,
               queue: false,
               easing: 'swing'
           });
       }
       jQuery(params.next).css({
           left: 50 * ( params.rewind ? -1 : 1 ), 
           opacity: 0
       }).animate({
           opacity: 1,
           left:0
       }, {
           duration: params.speed,
           complete: complete,
           queue: false,
           easing: 'swing'
       });
   }

};

jQuery.fn.galleria = function(options) {

   options = options || {};
   
   var selector = this.selector;
   if ( !options.keep_source ) {
       jQuery(this).children().hide();
   }
   
   options = G.prototype.mix(options, {target: selector } );
   var height = G.prototype.height(this) || G.prototype.getStyle(this, 'height', true);
   if (!options.height && height) {
       options = G.prototype.mix( { height: height }, options );
   }
   
   G.debug = !!options.debug;
   
   var gallery = new G(options);
   
   if (G.theme) {
       gallery.init();
   } else {
       jQuery(document).bind(G.THEMELOAD, function() {
           gallery.init();
       });
   }
   
   return gallery;
   

};


})();