Merge branch 'master' of https://source.jalview.org/git/jalviewjs.git
[jalviewjs.git] / site / js / JSmolCore.js
index b1a06e8..66517bb 100644 (file)
-// JSmolCore.js -- Jmol core capability \r
-\r
-// allows Jmol applets to be created on a page with more flexibility and extendability\r
-// provides an object-oriented interface for JSpecView and syncing of Jmol/JSpecView\r
-\r
-// see JSmolApi.js for public user-interface. All these are private functions\r
-\r
-// BH 5/30/2015 9:33:12 AM adds class swingjs-ui to ignore \r
-// BH 5/9/2015 3:38:52 PM adds data-ignoreMouse attribute for JTextField\r
-// BH 3/30/2015 9:46:53 PM adds JSAppletPanel for ready callback\r
-// BH 12/6/2014 3:32:54 PM Jmol.setAppletCss() broken\r
-// BH 9/13/2014 2:15:51 PM embedded JSME loads from SEARCH when Jmol should \r
-// BH 8/14/2014 2:52:38 PM drag-drop cache should not be cleared if SPT file is dropped\r
-// BH 8/5/2014 6:39:54 AM unnecessary messages about binary for PDB finally removed\r
-// BH 8/4/2014 5:30:00 AM automatically switch to no document after page loading\r
-// BH 8/2/2014 5:22:40 PM drag-drop broken in JSmol/HTML5 \r
-// BH 7/23/2014 5:34:08 PM setting a parameter such as readyFunction to null stops file loading\r
-// BH 7/3/2014 12:30:28 AM lost drag-drop of models\r
-// BH 7/2/2014 4:47:55 AM adding pdbe.org to direct database calls\r
-// BH 5/30/2014 7:20:07 AM better dragging for console and menu\r
-// BH 4/27/2014 6:31:52 PM allows _USE=SIGNED HTML5 as well as _USE=JAVA HTML5\r
-// BH 3/8/2014 5:50:51 PM adds support for dataURI download in FF and Chrome\r
-// BH 3/8/2014 8:43:10 AM moves PubChem access to https\r
-// BH 3/4/2014 8:40:15 PM adds Jmol.Cache for JSV/Jmol sharing files\r
-// BH 2/10/2014 10:07:14 AM added Info.z and Info.zIndexBase\r
-// BH 2/9/2014 9:56:06 PM updated JSmolCore.js with option to extend Viewer with code PRIOR to loading Viewer classes\r
-// BH 2/6/2014 8:46:25 AM disabled Jmol._tracker for localhost and 127.x.x.x \r
-// BH 1/29/2014 8:02:23 AM Jmol.View and Info.viewSet\r
-// BH 1/21/2014 12:06:59 PM adding Jmol.Info.cacheFiles (applet, true/false) and applet._cacheFiles and Jmol._fileCache\r
-// BH 1/13/2014 2:12:38 PM adding "http://www.nmrdb.org/tools/jmol/predict.php":"%URL", to _DirectDatabaseCalls\r
-// BH 12/21/2013 6:38:35 PM applet sync broken\r
-// BH 12/6/2013 6:18:32 PM cover.htm and coverImage fix\r
-// BH 12/4/2013 7:44:26 PM fix for JME independent search box\r
-// BH 12/3/2013 6:30:08 AM fix for ready function returning Boolean instead of boolean in HTML5 version\r
-// BH 11/30/2013 10:31:37 AM added type:"GET" for jQuery.ajax() requests instead of using defaults\r
-// BH 11/30/2013 10:31:37 AM added cache:true for jQuery.ajax() requests; can override with cache:"NO", not cache:false\r
-// BH 11/28/2013 11:09:27 AM added Jmol._alertNoBinary:true\r
-// BH 11/26/2013 8:19:55 PM fix !xxxx search commmand entry and stop MSIE from duplicating command\r
-// BH 11/25/2013 7:38:31 AM adds Jmol._tracker: option for GoogleAnalytics tracking\r
-// BH 11/25/2013 7:39:03 AM adds URL options _J2S=  _JAR=  _USE=\r
-// BH 11/23/2013 10:51:37 PM  adds JNLP support for local applet\r
-// BH 11/2/2013 12:05:11 PM JSmolJSME fixes; https access fixed\r
-// BH 10/31/2013 7:50:06 PM Jmol.Dialog as SwingController; Jmol._mouseOwner added\r
-// BH 10/19/2013 7:05:04 AM adding Jmol._ajaxCall for Error Contacting Server; database POST method enabled\r
-// BH 10/17/2013 1:40:51 PM  adding javajs/swing and Jmol.Dialog\r
-// BH 9/30/2013 6:42:24 PM: pdb.gz switch  pdb should only be for www.rcsb.org\r
-// BH 9/17/2013 10:17:51 AM: asynchronous file reading and saving\r
-// BH 8/16/2013 12:02:20 PM: JSmoljQueryExt.js pulled out\r
-// BH 8/16/2013 12:02:20 PM: Jmol._touching used properly\r
-\r
-// BH 3/22/2013 5:53:02 PM: Adds noscript option, JSmol.min.core.js\r
-// BH 1/17/2013 5:20:44 PM: Fixed problem with console not getting initial position if no first click\r
-// 1/13/2013 BH: Fixed MSIE not-reading-local-files problem.\r
-// 11/28/2012 BH: Fixed MacOS Safari binary ArrayBuffer problem\r
-// 11/21/2012 BH: restructuring of files as JS... instead of J...\r
-// 11/20/2012 BH: MSIE9 cannot do a synchronous file load cross-domain. See Jmol._getFileData\r
-// 11/4/2012 BH: RCSB REST format change "<structureId>" to "<dimStructure.structureId>"\r
-// 9/13/2012 BH: JmolCore.js changes for JSmol doAjax() method -- _3ata()\r
-// 6/12/2012 BH: JmolApi.js: adds Jmol.setInfo(applet, info, isShown) -- third parameter optional \r
-// 6/12/2012 BH: JmolApi.js: adds Jmol.getInfo(applet) \r
-// 6/12/2012 BH: JmolApplet.js: Fixes for MSIE 8\r
-// 6/5/2012  BH: fixes problem with Jmol "javascript" command not working and getPropertyAsArray not working\r
-// 6/4/2012  BH: corrects problem with MSIE requiring mouse-hover to activate applet\r
-// 5/31/2012 BH: added JSpecView interface and api -- see JmolJSV.js\r
-//               also changed "jmolJarPath" to just "jarPath"\r
-//               jmolJarFile->jarFile, jmolIsSigned->isSigned, jmolReadyFunction->readyFunction\r
-//               also corrects a double-loading issue\r
-// 5/14/2012 BH: added AJAX queue for ChemDoodle option with multiple canvases \r
-// 8/12/2012 BH: adds support for MSIE xdr cross-domain request (jQuery.iecors.js)\r
-\r
-       // BH 4/25 -- added text option. setAppletCss(null, "style=\"xxxx\"")\r
-       // note that since you must add the style keyword, this can be used to add any attribute to these tags, not just css. \r
-\r
-// required/optional libraries (preferably in the following order):\r
-\r
-//    jquery/jquery.js     -- at least jQuery.1.9\r
-//    js/JSmoljQueryext.js -- required for binary file transfer; otherwise standard jQuery should be OK\r
-//    js/JSmolCore.js      -- required\r
-//    js/j2sjmol.js        -- required\r
-//    js/JSmol.js          -- required\r
-//    js/JSmolApplet.js    -- required; internal functions for _Applet and _Image; must be after JSmolCore\r
-//    js/JSmolControls.js  -- optional; internal functions for buttons, links, menus, etc.; must be after JSmolCore\r
-//    js/JSmolConsole.js   -- optional; for the pop-up console\r
-//    js/JSmolApi.js       -- required; all user functions; must be after JSmolCore\r
-//    js/JSmolTHREE.js     -- optional; WebGL library required for JSmolGLmol.js\r
-//    js/JSmolGLmol.js     -- optional; WebGL version of JSmol.\r
-//    js/JSmolJME.js       -- optional; JSME (2D editor)\r
-//    jsme/jsme/jsme.nocache.js   --  required for JSME \r
-//    js/JSmolMenu.js      -- optional; required for menuing in JSV\r
-//    js/JSmolJSV.js       -- optional; for creating and interacting with a JSpecView applet \r
-\r
-// most of these will be loaded automatically, and for most installations, all you need is JSmol.min.js\r
-\r
-\r
-// Allows Jmol-like objects to be displayed on Java-challenged (iPad/iPhone)\r
-// or applet-challenged (Android/iPhone) platforms, with automatic switching to \r
-\r
-// For your installation, you should consider putting JmolData.jar and jsmol.php \r
-// on your own server. Nothing more than these two files is needed on the server, and this \r
-// allows more options for MSIE and Chrome when working with cross-domain files (such as RCSB or pubChem) \r
-\r
-// The NCI and RCSB databases are accessed via direct AJAX if available (xhr2/xdr).\r
-\r
-\r
-if(typeof(jQuery)=="undefined") alert ("Note -- JSmoljQuery is required for JSmol, but it's not defined.")\r
-\r
-// An example of how to extend Jmol with code PRIOR to JSmolCore.js or JSmol.min.js follows:\r
-//\r
-// \r
-//     Jmol = {\r
-//     z:3000,\r
-//             extend: function(what, obj) {if (what == "viewer") { obj._testing = true } }\r
-//     }\r
-\r
-self.Jmol || (Jmol = {});\r
-\r
-if (!Jmol._version)\r
-Jmol = (function(document) {\r
-       var z=Jmol.z || 9000;\r
-       var getZOrders = function(z) {\r
-               return {\r
-                       rear:z++,\r
-                       header:z++,\r
-                       main:z++,\r
-                       image:z++,\r
-                       front:z++,\r
-                       fileOpener:z++,\r
-                       coverImage:z++,\r
-                       dialog:z++, // could be several of these, JSV only\r
-                       menu:z+90000, // way front\r
-                       console:z+91000, // even more front\r
-      consoleImage:z+91001, // bit more front; increments\r
-                       monitorZIndex:z+99999 // way way front\r
-               }\r
-       };\r
-       var j = {\r
-               _version: "$Date: 2015-04-26 10:57:08 -0500 (Sun, 26 Apr 2015) $", // svn.keywords:lastUpdated\r
-               _alertNoBinary: true,\r
-               // this url is used to Google Analytics tracking of Jmol use. You may remove it or modify it if you wish. \r
-               _allowedJmolSize: [25, 2048, 300],   // min, max, default (pixels)\r
-               /*  By setting the Jmol.allowedJmolSize[] variable in the webpage\r
-                               before calling Jmol.getApplet(), limits for applet size can be overriden.\r
-                               2048 standard for GeoWall (http://geowall.geo.lsa.umich.edu/home.html)\r
-               */\r
-               _appletCssClass: "",\r
-               _appletCssText: "",\r
-               _fileCache: null, // enabled by Jmol.setFileCaching(applet, true/false)\r
-               _jarFile: null,  // can be set in URL using _JAR=\r
-               _j2sPath: null,  // can be set in URL using _J2S=\r
-               _use: null,      // can be set in URL using _USE=\r
-               _j2sLoadMonitorOpacity: 90, // initial opacity for j2s load monitor message\r
-               _applets: {},\r
-               _asynchronous: true,\r
-               _ajaxQueue: [],\r
-               _getZOrders: getZOrders,\r
-               _z:getZOrders(z),\r
-               _debugCode: true,  // set false in process of minimization\r
-               db: {\r
-                       _databasePrefixes: "$=:",\r
-                       _fileLoadScript: ";if (_loadScript = '' && defaultLoadScript == '' && _filetype == 'Pdb') { select protein or nucleic;cartoons Only;color structure; select * };",\r
-                       _nciLoadScript: ";n = ({molecule=1}.length < {molecule=2}.length ? 2 : 1); select molecule=n;display selected;center selected;",\r
-                       _pubChemLoadScript: "",\r
-                       _DirectDatabaseCalls:{\r
-                               // these sites are known to implement access-control-allow-origin * \r
-                               "cactus.nci.nih.gov": "%URL", \r
-                               "www.rcsb.org": "%URL",\r
-                               "pdbe.org": "%URL", \r
-                               "www.ebi.ac.uk": "%URL", \r
-                               "wwwdev.ebi.ac.uk": "%URL", \r
-                               "pubchem.ncbi.nlm.nih.gov":"%URL",\r
-                               "http://www.nmrdb.org/tools/jmol/predict.php":"%URL",\r
-                               "$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf&get3d=True",\r
-                               "$$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf",\r
-                               "=": "http://www.rcsb.org/pdb/files/%FILE.pdb",\r
-                               "*": "http://www.ebi.ac.uk/pdbe/entry-files/download/%FILE.cif",\r
-                               "==": "http://www.rcsb.org/pdb/files/ligand/%FILE.cif",\r
-                               ":": "http://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/%FILE/SDF?record_type=3d"\r
-                       },\r
-                       _restQueryUrl: "http://www.rcsb.org/pdb/rest/search",\r
-                       _restQueryXml: "<orgPdbQuery><queryType>org.pdb.query.simple.AdvancedKeywordQuery</queryType><description>Text Search</description><keywords>QUERY</keywords></orgPdbQuery>",\r
-                       _restReportUrl: "http://www.pdb.org/pdb/rest/customReport?pdbids=IDLIST&customReportColumns=structureId,structureTitle"\r
-               },\r
-               _debugAlert: false,\r
-               _document: document,\r
-               _isXHTML: false,\r
-               _lastAppletID: null,\r
-               _mousePageX: null,\r
-               _mouseOwner: null,\r
-               _serverUrl: "http://your.server.here/jsmol.php",\r
-               _syncId: ("" + Math.random()).substring(3),\r
-               _touching: false,\r
-               _XhtmlElement: null,\r
-               _XhtmlAppendChild: false\r
-       }\r
-       \r
-       var ref = document.location.href.toLowerCase();\r
-       j._httpProto = (ref.indexOf("https") == 0 ? "https://" : "http://"); \r
-       j._isFile = (ref.indexOf("file:") == 0);\r
-       j._ajaxTestSite = j._httpProto + "google.com";\r
-       var isLocal = (j._isFile || ref.indexOf("http://localhost") == 0 || ref.indexOf("http://127.") == 0);\r
-       j._tracker = (j._httpProto == "http://" && !isLocal && 'http://chemapps.stolaf.edu/jmol/JmolTracker.htm?id=UA-45940799-1');\r
-       \r
-       j._isChrome = (navigator.userAgent.toLowerCase().indexOf("chrome") >= 0);\r
-       j._isSafari = (!j._isChrome && navigator.userAgent.toLowerCase().indexOf("safari") >= 0);\r
-       j._isMsie = (window.ActiveXObject !== undefined);\r
-       j._useDataURI = !j._isSafari && !j._isMsie; // safari may be OK here -- untested\r
-\r
-       for(var i in Jmol) j[i] = Jmol[i]; // allows pre-definition\r
-       return j;\r
-})(document, Jmol);\r
-\r
-\r
-(function (Jmol, $) {\r
-\r
-// this library is organized into the following sections:\r
-\r
-       // jQuery interface \r
-       // protected variables\r
-       // feature detection\r
-       // AJAX-related core functionality\r
-       // applet start-up functionality\r
-       // misc core functionality\r
-       // mouse events\r
-\r
-\r
-       ////////////////////// jQuery interface ///////////////////////\r
-\r
-       // hooks to jQuery -- if you have a different AJAX tool, feel free to adapt.\r
-       // There should be no other references to jQuery in all the JSmol libraries.\r
-\r
-       // automatically switch to returning HTML after the page is loaded\r
-       $(document).ready(function(){ Jmol._document = null });\r
-\r
-       Jmol.$ = function(objectOrId, subdiv) {\r
-               // if a subdiv, then return $("#objectOrId._id_subdiv") \r
-               // or if no subdiv, then just $(objectOrId)\r
-               if (objectOrId == null)alert (subdiv + arguments.callee.caller.toString());\r
-                       return $(subdiv ? "#" + objectOrId._id + "_" + subdiv : objectOrId);\r
-       } \r
-\r
-       Jmol._$ = function(id) {\r
-               // either the object or $("#" + id)\r
-               return (typeof id == "string" ? $("#" + id) : id);\r
-       }\r
-\r
-       /// special functions:\r
-\r
-       Jmol.$ajax = function (info) {\r
-               Jmol._ajaxCall = info.url;\r
-               info.cache = (info.cache != "NO");\r
-               if (info.url.indexOf("http://pubchem.ncbi.nlm.nih") == 0)\r
-                       info.url = "https://" + info.url.substring(7);\r
-                       // fluke at pubchem --- requires https now from all pages 3/8/2014\r
-               // don't let jQuery add $_=... to URL unless we \r
-               // use cache:"NO"; other packages might use $.ajaxSetup() to set this to cache:false\r
-               return $.ajax(info);\r
-       }\r
-\r
-       Jmol._getNCIInfo = function(identifier, what, fCallback) {\r
-               return Jmol._getFileData("http://cactus.nci.nih.gov/chemical/structure/"+identifier +"/" + (what == "name" ? "names" : what));\r
-       }\r
-       \r
-\r
-\r
-       Jmol.$appEvent = function(app, subdiv, evt, f) {\r
-               var o = Jmol.$(app, subdiv); \r
-               o.off(evt) && f && o.on(evt, f);\r
-       }   \r
-\r
-       Jmol.$resize = function (f) {\r
-               return $(window).resize(f);\r
-       }\r
-\r
-       //// full identifier expected (could be "body", for example):\r
-\r
-       Jmol.$after = function (what, s) {\r
-               return $(what).after(s);\r
-       }\r
-\r
-       Jmol.$append = function (what, s) {\r
-               return $(what).append(s);\r
-       }\r
-\r
-       Jmol.$bind = function(what, list, f) {\r
-               return (f ? $(what).bind(list, f) : $(what).unbind(list));\r
-       }\r
-\r
-       Jmol.$closest = function(what, d) {\r
-               return $(what).closest(d);\r
-       }\r
-       \r
-       Jmol.$get = function(what, i) {\r
-       return $(what).get(i);\r
-       }\r
\r
-       // element id expected\r
-                        \r
-       Jmol.$documentOff = function(evt, id) {\r
-               return $(document).off(evt, "#" + id);\r
-       }\r
-\r
-       Jmol.$documentOn = function(evt, id, f) {\r
-               return $(document).on(evt, "#" + id, f);\r
-       }\r
-\r
-       Jmol.$getAncestorDiv = function(id, className) {\r
-               return $("div." + className + ":has(#" + id + ")")[0];\r
-       }\r
-\r
-       Jmol.$supportsIECrossDomainScripting = function() {\r
-               return $.support.iecors;\r
-       }\r
-\r
-       //// element id or jQuery object expected:\r
-\r
-       Jmol.$attr = function (id, a, val) {\r
-               return Jmol._$(id).attr(a, val);\r
-       }\r
-\r
-       Jmol.$css = function(id, style) {\r
-               return Jmol._$(id).css(style);\r
-       }\r
-        \r
-       Jmol.$find = function(id, d) {\r
-               return Jmol._$(id).find(d);\r
-       }\r
-       \r
-       Jmol.$focus = function(id) {\r
-               return Jmol._$(id).focus();\r
-       }\r
-\r
-       Jmol.$html = function(id, html) {\r
-               return Jmol._$(id).html(html);\r
-       }\r
-        \r
-       Jmol.$offset = function(id) {\r
-               return Jmol._$(id).offset();\r
-       }\r
-\r
-       Jmol.$windowOn = function(evt, f) {\r
-               return $(window).on(evt, f);\r
-       }\r
-\r
-       Jmol.$prop = function(id, p, val) {\r
-               var o = Jmol._$(id);\r
-               return (arguments.length == 3 ? o.prop(p, val) : o.prop(p));\r
-       }\r
-\r
-       Jmol.$remove = function(id) {\r
-               return Jmol._$(id).remove();\r
-       }\r
-\r
-       Jmol.$scrollTo = function (id, n) {\r
-               var o = Jmol._$(id);\r
-               return o.scrollTop(n < 0 ? o[0].scrollHeight : n);\r
-       }\r
-\r
-       Jmol.$setEnabled = function(id, b) {\r
-               return Jmol._$(id).attr("disabled", b ? null : "disabled");  \r
-       }\r
-\r
-       Jmol.$setSize = function(id, w, h) {\r
-               return Jmol._$(id).width(w).height(h);\r
-       }\r
-\r
-       Jmol.$setVisible = function(id, b) {\r
-               var o = Jmol._$(id);\r
-               return (b ? o.show() : o.hide());  \r
-       }\r
-\r
-       Jmol.$submit = function(id) {\r
-               return Jmol._$(id).submit();\r
-       }\r
-\r
-       Jmol.$val = function (id, v) {\r
-               var o = Jmol._$(id);\r
-               return (arguments.length == 1 ? o.val() : o.val(v));\r
-       }\r
-\r
-       ////////////// protected variables ///////////\r
-\r
-\r
-       Jmol._clearVars = function() {\r
-               // only on page closing -- appears to improve garbage collection\r
-\r
-               delete jQuery;\r
-               delete $;\r
-               delete Jmol;\r
-               delete SwingController;\r
-               delete J;\r
-               delete JM;\r
-               delete JS;\r
-               delete JSV;\r
-               delete JU;\r
-               delete JV;\r
-               delete java;\r
-               delete javajs;\r
-               delete Clazz;\r
-               delete c$; // used in p0p; could be gotten rid of\r
-       }\r
-\r
-       ////////////// feature detection ///////////////\r
-\r
-       Jmol.featureDetection = (function(document, window) {\r
-\r
-               var features = {};\r
-               features.ua = navigator.userAgent.toLowerCase()\r
-\r
-               features.os = (function(){\r
-                       var osList = ["linux","unix","mac","win"]\r
-                       var i = osList.length;\r
-\r
-                       while (i--){\r
-                               if (features.ua.indexOf(osList[i])!=-1) return osList[i]\r
-                       }\r
-                       return "unknown";\r
-               })();\r
-\r
-               features.browser = function(){\r
-                       var ua = features.ua;\r
-                       var browserList = ["konqueror","webkit","omniweb","opera","webtv","icab","msie","mozilla"];\r
-                       for (var i = 0; i < browserList.length; i++)\r
-                       if (ua.indexOf(browserList[i])>=0) \r
-                               return browserList[i];\r
-                       return "unknown";\r
-               }\r
-               features.browserName = features.browser();\r
-               features.browserVersion= parseFloat(features.ua.substring(features.ua.indexOf(features.browserName)+features.browserName.length+1));\r
-               features.supportsXhr2 = function() {return ($.support.cors || $.support.iecors)}\r
-               features.allowDestroy = (features.browserName != "msie");\r
-               features.allowHTML5 = (features.browserName != "msie" || navigator.appVersion.indexOf("MSIE 8") < 0);\r
-               features.getDefaultLanguage = function() {\r
-                       return navigator.language || navigator.userLanguage || "en-US";\r
-               };\r
-\r
-               features._webGLtest = 0;\r
-\r
-               features.supportsWebGL = function() {\r
-               if (!Jmol.featureDetection._webGLtest) { \r
-                       var canvas;\r
-                       Jmol.featureDetection._webGLtest = ( \r
-                               window.WebGLRenderingContext \r
-                                       && ((canvas = document.createElement("canvas")).getContext("webgl") \r
-                               || canvas.getContext("experimental-webgl")) ? 1 : -1);\r
-               }\r
-               return (Jmol.featureDetection._webGLtest > 0);\r
-       };\r
-\r
-       features.supportsLocalization = function() {\r
-               //<meta charset="utf-8">                                     \r
-               var metas = document.getElementsByTagName('meta'); \r
-               for (var i= metas.length; --i >= 0;) \r
-                       if (metas[i].outerHTML.toLowerCase().indexOf("utf-8") >= 0) return true;\r
-               return false;\r
-               };\r
-\r
-       features.supportsJava = function() {\r
-               if (!Jmol.featureDetection._javaEnabled) {\r
-                       if (Jmol._isMsie) {\r
-                               if (!navigator.javaEnabled()) {\r
-                                       Jmol.featureDetection._javaEnabled = -1;\r
-                               } else {\r
-                                       //more likely -- would take huge testing\r
-                                       Jmol.featureDetection._javaEnabled = 1;\r
-                               }\r
-                       } else {\r
-                               Jmol.featureDetection._javaEnabled = (navigator.javaEnabled() && (!navigator.mimeTypes || navigator.mimeTypes["application/x-java-applet"]) ? 1 : -1);\r
-                       }\r
-               }\r
-               return (Jmol.featureDetection._javaEnabled > 0);\r
-       };\r
-\r
-       features.compliantBrowser = function() {\r
-               var a = !!document.getElementById;\r
-               var os = features.os;\r
-               // known exceptions (old browsers):\r
-               if (features.browserName == "opera" && features.browserVersion <= 7.54 && os == "mac"\r
-                       || features.browserName == "webkit" && features.browserVersion < 125.12\r
-                       || features.browserName == "msie" && os == "mac"\r
-                       || features.browserName == "konqueror" && features.browserVersion <= 3.3\r
-               ) a = false;\r
-               return a;\r
-       }\r
-\r
-       features.isFullyCompliant = function() {\r
-               return features.compliantBrowser() && features.supportsJava();\r
-       }\r
-\r
-       features.useIEObject = (features.os == "win" && features.browserName == "msie" && features.browserVersion >= 5.5);\r
-       features.useHtml4Object = (features.browserName == "mozilla" && features.browserVersion >= 5) ||\r
-               (features.browserName == "opera" && features.browserVersion >= 8) ||\r
-               (features.browserName == "webkit"/* && features.browserVersion >= 412.2 && features.browserVersion < 500*/); // 500 is a guess; required for 536.3\r
-\r
-       features.hasFileReader = (window.File && window.FileReader);\r
-\r
-       return features;\r
-\r
-})(document, window);\r
-\r
-\r
-               ////////////// AJAX-related core functionality //////////////\r
-\r
-       Jmol._ajax = function(info) {\r
-               if (!info.async) {\r
-                       return Jmol.$ajax(info).responseText;\r
-               }\r
-               Jmol._ajaxQueue.push(info)\r
-               if (Jmol._ajaxQueue.length == 1)\r
-                       Jmol._ajaxDone()\r
-       }\r
-       Jmol._ajaxDone = function() {\r
-               var info = Jmol._ajaxQueue.shift();\r
-               info && Jmol.$ajax(info);\r
-       }\r
-\r
-       Jmol._grabberOptions = [\r
-               ["$", "NCI(small molecules)"],\r
-               [":", "PubChem(small molecules)"],\r
-               ["=", "RCSB(macromolecules)"],\r
-               ["*", "PDBe(macromolecules)"]\r
-       ];\r
-\r
-       Jmol._getGrabberOptions = function(applet) {\r
-               // feel free to adjust this look to anything you want\r
-               if (Jmol._grabberOptions.length == 0)\r
-                       return ""\r
-\r
-\r
-               var s = '<input type="text" id="ID_query" onfocus="jQuery(this).select()" onkeypress="if(13==event.which){Jmol._applets[\'ID\']._search();return false}" size="32" value="" />';\r
-               var b = '<button id="ID_submit" onclick="Jmol._applets[\'ID\']._search()">Search</button></nobr>'\r
-               if (Jmol._grabberOptions.length == 1) {\r
-                       s = '<nobr>' + s + '<span style="display:none">';\r
-                       b = '</span>' + b;\r
-               } else {\r
-                       s += '<br /><nobr>'\r
-               }\r
-               s += '<select id="ID_select">'\r
-               for (var i = 0; i < Jmol._grabberOptions.length; i++) {\r
-                       var opt = Jmol._grabberOptions[i];\r
-                       s += '<option value="' + opt[0] + '" ' + (i == 0 ? 'selected' : '') + '>' + opt[1] + '</option>';\r
-               }\r
-               s = (s + '</select>' + b).replace(/ID/g, applet._id);\r
-               return '<br />' + s;\r
-       }\r
-\r
-       Jmol._getScriptForDatabase = function(database) {\r
-               return (database == "$" ? Jmol.db._nciLoadScript : database == ":" ? Jmol.db._pubChemLoadScript : Jmol.db._fileLoadScript);\r
-       }\r
-\r
-        //   <dataset><record><structureId>1BLU</structureId><structureTitle>STRUCTURE OF THE 2[4FE-4S] FERREDOXIN FROM CHROMATIUM VINOSUM</structureTitle></record><record><structureId>3EUN</structureId><structureTitle>Crystal structure of the 2[4Fe-4S] C57A ferredoxin variant from allochromatium vinosum</structureTitle></record></dataset>\r
-\r
-       Jmol._setInfo = function(applet, database, data) {\r
-               var info = [];\r
-               var header = "";\r
-               if (data.indexOf("ERROR") == 0)\r
-                       header = data;\r
-               else\r
-                       switch (database) {\r
-                       case "=":\r
-                               var S = data.split("<dimStructure.structureId>");\r
-                               var info = ["<table>"];\r
-                               for (var i = 1; i < S.length; i++) {\r
-                                       info.push("<tr><td valign=top><a href=\"javascript:Jmol.search(" + applet._id + ",'=" + S[i].substring(0, 4) + "')\">" + S[i].substring(0, 4) + "</a></td>");\r
-                                       info.push("<td>" + S[i].split("Title>")[1].split("</")[0] + "</td></tr>");\r
-                               }\r
-                               info.push("</table>");\r
-                               header = (S.length - 1) + " matches";\r
-                               break;      \r
-                       case "$": // NCI\r
-                       case ":": // pubChem\r
-                       break;\r
-                       default:\r
-                               return;\r
-               }\r
-               applet._infoHeader = header;\r
-               applet._info = info.join("");\r
-               applet._showInfo(true);\r
-       }\r
-\r
-       Jmol._loadSuccess = function(a, fSuccess) {\r
-               if (!fSuccess)\r
-                       return;\r
-               Jmol._ajaxDone();\r
-               fSuccess(a);\r
-       }\r
-\r
-       Jmol._loadError = function(fError){\r
-               Jmol._ajaxDone();\r
-               Jmol.say("Error connecting to server: " + Jmol._ajaxCall);  \r
-               null!=fError&&fError()\r
-       }\r
-\r
-       Jmol._isDatabaseCall = function(query) {\r
-               return (Jmol.db._databasePrefixes.indexOf(query.substring(0, 1)) >= 0);\r
-       }\r
-\r
-       \r
-       Jmol._getDirectDatabaseCall = function(query, checkXhr2) {\r
-               if (checkXhr2 && !Jmol.featureDetection.supportsXhr2())\r
-                       return query;\r
-               var pt = 2;\r
-               var db;\r
-               var call = Jmol.db._DirectDatabaseCalls[query.substring(0,pt)];\r
-               if (!call)\r
-                       call = Jmol.db._DirectDatabaseCalls[db = query.substring(0,--pt)];\r
-               if (call) {\r
-                       if (db == ":") {\r
-                               var ql = query.toLowerCase();\r
-                               if (!isNaN(parseInt(query.substring(1)))) {\r
-                                       query = "cid/" + query.substring(1);\r
-                               } else if (ql.indexOf(":smiles:") == 0) {\r
-                                       call += "?POST?smiles=" + query.substring(8);\r
-                                       query = "smiles";\r
-                               } else if (ql.indexOf(":cid:") == 0) {\r
-                                       query = "cid/" + query.substring(5);\r
-                               } else {\r
-                                       if (ql.indexOf(":name:") == 0)\r
-                                               query = query.substring(5);\r
-                                       else if (ql.indexOf(":cas:") == 0)\r
-                                               query = query.substring(4);\r
-                                       query = "name/" + encodeURIComponent(query.substring(pt));\r
-                               }\r
-                       } else {\r
-                               query = encodeURIComponent(query.substring(pt));                \r
-                       }\r
-                       if (call.indexOf("FILENCI") >= 0) {\r
-                               query = query.replace(/\%2F/g, "/");                            \r
-                               query = call.replace(/\%FILENCI/, query);\r
-                       } else {\r
-                               query = call.replace(/\%FILE/, query);\r
-                       }\r
-               }               \r
-               return query;\r
-       }\r
-\r
-       Jmol._getRawDataFromServer = function(database,query,fSuccess,fError,asBase64,noScript){\r
-         // note that this method is now only enabled for "_"\r
-         // server-side processing of database queries was too slow and only useful for \r
-         // the IMAGE option, which has been abandoned.\r
-               var s = \r
-                       "?call=getRawDataFromDatabase&database=" + database + (query.indexOf("?POST?") >= 0 ? "?POST?" : "")\r
-                               + "&query=" + encodeURIComponent(query)\r
-                               + (asBase64 ? "&encoding=base64" : "")\r
-                               + (noScript ? "" : "&script=" + encodeURIComponent(Jmol._getScriptForDatabase(database)));\r
-               return Jmol._contactServer(s, fSuccess, fError);\r
-       }\r
-\r
-       Jmol._checkFileName = function(applet, fileName, isRawRet) {\r
-               if (Jmol._isDatabaseCall(fileName)) {\r
-                       if (isRawRet)\r
-                               Jmol._setQueryTerm(applet, fileName);\r
-                       fileName = Jmol._getDirectDatabaseCall(fileName, true);\r
-                       if (Jmol._isDatabaseCall(fileName)) {\r
-                               // xhr2 not supported (MSIE)\r
-                               fileName = Jmol._getDirectDatabaseCall(fileName, false);\r
-                               isRawRet && (isRawRet[0] = true);\r
-                       }\r
-               }\r
-               return fileName;\r
-       }\r
-       \r
-       Jmol._checkCache = function(applet, fileName, fSuccess) {\r
-               if (applet._cacheFiles && Jmol._fileCache && !fileName.endsWith(".js")) {\r
-                       var data = Jmol._fileCache[fileName];\r
-                       if (data) {\r
-                               System.out.println("using "  + data.length + " bytes of cached data for "  + fileName);\r
-                               fSuccess(data);\r
-                               return null;\r
-                       } else {\r
-                               fSuccess = function(fileName, data) { fSuccess(Jmol._fileCache[fileName] = data) };     \r
-                       }\r
-               }\r
-               return fSuccess;\r
-       }\r
-       \r
-       Jmol._loadFileData = function(applet, fileName, fSuccess, fError){\r
-               var isRaw = [];\r
-               fileName = Jmol._checkFileName(applet, fileName, isRaw);\r
-               fSuccess = Jmol._checkCache(applet, fileName, fSuccess);\r
-               if (isRaw[0]) {\r
-                               Jmol._getRawDataFromServer("_",fileName,fSuccess,fError);   \r
-                               return;\r
-               } \r
-               var info = {\r
-                       type: "GET",\r
-                       dataType: "text",\r
-                       url: fileName,\r
-                       async: Jmol._asynchronous,\r
-                       success: function(a) {Jmol._loadSuccess(a, fSuccess)},\r
-                       error: function() {Jmol._loadError(fError)}\r
-               }\r
-               Jmol._checkAjaxPost(info);\r
-               Jmol._ajax(info);\r
-       }\r
-\r
-       Jmol._getInfoFromDatabase = function(applet, database, query){\r
-               if (database == "====") {\r
-                       var data = Jmol.db._restQueryXml.replace(/QUERY/,query);\r
-\r
-                       var info = {\r
-                               dataType: "text",\r
-                               type: "POST",\r
-                               contentType:"application/x-www-form-urlencoded",\r
-                               url: Jmol.db._restQueryUrl,\r
-                               data: encodeURIComponent(data) + "&req=browser",\r
-                               success: function(data) {Jmol._ajaxDone();Jmol._extractInfoFromRCSB(applet, database, query, data)},\r
-                               error: function() {Jmol._loadError(null)},\r
-                               async: Jmol._asynchronous\r
-                       }\r
-                       return Jmol._ajax(info);\r
-               }   \r
-               query = "?call=getInfoFromDatabase&database=" + database\r
-                               + "&query=" + encodeURIComponent(query);\r
-               return Jmol._contactServer(query, function(data) {Jmol._setInfo(applet, database, data)});\r
-       }\r
-\r
-       Jmol._extractInfoFromRCSB = function(applet, database, query, output) {\r
-               var n = output.length/5;\r
-               if (n == 0)\r
-                       return; \r
-               if (query.length == 4 && n != 1) {\r
-                       var QQQQ = query.toUpperCase();\r
-                       var pt = output.indexOf(QQQQ);\r
-                       if (pt > 0 && "123456789".indexOf(QQQQ.substring(0, 1)) >= 0)\r
-                               output = QQQQ + "," + output.substring(0, pt) + output.substring(pt + 5);\r
-                       if (n > 50)\r
-                               output = output.substring(0, 250);\r
-                       output = output.replace(/\n/g,",");\r
-                       var url = Jmol._restReportUrl.replace(/IDLIST/,output);\r
-                       Jmol._loadFileData(applet, url, function(data) {Jmol._setInfo(applet, database, data) });   \r
-               }\r
-       }\r
-\r
-       Jmol._checkAjaxPost = function(info) {\r
-               var pt = info.url.indexOf("?POST?");\r
-               if (pt > 0) {\r
-                       info.data = info.url.substring(pt + 6);\r
-                       info.url = info.url.substring(0, pt);\r
-                       info.type = "POST";\r
-                       info.contentType = "application/x-www-form-urlencoded";\r
-               }\r
-       }\r
-       Jmol._contactServer = function(data,fSuccess,fError){\r
-               var info = {\r
-                       dataType: "text",\r
-                       type: "GET",\r
-                       url: Jmol._serverUrl + data,\r
-                       success: function(a) {Jmol._loadSuccess(a, fSuccess)},\r
-                       error:function() { Jmol._loadError(fError) },\r
-                       async:fSuccess ? Jmol._asynchronous : false\r
-               }\r
-               Jmol._checkAjaxPost(info);\r
-               return Jmol._ajax(info);\r
-       }\r
-\r
-       Jmol._setQueryTerm = function(applet, query) {\r
-               if (!query || !applet._hasOptions || query.substring(0, 7) == "http://")\r
-                       return;\r
-               if (Jmol._isDatabaseCall(query)) {\r
-                       var database = query.substring(0, 1);\r
-                       query = query.substring(1);\r
-                       if (query.substring(0,1) == database && "=$".indexOf(database) >= 0)\r
-                               query = query.substring(1);\r
-                       var d = Jmol._getElement(applet, "select");\r
-                       if (d && d.options)\r
-                               for (var i = 0; i < d.options.length; i++)\r
-                                       if (d[i].value == database)\r
-                                               d[i].selected = true;\r
-               }\r
-               Jmol.$val(Jmol.$(applet, "query"), query);\r
-       }\r
-\r
-       Jmol._search = function(applet, query, script) {\r
-               arguments.length > 1 || (query = null);\r
-               Jmol._setQueryTerm(applet, query);\r
-               query || (query = Jmol.$val(Jmol.$(applet, "query")))\r
-               if (query.indexOf("!") == 0) {\r
-               // command prompt in this box as well\r
-               // remove exclamation point "immediate" indicator\r
-                       applet._script(query.substring(1));\r
-                       return;\r
-               } \r
-               query && (query = query.replace(/\"/g, ""));\r
-               applet._showInfo(false);\r
-               Jmol._searchMol(applet, query, script, true);\r
-       }\r
-\r
-       Jmol._searchMol = function(applet, query, script, checkView) {\r
-               var database;\r
-               if (Jmol._isDatabaseCall(query)) {\r
-                       database = query.substring(0, 1);\r
-                       query = query.substring(1);\r
-               } else {\r
-                       database = (applet._hasOptions ? Jmol.$val(Jmol.$(applet, "select")) : "$");\r
-               }\r
-               if (database == "=" && query.length == 3)\r
-                       query = "=" + query; // this is a ligand      \r
-               var dm = database + query;\r
-               if (!query || dm.indexOf("?") < 0 && dm == applet._thisJmolModel) {\r
-                       return;    \r
-               }\r
-               applet._thisJmolModel = dm;\r
-               var view;\r
-               if (checkView && applet._viewSet != null && (view = Jmol.View.__findView(applet._viewSet, {chemID:dm})) != null) {\r
-                       Jmol.View.__setView(view, applet, false);\r
-                       return;\r
-               }\r
-\r
-               if (database == "$" || database == ":")\r
-                       applet._jmolFileType = "MOL";\r
-               else if (database == "=")\r
-                       applet._jmolFileType = "PDB";\r
-               applet._searchDatabase(query, database, script);\r
-       }\r
-\r
-       Jmol._searchDatabase = function(applet, query, database, script) {\r
-               applet._showInfo(false);\r
-               if (query.indexOf("?") >= 0) {\r
-                       Jmol._getInfoFromDatabase(applet, database, query.split("?")[0]);\r
-                       return true;\r
-               }\r
-               if (Jmol.db._DirectDatabaseCalls[database]) {\r
-                       applet._loadFile(database + query, script);\r
-                       return true;\r
-               }\r
-               return false;\r
-       }\r
-\r
-       Jmol._syncBinaryOK="?";\r
-\r
-       Jmol._canSyncBinary = function(isSilent) {\r
-               if (Jmol._isAsync) return true;\r
-               if (self.VBArray) return (Jmol._syncBinaryOK = false);\r
-               if (Jmol._syncBinaryOK != "?") return Jmol._syncBinaryOK;\r
-               Jmol._syncBinaryOK = true;\r
-               try {\r
-                       var xhr = new window.XMLHttpRequest();\r
-                       xhr.open( "text", Jmol._ajaxTestSite, false );\r
-                       if (xhr.hasOwnProperty("responseType")) {\r
-                               xhr.responseType = "arraybuffer";\r
-                       } else if (xhr.overrideMimeType) {\r
-                               xhr.overrideMimeType('text/plain; charset=x-user-defined');\r
-                       }\r
-               } catch( e ) {\r
-                       var s = "JSmolCore.js: synchronous binary file transfer is requested but not available";\r
-                       System.out.println(s);\r
-                       if (Jmol._alertNoBinary && !isSilent)\r
-                               alert (s)\r
-                       return Jmol._syncBinaryOK = false;\r
-               }\r
-               return true;  \r
-       }\r
-\r
-       Jmol._binaryTypes = [".gz",".jpg",".gif",".png",".zip",".jmol",".bin",".smol",".spartan",".mrc",".pse", ".map", ".omap"];\r
-\r
-       Jmol._isBinaryUrl = function(url) {\r
-               for (var i = Jmol._binaryTypes.length; --i >= 0;)\r
-                       if (url.indexOf(Jmol._binaryTypes[i]) >= 0) return true;\r
-               return false;\r
-       }\r
-\r
-       Jmol._getFileData = function(fileName, fSuccess, doProcess) {\r
-               // use host-server PHP relay if not from this host\r
-               var isBinary = Jmol._isBinaryUrl(fileName);\r
-               var isPDB = (fileName.indexOf("pdb.gz") >= 0 && fileName.indexOf("http://www.rcsb.org/pdb/files/") == 0);\r
-               var asBase64 = (isBinary && !Jmol._canSyncBinary(isPDB));\r
-               if (asBase64 && isPDB) {\r
-                       // avoid unnecessary binary transfer\r
-                       fileName = fileName.replace(/pdb\.gz/,"pdb");\r
-                       asBase64 = isBinary = false;\r
-               }\r
-               var isPost = (fileName.indexOf("?POST?") >= 0);\r
-               if (fileName.indexOf("file:/") == 0 && fileName.indexOf("file:///") != 0)\r
-                       fileName = "file://" + fileName.substring(5);      /// fixes IE problem\r
-               var isMyHost = (fileName.indexOf("://") < 0 || fileName.indexOf(document.location.protocol) == 0 && fileName.indexOf(document.location.host) >= 0);\r
-               var isDirectCall = Jmol._isDirectCall(fileName);\r
-               //if (fileName.indexOf("http://pubchem.ncbi.nlm.nih.gov/") == 0)isDirectCall = false;\r
-\r
-               var cantDoSynchronousLoad = (!isMyHost && Jmol.$supportsIECrossDomainScripting());\r
-               var data = null;\r
-               if ((!fSuccess || asBase64) && (cantDoSynchronousLoad || asBase64 || !isMyHost && !isDirectCall)) {\r
-                               data = Jmol._getRawDataFromServer("_",fileName, fSuccess, fSuccess, asBase64, true);\r
-               } else {\r
-                       fileName = fileName.replace(/file:\/\/\/\//, "file://"); // opera\r
-                       var info = {dataType:(isBinary ? "binary" : "text"),async:!!fSuccess};\r
-                       if (isPost) {\r
-                               info.type = "POST";\r
-                               info.url = fileName.split("?POST?")[0]\r
-                               info.data = fileName.split("?POST?")[1]\r
-                       } else {\r
-                               info.type = "GET";\r
-                               info.url = fileName;\r
-                       }\r
-                       if (fSuccess) {\r
-                               info.success = function(data) { fSuccess(Jmol._xhrReturn(info.xhr))};\r
-                               info.error = function() { fSuccess(info.xhr.statusText)};\r
-                       }\r
-                       info.xhr = Jmol.$ajax(info);\r
-                       if (!fSuccess) {\r
-                               data = Jmol._xhrReturn(info.xhr);\r
-                       }\r
-               }\r
-               if (!doProcess)\r
-                       return data;\r
-               if (data == null) {\r
-                       data = "";\r
-                       isBinary = false;\r
-               }\r
-               isBinary && (isBinary = Jmol._canSyncBinary(true));\r
-               return (isBinary ? Jmol._strToBytes(data) : JU.SB.newS(data));\r
-       }\r
-       \r
-       Jmol._xhrReturn = function(xhr){\r
-               if (!xhr.responseText || self.Clazz && Clazz.instanceOf(xhr.response, self.ArrayBuffer)) {\r
-                       // Safari or error \r
-                       return xhr.response || xhr.statusText;\r
-               } \r
-               return xhr.responseText;\r
-       }\r
-\r
-       Jmol._isDirectCall = function(url) {\r
-               for (var key in Jmol.db._DirectDatabaseCalls) {\r
-                       if (key.indexOf(".") >= 0 && url.indexOf(key) >= 0)\r
-                               return true;\r
-               }\r
-               return false;\r
-       }\r
-\r
-       Jmol._cleanFileData = function(data) {\r
-               if (data.indexOf("\r") >= 0 && data.indexOf("\n") >= 0) {\r
-                       return data.replace(/\r\n/g,"\n");\r
-               }\r
-               if (data.indexOf("\r") >= 0) {\r
-                       return data.replace(/\r/g,"\n");\r
-               }\r
-               return data;\r
-       };\r
-\r
-       Jmol._getFileType = function(name) {\r
-               var database = name.substring(0, 1);\r
-               if (database == "$" || database == ":")\r
-                       return "MOL";\r
-               if (database == "=")\r
-                       return (name.substring(1,2) == "=" ? "LCIF" : "PDB");\r
-               // just the extension, which must be PDB, XYZ..., CIF, or MOL\r
-               name = name.split('.').pop().toUpperCase();\r
-               return name.substring(0, Math.min(name.length, 3));\r
-       };\r
-\r
-       Jmol._getZ = function(applet, what) {\r
-               return applet && applet._z && applet._z[what] || Jmol._z[what];\r
-       }\r
-       \r
-       Jmol._incrZ = function(applet, what) {\r
-               return applet && applet._z && ++applet._z[what] || ++Jmol._z[what];\r
-       }\r
-       \r
-       Jmol._loadFileAsynchronously = function(fileLoadThread, applet, fileName, appData) {\r
-               if (fileName.indexOf("?") != 0) {\r
-                       // LOAD ASYNC command\r
-                       var fileName0 = fileName;\r
-                       fileName = Jmol._checkFileName(applet, fileName);\r
-                       var fSuccess = function(data) {Jmol._setData(fileLoadThread, fileName, fileName0, data, appData)};\r
-                       fSuccess = Jmol._checkCache(applet, fileName, fSuccess);\r
-                       if (fileName.indexOf("|") >= 0)\r
-                               fileName = fileName.split("|")[0];\r
-                       return (fSuccess == null ? null : Jmol._getFileData(fileName, fSuccess));               \r
-               }\r
-               // we actually cannot suggest a fileName, I believe.\r
-               if (!Jmol.featureDetection.hasFileReader)\r
-                               return fileLoadThread.setData("Local file reading is not enabled in your browser", null, null, appData);\r
-               if (!applet._localReader) {\r
-                       var div = '<div id="ID" style="z-index:'+Jmol._getZ(applet, "fileOpener") + ';position:absolute;background:#E0E0E0;left:10px;top:10px"><div style="margin:5px 5px 5px 5px;"><input type="file" id="ID_files" /><button id="ID_loadfile">load</button><button id="ID_cancel">cancel</button></div><div>'\r
-                       Jmol.$after("#" + applet._id + "_appletdiv", div.replace(/ID/g, applet._id + "_localReader"));\r
-                       applet._localReader = Jmol.$(applet, "localReader");\r
-               }\r
-               Jmol.$appEvent(applet, "localReader_loadfile", "click");\r
-               Jmol.$appEvent(applet, "localReader_loadfile", "click", function(evt) {\r
-                       var file = Jmol.$(applet, "localReader_files")[0].files[0];   \r
-                       var reader = new FileReader();\r
-                       reader.onloadend = function(evt) {\r
-                               if (evt.target.readyState == FileReader.DONE) { // DONE == 2\r
-                                       Jmol.$css(Jmol.$(applet, "localReader"), {display : "none"});\r
-                                       Jmol._setData(fileLoadThread, file.name, file.name, evt.target.result, appData);\r
-                               }\r
-                       };\r
-                       reader.readAsArrayBuffer(file);\r
-               });\r
-               Jmol.$appEvent(applet, "localReader_cancel", "click");\r
-               Jmol.$appEvent(applet, "localReader_cancel", "click", function(evt) {\r
-                       Jmol.$css(Jmol.$(applet, "localReader"), {display: "none"});\r
-                       fileLoadThread.setData(null, null, null, appData);\r
-               });\r
-               Jmol.$css(Jmol.$(applet, "localReader"), {display : "block"});\r
-       }\r
-\r
-  Jmol._setData = function(fileLoadThread, filename, filename0, data, appData) {\r
-       data = Jmol._strToBytes(data);\r
-               if (filename.indexOf(".jdx") >= 0)\r
-                       Jmol.Cache.put("cache://" + filename, data);\r
-               fileLoadThread.setData(filename, filename0, data, appData);\r
-  }\r
-  \r
-       Jmol._toBytes = function(data) {\r
-       if (typeof data == "string") \r
-               return data.getBytes();\r
-       // ArrayBuffer assumed here\r
-       data = new Uint8Array(data);\r
-       var b = Clazz.newByteArray(data.length, 0);\r
-               for (var i = data.length; --i >= 0;)\r
-                       b[i] = data[i];\r
-       return b;\r
-       }\r
-\r
-       Jmol._doAjax = function(url, postOut, dataOut) {\r
-               // called by org.jmol.awtjs2d.JmolURLConnection.doAjax()\r
-               url = url.toString();\r
-\r
-               if (dataOut != null) \r
-                       return Jmol._saveFile(url, dataOut);\r
-               if (postOut)\r
-                       url += "?POST?" + postOut;\r
-               return Jmol._getFileData(url, null, true);\r
-       }\r
-\r
-       // Jmol._localFileSaveFunction --  // do something local here; Maybe try the FileSave interface? return true if successful\r
-        \r
-       Jmol._saveFile = function(filename, data, mimetype, encoding) {\r
-               if (Jmol._localFileSaveFunction && Jmol._localFileSaveFunction(filename, data))\r
-                       return "OK";\r
-               var filename = filename.substring(filename.lastIndexOf("/") + 1);\r
-               mimetype || (mimetype = (filename.indexOf(".pdf") >= 0 ? "application/pdf" \r
-                       : filename.indexOf(".png") >= 0 ? "image/png" \r
-                       : filename.indexOf(".gif") >= 0 ? "image/gif" \r
-                       : filename.indexOf(".jpg") >= 0 ? "image/jpg" : ""));\r
-               var isString = (typeof data == "string");\r
-               if (!isString)\r
-                       data = (JU ? JU : J.util).Base64.getBase64(data).toString();\r
-               encoding || (encoding = (isString ? "" : "base64"));\r
-               var url = Jmol._serverUrl;\r
-               url && url.indexOf("your.server") >= 0 && (url = "");\r
-               if (Jmol._useDataURI || !url) {\r
-                       // Asynchronous output generated using an anchor tag\r
-                       encoding || (data = btoa(data));\r
-                       var a = document.createElement("a");\r
-                       a.href = "data:" + mimetype + ";base64," + data;\r
-                       a.type = mimetype || (mimetype = "text/plain"); \r
-                       a.download = filename;\r
-                       a.target = "_blank";\r
-                               $("body").append(a);\r
-                       a.click();\r
-                       a.remove();             \r
-               } else {\r
-               // Asynchronous outputto be reflected as a download\r
-                       if (!Jmol._formdiv) {\r
-                                       var sform = '<div id="__jsmolformdiv__" style="display:none">\\r
-                                               <form id="__jsmolform__" method="post" target="_blank" action="">\\r
-                                               <input name="call" value="saveFile"/>\\r
-                                               <input id="__jsmolmimetype__" name="mimetype" value=""/>\\r
-                                               <input id="__jsmolencoding__" name="encoding" value=""/>\\r
-                                               <input id="__jsmolfilename__" name="filename" value=""/>\\r
-                                               <textarea id="__jsmoldata__" name="data"></textarea>\\r
-                                               </form>\\r
-                                               </div>'\r
-                               Jmol.$after("body", sform);\r
-                               Jmol._formdiv = "__jsmolform__";\r
-                       }\r
-                       Jmol.$attr(Jmol._formdiv, "action", url + "?" + (new Date()).getMilliseconds());\r
-                       Jmol.$val("__jsmoldata__", data);\r
-                       Jmol.$val("__jsmolfilename__", filename);\r
-                       Jmol.$val("__jsmolmimetype__", mimetype);\r
-                       Jmol.$val("__jsmolencoding__", encoding);\r
-                       Jmol.$submit("__jsmolform__");\r
-                       Jmol.$val("__jsmoldata__", "");\r
-                       Jmol.$val("__jsmolfilename__", "");\r
-               }\r
-               return "OK";\r
-       }\r
-\r
-       Jmol._strToBytes = function(s) {\r
-               if (Clazz.instanceOf(s, self.ArrayBuffer))\r
-                       return Jmol._toBytes(s);\r
-               var b = Clazz.newByteArray(s.length, 0);\r
-               for (var i = s.length; --i >= 0;)\r
-                       b[i] = s.charCodeAt(i) & 0xFF;\r
-               return b;\r
-       }\r
-\r
-       ////////////// applet start-up functionality //////////////\r
-\r
-       Jmol._setConsoleDiv = function (d) {\r
-               if (!self.Clazz)return;\r
-               Clazz.setConsoleDiv(d);\r
-       }\r
-\r
-       Jmol._registerApplet = function(id, applet) {\r
-               return window[id] = Jmol._applets[id] = Jmol._applets[applet] = Jmol._applets[id + "__" + Jmol._syncId + "__"] = applet;\r
-       } \r
-\r
-       Jmol._readyCallback = function (appId,fullId,isReady,javaApplet,javaAppletPanel) {\r
-               appId = appId.split("_object")[0];\r
-    javaAppletPanel || (javaAppletPanel = javaApplet);\r
-               isReady = (isReady.booleanValue ? isReady.booleanValue() : isReady);\r
-               // necessary for MSIE in strict mode -- apparently, we can't call \r
-               // jmol._readyCallback, but we can call Jmol._readyCallback. Go figure...\r
-    var applet = Jmol._applets[appId];\r
-    applet._appletPanel = javaAppletPanel;\r
-    applet._applet = javaApplet;\r
-               Jmol._track(applet._readyCallback(appId, fullId, isReady));\r
-       }\r
-\r
-       Jmol._getWrapper = function(applet, isHeader) {\r
-\r
-                       // id_appletinfotablediv\r
-                       //     id_appletdiv\r
-                       //     id_coverdiv\r
-                       //     id_infotablediv\r
-                       //       id_infoheaderdiv\r
-                       //          id_infoheaderspan\r
-                       //          id_infocheckboxspan\r
-                       //       id_infodiv\r
-\r
-\r
-                       // for whatever reason, without DOCTYPE, with MSIE, "height:auto" does not work, \r
-                       // and the text scrolls off the page.\r
-                       // So I'm using height:95% here.\r
-                       // The table was a fix for MSIE with no DOCTYPE tag to fix the miscalculation\r
-                       // in height of the div when using 95% for height. \r
-                       // But it turns out the table has problems with DOCTYPE tags, so that's out. \r
-                       // The 95% is a compromise that we need until the no-DOCTYPE MSIE solution is found. \r
-                       // (100% does not work with the JME linked applet)\r
-               var s;\r
-               // ... here are just for clarification in this code; they are removed immediately\r
-               if (isHeader) {\r
-                       var img = ""; \r
-                       if (applet._coverImage){\r
-                               var more = " onclick=\"Jmol.coverApplet(ID, false)\" title=\"" + (applet._coverTitle) + "\"";\r
-                               var play = "<image id=\"ID_coverclickgo\" src=\"" + applet._j2sPath + "/img/play_make_live.jpg\" style=\"width:25px;height:25px;position:absolute;bottom:10px;left:10px;"\r
-                                       + "z-index:" + Jmol._getZ(applet, "coverImage")+";opacity:0.5;\"" + more + " />"  \r
-                               img = "<div id=\"ID_coverdiv\" style=\"background-color:red;z-index:" + Jmol._getZ(applet, "coverImage")+";width:100%;height:100%;display:inline;position:absolute;top:0px;left:0px\"><image id=\"ID_coverimage\" src=\""\r
-                                + applet._coverImage + "\" style=\"width:100%;height:100%\"" + more + "/>" + play + "</div>";\r
-                       }\r
-                       var css = Jmol._appletCssText.replace(/\'/g,'"');\r
-                       css = (css.indexOf("style=\"") >= 0 ? css.split("style=\"")[1] : "\" " + css);\r
-                       s = "\\r
-...<div id=\"ID_appletinfotablediv\" style=\"width:Wpx;height:Hpx;position:relative;font-size:14px;text-align:left\">IMG\\r
-......<div id=\"ID_appletdiv\" style=\"z-index:" + Jmol._getZ(applet, "header") + ";width:100%;height:100%;position:absolute;top:0px;left:0px;" + css + ">";\r
-                       var height = applet._height;\r
-                       var width = applet._width;\r
-                       if (typeof height !== "string" || height.indexOf("%") < 0) \r
-                               height += "px";\r
-                       if (typeof width !== "string" || width.indexOf("%") < 0)\r
-                               width += "px";\r
-                       s = s.replace(/IMG/, img).replace(/Hpx/g, height).replace(/Wpx/g, width);\r
-               } else {\r
-                       s = "\\r
-......</div>\\r
-......<div id=\"ID_2dappletdiv\" style=\"position:absolute;width:100%;height:100%;overflow:hidden;display:none\"></div>\\r
-......<div id=\"ID_infotablediv\" style=\"width:100%;height:100%;position:absolute;top:0px;left:0px\">\\r
-.........<div id=\"ID_infoheaderdiv\" style=\"height:20px;width:100%;background:yellow;display:none\"><span id=\"ID_infoheaderspan\"></span><span id=\"ID_infocheckboxspan\" style=\"position:absolute;text-align:right;right:1px;\"><a href=\"javascript:Jmol.showInfo(ID,false)\">[x]</a></span></div>\\r
-.........<div id=\"ID_infodiv\" style=\"position:absolute;top:20px;bottom:0px;width:100%;height:100%;overflow:auto\"></div>\\r
-......</div>\\r
-...</div>";\r
-               }\r
-               return s.replace(/\.\.\./g,"").replace(/[\n\r]/g,"").replace(/ID/g, applet._id);\r
-       }\r
-\r
-       Jmol._documentWrite = function(text) {\r
-               if (Jmol._document) {\r
-                       if (Jmol._isXHTML && !Jmol._XhtmlElement) {\r
-                               var s = document.getElementsByTagName("script");\r
-                               Jmol._XhtmlElement = s.item(s.length - 1);\r
-                               Jmol._XhtmlAppendChild = false;\r
-                       }\r
-                       if (Jmol._XhtmlElement)\r
-                               Jmol._domWrite(text);\r
-                       else\r
-                               Jmol._document.write(text);\r
-               }\r
-               return text;\r
-       }\r
-\r
-       Jmol._domWrite = function(data) {\r
-               var pt = 0\r
-               var Ptr = []\r
-               Ptr[0] = 0\r
-               while (Ptr[0] < data.length) {\r
-                       var child = Jmol._getDomElement(data, Ptr);\r
-                       if (!child)\r
-                               break;\r
-                       if (Jmol._XhtmlAppendChild)\r
-                               Jmol._XhtmlElement.appendChild(child);\r
-                       else\r
-                               Jmol._XhtmlElement.parentNode.insertBefore(child, _jmol.XhtmlElement);\r
-               }\r
-       }\r
-\r
-       Jmol._getDomElement = function(data, Ptr, closetag, lvel) {\r
-\r
-               // there is no "document.write" in XHTML\r
-\r
-               var e = document.createElement("span");\r
-               e.innerHTML = data;\r
-               Ptr[0] = data.length;\r
-\r
-/*\r
-       // unnecessary ?  \r
-\r
-               closetag || (closetag = "");\r
-               lvel || (lvel = 0);\r
-               var pt0 = Ptr[0];\r
-               var pt = pt0;\r
-               while (pt < data.length && data.charAt(pt) != "<") \r
-                       pt++\r
-               if (pt != pt0) {\r
-                       var text = data.substring(pt0, pt);\r
-                       Ptr[0] = pt;\r
-                       return document.createTextNode(text);\r
-               }\r
-               pt0 = ++pt;\r
-               var ch;\r
-               while (pt < data.length && "\n\r\t >".indexOf(ch = data.charAt(pt)) < 0) \r
-                       pt++;\r
-               var tagname = data.substring(pt0, pt);\r
-               var e = (tagname == closetag  || tagname == "/" ? ""\r
-                       : document.createElementNS ? document.createElementNS('http://www.w3.org/1999/xhtml', tagname)\r
-                       : document.createElement(tagname));\r
-               if (ch == ">") {\r
-                       Ptr[0] = ++pt;\r
-                       return e;\r
-               }\r
-               while (pt < data.length && (ch = data.charAt(pt)) != ">") {\r
-                       while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) \r
-                               pt++;\r
-                       pt0 = pt;\r
-                       while (pt < data.length && "\n\r\t =/>".indexOf(ch = data.charAt(pt)) < 0) \r
-                               pt++;\r
-                       var attrname = data.substring(pt0, pt).toLowerCase();\r
-                       if (attrname && ch != "=")\r
-                               e.setAttribute(attrname, "true");\r
-                       while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) \r
-                               pt++;\r
-                       if (ch == "/") {\r
-                               Ptr[0] = pt + 2;\r
-                               return e;\r
-                       } else if (ch == "=") {\r
-                               var quote = data.charAt(++pt);\r
-                               pt0 = ++pt;\r
-                               while (pt < data.length && (ch = data.charAt(pt)) != quote) \r
-                                       pt++;\r
-                               var attrvalue = data.substring(pt0, pt);\r
-                               e.setAttribute(attrname, attrvalue);\r
-                               pt++;\r
-                       }\r
-               }\r
-               Ptr[0] = ++pt;\r
-               while (Ptr[0] < data.length) {\r
-                       var child = Jmol._getDomElement(data, Ptr, "/" + tagname, lvel+1);\r
-                       if (!child)\r
-                               break;\r
-                       e.appendChild(child);\r
-               }\r
-*/\r
-               return e;    \r
-       }\r
-\r
-       Jmol._setObject = function(obj, id, Info) {\r
-               obj._id = id;\r
-               obj.__Info = {};\r
-               Info.z && Info.zIndexBase && (Jmol._z = Jmol._getZOrders(Info.zIndexBase));\r
-               for (var i in Info)\r
-                       obj.__Info[i] = Info[i];\r
-               (obj._z = Info.z) || Info.zIndexBase && (obj._z = obj.__Info.z = Jmol._getZOrders(Info.zIndexBase));\r
-               obj._width = Info.width;\r
-               obj._height = Info.height;\r
-               obj._noscript = !obj._isJava && Info.noscript;\r
-               obj._console = Info.console;\r
-               obj._cacheFiles = !!Info.cacheFiles;\r
-               obj._viewSet = (Info.viewSet == null || obj._isJava ? null : "Set" + Info.viewSet);\r
-               if (obj._viewSet != null) {\r
-                       Jmol.View.__init(obj);\r
-                       obj._currentView = null;\r
-               }\r
-               !Jmol._fileCache && obj._cacheFiles && (Jmol._fileCache = {});\r
-               if (!obj._console)\r
-                       obj._console = obj._id + "_infodiv";\r
-               if (obj._console == "none")\r
-                       obj._console = null;\r
-\r
-               obj._color = (Info.color ? Info.color.replace(/0x/,"#") : "#FFFFFF");\r
-               obj._disableInitialConsole = Info.disableInitialConsole;\r
-               obj._noMonitor = Info.disableJ2SLoadMonitor;\r
-               Jmol._j2sPath && (Info.j2sPath = Jmol._j2sPath);\r
-               obj._j2sPath = Info.j2sPath;\r
-               obj._coverImage = Info.coverImage;\r
-               obj._isCovered = !!obj._coverImage; \r
-               obj._deferApplet = Info.deferApplet || obj._isCovered && obj._isJava; // must do this if covered in Java\r
-               obj._deferUncover = Info.deferUncover && !obj._isJava; // can't do this with Java\r
-               obj._coverScript = Info.coverScript;\r
-               obj._coverTitle = Info.coverTitle;\r
-\r
-               if (!obj._coverTitle)\r
-                       obj._coverTitle = (obj._deferApplet ? "activate 3D model" : "3D model is loading...")\r
-               obj._containerWidth = obj._width + ((obj._width==parseFloat(obj._width))? "px":"");\r
-               obj._containerHeight = obj._height + ((obj._height==parseFloat(obj._height))? "px":"");\r
-               obj._info = "";\r
-               obj._infoHeader = obj._jmolType + ' "' + obj._id + '"'\r
-               obj._hasOptions = Info.addSelectionOptions;\r
-               obj._defaultModel = Info.defaultModel;\r
-               obj._readyScript = (Info.script ? Info.script : "");\r
-               obj._readyFunction = Info.readyFunction;\r
-               if (obj._coverImage && !obj._deferApplet)\r
-                       obj._readyScript += ";javascript " + id + "._displayCoverImage(false)";\r
-               obj._src = Info.src;\r
-\r
-       }\r
-\r
-       Jmol._addDefaultInfo = function(Info, DefaultInfo) {\r
-               for (var x in DefaultInfo)\r
-                       if (typeof Info[x] == "undefined")\r
-                               Info[x] = DefaultInfo[x];\r
-               Jmol._use && (Info.use = Jmol._use);\r
-               if (Info.use.indexOf("SIGNED") >= 0) {\r
-                       if (Info.jarFile.indexOf("Signed") < 0)\r
-                               Info.jarFile = Info.jarFile.replace(/Applet/,"AppletSigned");\r
-                       Info.use = Info.use.replace(/SIGNED/, "JAVA");\r
-                       Info.isSigned = true;\r
-               }\r
-       }\r
-\r
-       Jmol._syncedApplets = [];\r
-       Jmol._syncedCommands = [];\r
-       Jmol._syncedReady = [];\r
-       Jmol._syncReady = false;\r
-       Jmol._isJmolJSVSync = false;\r
-\r
-       Jmol._setReady = function(applet) {\r
-               Jmol._syncedReady[applet] = 1;\r
-               var n = 0;\r
-               for (var i = 0; i < Jmol._syncedApplets.length; i++) {\r
-                       if (Jmol._syncedApplets[i] == applet._id) {\r
-                               Jmol._syncedApplets[i] = applet;\r
-                               Jmol._syncedReady[i] = 1;\r
-                       } else if (!Jmol._syncedReady[i]) {\r
-                               continue;\r
-                       }\r
-                       n++;\r
-               }\r
-               if (n != Jmol._syncedApplets.length)\r
-                       return;\r
-               Jmol._setSyncReady();\r
-       }\r
-\r
-       Jmol._setDestroy = function(applet) {\r
-               //MSIE bug responds to any link click even if it is just a JavaScript call\r
-\r
-               if (Jmol.featureDetection.allowDestroy)\r
-                       Jmol.$windowOn('beforeunload', function () { Jmol._destroy(applet); } );\r
-       }\r
-\r
-       Jmol._destroy = function(applet) {\r
-               try {\r
-                       if (applet._appletPanel) applet._appletPanel.destroy();\r
-                       applet._applet = null;\r
-                       Jmol._unsetMouse(applet._canvas)\r
-                       applet._canvas = null;\r
-                       var n = 0;\r
-                       for (var i = 0; i < Jmol._syncedApplets.length; i++) {\r
-                               if (Jmol._syncedApplets[i] == applet)\r
-                                       Jmol._syncedApplets[i] = null;\r
-                               if (Jmol._syncedApplets[i])\r
-                                       n++;\r
-                       }\r
-                       if (n > 0)\r
-                               return;\r
-                       Jmol._clearVars();\r
-               } catch(e){}\r
-       }\r
-\r
-       ////////////// misc core functionality //////////////\r
-\r
-       Jmol._setSyncReady = function() {\r
-               Jmol._syncReady = true;\r
-               var s = ""\r
-               for (var i = 0; i < Jmol._syncedApplets.length; i++)\r
-                       if (Jmol._syncedCommands[i])\r
-                               s += "Jmol.script(Jmol._syncedApplets[" + i + "], Jmol._syncedCommands[" + i + "]);"\r
-               setTimeout(s, 50);  \r
-       }\r
-\r
-       Jmol._mySyncCallback = function(appFullName,msg) {\r
-               app = Jmol._applets[appFullName];\r
-               if (app._viewSet) {\r
-                       // when can we do this?\r
-//                     if (app._viewType == "JSV" && !app._currentView.JMOL)\r
-                               Jmol.View.updateFromSync(app, msg);\r
-                       return;\r
-               }\r
-               if(!Jmol._syncReady || !Jmol._isJmolJSVSync)\r
-                       return 1; // continue processing and ignore me\r
-               for (var i = 0; i < Jmol._syncedApplets.length; i++) {\r
-                       if (msg.indexOf(Jmol._syncedApplets[i]._syncKeyword) >= 0)\r
-                               Jmol._syncedApplets[i]._syncScript(msg);\r
-               }\r
-               return 0 // prevents further Jmol sync processing \r
-       }              \r
-\r
-       Jmol._getElement = function(applet, what) {\r
-               var d = document.getElementById(applet._id + "_" + what);\r
-               return (d || {});\r
-       } \r
-        \r
-       Jmol._evalJSON = function(s,key){\r
-               s = s + "";\r
-               if(!s)\r
-                       return [];\r
-               if(s.charAt(0) != "{") {\r
-                       if(s.indexOf(" | ") >= 0)\r
-                               s = s.replace(/\ \|\ /g, "\n");\r
-                       return s;\r
-               }\r
-               var A = (new Function( "return " + s ) )();\r
-               return (!A ? null : key && A[key] != undefined ? A[key] : A);\r
-       }\r
-\r
-       Jmol._sortMessages = function(A){\r
-               /*\r
-                * private function\r
-                */\r
-               function _sortKey0(a,b){\r
-                       return (a[0]<b[0]?1:a[0]>b[0]?-1:0);\r
-               }\r
-\r
-               if(!A || typeof (A) != "object")\r
-                       return [];\r
-               var B = [];\r
-               for(var i = A.length - 1; i >= 0; i--)\r
-                       for(var j = 0, jj= A[i].length; j < jj; j++)\r
-                               B[B.length] = A[i][j];\r
-               if(B.length == 0)\r
-                       return;\r
-               B = B.sort(_sortKey0);\r
-               return B;\r
-       }\r
-\r
-       //////////////////// mouse events //////////////////////\r
-\r
-       Jmol._setMouseOwner = function(who, tf) {\r
-               if (who == null || tf)\r
-                       Jmol._mouseOwner = who;\r
-               else if (Jmol._mouseOwner == who)\r
-                       Jmol._mouseOwner = null;\r
-       }\r
-\r
-       Jmol._jsGetMouseModifiers = function(ev) {\r
-               var modifiers = 0;\r
-               switch (ev.button) {\r
-               case 0:\r
-                       modifiers = 16;//J.api.Event.MOUSE_LEFT;\r
-                       break;\r
-               case 1:\r
-                       modifiers = 8;//J.api.Event.MOUSE_MIDDLE;\r
-                       break;\r
-               case 2:\r
-                       modifiers = 4;//J.api.Event.MOUSE_RIGHT;\r
-                       break;\r
-               }\r
-               if (ev.shiftKey)\r
-                       modifiers += 1;//J.api.Event.SHIFT_MASK;\r
-               if (ev.altKey)\r
-                       modifiers += 8;//J.api.Event.ALT_MASK;\r
-               if (ev.ctrlKey)\r
-                       modifiers += 2;//J.api.Event.CTRL_MASK;\r
-               return modifiers;\r
-       }\r
-\r
-       Jmol._jsGetXY = function(canvas, ev) {\r
-               if (!canvas.applet._ready || Jmol._touching && ev.type.indexOf("touch") < 0)\r
-                       return false;\r
-               //ev.preventDefault(); // removed 5/9/2015 -- caused loss of focus on text-box clicking in SwingJS\r
-               var offsets = Jmol.$offset(canvas.id);\r
-               var x, y;\r
-               var oe = ev.originalEvent;\r
-               // drag-drop jQuery event is missing pageX\r
-               ev.pageX || (ev.pageX = oe.pageX);\r
-               ev.pageY || (ev.pageY = oe.pageY);\r
-               Jmol._mousePageX = ev.pageX;\r
-               Jmol._mousePageY = ev.pageY;\r
-               if (oe.targetTouches && oe.targetTouches[0]) {\r
-                       x = oe.targetTouches[0].pageX - offsets.left;\r
-                       y = oe.targetTouches[0].pageY - offsets.top;\r
-               } else if (oe.changedTouches) {\r
-                       x = oe.changedTouches[0].pageX - offsets.left;\r
-                       y = oe.changedTouches[0].pageY - offsets.top;\r
-               } else {\r
-                       x = ev.pageX - offsets.left;\r
-                       y = ev.pageY - offsets.top;\r
-               }\r
-               return (x == undefined ? null : [Math.round(x), Math.round(y), Jmol._jsGetMouseModifiers(ev)]);\r
-       }\r
-\r
-       Jmol._gestureUpdate = function(canvas, ev) {\r
-               ev.stopPropagation();\r
-               ev.preventDefault();\r
-               var oe = ev.originalEvent;\r
-               switch (ev.type) {\r
-               case "touchstart":\r
-                       Jmol._touching = true;\r
-                       break;\r
-               case "touchend":\r
-                       Jmol._touching = false;\r
-                       break;\r
-               }\r
-               if (!oe.touches || oe.touches.length != 2) return false;\r
-               switch (ev.type) {\r
-               case "touchstart":\r
-                       canvas._touches = [[],[]];\r
-                       break;\r
-               case "touchmove":\r
-                       var offsets = Jmol.$offset(canvas.id);\r
-                       var t0 = canvas._touches[0];\r
-                       var t1 = canvas._touches[1];\r
-                       t0.push([oe.touches[0].pageX - offsets.left, oe.touches[0].pageY - offsets.top]);\r
-                       t1.push([oe.touches[1].pageX - offsets.left, oe.touches[1].pageY - offsets.top]);\r
-                       var n = t0.length;\r
-                       if (n > 3) {\r
-                               t0.shift();\r
-                               t1.shift();\r
-                       }\r
-                       if (n >= 2)\r
-                               canvas.applet._processGesture(canvas._touches);\r
-                       break;\r
-               }\r
-               return true;\r
-       }  \r
-\r
-       Jmol._jsSetMouse = function(canvas) {\r
-\r
-    var doIgnore = function(ev) { return (ev.target.className.indexOf("swingjs-ui") >= 0) };\r
-         \r
-               Jmol.$bind(canvas, 'mousedown touchstart', function(ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-                       Jmol._setMouseOwner(canvas, true);\r
-                       ev.stopPropagation();\r
-      var ui = ev.target["data-UI"];\r
-      if (!ui || !ui.handleJSEvent(canvas, 501, ev)) \r
-                       ev.preventDefault();\r
-                       canvas.isDragging = true;\r
-                       if ((ev.type == "touchstart") && Jmol._gestureUpdate(canvas, ev))\r
-                               return !!ui;\r
-                       Jmol._setConsoleDiv(canvas.applet._console);\r
-                       var xym = Jmol._jsGetXY(canvas, ev);\r
-                       if(xym) {\r
-                       if (ev.button != 2)\r
-          Jmol.Swing.hideMenus(canvas.applet);\r
-        canvas.applet._processEvent(501, xym); //java.awt.Event.MOUSE_DOWN\r
-      }\r
-                       return !!ui;\r
-               });\r
-    \r
-               Jmol.$bind(canvas, 'mouseup touchend', function(ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-                       Jmol._setMouseOwner(null);\r
-                       ev.stopPropagation();\r
-      var ui = ev.target["data-UI"];\r
-      if (!ui || !ui.handleJSEvent(canvas, 502, ev))\r
-                       ev.preventDefault();\r
-                       canvas.isDragging = false;\r
-                       if (ev.type == "touchend" && Jmol._gestureUpdate(canvas, ev))\r
-                               return !!ui;\r
-                       var xym = Jmol._jsGetXY(canvas, ev);\r
-                       if(xym)\r
-                       canvas.applet._processEvent(502, xym);//java.awt.Event.MOUSE_UP\r
-                       return !!ui;\r
-               });\r
-    \r
-               Jmol.$bind(canvas, 'mousemove touchmove', function(ev) { // touchmove\r
-      if (doIgnore(ev))\r
-        return true;\r
-                 // defer to console or menu when dragging within this canvas\r
-                       if (Jmol._mouseOwner && Jmol._mouseOwner != canvas && Jmol._mouseOwner.isDragging) {\r
-        if (!Jmol._mouseOwner.mouseMove)\r
-          return true;\r
-                               Jmol._mouseOwner.mouseMove(ev);\r
-                               return false;\r
-                       }\r
-                       return Jmol._drag(canvas, ev);\r
-               });\r
-               \r
-               Jmol._drag = function(canvas, ev) {\r
-      \r
-                       ev.stopPropagation();\r
-                       ev.preventDefault();\r
-      \r
-                       var isTouch = (ev.type == "touchmove");\r
-                       if (isTouch && Jmol._gestureUpdate(canvas, ev))\r
-                               return false;\r
-                       var xym = Jmol._jsGetXY(canvas, ev);\r
-                       if(!xym) return false;\r
-      \r
-                       if (!canvas.isDragging)\r
-                               xym[2] = 0;\r
-\r
-      var ui = ev.target["data-UI"];\r
-      if (canvas.isdragging && (!ui || !ui.handleJSEvent(canvas, 506, ev))) {}\r
-                       canvas.applet._processEvent((canvas.isDragging ? 506 : 503), xym); // java.awt.Event.MOUSE_DRAG : java.awt.Event.MOUSE_MOVE\r
-                       return !!ui;\r
-               }\r
-               \r
-               Jmol.$bind(canvas, 'DOMMouseScroll mousewheel', function(ev) { // Zoom\r
-      if (doIgnore(ev))\r
-        return true;\r
-                       ev.stopPropagation();\r
-                       ev.preventDefault();\r
-                       // Webkit or Firefox\r
-                       canvas.isDragging = false;\r
-                       var oe = ev.originalEvent;\r
-                       var scroll = (oe.detail ? oe.detail : (Jmol.featureDetection.os == "mac" ? 1 : -1) * oe.wheelDelta); // Mac and PC are reverse; but \r
-                       var modifiers = Jmol._jsGetMouseModifiers(ev);\r
-                       canvas.applet._processEvent(-1,[scroll < 0 ? -1 : 1,0,modifiers]);\r
-                       return false;\r
-               });\r
-\r
-               // context menu is fired on mouse down, not up, and it's handled already anyway.\r
-\r
-               Jmol.$bind(canvas, "contextmenu", function() {return false;});\r
-\r
-               Jmol.$bind(canvas, 'mouseout', function(ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-      if (Jmol._mouseOwner && !Jmol._mouseOwner.mouseMove) \r
-        Jmol._setMouseOwner(null);\r
-                       if (canvas.applet._appletPanel)\r
-                               canvas.applet._appletPanel.startHoverWatcher(false);\r
-                       //canvas.isDragging = false;\r
-                       var xym = Jmol._jsGetXY(canvas, ev);\r
-                       if (!xym)\r
-                               return false;\r
-                       //canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP\r
-                       //canvas.applet._processEvent(505, xym);//J.api.Event.MOUSE_EXITED\r
-                       return false;\r
-               });\r
-\r
-               Jmol.$bind(canvas, 'mouseenter', function(ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-                       if (canvas.applet._appletPanel)\r
-                               canvas.applet._appletPanel.startHoverWatcher(true);\r
-                       if (ev.buttons === 0 || ev.which === 0) {\r
-                               canvas.isDragging = false;\r
-                               var xym = Jmol._jsGetXY(canvas, ev);\r
-                               if (!xym)\r
-                                       return false;\r
-                               canvas.applet._processEvent(504, xym);//J.api.Event.MOUSE_ENTERED       \r
-                               canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP\r
-                               return false;\r
-                       }\r
-               });\r
-\r
-       Jmol.$bind(canvas, 'mousemoveoutjsmol', function(evspecial, target, ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-               if (canvas == Jmol._mouseOwner && canvas.isDragging) {\r
-                       return Jmol._drag(canvas, ev);\r
-               }\r
-       });\r
-\r
-               if (canvas.applet._is2D)\r
-                       Jmol.$resize(function() {\r
-                               if (!canvas.applet)\r
-                                       return;\r
-                               canvas.applet._resize();\r
-                       });\r
\r
-               Jmol.$bind('body', 'mouseup touchend', function(ev) {\r
-      if (doIgnore(ev))\r
-        return true;\r
-                       if (canvas.applet)\r
-                               canvas.isDragging = false;\r
-                       Jmol._setMouseOwner(null);\r
-               });\r
-\r
-       }\r
-\r
-       Jmol._jsUnsetMouse = function(canvas) {\r
-               canvas.applet = null;\r
-               Jmol.$bind(canvas, 'mousedown touchstart mousemove touchmove mouseup touchend DOMMouseScroll mousewheel contextmenu mouseout mouseenter', null);\r
-               Jmol._setMouseOwner(null);\r
-       }\r
-\r
-\r
-////// Jmol.Swing interface  for Javascript implementation of Swing dialogs and menus\r
-\r
-Jmol.Swing = {\r
-       // a static class\r
-       count:0,\r
-       menuInitialized:0,\r
-       menuCounter:0,\r
-       htDialogs:{}\r
-};\r
-\r
-(function(Swing) {\r
-\r
-SwingController = Swing; // see javajs.api.SwingController\r
-\r
-Swing.setDraggable = function(Obj) {\r
-       \r
-       var proto = Obj.prototype;\r
-       if (proto.setContainer)\r
-               return;\r
-       \r
-       // for menus, console, and \r
-       proto.setContainer = function(container) {\r
-               this.container = container;\r
-               container.obj = this;\r
-               this.isDragging = false;\r
-               this.ignoreMouse = false;\r
-               var me = this;\r
-               container.bind('mousedown touchstart', function(ev) {\r
-                       if (me.ignoreMouse) {\r
-                               me.ignoreMouse = false;\r
-                               return true;\r
-                       }\r
-                       Jmol._setMouseOwner(me, true);\r
-                       me.isDragging = true;\r
-                       me.pageX = ev.pageX;\r
-                       me.pageY = ev.pageY;\r
-                       return false;\r
-               });\r
-               container.bind('mousemove touchmove', function(ev) {\r
-                       if (me.isDragging && Jmol._mouseOwner == me) {\r
-                               me.mouseMove(ev);\r
-                               return false;\r
-                       }\r
-               });\r
-               container.bind('mouseup touchend', function(ev) {\r
-                       me.mouseUp(ev);\r
-                       Jmol._setMouseOwner(null);\r
-               });\r
-       };\r
-\r
-       proto.mouseUp = function(ev) {\r
-               if (this.isDragging && Jmol._mouseOwner == this) {\r
-                       this.pageX0 += (ev.pageX - this.pageX);\r
-                       this.pageY0 += (ev.pageY - this.pageY);\r
-                       this.isDragging = false;\r
-                       return false;\r
-               }\r
-               Jmol._setMouseOwner(null);\r
-       }\r
-\r
-       proto.setPosition = function() {\r
-               if (Jmol._mousePageX === null) {\r
-                       var id = this.applet._id + "_" + (this.applet._is2D ? "canvas2d" : "canvas");\r
-                       var offsets = Jmol.$offset(id);\r
-                       Jmol._mousePageX = offsets.left;\r
-                       Jmol._mousePageY = offsets.top;\r
-               }\r
-               this.pageX0 = Jmol._mousePageX;\r
-               this.pageY0 = Jmol._mousePageY;\r
-               var pos = { top: Jmol._mousePageY + 'px', left: Jmol._mousePageX + 'px' };\r
-               this.container.css(pos);\r
-       };\r
-\r
-       proto.mouseMove = function(ev) {\r
-               if (this.isDragging && Jmol._mouseOwner == this) {\r
-                       this.timestamp = System.currentTimeMillis(); // used for menu closure\r
-                       var x = this.pageX0 + (ev.pageX - this.pageX);\r
-                       var y = this.pageY0 + (ev.pageY - this.pageY);\r
-      Jmol._mousePageX = x;\r
-      Jmol._mousePageY = y;\r
-                       this.container.css({ top: y + 'px', left: x + 'px' })\r
-               }\r
-       };\r
-\r
-       proto.dragBind = function(isBind) {\r
-               this.applet._ignoreMouse = !isBind;\r
-               this.container.unbind('mousemoveoutjsmol');\r
-               this.container.unbind('touchmoveoutjsmol');\r
-               this.container.unbind('mouseupoutjsmol');\r
-               this.container.unbind('touchendoutjsmol');\r
-               Jmol._setMouseOwner(null);\r
-               if (isBind) {\r
-                       var me = this;\r
-                       this.container.bind('mousemoveoutjsmol touchmoveoutjsmol', function(evspecial, target, ev) {\r
-                               me.mouseMove(ev);\r
-                       });\r
-                       this.container.bind('mouseupoutjsmol touchendoutjsmol', function(evspecial, target, ev) {\r
-                               me.mouseUp(ev);\r
-                       });\r
-               }\r
-       };\r
-}\r
-\r
-// Dialog //\r
-\r
-Swing.JSDialog = function () {\r
-}\r
-\r
-Swing.setDraggable(Swing.JSDialog);\r
-\r
-///// calls from javajs and other Java-derived packages /////\r
-\r
-Swing.getScreenDimensions = function(d) {\r
-       d.width = $(window).width();\r
-       d.height = $(window).height();\r
-}\r
-\r
-Swing.dispose = function(dialog) {\r
-       Jmol.$remove(dialog.id + "_mover");\r
-       delete Swing.htDialogs[dialog.id]\r
-       dialog.container.obj.dragBind(false);\r
-//  var btns = $("#" + dialog.id + " *[id^='J']"); // add descendents with id starting with "J"\r
-//  for (var i = btns.length; --i >= 0;)\r
-//    delete Dialog.htDialogs[btns[i].id]\r
-       //System.out.println("JSmolCore.js: dispose " + dialog.id)\r
-}\r
\r
-Swing.register = function(dialog, type) {\r
-       dialog.id = type + (++Swing.count);\r
-       Swing.htDialogs[dialog.id] = dialog;\r
-       //System.out.println("JSmolCore.js: register " + dialog.id)\r
-\r
-}\r
-\r
-Swing.setDialog = function(dialog) {\r
-       Jmol._setMouseOwner(null);\r
-       Jmol.$remove(dialog.id);\r
-       //System.out.println("removed " + dialog.id)\r
-       var id = dialog.id + "_mover";\r
-       var container = Jmol._$(id);\r
-       var jd;\r
-       //System.out.println("JSmolCore.js: setDialog " + dialog.id);\r
-       if (container[0]) {\r
-               container.html(dialog.html);\r
-               jd = container[0].jd;\r
-       } else {\r
-               Jmol.$after("body","<div id='" + id + "' style='position:absolute;left:0px;top:0px;'>" + dialog.html + "</div>");\r
-               var jd = new Swing.JSDialog();\r
-               container = Jmol._$(id);\r
-               dialog.container = container;\r
-               jd.applet = dialog.manager.vwr.html5Applet;\r
-               jd.setContainer(container);\r
-               jd.dialog = dialog;\r
-               jd.setPosition();  \r
-               jd.dragBind(true);\r
-               container[0].jd = jd; \r
-       }\r
-       Jmol.$bind("#" + dialog.id + " .JButton", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JComboBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JCheckBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JTextField", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JTable", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JScrollPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-       Jmol.$bind("#" + dialog.id + " .JEditorPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });\r
-\r
-}\r
\r
-Swing.setSelected = function(chk) {\r
- Jmol.$prop(chk.id, 'checked', !!chk.selected);\r
-}\r
-\r
-Swing.setSelectedIndex = function(cmb) {\r
- Jmol.$prop(cmb.id, 'selectedIndex', cmb.selectedIndex);\r
-}\r
-\r
-Swing.setText = function(btn) {\r
- Jmol.$prop(btn.id, 'value', btn.text);\r
-}\r
-\r
-Swing.setVisible = function(c) {\r
-       Jmol.$setVisible(c.id, c.visible);\r
-}\r
-\r
-Swing.setEnabled = function(c) {\r
-       Jmol.$setEnabled(c.id, c.enabled);\r
-}\r
-\r
-/// callbacks from the HTML elements ////\r
\r
-Swing.click = function(element, keyEvent) {\r
-       var component = Swing.htDialogs[element.id];\r
-       if (component) {\r
-               //System.out.println("click " + element + " " + component)\r
-               var info = component.toString();\r
-               // table cells will have an id but are not registered\r
-               if (info.indexOf("JCheck") >= 0) {\r
-                               component.selected = element.checked;\r
-               } else if (info.indexOf("JCombo") >= 0) {\r
-                       component.selectedIndex = element.selectedIndex;\r
-               } else if (component.text != null) {  // JButton, JTextField\r
-                       component.text = element.value;\r
-                       if (keyEvent && ((keyEvent.charCode || keyEvent.keyCode) != 13))\r
-                               return;\r
-               }    \r
-       }\r
-       var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];\r
-       var key = (component ? component.name :  dialog.registryKey + "/" + element.id);\r
-       //System.out.println("JSmolCore.js: click " + key); \r
-       dialog.manager.actionPerformed(key);\r
-}\r
-\r
-Swing.setFront = function(dialog) {\r
-  var applet = dialog.manager.vwr.html5Applet;\r
-       if (dialog.zIndex != Jmol._getZ(applet, "dialog"))\r
-        dialog.zIndex = Jmol._incrZ(applet, "dialog");\r
-       dialog.container && ((dialog.container[0] || dialog.container).style.zIndex = dialog.zIndex);\r
-}\r
-\r
-Swing.hideMenus = function(applet) {\r
-       // called from LEFT-DOWN mouse event\r
-       var menus = applet._menus;\r
-       if (menus)\r
-               for (var i in menus)\r
-                       if (menus[i].visible)\r
-                               Swing.hideMenu(menus[i]);\r
-}\r
-\r
-Swing.windowClosing = function(element) {\r
-       var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];\r
-       if (dialog.registryKey) {\r
-               //System.out.println("JSmolCore.js: windowClosing " + dialog.registryKey); \r
-               dialog.manager.processWindowClosing(dialog.registryKey);\r
-       } else {\r
-               //System.out.println("JSmolCore.js: windowClosing " + dialog.title); \r
-               dialog.dispose();\r
-       }\r
-}\r
-\r
-})(Jmol.Swing);\r
-\r
-Jmol._track = function(applet) {\r
-       // this function inserts an iFrame that can be used to track your page's applet use. \r
-       // By default it tracks to a page at St. Olaf College, but you can change that. \r
-       // and you can use\r
-       //\r
-       // delete Jmol._tracker\r
-       //\r
-       // yourself to not have you page execute this \r
-       //\r
-       if (Jmol._tracker){\r
-               try {  \r
-                       var url = Jmol._tracker + "&applet=" + applet._jmolType + "&version=" + Jmol._version \r
-                               + "&appver=" + Jmol.___JmolVersion + "&url=" + encodeURIComponent(document.location.href);\r
-                       var s = '<iframe style="display:none" width="0" height="0" frameborder="0" tabindex="-1" src="' + url + '"></iframe>'\r
-                       Jmol.$after("body", s);\r
-               } catch (e) {\r
-                       // ignore\r
-               }\r
-               delete Jmol._tracker;\r
-       }\r
-       return applet;\r
-}\r
-\r
-Jmol.getProfile = function() {\r
-       window["j2s.doProfile"] = true;\r
-       if (self.Clazz && self.JSON) {\r
-               Clazz._profile || (Clazz._profile = {});\r
-               return Clazz.getProfile();\r
-       }\r
-}\r
-\r
-Jmol._getInChIKey = function(applet, data) {\r
-       if (data.indexOf("MOL=") >= 0)\r
-               data = data.split("MOL=")[1].split("\"")[0];\r
-\r
-}\r
-\r
-Jmol._getAttr = function(s, a) {\r
-       var pt = s.indexOf(a + "=");\r
-       return (pt >= 0 && (pt = s.indexOf('"', pt)) >= 0 \r
-               ? s.substring(pt+1, s.indexOf('"', pt+1)) : null);\r
-}\r
-\r
-Jmol.User = {\r
-       viewUpdatedCallback: null\r
-}\r
-\r
-Jmol.View = {\r
-\r
-// The objective of Jmol.View is to coordinate\r
-// asynchronous applet loading and atom/peak picking\r
-// among any combination of Jmol, JME, and JSV.\r
-// \r
-// basic element is a view object:\r
-//   view = {\r
-//     viewType1: viewRecord1,\r
-//     viewType2: viewRecord2,\r
-//     viewType3: viewRecord3\r
-//   }\r
-// where viewType is one of (Jmol, JME, JSV)\r
-// and a viewRecord is an object\r
-// with elements .chemID, .applet, .data, .script\r
-//\r
-// Jmol.View.sets is a list of cached views[0..n]\r
-// for a given group of applet objects with common Info.viewSet\r
-//\r
-// Bob Hanson 1/22/2014 7:05:38 AM\r
-\r
-       count: 0,\r
-       applets: {},\r
-       sets: {}\r
-};\r
-\r
-(function(View) {\r
-\r
-// methods called from other modules have no "_" in their name\r
-\r
-View.updateView = function(applet, Info, _View_updateView) {\r
-       // Info.chemID, Info.data, possibly Info.viewID if no chemID\r
-       // return from applet after asynchronous download of new data\r
-       if (applet._viewSet == null)\r
-               return;\r
-       Info.chemID || (applet._searchQuery = null);\r
-       Info.data || (Info.data = "N/A");\r
-       Info.type = applet._viewType;\r
-       if((applet._currentView = View.__findView(applet._viewSet, Info)) == null)\r
-               applet._currentView = View.__createViewSet(applet._viewSet, Info.chemID, Info.viewID || Info.chemID);\r
-       applet._currentView[Info.type].data = Info.data;\r
-       applet._currentView[Info.type].smiles = applet._getSmiles();\r
-       if (Jmol.User.viewUpdatedCallback)\r
-               Jmol.User.viewUpdatedCallback(applet, "updateView");\r
-       View.__setView(applet._currentView, applet, false);\r
-}\r
-\r
-View.updateFromSync = function(applet, msg) {\r
-       applet._updateMsg = msg;\r
-       var id = Jmol._getAttr(msg, "sourceID") || Jmol._getAttr(msg, "file");\r
-       if (!id)\r
-               return;\r
-       var view = View.__findView(applet._viewSet, {viewID:id});\r
-       if (view == null)\r
-               return Jmol.updateView(applet, msg); // JSV has been updated internally\r
-       if (view != applet._currentView)\r
-               View.__setView(view, applet, true);\r
-       var A = ((id = Jmol._getAttr(msg, "atoms")) && msg.indexOf("selectionhalos ON") >= 0  \r
-               ? eval("[" + id + "]") : []);\r
-       setTimeout(function(){if (applet._currentView == view)View.updateAtomPick(applet, A)}, 10);\r
-       if (Jmol.User.viewUpdatedCallback)\r
-               Jmol.User.viewUpdatedCallback(applet, "updateFromSync");\r
-}\r
-\r
-View.updateAtomPick = function(applet, A, _View_updateAtomPick) {\r
-       var view = applet._currentView;\r
-       if (view == null)\r
-               return;\r
-       for (var viewType in view) {\r
-               if (viewType != "info" && view[viewType].applet != applet)\r
-                       view[viewType].applet._updateAtomPick(A);\r
-       }\r
-       if (Jmol.User.viewUpdatedCallback)\r
-               Jmol.User.viewUpdatedCallback(applet, "updateAtomPick");\r
-}\r
-\r
-View.dumpViews = function(setID) {\r
-       var views = View.sets[setID];\r
-       if (!views)\r
-         return;\r
-       var s = "View set " + setID + ":\n";\r
-       var applets = View.applets[setID];\r
-       for (var i in applets)\r
-               s += "\napplet " + applets[i]._id\r
-                       + " currentView=" + (applets[i]._currentView ? applets[i]._currentView.info.viewID : null);\r
-       for (var i = views.length; --i >= 0;) {\r
-               var view = views[i];\r
-               s += "\n\n<b>view=" + i \r
-                       + " viewID=" + view.info.viewID \r
-                       + " chemID=" + view.info.chemID + "</b>\n"\r
-               var v;\r
-               for (var viewType in view) \r
-                       if (viewType != "info")\r
-                               s += "\nview=" + i + " type=" + viewType + " applet=" \r
-                                       + ((v = view[viewType]).applet ? v.applet._id : null) \r
-                                       + " SMILES=" + v.smiles + "\n"\r
-                                       + " atomMap=" + JSON.stringify(v.atomMap) + "\n"\r
-                                       + " data=\n" + v.data + "\n"\r
-       }\r
-       return s\r
-}\r
-\r
-\r
-// methods starting with "__" are "private" to JSmolCore.js\r
-\r
-View.__init = function(applet) {\r
-  var set = applet._viewSet;\r
-       var a = View.applets;\r
-       a[set] || (a[set] = {});\r
-       a[set][applet._viewType] = applet;\r
-}\r
-\r
-View.__findView = function(set, Info) {\r
-       var views = View.sets[set];\r
-       if (views == null)\r
-               views = View.sets[set] = [];\r
-       for (var i = views.length; --i >= 0;) {\r
-               var view = views[i];\r
-               if (Info.viewID) {\r
-                       if (view.info.viewID == Info.viewID)\r
-                               return view;\r
-               } else if (Info.chemID != null && Info.chemID == view.info.chemID) {\r
-                               return view;\r
-               } else {\r
-                       for (var viewType in view) { \r
-                               if (viewType != "info") {\r
-                                       if (Info.data != null && view[viewType].data != null ? Info.data == view[viewType].data \r
-                                               : Info.type == viewType)\r
-                                                       return view;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-       return null;  \r
-}\r
-\r
-View.__createViewSet = function(set, chemID, viewID, _createViewSet) {\r
-       View.count++;\r
-       var view = {info:{chemID: chemID, viewID: viewID || "model_" + View.count}};\r
-\r
-       for (var id in Jmol._applets) {\r
-               var a = Jmol._applets[id];\r
-               if (a._viewSet == set)\r
-                       view[a._viewType] = {applet:a, data: null};\r
-       }\r
-       View.sets[set].push(view);\r
-       return view;\r
-}\r
-\r
-View.__setView = function(view, applet, isSwitch, _setView) {\r
-       // called from Jmol._searchMol and Jmol.View.setCurrentView \r
-       // to notify the applets in the set that there may be new data for them\r
-       // skip the originating applet itself and cases where the data has not changed.\r
-       // stop at first null data, because that will initiate some sort of asynchronous\r
-       // call that will be back here afterward.\r
-\r
-       for (var viewType in view) {\r
-                       if (viewType == "info") \r
-                               continue;\r
-               var rec = view[viewType];\r
-               var a = rec.applet;\r
-               var modified = (isSwitch || a != null && a._molData == "<modified>");\r
-\r
-               if (a == null || a == applet && !modified)\r
-                       continue; // may be a mol3d required by JSV but not having a corresponding applet\r
-               var wasNull = (rec.data == null);\r
-               var haveView = (a._currentView != null);\r
-               a._currentView = view; \r
-               if (haveView && view[viewType].data == rec.data && !wasNull & !modified)\r
-                       continue;\r
-               a._loadModelFromView(view);\r
-               if (wasNull)\r
-                       break;\r
-       }\r
-\r
-       // Either all are taken care of or one was null,\r
-       // in which case we have started an asynchronous\r
-       // process to get the data, and we can quit here.\r
-       // In either case, we are done.\r
-}\r
-\r
-}) (Jmol.View);\r
-\r
-Jmol.Cache = {fileCache: {}};\r
-\r
-Jmol.Cache.get = function(filename) {\r
-       return Jmol.Cache.fileCache[filename];\r
-}\r
-\r
-Jmol.Cache.put = function(filename, data) {\r
-  Jmol.Cache.fileCache[filename] = data;\r
-}\r
-\r
-       Jmol.Cache.setDragDrop = function(me) {\r
-               Jmol.$appEvent(me, "appletdiv", "dragover", function(e) {\r
-                       e = e.originalEvent;\r
-                       e.stopPropagation();\r
-                       e.preventDefault();\r
-                       e.dataTransfer.dropEffect = 'copy';\r
-               });\r
-               Jmol.$appEvent(me, "appletdiv", "drop", function(e) {\r
-                       var oe = e.originalEvent;\r
-                       oe.stopPropagation();\r
-                       oe.preventDefault();\r
-                       var file = oe.dataTransfer.files[0];\r
-                       if (file == null) {\r
-                               // FF and Chrome will drop an image here\r
-                               // but it will be only a URL, not an actual file. \r
-                               try {\r
-                                 file = "" + oe.dataTransfer.getData("text");\r
-                                 if (file.indexOf("file:/") == 0 || file.indexOf("http:/") == 0) {\r
-                                       me._scriptLoad(file);\r
-                                       return;\r
-                               }\r
-                               } catch(e) {\r
-                                 return;\r
-                               }\r
-                         // some other format\r
-                         return;\r
-                       }\r
-                       // MSIE will drop an image this way, though, and load it!\r
-                       var reader = new FileReader();\r
-                       reader.onloadend = function(evt) {\r
-                               if (evt.target.readyState == FileReader.DONE) {\r
-                                       var cacheName = "cache://DROP_" + file.name;\r
-                                       var bytes = Jmol._toBytes(evt.target.result);\r
-                                       if (!cacheName.endsWith(".spt"))\r
-                                               me._appletPanel.cacheFileByName("cache://DROP_*",false);\r
-                                       if (me._viewType == "JSV" || cacheName.endsWith(".jdx")) // shared by Jmol and JSV\r
-                                               Jmol.Cache.put(cacheName, bytes);\r
-                                       else\r
-                                               me._appletPanel.cachePut(cacheName, bytes);\r
-                                       var xym = Jmol._jsGetXY(me._canvas, e);\r
-                                       if(xym && (!me._appletPanel.setStatusDragDropped || me._appletPanel.setStatusDragDropped(0, xym[0], xym[1], cacheName))) {\r
-                                               me._appletPanel.openFileAsyncSpecial(cacheName, 1);\r
-                                       }\r
-                               }\r
-                       };\r
-                       reader.readAsArrayBuffer(file);\r
-               });\r
-       }\r
-  \r
-})(Jmol, jQuery);\r
+// JSmolCore.js -- Jmol core capability 
+
+// allows Jmol applets to be created on a page with more flexibility and extendability
+// provides an object-oriented interface for JSpecView and syncing of Jmol/JSpecView
+
+// see JSmolApi.js for public user-interface. All these are private functions
+
+// BH 5/30/2015 9:33:12 AM adds class swingjs-ui to ignore 
+// BH 5/9/2015 3:38:52 PM adds data-ignoreMouse attribute for JTextField
+// BH 3/30/2015 9:46:53 PM adds JSAppletPanel for ready callback
+// BH 12/6/2014 3:32:54 PM Jmol.setAppletCss() broken
+// BH 9/13/2014 2:15:51 PM embedded JSME loads from SEARCH when Jmol should 
+// BH 8/14/2014 2:52:38 PM drag-drop cache should not be cleared if SPT file is dropped
+// BH 8/5/2014 6:39:54 AM unnecessary messages about binary for PDB finally removed
+// BH 8/4/2014 5:30:00 AM automatically switch to no document after page loading
+// BH 8/2/2014 5:22:40 PM drag-drop broken in JSmol/HTML5 
+// BH 7/23/2014 5:34:08 PM setting a parameter such as readyFunction to null stops file loading
+// BH 7/3/2014 12:30:28 AM lost drag-drop of models
+// BH 7/2/2014 4:47:55 AM adding pdbe.org to direct database calls
+// BH 5/30/2014 7:20:07 AM better dragging for console and menu
+// BH 4/27/2014 6:31:52 PM allows _USE=SIGNED HTML5 as well as _USE=JAVA HTML5
+// BH 3/8/2014 5:50:51 PM adds support for dataURI download in FF and Chrome
+// BH 3/8/2014 8:43:10 AM moves PubChem access to https
+// BH 3/4/2014 8:40:15 PM adds Jmol.Cache for JSV/Jmol sharing files
+// BH 2/10/2014 10:07:14 AM added Info.z and Info.zIndexBase
+// BH 2/9/2014 9:56:06 PM updated JSmolCore.js with option to extend Viewer with code PRIOR to loading Viewer classes
+// BH 2/6/2014 8:46:25 AM disabled Jmol._tracker for localhost and 127.x.x.x 
+// BH 1/29/2014 8:02:23 AM Jmol.View and Info.viewSet
+// BH 1/21/2014 12:06:59 PM adding Jmol.Info.cacheFiles (applet, true/false) and applet._cacheFiles and Jmol._fileCache
+// BH 1/13/2014 2:12:38 PM adding "http://www.nmrdb.org/tools/jmol/predict.php":"%URL", to _DirectDatabaseCalls
+// BH 12/21/2013 6:38:35 PM applet sync broken
+// BH 12/6/2013 6:18:32 PM cover.htm and coverImage fix
+// BH 12/4/2013 7:44:26 PM fix for JME independent search box
+// BH 12/3/2013 6:30:08 AM fix for ready function returning Boolean instead of boolean in HTML5 version
+// BH 11/30/2013 10:31:37 AM added type:"GET" for jQuery.ajax() requests instead of using defaults
+// BH 11/30/2013 10:31:37 AM added cache:true for jQuery.ajax() requests; can override with cache:"NO", not cache:false
+// BH 11/28/2013 11:09:27 AM added Jmol._alertNoBinary:true
+// BH 11/26/2013 8:19:55 PM fix !xxxx search commmand entry and stop MSIE from duplicating command
+// BH 11/25/2013 7:38:31 AM adds Jmol._tracker: option for GoogleAnalytics tracking
+// BH 11/25/2013 7:39:03 AM adds URL options _J2S=  _JAR=  _USE=
+// BH 11/23/2013 10:51:37 PM  adds JNLP support for local applet
+// BH 11/2/2013 12:05:11 PM JSmolJSME fixes; https access fixed
+// BH 10/31/2013 7:50:06 PM Jmol.Dialog as SwingController; Jmol._mouseOwner added
+// BH 10/19/2013 7:05:04 AM adding Jmol._ajaxCall for Error Contacting Server; database POST method enabled
+// BH 10/17/2013 1:40:51 PM  adding javajs/swing and Jmol.Dialog
+// BH 9/30/2013 6:42:24 PM: pdb.gz switch  pdb should only be for www.rcsb.org
+// BH 9/17/2013 10:17:51 AM: asynchronous file reading and saving
+// BH 8/16/2013 12:02:20 PM: JSmoljQueryExt.js pulled out
+// BH 8/16/2013 12:02:20 PM: Jmol._touching used properly
+
+// BH 3/22/2013 5:53:02 PM: Adds noscript option, JSmol.min.core.js
+// BH 1/17/2013 5:20:44 PM: Fixed problem with console not getting initial position if no first click
+// 1/13/2013 BH: Fixed MSIE not-reading-local-files problem.
+// 11/28/2012 BH: Fixed MacOS Safari binary ArrayBuffer problem
+// 11/21/2012 BH: restructuring of files as JS... instead of J...
+// 11/20/2012 BH: MSIE9 cannot do a synchronous file load cross-domain. See Jmol._getFileData
+// 11/4/2012 BH: RCSB REST format change "<structureId>" to "<dimStructure.structureId>"
+// 9/13/2012 BH: JmolCore.js changes for JSmol doAjax() method -- _3ata()
+// 6/12/2012 BH: JmolApi.js: adds Jmol.setInfo(applet, info, isShown) -- third parameter optional 
+// 6/12/2012 BH: JmolApi.js: adds Jmol.getInfo(applet) 
+// 6/12/2012 BH: JmolApplet.js: Fixes for MSIE 8
+// 6/5/2012  BH: fixes problem with Jmol "javascript" command not working and getPropertyAsArray not working
+// 6/4/2012  BH: corrects problem with MSIE requiring mouse-hover to activate applet
+// 5/31/2012 BH: added JSpecView interface and api -- see JmolJSV.js
+//               also changed "jmolJarPath" to just "jarPath"
+//               jmolJarFile->jarFile, jmolIsSigned->isSigned, jmolReadyFunction->readyFunction
+//               also corrects a double-loading issue
+// 5/14/2012 BH: added AJAX queue for ChemDoodle option with multiple canvases 
+// 8/12/2012 BH: adds support for MSIE xdr cross-domain request (jQuery.iecors.js)
+
+       // BH 4/25 -- added text option. setAppletCss(null, "style=\"xxxx\"")
+       // note that since you must add the style keyword, this can be used to add any attribute to these tags, not just css. 
+
+// required/optional libraries (preferably in the following order):
+
+//    jquery/jquery.js     -- at least jQuery.1.9
+//    js/JSmoljQueryext.js -- required for binary file transfer; otherwise standard jQuery should be OK
+//    js/JSmolCore.js      -- required
+//    js/j2sjmol.js        -- required
+//    js/JSmol.js          -- required
+//    js/JSmolApplet.js    -- required; internal functions for _Applet and _Image; must be after JSmolCore
+//    js/JSmolControls.js  -- optional; internal functions for buttons, links, menus, etc.; must be after JSmolCore
+//    js/JSmolConsole.js   -- optional; for the pop-up console
+//    js/JSmolApi.js       -- required; all user functions; must be after JSmolCore
+//    js/JSmolTHREE.js     -- optional; WebGL library required for JSmolGLmol.js
+//    js/JSmolGLmol.js     -- optional; WebGL version of JSmol.
+//    js/JSmolJME.js       -- optional; JSME (2D editor)
+//    jsme/jsme/jsme.nocache.js   --  required for JSME 
+//    js/JSmolMenu.js      -- optional; required for menuing in JSV
+//    js/JSmolJSV.js       -- optional; for creating and interacting with a JSpecView applet 
+
+// most of these will be loaded automatically, and for most installations, all you need is JSmol.min.js
+
+
+// Allows Jmol-like objects to be displayed on Java-challenged (iPad/iPhone)
+// or applet-challenged (Android/iPhone) platforms, with automatic switching to 
+
+// For your installation, you should consider putting JmolData.jar and jsmol.php 
+// on your own server. Nothing more than these two files is needed on the server, and this 
+// allows more options for MSIE and Chrome when working with cross-domain files (such as RCSB or pubChem) 
+
+// The NCI and RCSB databases are accessed via direct AJAX if available (xhr2/xdr).
+
+
+if(typeof(jQuery)=="undefined") alert ("Note -- JSmoljQuery is required for JSmol, but it's not defined.")
+
+// An example of how to extend Jmol with code PRIOR to JSmolCore.js or JSmol.min.js follows:
+//
+// 
+//     Jmol = {
+//     z:3000,
+//             extend: function(what, obj) {if (what == "viewer") { obj._testing = true } }
+//     }
+
+self.Jmol || (Jmol = {});
+
+if (!Jmol._version)
+Jmol = (function(document) {
+       var z=Jmol.z || 9000;
+       var getZOrders = function(z) {
+               return {
+                       rear:z++,
+                       header:z++,
+                       main:z++,
+                       image:z++,
+                       front:z++,
+                       fileOpener:z++,
+                       coverImage:z++,
+                       dialog:z++, // could be several of these, JSV only
+                       menu:z+90000, // way front
+                       console:z+91000, // even more front
+      consoleImage:z+91001, // bit more front; increments
+                       monitorZIndex:z+99999 // way way front
+               }
+       };
+       var j = {
+               _version: "$Date: 2015-04-26 10:57:08 -0500 (Sun, 26 Apr 2015) $", // svn.keywords:lastUpdated
+               _alertNoBinary: true,
+               // this url is used to Google Analytics tracking of Jmol use. You may remove it or modify it if you wish. 
+               _allowedJmolSize: [25, 2048, 300],   // min, max, default (pixels)
+               /*  By setting the Jmol.allowedJmolSize[] variable in the webpage
+                               before calling Jmol.getApplet(), limits for applet size can be overriden.
+                               2048 standard for GeoWall (http://geowall.geo.lsa.umich.edu/home.html)
+               */
+               _appletCssClass: "",
+               _appletCssText: "",
+               _fileCache: null, // enabled by Jmol.setFileCaching(applet, true/false)
+               _jarFile: null,  // can be set in URL using _JAR=
+               _j2sPath: null,  // can be set in URL using _J2S=
+               _use: null,      // can be set in URL using _USE=
+               _j2sLoadMonitorOpacity: 90, // initial opacity for j2s load monitor message
+               _applets: {},
+               _asynchronous: true,
+               _ajaxQueue: [],
+               _getZOrders: getZOrders,
+               _z:getZOrders(z),
+               _debugCode: true,  // set false in process of minimization
+               db: {
+                       _databasePrefixes: "$=:",
+                       _fileLoadScript: ";if (_loadScript = '' && defaultLoadScript == '' && _filetype == 'Pdb') { select protein or nucleic;cartoons Only;color structure; select * };",
+                       _nciLoadScript: ";n = ({molecule=1}.length < {molecule=2}.length ? 2 : 1); select molecule=n;display selected;center selected;",
+                       _pubChemLoadScript: "",
+                       _DirectDatabaseCalls:{
+                               // these sites are known to implement access-control-allow-origin * 
+                               "cactus.nci.nih.gov": "%URL", 
+                               "www.rcsb.org": "%URL",
+                               "pdbe.org": "%URL", 
+                               "www.ebi.ac.uk": "%URL", 
+                               "wwwdev.ebi.ac.uk": "%URL", 
+                               "pubchem.ncbi.nlm.nih.gov":"%URL",
+                               "http://www.nmrdb.org/tools/jmol/predict.php":"%URL",
+                               "$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf&get3d=True",
+                               "$$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf",
+                               "=": "http://www.rcsb.org/pdb/files/%FILE.pdb",
+                               "*": "http://www.ebi.ac.uk/pdbe/entry-files/download/%FILE.cif",
+                               "==": "http://www.rcsb.org/pdb/files/ligand/%FILE.cif",
+                               ":": "http://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/%FILE/SDF?record_type=3d"
+                       },
+                       _restQueryUrl: "http://www.rcsb.org/pdb/rest/search",
+                       _restQueryXml: "<orgPdbQuery><queryType>org.pdb.query.simple.AdvancedKeywordQuery</queryType><description>Text Search</description><keywords>QUERY</keywords></orgPdbQuery>",
+                       _restReportUrl: "http://www.pdb.org/pdb/rest/customReport?pdbids=IDLIST&customReportColumns=structureId,structureTitle"
+               },
+               _debugAlert: false,
+               _document: document,
+               _isXHTML: false,
+               _lastAppletID: null,
+               _mousePageX: null,
+               _mouseOwner: null,
+               _serverUrl: "http://your.server.here/jsmol.php",
+               _syncId: ("" + Math.random()).substring(3),
+               _touching: false,
+               _XhtmlElement: null,
+               _XhtmlAppendChild: false
+       }
+       
+       var ref = document.location.href.toLowerCase();
+       j._httpProto = (ref.indexOf("https") == 0 ? "https://" : "http://"); 
+       j._isFile = (ref.indexOf("file:") == 0);
+       j._ajaxTestSite = j._httpProto + "google.com";
+       var isLocal = (j._isFile || ref.indexOf("http://localhost") == 0 || ref.indexOf("http://127.") == 0);
+       j._tracker = (j._httpProto == "http://" && !isLocal && 'http://chemapps.stolaf.edu/jmol/JmolTracker.htm?id=UA-45940799-1');
+       
+       j._isChrome = (navigator.userAgent.toLowerCase().indexOf("chrome") >= 0);
+       j._isSafari = (!j._isChrome && navigator.userAgent.toLowerCase().indexOf("safari") >= 0);
+       j._isMsie = (window.ActiveXObject !== undefined);
+       j._useDataURI = !j._isSafari && !j._isMsie; // safari may be OK here -- untested
+
+       for(var i in Jmol) j[i] = Jmol[i]; // allows pre-definition
+       return j;
+})(document, Jmol);
+
+
+(function (Jmol, $) {
+
+// this library is organized into the following sections:
+
+       // jQuery interface 
+       // protected variables
+       // feature detection
+       // AJAX-related core functionality
+       // applet start-up functionality
+       // misc core functionality
+       // mouse events
+
+
+       ////////////////////// jQuery interface ///////////////////////
+
+       // hooks to jQuery -- if you have a different AJAX tool, feel free to adapt.
+       // There should be no other references to jQuery in all the JSmol libraries.
+
+       // automatically switch to returning HTML after the page is loaded
+       $(document).ready(function(){ Jmol._document = null });
+
+       Jmol.$ = function(objectOrId, subdiv) {
+               // if a subdiv, then return $("#objectOrId._id_subdiv") 
+               // or if no subdiv, then just $(objectOrId)
+               if (objectOrId == null)alert (subdiv + arguments.callee.caller.toString());
+                       return $(subdiv ? "#" + objectOrId._id + "_" + subdiv : objectOrId);
+       } 
+
+       Jmol._$ = function(id) {
+               // either the object or $("#" + id)
+               return (typeof id == "string" ? $("#" + id) : id);
+       }
+
+       /// special functions:
+
+       Jmol.$ajax = function (info) {
+               Jmol._ajaxCall = info.url;
+               info.cache = (info.cache != "NO");
+               if (info.url.indexOf("http://pubchem.ncbi.nlm.nih") == 0)
+                       info.url = "https://" + info.url.substring(7);
+                       // fluke at pubchem --- requires https now from all pages 3/8/2014
+               // don't let jQuery add $_=... to URL unless we 
+               // use cache:"NO"; other packages might use $.ajaxSetup() to set this to cache:false
+               return $.ajax(info);
+       }
+
+       Jmol._getNCIInfo = function(identifier, what, fCallback) {
+               return Jmol._getFileData("http://cactus.nci.nih.gov/chemical/structure/"+identifier +"/" + (what == "name" ? "names" : what));
+       }
+       
+
+
+       Jmol.$appEvent = function(app, subdiv, evt, f) {
+               var o = Jmol.$(app, subdiv); 
+               o.off(evt) && f && o.on(evt, f);
+       }   
+
+       Jmol.$resize = function (f) {
+               return $(window).resize(f);
+       }
+
+       //// full identifier expected (could be "body", for example):
+
+       Jmol.$after = function (what, s) {
+               return $(what).after(s);
+       }
+
+       Jmol.$append = function (what, s) {
+               return $(what).append(s);
+       }
+
+       Jmol.$bind = function(what, list, f) {
+               return (f ? $(what).bind(list, f) : $(what).unbind(list));
+       }
+
+       Jmol.$closest = function(what, d) {
+               return $(what).closest(d);
+       }
+       
+       Jmol.$get = function(what, i) {
+       return $(what).get(i);
+       }
+       // element id expected
+                        
+       Jmol.$documentOff = function(evt, id) {
+               return $(document).off(evt, "#" + id);
+       }
+
+       Jmol.$documentOn = function(evt, id, f) {
+               return $(document).on(evt, "#" + id, f);
+       }
+
+       Jmol.$getAncestorDiv = function(id, className) {
+               return $("div." + className + ":has(#" + id + ")")[0];
+       }
+
+       Jmol.$supportsIECrossDomainScripting = function() {
+               return $.support.iecors;
+       }
+
+       //// element id or jQuery object expected:
+
+       Jmol.$attr = function (id, a, val) {
+               return Jmol._$(id).attr(a, val);
+       }
+
+       Jmol.$css = function(id, style) {
+               return Jmol._$(id).css(style);
+       }
+        
+       Jmol.$find = function(id, d) {
+               return Jmol._$(id).find(d);
+       }
+       
+       Jmol.$focus = function(id) {
+               return Jmol._$(id).focus();
+       }
+
+       Jmol.$html = function(id, html) {
+               return Jmol._$(id).html(html);
+       }
+        
+       Jmol.$offset = function(id) {
+               return Jmol._$(id).offset();
+       }
+
+       Jmol.$windowOn = function(evt, f) {
+               return $(window).on(evt, f);
+       }
+
+       Jmol.$prop = function(id, p, val) {
+               var o = Jmol._$(id);
+               return (arguments.length == 3 ? o.prop(p, val) : o.prop(p));
+       }
+
+       Jmol.$remove = function(id) {
+               return Jmol._$(id).remove();
+       }
+
+       Jmol.$scrollTo = function (id, n) {
+               var o = Jmol._$(id);
+               return o.scrollTop(n < 0 ? o[0].scrollHeight : n);
+       }
+
+       Jmol.$setEnabled = function(id, b) {
+               return Jmol._$(id).attr("disabled", b ? null : "disabled");  
+       }
+
+       Jmol.$setSize = function(id, w, h) {
+               return Jmol._$(id).width(w).height(h);
+       }
+
+       Jmol.$setVisible = function(id, b) {
+               var o = Jmol._$(id);
+               return (b ? o.show() : o.hide());  
+       }
+
+       Jmol.$submit = function(id) {
+               return Jmol._$(id).submit();
+       }
+
+       Jmol.$val = function (id, v) {
+               var o = Jmol._$(id);
+               return (arguments.length == 1 ? o.val() : o.val(v));
+       }
+
+       ////////////// protected variables ///////////
+
+
+       Jmol._clearVars = function() {
+               // only on page closing -- appears to improve garbage collection
+
+               delete jQuery;
+               delete $;
+               delete Jmol;
+               delete SwingController;
+               delete J;
+               delete JM;
+               delete JS;
+               delete JSV;
+               delete JU;
+               delete JV;
+               delete java;
+               delete javajs;
+               delete Clazz;
+               delete c$; // used in p0p; could be gotten rid of
+       }
+
+       ////////////// feature detection ///////////////
+
+       Jmol.featureDetection = (function(document, window) {
+
+               var features = {};
+               features.ua = navigator.userAgent.toLowerCase()
+
+               features.os = (function(){
+                       var osList = ["linux","unix","mac","win"]
+                       var i = osList.length;
+
+                       while (i--){
+                               if (features.ua.indexOf(osList[i])!=-1) return osList[i]
+                       }
+                       return "unknown";
+               })();
+
+               features.browser = function(){
+                       var ua = features.ua;
+                       var browserList = ["konqueror","webkit","omniweb","opera","webtv","icab","msie","mozilla"];
+                       for (var i = 0; i < browserList.length; i++)
+                       if (ua.indexOf(browserList[i])>=0) 
+                               return browserList[i];
+                       return "unknown";
+               }
+               features.browserName = features.browser();
+               features.browserVersion= parseFloat(features.ua.substring(features.ua.indexOf(features.browserName)+features.browserName.length+1));
+               features.supportsXhr2 = function() {return ($.support.cors || $.support.iecors)}
+               features.allowDestroy = (features.browserName != "msie");
+               features.allowHTML5 = (features.browserName != "msie" || navigator.appVersion.indexOf("MSIE 8") < 0);
+               features.getDefaultLanguage = function() {
+                       return navigator.language || navigator.userLanguage || "en-US";
+               };
+
+               features._webGLtest = 0;
+
+               features.supportsWebGL = function() {
+               if (!Jmol.featureDetection._webGLtest) { 
+                       var canvas;
+                       Jmol.featureDetection._webGLtest = ( 
+                               window.WebGLRenderingContext 
+                                       && ((canvas = document.createElement("canvas")).getContext("webgl") 
+                               || canvas.getContext("experimental-webgl")) ? 1 : -1);
+               }
+               return (Jmol.featureDetection._webGLtest > 0);
+       };
+
+       features.supportsLocalization = function() {
+               //<meta charset="utf-8">                                     
+               var metas = document.getElementsByTagName('meta'); 
+               for (var i= metas.length; --i >= 0;) 
+                       if (metas[i].outerHTML.toLowerCase().indexOf("utf-8") >= 0) return true;
+               return false;
+               };
+
+       features.supportsJava = function() {
+               if (!Jmol.featureDetection._javaEnabled) {
+                       if (Jmol._isMsie) {
+                               if (!navigator.javaEnabled()) {
+                                       Jmol.featureDetection._javaEnabled = -1;
+                               } else {
+                                       //more likely -- would take huge testing
+                                       Jmol.featureDetection._javaEnabled = 1;
+                               }
+                       } else {
+                               Jmol.featureDetection._javaEnabled = (navigator.javaEnabled() && (!navigator.mimeTypes || navigator.mimeTypes["application/x-java-applet"]) ? 1 : -1);
+                       }
+               }
+               return (Jmol.featureDetection._javaEnabled > 0);
+       };
+
+       features.compliantBrowser = function() {
+               var a = !!document.getElementById;
+               var os = features.os;
+               // known exceptions (old browsers):
+               if (features.browserName == "opera" && features.browserVersion <= 7.54 && os == "mac"
+                       || features.browserName == "webkit" && features.browserVersion < 125.12
+                       || features.browserName == "msie" && os == "mac"
+                       || features.browserName == "konqueror" && features.browserVersion <= 3.3
+               ) a = false;
+               return a;
+       }
+
+       features.isFullyCompliant = function() {
+               return features.compliantBrowser() && features.supportsJava();
+       }
+
+       features.useIEObject = (features.os == "win" && features.browserName == "msie" && features.browserVersion >= 5.5);
+       features.useHtml4Object = (features.browserName == "mozilla" && features.browserVersion >= 5) ||
+               (features.browserName == "opera" && features.browserVersion >= 8) ||
+               (features.browserName == "webkit"/* && features.browserVersion >= 412.2 && features.browserVersion < 500*/); // 500 is a guess; required for 536.3
+
+       features.hasFileReader = (window.File && window.FileReader);
+
+       return features;
+
+})(document, window);
+
+
+               ////////////// AJAX-related core functionality //////////////
+
+       Jmol._ajax = function(info) {
+               if (!info.async) {
+                       return Jmol.$ajax(info).responseText;
+               }
+               Jmol._ajaxQueue.push(info)
+               if (Jmol._ajaxQueue.length == 1)
+                       Jmol._ajaxDone()
+       }
+       Jmol._ajaxDone = function() {
+               var info = Jmol._ajaxQueue.shift();
+               info && Jmol.$ajax(info);
+       }
+
+       Jmol._grabberOptions = [
+               ["$", "NCI(small molecules)"],
+               [":", "PubChem(small molecules)"],
+               ["=", "RCSB(macromolecules)"],
+               ["*", "PDBe(macromolecules)"]
+       ];
+
+       Jmol._getGrabberOptions = function(applet) {
+               // feel free to adjust this look to anything you want
+               if (Jmol._grabberOptions.length == 0)
+                       return ""
+
+
+               var s = '<input type="text" id="ID_query" onfocus="jQuery(this).select()" onkeypress="if(13==event.which){Jmol._applets[\'ID\']._search();return false}" size="32" value="" />';
+               var b = '<button id="ID_submit" onclick="Jmol._applets[\'ID\']._search()">Search</button></nobr>'
+               if (Jmol._grabberOptions.length == 1) {
+                       s = '<nobr>' + s + '<span style="display:none">';
+                       b = '</span>' + b;
+               } else {
+                       s += '<br /><nobr>'
+               }
+               s += '<select id="ID_select">'
+               for (var i = 0; i < Jmol._grabberOptions.length; i++) {
+                       var opt = Jmol._grabberOptions[i];
+                       s += '<option value="' + opt[0] + '" ' + (i == 0 ? 'selected' : '') + '>' + opt[1] + '</option>';
+               }
+               s = (s + '</select>' + b).replace(/ID/g, applet._id);
+               return '<br />' + s;
+       }
+
+       Jmol._getScriptForDatabase = function(database) {
+               return (database == "$" ? Jmol.db._nciLoadScript : database == ":" ? Jmol.db._pubChemLoadScript : Jmol.db._fileLoadScript);
+       }
+
+        //   <dataset><record><structureId>1BLU</structureId><structureTitle>STRUCTURE OF THE 2[4FE-4S] FERREDOXIN FROM CHROMATIUM VINOSUM</structureTitle></record><record><structureId>3EUN</structureId><structureTitle>Crystal structure of the 2[4Fe-4S] C57A ferredoxin variant from allochromatium vinosum</structureTitle></record></dataset>
+
+       Jmol._setInfo = function(applet, database, data) {
+               var info = [];
+               var header = "";
+               if (data.indexOf("ERROR") == 0)
+                       header = data;
+               else
+                       switch (database) {
+                       case "=":
+                               var S = data.split("<dimStructure.structureId>");
+                               var info = ["<table>"];
+                               for (var i = 1; i < S.length; i++) {
+                                       info.push("<tr><td valign=top><a href=\"javascript:Jmol.search(" + applet._id + ",'=" + S[i].substring(0, 4) + "')\">" + S[i].substring(0, 4) + "</a></td>");
+                                       info.push("<td>" + S[i].split("Title>")[1].split("</")[0] + "</td></tr>");
+                               }
+                               info.push("</table>");
+                               header = (S.length - 1) + " matches";
+                               break;      
+                       case "$": // NCI
+                       case ":": // pubChem
+                       break;
+                       default:
+                               return;
+               }
+               applet._infoHeader = header;
+               applet._info = info.join("");
+               applet._showInfo(true);
+       }
+
+       Jmol._loadSuccess = function(a, fSuccess) {
+               if (!fSuccess)
+                       return;
+               Jmol._ajaxDone();
+               fSuccess(a);
+       }
+
+       Jmol._loadError = function(fError){
+               Jmol._ajaxDone();
+               Jmol.say("Error connecting to server: " + Jmol._ajaxCall);  
+               null!=fError&&fError()
+       }
+
+       Jmol._isDatabaseCall = function(query) {
+               return (Jmol.db._databasePrefixes.indexOf(query.substring(0, 1)) >= 0);
+       }
+
+       
+       Jmol._getDirectDatabaseCall = function(query, checkXhr2) {
+               if (checkXhr2 && !Jmol.featureDetection.supportsXhr2())
+                       return query;
+               var pt = 2;
+               var db;
+               var call = Jmol.db._DirectDatabaseCalls[query.substring(0,pt)];
+               if (!call)
+                       call = Jmol.db._DirectDatabaseCalls[db = query.substring(0,--pt)];
+               if (call) {
+                       if (db == ":") {
+                               var ql = query.toLowerCase();
+                               if (!isNaN(parseInt(query.substring(1)))) {
+                                       query = "cid/" + query.substring(1);
+                               } else if (ql.indexOf(":smiles:") == 0) {
+                                       call += "?POST?smiles=" + query.substring(8);
+                                       query = "smiles";
+                               } else if (ql.indexOf(":cid:") == 0) {
+                                       query = "cid/" + query.substring(5);
+                               } else {
+                                       if (ql.indexOf(":name:") == 0)
+                                               query = query.substring(5);
+                                       else if (ql.indexOf(":cas:") == 0)
+                                               query = query.substring(4);
+                                       query = "name/" + encodeURIComponent(query.substring(pt));
+                               }
+                       } else {
+                               query = encodeURIComponent(query.substring(pt));                
+                       }
+                       if (call.indexOf("FILENCI") >= 0) {
+                               query = query.replace(/\%2F/g, "/");                            
+                               query = call.replace(/\%FILENCI/, query);
+                       } else {
+                               query = call.replace(/\%FILE/, query);
+                       }
+               }               
+               return query;
+       }
+
+       Jmol._getRawDataFromServer = function(database,query,fSuccess,fError,asBase64,noScript){
+         // note that this method is now only enabled for "_"
+         // server-side processing of database queries was too slow and only useful for 
+         // the IMAGE option, which has been abandoned.
+               var s = 
+                       "?call=getRawDataFromDatabase&database=" + database + (query.indexOf("?POST?") >= 0 ? "?POST?" : "")
+                               + "&query=" + encodeURIComponent(query)
+                               + (asBase64 ? "&encoding=base64" : "")
+                               + (noScript ? "" : "&script=" + encodeURIComponent(Jmol._getScriptForDatabase(database)));
+               return Jmol._contactServer(s, fSuccess, fError);
+       }
+
+       Jmol._checkFileName = function(applet, fileName, isRawRet) {
+               if (Jmol._isDatabaseCall(fileName)) {
+                       if (isRawRet)
+                               Jmol._setQueryTerm(applet, fileName);
+                       fileName = Jmol._getDirectDatabaseCall(fileName, true);
+                       if (Jmol._isDatabaseCall(fileName)) {
+                               // xhr2 not supported (MSIE)
+                               fileName = Jmol._getDirectDatabaseCall(fileName, false);
+                               isRawRet && (isRawRet[0] = true);
+                       }
+               }
+               return fileName;
+       }
+       
+       Jmol._checkCache = function(applet, fileName, fSuccess) {
+               if (applet._cacheFiles && Jmol._fileCache && !fileName.endsWith(".js")) {
+                       var data = Jmol._fileCache[fileName];
+                       if (data) {
+                               System.out.println("using "  + data.length + " bytes of cached data for "  + fileName);
+                               fSuccess(data);
+                               return null;
+                       } else {
+                               fSuccess = function(fileName, data) { fSuccess(Jmol._fileCache[fileName] = data) };     
+                       }
+               }
+               return fSuccess;
+       }
+       
+       Jmol._loadFileData = function(applet, fileName, fSuccess, fError){
+               var isRaw = [];
+               fileName = Jmol._checkFileName(applet, fileName, isRaw);
+               fSuccess = Jmol._checkCache(applet, fileName, fSuccess);
+               if (isRaw[0]) {
+                               Jmol._getRawDataFromServer("_",fileName,fSuccess,fError);   
+                               return;
+               } 
+               var info = {
+                       type: "GET",
+                       dataType: "text",
+                       url: fileName,
+                       async: Jmol._asynchronous,
+                       success: function(a) {Jmol._loadSuccess(a, fSuccess)},
+                       error: function() {Jmol._loadError(fError)}
+               }
+               Jmol._checkAjaxPost(info);
+               Jmol._ajax(info);
+       }
+
+       Jmol._getInfoFromDatabase = function(applet, database, query){
+               if (database == "====") {
+                       var data = Jmol.db._restQueryXml.replace(/QUERY/,query);
+
+                       var info = {
+                               dataType: "text",
+                               type: "POST",
+                               contentType:"application/x-www-form-urlencoded",
+                               url: Jmol.db._restQueryUrl,
+                               data: encodeURIComponent(data) + "&req=browser",
+                               success: function(data) {Jmol._ajaxDone();Jmol._extractInfoFromRCSB(applet, database, query, data)},
+                               error: function() {Jmol._loadError(null)},
+                               async: Jmol._asynchronous
+                       }
+                       return Jmol._ajax(info);
+               }   
+               query = "?call=getInfoFromDatabase&database=" + database
+                               + "&query=" + encodeURIComponent(query);
+               return Jmol._contactServer(query, function(data) {Jmol._setInfo(applet, database, data)});
+       }
+
+       Jmol._extractInfoFromRCSB = function(applet, database, query, output) {
+               var n = output.length/5;
+               if (n == 0)
+                       return; 
+               if (query.length == 4 && n != 1) {
+                       var QQQQ = query.toUpperCase();
+                       var pt = output.indexOf(QQQQ);
+                       if (pt > 0 && "123456789".indexOf(QQQQ.substring(0, 1)) >= 0)
+                               output = QQQQ + "," + output.substring(0, pt) + output.substring(pt + 5);
+                       if (n > 50)
+                               output = output.substring(0, 250);
+                       output = output.replace(/\n/g,",");
+                       var url = Jmol._restReportUrl.replace(/IDLIST/,output);
+                       Jmol._loadFileData(applet, url, function(data) {Jmol._setInfo(applet, database, data) });   
+               }
+       }
+
+       Jmol._checkAjaxPost = function(info) {
+               var pt = info.url.indexOf("?POST?");
+               if (pt > 0) {
+                       info.data = info.url.substring(pt + 6);
+                       info.url = info.url.substring(0, pt);
+                       info.type = "POST";
+                       info.contentType = "application/x-www-form-urlencoded";
+               }
+       }
+       Jmol._contactServer = function(data,fSuccess,fError){
+               var info = {
+                       dataType: "text",
+                       type: "GET",
+                       url: Jmol._serverUrl + data,
+                       success: function(a) {Jmol._loadSuccess(a, fSuccess)},
+                       error:function() { Jmol._loadError(fError) },
+                       async:fSuccess ? Jmol._asynchronous : false
+               }
+               Jmol._checkAjaxPost(info);
+               return Jmol._ajax(info);
+       }
+
+       Jmol._setQueryTerm = function(applet, query) {
+               if (!query || !applet._hasOptions || query.substring(0, 7) == "http://")
+                       return;
+               if (Jmol._isDatabaseCall(query)) {
+                       var database = query.substring(0, 1);
+                       query = query.substring(1);
+                       if (query.substring(0,1) == database && "=$".indexOf(database) >= 0)
+                               query = query.substring(1);
+                       var d = Jmol._getElement(applet, "select");
+                       if (d && d.options)
+                               for (var i = 0; i < d.options.length; i++)
+                                       if (d[i].value == database)
+                                               d[i].selected = true;
+               }
+               Jmol.$val(Jmol.$(applet, "query"), query);
+       }
+
+       Jmol._search = function(applet, query, script) {
+               arguments.length > 1 || (query = null);
+               Jmol._setQueryTerm(applet, query);
+               query || (query = Jmol.$val(Jmol.$(applet, "query")))
+               if (query.indexOf("!") == 0) {
+               // command prompt in this box as well
+               // remove exclamation point "immediate" indicator
+                       applet._script(query.substring(1));
+                       return;
+               } 
+               query && (query = query.replace(/\"/g, ""));
+               applet._showInfo(false);
+               Jmol._searchMol(applet, query, script, true);
+       }
+
+       Jmol._searchMol = function(applet, query, script, checkView) {
+               var database;
+               if (Jmol._isDatabaseCall(query)) {
+                       database = query.substring(0, 1);
+                       query = query.substring(1);
+               } else {
+                       database = (applet._hasOptions ? Jmol.$val(Jmol.$(applet, "select")) : "$");
+               }
+               if (database == "=" && query.length == 3)
+                       query = "=" + query; // this is a ligand      
+               var dm = database + query;
+               if (!query || dm.indexOf("?") < 0 && dm == applet._thisJmolModel) {
+                       return;    
+               }
+               applet._thisJmolModel = dm;
+               var view;
+               if (checkView && applet._viewSet != null && (view = Jmol.View.__findView(applet._viewSet, {chemID:dm})) != null) {
+                       Jmol.View.__setView(view, applet, false);
+                       return;
+               }
+
+               if (database == "$" || database == ":")
+                       applet._jmolFileType = "MOL";
+               else if (database == "=")
+                       applet._jmolFileType = "PDB";
+               applet._searchDatabase(query, database, script);
+       }
+
+       Jmol._searchDatabase = function(applet, query, database, script) {
+               applet._showInfo(false);
+               if (query.indexOf("?") >= 0) {
+                       Jmol._getInfoFromDatabase(applet, database, query.split("?")[0]);
+                       return true;
+               }
+               if (Jmol.db._DirectDatabaseCalls[database]) {
+                       applet._loadFile(database + query, script);
+                       return true;
+               }
+               return false;
+       }
+
+       Jmol._syncBinaryOK="?";
+
+       Jmol._canSyncBinary = function(isSilent) {
+               if (Jmol._isAsync) return true;
+               if (self.VBArray) return (Jmol._syncBinaryOK = false);
+               if (Jmol._syncBinaryOK != "?") return Jmol._syncBinaryOK;
+               Jmol._syncBinaryOK = true;
+               try {
+                       var xhr = new window.XMLHttpRequest();
+                       xhr.open( "text", Jmol._ajaxTestSite, false );
+                       if (xhr.hasOwnProperty("responseType")) {
+                               xhr.responseType = "arraybuffer";
+                       } else if (xhr.overrideMimeType) {
+                               xhr.overrideMimeType('text/plain; charset=x-user-defined');
+                       }
+               } catch( e ) {
+                       var s = "JSmolCore.js: synchronous binary file transfer is requested but not available";
+                       System.out.println(s);
+                       if (Jmol._alertNoBinary && !isSilent)
+                               alert (s)
+                       return Jmol._syncBinaryOK = false;
+               }
+               return true;  
+       }
+
+       Jmol._binaryTypes = [".gz",".jpg",".gif",".png",".zip",".jmol",".bin",".smol",".spartan",".mrc",".pse", ".map", ".omap"];
+
+       Jmol._isBinaryUrl = function(url) {
+               for (var i = Jmol._binaryTypes.length; --i >= 0;)
+                       if (url.indexOf(Jmol._binaryTypes[i]) >= 0) return true;
+               return false;
+       }
+
+       Jmol._getFileData = function(fileName, fSuccess, doProcess) {
+               // use host-server PHP relay if not from this host
+               var isBinary = Jmol._isBinaryUrl(fileName);
+               var isPDB = (fileName.indexOf("pdb.gz") >= 0 && fileName.indexOf("http://www.rcsb.org/pdb/files/") == 0);
+               var asBase64 = (isBinary && !Jmol._canSyncBinary(isPDB));
+               if (asBase64 && isPDB) {
+                       // avoid unnecessary binary transfer
+                       fileName = fileName.replace(/pdb\.gz/,"pdb");
+                       asBase64 = isBinary = false;
+               }
+               var isPost = (fileName.indexOf("?POST?") >= 0);
+               if (fileName.indexOf("file:/") == 0 && fileName.indexOf("file:///") != 0)
+                       fileName = "file://" + fileName.substring(5);      /// fixes IE problem
+               var isMyHost = (fileName.indexOf("://") < 0 || fileName.indexOf(document.location.protocol) == 0 && fileName.indexOf(document.location.host) >= 0);
+               var isDirectCall = Jmol._isDirectCall(fileName);
+               //if (fileName.indexOf("http://pubchem.ncbi.nlm.nih.gov/") == 0)isDirectCall = false;
+
+               var cantDoSynchronousLoad = (!isMyHost && Jmol.$supportsIECrossDomainScripting());
+               var data = null;
+               if ((!fSuccess || asBase64) && (cantDoSynchronousLoad || asBase64 || !isMyHost && !isDirectCall)) {
+                               data = Jmol._getRawDataFromServer("_",fileName, fSuccess, fSuccess, asBase64, true);
+               } else {
+                       fileName = fileName.replace(/file:\/\/\/\//, "file://"); // opera
+                       var info = {dataType:(isBinary ? "binary" : "text"),async:!!fSuccess};
+                       if (isPost) {
+                               info.type = "POST";
+                               info.url = fileName.split("?POST?")[0]
+                               info.data = fileName.split("?POST?")[1]
+                       } else {
+                               info.type = "GET";
+                               info.url = fileName;
+                       }
+                       if (fSuccess) {
+                               info.success = function(data) { fSuccess(Jmol._xhrReturn(info.xhr))};
+                               info.error = function() { fSuccess(info.xhr.statusText)};
+                       }
+                       info.xhr = Jmol.$ajax(info);
+                       if (!fSuccess) {
+                               data = Jmol._xhrReturn(info.xhr);
+                       }
+               }
+               if (!doProcess)
+                       return data;
+               if (data == null) {
+                       data = "";
+                       isBinary = false;
+               }
+               isBinary && (isBinary = Jmol._canSyncBinary(true));
+               return (isBinary ? Jmol._strToBytes(data) : JU.SB.newS(data));
+       }
+       
+       Jmol._xhrReturn = function(xhr){
+               if (!xhr.responseText || self.Clazz && Clazz.instanceOf(xhr.response, self.ArrayBuffer)) {
+                       // Safari or error 
+                       return xhr.response || xhr.statusText;
+               } 
+               return xhr.responseText;
+       }
+
+       Jmol._isDirectCall = function(url) {
+               for (var key in Jmol.db._DirectDatabaseCalls) {
+                       if (key.indexOf(".") >= 0 && url.indexOf(key) >= 0)
+                               return true;
+               }
+               return false;
+       }
+
+       Jmol._cleanFileData = function(data) {
+               if (data.indexOf("\r") >= 0 && data.indexOf("\n") >= 0) {
+                       return data.replace(/\r\n/g,"\n");
+               }
+               if (data.indexOf("\r") >= 0) {
+                       return data.replace(/\r/g,"\n");
+               }
+               return data;
+       };
+
+       Jmol._getFileType = function(name) {
+               var database = name.substring(0, 1);
+               if (database == "$" || database == ":")
+                       return "MOL";
+               if (database == "=")
+                       return (name.substring(1,2) == "=" ? "LCIF" : "PDB");
+               // just the extension, which must be PDB, XYZ..., CIF, or MOL
+               name = name.split('.').pop().toUpperCase();
+               return name.substring(0, Math.min(name.length, 3));
+       };
+
+       Jmol._getZ = function(applet, what) {
+               return applet && applet._z && applet._z[what] || Jmol._z[what];
+       }
+       
+       Jmol._incrZ = function(applet, what) {
+               return applet && applet._z && ++applet._z[what] || ++Jmol._z[what];
+       }
+       
+       Jmol._loadFileAsynchronously = function(fileLoadThread, applet, fileName, appData) {
+               if (fileName.indexOf("?") != 0) {
+                       // LOAD ASYNC command
+                       var fileName0 = fileName;
+                       fileName = Jmol._checkFileName(applet, fileName);
+                       var fSuccess = function(data) {Jmol._setData(fileLoadThread, fileName, fileName0, data, appData)};
+                       fSuccess = Jmol._checkCache(applet, fileName, fSuccess);
+                       if (fileName.indexOf("|") >= 0)
+                               fileName = fileName.split("|")[0];
+                       return (fSuccess == null ? null : Jmol._getFileData(fileName, fSuccess));               
+               }
+               // we actually cannot suggest a fileName, I believe.
+               if (!Jmol.featureDetection.hasFileReader)
+                               return fileLoadThread.setData("Local file reading is not enabled in your browser", null, null, appData);
+               if (!applet._localReader) {
+                       var div = '<div id="ID" style="z-index:'+Jmol._getZ(applet, "fileOpener") + ';position:absolute;background:#E0E0E0;left:10px;top:10px"><div style="margin:5px 5px 5px 5px;"><input type="file" id="ID_files" /><button id="ID_loadfile">load</button><button id="ID_cancel">cancel</button></div><div>'
+                       Jmol.$after("#" + applet._id + "_appletdiv", div.replace(/ID/g, applet._id + "_localReader"));
+                       applet._localReader = Jmol.$(applet, "localReader");
+               }
+               Jmol.$appEvent(applet, "localReader_loadfile", "click");
+               Jmol.$appEvent(applet, "localReader_loadfile", "click", function(evt) {
+                       var file = Jmol.$(applet, "localReader_files")[0].files[0];   
+                       var reader = new FileReader();
+                       reader.onloadend = function(evt) {
+                               if (evt.target.readyState == FileReader.DONE) { // DONE == 2
+                                       Jmol.$css(Jmol.$(applet, "localReader"), {display : "none"});
+                                       Jmol._setData(fileLoadThread, file.name, file.name, evt.target.result, appData);
+                               }
+                       };
+                       reader.readAsArrayBuffer(file);
+               });
+               Jmol.$appEvent(applet, "localReader_cancel", "click");
+               Jmol.$appEvent(applet, "localReader_cancel", "click", function(evt) {
+                       Jmol.$css(Jmol.$(applet, "localReader"), {display: "none"});
+                       fileLoadThread.setData(null, null, null, appData);
+               });
+               Jmol.$css(Jmol.$(applet, "localReader"), {display : "block"});
+       }
+
+  Jmol._setData = function(fileLoadThread, filename, filename0, data, appData) {
+       data = Jmol._strToBytes(data);
+               if (filename.indexOf(".jdx") >= 0)
+                       Jmol.Cache.put("cache://" + filename, data);
+               fileLoadThread.setData(filename, filename0, data, appData);
+  }
+  
+       Jmol._toBytes = function(data) {
+       if (typeof data == "string") 
+               return data.getBytes();
+       // ArrayBuffer assumed here
+       data = new Uint8Array(data);
+       var b = Clazz.newByteArray(data.length, 0);
+               for (var i = data.length; --i >= 0;)
+                       b[i] = data[i];
+       return b;
+       }
+
+       Jmol._doAjax = function(url, postOut, dataOut) {
+               // called by org.jmol.awtjs2d.JmolURLConnection.doAjax()
+               url = url.toString();
+
+               if (dataOut != null) 
+                       return Jmol._saveFile(url, dataOut);
+               if (postOut)
+                       url += "?POST?" + postOut;
+               return Jmol._getFileData(url, null, true);
+       }
+
+       // Jmol._localFileSaveFunction --  // do something local here; Maybe try the FileSave interface? return true if successful
+        
+       Jmol._saveFile = function(filename, data, mimetype, encoding) {
+               if (Jmol._localFileSaveFunction && Jmol._localFileSaveFunction(filename, data))
+                       return "OK";
+               var filename = filename.substring(filename.lastIndexOf("/") + 1);
+               mimetype || (mimetype = (filename.indexOf(".pdf") >= 0 ? "application/pdf" 
+                       : filename.indexOf(".png") >= 0 ? "image/png" 
+                       : filename.indexOf(".gif") >= 0 ? "image/gif" 
+                       : filename.indexOf(".jpg") >= 0 ? "image/jpg" : ""));
+               var isString = (typeof data == "string");
+               if (!isString)
+                       data = (JU ? JU : J.util).Base64.getBase64(data).toString();
+               encoding || (encoding = (isString ? "" : "base64"));
+               var url = Jmol._serverUrl;
+               url && url.indexOf("your.server") >= 0 && (url = "");
+               if (Jmol._useDataURI || !url) {
+                       // Asynchronous output generated using an anchor tag
+                       encoding || (data = btoa(data));
+                       var a = document.createElement("a");
+                       a.href = "data:" + mimetype + ";base64," + data;
+                       a.type = mimetype || (mimetype = "text/plain"); 
+                       a.download = filename;
+                       a.target = "_blank";
+                               $("body").append(a);
+                       a.click();
+                       a.remove();             
+               } else {
+               // Asynchronous outputto be reflected as a download
+                       if (!Jmol._formdiv) {
+                                       var sform = '<div id="__jsmolformdiv__" style="display:none">\
+                                               <form id="__jsmolform__" method="post" target="_blank" action="">\
+                                               <input name="call" value="saveFile"/>\
+                                               <input id="__jsmolmimetype__" name="mimetype" value=""/>\
+                                               <input id="__jsmolencoding__" name="encoding" value=""/>\
+                                               <input id="__jsmolfilename__" name="filename" value=""/>\
+                                               <textarea id="__jsmoldata__" name="data"></textarea>\
+                                               </form>\
+                                               </div>'
+                               Jmol.$after("body", sform);
+                               Jmol._formdiv = "__jsmolform__";
+                       }
+                       Jmol.$attr(Jmol._formdiv, "action", url + "?" + (new Date()).getMilliseconds());
+                       Jmol.$val("__jsmoldata__", data);
+                       Jmol.$val("__jsmolfilename__", filename);
+                       Jmol.$val("__jsmolmimetype__", mimetype);
+                       Jmol.$val("__jsmolencoding__", encoding);
+                       Jmol.$submit("__jsmolform__");
+                       Jmol.$val("__jsmoldata__", "");
+                       Jmol.$val("__jsmolfilename__", "");
+               }
+               return "OK";
+       }
+
+       Jmol._strToBytes = function(s) {
+               if (Clazz.instanceOf(s, self.ArrayBuffer))
+                       return Jmol._toBytes(s);
+               var b = Clazz.newByteArray(s.length, 0);
+               for (var i = s.length; --i >= 0;)
+                       b[i] = s.charCodeAt(i) & 0xFF;
+               return b;
+       }
+
+       ////////////// applet start-up functionality //////////////
+
+       Jmol._setConsoleDiv = function (d) {
+               if (!self.Clazz)return;
+               Clazz.setConsoleDiv(d);
+       }
+
+       Jmol._registerApplet = function(id, applet) {
+               return window[id] = Jmol._applets[id] = Jmol._applets[applet] = Jmol._applets[id + "__" + Jmol._syncId + "__"] = applet;
+       } 
+
+       Jmol._readyCallback = function (appId,fullId,isReady,javaApplet,javaAppletPanel) {
+               appId = appId.split("_object")[0];
+    javaAppletPanel || (javaAppletPanel = javaApplet);
+               isReady = (isReady.booleanValue ? isReady.booleanValue() : isReady);
+               // necessary for MSIE in strict mode -- apparently, we can't call 
+               // jmol._readyCallback, but we can call Jmol._readyCallback. Go figure...
+    var applet = Jmol._applets[appId];
+    applet._appletPanel = javaAppletPanel;
+    applet._applet = javaApplet;
+               Jmol._track(applet._readyCallback(appId, fullId, isReady));
+       }
+
+       Jmol._getWrapper = function(applet, isHeader) {
+
+                       // id_appletinfotablediv
+                       //     id_appletdiv
+                       //     id_coverdiv
+                       //     id_infotablediv
+                       //       id_infoheaderdiv
+                       //          id_infoheaderspan
+                       //          id_infocheckboxspan
+                       //       id_infodiv
+
+
+                       // for whatever reason, without DOCTYPE, with MSIE, "height:auto" does not work, 
+                       // and the text scrolls off the page.
+                       // So I'm using height:95% here.
+                       // The table was a fix for MSIE with no DOCTYPE tag to fix the miscalculation
+                       // in height of the div when using 95% for height. 
+                       // But it turns out the table has problems with DOCTYPE tags, so that's out. 
+                       // The 95% is a compromise that we need until the no-DOCTYPE MSIE solution is found. 
+                       // (100% does not work with the JME linked applet)
+               var s;
+               // ... here are just for clarification in this code; they are removed immediately
+               if (isHeader) {
+                       var img = ""; 
+                       if (applet._coverImage){
+                               var more = " onclick=\"Jmol.coverApplet(ID, false)\" title=\"" + (applet._coverTitle) + "\"";
+                               var play = "<image id=\"ID_coverclickgo\" src=\"" + applet._j2sPath + "/img/play_make_live.jpg\" style=\"width:25px;height:25px;position:absolute;bottom:10px;left:10px;"
+                                       + "z-index:" + Jmol._getZ(applet, "coverImage")+";opacity:0.5;\"" + more + " />"  
+                               img = "<div id=\"ID_coverdiv\" style=\"background-color:red;z-index:" + Jmol._getZ(applet, "coverImage")+";width:100%;height:100%;display:inline;position:absolute;top:0px;left:0px\"><image id=\"ID_coverimage\" src=\""
+                                + applet._coverImage + "\" style=\"width:100%;height:100%\"" + more + "/>" + play + "</div>";
+                       }
+                       var css = Jmol._appletCssText.replace(/\'/g,'"');
+                       css = (css.indexOf("style=\"") >= 0 ? css.split("style=\"")[1] : "\" " + css);
+                       s = "\
+...<div id=\"ID_appletinfotablediv\" style=\"width:Wpx;height:Hpx;position:relative;font-size:14px;text-align:left\">IMG\
+......<div id=\"ID_appletdiv\" style=\"z-index:" + Jmol._getZ(applet, "header") + ";width:100%;height:100%;position:absolute;top:0px;left:0px;" + css + ">";
+                       var height = applet._height;
+                       var width = applet._width;
+                       if (typeof height !== "string" || height.indexOf("%") < 0) 
+                               height += "px";
+                       if (typeof width !== "string" || width.indexOf("%") < 0)
+                               width += "px";
+                       s = s.replace(/IMG/, img).replace(/Hpx/g, height).replace(/Wpx/g, width);
+               } else {
+                       s = "\
+......</div>\
+......<div id=\"ID_2dappletdiv\" style=\"position:absolute;width:100%;height:100%;overflow:hidden;display:none\"></div>\
+......<div id=\"ID_infotablediv\" style=\"width:100%;height:100%;position:absolute;top:0px;left:0px\">\
+.........<div id=\"ID_infoheaderdiv\" style=\"height:20px;width:100%;background:yellow;display:none\"><span id=\"ID_infoheaderspan\"></span><span id=\"ID_infocheckboxspan\" style=\"position:absolute;text-align:right;right:1px;\"><a href=\"javascript:Jmol.showInfo(ID,false)\">[x]</a></span></div>\
+.........<div id=\"ID_infodiv\" style=\"position:absolute;top:20px;bottom:0px;width:100%;height:100%;overflow:auto\"></div>\
+......</div>\
+...</div>";
+               }
+               return s.replace(/\.\.\./g,"").replace(/[\n\r]/g,"").replace(/ID/g, applet._id);
+       }
+
+       Jmol._documentWrite = function(text) {
+               if (Jmol._document) {
+                       if (Jmol._isXHTML && !Jmol._XhtmlElement) {
+                               var s = document.getElementsByTagName("script");
+                               Jmol._XhtmlElement = s.item(s.length - 1);
+                               Jmol._XhtmlAppendChild = false;
+                       }
+                       if (Jmol._XhtmlElement)
+                               Jmol._domWrite(text);
+                       else
+                               Jmol._document.write(text);
+               }
+               return text;
+       }
+
+       Jmol._domWrite = function(data) {
+               var pt = 0
+               var Ptr = []
+               Ptr[0] = 0
+               while (Ptr[0] < data.length) {
+                       var child = Jmol._getDomElement(data, Ptr);
+                       if (!child)
+                               break;
+                       if (Jmol._XhtmlAppendChild)
+                               Jmol._XhtmlElement.appendChild(child);
+                       else
+                               Jmol._XhtmlElement.parentNode.insertBefore(child, _jmol.XhtmlElement);
+               }
+       }
+
+       Jmol._getDomElement = function(data, Ptr, closetag, lvel) {
+
+               // there is no "document.write" in XHTML
+
+               var e = document.createElement("span");
+               e.innerHTML = data;
+               Ptr[0] = data.length;
+
+/*
+       // unnecessary ?  
+
+               closetag || (closetag = "");
+               lvel || (lvel = 0);
+               var pt0 = Ptr[0];
+               var pt = pt0;
+               while (pt < data.length && data.charAt(pt) != "<") 
+                       pt++
+               if (pt != pt0) {
+                       var text = data.substring(pt0, pt);
+                       Ptr[0] = pt;
+                       return document.createTextNode(text);
+               }
+               pt0 = ++pt;
+               var ch;
+               while (pt < data.length && "\n\r\t >".indexOf(ch = data.charAt(pt)) < 0) 
+                       pt++;
+               var tagname = data.substring(pt0, pt);
+               var e = (tagname == closetag  || tagname == "/" ? ""
+                       : document.createElementNS ? document.createElementNS('http://www.w3.org/1999/xhtml', tagname)
+                       : document.createElement(tagname));
+               if (ch == ">") {
+                       Ptr[0] = ++pt;
+                       return e;
+               }
+               while (pt < data.length && (ch = data.charAt(pt)) != ">") {
+                       while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) 
+                               pt++;
+                       pt0 = pt;
+                       while (pt < data.length && "\n\r\t =/>".indexOf(ch = data.charAt(pt)) < 0) 
+                               pt++;
+                       var attrname = data.substring(pt0, pt).toLowerCase();
+                       if (attrname && ch != "=")
+                               e.setAttribute(attrname, "true");
+                       while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) 
+                               pt++;
+                       if (ch == "/") {
+                               Ptr[0] = pt + 2;
+                               return e;
+                       } else if (ch == "=") {
+                               var quote = data.charAt(++pt);
+                               pt0 = ++pt;
+                               while (pt < data.length && (ch = data.charAt(pt)) != quote) 
+                                       pt++;
+                               var attrvalue = data.substring(pt0, pt);
+                               e.setAttribute(attrname, attrvalue);
+                               pt++;
+                       }
+               }
+               Ptr[0] = ++pt;
+               while (Ptr[0] < data.length) {
+                       var child = Jmol._getDomElement(data, Ptr, "/" + tagname, lvel+1);
+                       if (!child)
+                               break;
+                       e.appendChild(child);
+               }
+*/
+               return e;    
+       }
+
+       Jmol._setObject = function(obj, id, Info) {
+               obj._id = id;
+               obj.__Info = {};
+               Info.z && Info.zIndexBase && (Jmol._z = Jmol._getZOrders(Info.zIndexBase));
+               for (var i in Info)
+                       obj.__Info[i] = Info[i];
+               (obj._z = Info.z) || Info.zIndexBase && (obj._z = obj.__Info.z = Jmol._getZOrders(Info.zIndexBase));
+               obj._width = Info.width;
+               obj._height = Info.height;
+               obj._noscript = !obj._isJava && Info.noscript;
+               obj._console = Info.console;
+               obj._cacheFiles = !!Info.cacheFiles;
+               obj._viewSet = (Info.viewSet == null || obj._isJava ? null : "Set" + Info.viewSet);
+               if (obj._viewSet != null) {
+                       Jmol.View.__init(obj);
+                       obj._currentView = null;
+               }
+               !Jmol._fileCache && obj._cacheFiles && (Jmol._fileCache = {});
+               if (!obj._console)
+                       obj._console = obj._id + "_infodiv";
+               if (obj._console == "none")
+                       obj._console = null;
+
+               obj._color = (Info.color ? Info.color.replace(/0x/,"#") : "#FFFFFF");
+               obj._disableInitialConsole = Info.disableInitialConsole;
+               obj._noMonitor = Info.disableJ2SLoadMonitor;
+               Jmol._j2sPath && (Info.j2sPath = Jmol._j2sPath);
+               obj._j2sPath = Info.j2sPath;
+               obj._coverImage = Info.coverImage;
+               obj._isCovered = !!obj._coverImage; 
+               obj._deferApplet = Info.deferApplet || obj._isCovered && obj._isJava; // must do this if covered in Java
+               obj._deferUncover = Info.deferUncover && !obj._isJava; // can't do this with Java
+               obj._coverScript = Info.coverScript;
+               obj._coverTitle = Info.coverTitle;
+
+               if (!obj._coverTitle)
+                       obj._coverTitle = (obj._deferApplet ? "activate 3D model" : "3D model is loading...")
+               obj._containerWidth = obj._width + ((obj._width==parseFloat(obj._width))? "px":"");
+               obj._containerHeight = obj._height + ((obj._height==parseFloat(obj._height))? "px":"");
+               obj._info = "";
+               obj._infoHeader = obj._jmolType + ' "' + obj._id + '"'
+               obj._hasOptions = Info.addSelectionOptions;
+               obj._defaultModel = Info.defaultModel;
+               obj._readyScript = (Info.script ? Info.script : "");
+               obj._readyFunction = Info.readyFunction;
+               if (obj._coverImage && !obj._deferApplet)
+                       obj._readyScript += ";javascript " + id + "._displayCoverImage(false)";
+               obj._src = Info.src;
+
+       }
+
+       Jmol._addDefaultInfo = function(Info, DefaultInfo) {
+               for (var x in DefaultInfo)
+                       if (typeof Info[x] == "undefined")
+                               Info[x] = DefaultInfo[x];
+               Jmol._use && (Info.use = Jmol._use);
+               if (Info.use.indexOf("SIGNED") >= 0) {
+                       if (Info.jarFile.indexOf("Signed") < 0)
+                               Info.jarFile = Info.jarFile.replace(/Applet/,"AppletSigned");
+                       Info.use = Info.use.replace(/SIGNED/, "JAVA");
+                       Info.isSigned = true;
+               }
+       }
+
+       Jmol._syncedApplets = [];
+       Jmol._syncedCommands = [];
+       Jmol._syncedReady = [];
+       Jmol._syncReady = false;
+       Jmol._isJmolJSVSync = false;
+
+       Jmol._setReady = function(applet) {
+               Jmol._syncedReady[applet] = 1;
+               var n = 0;
+               for (var i = 0; i < Jmol._syncedApplets.length; i++) {
+                       if (Jmol._syncedApplets[i] == applet._id) {
+                               Jmol._syncedApplets[i] = applet;
+                               Jmol._syncedReady[i] = 1;
+                       } else if (!Jmol._syncedReady[i]) {
+                               continue;
+                       }
+                       n++;
+               }
+               if (n != Jmol._syncedApplets.length)
+                       return;
+               Jmol._setSyncReady();
+       }
+
+       Jmol._setDestroy = function(applet) {
+               //MSIE bug responds to any link click even if it is just a JavaScript call
+
+               if (Jmol.featureDetection.allowDestroy)
+                       Jmol.$windowOn('beforeunload', function () { Jmol._destroy(applet); } );
+       }
+
+       Jmol._destroy = function(applet) {
+               try {
+                       if (applet._appletPanel) applet._appletPanel.destroy();
+                       applet._applet = null;
+                       Jmol._unsetMouse(applet._canvas)
+                       applet._canvas = null;
+                       var n = 0;
+                       for (var i = 0; i < Jmol._syncedApplets.length; i++) {
+                               if (Jmol._syncedApplets[i] == applet)
+                                       Jmol._syncedApplets[i] = null;
+                               if (Jmol._syncedApplets[i])
+                                       n++;
+                       }
+                       if (n > 0)
+                               return;
+                       Jmol._clearVars();
+               } catch(e){}
+       }
+
+       ////////////// misc core functionality //////////////
+
+       Jmol._setSyncReady = function() {
+               Jmol._syncReady = true;
+               var s = ""
+               for (var i = 0; i < Jmol._syncedApplets.length; i++)
+                       if (Jmol._syncedCommands[i])
+                               s += "Jmol.script(Jmol._syncedApplets[" + i + "], Jmol._syncedCommands[" + i + "]);"
+               setTimeout(s, 50);  
+       }
+
+       Jmol._mySyncCallback = function(appFullName,msg) {
+               app = Jmol._applets[appFullName];
+               if (app._viewSet) {
+                       // when can we do this?
+//                     if (app._viewType == "JSV" && !app._currentView.JMOL)
+                               Jmol.View.updateFromSync(app, msg);
+                       return;
+               }
+               if(!Jmol._syncReady || !Jmol._isJmolJSVSync)
+                       return 1; // continue processing and ignore me
+               for (var i = 0; i < Jmol._syncedApplets.length; i++) {
+                       if (msg.indexOf(Jmol._syncedApplets[i]._syncKeyword) >= 0)
+                               Jmol._syncedApplets[i]._syncScript(msg);
+               }
+               return 0 // prevents further Jmol sync processing 
+       }              
+
+       Jmol._getElement = function(applet, what) {
+               var d = document.getElementById(applet._id + "_" + what);
+               return (d || {});
+       } 
+        
+       Jmol._evalJSON = function(s,key){
+               s = s + "";
+               if(!s)
+                       return [];
+               if(s.charAt(0) != "{") {
+                       if(s.indexOf(" | ") >= 0)
+                               s = s.replace(/\ \|\ /g, "\n");
+                       return s;
+               }
+               var A = (new Function( "return " + s ) )();
+               return (!A ? null : key && A[key] != undefined ? A[key] : A);
+       }
+
+       Jmol._sortMessages = function(A){
+               /*
+                * private function
+                */
+               function _sortKey0(a,b){
+                       return (a[0]<b[0]?1:a[0]>b[0]?-1:0);
+               }
+
+               if(!A || typeof (A) != "object")
+                       return [];
+               var B = [];
+               for(var i = A.length - 1; i >= 0; i--)
+                       for(var j = 0, jj= A[i].length; j < jj; j++)
+                               B[B.length] = A[i][j];
+               if(B.length == 0)
+                       return;
+               B = B.sort(_sortKey0);
+               return B;
+       }
+
+       //////////////////// mouse events //////////////////////
+
+       Jmol._setMouseOwner = function(who, tf) {
+               if (who == null || tf)
+                       Jmol._mouseOwner = who;
+               else if (Jmol._mouseOwner == who)
+                       Jmol._mouseOwner = null;
+       }
+
+       Jmol._jsGetMouseModifiers = function(ev) {
+               var modifiers = 0;
+               switch (ev.button) {
+               case 0:
+                       modifiers = 16;//J.api.Event.MOUSE_LEFT;
+                       break;
+               case 1:
+                       modifiers = 8;//J.api.Event.MOUSE_MIDDLE;
+                       break;
+               case 2:
+                       modifiers = 4;//J.api.Event.MOUSE_RIGHT;
+                       break;
+               }
+               if (ev.shiftKey)
+                       modifiers += 1;//J.api.Event.SHIFT_MASK;
+               if (ev.altKey)
+                       modifiers += 8;//J.api.Event.ALT_MASK;
+               if (ev.ctrlKey)
+                       modifiers += 2;//J.api.Event.CTRL_MASK;
+               return modifiers;
+       }
+
+       Jmol._jsGetXY = function(canvas, ev) {
+               if (!canvas.applet._ready || Jmol._touching && ev.type.indexOf("touch") < 0)
+                       return false;
+               //ev.preventDefault(); // removed 5/9/2015 -- caused loss of focus on text-box clicking in SwingJS
+               var offsets = Jmol.$offset(canvas.id);
+               var x, y;
+               var oe = ev.originalEvent;
+               // drag-drop jQuery event is missing pageX
+               ev.pageX || (ev.pageX = oe.pageX);
+               ev.pageY || (ev.pageY = oe.pageY);
+               Jmol._mousePageX = ev.pageX;
+               Jmol._mousePageY = ev.pageY;
+               if (oe.targetTouches && oe.targetTouches[0]) {
+                       x = oe.targetTouches[0].pageX - offsets.left;
+                       y = oe.targetTouches[0].pageY - offsets.top;
+               } else if (oe.changedTouches) {
+                       x = oe.changedTouches[0].pageX - offsets.left;
+                       y = oe.changedTouches[0].pageY - offsets.top;
+               } else {
+                       x = ev.pageX - offsets.left;
+                       y = ev.pageY - offsets.top;
+               }
+               return (x == undefined ? null : [Math.round(x), Math.round(y), Jmol._jsGetMouseModifiers(ev)]);
+       }
+
+       Jmol._gestureUpdate = function(canvas, ev) {
+               ev.stopPropagation();
+               ev.preventDefault();
+               var oe = ev.originalEvent;
+               switch (ev.type) {
+               case "touchstart":
+                       Jmol._touching = true;
+                       break;
+               case "touchend":
+                       Jmol._touching = false;
+                       break;
+               }
+               if (!oe.touches || oe.touches.length != 2) return false;
+               switch (ev.type) {
+               case "touchstart":
+                       canvas._touches = [[],[]];
+                       break;
+               case "touchmove":
+                       var offsets = Jmol.$offset(canvas.id);
+                       var t0 = canvas._touches[0];
+                       var t1 = canvas._touches[1];
+                       t0.push([oe.touches[0].pageX - offsets.left, oe.touches[0].pageY - offsets.top]);
+                       t1.push([oe.touches[1].pageX - offsets.left, oe.touches[1].pageY - offsets.top]);
+                       var n = t0.length;
+                       if (n > 3) {
+                               t0.shift();
+                               t1.shift();
+                       }
+                       if (n >= 2)
+                               canvas.applet._processGesture(canvas._touches);
+                       break;
+               }
+               return true;
+       }  
+
+       Jmol._jsSetMouse = function(canvas) {
+
+    var doIgnore = function(ev) { return (ev.target.className.indexOf("swingjs-ui") >= 0) };
+         
+               Jmol.$bind(canvas, 'mousedown touchstart', function(ev) {
+      if (doIgnore(ev))
+        return true;
+                       Jmol._setMouseOwner(canvas, true);
+                       ev.stopPropagation();
+      var ui = ev.target["data-UI"];
+      if (!ui || !ui.handleJSEvent(canvas, 501, ev)) 
+                       ev.preventDefault();
+                       canvas.isDragging = true;
+                       if ((ev.type == "touchstart") && Jmol._gestureUpdate(canvas, ev))
+                               return !!ui;
+                       Jmol._setConsoleDiv(canvas.applet._console);
+                       var xym = Jmol._jsGetXY(canvas, ev);
+                       if(xym) {
+                       if (ev.button != 2)
+          Jmol.Swing.hideMenus(canvas.applet);
+        canvas.applet._processEvent(501, xym); //java.awt.Event.MOUSE_DOWN
+      }
+                       return !!ui;
+               });
+    
+               Jmol.$bind(canvas, 'mouseup touchend', function(ev) {
+      if (doIgnore(ev))
+        return true;
+                       Jmol._setMouseOwner(null);
+                       ev.stopPropagation();
+      var ui = ev.target["data-UI"];
+      if (!ui || !ui.handleJSEvent(canvas, 502, ev))
+                       ev.preventDefault();
+                       canvas.isDragging = false;
+                       if (ev.type == "touchend" && Jmol._gestureUpdate(canvas, ev))
+                               return !!ui;
+                       var xym = Jmol._jsGetXY(canvas, ev);
+                       if(xym)
+                       canvas.applet._processEvent(502, xym);//java.awt.Event.MOUSE_UP
+                       return !!ui;
+               });
+    
+               Jmol.$bind(canvas, 'mousemove touchmove', function(ev) { // touchmove
+      if (doIgnore(ev))
+        return true;
+                 // defer to console or menu when dragging within this canvas
+                       if (Jmol._mouseOwner && Jmol._mouseOwner != canvas && Jmol._mouseOwner.isDragging) {
+        if (!Jmol._mouseOwner.mouseMove)
+          return true;
+                               Jmol._mouseOwner.mouseMove(ev);
+                               return false;
+                       }
+                       return Jmol._drag(canvas, ev);
+               });
+               
+               Jmol._drag = function(canvas, ev) {
+      
+                       ev.stopPropagation();
+                       ev.preventDefault();
+      
+                       var isTouch = (ev.type == "touchmove");
+                       if (isTouch && Jmol._gestureUpdate(canvas, ev))
+                               return false;
+                       var xym = Jmol._jsGetXY(canvas, ev);
+                       if(!xym) return false;
+      
+                       if (!canvas.isDragging)
+                               xym[2] = 0;
+
+      var ui = ev.target["data-UI"];
+      if (canvas.isdragging && (!ui || !ui.handleJSEvent(canvas, 506, ev))) {}
+                       canvas.applet._processEvent((canvas.isDragging ? 506 : 503), xym); // java.awt.Event.MOUSE_DRAG : java.awt.Event.MOUSE_MOVE
+                       return !!ui;
+               }
+               
+               Jmol.$bind(canvas, 'DOMMouseScroll mousewheel', function(ev) { // Zoom
+      if (doIgnore(ev))
+        return true;
+                       ev.stopPropagation();
+                       ev.preventDefault();
+                       // Webkit or Firefox
+                       canvas.isDragging = false;
+                       var oe = ev.originalEvent;
+                       var scroll = (oe.detail ? oe.detail : (Jmol.featureDetection.os == "mac" ? 1 : -1) * oe.wheelDelta); // Mac and PC are reverse; but 
+                       var modifiers = Jmol._jsGetMouseModifiers(ev);
+                       canvas.applet._processEvent(-1,[scroll < 0 ? -1 : 1,0,modifiers]);
+                       return false;
+               });
+
+               // context menu is fired on mouse down, not up, and it's handled already anyway.
+
+               Jmol.$bind(canvas, "contextmenu", function() {return false;});
+
+               Jmol.$bind(canvas, 'mouseout', function(ev) {
+      if (doIgnore(ev))
+        return true;
+      if (Jmol._mouseOwner && !Jmol._mouseOwner.mouseMove) 
+        Jmol._setMouseOwner(null);
+                       if (canvas.applet._appletPanel)
+                               canvas.applet._appletPanel.startHoverWatcher(false);
+                       //canvas.isDragging = false;
+                       var xym = Jmol._jsGetXY(canvas, ev);
+                       if (!xym)
+                               return false;
+                       //canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP
+                       //canvas.applet._processEvent(505, xym);//J.api.Event.MOUSE_EXITED
+                       return false;
+               });
+
+               Jmol.$bind(canvas, 'mouseenter', function(ev) {
+      if (doIgnore(ev))
+        return true;
+                       if (canvas.applet._appletPanel)
+                               canvas.applet._appletPanel.startHoverWatcher(true);
+                       if (ev.buttons === 0 || ev.which === 0) {
+                               canvas.isDragging = false;
+                               var xym = Jmol._jsGetXY(canvas, ev);
+                               if (!xym)
+                                       return false;
+                               canvas.applet._processEvent(504, xym);//J.api.Event.MOUSE_ENTERED       
+                               canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP
+                               return false;
+                       }
+               });
+
+       Jmol.$bind(canvas, 'mousemoveoutjsmol', function(evspecial, target, ev) {
+      if (doIgnore(ev))
+        return true;
+               if (canvas == Jmol._mouseOwner && canvas.isDragging) {
+                       return Jmol._drag(canvas, ev);
+               }
+       });
+
+               if (canvas.applet._is2D)
+                       Jmol.$resize(function() {
+                               if (!canvas.applet)
+                                       return;
+                               canvas.applet._resize();
+                       });
+               Jmol.$bind('body', 'mouseup touchend', function(ev) {
+      if (doIgnore(ev))
+        return true;
+                       if (canvas.applet)
+                               canvas.isDragging = false;
+                       Jmol._setMouseOwner(null);
+               });
+
+       }
+
+       Jmol._jsUnsetMouse = function(canvas) {
+               canvas.applet = null;
+               Jmol.$bind(canvas, 'mousedown touchstart mousemove touchmove mouseup touchend DOMMouseScroll mousewheel contextmenu mouseout mouseenter', null);
+               Jmol._setMouseOwner(null);
+       }
+
+
+////// Jmol.Swing interface  for Javascript implementation of Swing dialogs and menus
+
+Jmol.Swing = {
+       // a static class
+       count:0,
+       menuInitialized:0,
+       menuCounter:0,
+       htDialogs:{}
+};
+
+(function(Swing) {
+
+SwingController = Swing; // see javajs.api.SwingController
+
+Swing.setDraggable = function(Obj) {
+       
+       var proto = Obj.prototype;
+       if (proto.setContainer)
+               return;
+       
+       // for menus, console, and 
+       proto.setContainer = function(container) {
+               this.container = container;
+               container.obj = this;
+               this.isDragging = false;
+               this.ignoreMouse = false;
+               var me = this;
+               container.bind('mousedown touchstart', function(ev) {
+                       if (me.ignoreMouse) {
+                               me.ignoreMouse = false;
+                               return true;
+                       }
+                       Jmol._setMouseOwner(me, true);
+                       me.isDragging = true;
+                       me.pageX = ev.pageX;
+                       me.pageY = ev.pageY;
+                       return false;
+               });
+               container.bind('mousemove touchmove', function(ev) {
+                       if (me.isDragging && Jmol._mouseOwner == me) {
+                               me.mouseMove(ev);
+                               return false;
+                       }
+               });
+               container.bind('mouseup touchend', function(ev) {
+                       me.mouseUp(ev);
+                       Jmol._setMouseOwner(null);
+               });
+       };
+
+       proto.mouseUp = function(ev) {
+               if (this.isDragging && Jmol._mouseOwner == this) {
+                       this.pageX0 += (ev.pageX - this.pageX);
+                       this.pageY0 += (ev.pageY - this.pageY);
+                       this.isDragging = false;
+                       return false;
+               }
+               Jmol._setMouseOwner(null);
+       }
+
+       proto.setPosition = function() {
+               if (Jmol._mousePageX === null) {
+                       var id = this.applet._id + "_" + (this.applet._is2D ? "canvas2d" : "canvas");
+                       var offsets = Jmol.$offset(id);
+                       Jmol._mousePageX = offsets.left;
+                       Jmol._mousePageY = offsets.top;
+               }
+               this.pageX0 = Jmol._mousePageX;
+               this.pageY0 = Jmol._mousePageY;
+               var pos = { top: Jmol._mousePageY + 'px', left: Jmol._mousePageX + 'px' };
+               this.container.css(pos);
+       };
+
+       proto.mouseMove = function(ev) {
+               if (this.isDragging && Jmol._mouseOwner == this) {
+                       this.timestamp = System.currentTimeMillis(); // used for menu closure
+                       var x = this.pageX0 + (ev.pageX - this.pageX);
+                       var y = this.pageY0 + (ev.pageY - this.pageY);
+      Jmol._mousePageX = x;
+      Jmol._mousePageY = y;
+                       this.container.css({ top: y + 'px', left: x + 'px' })
+               }
+       };
+
+       proto.dragBind = function(isBind) {
+               this.applet._ignoreMouse = !isBind;
+               this.container.unbind('mousemoveoutjsmol');
+               this.container.unbind('touchmoveoutjsmol');
+               this.container.unbind('mouseupoutjsmol');
+               this.container.unbind('touchendoutjsmol');
+               Jmol._setMouseOwner(null);
+               if (isBind) {
+                       var me = this;
+                       this.container.bind('mousemoveoutjsmol touchmoveoutjsmol', function(evspecial, target, ev) {
+                               me.mouseMove(ev);
+                       });
+                       this.container.bind('mouseupoutjsmol touchendoutjsmol', function(evspecial, target, ev) {
+                               me.mouseUp(ev);
+                       });
+               }
+       };
+}
+
+// Dialog //
+
+Swing.JSDialog = function () {
+}
+
+Swing.setDraggable(Swing.JSDialog);
+
+///// calls from javajs and other Java-derived packages /////
+
+Swing.getScreenDimensions = function(d) {
+       d.width = $(window).width();
+       d.height = $(window).height();
+}
+
+Swing.dispose = function(dialog) {
+       Jmol.$remove(dialog.id + "_mover");
+       delete Swing.htDialogs[dialog.id]
+       dialog.container.obj.dragBind(false);
+//  var btns = $("#" + dialog.id + " *[id^='J']"); // add descendents with id starting with "J"
+//  for (var i = btns.length; --i >= 0;)
+//    delete Dialog.htDialogs[btns[i].id]
+       //System.out.println("JSmolCore.js: dispose " + dialog.id)
+}
+Swing.register = function(dialog, type) {
+       dialog.id = type + (++Swing.count);
+       Swing.htDialogs[dialog.id] = dialog;
+       //System.out.println("JSmolCore.js: register " + dialog.id)
+
+}
+
+Swing.setDialog = function(dialog) {
+       Jmol._setMouseOwner(null);
+       Jmol.$remove(dialog.id);
+       //System.out.println("removed " + dialog.id)
+       var id = dialog.id + "_mover";
+       var container = Jmol._$(id);
+       var jd;
+       //System.out.println("JSmolCore.js: setDialog " + dialog.id);
+       if (container[0]) {
+               container.html(dialog.html);
+               jd = container[0].jd;
+       } else {
+               Jmol.$after("body","<div id='" + id + "' style='position:absolute;left:0px;top:0px;'>" + dialog.html + "</div>");
+               var jd = new Swing.JSDialog();
+               container = Jmol._$(id);
+               dialog.container = container;
+               jd.applet = dialog.manager.vwr.html5Applet;
+               jd.setContainer(container);
+               jd.dialog = dialog;
+               jd.setPosition();  
+               jd.dragBind(true);
+               container[0].jd = jd; 
+       }
+       Jmol.$bind("#" + dialog.id + " .JButton", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JComboBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JCheckBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JTextField", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JTable", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JScrollPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+       Jmol.$bind("#" + dialog.id + " .JEditorPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
+
+}
+Swing.setSelected = function(chk) {
+ Jmol.$prop(chk.id, 'checked', !!chk.selected);
+}
+
+Swing.setSelectedIndex = function(cmb) {
+ Jmol.$prop(cmb.id, 'selectedIndex', cmb.selectedIndex);
+}
+
+Swing.setText = function(btn) {
+ Jmol.$prop(btn.id, 'value', btn.text);
+}
+
+Swing.setVisible = function(c) {
+       Jmol.$setVisible(c.id, c.visible);
+}
+
+Swing.setEnabled = function(c) {
+       Jmol.$setEnabled(c.id, c.enabled);
+}
+
+/// callbacks from the HTML elements ////
+Swing.click = function(element, keyEvent) {
+       var component = Swing.htDialogs[element.id];
+       if (component) {
+               //System.out.println("click " + element + " " + component)
+               var info = component.toString();
+               // table cells will have an id but are not registered
+               if (info.indexOf("JCheck") >= 0) {
+                               component.selected = element.checked;
+               } else if (info.indexOf("JCombo") >= 0) {
+                       component.selectedIndex = element.selectedIndex;
+               } else if (component.text != null) {  // JButton, JTextField
+                       component.text = element.value;
+                       if (keyEvent && ((keyEvent.charCode || keyEvent.keyCode) != 13))
+                               return;
+               }    
+       }
+       var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];
+       var key = (component ? component.name :  dialog.registryKey + "/" + element.id);
+       //System.out.println("JSmolCore.js: click " + key); 
+       dialog.manager.actionPerformed(key);
+}
+
+Swing.setFront = function(dialog) {
+  var applet = dialog.manager.vwr.html5Applet;
+       if (dialog.zIndex != Jmol._getZ(applet, "dialog"))
+        dialog.zIndex = Jmol._incrZ(applet, "dialog");
+       dialog.container && ((dialog.container[0] || dialog.container).style.zIndex = dialog.zIndex);
+}
+
+Swing.hideMenus = function(applet) {
+       // called from LEFT-DOWN mouse event
+       var menus = applet._menus;
+       if (menus)
+               for (var i in menus)
+                       if (menus[i].visible)
+                               Swing.hideMenu(menus[i]);
+}
+
+Swing.windowClosing = function(element) {
+       var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];
+       if (dialog.registryKey) {
+               //System.out.println("JSmolCore.js: windowClosing " + dialog.registryKey); 
+               dialog.manager.processWindowClosing(dialog.registryKey);
+       } else {
+               //System.out.println("JSmolCore.js: windowClosing " + dialog.title); 
+               dialog.dispose();
+       }
+}
+
+})(Jmol.Swing);
+
+Jmol._track = function(applet) {
+       // this function inserts an iFrame that can be used to track your page's applet use. 
+       // By default it tracks to a page at St. Olaf College, but you can change that. 
+       // and you can use
+       //
+       // delete Jmol._tracker
+       //
+       // yourself to not have you page execute this 
+       //
+       if (Jmol._tracker){
+               try {  
+                       var url = Jmol._tracker + "&applet=" + applet._jmolType + "&version=" + Jmol._version 
+                               + "&appver=" + Jmol.___JmolVersion + "&url=" + encodeURIComponent(document.location.href);
+                       var s = '<iframe style="display:none" width="0" height="0" frameborder="0" tabindex="-1" src="' + url + '"></iframe>'
+                       Jmol.$after("body", s);
+               } catch (e) {
+                       // ignore
+               }
+               delete Jmol._tracker;
+       }
+       return applet;
+}
+
+Jmol.getProfile = function() {
+       window["j2s.doProfile"] = true;
+       if (self.Clazz && self.JSON) {
+               Clazz._profile || (Clazz._profile = {});
+               return Clazz.getProfile();
+       }
+}
+
+Jmol._getInChIKey = function(applet, data) {
+       if (data.indexOf("MOL=") >= 0)
+               data = data.split("MOL=")[1].split("\"")[0];
+
+}
+
+Jmol._getAttr = function(s, a) {
+       var pt = s.indexOf(a + "=");
+       return (pt >= 0 && (pt = s.indexOf('"', pt)) >= 0 
+               ? s.substring(pt+1, s.indexOf('"', pt+1)) : null);
+}
+
+Jmol.User = {
+       viewUpdatedCallback: null
+}
+
+Jmol.View = {
+
+// The objective of Jmol.View is to coordinate
+// asynchronous applet loading and atom/peak picking
+// among any combination of Jmol, JME, and JSV.
+// 
+// basic element is a view object:
+//   view = {
+//     viewType1: viewRecord1,
+//     viewType2: viewRecord2,
+//     viewType3: viewRecord3
+//   }
+// where viewType is one of (Jmol, JME, JSV)
+// and a viewRecord is an object
+// with elements .chemID, .applet, .data, .script
+//
+// Jmol.View.sets is a list of cached views[0..n]
+// for a given group of applet objects with common Info.viewSet
+//
+// Bob Hanson 1/22/2014 7:05:38 AM
+
+       count: 0,
+       applets: {},
+       sets: {}
+};
+
+(function(View) {
+
+// methods called from other modules have no "_" in their name
+
+View.updateView = function(applet, Info, _View_updateView) {
+       // Info.chemID, Info.data, possibly Info.viewID if no chemID
+       // return from applet after asynchronous download of new data
+       if (applet._viewSet == null)
+               return;
+       Info.chemID || (applet._searchQuery = null);
+       Info.data || (Info.data = "N/A");
+       Info.type = applet._viewType;
+       if((applet._currentView = View.__findView(applet._viewSet, Info)) == null)
+               applet._currentView = View.__createViewSet(applet._viewSet, Info.chemID, Info.viewID || Info.chemID);
+       applet._currentView[Info.type].data = Info.data;
+       applet._currentView[Info.type].smiles = applet._getSmiles();
+       if (Jmol.User.viewUpdatedCallback)
+               Jmol.User.viewUpdatedCallback(applet, "updateView");
+       View.__setView(applet._currentView, applet, false);
+}
+
+View.updateFromSync = function(applet, msg) {
+       applet._updateMsg = msg;
+       var id = Jmol._getAttr(msg, "sourceID") || Jmol._getAttr(msg, "file");
+       if (!id)
+               return;
+       var view = View.__findView(applet._viewSet, {viewID:id});
+       if (view == null)
+               return Jmol.updateView(applet, msg); // JSV has been updated internally
+       if (view != applet._currentView)
+               View.__setView(view, applet, true);
+       var A = ((id = Jmol._getAttr(msg, "atoms")) && msg.indexOf("selectionhalos ON") >= 0  
+               ? eval("[" + id + "]") : []);
+       setTimeout(function(){if (applet._currentView == view)View.updateAtomPick(applet, A)}, 10);
+       if (Jmol.User.viewUpdatedCallback)
+               Jmol.User.viewUpdatedCallback(applet, "updateFromSync");
+}
+
+View.updateAtomPick = function(applet, A, _View_updateAtomPick) {
+       var view = applet._currentView;
+       if (view == null)
+               return;
+       for (var viewType in view) {
+               if (viewType != "info" && view[viewType].applet != applet)
+                       view[viewType].applet._updateAtomPick(A);
+       }
+       if (Jmol.User.viewUpdatedCallback)
+               Jmol.User.viewUpdatedCallback(applet, "updateAtomPick");
+}
+
+View.dumpViews = function(setID) {
+       var views = View.sets[setID];
+       if (!views)
+         return;
+       var s = "View set " + setID + ":\n";
+       var applets = View.applets[setID];
+       for (var i in applets)
+               s += "\napplet " + applets[i]._id
+                       + " currentView=" + (applets[i]._currentView ? applets[i]._currentView.info.viewID : null);
+       for (var i = views.length; --i >= 0;) {
+               var view = views[i];
+               s += "\n\n<b>view=" + i 
+                       + " viewID=" + view.info.viewID 
+                       + " chemID=" + view.info.chemID + "</b>\n"
+               var v;
+               for (var viewType in view) 
+                       if (viewType != "info")
+                               s += "\nview=" + i + " type=" + viewType + " applet=" 
+                                       + ((v = view[viewType]).applet ? v.applet._id : null) 
+                                       + " SMILES=" + v.smiles + "\n"
+                                       + " atomMap=" + JSON.stringify(v.atomMap) + "\n"
+                                       + " data=\n" + v.data + "\n"
+       }
+       return s
+}
+
+
+// methods starting with "__" are "private" to JSmolCore.js
+
+View.__init = function(applet) {
+  var set = applet._viewSet;
+       var a = View.applets;
+       a[set] || (a[set] = {});
+       a[set][applet._viewType] = applet;
+}
+
+View.__findView = function(set, Info) {
+       var views = View.sets[set];
+       if (views == null)
+               views = View.sets[set] = [];
+       for (var i = views.length; --i >= 0;) {
+               var view = views[i];
+               if (Info.viewID) {
+                       if (view.info.viewID == Info.viewID)
+                               return view;
+               } else if (Info.chemID != null && Info.chemID == view.info.chemID) {
+                               return view;
+               } else {
+                       for (var viewType in view) { 
+                               if (viewType != "info") {
+                                       if (Info.data != null && view[viewType].data != null ? Info.data == view[viewType].data 
+                                               : Info.type == viewType)
+                                                       return view;
+                               }
+                       }
+               }
+       }
+       return null;  
+}
+
+View.__createViewSet = function(set, chemID, viewID, _createViewSet) {
+       View.count++;
+       var view = {info:{chemID: chemID, viewID: viewID || "model_" + View.count}};
+
+       for (var id in Jmol._applets) {
+               var a = Jmol._applets[id];
+               if (a._viewSet == set)
+                       view[a._viewType] = {applet:a, data: null};
+       }
+       View.sets[set].push(view);
+       return view;
+}
+
+View.__setView = function(view, applet, isSwitch, _setView) {
+       // called from Jmol._searchMol and Jmol.View.setCurrentView 
+       // to notify the applets in the set that there may be new data for them
+       // skip the originating applet itself and cases where the data has not changed.
+       // stop at first null data, because that will initiate some sort of asynchronous
+       // call that will be back here afterward.
+
+       for (var viewType in view) {
+                       if (viewType == "info") 
+                               continue;
+               var rec = view[viewType];
+               var a = rec.applet;
+               var modified = (isSwitch || a != null && a._molData == "<modified>");
+
+               if (a == null || a == applet && !modified)
+                       continue; // may be a mol3d required by JSV but not having a corresponding applet
+               var wasNull = (rec.data == null);
+               var haveView = (a._currentView != null);
+               a._currentView = view; 
+               if (haveView && view[viewType].data == rec.data && !wasNull & !modified)
+                       continue;
+               a._loadModelFromView(view);
+               if (wasNull)
+                       break;
+       }
+
+       // Either all are taken care of or one was null,
+       // in which case we have started an asynchronous
+       // process to get the data, and we can quit here.
+       // In either case, we are done.
+}
+
+}) (Jmol.View);
+
+Jmol.Cache = {fileCache: {}};
+
+Jmol.Cache.get = function(filename) {
+       return Jmol.Cache.fileCache[filename];
+}
+
+Jmol.Cache.put = function(filename, data) {
+  Jmol.Cache.fileCache[filename] = data;
+}
+
+       Jmol.Cache.setDragDrop = function(me) {
+               Jmol.$appEvent(me, "appletdiv", "dragover", function(e) {
+                       e = e.originalEvent;
+                       e.stopPropagation();
+                       e.preventDefault();
+                       e.dataTransfer.dropEffect = 'copy';
+               });
+               Jmol.$appEvent(me, "appletdiv", "drop", function(e) {
+                       var oe = e.originalEvent;
+                       oe.stopPropagation();
+                       oe.preventDefault();
+                       var file = oe.dataTransfer.files[0];
+                       if (file == null) {
+                               // FF and Chrome will drop an image here
+                               // but it will be only a URL, not an actual file. 
+                               try {
+                                 file = "" + oe.dataTransfer.getData("text");
+                                 if (file.indexOf("file:/") == 0 || file.indexOf("http:/") == 0) {
+                                       me._scriptLoad(file);
+                                       return;
+                               }
+                               } catch(e) {
+                                 return;
+                               }
+                         // some other format
+                         return;
+                       }
+                       // MSIE will drop an image this way, though, and load it!
+                       var reader = new FileReader();
+                       reader.onloadend = function(evt) {
+                               if (evt.target.readyState == FileReader.DONE) {
+                                       var cacheName = "cache://DROP_" + file.name;
+                                       var bytes = Jmol._toBytes(evt.target.result);
+                                       if (!cacheName.endsWith(".spt"))
+                                               me._appletPanel.cacheFileByName("cache://DROP_*",false);
+                                       if (me._viewType == "JSV" || cacheName.endsWith(".jdx")) // shared by Jmol and JSV
+                                               Jmol.Cache.put(cacheName, bytes);
+                                       else
+                                               me._appletPanel.cachePut(cacheName, bytes);
+                                       var xym = Jmol._jsGetXY(me._canvas, e);
+                                       if(xym && (!me._appletPanel.setStatusDragDropped || me._appletPanel.setStatusDragDropped(0, xym[0], xym[1], cacheName))) {
+                                               me._appletPanel.openFileAsyncSpecial(cacheName, 1);
+                                       }
+                               }
+                       };
+                       reader.readAsArrayBuffer(file);
+               });
+       }
+  
+})(Jmol, jQuery);