//	JavaScript: Core functions for WebMap
//
//	wmData.js
//
//	Data functions for server link
//
//	DataLink usage:
//
// 		reqObj = DataLink(url, opts)
//
//				url: Server url (req)
//				opts.method: POST or GET default POST
//				opts.async: true or false, default true
//				opts.callback: response processing function, default reqObj.rx
//				opts.user: user id (secure only)
//				opts.password: user password (secure only)
//				opts.headers{name:value,...} - HTTP Headers
//				opts.callfail - function to call on failure
//				opts.progress - function to call during the process
//
//		reqObj.tx (data, head)
//
//				Transmit data to server, add optional headers (head={name:value})
//
//		reqObj.abort()
//
//				Abort a request in progress
//
//		status = reqObj.progress()
//
//				get current request status

function DataLink (url, opts) {
								//	At least one parameter (URL) is required
	if (arguments.length < 1) {
		throw new Error ("DataLink: Invalid syntax");
	}
	var createCalls = [
		function() {return new XMLHttpRequest();},
		function() {return new ActiveXObject("Msxml2.XMLHTTP"); },
		function() {return new ActiveXObject("Microsoft.XMLHTTP"); }
		];
	this.msStart = new Date();
	this.msNow = new Date();
	this.msComplete = new Date();
	this.params = {};
	this.params.url = url;
	this.params.method = 'POST';
	this.params.async = true;
	this.params.timeLimit = 2000;
	this.params.headers = {'content-type':	'application/x-www-form-urlencoded',
						   'encoding':		'UTF-8'};
	if ((opts) && (typeof opts == "object")) {
		for (name in opts) {
			this.params[name] = opts[name];
		}
	}
	this.params.callfail = this.params.callfail || this.callfail;
	this.params.progress = this.params.progress || this.progress;
	this.params.callback = this.params.callback || this.rx;
	this.r = null;
	var i = 0;

	for (i; i < createCalls.length; i++) {
		try {
			var testCall = createCalls [i];
			this.r = testCall();
			break;
		}
		catch (e) {
			continue;
		}
	}
	if (this.r == null) {
		throw new Error ("DataLink: XHR function required but not supported");
	}
	var request = this.r;
	var requestor = this.params;
//	this.r.pageURL = this.params.url;
	this.r.onreadystatechange = function () {
		switch (request.readyState) {
			case 0:
			case 1:
			case 2:
			case 3:
				requestor.progress (request.readyState);
				break;
			case 4:
				if (request.status >= 200 && request.status < 300) {
					requestor.callback (request);
				} else {
					requestor.callfail (request);
				}
		}
	}
}
DataLink.prototype.timeCheck = function () {
	var interval = this.msNow.getTime() - this.msStart;
	return interval;
}
DataLink.prototype.status = function () {
	return this.r.readyState;
}
DataLink.prototype.rx = function(request) {
//									Default callback function for response

	if (request.readyState != 4) {
		return false;
	}
	switch (request.getResponseHeader("Content-Type")) {
		case "application/json":
			this.json = eval('(' + request.responseText + ')');
			this.response = '';
			break;
		default:
			this.json = {};
			this.response = request.responseText;
	}
	
}
DataLink.prototype.callfail = function (r) {
	throw new Error ("DataLink: Server request failed");
}
DataLink.prototype.tx = function (data, head) {
								//
								//	Check request available
								//
	if (this.r.readyState != 0 && this.r.readyState != 4) {
		return false;			//	Request in progress
	}
								//
								// 	Add headers to params if specified
								//
	if ((head) && (typeof head == "object")) {
		for (name in head) {
			this.params.headers [name] = head [name];
		}
	}
								//
								//	Encode using x-www-form-urlencoded format
								//
	if (data) {
		var encodedData = encodeData (data);
	} else {
		var encodedData = null;
	}
	this.target = this.params.url;
	if ((this.params.method == "GET") && (this.encodedData != null)) {
		this.target += "?" + this.encodedData;
	}
								//
								//	Open request
								//
	
	if (this.params.user) {
		this.r.open (this.params.method, this.target, this.params.async,
					 this.params.user, this.params.password);
	} else {
		this.r.open (this.params.method, this.target, this.params.async);
	}
								//
								//	Set request headers
								//
	for (name in this.params.headers) {
		this.r.setRequestHeader (name, this.params.headers [name]);
	}
								//
								//	Issue send request
								//
	if (this.params.method == "GET") {
		encodedData = null;
	}
	this.r.send(encodedData);
	this.msStart.getTime();
	return true;
	function encodeData (data) {
		var pairs = [];
		var regexp = /%20/g;	//	Match an encoded space
		for (var name in data) {
			var value = data[name].toString();
			var pair = encodeURIComponent(name).replace(regexp,"+") + '=' +
					   encodeURIComponent(value).replace(regexp,"+");
			pairs.push(pair);
			}
		return pairs.join('&');
	}
}
DataLink.prototype.abort = function() {
	this.r.abort();
	return;
}
DataLink.prototype.progress = function(state) {
	return state;
}
//
//														Get <body> content from response
//
function getHTMLbody (s) {
//
//															s is a string representing an HTML document
	var parts = [];
	var result = [];
	parts = s.split('<body>');								// split string at body tag
	if (parts.length != 2) {
		throw new Error ("WebMap - Invalid HTML doc: " + s);
	}
	result = parts[1].split('</body>');						// split at body close
	if (result.length != 2) {
		throw new Error ("WebMap - Invalid HTML doc: " + s);
	}
	return result[0];										// return content of body
}
//
//														Cache class
//
//														Cache holds information relating to stored HTML
//														pages. Cache provides storage for pages, a unique label,
//														a loaded indicator and a display index.
//
function Cache (bookName) {
//	if (bookName instanceof Book) {
//		throw new Error ("Cache: invalid parameter <bookName>: " + bookName);
//	}
	this.book = bookName								// Book class object for this cache
	this.index = [];									// store for page meta data
	this.pageCount = 0;									// number of pages in cache
							// index to pane within associated display Book
}
Cache.prototype.addEntry = function (label, URL) {
	if (typeof label != "string") {
		throw new Error ("Cache: invalid parameter on addEntry <label>: " + label);
	}
	if (typeof URL != "string") {
		throw new Error ("Cache: invalid parameter on addEntry <URL>: " + URL);
	}
	var iPage = this.pageCount++;					// set index and increment count
	this.index[iPage] = {};							// create page entry
	this.index[iPage].label = label;				// set label for index
	this.index[iPage].URL = URL;					// set URL for HTML page
	this.index[iPage].bookIndex = -1;				// indicate not loaded in book object
	return iPage;									// return cache index
}
Cache.prototype.findPage = function (label) {
	var iIndex = -1;
	for (var i = 0; i < this.pageCount; i++) {
		if (this.index[i].label == label) {
			iIndex = i;
			break;
		}
	}
	if (iIndex < 0) {
		throw new Error ("Cache: " + label + " not found");
	}
	return iIndex;
}
Cache.prototype.getURL = function (i) {
	return this.index[i].URL;
}
Cache.prototype.getBookIndex = function (i) {
	return this.index[i].bookIndex;
}
Cache.prototype.store = function (i, content) {
	var bI = this.book.load(content);
	this.index[i].bookIndex = bI;
	return bI;
}
Cache.prototype.setScript = function (i, functionName) {
	this.index[i].script = functionName;
	return;
}
Cache.prototype.callScript = function (i) {
	if (this.index[i].script) {
		this.index[i].script ();
		return true;
	}
	return false;
}