PageSourceSearch

https://huguenotmuseum.org/wp-content/plugins/modesbrowser/js/mode…?ver=84c30f929073b5bb43f95957ed962371

js huguenotmuseum.org collected 2026-10-02 11:42:56 UTC 195,346 bytes, 4,153 lines download raw bytes

1/*
2  modes-lib.js: javascript library for Modes browsing and search
3  support. Initially implemented as part of the Modes Wordpress
4  plugin.  Richard Light, 1 February 2013.
5*/
6
7/* XMLHTTP and XSLT */
8
9function loadXmlString(text) {
10  if (window.DOMParser)
11  {
12    parser=new DOMParser();
13    xmlDoc=parser.parseFromString(text,"text/xml");
14  }
15  else // Internet Explorer
16  {
17    xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
18    xmlDoc.async=false;
19    xmlDoc.loadXML(text);
20  }
21  return xmlDoc;
22}
23
24function xmlToString(xmlData) {
25    var xmlString = undefined;
26    if (window.ActiveXObject){
27        xmlString = xmlData[0].xml;
28    }
29    if (xmlString === undefined)
30    {
31        var oSerializer = new XMLSerializer();
32        xmlString = oSerializer.serializeToString(xmlData);
33    }
34//    alert(xmlString);
35    return xmlString;
36}
37
38function XMLHttpFactories() {
39  return [
40    function () {return new XMLHttpRequest()},
41    function () {return new ActiveXObject("Msxml2.XMLHTTP")},
42    function () {return new ActiveXObject("Msxml3.XMLHTTP")},
43    function () {return new ActiveXObject("Microsoft.XMLHTTP")}
44  ];
45}
46
47function createXMLHTTPObject() {
48  var xmlhttp = false;
49  var factories = XMLHttpFactories();
50  for (var i=0;i<factories.length;i++) {
51    try {
52      xmlhttp = factories[i]();
53    }
54    catch (e) {
55      continue;
56    }
57    break;
58  }
59  return xmlhttp;
60}
61
62function sendRequest(url,callback,callingobject,postData) {
63  var req = createXMLHTTPObject();
64  if (!req) return;
65  var method = (postData) ? "POST" : "GET";
66  req.onreadystatechange = function () {
67//    alert('URL: '+url+'; Ready state: '+req.readystate+'; Response: '+req.status);
68    if (req.readyState !== 4 || req.status == 0)
69    {
70//      alert('Ready state not 4 or status = 0: '+url+'; Ready state: '+req.readyState+'; Response: '+req.status);
71      return;
72    }
73//    if (req.status == 0) {
74//      alert('Ready status equals 0: '+url+'; Ready state: '+req.readystate+'; Response: '+req.status);
75//      req.status = 200; // guess ...
76//      return;
77//    }
78//    alert('URL: '+url+'; Ready state: '+req.readyState+'; Response: '+req.status);
79    if (req.status !== 200 && req.status !== 304) {
80      alert('HTTP error ' + req.status + '. ' + req.statusText);
81      return;
82    }
83//    alert('About to do callback: '+url+'; Ready state: '+req.readystate+'; Response: '+req.status);
84    callback(req, callingobject);
85  }
86//  alert('URL is '+url);
87  req.open(method,url,true);
88  req.setRequestHeader('User-Agent','XMLHTTP/1.0');
89  req.setRequestHeader('Accept', 'text/xml');
90//  req.setRequestHeader('Access-Control-Allow-Origin', 'http://www.culturegrid.org.uk/');
91//  req.setRequestHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
92//  req.setRequestHeader('Access-Control-Allow-Headers', '*');
93//  req.setRequestHeader('Access-Control-Max-Age', 1728000);
94  if (postData)
95    req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
96  if (req.readyState == 4) return;
97  req.send(postData);
98  return req;
99}
100
101function loadXMLDocFn(dname)
102{
103  var xhttp;
104  xhttp = createXMLHTTPObject();
105  if (!xhttp) return;
106//  xhttp.async = false;
107  xhttp.open("GET",dname,false);
108  try {
109    xhttp.send("");
110  }
111  catch(e) { alert(e+': failed to load '+dname); }
112  return xhttp.responseXML;
113}
114
115// parameter setting left in below to demonstrate how to do it. 'start' attribute already exists in XML source.
116function applyXsltToResponse(node, xml, XsltStylesheet)
117{
118//  alert('about to load XSL: '+XsltStylesheet);
119  xsl = loadXMLDocFn(XsltStylesheet);
120//  alert('loaded XSL: '+XsltStylesheet+xsl);
121// code for IE
122  if (window.ActiveXObject)
123  {
124//    xml.documentElement.setAttribute('startNumber', this.startRecord);
125    var ex=xml.transformNode(xsl);
126    node.innerHTML=ex;
127  }
128// code for Mozilla, Firefox, Opera, etc.
129  else if (document.implementation && document.implementation.createDocument)
130  {
131    var xsltProcessor=new XSLTProcessor();
132    xsltProcessor.async = false;
133    xsltProcessor.reset();
134//    alert('before stylesheet import');
135    xsltProcessor.importStylesheet(xsl);
136//    alert('after stylesheet import');
137//    xsltProcessor.setParameter(null, "startNumber", this.startRecord);
138//    alert('before XSLT transform: '+xsltProcessor);
139    var resultDocument = xsltProcessor.transformToFragment(xml,document);
140//    alert('after XSLT transform: '+resultDocument);
141    if (resultDocument)
142    {
143      if (node)
144      {
145        if (node.document) { node.document.appendChild(resultDocument); }
146        else if (node.contentWindow) {
147          if (node.contentWindow.document) { node.contentWindow.document.appendChild(resultDocument); }
148          else node.contentWindow.appendChild(resultDocument); }
149        else if (node.contentDocument) { node.contentDocument.appendChild(resultDocument); }
150        else {
151          while ( node.firstChild ) { node.removeChild( node.firstChild ); }
152          node.appendChild(resultDocument);
153        }
154      }
155    } else node.innerHTML = '<p>No result found</p>';
156  }
157};
158
159function attributesToString(atts)
160{
161  var s = '';
162  if (atts)
163  {
164    for (var i=0; i < atts.length; i++)
165    {
166      s = s+' '+atts.item(i).nodeName+'="'+atts.item(i).value+'"';
167    }
168  }
169  return s;
170}
171
172function childNodesToString(nodelist)
173{
174  var s = '';
175  if ((nodelist) && (nodelist.length > 0))
176  {
177    for (var i=0; i < nodelist.length; i++)
178    {
179      s = s+nodeToString(nodelist.item(i));
180    }
181  }
182  return s;
183}
184
185function nodeToString(node)
186{
187  if (node)
188  {
189    var type = node.nodeType;
190    if (type == 1)
191      { return '<'+node.nodeName+attributesToString(node.attributes)+'>'+childNodesToString(node.childNodes)+'</'+node.nodeName+'>'; }
192    else if (type == 3)
193      { return node.nodeValue; }
194    else if ((node.childNodes) && (node.childNodes.length > 0))
195      { return childNodesToString(node.childNodes); }
196    else return '';
197  }
198}
199
200 // var tl;
201 var modes_browser;
202
203function urlBaseFn(domain, datasource, progName)
204{
205  var s = '';
206    var prog = 'modescgi.exe';
207    if ((progName) && (progName !== ''))
208      prog = progName;
209  var sDir;
210//  if ((this.scriptDomain) && (this.scriptDomain !== '')) {
211//    if (this.linkedDataPublished) {
212    if (datasource.linkedData) {
213    if ((this.internalScriptDir) && (this.internalScriptDir !== ''))
214      sDir = this.internalScriptDir
215    else
216      sDir = this.scriptDir;
217    //s = 'http://' + domain + this.scriptDir + 'cgiForwarder.exe/?url=http://' + this.scriptDomain + sDir;
218        s = 'http://' + domain + this.scriptDir + this.forwarder + '/?url=http://' + this.scriptDomain + sDir;
219    s = s + prog + '/?';
220    return s;
221  } else {
222    s = 'http://' + domain + this.scriptDir + prog + '/?';
223    return s;
224  }
225}
226
227function getSeparatorFn(datasource) {
228  var sep = '&';
229//  if ((this.internalScriptDir) && (this.internalScriptDir !== ''))
230    if (datasource.linkedData) {
231        sep = '%26';
232    }
233    return sep;
234}
235
236function connectionFn(datasource) {
237  var sep = this.getSeparator(datasource);
238  return 'transport='+this.scriptTransport+sep+'serverName='+this.scriptServer+sep;
239}
240
241function transformXmlFn(xml, XsltStylesheet)
242{
243  var data;
244//  var xslUrl = 'http://'+this.domainName+'/wordpress/wp-content/plugins/'+this.browserFolder+'/js/'+XsltStylesheet;
245    var xslUrl = this.pluginsurl+'/'+this.browserFolder+'/xslt/'+XsltStylesheet;
246//  var xslUrl = '../'+XsltStylesheet;
247  if (this.debug) {
248    alert('loading stylesheet '+xslUrl);
249  }
250  xsl = this.loadXMLDoc(xslUrl);
251  if (xsl) {
252// code for IE
253    if (window.ActiveXObject)
254    {
255      data=xml.transformNode(xsl);
256      var dataXml = new ActiveXObject("Msxml2.DOMDocument.3.0");
257      dataXml.async = false;
258      dataXml.loadXML(data);
259      return dataXml;
260    }
261// code for Mozilla, Firefox, Opera, etc.
262    else if (document.implementation && document.implementation.createDocument)
263    {
264      var xsltProc2=new XSLTProcessor();
265      xsltProc2.async = false;
266      xsltProc2.reset();
267      xsltProc2.importStylesheet(xsl);
268      data = xsltProc2.transformToDocument(xml);
269      return data;
270    } else alert('failed to find XSLT support');
271  } else alert('failed to load XSLT: '+xslUrl);
272}
273
274function transformLocalFn(xml, XsltStylesheet)
275{
276  var data;
277  var xslUrl = XsltStylesheet;
278  xsl = this.loadXMLDoc(xslUrl);
279  if (xsl) {
280// code for IE
281    if (window.ActiveXObject)
282    {
283      data=xml.transformNode(xsl);
284      var dataXml = new ActiveXObject("Msxml2.DOMDocument.3.0");
285      dataXml.async = false;
286      dataXml.loadXML(data);
287      return dataXml;
288    }
289// code for Mozilla, Firefox, Opera, etc.
290    else if (document.implementation && document.implementation.createDocument)
291    {
292      var xsltProc2=new XSLTProcessor();
293      xsltProc2.async = false;
294      xsltProc2.reset();
295      xsltProc2.importStylesheet(xsl);
296      data = xsltProc2.transformToDocument(xml);
297      return data;
298    } else alert('failed to find XSLT support');
299  } else alert('failed to load XSLT '+XsltStylesheet);
300}
301
302function urlencode(str) {
303return escape(str).replace(/\+/g,'%2B').replace(/%20/g, '+').replace(/\*/g, '%2A').replace(/\//g, '%2F').replace(/@/g, '%40');
304}
305
306function loadTimelineFn() {
307  if (this.debug) {
308    alert('loading timeline');
309  }
310    if (!this.eventSource)
311      this.eventSource = new Timeline.DefaultEventSource();
312    var bandInfos = [
313      Timeline.createBandInfo({
314         eventSource:    this.eventSource,
315         date:           "Jan 01 1800 00:00:00 GMT",
316         height:         "70%",
317         width:          "70%",
318         intervalUnit:   Timeline.DateTime.DECADE,
319         intervalPixels: 100
320      }),
321      Timeline.createBandInfo({
322         overview:       true,
323         eventSource:    this.eventSource,
324         date:           "Jan 01 1800 00:00:00 GMT",
325         height:         "30%",
326         width:          "30%",
327         intervalUnit:   Timeline.DateTime.CENTURY,
328         intervalPixels: 200
329      })
330    ];
331
332    bandInfos[1].syncWith = 0;
333    bandInfos[1].highlight = true;
334
335    this.modesTimeline = Timeline.create(document.getElementById(this.modesTimelineControl), bandInfos);
336// }
337
338    var resizeTimerID = null;
339    function onResize() {
340       if (resizeTimerID == null) {
341           resizeTimerID = window.setTimeout(function() {
342               resizeTimerID = null;
343               tl.layout();
344           }, 500);
345       }
346   }
347  }
348
349  function updateTimelineFn(xml, url) {
350    this.eventSource.loadXML(xml, 'http://'+this.domainName+'/');
351  }
352
353/* -------------------------------------------------------- */
354
355/*
356    ModesBrowser: a class which encapsulates a Modes browser plugin
357    instance.
358*/
359
360// utility functions used by AJAX controls:
361/*
362function showLinks() {
363  if (jQuery('#".$formname."_home')) { jQuery('#".$formname."_home').css('display', 'inline'); }
364}
365
366function hideLinks() {
367  if (jQuery('#".$formname."_home')) { jQuery('#".$formname."_home').css('display', 'none'); }
368}
369*/
370
371function toggle_visibility(id) {
372   var e = jQuery('#'+id);
373   if(e.css('display') == 'block')
374      e.css('display', 'none');
375   else
376      jQuery('#'+id).css('display', 'block');
377}
378
379function addIfNotInSearch(aSearch, aNewTerm) {
380//  alert('search is '+aSearch+'; new term is '+aNewTerm);
381  if ((!aNewTerm) || (aNewTerm.indexOf('{}') !== -1)) {
382//    alert(aNewTerm);
383    return aSearch;
384  } else {
385    searchEmpty = (aSearch == '');
386    if (searchEmpty) {
387      return aNewTerm;
388    } else {
389      if (aSearch.indexOf(aNewTerm) == -1) {
390//        alert('adding '+aNewTerm);
391        return aSearch + ' AND ' + aNewTerm;
392      } else
393        return aSearch;
394
395    }
396  }
397}
398
399// ModesBrowser support functions:
400
401function findDatasourceFn(application, filename) {
402    for ( var i=0; i<this.datasources.length; i++) {
403//    alert('i = '+i+'; '+this.datasources[i]['application']+'; '+this.datasources[i]['filename']);
404        if ((this.datasource[this.datasources[i]]['application'] == application) && (this.datasource[this.datasources[i]]['filename'] == filename)) {
405            return this.datasource[this.datasources[i]];
406            break;
407        }
408    }
409}
410
411function findFilterFn(filtername) {
412    for ( var i=0; i<this.filters.length; i++) {
413        if (this.filters[i].name == filtername) {
414            return this.filters[i];
415            break;
416        }
417    }
418}
419
420function clearSearchFn() {
421  this.modesQuery = '';
422  if (this.debug) {
423    alert('search cleared');
424  }
425}
426
427function loadMapFn(map_id, lon, lat, zoom) {
428  if (this.modesMap)
429    this.modesMap.mapInit(map_id, lon, lat, zoom);
430//  modes_map_init(this.modesMapControl, -0.75, 51.65, 12);
431}
432
433/*
434  Now parse the HTML, creating a place mark for each relevant <div>.
435  Also, create it as a named layer which is associated with the current search.
436  (Actually, it maybe makes sense to clear the map each time, since the result
437  set will be the combination of all the currently selected factors.)
438*/
439function addLayerFromHtmlFn(xml) { // , colour, i) {
440    if (this.modesMap) {
441    if (this.debug)
442            alert(xml);
443    this.modesMap.mapLoad(xml); // , colour, i);
444  }
445}
446
447function displayControlFn(name) {
448  var aType;
449  var aDatasource;
450  var application;
451  var filename;
452  var domain;
453  var aScriptDir;
454//  this.connection = 'transport='+this.scriptTransport+'&serverName='+this.scriptServer+'&';
455  var activeQuery;
456  var start = 1;
457    var recordsperpage = 0; // rbl 10.11.2015 bug 3365
458  var linkedDataResource = (this.linkedDataPublished == 'yes');
459  var targetPage;
460  var aNavigator;
461  var aTransform;
462  var aSimpleSearchIndex;
463  var anOnThisDayIndex;
464  var aControl = this.findControl(name);
465//  var forwardRequest = ((this.scriptDomain) && (this.scriptDomain !== ''));
466    var forwardRequest = (aControl['datasource'].linkedData);
467  var sep = '&';
468  if (forwardRequest) { sep = '%26'; }
469  if (aControl) {
470    aType = aControl['type'];
471    if (!aType)
472      aType = 'browser';
473    datasourceName = aControl['datasource'];
474    if (datasourceName=='')
475      datasourceName = this.defaultDatasource;
476    aDatasource = this.datasource[datasourceName];
477    if (aDatasource) {
478      application = aDatasource.application;
479      filename = aDatasource.filename;
480      domain = aDatasource.domain;
481      linkedDataResource = (linkedDataResource) || (domain !== '');
482      if (aDatasource.currentStart)
483        start = aDatasource.currentStart;
484//      alert(linkedDataResource);
485    } else
486      alert('Error in Modes control element: no datasource found for '+datasourceName);
487    aNavigator = aControl.navigator;
488    aTransform = aControl['transform'];
489    aSimpleSearchIndex = aControl['simplesearchindex'];
490    if ((!aSimpleSearchIndex) || (aSimpleSearchIndex == ''))
491      aSimpleSearchIndex = this.simpleSearchIndex;
492    if (this.debug) {
493      alert('simple search index is '+aSimpleSearchIndex);
494    }
495    anOnThisDayIndex = aControl['onthisdayindex'];
496    activeQuery = this.modesQuery;
497//        alert('about to check filters; query is: '+activeQuery+'; filter length is '+this.filters.length);
498        if (this.filters.length > 0) {
499            this.updateQueryWithFilters();
500            activeQuery = this.filteredQuery;
501//            alert('updated with filters; query now: '+activeQuery);
502        }
503    if ((!domain) || (domain == ''))
504      domain = this.domainName;
505    aScriptDir = aDatasource.scriptDir;
506    if ((!aScriptDir) || (aScriptDir == ''))
507      aScriptDir = this.scriptDir;
508    if (this.debug) {
509      alert('data source is '+aDatasource+'; app is '+application+'; file is '+filename);
510    }
511    switch(aType) {
512      case 'datagrid':
513        this.displayDatagrid(name);
514      break;
515      case 'hitlist':
516        if ((!activeQuery) || (activeQuery == ''))
517          activeQuery = aControl['modes_query'];
518                sortby = aControl['sortby'];
519//                sortby = '';
520                title = aControl['title'];
521//        navigator = aControl['navigator'];
522                recordsperpage = recordsperpage + aControl['records_per_page']; // bug 3365, rbl 10.11.2015
523        targetPage = aControl['targetPage'];
524                sectionOrder = aControl['section_order'];
525                headingDiv = aControl['heading_div'];
526                headingSpan = aControl['heading_span'];
527                entryDiv = aControl['entry_div'];
528                entrySpan = aControl['entry_span'];
529                entryCutoff = aControl['entry_cutoff'];
530        if (this.debug) {
531          alert('target set to '+targetPage);
532        }
533        this.displayHitlist(name, domain, aScriptDir, application, filename, activeQuery, title, aNavigator, start, recordsperpage, targetPage, linkedDataResource, aDatasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff, sortby);
534      break;
535      case 'detail':
536        if ((!activeQuery) || (activeQuery == ''))
537          activeQuery = aControl['modes_query'];
538                sortby = aControl['sortby'];
539//                sortby = '';
540                recordsperpage = recordsperpage + aControl['records_per_page']; // bug 3365, rbl 10.11.2015
541                title = aControl['title'];
542                sectionOrder = aControl['section_order'];
543                headingDiv = aControl['heading_div'];
544                headingSpan = aControl['heading_span'];
545                entryDiv = aControl['entry_div'];
546                entrySpan = aControl['entry_span'];
547        this.displayDetail(name, domain, aScriptDir, application, filename, activeQuery, title, aDatasource, aNavigator, start, recordsperpage, linkedDataResource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, sortby);
548      break;
549            case 'navigator':
550                break;
551      case 'facets':
552        this.displayFacets(name, domain, aScriptDir, application, filename, activeQuery, aControl.facets, aControl.facetDisplayXslt, linkedDataResource, aDatasource);
553      break;
554      case 'summary':
555        this.displaySearchSummary(activeQuery);
556      break;
557      case 'map':
558        picker = aControl['picker'];
559        targetPage = aControl['targetPage'];
560        this.displayMap(name, domain, aScriptDir, application, filename, picker, targetPage, linkedDataResource, aDatasource);
561      break;
562      case 'timeline':
563        picker = aControl['picker'];
564        targetPage = aControl['targetPage'];
565        this.displayTimeline(name, domain, aScriptDir, application, filename, picker, targetPage, 'http://'+this.domainName+'/', aDatasource);
566      break;
567      case 'home_link':
568      break;
569      case 'page_link':
570      break;
571      case 'simple_search':
572        this.getSimpleSearchTerms(domain, application, filename, activeQuery, aSimpleSearchIndex, linkedDataResource, aDatasource);
573      break;
574      case 'index_search':
575        this.getIndexSearchTerms(domain, application, filename, activeQuery, linkedDataResource, aDatasource);
576      break;
577      case 'onthisday':
578        targetPage = aControl['targetPage'];
579        this.getOnThisDay(name, domain, aScriptDir, application, filename, activeQuery, anOnThisDayIndex, aTransform, start, targetPage, linkedDataResource, aDatasource);
580      break;
581            case 'browser':
582                break;
583            case 'randomrecord':
584                var aMode = aControl['mode'];
585                var aTimeout = aControl['duration'] * 1000;
586                var anInitialState = aControl['initial_state'];
587                // if modes_query not specified, use the current active search as a filter:
588                if (aControl['modes_query'] !== '')
589                    activeQuery = aControl['modes_query'];
590                if (aControl['transform'])
591                    aTransform = aControl['transform'];
592                this.displayRandomRecord(name, domain, application, filename, activeQuery, aDatasource, aTransform, linkedDataResource, aMode, aTimeout, anInitialState);
593                break;
594            case 'slideshow':
595                var aMode = aControl['mode'];
596                var aTimeout = aControl['duration'] * 1000;
597                var anInitialState = aControl['initial_state'];
598                var aSubsetFile = aControl['subset_file'];
599                // if modes_query not specified, use the current active search as a filter:
600                if (aControl['modes_query'] !== '')
601                    activeQuery = aControl['modes_query'];
602                if (aControl['transform'])
603                    aTransform = aControl['transform'];
604                this.displaySlideshow(name, domain, application, filename, activeQuery, aDatasource, aNavigator, aSubsetFile, aTransform, linkedDataResource, aMode, aTimeout, anInitialState);
605                break;
606        case 'commentary':
607          var commentaryApp = aControl['commentaryApp'];
608                var commentaryFile = aControl['commentaryFile'];
609                var commentaryIndex = aControl['commentaryIndex'];
610                var commentXslt = aControl['commentXslt'];
611                aControl.displayCommentary(name, domain, application, filename, activeQuery, aDatasource, aNavigator, start, commentaryApp, commentaryFile, commentaryIndex, commentXslt, linkedDataResource);
612          break;
613      case 'update':
614        aControl.displayUpdate(name, domain, application, filename, activeQuery, aDatasource, aNavigator, start, linkedDataResource);
615          break;
616            case 'objectcheck':
617                sectionOrder = aControl['section_order'];
618                headingDiv = aControl['heading_div'];
619                headingSpan = aControl['heading_span'];
620                entryDiv = aControl['entry_div'];
621                entrySpan = aControl['entry_span'];
622                entryCutoff = aControl['entry_cutoff'];
623                this.displayObjectCheck(name, domain, linkedDataResource, aDatasource, aScriptDir, application, filename, activeQuery, start, recordsperpage, targetPage, linkedDataResource, aDatasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff);
624                break;
625      case 'adddata':
626        var primaryIndex = aControl['primaryindex'];
627        var elementGroup = aControl['elementgroup'];
628        var adddata_type = aControl['adddata_type'];
629        sortby = aControl['sortby'];
630        title = aControl['title'];
631        recordsperpage = recordsperpage + aControl['records_per_page']; // bug 3365, rbl 10.11.2015
632        targetPage = aControl['targetPage'];
633        sectionOrder = aControl['section_order'];
634        headingDiv = aControl['heading_div'];
635        headingSpan = aControl['heading_span'];
636        entryDiv = aControl['entry_div'];
637        entrySpan = aControl['entry_span'];
638        entryCutoff = aControl['entry_cutoff'];
639        this.displaySearchSummary(activeQuery);
640        if (adddata_type == 'element group') {
641          this.displayHitlist(name, domain, aScriptDir, application, filename, activeQuery, title, aNavigator, start, recordsperpage, targetPage, linkedDataResource, aDatasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff, sortby);
642        } else if (adddata_type == 'merge file') {
643          this.updateHitlist(name, domain, aScriptDir, application, filename, activeQuery, title, aNavigator, start, recordsperpage, targetPage, linkedDataResource, aDatasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff, sortby);
644        } else
645          alert('Unknown adddata type: '+adddata_type);
646                this.displayAddDataToRecords(name, domain, linkedDataResource, aDatasource, aScriptDir, application, filename, activeQuery, targetPage, primaryIndex, elementGroup)
647        break;
648      case 'procedure':
649        break;
650      case 'procedure_file':
651//        anApp = aControl['application'];
652//        aFile = aControl['file'];
653        aQuery = ''; //aControl['query'];
654        aForm = aControl['form'];
655        aProcedure = aControl['procedure'];
656        //alert('query is '+aQuery);
657        //replace '$$$xxx$$$' in query by form value for xxx:
658        this.displayProcedureFile(name, aProcedure, aForm, domain, aScriptDir, aControl, aDatasource, aQuery);
659        break;
660      default:
661        alert('unknown type: '+aType);
662    }
663  }
664}
665
666function displayControlsFn() {
667  var aName;
668  for (var i=0; i<this.controls.length; i++) {
669    aName = this.controls[i]['name'];
670    if (aName)
671      this.displayControl(aName);
672  }
673}
674
675// !! this needs improving so it only renders controls relating to the specified data source:
676function displayModesDataForDatasourceFn(aDatasourceName) {
677  var aName;
678  for (var i=0; i<this.controls.length; i++) {
679    aName = this.controls[i]['name'];
680    if (aName)
681      this.displayControl(aName);
682  }
683}
684
685function findControlFn(name) {
686    for (var i=0; i<this.controls.length; i++) {
687        if (this.controls[i]['name'] == name) {
688            return this.controls[i];
689        }
690    }
691}
692
693function findNavigatorFn(name) {
694    for (var i=0; i<this.navigators.length; i++) {
695        if (this.navigators[i]['controlName'] == name) {
696            return this.navigators[i];
697        }
698    }
699}
700
701function modesFileToElement(domain, scriptDir, dir, file, maxsize) {
702  return '<img style="display: block; margin-left: auto; margin-right: auto" src="http://'+domain+scriptDir+'imageserver.exe/?filename='+dir+file+'&maxsize='+maxsize+'"/>';
703}
704
705function modesIdentityToImage(domain, scriptDir, dir, app, file, identity, maxsize) {
706    return '<img style="display: block; margin-left: auto; margin-right: auto" src="http://'+domain+scriptDir+'imageserver.exe/?application='+app+'&modesfile='+file+'&indexname=Identity number&indexkey='+identity+'&maxsize='+maxsize+'"/>';
707}
708
709function renderInitialRecordFn(s, imageDir, datasource) {
710  //return s;
711  if (s.indexOf('file:') == 0) {
712        return modesFileToElement(this.domainName, this.scriptDir, imageDir, s.substr(s.indexOf(':') + 1), this.imageMaxSize);
713    } else if (datasource) {
714        aDatasource = this.datasource[datasource];
715        if (aDatasource) {
716            return modesIdentityToImage(this.domainName, this.scriptDir, imageDir, aDatasource['application'], aDatasource['filename'], s, this.imageMaxSize);
717        } else
718            return '['+s+']';
719    } else {
720        return '['+s+']';
721    }
722}
723
724// now make pageurl into a proper link to the hit list page. Use the hit list page property to deduce the page no.
725function displayDatagridFn(name) {
726  var defaultGridDatasource;
727  var defaultGridTarget;
728  var imageDirectory = '';
729    var gridstyle = 'grid';
730  if (this.datagrid['datasource'] !== '')
731    defaultGridDatasource = this.datagrid['datasource']
732  else
733    defaultGridDatasource = this.datasource;
734  if (this.datagrid['targetpage'] !== '')
735    defaultGridTarget = this.pages[this.datagrid['targetpage']]
736  else
737    defaultGridTarget = this.pages[this.hitlistPage];
738  if (this.datagrid['imagedirectory'] !== '')
739    imageDirectory = this.datagrid['imagedirectory']
740  else
741    imageDirectory = this.imageDir + defaultGridDatasource.imageDir;
742    if (this.datagrid['style'] !== '')
743        gridstyle = this.datagrid['style'];
744  var pageurl = this.siteurl+'/?datasource='+defaultGridDatasource+'&page_id='+defaultGridTarget+'&modes_query=';
745//  var pageurl = 'http://'+this.domainName+'/wordpress/?datasource='+defaultGridDatasource+'&page_id='+defaultGridTarget+'&modes_query=';
746  var s = '';
747  if (gridstyle == 'grid')
748    s = '<table>';
749  var rows = this.datagrid.length;
750  var cols = this.datagrid[0].length;
751  var i;
752  var j;
753  var cellDatasource;
754  var cellTarget;
755//  alert(rows+' rows; '+cols+' columns');
756  for ( i = 0; i < rows; i++ ) {
757      if (gridstyle == 'grid')  s = s + '<tr>';
758      for ( j = 0; j < cols; j++ ) {
759          if ((this.datagrid[i][j]['datasource']) && (this.datagrid[i][j]['datasource'] !== ''))
760              cellDatasource = this.datagrid[i][j]['datasource'];
761          else
762              cellDatasource = defaultGridDatasource;
763          if ((this.datagrid[i][j]['targetpage']) && (this.datagrid[i][j]['targetpage'] !== ''))
764              cellTarget = this.pages[this.datagrid[i][j]['targetpage']];
765          else
766              cellTarget = defaultGridTarget;
767          cellurl = this.siteurl+'/?datasource='+cellDatasource+'&page_id='+cellTarget+'&modes_query=';
768          if ((this.datagrid[i][j]['query']) && (this.datagrid[i][j]['initial_record'])) {
769              if (gridstyle == 'grid') {
770                  s = s + '<td><a href="'+cellurl+this.datagrid[i][j]['query']+'"><div class="displayDatagrid" id="'+name+'_cell_'+i+'_'+j+'">'+this.renderInitialRecord(this.datagrid[i][j]['initial_record'], imageDirectory, cellDatasource)+'</
770div></a></td>';
771              } else {
772                  s = s + '<figure id="'+name+'_cell_'+i+'_'+j+'" class="wp-caption alignleft">';
773                  s = s + '<a href="'+cellurl+this.datagrid[i][j]['query']+'">'+this.renderInitialRecord(this.datagrid[i][j]['initial_record'], imageDirectory, cellDatasource)+'</a>';
774                  s = s + '<figcaption>'+this.datagrid[i][j]['heading']+'</figcaption>';
775                  s = s + '</figure>';
776              }
777          } else if (gridstyle == 'grid')
778              s = s + '<td></td>';
779      }
780      if (gridstyle == 'grid') {
781          s = s + '</tr>';
782          s = s + '<tr>';
783          for ( j = 0; j < cols; j++ ) {
784              s = s + '<th style="text-align: center;">'+this.datagrid[i][j]['heading']+'</th>';
785          }
786          s = s + '</tr>';
787      }
788  }
789  if (gridstyle == 'grid') {
790      s = s + '</table>';
791  }
792    jQuery('#'+name).html(s);
793 }
794
795// uses the type of the control for 'name' to switch to appropriate display routine
796function displayModesData(datasourceName) {
797  with (modes_browser) {
798    displayControls(datasourceName);
799  }
800}
801
802function navFirst(name, datasourceName) {
803    return '<span class="navbutton"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_double_arrow_left.png" style="height: 15px;" title="Go to start" onclick="modes_browser.navigators[\''+name+'\'].gotoStart(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span>';
804  //return '<span onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'lightgreen\';"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_double_arrow_left.png" style="height: 15px;" title="Go to start" onclick="modes_browser.navigators[\''+name+'\'].gotoStart(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span>';
805//  return '<span onmouseover="this.style.backgroundColor=\'red\';" onmouseout="this.style.backgroundColor=\'transparent\';"> <img src="http://'+modes_browser.domainName+'/wordpress/wp-content/plugins/'+this.browserFolder+'/icons/green_double_arrow_left.png" style="height: 15px;" title="Go to start" onclick="modes_browser.navigators[\''+name+'\'].gotoStart(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span>';
806}
807
808function navBack(name, datasourceName) {
809    return '<span class="navbutton" style="margin-left: 10px; margin-right: 10px;"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_single_arrow_left.png" style="height: 15px;" title="Previous entry/page" onclick="modes_browser.navigators[\''+name+'\'].goBack(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
810  //return '<span onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'lightgreen\';" style="margin-left: 10px; margin-right: 10px;"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_single_arrow_left.png" style="height: 15px;" title="Previous entry/page" onclick="modes_browser.navigators[\''+name+'\'].goBack(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
811//  return '<span onmouseover="this.style.backgroundColor=\'red\';" onmouseout="this.style.backgroundColor=\'transparent\';" style="margin-left: 10px; margin-right: 10px;"> <img src="http://'+modes_browser.domainName+'/wordpress/wp-content/plugins/'+this.browserFolder+'/icons/green_single_arrow_left.png" style="height: 15px;" title="Previous entry/page" onclick="modes_browser.navigators[\''+name+'\'].goBack(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
812}
813
814//function navForward(name, newStart, recsperpage, total) {
815function navForward(name, datasourceName) {
816    return '<span class="navbutton" style="margin-left: 10px; margin-right: 10px;"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_single_arrow_right.png" style="height: 15px;" title="Next entry/page" onclick="modes_browser.navigators[\''+name+'\'].goForward();
816 modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
817  //return '<span onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'lightgreen\';" style="margin-left: 10px; margin-right: 10px;"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_single_arrow_right.png" style="height: 15px;" title="Next entry/page" onclick="modes_browser.navigators[\''+name+'\'].goForward(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
818//  return '<span onmouseover="this.style.backgroundColor=\'red\';" onmouseout="this.style.backgroundColor=\'transparent\';" style="margin-left: 10px; margin-right: 10px;"> <img src="http://'+modes_browser.domainName+'/wordpress/wp-content/plugins/'+this.browserFolder+'/icons/green_single_arrow_right.png" style="height: 15px;" title="Next entry/page" onclick="modes_browser.navigators[\''+name+'\'].goForward(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /></span></span>';
819}
820
821function navLast(name, datasourceName) {
822    return '<span class="navbutton"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_double_arrow_right.png" style="height: 15px;" title="Go to end" onclick="modes_browser.navigators[\''+name+'\'].gotoEnd(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /> </span></span>';
823  //return '<span onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'lightgreen\';"> <img src="'+modes_browser.pluginsurl+'/'+modes_browser.browserFolder+'/icons/green_double_arrow_right.png" style="height: 15px;" title="Go to end" onclick="modes_browser.navigators[\''+name+'\'].gotoEnd(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /> </span></span>';
824//    return '<span onmouseover="this.style.backgroundColor=\'red\';" onmouseout="this.style.backgroundColor=\'transparent\';"> <img src="http://'+modes_browser.domainName+'/wordpress/wp-content/plugins/'+this.browserFolder+'/icons/green_double_arrow_right.png" style="height: 15px;" title="Go to end" onclick="modes_browser.navigators[\''+name+'\'].gotoEnd(); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');" /> </span></span>';
825}
826
827function navToRecord(name, datasourceName, record, page, currentPage) {
828    if (currentPage) {
829        return '<span class="currentPageNavigator" title="Current page '+record+'" onclick="modes_browser.navigators[\''+name+'\'].gotoRecord('+record+'); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');">'+page+'</span>';
830    } else {
831        return '<span class="pageNavigator" onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'lightgreen\';" title="Go to record '+record+'" onclick="modes_browser.navigators[\''+name+'\'].gotoRecord('+record+'); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');">'+page+'</span>';
832    }
833    //return '<span class="pageNavigator" onmouseover="this.style.backgroundColor=\'transparent\';" onmouseout="this.style.backgroundColor=\'transparent\';" title="Go to record '+record+'" onclick="modes_browser.navigators[\''+name+'\'].gotoRecord('+record+'); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');">'+page+'</span>';
834//    return '<span class="pageNavigator" onmouseover="this.style.backgroundColor=\'red\';" onmouseout="this.style.backgroundColor=\'transparent\';" title="Go to record '+record+'" onclick="modes_browser.navigators[\''+name+'\'].gotoRecord('+record+'); modes_browser.displayModesDataForDatasource(\''+datasourceName+'\');">'+page+'</span>';
835}
836
837function tidySearchString(s) {
838    return s.toLowerCase().replace(/[$%:;.,/# ]+/g, ' ');
839}
840
841function addBrackets(s) {
842    if (s.indexOf('(') !== 0) {
843        return '('+s +')';
844    } else {
845        return s;
846    }
847}
848
849function displayKey(key, lastKey, stem) {
850  if (key == stem) {
851    return key;
852  } else {
853    var remainder = key.substring(stem.length);
854    if (remainder.indexOf(' ', 1) != -1)
855      remainder = remainder.substring(0, remainder.indexOf(' ', 1));
856    if (stem+remainder != lastKey)
857      return stem+remainder;
858    return '';
859  }
860}
861
862function keyListContainsSpace(keylist, useValue) {
863  for (var i=0; i<keylist.length; i++) {
864    if (useValue) {
865      if (keylist[i].getAttribute('value').indexOf(' ') > -1)
866        return true;
867    } else {
868      if (keylist[i].textContent.indexOf(' ') > -1)
869        return true;
870    }
871  }
872  return false;
873}
874
875function getSimpleSearchTermsFn(domain, app, file, query, aSearchIndex, linkedData, datasource) {
876  var indexName = aSearchIndex;
877  var controlId = 'modes_index_'+indexName.replace(/ /g, '_');
878  var url;
879    var sep;
880  if (linkedData) {
881        sep = '%26';
882    //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexName+'/?count=100';
883        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexName+'/?wordcutoff=no'+sep+'count=100';
884    if ((query !== '') && (this.simpleSearchClearSearch!=='yes'))
885      url = url+sep+'modes_query='+addBrackets(query); // bug 3378: rbl 14.12.2015; ensure search has round brackets
886  } else {
887        sep = this.getSeparator(datasource);
888    url = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'indexname='+indexName+sep+'nodups=yes'+sep+'wordcutoff=no'+sep+'target=3'+sep+'level=2'+sep+'count=100'+sep+'relation==*';
889    if ((query !== '') && (this.simpleSearchClearSearch!=='yes'))
890      url = url+sep+'query='+query;
891  }
892  var thisInput = jQuery('#q');
893  // because indexName etc. will have different values when the autocomplete callback is
894  // invoked, we need to set the URL as a data property on the selected element, and then
895  // retrieve it via the widget's element property when autocomplete is invoked:
896  thisInput.data('autocompleteUrl', url);
897  thisInput.data('separator', sep);
898  if (this.debug) {
899    alert('URL for #q is '+thisInput.data('autocompleteUrl')+'; separator is '+sep);
900  }
901  jQuery('#q').autocomplete({
902    source: function(request, response) {
903            searchTerm = tidySearchString(request.term);
904      $.ajax({
905        url: this.element.data('autocompleteUrl')+this.element.data('separator')+'indexkey='+searchTerm+this.element.data('separator')+'count=100',
906/*
907        dataType: "xml",
908        data: {
909          count: 100,
910          indexkey: searchTerm
911        },
912*/
913        success: function( xmlResponse ) {
914          var arr = new Array();
915          var keys = xmlResponse.documentElement.getElementsByTagName('key');
916          if (keys.length == 0) {
917//            arr.push( { label: 'Nothing matches "'+searchTerm+'"', value: undefined } );
918                        // linked data case: already converted to <option> elements:
919                        keys = xmlResponse.documentElement.getElementsByTagName('option');
920                        if (keys.length == 0) {
921                            arr.push( { label: 'Nothing matches "'+searchTerm+'"', value: undefined } );
922                        } else {
923              var currentStem = searchTerm;
924              var noNumbers = keyListContainsSpace(keys, true);
925                            for (var i=0; i<keys.length; i++) {
926                var thisTerm = keys[i].getAttribute('value');
927                if (!noNumbers) {
928                  arr.push( { label: keys[i].textContent, value: keys[i].getAttribute('value') });
929                } else {
930                  thisTerm = displayKey(thisTerm, currentStem, searchTerm);
931                  if (thisTerm != '') {
932                    currentStem = thisTerm;
933                    arr.push( { label: thisTerm, value: thisTerm });
934                  }
935                }
936                            }
937                        }
938          } else {
939            var currentStem = searchTerm;
940            var noNumbers = keyListContainsSpace(keys, false);
941            for (var i=0; i<keys.length; i++) {
942              var thisTerm = keys[i].childNodes[0].nodeValue;
943              if (!noNumbers) {
944                arr.push( { label: keys[i].textContent+' ('+keys[i].getAttribute('count')+')', value: keys[i].textContent });
945              }
946              else {
947                thisTerm = displayKey(thisTerm, currentStem, searchTerm);
948                if (thisTerm != '') {
949                  currentStem = thisTerm;
950                  arr.push( { label: thisTerm, value: thisTerm });
951                }
952              }
953            }
954          }
955          response( arr );
956        },
957        error: function( jqXHR, status, error ) {
958          if (this.debug) {
959            alert('Autocomplete request failed: '+status+'; '+error);
960          }
961        }
962      });
963    },
964    minLength: 2
965  });
966}
967
968function addNextLevelTerms(node, name, value, url, linkedData) {
969    //alert('at '+node.nodeName+': adding next level terms at '+node.parentNode.nextElementSibling.nodeName);
970    if ((node.parentNode.nextElementSibling) && (node.parentNode.nextElementSibling.nodeName.toLowerCase() == 'div')) {
971//        alert('sub-headings for '+value+' already in place');
972        node.checked = true;
973    } else {
974        jQuery.ajax({
975            type: "get", dataType: "xml", url: url+value+'}',
976            success: function (xmlResponse) {
977                var s = '<div>';
978                if (linkedData) {
979                    var keys = xmlResponse.documentElement.getElementsByTagName('option');
980                    for (var i = 0; i < keys.length; i++) {
981                        s = s + '<label><input type="radio" name="'+name+'" id="'+name+'" value="' + keys[i].getAttributeNode('value').nodeValue + '"><span>' + keys[i].textContent + '</span></input></label>';
982                    }
983                } else {
984                    var keys = xmlResponse.documentElement.getElementsByTagName('key');
985                    if (keys[0].textContent !== '') {
986                        for (var i = 0; i < keys.length; i++) {
987                            s = s + '<label><input type="radio" name="'+name+'" id="'+name+'" value="'+keys[i].textContent+'" onclick="addNextLevelTerms(this, \''+name+'\', \'' + keys[i].textContent + '\', \'' + url + '\', '+linkedData+');"><span>' + keys[i].textContent + '</span></input></label>';
988                        }
989                        node.checked = false;
990                    } else {
991                        node.checked = true;
992                    }
993//                alert('Node '+value+' checked: '+node.checked);
994                }
995                s = s + '</div>';
996                jQuery(s).insertAfter(node.parentNode);
997            },
998            failure: function() {
999                node.checked = true;
1000            }
1001        });
1002    }
1003}
1004
1005// iterate through indexSearches array, picking up required data:
1006function getIndexSearchTermsFn(domain, app, file, query, linkedData, datasource) {
1007  var urls = new Array();
1008//  alert('checking indexes; '+this.indexSearches.length+' found. app is '+app+' and file is '+file);
1009  for (var i=0; i<this.indexSearches.length; i++) {
1010    var entryType = this.indexSearches[i]['searchType'];
1011    var indexName = this.indexSearches[i]['indexName'];
1012    var controlId = 'modes_index_'+indexName.replace(/ /g, '_');
1013    var url;
1014//    alert('Search type for index '+indexName+' is '+entryType);
1015    switch(entryType)
1016    {
1017      case 'plain box':
1018        // do nothing
1019        break;
1020      case 'assisted':
1021//        alert('control id: '+controlId);
1022        var sep = '&';
1023                if (linkedData) {
1024                    sep = '%26';
1025          //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexName+'/?count=100';
1026                    url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexName+'/?count=100';
1027//          if ((query !== '') && (this.simpleSearchClearSearch!=='yes'))
1028          if (query !== '')
1029            url = url + sep + 'modes_query='+query;
1030        } else {
1031          url = 'http://'+this.domainName+this.scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+app+'&filename='+file+'&indexname='+indexName+'&target=3&level=2&count=100&relation==*';
1032//          if ((query !== '') && (this.simpleSearchClearSearch!=='yes'))
1033          if (query !== '')
1034            url = url + '&query='+query;
1035        }
1036        var thisInput = jQuery( '#modes_index_'+indexName.replace(/ /g, '_'));
1037        // because indexName etc. will have different values when the autocomplete callback is
1038        // invoked, we need to set the URL as a data property on the selected element, and then
1039        // retrieve it via the widget's element property when autocomplete is invoked:
1040        thisInput.data('autocompleteUrl', url);
1041                thisInput.data('separator', sep);
1042        if (this.debug) {
1043          alert(indexName+': URL for #modes_index_'+indexName.replace(/ /g, '_')+' is '+thisInput.data('autocompleteUrl'));
1044        }
1045        jQuery( '#modes_index_'+indexName.replace(/ /g, '_')).autocomplete({
1046          source: function(request, response) {
1047                        searchTerm = tidySearchString(request.term);
1048                        $.ajax({
1049                            url: this.element.data('autocompleteUrl')+this.element.data('separator')+'indexkey='+searchTerm+this.element.data('separator')+'count=100',
1050              success: function( xmlResponse ) {
1051                var arr = new Array();
1052                                var keys;
1053                                if (linkedData) {
1054                                    keys = xmlResponse.documentElement.getElementsByTagName('option');
1055                                    if (keys.length == 0) {
1056                                        arr.push( { label: 'Nothing matches "'+request.term+'"', value: undefined } );
1057                                    } else {
1058                    var currentStem = searchTerm;
1059                    var noNumbers = keyListContainsSpace(keys, true);
1060                    for (var i=0; i<keys.length; i++) {
1061                      var thisTerm = keys[i].getAttribute('value');
1062                      if (!noNumbers) {
1063                        arr.push( { label: keys[i].textContent, value: keys[i].getAttribute('value') });
1064                      } else {
1065                        thisTerm = displayKey(thisTerm, currentStem, searchTerm);
1066                        if (thisTerm != '') {
1067                          currentStem = thisTerm;
1068                          arr.push( { label: thisTerm, value: thisTerm });
1069                        }
1070                      }
1071                    }
1072//                                        for (var i=0; i<keys.length; i++) {
1073//                                            arr.push( { label: ke
1073ys[i].textContent, value: keys[i].getAttribute('value') });
1074//                                        }
1075                                    }
1076                                } else {
1077                                    keys = xmlResponse.documentElement.getElementsByTagName('key');
1078                                    if (keys.length == 0) {
1079                                        arr.push( { label: 'Nothing matches "'+request.term+'"', value: undefined } );
1080                                    } else {
1081                    var currentStem = searchTerm;
1082                    var noNumbers = keyListContainsSpace(keys, false);
1083                    for (var i=0; i<keys.length; i++) {
1084                      var thisTerm = keys[i].childNodes[0].nodeValue;
1085                      if (!noNumbers) {
1086                        arr.push( { label: keys[i].textContent+' ('+keys[i].getAttribute('count')+')', value: keys[i].textContent });
1087                      }
1088                      else {
1089                        thisTerm = displayKey(thisTerm, currentStem, searchTerm);
1090                        if (thisTerm != '') {
1091                          currentStem = thisTerm;
1092                          arr.push( { label: thisTerm, value: thisTerm });
1093                        }
1094                      }
1095                    }
1096//                                        for (var i=0; i<keys.length; i++) {
1097//                                            arr.push( { label: keys[i].textContent+' ('+keys[i].getAttribute('count')+')', value: keys[i].textContent });
1098//                                        }
1099                                    }
1100                                }
1101                response( arr );
1102              }
1103            });
1104          },
1105          minLength: 2
1106        });
1107        break;
1108      case 'dropdown':
1109                var sep;
1110        var controlName = 'modes_index_'+indexName.replace(/ /g, '_');
1111        jQuery('#'+controlName).html('<option value="">Loading...</option>');
1112        if (linkedData) {
1113                    sep = '%26';
1114          //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexname+'/?modes_query='+query;
1115                    url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/keylist/xml/'+indexName+'/?modes_query='+query+sep+'param=eltToUpdate='+controlName;
1116        } else {
1117          var sep = this.getSeparator(datasource);
1118          url = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'indexname='+indexName+sep+'target=3'+sep+'level=2'+sep+'count=1000'+sep+'param=eltToUpdate='+controlName;
1119          if (query !== '')
1120            url = url+sep+'query='+query;
1121        }
1122        if (this.debug) {
1123          alert('dropdown url for '+controlName+' is '+url);
1124        }
1125//                var thisInput = jQuery( '#'+controlName );
1126//                thisInput.data('eltname', controlName);
1127//                alert(url);
1128        jQuery.ajax({ type: "get", dataType: "xml", url: url,
1129/*
1130                    data: {
1131                        eltToUpdate: controlName
1132                    },
1133                    processData: true,
1134                    async: false, // work-round to get the correct element to be updated.
1135 */
1136          success: function(xmlResponse) {
1137//            alert('populating response to '+xmlResponse.param1);
1138                        var s = '<option />';
1139                        var indexName = xmlResponse.documentElement.getAttribute('indexname');
1140                        if (linkedData) {
1141                            var keys = xmlResponse.documentElement.getElementsByTagName('option');
1142                            for (var i=0; i<keys.length; i++) {
1143                                s = s+'<option value="'+keys[i].getAttributeNode('value').nodeValue+'">'+keys[i].textContent+'</option>';
1144                            }
1145                        } else {
1146                            var keys = xmlResponse.documentElement.getElementsByTagName('key');
1147                            for (var i=0; i<keys.length; i++) {
1148                                s = s+'<option>'+keys[i].textContent+'</option>';
1149                            }
1150                        }
1151                        if ((indexName) && (indexName !== '')) {
1152                            var controlName = 'modes_index_'+indexName.replace(/ /g, '_');
1153                            if (jQuery('#'+controlName)) {
1154                                jQuery('#'+controlName).html(s);
1155//              alert('updated '+indexName+' contents with '+s);
1156                            } else alert('#'+controlName+' not found');
1157                        } else alert('indexName not specified in response');
1158          }
1159        })
1160        break;
1161            case 'combo dropdown':
1162                break;
1163            case 'multilevel dropdown':
1164                var sep;
1165                var indexDatasourceName = this.indexSearches[i]['indexDatasource'];
1166                var indexDatasource = this.datasource[indexDatasourceName];
1167                // now get app and filename for this data source:
1168                var indexApp = indexDatasource.application;
1169                var indexFile = indexDatasource.filename;
1170                var ttIndexName = this.indexSearches[i]['topTermsIndex'];
1171                var ntIndexName = this.indexSearches[i]['narrowTermsIndex'];
1172                var controlName = 'modes_index_'+indexName.replace(/ /g, '_');
1173                jQuery('#'+controlName).html('<span>Loading...</span>');
1174                if (linkedData) {
1175                    sep = '%26';
1176                    url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+indexApp+'/'+indexFile+'/keylist/xml/'+ttIndexName+'/?param=eltToUpdate='+controlName;
1177                    ntUrl = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+indexApp+'/'+indexFile+'/keylist/xml/'+ntIndexName+'/?param=eltToUpdate='+controlName;
1178                } else {
1179                    var sep = this.getSeparator(datasource);
1180                    url = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+indexApp+sep+'filename='+indexFile+sep+'indexname='+ttIndexName+sep+'target=3'+sep+'level=2'+sep+'count=1000'+sep+'param=eltToUpdate='+controlName;
1181                    ntUrl = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+indexApp+sep+'filename='+indexFile+sep+'indexname=Terms'+sep+'target=3'+sep+'level=2'+sep+'count=1000'+sep+'param=eltToUpdate='+controlName+sep+'query={'+ntIndexName+'}={';
1182                    // don't add query if it's a termlist:
1183//                    if (query !== '')
1184//                        url = url+sep+'query='+query;
1185                }
1186                if (this.debug) {
1187                    alert('dropdown url for '+controlName+' is '+url);
1188                }
1189                jQuery.ajax({ type: "get", dataType: "xml", url: url,
1190                    success: function(xmlResponse) {
1191//                        var s = '<option />';
1192                        var s = '';
1193//                        var indexName = xmlResponse.documentElement.getAttribute('indexname');
1194                        if (linkedData) {
1195                            var keys = xmlResponse.documentElement.getElementsByTagName('option');
1196                            for (var i=0; i<keys.length; i++) {
1197                                s = s+'<label><input type="radio" value="'+keys[i].getAttributeNode('value').nodeValue+'"><span>'+keys[i].textContent+'</span></input></label>';
1198                            }
1199                        } else {
1200                            var keys = xmlResponse.documentElement.getElementsByTagName('key');
1201                            for (var i=0; i<keys.length; i++) {
1202                                s = s+'<label><input type="radio" name="'+controlName+'" id="'+controlName+'" value="'+keys[i].textContent+'" onclick="addNextLevelTerms(this, \''+controlName+'\', \''+keys[i].textContent+'\', \''+ntUrl+'\', '+linkedData+');"><span>'+keys[i].textContent+'</span></input></label>';
1203                            }
1204                        }
1205//                        if ((indexName) && (indexName !== '')) {
1206//                            var controlName = 'modes_index_'+indexName.replace(/ /g, '_');
1207                            if (jQuery('#'+controlName)) {
1208                                jQuery('#'+controlName).html(s);
1209//                  alert('updated '+indexName+' contents with '+s);
1210                            } else alert('#'+controlName+' not found');
1211//                        } else alert('indexName not specified in response');
1212                    }
1213                })
1214                break;
1215      case 'from/to plain boxes':
1216        // do nothing - could validate content
1217        break;
1218      default:
1219        alert('unsupported index search type: '+entryType);
1220    }
1221  }
1222}
1223
1224function displayHitlistFn(name, domain, scriptDir, app, file, query, title, navigator, start, count, targetPage, linkedData, datasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff, sortby) {
1225  var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
1226  var recordsperpage = 0 + count; // bug 3365, rbl 10.11.2015
1227  var url;
1228//  alert(navigator);
1229  if (navigator) {
1230    navigator.setup();
1231    if (datasource) {
1232      datasource.setup();
1233      if (this.debug) {
1234        alert('data source name: '+datasource.name+'; image dir: '+datasource.imageDir );
1235      }
1236    }
1237  } else if (datasource) {
1238    datasource.setup();
1239    if (this.debug) {
1240      alert('data source name: '+datasource.name+'; image dir: '+datasource.imageDir );
1241    }
1242  } else alert('Error: hit list '+name+' has no navigator or data source');
1243  if (linkedData) {
1244        var sep = '%26';
1245        //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+'%26modes_query='+query+'%26transform='+aTransform;//this.hitlistXslt;
1246        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/xml/?start='+start+sep+'modes_query='+query+sep+'transform='+aTransform;
1247//        var sep = '&';
1248//        url = 'http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+sep+'modes_query='+query+sep+'transform='+aTransform;//this.hitlistXslt;
1249        if (sortby !== '')
1250            url = url+sep+'sortby='+sortby;
1251        if (targetPage !== '') {
1252            url = url+sep+'param=item_page='+this.pages[targetPage];
1253            if (this.debug) {
1254                alert('target page is '+targetPage);
1255            }
1256        } else if (this.itemPage)
1257            url = url+sep+'param=item_page='+this.pages[this.itemPage];
1258        if (query !== '')
1259            url = url+sep+'param=query='+query;
1260        url = url+sep+'param=datasource='+datasource.name;
1261        url = url+sep+'param=domain=http://'+domain;
1262        url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1263        url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1264        url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1265        if (sectionOrder !== '')
1266            url = url+sep+'param=section_order='+sectionOrder;
1267        if (headingDiv !== '')
1268            url = url+sep+'param=heading_as_div='+headingDiv;
1269        if (headingSpan !== '')
1270            url = url+sep+'param=heading_as_span='+headingSpan;
1271        if (entryDiv !== '')
1272            url = url+sep+'param=entry_as_div='+entryDiv;
1273        if (entrySpan !== '')
1274            url = url+sep+'param=entry_as_span='+entrySpan;
1275        if (entryCutoff !== '')
1276            url = url+sep+'param=entry_cutoff='+entryCutoff;
1277        if (linkedData)
1278            url = url+sep+'param=linked_data_url=http://'+domain+'/'+app+'/'+file+'/';
1279    if (this.debug)
1280    {
1281      alert(url);
1282    }
1283        // N.B. changed datatype to xml, to get DOM object returned. This allows access to the top-level attributes
1284        // which are required to display the navigator directly.
1285        // Use of xmlToString solves the first problem, but the second remains ...
1286        var thisNav = navigator;
1287    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1288      success: function(xml, status, xhr) {
1289//        alert('status: '+status+'; text: '+xml);
1290        if (jQuery('#'+name+'_data')) {
1291          jQuery('#'+name+'_data').html(xmlToString(xml));
1292        } else alert('#'+name+'_data not found');
1293                if (thisNav) {
1294                    // N.B. unary plus to force string value to integer
1295                    thisNav.position = +xml.documentElement.getAttribute('startIndex');
1296                    thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
1297                    thisNav.total = +xml.documentElement.getAttribute('totalResults');
1298                    thisNav.displayDirect();
1299                }
1300                if (modes_browser.customOnLoad) {
1301                    modes_browser.customOnLoad();
1302                }
1303      },
1304            error: function( jqXHR, status, error ) {
1305                if (this.debug) {
1306          alert('Hitlist request failed: '+status+'; '+error);
1307        }
1308        if ( console && console.log ){
1309          console.log('failed URL: ' + url);
1310        }
1311            }
1312    })
1313  } else {
1314//    url = 'http://'+domain+scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+app+'&filename='+file+'&query='+query+'&transform='+summarystyle+'&response=text/html&target=4&level=3&nodups=yes&count='+recordsperpage+'&start='+start;
1315    var sep = this.getSeparator(datasource);
1316    url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+summarystyle+sep+'response=text/xml'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
1317        if (sortby !== '')
1318            url = url+sep+'sortby='+sortby;
1319    if (targetPage !== '') {
1320      url = url+sep+'param=item_page='+this.pages[targetPage];
1321      if (this.debug) {
1322        alert('target page is '+targetPage);
1323      }
1324    } else if (this.itemPage)
1325      url = url+sep+'param=item_page='+this.pages[this.itemPage];
1326    url = url+sep+'param=datasource='+datasource.name;
1327        url = url+sep+'param=domain=http://'+domain;
1328        url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1329    url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1330    url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1331        if (sectionOrder !== '')
1332            url = url+sep+'param=section_order='+sectionOrder;
1333        if (headingDiv !== '')
1334            url = url+sep+'param=heading_as_div='+headingDiv;
1335        if (headingSpan !== '')
1336            url = url+sep+'param=heading_as_span='+headingSpan;
1337        if (entryDiv !== '')
1338            url = url+sep+'param=entry_as_div='+entryDiv;
1339        if (entrySpan !== '')
1340            url = url+sep+'param=entry_as_span='+entrySpan;
1341        if (entryCutoff !== '')
1342            url = url+sep+'param=entry_cutoff='+entryCutoff;
1343    if (linkedData)
1344      url = url+sep+'param=linked_data_url=http://'+domain+'/'+app+'/'+file+'/';
1345    if (this.debug)
1346    {
1347      alert(url);
1348    }
1349        // N.B. changed datatype to xml, to get DOM object returned. This allows access to the top-level attributes
1350        // which are required to display the navigator directly.
1351        // Use of xmlToString solves the first problem, but the second remains ...
1352        var thisNav = navigator;
1353    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1354      success: function(xml) {
1355        if (this.debug)
1356          alert('data: '+xml);
1357        if (jQuery('#'+name+'_data')) {
1358          jQuery('#'+name+'_data').html(xmlToString(xml));
1359        } else alert('#'+name+'_data not found');
1360                if (thisNav) {
1361                    // N.B. unary plus to force string value to integer
1362                    thisNav.position = +xml.documentElement.getAttribute('startIndex');
1363                    thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
1364                    thisNav.total = +xml.documentElement.getAttribute('totalResults');
1365                    thisNav.displayDirect();
1366                }
1367                if (modes_browser.customOnLoad) {
1368                    modes_browser.customOnLoad();
1369                }
1370      },
1371      error: function( jqXHR, status, error ) {
1372        if (this.debug) {
1373          alert('Hitlist request failed: '+status+'; '+error);
1374        }
1375        if ( console && console.log ){
1376          console.log('failed URL: ' + url);
1377        }
1378      }
1379    })
1380  }
1381}
1382
1383function getRandomInt(aMin, aMax) {
1384    return Math.floor(Math.random() * (aMax - aMin + 1)) + aMin;
1385}
1386
1387function subsetToQueryFn(aFilename) {
1388    var fileurl=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aFilename;
1389    var aQuery;
1390    jQuery.ajax({ type: "get", dataType: "xml", url: fileurl,
1391        async: false,
1392        success: function(xml) {
1393            if (this.debug)
1394                alert('subset: '+xml);
1395            aQuery = jQuery( xml ).find('RECORD').toArray();
1396        },
1397        error: function( jqXHR, status, error ) {
1398            if (this.debug) {
1399                alert('Subset file request failed: '+status+'; '+error);
1400            }
1401        }
1402    })
1403    return aQuery;
1404}
1405
1406function displayRandomRecordFn(name, domain, application, filename, activeQuery, datasource, aTransform, aLinkedData, aMode, aTimeout, anInitialState) {
1407    // alert('timeout: '+aTimeout);
1408    // get count of records in file which match query:
1409    if (datasource) {
1410        datasource.query = activeQuery;
1411        datasource.setup();
1412    }
1413    var currentObject = this;
1414    var timerId = setInterval( function() {
1415        var noOfRecords = datasource.hitCount;
1416        var recordNo = getRandomInt(1, noOfRecords);
1417        currentObject.getModesRecord(name, domain, currentObject.scriptDir, application, filename, activeQuery, datasource, aLinkedData, aTransform, recordNo);
1418    }, aTimeout);
1419}
1420
1421function displaySlideshowFn(name, domain, application, filename, activeQuery, datasource, aNavigator, aSubsetFile, aTransform, aLinkedData, aMode, aTimeout, anInitialState) {
1422    if (aMode == 'manual')
1423        this.displaySlideshowManual(name, domain, application, filename, activeQuery, datasource, aNavigator, aSubsetFile, aTransform, aLinkedData, aTimeout, anInitialState);
1424    else
1425        this.displaySlideshowAuto(name, domain, application, filename, activeQuery, datasource, aSubsetFile, aTransform, aLinkedData, aTimeout, anInitialState);
1426}
1427
1428function displaySlideshowManualFn(name, domain, application, filename, activeQuery, datasource, aNavigator, aSubsetFile, aTransform, aLinkedData, aTimeout, anInitialState) {
1429    // create slideshow as a static modesDetail, populated via current search or SCX file as appropriate.
1430    // it should be given a standard 'after' navigator, with which the user can move through the slides
1431    // get count of records in file which match query:
1432    var savedSearch;
1433    var noOfRecords;
1434    var useSavedSearch = true;
1435    if (datasource) {
1436        if (aSubsetFile !== '') {
1437            savedSearch = this.subsetToQuery(aSubsetFile);
1438            //var hits = savedSearch.length;
1439        } else {
1440            datasource.query = activeQuery;
1441        }
1442        datasource.setup();
1443    }
1444    if ((savedSearch) && (savedSearch.length > 0)) {
1445        noOfRecords = savedSearch.length;
1446    } else {
1447        noOfRecords = datasource.hitCount;
1448        useSavedSearch = false;
1449    }
1450    if (aNavigator) {
1451        aNavigator.total = noOfRecords;
1452//        aNavigator.display();
1453        aNavigator.displayDirect();
1454;    }
1455    var currentObject = this;
1456    var recordNo = aNavigator.position - 1;
1457    if (useSavedSearch) {
1458       var thisRecordQuery;
1459       var thisRecord = savedSearch[recordNo];
1460       if ($(thisRecord).find('ID').text() !== '') {
1461          thisRecordQuery = '{}={'+$(thisRecord).find('ID').text();
1462       }
1463       else if ($(thisRecord).find('PRIMARY-KEY').text() !== '') {
1464          thisRecordQuery = '{Identity number}={'+$(thisRecord).find('PRIMARY-KEY').text();
1465       }
1466       thisRecordQuery = thisRecordQuery + '}';
1467       currentObject.getModesRecord(name, domain, currentObject.scriptDir, application, filename, thisRecordQuery, datasource, aLinkedData, aTransform, 1);
1468    } else {
1469      currentObject.getModesRecord(name, domain, currentObject.scriptDir, application, filename, activeQuery, datasource, aLinkedData, aTransform, recordNo);
1470    }
1471}
1472
1473function displaySlideshowAutoFn(name, domain, application, filename, activeQuery, datasource, aSubsetFile, aTransform, aLinkedData, aTimeout, anInitialState) {
1474    // get count of records in file which match query:
1475    var savedSearch;
1476    if (datasource) {
1477        if (aSubsetFile !== '') {
1478            savedSearch = this.subsetToQuery(aSubsetFile);
1479            //var hits = savedSearch.length;
1480        } else {
1481            datasource.query = activeQuery;
1482        }
1483        datasource.setup();
1484    }
1485    var currentObject = this;
1486    var recordNo = 0;
1487    if (anInitialState == 'paused')
1488        jQuery('#'+name+'_control').attr('value', 'resume');
1489    else
1490        jQuery('#'+name+'_control').attr('value', 'pause');
1491    function showRecord() {
1492        var noOfRecords;
1493        var useSavedSearch = true;
1494        if ((savedSearch) && (savedSearch.length > 0)) {
1495            noOfRecords = savedSearch.length;
1496        } else {
1497            noOfRecords = datasource.hitCount;
1498            useSavedSearch = false;
1499        }
1500        if (useSavedSearch) {
1501            var thisRecordQuery;
1502            var thisRecord = savedSearch[recordNo];
1503            if ($(thisRecord).find('ID').text() !== '') {
1504                thisRecordQuery = '{}={'+$(thisRecord).find('ID').text();
1505            }
1506            else if ($(thisRecord).find('PRIMARY-KEY').text() !== '') {
1507                thisRecordQuery = '{Identity number}={'+$(thisRecord).find('PRIMARY-KEY').text();
1508
1509            }
1510            thisRecordQuery = thisRecordQuery + '}';
1511            currentObject.getModesRecord(name, domain, currentObject.scriptDir, application, filename, thisRecordQuery, datasource, aLinkedData, aTransform, 1);
1512        } else {
1513            currentObject.getModesRecord(name, domain, currentObject.scriptDir, application, filename, activeQuery, datasource, aLinkedData, aTransform, recordNo);
1514        }
1515        if (anInitialState == 'paused'){
1516            anInitialState = '';
1517            this.pause();
1518        }
1519        if (recordNo < noOfRecords - 1)
1520            recordNo++;
1521        else
1522            recordNo = 0;
1523    }
1524    showRecord();
1525    this.slideshow = jQuery.timer( showRecord, aTimeout, true);
1526}
1527
1528function displayAddDataToRecordsFn(name, domain, linkedData, datasource, scriptDir, app, file, query, targetPage, primaryIndex, elementGroup) {
1529    if (datasource) {
1530        datasource.setup();
1531        if (this.debug) {
1532            alert('add data datasource name: '+datasource.name);
1533        }
1534    } else alert('Error: add data '+name+' has no data source');
1535    if (linkedData)
1536        alert('Error: cannot update records via a Linked Data connection');
1537    var sep = this.getSeparator(datasource);
1538    var url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'indexname='+primaryIndex+sep+'target=3'+sep+'level=2'+sep+'relation=*=*'+sep+'count=100';
1539    if (this.debug) alert(url);
1540    var thisInput = jQuery('#'+datasource.name+'_adddata_add_record');
1541    // because indexName etc. will have different values when the autocomplete callback is
1542    // invoked, we need to set the URL as a data property on the selected element, and then
1543    // retrieve it via the widget's element property when autocomplete is invoked:
1544    thisInput.data('autocompleteUrl', url);
1545    thisInput.data('separator', sep);
1546    if (this.debug) {
1547        alert(name+': URL for #'+datasource.name+'_adddata_add_record is '+thisInput.data('autocompleteUrl'));
1548    }
1549//    alert('add record control is '+datasource.name+'_adddata_add_record');
1550    jQuery('#'+datasource.name+'_adddata_add_record').autocomplete({
1551        source: function(request, response) {
1552            searchTerm = request.term;
1553            $.ajax({
1554                url: this.element.data('autocompleteUrl')+this.element.data('separator')+'indexkey='+searchTerm+this.element.data('separator')+'count=100',
1555                success: function( xmlResponse ) {
1556//                    alert(url);
1557                    var arr = new Array();
1558                    var keys;
1559                    if (linkedData) {
1560                        keys = xmlResponse.documentElement.getElementsByTagName('option');
1561                        if (keys.length == 0) {
1562                            arr.push( { label: 'Nothing matches "'+request.term+'"', value: undefined } );
1563                        } else {
1564              var currentStem = searchTerm;
1565              var noNumbers = keyListContainsSpace(keys, true);
1566              for (var i=0; i<keys.length; i++) {
1567                var thisTerm = keys[i].getAttribute('value');
1568                if (!noNumbers) {
1569                  arr.push( { label: keys[i].textContent, value: keys[i].getAttribute('value') });
1570                } else {
1571                  thisTerm = displayKey(thisTerm, currentStem, searchTerm);
1572                  if (thisTerm != '') {
1573                    currentStem = thisTerm;
1574                    arr.push( { label: thisTerm, value: thisTerm });
1575                  }
1576                }
1577              }
1578//                            for (var i=0; i<keys.length; i++) {
1579//                                arr.push( { label: keys[i].textContent, value: keys[i].getAttribute('value') });
1580//                            }
1581                        }
1582                    } else {
1583                        keys = xmlResponse.documentElement.getElementsByTagName('key');
1584                        if (keys.length == 0) {
1585                            arr.push( { label: 'Nothing matches "'+request.term+'"', value: undefined } );
1586                        } else {
1587              var currentStem = searchTerm;
1588              var noNumbers = keyListContainsSpace(keys, false);
1589              for (var i=0; i<keys.length; i++) {
1590                var thisTerm = keys[i].childNodes[0].nodeValue;
1591                if (!noNumbers) {
1592                  arr.push( { label: keys[i].textContent+' ('+keys[i].getAttribute('count')+')', value: keys[i].textContent });
1593                }
1594                else {
1595                  thisTerm = displayKey(thisTerm, currentStem, searchTerm);
1596                  if (thisTerm != '') {
1597                    currentStem = thisTerm;
1598                    arr.push( { label: thisTerm, value: thisTerm });
1599                  }
1600                }
1601              }
1602//                            for (var i=0; i<keys.length; i++) {
1603//                                arr.push( { label: keys[i].textContent+' ('+keys[i].getAttribute('count')+')', value: keys[i].textContent });
1604//                            }
1605                        }
1606                    }
1607                    response( arr );
1608                }
1609            });
1610        },
1611        minLength: 2
1612    });
1613
1614}
1615
1616function displayProcedureFileFn(name, procedure, form, domain, scriptDir, aControl, datasource, query) {
1617  //alert('displaying '+name);
1618  var app = datasource.application;
1619  var file = datasource.filename;
1620  var procedureControl = this.findControl(procedure);
1621  var form = document.forms[form];
1622  var resolvedQuery;
1623  //alert(query);
1624  if (query !== '') {
1625    resolvedQuery = this.getFormValues(form, query);
1626  } else {
1627    if (form['modes_query']) {
1628      resolvedQuery = form['modes_query'].value;
1629    } else {
1630      resolvedQuery = this.modesQuery;
1631    }
1632  }
1633
1634  //alert('query is '+resolvedQuery);
1635  var sep = '&';
1636  var start = 1;
1637  var recordsperpage = 1000;
1638  var browser = this;
1639  if (resolvedQuery !== '') {
1640    var url = 'http://'+this.domainName+this.scriptDir+'modescgi.exe/'+'?transport=tcpip'+sep+'servername=marmoset'+sep+'application='+app+sep+'filename='+file+sep+'query='+resolvedQuery+sep+'response=text/xml'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
1641    //alert(url);
1642    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1643      success: function(xml, status, xhr) {
1644        browser.displayModesRecords(xml, aControl, procedureControl, datasource);
1645      },
1646      failure: function(html) {
1647        alert('failed to find records:'+html);
1648      }
1649    });
1650  }
1651}
1652
1653function displayModesRecordFn(xml, rec, aControl, procedureControl, offset) {
1654  var displayRec = '';
1655  var eltsToDisplay = procedureControl['fields'];
1656  var identity = aControl['identityxpath'];
1657  //alert('processing '+rec.nodeName);
1658  displayRec = displayRec + '<div class="record">';
1659  if (identity !== '') {
1660    //alert(identity+'; '+xmlToString(rec));
1661    var identityData = xml.evaluate(identity, rec, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1662    if (identityData.snapshotLength > 0) {
1663      //alert('processing '+identityData.snapshotItem(0).nodeValue);
1664      displayRec = displayRec + '<input type="hidden" name="mergeidentity_'+offset+'" value="'+identityData.snapshotItem(0).nodeValue+'"/>';
1665    }
1666  }
1667  if (eltsToDisplay) {
1668    for (var i=0; i<eltsToDisplay.length; i++) {
1669      eltTodisplay = eltsToDisplay[i];
1670      if (!eltTodisplay) {
1671        continue;
1672      }
1673      var fieldType = eltTodisplay['type'];
1674      //alert(i+': '+fieldType);
1675      if (fieldType == 'display') {
1676        //displayRec = displayRec + '<div class="display"><p>Node: '+rec.nodeName+'; XPath: '+eltTodisplay['xpath']+'</p></div>';
1677        var fieldData = xml.evaluate(eltTodisplay['xpath'], rec, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1678        if (fieldData.snapshotLength > 0) {
1679          displayRec = displayRec + '<div class="'+eltTodisplay['fieldtype']+'">'+fieldData.snapshotItem(0).nodeValue+'</div>';
1680        }
1681      } else if (fieldType == 'update') {
1682        var label = eltTodisplay['description'];
1683        var fieldName = eltTodisplay['name'];
1684        var fieldInputType = eltTodisplay['fieldtype'];
1685        //alert(fieldInputType);
1686        switch (fieldInputType) {
1687          case 'checkbox':
1688            displayRec = displayRec + '<div class="input"><label for="">'+label+'</label><input type="checkbox" name="mergedata_'+offset+'_'+i+'"/></div>';
1689            break;
1690          case 'identity':
1691            displayRec = displayRec + '<div class="input"><input type="hidden" name="mergedata_'+offset+'_'+i+'"/></div>';
1692            break;
1693          case 'formvalue':
1694            //displayRec = displayRec + '<div class="input"><input type="hidden" name="mergedata_'+offset+'_'+i+'" value="'+eltTodisplay['data']+'"/></div>';
1695            break;
1696          case 'fixedvalue':
1697            displayRec = displayRec + '<div class="input"><input type="hidden" name="mergedata_'+offset+'_'+i+'" value="'+eltTodisplay['data']+'"/></div>';
1698            break;
1699          case 'procedureidentity':
1700            break;
1701          default:
1702            displayRec = displayRec + '<div class="input"><label for="">'+label+'</label><input type="text" name="mergedata_'+offset+'_'+i+'"/></div>';
1703            break;
1704        }
1705      }
1706    }
1707  }
1708  displayRec = displayRec + '</div>';
1709  //alert(displayRec);
1710  return displayRec;
1711}
1712
1713function displayModesRecordsFn(xml, aControl, procedureControl, datasource) {
1714  var recs = xml.evaluate('/recordset/record/*', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1715  //alert(recs.snapshotLength+' records found for '+aControl['name']);
1716  var recordsElt = jQuery('#'+aControl['name']);
1717  //alert(recordsElt);
1718  var content = '';
1719  content = content + '<input type="hidden" name="hitcount" value="'+recs.snapshotLength+'"/>';
1720  if ($('#'+datasource.name+'_hitcount')) {
1721    $('#'+datasource.name+'_hitcount').html('<span>'+recs.snapshotLength+' hits</span>');
1722  }
1723  for (i=0; i<recs.snapshotLength; i++) {
1724    content = content + this.displayModesRecord(xml, recs.snapshotItem(i), aControl, procedureControl, i);
1725  }
1726  recordsElt.html(content);
1727}
1728
1729function updateHitlistFn(name, domain, scriptDir, app, file, query, title, navigator, start, count, targetPage, linkedData, datasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff, sortby) {
1730  var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
1731  var recordsperpage = 0 + count; // bug 3365, rbl 10.11.2015
1732  var url;
1733//  alert(navigator);
1734  if (navigator) {
1735    navigator.setup();
1736    if (datasource) {
1737      datasource.setup();
1738      if (this.debug) {
1739        alert('data source name: '+datasource.name+'; image dir: '+datasource.imageDir );
1740      }
1741    }
1742  } else if (datasource) {
1743    datasource.setup();
1744    if (this.debug) {
1745      alert('data source name: '+datasource.name+'; image dir: '+datasource.imageDir );
1746    }
1747  } else alert('Error: hit list '+name+' has no navigator or data source');
1748  if (linkedData) {
1749    var sep = '%26';
1750    //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+'%26modes_query='+query+'%26transform='+aTransform;//this.hitlistXslt;
1751    url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/xml/?start='+start+sep+'modes_query='+query+sep+'transform='+aTransform;
1752//        var sep = '&';
1753//        url = 'http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+sep+'modes_query='+query+sep+'transform='+aTransform;//this.hitlistXslt;
1754    if (sortby !== '')
1755      url = url+sep+'sortby='+sortby;
1756    if (targetPage !== '') {
1757      url = url+sep+'param=item_page='+this.pages[targetPage];
1758      if (this.debug) {
1759        alert('target page is '+targetPage);
1760      }
1761    } else if (this.itemPage)
1762      url = url+sep+'param=item_page='+this.pages[this.itemPage];
1763    if (query !== '')
1764      url = url+sep+'param=query='+query;
1765    url = url+sep+'param=datasource='+datasource.name;
1766    url = url+sep+'param=domain=http://'+domain;
1767    url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1768    url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1769    url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1770    if (sectionOrder !== '')
1771      url = url+sep+'param=section_order='+sectionOrder;
1772    if (headingDiv !== '')
1773      url = url+sep+'param=heading_as_div='+headingDiv;
1774    if (headingSpan !== '')
1775      url = url+sep+'param=heading_as_span='+headingSpan;
1776    if (entryDiv !== '')
1777      url = url+sep+'param=entry_as_div='+entryDiv;
1778    if (entrySpan !== '')
1779      url = url+sep+'param=entry_as_span='+entrySpan;
1780    if (entryCutoff !== '')
1781      url = url+sep+'param=entry_cutoff='+entryCutoff;
1782    if (linkedData)
1783      url = url+sep+'param=linked_data_url=http://'+domain+'/'+app+'/'+file+'/';
1784    if (this.debug)
1785    {
1786      alert(url);
1787    }
1788    // N.B. changed datatype to xml, to get DOM object returned. This allows access to the top-level attributes
1789    // which are required to display the navigator directly.
1790    // Use of xmlToString solves the first problem, but the second remains ...
1791    var thisNav = navigator;
1792    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1793      success: function(xml, status, xhr) {
1794//        alert('status: '+status+'; text: '+xml);
1795        if (jQuery('#'+name+'_data')) {
1796          jQuery('#'+name+'_data').html(xmlToString(xml));
1797        } else alert('#'+name+'_data not found');
1798        if (thisNav) {
1799          // N.B. unary plus to force string value to integer
1800          thisNav.position = +xml.documentElement.getAttribute('startIndex');
1801          thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
1802          thisNav.total = +xml.documentElement.getAttribute('totalResults');
1803          thisNav.displayDirect();
1804        }
1805        if (modes_browser.customOnLoad) {
1806          modes_browser.customOnLoad();
1807        }
1808      },
1809      error: function( jqXHR, status, error ) {
1810        if (this.debug) {
1811          alert('Hitlist request failed: '+status+'; '+error);
1812        }
1813        if ( console && console.log ){
1814          console.log('failed URL: ' + url);
1815        }
1816      }
1817    })
1818  } else {
1819//    url = 'http://'+domain+scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+app+'&filename='+file+'&query='+query+'&transform='+summarystyle+'&response=text/html&target=4&level=3&nodups=yes&count='+recordsperpage+'&start='+start;
1820    var sep = this.getSeparator(datasource);
1821    url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+summarystyle+sep+'response=text/xml'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
1822    if (sortby !== '')
1823      url = url+sep+'sortby='+sortby;
1824    if (targetPage !== '') {
1825      url = url+sep+'param=item_page='+this.pages[targetPage];
1826      if (this.debug) {
1827        alert('target page is '+targetPage);
1828      }
1829    } else if (this.itemPage)
1830      url = url+sep+'param=item_page='+this.pages[this.itemPage];
1831    url = url+sep+'param=datasource='+datasource.name;
1832    url = url+sep+'param=domain=http://'+domain;
1833    url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1834    url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1835    url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1836    if (sectionOrder !== '')
1837      url = url+sep+'param=section_order='+sectionOrder;
1838    if (headingDiv !== '')
1839      url = url+sep+'param=heading_as_div='+headingDiv;
1840    if (headingSpan !== '')
1841      url = url+sep+'param=heading_as_span='+headingSpan;
1842    if (entryDiv !== '')
1843      url = url+sep+'param=entry_as_div='+entryDiv;
1844    if (entrySpan !== '')
1845      url = url+sep+'param=entry_as_span='+entrySpan;
1846    if (entryCutoff !== '')
1847      url = url+sep+'param=entry_cutoff='+entryCutoff;
1848    if (linkedData)
1849      url = url+sep+'param=linked_data_url=http://'+domain+'/'+app+'/'+file+'/';
1850    if (this.debug)
1851    {
1852      alert(url);
1853    }
1854    // N.B. changed datatype to xml, to get DOM object returned. This allows access to the top-level attributes
1855    // which are required to display the navigator directly.
1856    // Use of xmlToString solves the first problem, but the second remains ...
1857    var thisNav = navigator;
1858    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1859      success: function(xml) {
1860        if (this.debug)
1861          alert('data: '+xml);
1862        xml2 = modes_browser.addInputs(xml, name);
1863        if (jQuery('#'+name+'_data')) {
1864          jQuery('#'+name+'_data').html(xmlToString(xml2));
1865        } else alert('#'+name+'_data not found');
1866        if (thisNav) {
1867          // N.B. unary plus to force string value to integer
1868          thisNav.position = +xml.documentElement.getAttribute('startIndex');
1869          thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
1870          thisNav.total = +xml.documentElement.getAttribute('totalResults');
1871          thisNav.displayDirect();
1872        }
1873        if (modes_browser.customOnLoad) {
1874          modes_browser.customOnLoad();
1875        }
1876      },
1877      error: function( jqXHR, status, error ) {
1878        if (this.debug) {
1879          alert('Hitlist request failed: '+status+'; '+error);
1880        }
1881        if ( console && console.log ){
1882          console.log('failed URL: ' + url);
1883        }
1884      }
1885    })
1886  }
1887}
1888
1889function displayObjectCheckFn(name, domain, linkedData, datasource, scriptDir, app, file, query, start, recordsperpage, targetPage, linkedDataResource, aDatasource, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, entryCutoff) {
1890    var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
1891    var recordsperpage = 1000;
1892    var url;
1893    if (datasource) {
1894        datasource.setup();
1895        if (this.debug) {
1896            alert('data source name: '+datasource.name+'; image dir: '+datasource.imageDir );
1897        }
1898    } else alert('Error: object check '+name+' has no data source');
1899//    alert('Name is '+name);
1900    if (linkedData)
1901        alert('Error: cannot update records via a Linked Data connection');
1902    var sep = this.getSeparator(datasource);
1903    url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+summarystyle+sep+'response=text/xml'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
1904//    if (sortby !== '')
1905//        url = url+sep+'sortby='+sortby;
1906    if (targetPage !== '') {
1907        url = url+sep+'param=item_page='+this.pages[targetPage];
1908        if (this.debug) {
1909            alert('target page is '+targetPage);
1910        }
1911    } else if (this.itemPage)
1912        url = url+sep+'param=item_page='+this.pages[this.itemPage];
1913    url = url+sep+'param=datasource='+datasource.name;
1914    url = url+sep+'param=domain=http://'+domain;
1915    url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1916    url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1917    url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1918    if (sectionOrder !== '')
1919        url = url+sep+'param=section_order='+sectionOrder;
1920    if (headingDiv !== '')
1921        url = url+sep+'param=heading_as_div='+headingDiv;
1922    if (headingSpan !== '')
1923        url = url+sep+'param=heading_as_span='+headingSpan;
1924    if (entryDiv !== '')
1925        url = url+sep+'param=entry_as_div='+entryDiv;
1926    if (entrySpan !== '')
1927        url = url+sep+'param=entry_as_span='+entrySpan;
1928    if (entryCutoff !== '')
1929        url = url+sep+'param=entry_cutoff='+entryCutoff;
1930    if (this.debug)
1931    {
1932        alert(url);
1933    }
1934    // N.B. changed datatype to xml, to get DOM object returned. This allows access to the top-level attributes
1935    // which are required to display the navigator directly.
1936    // Use of xmlToString solves the first problem, but the second remains ...
1937//    var thisNav = navigator;
1938    jQuery.ajax({ type: "get", dataType: "xml", url: url,
1939        success: function(xml) {
1940            if (this.debug)
1941                alert('data: '+xml);
1942            if (jQuery('#'+name+'_data')) {
1943                jQuery('#'+name+'_data').html(xmlToString(xml));
1944            } else alert('#'+name+'_data not found');
1945/*            if (thisNav) {
1946                // N.B. unary plus to force string value to integer
1947                thisNav.position = +xml.documentElement.getAttribute('startIndex');
1948                thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
1949                thisNav.total = +xml.documentElement.getAttribute('totalResults');
1950                thisNav.displayDirect();
1951            }
1952*/
1953            if (modes_browser.customOnLoad) {
1954                modes_browser.customOnLoad();
1955            }
1956        },
1957        error: function( jqXHR, status, error ) {
1958            if (this.debug) {
1959                alert('Object check request failed: '+status+'; '+error);
1960            }
1961        }
1962    })
1963}
1964
1965function getModesRecordFn(aName, domain, scriptDir, app, file, query, datasource, linkedData, aTransform, aStart, aDiv) {
1966    //alert('<span>Record '+aStart+'</span>');
1967    //return '<span>Record '+aStart+'</span>';
1968    var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
1969    var recordsperpage = 1;
1970    var url;
1971    if (datasource) {
1972        datasource.setup();
1973    } else alert('Error: record '+name+' has no data source');
1974    if (linkedData) {
1975        //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+aStart+'%26modes_query='+query+'%26count='+recordsperpage+'&transform='+aTransform;
1976        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+aStart+'%26modes_query='+query+'%26count='+recordsperpage+'&transform='+aTransform;
1977//    alert(url);
1978        jQuery.ajax({ type: "get", dataType: "html", url: url,
1979            success: function(xml, status, xhr) {
1980//        alert('status: '+status+'; text: '+xml);
1981                if (jQuery('#'+aName)) {
1982                    //alert('setting #'+name+' to '+xml);
1983                    jQuery('#'+aName).html(xml);
1984                } else
1985                    alert('#'+aName+' not found');
1986            }
1987        })
1988    } else {
1989        var sep = this.getSeparator(datasource);
1990        url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+aTransform+sep+'response=text/html'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+aStart;
1991        if (this.itemPage)
1992            url = url+sep+'param=item_page='+this.pages[this.itemPage];
1993        url = url+sep+'param=datasource='+datasource.name;
1994        url = url+sep+'param=domain=http://'+domain;
1995        url = url+sep+'param=script_folder=http://'+domain+scriptDir;
1996        url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
1997        url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
1998        if (this.debug)
1999            alert(url);
2000        jQuery.ajax({ type: "get", dataType: "html", url: url,
2001            success: function(xml) {
2002//                alert(xml);
2003                if (jQuery('#'+aName)) {
2004                    //alert('setting #'+name+' to '+xml);
2005                    jQuery('#'+aName).html(xml);
2006                } else
2007                    alert('#'+aName+' not found');
2008            }
2009        })
2010    }
2011}
2012
2013function getOnThisDayFn(name, domain, scriptDir, app, file, query, anOnThisDayIndex, aTransform, start, targetPage, linkedData, datasource) {
2014  if (linkedData)
2015    var summarystyle = aTransform;
2016  else
2017    var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
2018  var recordsperpage = 3;
2019  var url;
2020  if ((anOnThisDayIndex) && (anOnThisDayIndex !== '')) {
2021    // make query into "anOnThisDayIndex =* d.m":
2022    var today = new Date();
2023    var month = today.getMonth()+1;
2024    var thisDm = today.getDate()+'.'+month;
2025    query = '{'+anOnThisDayIndex+'}=*{'+thisDm+'}';
2026//    alert(query);
2027    if (linkedData) {
2028      //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+'%26modes_query='+query+'&transform='+summarystyle;
2029            url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+'%26modes_query='+query+'&transform='+summarystyle;
2030      if (this.debug)
2031        alert(url);
2032      jQuery.ajax({ type: "get", dataType: "html", url: url,
2033        success: function(xml, status, xhr) {
2034//        alert('status: '+status+'; text: '+xml);
2035          if (jQuery('#'+name)) {
2036            jQuery('#'+name).html(xml);
2037          } else alert('#'+name+' not found');
2038        }
2039      })
2040    } else {
2041      var sep = this.getSeparator(datasource);
2042      url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+summarystyle+sep+'response=text/html'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
2043      if (targetPage !== '') {
2044        url = url+sep+'param=item_page='+this.pages[targetPage];
2045        if (this.debug)
2046          alert('target page is '+targetPage);
2047      } else if (this.itemPage)
2048        url = url+sep+'param=item_page='+this.pages[this.itemPage];
2049      url = url+sep+'param=datasource='+datasource.name;
2050            url = url+sep+'param=domain=http://'+domain;
2051      url = url+sep+'param=script_folder=http://'+domain+scriptDir;
2052      url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
2053      url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
2054      if (this.debug)
2055        alert(url);
2056      jQuery.ajax({ type: "get", dataType: "html", url: url,
2057        success: function(xml) {
2058//        alert('data: '+xml);
2059//          alert('#'+name);
2060          if (jQuery('#'+name)) {
2061            jQuery('#'+name).html(xml);
2062          } else alert('#'+name+' not found');
2063        }
2064      })
2065    }
2066
2067  }
2068}
2069
2070function displayDetailFn(name, domain, scriptDir, app, file, query, title, datasource, navigator, start, count, linkedData, aTransform, sectionOrder, headingDiv, headingSpan, entryDiv, entrySpan, sortby) {
2071  var summarystyle=this.pluginsurl+'/'+this.browserFolder+'/xslt/'+aTransform;
2072  var recordsperpage = 0 + count; // bug 3365, rbl 10.11.2015
2073  var url;
2074  if (navigator) {
2075    navigator.setup();
2076  } else if (datasource) {
2077    datasource.setup();
2078  } else alert('Error: detail '+name+' has no navigator or data source');
2079  if (linkedData) {
2080        var sep = '%26';
2081    //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?start='+start+'%26modes_query='+query+'%26count='+recordsperpage+'&transform='+aTransform;//this.hitlistXslt;
2082        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/xml/?start='+start+sep+'modes_query='+query+sep+'count='+recordsperpage+sep+'transform='+aTransform;//this.hitlistXslt;
2083        if (sortby !== '')
2084            url = url+sep+'sortby='+sortby;
2085        if (this.itemPage)
2086            url = url+sep+'param=item_page='+this.pages[this.itemPage];
2087        url = url+sep+'param=domain=http://'+domain;
2088        url = url+sep+'param=script_folder=http://'+domain+scriptDir;
2089        url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
2090        url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
2091        if (sectionOrder !== '')
2092            url = url+sep+'param=section_order='+sectionOrder;
2093        if (headingDiv !== '')
2094            url = url+sep+'param=heading_as_div='+headingDiv;
2095        if (headingSpan !== '')
2096            url = url+sep+'param=heading_as_span='+headingSpan;
2097        if (entryDiv !== '')
2098            url = url+sep+'param=entry_as_div='+entryDiv;
2099        if (entrySpan !== '')
2100            url = url+sep+'param=entry_as_span='+entrySpan;
2101        if (linkedData)
2102            url = url+sep+'param=linked_data_url=http://'+domain+'/'+app+'/'+file+'/';
2103    if (this.debug)
2104            alert(url);
2105        var thisNav = navigator;
2106        // N.B. dataType changed to xml to support extraction of position info from result:
2107    jQuery.ajax({ type: "get", dataType: "xml", url: url,
2108      success: function(xml, status, xhr) {
2109//        alert('status: '+status+'; text: '+xml);
2110        if (jQuery('#'+name+'_data')) {
2111          jQuery('#'+name+'_data').html(xmlToString(xml));
2112        } else alert('#'+name+'_data not found');
2113                if (thisNav) {
2114                    // N.B. unary plus to force string value to integer
2115                    thisNav.position = +xml.documentElement.getAttribute('startIndex');
2116                    thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
2117                    thisNav.total = +xml.documentElement.getAttribute('totalResults');
2118                    thisNav.displayDirect();
2119                }
2120                if (modes_browser.customOnLoad) {
2121                    modes_browser.customOnLoad();
2122                }
2123      }
2124    })
2125  } else {
2126    var sep = this.getSeparator(datasource);
2127    url = this.urlBase(domain, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+summarystyle+sep+'response=text/xml'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count='+recordsperpage+sep+'start='+start;
2128        if (sortby !== '')
2129            url = url+sep+'sortby='+sortby;
2130    if (this.itemPage)
2131      url = url+sep+'param=item_page='+this.pages[this.itemPage];
2132        url = url+sep+'param=domain=http://'+domain;
2133    url = url+sep+'param=script_folder=http://'+domain+scriptDir;
2134    url = url+sep+'param=image_folder='+modes_browser.imageDir+datasource.imageDir;
2135    url = url+sep+'param=image_max_size='+modes_browser.imageMaxSize;
2136        if (sectionOrder !== '')
2137            url = url+sep+'param=section_order='+sectionOrder;
2138        if (headingDiv !== '')
2139            url = url+sep+'param=heading_as_div='+headingDiv;
2140        if (headingSpan !== '')
2141            url = url+sep+'param=heading_as_span='+headingSpan;
2142        if (entryDiv !== '')
2143            url = url+sep+'param=entry_as_div='+entryDiv;
2144        if (entrySpan !== '')
2145            url = url+sep+'param=entry_as_span='+entrySpan;
2146    if (this.debug)
2147      alert(url);
2148        var thisNav = navigator;
2149    jQuery.ajax({ type: "get", dataType: "xml", url: url,
2150      success: function(xml) {
2151        if (jQuery('#'+name+'_data')) {
2152          jQuery('#'+name+'_data').html(xmlToString(xml));
2153        } else alert('#'+name+'_data not found');
2154                if (thisNav) {
2155                    // N.B. unary plus to force string value to integer
2156                    thisNav.position = +xml.documentElement.getAttribute('startIndex');
2157                    thisNav.delta = +xml.documentElement.getAttribute('itemsPerPage');
2158                    thisNav.total = +xml.documentElement.getAttribute('totalResults');
2159                    thisNav.displayDirect();
2160                }
2161                if (modes_browser.customOnLoad) {
2162                    modes_browser.customOnLoad();
2163                }
2164      }
2165    })
2166  }
2167}
2168
2169function displayCommentaryFn(name, domain, app, file, query, datasource, navigator, start, targetApp, targetFile, targetIndex, commentXslt, linkedData) {
2170  var recordId;
2171  jQuery('#'+name+'_comments').html('');
2172//  alert('displaying commentary: data source recordids length is '+datasource.recordIds.length);
2173  if (datasource.recordIds.length > 0) {
2174    //recordId = datasource.recordIds[start - 1];
2175        recordId = datasource.recordIds[0];
2176//    alert('Commentary identity is '+recordId);
2177    var input = '<input type="hidden" name="start" value="'+start+'"/>' +
2178        '<input type="hidden" name="recordId" value="'+recordId+'"/>';
2179//      alert(input);
2180    if (jQuery('#'+name+'_inputs')) {
2181//        alert('#'+name+'_inputs found');
2182      jQuery('#'+name+'_inputs').html(input);
2183    } else alert('#'+name+'_inputs not found');
2184//        alert('start is '+start+'; record id array is '+recordIds+'; current record is '+recordId);
2185/*
2186    var thisform = document.forms[name];
2187    targetApp = thisform.elements['commentaryApp'].value;
2188    targetFile = thisform.elements['commentaryFile'].value;
2189    commentXslt = thisform.elements['commentXslt'].value;
2190*/
2191    var targetKey = '';
2192        if (targetIndex == 'Targets') {
2193            if (linkedData)
2194                targetKey = domain + ',';
2195            targetKey = targetKey + app + ',' + file + ',' + recordId;
2196        } else
2197            targetKey = recordId;
2198    var sep = modes_browser.getSeparator(datasource);
2199//    targetUrl = modes_browser.urlBase(modes_browser.domainName)+modes_browser.connection(datasource)+'application='+targetApp+sep+'filename='+targetFile+sep+'query={Targets}={'+targetKey+'}'+sep+'transform='+commentXslt+sep+'target=4'+sep+'level=3'+sep+'count=20';
2200        targetUrl =  this.browser.urlBase(domain, datasource)+this.browser.connection(datasource)+'application='+targetApp+sep+'filename='+targetFile+sep+'query={'+targetIndex+'}={'+targetKey+'}'+sep+'transform='+commentXslt+sep+'target=4'+sep+'level=3'+sep+'count=20';
2201//    alert('record id defined: '+targetUrl);
2202    jQuery.ajax({ type: "get", dataType: "html", url: targetUrl,
2203      success: function(xml) {
2204//        alert('start: '+start+'; url: '+targetUrl+'; comment: '+xml);
2205        jQuery('#'+name+'_comments').html(xml);
2206      }
2207    })
2208  } else {
2209        var sep = this.browser.getSeparator(datasource);
2210    if (linkedData)
2211      //url = 'http://'+this.browser.domainName+this.browser.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/xml/?modes_query='+query+sep+'count='+recordsperpage+sep+'start='+start;
2212            url = 'http://'+this.browser.domainName+this.browser.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/xml/?modes_query='+query+sep+'count='+recordsperpage+sep+'start='+start;
2213    else {
2214            if (datasource.primaryIndex !== '') {
2215                url = this.browser.urlBase(this.browser.domainName, datasource)+this.browser.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'start='+start+sep+'indexname='+datasource.primaryIndex+sep+'target=3'+sep+'level=2';
2216            } else {
2217                url = this.browser.urlBase(this.browser.domainName, datasource)+this.browser.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'start='+start+sep+'target=4'+sep+'level=2';
2218            }
2219    }
2220
2221    //alert('record ids not set up: trying URL '+url);
2222    var recordIds = [];
2223    jQuery.ajax({ type: "get", dataType: "xml", url: url,
2224      success: function(xml) {
2225        var recs = xml.documentElement.getElementsByTagName('record');
2226                var keys = xml.documentElement.getElementsByTagName('key');
2227                if (keys.length > 0) {
2228                    for ( var i = 0; i<keys.length; i++ ) {
2229                        recordIds.push(keys[i].childNodes[0].nodeValue);
2230                    }
2231                } else {
2232                    for ( var i = 0; i<recs.length; i++ ) {
2233                        recordIds.push(recs[i].getAttribute('id'));
2234                    }
2235                }
2236//        alert(recordIds);
2237        recordId = recordIds[0];
2238        var input = '<input type="hidden" name="start" value="'+start+'"/>' +
2239            '<input type="hidden" name="recordId" value="'+recordIds[0]+'"/>';
2240  //      alert(input);
2241        if (jQuery('#'+name+'_inputs')) {
2242  //        alert('#'+name+'_inputs found');
2243          jQuery('#'+name+'_inputs').html(input);
2244        } else alert('#'+name+'_inputs not found');
2245//        alert('start is '+start+'; record id array is '+recordIds+'; current record is '+recordId);
2246/*
2247        var thisform = document.forms[name];
2248        targetApp = thisform.elements['commentaryApp'].value;
2249        targetFile = thisform.elements['commentaryFile'].value;
2250        commentXslt = thisform.elements['commentXslt'].value;
2251 */
2252        var targetKey = '';
2253        if (targetIndex == 'Targets') {
2254                  if (linkedData)
2255                      targetKey = domain + ',';
2256                  targetKey = targetKey + app + ',' + file + ',' + recordId;
2257              } else {
2258                  targetKey = recordId;
2259              }
2260              //  alert('target key = '+targetKey);
2261              targetUrl = modes_browser.urlBase(domain, datasource)+modes_browser.connection(datasource)+'application='+targetApp+sep+'filename='+targetFile+sep+'query={'+targetIndex+'}={'+targetKey+'}'+sep+'transform='+commentXslt+sep+'target=4'+sep+'level=3'+sep+'count=20';
2262//        alert('record id fetched: '+targetUrl);
2263              var commentElt = '#'+name+'_comments';
2264        jQuery.ajax({ type: "get", dataType: "html", url: targetUrl,
2265          success: function(xml) {
2266            //alert('start: '+start+'; url: '+targetUrl+'; comment: '+xml);
2267            if (jQuery(commentElt)) {
2268                            jQuery(commentElt).html(xml);
2269                        } else alert(commentElt+' not found');
2270          }
2271        })
2272      }
2273    })
2274  }
2275}
2276
2277function displayUpdateFn(name, domain, app, file, query, datasource, navigator, start, linkedData) {
2278  modes_edit = new modesEdit(name, domain, query, this.browser, datasource, navigator, start, linkedData);
2279  modes_edit.update = this.update;
2280//  modes_edit.displayEdits(name, domain, app, file, query, datasource, navigator, start, linkedData);
2281//  modes_edit.displayEdits(name, domain, app, file, query, datasource, navigator, start, linkedData);
2282  modes_edit.displayEdits();
2283}
2284
2285function displayFacetsFn(name, domain, scriptDir, app, file, query, facets, facetstyle, linkedData, datasource) {
2286  var url;
2287  var style;
2288    var sep = this.getSeparator(datasource);
2289    var sep = '&';
2290    if (linkedData)
2291        sep = '%26';
2292  if ((facetstyle) && (facetstyle !== ''))
2293    style = facetstyle;
2294  else
2295    style = this.facetDisplayXslt;
2296  if (linkedData)
2297    //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?modes_query='+query+sep+'transform='+style+sep+'count=1'+sep+'param=query='+query;
2298        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+domain+'/'+app+'/'+file+'/search/html/?modes_query='+query+sep+'transform='+style+sep+'count=1'+sep+'param=query='+query;
2299  else
2300    url = 'http://'+domain+scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+query+sep+'transform='+style+sep+'response=text/html'+sep+'target=4'+sep+'level=3'+sep+'nodups=yes';
2301  if (this.hitlistPage)
2302    url = url+sep+'param=page='+this.currentPage;
2303  if ((facets) && (facets !== ''))
2304    url = url+sep+'facets='+facets;
2305  else {
2306    for (var i=0; i<this.facets.length; i++)
2307      url = url+sep+'facet='+this.facets[i];
2308  }
2309    alert('internal script dir: '+this.internalScriptDir+': '+url);
2310  jQuery.ajax({ type: "get", dataType: "html", url: url,
2311    success: function(xml) {
2312//      alert('facet data for '+name+': '+xml);
2313      if (jQuery('#'+name))
2314        jQuery('#'+name).html(xml);
2315    }
2316  })
2317}
2318
2319function displaySearchSummaryFn(aQuery) {
2320    function getKey(s) {
2321        keywordStart = s.lastIndexOf('{', s.length);
2322        keywordEnd = s.lastIndexOf('}', s.length);
2323        return s.substring(keywordStart+1, keywordEnd);
2324    }
2325    function extractKeywords(s, separator) {
2326        searchElts = s.split(' AND ');
2327        outS = '';
2328        for (i=0; i<searchElts.length - 1; i++) {
2329            outS = outS + getKey(searchElts[i]) + separator;
2330        }
2331        return outS + getKey(searchElts[searchElts.length - 1]);
2332    }
2333  function tidySearch(s, format, separator) {
2334        if (format == 'full') {
2335            s = s.replace(/{/g, '');
2336            s = s.replace(/}/g, '');
2337            s = s.replace(/\=\*/g, ' beginning ');
2338            return s.replace(/\=/g, ' equal to ');
2339        } else if (format == 'keywords') {
2340            //alert('keywords!');
2341            return extractKeywords(s, separator);
2342        }
2343  }
2344  if (this.searchSummary) {
2345        format = jQuery('#'+this.searchSummary).attr('format');
2346        separator = jQuery('#'+this.searchSummary).attr('separator');
2347        leadin = jQuery('#'+this.searchSummary).attr('leadin');
2348        nosearch = jQuery('#'+this.searchSummary).attr('nosearch');
2349    if (aQuery.trim() !== '')
2350      jQuery('#'+this.searchSummary).html('<div class="displaySearchSummary">'+leadin+tidySearch(aQuery, format, separator)+'</div>');
2351    else
2352      jQuery('#'+this.searchSummary).html('<div class="displaySearchSummary">'+nosearch+'</div>');
2353  }
2354}
2355
2356function displaySimpleSearchFn() {
2357}
2358
2359// The map checkboxes will each relate to a specific data source and map layer. The problem
2360// is that the map layers are populated asynchronously as the various callbacks return; often
2361// in a different order to that in which they were called. So each map layer record needs to
2362// contain information about the data source to which it relates.
2363function addMapCheckboxesFn() {
2364//  var thisapp;
2365//  var thisfile;
2366  var checkboxes = '<div class="map_options">';
2367  var colour;
2368  for ( var i=0; i<this.modesMap.layers.length; i++ ) {
2369    colour = this.modesMap.markerColours[i];
2370    checkboxes = checkboxes + '<div class="option"><input type="checkbox" id="'+this.modesMap.formname+
2371      '_option_'+this.modesMap.layers[i].filename+'" name="'+this.modesMap.formname+'_option_'+
2372      this.modesMap.layers[i].filename+'" value="'+this.modesMap.layers[i].filename+
2373      '" checked="checked" onclick="modes_browser.toggleMapLayer('+i+', \''+
2374      this.modesMap.formname+'_option_'+this.modesMap.layers[i].filename+'\');"> '+
2375      this.modesMap.layers[i].filename+' ('+this.modesMap.layers[i].placemarkCount+')</input><span><img src="./wp-content/plugins/'+this.browserFolder+'/js/OpenLayers-2.12/img/marker-'+colour+'.png" /></span></div>';
2376  }
2377  checkboxes = checkboxes + '</div>';
2378//  alert(checkboxes);
2379  if (jQuery('#'+this.modesMap.formname+'_prepicker')) {
2380    jQuery('#'+this.modesMap.formname+'_prepicker').html(checkboxes);
2381  }
2382  if (jQuery('#'+this.modesMap.formname+'_postpicker')) {
2383    jQuery('#'+this.modesMap.formname+'_postpicker').html(checkboxes);
2384  }
2385}
2386
2387function displayMapFn(name, domain, scriptDir, app, file, picker, targetPage, pageurl, datasource) {
2388//  alert('displaying map: '+name);
2389  if (this.modesQuery !== '') {
2390        var sep = this.getSeparator(datasource);
2391        if ((picker) && (picker !== 'none'))
2392    {
2393      var thisapp;
2394      var thisfile;
2395      var thisdomain;
2396      var thislocation;
2397      var thistransform;
2398      for ( var i=0; i<this.datasources.length; i++) {
2399        thisapp = this.datasource[this.datasources[i]]['application'];
2400        thisfile = this.datasource[this.datasources[i]]['filename'];
2401        thisdomain = this.datasource[this.datasources[i]]['domain'];
2402        thislocation = this.datasource[this.datasources[i]]['transformLocation'];
2403        thistransform = this.datasource[this.datasources[i]]['mapXslt'];
2404        var transform;
2405        var url;
2406        if ((thistransform) && (thistransform !== ''))
2407          transform = thistransform;
2408        else
2409          transform = this.modesMapXslt;
2410        if (this.debug) {
2411                    alert(transform+' used for '+thisfile+'; location is '+thislocation+' ; domain is '+thisdomain);
2412                }
2413        if (thisdomain !== '') {
2414                    sep = '%26';
2415          if (thislocation == 'local')
2416            //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+sep+'count=100'+sep+'pageurl='+pageurl;
2417                        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+sep+'count=100'+sep+'pageurl='+pageurl;
2418          else
2419            //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+sep+'transform='+transform+sep+'count=100'+sep+'pageurl='+pageurl;
2420                        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+sep+'transform='+transform+sep+'count=100'+sep+'pageurl='+pageurl;
2421          //url = url.replace(/&/g, '%26');
2422        } else {
2423          url = 'http://'+domain+scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+thisapp+sep+'filename='+thisfile+sep+'query='+this.modesQuery+sep+'transform='+transform+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count=100'+sep+'url='+pageurl;
2424//          alert('simple CGI call: '+url);
2425        }
2426//                alert('map URL is '+url);
2427// Problem is that variables like thisdomain and transform have taken on new values
2428// by the time that the success function is evaluated.  So we need to test the XML
2429// itself to decide whether to transform it.  Pick up the application and file from
2430// the XML and go back to the datasource array for guidance:
2431        jQuery.ajax({ type: "get", dataType: "xml", url: url,
2432          success: function(xml) {
2433            returnedApp = xml.documentElement.getAttribute('application');
2434            returnedFile = xml.documentElement.getAttribute('filename');
2435//            alert('returned app and file are '+returnedApp+', '+returnedFile);
2436            returnedSource = modes_browser.findDatasource(returnedApp, returnedFile);
2437            if (returnedSource) {
2438//              alert('found datasource for '+returnedFile);
2439              returnedDomain = returnedSource['domain'];
2440              returnedLocation = returnedSource['transformLocation'];
2441              if ((returnedDomain) && (returnedDomain !== '') && (returnedLocation == 'local')) {
2442//                alert('transforming returned XML to KML for '+returnedFile);
2443                returnedTransform = returnedSource['mapXslt'];
2444                var xml2 = modes_browser.transformXml(xml, returnedTransform);
2445                if (xml2)
2446                  modes_browser.addLayerFromHtml(xml2);
2447              } else {
2448//                alert('adding KML directly for '+returnedFile);
2449                modes_browser.addLayerFromHtml(xml);
2450              }
2451            }
2452            modes_browser.addMapCheckboxes();
2453          }
2454        });
2455      }
2456    } else {
2457      var url = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+this.modesQuery+sep+'transform='+this.modesMapXslt+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count=100'
2457+sep+'url='+pageurl;
2458      if (targetPage !== '') {
2459        url = url+sep+'param=item_page='+this.pages[targetPage];
2460      }
2461            //if ((this.scriptDomain) && (this.scriptDomain !== ''))
2462            //    url = url.replace(/&/g, '%26');
2463      if (this.debug) {
2464                alert('Looking for '+url);
2465            }
2466//            alert('map URL is '+url);
2467      jQuery.ajax({ type: "get", dataType: "xml", url: url,
2468        success: function(xml) {
2469            if (this.debug) {
2470                        alert('Found '+xml);
2471                    }
2472          modes_browser.addLayerFromHtml(xml);
2473        }
2474      })
2475    }
2476  } // else alert('no query for map');
2477}
2478
2479// need to check state of timeline layer toggle controls (which need creating)
2480// and only display those which are set to 'on'.  Then the onclick handler
2481// needs to redisplay the whole timeline control, unless we can find a 'layer'
2482// concept which can be switched on and off.
2483// Might be an idea to create a Modes timeline object, to make things clearer.
2484// Could experiment with the 'colour' property for timeline entries as well.
2485function addTimelineCheckboxesFn() {
2486  var checkboxes = '<div class="map_options">';
2487  var colour;
2488  for ( var i=0; i<this.datasources.length; i++) {
2489    thisapp = this.datasource[this.datasources[i]]['application'];
2490    thisfile = this.datasource[this.datasources[i]]['filename'];
2491    if (!this.datasource[this.datasources[i]]['timeline']) {
2492//      alert('setting timeline var for '+i);
2493      this.datasource[this.datasources[i]]['timeline'] = true;
2494    }
2495    checkboxes = checkboxes + '<div class="option"><input type="checkbox" id="'+this.formname+
2496      '_timeline_option_'+thisfile+'" name="'+this.formname+'_timeline_option_'+
2497      thisfile+'" value="'+thisfile+
2498      '" checked="modes_browser.datasource[modes_browser.datasources['+i+']][\'timeline\']" '+
2499      ' onclick="modes_browser.datasource[modes_browser.datasources['+i+']][\'timeline\'] = this.checked; modes_browser.refreshTimeline();"> '+
2500      thisfile+'</input></div>';
2501  }
2502  checkboxes = checkboxes + '</div>';
2503  if (jQuery('#'+this.formname+'_timeline_prepicker')) {
2504    jQuery('#'+this.formname+'_timeline_prepicker').html(checkboxes);
2505  }
2506  if (jQuery('#'+this.formname+'_timeline_postpicker')) {
2507    jQuery('#'+this.formname+'_timeline_postpicker').html(checkboxes);
2508  }
2509}
2510
2511function displayTimelineFn(name, domain, scriptDir, app, file, picker, targetPage, pageurl, datasource) {
2512  if (!this.eventSource)
2513    this.eventSource = new Timeline.DefaultEventSource();
2514  this.eventSource.clear();
2515  var url;
2516  if (this.modesQuery !== '') {
2517    if (picker == 'none') {
2518            sep = this.getSeparator(datasource);
2519      url = this.urlBase(this.domainName, datasource)+this.connection(datasource)+'application='+app+sep+'filename='+file+sep+'query='+this.modesQuery+sep+'transform='+this.modesTimelineXslt+sep+'target=4'+sep+'level=3'+sep+'nodups=yes'+sep+'count=1000'+sep+'url='+pageurl;
2520      if (targetPage !== '') {
2521        url = url+sep+'param=item_page='+this.pages[targetPage];
2522      }
2523            //if ((this.scriptDomain) && (this.scriptDomain !== ''))
2524            //    url = url.replace(/&/g, '%26');
2525//      alert(this.domainName+'; '+url);
2526      jQuery.ajax({ type: "get", dataType: "xml", url: url,
2527        success: function(xml) {
2528          modes_browser.updateTimeline(xml, url);
2529//          modes_browser.addTimelineCheckboxes();
2530        }
2531      })
2532    } else {
2533      modes_browser.addTimelineCheckboxes();
2534      for ( var i=0; i<this.datasources.length; i++) {
2535        if (this.datasource[this.datasources[i]]['timeline'] == true) {
2536          var thisapp = this.datasource[this.datasources[i]]['application'];
2537          var thisfile = this.datasource[this.datasources[i]]['filename'];
2538          var thisdomain = this.datasource[this.datasources[i]]['domain'];
2539          var thislocation = this.datasource[this.datasources[i]]['transformLocation'];
2540          var thistransform = this.datasource[this.datasources[i]]['timelineXslt'];
2541          var transform;
2542          if ((thistransform) && (thistransform !== ''))
2543            transform = thistransform;
2544          else
2545            transform = this.modesTimelineXslt;
2546//          alert(transform+' used for '+thisfile);
2547          if (thisdomain !== '') {
2548            if (thislocation == 'local')
2549              //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&count=100&pageurl='+pageurl;
2550                            url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&count=100&pageurl='+pageurl;
2551            else
2552              //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&transform='+transform+'&count=100&pageurl='+pageurl;
2553                            url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&transform='+transform+'&count=100&pageurl='+pageurl;
2554            url = url.replace(/&/g, '%26');
2555//            alert(url);
2556          } else
2557            url = 'http://'+domain+scriptDir+'modescgi.exe/?'+this.connection(datasource)+'application='+thisapp+'&filename='+thisfile+'&query='+this.modesQuery+'&transform='+transform+'&target=4&level=3&nodups=yes&count=100&url='+pageurl;
2558          jQuery.ajax({ type: "get", dataType: "xml", url: url,
2559            success: function(xml) {
2560              if ((thisdomain !== '') && (thislocation == 'local')) {
2561//                alert('about to transform '+thisfile+' using '+transform);
2562                var xml2 = modes_browser.transformXml(xml, transform);
2563                if (xml2) {
2564//                  alert(xml2.documentElement.xml);
2565                  modes_browser.updateTimeline(xml2, url);
2566                }
2567              } else {
2568                modes_browser.updateTimeline(xml, url);
2569              }
2570            }
2571          })
2572        }
2573      }
2574//    alert(url);
2575    }
2576  }
2577}
2578
2579// refreshTimeline: triggered when a timeline selection checkbox is clicked on. Re-displays timeline info for
2580// all selected data sources.
2581function refreshTimelineFn() {
2582  this.eventSource.clear();
2583  var pageurl = 'http://'+this.imageurl+'/?page_id='+this.pages[this.hitlistPage]+'&modes_query='+this.modesQuery;
2584  if (this.modesQuery !== '') {
2585    for ( var i=0; i<this.datasources.length; i++) {
2586      if (this.datasource[this.datasources[i]]['timeline'] == true) {
2587//        alert('data source timeline '+i+' true');
2588        var thisapp = this.datasource[this.datasources[i]]['application'];
2589        var thisfile = this.datasource[this.datasources[i]]['filename'];
2590        var thisdomain = this.datasource[this.datasources[i]]['domain'];
2591        var thislocation = this.datasource[this.datasources[i]]['transformLocation'];
2592        var thistransform = this.datasource[this.datasources[i]]['timelineXslt'];
2593        var transform;
2594        if ((thistransform) && (thistransform !== ''))
2595          transform = thistransform;
2596        else
2597          transform = this.modesTimelineXslt;
2598        if (thisdomain !== '') {
2599          if (thislocation == 'local')
2600            //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&count=100&pageurl='+pageurl;
2601                        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&count=100&pageurl='+pageurl;
2602          else
2603            //url = 'http://'+this.domainName+this.scriptDir+'cgiforwarder.exe/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&transform='+transform+'&count=100&pageurl='+pageurl;
2604                        url = 'http://'+this.domainName+this.scriptDir+this.forwarder+'/?url=http://'+thisdomain+'/'+thisapp+'/'+thisfile+'/search/xml/?modes_query='+this.modesQuery+'&transform='+transform+'&count=100&pageurl='+pageurl;
2605          url = url.replace(/&/g, '%26');
2606//            alert(url);
2607        } else
2608          url = 'http://'+this.domainName+this.scriptDir+'modescgi.exe/?'+this.connection(this.datasource[this.datasources[i]])+'application='+thisapp+'&filename='+thisfile+'&query='+this.modesQuery+'&transform='+transform+'&target=4&level=3&nodups=yes&count=100&url='+pageurl;
2609//        alert(url);
2610        jQuery.ajax({ type: "get", dataType: "xml", url: url,
2611          success: function(xml) {
2612            if ((thisdomain !== '') && (thislocation == 'local')) {
2613              var xml2 = modes_browser.transformXml(xml, transform);
2614              if (xml2) {
2615//                alert(xml2.documentElement.nodeName);
2616                modes_browser.updateTimeline(xml2, url);
2617              }
2618            } else {
2619              modes_browser.updateTimeline(xml, url);
2620            }
2621          }
2622        })
2623      } // else alert('data source timeline '+i+' false');
2624    }
2625  }
2626}
2627
2628function toggleMapLayerFn(i, name) {
2629  var elt = document.getElementById(name);
2630  if (elt) {
2631    if (elt.checked)
2632      this.modesMap.layerShow(i);
2633    else
2634      this.modesMap.layerHide(i);
2635  } else alert('element '+name+' not found');
2636}
2637
2638/* -------------------------------------------------------- */
2639
2640// Routines used by the page itself:
2641
2642function todayForModes() {
2643    var d = new Date();
2644    var day = d.getDate();
2645    var actualMonth = d.getMonth()+1;
2646    var y = d.getFullYear();
2647    return day+'.'+actualMonth+'.'+y;
2648}
2649
2650function submitCommentsFn(datasource, name, completionmessage) {
2651  //alert('submitting comments for data source '+datasource+'; name='+name);
2652  var thisform = document.forms[datasource+'_'+name+'_commentary'];
2653    if (!thisform)
2654        alert('form '+datasource+'_'+name+'_commentary'+' not found');
2655  var commentTarget = this.datasource[datasource];
2656/*
2657  alert('App: '+thisform.elements['commentaryApp'].value+
2658    '; file: '+thisform.elements['commentaryFile'].value+
2659    ';
2659 record id: '+thisform.elements['recordId'].value);
2660//    '; comment: '+thisform.elements[form+'_comment'].value);
2661*/
2662    var sep = this.getSeparator(datasource);
2663    var url = this.urlBase(this.domainName, datasource, 'modespost.exe')+this.connection(datasource)+'application='+thisform.elements['commentaryApp'].value+
2664        sep+'filename='+thisform.elements['commentaryFile'].value;
2665    /* // attempt to have application-specific URLs for submission. May still be required, e.g. if creating identities for comment records:
2666    switch (thisform.elements['commentaryApp'].value) {
2667        case 'Blogs':
2668            var url = this.urlBase(this.domainName, datasource, 'modespost.exe')+this.connection(datasource)+'application='+thisform.elements['commentaryApp'].value+
2669                sep+'filename='+thisform.elements['commentaryFile'].value;
2670            break;
2671        case 'bugs':
2672            var url = this.urlBase(this.domainName, datasource, 'modesupdate.exe')+this.connection(datasource)+'action=merge'+sep+'username=bugreporter'+sep+'application='+thisform.elements['commentaryApp'].value+
2673                sep+'filename='+thisform.elements['commentaryFile'].value+sep+'indexname=_id'+sep+'recordkey='+thisform.elements['recordId'].value;
2674            break;
2675    }
2676    */
2677
2678  //var keywords = thisform.elements[datasource+'_keywords'].value.split(',');
2679  var keys = '';
2680    var commentRecord;
2681    var currentDate = todayForModes();
2682    //alert(thisform.elements['commentTemplate'].value);
2683    if (thisform.elements['commentTemplate'].value !== '') {
2684        //alert(thisform.elements['commentTemplate']);
2685        var commentRecord = thisform.elements['commentTemplate'].value;
2686        var fieldCount = parseInt(thisform.elements['fields'].value);
2687        //alert('field count is '+fieldCount);
2688        for (var i=0; i<fieldCount; i++) {
2689            var j = i+1;
2690            //alert('updating field '+j);
2691            commentRecord = commentRecord.split('\{'+j+'\}').join(thisform.elements[name+'_'+j].value);
2692        }
2693        if (thisform.elements['recordId']) {
2694            commentRecord = commentRecord.replace(/\{id\}/g, thisform.elements['recordId'].value);
2695        }
2696        commentRecord = commentRecord.replace(/\{user\}/g, thisform.elements['commentaryUser'].value);
2697        commentRecord = commentRecord.replace(/\{email\}/g, thisform.elements['commentaryEmail'].value);
2698        commentRecord = commentRecord.replace(/\{date\}/g, currentDate);
2699        commentRecord = commentRecord.replace(/\{isodate\}/g, d.toISOString());
2700        //alert(commentRecord);
2701        url = url + sep + 'xmlrecord=' + commentRecord;
2702        //alert(url);
2703    } else {
2704        switch (thisform.elements['commentaryApp'].value) {
2705            case 'Blogs':
2706                for (var i=0; i<keywords.length; i++) {
2707                    keys = keys + '<Subject>'+keywords[i].trim()+'</Subject>';
2708                }
2709                commentRecord = '<Blog><Entry><Title>'+thisform.elements[datasource+'_heading'].value+'</Title>'+
2710                '<Content>'+thisform.elements[datasource+'_comment'].value+'</Content></Entry>'+
2711                '<Author><UserName>'+thisform.elements['commentaryUser'].value+'</UserName>'+
2712                '<FullName>'+thisform.elements['commentaryFullname'].value+'</FullName>'+
2713                '<Email>'+thisform.elements['commentaryEmail'].value+'</Email>'+
2714                '</Author>'+
2715                '<DateTime>'+currentDate+'</DateTime><Status>pending</Status>'+
2716                '<TargetRecord><Domain>'+commentTarget['domain']+'</Domain><Application>'+commentTarget['application']+
2717                '</Application><Filename>'+commentTarget['filename']+
2718                '</Filename><RecordId>'+thisform.elements['recordId'].value+'</RecordId></TargetRecord>'+keys+'</Blog>';
2719                url = url + sep + 'xmlrecord=' + commentRecord;
2720                break;
2721            case 'bugs':
2722                // could get record from ID: {_id}={[id]} as AJAX request, and then extract primary key
2723                if (thisform.elements[datasource+'_keywords'].value.trim() !== '') {
2724                    keys = '<p>Keywords: ';
2725                    keys = keys + keywords[0].trim();
2726                    for (var i=1; i<keywords.length; i++) {
2727                        keys = keys +'; '+keywords[i].trim();
2728                    }
2729                    keys = keys + '</p>';
2730                }
2731                commentRecord = '<report><?MXMLparent '+thisform.elements['recordId'].value+'?>'+
2732                '<report_type>response</report_type><summary>'+thisform.elements[datasource+'_heading'].value+'</summary>'+
2733                '<response><p>'+thisform.elements[datasource+'_comment'].value+'</p>'+keys+
2734                '<aut>
2734'+thisform.elements['commentaryUser'].value+'</aut>'+
2735                '<date>'+currentDate+'</date>'+
2736                '</response></report>';
2737                url = url + sep + 'xmlrecord=' + commentRecord;
2738                break;
2739        }
2740    }
2741
2742  //alert(url);
2743  jQuery.ajax({ type: "get", dataType: "html", url: url,
2744    success: function(html) {
2745            alert(completionmessage);
2746            window.location.reload();
2747    }
2748  })
2749}
2750function xml2Str(xmlNode) {
2751    try {
2752        // Gecko- and Webkit-based browsers (Firefox, Chrome), Opera.
2753        return (new XMLSerializer()).serializeToString(xmlNode);
2754    }
2755    catch (e) {
2756        try {
2757            // Internet Explorer.
2758            return xmlNode.xml;
2759        }
2760        catch (e) {
2761            //Other browsers without XML Serializer
2762            alert('Xmlserializer not supported');
2763        }
2764    }
2765    return false;
2766}
2767
2768function resolveDataPatternFn(s) {
2769    if (s == '$$$date$$$') {
2770        return todayForModes();
2771    } else if (s.startsWith('$$$query=')) {
2772        var query= s.substr(9, s.length-12);
2773        var q = this.modesQuery;
2774        var value = q.substr(q.indexOf('{'+query+'}')+query.length+2);
2775        value = value.substr(value.indexOf('{')+1, value.indexOf('}')-value.indexOf('{')-1);
2776    //alert('query is '+query+'; value is '+value);
2777        return value;
2778    } else {
2779        return s;
2780    }
2781}
2782
2783function addSelectedRecordFn(elt, datasource, controlName) {
2784    var control = this.findControl(controlName);
2785    var source = this.datasource[datasource];
2786    // need to grab the application and filename from the datasource:
2787    var app=source['application'];
2788    var file=source['filename'];
2789    var sep=this.getSeparator(datasource);
2790    var url = this.urlBase(this.domainName, datasource, 'getConstant.exe')+this.connection(datasource)+'application='+app+sep+'name='+control['elementgroup'];
2791    // get the element group, c.f.:
2792    // http://light.demon.co.uk/scripts/getConstant.exe/?transport=tcpip&serverName=marmoset&application=Object&name=Object%20location%20-%20home
2793    var browser = this;
2794    jQuery.ajax({ type: "get", dataType: "xml", url: url,
2795        success: function(xml) {
2796            // update it by applying the XPath expressions and associated data values
2797            for (var i=1; i<=control['mergecount']; i++) {
2798                var eltText = browser.resolveDataPattern(control['mergedata_'+i]);
2799                var elts = xml.evaluate(control['mergexpath_'+i], xml, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
2800                var thisElt = elts.iterateNext();
2801                if (thisElt) {
2802                    $(thisElt).text(eltText);
2803                }
2804            }
2805            // submit it as an update to the specified Modes record, c.f.:
2806            // http://localhost/scripts/modesUpdate.exe/?action=merge&transport=tcpip&serverName=localhost&username=richard&application=Object&filename=Ditchling&indexname=Identity%20number&recordkey=DITLM:1994.2010.2e&xml=%3CObjectLocation%20elementtype=%22home%20location%22%3E%3CLocation%3ECollection%20store,%20rack%201,%20bay%202,%20shelf%204%3C/Location%3E%3CDate%3E%3CDateBegin%3E16.12.2015%3C/DateBegin%3E%3CDateEnd/%3E%3CNote/%3E%3C/Date%3E%3C/ObjectLocation%3E
2807            var datasource = browser.datasource;
2808            updateUrl = browser.urlBase(browser.domainName, datasource, 'modesUpdate.exe')+browser.connection(datasource);
2809            updateUrl = updateUrl + 'action=merge&username=richard'+'&application='+app+'&filename='+file;
2810            updateUrl = updateUrl + '&indexname=Identity number&recordkey='+$('#'+elt).val();
2811            updateUrl = updateUrl + '&xml='+xml2Str(xml);
2812      //alert(updateUrl);
2813            jQuery.ajax({ type: "get", dataType: "html", url: updateUrl,
2814                success: function(html) {
2815                    //alert('Update succeeded: '+xml);
2816                    document.location.reload(false);
2817                },
2818                failure: function(html) {
2819                    alert('Update failed: '+html);
2820                }
2821            })
2822        }
2823    })
2824}
2825
2826function toggleFilterFn(aName) {
2827    var aFilter = this.findFilter(aName);
2828    aFilter.active = (!aFilter.active);
2829//    alert('filter '+aName+' now '+aFilter.active);
2830}
2831
2832function updateQueryWithFiltersFn() {
2833    var s = this.modesQuery;
2834    for ( var i=0; i<this.filters.length; i++) {
2835        if (this.filters[i].active) {
2836            if (s !== '') {
2837                s = s + ' AND (' + this.filters[i].modesQuery + ')';
2838            } else {
2839                s = this.filters[i].modesQuery;
2840            }
2841
2842        }
2843    }
2844    this.filteredQuery = s;
2845}
2846
2847function addInputsFn(xml, controlName) {
2848  var aControl = this.findControl(controlName);
2849  datasource = aControl.datasource;
2850  var mergeValue;
2851  for (var i = 1; i <= aControl['mergecount']; i++) {
2852    var elts = xml.evaluate("//div[@class='record']", xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
2853    var anXpath = xml.createElement('input');
2854    anXpath.setAttribute('type', 'hidden');
2855    anXpath.setAttribute('name', 'mergexpath_'+i);
2856    anXpath.setAttribute('value', aControl['mergexpath_'+i]);
2857    xml.documentElement.insertBefore(anXpath, xml.documentElement.firstElementChild);
2858    mergeValue = aControl['mergedata_'+i];
2859    if (mergeValue == '$$$user$$$') {
2860      if (!this.username) {
2861        var anElt = xml.createElement('input');
2862        anElt.setAttribute('name', 'mergedata_'+i);
2863        var aLabel = xml.createElement('label');
2864        aLabel.setAttribute('for', 'mergedata_'+i);
2865        $(aLabel).text('User name');
2866        xml.documentElement.insertBefore(anElt, xml.documentElement.firstElementChild);
2867        xml.documentElement.insertBefore(aLabel, anElt);
2868      }
2869    } else if ((mergeValue) && (mergeValue == ('$$$selectrecord$$$'))) {
2870      //alert('Checkbox found');
2871      for (var j=0; j < elts.snapshotLength; j++) {
2872        var aCheckbox = xml.createElement('input');
2873        aCheckbox.setAttribute('type', 'checkbox');
2874        aCheckbox.setAttribute('name', 'mergedata_'+i+'_'+j);
2875        recordElt = elts.snapshotItem(j);
2876        recordChildElt = recordElt.firstChild;
2877        recordChildElt.insertBefore(aCheckbox, recordChildElt.firstElementChild);
2878        if (recordElt.attributes.getNamedItem('id')) {
2879          var anIdentity = xml.createElement('input');
2880          anIdentity.setAttribute('type', 'hidden');
2881          anIdentity.setAttribute('name', 'mergeidentity_'+i+'_'+j);
2882          anIdentity.setAttribute('value', recordElt.attributes.getNamedItem('id').nodeValue);
2883          recordChildElt.insertBefore(anIdentity, recordChildElt.firstElementChild);
2884        }
2885      }
2886    } else if ((mergeValue) && (mergeValue.startsWith('$$$checkbox='))) {
2887      var checkboxName= mergeValue.substr(12, mergeValue.length-15);
2888      for (var j=0; j < elts.snapshotLength; j++) {
2889        var aLabel = xml.createElement('label');
2890        aLabel.setAttribute('for', 'mergedata_'+i+'_'+j);
2891        $(aLabel).text(checkboxName);
2892        var aText = xml.createElement('input');
2893        aText.setAttribute('type', 'checkbox');
2894        aText.setAttribute('name', 'mergedata_'+i+'_'+j);
2895        recordElt = elts.snapshotItem(j);
2896        recordElt.appendChild(aLabel);
2897        recordElt.appendChild(aText);
2898      }
2899    } else if ((mergeValue) && (mergeValue.startsWith('$$$text='))) {
2900      //alert('Text input found');
2901      var textName= mergeValue.substr(8, mergeValue.length-11);
2902      for (var j=0; j < elts.snapshotLength; j++) {
2903        var aLabel = xml.createElement('label');
2904        aLabel.setAttribute('for', 'mergedata_'+i+'_'+j);
2905        $(aLabel).text(textName);
2906        var aText = xml.createElement('input');
2907        aText.setAttribute('type', 'text');
2908        aText.setAttribute('name', 'mergedata_'+i+'_'+j);
2909        recordElt = elts.snapshotItem(j);
2910        recordElt.appendChild(aLabel);
2911        recordElt.appendChild(aText);
2912      }
2913    }
2914  }
2915  var submitButton = xml.createElement('input');
2916  submitButton.setAttribute('type', 'button');
2917  submitButton.setAttribute('value', 'Update records');
2918  submitButton.setAttribute('onclick', 'modes_browser.updateSelectedRecords(\''+datasource+'\', \''+controlName+'\');');
2919  xml.documentElement.appendChild(submitButton);
2920  return xml;
2921}
2922
2923function updateRecordsFn(controlName) {
2924  // find specified control; iterate through control updates, picking up corresponding form values;
2925  // create updates for specified records
2926  var aControl = this.findControl(controlName);
2927  var thisForm = document.forms[controlName+'_form'];
2928  //alert(aControl);
2929  //alert(thisForm);
2930  var hitCount;
2931  if (thisForm.elements['hitcount'])
2932    hitCount = thisForm.elements['hitcount'].value;
2933  for (var i=0; i<aControl['files'].length; i++) {
2934    var fileControl = this.findControl(aControl['files'][i]);
2935    //var aProc = this.findControl(aControl['procedure']);
2936    //alert(fileControl);
2937    //alert(aProc);
2938    if (!fileControl) { // || !aProc) {
2939      continue;
2940    }
2941    var aDatasource = this.datasource[fileControl['datasource']];
2942    var aFile = aDatasource['filename'];
2943    var anApp = aDatasource['application'];
2944    var anAction = fileControl['action'];
2945    var eltGroup = fileControl['elementgroup'].replace(/ /g, '+');
2946    var fields = aControl['fields'];
2947    //datasource = this.findDatasource(anApp, aFile);
2948    if (aDatasource) {
2949      var sep = this.getSeparator(aDatasource);
2950    } else
2951      var sep = '&';
2952    //alert(anAction);
2953    switch (anAction) {
2954      case 'create':
2955        // need to load an element group which is to be inserted into the procedure record.
2956        // This is where the data values will be inserted (not below).  There may need to be
2957        // some actual data inserted from the source object record, as well as form values.
2958        // Need to define (and implement) a strategy for assigning an identity to the newly-created
2959        // procedure record.  Also need functionality to calculate the number of object records being
2960        // listed in the procedure record (which is easy so long as every source record generates an
2961        // entry in the result).
2962        var browser = this;
2963        var template = fileControl['template'];
2964        //var eltgroup = fileControl['elementgroup'];
2965        var recordxpath = fileControl['recordxpath'];
2966        var idprefix = fileControl['identityprefix'];
2967        var identityxpath = fileControl['identityxpath'];
2968        var primaryindex = fileControl['primaryindex'];
2969        var url = this.urlBase(this.domainName, aDatasource, 'getConstant.exe') + this.connection(aDatasource) + 'application=' + anApp + sep + 'name=' + eltGroup;
2970        var templateurl = this.urlBase(this.domainName, aDatasource, 'getConstant.exe') + this.connection(aDatasource) + 'application=' + anApp + sep + 'type=template' + sep + 'name=' + template;
2971        //alert(recordurl);
2972        jQuery.ajax({
2973          type: "get", async: false, dataType: "xml", url: url,
2974          success: function (xml1) {
2975            var eachRecord = xml1;
2976            jQuery.ajax({
2977              type: "get", async: false, dataType: "xml", url: templateurl,
2978              success: function (xml2) {
2979                var baseRecord = xml2;
2980                //alert('Template: '+xmlToString(baseRecord));
2981                //alert('Elt group: '+xmlToString(eachRecord));
2982                for (var j=0; j<fields.length; j++) {
2983                  if (fields[j] && fields[j]['type'] == 'update' && fields[j]['datatoupdate'].contains('template')) {
2984                    browser.updateXmlWithValues2(fileControl['datasource'], aControl, fileControl['name'], thisForm, baseRecord, 0, j);
2985                  }
2986                }
2987                for (var k=0; k<hitCount; k++) {
2988                  var copyEltGroup = eachRecord.cloneNode(true);
2989                  for (var l=0; l<fields.length; l++) {
2990                    if (fields[l] && fields[l]['type'] == 'update' && fields[l]['datatoupdate'].contains('elementgroup')) {
2991                      browser.updateXmlWithValues2(fileControl['datasource'], aControl, fileControl['name'], thisForm, copyEltGroup, k, l);
2992                    }
2993                  }
2994                  // find insertion point for records (recordxpath property), and append this updated record:
2995                  var insertionPoint = baseRecord.evaluate(recordxpath, baseRecord, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
2996                  if (insertionPoint.snapshotLength > 0) {
2997                    thisElt = insertionPoint.snapshotItem(0);
2998                    if (thisElt) {
2999                      thisElt.appendChild(copyEltGroup.documentElement);
3000                    }
3001
3002                  }
3003                }
3004                //alert(xmlToString(baseRecord));
3005                var sep = browser.getSeparator(aDatasource);
3006                var postUrl = browser.urlBase(browser.domainName, aDatasource, 'modespost.exe')+
3007                  browser.connection(aDatasource)+'application='+anApp+
3008                  sep+'filename='+aFile+sep+'idprefix='+idprefix+sep+'identityxpath='+identityxpath+
3009                  sep+'primaryindex='+primaryindex+sep+'autoinc=yes';
3010                //postUrl = postUrl + sep + 'xmlrecord=' +xmlToString(baseRecord);
3011                //alert(postUrl);
3012                jQuery.ajax({
3013                  type: "post", async: false, dataType: "xml", processData: false, data: baseRecord, url: postUrl,
3014                  success: function(xml) {
3015                    //alert('post result: '+xml.documentElement.textContent);
3016                    aControl['procedureidentity'] = xml.documentElement.textContent;
3017                    //alert(fileControl['procedureidentity']);
3018                  }
3019                })
3020              }
3021            });
3022          }
3023        });
3024        break;
3025      case 'update':
3026        // this code needs updating. Need to remember the identity of the procedure record created above
3027        // (aControl['procedureidentity']), and then insert this into the cross-reference structure.
3028        // This implies that 'create' actions must always precede 'update' actions within a procedure
3029        var identityfield = fileControl['identityfield'];
3030        var identityxpath = fileControl['identityxpath'];
3031        var procedureidentity = aControl['procedureidentity'];
3032        var url = this.urlBase(this.domainName, aDatasource, 'getConstant.exe') + this.connection(aDatasource) + 'application=' + anApp + sep + 'name=' + eltGroup;
3033        var browser = this;
3034        var doc = document;
3035        var fields = fields;
3036        //alert(procedureidentity);
3037        //alert('about to submit '+url);
3038        jQuery.ajax({
3039          type: "get", async: false, dataType: "xml", url: url,
3040          success: function (xml) {
3041            var mergeXml = '<records>';
3042            for (var k=0; k<hitCount; k++) {
3043              mergeXml = mergeXml + '<record primary-key="'+thisForm.elements['mergeidentity_'+k].value+'">'
3044              var copyEltGroup = xml.cloneNode(true);
3045              for (var l=0; l<fields.length; l++) {
3046                if (fields[l] && fields[l]['type'] == 'update' && fields[l]['datatoupdate'].contains('objectlink')) {
3047                  browser.updateXmlWithValues2(fileControl['datasource'], aControl, fileControl['name'], thisForm, copyEltGroup, k, l);
3048                }
3049              }
3050              mergeXml = mergeXml + xmlToString(copyEltGroup.documentElement);
3051              mergeXml = mergeXml + '</record>';
3052            }
3053            mergeXml = mergeXml + '</records>';
3054            //alert(mergeXml);
3055            // now submit mergeXml as an update to the specified Modes file
3056            updateUrl = browser.urlBase(browser.domainName, aDatasource, 'modesUpdate.exe') + browser.connection(aDatasource);
3057            updateUrl = updateUrl + 'action=merge&username=richard' + '&application=' + anApp + '&filename=' + aFile;
3058            updateUrl = updateUrl + '&indexname=Identity number';
3059            //updateUrl = updateUrl + '&xml=' + mergeXml;
3060            //alert(updateUrl);
3061            var updateXml = loadXmlString(mergeXml);
3062            //alert(xmlToString(updateXml.documentElement));
3063            jQuery.ajax({
3064              type: "post", async: false, dataType: "xml", processData: false, data: updateXml, url: updateUrl,
3065              success: function (xml) {
3066                //alert('update post result: '+xml.documentElement.textContent);
3067              },
3068              failure: function (xml) {
3069                alert('update failed: '+xml.documentElement.textContent);
3070              }
3071            })
3072          },
3073          failure: function(xml) {
3074            //alert('failed to find element group: '+xml);
3075          }
3076        });
3077        //alert('after submitting '+url);
3078        break;
3079    }
3080
3081  }
3082}
3083
3084function updateSelectedRecordsFn(datasource, controlName) {
3085//  alert('updating selected records');
3086  var source = this.datasource[datasource];
3087  var aControl = this.findControl(controlName);
3088  var thisform = document.forms[controlName+'_data'];
3089  // while this works, we need to (a) find which is the checked criterion and (b) discover how many records there are
3090  //alert(thisform);
3091  // need to grab the application and filename from the datasource:
3092  var app = source['application'];
3093  var file = source['filename'];
3094  var sep = this.getSeparator(datasource);
3095  var url = this.urlBase(this.domainName, datasource, 'getConstant.exe') + this.connection(datasource) + 'application=' + app + sep + 'name=' + aControl['elementgroup'];
3096  //alert(url);
3097  var browser = this;
3098  var doc = document;
3099  jQuery.ajax({
3100    type: "get", dataType: "xml", url: url,
3101    success: function (xml) {
3102      //alert(xmlToString(xml));
3103      //find records to be checked:
3104      var checkedItem = undefined;
3105      for (var i = 1; i <= aControl['mergecount']; i++) {
3106        mergeValue = aControl['mergedata_'+i];
3107        if ((mergeValue) && (mergeValue == '$$$selectrecord$$$')) {
3108          checkedItem = i;
3109          break;
3110        }
3111      }
3112      var elts = doc.evaluate("//div[@class='record']", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
3113      //alert(elts);
3114      var recordCount = elts.snapshotLength;
3115      var mergeXml = '<records>';
3116      for (var i=0; i<recordCount; i++) {
3117        if ((!checkedItem) || (thisform.elements['mergedata_'+checkedItem+'_'+i].checked)) {
3118          //alert('item'+i+' checked');
3119          mergeXml = mergeXml + '<record primary-key="'+thisform.elements['mergeidentity_'+checkedItem+'_'+i].value+'">'
3120          // update for this record; add XML to merge string:
3121          //clone and update this XML for each record which has been selected:
3122          mergeXml = mergeXml + browser.updateXmlWithValues(datasource, controlName, xml, i);
3123          mergeXml = mergeXml + '</record>';
3124        }
3125      }
3126      mergeXml = mergeXml + '</records>';
3127      //alert(mergeXml);
3128      // now submit mergeXml as an update to the specified Modes file
3129      updateUrl = browser.urlBase(browser.domainName, datasource, 'modesUpdate.exe')+browser.connection(datasource);
3130      updateUrl = updateUrl + 'action=merge&username=richard'+'&application='+app+'&filename='+file;
3131      updateUrl = updateUrl + '&indexname=Identity number';
3132      updateUrl = updateUrl + '&xml='+mergeXml;
3133      //alert(updateUrl);
3134      jQuery.ajax({ type: "get", dataType: "xml", url: updateUrl,
3135        success: function(xml) {
3136          //alert('Update succeeded: '+xml);
3137          document.location.reload(false);
3138        },
3139        failure: function(xml) {
3140          alert('Update failed: '+xml);
3141        }
3142      })
3143    }
3144  })
3145}
3146
3147function updateXmlWithValuesFn(datasource, controlName, xml, offset) {
3148  var source = this.datasource[datasource];
3149  var aControl = this.findControl(controlName);
3150  var thisform = document.forms[controlName+'_data'];
3151  var copyXml = xml.cloneNode(true);
3152  for (var i = 1; i <= aControl['mergecount']; i++) {
3153    var controlValue = aControl['mergedata_'+i];
3154    var mergeValue = null;
3155    if (controlValue == '$$$date$$$') {
3156      mergeValue = todayForModes();
3157    }
3158    else if (controlValue == '$$$user$$$') {
3159      mergeValue = this.username;
3160    }
3161    else if (controlValue.startsWith('$$$checkbox=')) {
3162      if ((thisform.elements['mergedata_'+i+'_'+offset]) && (thisform.elements['mergedata_'+i+'_'+offset].checked)) {
3163        mergeValue = controlValue.substr(12, controlValue.length-15);
3164      } else {
3165        mergeValue = 'not '+controlValue.substr(12, controlValue.length-15);
3166      }
3167    }
3168    if (!mergeValue) {
3169      if (thisform.elements['mergedata_'+i]) {
3170        mergeValue = thisform.elements['mergedata_'+i].value;
3171      }
3172      else if (thisform.elements['mergedata_'+i+'_'+offset]) {
3173        mergeValue = thisform.elements['mergedata_'+i+'_'+offset].value;
3174      }
3175    }
3176    if ((mergeValue) && (thisform.elements['mergexpath_'+i]) && (thisform.elements['mergexpath_'+i].value !== '')) {
3177      var xpathElts = copyXml.evaluate(thisform.elements['mergexpath_'+i].value, copyXml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
3178      if (xpathElts.snapshotLength > 0) {
3179        thisElt = xpathElts.snapshotItem(0);
3180        if (thisElt) {
3181          thisElt.appendChild(copyXml.createTextNode(mergeValue));
3182        }
3183
3184      }
3185    }
3186  }
3187  return xmlToString(copyXml);
3188}
3189
3190function updateXmlWithValues2Fn(datasource, aControl, controlName, form, xml, recordoffset, fieldoffset) {
3191  var source = this.datasource[datasource];
3192  var fileControl = this.findControl(controlName);
3193  var thisform = form;
3194  //alert(thisform);
3195  //alert(controlName);
3196  var fieldControl = aControl['fields'][fieldoffset];
3197  if (fieldControl) {
3198    var controlFieldType = fieldControl['fieldtype'];
3199    //alert(controlFieldType);
3200    var mergeValue = null;
3201    switch (controlFieldType) {
3202      case 'checkbox':
3203        if (thisform.elements['mergedata_'+recordoffset+'_'+fieldoffset]) {
3204          var aFormElement = thisform.elements['mergedata_'+recordoffset+'_'+fieldoffset];
3205          if (aFormElement.checked) {
3206            mergeValue = fieldControl['checkedvalue'];
3207          } else {
3208            mergeValue = fieldControl['uncheckedvalue'];
3209          }
3210        }
3211        break;
3212      case 'text':
3213        if (thisform.elements['mergedata_'+recordoffset+'_'+fieldoffset]) {
3214          var aFormElement = thisform.elements['mergedata_'+recordoffset+'_'+fieldoffset];
3215          mergeValue = aFormElement.value;
3216        }
3217        break;
3218      case 'formvalue':
3219        //alert('looking for form value for '+fieldControl['data']);
3220        if (thisform.elements[fieldControl['data']]) {
3221          mergeValue = thisform.elements[fieldControl['data']].value;
3222          //alert('form value '+fieldControl['data']+' has value '+mergeValue);
3223        }
3224        break;
3225      case 'fixedvalue':
3226        //alert('fixed value found: '+fieldControl['data']);
3227        mergeValue = fieldControl['data'];
3228        break;
3229      case 'identity':
3230        if (thisform.elements['mergeidentity_'+recordoffset]) {
3231          var aFormIdentity = thisform.elements['mergeidentity_'+recordoffset];
3232          mergeValue = aFormIdentity.value;
3233        }
3234        break;
3235      case 'procedureidentity':
3236        if (aControl['procedureidentity']) {
3237          mergeValue = aControl['procedureidentity'];
3238        }
3239    }
3240
3241    if ((mergeValue) && (mergeValue !== '')) {
3242      //alert(mergeValue);
3243      //alert(aControl['fields'][fieldoffset]['xpath']);
3244      var xpathElts = xml.evaluate(fieldControl['xpath'], xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
3245      if (xpathElts.snapshotLength > 0) {
3246        //alert('merging '+mergeValue+' into data');
3247        thisElt = xpathElts.snapshotItem(0);
3248        if (thisElt) {
3249          //alert(thisElt.nodeType);
3250          if (thisElt.nodeType == 3) { //'TEXT_NODE') {
3251            //alert('updating text node with '+mergeValue);
3252            thisElt.nodeValue = mergeValue;
3253          }
3254          else {
3255            thisElt.appendChild(xml.createTextNode(mergeValue));
3256          }
3257        }
3258
3259      } // else alert('XPath '+fieldControl['xpath']+ ' failed  on '+xmlToString(xml));
3260    }
3261  }
3262}
3263
3264function updateValuesFn(s) {
3265  return s;
3266}
3267
3268function extractParameter(s, sep) {
3269  if (s.contains(sep)) {
3270    var s2 = s.substr(s.indexOf(sep)+sep.length);
3271    if (s2.contains(sep)) {
3272      return s2.substr(0, s2.indexOf(sep));
3273    } else return '';
3274    //alert(s2);
3275  } else return '';
3276}
3277
3278function replaceParameter(s, sep, value) {
3279  if (s.contains(sep)) {
3280    var s2 = s.substr(0, s.indexOf(sep));
3281    var s3 = s.substr(s.indexOf(sep)+sep.length);
3282    return s2+value+s3.substr(s3.indexOf(sep)+sep.length);
3283  } else return s;
3284}
3285
3286function getFormValuesFn(form, query) {
3287  var mappedQuery = query;
3288  while (mappedQuery.contains('$$$')) {
3289    var parameter = extractParameter(query, '$$$');
3290    var parmValue = form[parameter].value;
3291    //alert(parmValue);
3292    mappedQuery = replaceParameter(mappedQuery, '$$$', parmValue);
3293    break;
3294  }
3295  return mappedQuery;
3296}
3297
3298/* -------------------------------------------------------- */
3299
3300// declare ModesControl as a class:
3301
3302ModesControl.prototype.browser = undefined;
3303ModesControl.prototype.name = undefined;
3304ModesControl.prototype.type = undefined;
3305ModesControl.prototype.datasource = undefined;
3306ModesControl.prototype.displayCommentary = displayCommentaryFn;
3307ModesControl.prototype.displayUpdate = displayUpdateFn;
3308
3309function ModesControl(aBrowser, aName, aType, aDatasource) {
3310  this.browser = aBrowser;
3311  this.name = aName;
3312  this.type = aType;
3313  this.datasource = aDatasource;
3314}
3315
3316/* -------------------------------------------------------- */
3317
3318// declare ModesDatasource as a class:
3319
3320// set up datasource: currentStart will presumably have been passed in, so get
3321// recordsToFetch records from the data source, and populate the recordIds array,
3322// using the primaryIndex if specified; otherwise getting an array of Ids
3323// updated rbl 1.7.2015, to support the Linked Data case:
3324function setupDatasourceFn() {
3325//    alert('setting up data source '+this.name+'; linked data set to '+this.linkedData);
3326  var sep = this.browser.getSeparator(this);
3327    this.browser.updateQueryWithFilters();
3328    activeQuery = this.browser.filteredQuery;
3329
3330    if (this.linkedData) {
3331        var urlBase = 'http://'+this.browser.domainName+this.browser.scriptDir+this.browser.forwarder+'/?url=http://'+this.domain+'/'+this.application+'/'+this.filename;
3332        var thisQuery;
3333        if ((activeQuery) && (activeQuery !== ''))
3334            thisQuery = 'modes_query='+activeQuery+sep+'param=query='+activeQuery;
3335        else
3336            thisQuery = 'modes_query='+this.browser.modesQuery+sep+'param=query='+this.browser.modesQuery;
3337        url = urlBase+'/search/xml/?'+thisQuery;
3338//        alert(url);
3339        var datasource = this;
3340        if (!this.hitCount) {
3341            jQuery.ajax({ type: "get", dataType: "xml", url: url,
3342                success: function(xml) {
3343                    // getElementsByTagNameNS shamelessly fails for me. The fallback line below it is horrible, but works ...
3344                    var hitCountElts = xml.documentElement.getElementsByTagNameNS('http://a9.com/-/opensearch/extensions/sru/2.0/', 'totalResults');
3345                    if ((!hitCountElts) || (hitCountElts.length == 0)) {
3346                        hitCountElts = xml.documentElement.getElementsByTagName('opensearch:totalResults');
3347                    }
3348//                    alert(hitCountElts+'; '+hitCountElts.length);
3349                    if (!hitCountElts) {
3350                        datasource.hitCount = xml.documentElement.childNodes[0].nodeValue;
3351//                        alert('found hit count directly!: '+datasource.hitCount);
3352                    }
3353                    if (hitCountElts.length > 0) {
3354                        datasource.hitCount = hitCountElts.item(0).childNodes.item(0).nodeValue;
3355//                        alert('got hitcount from '+url+': '+datasource.hitCount+' hits');
3356                    } else if (datasource.browser.debug)
3357                        alert('Empty hit count nodelist');
3358                }
3359            })
3360        }
3361        var start = this.currentStart;
3362        datasource.recordIds = [];
3363        if (!this.primaryIndex) {
3364            url = urlBase+'/keylist/xml/_id/?start='+this.currentStart+sep+thisQuery;
3365        } else {
3366            url = urlBase+'/keylist/xml/'+this.primaryIndex+'/?start='+this.currentStart+sep+thisQuery;
3367        }
3368//        alert('keys url is '+url);
3369        jQuery.ajax({ type: "get", dataType: "xml", url: url,
3370            success: function(xml) {
3371                var recs = xml.documentElement.getElementsByTagName('option');
3372                for ( var i = 0; i<recs.length; i++ ) {
3373                    datasource.recordIds.push(recs[i].getAttribute('value'));
3374                }
3375//          alert('Data source: start: '+start+'; ids: '+datasource.recordIds);
3376            }
3377        })
3378    } else {
3379        if ((activeQuery) && (activeQuery !== ''))
3380            url = this.browser.urlBase(this.browser.domainName, this)+this.browser.connection(this)+'application='+this.application+
3381            sep+'filename='+this.filename+sep+'query='+activeQuery+sep+'nodups=yes';
3382        else
3383            url = this.browser.urlBase(this.browser.domainName, this)+this.browser.connection(this)+'application='+this.application+
3384            sep+'filename='+this.filename+sep+'query='+this.browser.modesQuery+sep+'nodups=yes';
3385
3386        var datasource = this;
3387        if (!this.hitCount) {
3388            urlCount = url+sep+'target=4'+sep+'level=1';
3389//        alert('getting hitcount from '+urlCount);
3390            jQuery.ajax({ type: "get", dataType: "xml", url: urlCount,
3391                success: function(xml) {
3392                    datasource.hitCount = xml.documentElement.getAttribute('total');
3393//        alert('got hitcount from '+urlCount+': '+datasource.hitCount+' hits');
3394                }
3395            })
3396        }
3397        //alert(url+'; primary index is '+this.primaryIndex);
3398        var start = this.currentStart;
3399        datasource.recordIds = [];
3400        if (!this.primaryIndex) {
3401            urlList = url+sep+'start='+this.currentStart+sep+'count='+this.recordsToFetch+sep+'target=4'+sep+'level=2';
3402            jQuery.ajax({ type: "get", dataType: "xml", url: urlList,
3403                success: function(xml) {
3404                    var recs = xml.documentElement.getElementsByTagName('record');
3405                    for ( var i = 0; i<recs.length; i++ ) {
3406                        datasource.recordIds.push(recs[i].getAttribute('id'));
3407                    }
3408//      alert('Data source: url: '+urlList+'; start: '+start+'; ids: '+datasource.recordIds);
3409                }
3410            })
3411        } else {
3412            urlList = url+sep+'start='+this.currentStart+sep+'count='+this.recordsToFetch+sep+'indexname='+this.primaryIndex+sep+'target=3'+sep+'level=2';
3413            jQuery.ajax({ type: "get", dataType: "xml", url: urlList,
3414                success: function(xml) {
3415                    var recs = xml.documentElement.getElementsByTagName('key');
3416                    for ( var i = 0; i<recs.length; i++ ) {
3417                        if (recs[i] && recs[i].childNodes[0])
3418                            datasource.recordIds.push(recs[i].childNodes[0].nodeValue);
3419                    }
3420//          alert('Data source: url: '+urlList+'; start: '+start+'; ids: '+datasource.recordIds);
3421                }
3422            })
3423        }
3424    }
3425
3426
3427
3428}
3429
3430function ModesDatasource(aBrowser, aName, aDomain, anApp, aFile, anImageDir, aStart) {
3431  this.browser = aBrowser;
3432  this.name = aName;
3433  this.domain = aDomain;
3434    this.linkedData = (this.domain !== '');
3435  this.application = anApp;
3436  this.filename = aFile;
3437  this.imageDir = anImageDir;
3438//  this.hitCount = 0; - leave it undefined so setup can test on this:
3439    this.hitCount = undefined;
3440// give recordsToFetch an initial value, which can be overridden by a consumer (navigator, hitlist) which requires more:
3441  this.recordsToFetch = 10;
3442  this.currentStart = aStart;
3443    this.primaryIndex = undefined;
3444    this.recordIds = new Array;
3445    this.setPrimaryIndex();
3446// No, it does NOT have its own navigator:
3447//  this.navigator = new ModesNavigator(this);
3448}
3449
3450function setPrimaryIndexFn() {
3451    switch (this.application) {
3452        case 'Object':
3453            this.primaryIndex = 'Identity number';
3454            break;
3455        case 'Help':
3456            this.primaryIndex = 'id';
3457            break;
3458        case 'Termlist':
3459            this.primaryIndex = 'Terms';
3460            break;
3461        case 'bugs':
3462            this.primaryIndex = 'Identity';
3463            break;
3464        case 'TEI-P5':
3465            this.primaryIndex = 'Id';
3466            break;
3467    }
3468}
3469
3470ModesDatasource.prototype.browser = undefined;
3471ModesDatasource.prototype.name = undefined;
3472ModesDatasource.prototype.domain = undefined;
3473ModesDatasource.prototype.application = undefined;
3474ModesDatasource.prototype.filename = undefined;
3475ModesDatasource.prototype.imageDir = undefined;
3476ModesDatasource.prototype.linkedData = false;
3477ModesDatasource.prototype.hitCount = undefined;
3478ModesDatasource.prototype.recordsToFetch = undefined;
3479ModesDatasource.prototype.currentStart = undefined;
3480ModesDatasource.prototype.recordIds = undefined;
3481ModesDatasource.prototype.query = undefined;
3482ModesDatasource.prototype.setup = setupDatasourceFn;
3483ModesDatasource.prototype.setPrimaryIndex = setPrimaryIndexFn;
3484
3485/* -------------------------------------------------------- */
3486
3487// declare ModesFilter as a class:
3488
3489function ModesFilter(aBrowser, aName, aDatasource, aQuery, anInitialState) {
3490    this.name = aName;
3491    this.datasource = aDatasource;
3492    this.modesQuery = aQuery;
3493    this.active = (anInitialState == 'on');
3494}
3495
3496ModesFilter.prototype.name = undefined;
3497ModesFilter.prototype.datasource = undefined;
3498ModesFilter.prototype.modesQuery = '';
3499ModesFilter.prototype.active = true;
3500
3501/* -------------------------------------------------------- */
3502
3503// declare ModesNavigator as a class:
3504
3505function navigatorStartFn() {
3506  this.position = 1;
3507    if (this.datasource)
3508      this.datasource.currentStart = this.position;
3509}
3510
3511function navigatorEndFn() {
3512    if (this.datasource) {
3513//        alert('hit count is '+this.datasource.hitCount);
3514      if (this.delta == 1) {
3515            this.position = this.total;
3516      } else {
3517            if (this.delta == 0) {
3518                this.delta = 1;
3519            }
3520            var noOfPages = Math.floor(this.total / this.delta);
3521        this.position = (noOfPages * this.delta) + 1;
3522            //alert('No of pages is '+noOfPages+'; position is '+this.position+'; total is '+this.total+'; delta is '+this.delta);
3523      }
3524      this.datasource.currentStart = this.position;
3525    } else
3526        this.position = this.total;
3527}
3528
3529function navigatorBackFn() {
3530  if (this.delta == 1) {
3531    if (this.position > 1)
3532      this.position = this.position - 1;
3533  } else {
3534        if (this.delta == 0) {
3535            this.delta = 1;
3536        }
3537    var startOfThisPage = (Math.floor(this.position / this.delta) * this.delta) + 1;
3538    if (startOfThisPage == this.position) { // already at the start of a page
3539      if (this.position > this.delta)
3540        this.position = this.position - this.delta;
3541      else
3542        this.position = 1;
3543    } else // move back to start of page
3544      this.position = startOfThisPage;
3545  }
3546//  alert('Back to '+this.position);
3547    if (this.datasource)
3548      this.datasource.currentStart = this.position;
3549//  alert('set data source to '+this.datasource.currentStart);
3550}
3551
3552function navigatorForwardFn() {
3553    if (this.datasource) {
3554        //alert('position is '+this.position+'; total is '+this.total+'; delta is '+this.delta);
3555      if (this.delta == 1) {
3556            if (this.position < this.total)
3557          this.position++; // = this.position + 1;
3558      } else {
3559            if (this.delta == 0) {
3560                this.delta = 1;
3561            }
3562        var startOfThisPage = (Math.floor(this.position / this.delta) * this.delta) + 1;
3563            if (startOfThisPage + this.delta < this.total)
3564          this.position = startOfThisPage + this.delta;
3565        else {
3566          this.position = startOfThisPage;
3567        }
3568      }
3569    } else
3570        this.position++; // = this.position + 1;
3571//  alert('Forward to '+this.position);
3572    if (this.datasource)
3573      this.datasource.currentStart = this.position;
3574  //alert('hit count='+this.datasource.hitCount+'; set data source to '+this.datasource.currentStart);
3575}
3576
3577function navigatorRecordFn(record) {
3578    if (this.datasource) {
3579//        alert(record);
3580        this.position = record;
3581        this.datasource.currentStart = this.position;
3582    }
3583}
3584
3585function setupNavigatorFn() {
3586  if (this.datasource) {
3587    this.datasource.setup();
3588    this.position = this.datasource.currentStart;
3589//    this.total = this.datasource.hitCount;
3590//    alert('navigator total is '+this.total);
3591        if (this.delta == 0)
3592            alert('Navigator error: delta has value 0');
3593        this.display();
3594  } else alert('Page coding error: navigator has no data source assigned');
3595}
3596
3597function displayNavigatorFn() {
3598    var recordsperpage = this.delta;
3599    var name = this.controlName;
3600    var resultPaged = this.paged;
3601    // bug 3347: need to test for 'true', not 'yes':
3602    var displayStatus = ((this.status) || (this.status == 'yes'));
3603    var noOfPages = this.pagesToDisplay;
3604    var aQuery;
3605    //alert('navigator '+this.controlName+' status is '+this.status);
3606    if (this.datasource.browser.filters.length > 0)
3607        aQuery = this.datasource.browser.filteredQuery;
3608    else
3609        aQuery = this.datasource.browser.modesQuery;
3610    //alert(this.controlName);
3611    var status = '<span class="status">';
3612    if ((this.datasource) && (this.datasource.linkedData)) {
3613        /*
3614         url = 'http://'+this.datasource.browser.domainName+this.datasource.browser.scriptDir+'cgiforwarder.exe/?url=http://'+
3615         this.datasource.domain+'/'+this.datasource.application+'/'+this.datasource.filename+'/search/xml/?modes_query='+aQuery;
3616         */
3617        url = 'http://'+this.datasource.browser.domainName+this.datasource.browser.scriptDir+this.datasource.browser.forwarder+'/?url=http://'+
3618        this.datasource.domain+'/'+this.datasource.application+'/'+this.datasource.filename+'/search/xml/?modes_query='+aQuery;
3619//      alert(url);
3620        var navigator = this;
3621        var datasource = this.datasource;
3622        jQuery.ajax({ type: "get", dataType: "xml", url: url,
3623            success: function(xml) {
3624//          alert('success '+xml);
3625                var pageStart = navigator.position;
3626                //var totalHits = xml.getElementsByTagName('opensearch:totalResults')[0].childNodes[0].nodeValue;
3627                var totalHits = datasource.hitCount; // rbl - already calculated while setting up data source
3628                if (totalHits)
3629                {
3630//            alert('total '+totalHits+'; data source is '+this.datasource.name);
3631                    var lastRecordDisplayed = Math.min(totalHits, parseInt(pageStart) - 1 + parseInt(recordsperpage));
3632                    if (jQuery('#'+datasource.name+'_hitcount')) {
3633                        jQuery('#'+datasource.name+'_hitcount').html('<span>'+totalHits+'</span>');
3634                    }
3635                    if (jQuery('#'+datasource.name+'_navigator_status')) {
3636                        jQuery('#'+datasource.name+'_navigator_status').html(status);
3637                    }
3638                    if (totalHits == 0)
3639                        status = 'No records found';
3640                    else if (recordsperpage > 1)
3641                        status = '<span>Records '+pageStart+' to '+lastRecordDisplayed+' of '+totalHits+'</span>';
3642                    else
3643                        status = '<span>Record '+pageStart+' of '+totalHits+'</span>';
3644                    status = status + '</span>';
3645                    var nav = '<div class=\"displayNavigator\">';
3646                    if (resultPaged) {
3647                        if (totalHits == 0) {
3648                            if (displayStatus)
3649                                nav = nav + status;
3650                        } else {
3651                            var totalPages = Math.ceil(totalHits / recordsperpage);
3652                            var currentPage;
3653                            if (recordsperpage == 1)
3654                                currentPage = pageStart;
3655                            else
3656                                currentPage = Math.floor(pageStart / recordsperpage) + 1;
3657//                        alert(currentPage);
3658                            var startPage = Math.max(1, currentPage - Math.floor(noOfPages / 2));
3659                            var endPage = Math.min(totalPages, (startPage + noOfPages - 1));
3660                            nav = nav + 'Page: ';
3661                            if (startPage > 1) {
3662                                nav = nav + navFirst(name, datasource['name']);
3663                            }
3664                            for ( var i=startPage; i<=endPage; i++) {
3665                                nav = nav + navToRecord(name, datasource['name'], (i - 1)*recordsperpage+1, i, (i==currentPage));
3666                            }
3667                            if (endPage < totalPages) {
3668                                nav = nav + navLast(name, datasource['name']);
3669                            }
3670                            if (displayStatus) {
3671                                nav = nav + '<br />' + status;
3672                            }
3673                        }
3674                    } else {
3675                        var lastRecordDisplayed = Math.min(totalHits, parseInt(pageStart) - 1 + parseInt(recordsperpage));
3676                        var newStartRecord = Math.max(1, pageStart - recordsperpage);
3677                        if (totalHits == 0) {
3678                            if (displayStatus)
3679                                nav = nav + status;
3680                        } else
3681                        {
3682                            if (pageStart > 1) { nav = nav + navFirst(name) + navBack(name, newStartRecord); }
3683                            if (displayStatus) {
3684                                nav = nav + status;
3685                            }
3686                            if (lastRecordDisplayed < totalHits) {
3687                                nav = nav + navForward(name, parseInt(lastRecordDisplayed)+1, recordsperpage, totalHits) + navLast(name, recordsperpage, totalHits);
3688                            }
3689                        }
3690                    }
3691                    nav = nav + '</div>';
3692                    //alert(nav);
3693                    /* // looks like a bug to me: could explain why hit list is wiped out by navigator:
3694                     if (jQuery('#'+name)) {
3695                     jQuery('#'+name).html(nav);
3696                     }
3697                     */
3698                    if (jQuery('#'+name+'_navigator_paged')) {
3699                        jQuery('#'+name+'_navigator_paged').html(nav);
3700                    }
3701                    if (jQuery('#'+name+'_navigator_default')) {
3702                        jQuery('#'+name+'_navigator_default').html(nav);
3703                    }
3704                    if (jQuery('#'+name+'_prenav')) {
3705                        jQuery('#'+name+'_prenav').html(nav);
3706                    }
3707                    if (jQuery('#'+name+'_postnav')) {
3708                        jQuery('#'+name+'_postnav').html(nav);
3709                    }
3710                }
3711            }
3712        })
3713    } else if (this.datasource) {
3714//    url = 'http://'+this.datasource.browser.domainName+this.datasource.browser.scriptDir+'modescgi.exe/?'+this.datasource.browser.connection(datasource)+'application='+
3715        var sep = this.datasource.browser.getSeparator(this.datasource);
3716        url = this.datasource.browser.urlBase(this.datasource.browser.domainName, this.datasource)+this.datasource.browser.connection(this.datasource)+'application='+
3717        this.datasource.application+sep+'filename='+this.datasource.filename+sep+'query='+aQuery+sep+'nodups=yes'+sep+'target=4'+sep+'level=1';
3718        var navigator = this; // rbl 24.5.2016
3719//    alert(url);
3720        jQuery.ajax({ type: "get", dataType: "xml", url: url,
3721            success: function(xml) {
3722                var pageStart = navigator.position; // rbl 24.5.2016
3723//                var pageStart = datasource.currentStart;
3724                returnedApp = xml.documentElement.getAttribute('application');
3725                returnedFile = xml.documentElement.getAttribute('filename');
3726                datasource = modes_browser.findDatasource(returnedApp, returnedFile);
3727                var totalHits = jQuery(xml).find('recordcount').attr('total');
3728                var lastRecordDisplayed = Math.min(totalHits, parseInt(pageStart) - 1 + parseInt(recordsperpage));
3729                var newStartRecord = Math.max(1, pageStart - recordsperpage);
3730                if (totalHits == 0)
3731                    status = 'No records found';
3732                else if (recordsperpage > 1)
3733                    status = '<span>Records '+pageStart+' to '+lastRecordDisplayed+' of '+totalHits+'</span>';
3734                else
3735                    status = '<span>Record '+pageStart+' of '+totalHits+'</span>';
3736                status = status + '</span>';
3737                var nav = '<div class=\"displayNavigator\">';
3738//                alert('total '+totalHits+'; data source is '+datasource.name);
3739                if (jQuery('#'+datasource.name+'_hitcount')) {
3740                    jQuery('#'+datasource.name+'_hitcount').html('<span>'+totalHits+'</span>');
3741                }
3742                if (jQuery('#'+datasource.name+'_navigator_status')) {
3743                    jQuery('#'+datasource.name+'_navigator_status').html(status);
3744                }
3745                if (resultPaged) {
3746                    if (totalHits == 0) {
3747                        if (displayStatus)
3748                            nav = nav + status;
3749                    } else {
3750                        var totalPages = Math.ceil(totalHits / recordsperpage);
3751                        var currentPage;
3752                        if (recordsperpage == 1)
3753                            currentPage = pageStart;
3754                        else
3755                            currentPage = Math.floor(pageStart / recordsperpage) + 1;
3756//                        alert(currentPage);
3757                        var startPage = Math.max(1, currentPage - Math.floor(noOfPages / 2));
3758                        var endPage = Math.min(totalPages, (startPage + noOfPages - 1));
3759                        nav = nav + 'Page: ';
3760                        if (startPage > 1) {
3761                            nav = nav + navFirst(name, datasource['name']);
3762                        }
3763                        for ( var i=startPage; i<=endPage; i++) {
3764                            nav = nav + navToRecord(name, datasource['name'], (i - 1)*recordsperpage+1, i, (i==currentPage));
3765                        }
3766                        if (endPage < totalPages) {
3767                            nav = nav + navLast(name, datasource['name']);
3768                        }
3769                        if (displayStatus) {
3770                            nav = nav + '<br/>' + status;
3771                        }
3772                    }
3773                } else {
3774                    if (totalHits == 0) {
3775                        if (displayStatus)
3776                            nav = nav + status;
3777                    } else
3778                    {
3779                        if (pageStart > 1) { nav = nav + navFirst(name, datasource['name']) + navBack(name, datasource['name']); }
3780                        if (displayStatus) {
3781                            // alert(url+': '+status)
3782                            nav = nav + status;
3783                        } // else alert('display status set to false');
3784
3785                        if (lastRecordDisplayed < totalHits) {
3786                            nav = nav + navForward(name, datasource['name']) + navLast(name, datasource['name']);
3787                        }
3788                    }
3789                }
3790                nav = nav + '</div>';
3791//        alert(nav+' for '+name);
3792                /* // looks like a bug to me: could explain why hit list is wiped out by navigator:
3793                 if (jQuery('#'+name)) {
3794                 jQuery('#'+name).html(nav);
3795                 }
3796                 */
3797                if (jQuery('#'+name+'_navigator_paged')) {
3798                    jQuery('#'+name+'_navigator_paged').html(nav);
3799                }
3800                if (jQuery('#'+name+'_navigator_default')) {
3801                    jQuery('#'+name+'_navigator_default').html(nav);
3802                }
3803                if (jQuery('#'+name+'_prenav')) {
3804                    jQuery('#'+name+'_prenav').html(nav);
3805                }
3806                if (jQuery('#'+name+'_postnav')) {
3807                    jQuery('#'+name+'_postnav').html(nav);
3808                }
3809            }
3810        })
3811    } else { // slideshow support: no data source
3812        var nav = '<div class="displayNavigator">';
3813        if ((this.total) && (this.total == 0)) {
3814            nav = nav + 'No slides found';
3815        } else
3816        {
3817            if (this.position > 1) { nav = nav + navFirst(name) + navBack(name); }
3818            nav = nav + '<span>Slide '+this.position+' of '+this.total+'</span>';
3819            if (this.position < this.total) {
3820                nav = nav + navForward(name) + navLast(name);
3821            }
3822        }
3823        nav = nav + '</div>';
3824        //  alert(nav+' for '+name);
3825        if (jQuery('#'+name+'_navigator')) {
3826            jQuery('#'+name+'_navigator').html(nav);
3827        }
3828        if (jQuery('#'+name+'_prenav')) {
3829            jQuery('#'+name+'_prenav').html(nav);
3830        }
3831        if (jQuery('#'+name+'_postnav')) {
3832            jQuery('#'+name+'_postnav').html(nav);
3833        }
3834    }
3835}
3836function displayNavigatorDirectFn() {
3837    var recordsperpage = this.delta;
3838    var name = this.controlName;
3839    var resultPaged = this.paged;
3840    // bug 3347: need to test for 'true', not 'yes':
3841    var displayStatus = ((this.status) || (this.status == 'yes'));
3842    var noOfPages = this.pagesToDisplay;
3843    var aQuery;
3844    if (this.datasource.browser.filters.length > 0)
3845        aQuery = this.datasource.browser.filteredQuery;
3846    else
3847        aQuery = this.datasource.browser.modesQuery;
3848    var status = '<span class="status">';
3849    var pageStart = this.position;
3850    var totalHits = this.total;
3851    if (totalHits)
3852    {
3853        //alert('total '+totalHits+' for '+this.datasource.name+'; control name is '+name);
3854        var lastRecordDisplayed = Math.min(totalHits, parseInt(pageStart) - 1 + parseInt(recordsperpage));
3855        if (jQuery('#'+name+'_hitcount')) {
3856            jQuery('#'+name+'_hitcount').html('<span>'+totalHits+'</span>');
3857        }
3858        if (jQuery('#'+name+'_navigator_status')) {
3859            jQuery('#'+name+'_navigator_status').html(status);
3860        }
3861        if (totalHits == 0)
3862            status = 'No records found';
3863        else if (recordsperpage > 1)
3864            status = '<span>Records '+pageStart+' to '+lastRecordDisplayed+' of '+totalHits+'</span>';
3865        else
3866            status = '<span>Record '+pageStart+' of '+totalHits+'</span>';
3867        status = status + '</span>';
3868        var nav = '<div class=\"displayNavigator\">';
3869        if (resultPaged) {
3870            if (totalHits == 0) {
3871                if (displayStatus)
3872                    nav = nav + status;
3873            } else {
3874                var totalPages = Math.ceil(totalHits / recordsperpage);
3875                var currentPage;
3876                if (recordsperpage == 1)
3877                    currentPage = pageStart;
3878                else
3879                    currentPage = Math.floor(pageStart / recordsperpage) + 1;
3880                var startPage = Math.max(1, currentPage - Math.floor(noOfPages / 2));
3881                var endPage = Math.min(totalPages, (startPage + noOfPages - 1));
3882                nav = nav + 'Page: ';
3883                if (startPage > 1) {
3884                    nav = nav + navFirst(name, this.datasource['name']);
3885                }
3886                for ( var i=startPage; i<=endPage; i++) {
3887                    nav = nav + navToRecord(name, this.datasource['name'], (i - 1)*recordsperpage+1, i, (i==currentPage));
3888                }
3889                if (endPage < totalPages) {
3890                    nav = nav + navLast(name, this.datasource['name']);
3891                }
3892                if (displayStatus) {
3893                    nav = nav + '<br />' + status;
3894                }
3895            }
3896        } else {
3897            var lastRecordDisplayed = Math.min(totalHits, parseInt(pageStart) - 1 + parseInt(recordsperpage));
3898            var newStartRecord = Math.max(1, pageStart - recordsperpage);
3899            if (totalHits == 0) {
3900                if (displayStatus)
3901                    nav = nav + status;
3902            } else
3903            {
3904                if (pageStart > 1) { nav = nav + navFirst(name) + navBack(name, newStartRecord); }
3905                if (displayStatus) {
3906                    nav = nav + status;
3907                }
3908                if (lastRecordDisplayed < totalHits) {
3909                    nav = nav + navForward(name, parseInt(lastRecordDisplayed)+1, recordsperpage, totalHits) + navLast(name, recordsperpage, totalHits);
3910                }
3911            }
3912        }
3913        nav = nav + '</div>';
3914        //alert(nav);
3915        if (jQuery('#'+name+'_paged')) {
3916            jQuery('#'+name+'_paged').html(nav);
3917        }
3918        if (jQuery('#'+name+'_default')) {
3919            jQuery('#'+name+'_default').html(nav);
3920        }
3921        if (jQuery('#'+name+'_prenav')) {
3922            //alert('prenav found: '+name+'_prenav');
3923            jQuery('#'+name+'_prenav').html(nav);
3924        }
3925        if (jQuery('#'+name+'_postnav')) {
3926            jQuery('#'+name+'_postnav').html(nav);
3927        }
3928    }
3929}
3930
3931function displayPagesFn(start, total) {
3932
3933}
3934
3935function ModesNavigator(aName, aDatasource) {
3936  this.controlName = aName;
3937  this.datasource = aDatasource;
3938  this.position = 1;
3939  this.delta = 1;
3940  this.total = 0;
3941}
3942
3943ModesNavigator.prototype.controlName = undefined;
3944ModesNavigator.prototype.datasource = undefined;
3945ModesNavigator.prototype.position = undefined;
3946ModesNavigator.prototype.total = undefined;
3947ModesNavigator.prototype.delta = undefined;
3948ModesNavigator.prototype.paged = false;
3949ModesNavigator.prototype.status = true;
3950ModesNavigator.prototype.pagesToDisplay = 0;
3951ModesNavigator.prototype.gotoStart = navigatorStartFn;
3952ModesNavigator.prototype.gotoEnd = navigatorEndFn;
3953ModesNavigator.prototype.goBack = navigatorBackFn;
3954ModesNavigator.prototype.goForward = navigatorForwardFn;
3955ModesNavigator.prototype.gotoRecord = navigatorRecordFn;
3956ModesNavigator.prototype.setup = setupNavigatorFn;
3957ModesNavigator.prototype.display = displayNavigatorFn;
3958ModesNavigator.prototype.displayDirect = displayNavigatorDirectFn;
3959ModesNavigator.prototype.displayPages = displayPagesFn;
3960
3961/* -------------------------------------------------------- */
3962
3963// declare ModesSlideshow as a class:
3964
3965function ModesSlideshow(formName, aMode) {
3966    this.formname = formName;
3967    this.mode = aMode;
3968}
3969
3970ModesSlideshow.prototype.controlName = undefined;
3971ModesSlideshow.prototype.datasource = undefined;
3972ModesSlideshow.prototype.position = undefined;
3973ModesSlideshow.prototype.total = undefined;
3974ModesSlideshow.prototype.mode = 'auto';
3975ModesSlideshow.prototype.navigator = undefined;
3976//ModesSlideshow.prototype.gotoNext = slideshowNextFn;
3977//ModesSlideshow.prototype.toggle = toggleSlideshowFn;
3978ModesSlideshow.prototype.setup = setupSlideshowFn;
3979
3980function setupSlideshowFn() {
3981
3982}
3983
3984function toggleSlideshowFn(elt) {
3985    if (this.slideshow) {
3986        this.slideshow.toggle();
3987        if (this.slideshow.isActive)
3988            $(elt).attr('value', 'pause');
3989        else
3990            $(elt).attr('value', 'resume');
3991    }
3992}
3993
3994/*
3995function slideshowNextFn(elt) {
3996    if (this.slideshow) {
3997        this.slideshow.toggle();
3998        if (this.slideshow.isActive)
3999            $(elt).attr('value', 'pause');
4000        else
4001            $(elt).attr('value', 'resume');
4002    }
4003}
4004*/
4005
4006/* -------------------------------------------------------- */
4007
4008// declare ModesBrowser as a class:
4009
4010// alert('about to declare ModesBrowser!');
4011
4012function ModesBrowser(formName) {
4013  this.formname = formName;
4014//  this.modesQuery = '({}<>{})&count=5';
4015  this.modesQuery = '';
4016    this.filters = new Array;
4017//  this.controls = [];
4018  this.recordIds = new Array;
4019  this.navigators = new Array;
4020}
4021
4022jQuery(document).ready( function(){
4023//  initializeModesBrowser();
4024  if (modes_browser) {
4025        modes_browser.displayControls();
4026    }
4027
4028
4029});
4030
4031ModesBrowser.prototype.loadTimeline = loadTimelineFn;
4032ModesBrowser.prototype.loadXMLDoc = loadXMLDocFn;
4033ModesBrowser.prototype.loadMap = loadMapFn;
4034ModesBrowser.prototype.addLayerFromHtml = addLayerFromHtmlFn;
4035ModesBrowser.prototype.eventSource = undefined;
4036ModesBrowser.prototype.updateTimeline = updateTimelineFn;
4037ModesBrowser.prototype.formname = undefined;
4038ModesBrowser.prototype.navigators = undefined;
4039ModesBrowser.prototype.facetDisplay = undefined;
4040ModesBrowser.prototype.searchTermDisplay = undefined;
4041ModesBrowser.prototype.lightbox = undefined;
4042ModesBrowser.prototype.modesMapControl = undefined;
4043ModesBrowser.prototype.modesMap = undefined;
4044ModesBrowser.prototype.modesMapHeight = undefined;
4045ModesBrowser.prototype.modesMapCentreLat = undefined;
4046ModesBrowser.prototype.modesMapCentreLon = undefined;
4047ModesBrowser.prototype.modesMapMagnification = undefined;
4048ModesBrowser.prototype.modesTimelineControl = undefined;
4049ModesBrowser.prototype.modesTimeline = undefined;
4050ModesBrowser.prototype.modesTimelineHeight = undefined;
4051ModesBrowser.prototype.modesTimelineDate = undefined;
4052ModesBrowser.prototype.searchSummary = undefined;
4053ModesBrowser.prototype.displaySearchSummary = displaySearchSummaryFn;
4054// following added as part of new strategy, 24.7.2013:
4055ModesBrowser.prototype.displayControls = displayControlsFn;
4056ModesBrowser.prototype.displayModesDataForDatasource = displayModesDataForDatasourceFn;
4057ModesBrowser.prototype.displayControl = displayControlFn;
4058ModesBrowser.prototype.datasource = undefined;
4059ModesBrowser.prototype.datasources = undefined;
4060ModesBrowser.prototype.defaultDatasource = undefined;
4061ModesBrowser.prototype.controls = undefined;
4062ModesBrowser.prototype.pages = undefined;
4063ModesBrowser.prototype.facets = undefined;
4064ModesBrowser.prototype.datagrid = undefined;
4065ModesBrowser.prototype.homePage = undefined;
4066ModesBrowser.prototype.hitlistPage = undefined;
4067ModesBrowser.prototype.itemPage = undefined;
4068ModesBrowser.prototype.recordsPerPage = undefined;
4069ModesBrowser.prototype.hitlistXslt = undefined;
4070ModesBrowser.prototype.detailXslt = undefined;
4071ModesBrowser.prototype.datagridXslt = undefined;
4072ModesBrowser.prototype.facetDisplayXslt = undefined;
4073ModesBrowser.prototype.modesTimelineXslt = undefined;
4074ModesBrowser.prototype.modesMapXslt = undefined;
4075ModesBrowser.prototype.domainName = undefined;
4076ModesBrowser.prototype.scriptDir = undefined;
4077ModesBrowser.prototype.siteurl = undefined;
4078ModesBrowser.prototype.scriptDomain = undefined;
4079ModesBrowser.prototype.internalScriptDir = undefined;
4080ModesBrowser.prototype.scriptTransport = 'tcpip';
4081ModesBrowser.prototype.scriptServer = 'localhost';
4082ModesBrowser.prototype.connection = connectionFn;
4083ModesBrowser.prototype.imageDir = undefined;
4084ModesBrowser.prototype.imageMaxSize = undefined;
4085ModesBrowser.prototype.simpleSearchIndex = undefined;
4086ModesBrowser.prototype.simpleSearchClearSearch = undefined;
4087ModesBrowser.prototype.modesQuery = undefined;
4088ModesBrowser.prototype.filteredQuery = undefined;
4089ModesBrowser.prototype.filters = undefined;
4090ModesBrowser.prototype.updateQueryWithFilters = updateQueryWithFiltersFn;
4091// ModesBrowser.prototype.displayModesData = displayModesDataFn;
4092ModesBrowser.prototype.urlBase = urlBaseFn;
4093ModesBrowser.prototype.getSeparator = getSeparatorFn;
4094ModesBrowser.prototype.displayDatagrid = displayDatagridFn;
4095ModesBrowser.prototype.displayHitlist = displayHitlistFn;
4096ModesBrowser.prototype.displayDetail = displayDetailFn;
4097ModesBrowser.prototype.displayFacets = displayFacetsFn;
4098ModesBrowser.prototype.displaySimpleSearch = displaySimpleSearchFn;
4099ModesBrowser.prototype.displayMap = displayMapFn;
4100ModesBrowser.prototype.displayRandomRecord = displayRandomRecordFn;
4101ModesBrowser.prototype.displaySlideshow = displaySlideshowFn;
4102ModesBrowser.prototype.displaySlideshowAuto = displaySlideshowAutoFn;
4103ModesBrowser.prototype.displaySlideshowManual = displaySlideshowManualFn;
4104ModesBrowser.prototype.displayObjectCheck = displayObjectCheckFn;
4105ModesBrowser.prototype.displayAddDataToRecords = displayAddDataToRecordsFn;
4106ModesBrowser.prototype.displayProcedureFile = displayProcedureFileFn;
4107ModesBrowser.prototype.displayModesRecords = displayModesRecordsFn;
4108ModesBrowser.prototype.displayModesRecord = displayModesRecordFn;
4109ModesBrowser.prototype.updateHitlist = updateHitlistFn;
4110//ModesBrowser.prototype.slideshowNext = slideshowNextFn;
4111ModesBrowser.prototype.subsetToQuery = subsetToQueryFn;
4112ModesBrowser.prototype.getModesRecord = getModesRecordFn;
4113ModesBrowser.prototype.toggleMapLayer = toggleMapLayerFn;
4114ModesBrowser.prototype.addMapCheckboxes = addMapCheckboxesFn;
4115ModesBrowser.prototype.displayTimeline = displayTimelineFn;
4116ModesBrowser.prototype.refreshTimeline = refreshTimelineFn;
4117ModesBrowser.prototype.addTimelineCheckboxes = addTimelineCheckboxesFn;
4118ModesBrowser.prototype.getIndexSearchTerms = getIndexSearchTermsFn;
4119ModesBrowser.prototype.getOnThisDay = getOnThisDayFn;
4120ModesBrowser.prototype.getSimpleSearchTerms = getSimpleSearchTermsFn;
4121ModesBrowser.prototype.clearSearch = clearSearchFn;
4122ModesBrowser.prototype.findControl = findControlFn;
4123ModesBrowser.prototype.findNavigator = findNavigatorFn;
4124ModesBrowser.prototype.renderInitialRecord = renderInitialRecordFn;
4125ModesBrowser.prototype.toggleSlideshow = toggleSlideshowFn;
4126ModesBrowser.prototype.currentPage = undefined;
4127ModesBrowser.prototype.currentUrl = undefined;
4128ModesBrowser.prototype.currentStart = undefined;
4129ModesBrowser.prototype.transformXml = transformXmlFn;
4130ModesBrowser.prototype.transformLocal = transformLocalFn;
4131ModesBrowser.prototype.findDatasource = findDatasourceFn;
4132ModesBrowser.prototype.recordIds = undefined;
4133ModesBrowser.prototype.submitComments = submitCommentsFn;
4134ModesBrowser.prototype.modes_edit = undefined;
4135ModesBrowser.prototype.slideshow = undefined;
4136ModesBrowser.prototype.debug = false;
4137ModesBrowser.prototype.linkedDataPublished = 'no';
4138ModesBrowser.prototype.version = 'free';
4139ModesBrowser.prototype.browserFolder = 'modesbrowser-free';
4140ModesBrowser.prototype.findFilter = findFilterFn;
4141ModesBrowser.prototype.toggleFilter = toggleFilterFn;
4142ModesBrowser.prototype.forwarder = undefined;
4143ModesBrowser.prototype.pluginsurl = undefined; // added 7.7.2015
4144ModesBrowser.prototype.customOnLoad = undefined; // added 9.7.2015
4145ModesBrowser.prototype.addSelectedRecord = addSelectedRecordFn; // added 16.12.2015
4146ModesBrowser.prototype.resolveDataPattern = resolveDataPatternFn;
4147ModesBrowser.prototype.addInputs = addInputsFn;
4148ModesBrowser.prototype.updateSelectedRecords = updateSelectedRecordsFn;
4149ModesBrowser.prototype.updateRecords = updateRecordsFn;
4150ModesBrowser.prototype.updateXmlWithValues = updateXmlWithValuesFn;
4151ModesBrowser.prototype.updateXmlWithValues2 = updateXmlWithValues2Fn;
4152ModesBrowser.prototype.username = undefined;
4153ModesBrowser.prototype.getFormValues = getFormValuesFn;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.