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

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

Cleanup stage one: 1. Not calling addFunctionalityToTable() on GPSmapcontainer, and no need for a cutdown addFunctionalityToGPSMap(), since the GPS map editor doesn't use any of the added functionality anyway, going out of its way to set the css to display:hide on all the added functionality (metaNameField, addRowButton and addAllButton). 2. Selecting divs that have attribute contenteditable='true' rather than all elements with .sectionText class to get a list of all the editable elements that will get converted to CKEditors. I think it's only divs that have contenteditable set to true, in which case don't need to look for any and all elements with that attribute and value. 3. Some cleanup.

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