transport.js
Summary
Zapatec Transport library. Used to fetch data from the server,
parse and serialize XML and JSON data.
Copyright (c) 2004-2007 by Zapatec, Inc.
http://www.zapatec.com
1700 MLK Way, Berkeley, California,
94709, U.S.A.
All rights reserved.
if (typeof Zapatec == 'undefined') {
Zapatec = function() {};
}
Zapatec.Transport = function() {};
if (typeof ActiveXObject != 'undefined') {
Zapatec.Transport.XMLDOM = null;
Zapatec.Transport.XMLHTTP = null;
Zapatec.Transport.pickActiveXVersion = function(aVersions) {
for (var iVn = 0; iVn < aVersions.length; iVn++) {
try {
var oDoc = new ActiveXObject(aVersions[iVn]);
if (oDoc) {
return aVersions[iVn];
}
} catch (oExpn) {};
}
return null;
};
Zapatec.Transport.XMLDOM = Zapatec.Transport.pickActiveXVersion([
'Msxml2.DOMDocument.4.0',
'Msxml2.DOMDocument.3.0',
'MSXML2.DOMDocument',
'MSXML.DOMDocument',
'Microsoft.XMLDOM'
]);
Zapatec.Transport.XMLHTTP = Zapatec.Transport.pickActiveXVersion([
'Msxml2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP'
]);
Zapatec.Transport.pickActiveXVersion = null;
}
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) {
var oContr = oArg.busyContainer;
if (typeof oContr == 'string') {
oContr = document.getElementById(oContr);
}
if (!oContr) {
return;
}
var sImage = oArg.busyImage;
if (typeof sImage != 'string') {
sImage = '';
}
sImage = sImage.split('/').pop();
if (!sImage.length) {
sImage = 'zpbusy.gif';
}
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) {
sSrc = sSrc.split('/').pop();
if (sSrc == sImage) {
return true;
}
}
}
}
}
return false;
};
Zapatec.Transport.showBusy = function(oArg) {
if (Zapatec.Transport.isBusy(oArg)) {
return;
}
var oContr = oArg.busyContainer;
if (typeof oContr == 'string') {
oContr = document.getElementById(oContr);
}
if (!oContr) {
return;
}
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';
}
if (!sImageHeight) {
sImageHeight = '35px';
}
var sPath = '';
if (sImage.indexOf('/') < 0) {
if (Zapatec.zapatecPath) {
sPath = Zapatec.zapatecPath;
} else {
sPath = Zapatec.Transport.getPath('transport.js');
}
}
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(' />');
var iContainerWidth = oContr.offsetWidth;
var iContainerHeight = oContr.offsetHeight;
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);
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) {
var oContr = oArg.busyContainer;
if (typeof oContr == 'string') {
oContr = document.getElementById(oContr);
}
if (!oContr) {
return;
}
if (Zapatec.Transport.isBusy(oArg)) {
oContr.removeChild(oContr.firstChild);
}
};
Zapatec.Transport.fetch = function(oArg) {
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;
}
var oRequest = Zapatec.Transport.createXmlHttpRequest();
if (oRequest == null) {
return null;
}
Zapatec.Transport.showBusy(oArg);
var bErrorDisplayed = false;
var funcOnReady = function () {
Zapatec.Transport.removeBusy(oArg);
try {
if (oRequest.status == 200 || oRequest.status == 304 ||
(location.protocol == 'file:' && !oRequest.status)) {
if (typeof oArg.onLoad == 'function') {
oArg.onLoad(oRequest);
}
} else if (!bErrorDisplayed) {
bErrorDisplayed = true;
Zapatec.Transport.displayError(oRequest.status,
"Error: Can't fetch " + oArg.url + '.\n' +
(oRequest.statusText || ''),
oArg.onError);
}
} catch (oExpn) {
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 {
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);
}
if (oArg.async) {
oRequest.onreadystatechange = function () {
if (oRequest.readyState == 4) {
funcOnReady();
oRequest.onreadystatechange = {};
}
};
}
if (oArg.contentType) {
oRequest.setRequestHeader('Content-Type', oArg.contentType);
}
oRequest.send(oArg.content);
if (!oArg.async) {
funcOnReady();
return oRequest;
}
} catch (oExpn) {
Zapatec.Transport.removeBusy(oArg);
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;
};
Zapatec.Transport.parseHtml = function(sHtml) {
sHtml += '';
sHtml = sHtml.replace(/^\s+/g, '');
var oTmpContr;
if (document.createElementNS) {
oTmpContr =
document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
} else {
oTmpContr = document.createElement('div');
}
oTmpContr.innerHTML = sHtml;
return oTmpContr;
};
Zapatec.Transport.evalGlobalScope = function(sScript) {
if (typeof sScript != 'string' || !sScript.match(/\S/)) {
return;
}
if (window.execScript) {
window.execScript(sScript, 'javascript');
} else if (window.eval) {
window.eval(sScript);
}
};
Zapatec.Transport.setInnerHtml = function(oArg) {
if (!oArg || typeof oArg.html != 'string') {
return;
}
var sHtml = oArg.html;
var oContr = null;
if (typeof oArg.container == 'string') {
oContr = document.getElementById(oArg.container);
} else if (typeof oArg.container == 'object') {
oContr = oArg.container;
}
var aScripts = [];
if (sHtml.match(/<\s*\/\s*script\s*>/i)) {
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/)) {
var aMatch = sToken.match(/<\s*script([^>]*)>/i);
if (aMatch) {
var aCouple = sToken.split(/<\s*script[^>]*>/i);
while (aCouple.length < 2) {
if (sToken.match(/^<\s*script[^>]*>/i)) {
aCouple.unshift('');
} else {
aCouple.push('');
}
}
aHtml.unshift(aCouple[0]);
var sAttrs = aMatch[1];
var srtScript = aCouple[1];
if (sAttrs.match(/\s+src\s*=/i)) {
srtScript = '';
} else {
srtScript = srtScript.replace(/function\s+([^(]+)/g, '$1=function');
}
aScripts.push([sAttrs, srtScript]);
} else if (iToken < aTokens.length - 1) {
aTokens[iToken - 1] += '</script>' + sToken;
} else {
aHtml.unshift(sToken);
}
} else {
aHtml.unshift(sToken);
}
}
sHtml = aHtml.join('');
}
if (oContr) {
if (window.opera) {
oContr.innerHTML = '<form></form>';
}
oContr.innerHTML = sHtml;
}
for (var iScript = 0; iScript < aScripts.length; iScript++) {
if (aScripts[iScript][1].length) {
Zapatec.Transport.evalGlobalScope(aScripts[iScript][1]);
}
var sAttrs = aScripts[iScript][0];
sAttrs = sAttrs.replace(/\s+/g, ' ').replace(/^\s/, '')
.replace(/\s$/, '').replace(/ = /g, '=');
if (sAttrs.indexOf('src=') >= 0) {
var oContr = document.body;
if (!oContr) {
oContr = document.getElementsByTagName('head')[0];
if (!oContr) {
oContr = document;
}
}
var aAttrs = sAttrs.split(' ');
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);
}
}
};
/**
* Fetches and parses XML document from the specified URL.
*
* <pre>
* When XML document is fetched and parsed, one of provided callback functions
* is called: onLoad on success or onError on error. In synchronous mode onLoad
* callback can be omitted. Instead use returned object.
*
* onLoad callback function receives XMLDocument object as argument and may use
* its documentElement and other properties.
*
* onError callback function receives following object:
* {
* errorCode: error code [number],
* errorDescription: human readable error description [string]
* }
* Error code will be 0 unless Zapatec.Transport.fetch was used to fetch URL
* and there was a problem during fetching.
*
* If method argument is not defined, more efficient XMLDOM in IE and
* document.implementation.createDocument in Mozilla will be used to fetch
* and parse document. Otherwise Zapatec.Transport.fetch will be used to fetch
* document and Zapatec.Transport.parseXml to parse.
*
* Note: Some browsers implement caching for GET requests. Caching can be
* prevented by adding 'r=' + Math.random() parameter to URL.
*
* If you use POST method, content argument should be something like
* 'var1=value1&var2=value'. If you wish to send other content, set appropriate
* contentType. E.g. to send XML string, you should set contentType: 'text/xml'.
*
* If server response contains non-ASCII characters, encoding must be specified.
* E.g. <?xml version="1.0" encoding="utf-8"?> or
* <?xml version="1.0" encoding="windows-1251"?>.
*
* If server response contains non-ASCII characters, server must send
* corresponding content-type header. E.g.
* "Content-type: text/xml; charset=utf-8" or
* "Content-type: text/xml; charset=windows-1251".
*
* Arguments object format:
* {
* url: [string] relative or absolute URL to fetch,
* method: [string, optional] method ('GET', 'POST', 'HEAD', 'PUT'),
* async: [boolean, optional] use asynchronous mode (default: true),
* contentType: [string, optional] content type when using POST,
* content: [string or object, optional] postable string or DOM object data
* when using POST,
* onLoad: [function, optional] function reference to call on success,
* onError: [function, optional] function reference to call on error,
* username: [string, optional] username,
* password: [string, optional] password,
* busyContainer: [object or string, optional] element or id of element where
* to put "Busy" animated GIF,
* busyImage: [string, optional] standard image name or custom image URL,
* busyImageWidth: [number or string, optional] image width,
* busyImageHeight: [number or string, optional] image height
* }
* </pre>
*
* @param {object} oArg Arguments object
* @return In synchronous mode XMLDocument object or null. In asynchronous mode
* always null.
* @type object
*/
Zapatec.Transport.fetchXmlDoc = function(oArg) {
// Check arguments
if (oArg == null || typeof oArg != 'object') {
return null;
}
if (!oArg.url) {
return null;
}
if (typeof oArg.async == 'undefined') {
oArg.async = true;
}
if (!oArg.onLoad) {
oArg.onLoad = null;
}
if (!oArg.onError) {
oArg.onError = null;
}
// Try more efficient methods first
if (!oArg.method && typeof oArg.username == 'undefined' &&
typeof oArg.password == 'undefined') {
if (document.implementation && document.implementation.createDocument) {
// Mozilla
var oDoc = null;
if (!oArg.reliable) {
oArg.reliable = false;
}
// Form argument for fetch
var oFetchArg = {};
for (var sKey in oArg) {
oFetchArg[sKey] = oArg[sKey];
}
// Prevent duplicate parseXml call in synchronous mode
if (oArg.async) {
oFetchArg.onLoad = function(oRequest) {
// Prevent onload being called more than once
oFetchArg.onLoad = null;
// Parse xml response string
var parser = new DOMParser();
oDoc = parser.parseFromString(oRequest.responseText, "text/xml");
// Remove "Busy" animated GIF
Zapatec.Transport.removeBusy(oArg);
// Process response
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
};
} else {
oFetchArg.onLoad = null;
}
// Fetch URL
var oRequest = Zapatec.Transport.fetch(oFetchArg);
// In synchronous mode the result is ready on the next line
if (!oArg.async && oRequest) {
// Parse xml response string
var parser = new DOMParser();
oDoc = parser.parseFromString(oRequest.responseText, "text/xml");
// Remove "Busy" animated GIF
Zapatec.Transport.removeBusy(oArg);
// Process response
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
return oDoc;
}
return null;
}
if (typeof ActiveXObject != 'undefined') {
// IE
// Show "Busy" animated GIF
Zapatec.Transport.showBusy(oArg);
// Load document
try {
var oDoc = new ActiveXObject(Zapatec.Transport.XMLDOM);
oDoc.async = oArg.async;
// Prevent duplicate onXmlDocLoad call in synchronous mode
if (oArg.async) {
oDoc.onreadystatechange = function () {
if (oDoc.readyState == 4) {
// Remove "Busy" animated GIF
Zapatec.Transport.removeBusy(oArg);
// Process response
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
// Prevent memory leak
oDoc.onreadystatechange = {};
}
};
}
oDoc.load(oArg.url);
// In synchronous mode the result is ready on the next line
if (!oArg.async) {
// Remove "Busy" animated GIF
Zapatec.Transport.removeBusy(oArg);
// Process response
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
return oDoc;
}
return null;
} catch (oExpn) {
// Remove "Busy" animated GIF
Zapatec.Transport.removeBusy(oArg);
};
}
}
// Try XMLHttpRequest
// Form argument for fetch
var oFetchArg = {};
for (var sKey in oArg) {
oFetchArg[sKey] = oArg[sKey];
}
// Prevent duplicate parseXml call in synchronous mode
if (oArg.async) {
oFetchArg.onLoad = function(oRequest) {
Zapatec.Transport.parseXml({
strXml: oRequest.responseText,
onLoad: oArg.onLoad,
onError: oArg.onError
});
};
} else {
oFetchArg.onLoad = null;
}
// Fetch URL
var oRequest = Zapatec.Transport.fetch(oFetchArg);
// In synchronous mode the result is ready on the next line
if (!oArg.async && oRequest) {
return Zapatec.Transport.parseXml({
strXml: oRequest.responseText,
onLoad: oArg.onLoad,
onError: oArg.onError
});
}
return null;
};
/**
* Parses XML string into XMLDocument object.
*
* <pre>
* When XML string is parsed, one of provided callback functions is called:
* onLoad on success or onError on error. In synchronous mode onLoad callback
* can be omitted. Instead use returned object.
*
* onLoad callback function receives XMLDocument object as argument and may use
* its documentElement and other properties.
*
* onError callback function receives following object:
* {
* errorCode: error code [number],
* errorDescription: human readable error description [string]
* }
* Error code will be always 0.
*
* Returns XMLDocument object, so onLoad callback function is optional.
* Returned value and its documentElement property should be checked before
* use because they can be null or undefined.
*
* If XML string contains non-ASCII characters, encoding must be specified.
* E.g. <?xml version="1.0" encoding="utf-8"?> or
* <?xml version="1.0" encoding="windows-1251"?>.
*
* Arguments object format:
* {
* strXml: XML string to parse [string],
* onLoad: function reference to call on success [function] (optional),
* onError: function reference to call on error [function] (optional)
* }
* </pre>
*
* @param {object} oArg Arguments object
* @return XMLDocument object or null
* @type object
*/
Zapatec.Transport.parseXml = function(oArg) {
if (oArg == null || typeof oArg != 'object') {
return null;
}
if (!oArg.strXml) {
return null;
}
if (!oArg.onLoad) {
oArg.onLoad = null;
}
if (!oArg.onError) {
oArg.onError = null;
}
if (window.DOMParser) {
// Mozilla
try {
var oDoc = (new DOMParser()).parseFromString(oArg.strXml,
'text/xml');
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
return oDoc;
} catch (oExpn) {
Zapatec.Transport.displayError(0,
"Error: Can't parse.\n" +
'String does not appear to be a valid XML fragment.',
oArg.onError);
};
return null;
}
if (typeof ActiveXObject != 'undefined') {
// IE
try {
var oDoc = new ActiveXObject(Zapatec.Transport.XMLDOM);
oDoc.loadXML(oArg.strXml);
Zapatec.Transport.onXmlDocLoad(oDoc, oArg.onLoad,
oArg.onError);
return oDoc;
} catch (oExpn) {};
}
return null;
};
/**
* Checks if there were errors during XML document fetching and parsing and
* calls onLoad or onError callback function correspondingly.
*
* @private
* @param {object} oDoc XMLDocument object
* @param {function} onLoad Callback function provided by user
* @param {function} onError Callback function provided by user
*/
Zapatec.Transport.onXmlDocLoad = function(oDoc, onLoad, onError) {
var sError = null;
if (oDoc.parseError) {
// Parsing error in IE
sError = oDoc.parseError.reason;
if (oDoc.parseError.srcText) {
sError += 'Location: ' + oDoc.parseError.url +
'\nLine number ' + oDoc.parseError.line + ', column ' +
oDoc.parseError.linepos + ':\n' +
oDoc.parseError.srcText + '\n';
}
} else if (oDoc.documentElement &&
oDoc.documentElement.tagName == 'parsererror') {
// If an error is caused while parsing, Mozilla doesn't throw an exception.
sError = oDoc.documentElement.firstChild.data + '\n' +
oDoc.documentElement.firstChild.nextSibling.firstChild.data;
} else if (!oDoc.documentElement) {
sError = 'String does not appear to be a valid XML fragment.';
}
if (sError) {
Zapatec.Transport.displayError(0,
"Error: Can't parse.\n" + sError,
onError);
} else {
if (typeof onLoad == 'function') {
onLoad(oDoc);
}
}
};
Zapatec.Transport.serializeXmlDoc = function(oDoc) {
if (window.XMLSerializer) {
return (new XMLSerializer).serializeToString(oDoc);
}
if (oDoc.xml) {
return oDoc.xml;
}
};
Zapatec.Transport.fetchJsonObj = function(oArg) {
if (oArg == null || typeof oArg != 'object') {
return null;
}
if (!oArg.url) {
return null;
}
if (typeof oArg.async == 'undefined') {
oArg.async = true;
}
if (!oArg.reliable) {
oArg.reliable = false;
}
var oFetchArg = {};
for (var sKey in oArg) {
oFetchArg[sKey] = oArg[sKey];
}
if (oArg.async) {
oFetchArg.onLoad = function(oRequest) {
Zapatec.Transport.parseJson({
strJson: oRequest.responseText,
reliable: oArg.reliable,
onLoad: oArg.onLoad,
onError: oArg.onError
});
};
} else {
oFetchArg.onLoad = null;
}
var oRequest = Zapatec.Transport.fetch(oFetchArg);
if (!oArg.async && oRequest) {
return Zapatec.Transport.parseJson({
strJson: oRequest.responseText,
reliable: oArg.reliable,
onLoad: oArg.onLoad,
onError: oArg.onError
});
}
return null;
};
Zapatec.Transport.parseJson = function(oArg) {
if (oArg == null || typeof oArg != 'object') {
return null;
}
if (!oArg.reliable) {
oArg.reliable = false;
}
if (!oArg.onLoad) {
oArg.onLoad = null;
}
if (!oArg.onError) {
oArg.onError = null;
}
var oJson = null;
try {
if (oArg.reliable) {
if (oArg.strJson) {
oJson = eval('(' + oArg.strJson + ')');
}
} else {
oJson = Zapatec.Transport.parseJsonStr(oArg.strJson);
}
} catch (oExpn) {
var sError =
"Error: Can't parse.\nString doesn't appear to be a valid JSON fragment: ";
sError += oExpn.message;
if (typeof oExpn.text != 'undefined' && oExpn.text.length) {
sError += '\n' + oExpn.text;
}
sError += '\n' + oArg.strJson;
Zapatec.Transport.displayError(0, sError, oArg.onError);
return null;
};
if (typeof oArg.onLoad == 'function') {
oArg.onLoad(oJson);
}
return oJson;
};
Zapatec.Transport.parseJsonStr = function(text) {
var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,
token,
operator;
function error(m, t) {
throw {
name: 'JSONError',
message: m,
text: t || operator || token
};
}
function next(b) {
if (b && b != operator) {
error("Expected '" + b + "'");
}
if (text) {
var t = p.exec(text);
if (t) {
if (t[2]) {
token = null;
operator = t[2];
} else {
operator = null;
try {
token = eval(t[1]);
} catch (e) {
error("Bad token", t[1]);
}
}
text = text.substring(t[0].length);
} else {
error("Unrecognized token", text);
}
} else {
// undefined changed to null because it is not supported in IE 5.0
token = operator = null;
}
}
function val() {
var k, o;
switch (operator) {
case '{':
next('{');
o = {};
if (operator != '}') {
for (;;) {
if (operator || typeof token != 'string') {
error("Missing key");
}
k = token;
next();
next(':');
o[k] = val();
if (operator != ',') {
break;
}
next(',');
}
}
next('}');
return o;
case '[':
next('[');
o = [];
if (operator != ']') {
for (;;) {
o.push(val());
if (operator != ',') {
break;
}
next(',');
}
}
next(']');
return o;
default:
if (operator !== null) {
error("Missing value");
}
k = token;
next();
return k;
}
}
next();
return val();
};
/**
* Serializes JSON object into JSON string.
*
* Was taken with changes from http://json.org/json.js.
*
* @param {object} v JSON object
* @return JSON string
* @type string
*/
Zapatec.Transport.serializeJsonObj = function(v) {
var a = [];
/*
Emit a string.
*/
function e(s) {
a[a.length] = s;
}
/*
Convert a value.
*/
function g(x) {
var c, i, l, v;
switch (typeof x) {
case 'object':
if (x) {
if (x instanceof Array) {
e('[');
l = a.length;
for (i = 0; i < x.length; i += 1) {
v = x[i];
if (typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(v);
}
}
e(']');
return;
} else if (typeof x.toString != 'undefined') {
e('{');
l = a.length;
for (i in x) {
v = x[i];
if (x.hasOwnProperty(i) &&
typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(i);
e(':');
g(v);
}
}
return e('}');
}
}
e('null');
return;
case 'number':
e(isFinite(x) ? +x : 'null');
return;
case 'string':
l = x.length;
e('"');
for (i = 0; i < l; i += 1) {
c = x.charAt(i);
if (c >= ' ') {
if (c == '\\' || c == '"') {
e('\\');
}
e(c);
} else {
switch (c) {
case '\b':
e('\\b');
break;
case '\f':
e('\\f');
break;
case '\n':
e('\\n');
break;
case '\r':
e('\\r');
break;
case '\t':
e('\\t');
break;
default:
c = c.charCodeAt();
e('\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16));
}
}
}
e('"');
return;
case 'boolean':
e(String(x));
return;
default:
e('null');
return;
}
}
g(v);
return a.join('');
};
/**
* Displays error message.
*
* <pre>
* Calls onError callback function provided by user. If there is no onError
* callback function, displays alert with human readable error description.
* onError callback function receives following object:
* {
* errorCode [number]: error code,
* errorDescription [string]: human readable error description
* }
* </pre>
*
* @private
* @param {number} iErrCode Error code
* @param {string} sError Human readable error description
* @param {function} onError Callback function provided by user
*/
Zapatec.Transport.displayError = function(iErrCode, sError, onError) {
if (typeof onError == 'function') {
onError({
errorCode: iErrCode,
errorDescription: sError
});
} else {
alert(sError);
}
};
/**
* Translates a URL to the URL relative to the specified or to absolute URL.
*
* <pre>
* Arguments object format:
* {
* url [string]: absolute or relative URL to translate; if absolute, will be
* returned as is,
* relativeTo [string, optional]: "url" will be translated to the URL relative
* to this absolute or relative URL; default: current page URL
* }
* </pre>
*
* @param {object} oArg Arguments object
* @return Translated URL
* @type string
*/
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, '
}
var sLocation;
if (sPort) {
sLocation = [oLocation.protocol, '//', oLocation.hostname, sPort,
oLocation.pathname].join('');
} else {
sLocation = oLocation.toString();
}
var sRelativeTo;
if (typeof oArg.relativeTo != 'string') {
sRelativeTo = sLocation.split('?', 2)[0];
} else {
sRelativeTo = oArg.relativeTo.split('?', 2)[0];
if (sRelativeTo.indexOf('/') < 0) {
sRelativeTo = sLocation.split('?', 2)[0];
} else if (sRelativeTo.charAt(0) != '/' &&
sRelativeTo.indexOf(':') < 0) {
sRelativeTo = Zapatec.Transport.translateUrl({
url: sRelativeTo
});
}
}
sRelativeTo = sRelativeTo.split('#')[0];
var aUrl = sUrl.split('/');
var aRelativeTo = sRelativeTo.split('/');
aRelativeTo.pop();
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('/');
return aFullUrl.join('?');
};
Zapatec.Transport.loading = {};
Zapatec.Transport.setupEvents = function(oArg) {
if (!oArg) {
return {};
}
if (oArg.force || !Zapatec.EventDriven || !oArg.url) {
return {
onLoad: oArg.onLoad,
onError: oArg.onError
};
}
var sUrl = oArg.url;
if (typeof oArg.onLoad == 'function') {
Zapatec.EventDriven.addEventListener('zpTransportOnLoad' + sUrl,
oArg.onLoad);
}
if (typeof oArg.onError == 'function') {
Zapatec.EventDriven.addEventListener('zpTransportOnError' + sUrl,
oArg.onError);
}
if (Zapatec.Transport.loading[sUrl]) {
return {
loading: true
};
} else {
Zapatec.Transport.loading[sUrl] = true;
return {
onLoad: new Function("Zapatec.EventDriven.fireEvent('zpTransportOnLoad" +
sUrl + "');Zapatec.EventDriven.removeEvent('zpTransportOnLoad" +
sUrl + "');Zapatec.EventDriven.removeEvent('zpTransportOnError" +
sUrl + "');Zapatec.Transport.loading['" + sUrl + "'] = false;"),
onError: new Function('oError',
"Zapatec.EventDriven.fireEvent('zpTransportOnError" +
sUrl + "',oError);Zapatec.EventDriven.removeEvent('zpTransportOnLoad" +
sUrl + "');Zapatec.EventDriven.removeEvent('zpTransportOnError" +
sUrl + "');Zapatec.Transport.loading['" + sUrl + "'] = false;")
};
}
};
Zapatec.Transport.loadedJS = {};
Zapatec.Transport.isLoadedJS = function(sUrl, sAbsUrl) {
if (typeof sAbsUrl == 'undefined') {
sAbsUrl = Zapatec.Transport.translateUrl({url: sUrl});
}
if (Zapatec.Transport.loadedJS[sAbsUrl]) {
return true;
}
var aScripts = document.getElementsByTagName('script');
for (var iScript = 0; iScript < aScripts.length; iScript++) {
var sSrc = aScripts[iScript].getAttribute('src') || '';
if (sSrc == sUrl) {
Zapatec.Transport.loadedJS[sAbsUrl] = true;
return true;
}
}
return false;
};
Zapatec.Transport.getPath = function(sScriptFileName) {
var aScripts = document.getElementsByTagName('script');
for (var iScript = aScripts.length - 1; iScript >= 0; iScript--) {
var sSrc = aScripts[iScript].getAttribute('src') || '';
var aTokens = sSrc.split('/');
var sLastToken = aTokens.pop();
if (sLastToken == sScriptFileName) {
return aTokens.length ? aTokens.join('/') + '/' : '';
}
}
for (var sSrc in Zapatec.Transport.loadedJS) {
var aTokens = sSrc.split('/');
var sLastToken = aTokens.pop();
if (sLastToken == sScriptFileName) {
return aTokens.length ? aTokens.join('/') + '/' : '';
}
}
return '';
};
Zapatec.Transport.include = function(sSrc, sId, bForce) {
if (Zapatec.doNotInclude) {
return;
}
var sAbsUrl = Zapatec.Transport.translateUrl({url: sSrc});
if (!bForce && Zapatec.Transport.isLoadedJS(sSrc, sAbsUrl)) {
return;
}
document.write('<script type="text/javascript" src="' + sSrc +
(typeof sId == 'string' ? '" id="' + sId : '') + '"></script>');
Zapatec.Transport.loadedJS[sAbsUrl] = true;
};
Zapatec.include = Zapatec.Transport.include;
Zapatec.Transport.includeJS = function(sSrc, sId) {
setTimeout(function() {
var oContr = document.body;
if (!oContr) {
oContr = document.getElementsByTagName('head')[0];
if (!oContr) {
oContr = document;
}
}
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sSrc;
if (typeof sId == 'string') {
oScript.id = sId;
}
oContr.appendChild(oScript);
}, 0);
};
Zapatec.Transport.loadJS = function(oArg) {
if (!(oArg instanceof Object)) {
return;
}
if (typeof oArg.async == 'undefined') {
oArg.async = true;
}
var sUrl = null;
if (oArg.url) {
sUrl = oArg.url;
} else if (oArg.module) {
var sPath = '';
if (typeof oArg.path != 'undefined') {
sPath = oArg.path;
} else if (typeof Zapatec.zapatecPath != 'undefined') {
sPath = Zapatec.zapatecPath;
}
sUrl = sPath + oArg.module + '.js';
} else {
return;
}
var sAbsUrl = Zapatec.Transport.translateUrl({url: sUrl});
if (!oArg.onLoad) {
oArg.onLoad = null;
}
if (!oArg.onError) {
oArg.onError = null;
}
if (Zapatec.doNotInclude ||
(!oArg.force && Zapatec.Transport.isLoadedJS(sUrl, sAbsUrl))) {
if (typeof oArg.onLoad == 'function') {
oArg.onLoad();
}
return;
}
var oHandlers = Zapatec.Transport.setupEvents({
url: sAbsUrl,
force: oArg.force,
onLoad: oArg.onLoad,
onError: oArg.onError
});
if (oHandlers.loading) {
return;
}
Zapatec.Transport.fetch({
url: sUrl,
async: oArg.async,
onLoad: function(oRequest) {
if (oArg.force || !Zapatec.Transport.loadedJS[sAbsUrl]) {
var aTokens = sUrl.split('/');
var sLastToken = aTokens.pop();
Zapatec.lastLoadedModule = aTokens.join('/') + '/';
Zapatec.Transport.evalGlobalScope(oRequest.responseText);
Zapatec.lastLoadedModule = null;
Zapatec.Transport.loadedJS[sAbsUrl] = true;
}
if (typeof oHandlers.onLoad == 'function') {
oHandlers.onLoad();
}
},
onError: oHandlers.onError
});
};
Zapatec.Transport.includeCSS = function(sHref) {
var oContr = document.getElementsByTagName('head')[0];
if (!oContr) {
return;
}
var oLink = document.createElement('link');
oLink.setAttribute('rel', 'stylesheet');
oLink.setAttribute('type', 'text/css');
oLink.setAttribute('href', sHref);
oContr.appendChild(oLink);
};
Zapatec.Transport.loadedCss = {};
Zapatec.Transport.loadCss = function(oArg) {
if (!(oArg instanceof Object)) {
return;
}
if (!oArg.url) {
return;
}
if (typeof oArg.async == 'undefined') {
oArg.async = true;
}
var sAbsUrl = Zapatec.Transport.translateUrl({url: oArg.url});
if (!oArg.force) {
if (Zapatec.Transport.loadedCss[sAbsUrl]) {
if (typeof oArg.onLoad == 'function') {
oArg.onLoad();
}
return;
}
var aLinks = document.getElementsByTagName('link');
for (var iLnk = 0; iLnk < aLinks.length; iLnk++) {
var sHref = aLinks[iLnk].getAttribute('href') || '';
sHref = Zapatec.Transport.translateUrl({url: sHref});
if (sHref == sAbsUrl) {
Zapatec.Transport.loadedCss[sAbsUrl] = true;
if (typeof oArg.onLoad == 'function') {
oArg.onLoad();
}
return;
}
}
}
var oHandlers = Zapatec.Transport.setupEvents({
url: sAbsUrl,
force: oArg.force,
onLoad: oArg.onLoad,
onError: oArg.onError
});
if (oHandlers.loading) {
return;
}
Zapatec.Transport.fetch({
url: oArg.url,
async: oArg.async,
onLoad: function(oRequest) {
var sCss = oRequest.responseText;
var aResultCss = [];
var aImgUrls = [];
var aCssUrls = [];
var iPos = 0;
var iNextPos = sCss.indexOf('url(', iPos);
while (iNextPos >= 0) {
iNextPos += 4;
var sToken = sCss.substring(iPos, iNextPos);
var bIsImport = /@import\s+url\($/.test(sToken);
aResultCss.push(sToken);
iPos = iNextPos;
iNextPos = sCss.indexOf(')', iPos);
if (iNextPos >= 0) {
var sImgUrl = sCss.substring(iPos, iNextPos);
sImgUrl = sImgUrl.replace(/['"]/g, '');
// Translate image URL relative to CSS file URL
sImgUrl = Zapatec.Transport.translateUrl({
url: sImgUrl,
relativeTo: oArg.url
});
// Convert to absolute URL
sImgUrl = Zapatec.Transport.translateUrl({
url: sImgUrl
});
// Add translated URL
aResultCss.push(sImgUrl);
// Add URL to the list
if (bIsImport) {
// Add CSS URL to load list
aCssUrls.push(sImgUrl);
} else {
// Add image URL to preload list
aImgUrls.push(sImgUrl);
}
// Move second cursor to the new location to start the search from
iPos = iNextPos;
// Search next pattern
iNextPos = sCss.indexOf('url(', iPos);
}
}
// Add the rest of string
aResultCss.push(sCss.substr(iPos));
// Get translated CSS text
sCss = aResultCss.join('');
// Load CSS files
Zapatec.Transport.loadCssList({
urls: aCssUrls,
async: oArg.async,
onLoad: function() {
// Add style sheet rules into the page
(new Zapatec.StyleSheet()).addParse(sCss);
// Fire event
if (typeof oHandlers.onLoad == 'function') {
oHandlers.onLoad();
}
}
});
// Add this URL to the list of loaded
Zapatec.Transport.loadedCss[sAbsUrl] = true;
// Preload images
Zapatec.Transport.preloadImages({
urls: aImgUrls,
timeout: 60000 // 1 minute
});
},
onError: oHandlers.onError
});
};
/**
* Loads several CSS files one by one it into the document.
*
* <pre>
* This function behaves differently from other Zapatec.Transport functions.
* onLoad callback function will be called in any case, even if errors occured
* during loading. If there are multiple errors, onError callback function will
* be called once for every passed URL that wasn't loaded successfully.
*
* onLoad callback function is called without arguments.
*
* onError callback function receives following object:
* {
* errorCode: server status number (404, etc.) [number],
* errorDescription: human readable error description [string]
* }
*
* Arguments object format:
* {
* urls: array of absolute or relative URLs of CSS files to load [object]
* (files will be loaded in order they appear in the array),
* async: [boolean, optional] use asynchronous mode (default: true),
* force: [boolean, optional] force reload if it is already loaded,
* onLoad: function reference to call on completion [function] (optional),
* onError: function reference to call on error [function] (optional)
* }
*
* Note: If "force" is used, you should add 'r=' + Math.random() parameter to
* URL to prevent loading from browser cache.
* </pre>
*
* @param {object} oArg Arguments object
*/
Zapatec.Transport.loadCssList = function(oArg) {
if (!(oArg instanceof Object)) {
return;
}
if (typeof oArg.async == 'undefined') {
oArg.async = true;
}
if (!oArg.onLoad) {
oArg.onLoad = null;
}
if (!oArg.onError) {
oArg.onError = null;
}
if (!oArg.urls || !oArg.urls.length) {
if (typeof oArg.onLoad == 'function') {
oArg.onLoad();
}
return;
}
var sUrl = oArg.urls.shift();
var funcOnLoad = function() {
Zapatec.Transport.loadCssList({
urls: oArg.urls,
async: oArg.async,
force: oArg.force,
onLoad: oArg.onLoad,
onError: oArg.onError
});
};
Zapatec.Transport.loadCss({
url: sUrl,
async: oArg.async,
force: oArg.force,
onLoad: funcOnLoad,
onError: function(oError) {
Zapatec.Transport.displayError(oError.errorCode, oError.errorDescription,
oArg.onError);
funcOnLoad();
}
});
};
Zapatec.Transport.imagePreloads = [];
Zapatec.Transport.preloadImages = function(oArg) {
Zapatec.Transport.imagePreloads.push(new Zapatec.PreloadImages(oArg));
};
Documentation generated by
JSDoc on Thu Aug 16 12:18:39 2007