You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by fe...@apache.org on 2011/10/09 15:46:27 UTC
svn commit: r1180610 [2/6] - in
/james/trunk/sandbox/felixk/james-parent/src: ./ reporting-site/ site/
site/resources/ site/resources/css/ site/resources/downloads/
site/resources/images/ site/resources/js/ site/resources/js/galleria/
site/resources/js...
Added: james/trunk/sandbox/felixk/james-parent/src/site/resources/js/galleria/galleria.js
URL: http://svn.apache.org/viewvc/james/trunk/sandbox/felixk/james-parent/src/site/resources/js/galleria/galleria.js?rev=1180610&view=auto
==============================================================================
--- james/trunk/sandbox/felixk/james-parent/src/site/resources/js/galleria/galleria.js (added)
+++ james/trunk/sandbox/felixk/james-parent/src/site/resources/js/galleria/galleria.js Sun Oct 9 13:46:24 2011
@@ -0,0 +1,3765 @@
+/*
+ * Galleria v 1.2 prerelease 1.1 2010-11-04
+ * http://galleria.aino.se
+ *
+ * Copyright (c) 2010, Aino
+ * Licensed under the MIT license.
+ */
+
+(function($) {
+
+// some references
+var undef,
+ window = this,
+ doc = document,
+ $doc = $( doc );
+
+// internal constants
+var DEBUG = false,
+ NAV = navigator.userAgent.toLowerCase(),
+ HASH = window.location.hash.replace(/#\//, ''),
+ CLICK = function() {
+ // use this to make touch devices snappier
+ return Galleria.TOUCH ? 'touchstart' : 'click';
+ },
+ IE = (function() {
+ var v = 3,
+ div = doc.createElement( 'div' );
+ while (
+ div.innerHTML = '<!--[if gt IE '+(++v)+']><i></i><![endif]-->',
+ div.getElementsByTagName('i')[0]
+ );
+ return v > 4 ? v : undef;
+ }() ),
+ DOM = function() {
+ return {
+ html: doc.documentElement,
+ body: doc.body,
+ head: doc.getElementsByTagName('head')[0],
+ title: doc.title
+ };
+ },
+
+ // the internal timeouts object
+ // provides helper methods for controlling timeouts
+ _timeouts = {
+
+ trunk: {},
+
+ add: function( id, fn, delay, loop ) {
+ loop = loop || false;
+ this.clear( id );
+ if ( loop ) {
+ var old = fn;
+ fn = function() {
+ old();
+ _timeouts.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 ];
+ };
+
+ if ( !!id && id in this.trunk ) {
+ del.call( _timeouts, id );
+
+ } else if ( typeof id == 'undefined' ) {
+ for ( var i in this.trunk ) {
+ del.call( _timeouts, i );
+ }
+ }
+ }
+ },
+
+ // the internal gallery holder
+ _galleries = [],
+
+ // the transitions holder
+ _transitions = {
+
+ fade: function(params, complete) {
+ $(params.next).css('opacity', 0).show().animate({
+ opacity: 1
+ }, params.speed, complete);
+
+ if (params.prev) {
+ $(params.prev).css('opacity', 1).show().animate({
+ opacity: 0
+ }, params.speed);
+ }
+ },
+
+ flash: function(params, complete) {
+ $(params.next).css('opacity', 0);
+ if (params.prev) {
+ $(params.prev).animate({
+ opacity: 0
+ }, (params.speed / 2), function() {
+ $(params.next).animate({
+ opacity: 1
+ }, params.speed, complete);
+ });
+ } else {
+ $(params.next).animate({
+ opacity: 1
+ }, params.speed, complete);
+ }
+ },
+
+ pulse: function(params, complete) {
+ if (params.prev) {
+ $(params.prev).hide();
+ }
+ $(params.next).css('opacity', 0).animate({
+ opacity:1
+ }, params.speed, complete);
+ },
+
+ slide: function(params, complete) {
+ var image = $(params.next).parent(),
+ images = this.$('images'), // ??
+ width = this._stageWidth,
+ easing = this.getOptions( 'easing' );
+
+ image.css({
+ left: width * ( params.rewind ? -1 : 1 )
+ });
+ images.animate({
+ left: width * ( params.rewind ? 1 : -1 )
+ }, {
+ duration: params.speed,
+ queue: false,
+ easing: easing,
+ complete: function() {
+ images.css('left', 0);
+ image.css('left', 0);
+ complete();
+ }
+ });
+ },
+
+ fadeslide: function(params, complete) {
+
+ var x = 0,
+ easing = this.getOptions('easing'),
+ distance = this.getStageWidth();
+
+ if (params.prev) {
+ x = Utils.parseValue( $(params.prev).css('left') );
+ $(params.prev).css({
+ opacity: 1,
+ left: x
+ }).animate({
+ opacity: 0,
+ left: x + ( distance * ( params.rewind ? 1 : -1 ) )
+ },{
+ duration: params.speed,
+ queue: false,
+ easing: easing
+ });
+ }
+
+ x = Utils.parseValue( $(params.next).css('left') );
+
+ $(params.next).css({
+ left: x + ( distance * ( params.rewind ? -1 : 1 ) ),
+ opacity: 0
+ }).animate({
+ opacity: 1,
+ left: x
+ }, {
+ duration: params.speed,
+ complete: complete,
+ queue: false,
+ easing: easing
+ });
+ }
+ },
+
+ // the Utils singleton
+ Utils = (function() {
+
+ return {
+
+ array : function( obj ) {
+ return Array.prototype.slice.call(obj);
+ },
+
+ create : function( className, nodeName ) {
+ nodeName = nodeName || 'div';
+ var elem = doc.createElement( nodeName );
+ elem.className = className;
+ return elem;
+ },
+
+ 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 ).removeAttr( 'style' );
+
+ if ( elem.data( 'styles' ) ) {
+ elem.attr( 'style', elem.data('styles') ).data( 'styles', null );
+ }
+ });
+ },
+
+ moveOut : function( elem ) {
+ Utils.forceStyles( elem, {
+ position: 'absolute',
+ left: -10000
+ });
+ },
+
+ moveIn : function() {
+ Utils.revertStyles.apply( Utils, Utils.array( arguments ) );
+ },
+
+ hide : function( elem, speed, callback ) {
+ elem = $(elem);
+
+ // save the value if not exist
+ if (! elem.data('opacity') ) {
+ elem.data('opacity', elem.css('opacity') );
+ }
+
+ // always hide
+ var style = { opacity: 0 };
+
+ if (speed) {
+ elem.stop().animate( style, speed, callback );
+ } else {
+ elem.css( style );
+ };
+ },
+
+ show : function( elem, speed, callback ) {
+ elem = $(elem);
+
+ // bring back saved opacity
+ var saved = parseFloat( elem.data('opacity') ) || 1,
+ style = { opacity: saved };
+
+ // reset save if opacity == 1
+ if (saved == 1) {
+ elem.data('opacity', null);
+ }
+
+ // animate or toggle
+ if (speed) {
+ elem.stop().animate( style, speed, callback );
+ } else {
+ elem.css( style );
+ };
+ },
+
+ addTimer : function() {
+ _timeouts.add.apply( _timeouts, Utils.array( arguments ) );
+ return this;
+ },
+
+ clearTimer : function() {
+ _timeouts.clear.apply( _timeouts, Utils.array( arguments ) );
+ return this;
+ },
+
+ wait : function(options) {
+ options = $.extend({
+ until : function() { return false; },
+ success : function() {},
+ error : function() { Galleria.raise('Could not complete wait function.'); },
+ timeout: 3000
+ }, options);
+
+ var start = Utils.timestamp(),
+ elapsed,
+ now;
+
+ window.setTimeout(function() {
+ now = Utils.timestamp();
+ elapsed = now - start;
+ if ( options.until( elapsed ) ) {
+ options.success();
+ return false;
+ }
+
+ if (now >= start + options.timeout) {
+ options.error();
+ return false;
+ }
+ window.setTimeout(arguments.callee, 2);
+ }, 2);
+ },
+
+ toggleQuality : function( img, force ) {
+
+ if ( !( IE == 7 || IE == 8 ) || !!img === false ) {
+ return;
+ }
+
+ if ( typeof force === 'undefined' ) {
+ force = img.style.msInterpolationMode == 'nearest-neighbor';
+ }
+
+ img.style.msInterpolationMode = force ? 'bicubic' : 'nearest-neighbor';
+ },
+
+ insertStyleTag : function( styles ) {
+ var style = doc.createElement( 'style' );
+ 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;
+
+ if (typeof callback == 'function') {
+ callback.call( this, this );
+ }
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ }
+ };
+
+ var s = doc.getElementsByTagName( 'script' )[0];
+ s.parentNode.insertBefore( script, s );
+ },
+
+ // 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();
+ },
+
+ // this is pretty crap, but works for now
+ // it will add a callback, but it can't guarantee that the styles can be fetched
+ // using getComputedStyle further checking needed, possibly a dummy element
+ loadCSS : function( href, id, callback ) {
+
+ var link,
+ ready = false,
+ 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 || function() {}; // 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;
+
+ // add timestamp if DEBUG is true
+ if ( DEBUG ) {
+ href += '?' + Utils.timestamp();
+ }
+
+ // check for existing id
+ if( $('#'+id).length ) {
+ $('#'+id).attr('href', href);
+ length--;
+ ready = true;
+ } else {
+ link = $( '<link>' ).attr({
+ rel: 'stylesheet',
+ href: href,
+ id: id
+ }).get(0);
+
+ window.setTimeout(function() {
+ var styles = $('link[rel="stylesheet"], style');
+ if ( styles.length ) {
+ styles.get(0).parentNode.insertBefore( link, styles[0] );
+ } else {
+ DOM().head.appendChild( link );
+ }
+
+ if ( IE ) {
+ link.attachEvent( 'onreadystatechange', function(e) {
+ if( link.readyState == 'complete' ) {
+ ready = true;
+ }
+ });
+ } else {
+ // what to do here? returning for now.
+ ready = true;
+ }
+ }, 10);
+ }
+
+ if (typeof callback == 'function') {
+
+ Utils.wait({
+ until: function() {
+ return ready && doc.styleSheets.length > length;
+ },
+ success: function() {
+ Utils.addTimer( 'css', function() {
+ callback.call( link, link );
+ }, 100);
+ },
+ error: function() {
+ Galleria.raise( 'Theme CSS could not load' );
+ },
+ timeout: 1000
+ });
+ }
+ return link;
+ }
+ };
+ })();
+
+/**
+ The main Galleria class
+
+ @class
+
+ @example var gallery = new Galleria();
+
+ @author http://aino.se
+
+ @requires jQuery
+
+ @returns {Galleria}
+*/
+
+Galleria = function() {
+
+ var self = this;
+
+ // the theme used
+ this._theme = undef;
+
+ // 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 = [];
+
+ // internal init flag
+ this._initialized = false;
+
+ // global stagewidth/height
+ this._stageWidth = 0;
+ this._stageHeight = 0;
+
+ // target holder
+ this._target = undef;
+
+ // instance id
+ this._id = Utils.timestamp();
+
+ // add some elements
+ var divs = 'container stage images image-nav image-nav-left image-nav-right ' +
+ 'info info-text info-title info-description info-author ' +
+ '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) {
+ for( var key in map ) {
+ var up = key.toUpperCase();
+ if ( up in keyboard.keys ) {
+ keyboard.map[ keyboard.keys[up] ] = map[key];
+ }
+ }
+ if ( !keyboard.bound ) {
+ keyboard.bound = true;
+ $doc.bind('keydown', keyboard.press);
+ }
+ },
+
+ detach: function() {
+ keyboard.bound = false;
+ $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() );
+ }
+ });
+ self.$( 'thumbnails-container' ).toggleClass( 'galleria-carousel', w > self._stageWidth );
+
+ self.$( 'thumbnails' ).css({
+ width: w,
+ height: h
+ });
+
+ carousel.max = w;
+ carousel.hooks = hooks;
+ carousel.width = self.$( 'thumbnails-list' ).width();
+ carousel.setClasses();
+
+ // todo: fix so the carousel moves to the left
+ },
+
+ bindControls: function() {
+
+ carousel.next.bind( CLICK(), function(e) {
+ e.preventDefault();
+
+ if ( self._options.carousel_steps == 'auto' ) {
+
+ for ( var 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.carousel_steps);
+ }
+ });
+
+ carousel.prev.bind( CLICK(), function(e) {
+ e.preventDefault();
+
+ if ( self._options.carousel_steps == 'auto' ) {
+
+ for ( var 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.carousel_steps );
+ }
+ });
+ },
+
+ // 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;
+ }
+
+ self.$( 'thumbnails' ).animate({
+ left: num
+ },{
+ duration: self._options.carousel_speed,
+ easing: self._options.easing,
+ queue: false
+ });
+ }
+ };
+
+ // tooltip control
+ // added in 1.2
+ var tooltip = this._tooltip = {
+
+ initialized : false,
+
+ active: null,
+
+ open: false,
+
+ 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);
+
+ self.$( 'tooltip' ).css('opacity', .8);
+ Utils.hide( self.get('tooltip') );
+
+ },
+
+ // 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._stageWidth - width,
+ maxY = self._stageHeight - height;
+
+ if ( !isNaN(x) && !isNaN(y) ) {
+
+ x += 15;
+ y -= 35;
+
+ 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 ) {
+
+ if (! tooltip.initialized ) {
+ tooltip.init();
+ }
+
+ var hover = function( elem, value) {
+
+ tooltip.define( elem, value );
+
+ $( elem ).hover(function() {
+
+ tooltip.active = elem;
+ Utils.clearTimer('switch_tooltip');
+ self.$('container').unbind( 'mousemove', tooltip.move ).bind( 'mousemove', tooltip.move ).trigger( 'mousemove' );
+ tooltip.show( elem );
+
+ Galleria.utils.addTimer( 'tooltip', function() {
+ self.$( 'tooltip' ).stop();
+ Utils.show( self.get( 'tooltip' ), 400 );
+ tooltip.open = true;
+
+ }, tooltip.open ? 0 : 1000);
+
+ }, function() {
+
+ tooltip.active = null;
+
+ self.$( 'container' ).unbind( 'mousemove', tooltip.move );
+ Utils.clearTimer( 'tooltip' );
+
+ Utils.hide( self.get( 'tooltip' ), 200, function() {
+ Utils.addTimer('switch_tooltip', function() {
+ tooltip.open = false;
+ }, 1000);
+ });
+ });
+ };
+
+ 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 ) {
+ var text = $(elem).data('tt');
+ if ( ! text ) {
+ return;
+ }
+ text = typeof text == 'function' ? text() : text;
+ self.$( 'tooltip' ).html( text.replace(/\s/, ' ') );
+ },
+
+ // redefine the tooltip here
+ define: function( elem, value ) {
+
+ // we store functions, not strings
+ if (typeof value !== 'function') {
+ var s = value;
+ value = function() {
+ return s;
+ };
+ }
+
+ if ( elem in self._dom ) {
+ elem = self.get( elem );
+ }
+
+ $(elem).data('tt', value);
+
+ tooltip.show( elem );
+
+ },
+
+ refresh: function() {
+ $.each( arguments, function(i, elem) {
+ if ( tooltip.active == elem ) {
+ tooltip.show( elem );
+ }
+ });
+ }
+ };
+
+ // internal fullscreen control
+ // added in 1.195
+ // still kind of experimental
+ var fullscreen = this._fullscreen = {
+ scrolled: 0,
+ enter: function(callback) {
+
+ // hide the image until rescale is complete
+ Utils.hide( self.getActiveImage() );
+
+ self.$( 'container' ).addClass( 'fullscreen' );
+
+ fullscreen.scrolled = $(window).scrollTop();
+
+ // begin styleforce
+ Utils.forceStyles(self.get('container'), {
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ width: '100%',
+ height: '100%',
+ zIndex: 10000
+ });
+
+ var htmlbody = {
+ height: '100%',
+ overflow: 'hidden',
+ margin:0,
+ padding:0
+ };
+
+ Utils.forceStyles( DOM().html, htmlbody );
+ Utils.forceStyles( DOM().body, htmlbody );
+
+ // attach some keys
+ self.attachKeyboard({
+ escape: self.exitFullscreen,
+ right: self.next,
+ left: self.prev
+ });
+
+ // init the first rescale and attach callbacks
+ self.rescale(function() {
+
+ Utils.addTimer('fullscreen_enter', function() {
+ // show the image after 50 ms
+ Utils.show( self.getActiveImage() );
+
+ if (typeof callback == 'function') {
+ callback.call( self );
+ }
+
+ }, 100);
+
+ self.trigger( Galleria.FULLSCREEN_ENTER );
+ });
+
+ // bind the scaling to the resize event
+ $(window).resize( function() {
+ fullscreen.scale();
+ } );
+ },
+
+ scale : function() {
+ self.rescale();
+ },
+
+ exit: function(callback) {
+
+ Utils.hide( self.getActiveImage() );
+
+ self.$('container').removeClass( 'fullscreen' );
+
+ // revert all styles
+ Utils.revertStyles( self.get('container'), DOM().html, DOM().body );
+
+ // scroll back
+ window.scrollTo(0, fullscreen.scrolled);
+
+ // detach all keyboard events (is this good?)
+ self.detachKeyboard();
+
+ self.rescale(function() {
+ Utils.addTimer('fullscreen_exit', function() {
+
+ // show the image after 50 ms
+ Utils.show( self.getActiveImage() );
+
+ if ( typeof callback == 'function' ) {
+ callback.call( self );
+ }
+
+ }, 50);
+
+ self.trigger( Galleria.FULLSCREEN_EXIT );
+ });
+
+ $(window).unbind('resize', fullscreen.scale);
+ }
+ };
+
+ // the internal idle object for controlling idle states
+ // TODO occational event conflicts
+ var idle = this._idle = {
+
+ trunk: [],
+
+ bound: false,
+
+ add: function(elem, to) {
+ if (!elem) {
+ return;
+ }
+ if (!idle.bound) {
+ idle.addEvent();
+ }
+ elem = $(elem);
+
+ var from = {};
+
+ for (var style in to) {
+ from[style] = elem.css(style);
+ }
+ elem.data('idle', {
+ from: from,
+ to: to,
+ complete: true,
+ busy: false
+ });
+ idle.addTimer();
+ idle.trunk.push(elem);
+ },
+
+ remove: function(elem) {
+
+ elem = jQuery(elem);
+
+ $.each(idle.trunk, function(i, el) {
+ if ( el.length && !el.not(elem).length ) {
+ self._idle.show(elem);
+ self._idle.trunk.splice(i, 1);
+ }
+ });
+
+ if (!idle.trunk.length) {
+ idle.removeEvent();
+ Utils.clearTimer('idle');
+ }
+ },
+
+ addEvent : function() {
+ idle.bound = true;
+ self.$('container').bind('mousemove click', idle.showAll );
+ },
+
+ removeEvent : function() {
+ idle.bound = false;
+ self.$('container').unbind('mousemove click', idle.showAll );
+ },
+
+ addTimer : function() {
+ Utils.addTimer('idle', function() {
+ self._idle.hide();
+ }, self._options.idle_time );
+ },
+
+ hide : function() {
+ self.trigger( Galleria.IDLE_ENTER );
+
+ $.each( idle.trunk, function(i, elem) {
+
+ var data = elem.data('idle');
+
+ if (! data) {
+ return;
+ }
+
+ data.complete = false;
+
+ elem.stop().animate(data.to, {
+ duration: 600,
+ queue: false,
+ easing: 'swing'
+ });
+ });
+ },
+
+ showAll : function() {
+ Utils.clearTimer('idle');
+
+ $.each(self._idle.trunk, function( i, elem ) {
+ self._idle.show( elem );
+ });
+ },
+
+ show: function(elem) {
+
+ var data = elem.data('idle');
+
+ if (!data.busy && !data.complete) {
+
+ data.busy = true;
+
+ self.trigger( Galleria.IDLE_EXIT );
+
+ elem.animate(data.from, {
+ duration: 300,
+ queue: false,
+ easing: 'swing',
+ complete: function() {
+ $(this).data('idle').busy = false;
+ $(this).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 : {},
+
+ 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 = '',
+ cssMap = {
+ overlay: 'position:fixed;display:none;opacity:'+op.overlay_opacity+';top:0;left:0;width:100%;height:100%;background:'+op.overlay_background+';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: 'position:absolute;background:#000;width:100%;height:100%;',
+ content: 'position:absolute;background-color:#fff;top:10px;left:10px;right:10px;bottom:10px;overflow:hidden',
+ info: 'position:absolute;bottom:10px;left:10px;right:10px;color:#444;font:11px/13px arial,sans-serif;height:13px',
+ close: 'position:absolute;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: 'position:absolute;top:10px;left:10px;right:10px;bottom:30px;overflow:hidden',
+ prevholder: 'position:absolute;width:50%;height:100%;cursor:pointer',
+ nextholder: 'position:absolute;width:50%;height:100%;right:0;cursor:pointer',
+ prev: 'position:absolute;top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;left:20px;display:none;line-height:40px;text-align:center;color:#000',
+ next: 'position:absolute;top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;right:20px;left:auto;display:none;line-height:40px;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' ); }
+ );
+ };
+
+ // create and insert CSS
+ $.each(cssMap, function( key, value ) {
+ css += '.galleria-lightbox-'+key+'{'+value+'}';
+ });
+
+ Utils.insertStyleTag( css );
+
+ // 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
+ self.append({
+ 'lightbox-box': ['lightbox-shadow','lightbox-content', 'lightbox-close','lightbox-prevholder','lightbox-nextholder'],
+ 'lightbox-info': ['lightbox-title','lightbox-counter'],
+ 'lightbox-content': ['lightbox-info', 'lightbox-image'],
+ 'lightbox-prevholder': 'lightbox-prev',
+ 'lightbox-nextholder': 'lightbox-next'
+ });
+
+ $( el.image ).append( lightbox.image.container );
+
+ $( DOM().body ).append( el.overlay, el.box );
+
+ // add the prev/next nav and bind some controls
+
+ hover( $( el.close ).bind( CLICK(), lightbox.hide ).html('×') );
+
+ $.each( ['Prev','Next'], function(i, dir) {
+
+ var $d = $( el[ dir.toLowerCase() ] ).html( /v/.test( dir ) ? 'â¹ ' : ' âº' );
+
+ $( el[ dir.toLowerCase()+'holder'] ).hover(function() {
+ $d.show();
+ }, function() {
+ $d.fadeOut( 200 );
+ }).bind( CLICK(), function() {
+ lightbox[ 'show' + dir ]();
+ });
+
+ });
+ $( el.overlay ).bind( CLICK(), lightbox.hide );
+
+ },
+
+ rescale: function(event) {
+
+ // calculate
+ var width = Math.min( $(window).width(), lightbox.width ),
+ height = Math.min( $(window).height(), lightbox.height ),
+ ratio = Math.min( (width - 60) / lightbox.width, (height - 80) / lightbox.height ),
+ destWidth = ( lightbox.width * ratio ) + 40,
+ destHeight = ( lightbox.height * ratio ) + 60,
+ to = {
+ width: destWidth,
+ height: destHeight,
+ marginTop: Math.ceil( destHeight / 2 ) *- 1,
+ marginLeft: Math.ceil( destWidth / 2 ) *- 1
+ };
+
+ // if rescale event, don't animate
+ if ( event ) {
+ $( lightbox.elems.box ).css( to );
+ } else {
+ $( lightbox.elems.box ).animate(
+ to,
+ self._options.lightbox_transition_speed,
+ self._options.easing,
+ function() {
+ var image = lightbox.image,
+ speed = self._options.lightbox_fade_speed;
+
+ self.trigger({
+ type: Galleria.LIGHTBOX_IMAGE,
+ imageTarget: image.image
+ });
+
+ image.show();
+ Utils.show( image.image, speed );
+ Utils.show( lightbox.elems.info, speed );
+ }
+ );
+ }
+ },
+
+ hide: function() {
+
+ // remove the image
+ lightbox.image.image = null;
+
+ $(window).unbind('resize', lightbox.rescale);
+
+ $( lightbox.elems.box ).hide();
+
+ Utils.hide( lightbox.elems.info );
+
+ Utils.hide( lightbox.elems.overlay, 200, function() {
+ $( this ).hide().css( 'opacity', self._options.overlay_opacity );
+ 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();
+
+ if ( !lightbox.initialized ) {
+ lightbox.init();
+ }
+
+ $(window).unbind('resize', lightbox.rescale );
+
+ var data = self.getData(index),
+ total = self.getDataLength();
+
+ Utils.hide( lightbox.elems.info );
+
+ lightbox.image.load( data.image, function( image ) {
+
+ lightbox.width = image.original.width;
+ lightbox.height = image.original.height;
+
+ $( image.image ).css({
+ width: '100.5%',
+ height: '100.5%',
+ top: 0,
+ zIndex: 99998,
+ opacity: 0
+ });
+
+ lightbox.elems.title.innerHTML = data.title;
+ lightbox.elems.counter.innerHTML = (index + 1) + ' / ' + total;
+ $(window).resize( lightbox.rescale );
+ lightbox.rescale();
+ });
+
+ $( lightbox.elems.overlay ).show();
+ $( lightbox.elems.box ).show();
+ }
+ };
+
+ return this;
+};
+
+// end Galleria constructor
+
+Galleria.prototype = {
+
+ /**
+ Use this function to initialize the gallery and start loading.
+ Should only be called once per instance.
+
+ @param {HTML Element} target The target element
+ @param {Object} options The gallery options
+
+ @returns {Galleria}
+ */
+
+ init: function( target, options ) {
+
+ var self = this;
+
+ // save the instance
+ _galleries.push( this );
+
+ // 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);
+
+ // raise error if no target is detected
+ if ( !this._target ) {
+ Galleria.raise('Target not found.');
+ return;
+ }
+
+ // apply options
+ this._options = {
+ autoplay: false,
+ carousel: true,
+ carousel_follow: true,
+ carousel_speed: 400,
+ carousel_steps: 'auto',
+ clicknext: false,
+ data_config : function( elem ) { return {}; },
+ data_selector: 'img',
+ data_source: this._target,
+ debug: undef,
+ easing: 'galleria',
+ extend: function(options) {},
+ height: 'auto',
+ idle_time: 3000,
+ image_crop: false,
+ image_margin: 0,
+ image_pan: false,
+ image_pan_smoothness: 12,
+ image_position: '50%',
+ keep_source: false,
+ lightbox_fade_speed: 200,
+ lightbox_transition_speed: 500,
+ link_source_images: true,
+ max_scale_ratio: undef,
+ min_scale_ratio: undef,
+ on_image: function(img,thumb) {},
+ overlay_opacity: .85,
+ overlay_background: '#0b0b0b',
+ pause_on_interaction: true, // 1.9.96
+ popup_links: false,
+ preload: 2,
+ queue: true,
+ show: 0,
+ show_info: true,
+ show_counter: true,
+ show_imagenav: true,
+ thumb_crop: true,
+ thumb_event_type: CLICK(),
+ thumb_fit: true,
+ thumb_margin: 0,
+ thumb_quality: 'auto',
+ thumbnails: true,
+ transition: 'fade',
+ transition_initial: undef,
+ transition_speed: 400,
+ width: 'auto'
+ };
+
+ // apply debug
+ if ( options && options.debug === true ) {
+ DEBUG = true;
+ }
+
+ // hide all content
+ $( this._target ).children().hide();
+
+ // now we just have to wait for the theme...
+ if ( Galleria.theme ) {
+ this._init();
+ } else {
+ Utils.addTimer('themeload', function() {
+ Galleria.raise( 'No theme found.', true);
+ }, 2000);
+
+ $doc.one( Galleria.THEMELOAD, function() {
+ Utils.clearTimer( 'themeload' );
+ self._init.call( self );
+ });
+ }
+ },
+
+ // the internal _init is called when the THEMELOAD event is triggered
+ // this method should only be called once per instance
+ // for manipulation of data, use the .load method
+
+ _init: function() {
+ var self = this;
+
+ 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.' );
+ return this;
+ }
+
+ // merge the theme & caller options
+ $.extend( true, this._options, Galleria.theme.defaults, this._original.options );
+
+ // 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' );
+
+ // the gallery is ready, let's just wait for the css
+ var num = { width: 0, height: 0 };
+ var testElem = Utils.create('galleria-image');
+
+ // check container and thumbnail height
+ Utils.wait({
+ until: function() {
+
+ // keep trying to get the value
+ $.each(['width', 'height'], function( i, m ) {
+
+ if (self._options[ m ] && typeof self._options[ m ] == 'number') {
+ num[ m ] = self._options[ m ];
+ } else {
+ num[m] = Utils.parseValue( self.$( 'target' ).css( m ) ) ||
+ Utils.parseValue( $container.css( m ) ) ||
+ self.$( 'target' )[ m ]() ||
+ $container[ m ]()
+ }
+
+ });
+
+ var thumbHeight = function() {
+ return true;
+ };
+
+ // make sure thumbnails have a height as well
+ if ( self._options.thumbnails ) {
+ self.$('thumbnails').append( testElem );
+ thumbHeight = function() {
+ return !!$( testElem ).height();
+ };
+ }
+
+ return thumbHeight() && num.width && num.height > 50;
+
+ },
+ success: function() {
+
+ // remove the testElem
+ $( testElem ).remove();
+
+ // apply the new meassures
+ $container.width( num.width );
+ $container.height( num.height );
+
+ // 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 a hard error
+ Galleria.raise('Width & Height not found.', true);
+ },
+ timeout: 2000
+ });
+ });
+
+ // postrun some stuff after the gallery is ready
+ // make sure it only runs once
+ var one = false;
+
+ this.bind( Galleria.READY, function() {
+
+ // show counter
+ Utils.show( this.get('counter') );
+
+ // bind clicknext
+ if ( this._options.clicknext ) {
+ $.each( this._data, function( i, data ) {
+ delete data.link;
+ });
+ this.$( 'stage' ).css({ cursor : 'pointer' }).bind( CLICK(), function(e) {
+ self.next();
+ });
+ }
+
+ // bind carousel nav
+ if ( this._options.carousel ) {
+ this._carousel.bindControls();
+ }
+
+ // start autoplay
+ if ( this._options.autoplay ) {
+
+ this.pause();
+
+ if ( typeof this._options.autoplay == 'number' ) {
+ this._playtime = this._options.autoplay;
+ }
+
+ this.trigger( Galleria.PLAY );
+ this._playing = true;
+ }
+
+ // if second load, just do the show and return
+ if ( one ) {
+ if ( typeof this._options.show == 'number' ) {
+ this.show( this._options.show );
+ }
+ return;
+ }
+
+ one = true;
+
+ // initialize the History plugin
+ if ( Galleria.History ) {
+
+ // bind the show method
+ Galleria.History.change(function(e) {
+
+ // grab history ID
+ var val = parseInt( e.value.replace( /\//, '' ) );
+
+ // if ID is NaN, the user pressed back from the first image
+ // return to previous address
+ if (isNaN(val)) {
+ window.history.go(-1);
+
+ // else show the image
+ } else {
+ self.show( val, undef, true );
+ }
+ });
+ }
+
+ // call the theme init method
+ Galleria.theme.init.call( this, this._options );
+
+ // call the extend option
+ this._options.extend.call( this, this._options );
+
+ // show the initial image
+ // first test for permalinks in history
+ if ( /^[0-9]{1,4}$/.test( HASH ) && Galleria.History ) {
+ this.show( HASH, undef, true );
+
+ } else {
+ this.show( this._options.show );
+ }
+
+ });
+
+ // build the gallery frame
+ this.append({
+ 'info-text' :
+ ['info-title', 'info-description', 'info-author'],
+ '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' ),
+ ' / ',
+ this.get( 'total' )
+ ) );
+
+ this.setCounter('–');
+
+ // add images to the controls
+ $.each( new Array(2), function(i) {
+
+ // create a new Picture instance
+ var image = new Galleria.Picture();
+
+ // apply some styles
+ $( image.container ).css({
+ position: 'absolute',
+ top: 0,
+ left: 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 ( self._options.clicknext ) {
+ e.stopPropagation();
+ }
+
+ // pause if options is set
+ if ( self._options.pause_on_interaction ) {
+ 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 ( self._options[ 'show_' + el.replace(/-/, '') ] === false ) {
+ Utils.moveOut( self.get( el ) );
+ }
+ });
+
+ // 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 ( !this._options.keep_source && !IE ) {
+ this._target.innerHTML = '';
+ }
+
+ // append the gallery frame
+ this.$( 'target' ).append( this.get( 'container' ) );
+
+ // parse the carousel on each thumb load
+ if ( this._options.carousel ) {
+ this.bind( Galleria.THUMBNAIL, function() {
+ this.updateCarousel();
+ });
+ }
+
+ // bind on_image helper
+ this.bind( Galleria.IMAGE, function( e ) {
+ this._options.on_image.call( this, e.imageTarget, e.thumbTarget );
+ });
+
+ return this;
+ },
+
+ // the internal _run method should be called after loading data into galleria
+ // creates thumbnails and makes sure the gallery has proper meassurements
+ _run : function() {
+ // shortcuts
+ var self = this,
+ o = this._options,
+
+ // width/height for calculations
+ width = 0,
+ height = 0,
+
+ // cache the thumbnail option
+ optval = typeof o.thumbnails == 'string' ? o.thumbnails.toLowerCase() : null;
+
+ // loop through data and create thumbnails
+ for( var i = 0; this._data[i]; i++ ) {
+
+ var thumb,
+ data = this._data[i],
+ $container;
+
+ if ( o.thumbnails === true ) {
+
+ // add a new Picture instance
+ thumb = new Galleria.Picture(i);
+
+ // get source from thumb or image
+ var src = data.thumb || data.image;
+
+ // append the thumbnail
+ this.$( 'thumbnails' ).append( thumb.container );
+
+ // cache the container
+ $container = $( thumb.container );
+
+ // 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
+ var getStyle = function( prop ) {
+ return doc.defaultView && doc.defaultView.getComputedStyle ?
+ doc.defaultView.getComputedStyle( thumb.container, null )[ prop ] :
+ $container.css( prop );
+ };
+
+ thumb.data = {
+ width : Utils.parseValue( getStyle( 'width' ) ),
+ height : Utils.parseValue( getStyle( 'height' ) ),
+ order : i
+ };
+
+ // grab & reset size for smoother thumbnail loads
+ $container.css(( o.thumb_fit && o.thumb_crop !== true ) ?
+ { width: 0, height: 0 } :
+ { width: thumb.data.width, height: thumb.data.height });
+
+ // load the thumbnail
+ thumb.load( src, function( thumb ) {
+
+ // scale when ready
+ thumb.scale({
+ width: thumb.data.width,
+ height: thumb.data.height,
+ crop: o.thumb_crop,
+ margin: o.thumb_margin,
+ complete: function( thumb ) {
+
+ // shrink thumbnails to fit
+ var top = ['left', 'top'];
+ var arr = ['Width', 'Height'];
+
+ // calculate shrinked positions
+ $.each(arr, function( i, meassure ) {
+ var m = meassure.toLowerCase();
+ if ( (o.thumb_crop !== true || o.thumb_crop == m ) && o.thumb_fit ) {
+ var css = {};
+ css[m] = thumb[m];
+ $( thumb.container ).css( css );
+ css = {};
+ css[top[i]] = 0;
+ $( thumb.image ).css( css);
+ }
+
+ // cache outer meassures
+ thumb['outer' + meassure] = $( thumb.container )['outer' + meassure]( true );
+ });
+
+ // set high quality if downscale is moderate
+ Utils.toggleQuality( thumb.image,
+ o.thumb_quality === true ||
+ ( o.thumb_quality == 'auto' && thumb.original.width < thumb.width * 3 )
+ );
+
+ // trigger the THUMBNAIL event
+ self.trigger({
+ type: Galleria.THUMBNAIL,
+ thumbTarget: thumb.image,
+ index: thumb.data.order
+ });
+ }
+ });
+ });
+
+ // preload all images here
+ if ( o.preload == 'all' ) {
+ thumb.add( data.image );
+ }
+
+ // create empty spans if thumbnails is set to 'empty'
+ } else if ( 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 );
+ }
+
+ $( thumb.container ).append( thumb.image );
+ this.$( 'thumbnails' ).append( thumb.container );
+
+ self.trigger({
+ type: Galleria.THUMBNAIL,
+ thumbTarget: thumb.image,
+ index: i
+ });
+
+ // 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.keep_source && o.link_source_images ? data.original : null )
+ .data('index', i).bind(o.thumb_event_type, function(e) {
+ // pause if option is set
+ if ( o.pause_on_interaction ) {
+ self.pause();
+ }
+
+ // extract the index from the data
+ var index = $( e.currentTarget ).data( 'index' );
+ if ( self.getIndex() !== index ) {
+ self.show( index );
+ }
+
+ e.preventDefault();
+ });
+
+ this._thumbnails.push( thumb );
+ }
+
+ // make sure we have a stageHeight && stageWidth
+
+ Utils.wait({
+
+ until: function() {
+ self._stageWidth = self.$( 'stage' ).width();
+ self._stageHeight = self.$( 'stage' ).height();
+ return( self._stageWidth && self._stageHeight > 50 ); // what is an acceptable height?
+ },
+
+ success: function() {
+ self.trigger( Galleria.READY );
+ },
+
+ error: function() {
+ Galleria.raise('stage meassures not found');
+ }
+
+ });
+ },
+
+ /**
+ Loads data into the gallery.
+ You can call this method on an existing gallery to reload the gallery with new data.
+
+ @param {Array or String} source Optional JSON array of data or selector of where to find data in the document.
+ Defaults to the Galleria target or data_source 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 data_config option for more information.
+
+ @returns {Galleria}
+ */
+
+ load : function( source, selector, config ) {
+
+ var self = this;
+
+ // 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 || this._options.data_source;
+
+ // use selector set by option
+ selector = selector || this._options.data_selector;
+
+ // use the data_config set by option
+ config = config || this._options.data_config;
+
+ // check if the data is an array already
+ if ( source.constructor == Array ) {
+ if ( this.validate( source) ) {
+ this._data = source;
+ this.trigger( Galleria.DATA );
+ } else {
+ Galleria.raise( 'Load failed: JSON Array not valid.' );
+ }
+ return this;
+ }
+ // loop through images and set data
+ $( source ).find( selector ).each( function( i, img ) {
+ var data = {},
+ img = $( img ),
+ parent = img.parent(),
+ href = parent.attr( 'href' );
+
+ // check if it's a link to another image
+ if ( /\.(png|gif|jpg|jpeg)$/i.test(href) ) {
+ data.image = href;
+
+ // else assign the href as a link if it exists
+ } else if ( href ) {
+ data.link = href;
+ }
+
+ // mix default extractions with the hrefs and config
+ // and push it into the data array
+ self._data.push( $.extend({
+
+ title: img.attr('title'),
+ thumb: img.attr('src'),
+ image: img.attr('src'),
+ description: img.attr('alt'),
+ link: img.attr('longdesc'),
+ original: img.get(0) // saved as a reference
+
+ }, data, config( img ) ) );
+
+ });
+ // trigger the DATA event and return
+ if ( this.getDataLength() ) {
+ this.trigger( Galleria.DATA );
+ } else {
+ Galleria.raise('Load failed: no data found.');
+ }
+ return this;
+
+ },
+
+ _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( Galleria.IMAGE, function() { Galleria.log('image shown') });
+
+ @returns {Galleria}
+ */
+
+ bind : function(type, fn) {
+ this.$( 'container' ).bind( type, this.proxy(fn) );
+ return this;
+ },
+
+ /**
+ Unbind any event to Galleria
+
+ @param {String} type The Event type to forget
+
+ @returns {Galleria}
+ */
+
+ unbind : function(type) {
+ this.$( 'container' ).unbind( type );
+ return this;
+ },
+
+ /**
+ Manually trigger a Galleria event
+
+ @param {String} type The Event to trigger
+
+ @returns {Galleria}
+ */
+
+ trigger : function( type ) {
+ type = typeof type == 'object' ?
+ $.extend( type, { scope: this } ) :
+ { type: 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 {HTML Element or String} elem The Dom node or selector to apply the idle state to
+ @param {Object} styles the CSS styles to apply
+
+ @example addIdleState( this.get('image-nav'), { opacity: 0 });
+ @example addIdleState( '.galleria-image-nav', { top: -200 });
+
+ @returns {Galleria}
+ */
+
+ addIdleState: function( elem, styles ) {
+ this._idle.add.apply( this._idle, Utils.array( arguments ) );
+ return this;
+ },
+
+ /**
+ Removes any idle state previously set using addIdleState()
+
+ @param {HTML Element or String} elem The Dom node or selector to remove the idle state from.
+
+ @returns {Galleria}
+ */
+
+ removeIdleState: function( elem ) {
+ this._idle.remove.apply( this._idle, Utils.array( arguments ) );
+ return this;
+ },
+
+ /**
+ Force Galleria to enter idle mode.
+
+ @returns {Galleria}
+ */
+
+ enterIdleMode: function() {
+ this._idle.hide();
+ return this;
+ },
+
+ /**
+ Force Galleria to exit idle mode.
+
+ @returns {Galleria}
+ */
+
+ exitIdleMode: function() {
+ this.idle._show();
+ return this;
+ },
+
+ /**
+ Enter FullScreen mode
+
+ @param {Function} callback the function to be executed when the fullscreen mode is fully applied.
+
+ @returns {Galleria}
+ */
+
+ 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 {Galleria}
+ */
+
+ exitFullscreen: function( callback ) {
+ this._fullscreen.exit.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 {HTML Element} elem The DOM Node to attach the event to
+ @param {String or 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 {Galleria}
+ */
+
+ bindTooltip: function( elem, value ) {
+ this._tooltip.bind.apply( this._tooltip, Utils.array(arguments) );
+ return this;
+ },
+
+ /**
+ Redefine a tooltip
+ Use this if you want to change the tooltip value at runtime
+
+ @param {HTML Element} elem The DOM Node to attach the event to
+ @param {String or Function} value The tooltip message. Can also be a function that returns a string.
+
+ @returns {Galleria}
+ */
+
+ defineTooltip: function( elem, value ) {
+ this._tooltip.define.apply( this._tooltip, Utils.array(arguments) );
+ return this;
+ },
+
+ // leave this out of the API for now
+
+ refreshTooltip: function() {
+ this._tooltip.refresh.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 {Galleria}
+ */
+
+ openLightbox: function() {
+ this._lightbox.show.apply( this._lightbox, Utils.array( arguments ) );
+ return this;
+ },
+
+ /**
+ Close the lightbox.
+
+ @returns {Galleria}
+ */
+
+ closeLightbox: function() {
+ this._lightbox.hide.apply( this._lightbox, Utils.array( arguments ) );
+ return this;
+ },
+
+ /**
+ Get the currently active image element.
+
+ @returns {HTML Element} The image element
+ */
+
+ getActiveImage: function() {
+ return this._getActive().image || undef;
+ },
+
+ /**
+ Get the currently active thumbnail element.
+
+ @returns {HTML Element} 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.$( 'stage' ).offset().left,
+ y: e.pageY - this.$( 'stage' ).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 {Galleria}
+ */
+
+ addPan : function( img ) {
+
+ if ( this._options.image_crop === false ) {
+ return;
+ }
+
+ img = $( img || this.getActiveImage() );
+
+ // define some variables and methods
+ var self = this,
+ x = img.width() / 2,
+ y = img.height() / 2,
+ curX = destX = parseInt( img.css( 'left' ) ) || 0,
+ curY = destY = parseInt( img.css( 'top' ) ) || 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.image_pan_smoothness;
+ curY += ( destY - curY ) / self._options.image_pan_smoothness;
+
+ 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
+ Utils.addTimer('pan', 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 function() {};
+ }
+ scope = scope || this;
+ return function() {
+ return fn.apply( scope, Utils.array( arguments ) );
+ };
+ },
+
+ /**
+ Removes the panning effect set by addPan()
+
+ @returns {Galleria}
+ */
+
+ removePan: function() {
+
+ if ( IE == 8 ) {
+ // todo: doublecheck this
+ }
+ this.$( 'stage' ).unbind( 'mousemove' );
+
+ Utils.clearTimer('pan');
+
+ this.rescale();
+
+ 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 {Galleria}
+ */
+
+ 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 {Galleria}
+ */
+
+ attachKeyboard : function( map ) {
+ this._keyboard.attach.apply( this._keyboard, Utils.array( arguments ) );
+ return this;
+ },
+
+ /**
+ Detach all keyboard events to Galleria
+
+ @returns {Galleria}
+ */
+
+ 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 {Galleria}
+ */
+
+ appendChild : function( parentID, childID ) {
+ this.$( parentID ).append( this.get( childID ) || childID );
+ 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 preppended
+ @param {String} childID the element ID that should be preppended
+
+ @example
+
+this.addElement('myElement');
+this.prependChild( 'info', 'myElement' );
+
+ @returns {Galleria}
+ */
+
+ 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 {Galleria}
+ */
+
+ 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 ) {
+ 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;
+ },
+
+ // an internal helper for scaling according to options
+ _scaleImage : function( image, options ) {
+
+ options = $.extend({
+ width: this._stageWidth,
+ height: this._stageHeight,
+ crop: this._options.image_crop,
+ max: this._options.max_scale_ratio,
+ min: this._options.min_scale_ratio,
+ margin: this._options.image_margin,
+ position: this._options.image_position
+ }, options );
+
+ ( image || this._controls.getActive() ).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 {Galleria}
+ */
+
+ updateCarousel : function() {
+ this._carousel.update();
+ return this;
+ },
+
+ /**
+ 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 {Galleria}
+ */
+
+ rescale : function( width, height, complete ) {
+
+ var self = this;
+
+ // allow rescale(fn)
+ if ( typeof width == 'function' ) {
+ complete = width;
+ width = undef;
+ }
+
+ var scale = function() {
+
+ // shortcut
+ var o = self._options;
+
+ // 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 );
+ }
+ };
+
+ if ( Galleria.WEBKIT && !width && !height ) {
+ Utils.addTimer( 'scale', scale, 5 );// webkit is too fast
+ } else {
+ 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 {Galleria}
+ */
+
+ refreshImage : function() {
+ this._scaleImage();
+ if ( this._options.image_pan ) {
+ this.addPan();
+ }
+ return this;
+ },
+
+ /**
+ Shows an image by index
+
+ @param {Number} index The index to show
+ @param {Boolean} rewind A boolean that should be true if you want the transition to go back
+
+ @returns {Galleria}
+ */
+
+ show : function( index, rewind, _history ) {
+
+ // do nothing if index is false or queue is false and transition is in progress
+ if ( index === false || !this._options.queue && this._queue.stalled ) {
+ return;
+ }
+
+ index = Math.max( 0, Math.min( parseInt(index), 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.value( index.toString() );
+ return;
+ }
+
+ this._active = index;
+
+ Array.prototype.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.image,
+ active = this._controls.getActive(),
+ next = this._controls.getNext(),
+ cached = next.isCached( src ),
+ thumb = this._thumbnails[ queue.index ];
+
+ // to be fired when loading & transition is complete:
+ var complete = function() {
+
+ // remove stalled
+ self._queue.stalled = false;
+
+ // optimize quality
+ Utils.toggleQuality( next.image, self._options.image_quality );
+
+ // swap
+ $( active.container ).css({
+ zIndex: 0,
+ opacity: 0
+ });
+ $( next.container ).css({
+ zIndex: 1,
+ opacity: 1
+ });
+ self._controls.swap();
+
+ // add pan according to option
+ if ( self._options.image_pan ) {
+ self.addPan( next.image );
+ }
+
+ // make the image link
+ if ( data.link ) {
+ $( next.image ).css({
+ cursor: 'pointer'
+ }).bind( CLICK(), function() {
+
+ // popup link
+ if ( self._options.popup_links ) {
+ var win = window.open( data.link, '_blank' );
+ } else {
+ window.location.href = data.link;
+ }
+ });
+ }
+
+ // remove the queued image
+ Array.prototype.shift.call( self._queue );
+
+ // if we still have images in the queue, show it
+ if ( self._queue.length ) {
+ self._show();
+ }
+
+ // check if we are playing
+ self._playCheck();
+
+ // trigger IMAGE event
+ self.trigger({
+ type: Galleria.IMAGE,
+ index: queue.index,
+ imageTarget: next.image,
+ thumbTarget: thumb.image
+ });
+ };
+
+ // let the carousel follow
+ if ( this._options.carousel && this._options.carousel_follow ) {
+ this._carousel.follow( queue.index );
+ }
+
+ // preload images
+ if ( this._options.preload ) {
+
+ var p,
+ n = this.getNext();
+
+ try {
+ for ( var i = this._options.preload; i > 0; i-- ) {
+ p = new Galleria.Picture();
+ p.add( self.getData( n ).image );
+ n = self.getNext( n );
+ }
+ } catch(e) {}
+ }
+
+ // show the next image, just in case
+ Utils.show( next.container );
+
+ // 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,
+ imageTarget: next.image,
+ thumbTarget: thumb.image
+ });
+
+ // begin loading the next image
+ next.load( src, function( next ) {
+ self._scaleImage( next, {
+
+ complete: function( next ) {
+
+ Utils.show( next.container );
+
+ // toggle low quality for IE
+ if ( 'image' in active ) {
+ Utils.toggleQuality( active.image, false );
+ }
+ Utils.toggleQuality( next.image, false );
+
+ // stall the queue
+ self._queue.stalled = true;
+
+ // remove the image panning, if applied
+ self.removePan();
+
+ // set the captions and counter
+ self.setInfo( queue.index );
+ self.setCounter( queue.index );
+
+ // trigger the LOADFINISH event
+ self.trigger({
+ type: Galleria.LOADFINISH,
+ cached: cached,
+ index: queue.index,
+ imageTarget: next.image,
+ thumbTarget: self._thumbnails[ queue.index ].image
+ });
+
+ var transition = active.image === null && self._options.transition_initial ?
+ self._options.transition_initial : self._options.transition;
+
+ // validate the transition
+ if ( transition in _transitions === false ) {
+
+ complete();
+
+ } else {
+ var params = {
+ prev: active.image,
+ next: next.image,
+ rewind: queue.rewind,
+ speed: self._options.transition_speed || 400
+ };
+
+ // call the transition function and send some stuff
+ _transitions[ transition ].call(self, params, complete );
+
+ }
+ }
+ });
+ });
+ },
+
+ /**
+ 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
+
[... 924 lines stripped ...]
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org