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

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

Adding a comment that deletions to GPS.mapOverlay metadata are treated as changes not separately as deletions, and are recorded in doc.xml as [].

  • Property svn:executable set to *
File size: 41.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 var request = "[";
348 for(var i = 0; i < _transactions.length; i++)
349 {
350 request += _transactions[i];
351 if(i != _transactions.length - 1)
352 {
353 request += ",";
354 }
355 }
356 request += "]";
357
358 var statusID;
359 var ajax = new gs.functions.ajaxRequest();
360 ajax.open("POST", gs.xsltParams.library_name, true);
361 ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
362 ajax.onreadystatechange = function()
363 {
364 if(ajax.readyState == 4 && ajax.status == 200)
365 {
366 var text = ajax.responseText;
367 var xml = validateXML(text);
368
369 var errorElems;
370 if(!xml || checkForErrors(xml))
371 {
372 alert(gs.text.dse.error_saving);
373
374 enableSaveButtons(true);
375
376 if(_statusBar)
377 {
378 _statusBar.removeStatus(statusID);
379 }
380 return;
381 }
382
383 if(_statusBar)
384 {
385 _statusBar.removeStatus(statusID);
386 }
387 if (rebuild) {
388 buildCollections(_collectionsToBuild, null, reloadUponRebuild);
389 } else {
390 // reset the save button here
391 enableSaveButtons(true);
392 // saving to archives is now done, clear the transactions
393 // that were keeping track of the full text changes that have now
394 // been performed to archives (no member var keeps track of meta changes, only a local var)
395 _transactions = new Array();
396 }
397 }
398 }
399
400 if(_collectionsToBuild.length > 0)
401 {
402 enableSaveButtons(false);
403
404 if(_statusBar)
405 {
406 statusID = _statusBar.addStatus(gs.text.dse.modifying_archives + "...");
407 }
408 ajax.send("a=g&rt=r&s=DocumentExecuteTransaction&s1.transactions=" + request);
409 }
410 } // end sendBuildRequest definition
411
412 var metadataChanges = new Array();
413 if (_deletedMetadata.length > 0) {
414 //addCollectionToBuild(collection);
415
416 for(var i = 0; i < _deletedMetadata.length; i++) {
417
418 var currentRow = _deletedMetadata[i];
419
420 //Get document ID
421 var currentElem = currentRow;
422 while((currentElem = currentElem.parentNode).tagName != "TABLE");
423 var docID = currentElem.getAttribute("id").substring(4);
424
425 //Get metadata name
426 var cells = currentRow.getElementsByTagName("TD");
427 var nameCell = cells[0];
428 // metadata name cell might have the multivalue indicator in it, so just want the first word
429 var name = nameCell.innerHTML.split(" ")[0];
430 var valueCell = cells[1];
431 var value = valueCell.getElementsByTagName("TEXTAREA")[0].value;
432 if (value.length) {
433 // check for non empty value, in case all they have done is add a field then deleted it.
434 metadataChanges.push({type:'delete', docID:docID, name:name, value:value});
435 addCollectionToBuild(collection);
436 }
437 removeFromParent(currentRow);
438 }
439 }
440
441 var changes = changesToUpdate();
442
443 //Clean changes
444 editableInitStates = editableLastStates;
445
446 for(var i = 0; i < changes.length; i++)
447 {
448 var changedElem = changes[i];
449 //Save metadata
450
451 if(gs.functions.hasClass(changedElem, "metaTableCellArea"))
452 {
453 //Get document ID
454 var currentElem = changedElem;
455 while((currentElem = currentElem.parentNode).tagName != "TABLE");
456 var docID = currentElem.getAttribute("id").substring(4);
457
458 //Get metadata name
459 var row = changedElem.parentNode.parentNode;
460 var cells = row.getElementsByTagName("TD");
461 var nameCell = cells[0];
462 // metadata name cell might have the multivalue indicator in it, so just want the first word
463 var name = nameCell.innerHTML.split(" ")[0];
464 var value = changedElem.value;
465 value = value.replace(/&nbsp;/g, " ");
466
467 var orig = changedElem.originalValue;
468 if (orig) {
469 orig = orig.replace(/&nbsp;/g, " ");
470 }
471 if (jQuery.inArray(name, multiValuedMetadata) != -1) {
472
473 // split the values
474 var values_list = value.split(mvm_delimiter);
475 var orig_list;
476 var num_orig;
477 if (orig) {
478 orig_list = orig.split(mvm_delimiter);
479 num_orig = orig_list.length;
480 }
481
482 for(var i = 0; i < values_list.length; i++) {
483 var val = values_list[i];
484 var ori =null;
485 if (orig && i<num_orig) {
486 ori = orig_list[i];
487 }
488 metadataChanges.push({collection:collection, docID:docID, name:name, value:val, orig:ori});
489 }
490 } else {
491 metadataChanges.push({collection:collection, docID:docID, name:name, value:value, orig:orig});
492 }
493 changedElem.originalValue = changedElem.value;
494 addCollectionToBuild(collection);
495 }
496 //Save content
497 else if(gs.functions.hasClass(changedElem, "renderedText"))
498 {
499 var section = changedElem.parentDiv.parentItem;
500 saveTransaction('{"operation":"setText", "text":"' + CKEDITOR.instances[changedElem.getAttribute("id")].getData().replace(/%/g, "%25").replace(/"/g, "\\\"").replace(/&/g, "%26") + '", "collection":"' + section.collection + '", "oid":"' + section.nodeID + '"}'); //'
501 addCollectionToBuild(section.collection);
502 }
503 else if(gs.functions.hasClass(changedElem, "sectionText"))
504 {
505 var id = changedElem.getAttribute("id");
506 var sectionID = id.substring(4);
507 saveTransaction('{"operation":"setText", "text":"' + CKEDITOR.instances[changedElem.getAttribute("id")].getData().replace(/%/g, "%25").replace(/"/g, "\\\"").replace(/&/g, "%26") + '", "collection":"' + gs.cgiParams.c + '", "oid":"' + sectionID + '"}'); //'
508 addCollectionToBuild(gs.cgiParams.c);
509 }
510 }
511
512 // Check for changes to any map editors in the document
513 // NOTE: At present, we don't maintain a list of deletions for the map editor:
514 // GPS.mapOverlay data that has been removed is recorded as a change not a deletion,
515 // with the metadata's new value being the string of an empty JSON array, [],
516 // and entered as such into doc.xml.
517 var modified_map_editors_data = getDocMapsEditDataForSaving(gs.cgiParams.c); // collection
518 for(var i = 0; i < modified_map_editors_data.length; i++) {
519 metadataChanges.push(modified_map_editors_data[i]); // of the form { collection: gs.cgiParams.c, docID: nodeID, name:"GSP.mapOverlay", metapos: 0, value: <stringifiedJSON> }
520 addCollectionToBuild(gs.cgiParams.c); // collection
521 }
522
523
524
525 var processChangesLoop = function(index)
526 {
527 var change = metadataChanges[index];
528
529 var callbackFunction;
530 if(index + 1 == metadataChanges.length)
531 {
532 callbackFunction = sendBuildRequest;
533 }
534 else
535 {
536 callbackFunction = function(){processChangesLoop(index + 1)};
537 }
538 if (change.type == "delete") {
539 gs.functions.removeArchivesMetadata(collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), function(){callbackFunction();});
540 } else {
541 // 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.
542 // https://ultimatecourses.com/blog/methods-to-determine-if-an-object-has-a-given-property
543 if('metapos' in change && change.metapos === 0) {// && change.metapos === 0) { // for maps
544
545 // collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, responseFunction
546 //console.log("@@@ metapos! change: ", change);
547 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, change.metapos, encodeDelimiters(change.value), null, "override", function(){callbackFunction();});
548 }
549 else if(change.orig)
550 {
551 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), encodeDelimiters(change.orig), "override", function(){callbackFunction();});
552 }
553 else
554 {
555 gs.functions.setArchivesMetadata(change.collection, gs.xsltParams.site_name, change.docID, change.name, null, encodeDelimiters(change.value), null, "accumulate", function(){callbackFunction();});
556 }
557 }
558 }
559 if (metadataChanges.length>0) {
560 // this will process each change one by one, and then send the build request
561 processChangesLoop(0);
562 }
563 else if(_collectionsToBuild.length > 0) {
564 // if there are no metadata changes, but some other changes eg text have happened, then we need to send the build request.
565 sendBuildRequest();
566 }
567
568 /* need to clear the changes from the page so that we don't process them again next time */
569 while (_deletedMetadata.length>0) {
570 _deletedMetadata.pop();
571 }
572
573}
574
575
576function buildCollections(collections, documents, callback)
577{
578 if(!collections || collections.length == 0)
579 {
580 console.log(gs.text.dse.empty_collection_list);
581 enableSaveButtons(true);
582 return;
583 }
584
585 var docs = "";
586 var buildOperation = "";
587 if(documents)
588 {
589 buildOperation = "ImportCollection";
590 docs += "&s1.documents=";
591 for(var i = 0; i < documents.length; i++)
592 {
593 docs += documents[i];
594 if(i < documents.length - 1)
595 {
596 docs += ",";
597 }
598 }
599 }
600 else
601 {
602 buildOperation = "BuildAndActivateCollection";
603 }
604
605 var counter = 0;
606 var statusID = 0;
607 var buildFunction = function()
608 {
609 var ajax = new gs.functions.ajaxRequest();
610 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=" + buildOperation + "&s1.incremental=true&s1.collection=" + collections[counter] + docs);
611 ajax.onreadystatechange = function()
612 {
613 if(ajax.readyState == 4 && ajax.status == 200)
614 {
615 var text = ajax.responseText;
616 var xml = validateXML(text);
617
618 if(!xml || checkForErrors(xml))
619 {
620 alert(gs.text.dse.could_not_build_p1 + " " + collections[counter] + gs.text.dse.could_not_build_p2);
621
622 if(_statusBar)
623 {
624 _statusBar.removeStatus(statusID);
625 }
626 enableSaveButtons(true);
627
628 return;
629 }
630
631 var status = xml.getElementsByTagName("status")[0];
632 var pid = status.getAttribute("pid");
633
634 startCheckLoop(pid, buildOperation, statusID, function()
635 {
636 /*
637 var localAjax = new gs.functions.ajaxRequest();
638 localAjax.open("GET", gs.xsltParams.library_name + "?a=g&rt=r&ro=1&s=ActivateCollection&s1.collection=" + collections[counter], true);
639 localAjax.onreadystatechange = function()
640 {
641 if(localAjax.readyState == 4 && localAjax.status == 200)
642 {
643 var localText = localAjax.responseText;
644 var localXML = validateXML(localText);
645
646 if(!xml || checkForErrors(xml))
647 {
648 alert(gs.text.dse.could_not_activate_p1 + " " + collections[counter] + gs.text.dse.could_not_activate_p2);
649
650 if(_statusBar)
651 {
652 _statusBar.removeStatus(statusID);
653 }
654 enableSaveButtons(true);
655
656 return;
657 }
658
659 var localStatus = localXML.getElementsByTagName("status")[0];
660 if(localStatus)
661 {
662 var localPID = localStatus.getAttribute("pid");
663 startCheckLoop(localPID, "ActivateCollection", statusID, function()
664 {
665 */
666 if(counter == collections.length - 1)
667 {
668 removeCollectionsFromBuildList(collections);
669 if(callback)
670 {
671 callback();
672 }
673 }
674 else
675 {
676 counter++;
677 buildFunction();
678 }
679
680 _transactions = new Array();
681
682 if(_statusBar)
683 {
684 _statusBar.removeStatus(statusID);
685 }
686 enableSaveButtons(true);
687 /*
688 });
689 }
690 }
691 }
692 if(_statusBar)
693 {
694 _statusBar.changeStatus(statusID, gs.text.dse.activating + " " + collections[counter] + "...");
695 }
696 localAjax.send();
697 */
698 });
699 }
700 }
701 if(_statusBar)
702 {
703 statusID = _statusBar.addStatus(gs.text.dse.building + " " + collections[counter] + "...");
704 }
705 ajax.send();
706 }
707 buildFunction();
708}
709
710function startCheckLoop(pid, serverFunction, statusID, callbackFunction)
711{
712 var ajaxFunction = function()
713 {
714 var ajax = new gs.functions.ajaxRequest();
715 ajax.open("GET", gs.xsltParams.library_name + "?a=g&rt=s&ro=1&s=" + serverFunction + "&s1.pid=" + pid, true);
716 ajax.onreadystatechange = function()
717 {
718 if(ajax.readyState == 4 && ajax.status == 200)
719 {
720 var text = ajax.responseText;
721 var xml = validateXML(text);
722
723 if(!xml || checkForErrors(xml))
724 {
725 alert(gs.text.dse.could_not_check_status_p1 + " " + serverFunction + gs.text.dse.could_not_check_status_p2a);
726
727 if(_statusBar)
728 {
729 _statusBar.removeStatus(statusID);
730 }
731 enableSaveButtons(true);
732
733 return;
734 }
735
736 var status = xml.getElementsByTagName("status")[0];
737 var code = status.getAttribute("code");
738
739 if (code == COMPLETED || code == SUCCESS)
740 {
741 callbackFunction();
742 }
743 else if (code == HALTED || code == ERROR)
744 {
745 alert(gs.text.dse.could_not_check_status_p1 + " " + serverFunction + gs.text.dse.could_not_check_status_p2b);
746
747 if(_statusBar)
748 {
749 _statusBar.removeStatus(statusID);
750 }
751 enableSaveButtons(true);
752 }
753 else
754 {
755 setTimeout(ajaxFunction, 1000);
756 }
757 }
758 }
759 ajax.send();
760 }
761 ajaxFunction();
762}
763
764function removeCollectionsFromBuildList(collections)
765{
766 var tempArray = new Array();
767 for(var i = 0; i < _collectionsToBuild.length; i++)
768 {
769 var found = false;
770 for(var j = 0; j < collections.length; j++)
771 {
772 if(collections[j] == _collectionsToBuild[i])
773 {
774 found = true;
775 break;
776 }
777 }
778
779 if(!found)
780 {
781 tempArray.push(_collectionsToBuild[i]);
782 }
783 }
784 _collectionsToBuild = tempArray;
785}
786
787function checkForErrors(xml)
788{
789 var errorElems = xml.getElementsByTagName("error");
790
791 if(errorElems && errorElems.length > 0)
792 {
793 var errorString = gs.text.dse.error_saving_changes + ": ";
794 for(var i = 0; i < errorElems.length; i++)
795 {
796 errorString += " " + errorElems.item(i).firstChild.nodeValue;
797 }
798 alert(errorString);
799 return true;
800 }
801 return false; //No errors
802}
803
804function validateXML(txt)
805{
806 // code for IE
807 if (window.ActiveXObject)
808 {
809 var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
810 xmlDoc.async = "false";
811 xmlDoc.loadXML(document.all(txt).value);
812
813 if(xmlDoc.parseError.errorCode!=0)
814 {
815 txt = dse.error_code + ": " + xmlDoc.parseError.errorCode + "\n";
816 txt = txt + dse.error_reason + ": " + xmlDoc.parseError.reason;
817 txt = txt + dse.error_line + ": " + xmlDoc.parseError.line;
818 console.log(txt);
819 return null;
820 }
821
822 return xmlDoc;
823 }
824 // code for Mozilla, Firefox, Opera, etc.
825 else if (document.implementation.createDocument)
826 {
827 var parser = new DOMParser();
828 var xmlDoc = parser.parseFromString(txt,"text/xml");
829
830 if (xmlDoc.getElementsByTagName("parsererror").length > 0)
831 {
832 console.log(gs.text.dse.xml_error);
833 return null;
834 }
835
836 return xmlDoc;
837 }
838 else
839 {
840 console.log(gs.text.dse.browse_cannot_validate_xml);
841 }
842 return null;
843}
844
845function onVisibleMetadataSetChange()
846{
847 var metadataList = document.getElementById("metadataSetList");
848 var index = metadataList.selectedIndex;
849 var options = metadataList.getElementsByTagName("OPTION");
850 var selectedOption = options[index];
851
852 var selectedSet = selectedOption.value;
853 changeVisibleMetadata(selectedSet);
854}
855
856function changeVisibleMetadata(metadataSetName)
857{
858 var metaSetList = metadataSetName.split(",");
859 var tables = document.getElementsByTagName("TABLE");
860 for(var i = 0; i < tables.length; i++)
861 {
862 var id = tables[i].getAttribute("id");
863 if(id && id.search(/^meta/) != -1)
864 {
865 var rows = tables[i].getElementsByTagName("TR");
866 for(var j = 0; j < rows.length; j++)
867 {
868 if(metadataSetName == "All")
869 {
870 rows[j].style.display = "table-row";
871 }
872 else
873 {
874 var cells = rows[j].getElementsByTagName("TD");
875 // metadata name cell might have the multivalue indicator in it, so just want the first word
876 var cellName = cells[0].innerHTML.split(" ")[0];
877
878 if(cellName.indexOf(".") == -1)
879 {
880 rows[j].style.display = "none";
881 }
882 else
883 {
884 var setName = cellName.substring(0, cellName.lastIndexOf("."));
885 if (metaSetList.indexOf(setName)!= -1)
886 {
887 rows[j].style.display = "table-row";
888 }
889 else
890 {
891 rows[j].style.display = "none";
892 }
893 }
894 }
895 }
896 }
897 }
898}
899
900function asyncRegisterEditSection(cell)
901{
902 //This registering can cause a sizeable delay so we'll thread it (effectively) so the browser is not paused
903 cell.originalValue = cell.value;
904 setTimeout(function(){
905 addEditableState(cell, editableInitStates);
906 // finished initialising one more editable element,
907 // so decrement the counter keeping track of how many elements still need initialising
908 editableElementsInitialisationProgress--;
909 },0);
910}
911
912function addOptionToList(list, optionvalue, optiontext, selected) {
913 var newOption = $("<option>");
914 if (optiontext) {
915 newOption.html(optiontext);
916 newOption.attr("value", optionvalue);
917 } else {
918 newOption.html(optionvalue);
919 }
920 if (selected) {
921 newOption.attr("selected", true);
922 }
923 list.append(newOption);
924}
925
926/* returns either an input or a select element. Data based on
927 availableMetadataElements var. */
928function createMetadataElementSelector() {
929 var metaNameField;
930 if (new_metadata_field_input_type == "fixedlist") {
931 metaNameField = $("<select>", {"class": "ui-state-default"});
932 for(var i=0; i<availableMetadataElements.length; i++) {
933 addOptionToList(metaNameField, availableMetadataElements[i]);
934 }
935 return metaNameField;
936 }
937 metaNameField = $("<input>", {"type": "text","style":"margin: 5px; border: 1px solid #000;"});
938 if (new_metadata_field_input_type == "autocomplete") {
939 metaNameField.autocomplete({
940 minLength: 0,
941 source: availableMetadataElements
942 });
943 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'");
944 } else {
945 metaNameField.attr("title", gs.text.de.enter_meta_name); //"Enter a metadata name, then click 'Add New Metadata'");
946 }
947
948 return metaNameField;
949}
950
951function addFunctionalityToTable(table)
952{
953 var $tr_array = table.find("tr");
954
955
956 // We need to keep track of editableElementsInitialisationProgress: the number of editable elements that still need to be initialised/need to finish initialising
957 // Each table's rows means *that* many more editable elements still need initialising.
958 // So each time addFunctionalityToTable() is called on a table, we must increment our counter editableElementsInitialisationProgress
959 // with how many editable cells it has/how many rows it has.
960 editableElementsInitialisationProgress += $tr_array.length;
961
962 $tr_array.each(function()
963 {
964 var cells = $(this).find("td");
965 var metadataName = $(cells[0]).html();
966
967 if(dynamic_metadata_set_list == true && metadataName.indexOf(".") != -1)
968 {
969 var metadataSetName = metadataName.substring(0, metadataName.lastIndexOf("."));
970
971 var found = false;
972 for(var j = 0; j < _metadataSetList.length; j++)
973 {
974 if(metadataSetName == _metadataSetList[j])
975 {
976 found = true;
977 break;
978 }
979 }
980
981 if(!found)
982 {
983 _metadataSetList.push(metadataSetName);
984 addOptionToList( $("#metadataSetList"), metadataSetName);
985 }
986 }
987
988 asyncRegisterEditSection(cells[1].getElementsByTagName("textarea")[0]);
989 addRemoveLinkToRow(this);
990
991 // add multivalued indicator if needed
992 if (jQuery.inArray(metadataName, multiValuedMetadata) != -1) {
993 //if (multiValuedMetadata.includes(metadataName)){
994 $(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 ;
995 }
996
997 });
998
999 // set up autocomplete values
1000 var value_cells = $(".metaTableCellArea");
1001 for (var k=0; k<autocompleteMetadata.length; k++) {
1002 var source_name = autocompleteMetadata[k].replace(/[\.-]/g, "");
1003 var source_obj = window[source_name+"_values"];
1004 if (source_obj) {
1005 value_cells.filter("."+source_name).autocomplete({
1006 minLength: 0,
1007 source: source_obj
1008 });
1009 }
1010 }
1011
1012 // add metadata field selector
1013 var metaNameField = createMetadataElementSelector();
1014 table.after(metaNameField);
1015 table.metaNameField = metaNameField;
1016
1017 /* add the buttons */
1018 // check enable_add_all_button - only valid for fixedlist and autocomplete
1019 if (enable_add_all_metadata_button == true) {
1020 if (new_metadata_field_input_type != "fixedlist" && new_metadata_field_input_type != "autocomplete") {
1021 enable_add_all_metadata_button = false;
1022 }
1023 }
1024
1025 // add single metadata button
1026 var addRowButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1027
1028 addRowButton.html(gs.text.de.add_new_metadata);
1029 addRowButton.click(function()
1030 {
1031 var name = metaNameField.val();
1032 if(!name || name == "")
1033 {
1034 console.log(gs.text.de.no_meta_name_given);
1035 return;
1036 }
1037 addNewMetadataRow(table, name);
1038
1039
1040 });
1041 table.addRowButton = addRowButton;
1042 metaNameField.after(addRowButton);
1043
1044 // add all metadata button
1045 if (enable_add_all_metadata_button == true) {
1046 var addAllButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1047 addAllButton.html(gs.text.de.add_all_metadata);
1048 addAllButton.click(function()
1049 {
1050 for(var i=0; i<availableMetadataElements.length; i++) {
1051
1052 addNewMetadataRow(table, availableMetadataElements[i])
1053 }
1054
1055 });
1056 table.addAllButton = addAllButton;
1057 addRowButton.after(addAllButton);
1058
1059 }
1060
1061}
1062
1063function addNewMetadataRow(table, name) {
1064
1065 var clean_name = name.replace(/[\.-]/g, "");
1066 var newRow = $("<tr>", {"style": "display: table-row;"});
1067 var nameCell;
1068 if (jQuery.inArray(name, multiValuedMetadata) != -1) {
1069 nameCell = $("<td>" + name + " <span title='"+gs.text.de.multi_valued_tooltip + "' style='float:right;'>"+mvm_delimiter+"</span></td>");
1070 } else {
1071 nameCell = $("<td>" + name + "</td>");
1072 }
1073 nameCell.attr("class", "metaTableCellName");
1074 var valueCell = $("<td>", {"class": "metaTableCell"});
1075 var textValue = $("<textarea>", {"class": "metaTableCellArea "+ clean_name});
1076
1077 if (jQuery.inArray(name, autocompleteMetadata) != -1) {
1078 var source_obje = window[clean_name +"_values"];
1079 if (source_obje) {
1080 textValue.autocomplete({
1081 minLength: 0,
1082 source: source_obje
1083 });
1084 }
1085 }
1086 valueCell.append(textValue);
1087 newRow.append(nameCell);
1088 newRow.append(valueCell);
1089 addRemoveLinkToRow(newRow.get(0));
1090 table.append(newRow);
1091
1092 var undo = new Array();
1093 undo.op = "delMeta";
1094 undo.srcElem = newRow;
1095 undo.removeTransaction = false;
1096 _undoOperations.push(undo);
1097 if ( hierarchyStorage && hierarchyStorage[name])
1098 {
1099 setHierarchyEventsWrappers(name);
1100 }
1101}
1102
1103function addRemoveLinkToRow(row)
1104{
1105 var newCell = $("<td>");
1106 var removeLink = $("<a>"+gs.text.de.remove+"</a>", {"href": "javascript:;"});
1107 removeLink.click(function()
1108 {
1109 var undo = new Array();
1110 undo.srcElem = row;
1111 undo.op = "display";
1112 undo.subOp = "table-row";
1113 undo.removeDeletedMetadata = true;
1114 _undoOperations.push(undo);
1115 _deletedMetadata.push(row);
1116 //row.css("display", "none");
1117 $(row).hide();
1118 });
1119 newCell.append(removeLink);
1120 newCell.attr({"class": "metaTableCellRemove", "style": "font-size:0.6em; padding-left: 3px; padding-right: 3px;"});
1121 $(row).append(newCell);
1122}
1123
1124/* This is for 'edit structure' menu bar */
1125function createTopMenuBar()
1126{
1127 //Create the top menu bar
1128 var headerTable = document.createElement("TABLE");
1129 var tableBody = document.createElement("TBODY");
1130 var row = document.createElement("TR");
1131 var newDocCell = document.createElement("TD");
1132 var newSecCell = document.createElement("TD");
1133 var saveCell = document.createElement("TD");
1134 var undoCell = document.createElement("TD");
1135 var metadataListCell = document.createElement("TD");
1136
1137 var metadataListLabel = document.createElement("SPAN");
1138 metadataListLabel.innerHTML = gs.text.de.visible_metadata;
1139 var metadataList = document.createElement("SELECT");
1140 metadataList.setAttribute("id", "metadataSetList");
1141 metadataList.onchange = onVisibleMetadataSetChange;
1142 var allMetadataOption = document.createElement("OPTION");
1143 metadataList.appendChild(allMetadataOption);
1144 allMetadataOption.innerHTML = gs.text.de.all_metadata;
1145 metadataListCell.appendChild(metadataListLabel);
1146 metadataListCell.appendChild(metadataList);
1147
1148 metadataListCell.setAttribute("class", "headerTableTD");
1149 newDocCell.setAttribute("class", "headerTableTD");
1150 newSecCell.setAttribute("class", "headerTableTD");
1151 undoCell.setAttribute("class", "headerTableTD");
1152 saveCell.setAttribute("class", "headerTableTD");
1153
1154 headerTable.appendChild(tableBody);
1155 tableBody.appendChild(row);
1156 row.appendChild(saveCell);
1157 row.appendChild(undoCell);
1158 row.appendChild(newDocCell);
1159 row.appendChild(newSecCell);
1160 row.appendChild(metadataListCell);
1161
1162 //The "Save changes" button
1163 var saveButton = document.createElement("BUTTON");
1164 saveButton.innerHTML = gs.text.de.save_changes;
1165 saveButton.setAttribute("onclick", "saveAndRebuild();");
1166 saveButton.setAttribute("id", "saveButton");
1167 saveCell.appendChild(saveButton);
1168
1169 //The "Undo" button
1170 var undoButton = document.createElement("BUTTON");
1171 undoButton.innerHTML = gs.text.dse.undo;
1172 undoButton.setAttribute("onclick", "undo();");
1173 undoCell.appendChild(undoButton);
1174
1175 //The "Create new document" button
1176 var newDocButton = document.createElement("BUTTON");
1177 newDocButton.innerHTML = gs.text.dse.create_new_document;
1178 newDocButton.setAttribute("onclick", "createNewDocumentArea();");
1179 newDocButton.setAttribute("id", "createNewDocumentButton");
1180 newDocCell.appendChild(newDocButton);
1181
1182 //The "Insert new section" LI
1183 var newSecLI = createDraggableNewSection(newSecCell);
1184
1185 return headerTable;
1186}
1187
1188function getMetadataFromNode(node, name)
1189{
1190 var currentNode = node.firstChild;
1191 while(currentNode != null)
1192 {
1193 if(currentNode.nodeName == "metadataList")
1194 {
1195 currentNode = currentNode.firstChild;
1196 break;
1197 }
1198
1199 currentNode = currentNode.nextSibling;
1200 }
1201
1202 while(currentNode != null)
1203 {
1204 if(currentNode.nodeName == "metadata" && currentNode.getAttribute("name") == name)
1205 {
1206 return currentNode.firstChild.nodeValue;
1207 }
1208
1209 currentNode = currentNode.nextSibling;
1210 }
1211 return "";
1212}
1213
1214function storeMetadata(node, listItem)
1215{
1216 listItem.metadata = new Array();
1217
1218 var currentNode = node.firstChild;
1219 while(currentNode != null)
1220 {
1221 if(currentNode.nodeName == "metadataList")
1222 {
1223 currentNode = currentNode.firstChild;
1224 break;
1225 }
1226
1227 currentNode = currentNode.nextSibling;
1228 }
1229
1230 while(currentNode != null)
1231 {
1232 if(currentNode.nodeName == "metadata")
1233 {
1234 listItem.metadata[currentNode.getAttribute("name")] = currentNode.firstChild.nodeValue;
1235 }
1236
1237 currentNode = currentNode.nextSibling;
1238 }
1239}
1240
1241function getNodeContent(node)
1242{
1243 var currentNode = node.firstChild;
1244 while(currentNode != null)
1245 {
1246 if(currentNode.nodeName == "nodeContent")
1247 {
1248 return currentNode.firstChild;
1249 }
1250
1251 currentNode = currentNode.nextSibling;
1252 }
1253 return null;
1254}
1255
1256function containsDocumentNode(node)
1257{
1258 var currentNode = node.firstChild;
1259 while(currentNode != null)
1260 {
1261 if(currentNode.nodeName == "documentNode")
1262 {
1263 return true;
1264 }
1265
1266 currentNode = currentNode.nextSibling;
1267 }
1268 return false;
1269}
1270
1271function isExpanded(textDiv)
1272{
1273 if(typeof textDiv.style == "undefined" || typeof textDiv.style.display == "undefined" || textDiv.style.display == "block")
1274 {
1275 return true;
1276 }
1277 return false;
1278}
1279
1280function toggleTextDiv(section)
1281{
1282 var textDiv = section.textDiv;
1283 if(textDiv)
1284 {
1285 if(isExpanded(textDiv))
1286 {
1287 textDiv.style.display = "none";
1288 section.menu.editTextLink.innerHTML = gs.text.dse.edit;
1289 }
1290 else
1291 {
1292 textDiv.style.display = "block";
1293 section.menu.editTextLink.innerHTML = gs.text.dse.hide;
1294 }
1295 }
1296}
1297
1298function updateFromTop()
1299{
1300 updateRecursive(document.getElementById("dbDiv"), null, null, 0);
1301}
1302
1303function insertAfter(elem, refElem)
1304{
1305 if(refElem.nextSibling)
1306 {
1307 refElem.parentNode.insertBefore(elem, refElem.nextSibling);
1308 }
1309 else
1310 {
1311 refElem.parentNode.appendChild(elem);
1312 }
1313}
1314
1315function removeFromParent(elem)
1316{
1317 elem.parentNode.removeChild(elem);
1318}
1319
1320function createSectionTitle(text)
1321{
1322 var textSpan = document.createElement("SPAN");
1323 if(text)
1324 {
1325 textSpan.appendChild(document.createTextNode(" " + text + " "));
1326 }
1327 else
1328 {
1329 textSpan.appendChild(document.createTextNode(" [" + gs.text.dse.untitled_section + "] "));
1330 }
1331 return textSpan;
1332}
1333
1334function setMouseOverAndOutFunctions(section)
1335{
1336 //Colour the list item and display the menu on mouse over
1337 section.onmouseover = function(e)
1338 {
1339 if(this.menu){this.menu.style.display = "inline";}
1340 this.style.background = "rgb(255, 200, 0)";
1341 };
1342 //Uncolour the list item and hide the menu on mouse out
1343 section.onmouseout = function(e)
1344 {
1345 if(this.menu){this.menu.style.display = "none";}
1346 this.style.background = "none";
1347 };
1348}
1349
1350function createDraggableNewSection(parent)
1351{
1352 var newSecLI = document.createElement("LI");
1353 var newSpan = document.createElement("SPAN");
1354 newSpan.innerHTML = gs.text.dse.insert_new_section + " ";
1355
1356 newSecLI.sectionTitle = newSpan;
1357 newSecLI.appendChild(newSpan);
1358 newSecLI.setAttribute("class", "dragItem newSection");
1359 newSecLI.newSection = true;
1360 newSecLI.parent = parent;
1361 newSecLI.index = -1;
1362 new YAHOO.example.DDList(newSecLI);
1363 parent.appendChild(newSecLI);
1364}
1365
1366function closeAllOpenContents()
1367{
1368 for(var i = 0; i < _allContents.length; i++)
1369 {
1370 if(isExpanded(_allContents[i].textDiv))
1371 {
1372 toggleTextDiv(_allContents[i]);
1373 }
1374 }
1375 DDM.refreshCache();
1376}
1377
1378//Status Bar class (initialised with new StatusBar(elem);)
1379function StatusBar(mainElem)
1380{
1381 var _statusMap = new Array();
1382 var _statusIDCounter = 0;
1383 var _mainElem = mainElem;
1384 var _activeMessages = 0;
1385
1386 _mainElem.style.display = "none";
1387
1388 this.addStatus = function(newStatus)
1389 {
1390 _mainElem.style.display = "block";
1391 var newStatusDiv = document.createElement("DIV");
1392 var newStatusSpan = document.createElement("SPAN");
1393
1394 var workingImage = document.createElement("IMG");
1395 workingImage.setAttribute("src", gs.imageURLs.loading);
1396 workingImage.setAttribute("height", "16px");
1397 workingImage.setAttribute("width", "16px");
1398 newStatusDiv.appendChild(workingImage);
1399
1400 newStatusDiv.appendChild(newStatusSpan);
1401 newStatusSpan.innerHTML = " " + newStatus;
1402 newStatusDiv.setAttribute("class", "statusMessage");
1403 newStatusDiv.span = newStatusSpan;
1404
1405 _mainElem.appendChild(newStatusDiv);
1406 _statusMap["status" + _statusIDCounter] = newStatusDiv;
1407 _activeMessages++;
1408 return _statusIDCounter++;
1409 }
1410
1411 this.changeStatus = function(id, newStatus)
1412 {
1413 if(_statusMap["status" + id])
1414 {
1415 _statusMap["status" + id].span.innerHTML = " " + newStatus;
1416 }
1417 }
1418
1419 this.removeStatus = function(id)
1420 {
1421 if(_statusMap["status" + id])
1422 {
1423 removeFromParent(_statusMap["status" + id]);
1424
1425 if(--_activeMessages == 0)
1426 {
1427 _mainElem.style.display = "none";
1428 }
1429 }
1430 }
1431
1432 this.clear = function()
1433 {
1434 for(var p in _statusMap)
1435 {
1436 if(_statusMap.hasOwnProperty(p))
1437 {
1438 if(_statusMap[p] && _statusMap[p].parentNode)
1439 {
1440 removeFromParent(_statusMap[p]);
1441 }
1442
1443 if(--_activeMessages == 0)
1444 {
1445 _mainElem.style.display = "none";
1446 }
1447 }
1448 }
1449 }
1450}
1451
1452/*
1453function toggleEdit(e)
1454{
1455 var mousePos = de.events.getXYInWindowFromEvent(e);
1456 var cDesc = de.cursor.getCursorDescAtXY(mousePos.x, mousePos.y, de.events.getEventTarget(e));
1457 de.cursor.setCursor(cDesc);
1458}
1459*/
1460
1461
1462$( document ).ready(function() {
1463 // DOM Ready.
1464
1465 // Now can add handlers to monitor when ckeditor instances are all ready
1466 // See https://stackoverflow.com/questions/18461206/how-to-retrieve-the-ckeditor-status-ready
1467 // (and https://stackoverflow.com/questions/3447803/how-to-determine-if-ckeditor-is-loaded )
1468 if(gs.cgiParams.docEdit == "1") { // CKEDITOR only exists in docEdit mode
1469
1470 //CKEDITOR.on( 'loaded', function( evt ) { // not triggered
1471 //console.log("*** CKEDITOR loaded");
1472
1473 CKEDITOR.on( 'instanceReady', function( evt ) {
1474 //console.log("*** CKEDITOR instanceReady", evt);
1475 addCKEEditableState(evt,editableInitStates);
1476 // finished initialising one more editable element,
1477 // so decrement the counter keeping track of how many elements still need initialising
1478 editableElementsInitialisationProgress--;
1479 } );
1480 //} );
1481 }
1482
1483});
1484
1485
1486
Note: See TracBrowser for help on using the repository browser.