Changeset 32852 for main

Show
Ignore:
Timestamp:
04.03.2019 21:13:35 (9 months ago)
Author:
ak19
Message:

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.

Location:
main/trunk/greenstone3/web/interfaces/default/js
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/web/interfaces/default/js/documentedit_scripts.js

    r32850 r32852  
    233233    mapGPScontainer.css("display", "none"); 
    234234 
    235     addFunctionalityToTable(mapGPScontainer); 
     235    addFunctionalityToTable(mapGPScontainer); // ********************************** 
    236236    mapGPScontainer.metaNameField.css("display", "none"); 
    237237    mapGPScontainer.addRowButton.css("display", "none"); 
     
    306306    $("#editContentButton").html(gs.text.de.hide_editor); 
    307307    //wait for 0.5 sec to let ckeditor up 
     308     
     309    // The following is now done in the CKEDTIOR.on('instanceReady') handler, which is added when docReady, see documentedit_scripts_util::$( document ).ready(...) 
     310    // Attempting CKEDTIOR.on('instanceReady') at the start of this method didn't work because it was probably too late in page load phase to add the event handler then 
     311    // (the instanceReady() event would have been triggered before this method finally got called). 
     312    /* 
    308313    setTimeout(function () { 
    309314        $(".sectionText").each(function () { 
     
    311316        }); 
    312317    }, 500); 
     318    */ 
    313319    var editBar = $("#editBarLeft"); 
    314320 
     
    371377        addEditMapGPSLink(titleDivs[i]); 
    372378    } 
    373  
     379     
     380    // We need to keep track of editableElementsInitialisationProgress: the number of editable elements that need to be initialised/need to finish initialising 
     381    // As CKEditors will be added, meaning more editable elements, must increment our counter editableElementsInitialisationProgress 
     382    var $num_editable_textareas = $(".sectionText"); // consider searching for 'contenteditable="true"' as this is what CKEDITOR is looking for (we think!)  
     383    editableElementsInitialisationProgress += $num_editable_textareas.length; 
     384     
    374385    _baseURL = gs.xsltParams.library_name; 
    375386    onVisibleMetadataSetChange(); // make sure that the selected item in the list is active 
     387     
     388    // If the user is attempting to leave the page, check if there are unsaved changes 
     389    // and if so, display an "Are you sure you want to leave" message. 
     390    // https://stackoverflow.com/questions/7080269/javascript-before-leaving-the-page 
     391    // Newer versions of Firefox/Chrome don't display custom message (security feature): 
     392    // https://stackoverflow.com/questions/22776544/why-is-jquery-onbeforeunload-not-working-in-chrome-and-firefox 
     393    // and http://jsfiddle.net/XZAWS/ 
     394    // jquery bind() is deprecated: https://stackoverflow.com/questions/33654716/is-jquery-bind-deprecated   
     395    console.log("**** away to set up beforeunload handler!"); 
     396    $(window).on("beforeunload", function(event) { 
     397         
     398        if(gs.cgiParams.docEdit == "1") { // like document.xsl, which checks the same var upon onload 
     399            // shouldn't check for whether changes are saved unless on Doc Editing page (DocEdit=1) 
     400            // else the following pop up always ends up appearing when attempting 
     401            // to leave a doc view page in Doc Editing Mode (when not yet actually Doc Editing) 
     402             
     403            // Because we've done extra work now in maintaining "editableElementsInitialisationProgress", which is 
     404            // the number of editable elements that still need to finish initialising, we can now be confident that 
     405            // the call to changesToUpdate() below won't return the wrong answers if a page with docEdit turned on 
     406            // is asked to unload (e.g. by pressing Reload) before the page has finished loading. 
     407            var changes = changesToUpdate(); 
     408             
     409            console.log("#### CHANGES before page reload: ", changes); 
     410             
     411            if(changes.length > 0) { 
     412                console.log("The collection hasn't yet been saved after editing. Are you sure you want to leave?"); 
     413                return "The collection hasn't yet been saved after editing. Are you sure you want to leave?";    
     414            } 
     415        } 
     416    }); 
     417 
     418 
    376419} 
    377420 
  • main/trunk/greenstone3/web/interfaces/default/js/documentedit_scripts_util.js

    r32850 r32852  
    2121 
    2222 
    23 // If the user is attempting to leave the page, check if there are unsaved changes 
    24 // and if so, display an "Are you sure you want to leave" message. 
    25 // https://stackoverflow.com/questions/7080269/javascript-before-leaving-the-page 
    26 // Newer versions of Firefox/Chrome don't display custom message (security feature): 
    27 // https://stackoverflow.com/questions/22776544/why-is-jquery-onbeforeunload-not-working-in-chrome-and-firefox 
    28 // and http://jsfiddle.net/XZAWS/ 
    29 // jquery bind() is deprecated: https://stackoverflow.com/questions/33654716/is-jquery-bind-deprecated 
    30 $(window).on("beforeunload", function(event) { 
    31      
    32     if(gs.cgiParams.docEdit == "1") { // like document.xsl, which checks the same var upon onload 
    33     // shouldn't check for whether changes are saved unless on Doc Editing page (DocEdit=1) 
    34     // else the following pop up always ends up appearing when attempting 
    35     // to leave a doc view page in Doc Editing Mode (when not yet actually Doc Editing) 
    36      
    37      
    38     var changes = changesToUpdate(); 
    39      
    40     //console.log("#### CHANGES before page reload: ", changes); 
    41      
    42     if(changes.length > 0) { 
    43         console.log("The collection hasn't yet been saved after editing. Are you sure you want to leave?"); 
    44         return "The collection hasn't yet been saved after editing. Are you sure you want to leave?";    
    45     } 
    46     } 
    47  
    48 }); 
    49  
     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     
    5029 
    5130function encodeDelimiters(meta_value) { 
     
    305284{ 
    306285    var resultArray = new Array(); 
    307     getLastEditableStates(); 
    308     for (var j in editableLastStates)  
    309     {    
    310         if (isNodeChanged(editableLastStates[j]))  
    311         { 
    312             resultArray.push(editableLastStates[j].editableNode); 
     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            } 
    313301        } 
    314302    } 
     
    337325    } 
    338326 
     327    console.log("**** isNodeChanged() editableInitStates = ", editableInitStates); 
    339328    return true; 
    340329     
     
    909898    //This registering can cause a sizeable delay so we'll thread it (effectively) so the browser is not paused 
    910899    cell.originalValue = cell.value; 
    911     setTimeout(function(){addEditableState(cell, editableInitStates)}, 0); 
     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); 
    912906} 
    913907 
     
    958952function addFunctionalityToTable(table) 
    959953{ 
    960     table.find("tr").each(function() 
     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() 
    961965    { 
    962966        var cells = $(this).find("td"); 
     
    14561460} 
    14571461*/ 
     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