
// $Id: transport.js 7699 2007-08-08 11:33:54Z alex $

if (typeof Zapatec == 'undefined') {
	/**
	 * @ignore Namespace definition.
	 */
	Zapatec = function() {};
}

/**
 * @constructor
 */
Zapatec.Transport = function() {};

// Determine most current versions of ActiveX objects available
if (typeof ActiveXObject != 'undefined') {

	/**
	 * String variable with most current version of XMLDOM ActiveX object name
	 * available.
	 * @private
	 */
	Zapatec.Transport.XMLDOM = null;

	/**
	 * String variable with Most current version of XMLHTTP ActiveX object name
	 * available.
	 * @private
	 */
	Zapatec.Transport.XMLHTTP = null;

	/**
	 * @ignore
	 * Returns first available ActiveX object name from the given list.
	 *
	 * @param {object} aVersions List of ActiveX object names to test
	 * @return First available ActiveX object name or null
	 * @type string
	 */
	Zapatec.Transport.pickActiveXVersion = function(aVersions) {
		for (var iVn = 0; iVn < aVersions.length; iVn++) {
			try {
				var oDoc = new ActiveXObject(aVersions[iVn]);
				if (oDoc) {
					// If it gets to this point, the string worked
					return aVersions[iVn];
				}
			} catch (oExpn) {};
		}
		return null;
	};

	/**
	 * Most current version of XMLDOM ActiveX object.
	 * @private
	 */
	Zapatec.Transport.XMLDOM = Zapatec.Transport.pickActiveXVersion([
		'Msxml2.DOMDocument.4.0',
		'Msxml2.DOMDocument.3.0',
		'MSXML2.DOMDocument',
		'MSXML.DOMDocument',
		'Microsoft.XMLDOM'
	]);

	/**
	 * Most current version of XMLHTTP ActiveX object.
	 * @private
	 */
	Zapatec.Transport.XMLHTTP = Zapatec.Transport.pickActiveXVersion([
		'Msxml2.XMLHTTP.4.0',
		'MSXML2.XMLHTTP.3.0',
		'MSXML2.XMLHTTP',
		'Microsoft.XMLHTTP'
	]);

	// We don't need this any more
	Zapatec.Transport.pickActiveXVersion = null;

}

/**
 * Creates cross browser XMLHttpRequest object.
 *
 * @return New XMLHttpRequest object.
 * @type object
 */
Zapatec.Transport.createXmlHttpRequest = function() {
	if (typeof ActiveXObject != 'undefined') {
		try {
			return new ActiveXObject(Zapatec.Transport.XMLHTTP);
		} catch (oExpn) {};
	}
	if (typeof XMLHttpRequest != 'undefined') {
		return new XMLHttpRequest();
	}
	return null;
};

Zapatec.Transport.isBusy = function(oArg) {
	// Get container
	var oContr = oArg.busyContainer;
	if (typeof oContr == 'string') {
		oContr = document.getElementById(oContr);
	}
	if (!oContr) {
		return;
	}
	// Get image name
	var sImage = oArg.busyImage;
	if (typeof sImage != 'string') {
		sImage = '';
	}
	sImage = sImage.split('/').pop();
	if (!sImage.length) {
		sImage = 'zpbusy.gif';
	}
	// Check if image is displayed
	var oFC = oContr.firstChild;
	if (oFC) {
		oFC = oFC.firstChild;
		if (oFC) {
			oFC = oFC.firstChild;
			if (oFC && oFC.tagName && oFC.tagName.toLowerCase() == 'img') {
				var sSrc = oFC.getAttribute('src');
				if (typeof sSrc == 'string' && sSrc.length) {
					// Get last token
					sSrc = sSrc.split('/').pop();
					if (sSrc == sImage) {
						return true;
					}
				}
			}
		}
	}
	return false;
};

