//
// DISPLAY FLICKR PHOTOS IN A PHOTO ALBUM
//	with paging support, click to view, album selection, more...
//
// Author: Dr. David Greenwood
// Copyright: none.
// License: none. use it. have fun.
//
// Why?
//	I was looking for a decent, flexible, attractive photo album viewer to use
//	on a website I manage, and tried every tool on the Great Flickr Tools Collection.
//	http://www.quickonlinetips.com/archives/2005/03/great-flickr-tools-collection/
//	None of them quite meet the need - a simple browsable photo album that uses
//	flickr as the backend.  So I built one.  Flickr has a 100 photo limit on tag,
//	photoset, etc. type queries, so I needed so a good way to allow for selecting
//	different photo albums. This tool does that too.  I wanted the album to fill
//	gracefully, with ajax wait spinners for each thumbnail. Got that too.
//	Lot's more I could do...
//
// Browsers tested:
//		IE6, IE7, FF 3.0.5 Win and Mac, Safari 3
//
// Usage:
//	FLICKR.init(containting div);	// called once
//	FLICKR.load(title text, source type, source id, image size);
//
// Example:
//	<script type="text/javascript">
//		FLICKR.init("my_div");
//		FLICKR.load("My Photo Album", "photoset", "92159612203339933");
//	</script>
//  or search by tag with e.g.
//		FLICKR.load("Puppy Album", "tag", "puppy");
//	
// Where:
//  containing div is the existing div in which to build the Album
//  title text is the Title for the Album
//	source type is one of "photoset", "tag", "user", "contacts", "favorites", "group", "interesting"
//	source id is photoset id, tag(s), user id, group id, empty string for "interesting"
//		e.g. flickr_source_id = "8407953@N03";
//		     flickr_source_id = "21065760@N03";
//	image size is "s", "t" (default), "m", "", "b", "o" - adjust the style sheet accordingly
//
// CSS:
//	this script uses a stylesheet to ID page elements and position
//	them within a given containing div.  The style sheet must be updated depending
//	on the image size selected.	 You can enable (display) or disable (hide)
//	certain page elements, such as the album title, or Prev/Next links.
//  Required div id elements used in the Album (for this layout) are:
//			Album title area: 	fpbrowser-title
//			thumbnail title: 	fpbrowser-title[n]
//			thumbnail image: 	fpbrowser-photo[n]
//			paging navigation: 	fpbrowser-photonav
//  Required div id eleements used for the detail view (for this layout) are:
//			Detail title area: 	fpbrowser-detailTitle
//			the large(r) image: fpbrowser-detailPic
//  Other elements are defined, but not used programatically.
//  See the css file for additional comments to set various image attributes.
//
// Setup: 
//	Before calling FLICKR.init() include the required css and this script file.
//		<link href="fpbrowser4.css" rel="stylesheet" type="text/css" />
//		<script type="text/javascript" src="fpbrowser4.js"></script>
//
//

