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

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

Dr Bainbridge solved the weird bug whereby when you're in doc editing mode and quickly try to leave/reload the page before it's finished changing, you're asked to save changes. But no changes were made. The cause was complex: it checks for changes on page unload by comparing the initial states of editable elements against their final state on unload. When the page is unloaded prematurely (before the doc has finished loading) not all the editable elements have finished initialising, so the array keep track of initial states of editable elements, editableInitStates, does not match the final state. Dr Bainbridge added a counter variable to keep track of the number of editable elements that still need to finish initialising. It's incremented for every metadataTable row, since each is editable, and for every sectionText as this gets turned into a CKEditor. The count then gets decremented when each such individual element has finished initialising, which happens in the CKEditor.instanceReady handler for CKEditor items and in the setTimeout() which initialises metadataTable row elements. Previously the call to CKEditor's instanceReady() took place too late, in function documentedit_scripts::readyPageForEditing(), which got called much after CKEditor instances were all ready, and that's why adding the CKEDITOR's instanceReady handler in there didn't work. That problem is now overcome in adding the CKEDITOR instanceReady handler on documentReady. Another notable change is that the beforeunload() handler is now moved into the end of documentedit_scripts::readyPageForEditing(), since we don't want check for changes that need to be saved until the page has fully loaded. Further, it won't find any changes until editableInitStates and editableLastStates (current states) are both ready/all initialised, so that any popup about unsaved changes is now also accurate if the doc-editing page gets prematurely unloaded.

  • 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 addFunctionalityToMapGPS(mapGPScontainer)