Zapatec.Transport.showBusy = function(oArg) {
	// Make sure image is not displayed yet
	if (Zapatec.Transport.isBusy(oArg)) {
		return;
	}
	// Get container
	var oContr = oArg.busyContainer;
	if (typeof oContr == 'string') {
		oContr = document.getElementById(oContr);
	}
	if (!oContr) {
		return;
	}
	// Get image name and dimensions
	var sImage = oArg.busyImage;
	var sImageWidth = oArg.busyImageWidth;
	var sImageHeight = oArg.busyImageHeight;
	if (typeof sImage != 'string' || !sImage.length) {
		sImage = 'zpbusy.gif';
	} else {
		if (typeof sImageWidth == 'number' ||
		 (typeof sImageWidth == 'string' && /\d$/.test(sImageWidth))) {
			sImageWidth += 'px';
		}
		if (typeof sImageHeight == 'number' ||
		 (typeof sImageHeight == 'string' && /\d$/.test(sImageHeight))) {
			sImageHeight += 'px';
		}
	}
	if (!sImageWidth) {
		//sImageWidth = '65px';
		sImageWidth = '';
	}
	if (!sImageHeight) {
		//sImageHeight = '35px';
		sImageHeight = '';
	}
	// Get path
	var sPath = '';
	// Check if path is specified
	if (sImage.indexOf('/') < 0) {
		// Use default path
		if (Zapatec.zapatecPath) {
			sPath = Zapatec.zapatecPath;
		} else {
			sPath = Zapatec.Transport.getPath('transport.js');
		}
	}
	// Form tag
	var aImg = [];
	aImg.push('<img src="');
	aImg.push(sPath);
	aImg.push(sImage);
	aImg.push('"');
	if (sImageWidth || sImageHeight) {
		aImg.push(' style="');
		if (sImageWidth) {
			aImg.push('width:');
			aImg.push(sImageWidth);
			aImg.push(';');
		}
		if (sImageHeight) {
			aImg.push('height:');
			aImg.push(sImageHeight);
		}
		aImg.push('"');
	}
	aImg.push(' />');
	// Get container dimensions
	var iContainerWidth = oContr.offsetWidth;
	var iContainerHeight = oContr.offsetHeight;
	// Display image
	var oBusyContr = Zapatec.Utils.createElement('div');
	oBusyContr.style.position = 'relative';
	oBusyContr.style.zIndex = 2147483583;
	var oBusy = Zapatec.Utils.createElement('div', oBusyContr);
	oBusy.style.position = 'absolute';
	oBusy.innerHTML = aImg.join('');
	oContr.insertBefore(oBusyContr, oContr.firstChild);
	// Move to the center of container
	var iBusyWidth = oBusy.offsetWidth;
	var iBusyHeight = oBusy.offsetHeight;
	if (iContainerWidth > iBusyWidth) {
		oBusy.style.left = oContr.scrollLeft +
		 (iContainerWidth - iBusyWidth) / 2 + 'px';
	}
	if (iContainerHeight > iBusyHeight) {
		oBusy.style.top = oContr.scrollTop +
		 (iContainerHeight - iBusyHeight) / 2 + 'px';
	}
};


Zapatec.Transport.removeBusy = function(oArg) {
	// Get container
	var oContr = oArg.busyContainer;
	if (typeof oContr == 'string') {
		oContr = document.getElementById(oContr);
	}
	if (!oContr) {
		return;
	}
	// Make sure image is displayed
	if (Zapatec.Transport.isBusy(oArg)) {
		// Remove image
		oContr.removeChild(oContr.firstChild);
	}
};

