// ======================================================================================
// This Photo Gallery written by Courts Carter, pizzabytheslice.com, v2.4.1 (1999-2008)
// Use, distribute, modify (improve. please?) according to Creative Commons agreement:
// http://creativecommons.org/licenses/by-nc/2.0/
// ======================================================================================
// 
//			DO NOT DIRECTLY ACCESS ANY VARIABLE
//			USE "GETTER" OR "SETTER" FUNCTIONS
// ---------------------------------------------------


// ==================================================================
// OBJECT:	pbtsImg
// Scope:	semi-public
// USE:		 
// DOES:	 storage
// ==================================================================
function pbtsImg(){
	this.src		= null; // image file names, for the src attribute of image tag; will be concatenated to thumb/fullsize URL
	this.descr	= "";
	this.width	= "";
	this.height	= "";
	this.title	= "";
	this.extra1	= "";
	this.extra2	= "";
	this.extra3	= "";
	this.extra4	= "";
	this.date		= "";
	// for thumbs:
	this.img			= new Image();
	this.isLoaded	= false; // cofirmed that image has been loaded... used with timer event
} // pbtsImg (object)


/* -----------------------------------------------------------------------------
		PBTSGallery
* ------------------------------------------------------------------------------- */
PBTSGallery = function(){
}

PBTSGallery.prototype = {
	// Private Fields
	// array of pbtsImg objects, image attributes, flags, and image object
	_aryImgs: [],
	_defaults: new pbtsImg,// fills-in for missing image attributes

	// slideshow
	_isSlideshow: false, // toggle
	_slideshowIntId: null, // timer interval id/handle
	_slideshowLoopIdx: 0, // counts times through the slideshow
	_thisImg: null, // index of currently selected image
	_nextImg: null,
	_prevImg: null,
	_lastImg: null, // index to last image
	_priorImg: null, // new, used when setting currently selected thumb, index of previously selected image
	
	_thmsPerPage: null,
	_firstImgThisPg: null,
	_isShowingAll: false, // true when the Thum Grid is showing all images
	_isAutoScroll: false, // if true and showAll set then the page is scrolled to show the image when thumb image is clicked.
	_isEnableKeys: true,

	_imgAllowedAttribs: ["file","description","width","height","title","date","extra1","extra2","extra3","extra4"],
	_aryAddArg: [],
	
	_callbackFn: function(){ return true;},
	_slideshowDelay: 4000, // delay in miliseconds

	// ElementIds, if blank = not shown
	_elementId: {
		Info		: "", // File Info
		Img			: "theimage", // Image
		Descr		: "imgdescr", // Image Description/Caption
		Title		: "",
		Extra1	: "",
		Extra2	: "",
		Extra3	: "",
		Extra4	: "",
		aDate		: "",
		PgNav		: "", // Thumbnail page index
		Thms		: "", // Area containing the thumbnails
		Link		: "" // Perma-link to a specific image
	},
	
	_thmDir: "images/photos/thumbs/", 
	_imgDir: "images/photos/", 
	_HTMLThmPg: "",
	_HTMLImgPg: "",
	_spacerImg: "images/shim.gif", 
	_loadingImg: "images/shim.gif", 

	_thmWidth: 175,
	_thmClass: "", // 
	_thmSelectedClass: "",
	_thmEmptyClass: "", // 
	_thmSpacing: 2,
	_thmCols: 3, 
	_thmRows: 3,
	_isThmTitles: false,
	_isThmAll: false,
	_thmPrefix: "", // 
	_isWrap: true,
	_isRotate90: false,
	_isSetDescrWidth: false,
	_thmIdSeed: Math.round(1000*Math.random()),

	initialize: function(){
		this._defaults.height = 1;
		this._defaults.width	= 1;
		this._aryAddArg	= this._aryAddArg.concat(this._imgAllowedAttribs);
		
		if ((arguments.length > 0) && (typeof(arguments[0]) == "object")){
			props = arguments[0];
			for (var key in props){
				this.setProp( key, props[key] );
			}
		}
		// this._loadingImg	= this._spacerImg; // todo:
	},


	// ====================================================================================================
	//	 P R I V A T E
	// ====================================================================================================
	
	// ==================================================================
	// FUNCTION: ReadURLIdx
	// scope:		private
	// USE:			all
	// DOES:		 looks at URL parameters, accepts either "img=???", where ??? is index of image within 
	//					 this gallery or "file=???", where ??? is image filename
	// RETURNS:	index of image requested in the URL
	// PARAMS:	 none
	// ==================================================================
	_readURL: function(){
		var tempStr=new String(window.location.search).toLowerCase();
		var id=0;
		if (tempStr.substr(1,3)=="img"){
			// strip "img=" from URL
			tempStr= new String( tempStr.substr( 5, tempStr.length-5 ));
			if (tempStr.length < 1)
				tempStr= "0";
			else if (isNaN(tempStr))
				alert("\""+tempStr+"\" is not a valid number.");
			else
				id= parseInt( tempStr );
			if (id >= this._aryImgs.length)
				id = (this._aryImgs.length-1);
		}
		else if (tempStr.substr(1,4)=="file"){
			// stip "file=" from URL
			var found=false;
			tempStr= tempStr.substr( 6, tempStr.length-6 );
			for (var i=0;i < this._aryImgs.length; i++){
				if (this._aryImgs[i].src.toLowerCase() == tempStr){
					id=i;
					found=true;
					break;
				} // if
			} // for
			if (!found)
				alert( "Sorry, did not find file \""+ tempStr+"\".");
		} // if URL handler
		return	id;
	}, // ReadURLIdx
	
	// ==================================================================
	_arrayExists: function(needle,haystack){
		var found=false;
		for (var j=0;j <= haystack.length; j++){
			if (needle == haystack[j]){
				found=true;
				break;
			}
		}
		return found;
	},
	
	//	helper method used to translate an object to its setter function, that is, when on initialize the user passes in object with 
	//	"thumbDir" this function loops over methods in this object returning name of the the "set_thumbDir" method, if it exists
	_getSetterName: function(property){
		property= "set_"+property.toLowerCase();
		for (var key in this){
			if (key.toLowerCase() == property){
				return key;
			}
		}
		return "";
	},
	
	// ==================================================================
	// FUNCTION: TimeLoadImg
	// SCOPE:		PRIVATE
	// ==================================================================
	_timeLoadImg: function(){
		if (this._aryImgs[this._thisImg].img.complete){
			document.getElementById(this._elementId.Img).src= this._aryImgs[this._thisImg].img.src;
			this._aryImgs[this._thisImg].isLoaded= true;
			clearInterval(this._TimerId);
			this._setSelected( this._thisImg );
			this._callbackFn( this._thisImg );
		}
	}, // TimeLoadImg
	
	
	// ==================================================================
	// FUNCTION: InitImgPg
	// SCOPE:		PRIVATE
	// USE:			Call this AFTER the setters on an image page
	// DOES:		 
	// RETURNS:	none
	// PARAMS:	 none
	// ==================================================================
	_initImgPg: function(){
		this._nextImg= this._thisImg + 1 ;
		this._prevImg= this._thisImg - 1 ;
		this._lastImg= this._aryImgs.length-1;
		this.switchPic( this._thisImg, false );
	}, // InitImgPg
	
	
	// ==================================================================
	// FUNCTION: InitThmbPg
	// SCOPE:		PRIVATE
	// USE:			Call this AFTER the setters on an thumbnail page
	// DOES:		 xxxx
	// RETURNS:	none
	// PARAMS:	 none
	// ==================================================================
	_initThmbPg: function(){
		this._thmsPerPage= (this._thmCols * this._thmRows);
		this._lastPage = Math.ceil( this._aryImgs.length/ this._thmsPerPage);
		this._getFirstIdx();
		this._renderPaging();
		this._renderThumbs();
	}, // InitThmbPg
	
	// ==================================================================
	// FUNCTION: GetFirstIdx
	// USE:			
	// DOES:		 Using the Current Image Index calculate which thumbnail page it falls on, return the 
	//					 index of the first image on this page
	//					 Sets global this._firstImgThisPg 
	// RETURNS:	see above
	// PARAMS:	 NONE
	// ==================================================================
	_getFirstIdx: function(){
		if ((this._thisImg % this._thmsPerPage) == 0)
			this._firstImgThisPg = this._thisImg;
		else
			this._firstImgThisPg = this._thisImg - (this._thisImg % this._thmsPerPage);
		return this._firstImgThisPg;
	}, // GetFirstIdx
	
	
	// ==================================================================
	// FUNCTION: _renderThumbs
	// USE:			thumbnail page
	// DOES:		 
	// NOTES:		chooses min of width or height
	// RETURNS:	none
	// PARAMS:	 change:
	//						OLD: ElementId (optional) for area containing the thumbnails
	//						NEW: isShowAll (optional) if true shows all thumbs
	// ==================================================================
	_renderThumbs: function(){
		var newHTML= "";
		var title="";
		var start, stop;
		var isOnePg = (this._HTMLThmPg == this._HTMLImgPg); // 
		var spcHt= 0;
		var spcWd= 0;
		var thmId="";
		if (this._defaults.width > this._defaults.height){
			spcWd = this._thmWidth;
			spcHt = Math.round(this._thmWidth*(this._defaults.height/this._defaults.width));
		}
		else {
			spcWd = Math.round(this._thmWidth*(this._defaults.width/this._defaults.height));
			spcHt = this._thmWidth;
		}
	
		this._isShowingAll= (arguments.length > 0)?arguments[0]:false;
		
		if (this._isShowingAll){
			start= 0; 
			stop=	this._aryImgs.length;
		}
		else {
			start= this._firstImgThisPg; 
			stop=	this._firstImgThisPg+this._thmsPerPage;
		}
	
		for (var i=start; i < stop; i++ ){
	
			if ((i % this._thmCols) == 0) 
				newHTML += "<tr>";
	
			if (i < this._aryImgs.length){
				if ((this._isThmTitles) && (this._aryImgs[i].title.length > 0))
					title= "<br />"+this._aryImgs[i].title;
				else
					title= "";
				newHTML += "<td id=\"thm"+this._thmIdSeed+"td_"+i+"\" class=\""+this._thmClass+"\">"; // valign=top 
				thmId = "thm"+this._thmIdSeed+"a_"+i;
				if (isOnePg)
					newHTML += "<a id=\""+thmId+"\" idx=\""+i+"\" href=../working/wireframe/js//""+(this._HTMLImgPg+"?file=" + this._aryImgs[i].src)+"\" title=\"click for larger image\">";
				else 
					newHTML += "<a id=\""+thmId+"\" href=\""+(this._HTMLImgPg+"?img=" + i)+ "\" title=\"click for larger image\">";
				newHTML +="<img src=../working/wireframe/js//"" +this._thmDir+ this._thmPrefix+ this._aryImgs[i].src+ "\" border=\"0\" "; //
				newHTML +=((this._aryImgs[i].width > this._aryImgs[i].height)?"width=":"height=")+this._thmWidth;
				newHTML +="></a>"+title+"</td>";
			}
			else {
				newHTML += "<td class=\""+this._thmEmptyClass+"\"><img src=../working/wireframe/js//""+this._spacerImg+"/" width="+spcWd+" height="+spcHt+"></td>"; // valign=top 
			} // if
	
			if ((i % this._thmCols) == (this._thmCols-1)) 
				newHTML += "</tr>";
	
		} // for
	
		document.getElementById(this._elementId.Thms).innerHTML= "<table cellspacing='"+this._thmSpacing+"'>"+ newHTML+ "</table>";
		if (isOnePg)
			this._attachThmEvents(start,stop);
	
	},// _renderThumbs
	
	_attachThmEvents: function(startIdx,stopIdx){
		var h=null;
		var myThis = this;
		for (var i=startIdx; i < stopIdx; i++){
			h=document.getElementById("thm"+this._thmIdSeed+"a_"+i);
			if (h){
				h.onclick = function(){
					var idx = parseInt(this.getAttribute("idx"));
					myThis.switchPic(idx);
					return false;
				}
			}
		}
	},

	// ==================================================================
	// FUNCTION: KeyEvent
	// SCOPE:		PRIVATE
	// USE:			
	// DOES:		 handles keyPress events
	// HISTORY:	modified detector code from: www.howtocreate.co.uk
	// RETURNS:	true
	// PARAMS:	 
	// ==================================================================
	_keyEvent: function(e){
		var action= 0;
		if (typeof(e) == "undefined") // IE likes to use omnipresent "event"
			e=event; 
	
		if( typeof( e.which ) == "number") //NS 4, NS 6+, Mozilla 0.9+, Opera
			action = e.which;
		else if( typeof( e.keyCode ) == "number") //IE, NS 6+, Mozilla 0.9+
			action = e.keyCode;
		else if( typeof( e.charCode ) == "number") //also NS 6+, Mozilla 0.9+
			action = e.charCode;
		else
			action="";
	
		switch (action){ 
			case	72: //	Help: H
			case 104:
				this.showHelp();
				break;
			case	83: //	Start/Stop Slideshow: S
			case 115:
				if (!this._isSlideshow)
					this.startSlideshow();
				else
					this.stopSlideshow();
				break;
			case	80: //	Previous: P
			case 112:
				this.gotoPrevPic();
				break;
			case	78: //	Next:	N
			case 110:
				this.gotoNextPic();
				break;
			case	76: // Last/End: "L" 
			case 108: // "l"
				this.gotoLastPic();
				break;
			case	70: // First/Home: "F"
			case 102: // "f"
				this.gotoFirstPic();
				break;
			case	85: //	page Up: U
			case 117:
				if ((this._firstImgThisPg+this._thmsPerPage) < this._aryImgs.length){
					this._firstImgThisPg=this._firstImgThisPg+this._thmsPerPage;
					this._renderThumbs();
				}
				break;
			case	68: //	page Down: D
			case 100:
				if (this._firstImgThisPg >= this._thmsPerPage){
					this._firstImgThisPg=this._firstImgThisPg-this._thmsPerPage;
					this._renderThumbs();
				}
				break;
			default: 
				// numbers 0-9 {1-9 & 0=10}
				if ((action >= 48) && (action<=57)){
					var i	= action-48;
					if (i > 0)
						i--;
					else
						i=9; // press 0 for tenth image
					if ((this._firstImgThisPg+i) < this._aryImgs.length){
						this.switchPic(this._firstImgThisPg+i, false);
					} // inbounds
				} // 0-9 pressed
				// end:default
		} // switch
	
		return true;
	}, //KeyEvent
	
	// ==================================================================
	// FUNCTION: _renderPaging
	// USE:			thumbnail page
	// DOES:		 xxxx
	// RETURNS:	xxxx
	// PARAMS:	 ElementID (optional) where innerHTMl will write the thumbnail page links
	// ==================================================================
	_renderPaging: function(){
		var newHTML= "";
		var h= null;
		var myThis = this;
	
		if (arguments.length > 0)
			this._elementId.PgNav= arguments[0];
	
		if (this._elementId.PgNav == "")
			return;
	
		for (var i=1; i <= this._lastPage; i++ ){
			if ((this._firstImgThisPg >= ((i-1)*this._thmsPerPage)) && (this._firstImgThisPg <= ((i*this._thmsPerPage)-1)))
				newHTML += " <strong>" + i + "</strong> ";
			else
				newHTML +=" <a href=\"javascript:void(0)\" id=\"pg"+this._thmIdSeed+"_"+i+"\" idx=\"" + ((i-1)*this._thmsPerPage) + "\" title=\"page "+i+" thumbnails\">" + i + "</a> ";
		} // for
		if (this._isThmAll && (this._lastPage > 1))
			newHTML +=" <a href=\"javascript:void(0)\" id=\"pg"+this._thmIdSeed+"_all\" title=\"show all thumbnails\">all</a> ";
		
		document.getElementById(this._elementId.PgNav).innerHTML= newHTML;
		
		// attach Events
		for (var i=1; i <= this._lastPage; i++ ){
			h=document.getElementById("pg"+this._thmIdSeed+"_"+i);
			if (h){
				h.onclick = function(){
					var idx = parseInt(this.getAttribute("idx"));
					myThis._syncPaging(idx);
					return false;
				}
			}
		}
		
		h=document.getElementById("pg"+this._thmIdSeed+"_all");
		if (h){
			h.onclick = function(){
				myThis.showAll();
				return false;
			}
		}

	}, // _renderPaging
	
	// ==================================================================
	// FUNCTION: GetLink
	// DOES:		 returns string, a Link to image
	// RETURNS:	string
	// PARAMS:	 index
	// ==================================================================
	_getLink: function(i){
		var s=new String(document.location);
		var re=/^([^?]+)\?/
		if (s.match(re)){
			var m=s.match(re);
			s=m[1];
		}
		return "<a href=../working/wireframe/js//""+s+"?file="+this._aryImgs[i].src+"\">link</a>";
	},



	// ====================================================================================================
	//	 W R I T T E N
	// ====================================================================================================
	_setSelected: function(idx){
		// new: sets (adds class to) the selected thumbnail cell
		// question: add class to the anchor, too?
		// todo: crc, should do add/remove. You're so lazy. Shesh.
		var h= document.getElementById("thm"+this._thmIdSeed+"td_"+this._priorImg);
		if (h) h.className = this._thmClass;
		var h= document.getElementById("thm"+this._thmIdSeed+"td_"+idx);
		if (h) h.className = this._thmClass +" "+this._thmSelectedClass;
		this._priorImg = idx;
	},
	
	// ==================================================================
	// FUNCTION: _syncPaging
	// USE:			thumbnail page
	// DOES:		 rewrite page index & thumbnails (gets 'em in sync)
	// RETURNS:	
	// PARAMS:	 
	// ==================================================================
	_syncPaging: function(idx){
		this._thisImg= idx;
		this._getFirstIdx();
		this._renderPaging();
		this._renderThumbs();
		if (this._HTMLThmPg == this._HTMLImgPg)
			this.switchPic( idx, false );
	},
	
	// ==================================================================
	// FUNCTION: switchPic
	// USE:			image page
	// DOES:		 xxxx
	// RETURNS:	xxxx
	// PARAMS:	 idxToShow: index of image to show
	//					 isScroll (default true) deteremines whether the page should scroll to the image IF AutoScroll is TRUE:
	// ==================================================================
	switchPic: function( idxToShow ){
		var pic= document.getElementById(this._elementId.Img);
		var isScroll= this._isAutoScroll && this._isShowingAll;
		if (arguments.length > 1)
			isScroll= isScroll && arguments[1];
		if (isScroll)
			window.scrollTo(0,pic.top);
		if (idxToShow < 0){
			if (this._isWrap)
				this.switchPic(this._lastImg);
			else
				alert( "this is the first picture" );
		}
		else if (idxToShow > this._lastImg){
			if (this._isWrap)
				this.switchPic(0);
			else
				alert( "this is the first picture" );
		}
		else {
			// to avoid ugly resizing 'blank' the image before changing dimensions
			if (!this._aryImgs[idxToShow].isLoaded)
				pic.src = this._loadingImg;
			else
				pic.src= this._imgDir + this._aryImgs[idxToShow].src;
	
			// 
			pic.width	= this._aryImgs[idxToShow].width;
			pic.height = this._aryImgs[idxToShow].height;
			if (this._elementId.Descr != ""){
				if (this._isSetDescrWidth)
					document.getElementById(this._elementId.Descr).innerHTML= "<p style=\"width:" +this._aryImgs[idxToShow].width+ "px;\">"+this._aryImgs[idxToShow].descr+"</p>";
				else
					document.getElementById(this._elementId.Descr).innerHTML= this._aryImgs[idxToShow].descr;
			}
			if (this._elementId.Info != "") document.getElementById(this._elementId.Info).innerHTML= "file: "+ this._aryImgs[idxToShow].src +" (" +this._aryImgs[idxToShow].width+ "W x "+ this._aryImgs[idxToShow].height+"H)";
			if (this._elementId.Title	!= "") document.getElementById(this._elementId.Title).innerHTML = this._aryImgs[idxToShow].title;
			if (this._elementId.Extra1 != "") document.getElementById(this._elementId.Extra1).innerHTML= this._aryImgs[idxToShow].extra1;
			if (this._elementId.Extra2 != "") document.getElementById(this._elementId.Extra2).innerHTML= this._aryImgs[idxToShow].extra2;
			if (this._elementId.Extra3 != "") document.getElementById(this._elementId.Extra3).innerHTML= this._aryImgs[idxToShow].extra3;
			if (this._elementId.Extra4 != "") document.getElementById(this._elementId.Extra4).innerHTML= this._aryImgs[idxToShow].extra4;
			if (this._elementId.Link != "") document.getElementById(this._elementId.Link).innerHTML= this._getLink(idxToShow);
			if (this._elementId.aDate	!= ""){
				if (this._aryImgs[idxToShow].date!="")
					document.getElementById(this._elementId.aDate).innerHTML = "taken :"+this._aryImgs[idxToShow].date;
				else
					document.getElementById(this._elementId.aDate).innerHTML = "";
			}
	
			this._thisImg= idxToShow;
			this._nextImg= idxToShow + 1 ;
			this._prevImg= idxToShow - 1 ;
	
			// now all is in place to either exit or download the image.
			if (!this._aryImgs[idxToShow].isLoaded){
				this._aryImgs[idxToShow].img.src = this._imgDir + this._aryImgs[idxToShow].src;
				var myThis = this;
				this._TimerId = setInterval(function(){myThis._timeLoadImg();}, 250 );
			}
			else {
				this._setSelected( idxToShow );
				this._callbackFn( idxToShow );
			}
		} // if
	}, // switchPic
	
	// ====================================================================================================
	//	 D E V E L O P M E N T
	// ====================================================================================================
	
	// ==================================================================
	// FUNCTION: writeMaxWidthShim
	// USE:			detail page
	// DOES:		 xxxx
	// RETURNS:	xxxx
	// PARAMS:	 xxxx
	// ==================================================================
	 writeMaxWidthShim: function(id){
		var maxWidth=0;// 
		for (var i=this._firstImgThisPg; i< (this._firstImgThisPg+this._thmsPerPage); i++ ){
			if (i < this._aryImgs.length){
				if (this._aryImgs[i].width > maxWidth)
					maxWidth= this._aryImgs[i].width;
			} // if
		} // for
		document.getElementById(id).innerHTML= "<img src=../working/wireframe/js//""+this._spacerImg+"/" width="+maxWidth+" height=\"1\">";
	}, // writeMaxWidthShim
	
	
	// ====================================================================================================
	//		 P U B L I C
	// ====================================================================================================
	
	// ==================================================================
	// FUNCTION: addImg
	// USE:			all
	// DOES:		 adds an image
	// RETURNS:	index of the new Image item
	// PARAMS:	 this._imgAllowedAttribs, unless you've re-ordered the list
	// ==================================================================
	 addImg: function(){
		if (arguments.length > this._aryAddArg.length){
			alert( "Photo Gallery function \"addImg\" received too many arguments. Call had "+arguments.length+" arguments.");
			return false;
		}//if
	
		var tempImg= new pbtsImg;
		tempImg.src		 	= "";
		tempImg.descr		= this._defaults.descr;
		tempImg.title		= this._defaults.title;
		tempImg.date		= this._defaults.date;
		tempImg.extra1	= this._defaults.extra1;
		tempImg.extra2	= this._defaults.extra2;
		tempImg.extra3	= this._defaults.extra3;
		tempImg.extra4	= this._defaults.extra4;
		tempImg.isLoaded= false;
	
		if (this._isRotate90){
			tempImg.width = this._defaults.height;
			tempImg.height= this._defaults.width;
			this._isRotate90	= false;
		}
		else {
			tempImg.width		= this._defaults.width;
			tempImg.height	= this._defaults.height;
		}
	
		for (var i=0; i < arguments.length; i++){
			if (arguments[i]!=null){
				switch (this._aryAddArg[i]){
					case "file"		: tempImg.src	 = arguments[i]; break;
					case "description": tempImg.descr = arguments[i]; break;
					case "width"	: tempImg.width = arguments[i]; break;
					case "height"	: tempImg.height= arguments[i]; break;
					case "title"	: tempImg.title = arguments[i]; break;
					case "date"		: tempImg.date	= arguments[i]; break;
					case "extra1"	: tempImg.extra1= arguments[i]; break;
					case "extra2"	: tempImg.extra2= arguments[i]; break;
					case "extra3"	: tempImg.extra2= arguments[i]; break;
					case "extra4"	: tempImg.extra4= arguments[i]; break;
				}// switch
			}//if null skip
		}// for
	
		this._aryImgs[this._aryImgs.length]=tempImg;
		return this._aryImgs.length;
	
	}, // addImg
	
	
	// ==================================================================
	// FUNCTION: addImg90
	// USE:			all
	// DOES:		 adds an image, 90 degree rotation (portrait v. landscape)
	// RETURNS:	see addImg
	// PARAMS:	 see addImg
	// ==================================================================
	addImg90: function(){
		var i=0;
		var attribs= new Array( this._imgAllowedAttribs.length );
		for (i=0;i < this._imgAllowedAttribs.length; i++) attribs[i]=null;
		for (i=0;i < arguments.length; i++) attribs[i]=arguments[i];
		this._isRotate90= true;
		return this.addImg( attribs[0],attribs[1],attribs[2],attribs[3],attribs[4],attribs[5],attribs[6],attribs[7],attribs[8],attribs[9] );
	}, // addImg90
	
	// ==================================================================
	// FUNCTION: goto[Prev | Next | First | Last ]Pic
	// USE:			image page
	// DOES:		 switches picture
	// RETURNS:	xxxx
	// PARAMS:	 none
	// ==================================================================
	 gotoPrevPic: function(){return this.switchPic( this._prevImg, false );},
	 gotoNextPic: function(){return this.switchPic( this._nextImg, false );},
	 gotoFirstPic: function(){return this.switchPic(0, false);},
	 gotoLastPic: function(){return this.switchPic( this._lastImg, false );},
	
	// ==================================================================
	// FUNCTION: gotoThumbs
	// USE:			image page
	// DOES:		 changes page to ...
	// RETURNS:	none
	// PARAMS:	 none
	// ==================================================================
	gotoThumbs: function(){ 
		window.location = this._HTMLThmPg +"?img="+ this._thisImg ;
		return false;
	},
	
	// ==================================================================
	showHelp: function(){
		alert( "Photo Gallery Help (v2.4.1)\nwww.pizzabytheslice.com\n\nF= First image\nL= Last\n\nN= Next Image\nP=Previous\n\n1-9,0 enlarge thumbnail 1-10\n\nS= Start/Stop Slideshow (delay is "+ (this._slideshowDelay/1000)+ " seconds)\n\nU= page Up (next thumbnail page)\nD= page Down"	);
	},
	
	showAll: function(){ 
	 	this._renderThumbs(true);
	},
	
	// ==================================================================
	startSlideshow: function(){
		var myThis = this;
		this.stopSlideshow();
		this._slideshowIntId= window.setInterval(function(){myThis.gotoNextPic();}, this._slideshowDelay );
		this._isSlideshow= true;
		this._slideshowLoopIdx= 0;
	},
	
	stopSlideshow: function(){
		if (this._isSlideshow){
			this._isSlideshow= false;
			clearInterval(this._slideshowIntId);
		}
	},
	
	// DOES:		starts/stops slideshow. 
	// RETURNS: the new slideshow state; true if slideshow has been started, false if it has been STOPPED.
	toggleSlideshow: function(){
		if (this._isSlideshow)
			this.stopSlideshow();
		else
			this.startSlideshow();
		return this._isSlideshow;
	},
	
	// ==================================================================
	enableQuickKeys: function(){
		this._isEnableKeys = true;
		var myThis = this;
		document.onkeypress= function(e){myThis._keyEvent(e);}
	},
	
	// ==================================================================
	// FUNCTION: writeGallery
	// USE:			All. Call AFTER you've called all of your "setters"
	// DOES:		 xxxx
	// RETURNS:	xxxx
	// PARAMS:	 imageIndex (optional) if missing reads index of current Image from the URL
	// ==================================================================
	writeGallery: function(){
		var tempStr=new String(window.location.pathname).toLowerCase();
		var thm= new String(this._HTMLThmPg).toLowerCase();
		var img= new String(this._HTMLImgPg).toLowerCase();
	
		if (this._aryImgs.length < 1){
			alert( "Photo Gallery function \"writeGallery\" failed because the gallery contains no images." );
			return false;
		}
	
		this._thisImg= (arguments.length > 0)?arguments[0]:this._readURL();
	
		if (thm.toString() == img.toString()){
			this._initThmbPg();
			this._initImgPg();
		}
		else if ((thm.length > 0) && (tempStr.indexOf(thm.toString()) >= 0)){
			this._initThmbPg();
		}
		else if ((img.length > 0) && (tempStr.indexOf(img.toString()) >= 0)){
			this._initImgPg();
		}
		else {
			alert( "Photo Gallery function \"writeGallery\" failed to recognize Thumbnail or Image page" );
		}
		
		if (this._isEnableKeys) this.enableQuickKeys();
	},
	
	// ==================================================================
	// FUNCTION: setElementId
	// USE:			all
	// DOES:		 
	// RETURNS:	
	// PARAMS:	 attribute, element's Id
	// ==================================================================
	setElementId: function( key, value ){
		switch (key.toLowerCase()){
			case "image"		: this._elementId.Img			= value;break;
			case "description": this._elementId.Descr	= value;break;
			case "title"		: this._elementId.Title		= value;break;
			case "extra1"		: this._elementId.Extra1	= value;break;
			case "extra2"		: this._elementId.Extra2	= value;break;
			case "extra3"		: this._elementId.Extra3	= value;break;
			case "extra4"		: this._elementId.Extra4	= value;break;
			case "date"			: this._elementId.aDate	 	= value;break;
			case "info"			: this._elementId.Info	 	= value;break;
			case "pages"		: this._elementId.PgNav		= value;break;
			case "thumbs"		: this._elementId.Thms	 	= value;break;
			case "link"			: this._elementId.Link	 	= value;break;
			default:
				alert( "Photo Gallery function \"setElementId\" did not recognize attribute \""+key+"\"" );
				break;
		} // switch
	},
	
	
	// ==================================================================
	// FUNCTION: setDefault
	// USE:			
	// DOES:		 establishes the value to be used for all subsequent calls to addImg 
	// RETURNS:	
	// PARAMS:	 attribute, p_defaultValue 
	// ==================================================================
	setDefault: function( key, value ){
		key = key.toLowerCase();
		if (!this._arrayExists(key,this._imgAllowedAttribs)){
			alert( "Photo Gallery function \"setDefault\" did not recognize attribute \""+key+"\"" );
			return false;
		}// if
		switch (key){
			case "description": this._defaults.descr= value; break;
			case "width"	: this._defaults.width	=	value; break;
			case "height"	: this._defaults.height	= value; break;
			case "title"	: this._defaults.title	=	value; break;
			case "date"		: this._defaults.date		=	value; break;
			case "extra1"	: this._defaults.extra1	= value; break;
			case "extra2"	: this._defaults.extra2	= value; break;
			case "extra3"	: this._defaults.extra3	= value; break;
			case "extra4"	: this._defaults.extra4	= value; break;
			default:
				alert( "Photo Gallery function \"setDefault\" \""+key+"\" does not accept defaults." );
				break;
		}// switch
	},
	
	
	// ==================================================================
	// FUNCTION: setters
	// USE:			PUBLIC
	// ==================================================================
	setDefDims: function( width, height ){ 
		if (width < 1)
			width=1;
		this._defaults.width=width; 
		if (height < 1) 
			height=1;
		this._defaults.height=height;
	},

	// ==================================================================
	// 	setProp ... A generic setter
	// ==================================================================
	setProp: function( key, value ){
		switch (key.toLowerCase()){
			case "defaults": 
				for (var newKey in value)
					this.setDefault( newKey, value[newKey] );
				break;
			case "ids": 
				for (var newKey in value)
					this.setElementId( newKey, value[newKey] );
				break;
			default:
				var setterName = this._getSetterName( key );
				if (setterName == null)
					alert( "Photo Gallery function \"setProp\" did not recognize key \""+key+"\"" );
				else
					this[setterName](value);
				break;
		} // switch
		//case "defaultwidth": this._defaults.width=parseInt(value); break; // more validation?
		//case "defaultheight": this._defaults.height=parseInt(value); break;
	},

	// -------------------------------------------
	// setters
	// -------------------------------------------
	// ==================================================================
	// FUNCTION: set_PropOrder (formerly "reorderArgs")
	// USE:			public
	// DOES:		 sets the expected argument order for the "addImg" functions. Can safely set this order at any time.
	// RETURNS:	true if successful, false if an unrecognized property is named.
	// PARAMS:	 ordered list of image attributes, either as CSV or an array
	// ==================================================================
	set_PropOrder: function(){
		if (arguments.length < 1) return;
		var i=0,Str=new String,Ary= [], props = [];

		if (typeof(arguments[0]) == "string")
			props= arguments;
		else
			props= arguments[0];
	
		// validate attribes
		for (i=0;i<props.length;i++){
			Str= props[i];
			Ary[Ary.length]=Str.toLowerCase();
			if (!this._arrayExists(Ary[i],this._imgAllowedAttribs)){
				alert( "Photo Gallery function \"reorderArgs\" did not recognize image property \""+Ary[i]+"\"" );
				return false;
			}
		}

		// just add the missing args
		for (i=0;i<this._imgAllowedAttribs.length;i++){
			if (!this._arrayExists(this._imgAllowedAttribs[i],Ary))
				Ary[Ary.length]=this._imgAllowedAttribs[i];
		}

		// now copy 
		for (i=0;i<Ary.length;i++)
			this._aryAddArg[i]=Ary[i];

		return true;
	},
	
	/* file names and locations functions */
	set_ThumbDir: function(value){ this._thmDir= value;},
	set_ImgDir: function(value){ this._imgDir= value; },
	set_ThumbPage: function(value){ this._HTMLThmPg= value; },
	set_ImgPage: function(value){ this._HTMLImgPg= value; },
	set_Spacer: function(value){ this._spacerImg= value; },
	set_LoadingImg: function(value){ this._loadingImg= value; },
	set_ThumbPrefix: function(value){ this._thmPrefix= value; },
	
	// -------------------------------------------
	/* thumbnail appearance functions */
	set_Rows: function(value){ this._thmRows=	value;},
	set_Cols: function(value){ this._thmCols=	value;},
	set_ThumbClass: function(value){ this._thmClass= value; },
	set_ThumbSelectedClass: function(value){ this._thmSelectedClass= value; },
	set_ThumbEmptyClass: function(value){ this._thmEmptyClass= value; },
	set_ThumbWidth: function(value){ this._thmWidth= value; },
	set_ThumbSpacing: function(value){ this._thmSpacing= value; },
	set_ThumbTitles: function(value){ this._isThmTitles= value; },
	set_ThumbAll: function(value){ this._isThmAll= value; },
	set_AutoScroll: function(value){ this._isAutoScroll= value; },

	// -------------------------------------------
	/* behavior and defaults functions */
	set_EnableKeys: function(value){ this._isEnableKeys= value; },
	set_Delay: function(value){ this._slideshowDelay= value;}, // milliseconds
	set_Wrap: function(value){ this._isWrap= value; }, // boolean
	
	// -------------------------------------------
	/* Element Id setting functions */
	set_ImageId: function(value){ this._elementId.Img= value;},
	set_ImageDescrId: function(value){ this._elementId.Descr= value;},
	set_ImageInfoId: function(value){ this._elementId.Info= value;},
	set_ImageTitleId: function(value){ this._elementId.Title= value;},
	
	set_ThumbIndexId: function(value){ this._elementId.PgNav= value;},
	set_ThumbGridId: function(value){ this._elementId.Thms=	value;},
	
	// -------------------------------------------
	// setOnChange 
	set_Callback: function(value){ this._callbackFn= value;},
	
	// ==================================================================
	// setSetDescrWidth ... this is a temp kludge
	// this will fix the width of the caption (i.e. the description) for an image to be the same as the image's width.
	// this is useful when the image detail and thumbnails appear on the same page.
	// ==================================================================
	set_IsSetDescrWidth: function(value){ this._isSetDescrWidth= value; },
	// retired: set_DescrWidth: function(value){ this._isSetDescrWidth= value; }, // bool
	
	// ==================================================================
	// : getters : function(returns values of stuff)
	// USE:			PUBLIC
	// ==================================================================
	getCount: function(){ return this._aryImgs.length; },
	
	// ==================================================================
	// FUNCTION: getIsSlideshow
	// USE:			all
	// RETURNS:	returns TRUE if currently in slideshow mode, false otherwise (slideshow has been stopped or never started)
	// PARAMS:	 none
	// ==================================================================
	getIsSlideshow: function(){ return this._isSlideshow; },
	
	// ==================================================================
	// FUNCTION: getValue
	// USE:			all
	// DOES:		 
	// RETURNS:	value of the specified attribute for the specified image (or current one, in none given)
	// PARAMS:	 attribute
	//					 index (optional)
	// ==================================================================
	getValue: function( attribute ){
		var i=(arguments.length > 1)?arguments[1]:this._thisImg;
		switch (attribute.toLowerCase()){
			case "image":			return this._aryImgs[i]; 
			case "file":			return this._aryImgs[i].src; 
			case "description": return this._aryImgs[i].descr; 
			case "width":			return this._aryImgs[i].width; 
			case "height":		return this._aryImgs[i].height;
			case "title":			return this._aryImgs[i].title;
			case "extra1":		return this._aryImgs[i].extra1;
			case "extra2":		return this._aryImgs[i].extra2;
			case "extra3":		return this._aryImgs[i].extra3;
			case "extra4":		return this._aryImgs[i].extra4;
			case "date":			return this._aryImgs[i].date;
	
			case "dir":				return this._imgDir; 
			case "thumbdir":	return this._thmDir; 
			case "pathname":	return this._imgDir+this._aryImgs[i].src; 
	
			case "count":			return this._aryImgs.length;
			case "current":		return this._thisImg; 
			case "maxwidth":
				var m=-1;
				for (var i=0;i < this._aryImgs.length; i++)
					m=Math.max(m, this._aryImgs[i].width);
				return m;
				break;
			case "maxheight":
				var m=-1;
				for (var i=0;i < this._aryImgs.length; i++)
					m=Math.max(m, this._aryImgs[i].height);
				return m;
				break;
			default:
				alert( "Photo Gallery function \"getValue\" did not recognize attribute \""+attribute+"\"" );
				break;
		} // switch
	}
	
}

