source: main/trunk/greenstone3/web/interfaces/default/js/javascript-global-functions.js@ 33186

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