Zapatec.Transport.fetch = function(oArg) {
    // Check arguments
    if (oArg == null || typeof oArg != 'object') {
        return null;
    }
    if (!oArg.url) {
        return null;
    }
    if (!oArg.method) {
        oArg.method = 'GET';
    }
    if (typeof oArg.async == 'undefined') {
        oArg.async = true;
    }
    if (!oArg.contentType && oArg.method.toUpperCase() == 'POST') {
        oArg.contentType = 'application/x-www-form-urlencoded';
    }
    if (!oArg.content) {
        oArg.content = null;
    }
    if (!oArg.onLoad) {
        oArg.onLoad = null;
    }
    if (!oArg.onError) {
        oArg.onError = null;
    }
    // Request URL
    var oRequest = Zapatec.Transport.createXmlHttpRequest();
    if (oRequest == null) {
        return null;
    }
    // Show "Busy" animated GIF
    Zapatec.Transport.showBusy(oArg);
    // IE 6 calls onreadystatechange and then raises exception if local file was
    // not found. This flag is used to prevent duplicate onError calls.
    var bErrorDisplayed = false;
    // Onready handler
    var funcOnReady = function() {
        // Remove "Busy" animated GIF
        Zapatec.Transport.removeBusy(oArg);
        // Process response
        //alert(oArg.url);
        try {
            if (oRequest.status == 200 || oRequest.status == 304 ||
			 (location.protocol == 'file:' && !oRequest.status)) {
                // OK or found, but determined unchanged and loaded from cache
                if (typeof oArg.onLoad == 'function') {
                    oArg.onLoad(oRequest);
                }
            } else if (!bErrorDisplayed) {
                bErrorDisplayed = true;
                // 404 Not found, etc.
                Zapatec.Transport.displayError(oRequest.status,
				 "Error: Can't fetch " + oArg.url + '.\n' +
				 (oRequest.statusText || ''),
				 oArg.onError);
            }
        } catch (oExpn) {
            // Firefox 1.5 raises exception on attempt to access any property of
            // oRequest if URL was not found
            if (!bErrorDisplayed) {
                bErrorDisplayed = true;
                if (oExpn.name && oExpn.name == 'NS_ERROR_NOT_AVAILABLE') {
                    Zapatec.Transport.displayError(0,
					 "Error: Can't fetch " + oArg.url + '.\nFile not found.',
					 oArg.onError);
                } else {
                    Zapatec.Transport.displayError(0,
					 "Error: Can't fetch " + oArg.url + '.\n' +
					 (oExpn.message || ''),
					 oArg.onError);
                }
            }
        };
    };
    try {
        // Open request
        if (typeof oArg.username != 'undefined' &&
		 typeof oArg.password != 'undefined') {
            oRequest.open(oArg.method, oArg.url, oArg.async,
			 oArg.username, oArg.password);
        } else {
            oRequest.open(oArg.method, oArg.url, oArg.async);
        }
        // Prevent duplicate funcOnReady call in synchronous mode
        if (oArg.async) {
            // Set onreadystatechange handler
            oRequest.onreadystatechange = function() {
                if (oRequest.readyState == 4) {
                    // Request complete
                    funcOnReady();
                    // Prevent memory leak
                    oRequest.onreadystatechange = {};
                }
            };
        }
        // Set content type if needed
        if (oArg.contentType) {
            oRequest.setRequestHeader('Content-Type', oArg.contentType);
        }
        // Send request
        oRequest.send(oArg.content);
        // In synchronous mode the result is ready on the next line
        if (!oArg.async) {
            funcOnReady();
            return oRequest;
        }
    } catch (oExpn) {
        // Remove "Busy" animated GIF
        Zapatec.Transport.removeBusy(oArg);
        // Process error
        if (!bErrorDisplayed) {
            bErrorDisplayed = true;
            if (oExpn.name && oExpn.name == 'NS_ERROR_FILE_NOT_FOUND') {
                Zapatec.Transport.displayError(0,
				 "Error: Can't fetch " + oArg.url + '.\nFile not found.',
				 oArg.onError);
            } else {
                Zapatec.Transport.displayError(0,
				 "Error: Can't fetch " + oArg.url + '.\n' +
				 (oExpn.message || ''),
				 oArg.onError);
            }
        }
    };
    return null;
};

/**
 * Parses HTML fragment into HTMLElement object.
 *
 * @param {string} sHtml HTML fragment
 * @return Div element which contains parsed HTML fragment
 * @type object
 */
Zapatec.Transport.parseHtml = function(sHtml) {
	// Convert to string
	sHtml += '';
	// Remove leading whitespace characters because Firefox and Opera don't parse
	// fragment that starts from whitespace character
	sHtml = sHtml.replace(/^\s+/g, '');
	// Create temporaty container
	var oTmpContr;
	if (document.createElementNS) {
		// use the XHTML namespace
		oTmpContr =
		 document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
	} else {
		oTmpContr = document.createElement('div');
	}
	// Parse HTML fragment
	oTmpContr.innerHTML = sHtml;
	// Return container element
	return oTmpContr;
};

