Merge branch 'master' of ssh://git.indexdata.com/home/git/pub/pazpar2
[pazpar2-moved-to-github.git] / www / iphone / example_client.js
1 /* A very simple client that shows a basic usage of the pz2.js
2 */
3
4 // create a parameters array and pass it to the pz2's constructor
5 // then register the form submit event with the pz2.search function
6 // autoInit is set to true on default
7 var usesessions = false;
8 var pazpar2path = '/service-proxy/';
9 var showResponseType = '';
10 // Facet configuration
11 var querys = {'su': '', 'au': '', 'xt': ''};
12 var query_client_server = {'su': 'subject', 'au': 'author', 'xt': 'xtargets'};
13 var querys_server = {};
14 var useLimit = 1;
15 // Fail to get JSON working stabil.
16 var showResponseType = 'xml';
17 if (document.location.hash == '#pazpar2' || document.location.search.match("useproxy=false")) {
18     usesessions = true;
19     pazpar2path = '/pazpar2/search.pz2';
20     showResponseType = 'xml';
21 }
22
23
24 my_paz = new pz2( { "onshow": my_onshow,
25                     "showtime": 500,            //each timer (show, stat, term, bytarget) can be specified this way
26                     "pazpar2path": pazpar2path,
27                     "oninit": my_oninit,
28                     "onstat": my_onstat,
29                     "onterm": my_onterm_iphone,
30                     "termlist": "xtargets,subject,author",
31                     "onbytarget": my_onbytarget,
32                     "usesessions" : usesessions,
33                     "showResponseType": showResponseType,
34                     "onrecord": my_onrecord } );
35 // some state vars
36 var curPage = 1;
37 var recPerPage = 100;
38 var recToShowPageSize = 20;
39 var recToShow = recToShowPageSize;
40 var recIDs = {};
41 var totalRec = 0;
42 var curDetRecId = '';
43 var curDetRecData = null;
44 var curSort = 'relevance';
45 var curFilter = 'ALL';
46 var submitted = false;
47 var SourceMax = 16;
48 var SubjectMax = 10;
49 var AuthorMax = 10;
50 var tab = "recordview"; 
51
52 var triedPass = "";
53 var triedUser = "";
54
55 function loginFormSubmit() {
56     triedUser = document.loginForm.username.value;
57     triedPass = document.loginForm.password.value;
58     auth.login( {"username": triedUser,
59                 "password": triedPass},
60         authCb, authCb);
61 }
62
63 function handleKeyPress(e)  
64 {  
65   var key;  
66   if(window.event)  
67     key = window.event.keyCode;  
68   else  
69     key = e.which;  
70
71   if(key == 13 || key == 10)  
72   {  
73       button = document.getElementById('button');
74       button.focus();
75       button.click();
76
77       return false;  
78   }  
79   else  
80     return true;  
81 }  
82
83 function authCb(authData) {
84     if (!authData.loginFailed) {
85         triedUser = "";
86         triedPass = "";
87     }
88
89     if (authData.loggedIn == true) {        
90         showhide("recordview");
91     }
92 }
93
94 function logOutClick() {
95     auth.logOut(authCb, authCb);
96 }
97
98 function loggedOut() {
99     var login = document.getElementById("login");
100     login.innerHTML = 'Login';
101 }
102
103 function loggingOutFailed() {
104     alert("Logging out failed");
105 }
106
107 function login() {
108     showhide("login");
109 }
110
111 function logout() {
112     auth.logOut(loggedOut, loggingOutFailed, true);
113 }
114
115 function logInOrOut() {
116     var loginElement = document.getElementById("login");
117     if (loginElement.innerHTML == 'Login')
118         login();
119     else
120         logout();
121 }
122 function loggedIn() {
123     var login = document.getElementById("login");
124     login.innerHTML = 'Logout(' + auth.displayName + ')';
125     document.getElementById("log").innerHTML = login.innerHTML;
126 }
127
128 function auth_check() {
129     auth.check(loggedIn, login);
130     domReady();
131 }
132
133 //
134 // Pz2.js event handlers:
135 //
136 function my_oninit() {
137     my_paz.stat();
138     my_paz.bytarget();
139 }
140
141 function showMoreRecords() {
142     var i = recToShow;
143     recToShow += recToShowPageSize;
144     for ( ; i < recToShow && i < recPerPage; i++) {
145         var element = document.getElementById(recIDs[i]);
146         if (element)
147             element.style.display = '';
148     }
149     if (i == recPerPage) {
150         var element = document.getElementById('recdiv_END');
151         if (element)
152             element.style.display = 'none';
153     }
154 }
155
156 function my_onshow(data) {
157     totalRec = data.merged;
158     // move it out
159     var pager = document.getElementById("pager");
160     pager.innerHTML = "";
161     pager.innerHTML +='<hr/><div style="float: right">Displaying: ' 
162                     + (data.start + 1) + ' to ' + (data.start + data.num) +
163                      ' of ' + data.merged + ' (found: ' 
164                      + data.total + ')</div>';
165     drawPager(pager);
166
167     var results = document.getElementById("results");
168     
169     var html = [];
170     if (data.hits == undefined) 
171         return ;
172     var style = '';
173     for (var i = 0; i < data.hits.length; i++) {
174         var hit = data.hits[i];
175         var recID = "recdiv_" + hit.recid; 
176         //var recID = "recdiv_" + i; 
177         recIDs[i] = recID;
178         if (i == recToShow)
179             style = ' style="display:none" ';
180         html.push('<li id="' + recID + '" ' + style +  '>' 
181                   +'<a href="#" id="rec_'+hit.recid
182                   +'" onclick="showDetails(this.id);return false;">' 
183                   + hit["md-title"] +'</a> '); 
184         if (hit["md-title-remainder"] !== undefined) {
185             html.push('<a href="#">' + hit["md-title-remainder"] + ' </a> ');
186         }
187         if (hit["md-author"] != undefined) {
188             html.push('<a href="#">'+hit["md-author"]+'</a> ');
189         }
190         else if (hit["md-title-responsibility"] !== undefined) {
191             html.push('<a href="#">'+hit["md-title-responsibility"]+'</a> ');
192         }
193         if (hit.recid == curDetRecId) {
194             html.push(renderDetails_iphone(curDetRecData));
195         }
196         html.push('</li>');
197     }
198     // set up "More..." if needed. 
199     style = 'display:none';
200     if (recToShow < recPerPage) {
201         style = 'display:block';
202     }
203     html.push('<li id="recdiv_END" style="' + style + '"><a onclick="showMoreRecords()">More...</a></li>');     
204
205     replaceHtml(results, html.join(''));
206 }
207
208 function my_onstat(data) {
209     var stat = document.getElementById("stat");
210     if (stat == null)
211         return;
212     
213     stat.innerHTML = '<b> .:STATUS INFO</b> -- Active clients: '
214                         + data.activeclients
215                         + '/' + data.clients + ' -- </span>'
216                         + '<span>Retrieved records: ' + data.records
217                         + '/' + data.hits + ' :.</span>';
218 }
219
220 function showhide(newtab) {
221     var showtermlist = false;
222     if (newtab != null)
223         tab = newtab;
224     
225     if (tab == "recordview") {
226         document.getElementById("recordview").style.display = '';
227     }
228     else 
229         document.getElementById("recordview").style.display = 'none';
230
231     if (tab == "xtargets") {
232         document.getElementById("term_xtargets").style.display = '';
233         showtermlist = true;
234     }
235     else
236         document.getElementById("term_xtargets").style.display = 'none';
237
238     if (tab == "subjects") {
239         document.getElementById("term_subjects").style.display = '';
240         showtermlist = true;
241     }
242     else
243         document.getElementById("term_subjects").style.display = 'none';
244
245     if (tab == "authors") {
246         document.getElementById("term_authors").style.display = '';
247         showtermlist = true;
248     }
249     else
250         document.getElementById("term_authors").style.display = 'none';
251
252     if (showtermlist == false) 
253         document.getElementById("termlist").style.display = 'none';
254     else 
255         document.getElementById("termlist").style.display = '';
256
257     var tabDiv = document.getElementById("loginDiv");
258     if (tab == "login") {
259         tabDiv.style.display = '';
260     }
261     else {
262         tabDiv.style.display = 'none';
263     }
264 }
265
266 function my_onterm(data) {
267     var termlists = [];
268     
269     termlists.push('<div id="term_xtargets" >');
270     termlists.push('<h4 class="termtitle">Sources</h4>');
271     termlists.push('<ul>');
272     termlists.push('<li><a href="#" target_id="reset_xt" onclick="limitOrResetTarget(\'reset_xt\',\'All\');return false;">All</a></li>');
273     for (var i = 0; i < data.xtargets.length && i < SourceMax; i++ ) {
274         termlists.push('<li><a href="#" target_id='+data.xtargets[i].id
275             + ' onclick="limitOrResetTarget(this.getAttribute(\'target_id\'), \'' + data.xtargets[i].name + '\');return false;">' 
276             + data.xtargets[i].name + ' (' + data.xtargets[i].freq + ')</a></li>');
277     }
278     termlists.push('</ul>');
279     termlists.push('</div>');
280      
281     termlists.push('<div id="term_subjects" >');
282     termlists.push('<h4>Subjects</h4>');
283     termlists.push('<ul>');
284     termlists.push('<li><a href="#" target_id="reset_su" onclick="limitOrResetQuery(\'reset_su\',\'All\');return false;">All</a></li>');
285     for (var i = 0; i < data.subject.length && i < SubjectMax; i++ ) {
286         termlists.push('<li><a href="#" onclick="limitOrResetQuery(\'su\', \'' + data.subject[i].name + '\');return false;">' 
287                        + data.subject[i].name + ' (' + data.subject[i].freq + ')</a></li>');
288     }
289     termlists.push('</ul>');
290     termlists.push('</div>');
291             
292     termlists.push('<div id="term_authors" >');
293     termlists.push('<h4 class="termtitle">Authors</h4>');
294     termlists.push('<ul>');
295     termlists.push('<li><a href="#" onclick="limitOrResetQuery(\'reset_au\',\'All\');return false;">All<a></li>');
296     for (var i = 0; i < data.author.length && i < AuthorMax; i++ ) {
297         termlists.push('<li><a href="#" onclick="limitOrResetQuery(\'au\', \'' + data.author[i].name +'\');return false;">' 
298                             + data.author[i].name 
299                             + '  (' 
300                             + data.author[i].freq 
301                             + ')</a></li>');
302     }
303     termlists.push('</ul>');
304     termlists.push('</div>');
305     var termlist = document.getElementById("termlist");
306     replaceHtml(termlist, termlists.join(''));
307     showhide();
308 }
309
310 var termlist = {};
311 function my_onterm_iphone(data) {
312     my_onterm(data);
313     var targets = "reset_xt|All\n";
314     
315     for (var i = 0; i < data.xtargets.length; i++ ) {
316         
317         targets = targets + data.xtargets[i].id + "|" + data.xtargets[i].name + "|" + data.xtargets[i].freq + "\n";
318     }
319     termlist["xtargets"] = targets;
320     var subjects = "reset_su|All\n";
321     for (var i = 0; i < data.subject.length; i++ ) {
322         subjects = subjects + "su" + "|" + data.subject[i].name + "|" + data.subject[i].freq + "\n";
323     }
324     termlist["subjects"] = subjects;
325     var authors = "reset_au|All\n";
326     for (var i = 0; i < data.author.length; i++ ) {
327         authors = authors + "au" + "|" + data.author[i].name + "|" + data.author[i].freq + "\n";
328     }
329     termlist["authors"] = authors;
330     callback.send("termlist", "refresh");
331 }
332
333 function getTargets() {
334         return termlist['xtargets'];
335 }
336
337 function getSubjects() {
338         return termlist['subjects'];
339 }
340
341 function getAuthors() {
342         return termlist['authors'];
343 }
344
345 function my_onrecord(data) {
346     // FIXME: record is async!!
347     clearTimeout(my_paz.recordTimer);
348     // in case on_show was faster to redraw element
349     var detRecordDiv = document.getElementById('det_'+data.recid);
350     if (detRecordDiv) return;
351     curDetRecData = data;
352     var recordDiv = document.getElementById('recdiv_'+curDetRecData.recid);
353     var html = renderDetails_iphone(curDetRecData);
354     recordDiv.innerHTML += html;
355 }
356
357 function my_onrecord_iphone(data) {
358     my_onrecord(data);
359     callback.send("record", data.recid, data, data.xtargets[i].freq);
360 }
361
362
363 function my_onbytarget(data) {
364     var targetDiv = document.getElementById("bytarget");
365     var table ='<table><thead><tr><td>Target ID</td><td>Hits</td><td>Diags</td>'
366         +'<td>Records</td><td>State</td></tr></thead><tbody>';
367     
368     for (var i = 0; i < data.length; i++ ) {
369         table += "<tr><td>" + data[i].id +
370             "</td><td>" + data[i].hits +
371             "</td><td>" + data[i].diagnostic +
372             "</td><td>" + data[i].records +
373             "</td><td>" + data[i].state + "</td></tr>";
374     }
375
376     table += '</tbody></table>';
377     targetDiv.innerHTML = table;
378 }
379
380 ////////////////////////////////////////////////////////////////////////////////
381 ////////////////////////////////////////////////////////////////////////////////
382
383 // wait until the DOM is ready
384 function domReady () 
385
386     document.search.onsubmit = onFormSubmitEventHandler;
387     document.search.query.value = '';
388     document.select.sort.onchange = onSelectDdChange;
389     document.select.perpage.onchange = onSelectDdChange;
390     if (document.location.search.match("inApp=true")) 
391         applicationMode(true);
392     else
393         applicationMode(false);
394 }
395  
396 function applicationMode(newmode) 
397 {
398     var searchdiv = document.getElementById("searchForm");
399     if (newmode)
400         inApp = newmode;
401     if (inApp) {
402         document.getElementById("heading").style.display="none";
403         searchdiv.style.display = 'none';
404     }
405     else { 
406         
407         document.getElementById("nav").style.display="";
408         document.getElementById("normal").style.display="inline";
409         document.getElementById("normal").style.visibility="";
410         searchdiv.style.display = '';
411         document.search.onsubmit = onFormSubmit;
412     }
413     callback.init();
414 }
415 // when search button pressed
416 function onFormSubmitEventHandler() 
417 {
418     resetPage();
419     document.getElementById("logo").style.display = 'none';
420     loadSelect();
421     triggerSearch();
422     submitted = true;
423     return true;
424 }
425
426 function onSelectDdChange()
427 {
428     if (!submitted) return false;
429     resetPage();
430     loadSelect();
431     my_paz.show(0, recPerPage, curSort);
432     return false;
433 }
434
435 function resetPage()
436 {
437     curPage = 1;
438     totalRec = 0;
439 }
440
441 function getFacets() {
442     var result = "";
443     for (var key in querys_server) {
444         if (result.length > 0)
445             result += ","
446         result += querys_server[key];
447     }
448     return result;
449 }
450
451 function triggerSearch ()
452 {
453     // Restore to initial page size
454     recToShow = recToShowPageSize;
455     my_paz.search(document.search.query.value, recPerPage, curSort, curFilter, undefined,
456         {
457            "limit" : getFacets() 
458         }
459         );
460 }
461
462 function loadSelect ()
463 {
464     curSort = document.select.sort.value;
465     recPerPage = document.select.perpage.value;
466 }
467
468 // limit the query after clicking the facet
469 function limitQuery(field, value)
470 {
471   var newQuery = ' and ' + field + '="' + value + '"';
472   querys[field] += newQuery;
473   document.search.query.value += newQuery;
474   onFormSubmitEventHandler();
475   showhide("recordview");
476 }
477
478 // limit the query after clicking the facet
479 function limitQueryServer(field, value)
480 {
481     // Check for client field usage
482     var fieldname = query_client_server[field];
483     if (!fieldname) 
484         fieldname = field;      
485     
486     var newQuery = fieldname + '=' + value.replace(",", "\\,").replace("|", "\\,");
487     // Does it already exists?
488     if (querys_server[fieldname]) 
489         querys_server[fieldname] += "," + newQuery;
490     else
491         querys_server[fieldname] = newQuery;
492 //  document.search.query.value += newQuery;
493   onFormSubmitEventHandler();
494   showhide("recordview");
495 }
496
497
498
499 // limit the query after clicking the facet
500 function removeQuery (field, value) {
501         document.search.query.value.replace(' and ' + field + '="' + value + '"', '');
502     onFormSubmitEventHandler();
503     showhide("recordview");
504 }
505
506 // limit the query after clicking the facet
507 function limitOrResetQuery (field, value, selected) {
508     if (useLimit) {
509         limitOrResetQueryServer(field,value, selected);
510         return ;
511     }
512     if (field == 'reset_su' || field == 'reset_au') {
513         var reset_field = field.substring(6);
514         document.search.query.value = document.search.query.value.replace(querys[reset_field], '');
515         querys[reset_field] = '';
516         onFormSubmitEventHandler();
517         showhide("recordview");
518     }
519     else 
520         limitQuery(field, value);
521         //alert("limitOrResetQuerry: query after: " + document.search.query.value);
522 }
523
524 // limit the query after clicking the facet
525 function limitOrResetQueryServer (field, value, selected) {
526     if (field.substring(0,6) == 'reset_') {
527         var clientname = field.substring(6);
528         var fieldname = query_client_server[clientname];
529         if (!fieldname) 
530             fieldname = clientname;     
531         delete querys_server[fieldname];
532         onFormSubmitEventHandler();
533         showhide("recordview");
534     }
535     else 
536         limitQueryServer(field, value);
537         //alert("limitOrResetQuerry: query after: " + document.search.query.value);
538 }
539
540
541
542
543 // limit by target functions
544 function limitTarget (id, name)
545 {
546     curFilter = 'pz:id=' + id;
547     resetPage();
548     loadSelect();
549     triggerSearch();
550     showhide("recordview");
551     return false;
552 }
553
554 function delimitTarget ()
555 {
556     curFilter = 'ALL'; 
557     resetPage();
558     loadSelect();
559     triggerSearch();
560     return false;
561 }
562
563 function limitOrResetTarget(id, name) {
564         if (id == 'reset_xt') {
565                 delimitTarget();
566         }
567         else {
568                 limitTarget(id,name);
569         }
570 }
571
572 function drawPager (pagerDiv)
573 {
574     //client indexes pages from 1 but pz2 from 0
575     var onsides = 6;
576     var pages = Math.ceil(totalRec / recPerPage);
577     
578     var firstClkbl = ( curPage - onsides > 0 ) 
579         ? curPage - onsides
580         : 1;
581
582     var lastClkbl = firstClkbl + 2*onsides < pages
583         ? firstClkbl + 2*onsides
584         : pages;
585
586     var prev = '<span id="prev">&#60;&#60; Prev</span><b> | </b>';
587     if (curPage > 1)
588         var prev = '<a href="#" id="prev" onclick="pagerPrev();">'
589         +'&#60;&#60; Prev</a><b> | </b>';
590
591     var middle = '';
592     for(var i = firstClkbl; i <= lastClkbl; i++) {
593         var numLabel = i;
594         if(i == curPage)
595             numLabel = '<b>' + i + '</b>';
596
597         middle += '<a href="#" onclick="showPage(' + i + ')"> '
598             + numLabel + ' </a>';
599     }
600     
601     var next = '<b> | </b><span id="next">Next &#62;&#62;</span>';
602     if (pages - curPage > 0)
603     var next = '<b> | </b><a href="#" id="next" onclick="pagerNext()">'
604         +'Next &#62;&#62;</a>';
605
606     predots = '';
607     if (firstClkbl > 1)
608         predots = '...';
609
610     postdots = '';
611     if (lastClkbl < pages)
612         postdots = '...';
613
614     pagerDiv.innerHTML += '<div style="float: none">' 
615         + prev + predots + middle + postdots + next + '</div><hr/>';
616 }
617
618 function showPage (pageNum)
619 {
620     curPage = pageNum;
621     my_paz.showPage( curPage - 1 );
622 }
623
624 // simple paging functions
625
626 function pagerNext() {
627     if ( totalRec - recPerPage*curPage > 0) {
628         my_paz.showNext();
629         curPage++;
630     }
631 }
632
633 function pagerPrev() {
634     if ( my_paz.showPrev() != false )
635         curPage--;
636 }
637
638 // swithing view between targets and records
639
640 function switchView(view) {
641     
642     var targets = document.getElementById('targetview');
643     var records = document.getElementById('recordview');
644     
645     switch(view) {
646         case 'targetview':
647             targets.style.display = "block";            
648             records.style.display = "none";
649             break;
650         case 'recordview':
651             targets.style.display = "none";            
652             records.style.display = "block";
653             break;
654         default:
655             alert('Unknown view.');
656     }
657 }
658
659 // detailed record drawing
660 function showDetails (prefixRecId) {
661     var recId = prefixRecId.replace('rec_', '');
662     var oldRecId = curDetRecId;
663     curDetRecId = recId;
664     
665     // remove current detailed view if any
666     var detRecordDiv = document.getElementById('det_'+oldRecId);
667     //alert("oldRecId: " + oldRecId + " " + detRecordDiv != null); 
668     // lovin DOM!
669     if (detRecordDiv)
670       detRecordDiv.parentNode.removeChild(detRecordDiv);
671
672     // if the same clicked, just hide
673     if (recId == oldRecId) {
674         curDetRecId = '';
675         curDetRecData = null;
676         return;
677     }
678     // request the record
679     my_paz.record(recId);
680 }
681
682 function replaceHtml(el, html) {
683   var oldEl = typeof el === "string" ? document.getElementById(el) : el;
684   /*@cc_on // Pure innerHTML is slightly faster in IE
685     oldEl.innerHTML = html;
686     return oldEl;
687     @*/
688   var newEl = oldEl.cloneNode(false);
689   newEl.innerHTML = html;
690   oldEl.parentNode.replaceChild(newEl, oldEl);
691   /* Since we just removed the old element from the DOM, return a reference
692      to the new element, which can be used to restore variable references. */
693   return newEl;
694 };
695
696 function renderDetails(data, marker)
697 {
698     var details = '<div class="details" id="det_'+data.recid+'"><table>';
699     if (marker) details += '<tr><td>'+ marker + '</td></tr>';
700     if (data["md-title"] != undefined) {
701         details += '<tr><td><b>Title</b></td><td><b>:</b> '+data["md-title"];
702         if (data["md-title-remainder"] !== undefined) {
703               details += ' : <span>' + data["md-title-remainder"] + ' </span>';
704         }
705         if (data["md-author"] !== undefined) {
706               details += ' <span><i>'+ data["md-auhtor"] +'</i></span>';
707         }
708           details += '</td></tr>';
709     }
710     if (data["md-date"] != undefined)
711         details += '<tr><td><b>Date</b></td><td><b>:</b> ' + data["md-date"] + '</td></tr>';
712     if (data["md-author"] != undefined)
713         details += '<tr><td><b>Author</b></td><td><b>:</b> ' + data["md-author"] + '</td></tr>';
714     if (data["md-electronic-url"] != undefined)
715         details += '<tr><td><b>URL</b></td><td><b>:</b> <a href="' + data["md-electronic-url"] + '" target="_blank">' + data["md-electronic-url"] + '</a>' + '</td></tr>';
716     if (data["location"][0]["md-subject"] != undefined)
717         details += '<tr><td><b>Subject</b></td><td><b>:</b> ' + data["location"][0]["md-subject"] + '</td></tr>';
718     if (data["location"][0]["@name"] != undefined)
719         details += '<tr><td><b>Location</b></td><td><b>:</b> ' + data["location"][0]["@name"] + " (" +data["location"][0]["@id"] + ")" + '</td></tr>';
720     details += '</table></div>';
721     return details;
722 }
723
724 function renderLine(title, value) {
725     if (value != undefined)
726         return '<li><h3>' + title + '</h3> <big>' + value + '</big></li>';
727     return '';
728 }
729
730 function renderLineURL(title, URL, display) {
731     if (URL != undefined)
732         return '<li><h3>' + title + '</h3> <a href="' + URL + '" target="_blank">' + display + '</a></li>';
733     return '';
734 }
735
736 function renderLineEmail(dtitle, email, display) {
737     if (email != undefined)
738         return '<li><h3>' + title + '</h3> <a href="mailto:' + email + '" target="_blank">' + display + '</a></li>';
739     return '';
740 }
741
742 function renderDetails_iphone(data, marker)
743 {
744         //return renderDetails(data,marker);
745
746     if (!data) 
747         return ""; 
748     var details = '<div class="details" id="det_'+data.recid+'" >'
749 /*
750     details = '<div id="header" id="det_'+data.recid+'">' 
751         + '<h1>Detailed Info</h1>' 
752         + '<a id="backbutton" href="hidedetail(\'det_' + data.recid + '\')">Back</a>' 
753         + '</div>';
754 */
755     if (marker) 
756         details += '<h4>'+ marker + '</h4>'; 
757     details += '<ul class="field">';
758     if (data["md-title"] != undefined) {
759         details += '<li><h3>Title</h3> <big> ' + data["md-title"];
760         if (data["md-title-remainder"] !== undefined) {
761               details += ' ' + data["md-title-remainder"] + ' ';
762         }
763         if (data["md-author"] !== undefined) {
764               details += '<i>'+ data["md-author"] +'</i>';
765         } else if (data["md-title-responsibility"] !== undefined) {
766               details += '<i>'+ data["md-title-responsibility"] +'</i>';
767         }
768         details += '</big>'
769         details += '</li>'
770     }
771     details 
772         +=renderLine('Date',    data["md-date"])
773         + renderLine('Author',  data["md-author"])
774         + renderLineURL('URL',  data["md-electronic-url"], data["md-electronic-url"])
775         + renderLine('Subject', data["location"][0]["md-subject"]);
776     
777     if (data["location"][0]["@name"] != undefined)
778         details += renderLine('Location', data["location"][0]["@name"] + " (" +data["location"][0]["@id"] + ")");
779     details += '</ul></div>';
780     return details;
781 }
782
783 //EOF