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

Last change on this file was 38426, checked in by anupama, 5 months ago

Don't need to do setMetadataArray on edits in user comment rows that are not just edited but also deleted. (Avoiding the extra work of first editing then deleting the affected user comment row).

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