function doLayout(el) {
	// this is a 3 x 2 thumbnail layout, with the detail window as pop-up
	// I have versions that are pure div/css based, but a table is actually
	// appropriate in this instance. All the styling is in the accompanying css.
	var w = document.getElementById(el);
	w.innerHTML = '<div id="fpbrowser" class="roundify234">'
				+	'<table cellpadding="0" cellspacing="5" border="0" width="100%">'
				+		'<tr>'
				+ 			'<td colspan="3">'
				+				'<div id="fpbrowser-title" class="albumTitle"></div>'
				+			'</td>'
				+ 		'</tr>'
				+		'<tr>'
				+ 			'<td class="separator">'
				+				'<div id="fpbrowser-title1" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo1" class="photoBox" ></div>'
				+			'</td>'
				+ 			'<td class="separator">'
				+ 				'<div id="fpbrowser-title2" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo2" class="photoBox"></div>'
				+			'</td>'
				+ 			'<td>'
				+ 				'<div id="fpbrowser-title3" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo3" class="photoBox"></div>'
				+			'</td>'
				+		'</tr>'
				+		'<tr><td colspan="3"><hr></div></td></tr>'
				+		'<tr>'
				+ 			'<td class="separator">'
				+ 				'<div id="fpbrowser-title4" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo4" class="photoBox"></div>'
				+			'</td>'
				+ 			'<td class="separator">'
				+ 				'<div id="fpbrowser-title5" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo5" class="photoBox"></div>'
				+			'</td>'
				+ 			'<td>'
				+ 				'<div id="fpbrowser-title6" class="photoTitle"></div>'
				+ 				'<div id="fpbrowser-photo6" class="photoBox"></div>'
				+			'</td>'
				+		'</tr>'
				+ 		'<tr>'
				+ 			'<td colspan="3">'
				+ 				'<div id="fpbrowser-photonav" class="albumNav"></div>'
				+			'</td>'
				+		'</tr>'
				+	'</table>'
				+ '</div>';
	
	/* insert a new div at the body level of the document so that */
	/* we can make it "float" properly above all other page content */
	/* var bodyID = document.getElementsByTagName('body')[0];         
	var newDiv = document.createElement('div');
	newDiv.id = 'fpbrowser-detailFrame';
	newDiv.className = 'fpbrowser-drag';
	bodyID.appendChild(newDiv);*/		
/* IE6 works if you try this file standalone, but not withint the joomla page */
/* so for now, I just define the detailFrame div in the main index.php file */
	
	/* retrieve the new DOM div element and update it */
	var p = document.getElementById('fpbrowser-detailFrame');
	p.innerHTML = /* '<div id="fpbrowser-detailFrame" class="fpbrowser-drag roundify1234">' */
				 	'<table cellpadding=0 cellspacing=0>'
				+ 		'<tr>'
				+ 			'<td align="left">'
				+ 				'<div id="fpbrowser-detailNav" class="fpbrowser-drag">'
				+ 					'<a href="javascript:FLICKR.prevPic()" class="leftNav"></a>'
				+ 					'<a href="javascript:FLICKR.nextPic()" class="rightNav"></a>'
				+ 				'</div>'
				+ 			'</td>'
				+ 			'<td align="middle">'
				+				'<div id="fpbrowser-detailTitle" class="fpbrowser-drag"></div>'
				+			'</td>'
				+ 			'<td align="right">'
				+ 				'<div id="fpbrowser-detailClose" class="fpbrowser-drag">'
				+ 					'<a href="javascript:FLICKR.closePic()" class="closeWin"></a>'
				+ 					'<a href="javascript:FLICKR.zoomPic()" class="zoomFlickr"></a>'
				+ 				'</div>'
				+ 			'</td>'
				+ 		'</tr>'
				+ 		'<tr>'
				+ 			'<td colspan="3" align="middle">'
				+				'<div id="fpbrowser-detailPic" class="fpbrowser-drag"></div>'
				+ 			'</td>'
				+ 		'</tr>'
				+ 	'</table>';
				/*+ '</div>'; */
}

