
/** documentation @  */

/***
  * Globals
  */
var JS_GALLERY_LOADED = true;     	
var JS_GALLERY_FILE = 'gallery.0.5.beta.js';			
var JS_GALLERY_VERSION = '0.5.beta';
var JS_GALLERY_BUILD_DATE = '2005/09/12';


/***
   * Const
	*/
var JS_GALLERY_DISPLAYTYPE_NONE = 1; 
var JS_GALLERY_DISPLAYTYPE_BLOCK = 2; 
var JS_GALLERY_DISPLAYTYPE_TABLE = 8;
var JS_GALLERY_DISPLAYTYPE_VISIBLE = 16;


/***
  * Gallery Object
  */
function Gallery( opts )
{
	this.init( opts );
}


/***
  * Prototype
  */
Gallery.prototype = 
{
	
	/***
  	  * init: on Gallery instantiation
	  * @opts: list of user-defined options 
  	  */	
	init : function ( opts )
	{
	
		// private vars
		this.history = new Array();	// stores log of slide history
		this.position = 1;			// maintains reference of currently 'active' slide.
	
		this.options =
		{

			// references to canvas element/@id
			slide_id_ref : 'slide-',		// slide @id attribute prefixes: slide-1, slide-2, slide-3
			slide_counter_id_ref : 'counter',  //TODO - make arg of callback
			
			// callbacks. custom event handlers invoked at points in the program flow.
			// default to anonymous functions
			on_position_change : new Function( "p", "return true" ),
			//on_goto : new Function( "return true" ),
						
			// number of slides on the canvas
			quantity : 0,
	
			// can slides wrap. eg. move to first when at the last slide
			carosel : new Boolean(),
			
			// block, none etc. allows you to use many types of elements as slides
			// this value also hold whether the slides disapear from the canvas or simply become invisible
			display_type : 1
			
		};

		if ( opts ) // set any var's passed on init
		{
			this.set_options( opts );
		};
	},
		




	/***
  	  * back - move to position--
  	  */	 
	back : function ( )
	{
		this.cycle( this.position, this._position_decrement() );
	},


	/***
  	  * clear_history
  	  */	 
	clear_history : function ( )
	{
		this.history = new Array();
	},

	
	/***
  	  * go_to - move to a given slide
  	  */	 
	go_to : function ( item )
	{
		this.cycle( this.position, item );
		return true;
	},


	/***
  	  * go_to_first - move to a given slide
  	  */	 
	go_to_first : function ( )
	{
		this.go_to( this.position, 1 );
		return true;
	},

	
	/***
  	  * go_to_last - move to last slide
  	  */	 
	go_to_last : function ( )
	{
		this.go_to( this.position, this.options.quantity );
		return true;
	},
	
	
	/***
  	  * go_to_internal_anchor: parse the URI, and move to a given slide if found
  	  */	 
	go_to_internal_anchor : function ( uri )
	{ 	
		var uri = ( uri ) ? uri : self.location;  // typically self.location, though feasible we pass a url. especially when having to preprocess the REQUEST_URI for 'crud' or use Frames.

		var re = /\#([0-9]+)/;	 // finds anchor reference
	 	if ( ( slide = re.exec(uri) ) && slide[1] <= this.options.quantity )
			this.go_to( slide[1] );
	 	else
			this.go_to_first(); 
			
		return true;
	},

	
	/***
  	  * go_to_random: 
  	  */	 
	go_to_random : function ( )
	{ 	
			this.go_to( Math.ceil( Math.random() * this.options.quantity ) );
	},
	
	
	/***
  	  * hide: removes a slide from the canvas
  	  */	 
	hide : function ( item )
	{
   	this._go( item, false );
		this._position_decrement();
	},

  
	/***
  	  * hide_all: removes a slide from the canvas
  	  */	 
	hide_all : function ( item )
	{
 		for (var i = this.options.quantity; i > 0; i-- )
 		{
			this.go_to( i, false );
 		}
	},

	
	/***
  	  * next - move to position++
  	  */	 
	next : function ( )
	{
		this.cycle( this.position, this._position_increment() );
	},

	
	/***
  	  * set_options: Interface to set one or more user defined options. Called on init.
	  * @opts 
  	  */	
	set_options: function( opts )
	{
		for ( var key in opts )
		{
			this.options[key] = opts[key];
		}
	},

	
	set_carosel_off: function( )
	{
		this.options.carosel = false;
	},
	
	set_carosel_on: function( )
	{
		this.options.carosel = true;
	},

	set_display_type: function( type )
	{
		this.options.display_type = type;
	},
	
	set_slide_ref: function( ref )
	{
		this.options.slide_ref = ref;
	},
			
	/***
  	  * show: draws a slide on the canvas
  	  */	 
	show : function ( item )
	{
   	this._go( item, true );
	},


	
	/***
  	  * show_all: removes a slide from the canvas
  	  */	 
	show_all : function ( item )
	{
 		for (var i = this.options.quantity; i > 0; i-- )
 		{
			this._go( i, true );
 		}
	},


	/***
  	  * show_range: 
  	  */	 
	show_range : function ( start, length )
	{
 		for (var i = this.options.quantity; i > 0; i-- )
 		{
 			if ( i >= start_index && ( i < start_index + length ) ) 
				this._go( i, true );
			else
				this._go( i, false );
 		}
	},
	
	

	/***
  	  * show_selective: 
  	  */	 
	show_selective : function ( )
	{
		for (var i = arguments.length; i >= 0; i-- )
		{
			this._go( arguments[i], true );
		}
	},
	

	/***
  	  * skip_back: 
  	  */	 
	skip_back : function ( items )
	{
		 var old_position = this.position;
		 var new_position;
		 for (var i = 1; i <= items; i++ )
		 {
			new_position = this._position_decrement();	 
		 }
		 this.cycle( old_position, new_position );
	},
	
	
	/***
  	  * skip_forward: 
  	  */	 
	skip_forward : function ( items )
	{
		 var old_position = this.position;
		 var new_position;
		 for (var i = 1; i <= items; i++ )
		 {
			new_position = this._position_increment();	 
		 }
		 this.cycle( old_position, new_position );
	},
	
	
	
	/***
  	   * cycle : go between public api and private methods
	   * effectivly controls the object functions, callbacks etc.
	   * makes the various calls to the private methods
 	   * required to replace an old item with a new one.
 		* seperating this out from the public methods
 		* allows for lighter API's + no repeated code.
  	  */	 	
	cycle: function( old_item, new_item )
   	{	
   		this._go( old_item, false ); // hide the current item
			this._go( new_item, true );	// show the new item

			this.history[ this.history.length ] = new_item; // push/log the item in history

			this._set_position( new_item );
					
			// callbacks
			this.options.on_position_change( old_item, this.position );			

   		return true;
   	},
				
			
	/***
		* go - toggle slide in/out of view
		* returns true if slide swap operation is success, otherwise false
	   */
	_go : function( item, state )
		{
		 	if ( isNaN( Math.ceil( item ) ) ) {
				warn('gallery.go : item "'+item+'" is not a number' );
				return false; }
			

			// TODO - doc.all fallback or return false
			var slide_ref = document.getElementById( this.options.slide_id_ref + item );
						
			if ( state ) //show
			{
			
				switch ( this.options.display_type )
				{
					// TODO - could abstract style.property to user defined in future
				case JS_GALLERY_DISPLAYTYPE_NONE : // block
					slide_ref.style.display = 'block';
					break;
				
				case JS_GALLERY_DISPLAYTYPE_BLOCK :	// 
					slide_ref.style.display = '';
					break;

				case JS_GALLERY_DISPLAYTYPE_TABLE :	// table
					slide_ref.style.display = 'table';
					break;

				case JS_GALLERY_DISPLAYTYPE_VISIBLE :	// visible
					slide_ref.style.visibility = 'visible';
					break;
					
				default :
					break;
				}
			}			
				
			else //hide
			{

				switch ( this.options.display_type ) 
				{

					case JS_GALLERY_DISPLAYTYPE_VISIBLE :
						slide_ref.style.visibility = 'hidden';
						break;

					default :
						slide_ref.style.display = 'none';			
						break;
				}

			}
		return true;
		},
	
	
	/***
		*	position_increment - responsible for maintaining position 
		*/
	_set_position : function( item )
	{
		this.position = item;
		return true;
	},
	
		
	/***
		*	position_increment - responsible for maintaining position 
		*/
	_position_increment : function()
	{
 		if ( this.position < this.options.quantity )
			this.position++; // increment
		else if ( ( this.position == this.options.quantity ) && ( this.options.carosel ) )
		 	this.position = 1; // reset to start
		else 
			return false;
			
		return this.position;
	},
	
	
	/***
		* ...
		*/
	_position_decrement : function()
	{
 		if ( this.position > 1 )
			this.position--;
		else if ( ( this.position == 1 ) && ( this.options.carosel ) )
			this.position = this.options.quantity; // reset to max
		else
			return false;
			
		return this.position;
	}
	  
}

