/*
+ * $Id$
** pz2.js - pazpar2's javascript client library.
*/
// at least one callback required
if ( !paramArray )
- throw new Error("Pz2.js: Array with parameters has to be suplied.");
+ throw new Error("Pz2.js: Array with parameters has to be supplied.");
//supported pazpar2's protocol version
+ this.windowid = paramArray.windowid || window.name;
this.suppProtoVer = '1';
if (typeof paramArray.pazpar2path != "undefined")
this.pz2String = paramArray.pazpar2path;
}
this.errorHandler = paramArray.errorhandler || null;
+ this.showResponseType = paramArray.showResponseType || "xml";
// function callbacks
this.initCallback = paramArray.oninit || null;
this.keepAlive = paramArray.keepAlive;
this.sessionID = null;
+ this.serviceId = paramArray.serviceId || null;
this.initStatusOK = false;
this.pingStatusOK = false;
this.searchStatusOK = false;
+ this.mergekey = paramArray.mergekey || null;
+ this.rank = paramArray.rank || null;
// for sorting
this.currentSort = "relevance";
// where are we?
this.currentStart = 0;
+ // currentNum can be overwritten in show
this.currentNum = 20;
// last full record retrieved
this.currRecOffset = null;
//timers
+ this.pingTimer = null;
this.statTime = paramArray.stattime || 1000;
this.statTimer = null;
this.termTime = paramArray.termtime || 1000;
}
// else, auto init session or wait for a user init?
if (this.useSessions && paramArray.autoInit !== false) {
- this.init();
+ this.init(this.sessionID, this.serviceId);
}
+ // Version parameter
+ this.version = paramArray.version || null;
};
pz2.prototype =
this.sessionID = null;
this.initStatusOK = false;
this.pingStatusOK = false;
+ clearTimeout(this.pingTimer);
}
this.searchStatusOK = false;
this.stop();
if ( this.resetCallback )
- this.resetCallback();
+ this.resetCallback(this.windowid);
},
- init: function ( sessionId )
+ init: function (sessionId, serviceId)
{
this.reset();
// session id as a param
- if ( sessionId != undefined && this.useSessions ) {
+ if (sessionId && this.useSessions ) {
this.initStatusOK = true;
this.sessionID = sessionId;
this.ping();
} else if (this.useSessions) {
var context = this;
var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+ var opts = {'command' : 'init'};
+ if (serviceId) opts.service = serviceId;
request.safeGet(
- { "command": "init" },
+ opts,
function(data) {
if ( data.getElementsByTagName("status")[0]
.childNodes[0].nodeValue == "OK" ) {
context.sessionID =
data.getElementsByTagName("session")[0]
.childNodes[0].nodeValue;
- setTimeout(
- function () {
- context.ping();
- },
- context.keepAlive
- );
+ if (data.getElementsByTagName("keepAlive").length > 0) {
+ context.keepAlive = data.getElementsByTagName("keepAlive")[0].childNodes[0].nodeValue;
+ }
+ context.pingTimer =
+ setTimeout(
+ function () {
+ context.ping();
+ },
+ context.keepAlive
+ );
if ( context.initCallback )
- context.initCallback();
+ context.initCallback(context.windowid);
}
else
context.throwError('Init failed. Malformed WS resonse.',
'Pz2.js: Ping not allowed (proxy mode) or session not initialized.'
);
var context = this;
+
+ clearTimeout(context.pingTimer);
+
var request = new pzHttpRequest(this.pz2String, this.errorHandler);
request.safeGet(
- { "command": "ping", "session": this.sessionID },
+ { "command": "ping", "session": this.sessionID, "windowid" : context.windowid },
function(data) {
if ( data.getElementsByTagName("status")[0]
.childNodes[0].nodeValue == "OK" ) {
context.pingStatusOK = true;
- setTimeout(
- function () {
- context.ping();
- },
- context.keepAlive
- );
+ context.pingTimer =
+ setTimeout(
+ function () {
+ context.ping();
+ },
+ context.keepAlive
+ );
}
else
context.throwError('Ping failed. Malformed WS resonse.',
var start = 0;
var searchParams = {
- "command": "search",
- "query": this.currQuery,
- "session": this.sessionID
+ "command": "search",
+ "query": this.currQuery,
+ "session": this.sessionID,
+ "windowid" : this.windowid
};
- if (filter !== undefined)
- searchParams["filter"] = filter;
+ if( sort !== undefined ) {
+ this.currentSort = sort;
+ searchParams["sort"] = sort;
+ }
+ if (filter !== undefined) searchParams["filter"] = filter;
+ if (this.mergekey) searchParams["mergekey"] = this.mergekey;
+ if (this.rank) searchParams["rank"] = this.rank;
// copy additional parmeters, do not overwrite
if (addParamsArr != undefined) {
context.searchStatusOK = true;
//piggyback search
context.show(start, num, sort);
- if ( context.statCallback )
+ if (context.statCallback)
context.stat();
- if ( context.termlistCallback )
+ if (context.termlistCallback)
context.termlist();
- if ( context.bytargetCallback )
+ if (context.bytargetCallback)
context.bytarget();
}
else
var context = this;
var request = new pzHttpRequest(this.pz2String, this.errorHandler);
request.safeGet(
- { "command": "stat", "session": this.sessionID },
+ { "command": "stat", "session": this.sessionID, "windowid" : context.windowid },
function(data) {
if ( data.getElementsByTagName("stat") ) {
var activeClients =
},
delay
);
- context.statCallback(stat);
+ context.statCallback(stat, context.windowid);
}
else
context.throwError('Stat failed. Malformed WS resonse.',
}
);
},
- show: function(start, num, sort)
+ show: function(start, num, sort, query_state)
{
if( !this.searchStatusOK && this.useSessions )
throw new Error(
var context = this;
var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+ var requestParameters =
+ {
+ "command": "show",
+ "session": this.sessionID,
+ "start": this.currentStart,
+ "num": this.currentNum,
+ "sort": this.currentSort,
+ "block": 1,
+ "type": this.showResponseType,
+ "windowid" : this.windowid
+ };
+ if (query_state)
+ requestParameters["query-state"] = query_state;
+ if (this.version && this.version > 0)
+ requestParameters["version"] = this.version;
request.safeGet(
- {
- "command": "show",
- "session": this.sessionID,
- "start": this.currentStart,
- "num": this.currentNum,
- "sort": this.currentSort,
- "block": 1
- },
- function(data) {
- if ( data.getElementsByTagName("status")[0]
- .childNodes[0].nodeValue == "OK" ) {
- // first parse the status data send along with records
- // this is strictly bound to the format
- var activeClients =
- Number( data.getElementsByTagName("activeclients")[0]
- .childNodes[0].nodeValue );
- context.activeClients = activeClients;
- var show = {
- "activeclients": activeClients,
- "merged":
- Number( data.getElementsByTagName("merged")[0]
- .childNodes[0].nodeValue ),
- "total":
- Number( data.getElementsByTagName("total")[0]
- .childNodes[0].nodeValue ),
- "start":
- Number( data.getElementsByTagName("start")[0]
- .childNodes[0].nodeValue ),
- "num":
- Number( data.getElementsByTagName("num")[0]
- .childNodes[0].nodeValue ),
- "hits": []
- };
- // parse all the first-level nodes for all <hit> tags
- var hits = data.getElementsByTagName("hit");
- for (i = 0; i < hits.length; i++)
- show.hits[i] = Element_parseChildNodes(hits[i]);
-
- context.showCounter++;
- var delay = context.showTime;
- if (context.showCounter > context.showFastCount)
- delay += context.showCounter * context.dumpFactor;
- if ( activeClients > 0 )
- context.showTimer = setTimeout(
- function () {
- context.show();
- },
- delay);
- global_show = show;
- context.showCallback(show);
- }
- else
- context.throwError('Show failed. Malformed WS resonse.',
- 114);
- }
+ requestParameters,
+ function(data, type) {
+ var show = null;
+ var activeClients = 0;
+ if (type === "json") {
+ show = {};
+ activeClients = Number(data.activeclients[0]);
+ show.activeclients = activeClients;
+ show.merged = Number(data.merged[0]);
+ show.total = Number(data.total[0]);
+ show.start = Number(data.start[0]);
+ show.num = Number(data.num[0]);
+ show.hits = data.hit;
+ } else if (data.getElementsByTagName("status")[0]
+ .childNodes[0].nodeValue == "OK") {
+ // first parse the status data send along with records
+ // this is strictly bound to the format
+ activeClients =
+ Number(data.getElementsByTagName("activeclients")[0]
+ .childNodes[0].nodeValue);
+ show = {
+ "activeclients": activeClients,
+ "merged":
+ Number( data.getElementsByTagName("merged")[0]
+ .childNodes[0].nodeValue ),
+ "total":
+ Number( data.getElementsByTagName("total")[0]
+ .childNodes[0].nodeValue ),
+ "start":
+ Number( data.getElementsByTagName("start")[0]
+ .childNodes[0].nodeValue ),
+ "num":
+ Number( data.getElementsByTagName("num")[0]
+ .childNodes[0].nodeValue ),
+ "hits": []
+ };
+ // parse all the first-level nodes for all <hit> tags
+ var hits = data.getElementsByTagName("hit");
+ for (i = 0; i < hits.length; i++)
+ show.hits[i] = Element_parseChildNodes(hits[i]);
+ } else {
+ context.throwError('Show failed. Malformed WS resonse.',
+ 114);
+ };
+
+ var approxNode = data.getElementsByTagName("approximation");
+ if (approxNode && approxNode[0] && approxNode[0].childNodes[0] && approxNode[0].childNodes[0].nodeValue)
+ show['approximation'] =
+ Number( approxNode[0].childNodes[0].nodeValue);
+
+
+ data.getElementsByTagName("")
+ context.activeClients = activeClients;
+ context.showCounter++;
+ var delay = context.showTime;
+ if (context.showCounter > context.showFastCount)
+ delay += context.showCounter * context.dumpFactor;
+ if ( activeClients > 0 )
+ context.showTimer = setTimeout(
+ function () {
+ context.show();
+ },
+ delay);
+ context.showCallback(show, context.windowid);
+ }
);
},
record: function(id, offset, syntax, handler)
var recordParams = {
"command": "record",
"session": this.sessionID,
- "id": this.currRecID
+ "id": this.currRecID,
+ "windowid" : this.windowid
};
this.currRecOffset = null;
record = new Array();
record['xmlDoc'] = data;
record['offset'] = context.currRecOffset;
- callback(record, args);
+ callback(record, args, context.windowid);
//pz2 record
} else if ( recordNode =
data.getElementsByTagName("record")[0] ) {
},
delay
);
- callback(record, args);
+ callback(record, args, context.windowid);
}
else
context.throwError('Record failed. Malformed WS resonse.',
{
"command": "termlist",
"session": this.sessionID,
- "name": this.termKeys
+ "name": this.termKeys,
+ "windowid" : this.windowid,
+ "version" : this.version
+
},
function(data) {
if ( data.getElementsByTagName("termlist") ) {
.childNodes[0].nodeValue || 'ERROR'
};
+ // Only for xtargets: id, records, filtered
var termIdNode =
terms[j].getElementsByTagName("id");
if(terms[j].getElementsByTagName("id").length)
term["id"] =
termIdNode[0].childNodes[0].nodeValue;
termList[listName][j] = term;
+
+ var recordsNode = terms[j].getElementsByTagName("records");
+ if (recordsNode && recordsNode.length)
+ term["records"] = recordsNode[0].childNodes[0].nodeValue;
+
+ var filteredNode = terms[j].getElementsByTagName("filtered");
+ if (filteredNode && filteredNode.length)
+ term["filtered"] = filteredNode[0].childNodes[0].nodeValue;
+
}
}
delay
);
- context.termlistCallback(termList);
+ context.termlistCallback(termList, context.windowid);
}
else
context.throwError('Termlist failed. Malformed WS resonse.',
var context = this;
var request = new pzHttpRequest(this.pz2String, this.errorHandler);
request.safeGet(
- { "command": "bytarget", "session": this.sessionID },
+ {
+ "command": "bytarget",
+ "session": this.sessionID,
+ "block": 1,
+ "windowid" : this.windowid,
+ "version" : this.version
+ },
function(data) {
if ( data.getElementsByTagName("status")[0]
.childNodes[0].nodeValue == "OK" ) {
== Node.ELEMENT_NODE ) {
var nodeName =
targetNodes[i].childNodes[j].nodeName;
- var nodeText =
- targetNodes[i].childNodes[j]
- .firstChild.nodeValue;
- bytarget[i][nodeName] = nodeText;
+ if (targetNodes[i].childNodes[j].firstChild != null)
+ {
+ var nodeText = targetNodes[i].childNodes[j]
+ .firstChild.nodeValue;
+ bytarget[i][nodeName] = nodeText;
+ }
+ else {
+ bytarget[i][nodeName] = "";
+ }
+
+
}
}
+ if (bytarget[i]["state"]=="Client_Disconnected") {
+ bytarget[i]["hits"] = "Error";
+ } else if (bytarget[i]["state"]=="Client_Error") {
+ bytarget[i]["hits"] = "Error";
+ } else if (bytarget[i]["state"]=="Client_Working") {
+ bytarget[i]["hits"] = "...";
+ }
+ if (bytarget[i].diagnostic == "1") {
+ bytarget[i].diagnostic = "Permanent system error";
+ } else if (bytarget[i].diagnostic == "2") {
+ bytarget[i].diagnostic = "Temporary system error";
+ }
+ var targetsSuggestions = targetNodes[i].getElementsByTagName("suggestions");
+ if (targetsSuggestions != undefined && targetsSuggestions.length>0) {
+ var suggestions = targetsSuggestions[0];
+ bytarget[i]["suggestions"] = Element_parseChildNodes(suggestions);
+ }
}
context.bytargetCounter++;
delay
);
- context.bytargetCallback(bytarget);
+ context.bytargetCallback(bytarget, context.windowid);
}
else
context.throwError('Bytarget failed. Malformed WS resonse.',
** AJAX HELPER CLASS ***********************************************************
********************************************************************************
*/
-var pzHttpRequest = function ( url, errorHandler ) {
+var pzHttpRequest = function (url, errorHandler, cookieDomain) {
this.maxUrlLength = 2048;
this.request = null;
this.url = url;
this.errorHandler = errorHandler || null;
this.async = true;
this.requestHeaders = {};
-
- if ( window.XMLHttpRequest ) {
- this.request = new XMLHttpRequest();
- } else if ( window.ActiveXObject ) {
- try {
- this.request = new ActiveXObject( 'Msxml2.XMLHTTP' );
- } catch (err) {
- this.request = new ActiveXObject( 'Microsoft.XMLHTTP' );
- }
+ this.isXDR = false;
+ this.domainRegex = /https?:\/\/([^:/]+).*/;
+ this.cookieDomain = cookieDomain || null;
+
+ var xhr = new XMLHttpRequest();
+ var domain = this._getDomainFromUrl(url);
+ if ("withCredentials" in xhr) {
+ // XHR for Chrome/Firefox/Opera/Safari.
+ } else if (domain && this._isCrossDomain(domain) &&
+ typeof XDomainRequest != "undefined") {
+ // use XDR (IE7/8) when no other way
+ xhr = new XDomainRequest();
+ this.isXDR = true;
+ } else {
+ // CORS not supported.
}
+ this.request = xhr;
};
+
pzHttpRequest.prototype =
{
safeGet: function ( params, callback )
return encoded;
},
+ _getDomainFromUrl: function (url)
+ {
+ if (this.cookieDomain) return this.cookieDomain; //explicit cookie domain
+ var m = this.domainRegex.exec(url);
+ return (m && m.length > 1) ? m[1] : null;
+ },
+
+ _strEndsWith: function (str, suffix)
+ {
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+ },
+
+ _isCrossDomain: function (domain)
+ {
+ if (this.cookieDomain) return true; //assume xdomain is cookie domain set
+ return !this._strEndsWith(domain, document.domain);
+ },
+
+ getCookie: function (sKey) {
+ return decodeURI(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*"
+ + encodeURI(sKey).replace(/[\-\.\+\*]/g, "\\$&")
+ + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
+ },
+
+ setCookie: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
+ if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
+ return false;
+ }
+ var sExpires = "";
+ if (vEnd) {
+ switch (vEnd.constructor) {
+ case Number:
+ sExpires = vEnd === Infinity
+ ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT"
+ : "; max-age=" + vEnd;
+ break;
+ case String:
+ sExpires = "; expires=" + vEnd;
+ break;
+ case Date:
+ sExpires = "; expires=" + vEnd.toGMTString();
+ break;
+ }
+ }
+ document.cookie = encodeURI(sKey) + "=" + encodeURI(sValue)
+ + sExpires
+ + (sDomain ? "; domain=" + sDomain : "")
+ + (sPath ? "; path=" + sPath : "")
+ + (bSecure ? "; secure" : "");
+ return true;
+ },
+
_send: function ( type, url, data, callback)
{
var context = this;
this.callback = callback;
this.async = true;
+ //we never do withCredentials, so if it's CORS and we have
+ //session cookie, resend it
+ var domain = this._getDomainFromUrl(url);
+ if (domain && this._isCrossDomain(domain) &&
+ this.getCookie(domain+":SESSID")) {
+ //rewrite the URL
+ var sessparam = ';jsessionid=' + this.getCookie(domain+":SESSID");
+ var q = url.indexOf('?');
+ if (q == -1) {
+ url += sessparam;
+ } else {
+ url = url.substring(0, q) + sessparam + url.substring(q);
+ }
+ }
this.request.open( type, url, this.async );
- for (var key in this.requestHeaders)
+ if (!this.isXDR) {
+ //setting headers is only allowed with XHR
+ for (var key in this.requestHeaders)
this.request.setRequestHeader(key, this.requestHeaders[key]);
- this.request.onreadystatechange = function () {
+ }
+ if (this.isXDR) {
+ this.request.onload = function () {
+ //fake XHR props
+ context.request.status = 200;
+ context.request.readyState = 4;
+ //handle
+ context._handleResponse(url);
+ }
+ this.request.onerror = function () {
+ //fake XHR props
+ context.request.status = 417; //not really, but what can we do
+ context.request.readyState = 4;
+ //handle
+ context._handleResponse(url);
+ }
+ } else {
+ this.request.onreadystatechange = function () {
context._handleResponse(url); /// url used ONLY for error reporting
+ }
}
this.request.send(data);
},
return this.url;
},
- _handleResponse: function (savedUrlForErrorReporting)
+ _handleResponse: function (requestUrl)
{
if ( this.request.readyState == 4 ) {
// pick up appplication errors first
var errNode = null;
+ // xdomainreq does not have responseXML
+ if (this.isXDR) {
+ if (this.request.contentType.match(/\/xml/)){
+ var dom = new ActiveXObject('Microsoft.XMLDOM');
+ dom.async = false;
+ dom.loadXML(this.request.responseText);
+ this.request.responseXML = dom;
+ } else {
+ this.request.responseXML = null;
+ }
+ }
if (this.request.responseXML &&
(errNode = this.request.responseXML.documentElement)
&& errNode.nodeName == 'error') {
else {
throw err;
}
- } else if (this.request.status == 200 &&
- this.request.responseXML == null) {
- var err = new Error("XML response is empty but no error " +
- "for " + savedUrlForErrorReporting);
+ }
+ else if (this.request.status == 200 &&
+ this.request.responseXML === null) {
+ if (this.request.responseText !== null) {
+ //assume JSON
+ var json = null;
+ var text = this.request.responseText;
+ if (typeof window.JSON == "undefined") {
+ json = eval("(" + text + ")");
+ } else {
+ try {
+ json = JSON.parse(text);
+ } catch (e) {
+ }
+ }
+ this.callback(json, "json");
+ } else {
+ var err = new Error("XML/Text response is empty but no error " +
+ "for " + requestUrl);
err.code = -1;
if (this.errorHandler) {
this.errorHandler(err);
} else {
throw err;
}
+ }
} else if (this.request.status == 200) {
+ //set cookie manually only if cross-domain
+ var domain = this._getDomainFromUrl(requestUrl);
+ if (domain && this._isCrossDomain(domain)) {
+ var jsessionId = this.request.responseXML
+ .documentElement.getAttribute('jsessionId');
+ if (jsessionId)
+ this.setCookie(domain+":SESSID", jsessionId);
+ }
this.callback(this.request.responseXML);
} else {
var err = new Error("HTTP response not OK: "
+ this.request.status + " - "
+ this.request.statusText );
- err.code = '00' + this.request.status;
-
+ err.code = '00' + this.request.status;
if (this.errorHandler) {
this.errorHandler(err);
}
{
var parsed = {};
var hasChildElems = false;
+ var textContent = '';
if (node.hasChildNodes()) {
var children = node.childNodes;
for (var i = 0; i < children.length; i++) {
var child = children[i];
- if (child.nodeType == Node.ELEMENT_NODE) {
+ switch (child.nodeType) {
+ case Node.ELEMENT_NODE:
hasChildElems = true;
var nodeName = child.nodeName;
if (!(nodeName in parsed))
parsed[nodeName] = [];
parsed[nodeName].push(Element_parseChildNodes(child));
+ break;
+ case Node.TEXT_NODE:
+ textContent += child.nodeValue;
+ break;
+ case Node.CDATA_SECTION_NODE:
+ textContent += child.nodeValue;
+ break;
}
}
}
var attrs = node.attributes;
for (var i = 0; i < attrs.length; i++) {
+ hasChildElems = true;
var attrName = '@' + attrs[i].nodeName;
- var attrValue = attrs[i].nodeValue;
+ var attrValue = attrs[i].value;
parsed[attrName] = attrValue;
}
- // if no nested elements, get text content
- if (node.hasChildNodes() && !hasChildElems) {
- if (node.attributes.length)
- parsed['textContent'] = node.firstChild.nodeValue;
- else
- parsed = node.firstChild.nodeValue;
- }
+ // if no nested elements/attrs set value to text
+ if (hasChildElems)
+ parsed['#text'] = textContent;
+ else
+ parsed = textContent;
return parsed;
}