Initially copied from ../spdemo
authorMike Taylor <mike@indexdata.com>
Fri, 14 Jun 2013 15:51:40 +0000 (16:51 +0100)
committerMike Taylor <mike@indexdata.com>
Fri, 14 Jun 2013 15:51:40 +0000 (16:51 +0100)
experiments/spclient/example_client.js [new file with mode: 0644]
experiments/spclient/favicon.ico [new file with mode: 0644]
experiments/spclient/index.html [new file with mode: 0644]
experiments/spclient/indexdata_logo.png [new file with mode: 0644]
experiments/spclient/pz2.js [new file with mode: 0644]
experiments/spclient/robots.txt [new file with mode: 0644]
experiments/spclient/spclient.html [deleted file]
experiments/spclient/styles.css [new file with mode: 0644]

diff --git a/experiments/spclient/example_client.js b/experiments/spclient/example_client.js
new file mode 100644 (file)
index 0000000..a8b9a9a
--- /dev/null
@@ -0,0 +1,394 @@
+/* A very simple client that shows a basic usage of the pz2.js
+*/
+
+// create a parameters array and pass it to the pz2's constructor
+// then register the form submit event with the pz2.search function
+// autoInit is set to true on default
+
+var pazpar2URL = "/pazpar2/search.pz2";
+var serviceProxyURL = "/service-proxy/";
+var authURLServiceProxy = "/service-proxy-auth";
+var pazpar2path = useServiceProxy ? serviceProxyURL : pazpar2URL;
+
+var usesessions;
+
+var showResponseType = '';
+if (document.location.hash == '#useproxy') {
+    usesessions = false;
+    pazpar2path = '/service-proxy/';
+    showResponseType = 'json';
+}
+if (useServiceProxy) {
+    usesessions = false;
+}
+
+var my_paz = new pz2( { "onshow": my_onshow,
+                    "showtime": 500,            //each timer (show, stat, term, bytarget) can be specified this way
+                    "pazpar2path": pazpar2path,
+                    "oninit": my_oninit,
+                    "onstat": my_onstat,
+                    "onterm": my_onterm,
+                    "termlist": "xtargets,subject,author",
+                    "onbytarget": my_onbytarget,
+                   "usesessions" : usesessions,
+                    "showResponseType": showResponseType,
+                    "onrecord": my_onrecord } );
+// some state vars
+var curPage = 1;
+var recPerPage = 20;
+var totalRec = 0;
+var curDetRecId = '';
+var curDetRecData = null;
+var curSort = 'relevance';
+var curFilter = null;
+var submitted = false;
+var SourceMax = 16;
+var SubjectMax = 10;
+var AuthorMax = 10;
+
+//
+// pz2.js event handlers:
+//
+function my_oninit() {
+    my_paz.stat();
+    my_paz.bytarget();
+}
+
+function my_onshow(data) {
+    totalRec = data.merged;
+    // move it out
+    var pager = document.getElementById("pager");
+    pager.innerHTML = "";
+    pager.innerHTML +='<hr/><div style="float: right">Displaying: ' 
+                    + (data.start + 1) + ' to ' + (data.start + data.num) +
+                     ' of ' + data.merged + ' (found: ' 
+                     + data.total + ')</div>';
+    drawPager(pager);
+    // navi
+    var results = document.getElementById("results");
+  
+    var html = [];
+    for (var i = 0; i < data.hits.length; i++) {
+        var hit = data.hits[i];
+             html.push('<div class="record" id="recdiv_'+hit.recid+'" >'
+            +'<span>'+ (i + 1 + recPerPage * (curPage - 1)) +'. </span>'
+            +'<a href="#" id="rec_'+hit.recid
+            +'" onclick="showDetails(this.id);return false;"><b>' 
+            + hit["md-title"] +' </b></a>'); 
+             if (hit["md-title-remainder"] !== undefined) {
+               html.push('<span>' + hit["md-title-remainder"] + ' </span>');
+             }
+             if (hit["md-title-responsibility"] !== undefined) {
+           html.push('<span><i>'+hit["md-title-responsibility"]+'</i></span>');
+       }
+        if (hit.recid == curDetRecId) {
+            html.push(renderDetails(curDetRecData));
+        }
+       html.push('</div>');
+    }
+    replaceHtml(results, html.join(''));
+}
+
+function my_onstat(data) {
+    var stat = document.getElementById("stat");
+    if (stat == null)
+       return;
+    
+    stat.innerHTML = '<b> .:STATUS INFO</b> -- Active clients: '
+                        + data.activeclients
+                        + '/' + data.clients + ' -- </span>'
+                        + '<span>Retrieved records: ' + data.records
+                        + '/' + data.hits
+                       + ' -- by ' 
+                       + (useServiceProxy ? 'service proxy' : 'pazpar2')
+                       + ' :.</span>';
+}
+
+function my_onterm(data) {
+    var termlists = [];
+    termlists.push('<hr/><b>TERMLISTS:</b><hr/><div class="termtitle">.::Sources</div>');
+    for (var i = 0; i < data.xtargets.length && i < SourceMax; i++ ) {
+        termlists.push('<a href="#" target_id='+data.xtargets[i].id
+            + ' onclick="limitTarget(this.getAttribute(\'target_id\'), this.firstChild.nodeValue);return false;">' + data.xtargets[i].name 
+        + ' </a><span> (' + data.xtargets[i].freq + ')</span><br/>');
+    }
+     
+    termlists.push('<hr/><div class="termtitle">.::Subjects</div>');
+    for (var i = 0; i < data.subject.length && i < SubjectMax; i++ ) {
+        termlists.push('<a href="#" onclick="limitQuery(\'su\', this.firstChild.nodeValue);return false;">' + data.subject[i].name + '</a><span>  (' 
+              + data.subject[i].freq + ')</span><br/>');
+    }
+     
+    termlists.push('<hr/><div class="termtitle">.::Authors</div>');
+    for (var i = 0; i < data.author.length && i < AuthorMax; i++ ) {
+        termlists.push('<a href="#" onclick="limitQuery(\'au\', this.firstChild.nodeValue);return false;">' 
+                            + data.author[i].name 
+                            + ' </a><span> (' 
+                            + data.author[i].freq 
+                            + ')</span><br/>');
+    }
+    var termlist = document.getElementById("termlist");
+    replaceHtml(termlist, termlists.join(''));
+}
+
+function my_onrecord(data) {
+    // FIXME: record is async!!
+    clearTimeout(my_paz.recordTimer);
+    // in case on_show was faster to redraw element
+    var detRecordDiv = document.getElementById('det_'+data.recid);
+    if (detRecordDiv) return;
+    curDetRecData = data;
+    var recordDiv = document.getElementById('recdiv_'+curDetRecData.recid);
+    var html = renderDetails(curDetRecData);
+    recordDiv.innerHTML += html;
+}
+
+function my_onbytarget(data) {
+    var targetDiv = document.getElementById("bytarget");
+    var table ='<table><thead><tr><td>Target ID</td><td>Hits</td><td>Diags</td>'
+        +'<td>Records</td><td>State</td></tr></thead><tbody>';
+    
+    for (var i = 0; i < data.length; i++ ) {
+        table += "<tr><td>" + data[i].id +
+            "</td><td>" + data[i].hits +
+            "</td><td>" + data[i].diagnostic +
+            "</td><td>" + data[i].records +
+            "</td><td>" + data[i].state + "</td></tr>";
+    }
+
+    table += '</tbody></table>';
+    targetDiv.innerHTML = table;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// wait until the DOM is ready
+function domReady () 
+{ 
+    document.search.onsubmit = onFormSubmitEventHandler;
+    document.search.query.value = '';
+    document.select.sort.onchange = onSelectDdChange;
+    document.select.perpage.onchange = onSelectDdChange;
+}
+
+// when search button pressed
+function onFormSubmitEventHandler() 
+{
+    resetPage();
+    loadSelect();
+    triggerSearch();
+    submitted = true;
+    return false;
+}
+
+function onSelectDdChange()
+{
+    if (!submitted) return false;
+    resetPage();
+    loadSelect();
+    my_paz.show(0, recPerPage, curSort);
+    return false;
+}
+
+function resetPage()
+{
+    curPage = 1;
+    totalRec = 0;
+}
+
+function triggerSearch ()
+{
+    my_paz.search(document.search.query.value, recPerPage, curSort, curFilter);
+}
+
+function loadSelect ()
+{
+    curSort = document.select.sort.value;
+    recPerPage = document.select.perpage.value;
+}
+
+// limit the query after clicking the facet
+function limitQuery (field, value)
+{
+    document.search.query.value += ' and ' + field + '="' + value + '"';
+    onFormSubmitEventHandler();
+}
+
+// limit by target functions
+function limitTarget (id, name)
+{
+    var navi = document.getElementById('navi');
+    navi.innerHTML = 
+        'Source: <a class="crossout" href="#" onclick="delimitTarget();return false;">'
+        + name + '</a>';
+    navi.innerHTML += '<hr/>';
+    curFilter = 'pz:id=' + id;
+    resetPage();
+    loadSelect();
+    triggerSearch();
+    return false;
+}
+
+function delimitTarget ()
+{
+    var navi = document.getElementById('navi');
+    navi.innerHTML = '';
+    curFilter = null; 
+    resetPage();
+    loadSelect();
+    triggerSearch();
+    return false;
+}
+
+function drawPager (pagerDiv)
+{
+    //client indexes pages from 1 but pz2 from 0
+    var onsides = 6;
+    var pages = Math.ceil(totalRec / recPerPage);
+    
+    var firstClkbl = ( curPage - onsides > 0 ) 
+        ? curPage - onsides
+        : 1;
+
+    var lastClkbl = firstClkbl + 2*onsides < pages
+        ? firstClkbl + 2*onsides
+        : pages;
+
+    var prev = '<span id="prev">&#60;&#60; Prev</span><b> | </b>';
+    if (curPage > 1)
+        prev = '<a href="#" id="prev" onclick="pagerPrev();">'
+        +'&#60;&#60; Prev</a><b> | </b>';
+
+    var middle = '';
+    for(var i = firstClkbl; i <= lastClkbl; i++) {
+        var numLabel = i;
+        if(i == curPage)
+            numLabel = '<b>' + i + '</b>';
+
+        middle += '<a href="#" onclick="showPage(' + i + ')"> '
+            + numLabel + ' </a>';
+    }
+    
+    var next = '<b> | </b><span id="next">Next &#62;&#62;</span>';
+    if (pages - curPage > 0)
+        next = '<b> | </b><a href="#" id="next" onclick="pagerNext()">'
+        +'Next &#62;&#62;</a>';
+
+    var predots = '';
+    if (firstClkbl > 1)
+        predots = '...';
+
+    var postdots = '';
+    if (lastClkbl < pages)
+        postdots = '...';
+
+    pagerDiv.innerHTML += '<div style="float: clear">' 
+        + prev + predots + middle + postdots + next + '</div><hr/>';
+}
+
+function showPage (pageNum)
+{
+    curPage = pageNum;
+    my_paz.showPage( curPage - 1 );
+}
+
+// simple paging functions
+
+function pagerNext() {
+    if ( totalRec - recPerPage*curPage > 0) {
+        my_paz.showNext();
+        curPage++;
+    }
+}
+
+function pagerPrev() {
+    if ( my_paz.showPrev() != false )
+        curPage--;
+}
+
+// swithing view between targets and records
+
+function switchView(view) {
+    
+    var targets = document.getElementById('targetview');
+    var records = document.getElementById('recordview');
+    
+    switch(view) {
+        case 'targetview':
+            targets.style.display = "block";            
+            records.style.display = "none";
+            break;
+        case 'recordview':
+            targets.style.display = "none";            
+            records.style.display = "block";
+            break;
+        default:
+            alert('Unknown view.');
+    }
+}
+
+// detailed record drawing
+function showDetails (prefixRecId) {
+    var recId = prefixRecId.replace('rec_', '');
+    var oldRecId = curDetRecId;
+    curDetRecId = recId;
+    
+    // remove current detailed view if any
+    var detRecordDiv = document.getElementById('det_'+oldRecId);
+    // lovin DOM!
+    if (detRecordDiv)
+      detRecordDiv.parentNode.removeChild(detRecordDiv);
+
+    // if the same clicked, just hide
+    if (recId == oldRecId) {
+        curDetRecId = '';
+        curDetRecData = null;
+        return;
+    }
+    // request the record
+    my_paz.record(recId);
+}
+
+function replaceHtml(el, html) {
+  var oldEl = typeof el === "string" ? document.getElementById(el) : el;
+  /*@cc_on // Pure innerHTML is slightly faster in IE
+    oldEl.innerHTML = html;
+    return oldEl;
+    @*/
+  var newEl = oldEl.cloneNode(false);
+  newEl.innerHTML = html;
+  oldEl.parentNode.replaceChild(newEl, oldEl);
+  /* Since we just removed the old element from the DOM, return a reference
+     to the new element, which can be used to restore variable references. */
+  return newEl;
+};
+
+function renderDetails(data, marker)
+{
+    var details = '<div class="details" id="det_'+data.recid+'"><table>';
+    if (marker) details += '<tr><td>'+ marker + '</td></tr>';
+    if (data["md-title"] != undefined) {
+        details += '<tr><td><b>Title</b></td><td><b>:</b> '+data["md-title"];
+       if (data["md-title-remainder"] !== undefined) {
+             details += ' : <span>' + data["md-title-remainder"] + ' </span>';
+       }
+       if (data["md-title-responsibility"] !== undefined) {
+             details += ' <span><i>'+ data["md-title-responsibility"] +'</i></span>';
+       }
+         details += '</td></tr>';
+    }
+    if (data["md-date"] != undefined)
+        details += '<tr><td><b>Date</b></td><td><b>:</b> ' + data["md-date"] + '</td></tr>';
+    if (data["md-author"] != undefined)
+        details += '<tr><td><b>Author</b></td><td><b>:</b> ' + data["md-author"] + '</td></tr>';
+    if (data["md-electronic-url"] != undefined)
+        details += '<tr><td><b>URL</b></td><td><b>:</b> <a href="' + data["md-electronic-url"] + '" target="_blank">' + data["md-electronic-url"] + '</a>' + '</td></tr>';
+    if (data["location"][0]["md-subject"] != undefined)
+        details += '<tr><td><b>Subject</b></td><td><b>:</b> ' + data["location"][0]["md-subject"] + '</td></tr>';
+    if (data["location"][0]["@name"] != undefined)
+        details += '<tr><td><b>Location</b></td><td><b>:</b> ' + data["location"][0]["@name"] + " (" +data["location"][0]["@id"] + ")" + '</td></tr>';
+    details += '</table></div>';
+    return details;
+}
+ //EOF
diff --git a/experiments/spclient/favicon.ico b/experiments/spclient/favicon.ico
new file mode 100644 (file)
index 0000000..35c4899
Binary files /dev/null and b/experiments/spclient/favicon.ico differ
diff --git a/experiments/spclient/index.html b/experiments/spclient/index.html
new file mode 100644 (file)
index 0000000..0954f03
--- /dev/null
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Pazpar2 demo client</title>
+  <link rel="stylesheet" href="styles.css"></link>
+  <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
+
+  <!-- pre configure -->
+  <script type="text/javascript">
+    var useServiceProxy = true;
+  </script>
+
+  <script type="text/javascript" src="pz2.js"></script>
+  <script type="text/javascript" src="example_client.js"></script>
+
+  <!-- post configure -->
+  <script type="text/javascript">
+    $(document).ready(function() { 
+       if (useServiceProxy) { 
+           var jqxhr = jQuery.get(authURLServiceProxy)
+               .fail(function() { alert("service proxy authentifiction failed"); });
+       } 
+      } 
+    );
+  </script>
+ </head>
+ <body onload="domReady();">
+
+  <div id="noscript">
+    <noscript>
+       <p>Your browser does not support or allow execution of scripts required by this site to work properly.</p>
+       <p>If you run Internet Explorer 6+ or FireFox 2.0+ and see this message ensure the JavaScript is enabled in your browser.</p>
+       <p>If you run Internet Explorer 7 add the site to the Trusted Sites group.</p>
+       <p>Finally, check your personal firewall settings.</p>
+    </noscript>
+  </div>
+
+  <div id="switchmenu">
+   <a href="#" onclick="switchView('recordview')">Record Browser</a>
+   <span> | </span>
+   <a href="#" onclick="switchView('targetview')">Target Info</a>
+  </div>
+  <div id="heading">
+   <table width="100%" border="0" cellpadding="6" cellspacing="0">
+    <tr>
+     <td width="250" height="100" align="center"><b>Pazpar2</b></td>
+     <td>
+      <form id="searchForm" name="search">
+       <input id="query" type="text" size="50"/>
+       <input id="button" type="submit" value="Search"/>
+      </form>
+     </td>
+     <td>
+      <a href="http://www.indexdata.com"><img border="0" title="IndexData home page" src="indexdata_logo.png" height="98" align="right" alt="" /></a>
+     </td>
+    </tr>
+   </table>
+  </div>
+  
+  <div id="recordview">
+   <table width="100%" border="0" cellpadding="6" cellspacing="0">
+    <tr>
+     <td width="250" valign="top">
+      <div id="termlist"></div>
+     </td>
+     <td valign="top">
+      <div id="ranking">
+       <form name="select">
+        Sort by
+        <select name="sort" id="sort">
+         <option value="relevance" selected="selected">relevance</option>
+         <option value="title:1">title</option>
+         <option value="date:0">newest</option>
+         <option value="date:1">oldest</option>
+        </select>
+        and show 
+        <select name="perpage" id="perpage">
+         <option value="10">10</option>
+         <option value="20" selected="selected">20</option>
+         <option value="30">30</option>
+         <option value="50">50</option>
+        </select>
+        per page.
+       </form>
+      </div>
+      <div id="pager"></div>
+      <div id="navi"></div>
+      <div id="results"></div>
+     </td>
+    </tr>
+   </table>
+  </div>
+  
+  <div id="targetview" style="display: none">
+   <div id="bytarget">
+       No information available yet.
+   </div>
+  </div>
+  
+  <div id="footer">
+      <div id="stat"></div>
+      <span>Copyright &copy; 1999-2013 by <a href="http://www.indexdata.com">Index Data</a></span> 
+  </div>
+
+ </body>
+</html>
diff --git a/experiments/spclient/indexdata_logo.png b/experiments/spclient/indexdata_logo.png
new file mode 100644 (file)
index 0000000..171c039
Binary files /dev/null and b/experiments/spclient/indexdata_logo.png differ
diff --git a/experiments/spclient/pz2.js b/experiments/spclient/pz2.js
new file mode 100644 (file)
index 0000000..309ad1e
--- /dev/null
@@ -0,0 +1,1110 @@
+/*
+ * $Id: 3a9980787bb5d0fe966140b243a4a5eb6768913e $
+** pz2.js - pazpar2's javascript client library.
+*/
+
+//since explorer is flawed
+if (!window['Node']) {
+    window.Node = new Object();
+    Node.ELEMENT_NODE = 1;
+    Node.ATTRIBUTE_NODE = 2;
+    Node.TEXT_NODE = 3;
+    Node.CDATA_SECTION_NODE = 4;
+    Node.ENTITY_REFERENCE_NODE = 5;
+    Node.ENTITY_NODE = 6;
+    Node.PROCESSING_INSTRUCTION_NODE = 7;
+    Node.COMMENT_NODE = 8;
+    Node.DOCUMENT_NODE = 9;
+    Node.DOCUMENT_TYPE_NODE = 10;
+    Node.DOCUMENT_FRAGMENT_NODE = 11;
+    Node.NOTATION_NODE = 12;
+}
+
+// prevent execution of more than once
+if(typeof window.pz2 == "undefined") {
+window.undefined = window.undefined;
+
+var pz2 = function ( paramArray )
+{
+    
+    // at least one callback required
+    if ( !paramArray )
+        throw new Error("Pz2.js: Array with parameters has to be supplied."); 
+
+    //supported pazpar2's protocol version
+    this.suppProtoVer = '1';
+    if (typeof paramArray.pazpar2path != "undefined")
+        this.pz2String = paramArray.pazpar2path;
+    else
+        this.pz2String = "/pazpar2/search.pz2";
+    this.useSessions = true;
+    
+    this.stylesheet = paramArray.detailstylesheet || null;
+    //load stylesheet if required in async mode
+    if( this.stylesheet ) {
+        var context = this;
+        var request = new pzHttpRequest( this.stylesheet );
+        request.get( {}, function ( doc ) { context.xslDoc = doc; } );
+    }
+    
+    this.errorHandler = paramArray.errorhandler || null;
+    this.showResponseType = paramArray.showResponseType || "xml";
+    
+    // function callbacks
+    this.initCallback = paramArray.oninit || null;
+    this.statCallback = paramArray.onstat || null;
+    this.showCallback = paramArray.onshow || null;
+    this.termlistCallback = paramArray.onterm || null;
+    this.recordCallback = paramArray.onrecord || null;
+    this.bytargetCallback = paramArray.onbytarget || null;
+    this.resetCallback = paramArray.onreset || null;
+
+    // termlist keys
+    this.termKeys = paramArray.termlist || "subject";
+    
+    // some configurational stuff
+    this.keepAlive = 50000;
+    
+    if ( paramArray.keepAlive < this.keepAlive )
+        this.keepAlive = paramArray.keepAlive;
+
+    this.sessionID = null;
+    this.serviceId = paramArray.serviceId || null;
+    this.initStatusOK = false;
+    this.pingStatusOK = false;
+    this.searchStatusOK = false;
+    
+    // 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.currRecID = null;
+    
+    // current query
+    this.currQuery = null;
+
+    //current raw record offset
+    this.currRecOffset = null;
+
+    //timers
+    this.pingTimer = null;
+    this.statTime = paramArray.stattime || 1000;
+    this.statTimer = null;
+    this.termTime = paramArray.termtime || 1000;
+    this.termTimer = null;
+    this.showTime = paramArray.showtime || 1000;
+    this.showTimer = null;
+    this.showFastCount = 4;
+    this.bytargetTime = paramArray.bytargettime || 1000;
+    this.bytargetTimer = null;
+    this.recordTime = paramArray.recordtime || 500;
+    this.recordTimer = null;
+
+    // counters for each command and applied delay
+    this.dumpFactor = 500;
+    this.showCounter = 0;
+    this.termCounter = 0;
+    this.statCounter = 0;
+    this.bytargetCounter = 0;
+    this.recordCounter = 0;
+
+    // active clients, updated by stat and show
+    // might be an issue since bytarget will poll accordingly
+    this.activeClients = 1;
+
+    // if in proxy mode no need to init
+    if (paramArray.usesessions != undefined) {
+         this.useSessions = paramArray.usesessions;
+        this.initStatusOK = true;
+    }
+    // else, auto init session or wait for a user init?
+    if (this.useSessions && paramArray.autoInit !== false) {
+        this.init(this.sessionID, this.serviceId);
+    }
+    // Version parameter
+    this.version = paramArray.version || null;
+};
+
+pz2.prototype = 
+{
+    //error handler for async error throws
+   throwError: function (errMsg, errCode)
+   {
+        var err = new Error(errMsg);
+        if (errCode) err.code = errCode;
+                
+        if (this.errorHandler) {
+            this.errorHandler(err);
+        }
+        else {
+            throw err;
+        }
+   },
+
+    // stop activity by clearing tiemouts 
+   stop: function ()
+   {
+       clearTimeout(this.statTimer);
+       clearTimeout(this.showTimer);
+       clearTimeout(this.termTimer);
+       clearTimeout(this.bytargetTimer);
+    },
+    
+    // reset status variables
+    reset: function ()
+    {   
+        if ( this.useSessions ) {
+            this.sessionID = null;
+            this.initStatusOK = false;
+            this.pingStatusOK = false;
+            clearTimeout(this.pingTimer);
+        }
+        this.searchStatusOK = false;
+        this.stop();
+            
+        if ( this.resetCallback )
+                this.resetCallback();
+    },
+
+    init: function (sessionId, serviceId) 
+    {
+        this.reset();
+        
+        // session id as a param
+        if (sessionId && this.useSessions ) {
+            this.initStatusOK = true;
+            this.sessionID = sessionId;
+            this.ping();
+        // old school direct pazpar2 init
+        } 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(
+                opts,
+                function(data) {
+                    if ( data.getElementsByTagName("status")[0]
+                            .childNodes[0].nodeValue == "OK" ) {
+                        if ( data.getElementsByTagName("protocol")[0]
+                                .childNodes[0].nodeValue 
+                            != context.suppProtoVer )
+                            throw new Error(
+                                "Server's protocol not supported by the client"
+                            );
+                        context.initStatusOK = true;
+                        context.sessionID = 
+                            data.getElementsByTagName("session")[0]
+                                .childNodes[0].nodeValue;
+                        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();
+                    }
+                    else
+                        context.throwError('Init failed. Malformed WS resonse.',
+                                            110);
+                }
+            );
+        // when through proxy no need to init
+        } else {
+            this.initStatusOK = true;
+       }
+    },
+    // no need to ping explicitly
+    ping: function () 
+    {
+        // pinging only makes sense when using pazpar2 directly
+        if( !this.initStatusOK || !this.useSessions )
+            throw new Error(
+            '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, "windowid" : window.name },
+            function(data) {
+                if ( data.getElementsByTagName("status")[0]
+                        .childNodes[0].nodeValue == "OK" ) {
+                    context.pingStatusOK = true;
+                    context.pingTimer =
+                        setTimeout(
+                            function () {
+                                context.ping();
+                            },
+                            context.keepAlive
+                        );
+                }
+                else
+                    context.throwError('Ping failed. Malformed WS resonse.',
+                                        111);
+            }
+        );
+    },
+    search: function (query, num, sort, filter, showfrom, addParamsArr)
+    {
+        clearTimeout(this.statTimer);
+        clearTimeout(this.showTimer);
+        clearTimeout(this.termTimer);
+        clearTimeout(this.bytargetTimer);
+        
+        this.showCounter = 0;
+        this.termCounter = 0;
+        this.bytargetCounter = 0;
+        this.statCounter = 0;
+        this.activeClients = 1;
+        
+        // no proxy mode
+        if( !this.initStatusOK )
+            throw new Error('Pz2.js: session not initialized.');
+        
+        if( query !== undefined )
+            this.currQuery = query;
+        else
+            throw new Error("Pz2.js: no query supplied to the search command.");
+        
+        if ( showfrom !== undefined )
+            var start = showfrom;
+        else
+            var start = 0;
+
+       var searchParams = { 
+          "command": "search",
+          "query": this.currQuery, 
+          "session": this.sessionID,
+          "windowid" : window.name
+        };
+       
+        if( sort !== undefined ) {
+            this.currentSort = sort;
+           searchParams["sort"] = sort;
+       }
+        if (filter !== undefined)
+               searchParams["filter"] = filter;
+
+        // copy additional parmeters, do not overwrite
+        if (addParamsArr != undefined) {
+            for (var prop in addParamsArr) {
+                if (!searchParams.hasOwnProperty(prop))
+                    searchParams[prop] = addParamsArr[prop];
+            }
+        }
+        
+        var context = this;
+        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+        request.safeGet(
+            searchParams,
+            function(data) {
+                if ( data.getElementsByTagName("status")[0]
+                        .childNodes[0].nodeValue == "OK" ) {
+                    context.searchStatusOK = true;
+                    //piggyback search
+                    context.show(start, num, sort);
+                    if (context.statCallback)
+                        context.stat();
+                    if (context.termlistCallback)
+                        context.termlist();
+                    if (context.bytargetCallback)
+                        context.bytarget();
+                }
+                else
+                    context.throwError('Search failed. Malformed WS resonse.',
+                                        112);
+            }
+        );
+    },
+    stat: function()
+    {
+        if( !this.initStatusOK )
+            throw new Error('Pz2.js: session not initialized.');
+        
+        // if called explicitly takes precedence
+        clearTimeout(this.statTimer);
+        
+        var context = this;
+        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+        request.safeGet(
+            { "command": "stat", "session": this.sessionID, "windowid" : window.name },
+            function(data) {
+                if ( data.getElementsByTagName("stat") ) {
+                    var activeClients = 
+                        Number( data.getElementsByTagName("activeclients")[0]
+                                    .childNodes[0].nodeValue );
+                    context.activeClients = activeClients;
+
+                   var stat = Element_parseChildNodes(data.documentElement);
+
+                    context.statCounter++;
+                   var delay = context.statTime 
+                        + context.statCounter * context.dumpFactor;
+                    
+                    if ( activeClients > 0 )
+                        context.statTimer = 
+                            setTimeout( 
+                                function () {
+                                    context.stat();
+                                },
+                                delay
+                            );
+                    context.statCallback(stat);
+                }
+                else
+                    context.throwError('Stat failed. Malformed WS resonse.',
+                                        113);
+            }
+        );
+    },
+    show: function(start, num, sort, query_state)
+    {
+        if( !this.searchStatusOK && this.useSessions )
+            throw new Error(
+                'Pz2.js: show command has to be preceded with a search command.'
+            );
+        
+        // if called explicitly takes precedence
+        clearTimeout(this.showTimer);
+        
+        if( sort !== undefined )
+            this.currentSort = sort;
+        if( start !== undefined )
+            this.currentStart = Number( start );
+        if( num !== undefined )
+            this.currentNum = Number( num );
+
+        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" : window.name
+          };
+        if (query_state)
+          requestParameters["query-state"] = query_state;
+       if (this.version && this.version > 0)
+           requestParameters["version"] = this.version;
+        request.safeGet(
+         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);
+          }
+        );
+    },
+    record: function(id, offset, syntax, handler)
+    {
+        // we may call record with no previous search if in proxy mode
+        if(!this.searchStatusOK && this.useSessions)
+           throw new Error(
+            'Pz2.js: record command has to be preceded with a search command.'
+            );
+        
+        if( id !== undefined )
+            this.currRecID = id;
+        
+       var recordParams = { 
+            "command": "record", 
+            "session": this.sessionID,
+            "id": this.currRecID,
+            "windowid" : window.name
+        };
+       
+       this.currRecOffset = null;
+        if (offset != undefined) {
+           recordParams["offset"] = offset;
+            this.currRecOffset = offset;
+        }
+
+        if (syntax != undefined)
+            recordParams['syntax'] = syntax;
+
+        //overwrite default callback id needed
+        var callback = this.recordCallback;
+        var args = undefined;
+        if (handler != undefined) {
+            callback = handler['callback'];
+            args = handler['args'];
+        }
+        
+        var context = this;
+        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+
+        request.safeGet(
+           recordParams,
+            function(data) {
+                var recordNode;
+                var record;                                
+                //raw record
+                if (context.currRecOffset !== null) {
+                    record = new Array();
+                    record['xmlDoc'] = data;
+                    record['offset'] = context.currRecOffset;
+                    callback(record, args);
+                //pz2 record
+                } else if ( recordNode = 
+                    data.getElementsByTagName("record")[0] ) {
+                    // if stylesheet was fetched do not parse the response
+                    if ( context.xslDoc ) {
+                        record = new Array();
+                        record['xmlDoc'] = data;
+                        record['xslDoc'] = context.xslDoc;
+                        record['recid'] = 
+                            recordNode.getElementsByTagName("recid")[0]
+                                .firstChild.nodeValue;
+                    //parse record
+                    } else {
+                        record = Element_parseChildNodes(recordNode);
+                    }    
+                   var activeClients = 
+                      Number( data.getElementsByTagName("activeclients")[0]
+                               .childNodes[0].nodeValue );
+                   context.activeClients = activeClients; 
+                    context.recordCounter++;
+                    var delay = context.recordTime + context.recordCounter * context.dumpFactor;
+                    if ( activeClients > 0 )
+                        context.recordTimer = 
+                           setTimeout ( 
+                               function() {
+                                  context.record(id, offset, syntax, handler);
+                                  },
+                                  delay
+                               );                                    
+                    callback(record, args);
+                }
+                else
+                    context.throwError('Record failed. Malformed WS resonse.',
+                                        115);
+            }
+        );
+    },
+
+    termlist: function()
+    {
+        if( !this.searchStatusOK && this.useSessions )
+            throw new Error(
+            'Pz2.js: termlist command has to be preceded with a search command.'
+            );
+
+        // if called explicitly takes precedence
+        clearTimeout(this.termTimer);
+        
+        var context = this;
+        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+        request.safeGet(
+            { 
+                "command": "termlist", 
+                "session": this.sessionID, 
+                "name": this.termKeys,
+                "windowid" : window.name, 
+               "version" : this.version
+       
+            },
+            function(data) {
+                if ( data.getElementsByTagName("termlist") ) {
+                    var activeClients = 
+                        Number( data.getElementsByTagName("activeclients")[0]
+                                    .childNodes[0].nodeValue );
+                    context.activeClients = activeClients;
+                    var termList = { "activeclients":  activeClients };
+                    var termLists = data.getElementsByTagName("list");
+                    //for each termlist
+                    for (i = 0; i < termLists.length; i++) {
+                       var listName = termLists[i].getAttribute('name');
+                        termList[listName] = new Array();
+                        var terms = termLists[i].getElementsByTagName('term');
+                        //for each term in the list
+                        for (j = 0; j < terms.length; j++) { 
+                            var term = {
+                                "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'
+                            };
+
+                           // 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;
+                              
+                        }
+                    }
+
+                    context.termCounter++;
+                    var delay = context.termTime 
+                        + context.termCounter * context.dumpFactor;
+                    if ( activeClients > 0 )
+                        context.termTimer = 
+                            setTimeout(
+                                function () {
+                                    context.termlist();
+                                }, 
+                                delay
+                            );
+                   
+                   context.termlistCallback(termList);
+                }
+                else
+                    context.throwError('Termlist failed. Malformed WS resonse.',
+                                        116);
+            }
+        );
+
+    },
+    bytarget: function()
+    {
+        if( !this.initStatusOK && this.useSessions )
+            throw new Error(
+            'Pz2.js: bytarget command has to be preceded with a search command.'
+            );
+        
+        // no need to continue
+        if( !this.searchStatusOK )
+            return;
+
+        // if called explicitly takes precedence
+        clearTimeout(this.bytargetTimer);
+        
+        var context = this;
+        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
+        request.safeGet(
+            { 
+               "command": "bytarget", 
+               "session": this.sessionID, 
+               "block": 1,
+               "windowid" : window.name,
+               "version" : this.version
+           },
+            function(data) {
+                if ( data.getElementsByTagName("status")[0]
+                        .childNodes[0].nodeValue == "OK" ) {
+                    var targetNodes = data.getElementsByTagName("target");
+                    var bytarget = new Array();
+                    for ( i = 0; i < targetNodes.length; i++) {
+                        bytarget[i] = new Array();
+                        for( j = 0; j < targetNodes[i].childNodes.length; j++ ) {
+                            if ( targetNodes[i].childNodes[j].nodeType 
+                                == Node.ELEMENT_NODE ) {
+                                var nodeName = 
+                                    targetNodes[i].childNodes[j].nodeName;
+                               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++;
+                    var delay = context.bytargetTime 
+                        + context.bytargetCounter * context.dumpFactor;
+                    if ( context.activeClients > 0 )
+                        context.bytargetTimer = 
+                            setTimeout(
+                                function () {
+                                    context.bytarget();
+                                }, 
+                                delay
+                            );
+
+                    context.bytargetCallback(bytarget);
+                }
+                else
+                    context.throwError('Bytarget failed. Malformed WS resonse.',
+                                        117);
+            }
+        );
+    },
+    
+    // just for testing, probably shouldn't be here
+    showNext: function(page)
+    {
+        var step = page || 1;
+        this.show( ( step * this.currentNum ) + this.currentStart );     
+    },
+
+    showPrev: function(page)
+    {
+        if (this.currentStart == 0 )
+            return false;
+        var step = page || 1;
+        var newStart = this.currentStart - (step * this.currentNum );
+        this.show( newStart > 0 ? newStart : 0 );
+    },
+
+    showPage: function(pageNum)
+    {
+        //var page = pageNum || 1;
+        this.show(pageNum * this.currentNum);
+    }
+};
+
+/*
+********************************************************************************
+** AJAX HELPER CLASS ***********************************************************
+********************************************************************************
+*/
+var pzHttpRequest = function ( url, errorHandler ) {
+        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' );
+            }
+        }
+};
+
+
+pzHttpRequest.prototype = 
+{
+    safeGet: function ( params, callback )
+    {
+        var encodedParams =  this.encodeParams(params);
+        var url = this._urlAppendParams(encodedParams);
+        if (url.length >= this.maxUrlLength) {
+            this.requestHeaders["Content-Type"]
+                = "application/x-www-form-urlencoded";
+            this._send( 'POST', this.url, encodedParams, callback );
+        } else {
+            this._send( 'GET', url, '', callback );
+        }
+    },
+
+    get: function ( params, callback ) 
+    {
+        this._send( 'GET', this._urlAppendParams(this.encodeParams(params)), 
+            '', callback );
+    },
+
+    post: function ( params, data, callback )
+    {
+        this._send( 'POST', this._urlAppendParams(this.encodeParams(params)), 
+            data, callback );
+    },
+
+    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;
+    },
+
+    encodeParams: function (params)
+    {
+        var sep = "";
+        var encoded = "";
+        for (var key in params) {
+            if (params[key] != null) {
+                encoded += sep + key + '=' + encodeURIComponent(params[key]);
+                sep = '&';
+            }
+        }
+        return encoded;
+    },
+
+    _send: function ( type, url, data, callback)
+    {
+        var context = this;
+        this.callback = callback;
+        this.async = true;
+        this.request.open( type, url, this.async );
+        for (var key in this.requestHeaders)
+            this.request.setRequestHeader(key, this.requestHeaders[key]);
+        this.request.onreadystatechange = function () {
+            context._handleResponse(url); /// url used ONLY for error reporting
+        }
+        this.request.send(data);
+    },
+
+    _urlAppendParams: function (encodedParams)
+    {
+        if (encodedParams)
+            return this.url + "?" + encodedParams;
+        else
+            return this.url;
+    },
+
+    _handleResponse: function (savedUrlForErrorReporting)
+    {
+        if ( this.request.readyState == 4 ) { 
+            // pick up appplication errors first
+            var errNode = null;
+            if (this.request.responseXML &&
+                (errNode = this.request.responseXML.documentElement)
+                && errNode.nodeName == 'error') {
+                var errMsg = errNode.getAttribute("msg");
+                var errCode = errNode.getAttribute("code");
+                var errAddInfo = '';
+                if (errNode.childNodes.length)
+                    errAddInfo = ': ' + errNode.childNodes[0].nodeValue;
+                           
+                var err = new Error(errMsg + errAddInfo);
+                err.code = errCode;
+           
+                if (this.errorHandler) {
+                    this.errorHandler(err);
+                }
+                else {
+                    throw err;
+                }
+            } 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) {
+                       // Safari: eval will fail as well. Considering trying JSON2 (non-native implementation) instead
+                       /* DEBUG only works in mk2-mobile
+                       if (document.getElementById("log")) 
+                           document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
+                       */
+                       try {
+                           json = eval("(" + text + ")");
+                       }
+                       catch (e) {
+                           /* DEBUG only works in mk2-mobile
+                           if (document.getElementById("log")) 
+                               document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
+                           */
+                       }
+                   }
+               } 
+               this.callback(json, "json");
+              } else {
+                var err = new Error("XML response is empty but no error " +
+                                    "for " + savedUrlForErrorReporting);
+                err.code = -1;
+                if (this.errorHandler) {
+                    this.errorHandler(err);
+                } else {
+                    throw err;
+                }
+              }
+            } else if (this.request.status == 200) {
+                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;        
+                if (this.errorHandler) {
+                    this.errorHandler(err);
+                }
+                else {
+                    throw err;
+                }
+            }
+        }
+    }
+};
+
+/*
+********************************************************************************
+** XML HELPER FUNCTIONS ********************************************************
+********************************************************************************
+*/
+
+// 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;
+}
+
+DOMDoc.transformToDoc = function (xmlDoc, xslDoc)
+{
+    if ( window.XSLTProcessor ) {
+        var proc = new XSLTProcessor();
+        proc.importStylesheet( xslDoc );
+        return proc.transformToDocument(xmlDoc);
+    } else if ( window.ActiveXObject ) {
+        return document.parseXmlFromString(xmlDoc.transformNode(xslDoc));
+    } else {
+        alert( 'Unable to perform XSLT transformation in this browser' );
+    }
+}
+// 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 ( typeof DOM_Element.textContent != 'undefined' ) {
+        return DOM_Element.textContent;
+    } else if (typeof DOM_Element.text != 'undefined') {
+        return DOM_Element.text;
+    } else {
+        throw new Error("Cannot get text content of the node, no such method.");
+    }
+}
+
+Element_parseChildNodes = function (node)
+{
+    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];
+            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;
+        parsed[attrName] = attrValue;
+    }
+
+    // if no nested elements/attrs set value to text
+    if (hasChildElems)
+      parsed['#text'] = textContent;
+    else
+      parsed = textContent;
+    
+    return parsed;
+}
+
+/* do not remove trailing bracket */
+}
diff --git a/experiments/spclient/robots.txt b/experiments/spclient/robots.txt
new file mode 100644 (file)
index 0000000..1f53798
--- /dev/null
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/experiments/spclient/spclient.html b/experiments/spclient/spclient.html
deleted file mode 100644 (file)
index 08bb940..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html 
-         PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-  <head>
-    <title>MasterKey Widget Set: SP Client</title>
-    <style type="text/css">
-      .info {
-        background: #ffe0ff;
-        padding: 1em;
-      }
-      .greeting {
-        background: #ffffc0;
-        padding: 1em;
-      }
-    </style>
-  </head>
-  <body>
-    <p class="info">
-      JQuery UI demo.
-    </p>
-    <p class="greeting ui-widget-content">
-      Hello, <b>world!</b>
-    </p>
-    <p class="info">
-      That seemed to go OK.
-    </p>
-  </body>
-</html>
diff --git a/experiments/spclient/styles.css b/experiments/spclient/styles.css
new file mode 100644 (file)
index 0000000..e6cd2b0
--- /dev/null
@@ -0,0 +1,110 @@
+html {
+    overflow-y: scroll;
+    font-size: 12px;
+}
+
+body {
+    font-family: tahoma, arial, sans-serif;
+    color: #156a16;
+}
+
+hr {
+    border: 0;
+    color: #156a16;
+    background-color: #156a16;
+    height: 1px;
+}
+
+thead {
+    font-weight: bold;
+}
+
+a {
+    color: #005701; 
+    text-decoration: none;
+}
+
+a.extern {
+    color: #CC6600;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+a.crossout:hover {
+    text-decoration: line-through;
+    font-weight: bold;
+}
+
+
+#heading {
+    background-color: #87c9af;
+    border-top: 1px solid  #156a16;
+    color: #ffffff;
+    font-size: large;
+}
+
+input#button {
+    border: 3px outset #132194;
+    background-color: #132194;
+    padding: 2px;
+    width: 6em;
+    color: #FFFFFF;
+    font-weight: bold;
+    text-transform: uppercase;
+    font-size: 10px;
+    margin-left: 8px;
+    cursor: pointer;
+}
+
+input#query {
+    border: 2px inset #34cc67;
+    padding: 3px;
+    font-size: 12px;
+}
+
+div.termtitle {
+    margin: 4px;
+    font-weight: bold;
+}
+
+div.record {
+    padding: 5px;
+}
+
+div.details {
+    border: 3px dashed gray;
+    color: gray;
+    padding: 5px;
+    margin: 4px;
+}
+
+#switchmenu {
+    padding-bottom: 3px;
+    text-align: right;
+}
+
+#recordview {
+    background-color: #fafafa;
+    border-bottom: 1px solid  #156a16;
+}
+
+#targetview {
+    background-color: #fafafa;
+    border-bottom: 1px solid  #156a16;
+}
+
+#bytarget {
+    padding: 7px;
+}
+
+#footer {
+    padding-top: 4px;
+    color: #74c775;
+    text-align: center;
+}
+
+#stat {
+    font-weight: bold;
+}