948{
949
950}
951
952function addFunctionalityToTable(table)
953{
954 var $tr_array = table.find("tr");
955
956
957 // We need to keep track of editableElementsInitialisationProgress: the number of editable elements that still need to be initialised/need to finish initialising
958 // Each table's rows means *that* many more editable elements still need initialising.
959 // So each time addFunctionalityToTable() is called on a table, we must increment our counter editableElementsInitialisationProgress
960 // with how many editable cells it has/how many rows it has.
961 editableElementsInitialisationProgress += $tr_array.length;
962 console.log("**** addFunctionalityToTable::editableElementsInitialisationProgress = " + editableElementsInitialisationProgress);
963
964 $tr_array.each(function()
965 {
966 var cells = $(this).find("td");
967 var metadataName = $(cells[0]).html();
968
969 if(dynamic_metadata_set_list == true && metadataName.indexOf(".") != -1)
970 {
971 var metadataSetName = metadataName.substring(0, metadataName.lastIndexOf("."));
972
973 var found = false;
974 for(var j = 0; j < _metadataSetList.length; j++)
975 {
976 if(metadataSetName == _metadataSetList[j])
977 {
978 found = true;
979 break;
980 }
981 }
982
983 if(!found)
984 {
985 _metadataSetList.push(metadataSetName);
986 addOptionToList( $("#metadataSetList"), metadataSetName);
987 }
988 }
989
990 asyncRegisterEditSection(cells[1].getElementsByTagName("textarea")[0]);
991 addRemoveLinkToRow(this);
992
993 // add multivalued indicator if needed
994 if (jQuery.inArray(metadataName, multiValuedMetadata) != -1) {
995 //if (multiValuedMetadata.includes(metadataName)){
996 $(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 ;
997 }
998
999 });
1000
1001 // set up autocomplete values
1002 var value_cells = $(".metaTableCellArea");
1003 for (var k=0; k<autocompleteMetadata.length; k++) {
1004 var source_name = autocompleteMetadata[k].replace(/[\.-]/g, "");
1005 var source_obj = window[source_name+"_values"];
1006 if (source_obj) {
1007 value_cells.filter("."+source_name).autocomplete({
1008 minLength: 0,
1009 source: source_obj
1010 });
1011 }
1012 }
1013
1014 // add metadata field selector
1015 var metaNameField = createMetadataElementSelector();
1016 table.after(metaNameField);
1017 table.metaNameField = metaNameField;
1018
1019 /* add the buttons */
1020 // check enable_add_all_button - only valid for fixedlist and autocomplete
1021 if (enable_add_all_metadata_button == true) {
1022 if (new_metadata_field_input_type != "fixedlist" && new_metadata_field_input_type != "autocomplete") {
1023 enable_add_all_metadata_button = false;
1024 }
1025 }
1026
1027 // add single metadata button
1028 var addRowButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1029
1030 addRowButton.html(gs.text.de.add_new_metadata);
1031 addRowButton.click(function()
1032 {
1033 var name = metaNameField.val();
1034 if(!name || name == "")
1035 {
1036 console.log(gs.text.de.no_meta_name_given);
1037 return;
1038 }
1039 addNewMetadataRow(table, name);
1040
1041
1042 });
1043 table.addRowButton = addRowButton;
1044 metaNameField.after(addRowButton);
1045
1046 // add all metadata button
1047 if (enable_add_all_metadata_button == true) {
1048 var addAllButton = $("<button>",{"class": "ui-state-default ui-corner-all", "style": "margin: 5px;"});
1049 addAllButton.html(gs.text.de.add_all_metadata);
1050 addAllButton.click(function()
1051 {
1052 for(var i=0; i<availableMetadataElements.length; i++) {
1053
1054 addNewMetadataRow(table, availableMetadataElements[i])
1055 }
1056
1057 });
1058 table.addAllButton = addAllButton;
1059 addRowButton.after(addAllButton);
1060
1061 }
1062
1063}
1064
1065function addNewMetadataRow(table, name) {
1066
1067 var clean_name = name.replace(/[\.-]/g, "");
1068 var newRow = $("<tr>", {"style": "display: table-row;"});
1069 var nameCell;
1070 if (jQuery.inArray(name, multiValuedMetadata) != -1) {
1071 nameCell = $("<td>" + name + " <span title='"+gs.text.de.multi_valued_tooltip + "' style='float:right;'>"+mvm_delimiter+"</span></td>");
1072 } else {
1073 nameCell = $("<td>" + name + "</td>");
1074 }
1075 nameCell.attr("class", "metaTableCellName");
1076 var valueCell = $("<td>", {"class": "metaTableCell"});
1077 var textValue = $("<textarea>", {"class": "metaTableCellArea "+ clean_name});
1078
1079 if (jQuery.inArray(name, autocompleteMetadata) != -1) {
1080 var source_obje = window[clean_name +"_values"];
1081 if (source_obje) {
1082 textValue.autocomplete({
1083 minLength: 0,
1084 source: source_obje
1085 });
1086 }
1087 }
1088 valueCell.append(textValue);
1089 newRow.append(nameCell);
1090 newRow.append(valueCell);
1091 addRemoveLinkToRow(newRow.get(0));
1092 table.append(newRow);
1093
1094 var undo = new Array();
1095 undo.op = "delMeta";
1096 undo.srcElem = newRow;
1097 undo.removeTransaction = false;
1098 _undoOperations.push(undo);
1099 if ( hierarchyStorage && hierarchyStorage[name])
1100 {
1101 setHierarchyEventsWrappers(name);
1102 }
1103}
1104
1105function addRemoveLinkToRow(row)
1106{
1107 var newCell = $("<td>");
1108 var removeLink = $("<a>"+gs.text.de.remove+"</a>", {"href": "javascript:;"});
1109 removeLink.click(function()
1110 {
1111 var undo = new Array();
1112 undo.srcElem = row;
1113 undo.op = "display";
1114 undo.subOp = "table-row";
1115 undo.removeDeletedMetadata = true;
1116 _undoOperations.push(undo);
1117 _deletedMetadata.push(row);
1118 //row.css("display", "none");
1119 $(row).hide();
1120 });
1121 newCell.append(removeLink);
1122 newCell.attr({"class": "metaTableCellRemove", "style": "font-size:0.6em; padding-left: 3px; padding-right: 3px;"});
1123 $(row).append(newCell);
1124}
1125
1126/* This is for 'edit structure' menu bar */
1127function createTopMenuBar()
1128{
1129 //Create the top menu bar
1130 var headerTable = document.createElement("TABLE");
1131 var tableBody = document.createElement("TBODY");
1132 var row = document.createElement("TR");
1133 var newDocCell = document.createElement("TD");
1134 var newSecCell = document.createElement("TD");
1135 var saveCell = document.createElement("TD");
1136 var undoCell = document.createElement("TD");
1137 var metadataListCell = document.createElement("TD");
1138
1139 var metadataListLabel = document.createElement("SPAN");
1140 metadataListLabel.innerHTML = gs.text.de.visible_metadata;
1141 var metadataList = document.createElement("SELECT");
1142 metadataList.setAttribute("id", "metadataSetList");
1143 metadataList.onchange = onVisibleMetadataSetChange;
1144 var allMetadataOption = document.createElement("OPTION");
1145 metadataList.appendChild(allMetadataOption);
1146 allMetadataOption.innerHTML = gs.text.de.all_metadata;
1147 metadataListCell.appendChild(metadataListLabel);
1148 metadataListCell.appendChild(metadataList);
1149
1150 metadataListCell.setAttribute("class", "headerTableTD");
1151 newDocCell.setAttribute("class", "headerTableTD");
1152 newSecCell.setAttribute("class", "headerTableTD");
1153 undoCell.setAttribute("class", "headerTableTD");
1154 saveCell.setAttribute("class", "headerTableTD");
1155
1156 headerTable.appendChild(tableBody);
1157 tableBody.appendChild(row);
1158 row.appendChild(saveCell);
1159 row.appendChild(undoCell);
1160 row.appendChild(newDocCell);
1161 row.appendChild(newSecCell);
1162 row.appendChild(metadataListCell);
1163
1164 //The "Save changes" button
1165 var saveButton = document.createElement("BUTTON");
1166 saveButton.innerHTML = gs.text.de.save_changes;
1167 saveButton.setAttribute("onclick", "saveAndRebuild();");
1168 saveButton.setAttribute("id", "saveButton");
1169 saveCell.appendChild(saveButton);
1170
1171 //The "Undo" button
1172 var undoButton = document.createElement("BUTTON");
1173 undoButton.innerHTML = gs.text.dse.undo;
1174 undoButton.setAttribute("onclick", "undo();");
1175 undoCell.appendChild(undoButton);
1176
1177 //The "Create new document" button
1178 var newDocButton = document.createElement("BUTTON");
1179 newDocButton.innerHTML = gs.text.dse.create_new_document;
1180 newDocButton.setAttribute("onclick", "createNewDocumentArea();");
1181 newDocButton.setAttribute("id", "createNewDocumentButton");
1182 newDocCell.appendChild(newDocButton);
1183
1184 //The "Insert new section" LI
1185 var newSecLI = createDraggableNewSection(newSecCell);
1186
1187 return headerTable;
1188}
1189
1190function getMetadataFromNode(node, name)
1191{
1192 var currentNode = node.firstChild;
1193 while(currentNode != null)
1194 {
1195 if(currentNode.nodeName == "metadataList")
1196 {
1197 currentNode = currentNode.firstChild;
1198 break;
1199 }
1200
1201 currentNode = currentNode.nextSibling;
1202 }
1203
1204 while(currentNode != null)
1205 {
1206 if(currentNode.nodeName == "metadata" && currentNode.getAttribute("name") == name)
1207 {
1208 return currentNode.firstChild.nodeValue;
1209 }
1210
1211 currentNode = currentNode.nextSibling;
1212 }
1213 return "";
1214}
1215
1216function storeMetadata(node, listItem)
1217{
1218 listItem.metadata = new Array();
1219
1220 var currentNode = node.firstChild;
1221 while(currentNode != null)
1222 {
1223 if(currentNode.nodeName == "metadataList")
1224 {
1225 currentNode = currentNode.firstChild;
1226 break;
1227 }
1228
1229 currentNode = currentNode.nextSibling;
1230 }
1231
1232 while(currentNode != null)
1233 {
1234 if(currentNode.nodeName == "metadata")
1235 {
1236 listItem.metadata[currentNode.getAttribute("name")] = currentNode.firstChild.nodeValue;
1237 }
1238
1239 currentNode = currentNode.nextSibling;
1240 }
1241}
1242
1243function getNodeContent(node)
1244{
1245 var currentNode = node.firstChild;
1246 while(currentNode != null)
1247 {
1248 if(currentNode.nodeName == "nodeContent")
1249 {
1250 return currentNode.firstChild;
1251 }
1252
1253 currentNode = currentNode.nextSibling;
1254 }
1255 return null;
1256}
1257
1258function containsDocumentNode(node)
1259{
1260 var currentNode = node.firstChild;
1261 while(currentNode != null)
1262 {
1263 if(currentNode.nodeName == "documentNode")
1264 {
1265 return true;
1266 }
1267
1268 currentNode = currentNode.nextSibling;
1269 }
1270 return false;
1271}
1272
1273function isExpanded(textDiv)
1274{
1275 if(typeof textDiv.style == "undefined" || typeof textDiv.style.display == "undefined" || textDiv.style.display == "block")
1276 {
1277 return true;
1278 }
1279 return false;
1280}
1281
1282function toggleTextDiv(section)
1283{
1284 var textDiv = section.textDiv;
1285 if(textDiv)
1286 {
1287 if(isExpanded(textDiv))
1288 {
1289 textDiv.style.display = "none";
1290 section.menu.editTextLink.innerHTML = gs.text.dse.edit;
1291 }
1292 else
1293 {
1294 textDiv.style.display = "block";
1295 section.menu.editTextLink.innerHTML = gs.text.dse.hide;
1296 }
1297 }
1298}
1299
1300function updateFromTop()
1301{
1302 updateRecursive(document.getElementById("dbDiv"), null, null, 0);
1303}
1304
1305function insertAfter(elem, refElem)
1306{
1307 if(refElem.nextSibling)
1308 {
1309 refElem.parentNode.insertBefore(elem, refElem.nextSibling);
1310 }
1311 else
1312 {
1313 refElem.parentNode.appendChild(elem);
1314 }
1315}
1316
1317function removeFromParent(elem)
1318{
1319 elem.parentNode.removeChild(elem);
1320}
1321
1322function createSectionTitle(text)
1323{
1324 var textSpan = document.createElement("SPAN");
1325 if(text)
1326 {
1327 textSpan.appendChild(document.createTextNode(" " + text + " "));
1328 }
1329 else
1330 {
1331 textSpan.appendChild(document.createTextNode(" [" + gs.text.dse.untitled_section + "] "));
1332 }
1333 return textSpan;
1334}
1335
1336function setMouseOverAndOutFunctions(section)
1337{
1338 //Colour the list item and display the menu on mouse over
1339 section.onmouseover = function(e)
1340 {
1341 if(this.menu){this.menu.style.display = "inline";}
1342 this.style.background = "rgb(255, 200, 0)";
1343 };
1344 //Uncolour the list item and hide the menu on mouse out
1345 section.onmouseout = function(e)
1346 {
1347 if(this.menu){this.menu.style.display = "none";}
1348 this.style.background = "none";
1349 };
1350}
1351
1352function createDraggableNewSection(parent)
1353{
1354 var newSecLI = document.createElement("LI");
1355 var newSpan = document.createElement("SPAN");
1356 newSpan.innerHTML = gs.text.dse.insert_new_section + " ";
1357
1358 newSecLI.sectionTitle = newSpan;
1359 newSecLI.appendChild(newSpan);
1360 newSecLI.setAttribute("class", "dragItem newSection");
1361 newSecLI.newSection = true;
1362 newSecLI.parent = parent;
1363 newSecLI.index = -1;
1364 new YAHOO.example.DDList(newSecLI);
1365 parent.appendChild(newSecLI);
1366}
1367
1368function closeAllOpenContents()
1369{
1370 for(var i = 0; i < _allContents.length; i++)
1371 {
1372 if(isExpanded(_allContents[i].textDiv))
1373 {
1374 toggleTextDiv(_allContents[i]);
1375 }
1376 }
1377 DDM.refreshCache();
1378}
1379
1380//Status Bar class (initialised with new StatusBar(elem);)
1381function StatusBar(mainElem)
1382{
1383 var _statusMap = new Array();
1384 var _statusIDCounter = 0;
1385 var _mainElem = mainElem;
1386 var _activeMessages = 0;
1387
1388 _mainElem.style.display = "none";
1389
1390 this.addStatus = function(newStatus)
1391 {
1392 _mainElem.style.display = "block";
1393 var newStatusDiv = document.createElement("DIV");
1394 var newStatusSpan = document.createElement("SPAN");
1395
1396 var workingImage = document.createElement("IMG");
1397 workingImage.setAttribute("src", gs.imageURLs.loading);
1398 workingImage.setAttribute("height", "16px");
1399 workingImage.setAttribute("width", "16px");
1400 newStatusDiv.appendChild(workingImage);
1401
1402 newStatusDiv.appendChild(newStatusSpan);
1403 newStatusSpan.innerHTML = " " + newStatus;
1404 newStatusDiv.setAttribute("class", "statusMessage");
1405 newStatusDiv.span = newStatusSpan;
1406
1407 _mainElem.appendChild(newStatusDiv);
1408 _statusMap["status" + _statusIDCounter] = newStatusDiv;
1409 _activeMessages++;
1410 return _statusIDCounter++;
1411 }
1412
1413 this.changeStatus = function(id, newStatus)
1414 {
1415 if(_statusMap["status" + id])
1416 {
1417 _statusMap["status" + id].span.innerHTML = " " + newStatus;
1418 }
1419 }
1420
1421 this.removeStatus = function(id)
1422 {
1423 if(_statusMap["status" + id])
1424 {
1425 removeFromParent(_statusMap["status" + id]);
1426
1427 if(--_activeMessages == 0)
1428 {
1429 _mainElem.style.display = "none";
1430 }
1431 }
1432 }
1433
1434 this.clear = function()
1435 {
1436 for(var p in _statusMap)
1437 {
1438 if(_statusMap.hasOwnProperty(p))
1439 {
1440 if(_statusMap[p] && _statusMap[p].parentNode)
1441 {
1442 removeFromParent(_statusMap[p]);
1443 }
1444
1445 if(--_activeMessages == 0)
1446 {
1447 _mainElem.style.display = "none";
1448 }
1449 }
1450 }
1451 }
1452}
1453
1454/*
1455function toggleEdit(e)
1456{
1457 var mousePos = de.events.getXYInWindowFromEvent(e);
1458 var cDesc = de.cursor.getCursorDescAtXY(mousePos.x, mousePos.y, de.events.getEventTarget(e));
1459 de.cursor.setCursor(cDesc);
1460}
1461*/
1462
1463
1464$( document ).ready(function() {
1465 console.log( "**** Dom ready!" );
1466
1467 // monitor whether ckeditor instances are all ready
1468 // See https://stackoverflow.com/questions/18461206/how-to-retrieve-the-ckeditor-status-ready
1469 // (and https://stackoverflow.com/questions/3447803/how-to-determine-if-ckeditor-is-loaded )
1470 if(gs.cgiParams.docEdit == "1") { // CKEDITOR only exists in docEdit mode
1471 //CKEDITOR.on( 'loaded', function( evt ) { // not triggered
1472 //console.log("*** CKEDITOR loaded");
1473 CKEDITOR.on( 'instanceReady', function( evt ) {
1474 console.log("*** CKEDITOR instanceReady", evt);
1475 addCKEEditableState(evt,editableInitStates);
1476 // finished initialising one more editable element,
1477 // so decrement the counter keeping track of how many elements still need initialising
1478 editableElementsInitialisationProgress--;
1479 } );
1480 //} );
1481 }
1482
1483});
1484
1485
1486
Note: See TracBrowser for help on using the repository browser.