source: main/trunk/greenstone3/web/interfaces/default/js/documentedit_scripts_util.js@ 32873

Last change on this file since 32873 was 32873, checked in by ak19, 5 years ago

javascript-global-functions' local function callMetadataServer() now renamed to _callMetadataServer() and takes and extra parameter BEFORE existing last parameter opts. The extra parameter is errorResponseFunction, which if not null is called on an ajax error if using jQuery mode to do ajax (errorResponseFunction is not used by gsajaxapi yet). All functions that call _callMetadataServer() now also take an extra last param, errorResponseFunction, and force it to null if undefined. It is always the last param for these public functions, so that the existing method calls from outside the file will continue to work. Tested getting and setting regular meta with the doceditor and mapeditor: saving and reloading saved data still works. Not tested set/getMetaArrays used by usercomments, but the changes haven't been drastic or complicated, so shouldn't have broken anything in those 2 functions either.

  • Property svn:executable set to *
File size: 42.0 KB
Line 
1/** Javascript file containing useful functions used by both documentedit_scripts.js and documentmaker_scripts.js */
2
3
4//Some "constants" to match the server constants
5var SUCCESS = 1;
6var ACCEPTED = 2;
7var ERROR = 3;
8var CONTINUING = 10;
9var COMPLETED = 11;
10var HALTED = 12;
11
12var _transactions = new Array();
13var _collectionsToBuild = new Array();
14var _allContents = new Array();
15var _deletedSections = new Array();
16var _deletedMetadata = new Array();
17var _undoOperations = new Array();
18var _baseURL;
19var _statusBar;
20var _metadataSetList = new Array();
21
22
23// We need to wait for all editable elements (metadataTable and sectionText) to be finished with initialising
24// before we try to store their current states in the editableInitStates array
25// To do this, we need to keep track of how many elements still require initialising.
26var editableElementsInitialisationProgress = 0;
27
28
29
30function encodeDelimiters(meta_value) {
31
32 var new_value = meta_value.replace(/;/g, "%253B");
33 return new_value.replace(/&/g, "%2526");
34}
35
36function getElementsByClassName(cl, parent)
37{
38 var elemArray = [];
39 var classRegEx = new RegExp("\\b" + cl + "\\b");
40 var elems;
41 if(parent)
42 {
43 elems = parent.getElementsByTagName("*");
44 }
45 else
46 {
47 elems = document.getElementsByTagName("*");
48 }
49
50 for (var i = 0; i < elems.length; i++)
51 {
52 var classeName = elems[i].className;
53 if (classRegEx.test(classeName)) elemArray.push(elems[i]);
54 }
55
56 return elemArray;
57};
58
59function getNextSiblingOfType(elem, type)
60{
61 if(elem == null)
62 {
63 return null;
64 }
65
66 var current = elem.nextSibling;
67 while(current != null)
68 {
69 if(current.nodeName.toLowerCase() == type)
70 {
71 return current;
72 }
73
74 current = current.nextSibling;
75 }
76 return null;
77}
78
79function getPrevSiblingOfType(elem, type)
80{
81 if(elem == null)
82 {
83 return null;
84 }
85
86 var current = elem.previousSibling;
87 while(current != null)
88 {
89 if(current.nodeName.toLowerCase() == type)
90 {
91 return current;
92 }
93
94 current = current.previousSibling;
95 }
96 return null;
97}
98
99function saveTransaction(transaction)
100{
101 console.log(transaction);
102 _transactions.push(transaction);
103}
104
105function undo()
106{
107 if(_undoOperations.length == 0)
108 {
109 return;
110 }
111
112 var undoOp = _undoOperations.pop();
113
114 //Create/Duplicate undo
115 if(undoOp.op == "del")
116 {
117 if(undoOp.srcElem.childList)
118 {
119 removeFromParent(undoOp.srcElem.childList);
120 }
121 if(undoOp.srcElem.parentItem)
122 {
123 undoOp.srcElem.parentItem.menu.newSectionLink.style.display = "inline";
124 undoOp.srcElem.parentItem.childList = null;
125 }
126 removeFromParent(undoOp.srcElem);
127 }
128
129 if(undoOp.op == "delMeta")
130 {
131 if(undoOp.srcElem.childList)
132 {
133 removeFromParent(undoOp.srcElem.childList);
134 }
135 if(undoOp.srcElem.parentItem)
136 {
137 undoOp.srcElem.parentItem.menu.newSectionLink.style.display = "inline";
138 undoOp.srcElem.parentItem.childList = null;
139 }
140 de.doc.unregisterEditSection(undoOp.srcElem);
141 removeFromParent(undoOp.srcElem);
142 }
143
144 //Move undo (mva is move after, mvb is move before, mvi is move into)
145 else if(undoOp.op == "mva" || undoOp.op == "mvb" || undoOp.op == "mvi")
146 {
147 if(undoOp.op == "mvb")
148 {
149 undoOp.refElem.parentNode.insertBefore(undoOp.srcElem, undoOp.refElem);
150 }
151 else if(undoOp.op == "mva")
152 {
153 insertAfter(undoOp.srcElem, undoOp.refElem);
154 }
155 else
156 {
157 undoOp.refElem.removeChild(undoOp.refElem.firstChild);
158 undoOp.refElem.appendChild(undoOp.srcElem);
159 }
160
161 if(undoOp.srcElem.textDiv)
162 {
163 insertAfter(undoOp.srcElem.textDiv, undoOp.srcElem);
164 }
165 if(undoOp.srcElem.childList)
166 {
167 insertAfter(undoOp.srcElem.childList, undoOp.srcElem.textDiv);
168 }
169
170 if(undoOp.srcElem.onmouseout)
171 {
172 //Uncolour the section if it coloured
173 undoOp.srcElem.onmouseout();
174 }
175 updateFromTop();
176 }
177 else if(undoOp.op == "display")
178 {
179 undoOp.srcElem.style.display = undoOp.subOp;
180 }
181
182 if(undoOp.removeDeletedMetadata)
183 {
184 _deletedMetadata.pop();
185 }
186
187 if(undoOp.removeTransaction)
188 {
189 _transactions.pop();
190 }
191}
192
193function enableSaveButtons(enabled) {
194 if (enabled) {
195 $("#saveButton, #quickSaveButton").html(gs.text.de.save_changes);
196 $("#saveButton, #quickSaveButton").removeAttr("disabled");
197
198 } else {
199 $("#saveButton, #quickSaveButton").html(gs.text.de.saving + "...");
200 $("#saveButton, #quickSaveButton").attr("disabled", "disabled");
201
202 }
203}
204function addCollectionToBuild(collection)
205{
206 for(var i = 0; i < _collectionsToBuild.length; i++)
207 {
208 if(collection == _collectionsToBuild[i])
209 {
210 return;
211 }
212 }
213 _collectionsToBuild.push(collection);
214}
215
216function save() {
217 saveAndRebuild(false);
218}
219
220function rebuildCurrentCollection() {
221
222 console.log(gs.text.de.rebuilding_collection);
223 enableSaveButtons(false);
224 var collection = gs.cgiParams.c;
225
226 var collectionsArray = new Array();
227 collectionsArray.push(collection);
228 buildCollections(collectionsArray, null, reloadUponRebuild); // passing in callback to reload the page after build, as requested by Kathy
229}
230
231
232function reloadUponRebuild() {
233 // finished rebuilding - reload the page after rebuild, but first
234 // clear transactions array of saved changes, now that we're done processing these changes during rebuild,
235 // since we don't want the "are you sure to leave page" popup which appears on _transactions array being non-empty
236 _transactions = null;
237 location.reload(true); // force reload, not from cache, https://www.w3schools.com/jsref/met_loc_reload.asp
238}
239
240/************************************
241* TEXT EDIT (CKEDITOR) SCRIPTS *
242**************************************/
243
244// not using this anymore as the instanceready never seems to get called
245function addCKEEditableState(evt,stateArray)
246{
247 // Event->Editor->CKE DOM Inline Element that editor was for->underlying jquery element
248 element = evt.editor.element.$;
249 nodeText = element.innerHTML;
250 stateArray.push({
251 editableNode : element,
252 initHTML : nodeText
253 });
254}
255
256function addEditableState(editable,stateArray)
257{
258
259 if(editable.tagName == 'TEXTAREA')
260 {
261 nodeText = editable.value;
262 }
263 else
264 {
265 nodeText = editable.innerHTML;
266 }
267
268 stateArray.push({
269 editableNode : editable,
270 initHTML : nodeText
271 });
272
273}
274
275function getLastEditableStates()
276{
277 editableLastStates = [];
278 $(".sectionText").each(function(){addEditableState(this,editableLastStates);});
279 $(".metaTableCellArea").each(function(){addEditableState(this,editableLastStates);});
280
281}
282
283function changesToUpdate()
284{
285 var resultArray = new Array();
286
287 //console.log("**** changesToUpdate::editableElementsInitialisationProgress = " + editableElementsInitialisationProgress);
288
289 // Only want to check for valid edited states if the editableInitStates has been fully set up:
290 // We don't bother if editableInitStates is not ready:
291 // if editableInitStates array has nothing in it (which means CKEditor is not ready)
292 // OR if some of the editable elements (metadataTable OR ckeditor editable values) still need to be initialised/initialising is only partway through
293 if ((editableInitStates.length > 0) && (editableElementsInitialisationProgress == 0)) {
294 getLastEditableStates();
295 for (var j in editableLastStates)
296 {
297 if (isNodeChanged(editableLastStates[j]))
298 {
299 resultArray.push(editableLastStates[j].editableNode);
300 }
301 }
302 }
303 return resultArray;
304}
305
306
307function isNodeChanged(StateToCheck){
308 for (var i in editableInitStates)
309 {
310 if ((StateToCheck.editableNode === editableInitStates[i].editableNode)) {
311 if ( StateToCheck.initHTML === editableInitStates[i].initHTML )
312 {
313 return false;
314 }
315 //console.log("current="+StateToCheck.initHTML);
316 //console.log("init="+editableInitStates[i].initHTML);
317 return true;
318 }
319
320 }
321 // if get here, this must be a new node, as wasn't in init states
322 // make sure its not empty - we won't add empty nodes.
323 if (StateToCheck.initHTML == "") {
324 return false;
325 }
326
327 console.log("**** isNodeChanged() editableInitStates = ", editableInitStates);
328 return true;
329
330}
331
332
333function saveAndRebuild(rebuild)
334{
335//This works in most cases but will not work when taking a doc from one collection to another, will need to be fixed at some point
336 var collection;
337 if(gs.cgiParams.c && gs.cgiParams.c != "") {
338
339 collection = gs.cgiParams.c;
340 }
341 else {
342 collection = gs.cgiParams.p_c;
343 }
344
345 var sendBuildRequest = function()
346 {
347 console.log("Replacing init states with current states");
348 // Clean changes. We're here because setting meta for all meta changes was successful, so
349 // - update doc's metadata initial states to current:
350 editableInitStates = editableLastStates;
351 // - update doc's map editors' initial states to current:
352 var map_editors_array = Object.values(gsmap_store);
353 for(var i = 0; i < map_editors_array.length; i++) {
354 var map_editor = map_editors_array[i];
355 map_editor.savedOverlays = JSON.stringify(ShapesUtil.overlayToJSON(map_editor.overlays));
356 }
357
358
359 var request = "[";
360 for(var i = 0; i < _transactions.length; i++)
361 {
362 request += _transactions[i];
363 if(i != _transactions.length - 1)
364 {
365 request += ",";
366 }
367 }
368 request += "]";
369
370 var statusID;
371 var ajax = new gs.functions.ajaxRequest();
372 ajax.open("POST", gs.xsltParams.library_name, true);
373 ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
374 ajax.onreadystatechange = function()
375 {
376 if(ajax.readyState == 4 && ajax.status == 200)
377 {
378 var text = ajax.responseText;
379 var xml = validateXML(text);
380
381 var errorElems;
382 if(!xml || checkForErrors(xml))
383 {
384 alert(gs.text.dse.error_saving);
385
386 enableSaveButtons(true);
387
388 if(_statusBar)
389 {
390 _statusBar.removeStatus(statusID);
391 }
392 return;
393 }
394
395 if(_statusBar)
396 {
397 _statusBar.removeStatus(statusID);
398 }
399 if (rebuild) {
400 buildCollections(_collectionsToBuild, null, reloadUponRebuild);
401 } else {
402 // reset the save button here
403 enableSaveButtons(true);
404 // saving to archives is now done, clear the transactions
405 // that were keeping track of the full text changes that have now
406 // been performed to archives (no member var keeps track of meta changes, only a local var)
407 _transactions = new Array();
408 }
409 }
410 }
411
412 if(_collectionsToBuild.length > 0)
413 {
414 enableSaveButtons(false);
415
416 if(_statusBar)
417 {
418 statusID = _statusBar.addStatus(gs.text.dse.modifying_archives + "...");
419 }
420 ajax.send("a=g&rt=r&s=DocumentExecuteTransaction&s1.transactions=" + request);
421 }
422 } // end sendBuildRequest definition
423
424 var metadataChanges = new Array();
425 if (_deletedMetadata.length > 0) {
426 //addCollectionToBuild(collection);
427
428 for(var i = 0; i < _deletedMetadata.length; i++) {
429
430 var currentRow = _deletedMetadata[i];
431
432 //Get document ID
433 var currentElem = currentRow;
434 while((currentElem = currentElem.parentNode).tagName != "TABLE");
435 var docID = currentElem.getAttribute("id").substring(4);
436
437 //Get metadata name
438 var cells = currentRow.getElementsByTagName("TD");
439 var nameCell = cells[0];
440 // metadata name cell might have the multivalue indicator in it, so just want the first word
441 var name = nameCell.innerHTML.split(" ")[0];
442 var valueCell = cells[1];
443 var value = valueCell.getElementsByTagName("TEXTAREA")[0].value;
444 if (value.length) {
445 // check for non empty value, in case all they have done is add a field then deleted it.
446 metadataChanges.push({type:'delete', docID:docID, name:name, value:value});
447 addCollectionToBuild(collection);
448 }
449 removeFromParent(currentRow);
450 }
451 }
452
453 var changes = changesToUpdate();
454
455 //Clean changes
456 ////editableInitStates = editableLastStates; // moved into processChangesLoop(): when all changes have successfully been processed.
457 // Alternative is to set initState per metadata in changes array: after each setArchiveMetadata call for each individual meta change.
458 // But since our setMeta calls are always synchronous, happening in sequence, if one setArchivesMeta() call fails
459 // we'll not attempt subsequent ones or coll building at the end.
460
461 for(var i = 0; i < changes.length; i++)
462 {
463 var changedElem = changes[i];
464 //Save metadata
465
466 if(gs.functions.hasClass(changedElem, "metaTableCellArea"))
467 {
468 //Get document ID
469 var currentElem = changedElem;
470 while((currentElem = currentElem.parentNode).tagName != "TABLE");
471 var docID = currentElem.getAttribute("id").substring(4);
472
473 //Get metadata name
474 var row = changedElem.parentNode.parentNode;
475 var cells = row.getElementsByTagName("TD");
476 var nameCell = cells[0];
477 // metadata name cell might have the multivalue indicator in it, so just want the first word
478 var name = nameCell.innerHTML.split(" ")[0];
479 var value = changedElem.value;
480 value = value.replace(/&nbsp;/g, " ");
481
482 var orig = changedElem.originalValue;
483 if (orig) {
484 orig = orig.replace(/&nbsp;/g, " ");
485 }
486 if (jQuery.inArray(name, multiValuedMetadata) != -1) {
487
488 // split the values
489 var values_list = value.split(mvm_delimiter);
490 var orig_list;
491 var num_orig;
492 if (orig) {
493 orig_list = orig.split(mvm_delimiter);
494 num_orig = orig_list.length;
495 }
496
497 for(var i = 0; i < values_list.length; i++) {
498 var val = values_list[i];
499 var ori =null;
500 if (orig && i<num_orig) {
501 ori = orig_list[i];
502 }
503 metadataChanges.push({collection:collection, docID:docID, name:name, value:val, orig:ori});
504 }
505 } else {
506 metadataChanges.push({collection:collection, docID:docID, name:name, value:value, orig:orig});
507 }
508 changedElem.originalValue = changedElem.value;
509 addCollectionToBuild(collection);
510 }
511 //Save content
512 else if(gs.functions.hasClass(changedElem, "renderedText"))
513 {
514 var section = changedElem.parentDiv.parentItem;
515 saveTransaction('{"operation":"setText", "text":"' + CKEDITOR.instances[changedElem.getAttribute("id")].getData().replace(/%/g, "%25").replace(/"/g, "\\\"").replace(/&/g, "%26") + '", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '"}'); //'
516 addCollectionToBuild(section.collection);
517 }
518 else if(gs.functions.hasClass(changedElem, "sectionText"))
519 {
520 var id = changedElem.getAttribute("id");
521 var sectionID = id.substring(4);
522 saveTransaction('{"operation":"setText", "text":"' + CKEDITOR.instances[changedElem.getAttribute("id")].getData().replace(/%/g, "%25").replace(/"/g, "\\\"").replace(/&/g, "%26") + '", "collection":"' + gs.cgiParams.c + '", "oid":"' + sectionID + '"}'); //'
523 addCollectionToBuild(gs.cgiParams.c);
524 }
525 }
526
527 // Check for changes to any map editors in the document
528 // NOTE: At present, we don't maintain a list of deletions for the map editor:
529 // GPS.mapOverlay data that has been removed is recorded as a change not a deletion,
530 // with the metadata's new value being the string of an empty JSON array, [],
531 // and entered as such into doc.xml.
532 var modified_map_editors_data = getDocMapsEditDataForSaving(gs.cgiParams.c); // collection
533 for(var i = 0; i < modified_map_editors_data.length; i++) {
534 metadataChanges.push(modified_map_editors_data[i]); // of the form { collection: gs.cgiParams.c, docID: nodeID, name:"GSP.mapOverlay", metapos: 0, value: <stringifiedJSON> }
535 addCollectionToBuild(gs.cgiParams.c); // collection
536 }
537
538
539
540 var processChangesLoop = function(index)
541 {
542 var change = metadataChanges[index];
543
544 var callbackFunction;
545 if(index + 1 == metadataChanges.length)
546 {
547 callbackFunction = sendBuildRequest;
548 }
549 else
550 {
551 callbackFunction = function(){processChangesLoop(index + 1)};
552 }
553 if (change.type == "delete") {
554 gs.functions.removeArchivesMetadata(collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), function(){callbackFunction();});
555 } else {
556 // Checking "if(change.metapos)" doesn't work for us as it becomes false when the property doesn't exist AND when the property is 0. But metapos IS 0 for us.
557 // https://ultimatecourses.com/blog/methods-to-determine-if-an-object-has-a-given-property
558 if('metapos' in change && change.metapos === 0) {// && change.metapos === 0) { // for maps
559
560 // collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, responseFunction
561 //console.log("@@@ metapos! change: ", change);
562 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, change.metapos, encodeDelimiters(change.value), null, "override", function(){callbackFunction();});
563 }
564 else if(change.orig)
565 {
566 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), encodeDelimiters(change.orig), "override", function(){callbackFunction();});
567 }
568 else
569 {
570 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), null, "accumulate", function(){callbackFunction();});
571 }
572 }
573 }
574 if (metadataChanges.length>0) {
575 // this will process each change one by one, and then send the build request
576 processChangesLoop(0);
577 }
578 else if(_collectionsToBuild.length > 0) {
579 // if there are no metadata changes, but some other changes eg text have happened, then we need to send the build request.
580 sendBuildRequest();
581 }
582
583 /* need to clear the changes from the page so that we don't process them again next time */
584 while (_deletedMetadata.length>0) {
585 _deletedMetadata.pop();
586 }
587
588}
589
590
591function buildCollections(collections, documents, callback)
592{
593 if(!collections || collections.length == 0)
594 {
595 console.log(gs.text.dse.empty_collection_list);
596 enableSaveButtons(true);
597 return;
598 }
599
600 var docs = "";
601 var buildOperation = "";
602 if(documents)
603 {
604 buildOperation = "ImportCollection";
605 docs += "&s1.documents=";
606 for(var i = 0; i < documents.length; i++)
607 {
608 docs += documents[i];
609 if(i < documents.length - 1)
610 {
611 docs += ",";
612 }
613 }
614 }
615 else
616 {
617 buildOperation = "BuildAndActivateCollection";
618 }
619
620 var counter = 0;
621 var statusID = 0;
622 var buildFunction = function()
623 {
624 var ajax = new gs.functions.ajaxRequest();
625 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=" + buildOperation + "&s1.incremental=true&s1.collection=" + collections[counter] + docs);
626 ajax.onreadystatechange = function()
627 {
628 if(ajax.readyState == 4 && ajax.status == 200)
629 {
630 var text = ajax.responseText;
631 var xml = validateXML(text);
632
633 if(!xml || checkForErrors(xml))
634 {
635 alert(gs.text.dse.could_not_build_p1 + " " + collections[counter] + gs.text.dse.could_not_build_p2);
636
637 if(_statusBar)
638 {
639 _statusBar.removeStatus(statusID);
640 }
641 enableSaveButtons(true);
642
643 return;
644 }
645
646 var status = xml.getElementsByTagName("status")[0];
647 var pid = status.getAttribute("pid");
648
649 startCheckLoop(pid, buildOperation, statusID, function()
650 {
651 /*
652 var localAjax = new gs.functions.ajaxRequest();
653 localAjax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ActivateCollection&s1.collection=" + collections[counter], true);
654 localAjax.onreadystatechange = function()
655 {
656 if(localAjax.readyState == 4 && localAjax.status == 200)
657 {
658 var localText = localAjax.responseText;
659 var localXML = validateXML(localText);
660
661 if(!xml || checkForErrors(xml))
662 {
663 alert(gs.text.dse.could_not_activate_p1 + " " + collections[counter] + gs.text.dse.could_not_activate_p2);
664
665 if(_statusBar)
666 {
667 _statusBar.removeStatus(statusID);
668 }
669 enableSaveButtons(true);
670
671 return;
672 }
673
674 var localStatus = localXML.getElementsByTagName("status")[0];
675 if(localStatus)
676 {
677 var localPID = localStatus.getAttribute("pid");
678 startCheckLoop(localPID, "ActivateCollection", statusID, function()
679 {
680 */
681 if(counter == collections.length - 1)
682 {
683 removeCollectionsFromBuildList(collections);
684 if(callback)
685 {
686 callback();
687 }
688 }
689 else
690 {
691 counter++;
692 buildFunction();
693 }
694
695 _transactions = new Array();
696
697 if(_statusBar)
698 {
699 _statusBar.removeStatus(statusID);
700 }
701 enableSaveButtons(true);
702 /*
703 });
704 }
705 }
706 }
707 if(_statusBar)
708 {
709 _statusBar.changeStatus(statusID, gs.text.dse.activating + " " + collections[counter] + "...");
710 }
711 localAjax.send();
712 */
713 });
714 }
715 }
716 if(_statusBar)
717 {
718 statusID = _statusBar.addStatus(gs.text.dse.building + " " + collections[counter] + "...");
719 }
720 ajax.send();
721 }
722 buildFunction();
723}
724
725function startCheckLoop(pid, serverFunction, statusID, callbackFunction)
726{
727 var ajaxFunction = function()
728 {
729 var ajax = new gs.functions.ajaxRequest();
730 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=s&ro=1&s=" + serverFunction + "&s1.pid=" + pid, true);
731 ajax.onreadystatechange = function()
732 {
733 if(ajax.readyState == 4 && ajax.status == 200)
734 {
735 var text = ajax.responseText;
736 var xml = validateXML(text);
737
738 if(!xml || checkForErrors(xml))
739 {
740 alert(gs.text.dse.could_not_check_status_p1 + " " + serverFunction + gs.text.dse.could_not_check_status_p2a);
741
742 if(_statusBar)
743 {
744 _statusBar.removeStatus(statusID);
745 }
746 enableSaveButtons(true);
747
748 return;
749 }
750
751 var status = xml.getElementsByTagName("status")[0];
752 var code = status.getAttribute("code");
753
754 if (code == COMPLETED || code == SUCCESS)
755 {
756 callbackFunction();
757 }
758 else if (code == HALTED || code == ERROR)
759 {
760 alert(gs.text.dse.could_not_check_status_p1 + " " + serverFunction + gs.text.dse.could_not_check_status_p2b);
761
762 if(_statusBar)
763 {
764 _statusBar.removeStatus(statusID);
765 }
766 enableSaveButtons(true);
767 }
768 else
769 {
770 setTimeout(ajaxFunction, 1000);
771 }
772 }
773 }
774 ajax.send();
775 }
776 ajaxFunction();
777}
778
779function removeCollectionsFromBuildList(collections)
780{
781 var tempArray = new Array();
782 for(var i = 0; i < _collectionsToBuild.length; i++)
783 {
784 var found = false;
785 for(var j = 0; j < collections.length; j++)
786 {
787 if(collections[j] == _collectionsToBuild[i])
788 {
789 found = true;
790 break;
791 }
792 }
793
794 if(!found)
795 {
796 tempArray.push(_collectionsToBuild[i]);
797 }
798 }
799 _collectionsToBuild = tempArray;
800}
801
802function checkForErrors(xml)
803{
804 var errorElems = xml.getElementsByTagName("error");
805
806 if(errorElems && errorElems.length > 0)
807 {
808 var errorString = gs.text.dse.error_saving_changes + ": ";
809 for(var i = 0; i < errorElems.length; i++)
810 {
811 errorString += " " + errorElems.item(i).firstChild.nodeValue;
812 }
813 alert(errorString);
814 return true;
815 }
816 return false; //No errors
817}
818
819function validateXML(txt)
820{
821 // code for IE
822 if (window.ActiveXObject)
823 {
824 var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
825 xmlDoc.async = "false";
826 xmlDoc.loadXML(document.all(txt).value);
827
828 if(xmlDoc.parseError.errorCode!=0)
829 {
830 txt = dse.error_code + ": " + xmlDoc.parseError.errorCode + "\n";
831 txt = txt + dse.error_reason + ": " + xmlDoc.parseError.reason;
832 txt = txt + dse.error_line + ": " + xmlDoc.parseError.line;
833 console.log(txt);
834 return null;
835 }
836
837 return xmlDoc;
838 }
839 // code for Mozilla, Firefox, Opera, etc.
840 else if (document.implementation.createDocument)
841 {
842 var parser = new DOMParser();
843 var xmlDoc = parser.parseFromString(txt,"text/xml");
844
845 if (xmlDoc.getElementsByTagName("parsererror").length > 0)
846 {
847 console.log(gs.text.dse.xml_error);
848 return null;
849 }
850
851 return xmlDoc;
852 }
853 else
854 {
855 console.log(gs.text.dse.browse_cannot_validate_xml);
856 }
857 return null;
858}
859
860function onVisibleMetadataSetChange()
861{
862 var metadataList = document.getElementById("metadataSetList");
863 var index = metadataList.selectedIndex;
864 var options = metadataList.getElementsByTagName("OPTION");
865 var selectedOption = options[index];
866
867 var selectedSet = selectedOption.value;
868 changeVisibleMetadata(selectedSet);
869}
870
871function changeVisibleMetadata(metadataSetName)
872{
873 var metaSetList = metadataSetName.split(",");
874 var tables = document.getElementsByTagName("TABLE");
875 for(var i = 0; i < tables.length; i++)
876 {
877 var id = tables[i].getAttribute("id");
878 if(id && id.search(/^meta/) != -1)
879 {
880 var rows = tables[i].getElementsByTagName("TR");
881 for(var j = 0; j < rows.length; j++)
882 {
883 if(metadataSetName == "All")
884 {
885 rows[j].style.display = "table-row";
886 }
887 else
888 {
889 var cells = rows[j].getElementsByTagName("TD");
890 // metadata name cell might have the multivalue indicator in it, so just want the first word
891 var cellName = cells[0].innerHTML.split(" ")[0];
892
893 if(cellName.indexOf(".") == -1)
894 {
895 rows[j].style.display = "none";
896 }
897 else
898 {
899 var setName = cellName.substring(0, cellName.lastIndexOf("."));
900 if (metaSetList.indexOf(setName)!= -1)
901 {
902 rows[j].style.display = "table-row";
903 }
904 else
905 {
906 rows[j].style.display = "none";
907 }
908 }
909 }
910 }
911 }
912 }
913}
914
915function asyncRegisterEditSection(cell)
916{
917 //This registering can cause a sizeable delay so we'll thread it (effectively) so the browser is not paused
918 cell.originalValue = cell.value;
919 setTimeout(function(){
920 addEditableState(cell, editableInitStates);
921 // finished initialising one more editable element,
922 // so decrement the counter keeping track of how many elements still need initialising
923 editableElementsInitialisationProgress--;
924 },0);
925}
926
927function addOptionToList(list, optionvalue, optiontext, selected) {
928 var newOption = $("<option>");
929 if (optiontext) {
930 newOption.html(optiontext);
931 newOption.attr("value", optionvalue);
932 } else {
933 newOption.html(optionvalue);
934 }
935 if (selected) {
936 newOption.attr("selected", true);
937 }
938 list.append(newOption);
939}
940
941/* returns either an input or a select element. Data based on
942 availableMetadataElements var. */
943function createMetadataElementSelector() {
944 var metaNameField;
945 if (new_metadata_field_input_type == "fixedlist") {
946 metaNameField = $("<select>", {"class": "ui-state-default"});
947 for(var i=0; i<availableMetadataElements.length; i++) {
948 addOptionToList(metaNameField, availableMetadataElements[i]);
949 }
950 return metaNameField;
951 }
952 metaNameField = $("<input>", {"type": "text","style":"margin: 5px; border: 1px solid #000;"});
953 if (new_metadata_field_input_type == "autocomplete") {
954 metaNameField.autocomplete({
955 minLength: 0,
956 source: availableMetadataElements
957 });
958 metaNameField.attr("title", gs.text.de.enter_meta_dropdwon); //"Enter a metadata name, or use the down arrow to select one, then click 'Add New Metadata'");
959 } else {
960 metaNameField.attr("title", gs.text.de.enter_meta_name); //"Enter a metadata name, then click 'Add New Metadata'");
961 }
962
963 return metaNameField;
964}
965
966function addFunctionalityToTable(table)
967{
968 var $tr_array = table.find("tr");
969
970
971 // We need to keep track of editableElementsInitialisationProgress: the number of editable elements that still need to be initialised/need to finish initialising
972 // Each table's rows means *that* many more editable elements still need initialising.
973 // So each time addFunctionalityToTable() is called on a table, we must increment our counter editableElementsInitialisationProgress
974 // with how many editable cells it has/how many rows it has.
975 editableElementsInitialisationProgress += $tr_array.length;
976
977 $tr_array.each(function()
978 {
979 var cells = $(this).find("td");
980 var metadataName = $(cells[0]).html();
981
982 if(dynamic_metadata_set_list == true && metadataName.indexOf(".") != -1)
983 {
984 var metadataSetName = metadataName.substring(0, metadataName.lastIndexOf("."));
985
986 var found = false;
987 for(var j = 0; j < _metadataSetList.length; j++)
988 {
989 if(metadataSetName == _metadataSetList[j])
990 {
991 found = true;
992 break;
993 }
994 }
995
996 if(!found)
997 {
998 _metadataSetList.push(metadataSetName);
999 addOptionToList( $("#metadataSetList"), metadataSetName);
1000 }
1001 }
1002
1003 asyncRegisterEditSection(cells[1].getElementsByTagName("textarea")[0]);
1004 addRemoveLinkToRow(this);
1005
1006 // add multivalued indicator if needed
1007 if (jQuery.inArray(metadataName, multiValuedMetadata) != -1) {
1008 //if (multiValuedMetadata.includes(metadataName)){
1009 $(cells[0]).html(metadataName + " <span title='"+gs.text.de.multi_valued_tooltip + "' style='float:right;'>"+mvm_delimiter+"</span>"); //Multi-valued metadata. Separate values with semi-colon ;
1010 }
1011
1012 });
1013
1014 // set up autocomplete values
1015 var value_cells = $(".metaTableCellArea");
1016 for (var k=0; k<autocompleteMetadata.length; k++) {
1017 var source_name = autocompleteMetadata[k].replace(/[\.-]/g, "");
1018 var source_obj = window[source_name+"_values"];
1019 if (source_obj) {
1020 value_cells.filter("."+source_name).autocomplete({
1021 minLength: 0,
1022 source: source_obj
1023 });
1024 }
1025 }
1026
1027 // add metadata field selector
1028 var metaNameField = createMetadataElementSelector();
1029 table.after(metaNameField);
1030 table.metaNameField = metaNameField;
1031
1032 /* add the buttons */
1033 // check enable_add_all_button - only valid for fixedlist and autocomplete
1034 if (enable_add_all_metadata_button == true) {
1035 if (new_metadata_field_input_type != "fixedlist" && new_metadata_field_input_type != "autocomplete") {
1036 enable_add_all_metadata_button = false;
1037 }
1038 }
1039
1040 // add single metadata button
1041 var addRowButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1042
1043 addRowButton.html(gs.text.de.add_new_metadata);
1044 addRowButton.click(function()
1045 {
1046 var name = metaNameField.val();
1047 if(!name || name == "")
1048 {
1049 console.log(gs.text.de.no_meta_name_given);
1050 return;
1051 }
1052 addNewMetadataRow(table, name);
1053
1054
1055 });
1056 table.addRowButton = addRowButton;
1057 metaNameField.after(addRowButton);
1058
1059 // add all metadata button
1060 if (enable_add_all_metadata_button == true) {
1061 var addAllButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1062 addAllButton.html(gs.text.de.add_all_metadata);
1063 addAllButton.click(function()
1064 {
1065 for(var i=0; i<availableMetadataElements.length; i++) {
1066
1067 addNewMetadataRow(table, availableMetadataElements[i])
1068 }
1069
1070 });
1071 table.addAllButton = addAllButton;
1072 addRowButton.after(addAllButton);
1073
1074 }
1075
1076}
1077
1078function addNewMetadataRow(table, name) {
1079
1080 var clean_name = name.replace(/[\.-]/g, "");
1081 var newRow = $("<tr>", {"style": "display: table-row;"});
1082 var nameCell;
1083 if (jQuery.inArray(name, multiValuedMetadata) != -1) {
1084 nameCell = $("<td>" + name + " <span title='"+gs.text.de.multi_valued_tooltip + "' style='float:right;'>"+mvm_delimiter+"</span></td>");
1085 } else {
1086 nameCell = $("<td>" + name + "</td>");
1087 }
1088 nameCell.attr("class", "metaTableCellName");
1089 var valueCell = $("<td>", {"class": "metaTableCell"});
1090 var textValue = $("<textarea>", {"class": "metaTableCellArea "+ clean_name});
1091
1092 if (jQuery.inArray(name, autocompleteMetadata) != -1) {
1093 var source_obje = window[clean_name +"_values"];
1094 if (source_obje) {
1095 textValue.autocomplete({
1096 minLength: 0,
1097 source: source_obje
1098 });
1099 }
1100 }
1101 valueCell.append(textValue);
1102 newRow.append(nameCell);
1103 newRow.append(valueCell);
1104 addRemoveLinkToRow(newRow.get(0));
1105 table.append(newRow);
1106
1107 var undo = new Array();
1108 undo.op = "delMeta";
1109 undo.srcElem = newRow;
1110 undo.removeTransaction = false;
1111 _undoOperations.push(undo);
1112 if ( hierarchyStorage && hierarchyStorage[name])
1113 {
1114 setHierarchyEventsWrappers(name);
1115 }
1116}
1117
1118function addRemoveLinkToRow(row)
1119{
1120 var newCell = $("<td>");
1121 var removeLink = $("<a>"+gs.text.de.remove+"</a>", {"href": "javascript:;"});
1122 removeLink.click(function()
1123 {
1124 var undo = new Array();
1125 undo.srcElem = row;
1126 undo.op = "display";
1127 undo.subOp = "table-row";
1128 undo.removeDeletedMetadata = true;
1129 _undoOperations.push(undo);
1130 _deletedMetadata.push(row);
1131 //row.css("display", "none");
1132 $(row).hide();
1133 });
1134 newCell.append(removeLink);
1135 newCell.attr({"class": "metaTableCellRemove", "style": "font-size:0.6em; padding-left: 3px; padding-right: 3px;"});
1136 $(row).append(newCell);
1137}
1138
1139/* This is for 'edit structure' menu bar */
1140function createTopMenuBar()
1141{
1142 //Create the top menu bar
1143 var headerTable = document.createElement("TABLE");
1144 var tableBody = document.createElement("TBODY");
1145 var row = document.createElement("TR");
1146 var newDocCell = document.createElement("TD");
1147 var newSecCell = document.createElement("TD");
1148 var saveCell = document.createElement("TD");
1149 var undoCell = document.createElement("TD");
1150 var metadataListCell = document.createElement("TD");
1151
1152 var metadataListLabel = document.createElement("SPAN");
1153 metadataListLabel.innerHTML = gs.text.de.visible_metadata;
1154 var metadataList = document.createElement("SELECT");
1155 metadataList.setAttribute("id", "metadataSetList");
1156 metadataList.onchange = onVisibleMetadataSetChange;
1157 var allMetadataOption = document.createElement("OPTION");
1158 metadataList.appendChild(allMetadataOption);
1159 allMetadataOption.innerHTML = gs.text.de.all_metadata;
1160 metadataListCell.appendChild(metadataListLabel);
1161 metadataListCell.appendChild(metadataList);
1162
1163 metadataListCell.setAttribute("class", "headerTableTD");
1164 newDocCell.setAttribute("class", "headerTableTD");
1165 newSecCell.setAttribute("class", "headerTableTD");
1166 undoCell.setAttribute("class", "headerTableTD");
1167 saveCell.setAttribute("class", "headerTableTD");
1168
1169 headerTable.appendChild(tableBody);
1170 tableBody.appendChild(row);
1171 row.appendChild(saveCell);
1172 row.appendChild(undoCell);
1173 row.appendChild(newDocCell);
1174 row.appendChild(newSecCell);
1175 row.appendChild(metadataListCell);
1176
1177 //The "Save changes" button
1178 var saveButton = document.createElement("BUTTON");
1179 saveButton.innerHTML = gs.text.de.save_changes;
1180 saveButton.setAttribute("onclick", "saveAndRebuild();");
1181 saveButton.setAttribute("id", "saveButton");
1182 saveCell.appendChild(saveButton);
1183
1184 //The "Undo" button
1185 var undoButton = document.createElement("BUTTON");
1186 undoButton.innerHTML = gs.text.dse.undo;
1187 undoButton.setAttribute("onclick", "undo();");
1188 undoCell.appendChild(undoButton);
1189
1190 //The "Create new document" button
1191 var newDocButton = document.createElement("BUTTON");
1192 newDocButton.innerHTML = gs.text.dse.create_new_document;
1193 newDocButton.setAttribute("onclick", "createNewDocumentArea();");
1194 newDocButton.setAttribute("id", "createNewDocumentButton");
1195 newDocCell.appendChild(newDocButton);
1196
1197 //The "Insert new section" LI
1198 var newSecLI = createDraggableNewSection(newSecCell);
1199
1200 return headerTable;
1201}
1202
1203function getMetadataFromNode(node, name)
1204{
1205 var currentNode = node.firstChild;
1206 while(currentNode != null)
1207 {
1208 if(currentNode.nodeName == "metadataList")
1209 {
1210 currentNode = currentNode.firstChild;
1211 break;
1212 }
1213
1214 currentNode = currentNode.nextSibling;
1215 }
1216
1217 while(currentNode != null)
1218 {
1219 if(currentNode.nodeName == "metadata" && currentNode.getAttribute("name") == name)
1220 {
1221 return currentNode.firstChild.nodeValue;
1222 }
1223
1224 currentNode = currentNode.nextSibling;
1225 }
1226 return "";
1227}
1228
1229function storeMetadata(node, listItem)
1230{
1231 listItem.metadata = new Array();
1232
1233 var currentNode = node.firstChild;
1234 while(currentNode != null)
1235 {
1236 if(currentNode.nodeName == "metadataList")
1237 {
1238 currentNode = currentNode.firstChild;
1239 break;
1240 }
1241
1242 currentNode = currentNode.nextSibling;
1243 }
1244
1245 while(currentNode != null)
1246 {
1247 if(currentNode.nodeName == "metadata")
1248 {
1249 listItem.metadata[currentNode.getAttribute("name")] = currentNode.firstChild.nodeValue;
1250 }
1251
1252 currentNode = currentNode.nextSibling;
1253 }
1254}
1255
1256function getNodeContent(node)
1257{
1258 var currentNode = node.firstChild;
1259 while(currentNode != null)
1260 {
1261 if(currentNode.nodeName == "nodeContent")
1262 {
1263 return currentNode.firstChild;
1264 }
1265
1266 currentNode = currentNode.nextSibling;
1267 }
1268 return null;
1269}
1270
1271function containsDocumentNode(node)
1272{
1273 var currentNode = node.firstChild;
1274 while(currentNode != null)
1275 {
1276 if(currentNode.nodeName == "documentNode")
1277 {
1278 return true;
1279 }
1280
1281 currentNode = currentNode.nextSibling;
1282 }
1283 return false;
1284}
1285
1286function isExpanded(textDiv)
1287{
1288 if(typeof textDiv.style == "undefined" || typeof textDiv.style.display == "undefined" || textDiv.style.display == "block")
1289 {
1290 return true;
1291 }
1292 return false;
1293}
1294
1295function toggleTextDiv(section)
1296{
1297 var textDiv = section.textDiv;
1298 if(textDiv)
1299 {
1300 if(isExpanded(textDiv))
1301 {
1302 textDiv.style.display = "none";
1303 section.menu.editTextLink.innerHTML = gs.text.dse.edit;
1304 }
1305 else
1306 {
1307 textDiv.style.display = "block";
1308 section.menu.editTextLink.innerHTML = gs.text.dse.hide;
1309 }
1310 }
1311}
1312
1313function updateFromTop()
1314{
1315 updateRecursive(document.getElementById("dbDiv"), null, null, 0);
1316}
1317
1318function insertAfter(elem, refElem)
1319{
1320 if(refElem.nextSibling)
1321 {
1322 refElem.parentNode.insertBefore(elem, refElem.nextSibling);
1323 }
1324 else
1325 {
1326 refElem.parentNode.appendChild(elem);
1327 }
1328}
1329
1330function removeFromParent(elem)
1331{
1332 elem.parentNode.removeChild(elem);
1333}
1334
1335function createSectionTitle(text)
1336{
1337 var textSpan = document.createElement("SPAN");
1338 if(text)
1339 {
1340 textSpan.appendChild(document.createTextNode(" " + text + " "));
1341 }
1342 else
1343 {
1344 textSpan.appendChild(document.createTextNode(" [" + gs.text.dse.untitled_section + "] "));
1345 }
1346 return textSpan;
1347}
1348
1349function setMouseOverAndOutFunctions(section)
1350{
1351 //Colour the list item and display the menu on mouse over
1352 section.onmouseover = function(e)
1353 {
1354 if(this.menu){this.menu.style.display = "inline";}
1355 this.style.background = "rgb(255, 200, 0)";
1356 };
1357 //Uncolour the list item and hide the menu on mouse out
1358 section.onmouseout = function(e)
1359 {
1360 if(this.menu){this.menu.style.display = "none";}
1361 this.style.background = "none";
1362 };
1363}
1364
1365function createDraggableNewSection(parent)
1366{
1367 var newSecLI = document.createElement("LI");
1368 var newSpan = document.createElement("SPAN");
1369 newSpan.innerHTML = gs.text.dse.insert_new_section + " ";
1370
1371 newSecLI.sectionTitle = newSpan;
1372 newSecLI.appendChild(newSpan);
1373 newSecLI.setAttribute("class", "dragItem newSection");
1374 newSecLI.newSection = true;
1375 newSecLI.parent = parent;
1376 newSecLI.index = -1;
1377 new YAHOO.example.DDList(newSecLI);
1378 parent.appendChild(newSecLI);
1379}
1380
1381function closeAllOpenContents()
1382{
1383 for(var i = 0; i < _allContents.length; i++)
1384 {
1385 if(isExpanded(_allContents[i].textDiv))
1386 {
1387 toggleTextDiv(_allContents[i]);
1388 }
1389 }
1390 DDM.refreshCache();
1391}
1392
1393//Status Bar class (initialised with new StatusBar(elem);)
1394function StatusBar(mainElem)
1395{
1396 var _statusMap = new Array();
1397 var _statusIDCounter = 0;
1398 var _mainElem = mainElem;
1399 var _activeMessages = 0;
1400
1401 _mainElem.style.display = "none";
1402
1403 this.addStatus = function(newStatus)
1404 {
1405 _mainElem.style.display = "block";
1406 var newStatusDiv = document.createElement("DIV");
1407 var newStatusSpan = document.createElement("SPAN");
1408
1409 var workingImage = document.createElement("IMG");
1410 workingImage.setAttribute("src", gs.imageURLs.loading);
1411 workingImage.setAttribute("height", "16px");
1412 workingImage.setAttribute("width", "16px");
1413 newStatusDiv.appendChild(workingImage);
1414
1415 newStatusDiv.appendChild(newStatusSpan);
1416 newStatusSpan.innerHTML = " " + newStatus;
1417 newStatusDiv.setAttribute("class", "statusMessage");
1418 newStatusDiv.span = newStatusSpan;
1419
1420 _mainElem.appendChild(newStatusDiv);
1421 _statusMap["status" + _statusIDCounter] = newStatusDiv;
1422 _activeMessages++;
1423 return _statusIDCounter++;
1424 }
1425
1426 this.changeStatus = function(id, newStatus)
1427 {
1428 if(_statusMap["status" + id])
1429 {
1430 _statusMap["status" + id].span.innerHTML = " " + newStatus;
1431 }
1432 }
1433
1434 this.removeStatus = function(id)
1435 {
1436 if(_statusMap["status" + id])
1437 {
1438 removeFromParent(_statusMap["status" + id]);
1439
1440 if(--_activeMessages == 0)
1441 {
1442 _mainElem.style.display = "none";
1443 }
1444 }
1445 }
1446
1447 this.clear = function()
1448 {
1449 for(var p in _statusMap)
1450 {
1451 if(_statusMap.hasOwnProperty(p))
1452 {
1453 if(_statusMap[p] && _statusMap[p].parentNode)
1454 {
1455 removeFromParent(_statusMap[p]);
1456 }
1457
1458 if(--_activeMessages == 0)
1459 {
1460 _mainElem.style.display = "none";
1461 }
1462 }
1463 }
1464 }
1465}
1466
1467/*
1468function toggleEdit(e)
1469{
1470 var mousePos = de.events.getXYInWindowFromEvent(e);
1471 var cDesc = de.cursor.getCursorDescAtXY(mousePos.x, mousePos.y, de.events.getEventTarget(e));
1472 de.cursor.setCursor(cDesc);
1473}
1474*/
1475
1476
1477$( document ).ready(function() {
1478 // DOM Ready.
1479
1480 // Now can add handlers to monitor when ckeditor instances are all ready
1481 // See https://stackoverflow.com/questions/18461206/how-to-retrieve-the-ckeditor-status-ready
1482 // (and https://stackoverflow.com/questions/3447803/how-to-determine-if-ckeditor-is-loaded )
1483 if(gs.cgiParams.docEdit == "1") { // CKEDITOR only exists in docEdit mode
1484
1485 //CKEDITOR.on( 'loaded', function( evt ) { // not triggered
1486 //console.log("*** CKEDITOR loaded");
1487
1488 CKEDITOR.on( 'instanceReady', function( evt ) {
1489 //console.log("*** CKEDITOR instanceReady", evt);
1490 addCKEEditableState(evt,editableInitStates);
1491 // finished initialising one more editable element,
1492 // so decrement the counter keeping track of how many elements still need initialising
1493 editableElementsInitialisationProgress--;
1494 } );
1495 //} );
1496 }
1497
1498});
1499
1500
1501
Note: See TracBrowser for help on using the repository browser.