var FLICKR = {
	
	/* ------ PROPERTIES -------- */
	
	api_key: '625c4e77af956ca9b1d7de89f4896587',
	data: null,
	current: null,
	total: 0,
	isInitialized: false,
	container_div: "",
	title_div: "fpbrowser-title",
	source_type: null,
	source_id: null,
	set_owner: null,
	image_size: "t",
	dragXoffset: 0,
	dragYoffset: 0,
	clickMsg: "Click to View",

	// a few state variables used in the detail view handlers
	_savedTarget: null,
	_cursorTarget: null,
	_dragXoffset: 0,
	_dragYoffset: 0,
	_orgCursor: null,
	_dragOK: false,
	_picNum: 0,
	
	/* ------- ALBUM SETUP -------- */
	
	init: function(container_div) {

		if(this.isInitialized == true) return;
		this.isInitialized = true;
		
		this.container_div = container_div;

		// build it
		doLayout(this.container_div);			
				
        document.onmousedown = dragHandler;

		this._savedTarget = document.getElementById('fpbrowser-detailFrame');
		this._savedTarget.style.top='200px'; 	// Initialize the location of the detail window
		this._savedTarget.style.left='200px'; 
		
		/* IE6 fix - initializes some internal state so that the window displays with DD_Roundies correctly */
		this._savedTarget.style.display='block'; // unhide it
		this._savedTarget.style.display='none'; // hide it
		  
	},

	/* ------- ALBUM LOADer -------- */
	
	load: function(title_text, source_type, source_id, image_size) {

		this.source_type = source_type.toLowerCase();
		this.source_id = source_id;
		
		if (image_size && image_size != "" && image_size != null)
			this.image_size = image_size.toLowerCase();

		this.updateDiv(this.title_div, title_text)
			
		// reset	
		this.current = 0;		
		this.data = null;
		this.total = 0;
		this.loadImages();
	
		this.requestPhotoData();
	},
		
	/* ------- RESTful JSON REQUEST -------- */
	
	requestPhotoData: function() { 
		/* see http://www.flickr.com/services/api/flickr.photos.search.html for example parameters */
		var flickr_api_path = "http://api.flickr.com/services/rest/?format=json&jsoncallback=FLICKR.handleResponse&api_key=" + this.api_key;
		
		switch (this.source_type) {
			case 'photoset':
				flickr_api_path += "&method=flickr.photosets.getPhotos&photoset_id=" + this.source_id;
				break;
			case 'user':
				flickr_api_path += "&method=flickr.people.getPublicPhotos&user_id=" + this.source_id;
				break;
			case 'contacts':
				flickr_api_path += "&method=flickr.photos.getContactsPublicPhotos&user_id=" + this.source_id;
				break;
			case 'favorites':
				flickr_api_path += "&method=flickr.favorites.getPublicList&user_id=" + this.source_id;
				break;
			case 'group':
				flickr_api_path += "&method=flickr.groups.pools.getPhotos&group_id=" + this.source_id;
				break;
			case 'interesting':
				flickr_api_path += "&method=flickr.interestingness.getList";
				break;
			case 'tag':
				flickr_api_path += "&method=flickr.photos.search&tag_mode=all&tags=" + this.source_id;
				break;
			default:
				this.setError("unknown source type \'" + this.source_type + "\'");
				return;
		}

		// push this REST json script into the page for execution
		this.executeScript(flickr_api_path);
	},
	
	executeScript: function(src) {
		/* http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS */
		var headID = document.getElementsByTagName("head")[0];         
		var newScript = document.createElement('script');
		newScript.type = 'text/javascript';
		newScript.src = src;
		headID.appendChild(newScript);	
	},
		
	/* ----- JSON CALLBACK ----- */
	
	handleResponse: function(rsp) {
	
		if (rsp.stat.toLowerCase() == "fail") {
			this.setError(rsp.message);
			return;
		}
		
		switch (this.source_type) {
			case 'photoset':
				this.set_owner = rsp.photoset.owner;
				this.data = rsp.photoset.photo;
				break;
			default:
				this.data = rsp.photos.photo;
				break;
		}
		
		this.readyToGo();
		
	},
	
	readyToGo: function() {
		if (location.hash != "") {
			var i = this.getPhotoIndexById(location.hash.substr(1));
			if (i > -1) this.current = i;
		}
		this.total = this.data.length;
		this.loadImages();
	},
		
	/* ------ DIVS -------- */
	
	updateDiv: function(div_id, html) {
		document.getElementById(div_id).innerHTML = html;
	},
	
	/* -------- IMAGES -------- */
	
	loadImages: function() {
		this.setTitles();
		this.setNav();
		this.setPhotos();
		if (this.update_browser) this.updateBrowserLocation();
	},
	
	/* -------- NAVIGATION ----- */
	
	goNextPhoto: function() {
		this.current += 6;
		if (this.current >= this.total) this.current = 0;

		this.loadImages();
	},
	
	goPreviousPhoto: function() {
		if(this.current == 0) {
			this.current = 6 * Math.floor(this.total / 6);
			if((this.total % 6) == 0) this.current -= 6;
		} else this.current -= 6;
		//this.current -= 6;
		if (this.current < 0) this.current = this.total - 6;
				
		this.loadImages();
	},
	
	/* ------- DISPLAY ------- */
	
	setNav: function() {
		var html_prev = "<a href=\"#\" title=\"Previous\" onclick=\"FLICKR.goPreviousPhoto();"
						+" return false;\">Previous Page \<\<</a>";
		var html_next = "<a href=\"#\" title=\"Next\" onclick=\"FLICKR.goNextPhoto();"
						+" return false;\">\>\> Next Page</a>";
		this.updateDiv("fpbrowser-photonav", html_prev + "  |  " 
					+ this.getCurrentCount() + "  |  " + html_next);
	},
	
	setTitles: function() {
		this.updateDiv("fpbrowser-title1", this.getPhotoTitle(this.current));
		
		for(var i=1; i<6; i++) {			
			if(this.current+i < this.total)
				this.updateDiv("fpbrowser-title"+(i+1), this.getPhotoTitle(this.current+i));
			else
				this.updateDiv("fpbrowser-title"+(i+1), "");
		}
	},		

	setDivStyleLoading: function(div_id, enable) {
		var div = document.getElementById(div_id);
		if(enable) {
			div.className = "photoBox hasloading"; 
		} else {
			div.className = "photoBox noloading"
		}
	},
	
	setPhotos: function() {
		for(var i=0; i < 6; i++) {
			var div = 'fpbrowser-photo'+(i+1);
			var id = 'photo'+(i+1);
			if(this.current+i < this.total) {
				// display a loading image in the background
				this.setDivStyleLoading(div,true);
				// load the new image
				html = "<img id=\"" + id + "\" onMouseUp=\"FLICKR.showPic(" + (this.current+i) 
						+ ")\" onMouseOver=\"FLICKR.onMouseOver(\'" + id 
						+ "\')\" onMouseOut=\"FLICKR.onMouseOut(\'" + id 
						+ "\')\" title=\"" + this.clickMsg 
						+ "\" src=\"" + this.getPhotoUrl(this.current+i) 
						+ "\" alt=\"\" class=\"hidden\" "					// initially "hidden" until...
						+ "onload=\"FLICKR.afterLoadedImg('"+id+"')\" />";  // onload, then center "visible"
				//alert(html);
				this.updateDiv(div, html);
			} else {
				// no image, blank out this div
				this.setDivStyleLoading(div,false);
				this.updateDiv(div, "");
			}	
		}
	},

	/* ------- BEHAVIOR ------- */
	
	getStyle: function(el,styleProp) {
		var x = document.getElementById(el);
		if (x.currentStyle)
			var y = x.currentStyle[styleProp];
		else if (window.getComputedStyle)
			var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
		return y;
	},
	
	afterLoadedImg: function(id) {
		var img_el = document.getElementById(id);
		var fitDiv = img_el.parentNode.id;
		
		// Note: "hidden" images are set with a visibility:hidden style
		// and not display:none, because IE6 does not set size attributes
		// on a loaded image when display:none, but does set them for
		// visibility:hidden (which makes sense since space for the image
		// needs to be calulated, just not shown). In order to correctly
		// center the image in the div, we need the size.

		// center the image in the parent div		
		var divHeight = parseInt(this.getStyle(fitDiv,"height"));
		var divWidth = parseInt(this.getStyle(fitDiv,"width")); 
		var voffset = Math.floor((divHeight - img_el.height)/2);
		var hoffset = Math.floor((divWidth - img_el.width)/2);
		img_el.style.top = voffset.toString() + 'px';
		img_el.style.left = hoffset.toString() + 'px';

		// parent div, no longer 'loading'
		FLICKR.setDivStyleLoading(fitDiv, false);
		
		// show it
		img_el.className = "visible";
	},
		
	onMouseOver: function(itemId)	{
		var element = document.getElementById(itemId);
		if(element) element.style.cursor = 'pointer';
	},

	onMouseOut: function(itemId)	{
		var element = document.getElementById(itemId);
		if(element) element.style.cursor = 'default';
	},
	
	setError: function(err) {
		var html = "Flickr Error:<br>" + err;
		this.updateDiv("fpbrowser-title", html);
	},

	/* -------- GETers ------ */
	
	getPhotoIndexById: function(id) {
		var i = this.data.length;
		while (i--) {
			if (this.data[i].id == id) return i;
		}
		return -1;
	},
	
	getPhotoId: function(i) {
		return this.data[i].id;
	},
	
	getPhotoTitle: function(i) {
		if(this.data == null || this.data[i] == null) return "Loading...";
		return this.data[i].title;
	},
	
	getPhotoUrl: function(i) {
		if(this.data == null || this.data[i] == null) return "";
		var photo = this.data[i];
		var server = photo.server;
		var id = photo.id;
		var secret = photo.secret;
		var s = (this.image_size == "") ? "" : "_" + this.image_size;
		var url = "http://static.flickr.com/" + server + "/" + id + "_" + secret + s + ".jpg";
		return url;
	},
	
	getPhotoUrl_show: function(i) {
		if(this.data == null || this.data[i] == null) return "";
		var photo = this.data[i];
		var server = photo.server;
		var id = photo.id;
		var secret = photo.secret;
		var s = "";
		var url = "http://static.flickr.com/" + server + "/" + id + "_" + secret + s + ".jpg";
		return url;
	},
	
	getPhotoUrl_zoom: function(i) {
		if(this.data == null || this.data[i] == null) return "";
		var photo = this.data[i];
		var server = photo.server;
		var id = photo.id;
		var secret = photo.secret;
		var s = "_o";
		var url = "http://www.flickr.com/photo_zoom.gne?id=" + id + "&size=m";
		return url;
	},
	
	getFlickrUrl: function(i) {
		if(this.data == null || this.data[i] == null) return "";		
		var photo = this.data[i];
		var owner = this.set_owner == null ? photo.owner : this.set_owner;
		var id = photo.id;
		var url = "http://www.flickr.com/photos/" + owner + "/" + id;
		return url;
	},
	
	getCurrentCount: function() {
		if(this.data == null) return " (no photos) ";
		var page = Math.floor((this.current+1)/6) + 1;
		var totalpage = Math.floor(this.data.length/6);
		if((this.data.length % 6) != 0) totalpage += 1;
		return page + " of " + totalpage;
	},
	
	/* ------- DETAIL DISPLAY ------- */
	
	showPic: function(picNum) {
      var detail = document.getElementById("fpbrowser-detailFrame");
      var detailT = document.getElementById("fpbrowser-detailTitle");
      var detailImg = document.getElementById("fpbrowser-detailPic");  
      // load the image
      this._picNum = picNum; // needed below in zoomPic()
      detailImg.innerHTML = "<img id=\"fpbrowser-detailImage\" class=\"fpbrowser-drag\" src=\"" + this.getPhotoUrl_show(this._picNum) + "\" alt=\"\" />";
      detailT.innerHTML = this.getPhotoTitle(this._picNum);
		// for debug    = "\"" + this._picNum + ": " + this.getPhotoUrl_show(this._picNum) + "\""; 
		
	  //FLICKR._savedTarget.style.top = detail.style.top;
	  //FLICKR._savedTarget.style.left = detail.style.left;   
	
      detail.style.display='block'; // unhide it
	},
	
	closePic: function() {
      var detail=document.getElementById("fpbrowser-detailFrame");
	  detail.style.display='none'; // hide it
	},
	
	zoomPic: function() {
		document.location.target='_new'; // not working!
		document.location.href=this.getPhotoUrl_zoom(this._picNum);
		//document.location.href=this.getFlickrUrl(this._picNum);
	},
	
	nextPic: function() {
		this._picNum += 1;
		if(this._picNum >= this.total) this._picNum = 0;
		this.showPic(this._picNum);
	},
	
	prevPic: function() {
		this._picNum -= 1;
		if(this._picNum < 0) this._picNum = this.total - 1;
		this.showPic(this._picNum);
	}
};