/**
 * Evaluates javascript in global scope.
 *
 * <p><b>
 * Note: Global variables must be declared without "var" keyword. Otherwise
 * they will be ignored by Safari.
 * </b></p>
 *
 * @param {string} sScript Script to evaluate
 */
Zapatec.Transport.evalGlobalScope = function(sScript) {
	if (typeof sScript != 'string' || !sScript.match(/\S/)) {
		return;
	}
	if (window.execScript) {
		// IE
		window.execScript(sScript, 'javascript');
	} else if (window.eval) {
		// Others
		window.eval(sScript);
/*
 This should never be reached
	} else {
		var funcScript = new Function(sScript);
		funcScript.call(window);
*/
	}
};

/**
 * Assigns passed HTML fragment to the specified element's innerHTML property
 * and evaluates in global scope javascripts found in the fragment.
 *
 * <pre>
 * Arguments object format:
 * {
 *   html: [string] HTML fragment,
 *   container: [object or string, optional] element or id of element to put
 *    HTML fragment into
 * }
 * </pre>
 *
 * <p><b>
 * Note: Scripts are executed after HTML fragment is assigned to innerHTML.
 * If external scripts are used, they are loaded asynchronously and execution
 * sequence is not preserved.
 * </b></p>
 *
 * <p><b>
 * Note: Global variables must be declared without "var" keyword. Otherwise
 * they will be ignored by Safari.
 * </b></p>
 *
 * @param {object} oArg Arguments object
 */
