// JavaScript Document
// This script requires the use of dummyPage.php for the history manager.

//var pageWanted;  // This will be the page value from the query string.
var request;
addLoadEvent(init);

/* FUNCTIONS */
function init() {
	request = new HttpRequest();  
	var slideshow = new Slideshow();
}

/* AJAX REQUEST CLASS */

// HttpRequest: This is the parent object that must be instantiated to make http requests. 
// Use the HttpRequest.call() method to make server calls.
function HttpRequest() {
	this.request = new XmlHttpObject();
}
HttpRequest.prototype.call = callServer;

// XmlHttpObject: Returns a new xmlHttpRequest object.
function XmlHttpObject() {
	// Try W3C compliant browsers.
	if(window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if(window.ActiveXObject) {  // Try Internet Explorer browsers.
		var msxmls = new Array('Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP');
		for (var i = 0; i < msxmls.length; i++) {
			try {
				return new ActiveXObject(msxmls[i]);
			} catch (e) {}
		}
	}
	// XMLHttpRequest object failed to instantiate.
	throw new Error("Could not instantiate XMLHttpRequest.");
}

// callServer: Prototype of the HttpRequest.call() method. Makes the request to the server and calls a callback function to handle the response.
function callServer(url, requestParams, method, callback, callbackParams) { 

	var instance = this; 
	var params = arguments;
	///// add functionality to do PUT and DELETE.
	
	this.method = method || 'get';  // If method is not set, default to "get".	
	this.request.open(this.method, url, true);  // The request must be opened before calling "setRequestHeader" or the call will fail in Firefox.
	
	if(this.method == "get" || this.method.toLowerCase() == "get") {	
		if(requestParams != undefined) {
			url += "?" + requestParams;  // Add parameters to url call.
		} 
		requestParams = null;  // Reset variable for send call.
	} else if(this.method == "post" || this.method.toLowerCase() == "post") { 
		// Send the proper header information along with the post request.
		this.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		// The following to lines may make form posts on Win 2000 with IE6 take 2 minutes or longer to complete.  Need to research further.
		//this.request.setRequestHeader("Content-length", requestParams.length);  
		//this.request.setRequestHeader("Connection", "close");
	} 
	
	///// Add function to notify of readystatechanges and provide visual feedback.
	this.request.onreadystatechange = function() {
		switch(instance.request.readyState) {
			case 1:  // loading.			
				break;
			case 2:  // loaded.
				break;
			case 3:  // interactive.
				break;
			case 4:  // Request has completed.
				if (instance.request.statusText == "OK") { 
					params = collectionToArray(params);  // Turn arguments collection into an actual array.
					params = params.slice(4);  // Remove the first three parameters because they are not needed by the callback function.
					params.unshift(instance.request);  // Pass the request object as the first parameter for the callback function. 
					callback.apply(null, params);  // Call the callback function. 
				}	
				break;
		}
	}
	///// Add timer to abort request if it takes too long.
	this.request.send(requestParams);
}

/* NAVIGATION FUNCTIONS */

// attachAJAXNavigation: Attaches an onclick event to all anchor links, converting the static links to AJAX calls.
function attachAJAXNavigation() {

	if (!document.getElementsByTagName) return false;  // Test for support.
	if (!document.getElementsByTagName("a")) return false;  // Test for existence.	
	
	var aTags = document.getElementsByTagName("a"); // Collect all of the anchor elements.
	var href;
	var target;
	var qStringPosition;
	var queryString;
	
	var numLinks = aTags.length; 
	for (var i=0; i < numLinks; i++) {
		
		if (!aTags[i].getAttribute("href")) continue;  // If the anchor does not have an href, then skip it. 		
		href = aTags[i].getAttribute("href");
		if (href.charAt(0) == "#") continue;  // If the link's href attribute starts with a # sign, then skip it because it is a bookmark on the current page.
		target = aTags[i].getAttribute("target");
		if (target == "_blank") continue;  // Exclude pop up windows.
		qStringPosition = href.indexOf("?");
		if (qStringPosition == -1) continue;  // If there is no query string, then skip the link because it probably links to an external site.  
		
		// Attach an onclick event to the anchor elements.
		aTags[i].onclick = function() {  
			var url = parseHref(this.getAttribute("href"));
			var urlArray = url.split("?");
			request.call(urlArray[0], urlArray[1], "get", updatePage, this.getAttribute("href"));
			return false;
		};
	}	
}
// parseHref: Parse a link's href into a more usable url for the http request.
function parseHref(href) {
	
	var queryString = "";
	// Remove url hash.
	if (href.indexOf("?") > -1) { 
		if (href.indexOf("#") == -1) {
			queryString = href.slice(href.indexOf("?")+1); 
		} else { // If url hash already exists, remove it.
			queryString = href.slice(href.indexOf("?")+1, href.indexOf("#")); 
		}
	} 
	
	var valueName = 'page';
	var newQueryString = null; 
	var nameValueStart = queryString.indexOf(valueName+"=");
	var filePath = 'html/';  // The file path where the content pages reside.
	var homePage = 'our-listings';
	var url; 
	var historyframe = document.getElementsByName("historyFrame")[0];
	
	// If there are multiple name=value pairs in the query string, then parse through the query string for the "page" value.
	if (queryString.indexOf("&") > -1) { 
	
		var nameValueEnd = queryString.indexOf("&", nameValueStart);
		pageWanted = queryString.slice(nameValueStart+valueName.length+1, nameValueEnd);
		location.hash = pageWanted;  // Update url hash with new page.
		location.replace("html/dummyPage.php?page="+pageWanted);
		historyframe.src = "html/dummyPage.php?page="+pageWanted;
		var newQueryStringLeft = queryString.slice(0, nameValueStart);
		var newQueryStringRight = queryString.slice(nameValueEnd+1);
		newQueryString = newQueryStringLeft + newQueryStringRight;
		url = filePath+pageWanted+ ".php?" + newQueryString;
	
	// If there is no query string.
	} else if (href.indexOf("?") == -1) {
		
		pageWanted = homePage;
		if (document.all) historyframe.src = "html/dummyPage.php?page="+pageWanted;
		url = filePath+pageWanted+".php";
	// If there is a querystring with only one name=value pair.
	} else { 
		
		pageWanted = queryString.slice(valueName.length+1);
		location.hash = pageWanted;  // Update url hash with new page.
		
		// Record history for Internet Explorer.
		if (document.all) frames['historyFrame'].location.href = "html/dummyPage.php?page="+pageWanted;
		
		url = filePath+pageWanted+".php";
	}
	return(url);
}

// updatePage: Updates the content portion of the template, indicates the corresponding navigation link, and adds the page name to the body's id attribute.
function updatePage(placeHolder, url) {
	// Update the content holder with the new content.
	var contentDiv = document.getElementById("content");  // This is the element in the template where the main content will be inserted.
	contentDiv.innerHTML = request.request.responseText;
			
	// Change the id attribute value for the html body to the page's file name.
	var htmlBody = document.getElementsByTagName("body");
	htmlBody[0].setAttribute("id", pageWanted);

	// When new content is inserted, the links must have their onclicks re-attached.
	attachAJAXNavigation();
	attachFormSubmit();  // Re-attach the form onsubmit events.

	// Indicate current page.
	indicatePage(url);  
}

// indicatePage: Alter all page links to indicate the current page.
function indicatePage(url) {

	if (location.search) {
		var url = url || location.search;  // Defaults to the url of the freshly loaded page.	
	} else {
		var url = url || 'page=our-listings';  // Defaults to the home page value.
	}
	
	var aTags = getElementsByClassName("currentPage", "a");
	var numLinks = aTags.length; 
	for (var i=0; i < numLinks; i++) {
		
		// Remove the class indicating the current page.
		if (aTags[i].className == "currentPage") { 
			removeClass(aTags[i], "currentPage");
		}		
	}
	aTags = document.getElementsByTagName("a");
	var numLinks = aTags.length; 
	for (var i=0; i < numLinks; i++) {
		// Indicate which link is the new page.
		if (aTags[i].getAttribute("href").indexOf(url) > -1) {
			addClass(aTags[i], "currentPage");
		} 
	}
}

function HistoryManager() { 
	
	// If no hash exists, add the current page into the hash to record the page in the browser's history.
	if (location.href.indexOf("#") == -1) { 
		var page;
		if (location.href.indexOf("?") == -1) { // If there is no query string, go to home page.
			page = 'our-listings';
		} else { 
			if (location.href.indexOf("&") == -1) { 
				page = location.href.slice(location.href.indexOf("=")+1); // Get page name from url.
			} else {
				page = location.href.slice(location.href.indexOf("page=")+1, location.href.indexOf("&")); // Get page name from url.
			}
		}
		location.hash = page; 
	} 
	// Create an Iframe to save the history for Internet Explorer
	if (document.all) {
		var iframe = document.createElement('<iframe name="historyFrame" src="html/dummyPage.php" style="height:0px; width:0px; visibility:hidden; overflow:hidden;"></iframe>');
		document.getElementsByTagName("body")[0].appendChild(iframe);
	}
	this.pollHash(this.check);
}
HistoryManager.prototype.check = checkState;
HistoryManager.prototype.pollHash = startPolling;

// startPolling: Poll a function repeatedly.
function startPolling(funct) { 
		funct.call(this);
		setTimeout(function() {startPolling(funct);}, 500); // Polls the function every half second.
}
  
// checkState: Check the page state against the url and update the page if needed.
function checkState() {
	var url = window.location.href;
	// Check for hash.
	if(url.indexOf("#") != -1){
		var pageName = window.location.hash.slice(1);
		if(pageName != pageWanted) {
			var url = parseHref(pageName+".php?page="+pageName);
			var urlArray = url.split("?");
			request.call(urlArray[0], urlArray[1], "get", updatePage, "index.php?page="+pageName);  // Update page according to url hash.
		}
	}
}

/* FORM FUNCTIONS */

// attachFormSubmit: Attaches an onsubmit event to all forms, converting them to AJAX calls.
function attachFormSubmit() {
	if (!document.getElementsByTagName) return false;  // Test for support.
	if (!document.getElementsByTagName("form")) return false;  // Test for existence.	
	
	var formElements = document.getElementsByTagName("form"); // Collect all of the form elements.
	var numForms = formElements.length;
	// Go through all of the forms.
	for (var i=0; i < numForms; i++) {
		
		// Attach an onsubmit event to the form elements.
		var numElements = formElements[i].elements.length;
		
		formElements[i].onsubmit = function() {  			
			var params = [];
			// Create query string for AJAX form post from element values.
			for(var j=0; j < numElements; j++) {	 
				if(this.elements[j].name) { // Only elements with names will be added.
					params.push(this.elements[j].name+"="+encodeURIComponent(this.elements[j].value));
				}
			}		
			var queryString = params.join("&");
			request.call("html/"+this.page.value+".php", queryString, this.method, updatePage, this.action+"?page="+this.page.value);
			return false;  // Disable the default form submission.
		};
	}	
}

/* SLIDESHOW */

function Slideshow() {
	if(!document.getElementById) return false;  // test for support.
	if(!document.getElementById('slideshowPhoto')) return false;  // test for existence. 
	this.image = document.getElementById('slideshowPhoto');
	//this.image.style.visibility = "hidden";
	
	// make http request to get list of images.
	request.call("SlideShowList.php?page=home", null, "get", this.queueImages);
	// this.image.style.visibility = "hidden";
	// setOpacity(this.image, 0);
	// this.image.style.visibility = "visible";

	//fadeIn(this.image, 0);
}

Slideshow.prototype.queueImages = queueImages;
Slideshow.prototype.timer = timer;

function queueImages(placeholder) { 
	var xml = request.request.responseXML; 
	var photos = xml.getElementsByTagName("photo"); 
	var slideshowLength = photos.length;
	var slideshow = new Array;
	// Preload all of the images.
	for (var i=0; i < slideshowLength; i++) { 
		var filePath = photos[i].getAttribute("filepath");
		slideshow.push(preloadImage(filePath));
	}
	// Show the default photo for 4 seconds, then start the timer.
	window.setTimeout(function(){timer(slideshow, 0);}, 4000);
}	

function timer(slideshow, counter) { 
	var slideshowLength = slideshow.length; 
	var i = counter;
	if (i < slideshowLength-1) {
		// increment photo.
		i++;
	} else {
		// reset slideshow.
		i = 0;
	}
	displayPhoto(slideshow[i]);
	window.setTimeout(function(){timer(slideshow, i);}, 4000);  // 4 seconds
}

function displayPhoto(photo) {
	var oldPhoto = document.getElementById("slideshowPhoto"); 
	oldPhoto.parentNode.replaceChild(photo, oldPhoto);
	photo.setAttribute("id", "slideshowPhoto");
	fadeIn(photo, 0);
}

function fadeIn(element,opacity) { 
	if (opacity <= 100) {
  		setOpacity(element, opacity);
  		opacity += 5;
  		window.setTimeout(function(){fadeIn(element, opacity)}, 50);
	}
}

function setOpacity(element, opacity) {
	opacity = (opacity == 100)?99.999:opacity;
  
	// IE/Win
	element.style.filter = "alpha(opacity:"+opacity+")";
 
	// Safari<1.2, Konqueror
	element.style.KHTMLOpacity = opacity/100;
	  
	// Older Mozilla and Firefox
	element.style.MozOpacity = opacity/100;
  
	// Safari 1.2, newer Firefox and Mozilla, CSS3
	element.style.opacity = opacity/100;
}
function preloadImage(filePath) {
	if (document.images) {  // Test for browser support.
		var image = new Image(752,450); 
		image.src = filePath;
		return image;
	}
}

/* HELPER FUNCTIONS */

// addLoadEvent: attaches functions to the window onload event.  The functions are called immediately upon page load.
function addLoadEvent(func) { 
	var old_onload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			old_onload();
			func();
		};
	}
}

