Added IE/FF compatibility functions.
[pazpar2-moved-to-github.git] / js / pz2.js
index 723b176..028a555 100644 (file)
--- a/js/pz2.js
+++ b/js/pz2.js
@@ -1,5 +1,5 @@
 /*
-** $Id: pz2.js,v 1.17 2007-05-18 12:38:48 jakub Exp $
+** $Id: pz2.js,v 1.49 2007-07-26 13:47:52 jakub Exp $
 ** pz2.js - pazpar2's javascript client library.
 */
 
@@ -30,20 +30,19 @@ var pz2 = function(paramArray) {
 
     //supported pazpar2's protocol version
     __myself.suppProtoVer = '1';
-    __myself.pz2String = "search.pz2";
+    __myself.pz2String = paramArray.pazpar2path || "/pazpar2/search.pz2";
     __myself.stylesheet = paramArray.detailstylesheet || null;
-
+    __myself.useSessions = true;
+    if (paramArray.usesessions != undefined) {
+         __myself.useSessions = paramArray.usesessions;
+    }
+       
     //load stylesheet if required in async mode
     if( __myself.stylesheet ) {
         var request = new pzHttpRequest( __myself.stylesheet );
-        request.get(
-                {},
-                function ( doc ) {
-                    __myself.xslDoc = doc;
-                }
-        );
+        request.get( {}, function ( doc ) { __myself.xslDoc = doc; } );
     }
-
+       
     // at least one callback required
     if ( !paramArray )
         throw new Error("An array with parameters has to be suplied when instantiating a class");
@@ -84,7 +83,7 @@ var pz2 = function(paramArray) {
     __myself.currQuery = null;
 
     //timers
-    __myself.statTime = paramArray.stattime || 2000;
+    __myself.statTime = paramArray.stattime || 1000;
     __myself.statTimer = null;
     __myself.termTime = paramArray.termtime || 1000;
     __myself.termTimer = null;
@@ -94,10 +93,12 @@ var pz2 = function(paramArray) {
     __myself.bytargetTime = paramArray.bytargettime || 1000;
     __myself.bytargetTimer = null;
 
-    //useful?
+    // counters for each command and applied delay
     __myself.dumpFactor = 500;
     __myself.showCounter = 0;
     __myself.termCounter = 0;
+    __myself.statCounter = 0;
+    __myself.bytargetCounter = 0;
 
     // active clients, updated by stat and show
     // might be an issue since bytarget will poll accordingly
@@ -107,7 +108,16 @@ var pz2 = function(paramArray) {
     if (paramArray.autoInit !== false)
         __myself.init();
 };
-pz2.prototype = {
+pz2.prototype = 
+{
+   stop: function ()
+   {
+       clearTimeout(__myself.statTimer);
+       clearTimeout(__myself.showTimer);
+       clearTimeout(__myself.termTimer);
+       clearTimeout(__myself.bytargetTimer);
+    },
+    
     reset: function ()
     {
         __myself.sessionID = null;
@@ -115,10 +125,7 @@ pz2.prototype = {
         __myself.pingStatusOK = false;
         __myself.searchStatusOK = false;
 
-        clearTimeout(__myself.statTimer);
-        clearTimeout(__myself.showTimer);
-        clearTimeout(__myself.termTimer);
-        clearTimeout(__myself.bytargetTimer);
+        __myself.stop();
             
         if ( __myself.resetCallback )
                 __myself.resetCallback();
@@ -126,12 +133,12 @@ pz2.prototype = {
     init: function ( sessionId ) 
     {
         __myself.reset();
+
         if ( sessionId != undefined ) {
             __myself.initStatusOK = true;
             __myself.sessionID = sessionId;
             __myself.ping();
-
-        } else {
+        } else if (__myself.useSessions) {
             var request = new pzHttpRequest(__myself.pz2String, __myself.errorHandler);
             request.get(
                 { "command": "init" },
@@ -149,7 +156,9 @@ pz2.prototype = {
                         setTimeout("__myself.init()", 1000);
                 }
             );
-        }
+        } else {
+            __myself.initStatusOK = true;
+       }
     },
     // no need to ping explicitly
     ping: function () 
@@ -181,6 +190,8 @@ pz2.prototype = {
         
         __myself.showCounter = 0;
         __myself.termCounter = 0;
+        __myself.bytargetCounter = 0;
+        __myself.statCounter = 0;
         
         if( !__myself.initStatusOK )
             return;
@@ -190,10 +201,11 @@ pz2.prototype = {
         else
             throw new Error("You need to supply query to the search command");
 
-        if( filter !== undefined )
-            var searchParams = { "command": "search", "session": __myself.sessionID, "query": __myself.currQuery, "filter": filter };
-        else
-            var searchParams = { "command": "search", "session": __myself.sessionID, "query": __myself.currQuery };
+       var searchParams = { "command": "search", "query": __myself.currQuery, "session": __myself.sessionID };
+       
+        if (filter !== undefined)
+           searchParams["filter"] = filter;
+        
         var request = new pzHttpRequest(__myself.pz2String, __myself.errorHandler);
         request.get(
             searchParams,
@@ -203,23 +215,25 @@ pz2.prototype = {
                     //piggyback search
                     __myself.show(0, num, sort);
                     if ( __myself.statCallback )
-                        __myself.statTimer = setTimeout("__myself.stat()", __myself.statTime / 2);
+                        __myself.stat();
+                        //__myself.statTimer = setTimeout("__myself.stat()", __myself.statTime / 4);
                     if ( __myself.termlistCallback )
-                        //__myself.termlist();
-                        __myself.termTimer = setTimeout("__myself.termlist()", __myself.termTime / 2);
+                        __myself.termlist();
+                        //__myself.termTimer = setTimeout("__myself.termlist()", __myself.termTime / 4);
                     if ( __myself.bytargetCallback )
-                        __myself.bytargetTimer = setTimeout("__myself.bytarget()", __myself.bytargetTime / 2);
+                        __myself.bytarget();
+                        //__myself.bytargetTimer = setTimeout("__myself.bytarget()", __myself.bytargetTime / 4);
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
                     // but the response was invalid, it should never occur
-                    setTimeout("__myself.search(__myself.currQuery)", 1000);
+                    setTimeout("__myself.search(__myself.currQuery)", 500);
             }
         );
     },
     stat: function()
     {
-        if( !__myself.searchStatusOK )
+        if( !__myself.initStatusOK )
             return;
         // if called explicitly takes precedence
         clearTimeout(__myself.statTimer);
@@ -242,9 +256,13 @@ pz2.prototype = {
                     "failed": Number( data.getElementsByTagName("failed")[0].childNodes[0].nodeValue ),
                     "error": Number( data.getElementsByTagName("error")[0].childNodes[0].nodeValue )
                     };
+                    
+                    __myself.statCounter++;
+                   var delay = __myself.statTime + __myself.statCounter * __myself.dumpFactor;
+                    if ( activeClients > 0 )
+                        __myself.statTimer = setTimeout("__myself.stat()", delay);
+                    
                     __myself.statCallback(stat);
-                    if (activeClients > 0)
-                        __myself.statTimer = setTimeout("__myself.stat()", __myself.statTime); 
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
@@ -276,6 +294,7 @@ pz2.prototype = {
                     // 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 );
+                    __myself.activeClients = activeClients; 
                     var show = {
                     "activeclients": activeClients,
                     "merged": Number( data.getElementsByTagName("merged")[0].childNodes[0].nodeValue ),
@@ -303,20 +322,22 @@ pz2.prototype = {
                                }
                                else {
                                    var nodeName = hits[i].childNodes[j].nodeName;
-                                    var nodeText = hits[i].childNodes[j].firstChild.nodeValue;
+                                    var nodeText = 'ERROR'
+                                    if ( hits[i].childNodes[j].firstChild )
+                                        nodeText = hits[i].childNodes[j].firstChild.nodeValue;
                                    show.hits[i][nodeName] = nodeText;
                                }
                             }
                         }
                     }
-                    __myself.showCallback(show);
                     __myself.showCounter++;
                    var delay = __myself.showTime;
                    if (__myself.showCounter > __myself.showFastCount)
-                           //delay *= 2;
                             delay += __myself.showCounter * __myself.dumpFactor;
-                    if (activeClients > 0)
+                    if ( activeClients > 0 )
                         __myself.showTimer = setTimeout("__myself.show()", delay);
+
+                    __myself.showCallback(show);
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
@@ -325,23 +346,52 @@ pz2.prototype = {
             }
         );
     },
-    record: function(id)
+    record: function(id,offset, params)
     {
-        if( !__myself.searchStatusOK )
+        if ( params == undefined )
+            params = {};
+
+        if ( params.callback != undefined ) {
+            callback = params.callback;
+        } else {
+            callback = __myself.recordCallback;
+        }
+
+        if ( params['handle'] == undefined )
+            handle = {};
+        else
+            handle = params['handle'];
+
+        if( !__myself.searchStatusOK && __myself.useSessions)
             return;
 
         if( id !== undefined )
             __myself.currRecID = id;
         var request = new pzHttpRequest(__myself.pz2String, __myself.errorHandler);
+
+       var recordParams = { "command": "record", "session": __myself.sessionID, "id": __myself.currRecID };
+       if (offset !== undefined) {
+               recordParams["offset"] = offset;
+       }
+
+        if (params.syntax != undefined) {
+            recordParams['syntax'] = params.syntax;
+        }
+
+       __myself.currRecOffset = offset;
         request.get(
-            { "command": "record", "session": __myself.sessionID, "id": __myself.currRecID },
+           recordParams,
             function(data) {
                 var recordNode;
                 var record = new Array();
-                if ( recordNode = data.getElementsByTagName("record")[0] ) {
+                record['xmlDoc'] = data;
+               if (__myself.currRecOffset !== undefined) {
+                    record['offset'] = __myself.currRecOffset;
+                    callback(record, handle);
+                } else if ( recordNode = data.getElementsByTagName("record")[0] ) {
                     // if stylesheet was fetched do not parse the response
                     if ( __myself.xslDoc ) {
-                        record['xmlDoc'] = data;
+                        record['recid'] = recordNode.getElementsByTagName("recid")[0].firstChild.nodeValue;
                         record['xslDoc'] = __myself.xslDoc;
                     } else {
                         for ( i = 0; i < recordNode.childNodes.length; i++) {
@@ -372,12 +422,13 @@ pz2.prototype = {
                             }
                         }
                     }
-                    __myself.recordCallback(record);
+                    
+                    callback(record, handle);
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
                     // but the response was invalid, it should never occur
-                    setTimeout("__myself.record(__myself.currRecID)", 1000);
+                    setTimeout("__myself.record(__myself.currRecID)", 500);
             }
         );
     },
@@ -392,7 +443,9 @@ pz2.prototype = {
             { "command": "termlist", "session": __myself.sessionID, "name": __myself.termKeys },
             function(data) {
                 if ( data.getElementsByTagName("termlist") ) {
-                    var termList = { "activeclients": Number( data.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue ) };
+                    var activeClients = Number( data.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue );
+                    __myself.activeClients = activeClients;
+                    var termList = { "activeclients":  activeClients };
                     var termLists = data.getElementsByTagName("list");
                     //for each termlist
                     for (i = 0; i < termLists.length; i++) {
@@ -402,8 +455,10 @@ pz2.prototype = {
                         //for each term in the list
                         for (j = 0; j < terms.length; j++) { 
                             var term = {
-                                "name": terms[j].getElementsByTagName("name")[0].childNodes[0].nodeValue,
-                                "freq": terms[j].getElementsByTagName("frequency")[0].childNodes[0].nodeValue
+                                "name": (terms[j].getElementsByTagName("name")[0].childNodes.length 
+                                                ? terms[j].getElementsByTagName("name")[0].childNodes[0].nodeValue
+                                                : 'ERROR'),
+                                "freq": terms[j].getElementsByTagName("frequency")[0].childNodes[0].nodeValue || 'ERROR'
                             };
 
                             var termIdNode = terms[j].getElementsByTagName("id");
@@ -414,10 +469,12 @@ pz2.prototype = {
                         }
                     }
 
-                    __myself.termlistCallback(termList);
                     __myself.termCounter++;
-                    if (termList["activeclients"] > 0)
-                        __myself.termTimer = setTimeout("__myself.termlist()", (__myself.termTime + __myself.termCounter*__myself.dumpFactor)); 
+                    var delay = __myself.termTime + __myself.termCounter * __myself.dumpFactor;
+                    if ( activeClients > 0 )
+                        __myself.termTimer = setTimeout("__myself.termlist()", delay);
+                   
+                   __myself.termlistCallback(termList);
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
@@ -450,9 +507,13 @@ pz2.prototype = {
                             }
                         }
                     }
-                    __myself.bytargetCallback(bytarget);
+                    
+                    __myself.bytargetCounter++;
+                    var delay = __myself.bytargetTime + __myself.bytargetCounter * __myself.dumpFactor;
                     if ( __myself.activeClients > 0 )
-                        __myself.bytargetTimer = setTimeout("__myself.bytarget()", __myself.bytargetTime);
+                        __myself.bytargetTimer = setTimeout("__myself.bytarget()", delay);
+
+                    __myself.bytargetCallback(bytarget);
                 }
                 else
                     // if it gets here the http return code was 200 (pz2 errors are 417)
@@ -491,6 +552,7 @@ var pzHttpRequest = function ( url, errorHandler ) {
         this.request = null;
         this.url = url;
         this.errorHandler = errorHandler || null;
+        this.async = true;
         
         if ( window.XMLHttpRequest ) {
             this.request = new XMLHttpRequest();
@@ -507,24 +569,48 @@ pzHttpRequest.prototype =
 {
     get: function ( params, callback ) 
     {
-        this.callback = callback;
-        
-        var getUrl = this.url;
-        var paramArr = new Array();
+        this._send( 'GET', params, '', callback );
+    },
 
-        for ( var key in params ) {
-            paramArr.push(key + '=' + escape(params[key]));
-        }
+    post: function ( params, data, callback )
+    {
+        this._send( 'POST', params, data, callback );
+    },
 
-        if ( paramArr.length )
-            getUrl += '?' + paramArr.join('&');
+    load: function ()
+    {
+        this.async = false;
+        this.request.open( 'GET', this.url, this.async );
+        this.request.send('');
+        if ( this.request.status == 200 )
+            return this.request.responseXML;
+    },
 
+    _send: function ( type, params, data, callback )
+    {
+        this.callback = callback;
         var context = this;
-        this.request.open( 'GET', getUrl, true );
+        this.async = true;
+        this.request.open( type, this._urlAppendParams(params), this.async );
         this.request.onreadystatechange = function () {
             context._handleResponse();
         }
-        this.request.send(null);
+        this.request.send(data);
+    },
+
+    _urlAppendParams: function (params)
+    {
+        var getUrl = this.url;
+
+       var sep = '?';
+        var el = params;
+        for (var key in el) {
+            if (el[key] != null) {
+                getUrl += sep + key + '=' + encodeURI(el[key]);
+                sep = '&';
+            }
+        }
+        return getUrl;
     },
 
     _handleResponse: function ()
@@ -567,6 +653,116 @@ pzHttpRequest.prototype =
 
 /*
 *********************************************************************************
+** XML HELPER CLASS ************************************************************
+*********************************************************************************
+*/
+
+// DOMDocument
+
+if ( window.ActiveXObject) {
+    var DOMDoc = document;
+} else {
+    var DOMDoc = Document.prototype;
+}
+
+DOMDoc.newXmlDoc = function ( root )
+{
+    var doc;
+
+    if (document.implementation && document.implementation.createDocument) {
+        doc = document.implementation.createDocument('', root, null);
+    } else if ( window.ActiveXObject ) {
+        doc = new ActiveXObject("MSXML2.DOMDocument");
+        doc.loadXML('<' + root + '/>');
+    } else {
+        throw new Error ('No XML support in this browser');
+    }
+
+    return doc;
+}
+
+   
+DOMDoc.parseXmlFromString = function ( xmlString ) 
+{
+    var doc;
+
+    if ( window.DOMParser ) {
+        var parser = new DOMParser();
+        doc = parser.parseFromString( xmlString, "text/xml");
+    } else if ( window.ActiveXObject ) {
+        doc = new ActiveXObject("MSXML2.DOMDocument");
+        doc.loadXML( xmlString );
+    } else {
+        throw new Error ("No XML parsing support in this browser.");
+    }
+
+    return doc;
+}
+
+// DOMElement
+
+Element_removeFromDoc = function (DOM_Element)
+{
+    DOM_Element.parentNode.removeChild(DOM_Element);
+}
+
+Element_emptyChildren = function (DOM_Element)
+{
+    while( DOM_Element.firstChild ) {
+        DOM_Element.removeChild( DOM_Element.firstChild )
+    }
+}
+
+Element_appendTransformResult = function ( DOM_Element, xmlDoc, xslDoc )
+{
+    if ( window.XSLTProcessor ) {
+        var proc = new XSLTProcessor();
+        proc.importStylesheet( xslDoc );
+        var docFrag = false;
+        docFrag = proc.transformToFragment( xmlDoc, DOM_Element.ownerDocument );
+        DOM_Element.appendChild(docFrag);
+    } else if ( window.ActiveXObject ) {
+        DOM_Element.innerHTML = xmlDoc.transformNode( xslDoc );
+    } else {
+        alert( 'Unable to perform XSLT transformation in this browser' );
+    }
+}
+Element_appendTextNode = function (DOM_Element, tagName, textContent )
+{
+    var node = DOM_Element.ownerDocument.createElement(tagName);
+    var text = DOM_Element.ownerDocument.createTextNode(textContent);
+
+    DOM_Element.appendChild(node);
+    node.appendChild(text);
+
+    return node;
+}
+
+Element_setTextContent = function ( DOM_Element, textContent )
+{
+    if (typeof DOM_Element.textContent !== "undefined") {
+        DOM_Element.textContent = textContent;
+    } else if (typeof DOM_Element.innerText !== "undefined" ) {
+        DOM_Element.innerText = textContent;
+    } else {
+        throw new Error("Cannot set text content of the node, no such method.");
+    }
+}
+
+Element_getTextContent = function (DOM_Element)
+{
+    if (DOM_Element.textContent) {
+        return DOM_Element.textContent;
+    } else if (DOM_Element.text ) {
+        return DOM_Element.text;
+    } else {
+        throw new Error("Cannot get text content of the node, no such method.");
+    }
+}
+
+/*
+*********************************************************************************
 ** QUERY CLASS ******************************************************************
 *********************************************************************************
 */
@@ -587,6 +783,10 @@ pzQuery.prototype = {
         this.simpleFilter = null;
         this.numTerms = 0;
     },
+    clearSimpleQuery: function()
+    {
+        this.simpleQuery = '';
+    },
     addTerm: function(field, value)
     {
         var term = {"field": field, "value": value};
@@ -631,7 +831,7 @@ pzQuery.prototype = {
     {   
         var ccl = '';
         if( this.simpleQuery != '')
-            ccl = '"'+this.simpleQuery+'"';
+            ccl = this.simpleQuery;
         for(var i = 0; i < this.advTerms.length; i++)
         {
             if (ccl != '') ccl = ccl + ' and ';