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