JAL-1807 update
[jalviewjs.git] / site / swingjs / js / JSmolCore.js
1 // JSmolCore.js -- Jmol core capability 
2
3 // allows Jmol applets to be created on a page with more flexibility and extendability
4 // provides an object-oriented interface for JSpecView and syncing of Jmol/JSpecView
5
6 // see JSmolApi.js for public user-interface. All these are private functions
7
8 // BH 5/30/2015 9:33:12 AM adds class swingjs-ui to ignore 
9 // BH 5/9/2015 3:38:52 PM adds data-ignoreMouse attribute for JTextField
10 // BH 3/30/2015 9:46:53 PM adds JSAppletPanel for ready callback
11 // BH 12/6/2014 3:32:54 PM Jmol.setAppletCss() broken
12 // BH 9/13/2014 2:15:51 PM embedded JSME loads from SEARCH when Jmol should 
13 // BH 8/14/2014 2:52:38 PM drag-drop cache should not be cleared if SPT file is dropped
14 // BH 8/5/2014 6:39:54 AM unnecessary messages about binary for PDB finally removed
15 // BH 8/4/2014 5:30:00 AM automatically switch to no document after page loading
16 // BH 8/2/2014 5:22:40 PM drag-drop broken in JSmol/HTML5 
17 // BH 7/23/2014 5:34:08 PM setting a parameter such as readyFunction to null stops file loading
18 // BH 7/3/2014 12:30:28 AM lost drag-drop of models
19 // BH 7/2/2014 4:47:55 AM adding pdbe.org to direct database calls
20 // BH 5/30/2014 7:20:07 AM better dragging for console and menu
21 // BH 4/27/2014 6:31:52 PM allows _USE=SIGNED HTML5 as well as _USE=JAVA HTML5
22 // BH 3/8/2014 5:50:51 PM adds support for dataURI download in FF and Chrome
23 // BH 3/8/2014 8:43:10 AM moves PubChem access to https
24 // BH 3/4/2014 8:40:15 PM adds Jmol.Cache for JSV/Jmol sharing files
25 // BH 2/10/2014 10:07:14 AM added Info.z and Info.zIndexBase
26 // BH 2/9/2014 9:56:06 PM updated JSmolCore.js with option to extend Viewer with code PRIOR to loading Viewer classes
27 // BH 2/6/2014 8:46:25 AM disabled Jmol._tracker for localhost and 127.x.x.x 
28 // BH 1/29/2014 8:02:23 AM Jmol.View and Info.viewSet
29 // BH 1/21/2014 12:06:59 PM adding Jmol.Info.cacheFiles (applet, true/false) and applet._cacheFiles and Jmol._fileCache
30 // BH 1/13/2014 2:12:38 PM adding "http://www.nmrdb.org/tools/jmol/predict.php":"%URL", to _DirectDatabaseCalls
31 // BH 12/21/2013 6:38:35 PM applet sync broken
32 // BH 12/6/2013 6:18:32 PM cover.htm and coverImage fix
33 // BH 12/4/2013 7:44:26 PM fix for JME independent search box
34 // BH 12/3/2013 6:30:08 AM fix for ready function returning Boolean instead of boolean in HTML5 version
35 // BH 11/30/2013 10:31:37 AM added type:"GET" for jQuery.ajax() requests instead of using defaults
36 // BH 11/30/2013 10:31:37 AM added cache:true for jQuery.ajax() requests; can override with cache:"NO", not cache:false
37 // BH 11/28/2013 11:09:27 AM added Jmol._alertNoBinary:true
38 // BH 11/26/2013 8:19:55 PM fix !xxxx search commmand entry and stop MSIE from duplicating command
39 // BH 11/25/2013 7:38:31 AM adds Jmol._tracker: option for GoogleAnalytics tracking
40 // BH 11/25/2013 7:39:03 AM adds URL options _J2S=  _JAR=  _USE=
41 // BH 11/23/2013 10:51:37 PM  adds JNLP support for local applet
42 // BH 11/2/2013 12:05:11 PM JSmolJSME fixes; https access fixed
43 // BH 10/31/2013 7:50:06 PM Jmol.Dialog as SwingController; Jmol._mouseOwner added
44 // BH 10/19/2013 7:05:04 AM adding Jmol._ajaxCall for Error Contacting Server; database POST method enabled
45 // BH 10/17/2013 1:40:51 PM  adding javajs/swing and Jmol.Dialog
46 // BH 9/30/2013 6:42:24 PM: pdb.gz switch  pdb should only be for www.rcsb.org
47 // BH 9/17/2013 10:17:51 AM: asynchronous file reading and saving
48 // BH 8/16/2013 12:02:20 PM: JSmoljQueryExt.js pulled out
49 // BH 8/16/2013 12:02:20 PM: Jmol._touching used properly
50
51 // BH 3/22/2013 5:53:02 PM: Adds noscript option, JSmol.min.core.js
52 // BH 1/17/2013 5:20:44 PM: Fixed problem with console not getting initial position if no first click
53 // 1/13/2013 BH: Fixed MSIE not-reading-local-files problem.
54 // 11/28/2012 BH: Fixed MacOS Safari binary ArrayBuffer problem
55 // 11/21/2012 BH: restructuring of files as JS... instead of J...
56 // 11/20/2012 BH: MSIE9 cannot do a synchronous file load cross-domain. See Jmol._getFileData
57 // 11/4/2012 BH: RCSB REST format change "<structureId>" to "<dimStructure.structureId>"
58 // 9/13/2012 BH: JmolCore.js changes for JSmol doAjax() method -- _3ata()
59 // 6/12/2012 BH: JmolApi.js: adds Jmol.setInfo(applet, info, isShown) -- third parameter optional 
60 // 6/12/2012 BH: JmolApi.js: adds Jmol.getInfo(applet) 
61 // 6/12/2012 BH: JmolApplet.js: Fixes for MSIE 8
62 // 6/5/2012  BH: fixes problem with Jmol "javascript" command not working and getPropertyAsArray not working
63 // 6/4/2012  BH: corrects problem with MSIE requiring mouse-hover to activate applet
64 // 5/31/2012 BH: added JSpecView interface and api -- see JmolJSV.js
65 //               also changed "jmolJarPath" to just "jarPath"
66 //               jmolJarFile->jarFile, jmolIsSigned->isSigned, jmolReadyFunction->readyFunction
67 //               also corrects a double-loading issue
68 // 5/14/2012 BH: added AJAX queue for ChemDoodle option with multiple canvases 
69 // 8/12/2012 BH: adds support for MSIE xdr cross-domain request (jQuery.iecors.js)
70
71         // BH 4/25 -- added text option. setAppletCss(null, "style=\"xxxx\"")
72         // note that since you must add the style keyword, this can be used to add any attribute to these tags, not just css. 
73
74 // required/optional libraries (preferably in the following order):
75
76 //    jquery/jquery.js     -- at least jQuery.1.9
77 //    js/JSmoljQueryext.js -- required for binary file transfer; otherwise standard jQuery should be OK
78 //    js/JSmolCore.js      -- required
79 //    js/j2sjmol.js        -- required
80 //    js/JSmol.js          -- required
81 //    js/JSmolApplet.js    -- required; internal functions for _Applet and _Image; must be after JSmolCore
82 //    js/JSmolControls.js  -- optional; internal functions for buttons, links, menus, etc.; must be after JSmolCore
83 //    js/JSmolConsole.js   -- optional; for the pop-up console
84 //    js/JSmolApi.js       -- required; all user functions; must be after JSmolCore
85 //    js/JSmolTHREE.js     -- optional; WebGL library required for JSmolGLmol.js
86 //    js/JSmolGLmol.js     -- optional; WebGL version of JSmol.
87 //    js/JSmolJME.js       -- optional; JSME (2D editor)
88 //    jsme/jsme/jsme.nocache.js   --  required for JSME 
89 //    js/JSmolMenu.js      -- optional; required for menuing in JSV
90 //    js/JSmolJSV.js       -- optional; for creating and interacting with a JSpecView applet 
91
92 // most of these will be loaded automatically, and for most installations, all you need is JSmol.min.js
93
94
95 // Allows Jmol-like objects to be displayed on Java-challenged (iPad/iPhone)
96 // or applet-challenged (Android/iPhone) platforms, with automatic switching to 
97
98 // For your installation, you should consider putting JmolData.jar and jsmol.php 
99 // on your own server. Nothing more than these two files is needed on the server, and this 
100 // allows more options for MSIE and Chrome when working with cross-domain files (such as RCSB or pubChem) 
101
102 // The NCI and RCSB databases are accessed via direct AJAX if available (xhr2/xdr).
103
104
105 if(typeof(jQuery)=="undefined") alert ("Note -- JSmoljQuery is required for JSmol, but it's not defined.")
106
107 // An example of how to extend Jmol with code PRIOR to JSmolCore.js or JSmol.min.js follows:
108 //
109 // 
110 //      Jmol = {
111 //      z:3000,
112 //              extend: function(what, obj) {if (what == "viewer") { obj._testing = true } }
113 //      }
114
115 self.Jmol || (Jmol = {});
116
117 if (!Jmol._version)
118 Jmol = (function(document) {
119         var z=Jmol.z || 9000;
120         var getZOrders = function(z) {
121                 return {
122                         rear:z++,
123                         header:z++,
124                         main:z++,
125                         image:z++,
126                         front:z++,
127                         fileOpener:z++,
128                         coverImage:z++,
129                         dialog:z++, // could be several of these, JSV only
130                         menu:z+90000, // way front
131                         console:z+91000, // even more front
132       consoleImage:z+91001, // bit more front; increments
133                         monitorZIndex:z+99999 // way way front
134                 }
135         };
136         var j = {
137                 _version: "$Date: 2015-04-26 10:57:08 -0500 (Sun, 26 Apr 2015) $", // svn.keywords:lastUpdated
138                 _alertNoBinary: true,
139                 // this url is used to Google Analytics tracking of Jmol use. You may remove it or modify it if you wish. 
140                 _allowedJmolSize: [25, 2048, 300],   // min, max, default (pixels)
141                 /*  By setting the Jmol.allowedJmolSize[] variable in the webpage
142                                 before calling Jmol.getApplet(), limits for applet size can be overriden.
143                                 2048 standard for GeoWall (http://geowall.geo.lsa.umich.edu/home.html)
144                 */
145                 _appletCssClass: "",
146                 _appletCssText: "",
147                 _fileCache: null, // enabled by Jmol.setFileCaching(applet, true/false)
148                 _jarFile: null,  // can be set in URL using _JAR=
149                 _j2sPath: null,  // can be set in URL using _J2S=
150                 _use: null,      // can be set in URL using _USE=
151                 _j2sLoadMonitorOpacity: 90, // initial opacity for j2s load monitor message
152                 _applets: {},
153                 _asynchronous: true,
154                 _ajaxQueue: [],
155                 _getZOrders: getZOrders,
156                 _z:getZOrders(z),
157                 _debugCode: true,  // set false in process of minimization
158                 db: {
159                         _databasePrefixes: "$=:",
160                         _fileLoadScript: ";if (_loadScript = '' && defaultLoadScript == '' && _filetype == 'Pdb') { select protein or nucleic;cartoons Only;color structure; select * };",
161                         _nciLoadScript: ";n = ({molecule=1}.length < {molecule=2}.length ? 2 : 1); select molecule=n;display selected;center selected;",
162                         _pubChemLoadScript: "",
163                         _DirectDatabaseCalls:{
164                                 // these sites are known to implement access-control-allow-origin * 
165                                 "cactus.nci.nih.gov": "%URL", 
166                                 "www.rcsb.org": "%URL",
167                                 "pdbe.org": "%URL", 
168                                 "www.ebi.ac.uk": "%URL", 
169                                 "wwwdev.ebi.ac.uk": "%URL", 
170                                 "pubchem.ncbi.nlm.nih.gov":"%URL",
171                                 "http://www.nmrdb.org/tools/jmol/predict.php":"%URL",
172                                 "$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf&get3d=True",
173                                 "$$": "http://cactus.nci.nih.gov/chemical/structure/%FILENCI/file?format=sdf",
174                                 "=": "http://www.rcsb.org/pdb/files/%FILE.pdb",
175                                 "*": "http://www.ebi.ac.uk/pdbe/entry-files/download/%FILE.cif",
176                                 "==": "http://www.rcsb.org/pdb/files/ligand/%FILE.cif",
177                                 ":": "http://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/%FILE/SDF?record_type=3d"
178                         },
179                         _restQueryUrl: "http://www.rcsb.org/pdb/rest/search",
180                         _restQueryXml: "<orgPdbQuery><queryType>org.pdb.query.simple.AdvancedKeywordQuery</queryType><description>Text Search</description><keywords>QUERY</keywords></orgPdbQuery>",
181                         _restReportUrl: "http://www.pdb.org/pdb/rest/customReport?pdbids=IDLIST&customReportColumns=structureId,structureTitle"
182                 },
183                 _debugAlert: false,
184                 _document: document,
185                 _isXHTML: false,
186                 _lastAppletID: null,
187                 _mousePageX: null,
188                 _mouseOwner: null,
189                 _serverUrl: "http://your.server.here/jsmol.php",
190                 _syncId: ("" + Math.random()).substring(3),
191                 _touching: false,
192                 _XhtmlElement: null,
193                 _XhtmlAppendChild: false
194         }
195         
196         var ref = document.location.href.toLowerCase();
197         j._httpProto = (ref.indexOf("https") == 0 ? "https://" : "http://"); 
198         j._isFile = (ref.indexOf("file:") == 0);
199         j._ajaxTestSite = j._httpProto + "google.com";
200         var isLocal = (j._isFile || ref.indexOf("http://localhost") == 0 || ref.indexOf("http://127.") == 0);
201         j._tracker = (j._httpProto == "http://" && !isLocal && 'http://chemapps.stolaf.edu/jmol/JmolTracker.htm?id=UA-45940799-1');
202         
203         j._isChrome = (navigator.userAgent.toLowerCase().indexOf("chrome") >= 0);
204         j._isSafari = (!j._isChrome && navigator.userAgent.toLowerCase().indexOf("safari") >= 0);
205         j._isMsie = (window.ActiveXObject !== undefined);
206         j._useDataURI = !j._isSafari && !j._isMsie; // safari may be OK here -- untested
207
208         for(var i in Jmol) j[i] = Jmol[i]; // allows pre-definition
209         return j;
210 })(document, Jmol);
211
212
213 (function (Jmol, $) {
214
215 // this library is organized into the following sections:
216
217         // jQuery interface 
218         // protected variables
219         // feature detection
220         // AJAX-related core functionality
221         // applet start-up functionality
222         // misc core functionality
223         // mouse events
224
225
226         ////////////////////// jQuery interface ///////////////////////
227
228         // hooks to jQuery -- if you have a different AJAX tool, feel free to adapt.
229         // There should be no other references to jQuery in all the JSmol libraries.
230
231         // automatically switch to returning HTML after the page is loaded
232         $(document).ready(function(){ Jmol._document = null });
233
234         Jmol.$ = function(objectOrId, subdiv) {
235                 // if a subdiv, then return $("#objectOrId._id_subdiv") 
236                 // or if no subdiv, then just $(objectOrId)
237                 if (objectOrId == null)alert (subdiv + arguments.callee.caller.toString());
238                         return $(subdiv ? "#" + objectOrId._id + "_" + subdiv : objectOrId);
239         } 
240
241         Jmol._$ = function(id) {
242                 // either the object or $("#" + id)
243                 return (typeof id == "string" ? $("#" + id) : id);
244         }
245
246         /// special functions:
247
248         Jmol.$ajax = function (info) {
249                 Jmol._ajaxCall = info.url;
250                 info.cache = (info.cache != "NO");
251                 if (info.url.indexOf("http://pubchem.ncbi.nlm.nih") == 0)
252                         info.url = "https://" + info.url.substring(7);
253                         // fluke at pubchem --- requires https now from all pages 3/8/2014
254                 // don't let jQuery add $_=... to URL unless we 
255                 // use cache:"NO"; other packages might use $.ajaxSetup() to set this to cache:false
256                 return $.ajax(info);
257         }
258
259         Jmol._getNCIInfo = function(identifier, what, fCallback) {
260                 return Jmol._getFileData("http://cactus.nci.nih.gov/chemical/structure/"+identifier +"/" + (what == "name" ? "names" : what));
261         }
262         
263
264
265         Jmol.$appEvent = function(app, subdiv, evt, f) {
266                 var o = Jmol.$(app, subdiv); 
267                 o.off(evt) && f && o.on(evt, f);
268         }   
269
270         Jmol.$resize = function (f) {
271                 return $(window).resize(f);
272         }
273
274         //// full identifier expected (could be "body", for example):
275
276         Jmol.$after = function (what, s) {
277                 return $(what).after(s);
278         }
279
280         Jmol.$append = function (what, s) {
281                 return $(what).append(s);
282         }
283
284         Jmol.$bind = function(what, list, f) {
285                 return (f ? $(what).bind(list, f) : $(what).unbind(list));
286         }
287
288         Jmol.$closest = function(what, d) {
289                 return $(what).closest(d);
290         }
291         
292         Jmol.$get = function(what, i) {
293         return $(what).get(i);
294         }
295  
296         // element id expected
297                          
298         Jmol.$documentOff = function(evt, id) {
299                 return $(document).off(evt, "#" + id);
300         }
301
302         Jmol.$documentOn = function(evt, id, f) {
303                 return $(document).on(evt, "#" + id, f);
304         }
305
306         Jmol.$getAncestorDiv = function(id, className) {
307                 return $("div." + className + ":has(#" + id + ")")[0];
308         }
309
310         Jmol.$supportsIECrossDomainScripting = function() {
311                 return $.support.iecors;
312         }
313
314         //// element id or jQuery object expected:
315
316         Jmol.$attr = function (id, a, val) {
317                 return Jmol._$(id).attr(a, val);
318         }
319
320         Jmol.$css = function(id, style) {
321                 return Jmol._$(id).css(style);
322         }
323          
324         Jmol.$find = function(id, d) {
325                 return Jmol._$(id).find(d);
326         }
327         
328         Jmol.$focus = function(id) {
329                 return Jmol._$(id).focus();
330         }
331
332         Jmol.$html = function(id, html) {
333                 return Jmol._$(id).html(html);
334         }
335          
336         Jmol.$offset = function(id) {
337                 return Jmol._$(id).offset();
338         }
339
340         Jmol.$windowOn = function(evt, f) {
341                 return $(window).on(evt, f);
342         }
343
344         Jmol.$prop = function(id, p, val) {
345                 var o = Jmol._$(id);
346                 return (arguments.length == 3 ? o.prop(p, val) : o.prop(p));
347         }
348
349         Jmol.$remove = function(id) {
350                 return Jmol._$(id).remove();
351         }
352
353         Jmol.$scrollTo = function (id, n) {
354                 var o = Jmol._$(id);
355                 return o.scrollTop(n < 0 ? o[0].scrollHeight : n);
356         }
357
358         Jmol.$setEnabled = function(id, b) {
359                 return Jmol._$(id).attr("disabled", b ? null : "disabled");  
360         }
361
362         Jmol.$setSize = function(id, w, h) {
363                 return Jmol._$(id).width(w).height(h);
364         }
365
366         Jmol.$setVisible = function(id, b) {
367                 var o = Jmol._$(id);
368                 return (b ? o.show() : o.hide());  
369         }
370
371         Jmol.$submit = function(id) {
372                 return Jmol._$(id).submit();
373         }
374
375         Jmol.$val = function (id, v) {
376                 var o = Jmol._$(id);
377                 return (arguments.length == 1 ? o.val() : o.val(v));
378         }
379
380         ////////////// protected variables ///////////
381
382
383         Jmol._clearVars = function() {
384                 // only on page closing -- appears to improve garbage collection
385
386                 delete jQuery;
387                 delete $;
388                 delete Jmol;
389                 delete SwingController;
390                 delete J;
391                 delete JM;
392                 delete JS;
393                 delete JSV;
394                 delete JU;
395                 delete JV;
396                 delete java;
397                 delete javajs;
398                 delete Clazz;
399                 delete c$; // used in p0p; could be gotten rid of
400         }
401
402         ////////////// feature detection ///////////////
403
404         Jmol.featureDetection = (function(document, window) {
405
406                 var features = {};
407                 features.ua = navigator.userAgent.toLowerCase()
408
409                 features.os = (function(){
410                         var osList = ["linux","unix","mac","win"]
411                         var i = osList.length;
412
413                         while (i--){
414                                 if (features.ua.indexOf(osList[i])!=-1) return osList[i]
415                         }
416                         return "unknown";
417                 })();
418
419                 features.browser = function(){
420                         var ua = features.ua;
421                         var browserList = ["konqueror","webkit","omniweb","opera","webtv","icab","msie","mozilla"];
422                         for (var i = 0; i < browserList.length; i++)
423                         if (ua.indexOf(browserList[i])>=0) 
424                                 return browserList[i];
425                         return "unknown";
426                 }
427                 features.browserName = features.browser();
428                 features.browserVersion= parseFloat(features.ua.substring(features.ua.indexOf(features.browserName)+features.browserName.length+1));
429                 features.supportsXhr2 = function() {return ($.support.cors || $.support.iecors)}
430                 features.allowDestroy = (features.browserName != "msie");
431                 features.allowHTML5 = (features.browserName != "msie" || navigator.appVersion.indexOf("MSIE 8") < 0);
432                 features.getDefaultLanguage = function() {
433                         return navigator.language || navigator.userLanguage || "en-US";
434                 };
435
436                 features._webGLtest = 0;
437
438                 features.supportsWebGL = function() {
439                 if (!Jmol.featureDetection._webGLtest) { 
440                         var canvas;
441                         Jmol.featureDetection._webGLtest = ( 
442                                 window.WebGLRenderingContext 
443                                         && ((canvas = document.createElement("canvas")).getContext("webgl") 
444                                 || canvas.getContext("experimental-webgl")) ? 1 : -1);
445                 }
446                 return (Jmol.featureDetection._webGLtest > 0);
447         };
448
449         features.supportsLocalization = function() {
450                 //<meta charset="utf-8">                                     
451                 var metas = document.getElementsByTagName('meta'); 
452                 for (var i= metas.length; --i >= 0;) 
453                         if (metas[i].outerHTML.toLowerCase().indexOf("utf-8") >= 0) return true;
454                 return false;
455                 };
456
457         features.supportsJava = function() {
458                 if (!Jmol.featureDetection._javaEnabled) {
459                         if (Jmol._isMsie) {
460                                 if (!navigator.javaEnabled()) {
461                                         Jmol.featureDetection._javaEnabled = -1;
462                                 } else {
463                                         //more likely -- would take huge testing
464                                         Jmol.featureDetection._javaEnabled = 1;
465                                 }
466                         } else {
467                                 Jmol.featureDetection._javaEnabled = (navigator.javaEnabled() && (!navigator.mimeTypes || navigator.mimeTypes["application/x-java-applet"]) ? 1 : -1);
468                         }
469                 }
470                 return (Jmol.featureDetection._javaEnabled > 0);
471         };
472
473         features.compliantBrowser = function() {
474                 var a = !!document.getElementById;
475                 var os = features.os;
476                 // known exceptions (old browsers):
477                 if (features.browserName == "opera" && features.browserVersion <= 7.54 && os == "mac"
478                         || features.browserName == "webkit" && features.browserVersion < 125.12
479                         || features.browserName == "msie" && os == "mac"
480                         || features.browserName == "konqueror" && features.browserVersion <= 3.3
481                 ) a = false;
482                 return a;
483         }
484
485         features.isFullyCompliant = function() {
486                 return features.compliantBrowser() && features.supportsJava();
487         }
488
489         features.useIEObject = (features.os == "win" && features.browserName == "msie" && features.browserVersion >= 5.5);
490         features.useHtml4Object = (features.browserName == "mozilla" && features.browserVersion >= 5) ||
491                 (features.browserName == "opera" && features.browserVersion >= 8) ||
492                 (features.browserName == "webkit"/* && features.browserVersion >= 412.2 && features.browserVersion < 500*/); // 500 is a guess; required for 536.3
493
494         features.hasFileReader = (window.File && window.FileReader);
495
496         return features;
497
498 })(document, window);
499
500
501                 ////////////// AJAX-related core functionality //////////////
502
503         Jmol._ajax = function(info) {
504                 if (!info.async) {
505                         return Jmol.$ajax(info).responseText;
506                 }
507                 Jmol._ajaxQueue.push(info)
508                 if (Jmol._ajaxQueue.length == 1)
509                         Jmol._ajaxDone()
510         }
511         Jmol._ajaxDone = function() {
512                 var info = Jmol._ajaxQueue.shift();
513                 info && Jmol.$ajax(info);
514         }
515
516         Jmol._grabberOptions = [
517                 ["$", "NCI(small molecules)"],
518                 [":", "PubChem(small molecules)"],
519                 ["=", "RCSB(macromolecules)"],
520                 ["*", "PDBe(macromolecules)"]
521         ];
522
523         Jmol._getGrabberOptions = function(applet) {
524                 // feel free to adjust this look to anything you want
525                 if (Jmol._grabberOptions.length == 0)
526                         return ""
527
528
529                 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="" />';
530                 var b = '<button id="ID_submit" onclick="Jmol._applets[\'ID\']._search()">Search</button></nobr>'
531                 if (Jmol._grabberOptions.length == 1) {
532                         s = '<nobr>' + s + '<span style="display:none">';
533                         b = '</span>' + b;
534                 } else {
535                         s += '<br /><nobr>'
536                 }
537                 s += '<select id="ID_select">'
538                 for (var i = 0; i < Jmol._grabberOptions.length; i++) {
539                         var opt = Jmol._grabberOptions[i];
540                         s += '<option value="' + opt[0] + '" ' + (i == 0 ? 'selected' : '') + '>' + opt[1] + '</option>';
541                 }
542                 s = (s + '</select>' + b).replace(/ID/g, applet._id);
543                 return '<br />' + s;
544         }
545
546         Jmol._getScriptForDatabase = function(database) {
547                 return (database == "$" ? Jmol.db._nciLoadScript : database == ":" ? Jmol.db._pubChemLoadScript : Jmol.db._fileLoadScript);
548         }
549
550          //   <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>
551
552         Jmol._setInfo = function(applet, database, data) {
553                 var info = [];
554                 var header = "";
555                 if (data.indexOf("ERROR") == 0)
556                         header = data;
557                 else
558                         switch (database) {
559                         case "=":
560                                 var S = data.split("<dimStructure.structureId>");
561                                 var info = ["<table>"];
562                                 for (var i = 1; i < S.length; i++) {
563                                         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>");
564                                         info.push("<td>" + S[i].split("Title>")[1].split("</")[0] + "</td></tr>");
565                                 }
566                                 info.push("</table>");
567                                 header = (S.length - 1) + " matches";
568                                 break;      
569                         case "$": // NCI
570                         case ":": // pubChem
571                         break;
572                         default:
573                                 return;
574                 }
575                 applet._infoHeader = header;
576                 applet._info = info.join("");
577                 applet._showInfo(true);
578         }
579
580         Jmol._loadSuccess = function(a, fSuccess) {
581                 if (!fSuccess)
582                         return;
583                 Jmol._ajaxDone();
584                 fSuccess(a);
585         }
586
587         Jmol._loadError = function(fError){
588                 Jmol._ajaxDone();
589                 Jmol.say("Error connecting to server: " + Jmol._ajaxCall);  
590                 null!=fError&&fError()
591         }
592
593         Jmol._isDatabaseCall = function(query) {
594                 return (Jmol.db._databasePrefixes.indexOf(query.substring(0, 1)) >= 0);
595         }
596
597         
598         Jmol._getDirectDatabaseCall = function(query, checkXhr2) {
599                 if (checkXhr2 && !Jmol.featureDetection.supportsXhr2())
600                         return query;
601                 var pt = 2;
602                 var db;
603                 var call = Jmol.db._DirectDatabaseCalls[query.substring(0,pt)];
604                 if (!call)
605                         call = Jmol.db._DirectDatabaseCalls[db = query.substring(0,--pt)];
606                 if (call) {
607                         if (db == ":") {
608                                 var ql = query.toLowerCase();
609                                 if (!isNaN(parseInt(query.substring(1)))) {
610                                         query = "cid/" + query.substring(1);
611                                 } else if (ql.indexOf(":smiles:") == 0) {
612                                         call += "?POST?smiles=" + query.substring(8);
613                                         query = "smiles";
614                                 } else if (ql.indexOf(":cid:") == 0) {
615                                         query = "cid/" + query.substring(5);
616                                 } else {
617                                         if (ql.indexOf(":name:") == 0)
618                                                 query = query.substring(5);
619                                         else if (ql.indexOf(":cas:") == 0)
620                                                 query = query.substring(4);
621                                         query = "name/" + encodeURIComponent(query.substring(pt));
622                                 }
623                         } else {
624                                 query = encodeURIComponent(query.substring(pt));                
625                         }
626                         if (call.indexOf("FILENCI") >= 0) {
627                                 query = query.replace(/\%2F/g, "/");                            
628                                 query = call.replace(/\%FILENCI/, query);
629                         } else {
630                                 query = call.replace(/\%FILE/, query);
631                         }
632                 }               
633                 return query;
634         }
635
636         Jmol._getRawDataFromServer = function(database,query,fSuccess,fError,asBase64,noScript){
637           // note that this method is now only enabled for "_"
638           // server-side processing of database queries was too slow and only useful for 
639           // the IMAGE option, which has been abandoned.
640                 var s = 
641                         "?call=getRawDataFromDatabase&database=" + database + (query.indexOf("?POST?") >= 0 ? "?POST?" : "")
642                                 + "&query=" + encodeURIComponent(query)
643                                 + (asBase64 ? "&encoding=base64" : "")
644                                 + (noScript ? "" : "&script=" + encodeURIComponent(Jmol._getScriptForDatabase(database)));
645                 return Jmol._contactServer(s, fSuccess, fError);
646         }
647
648         Jmol._checkFileName = function(applet, fileName, isRawRet) {
649                 if (Jmol._isDatabaseCall(fileName)) {
650                         if (isRawRet)
651                                 Jmol._setQueryTerm(applet, fileName);
652                         fileName = Jmol._getDirectDatabaseCall(fileName, true);
653                         if (Jmol._isDatabaseCall(fileName)) {
654                                 // xhr2 not supported (MSIE)
655                                 fileName = Jmol._getDirectDatabaseCall(fileName, false);
656                                 isRawRet && (isRawRet[0] = true);
657                         }
658                 }
659                 return fileName;
660         }
661         
662         Jmol._checkCache = function(applet, fileName, fSuccess) {
663                 if (applet._cacheFiles && Jmol._fileCache && !fileName.endsWith(".js")) {
664                         var data = Jmol._fileCache[fileName];
665                         if (data) {
666                                 System.out.println("using "  + data.length + " bytes of cached data for "  + fileName);
667                                 fSuccess(data);
668                                 return null;
669                         } else {
670                                 fSuccess = function(fileName, data) { fSuccess(Jmol._fileCache[fileName] = data) };     
671                         }
672                 }
673                 return fSuccess;
674         }
675         
676         Jmol._loadFileData = function(applet, fileName, fSuccess, fError){
677                 var isRaw = [];
678                 fileName = Jmol._checkFileName(applet, fileName, isRaw);
679                 fSuccess = Jmol._checkCache(applet, fileName, fSuccess);
680                 if (isRaw[0]) {
681                                 Jmol._getRawDataFromServer("_",fileName,fSuccess,fError);   
682                                 return;
683                 } 
684                 var info = {
685                         type: "GET",
686                         dataType: "text",
687                         url: fileName,
688                         async: Jmol._asynchronous,
689                         success: function(a) {Jmol._loadSuccess(a, fSuccess)},
690                         error: function() {Jmol._loadError(fError)}
691                 }
692                 Jmol._checkAjaxPost(info);
693                 Jmol._ajax(info);
694         }
695
696         Jmol._getInfoFromDatabase = function(applet, database, query){
697                 if (database == "====") {
698                         var data = Jmol.db._restQueryXml.replace(/QUERY/,query);
699
700                         var info = {
701                                 dataType: "text",
702                                 type: "POST",
703                                 contentType:"application/x-www-form-urlencoded",
704                                 url: Jmol.db._restQueryUrl,
705                                 data: encodeURIComponent(data) + "&req=browser",
706                                 success: function(data) {Jmol._ajaxDone();Jmol._extractInfoFromRCSB(applet, database, query, data)},
707                                 error: function() {Jmol._loadError(null)},
708                                 async: Jmol._asynchronous
709                         }
710                         return Jmol._ajax(info);
711                 }   
712                 query = "?call=getInfoFromDatabase&database=" + database
713                                 + "&query=" + encodeURIComponent(query);
714                 return Jmol._contactServer(query, function(data) {Jmol._setInfo(applet, database, data)});
715         }
716
717         Jmol._extractInfoFromRCSB = function(applet, database, query, output) {
718                 var n = output.length/5;
719                 if (n == 0)
720                         return; 
721                 if (query.length == 4 && n != 1) {
722                         var QQQQ = query.toUpperCase();
723                         var pt = output.indexOf(QQQQ);
724                         if (pt > 0 && "123456789".indexOf(QQQQ.substring(0, 1)) >= 0)
725                                 output = QQQQ + "," + output.substring(0, pt) + output.substring(pt + 5);
726                         if (n > 50)
727                                 output = output.substring(0, 250);
728                         output = output.replace(/\n/g,",");
729                         var url = Jmol._restReportUrl.replace(/IDLIST/,output);
730                         Jmol._loadFileData(applet, url, function(data) {Jmol._setInfo(applet, database, data) });   
731                 }
732         }
733
734         Jmol._checkAjaxPost = function(info) {
735                 var pt = info.url.indexOf("?POST?");
736                 if (pt > 0) {
737                         info.data = info.url.substring(pt + 6);
738                         info.url = info.url.substring(0, pt);
739                         info.type = "POST";
740                         info.contentType = "application/x-www-form-urlencoded";
741                 }
742         }
743         Jmol._contactServer = function(data,fSuccess,fError){
744                 var info = {
745                         dataType: "text",
746                         type: "GET",
747                         url: Jmol._serverUrl + data,
748                         success: function(a) {Jmol._loadSuccess(a, fSuccess)},
749                         error:function() { Jmol._loadError(fError) },
750                         async:fSuccess ? Jmol._asynchronous : false
751                 }
752                 Jmol._checkAjaxPost(info);
753                 return Jmol._ajax(info);
754         }
755
756         Jmol._setQueryTerm = function(applet, query) {
757                 if (!query || !applet._hasOptions || query.substring(0, 7) == "http://")
758                         return;
759                 if (Jmol._isDatabaseCall(query)) {
760                         var database = query.substring(0, 1);
761                         query = query.substring(1);
762                         if (query.substring(0,1) == database && "=$".indexOf(database) >= 0)
763                                 query = query.substring(1);
764                         var d = Jmol._getElement(applet, "select");
765                         if (d && d.options)
766                                 for (var i = 0; i < d.options.length; i++)
767                                         if (d[i].value == database)
768                                                 d[i].selected = true;
769                 }
770                 Jmol.$val(Jmol.$(applet, "query"), query);
771         }
772
773         Jmol._search = function(applet, query, script) {
774                 arguments.length > 1 || (query = null);
775                 Jmol._setQueryTerm(applet, query);
776                 query || (query = Jmol.$val(Jmol.$(applet, "query")))
777                 if (query.indexOf("!") == 0) {
778                 // command prompt in this box as well
779                 // remove exclamation point "immediate" indicator
780                         applet._script(query.substring(1));
781                         return;
782                 } 
783                 query && (query = query.replace(/\"/g, ""));
784                 applet._showInfo(false);
785                 Jmol._searchMol(applet, query, script, true);
786         }
787
788         Jmol._searchMol = function(applet, query, script, checkView) {
789                 var database;
790                 if (Jmol._isDatabaseCall(query)) {
791                         database = query.substring(0, 1);
792                         query = query.substring(1);
793                 } else {
794                         database = (applet._hasOptions ? Jmol.$val(Jmol.$(applet, "select")) : "$");
795                 }
796                 if (database == "=" && query.length == 3)
797                         query = "=" + query; // this is a ligand      
798                 var dm = database + query;
799                 if (!query || dm.indexOf("?") < 0 && dm == applet._thisJmolModel) {
800                         return;    
801                 }
802                 applet._thisJmolModel = dm;
803                 var view;
804                 if (checkView && applet._viewSet != null && (view = Jmol.View.__findView(applet._viewSet, {chemID:dm})) != null) {
805                         Jmol.View.__setView(view, applet, false);
806                         return;
807                 }
808
809                 if (database == "$" || database == ":")
810                         applet._jmolFileType = "MOL";
811                 else if (database == "=")
812                         applet._jmolFileType = "PDB";
813                 applet._searchDatabase(query, database, script);
814         }
815
816         Jmol._searchDatabase = function(applet, query, database, script) {
817                 applet._showInfo(false);
818                 if (query.indexOf("?") >= 0) {
819                         Jmol._getInfoFromDatabase(applet, database, query.split("?")[0]);
820                         return true;
821                 }
822                 if (Jmol.db._DirectDatabaseCalls[database]) {
823                         applet._loadFile(database + query, script);
824                         return true;
825                 }
826                 return false;
827         }
828
829         Jmol._syncBinaryOK="?";
830
831         Jmol._canSyncBinary = function(isSilent) {
832                 if (Jmol._isAsync) return true;
833                 if (self.VBArray) return (Jmol._syncBinaryOK = false);
834                 if (Jmol._syncBinaryOK != "?") return Jmol._syncBinaryOK;
835                 Jmol._syncBinaryOK = true;
836                 try {
837                         var xhr = new window.XMLHttpRequest();
838                         xhr.open( "text", Jmol._ajaxTestSite, false );
839                         if (xhr.hasOwnProperty("responseType")) {
840                                 xhr.responseType = "arraybuffer";
841                         } else if (xhr.overrideMimeType) {
842                                 xhr.overrideMimeType('text/plain; charset=x-user-defined');
843                         }
844                 } catch( e ) {
845                         var s = "JSmolCore.js: synchronous binary file transfer is requested but not available";
846                         System.out.println(s);
847                         if (Jmol._alertNoBinary && !isSilent)
848                                 alert (s)
849                         return Jmol._syncBinaryOK = false;
850                 }
851                 return true;  
852         }
853
854         Jmol._binaryTypes = [".gz",".jpg",".gif",".png",".zip",".jmol",".bin",".smol",".spartan",".mrc",".pse", ".map", ".omap"];
855
856         Jmol._isBinaryUrl = function(url) {
857                 for (var i = Jmol._binaryTypes.length; --i >= 0;)
858                         if (url.indexOf(Jmol._binaryTypes[i]) >= 0) return true;
859                 return false;
860         }
861
862         Jmol._getFileData = function(fileName, fSuccess, doProcess) {
863                 // use host-server PHP relay if not from this host
864                 var isBinary = Jmol._isBinaryUrl(fileName);
865                 var isPDB = (fileName.indexOf("pdb.gz") >= 0 && fileName.indexOf("http://www.rcsb.org/pdb/files/") == 0);
866                 var asBase64 = (isBinary && !Jmol._canSyncBinary(isPDB));
867                 if (asBase64 && isPDB) {
868                         // avoid unnecessary binary transfer
869                         fileName = fileName.replace(/pdb\.gz/,"pdb");
870                         asBase64 = isBinary = false;
871                 }
872                 var isPost = (fileName.indexOf("?POST?") >= 0);
873                 if (fileName.indexOf("file:/") == 0 && fileName.indexOf("file:///") != 0)
874                         fileName = "file://" + fileName.substring(5);      /// fixes IE problem
875                 var isMyHost = (fileName.indexOf("://") < 0 || fileName.indexOf(document.location.protocol) == 0 && fileName.indexOf(document.location.host) >= 0);
876                 var isDirectCall = Jmol._isDirectCall(fileName);
877                 //if (fileName.indexOf("http://pubchem.ncbi.nlm.nih.gov/") == 0)isDirectCall = false;
878
879                 var cantDoSynchronousLoad = (!isMyHost && Jmol.$supportsIECrossDomainScripting());
880                 var data = null;
881                 if ((!fSuccess || asBase64) && (cantDoSynchronousLoad || asBase64 || !isMyHost && !isDirectCall)) {
882                                 data = Jmol._getRawDataFromServer("_",fileName, fSuccess, fSuccess, asBase64, true);
883                 } else {
884                         fileName = fileName.replace(/file:\/\/\/\//, "file://"); // opera
885                         var info = {dataType:(isBinary ? "binary" : "text"),async:!!fSuccess};
886                         if (isPost) {
887                                 info.type = "POST";
888                                 info.url = fileName.split("?POST?")[0]
889                                 info.data = fileName.split("?POST?")[1]
890                         } else {
891                                 info.type = "GET";
892                                 info.url = fileName;
893                         }
894                         if (fSuccess) {
895                                 info.success = function(data) { fSuccess(Jmol._xhrReturn(info.xhr))};
896                                 info.error = function() { fSuccess(info.xhr.statusText)};
897                         }
898                         info.xhr = Jmol.$ajax(info);
899                         if (!fSuccess) {
900                                 data = Jmol._xhrReturn(info.xhr);
901                         }
902                 }
903                 if (!doProcess)
904                         return data;
905                 if (data == null) {
906                         data = "";
907                         isBinary = false;
908                 }
909                 isBinary && (isBinary = Jmol._canSyncBinary(true));
910                 return (isBinary ? Jmol._strToBytes(data) : JU.SB.newS(data));
911         }
912         
913         Jmol._xhrReturn = function(xhr){
914                 if (!xhr.responseText || self.Clazz && Clazz.instanceOf(xhr.response, self.ArrayBuffer)) {
915                         // Safari or error 
916                         return xhr.response || xhr.statusText;
917                 } 
918                 return xhr.responseText;
919         }
920
921         Jmol._isDirectCall = function(url) {
922                 for (var key in Jmol.db._DirectDatabaseCalls) {
923                         if (key.indexOf(".") >= 0 && url.indexOf(key) >= 0)
924                                 return true;
925                 }
926                 return false;
927         }
928
929         Jmol._cleanFileData = function(data) {
930                 if (data.indexOf("\r") >= 0 && data.indexOf("\n") >= 0) {
931                         return data.replace(/\r\n/g,"\n");
932                 }
933                 if (data.indexOf("\r") >= 0) {
934                         return data.replace(/\r/g,"\n");
935                 }
936                 return data;
937         };
938
939         Jmol._getFileType = function(name) {
940                 var database = name.substring(0, 1);
941                 if (database == "$" || database == ":")
942                         return "MOL";
943                 if (database == "=")
944                         return (name.substring(1,2) == "=" ? "LCIF" : "PDB");
945                 // just the extension, which must be PDB, XYZ..., CIF, or MOL
946                 name = name.split('.').pop().toUpperCase();
947                 return name.substring(0, Math.min(name.length, 3));
948         };
949
950         Jmol._getZ = function(applet, what) {
951                 return applet && applet._z && applet._z[what] || Jmol._z[what];
952         }
953         
954         Jmol._incrZ = function(applet, what) {
955                 return applet && applet._z && ++applet._z[what] || ++Jmol._z[what];
956         }
957         
958         Jmol._loadFileAsynchronously = function(fileLoadThread, applet, fileName, appData) {
959                 if (fileName.indexOf("?") != 0) {
960                         // LOAD ASYNC command
961                         var fileName0 = fileName;
962                         fileName = Jmol._checkFileName(applet, fileName);
963                         var fSuccess = function(data) {Jmol._setData(fileLoadThread, fileName, fileName0, data, appData)};
964                         fSuccess = Jmol._checkCache(applet, fileName, fSuccess);
965                         if (fileName.indexOf("|") >= 0)
966                                 fileName = fileName.split("|")[0];
967                         return (fSuccess == null ? null : Jmol._getFileData(fileName, fSuccess));               
968                 }
969                 // we actually cannot suggest a fileName, I believe.
970                 if (!Jmol.featureDetection.hasFileReader)
971                                 return fileLoadThread.setData("Local file reading is not enabled in your browser", null, null, appData);
972                 if (!applet._localReader) {
973                         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>'
974                         Jmol.$after("#" + applet._id + "_appletdiv", div.replace(/ID/g, applet._id + "_localReader"));
975                         applet._localReader = Jmol.$(applet, "localReader");
976                 }
977                 Jmol.$appEvent(applet, "localReader_loadfile", "click");
978                 Jmol.$appEvent(applet, "localReader_loadfile", "click", function(evt) {
979                         var file = Jmol.$(applet, "localReader_files")[0].files[0];   
980                         var reader = new FileReader();
981                         reader.onloadend = function(evt) {
982                                 if (evt.target.readyState == FileReader.DONE) { // DONE == 2
983                                         Jmol.$css(Jmol.$(applet, "localReader"), {display : "none"});
984                                         Jmol._setData(fileLoadThread, file.name, file.name, evt.target.result, appData);
985                                 }
986                         };
987                         reader.readAsArrayBuffer(file);
988                 });
989                 Jmol.$appEvent(applet, "localReader_cancel", "click");
990                 Jmol.$appEvent(applet, "localReader_cancel", "click", function(evt) {
991                         Jmol.$css(Jmol.$(applet, "localReader"), {display: "none"});
992                         fileLoadThread.setData(null, null, null, appData);
993                 });
994                 Jmol.$css(Jmol.$(applet, "localReader"), {display : "block"});
995         }
996
997   Jmol._setData = function(fileLoadThread, filename, filename0, data, appData) {
998         data = Jmol._strToBytes(data);
999                 if (filename.indexOf(".jdx") >= 0)
1000                         Jmol.Cache.put("cache://" + filename, data);
1001                 fileLoadThread.setData(filename, filename0, data, appData);
1002   }
1003   
1004         Jmol._toBytes = function(data) {
1005         if (typeof data == "string") 
1006                 return data.getBytes();
1007         // ArrayBuffer assumed here
1008         data = new Uint8Array(data);
1009         var b = Clazz.newByteArray(data.length, 0);
1010                 for (var i = data.length; --i >= 0;)
1011                         b[i] = data[i];
1012         return b;
1013         }
1014
1015         Jmol._doAjax = function(url, postOut, dataOut) {
1016                 // called by org.jmol.awtjs2d.JmolURLConnection.doAjax()
1017                 url = url.toString();
1018
1019                 if (dataOut != null) 
1020                         return Jmol._saveFile(url, dataOut);
1021                 if (postOut)
1022                         url += "?POST?" + postOut;
1023                 return Jmol._getFileData(url, null, true);
1024         }
1025
1026         // Jmol._localFileSaveFunction --  // do something local here; Maybe try the FileSave interface? return true if successful
1027          
1028         Jmol._saveFile = function(filename, data, mimetype, encoding) {
1029                 if (Jmol._localFileSaveFunction && Jmol._localFileSaveFunction(filename, data))
1030                         return "OK";
1031                 var filename = filename.substring(filename.lastIndexOf("/") + 1);
1032                 mimetype || (mimetype = (filename.indexOf(".pdf") >= 0 ? "application/pdf" 
1033                         : filename.indexOf(".png") >= 0 ? "image/png" 
1034                         : filename.indexOf(".gif") >= 0 ? "image/gif" 
1035                         : filename.indexOf(".jpg") >= 0 ? "image/jpg" : ""));
1036                 var isString = (typeof data == "string");
1037                 if (!isString)
1038                         data = (JU ? JU : J.util).Base64.getBase64(data).toString();
1039                 encoding || (encoding = (isString ? "" : "base64"));
1040                 var url = Jmol._serverUrl;
1041                 url && url.indexOf("your.server") >= 0 && (url = "");
1042                 if (Jmol._useDataURI || !url) {
1043                         // Asynchronous output generated using an anchor tag
1044                         encoding || (data = btoa(data));
1045                         var a = document.createElement("a");
1046                         a.href = "data:" + mimetype + ";base64," + data;
1047                         a.type = mimetype || (mimetype = "text/plain"); 
1048                         a.download = filename;
1049                         a.target = "_blank";
1050                                 $("body").append(a);
1051                         a.click();
1052                         a.remove();             
1053                 } else {
1054                 // Asynchronous outputto be reflected as a download
1055                         if (!Jmol._formdiv) {
1056                                         var sform = '<div id="__jsmolformdiv__" style="display:none">\
1057                                                 <form id="__jsmolform__" method="post" target="_blank" action="">\
1058                                                 <input name="call" value="saveFile"/>\
1059                                                 <input id="__jsmolmimetype__" name="mimetype" value=""/>\
1060                                                 <input id="__jsmolencoding__" name="encoding" value=""/>\
1061                                                 <input id="__jsmolfilename__" name="filename" value=""/>\
1062                                                 <textarea id="__jsmoldata__" name="data"></textarea>\
1063                                                 </form>\
1064                                                 </div>'
1065                                 Jmol.$after("body", sform);
1066                                 Jmol._formdiv = "__jsmolform__";
1067                         }
1068                         Jmol.$attr(Jmol._formdiv, "action", url + "?" + (new Date()).getMilliseconds());
1069                         Jmol.$val("__jsmoldata__", data);
1070                         Jmol.$val("__jsmolfilename__", filename);
1071                         Jmol.$val("__jsmolmimetype__", mimetype);
1072                         Jmol.$val("__jsmolencoding__", encoding);
1073                         Jmol.$submit("__jsmolform__");
1074                         Jmol.$val("__jsmoldata__", "");
1075                         Jmol.$val("__jsmolfilename__", "");
1076                 }
1077                 return "OK";
1078         }
1079
1080         Jmol._strToBytes = function(s) {
1081                 if (Clazz.instanceOf(s, self.ArrayBuffer))
1082                         return Jmol._toBytes(s);
1083                 var b = Clazz.newByteArray(s.length, 0);
1084                 for (var i = s.length; --i >= 0;)
1085                         b[i] = s.charCodeAt(i) & 0xFF;
1086                 return b;
1087         }
1088
1089         ////////////// applet start-up functionality //////////////
1090
1091         Jmol._setConsoleDiv = function (d) {
1092                 if (!self.Clazz)return;
1093                 Clazz.setConsoleDiv(d);
1094         }
1095
1096         Jmol._registerApplet = function(id, applet) {
1097                 return window[id] = Jmol._applets[id] = Jmol._applets[applet] = Jmol._applets[id + "__" + Jmol._syncId + "__"] = applet;
1098         } 
1099
1100         Jmol._readyCallback = function (appId,fullId,isReady,javaApplet,javaAppletPanel) {
1101                 appId = appId.split("_object")[0];
1102     javaAppletPanel || (javaAppletPanel = javaApplet);
1103                 isReady = (isReady.booleanValue ? isReady.booleanValue() : isReady);
1104                 // necessary for MSIE in strict mode -- apparently, we can't call 
1105                 // jmol._readyCallback, but we can call Jmol._readyCallback. Go figure...
1106     var applet = Jmol._applets[appId];
1107     applet._appletPanel = javaAppletPanel;
1108     applet._applet = javaApplet;
1109                 Jmol._track(applet._readyCallback(appId, fullId, isReady));
1110         }
1111
1112         Jmol._getWrapper = function(applet, isHeader) {
1113
1114                         // id_appletinfotablediv
1115                         //     id_appletdiv
1116                         //     id_coverdiv
1117                         //     id_infotablediv
1118                         //       id_infoheaderdiv
1119                         //          id_infoheaderspan
1120                         //          id_infocheckboxspan
1121                         //       id_infodiv
1122
1123
1124                         // for whatever reason, without DOCTYPE, with MSIE, "height:auto" does not work, 
1125                         // and the text scrolls off the page.
1126                         // So I'm using height:95% here.
1127                         // The table was a fix for MSIE with no DOCTYPE tag to fix the miscalculation
1128                         // in height of the div when using 95% for height. 
1129                         // But it turns out the table has problems with DOCTYPE tags, so that's out. 
1130                         // The 95% is a compromise that we need until the no-DOCTYPE MSIE solution is found. 
1131                         // (100% does not work with the JME linked applet)
1132                 var s;
1133                 // ... here are just for clarification in this code; they are removed immediately
1134                 if (isHeader) {
1135                         var img = ""; 
1136                         if (applet._coverImage){
1137                                 var more = " onclick=\"Jmol.coverApplet(ID, false)\" title=\"" + (applet._coverTitle) + "\"";
1138                                 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;"
1139                                         + "z-index:" + Jmol._getZ(applet, "coverImage")+";opacity:0.5;\"" + more + " />"  
1140                                 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=\""
1141                                  + applet._coverImage + "\" style=\"width:100%;height:100%\"" + more + "/>" + play + "</div>";
1142                         }
1143                         var css = Jmol._appletCssText.replace(/\'/g,'"');
1144                         css = (css.indexOf("style=\"") >= 0 ? css.split("style=\"")[1] : "\" " + css);
1145                         s = "\
1146 ...<div id=\"ID_appletinfotablediv\" style=\"width:Wpx;height:Hpx;position:relative;font-size:14px;text-align:left\">IMG\
1147 ......<div id=\"ID_appletdiv\" style=\"z-index:" + Jmol._getZ(applet, "header") + ";width:100%;height:100%;position:absolute;top:0px;left:0px;" + css + ">";
1148                         var height = applet._height;
1149                         var width = applet._width;
1150                         if (typeof height !== "string" || height.indexOf("%") < 0) 
1151                                 height += "px";
1152                         if (typeof width !== "string" || width.indexOf("%") < 0)
1153                                 width += "px";
1154                         s = s.replace(/IMG/, img).replace(/Hpx/g, height).replace(/Wpx/g, width);
1155                 } else {
1156                         s = "\
1157 ......</div>\
1158 ......<div id=\"ID_2dappletdiv\" style=\"position:absolute;width:100%;height:100%;overflow:hidden;display:none\"></div>\
1159 ......<div id=\"ID_infotablediv\" style=\"width:100%;height:100%;position:absolute;top:0px;left:0px\">\
1160 .........<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>\
1161 .........<div id=\"ID_infodiv\" style=\"position:absolute;top:20px;bottom:0px;width:100%;height:100%;overflow:auto\"></div>\
1162 ......</div>\
1163 ...</div>";
1164                 }
1165                 return s.replace(/\.\.\./g,"").replace(/[\n\r]/g,"").replace(/ID/g, applet._id);
1166         }
1167
1168         Jmol._documentWrite = function(text) {
1169                 if (Jmol._document) {
1170                         if (Jmol._isXHTML && !Jmol._XhtmlElement) {
1171                                 var s = document.getElementsByTagName("script");
1172                                 Jmol._XhtmlElement = s.item(s.length - 1);
1173                                 Jmol._XhtmlAppendChild = false;
1174                         }
1175                         if (Jmol._XhtmlElement)
1176                                 Jmol._domWrite(text);
1177                         else
1178                                 Jmol._document.write(text);
1179                 }
1180                 return text;
1181         }
1182
1183         Jmol._domWrite = function(data) {
1184                 var pt = 0
1185                 var Ptr = []
1186                 Ptr[0] = 0
1187                 while (Ptr[0] < data.length) {
1188                         var child = Jmol._getDomElement(data, Ptr);
1189                         if (!child)
1190                                 break;
1191                         if (Jmol._XhtmlAppendChild)
1192                                 Jmol._XhtmlElement.appendChild(child);
1193                         else
1194                                 Jmol._XhtmlElement.parentNode.insertBefore(child, _jmol.XhtmlElement);
1195                 }
1196         }
1197
1198         Jmol._getDomElement = function(data, Ptr, closetag, lvel) {
1199
1200                 // there is no "document.write" in XHTML
1201
1202                 var e = document.createElement("span");
1203                 e.innerHTML = data;
1204                 Ptr[0] = data.length;
1205
1206 /*
1207         // unnecessary ?  
1208
1209                 closetag || (closetag = "");
1210                 lvel || (lvel = 0);
1211                 var pt0 = Ptr[0];
1212                 var pt = pt0;
1213                 while (pt < data.length && data.charAt(pt) != "<") 
1214                         pt++
1215                 if (pt != pt0) {
1216                         var text = data.substring(pt0, pt);
1217                         Ptr[0] = pt;
1218                         return document.createTextNode(text);
1219                 }
1220                 pt0 = ++pt;
1221                 var ch;
1222                 while (pt < data.length && "\n\r\t >".indexOf(ch = data.charAt(pt)) < 0) 
1223                         pt++;
1224                 var tagname = data.substring(pt0, pt);
1225                 var e = (tagname == closetag  || tagname == "/" ? ""
1226                         : document.createElementNS ? document.createElementNS('http://www.w3.org/1999/xhtml', tagname)
1227                         : document.createElement(tagname));
1228                 if (ch == ">") {
1229                         Ptr[0] = ++pt;
1230                         return e;
1231                 }
1232                 while (pt < data.length && (ch = data.charAt(pt)) != ">") {
1233                         while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) 
1234                                 pt++;
1235                         pt0 = pt;
1236                         while (pt < data.length && "\n\r\t =/>".indexOf(ch = data.charAt(pt)) < 0) 
1237                                 pt++;
1238                         var attrname = data.substring(pt0, pt).toLowerCase();
1239                         if (attrname && ch != "=")
1240                                 e.setAttribute(attrname, "true");
1241                         while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) 
1242                                 pt++;
1243                         if (ch == "/") {
1244                                 Ptr[0] = pt + 2;
1245                                 return e;
1246                         } else if (ch == "=") {
1247                                 var quote = data.charAt(++pt);
1248                                 pt0 = ++pt;
1249                                 while (pt < data.length && (ch = data.charAt(pt)) != quote) 
1250                                         pt++;
1251                                 var attrvalue = data.substring(pt0, pt);
1252                                 e.setAttribute(attrname, attrvalue);
1253                                 pt++;
1254                         }
1255                 }
1256                 Ptr[0] = ++pt;
1257                 while (Ptr[0] < data.length) {
1258                         var child = Jmol._getDomElement(data, Ptr, "/" + tagname, lvel+1);
1259                         if (!child)
1260                                 break;
1261                         e.appendChild(child);
1262                 }
1263 */
1264                 return e;    
1265         }
1266
1267         Jmol._setObject = function(obj, id, Info) {
1268                 obj._id = id;
1269                 obj.__Info = {};
1270                 Info.z && Info.zIndexBase && (Jmol._z = Jmol._getZOrders(Info.zIndexBase));
1271                 for (var i in Info)
1272                         obj.__Info[i] = Info[i];
1273                 (obj._z = Info.z) || Info.zIndexBase && (obj._z = obj.__Info.z = Jmol._getZOrders(Info.zIndexBase));
1274                 obj._width = Info.width;
1275                 obj._height = Info.height;
1276                 obj._noscript = !obj._isJava && Info.noscript;
1277                 obj._console = Info.console;
1278                 obj._cacheFiles = !!Info.cacheFiles;
1279                 obj._viewSet = (Info.viewSet == null || obj._isJava ? null : "Set" + Info.viewSet);
1280                 if (obj._viewSet != null) {
1281                         Jmol.View.__init(obj);
1282                         obj._currentView = null;
1283                 }
1284                 !Jmol._fileCache && obj._cacheFiles && (Jmol._fileCache = {});
1285                 if (!obj._console)
1286                         obj._console = obj._id + "_infodiv";
1287                 if (obj._console == "none")
1288                         obj._console = null;
1289
1290                 obj._color = (Info.color ? Info.color.replace(/0x/,"#") : "#FFFFFF");
1291                 obj._disableInitialConsole = Info.disableInitialConsole;
1292                 obj._noMonitor = Info.disableJ2SLoadMonitor;
1293                 Jmol._j2sPath && (Info.j2sPath = Jmol._j2sPath);
1294                 obj._j2sPath = Info.j2sPath;
1295                 obj._coverImage = Info.coverImage;
1296                 obj._isCovered = !!obj._coverImage; 
1297                 obj._deferApplet = Info.deferApplet || obj._isCovered && obj._isJava; // must do this if covered in Java
1298                 obj._deferUncover = Info.deferUncover && !obj._isJava; // can't do this with Java
1299                 obj._coverScript = Info.coverScript;
1300                 obj._coverTitle = Info.coverTitle;
1301
1302                 if (!obj._coverTitle)
1303                         obj._coverTitle = (obj._deferApplet ? "activate 3D model" : "3D model is loading...")
1304                 obj._containerWidth = obj._width + ((obj._width==parseFloat(obj._width))? "px":"");
1305                 obj._containerHeight = obj._height + ((obj._height==parseFloat(obj._height))? "px":"");
1306                 obj._info = "";
1307                 obj._infoHeader = obj._jmolType + ' "' + obj._id + '"'
1308                 obj._hasOptions = Info.addSelectionOptions;
1309                 obj._defaultModel = Info.defaultModel;
1310                 obj._readyScript = (Info.script ? Info.script : "");
1311                 obj._readyFunction = Info.readyFunction;
1312                 if (obj._coverImage && !obj._deferApplet)
1313                         obj._readyScript += ";javascript " + id + "._displayCoverImage(false)";
1314                 obj._src = Info.src;
1315
1316         }
1317
1318         Jmol._addDefaultInfo = function(Info, DefaultInfo) {
1319                 for (var x in DefaultInfo)
1320                         if (typeof Info[x] == "undefined")
1321                                 Info[x] = DefaultInfo[x];
1322                 Jmol._use && (Info.use = Jmol._use);
1323                 if (Info.use.indexOf("SIGNED") >= 0) {
1324                         if (Info.jarFile.indexOf("Signed") < 0)
1325                                 Info.jarFile = Info.jarFile.replace(/Applet/,"AppletSigned");
1326                         Info.use = Info.use.replace(/SIGNED/, "JAVA");
1327                         Info.isSigned = true;
1328                 }
1329         }
1330
1331         Jmol._syncedApplets = [];
1332         Jmol._syncedCommands = [];
1333         Jmol._syncedReady = [];
1334         Jmol._syncReady = false;
1335         Jmol._isJmolJSVSync = false;
1336
1337         Jmol._setReady = function(applet) {
1338                 Jmol._syncedReady[applet] = 1;
1339                 var n = 0;
1340                 for (var i = 0; i < Jmol._syncedApplets.length; i++) {
1341                         if (Jmol._syncedApplets[i] == applet._id) {
1342                                 Jmol._syncedApplets[i] = applet;
1343                                 Jmol._syncedReady[i] = 1;
1344                         } else if (!Jmol._syncedReady[i]) {
1345                                 continue;
1346                         }
1347                         n++;
1348                 }
1349                 if (n != Jmol._syncedApplets.length)
1350                         return;
1351                 Jmol._setSyncReady();
1352         }
1353
1354         Jmol._setDestroy = function(applet) {
1355                 //MSIE bug responds to any link click even if it is just a JavaScript call
1356
1357                 if (Jmol.featureDetection.allowDestroy)
1358                         Jmol.$windowOn('beforeunload', function () { Jmol._destroy(applet); } );
1359         }
1360
1361         Jmol._destroy = function(applet) {
1362                 try {
1363                         if (applet._appletPanel) applet._appletPanel.destroy();
1364                         applet._applet = null;
1365                         Jmol._unsetMouse(applet._canvas)
1366                         applet._canvas = null;
1367                         var n = 0;
1368                         for (var i = 0; i < Jmol._syncedApplets.length; i++) {
1369                                 if (Jmol._syncedApplets[i] == applet)
1370                                         Jmol._syncedApplets[i] = null;
1371                                 if (Jmol._syncedApplets[i])
1372                                         n++;
1373                         }
1374                         if (n > 0)
1375                                 return;
1376                         Jmol._clearVars();
1377                 } catch(e){}
1378         }
1379
1380         ////////////// misc core functionality //////////////
1381
1382         Jmol._setSyncReady = function() {
1383                 Jmol._syncReady = true;
1384                 var s = ""
1385                 for (var i = 0; i < Jmol._syncedApplets.length; i++)
1386                         if (Jmol._syncedCommands[i])
1387                                 s += "Jmol.script(Jmol._syncedApplets[" + i + "], Jmol._syncedCommands[" + i + "]);"
1388                 setTimeout(s, 50);  
1389         }
1390
1391         Jmol._mySyncCallback = function(appFullName,msg) {
1392                 app = Jmol._applets[appFullName];
1393                 if (app._viewSet) {
1394                         // when can we do this?
1395 //                      if (app._viewType == "JSV" && !app._currentView.JMOL)
1396                                 Jmol.View.updateFromSync(app, msg);
1397                         return;
1398                 }
1399                 if(!Jmol._syncReady || !Jmol._isJmolJSVSync)
1400                         return 1; // continue processing and ignore me
1401                 for (var i = 0; i < Jmol._syncedApplets.length; i++) {
1402                         if (msg.indexOf(Jmol._syncedApplets[i]._syncKeyword) >= 0)
1403                                 Jmol._syncedApplets[i]._syncScript(msg);
1404                 }
1405                 return 0 // prevents further Jmol sync processing 
1406         }              
1407
1408         Jmol._getElement = function(applet, what) {
1409                 var d = document.getElementById(applet._id + "_" + what);
1410                 return (d || {});
1411         } 
1412          
1413         Jmol._evalJSON = function(s,key){
1414                 s = s + "";
1415                 if(!s)
1416                         return [];
1417                 if(s.charAt(0) != "{") {
1418                         if(s.indexOf(" | ") >= 0)
1419                                 s = s.replace(/\ \|\ /g, "\n");
1420                         return s;
1421                 }
1422                 var A = (new Function( "return " + s ) )();
1423                 return (!A ? null : key && A[key] != undefined ? A[key] : A);
1424         }
1425
1426         Jmol._sortMessages = function(A){
1427                 /*
1428                  * private function
1429                  */
1430                 function _sortKey0(a,b){
1431                         return (a[0]<b[0]?1:a[0]>b[0]?-1:0);
1432                 }
1433
1434                 if(!A || typeof (A) != "object")
1435                         return [];
1436                 var B = [];
1437                 for(var i = A.length - 1; i >= 0; i--)
1438                         for(var j = 0, jj= A[i].length; j < jj; j++)
1439                                 B[B.length] = A[i][j];
1440                 if(B.length == 0)
1441                         return;
1442                 B = B.sort(_sortKey0);
1443                 return B;
1444         }
1445
1446         //////////////////// mouse events //////////////////////
1447
1448         Jmol._setMouseOwner = function(who, tf) {
1449                 if (who == null || tf)
1450                         Jmol._mouseOwner = who;
1451                 else if (Jmol._mouseOwner == who)
1452                         Jmol._mouseOwner = null;
1453         }
1454
1455         Jmol._jsGetMouseModifiers = function(ev) {
1456                 var modifiers = 0;
1457                 switch (ev.button) {
1458                 case 0:
1459                         modifiers = 16;//J.api.Event.MOUSE_LEFT;
1460                         break;
1461                 case 1:
1462                         modifiers = 8;//J.api.Event.MOUSE_MIDDLE;
1463                         break;
1464                 case 2:
1465                         modifiers = 4;//J.api.Event.MOUSE_RIGHT;
1466                         break;
1467                 }
1468                 if (ev.shiftKey)
1469                         modifiers += 1;//J.api.Event.SHIFT_MASK;
1470                 if (ev.altKey)
1471                         modifiers += 8;//J.api.Event.ALT_MASK;
1472                 if (ev.ctrlKey)
1473                         modifiers += 2;//J.api.Event.CTRL_MASK;
1474                 return modifiers;
1475         }
1476
1477         Jmol._jsGetXY = function(canvas, ev) {
1478                 if (!canvas.applet._ready || Jmol._touching && ev.type.indexOf("touch") < 0)
1479                         return false;
1480                 //ev.preventDefault(); // removed 5/9/2015 -- caused loss of focus on text-box clicking in SwingJS
1481                 var offsets = Jmol.$offset(canvas.id);
1482                 var x, y;
1483                 var oe = ev.originalEvent;
1484                 // drag-drop jQuery event is missing pageX
1485                 ev.pageX || (ev.pageX = oe.pageX);
1486                 ev.pageY || (ev.pageY = oe.pageY);
1487                 Jmol._mousePageX = ev.pageX;
1488                 Jmol._mousePageY = ev.pageY;
1489                 if (oe.targetTouches && oe.targetTouches[0]) {
1490                         x = oe.targetTouches[0].pageX - offsets.left;
1491                         y = oe.targetTouches[0].pageY - offsets.top;
1492                 } else if (oe.changedTouches) {
1493                         x = oe.changedTouches[0].pageX - offsets.left;
1494                         y = oe.changedTouches[0].pageY - offsets.top;
1495                 } else {
1496                         x = ev.pageX - offsets.left;
1497                         y = ev.pageY - offsets.top;
1498                 }
1499                 return (x == undefined ? null : [Math.round(x), Math.round(y), Jmol._jsGetMouseModifiers(ev)]);
1500         }
1501
1502         Jmol._gestureUpdate = function(canvas, ev) {
1503                 ev.stopPropagation();
1504                 ev.preventDefault();
1505                 var oe = ev.originalEvent;
1506                 switch (ev.type) {
1507                 case "touchstart":
1508                         Jmol._touching = true;
1509                         break;
1510                 case "touchend":
1511                         Jmol._touching = false;
1512                         break;
1513                 }
1514                 if (!oe.touches || oe.touches.length != 2) return false;
1515                 switch (ev.type) {
1516                 case "touchstart":
1517                         canvas._touches = [[],[]];
1518                         break;
1519                 case "touchmove":
1520                         var offsets = Jmol.$offset(canvas.id);
1521                         var t0 = canvas._touches[0];
1522                         var t1 = canvas._touches[1];
1523                         t0.push([oe.touches[0].pageX - offsets.left, oe.touches[0].pageY - offsets.top]);
1524                         t1.push([oe.touches[1].pageX - offsets.left, oe.touches[1].pageY - offsets.top]);
1525                         var n = t0.length;
1526                         if (n > 3) {
1527                                 t0.shift();
1528                                 t1.shift();
1529                         }
1530                         if (n >= 2)
1531                                 canvas.applet._processGesture(canvas._touches);
1532                         break;
1533                 }
1534                 return true;
1535         }  
1536
1537         Jmol._jsSetMouse = function(canvas) {
1538
1539     var doIgnore = function(ev) { return (ev.target.className.indexOf("swingjs-ui") >= 0) };
1540          
1541                 Jmol.$bind(canvas, 'mousedown touchstart', function(ev) {
1542       if (doIgnore(ev))
1543         return true;
1544                         Jmol._setMouseOwner(canvas, true);
1545                         ev.stopPropagation();
1546       var ui = ev.target["data-UI"];
1547       if (!ui || !ui.handleJSEvent(canvas, 501, ev)) 
1548                         ev.preventDefault();
1549                         canvas.isDragging = true;
1550                         if ((ev.type == "touchstart") && Jmol._gestureUpdate(canvas, ev))
1551                                 return !!ui;
1552                         Jmol._setConsoleDiv(canvas.applet._console);
1553                         var xym = Jmol._jsGetXY(canvas, ev);
1554                         if(xym) {
1555                         if (ev.button != 2)
1556           Jmol.Swing.hideMenus(canvas.applet);
1557         canvas.applet._processEvent(501, xym); //java.awt.Event.MOUSE_DOWN
1558       }
1559                         return !!ui;
1560                 });
1561     
1562                 Jmol.$bind(canvas, 'mouseup touchend', function(ev) {
1563       if (doIgnore(ev))
1564         return true;
1565                         Jmol._setMouseOwner(null);
1566                         ev.stopPropagation();
1567       var ui = ev.target["data-UI"];
1568       if (!ui || !ui.handleJSEvent(canvas, 502, ev))
1569                         ev.preventDefault();
1570                         canvas.isDragging = false;
1571                         if (ev.type == "touchend" && Jmol._gestureUpdate(canvas, ev))
1572                                 return !!ui;
1573                         var xym = Jmol._jsGetXY(canvas, ev);
1574                         if(xym)
1575                         canvas.applet._processEvent(502, xym);//java.awt.Event.MOUSE_UP
1576                         return !!ui;
1577                 });
1578     
1579                 Jmol.$bind(canvas, 'mousemove touchmove', function(ev) { // touchmove
1580       if (doIgnore(ev))
1581         return true;
1582                   // defer to console or menu when dragging within this canvas
1583                         if (Jmol._mouseOwner && Jmol._mouseOwner != canvas && Jmol._mouseOwner.isDragging) {
1584         if (!Jmol._mouseOwner.mouseMove)
1585           return true;
1586                                 Jmol._mouseOwner.mouseMove(ev);
1587                                 return false;
1588                         }
1589                         return Jmol._drag(canvas, ev);
1590                 });
1591                 
1592                 Jmol._drag = function(canvas, ev) {
1593       
1594                         ev.stopPropagation();
1595                         ev.preventDefault();
1596       
1597                         var isTouch = (ev.type == "touchmove");
1598                         if (isTouch && Jmol._gestureUpdate(canvas, ev))
1599                                 return false;
1600                         var xym = Jmol._jsGetXY(canvas, ev);
1601                         if(!xym) return false;
1602       
1603                         if (!canvas.isDragging)
1604                                 xym[2] = 0;
1605
1606       var ui = ev.target["data-UI"];
1607       if (canvas.isdragging && (!ui || !ui.handleJSEvent(canvas, 506, ev))) {}
1608                         canvas.applet._processEvent((canvas.isDragging ? 506 : 503), xym); // java.awt.Event.MOUSE_DRAG : java.awt.Event.MOUSE_MOVE
1609                         return !!ui;
1610                 }
1611                 
1612                 Jmol.$bind(canvas, 'DOMMouseScroll mousewheel', function(ev) { // Zoom
1613       if (doIgnore(ev))
1614         return true;
1615                         ev.stopPropagation();
1616                         ev.preventDefault();
1617                         // Webkit or Firefox
1618                         canvas.isDragging = false;
1619                         var oe = ev.originalEvent;
1620                         var scroll = (oe.detail ? oe.detail : (Jmol.featureDetection.os == "mac" ? 1 : -1) * oe.wheelDelta); // Mac and PC are reverse; but 
1621                         var modifiers = Jmol._jsGetMouseModifiers(ev);
1622                         canvas.applet._processEvent(-1,[scroll < 0 ? -1 : 1,0,modifiers]);
1623                         return false;
1624                 });
1625
1626                 // context menu is fired on mouse down, not up, and it's handled already anyway.
1627
1628                 Jmol.$bind(canvas, "contextmenu", function() {return false;});
1629
1630                 Jmol.$bind(canvas, 'mouseout', function(ev) {
1631       if (doIgnore(ev))
1632         return true;
1633       if (Jmol._mouseOwner && !Jmol._mouseOwner.mouseMove) 
1634         Jmol._setMouseOwner(null);
1635                         if (canvas.applet._appletPanel)
1636                                 canvas.applet._appletPanel.startHoverWatcher(false);
1637                         //canvas.isDragging = false;
1638                         var xym = Jmol._jsGetXY(canvas, ev);
1639                         if (!xym)
1640                                 return false;
1641                         //canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP
1642                         //canvas.applet._processEvent(505, xym);//J.api.Event.MOUSE_EXITED
1643                         return false;
1644                 });
1645
1646                 Jmol.$bind(canvas, 'mouseenter', function(ev) {
1647       if (doIgnore(ev))
1648         return true;
1649                         if (canvas.applet._appletPanel)
1650                                 canvas.applet._appletPanel.startHoverWatcher(true);
1651                         if (ev.buttons === 0 || ev.which === 0) {
1652                                 canvas.isDragging = false;
1653                                 var xym = Jmol._jsGetXY(canvas, ev);
1654                                 if (!xym)
1655                                         return false;
1656                                 canvas.applet._processEvent(504, xym);//J.api.Event.MOUSE_ENTERED       
1657                                 canvas.applet._processEvent(502, xym);//J.api.Event.MOUSE_UP
1658                                 return false;
1659                         }
1660                 });
1661
1662         Jmol.$bind(canvas, 'mousemoveoutjsmol', function(evspecial, target, ev) {
1663       if (doIgnore(ev))
1664         return true;
1665                 if (canvas == Jmol._mouseOwner && canvas.isDragging) {
1666                         return Jmol._drag(canvas, ev);
1667                 }
1668         });
1669
1670                 if (canvas.applet._is2D)
1671                         Jmol.$resize(function() {
1672                                 if (!canvas.applet)
1673                                         return;
1674                                 canvas.applet._resize();
1675                         });
1676  
1677                 Jmol.$bind('body', 'mouseup touchend', function(ev) {
1678       if (doIgnore(ev))
1679         return true;
1680                         if (canvas.applet)
1681                                 canvas.isDragging = false;
1682                         Jmol._setMouseOwner(null);
1683                 });
1684
1685         }
1686
1687         Jmol._jsUnsetMouse = function(canvas) {
1688                 canvas.applet = null;
1689                 Jmol.$bind(canvas, 'mousedown touchstart mousemove touchmove mouseup touchend DOMMouseScroll mousewheel contextmenu mouseout mouseenter', null);
1690                 Jmol._setMouseOwner(null);
1691         }
1692
1693
1694 ////// Jmol.Swing interface  for Javascript implementation of Swing dialogs and menus
1695
1696 Jmol.Swing = {
1697         // a static class
1698         count:0,
1699         menuInitialized:0,
1700         menuCounter:0,
1701         htDialogs:{}
1702 };
1703
1704 (function(Swing) {
1705
1706 SwingController = Swing; // see javajs.api.SwingController
1707
1708 Swing.setDraggable = function(Obj) {
1709         
1710         var proto = Obj.prototype;
1711         if (proto.setContainer)
1712                 return;
1713         
1714         // for menus, console, and 
1715         proto.setContainer = function(container) {
1716                 this.container = container;
1717                 container.obj = this;
1718                 this.isDragging = false;
1719                 this.ignoreMouse = false;
1720                 var me = this;
1721                 container.bind('mousedown touchstart', function(ev) {
1722                         if (me.ignoreMouse) {
1723                                 me.ignoreMouse = false;
1724                                 return true;
1725                         }
1726                         Jmol._setMouseOwner(me, true);
1727                         me.isDragging = true;
1728                         me.pageX = ev.pageX;
1729                         me.pageY = ev.pageY;
1730                         return false;
1731                 });
1732                 container.bind('mousemove touchmove', function(ev) {
1733                         if (me.isDragging && Jmol._mouseOwner == me) {
1734                                 me.mouseMove(ev);
1735                                 return false;
1736                         }
1737                 });
1738                 container.bind('mouseup touchend', function(ev) {
1739                         me.mouseUp(ev);
1740                         Jmol._setMouseOwner(null);
1741                 });
1742         };
1743
1744         proto.mouseUp = function(ev) {
1745                 if (this.isDragging && Jmol._mouseOwner == this) {
1746                         this.pageX0 += (ev.pageX - this.pageX);
1747                         this.pageY0 += (ev.pageY - this.pageY);
1748                         this.isDragging = false;
1749                         return false;
1750                 }
1751                 Jmol._setMouseOwner(null);
1752         }
1753
1754         proto.setPosition = function() {
1755                 if (Jmol._mousePageX === null) {
1756                         var id = this.applet._id + "_" + (this.applet._is2D ? "canvas2d" : "canvas");
1757                         var offsets = Jmol.$offset(id);
1758                         Jmol._mousePageX = offsets.left;
1759                         Jmol._mousePageY = offsets.top;
1760                 }
1761                 this.pageX0 = Jmol._mousePageX;
1762                 this.pageY0 = Jmol._mousePageY;
1763                 var pos = { top: Jmol._mousePageY + 'px', left: Jmol._mousePageX + 'px' };
1764                 this.container.css(pos);
1765         };
1766
1767         proto.mouseMove = function(ev) {
1768                 if (this.isDragging && Jmol._mouseOwner == this) {
1769                         this.timestamp = System.currentTimeMillis(); // used for menu closure
1770                         var x = this.pageX0 + (ev.pageX - this.pageX);
1771                         var y = this.pageY0 + (ev.pageY - this.pageY);
1772       Jmol._mousePageX = x;
1773       Jmol._mousePageY = y;
1774                         this.container.css({ top: y + 'px', left: x + 'px' })
1775                 }
1776         };
1777
1778         proto.dragBind = function(isBind) {
1779                 this.applet._ignoreMouse = !isBind;
1780                 this.container.unbind('mousemoveoutjsmol');
1781                 this.container.unbind('touchmoveoutjsmol');
1782                 this.container.unbind('mouseupoutjsmol');
1783                 this.container.unbind('touchendoutjsmol');
1784                 Jmol._setMouseOwner(null);
1785                 if (isBind) {
1786                         var me = this;
1787                         this.container.bind('mousemoveoutjsmol touchmoveoutjsmol', function(evspecial, target, ev) {
1788                                 me.mouseMove(ev);
1789                         });
1790                         this.container.bind('mouseupoutjsmol touchendoutjsmol', function(evspecial, target, ev) {
1791                                 me.mouseUp(ev);
1792                         });
1793                 }
1794         };
1795 }
1796
1797 // Dialog //
1798
1799 Swing.JSDialog = function () {
1800 }
1801
1802 Swing.setDraggable(Swing.JSDialog);
1803
1804 ///// calls from javajs and other Java-derived packages /////
1805
1806 Swing.getScreenDimensions = function(d) {
1807         d.width = $(window).width();
1808         d.height = $(window).height();
1809 }
1810
1811 Swing.dispose = function(dialog) {
1812         Jmol.$remove(dialog.id + "_mover");
1813         delete Swing.htDialogs[dialog.id]
1814         dialog.container.obj.dragBind(false);
1815 //  var btns = $("#" + dialog.id + " *[id^='J']"); // add descendents with id starting with "J"
1816 //  for (var i = btns.length; --i >= 0;)
1817 //    delete Dialog.htDialogs[btns[i].id]
1818         //System.out.println("JSmolCore.js: dispose " + dialog.id)
1819 }
1820  
1821 Swing.register = function(dialog, type) {
1822         dialog.id = type + (++Swing.count);
1823         Swing.htDialogs[dialog.id] = dialog;
1824         //System.out.println("JSmolCore.js: register " + dialog.id)
1825
1826 }
1827
1828 Swing.setDialog = function(dialog) {
1829         Jmol._setMouseOwner(null);
1830         Jmol.$remove(dialog.id);
1831         //System.out.println("removed " + dialog.id)
1832         var id = dialog.id + "_mover";
1833         var container = Jmol._$(id);
1834         var jd;
1835         //System.out.println("JSmolCore.js: setDialog " + dialog.id);
1836         if (container[0]) {
1837                 container.html(dialog.html);
1838                 jd = container[0].jd;
1839         } else {
1840                 Jmol.$after("body","<div id='" + id + "' style='position:absolute;left:0px;top:0px;'>" + dialog.html + "</div>");
1841                 var jd = new Swing.JSDialog();
1842                 container = Jmol._$(id);
1843                 dialog.container = container;
1844                 jd.applet = dialog.manager.vwr.html5Applet;
1845                 jd.setContainer(container);
1846                 jd.dialog = dialog;
1847                 jd.setPosition();  
1848                 jd.dragBind(true);
1849                 container[0].jd = jd; 
1850         }
1851         Jmol.$bind("#" + dialog.id + " .JButton", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1852         Jmol.$bind("#" + dialog.id + " .JComboBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1853         Jmol.$bind("#" + dialog.id + " .JCheckBox", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1854         Jmol.$bind("#" + dialog.id + " .JTextField", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1855         Jmol.$bind("#" + dialog.id + " .JTable", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1856         Jmol.$bind("#" + dialog.id + " .JScrollPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1857         Jmol.$bind("#" + dialog.id + " .JEditorPane", "mousedown touchstart", function(event) { jd.ignoreMouse=true });
1858
1859 }
1860  
1861 Swing.setSelected = function(chk) {
1862  Jmol.$prop(chk.id, 'checked', !!chk.selected);
1863 }
1864
1865 Swing.setSelectedIndex = function(cmb) {
1866  Jmol.$prop(cmb.id, 'selectedIndex', cmb.selectedIndex);
1867 }
1868
1869 Swing.setText = function(btn) {
1870  Jmol.$prop(btn.id, 'value', btn.text);
1871 }
1872
1873 Swing.setVisible = function(c) {
1874         Jmol.$setVisible(c.id, c.visible);
1875 }
1876
1877 Swing.setEnabled = function(c) {
1878         Jmol.$setEnabled(c.id, c.enabled);
1879 }
1880
1881 /// callbacks from the HTML elements ////
1882  
1883 Swing.click = function(element, keyEvent) {
1884         var component = Swing.htDialogs[element.id];
1885         if (component) {
1886                 //System.out.println("click " + element + " " + component)
1887                 var info = component.toString();
1888                 // table cells will have an id but are not registered
1889                 if (info.indexOf("JCheck") >= 0) {
1890                                 component.selected = element.checked;
1891                 } else if (info.indexOf("JCombo") >= 0) {
1892                         component.selectedIndex = element.selectedIndex;
1893                 } else if (component.text != null) {  // JButton, JTextField
1894                         component.text = element.value;
1895                         if (keyEvent && ((keyEvent.charCode || keyEvent.keyCode) != 13))
1896                                 return;
1897                 }    
1898         }
1899         var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];
1900         var key = (component ? component.name :  dialog.registryKey + "/" + element.id);
1901         //System.out.println("JSmolCore.js: click " + key); 
1902         dialog.manager.actionPerformed(key);
1903 }
1904
1905 Swing.setFront = function(dialog) {
1906   var applet = dialog.manager.vwr.html5Applet;
1907         if (dialog.zIndex != Jmol._getZ(applet, "dialog"))
1908          dialog.zIndex = Jmol._incrZ(applet, "dialog");
1909         dialog.container && ((dialog.container[0] || dialog.container).style.zIndex = dialog.zIndex);
1910 }
1911
1912 Swing.hideMenus = function(applet) {
1913         // called from LEFT-DOWN mouse event
1914         var menus = applet._menus;
1915         if (menus)
1916                 for (var i in menus)
1917                         if (menus[i].visible)
1918                                 Swing.hideMenu(menus[i]);
1919 }
1920
1921 Swing.windowClosing = function(element) {
1922         var dialog = Swing.htDialogs[Jmol.$getAncestorDiv(element.id, "JDialog").id];
1923         if (dialog.registryKey) {
1924                 //System.out.println("JSmolCore.js: windowClosing " + dialog.registryKey); 
1925                 dialog.manager.processWindowClosing(dialog.registryKey);
1926         } else {
1927                 //System.out.println("JSmolCore.js: windowClosing " + dialog.title); 
1928                 dialog.dispose();
1929         }
1930 }
1931
1932 })(Jmol.Swing);
1933
1934 Jmol._track = function(applet) {
1935         // this function inserts an iFrame that can be used to track your page's applet use. 
1936         // By default it tracks to a page at St. Olaf College, but you can change that. 
1937         // and you can use
1938         //
1939         // delete Jmol._tracker
1940         //
1941         // yourself to not have you page execute this 
1942         //
1943         if (Jmol._tracker){
1944                 try {  
1945                         var url = Jmol._tracker + "&applet=" + applet._jmolType + "&version=" + Jmol._version 
1946                                 + "&appver=" + Jmol.___JmolVersion + "&url=" + encodeURIComponent(document.location.href);
1947                         var s = '<iframe style="display:none" width="0" height="0" frameborder="0" tabindex="-1" src="' + url + '"></iframe>'
1948                         Jmol.$after("body", s);
1949                 } catch (e) {
1950                         // ignore
1951                 }
1952                 delete Jmol._tracker;
1953         }
1954         return applet;
1955 }
1956
1957 Jmol.getProfile = function() {
1958         window["j2s.doProfile"] = true;
1959         if (self.Clazz && self.JSON) {
1960                 Clazz._profile || (Clazz._profile = {});
1961                 return Clazz.getProfile();
1962         }
1963 }
1964
1965 Jmol._getInChIKey = function(applet, data) {
1966         if (data.indexOf("MOL=") >= 0)
1967                 data = data.split("MOL=")[1].split("\"")[0];
1968
1969 }
1970
1971 Jmol._getAttr = function(s, a) {
1972         var pt = s.indexOf(a + "=");
1973         return (pt >= 0 && (pt = s.indexOf('"', pt)) >= 0 
1974                 ? s.substring(pt+1, s.indexOf('"', pt+1)) : null);
1975 }
1976
1977 Jmol.User = {
1978         viewUpdatedCallback: null
1979 }
1980
1981 Jmol.View = {
1982
1983 // The objective of Jmol.View is to coordinate
1984 // asynchronous applet loading and atom/peak picking
1985 // among any combination of Jmol, JME, and JSV.
1986 // 
1987 // basic element is a view object:
1988 //   view = {
1989 //     viewType1: viewRecord1,
1990 //     viewType2: viewRecord2,
1991 //     viewType3: viewRecord3
1992 //   }
1993 // where viewType is one of (Jmol, JME, JSV)
1994 // and a viewRecord is an object
1995 // with elements .chemID, .applet, .data, .script
1996 //
1997 // Jmol.View.sets is a list of cached views[0..n]
1998 // for a given group of applet objects with common Info.viewSet
1999 //
2000 // Bob Hanson 1/22/2014 7:05:38 AM
2001
2002         count: 0,
2003         applets: {},
2004         sets: {}
2005 };
2006
2007 (function(View) {
2008
2009 // methods called from other modules have no "_" in their name
2010
2011 View.updateView = function(applet, Info, _View_updateView) {
2012         // Info.chemID, Info.data, possibly Info.viewID if no chemID
2013         // return from applet after asynchronous download of new data
2014         if (applet._viewSet == null)
2015                 return;
2016         Info.chemID || (applet._searchQuery = null);
2017         Info.data || (Info.data = "N/A");
2018         Info.type = applet._viewType;
2019         if((applet._currentView = View.__findView(applet._viewSet, Info)) == null)
2020                 applet._currentView = View.__createViewSet(applet._viewSet, Info.chemID, Info.viewID || Info.chemID);
2021         applet._currentView[Info.type].data = Info.data;
2022         applet._currentView[Info.type].smiles = applet._getSmiles();
2023         if (Jmol.User.viewUpdatedCallback)
2024                 Jmol.User.viewUpdatedCallback(applet, "updateView");
2025         View.__setView(applet._currentView, applet, false);
2026 }
2027
2028 View.updateFromSync = function(applet, msg) {
2029         applet._updateMsg = msg;
2030         var id = Jmol._getAttr(msg, "sourceID") || Jmol._getAttr(msg, "file");
2031         if (!id)
2032                 return;
2033         var view = View.__findView(applet._viewSet, {viewID:id});
2034         if (view == null)
2035                 return Jmol.updateView(applet, msg); // JSV has been updated internally
2036         if (view != applet._currentView)
2037                 View.__setView(view, applet, true);
2038         var A = ((id = Jmol._getAttr(msg, "atoms")) && msg.indexOf("selectionhalos ON") >= 0  
2039                 ? eval("[" + id + "]") : []);
2040         setTimeout(function(){if (applet._currentView == view)View.updateAtomPick(applet, A)}, 10);
2041         if (Jmol.User.viewUpdatedCallback)
2042                 Jmol.User.viewUpdatedCallback(applet, "updateFromSync");
2043 }
2044
2045 View.updateAtomPick = function(applet, A, _View_updateAtomPick) {
2046         var view = applet._currentView;
2047         if (view == null)
2048                 return;
2049         for (var viewType in view) {
2050                 if (viewType != "info" && view[viewType].applet != applet)
2051                         view[viewType].applet._updateAtomPick(A);
2052         }
2053         if (Jmol.User.viewUpdatedCallback)
2054                 Jmol.User.viewUpdatedCallback(applet, "updateAtomPick");
2055 }
2056
2057 View.dumpViews = function(setID) {
2058         var views = View.sets[setID];
2059         if (!views)
2060           return;
2061         var s = "View set " + setID + ":\n";
2062         var applets = View.applets[setID];
2063         for (var i in applets)
2064                 s += "\napplet " + applets[i]._id
2065                         + " currentView=" + (applets[i]._currentView ? applets[i]._currentView.info.viewID : null);
2066         for (var i = views.length; --i >= 0;) {
2067                 var view = views[i];
2068                 s += "\n\n<b>view=" + i 
2069                         + " viewID=" + view.info.viewID 
2070                         + " chemID=" + view.info.chemID + "</b>\n"
2071                 var v;
2072                 for (var viewType in view) 
2073                         if (viewType != "info")
2074                                 s += "\nview=" + i + " type=" + viewType + " applet=" 
2075                                         + ((v = view[viewType]).applet ? v.applet._id : null) 
2076                                         + " SMILES=" + v.smiles + "\n"
2077                                         + " atomMap=" + JSON.stringify(v.atomMap) + "\n"
2078                                         + " data=\n" + v.data + "\n"
2079         }
2080         return s
2081 }
2082
2083
2084 // methods starting with "__" are "private" to JSmolCore.js
2085
2086 View.__init = function(applet) {
2087   var set = applet._viewSet;
2088         var a = View.applets;
2089         a[set] || (a[set] = {});
2090         a[set][applet._viewType] = applet;
2091 }
2092
2093 View.__findView = function(set, Info) {
2094         var views = View.sets[set];
2095         if (views == null)
2096                 views = View.sets[set] = [];
2097         for (var i = views.length; --i >= 0;) {
2098                 var view = views[i];
2099                 if (Info.viewID) {
2100                         if (view.info.viewID == Info.viewID)
2101                                 return view;
2102                 } else if (Info.chemID != null && Info.chemID == view.info.chemID) {
2103                                 return view;
2104                 } else {
2105                         for (var viewType in view) { 
2106                                 if (viewType != "info") {
2107                                         if (Info.data != null && view[viewType].data != null ? Info.data == view[viewType].data 
2108                                                 : Info.type == viewType)
2109                                                         return view;
2110                                 }
2111                         }
2112                 }
2113         }
2114         return null;  
2115 }
2116
2117 View.__createViewSet = function(set, chemID, viewID, _createViewSet) {
2118         View.count++;
2119         var view = {info:{chemID: chemID, viewID: viewID || "model_" + View.count}};
2120
2121         for (var id in Jmol._applets) {
2122                 var a = Jmol._applets[id];
2123                 if (a._viewSet == set)
2124                         view[a._viewType] = {applet:a, data: null};
2125         }
2126         View.sets[set].push(view);
2127         return view;
2128 }
2129
2130 View.__setView = function(view, applet, isSwitch, _setView) {
2131         // called from Jmol._searchMol and Jmol.View.setCurrentView 
2132         // to notify the applets in the set that there may be new data for them
2133         // skip the originating applet itself and cases where the data has not changed.
2134         // stop at first null data, because that will initiate some sort of asynchronous
2135         // call that will be back here afterward.
2136
2137         for (var viewType in view) {
2138                         if (viewType == "info") 
2139                                 continue;
2140                 var rec = view[viewType];
2141                 var a = rec.applet;
2142                 var modified = (isSwitch || a != null && a._molData == "<modified>");
2143
2144                 if (a == null || a == applet && !modified)
2145                         continue; // may be a mol3d required by JSV but not having a corresponding applet
2146                 var wasNull = (rec.data == null);
2147                 var haveView = (a._currentView != null);
2148                 a._currentView = view; 
2149                 if (haveView && view[viewType].data == rec.data && !wasNull & !modified)
2150                         continue;
2151                 a._loadModelFromView(view);
2152                 if (wasNull)
2153                         break;
2154         }
2155
2156         // Either all are taken care of or one was null,
2157         // in which case we have started an asynchronous
2158         // process to get the data, and we can quit here.
2159         // In either case, we are done.
2160 }
2161
2162 }) (Jmol.View);
2163
2164 Jmol.Cache = {fileCache: {}};
2165
2166 Jmol.Cache.get = function(filename) {
2167         return Jmol.Cache.fileCache[filename];
2168 }
2169
2170 Jmol.Cache.put = function(filename, data) {
2171   Jmol.Cache.fileCache[filename] = data;
2172 }
2173
2174         Jmol.Cache.setDragDrop = function(me) {
2175                 Jmol.$appEvent(me, "appletdiv", "dragover", function(e) {
2176                         e = e.originalEvent;
2177                         e.stopPropagation();
2178                         e.preventDefault();
2179                         e.dataTransfer.dropEffect = 'copy';
2180                 });
2181                 Jmol.$appEvent(me, "appletdiv", "drop", function(e) {
2182                         var oe = e.originalEvent;
2183                         oe.stopPropagation();
2184                         oe.preventDefault();
2185                         var file = oe.dataTransfer.files[0];
2186                         if (file == null) {
2187                                 // FF and Chrome will drop an image here
2188                                 // but it will be only a URL, not an actual file. 
2189                                 try {
2190                                   file = "" + oe.dataTransfer.getData("text");
2191                                   if (file.indexOf("file:/") == 0 || file.indexOf("http:/") == 0) {
2192                                         me._scriptLoad(file);
2193                                         return;
2194                                 }
2195                                 } catch(e) {
2196                                   return;
2197                                 }
2198                           // some other format
2199                           return;
2200                         }
2201                         // MSIE will drop an image this way, though, and load it!
2202                         var reader = new FileReader();
2203                         reader.onloadend = function(evt) {
2204                                 if (evt.target.readyState == FileReader.DONE) {
2205                                         var cacheName = "cache://DROP_" + file.name;
2206                                         var bytes = Jmol._toBytes(evt.target.result);
2207                                         if (!cacheName.endsWith(".spt"))
2208                                                 me._appletPanel.cacheFileByName("cache://DROP_*",false);
2209                                         if (me._viewType == "JSV" || cacheName.endsWith(".jdx")) // shared by Jmol and JSV
2210                                                 Jmol.Cache.put(cacheName, bytes);
2211                                         else
2212                                                 me._appletPanel.cachePut(cacheName, bytes);
2213                                         var xym = Jmol._jsGetXY(me._canvas, e);
2214                                         if(xym && (!me._appletPanel.setStatusDragDropped || me._appletPanel.setStatusDragDropped(0, xym[0], xym[1], cacheName))) {
2215                                                 me._appletPanel.openFileAsyncSpecial(cacheName, 1);
2216                                         }
2217                                 }
2218                         };
2219                         reader.readAsArrayBuffer(file);
2220                 });
2221         }
2222   
2223 })(Jmol, jQuery);