// collectionToArray: Takes an array collection or function arguments collection and converts it to an array so that array methods can be performed used.
function collectionToArray(collection) {
	var length = collection.length
	var newArray = new Array();
	for (var i=0; i < length; i++) {
		newArray[i] = collection[i];	
	}
	return(newArray);
}

// addClass: Add a class to an element.
function addClass(element, value) {
	if (!element.className) {
    	element.className = value;
    } else {
	    var newClassName = element.className+" "+value;
	    element.className = newClassName;
    }
}

// removeClass: Remove a class from an element.
function removeClass(element, value) {
	if (element.className) { 
		var classArray = element.className.split(' ');  // Check for mulitple classes.
		for (var i=0; i < classArray.length; i++ ) {
			if (classArray[i] == value) { 
				classArray.splice(i, 1);
				i--;  // Decrement counter because object was removed.
				element.className = classArray.join(' ');
			}
		}
	}
}
// getElementsByClassName: Grabs all HTML elements by a specified class name. Optionally, the type of HTML element to loop through can be indicated as a second parameter.
function getElementsByClassName(classname, elem) {
	
	if (!document.getElementsByTagName) return false;  // Test for support.
	
	var aFiltered = Array();
	if(!elem) elem = "*";
	var re = new RegExp('(^| )'+classname+'( |$)');
	var aElements = document.getElementsByTagName(elem); // Grab all of the specified html elements.
	
	for(var i=0; i<aElements.length ; i++) {
		if (re.test(aElements[i].className)) {			
				aFiltered.push(aElements[i]);
		}
	}

	if (aFiltered.length === 0) {
		return false;  // No elements exist.
	} else {
		return aFiltered;  // Returns an array of elements containing the selected class.	
	}
}

function updateElement(elementid,content){
	if (document.getElementById && !document.all){
		rng = document.createRange();
		el = document.getElementById(elementid);
		rng.setStartBefore(el);
		htmlFrag = rng.createContextualFragment(content);
		while (el.hasChildNodes())
		el.removeChild(el.lastChild);
		el.appendChild(htmlFrag);
	}
}