function moveHandler(e) {
	var e = e || window.event;
	if (e.button<=1 && FLICKR._dragOK) { // Make sure mouse button is down
		FLICKR._savedTarget.style.left = e.clientX- FLICKR._dragXoffset + 'px';   // Move to the new left offset
		FLICKR._savedTarget.style.top = e.clientY- FLICKR._dragYoffset + 'px';    // Move to the new top offset.
		return false; // Return false so browser doesn't do its own thing
	}
}

function cleanup(e) {
	// called when user releases a mouse button after a drag event
	FLICKR._cursorTarget.style.cursor = FLICKR._orgCursor;
	FLICKR._dragOK = false;
	document.onmousemove = null;  // Disable the mousemove handler
	document.onmouseup = null;    // Disable the mouseup handler
}

function dragHandler(e) {
	var cursorType='-moz-grabbing';                             // Set cursor to "grab"
    if (e == null) { e = window.event; cursorType='move';}		// for IE
	var target = e.target != null ? e.target : e.srcElement;

	if(target.id == 'fpbrowser-detailFrame' 
	|| target.id == 'fpbrowser-detailNav'
	|| target.id == 'fpbrowser-detailTitle'
	|| target.id == 'fpbrowser-detailClose'
	|| target.id == 'fpbrowser-detailImage'
	|| target.id == 'fpbrowser-detailPic') {		
		FLICKR._cursorTarget = target;				// swap the cursor to "grab"
		FLICKR._orgCursor = target.style.cursor;
        target.style.cursor=cursorType;
        FLICKR._dragOK = true;
    	FLICKR._dragXoffset = e.clientX-parseInt(FLICKR._savedTarget.style.left);
    	FLICKR._dragYoffset = e.clientY-parseInt(FLICKR._savedTarget.style.top);
    	document.onmousemove = moveHandler;
    	document.onmouseup = cleanup;
    	
    	return false; // return FALSE -- IMPORTANT!!!!!!!
	}
}
	
/* ------------------ A handy error routine ------------------ */
/* works with both firefox and IE to display javascript errors */
var errorCount = 0
function reportError(msg,url,line) {
  var w = window.open("","error"+errorCount++,"resizable,status,width=400,height=300")
  var d = w.document
  d.write('<div align=center>')
  d.write('<font size=4 face="helvetica"><B>')
  d.write('opps..... A JavaScript Error Has Occurred!')
  d.write('</b></font><br><hr size=4 width="80%">')
  d.write('<form enctype="text/plain">')
  d.write('<font size=3>')
  d.write('</div><div align=left>')
  d.write('Error Message: <br>')
  d.write('<input size=60 name="message" value="'+msg+'">')
  d.write('<br>Document:<br> <input size=60 name="url" value="'+url+'">')
  d.write('<br>Line Number:<br> <input size=60 name="line" value="'+line+'">')
  d.write('<br>Browser Version:<br>')
  d.write('<input size=60 name="version" value="'+navigator.userAgent+'">')
  d.write('<br><br><input type="button" value="Dismiss" onClick="self.close()">')
  d.write('<div></font></form>')
  d.close()
  return true
}
self.onerror=reportError; /* Firefox */
onerror=reportError; /* IE */
