source: gs3-installations/thewillow/trunk/sites/thewillow/dlcol-chatgpt/iframe-page/dl-chatgpt_files/javascript-global-functions.js.download@ 38779

Last change on this file since 38779 was 38779, checked in by davidb, 3 months ago

Files that need to be moved to <gsdl3srchome>/web to provide a static web page example using an iframe to embedd the React/NextJS based chatbot

File size: 44.3 KB
Line 
1var SUCCESS = 1;
2var ACCEPTED = 2;
3var ERROR = 3;
4var CONTINUING = 10;
5var COMPLETED = 11;
6var HALTED = 12;
7
8var runningAjaxCalls = [];
9
10gs.functions = {};
11
12gs.jqGet = function(id)
13{
14 return $("#" + id.replace(/\./g, "\\.").replace(/:/g,"\\:"));
15}
16
17// Debugging function to print a string's non-basic chars in hex. So does string2hex on all non-basic and non-printable ASCII chars
18// Dr Bainbridge said that printing anything with charCode over 128 in hex is okay, but I'd already made extra allowances for non-printable ASCII
19// Based on https://stackoverflow.com/questions/36637146/javascript-encode-string-to-hex/36637293
20// https://stackoverflow.com/questions/21647928/javascript-unicode-string-to-hex
21gs.functions.debug_unicode_string = function(str) {
22 var hex, i;
23
24 var result = "";
25 for (i=0; i<str.length; i++) {
26 charcode = str.charCodeAt(i);
27 // ASCII table: https://cdn.sparkfun.com/assets/home_page_posts/2/1/2/1/ascii_table_black.png
28 // if the unicode character code pt is less than the ASCII code for space and greater than for tilda, let's display the char in hex (x0000 format)
29 if(charcode < 20 || charcode > 126) { //doesn't work: if(str.charAt(i) < ' ' || str.charAt(i) > '~') {
30 hex = charcode.toString(16);
31 result += "x{" + ("000"+hex).slice(-4) + "}"; // looks like: x{4-char-codepoint}
32 }
33 else {
34 result += str.charAt(i);
35 }
36 }
37
38 return result;
39}
40
41gs.functions.ajaxRequest = function()
42{
43 var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
44 if(window.ActiveXObject)
45 {
46 for (var i=0; i<activexmodes.length; i++)
47 {
48 try
49 {
50 return new ActiveXObject(activexmodes[i]);
51 }
52 catch(e){}
53 }
54 }
55 else if (window.XMLHttpRequest)
56 {
57 return new XMLHttpRequest();
58 }
59 else
60 {
61 return false
62 }
63}
64
65gs.functions.hasClass = function(elem, classVal)
66{
67 if(!elem || !elem.getAttribute("class"))
68 {
69 return false;
70 }
71
72 return (elem.getAttribute("class").search(classVal) != -1)
73}
74
75gs.functions.getElementsByClassName = function(cl)
76{
77 var nodes = new Array();
78 var classRegEx = new RegExp('\\b'+cl+'\\b');
79 var allElems = document.getElementsByTagName('*');
80
81 for (var i = 0; i < allElems.length; i++)
82 {
83 var classes = allElems[i].className;
84 if (classRegEx.test(classes))
85 {
86 nodes.push(allElems[i]);
87 }
88 }
89 return nodes;
90};
91
92gs.functions.makeToggle = function(buttons, divs)
93{
94 var buttonArray = (buttons.length) ? buttons : [buttons];
95 var divArray = (divs.length) ? divs : [divs];
96
97 for(var i = 0; i < buttonArray.length; i++)
98 {
99 buttonArray[i].onclick = function()
100 {
101 for(var j = 0; j < divArray.length; j++)
102 {
103 if(divArray[j].style.display == "none")
104 {
105 divArray[j].style.display = "block";
106 }
107 else
108 {
109 divArray[j].style.display = "none";
110 }
111 }
112
113 for(var j = 0; j < buttonArray.length; j++)
114 {
115 if(buttonArray[j].getAttribute("src") == gs.imageURLs.collapse)
116 {
117 buttonArray[j].setAttribute("src", gs.imageURLs.expand);
118 }
119 else if(buttonArray[j].getAttribute("src") == gs.imageURLs.expand)
120 {
121 buttonArray[j].setAttribute("src", gs.imageURLs.collapse);
122 }
123 }
124 };
125 }
126}
127
128gs.functions.checkForErrors = function(xml)
129{
130 var errorElems = xml.getElementsByTagName("error");
131
132 if(errorElems && errorElems.length > 0)
133 {
134 var errorString = gs.text.dse.error_saving_changes + ": ";
135 for(var i = 0; i < errorElems.length; i++)
136 {
137 errorString += " " + errorElems.item(i).firstChild.nodeValue;
138 }
139 alert(errorString);
140 return true;
141 }
142 return false; //No errors
143}
144
145gs.functions.validateXML = function(txt)
146{
147 // code for IE
148 if (window.ActiveXObject)
149 {
150 var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
151 xmlDoc.async = "false";
152 xmlDoc.loadXML(document.all(txt).value);
153
154 if(xmlDoc.parseError.errorCode!=0)
155 {
156 txt = dse.error_code + ": " + xmlDoc.parseError.errorCode + "\n";
157 txt = txt + dse.error_reason + ": " + xmlDoc.parseError.reason;
158 txt = txt + dse.error_line + ": " + xmlDoc.parseError.line;
159 console.log(txt);
160 return null;
161 }
162
163 return xmlDoc;
164 }
165 // code for Mozilla, Firefox, Opera, etc.
166 else if (document.implementation.createDocument)
167 {
168 var parser = new DOMParser();
169 var xmlDoc = parser.parseFromString(txt,"text/xml");
170
171 if (xmlDoc.getElementsByTagName("parsererror").length > 0)
172 {
173 console.log(gs.text.dse.xml_error);
174 return null;
175 }
176
177 return xmlDoc;
178 }
179 else
180 {
181 console.log(gs.text.dse.browse_cannot_validate_xml);
182 }
183 return null;
184}
185
186gs.functions.buildCollections = function(collections, finalFunction)
187{
188 if(!collections || collections.length == 0)
189 {
190 console.log("List of collections to build is empty");
191 return;
192 }
193
194 var counter = 0;
195 var buildFunction = function()
196 {
197 var ajax = new gs.functions.ajaxRequest();
198 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=BuildCollection&s1.collection=" + collections[counter]);
199 ajax.onreadystatechange = function()
200 {
201 if(ajax.readyState == 4 && ajax.status == 200)
202 {
203 var text = ajax.responseText;
204 var xml = gs.functions.validateXML(text);
205
206 if(!xml || gs.functions.checkForErrors(xml))
207 {
208 console.log("Could not build collection -> " + collections[counter] + ", aborting");
209 return;
210 }
211
212 var status = xml.getElementsByTagName("status")[0];
213 var pid = status.getAttribute("pid");
214
215 gs.functions.startCheckLoop(pid, "BuildCollection", function()
216 {
217 var localAjax = new gs.functions.ajaxRequest();
218 localAjax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ActivateCollection&s1.collection=" + collections[counter], true);
219 localAjax.onreadystatechange = function()
220 {
221 if(localAjax.readyState == 4 && localAjax.status == 200)
222 {
223 var localText = localAjax.responseText;
224 var localXML = gs.functions.validateXML(localText);
225
226 if(!xml || gs.functions.checkForErrors(xml))
227 {
228 console.log("Could not activate collection -> " + collections[counter] + ", aborting");
229 return;
230 }
231
232 var localStatus = localXML.getElementsByTagName("status")[0];
233 if(localStatus)
234 {
235 var localPID = localStatus.getAttribute("pid");
236 gs.functions.startCheckLoop(localPID, "ActivateCollection", function()
237 {
238 if (++counter == collections.length)
239 {
240 //Run this function once we are done building all the collections
241 if(finalFunction){finalFunction();}
242 }
243 else
244 {
245 buildFunction();
246 }
247 });
248 }
249 }
250 }
251 localAjax.send();
252 });
253 }
254 }
255 ajax.send();
256 }
257 buildFunction();
258}
259
260gs.functions.startCheckLoop = function(pid, serverFunction, callbackFunction)
261{
262 var ajaxFunction = function()
263 {
264 var ajax = new gs.functions.ajaxRequest();
265 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=s&ro=1&s=" + serverFunction + "&s1.pid=" + pid, true);
266 ajax.onreadystatechange = function()
267 {
268 if(ajax.readyState == 4 && ajax.status == 200)
269 {
270 var text = ajax.responseText;
271 var xml = gs.functions.validateXML(text);
272
273 if(!xml || gs.functions.checkForErrors(xml))
274 {
275 console.log("Could not check status of " + serverFunction + ", there was an error in the XML, aborting");
276 return;
277 }
278
279 var status = xml.getElementsByTagName("status")[0];
280 var code = status.getAttribute("code");
281
282 if (code == COMPLETED || code == SUCCESS)
283 {
284 callbackFunction();
285 }
286 else if (code == HALTED || code == ERROR)
287 {
288 console.log("Could not check status of " + serverFunction + ", there was an error on the server, aborting");
289 }
290 else
291 {
292 setTimeout(ajaxFunction, 1000);
293 }
294 }
295 }
296 ajax.send();
297 }
298 ajaxFunction();
299}
300
301function inc(a, b)
302{
303 var carry = 0;
304 var num = 0;
305 var i = 0;
306
307 while((carry || (i < a.length) || (i < b.length)) && (i < 100))
308 {
309 num = carry;
310 if(i < a.length){num += a[i];}
311 if(i < b.length){num += b[i];}
312
313 if(num >= 256)
314 {
315 num -= 256;
316 carry = 1;
317 }
318 else
319 {
320 carry = 0;
321 }
322
323 a[i] = num;
324
325 i++;
326 }
327}
328
329function ifposDec(a, b)
330{
331 var carry = 0;
332 var num = 0;
333 var i = 0;
334
335 if(b.length > a.length){return a;}
336 if(b.length == a.length)
337 {
338 i = a.length - 1;
339 while(i >= 0)
340 {
341 if(a[i] > b[i]){break;}
342 if(a[i] < b[i]){return a;}
343 i--;
344 }
345 }
346
347 i = 0;
348 var len = 0;
349 var outString = "";
350 while((i < a.length) || (i < b.length))
351 {
352 num = -carry;
353 if(i < a.length){num += a[i];}
354 if(i < b.length){num -= b[i];}
355
356 if(num < 0)
357 {
358 num += 256;
359 carry = 1;
360 }
361 else
362 {
363 carry = 0;
364 }
365
366 a[i] = num;
367 outString += num + ","
368 i++
369
370 if(num != 0){len = i}
371 }
372
373 if(len < a.length)
374 {
375 a = a.slice(0, len);
376 }
377
378 return a;
379}
380
381function convertNum(a)
382{
383 var result = new Array();
384 var i;
385 var convert = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
386
387 if(a.length == 0)
388 {
389 result.push("0");
390 return result;
391 }
392
393 for(i = a.length - 1; i >= 0; i--)
394 {
395 result.push(convert[Math.floor(a[i]/16)]);
396 result.push(convert[Math.floor(a[i]%16)]);
397 }
398
399 var resultString = "";
400 for(var j = 0; j < result.length; j++)
401 {
402 resultString += result[j];
403 }
404
405 return resultString;
406}
407
408gs.functions.hashString = function(str)
409{
410 var remainder = new Array();
411 var primePow = new Array();
412 var pow =
413 [
414 255, 255, 255,
415 255, 255, 255,
416 255, 255, 255,
417 255, 255, 1
418 ];
419
420 for(var i = 0; i < 8; i++)
421 {
422 primePow.push(pow.slice()); //The javascript way to do an array copy (yuck!)
423 inc(pow, pow);
424 }
425
426 for(var i = 0; i < str.length; i++)
427 {
428 var c = str.charCodeAt(i);
429
430 if(remainder.length == 99)
431 {
432 return null;
433 }
434
435 for(var j = remainder.length; j > 0; j--)
436 {
437 remainder[j] = remainder[j-1];
438 }
439 remainder[0] = c;
440
441 for(var j = 7; j >= 0; j--)
442 {
443 remainder = ifposDec(remainder, primePow[j]);
444 }
445 }
446
447 return convertNum(remainder);
448}
449
450// This method performs an AJAX call after working out, based on parameters and internal decision-making code,
451// if it's using GET or POST,
452// asynchronous or synchronous AJAX,
453// jQuery's .ajax() method or gsajaxapi.js' regular JavaScript way of calling AJAX (necessary functions
454// now ported from GS2 to GS3)
455// and whether it needs to transmit the payload in URL or data structure (Java object) form.
456// In the past, the AJAX calls to metadataserver.pl only dealt with URLs and used jQuery .ajax(). As a
457// consequence of this particular combination, the calls in the past were all GET operations.
458//
459// - payload param: contains both the URL form and the data object form of the package to transmit over
460// AJAX to metadataserver.pl. Based on the parameters and some internal variables, _callMetadataServer()
461// determines which to use.
462// - opts param: No function overloading in JavaScript. Can pass a custom object, however can pass opts,
463// see http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices
464//
465// BEWARE:
466// errorResponseFunction is at present only called on error if using jQuery ajax (sync/async) and not if using gsajaxapi (sync/async).
467gs.functions._callMetadataServer = function(callingFunction, payload, successResponseFunction, errorResponseFunction, opts)
468{
469
470 // async AJAX by default for get operations: because internal processing of 'read' operations (get meta)
471 // is not order dependent.
472 // Set/remove operations will switch to synchronous AJAX, unless opt["forceSync"] is set otherwise
473 var async_setting = true;
474 var method = "POST"; // GET was the default before
475
476 // Set to false if you wish to use the regular JavaScript AJAX way in gsajaxapi.js (will use payload.url)
477 // Set to true if using jQuery AJAX (will use payload.data).
478 var _use_jQuery_ajax_not_gsajaxapi = true;
479
480 // _use_payload_in_data_not_url_form is determined based on vars method and _use_jQuery_ajax_not_gsajaxapi
481 // If using AJAX with payload data (with jQuery) rather than using URLs containing data (payload in url):
482 // using data will allow us to use jQuery to POST stuff too.
483 // For gsajaxapi, payload to be transmitted over AJAX must be in URL form, whether GET or POST.
484 // For jQuery, AJAX calls ended up as GET when the payload is in URL form.
485 // Default used to be payload in url form. To get the default back,
486 // set method = "GET" (above, but also in calling functions that specify this optional parameter!)
487 // and set the default here below for _use_payload_in_data_not_url_form to false.
488 var _use_payload_in_data_not_url_form = true; // code will anyway set this to true for jQuery $.ajax() with POST
489
490 var _modifyingMeta = false;
491
492 var url = payload["url"]; // for jQuery GET, and for GET and POST using JavaScript AJAX
493 var data = payload["data"]; // for jQuery POST
494
495
496 // check for any caller overrides
497 if(opts != null) {
498 if(opts["requestMethod"] != null) {
499 method = opts["requestMethod"];
500 }
501 }
502
503 // sync or async? Generally, synchronous AJAX for set-meta operations, and asynchronous for get-meta ops
504 var metaServerCommand = (data["s1.a"] == null) ? data["a"] : data["s1.a"];
505 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
506 _modifyingMeta = true;
507 async_setting = false; // for 'write' operations (set/remove meta), we force sequential processing of the internal operation.
508
509 }
510 // check for any overrides by calling code that knows what it's doing
511 if (opts != null && opts["forceSync"] != null) {
512 async_setting = (!opts["forceSync"]);
513 }
514
515 if(_use_jQuery_ajax_not_gsajaxapi) {
516 if(method == "POST") {
517 _use_payload_in_data_not_url_form = true;
518 } // if GET, can use payload in URL form or in data form for jQuery AJAX
519 // to put it another way: can't do jQuery POST operations with payload in URL form
520
521 } else { // using gsajaxapi.js, which only accepts the payload in URL form, whether GET or POST
522 _use_payload_in_data_not_url_form = false;
523 }
524
525 // use the URL form or the data form to transmit the payload over AJAX?
526 // Payload in data form implies jQuery AJAX, not gsajaxapi calls,
527 // since we can't use gsajaxapi.js AJAX GET/POST calls without payload in URL form
528 if(_use_payload_in_data_not_url_form) { // using data payload to do AJAX (regardless of request method)
529
530 // for get-meta operations, go directly through metadata-server.pl
531 // for set-meta ops, should go via GS3 authentication, which is off the GS3 library servlet
532 url = (_modifyingMeta) ? gs.xsltParams.library_name : "cgi-bin/metadata-server.pl";
533
534 } else { // uses data encoded into URL, rather than a data structure.
535 data = null; // we're using the URL as payload, don't duplicate the payload to be transmitted into data
536
537 url = payload["url"]; // payload["url"] contains the URL + data encoded in URL form
538 // URL is already correct for get-meta vs meta-modification operations.
539 // For meta-modification ops, it will through GS3 authentication first rather than metadata-server.pl
540
541 }
542
543 // finally, can do the AJAX call
544
545 //console.log("*** Away to call: " + url);
546 var ajaxResponse = async_setting ? "*** No response received yet, async ajax request" : null;
547
548
549 if(_use_jQuery_ajax_not_gsajaxapi) {
550 // ajax calls default to using method GET, we want to do POST operations for get-meta and set-meta requests
551 // since get-meta-array and especially set-meta-array can be large, e.g. for user comments.
552 var ajaxCall = $.ajax({url: url, async: async_setting, type: method, data: data})
553 .done(function(response) {
554 ajaxResponse = response;
555// console.log("** (" + callingFunction + ") Response received from server: " + ajaxResponse);
556
557 //var xml = $.parseXML(response);
558 //console.log(xml);
559
560
561 // Handle the otherwise silent die statement of gsdl_cgi->generate_error(),
562 // forcing the errorResponseFunction route in such a case
563 if(response.indexOf("ERROR: ") !== -1 || response.indexOf("<Error>") !== -1) {
564 if(errorResponseFunction != null) {
565 console.log("!!!! (" + callingFunction + ") Failed with gsdlcgi error:\n" + response);
566 errorResponseFunction(response);
567 } else {
568 alert("@@@ (" + callingFunction + ") Failed\n" + response);
569 console.log("@@@ (" + callingFunction + ") Failed\n" + response);
570 }
571 }
572
573 else if(successResponseFunction != null) {
574 //console.log("XXXX callMetaServer - Got response: " + response);
575 successResponseFunction(response);
576 }
577 })
578 .fail(function(response, textStatus, errorThrown) {
579 // https://stackoverflow.com/questions/13258784/stopping-an-ajax-call-on-page-unload
580 // https://api.jquery.com/jQuery.ajax/
581 if(textStatus == "abort") {
582 console.log ("INFO: Navigating away from page, so aborted running ajax.");
583 } else {
584
585 if(errorResponseFunction != null) {
586 console.log("*** (" + callingFunction + ") Failed\n" + response);
587 errorResponseFunction(response);
588 } else {
589 alert("(" + callingFunction + ") Failed\n" + response);
590 console.log("*** (" + callingFunction + ") Failed\n" + response);
591 }
592 }
593 })
594 .always(function(data) { // https://api.jquery.com/jQuery.ajax/
595
596 // this call has terminated one way or another, remove it from the list we maintain
597 // of runningAjaxCalls
598 var index_of_call = runningAjaxCalls.indexOf(ajaxCall);
599 if(index_of_call != -1) {
600 // null this ajaxCall in the runningAjaxCalls array if not already nulled
601 //runningAjaxCalls[index_of_call] = null;
602
603 // remove this 1 call from the runningAjaxCalls array
604 runningAjaxCalls.splice(index_of_call, 1);
605 }
606
607 });
608
609 // now we've launched an ajax call, let's add it to our list of runningAjaxCalls
610 runningAjaxCalls.push(ajaxCall);
611 }
612 else {
613 // USES GSAJAXAPI.JS to do AJAX. In this case, the payload must be in URL form
614
615 var splitURL = url.split("?");
616 url = splitURL[0]; // base URL
617 var params = splitURL[1]; // query-string
618
619 // Don't need to delete objects created with 'new' in JavaScript. Garbage collection will do it.
620 // http://stackoverflow.com/questions/4869712/new-without-delete-on-same-variable-in-javascript
621 var gsapi = new GSAjaxAPI(url);
622
623 // ajax calls default to using method GET, we want to do POST operations for get-meta and set-meta requests
624 // since get-meta-array and especially set-meta-array can be large, e.g. for user comments.
625
626 if(async_setting) {
627 gsapi.urlPostAsync(url, params, successResponseFunction, errorResponseFunction);
628 } else {
629 ajaxResponse = gsapi.urlPostSync(url, params);
630 //ajaxResponse = ajaxResponse;
631 }
632
633// console.log("*** (" + callingFunction + ") Response from server: " + ajaxResponse);
634
635 }
636
637// console.log("*** Finished ajax call to: " + url);
638// console.log("*** Got response: " + ajaxResponse);
639
640 return ajaxResponse;
641}
642
643
644// Prepare the payload (data package) to transmit to metadataserver.pl over AJAX.
645// These next 2 functions prepare both the URL version of the payload and the data object version
646// of the payload. Then calling functions, and the _callMetadataServer() function they call, will control
647// and determine which of the two forms ultimately gets used.
648// (Constructing both URL and data structure, as I could not successfully convert old URL way to data structure
649// http://stackoverflow.com/questions/8648892/convert-url-parameters-to-a-javascript-object)
650gs.functions.getBasicDataForMetadataServer = function(metaServerCommand, collection, site, documentID, metadataName,
651 metamode, metadataValue, prevMetadataValue, metadataPosition)
652{
653 // if we're doing set- or remove- metadata operations,
654 // then we need to change the data params that will make up the query string
655 // to make sure we go through GS3's authentication
656 // 1. prefix meta names with s1,
657 // 2. use s1.collection for collectionName since c is a special param name for GS2Construct
658 // 3. Additional parameters for rerouting through Authentication: a=g&rt=r&ro=1&s=ModifyMetadata
659
660 var modifyingMeta = false;
661 var prefix = "";
662 var colPropName = "c";
663 var baseURL = "cgi-bin/metadata-server.pl?";
664
665 // if we need authentication:
666 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
667 modifyingMeta = true;
668 prefix = "s1.";
669 colPropName = prefix+"collection"; // "s1.collection"
670 baseURL = gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ModifyMetadata&";
671 }
672
673
674 // 1. when using jQuery to POST, need to invoke AJAX with a data structure rather than a URL
675 var data = {};
676
677 // customizable portion of ajax call
678 data[prefix+"a"] = metaServerCommand;
679 data[colPropName] = collection;
680 data[prefix+"site"] = site;
681 data[prefix+"d"] = documentID;
682 data[prefix+"metaname"] = metadataName;
683 data[prefix+"metapos"] = metadataPosition;
684 data[prefix+"metavalue"] = metadataValue;
685 data[prefix+"prevmetavalue"] = prevMetadataValue;
686 data[prefix+"metamode"] = metamode;
687
688 if(modifyingMeta) {
689 // fixed portion of url: add the a=g&rt=r&ro=1&s=ModifyMetadata part of the GS3 URL for
690 // going through authentication. Don't prefix "s1." to these!
691 data["a"] = "g";
692 data["rt"] = "r";
693 data["ro"] = "1";
694 data["s"] = "ModifyMetadata";
695 }
696
697 // 2. Construct the URL version of the metadata-server.pl operation:
698 // for GET requests, the URL can contain the data.
699 // Regular JavaScript AJAX code in gsajaxapi.js can also POST data in URL form, but not jQuery's .ajax().
700
701
702 // If doing set- or remove- (not get-) metadata, then rewrite URLs to call GS2Construct's ModfiyMetadata service instead (which will ensure this only works when authenticated).
703 // From:
704 // <gs3server>/cgi-bin/metadata-server.pl?a=set-archives-metadata&c=smallcol&site=localsite&d=HASH01454f31011f6b6b26eaf8d7&metaname=Title&metavalue=Moo&prevmetavalue=Blabla&metamode=override
705 // To:
706 // <gs3server>/library?a=g&rt=r&ro=1&s=ModifyMetadata&s1.a=set-archives-metadata&s1.collection=smallcol&s1.site=localsite&s1.d=HASH01454f31011f6b6b26eaf8d7&s1.metaname=Title&s1.metavalue=Moo&s1.prevmetavalue=Blabla&s1.metamode=override
707
708 var extraParams = "";
709
710 if(metadataValue != null) {
711 extraParams += "&"+prefix+"metavalue=" + metadataValue;
712 }
713
714 if(metadataPosition != null)
715 {
716 extraParams += "&"+prefix+"metapos=" + metadataPosition;
717 }
718
719 if(prevMetadataValue != null) {
720 extraParams += "&"+prefix+"prevmetavalue=" + prevMetadataValue;
721 }
722
723 var url = baseURL + prefix+"a=" + metaServerCommand + "&"+colPropName+"=" + collection + "&"+prefix+"site=" + site + "&"+prefix+"d=" + documentID + "&"+prefix+"metaname=" + metadataName + extraParams + "&"+prefix+"metamode=" + metamode;
724
725 // 3. Return both the constructed url & data variants of the payload to be transmitted over ajax
726 var payload = {
727 url: url,
728 data: data
729 };
730
731 return payload;
732}
733
734// See description for getBasicDataForMetadataServer()
735gs.functions.getComplexDataForMetadataServer = function(metaServerCommand, collection, site, docArray, metamode, where)
736{
737 var docArrayJSON = JSON.stringify(docArray);
738
739 // if we're doing set- or remove- metadata operations,
740 // then we need change the data params that will make up the query string
741 // to make sure we go through GS3's authentication
742 // 1. prefix meta names with s1,
743 // 2. use s1.collection for collectionName since c is a special param name for GS2Construct
744 // 3. Additional parameters for rerouting through Authentication: a=g&rt=r&ro=1&s=ModifyMetadata
745
746 var modifyingMeta = false;
747 var prefix = "";
748 var colPropName = "c";
749 var baseURL = "cgi-bin/metadata-server.pl?";
750
751 // if we need authentication:
752 if(metaServerCommand.indexOf("set-") != -1 || metaServerCommand.indexOf("remove-") != -1) {
753 modifyingMeta = true;
754 prefix = "s1.";
755 colPropName = prefix+"collection"; // "s1.collection"
756 baseURL = gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ModifyMetadata&";
757 }
758
759 // 1. when using jQuery to POST, need to invoke AJAX with a data structure rather than a URL
760 var data = {};
761
762 // customizable portion of ajax call
763 data[prefix+"a"] = metaServerCommand;
764 data[colPropName] = collection;
765 data[prefix+"site"] = site;
766 data[prefix+"json"] = docArrayJSON;
767
768 if(where != null) {
769 data[prefix+"where"] = where;
770 }
771 if (metamode!=null) {
772 data[prefix+"metamode"] = metamode;
773 }
774
775 if(modifyingMeta) {
776 // fixed portion of url: add the a=g&rt=r&ro=1&s=ModifyMetadata part of the GS3 URL for
777 // going through authentication. Don't prefix "s1." to these!
778 data["a"] = "g";
779 data["rt"] = "r";
780 data["ro"] = "1";
781 data["s"] = "ModifyMetadata";
782 }
783
784
785 // 2. URL for when doing AJAX in URL mode. GET with jQuery allows the data to be part of the URL, but
786 // not jQuery POST. But our regular JavaScript AJAX code in gsajaxapi.js allows GET and POST with URLs
787 // containing the data.
788
789 var params = prefix+"a=" + escape(metaServerCommand); //"a=set-metadata-array";
790 if(where != null) {
791 params += "&"+prefix+"where=" + escape(where); // if where not specified, meta-server will default to setting index meta
792 //} else {
793 // params += "&"+prefix+"where=import|archives|index";
794 }
795 params += "&"+colPropName+"="+escape(collection);
796 params += "&"+prefix+"site="+escape(site);
797 params += "&"+prefix+"json="+escape(docArrayJSON);
798
799 if (metamode!=null) {
800 params += "&"+prefix+"metamode=" + escape(metamode);
801 }
802
803 // 3. Return both the constructed url & data variants of the payload to be transmitted over ajax
804 var payload = {
805 url: baseURL + params,
806 data: data
807 };
808
809 return payload;
810}
811
812/*************************
813* SET METADATA FUNCTIONS *
814*************************/
815
816gs.functions.setImportMetadata = function(collection, site, documentID, metadataName, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
817{
818 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
819
820 gs.functions._callMetadataServer(
821 "setImportMetadata",
822 gs.functions.getBasicDataForMetadataServer("set-import-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, null /*metapos*/),
823 successResponseFunction,
824 errorResponseFunction);
825
826}
827
828gs.functions.setArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
829{
830 if(metadataValue) console.log("metaval: " + metadataValue + " | " + gs.functions.debug_unicode_string(metadataValue)); //metadataValue.hexEncode()
831 if(prevMetadataValue) console.log("prevmetaval: " + prevMetadataValue + " | " + gs.functions.debug_unicode_string(prevMetadataValue));
832
833 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
834
835 if(metadataPosition != null) {
836 prevMetadataValue = null; // to force the same ultimate behaviour as in the old version of this code
837 }
838
839 gs.functions._callMetadataServer(
840 "setArchivesMetadata",
841 gs.functions.getBasicDataForMetadataServer("set-archives-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, metadataPosition),
842 successResponseFunction,
843 errorResponseFunction);
844
845}
846
847gs.functions.setIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, successResponseFunction, errorResponseFunction)
848{
849 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
850
851 if(metadataPosition != null) {
852 prevMetadataValue = null; // to force the same ultimate behaviour as in the old version of this code
853 }
854
855 // old version of this function would only call _callMetadataServer if either metapos
856 // or prevMetaValue had a value. So sticking to the same behaviour in rewriting this function.
857 if(metadataPosition != null || prevMetadataValue != null) {
858
859 gs.functions._callMetadataServer(
860 "setIndexMetadata",
861 gs.functions.getBasicDataForMetadataServer("set-metadata", collection, site, documentID, metadataName, metamode, metadataValue, prevMetadataValue, metadataPosition),
862 successResponseFunction,
863 errorResponseFunction);
864 }
865}
866
867gs.functions.setMetadata = function(collection, site, documentID, metadataName, metadataValue, metamode, successResponseFunction, errorResponseFunction)
868{
869 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
870
871 var nameArray = ["setImportMetadata", "setArchivesMetadata", "setIndexMetadata"];
872 var functionArray = ["set-import-metadata", "set-archives-metadata", "set-metadata"];
873
874 for(var i = 0; i < nameArray.length; i++)
875 {
876 // previous version of this function did not allow setting metapos or prevMetavalue
877 // so leaving the behaviour the same along with function signature.
878 gs.functions._callMetadataServer(
879 nameArray[i],
880 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, metamode, metadataValue, null /*prevMetadataValue*/, null /*metadataPosition*/),
881 successResponseFunction,
882 errorResponseFunction);
883 }
884}
885
886// New. Modified version of the GS2 version of this method in gsajaxapi.js.
887// The where parameter can be specified as one or more of: import, archives, index, live
888// separated by |. If null, it is assumed to be index which is the original default
889// behaviour of calling set-metadata-array. E.g. where=import|archives|index
890// THIS METHOD IS SYNCHRONOUS by default. Set forceSync to false to override this default behaviour
891gs.functions.setMetadataArray = function(collection, site, docArray, metamode, where, successResponseFunction, forceSync, errorResponseFunction)
892{
893 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
894
895 var payload = gs.functions.getComplexDataForMetadataServer("set-metadata-array", collection, site, docArray, metamode, where);
896
897 // set operations are generally synchronous, but allow calling function to force ajax call
898 // to be synchronous or not. Default is synchronous, as it was for GS2
899 if(forceSync == null) {
900 forceSync = true;
901 }
902
903 //console.log("cgi-bin/metadata-server.pl?"+params);
904
905 var response = gs.functions._callMetadataServer("Setting metadata in "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync": forceSync, "requestMethod": "POST"});
906
907 return response;
908}
909
910
911/*************************
912* GET METADATA FUNCTIONS *
913*************************/
914
915// New. Modified version of the GS2 version of this method in gsajaxapi.js.
916// See description for setMetadataArray above for information about the 'where' parameter.
917// THIS METHOD IS SYNCHRONOUS BY DEFAULT. Set forceSync to false to override this default behaviour
918gs.functions.getMetadataArray = function(collection, site, docArray, where, successResponseFunction, forceSync, errorResponseFunction)
919{
920 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
921
922 var payload = gs.functions.getComplexDataForMetadataServer("get-metadata-array", collection, site, docArray, null /*metamode*/, where);
923
924 // get operations are generally asynchronous, but allow calling function to force ajax call
925 // to be synchronous or not. Default for get-metadata-array is synchronous, as it was for GS2
926 if(forceSync == null) {
927 forceSync = true;
928 }
929 // Objects/maps can use identifiers or strings for property names
930 // http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices
931 // https://www.w3schools.com/js/js_objects.asp
932 var response = gs.functions._callMetadataServer("Getting metadata from "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync":forceSync, "requestMethod": "POST"});
933
934 return response;
935}
936
937
938gs.functions.getImportMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
939{
940 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
941
942 var payload = gs.functions.getBasicDataForMetadataServer("get-import-metadata", collection, site, documentID, metadataName);
943 gs.functions._callMetadataServer("getImportMetadata", payload, function(responseText) {
944 var metadata = new GSMetadata(collection, site, documentID, metadataName, null, null, responseText);
945 if(successResponseFunction != null)
946 {
947 successResponseFunction(metadata);
948 }
949 },
950 errorResponseFunction);
951}
952
953gs.functions.getArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
954{
955 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
956
957 var payload = gs.functions.getBasicDataForMetadataServer("get-archives-metadata", collection, site, documentID, metadataName, null /*metamode*/, null /*metavalue*/, null /*prevmetavalue*/, metadataPosition);
958
959 gs.functions._callMetadataServer("getArchivesMetadata", payload, function(responseText) {
960 var metadata = new GSMetadata(collection, site, documentID, metadataName, null, metadataPosition, responseText); // indexPos, archivesPos, metaval (responseText)
961 if(successResponseFunction != null)
962 {
963 successResponseFunction(metadata);
964 }
965 },
966 errorResponseFunction);
967}
968
969gs.functions.getIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
970{
971 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
972
973 var payload = gs.functions.getBasicDataForMetadataServer("get-metadata", collection, site, documentID, metadataName, null /*metamode*/, null /*metavalue*/, null /*prevmetavalue*/, metadataPosition);
974
975 gs.functions._callMetadataServer("getIndexMetadata", payload, function(responseText) {
976 var metadata = new GSMetadata(collection, site, documentID, metadataName, metadataPosition, null, responseText); // indexPos, archivesPos, metaval (responseText)
977 if(successResponseFunction != null)
978 {
979 successResponseFunction(metadata);
980 }
981 },
982 errorResponseFunction);
983}
984
985/****************************
986* REMOVE METADATA FUNCTIONS *
987****************************/
988
989gs.functions.removeImportMetadata = function(collection, site, documentID, metadataName, metadataValue, successResponseFunction, errorResponseFunction)
990{
991 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
992
993 gs.functions._callMetadataServer(
994 "removeImportMetadata",
995 gs.functions.getBasicDataForMetadataServer("remove-import-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, null /*metapos*/),
996 successResponseFunction,
997 errorResponseFunction);
998}
999
1000gs.functions.removeArchivesMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, successResponseFunction, errorResponseFunction)
1001{
1002 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1003
1004 if(metadataPosition != null) {
1005 metadataValue = null; // retaining behaviour of previous version of this function removeArchivesMetadata()
1006 }
1007
1008 gs.functions._callMetadataServer(
1009 "removeArchivesMetadata",
1010 gs.functions.getBasicDataForMetadataServer("remove-archives-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, metadataPosition),
1011 successResponseFunction,
1012 errorResponseFunction);
1013}
1014
1015gs.functions.removeIndexMetadata = function(collection, site, documentID, metadataName, metadataPosition, metadataValue, successResponseFunction, errorResponseFunction)
1016{
1017 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1018
1019 if(metadataPosition != null) {
1020 metadataValue = null; // retaining behaviour of previous version of this function removeIndexMetadata()
1021 }
1022
1023 gs.functions._callMetadataServer(
1024 "removeIndexMetadata",
1025 gs.functions.getBasicDataForMetadataServer("remove-metadata", collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, metadataPosition),
1026 successResponseFunction,
1027 errorResponseFunction);
1028}
1029
1030gs.functions.removeMetadata = function(collection, site, documentID, metadataName, metadataValue, successResponseFunction, errorResponseFunction)
1031{
1032 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1033
1034 var nameArray = ["removeImportMetadata", "removeArchivesMetadata", "removeIndexMetadata"];
1035 var functionArray = ["remove-import-metadata", "remove-archives-metadata", "remove-metadata"];
1036
1037 for(var i = 0; i < nameArray.length; i++)
1038 {
1039 gs.functions._callMetadataServer(
1040 nameArray[i],
1041 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, null /*metamode*/, metadataValue, null /*prevmetavalue*/, null /*metapos*/),
1042 successResponseFunction,
1043 errorResponseFunction);
1044 }
1045}
1046
1047gs.functions.removeMetadataAtPos = function(collection, site, documentID, metadataName, metadataPosition, successResponseFunction, errorResponseFunction)
1048{
1049 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1050
1051 var nameArray = ["removeImportMetadata", "removeArchivesMetadata", "removeIndexMetadata"];
1052 var functionArray = ["remove-import-metadata", "remove-archives-metadata", "remove-metadata"];
1053
1054 for(var i = 0; i < nameArray.length; i++)
1055 {
1056 gs.functions._callMetadataServer(
1057 nameArray[i],
1058 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, null /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, metadataPosition),
1059 successResponseFunction,
1060 errorResponseFunction);
1061 }
1062}
1063
1064// metamode=override is in theory supported by remove_archives and remove_import metadata
1065// but not by remove_index and remove_live metadata. However, even in the former 2 cases,
1066// passing in metamode=override still appears to be ineffective, see recent svn revision 38193.
1067// And metamode accumulate prevents all removal.
1068// So for now, this function forces metamode to null, regardless of what is passed in.
1069// At least this ensures a more consistent effect with remove_index (and remove_live) functions.
1070gs.functions.removeMetadataArray = function(collection, site, docArray, metamode,
1071 where, successResponseFunction, forceSync, errorResponseFunction)
1072{
1073 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1074
1075 var payload = gs.functions.getComplexDataForMetadataServer("remove-metadata-array", collection, site, docArray, null/*metamode*/, where);
1076
1077 // set operations are generally synchronous, but allow calling function to force ajax call
1078 // to be synchronous or not. Default is synchronous, as it was for GS2
1079 if(forceSync == null) {
1080 forceSync = true;
1081 }
1082
1083 //console.log("cgi-bin/metadata-server.pl?"+params);
1084
1085 var response = gs.functions._callMetadataServer("Removing metadata from "+where, payload, successResponseFunction, errorResponseFunction, {"forceSync": forceSync, "requestMethod": "POST"});
1086
1087 //console.log("in gs.functions.removeMetaArray: " + JSON.stringify(payload));
1088
1089 return response;
1090}
1091
1092
1093/***********************************************************
1094* ERASE METADATA FUNCTIONS *
1095* Erase all meta in docID (sectionID) that match metaname. *
1096***********************************************************/
1097
1098gs.functions.eraseImportMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1099{
1100 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1101
1102 gs.functions._callMetadataServer(
1103 "eraseImportMetadata",
1104 gs.functions.getBasicDataForMetadataServer("erase-import-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1105 successResponseFunction,
1106 errorResponseFunction);
1107}
1108
1109gs.functions.eraseArchivesMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1110{
1111 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1112
1113 gs.functions._callMetadataServer(
1114 "eraseArchivesMetadata",
1115 gs.functions.getBasicDataForMetadataServer("erase-archives-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1116 successResponseFunction,
1117 errorResponseFunction);
1118}
1119
1120gs.functions.eraseIndexMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1121{
1122 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1123
1124 gs.functions._callMetadataServer(
1125 "eraseIndexMetadata",
1126 gs.functions.getBasicDataForMetadataServer("erase-metadata", collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1127 successResponseFunction,
1128 errorResponseFunction);
1129}
1130
1131gs.functions.eraseMetadata = function(collection, site, documentID, metadataName, successResponseFunction, errorResponseFunction)
1132{
1133 if( typeof errorResponseFunction === 'undefined' ) { errorResponseFunction = null; } // force error callback to be defined: either null or has value
1134
1135 var nameArray = ["eraseImportMetadata", "eraseArchivesMetadata", "eraseIndexMetadata"];
1136 var functionArray = ["erase-import-metadata", "erase-archives-metadata", "erase-metadata"];
1137
1138 for(var i = 0; i < nameArray.length; i++)
1139 {
1140 gs.functions._callMetadataServer(
1141 nameArray[i],
1142 gs.functions.getBasicDataForMetadataServer(functionArray[i], collection, site, documentID, metadataName, "override" /*metamode*/, null /*metadataValue*/, null /*prevmetavalue*/, null /*metadataPosition*/),
1143 successResponseFunction,
1144 errorResponseFunction);
1145 }
1146}
1147
1148// When user prematurely navigates away from current page before the page has finished all
1149// running ajax calls, abort those still running. Else the error callback is called
1150// and error messages are displayed.
1151// https://stackoverflow.com/questions/13258784/stopping-an-ajax-call-on-page-unload
1152// See also gs.functions._callMetadataServer() which maintains the array of running ajax calls
1153// and removes finished calls from the runningAjaxCalls array.
1154//window.onbeforeunload = function(event) {
1155$(window).on("beforeunload", function(event) {
1156 var i = 0;
1157 var ajaxCall;
1158 for (i = 0; i < runningAjaxCalls.length; i++) { // list of only *running* ajax calls
1159 ajaxCall = runningAjaxCalls[i];
1160 // abort each still running ajaxCall (gets removed from runningAjaxCalls list elsewhere)
1161 ajaxCall.abort();
1162 }
1163});
Note: See TracBrowser for help on using the repository browser.