Zapatec.Transport.setInnerHtml = function(oArg) {
	// Check arguments
	if (!oArg || typeof oArg.html != 'string') {
		return;
	}
	var sHtml = oArg.html;
	// Get container
	var oContr = null;
	if (typeof oArg.container == 'string') {
		oContr = document.getElementById(oArg.container);
	} else if (typeof oArg.container == 'object') {
		oContr = oArg.container;
	}
	// Extract javascripts
	var aScripts = [];
	if (sHtml.match(/<\s*\/\s*script\s*>/i)) {
		// Split whole string by </script>
		var aTokens = sHtml.split(/<\s*\/\s*script\s*>/i);
		var aHtml = [];
		for (var iToken = aTokens.length - 1; iToken >= 0; iToken--) {
			var sToken = aTokens[iToken];
			if (sToken.match(/\S/)) {
				// Search <script ... > in the middle of each token
				var aMatch = sToken.match(/<\s*script([^>]*)>/i);
				if (aMatch) {
					// Separate HTML from javascript
					var aCouple = sToken.split(/<\s*script[^>]*>/i);
					// IE doesn't put empty tokens into the array
					while (aCouple.length < 2) {
						if (sToken.match(/^<\s*script[^>]*>/i)) {
							// HTML part is absent
							aCouple.unshift('');
						} else {
							// javascript part is absent
							aCouple.push('');
						}
					}
					// Save HTML fragment
					aHtml.unshift(aCouple[0]);
					// Get script attributes
					var sAttrs = aMatch[1];
					// Get script text
					var srtScript = aCouple[1];
					// Ignore script text if "src" attribute is present
					if (sAttrs.match(/\s+src\s*=/i)) {
						srtScript = '';
					} else {
						// Fix functions: function aaa() -> aaa = function()
						srtScript = srtScript.replace(/function\s+([^(]+)/g, '$1=function');
					}
					aScripts.push([sAttrs, srtScript]);
				} else if (iToken < aTokens.length - 1) {
					// On error assume this token is a part of previous token
					aTokens[iToken - 1] += '</script>' + sToken;
				} else {
					// If this is last token, assume it is HTML fragment
					aHtml.unshift(sToken);
				}
			} else {
				// Empty token
				aHtml.unshift(sToken);
			}
		}
		// Get HTML part
		sHtml = aHtml.join('');
	}
	// Set inner HTML
	if (oContr) {
		// Opera hack
		if (window.opera) {
			// Without this line Opera will not form correct DOM structure if HTML
			// fragment contains forms
			oContr.innerHTML = '<form></form>';
		}
		oContr.innerHTML = sHtml;
	}
	// Evaluate javascripts
	for (var iScript = 0; iScript < aScripts.length; iScript++) {
		if (aScripts[iScript][1].length) {
			// Evaluate in global scope
			Zapatec.Transport.evalGlobalScope(aScripts[iScript][1]);
		}
		// Load external script
		var sAttrs = aScripts[iScript][0];
		sAttrs = sAttrs.replace(/\s+/g, ' ').replace(/^\s/, '')
		 .replace(/\s$/, '').replace(/ = /g, '=');
		if (sAttrs.indexOf('src=') >= 0) {
			// Get container
			var oContr = document.body;
			if (!oContr) {
				oContr = document.getElementsByTagName('head')[0];
				if (!oContr) {
					oContr = document;
				}
			}
			// Get attributes
			var aAttrs = sAttrs.split(' ');
			// Load script
			var oScript = Zapatec.Utils.createElement('script');
			for (var iAttr = 0; iAttr < aAttrs.length; iAttr++) {
				var aAttr = aAttrs[iAttr].split('=');
				if (aAttr.length > 1) {
					oScript.setAttribute(aAttr[0],
					 aAttr[1].match(/^[\s|"|']*([\s|\S]*[^'|"])[\s|"|']*$/)[1]);
				} else {
					oScript.setAttribute(aAttr[0], aAttr[0]);
				}
			}
			// It's important for Safari to assign attributes before appending
			oContr.appendChild(oScript);
		}
	}
};

Zapatec.Transport.displayError = function(iErrCode, sError, onError) {
    return;
    if (typeof onError == 'function') {
        onError({
            errorCode: iErrCode,
            errorDescription: sError
        });
    } else {
        //alert(sError);
    }
};


Zapatec.Transport.translateUrl = function(oArg) {
	if (!oArg || !oArg.url) {
		return null;
	}
	// Cut arguments part from url
	var aFullUrl = oArg.url.split('?', 2);
	var sUrl = aFullUrl[0];
	// Check url
	if (sUrl.indexOf(':') >= 0) {
		// Return absolute URL as is
		return oArg.url;
	}
	var oLocation = document.location;
	var sPort = oLocation.port;
	if (sPort) {
		sPort = ':' + sPort;
	}
	if (sUrl[0] == '/') {
		// Add hostname and return absolute URL as is
		return [oLocation.protocol, '//', oLocation.hostname, sPort, sUrl].join('');
	}
	// Get relativeTo
	var sLocation;
	if (sPort) {
		sLocation = [oLocation.protocol, '//', oLocation.hostname, sPort,
		 oLocation.pathname].join('');
	} else {
		sLocation = oLocation.toString();
	}
	var sRelativeTo;
	if (typeof oArg.relativeTo != 'string') {
		// By default relative to current page URL
		sRelativeTo = sLocation.split('?', 2)[0];
	} else {
		// Remove arguments from relativeTo
		sRelativeTo = oArg.relativeTo.split('?', 2)[0];
		// Check relativeTo
		if (sRelativeTo.indexOf('/') < 0) {
			// Relative to current page URL
			sRelativeTo = sLocation.split('?', 2)[0];
		} else if (sRelativeTo.charAt(0) != '/' &&
		 sRelativeTo.indexOf(':') < 0) {
			// Transform relativeTo to absolute URL to be able to translate URLs
			// starting from ../
			sRelativeTo = Zapatec.Transport.translateUrl({
				url: sRelativeTo
			});
		}
	}
	// Remove #
	sRelativeTo = sRelativeTo.split('#')[0];
	// Split URLs
	var aUrl = sUrl.split('/');
	var aRelativeTo = sRelativeTo.split('/');
	// Remove file name
	aRelativeTo.pop();
	// Form new URL
	for (var iToken = 0; iToken < aUrl.length; iToken++) {
		var sToken = aUrl[iToken];
		if (sToken == '..') {
			aRelativeTo.pop();
		} else if (sToken != '.') {
			aRelativeTo.push(sToken);
		}
	}
	aFullUrl[0] = aRelativeTo.join('/');
	// Restore arguments part
	return aFullUrl.join('?');
};

/**
 * Holds currently loading URLs to prevent duplicate loads.
 * @private
 */
Zapatec.Transport.loading = {};

