Ticket #9238: trac_9238_jmol_lib_async.patch

File trac_9238_jmol_lib_async.patch, 27.0 KB (added by gutow, 10 years ago)

fix to async load of many jmols and workaround for Safari bug (memory leak?)

  • sagenb/data/sage/js/jmol_lib.js

    # HG changeset patch
    # User Jonathan H. Gutow <gutow@uwosh.edu
    # Date 1301272263 18000
    # Node ID dc9aae4faac7a86d66058bb2f557d9e433b2614f
    # Parent  1f6311a9060b14b23dcc36597650962b66eb870a
    trac 9238: fixes to asynchronous load of lots of Jmols and workaround for Safari bug (memory leak?)
    
    diff -r 1f6311a9060b -r dc9aae4faac7 sagenb/data/sage/js/jmol_lib.js
    a b  
    5757var jmol_count = 0;
    5858
    5959var jmolStatus = {
    60     maxLiveAllowed: 5,
     60    maxLiveAllowed: 4,
    6161    numLive: 0,
    62     jmolArray: new Array(),//-1 deleted, 0 awake, 1 sleeping, 2 loading.
     62    jmolArray: new Array(),//-2 loading failed, -1 deleted, 0 awake, 1 sleeping, 2 loading, 3 waiting to load.
    6363    urls: new Array(),
    6464    defaultdirectory: new Array(),
    6565    widths: new Array(),
     
    6969    pictureStrs: new Array(),
    7070    stateScripts: new Array(),
    7171    cntrls: new Array(),
     72    attempts: new Array(),
    7273    }
    7374
    7475//Some default constants
     
    8283captionStr = ''; //empty no caption
    8384controlStr = ' '; //could put special controls here.  Must not be empty for default controls to appear, that is why it is a space.
    8485
     86function jmol_checkbrowserOS(){
     87    jmolStatus.os = _jmol.os;
     88    jmolStatus.browser=_jmol.browser;
     89    if (_jmol.os=="mac"){
     90        if (_jmol.browser=="mozilla"){
     91            alert("You are using a Firefox/Mozilla browser on MacOS.  Many people experience inconsistent behavior of the 3-D viewer or no images using this combination.  It is recommended that you use Chrome (or another webkit browser) instead.");
     92        }
     93        if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1){
     94            jmolStatus.browser="chrome";
     95        }
     96    }
     97    }
     98
    8599function jmol_pulldown(theform) {
    86100    /*
    87101    This is called when the user selects a menu item.  This just
     
    99113        }else {
    100114        result = jmolScriptWait("spin off", n);
    101115        }
    102     storeDefaultDir(n);
    103116    jmolUpdateState(n);
    104117    }
    105118
     
    111124        scriptStr = ''+type+' '+whichsurface+' off;';
    112125        result = jmolScriptWait(scriptStr, n);
    113126        }
    114     storeDefaultDir(n);
    115127    jmolUpdateState(n);
    116128    }
    117129
    118130function do_jmolScriptWait(scriptStr, n){
    119131    result = jmolScriptWait(scriptStr,n);
    120     storeDefaultDir(n);
    121132    jmolUpdateState(n);
    122133    }
    123134
    124135function jmolSurfColor(color,surface,n){
    125136    scriptStr = 'color $'+surface+' '+color;
    126137    result = jmolScriptWait(scriptStr,n);
    127     storeDefaultDir(n);
    128138    jmolUpdateState(n);
    129139    }
    130140
     
    137147    }
    138148
    139149function jmolUpdateState(n){
     150    //make sure the default directory is correct
     151//    jmolScript('x=defaultdirectory;data "directory @x";');<--this is done on launch of the applet.
     152    jmolStatus.defaultdirectory[n] = jmolEvaluate("x",n);
    140153    var divID = 'jmolStateDiv'+n;
    141     var stateStr ="#a comment to guarrantee one line";
     154    var stateStr ="#a comment to guarrantee one line\n";
    142155    stateStr+= jmolGetPropertyAsString("stateInfo", "", n);
    143156    re_modelinline = /data "model list"(.|\n|\r)*end "model list"/;
    144     var modelStr = (stateStr.match(re_modelinline))[0];
    145     modelStr = modelStr.replace(/\r\n/g,'|').replace(/\r/g, '|').replace(/\n/g,'|').replace(/\|\|/g, '|');
     157    if(stateStr.match(re_modelinline)){//If we didn't get a good response we'll ignore and get later
     158        var modelStr = (stateStr.match(re_modelinline))[0];
     159        modelStr = modelStr.replace(/\r\n/g,'|').replace(/\r/g, '|').replace(/\n/g,'|').replace(/\|\|/g, '|');
    146160//    modelStr = 'fix between here '+modelStr+' and here';
    147     stateStr = stateStr.replace(re_modelinline, modelStr);
    148     re_endofline = /\n/g;
    149     re_doublequote = /"/g;
    150     get_element(divID).innerHTML = '<div style="overflow:scroll;max-height:400px;">set '+ jmolStatus.defaultdirectory[n] +';<br>'+stateStr.replace(re_endofline,'<br>')+'</div>';
    151     jmolStatus.stateScripts[n] = 'set '+ jmolStatus.defaultdirectory[n] +';\n'+stateStr;
    152     }
    153 
    154 function storeDefaultDir(n) {
    155     result = jmolScript('set MessageCallback "jmolMessageHandler"; show defaultdirectory; delay 1;',n);
     161        stateStr = stateStr.replace(re_modelinline, modelStr);
     162        re_endofline = /\n/g;
     163        re_doublequote = /"/g;
     164        get_element(divID).innerHTML = '<div style="overflow:scroll;max-height:400px;">set defaultdirectory=\"'+ jmolStatus.defaultdirectory[n] +'\";<br>'+stateStr.replace(re_endofline,'<br>')+'</div>';
     165        jmolStatus.stateScripts[n] = 'set defaultdirectory=\"'+ jmolStatus.defaultdirectory[n] +'\";\n'+stateStr;
     166        jmolStatus.defaultdirectory[n]="done";//update finished.
     167        }
    156168    }
    157169
    158170var jmolLastDefaultDir = '';
     
    163175    var javaScriptStr = ''+messageStr;
    164176    var appletname = ""+applet;
    165177    var messagenum = ""+number;
    166     if (javaScriptStr.search(re_defaultdirectory)!=-1){
    167         jmolLastDefaultDir = javaScriptStr;
    168         appletN = appletname[appletname.search(re_jmolApplet),(appletname.length-1)];
    169         jmolStatus.defaultdirectory[appletN]= javaScriptStr;
     178    var appletN = appletname.substring(10);
     179//    if (javaScriptStr.search(re_defaultdirectory)!=-1){
     180//        jmolLastDefaultDir = javaScriptStr;
     181//        jmolStatus.defaultdirectory[appletN]= javaScriptStr;
     182//        }
     183    if (jmolStatus.jmolArray[appletN]==2) {//finishing load process
     184        setTimeout('jmolUpdateState('+appletN+')',300);
    170185        }
    171186   }
     187   
     188function jmolAppletReady_jmolUpdateState(n){
     189    jmolUpdateState(n);
     190//    setTimeout('jmolAppletLive('+n+')',300);
     191   }
    172192
     193function jmol_numLiveUpdate(){
     194    var liveCount = 0;
     195    for (i=0;i < jmolStatus.jmolArray.length; i++){ //reset the number of live Jmols
     196        if (jmolStatus.jmolArray[i]==0) {liveCount = liveCount+1;}
     197        }
     198    jmolStatus.numLive = liveCount;
     199    }
    173200
    174201function jmolCntrlPanels(whichActive,cntrlPanels){ //The default definition for a group of jmolCntrlPanels
    175202    this.whichActive = whichActive; //array index for the active, front, panel
     
    182209    panelID = 'size_jmol'+n;
    183210    tabID = 'tab_'+panelID;
    184211    //size control
    185     panelHTML ='3-D display size: <select class="jmol" title ="Select a size" onchange="jmol_pulldown(this);">';
    186     panelHTML += '<option value = "jmolResizeApplet('+miniature+','+n+');"> Miniature ('+miniature+'px)</option>';
    187     panelHTML += '<option  value = "jmolResizeApplet('+small+','+n+');"> Small ('+small+'px)</option>';
    188     panelHTML += '<option selected = "" value = "jmolResizeApplet('+medium+','+n+');"> Medium ('+medium+'px)</option>';
    189     panelHTML += '<option value = "jmolResizeApplet('+large+','+n+');"> Large ('+large+'px)</option>';
    190     panelHTML += '</';
    191     panelHTML += 'select><br/>';
     212    if(_jmol.os=="mac" && _jmol.browser=="mozilla"){
     213        panelHTML ='';
     214        }else{
     215        panelHTML ='3-D display size: <select class="jmol" title ="Select a size" onchange="jmol_pulldown(this);">';
     216        panelHTML += '<option value = "jmolResizeApplet('+miniature+','+n+');"> Miniature ('+miniature+'px)</option>';
     217        panelHTML += '<option  value = "jmolResizeApplet('+small+','+n+');"> Small ('+small+'px)</option>';
     218        panelHTML += '<option selected = "" value = "jmolResizeApplet('+medium+','+n+');"> Medium ('+medium+'px)</option>';
     219        panelHTML += '<option value = "jmolResizeApplet('+large+','+n+');"> Large ('+large+'px)</option>';
     220        panelHTML += '</';
     221        panelHTML += 'select><br/>';
     222        }
    192223    panelHTML +='<ul><li><a href="javascript:void(jmol_popup(\''+n+'\'))">Arbitrarily resizable in own window</a></li>';
    193224    //static image to save
    194225    panelHTML +='<li><a href="javascript:void(jmol_image('+n+'))">Get static image to save</a></li>';
     
    225256    }
    226257
    227258function jmol_applet(size, url, cell_num, functionnames) { //makes a new applet. Presently ignoring size, kept for backwards compatibility
     259    var appletID = jmol_count;
     260    if (jmol_count==0){//time to start the jmolQueueWatcher to manage multiple Jmol launches.
     261        jmolQueue = setInterval('jmolQueueWatcher();',1500);
     262        }
     263    jmol_count = jmol_count + 1;
     264    jmolStatus.jmolArray[appletID] = 3; //queued to load.
    228265    size = medium; //overriding value from server.  Probably should accept server value.
    229266    jmolSetDocument(false);
    230267    //Where am I?  Need to know in cases where I need to write directly to this cell.  Not used initially.
    231268    cell_ID = 'cell_output_html_'+cell_num;
    232269    //write everything to the cell using cell writer so that it will appear next time the cell is opened.
    233     cntrlPanels = makeCntrlPanels(url, jmol_count, functionnames);
    234     controlStr = makeControlStr(url, jmol_count, cntrlPanels);
    235     str = newJmolTableStr(jmolStatus, size, size, url, wakeMessage, sleepMessage, captionStr, controlStr);
     270    cntrlPanels = makeCntrlPanels(url, appletID, functionnames);
     271    controlStr = makeControlStr(url, appletID, cntrlPanels);
     272    str = newJmolTableStr(appletID, size, size, url, wakeMessage, sleepMessage, captionStr, controlStr);
    236273    //add debugging div
    237274    //str += '<div id="JmolDebug">Jmol Debugging goes here</div>';
    238     //sleep some if necessary
    239     limitlive(jmol_count, jmolStatus);
    240275    //now we can start the new one
    241276    cell_writer.write(str);
    242277    var scriptStr = 'script "'+url+'"; isosurface fullylit; pmesh o* fullylit; set antialiasdisplay on;';
    243     scriptStr += 'set MessageCallback "jmolMessageHandler"; show defaultdirectory; delay 1; javascript "jmolAppletLive('+ jmol_count +');jmolUpdateState('+ jmol_count +');";';
     278    scriptStr +='x=defaultdirectory; data "directory @x";';
     279    scriptStr += 'set MessageCallback "jmolMessageHandler"; show defaultdirectory;';
    244280    jmolSetAppletColor("white");
     281    if (appletID==0){
     282        jmol_checkbrowserOS();
     283        }
    245284    //we will still set all the data for this applet so that other asynchronously created applets do not grab its ID.
    246     jmolStatus.urls[jmol_count]=url;
    247     jmolStatus.widths[jmol_count] = size;
    248     jmolStatus.heights[jmol_count]= size;
    249     jmolStatus.numLive = jmolStatus.numLive+1;
    250     jmolStatus.jmolArray[jmol_count] = 2; //it's now  "loading".
    251     jmolStatus.controlStrs[jmol_count] = controlStr;
    252     jmolStatus.captionStrs[jmol_count] = captionStr;
    253     jmolStatus.cntrls[jmol_count]=cntrlPanels;
    254     jmol_count += 1;
    255     //Now we wait for the server by calling a function that waits if the div is not yet written.
    256     launchNewJmol(size,scriptStr,(jmol_count-1));
     285    jmolStatus.urls[appletID]=url;
     286    jmolStatus.widths[appletID] = size;
     287    jmolStatus.heights[appletID]= size;
     288    //    jmolStatus.numLive = jmolStatus.numLive+1;
     289    jmolStatus.controlStrs[appletID] = controlStr;
     290    jmolStatus.captionStrs[appletID] = captionStr;
     291    jmolStatus.cntrls[appletID]=cntrlPanels;
     292//Now we wait for the server by calling a function that waits if the div is not yet written.
     293//    launchNewJmol(size,scriptStr,appletID);
    257294    return str;
    258295    }
    259296
    260 function launchNewJmol(size,scriptStr,n){
    261         if (!get_element("Jmol"+ n)){
    262             var launchStr = 'launchNewJmol('+size+',\''+scriptStr+'\','+n+')';
    263 //            alert("Waiting for Jmol"+n+" div.");
    264             setTimeout(launchStr, 500);
    265         }else{
    266             get_element("Jmol"+ n).innerHTML = jmolApplet([size, size], scriptStr, n);
    267 //            jmolStatus.jmolArray[n]=0; //it's done loading and is "live".
     297function jmolQueueWatcher(){
     298    //this function should be started on a 1500 ms interval as soon as the first Jmol applet is called for.
     299    //This controls launch of applet timing and order when multiple Jmols are trying to launch. Necessary
     300    //for the asynchronous launching caused by openning an old worksheet with lots of Jmols.
     301    //Check for Jmols in the launching state (should be one or none)
     302    jmol_numLiveUpdate();
     303    numAppletsAtStart =jmolStatus.jmolArray.length;//may change during checks...ignore new additions
     304    loading = -1;
     305    for (n=0;n<numAppletsAtStart;n++){
     306        if(jmolStatus.jmolArray[n]==2){loading=n;};
     307        }
     308    if(loading>=0){//we found a loading applet
     309        jmolStatus.attempts[loading]+=1; //update number of checks for load completion.
     310        if(jmolStatus.defaultdirectory[loading]=="done"){//Applet is ready.
     311            jmolAppletLive(loading);
     312            }else{ //Applet not ready. How many checks have we done?
     313            if(jmolStatus.attempts[loading]==10){
     314                alert("Jmol Applet #"+loading+" is having trouble loading.  Will retry once.");
     315                var scriptStr = 'x=defaultdirectory; data "directory @x";';
     316                scriptStr += 'set MessageCallback "jmolMessageHandler"; show defaultdirectory;';
     317                jmolScript(scriptStr);
     318                }
     319            if(jmolStatus.attempts[loading]==20){
     320                alert("Second attempt to finish launch of Jmol Applet #"+loading+" failed.  Recommend reevaluating the cell manually.");
     321                jmolStatus.jmolArray[loading]=-2; //launch failed.
     322                }
     323            }
     324        }else{//no loading applets. Search for queued applet.
     325        queued = -1;
     326        for (n=0;n<numAppletsAtStart;n++){
     327            if(jmolStatus.jmolArray[n]==3){queued=n;};//will use the last one we find
     328            }
     329        if(queued>=0){//we found a queued applet and can start its launch.
     330            //alert("About to launch applet #"+queued);
     331            var scriptStr = 'script "'+jmolStatus.urls[queued]+'"; isosurface fullylit; pmesh o* fullylit;';
     332            scriptStr +='set antialiasdisplay on;x=defaultdirectory; data "directory @x";';
     333            scriptStr += 'set MessageCallback "jmolMessageHandler"; show defaultdirectory;';
     334            //alert("About to look for the div to put it in");
     335            if (get_element("Jmol"+ queued) ){//the div is ready
     336                //sleep some if necessary
     337                //alert("Found div.  About to enter LimitLive.");
     338                limitlive(queued, jmolStatus);
     339                //alert("left LimitLive");
     340                jmolStatus.attempts[queued]=0; //no checks on load completion yet.
     341                jmolStatus.jmolArray[queued]=2; //now it is loading
     342                get_element("Jmol"+ queued).innerHTML = jmolApplet([jmolStatus.widths[queued], jmolStatus.heights[queued]], scriptStr, queued);
     343                }                 
     344            }
    268345        }
    269346    }
    270347
    271348function jmolAppletLive(n){//called after an applet is loaded to say set state to live
    272349        jmolStatus.jmolArray[n]=0;
     350    jmol_numLiveUpdate();
    273351    }
    274 function newJmolTableStr(jmolStatus, width, height, url, wakeMessage, sleepMessage, captionStr, controlStr){
     352
     353function newJmolTableStr(n, width, height, url, wakeMessage, sleepMessage, captionStr, controlStr){
    275354    //if captionStr or controlStr is the empty string, '', then the caption or the controls  respectively will not be shown.
    276     n = jmolStatus.jmolArray.length;
    277355    Id = 'Jmol'+n;
    278356    tableId = 'Jmol_Table_'+Id;
    279357    tableStr = '<table id="'+tableId+'" border="1"><tr><td id="'+tableId+'_cell_0_0">';
     
    284362    tableStr += 'Loading Jmol 3-D viewer...</div>'; 
    285363            tableStr+='</td>';
    286364    if (controlStr!=''){
    287             //tempCntrlStr = '<a href="javascript:void(wakeJmol('+n+',jmolStatus))">'+ wakeMessage +'</a>';
    288365            tempCntrlStr ='<button onClick="javascript:void(sleepJmol('+n+',jmolStatus))">'+sleepMessage+'</button>';
    289366        tempCntrlStr += '<button onClick="javascript:void(jmol_help())">Help for Jmol 3-D viewer</button>'+controlStr;
    290367        tableStr += '<td id="'+tableId+'_cell_0_1" style="display:none;">'+tempCntrlStr+'</td>';
     
    346423//    for (i in scriptArray){
    347424//        dispStr +=scriptArray[i]+'<br/>';
    348425//        }
    349     dispStr +='<table  border="1"><tr><td>Function</td><td>Color</td><td>Translucency</td><td>On?</td><td>Mesh Color</td><td>Mesh on?</td></tr>';
     426    dispStr +='<table  border="1"><tr><td>Function</td><td>Color</td><td>On?</td><td>Translucency</td><td>Mesh Color</td><td>Mesh on?</td></tr>';
    350427    for (i in surfaceArray){
    351428//        dispStr +='Surface #'+i+'<br/>';
    352429        dispStr +='<tr>';
     
    359436        var scriptStr = 'color $'+surfaceArray[i].ID+' $COLOR$';
    360437        var boxIdStr = 'colorBox_'+n+'_'+i;
    361438        dispStr +='<td>'+JmolColorPickerBoxStr(scriptStr,surfaceArray[i].color,boxIdStr,n)+'</td>';
     439//        dispStr +='Visibility:' +surfaceArray[i].visibility+'<br/>';
     440        var checkedStr = 'checked = "true"';
     441        re_off = /off/i;
     442        if (surfaceArray[i].visibility.match(re_off)){
     443            checkedStr = '';
     444            }
     445        dispStr +='<td><input class="worksheet" type="checkbox" '+checkedStr+' onchange="jmol_show_element(this.checked,\''+surfaceArray[i].ID+'\',\''+surfaceArray[i].type+'\','+n+');" title="Show function"/></td>';
    362446//        dispStr +='Fill State:' +surfaceArray[i].fillState+'<br/>';
    363447        dispStr +='<td><select class="jmol" title ="Select transparency" onchange="jmolSurfColor(this.value,\''+surfaceArray[i].ID+'\','+n+');">';
    364448        dispStr +='<option selected="" value ="'+surfaceArray[i].fillState+'">Default</option>';
     
    374458        dispStr +='<option value = "'+ fillState +'">translucent 128</option>';
    375459        dispStr +='</';
    376460        dispStr += 'select></td>';
    377 //        dispStr +='Visibility:' +surfaceArray[i].visibility+'<br/>';
    378         var checkedStr = 'checked = "true"';
    379         re_off = /off/i;
    380         if (surfaceArray[i].visibility.match(re_off)){
    381             checkedStr = '';
    382             }
    383         dispStr +='<td><input class="worksheet" type="checkbox" '+checkedStr+' onchange="jmol_show_element(this.checked,\''+surfaceArray[i].ID+'\',\''+surfaceArray[i].type+'\','+n+');" title="Show function"/></td>';
    384461//        dispStr +='Mesh ID:' +surfaceArray[i].mesh_ID+'<br/>';
    385462//        dispStr +='Mesh Color:' +surfaceArray[i].meshColor+'<br/>';
    386463        if (surfaceArray[i].mesh_ID ==''){//we don't have a mesh so  need to make one
     
    395472            surfaceArray[i].meshColor = '[x000000]';
    396473            surfaceArray[i].mesh_visibility = 'off';
    397474            //we've changed the state, so save
    398             storeDefaultDir(n);
    399475            jmolUpdateState(n);
    400476            }
    401477        scriptStr = 'color $'+surfaceArray[i].mesh_ID+' $COLOR$';
     
    693769
    694770function sleepJmol(n,jmolStatus) {
    695771    if(jmolStatus.jmolArray[n]==0){//it's awake, so put to sleep
    696         //get a picture to replace the applet
    697         jmolStatus.pictureStrs[n] = get_jmol_image(n, jmolStatus);
     772        //alert('applet #'+n+' is awake.');
     773        //get a picture to replace the applet
     774        jmolStatus.pictureStrs[n] = get_jmol_image(n, jmolStatus);
     775        //alert('got image from applet.');
    698776        //make sure the state is up-to-date
    699777        jmolUpdateState(n);
     778        //alert("Have got the picture and updated the state before sleeping #"+n);
    700779        //Different browsers pass different versions of null and undefined.
    701         if(jmolStatus.pictureStrs[n]=="null"||jmolStatus.pictureStrs[n]==""||jmolStatus.pictureStrs[n]==undefined||jmolStatus.pictureStrs[n]==null||jmolStatus.pictureStrs[n]=="undefined"){//don't have a picture put up text instead
    702             get_element("Jmol"+n).innerHTML = 'Sleeping...<br/>Static plot unavailable. <br/>  Click Wake Up to get live plot.';
    703             }else{
    704             var imageID = 'Jmol_Image'+n;
    705             //The below does not work with Safari, doesn't show alternate text when no image data.
    706             var imageStr = '<image id='+imageID+' alt="If no plot appears here click Wake Up" src="data:image/jpeg;base64, ' + jmolStatus.pictureStrs[n] + '">';
    707             get_element("Jmol"+n).innerHTML = 'Sleeping...<button onClick="javascript:void(wakeJmol('+n+',jmolStatus))">Make Interactive</button><br/>'+imageStr;
     780        if(jmolStatus.pictureStrs[n]=="null"||jmolStatus.pictureStrs[n]==""||jmolStatus.pictureStrs[n]==undefined||jmolStatus.pictureStrs[n]==null||jmolStatus.pictureStrs[n]=="undefined"){//don't have a picture put up text instead
     781            get_element("Jmol"+n).innerHTML = 'Sleeping...<br/>Static plot unavailable. <br/>  <button onClick="javascript:void(wakeJmol('+n+',jmolStatus))">Make Interactive</button> to get live plot.';
     782                }else{
     783                var imageID = 'Jmol_Image'+n;
     784                //The below does not work with Safari, doesn't show alternate text when no image data.
     785                var imageStr = '<image id='+imageID+' alt="If no plot appears here click Wake Up" src="data:image/jpeg;base64, ' + jmolStatus.pictureStrs[n] + '">';
     786                get_element("Jmol"+n).innerHTML = 'Sleeping...<button onClick="javascript:void(wakeJmol('+n+',jmolStatus))">Make Interactive</button><br/>'+imageStr;
    708787           }
    709788        jmolStatus.jmolArray[n]=1; //we've turned it off
    710789        //make sure the controls that only work with live Jmol are hidden
     
    712791        get_element(cellID).setAttribute("style","display: none;");
    713792        var togname = '#Adv_but_Jmol'+n;
    714793        $(togname).toggle();       
    715             jmolStatus.numLive = jmolStatus.numLive-1;
     794            jmol_numLiveUpdate();
    716795       }
    717796    }
    718797
     
    727806        re_linebreak = /<br>/gi;
    728807//        scriptStr = get_element("jmolStateDiv"+n).innerHTML.replace(re_linebreak,'\n');
    729808        var scriptStr = jmolStatus.stateScripts[n];
    730 //to avoid a problem with testing of numbers in Jmol.js  quote the appletID #
    731809        var nquote = n;
    732810        get_element("Jmol"+ nquote).innerHTML = jmolApplet([width,height], scriptStr, nquote);
    733811        if (jmolStatus.jmolArray[n]!=0){//it wasn't on, if it was we've just done a reset so don't need to update status
    734812                jmolStatus.jmolArray[n]=0; //we've turned it on
    735                 jmolStatus.numLive = jmolStatus.numLive+1;
     813                jmol_numLiveUpdate();
    736814        var togname = '#Adv_but_Jmol'+n;
    737815        $(togname).toggle();       
    738816                }
     
    744822        //will attempt to shut down a Jmol as far away as possible.
    745823        //needs to be more sophisticated about updating jmolStatus because
    746824        //applets may have been removed without updating.
    747     j = 0;
    748     loading= false;
    749     while (j<jmolStatus.jmolArray.length){
    750         if(jmolStatus.jmolArray[j]==2){
    751             loading = true;           
     825        while (jmolStatus.numLive >= jmolStatus.maxLiveAllowed) {
     826        //search only from zero
     827        k=0;
     828        while (jmolStatus.jmolArray[k]!=0){
     829            k = k +1;
    752830            }
    753         j=j+1;
    754         }
    755     if (loading){ //if any are still loading we wait
    756         var launchStr = 'limitlive('+nWake+',jmolStatus)';
    757 //        alert("Waiting for Jmol"+n+" div.");
    758         setTimeout(launchStr, 1000); }else{
    759             while (jmolStatus.numLive >= jmolStatus.maxLiveAllowed) {
    760                 //search only from zero
    761                 k=0;
    762                 while (jmolStatus.jmolArray[k]!=0){
    763                         k = k +1;
    764                         }
    765                 //search only from max
    766                 i = jmolStatus.jmolArray.length-1;
    767                 while (jmolStatus.jmolArray[i]!=0){
    768                                 i = i -1;
    769                         }
    770                 if (Math.abs(nWake-i) > Math.abs(nWake-k)){
    771                         nSleep = i;}else{
    772                         nSleep = k;}
    773                 sleepJmol(nSleep, jmolStatus);
    774                 }
     831        //search only from max
     832        i = jmolStatus.jmolArray.length-1;
     833        while (jmolStatus.jmolArray[i]!=0){
     834            i = i -1;
     835            }
     836        if (Math.abs(nWake-i) > Math.abs(nWake-k)){
     837            nSleep = i;}else{
     838            nSleep = k;}
     839        //alert("About to sleep applet#"+nSleep);
     840        sleepJmol(nSleep, jmolStatus);
    775841        }
    776842        }
    777843
     
    793859            //each time a page is opened.
    794860        }
    795861    }
    796     for ( i = 0; i < liveCount; i++){ //reset the number of live Jmols
    797         if (jmolStatus.jmolArray[i]!=0) {liveCount = liveCount-1;}
    798     }
    799     jmolStatus.numLive = liveCount;
     862    jmol_numLiveUpdate();
    800863}
    801864
    802865function jmol_image(jmol_count) {
    803   var myImage = jmolGetPropertyAsString("image","",jmol_count)
    804   mywindow = window.open("","Jmol Image","menubar=no,width=600,height=600,toolbar=no");
    805   s = '<HTML><TITLE>Jmol Image</TITLE><BODY>';
    806   s += '<img src="data:image/jpeg;base64,' + myImage + '">';
    807   s += '<p>To save this image, you can try right-clicking on the image to copy it or save it to a file, or you may be able to just drag the image to your desktop.</p>';
    808   s += '</BODY></HTML>';
    809   mywindow.document.write(s);
    810 }
     866    var myImage = jmolGetPropertyAsString("image","",jmol_count);
     867    mywindow = window.open("","Jmol Image","menubar=no,width=600,height=600,toolbar=no");
     868    s = '<HTML><TITLE>Jmol Image</TITLE><BODY>';
     869    s += '<img src="data:image/jpeg;base64,' + myImage + '">';
     870    s += '<p>To save this image, you can try right-clicking on the image to copy it or save it to a file, or you may be able to just drag the image to your desktop.</p>';
     871    s += '</BODY></HTML>';
     872    mywindow.document.write(s);
     873    }
    811874
    812875
    813876function get_jmol_image(n, jmolStatus){
    814877    var pictureStr="";
    815878    if(jmolStatus.jmolArray[n] == 0) {//it's live
    816         pictureStr = jmolGetPropertyAsString("image","",n);
     879        if(jmolStatus.jmolArray.length < 10 ||jmolStatus.os!='mac'||jmolStatus.browser!='webkit'){
     880            pictureStr = jmolGetPropertyAsString("image","",n);
     881        }else{
     882            alert('More than 9 Jmols have been launched on the worksheet since it was last openned. Unable to get a static image from Jmol#'+n+' You might want to try Chrome as this problem does not exist with Chrome on MacOS.');
    817883        }
     884    }
    818885    return(pictureStr);
    819886    }
    820887
    821888function jmol_popup(n) {
     889    //first make sure we have the current state
     890    jmolUpdateState(n);
    822891    win = window.open ("", "jmol viewer", "width=600,height=600,resizable=1,statusbar=0");
    823892    win.document.body.innerHTML = "";
    824893    win.document.title = "Sage 3d Viewer";
     
    827896//    scriptStr = 'script "'+url+'"; isosurface fullylit; pmesh o* fullylit; set antialiasdisplay on;';
    828897    re_linebreak = /<br>/gi;
    829898    var scriptStr = get_element("jmolStateDiv"+n).innerHTML;
    830     scriptStr = scriptStr.replace(re_linebreak,'\n')
     899    scriptStr = scriptStr.replace(re_linebreak,'\n');
    831900    scriptStr = jmolStatus.stateScripts[n];//comment out to use the script in jmolStateDiv.
     901    //sleep all the applets before openning the big window.
     902    jmol_sleepall();
    832903    jmolApplet("100%", scriptStr, n);
    833904    win.focus();
    834905}
    835906
     907function jmol_sleepall(){
     908    var j = 0;
     909    while (j < jmolStatus.jmolArray.length){
     910        if (jmolStatus.jmolArray[j]==0){//it is awake and needs to be put to sleep
     911            sleepJmol(j, jmolStatus);
     912            }
     913        j = j+1;
     914        }
     915    }
    836916function jmol_help(){
    837917    win = window.open("/java/jmol/appletweb/JmolHelp.html","Jmol Help","width=400, height=600");
    838918    win.focus();
     
    10121092        var rgbCodes = colorStr.replace(/rgb/i,'').replace('(','[').replace(')',']');
    10131093        var scriptStr = JmolColorPickerBoxes[boxNum].scriptStr.replace('$COLOR$', rgbCodes);
    10141094        jmolScript(scriptStr,JmolColorPickerBoxes[boxNum].appletID);
    1015         storeDefaultDir(JmolColorPickerBoxes[boxNum].appletID);
    10161095        jmolUpdateState(JmolColorPickerBoxes[boxNum].appletID);
    10171096    }
    10181097//    tempStr += '<br/>The picked color is: '+colorStr+'.<br/>'+pickerDiv.id; 
     
    10691148    var rgbCodes = pickedColor.replace(/rgb/i,'').replace('(','[').replace(')',']');
    10701149    var scriptStr = JmolColorPickerBoxes[boxNum].scriptStr.replace('$COLOR$', rgbCodes);
    10711150    jmolScript(scriptStr,JmolColorPickerBoxes[boxNum].appletID);
    1072     storeDefaultDir(JmolColorPickerBoxes[boxNum].appletID);
    10731151    jmolUpdateState(JmolColorPickerBoxes[boxNum].appletID);
    10741152}
    10751153