JAL-1551 crlf changes due to checkout with text=auto
[jalview.git] / examples / jmol / Jmol.js
1 /* Jmol 12.0 script library Jmol.js 9:48 PM 1/31/2011 Bob Hanson
2
3  checkbox heirarchy -- see http://chemapps.stolaf.edu/jmol/docs/examples-11/check.htm
4
5     based on:
6  *
7  * Copyright (C) 2004-2005  Miguel, Jmol Development, www.jmol.org
8  *
9  * Contact: hansonr@stolaf.edu
10  *
11  *  This library is free software; you can redistribute it and/or
12  *  modify it under the terms of the GNU Lesser General Public
13  *  License as published by the Free Software Foundation; either
14  *  version 2.1 of the License, or (at your option) any later version.
15  *
16  *  This library is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  Lesser General Public License for more details.
20  *
21  *  You should have received a copy of the GNU Lesser General Public
22  *  License along with this library; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  */
26
27 // for documentation see www.jmol.org/jslibrary
28
29 try{if(typeof(_jmol)!="undefined")exit()
30
31 // place "?NOAPPLET" on your command line to check applet control action with a textarea
32 // place "?JMOLJAR=xxxxx" to use a specific jar file
33
34 // bob hanson -- jmolResize(w,h) -- resizes absolutely or by percent (w or h 0.5 means 50%)
35 //    angel herraez -- update of jmolResize(w,h,targetSuffix) so it is not tied to first applet
36 // bob hanson -- jmolEvaluate -- evaluates molecular math 8:37 AM 2/23/2007
37 // bob hanson -- jmolScriptMessage -- returns all "scriptStatus" messages 8:37 AM 2/23/2007
38 // bob hanson -- jmolScriptEcho -- returns all "scriptEcho" messages 8:37 AM 2/23/2007
39 // bob hanson -- jmolScriptWait -- 11:31 AM 5/2/2006
40 // bob hanson -- remove trailing separatorHTML in radio groups -- 12:18 PM 5/6/2006
41 // bob hanson -- adds support for dynamic DOM script nodes 7:04 AM 5/19/2006
42 // bob hanson -- adds try/catch for wiki - multiple code passes 7:05 AM 5/19/2006
43 // bob hanson -- auto-initiates to defaultdir/defaultjar -- change as desired.
44 // bob hanson -- adding save/restore orientation w/ and w/o delay 11:49 AM 5/25/2006
45 // bob hanson -- adding AjaxJS service 11:16 AM 6/3/2006
46 // bob hanson -- fix for iframes not available for finding applet
47 // bob hanson -- added applet fake ?NOAPPLET URL flag
48 // bob hanson -- added jmolSetCallback(calbackName, funcName) 3:32 PM 6/13/2006
49 //                      used PRIOR to jmolApplet() or jmolAppletInline()
50 //               added 4th array element in jmolRadioGroup -- title
51 //               added <span> and id around link, checkbox, radio, menu
52 //               fixing AJAX loads for MSIE/Opera-Mozilla incompatibility
53 //            -- renamed Jmol-11.js from Jmol-new.js; JmolApplet.jar from JmolAppletProto.jar
54 //               renamed Jmol.js for Jmol 11 distribution
55 //            -- modified jmolRestoreOrientation() to be immediate, no 1-second delay
56 // bob hanson -- jmolScriptWait always returns a string -- 11:23 AM 9/16/2006
57 // bh         -- jmolCommandInput()
58 // bh         -- jmolSetTranslation(TF) -- forces translation even if there might be message callback issues
59 // bh         -- minor fixes suggested by Angel
60 // bh         -- adds jmolSetSyncId() and jmolGetSyncId()
61 // bh 3/2008  -- adds jmolAppendInlineScript() and jmolAppendInlineArray()
62 // bh 3/2008  -- fixes IE7 bug in relation to jmolLoadInlineArray()
63 // bh 6/2008  -- adds jmolSetAppletWindow()
64 // Angel H. 6/2008  -- added html <label> tags to checkboxes and radio buttons [in jmolCheckbox() and _jmolRadio() functions]
65 // bh 7/2008  -- code fix "for(i..." not "for(var i..."
66 // bh 12/2008 -- jmolLoadInline, jmolLoadInlineArray, jmolLoadInlineScript, jmolAppendInlineScript, jmolAppendInlineArray all return error message or null (Jmol 11.7.16)
67 // bh 12/2008 -- jmolScriptWaitOutput() -- waits for script to complete and delivers output normally sent to console
68
69 // bh 5/2009  -- Support for XHTML using jmolSetXHTML(id)
70 // ah & bh 6/2009 -- New jmolResizeApplet() more flexible, similar to jmolApplet() size syntax
71 // bh 11/2009 -- care in accessing top.document
72 // bh 12/2009 -- added jmolSetParameter(name, value)
73 // bh 12/2009 -- added PARAMS=name:value;name:value;name:value... for command line
74 // bh 12/2009 -- overhaul of target checking
75 // bh 1/2010  -- all _xxxx() methods ALWAYS have complete argument list
76 // bh 1/2010  -- adds option to run a JavaScript function from any Jmol control. 
77 //               This is accomplished by passing an array rather than a script:
78 //               jmolHref([myfunc,"my param 1", "my param 2"], "testing")
79 //               function myfunc(jmolControlObject, [myfunc,"my param 1", "my param 2"], target){...}
80 //               and allows much more flexibility with responding to controls
81 // bh 4/2010  -- added jmolSetMemoryMb(nMb)
82 // ah 1/2011  -- wider detection of browsers; more browsers now use the object tag instead of the applet tag; 
83 //               fix of object tag (removed classid) accounts for change of behavior in Chrome
84 // bh 3/2011  -- added jmolLoadAjax_STOLAF_NIH
85
86 var defaultdir = "."
87 var defaultjar = "JmolApplet.jar"
88
89
90 // Note added 12:41 PM 9/21/2008 by Bob Hanson, hansonr@stolaf.edu:
91
92 // JMOLJAR=xxxxx.jar on the URL for this page will override
93 // the JAR file specified in the jmolInitialize() call.
94
95 // The idea is that it can be very useful to test a web page with different JAR files
96 // Or for an expert user to substitute a signed applet for an unsigned one
97 // so as to use a broader range of models or to create JPEG files, for example.
98
99 // If the JAR file is not in the current directory (has any sort of "/" in its name)
100 // then the user is presented with a warning and asked whether it is OK to change Jar files.
101 // The default action, if the user just presses "OK" is to NOT allow the change. 
102 // The user must type the word "yes" in the prompt box for the change to be approved.
103
104 // If you don't want people to be able to switch in their own JAR file on your page,
105 // simply set this next line to read "var allowJMOLJAR = false".
106
107
108 var undefined; // for IE 5 ... wherein undefined is undefined
109
110 ////////////////////////////////////////////////////////////////
111 // Basic Scripting infrastruture
112 ////////////////////////////////////////////////////////////////
113
114 function jmolInitialize(codebaseDirectory, fileNameOrUseSignedApplet) {
115   if (_jmol.initialized)
116     return;
117   _jmol.initialized = true;
118   if(_jmol.jmoljar) {
119     var f = _jmol.jmoljar;
120     if (f.indexOf("/") >= 0) {
121       alert ("This web page URL is requesting that the applet used be " + f + ". This is a possible security risk, particularly if the applet is signed, because signed applets can read and write files on your local machine or network.")
122       var ok = prompt("Do you want to use applet " + f + "? ","yes or no")
123       if (ok == "yes") {
124         codebaseDirectory = f.substring(0, f.lastIndexOf("/"));
125         fileNameOrUseSignedApplet = f.substring(f.lastIndexOf("/") + 1);
126       } else {
127         _jmolGetJarFilename(fileNameOrUseSignedApplet);
128         alert("The web page URL was ignored. Continuing using " + _jmol.archivePath + ' in directory "' + codebaseDirectory + '"');
129       }
130     } else {
131       fileNameOrUseSignedApplet = f;
132     }
133   }
134   _jmolSetCodebase(codebaseDirectory);
135   _jmolGetJarFilename(fileNameOrUseSignedApplet);
136   _jmolOnloadResetForms();
137 }
138
139 function jmolSetTranslation(TF) {
140   _jmol.params.doTranslate = ''+TF;
141 }
142
143 function _jmolGetJarFilename(fileNameOrFlag) {
144   _jmol.archivePath =
145     (typeof(fileNameOrFlag) == "string"  ? fileNameOrFlag : (fileNameOrFlag ?  "JmolAppletSigned" : "JmolApplet") + "0.jar");
146 }
147
148 function jmolSetDocument(doc) {
149   _jmol.currentDocument = doc;
150 }
151
152 function jmolSetAppletColor(boxbgcolor, boxfgcolor, progresscolor) {
153   _jmolInitCheck();
154   _jmol.params.boxbgcolor = boxbgcolor;
155   if (boxfgcolor)
156     _jmol.params.boxfgcolor = boxfgcolor
157   else if (boxbgcolor == "white" || boxbgcolor == "#FFFFFF")
158     _jmol.params.boxfgcolor = "black";
159   else
160     _jmol.params.boxfgcolor = "white";
161   if (progresscolor)
162     _jmol.params.progresscolor = progresscolor;
163   if (_jmol.debugAlert)
164     alert(" boxbgcolor=" + _jmol.params.boxbgcolor +
165           " boxfgcolor=" + _jmol.params.boxfgcolor +
166           " progresscolor=" + _jmol.params.progresscolor);
167 }
168
169 function jmolSetAppletWindow(w) {
170   _jmol.appletWindow = w;
171 }
172
173 function jmolApplet(size, script, nameSuffix) {
174   _jmolInitCheck();
175   return _jmolApplet(size, null, script, nameSuffix);
176 }
177
178 ////////////////////////////////////////////////////////////////
179 // Basic controls
180 ////////////////////////////////////////////////////////////////
181
182 // undefined means it wasn't there; null means it was explicitly listed as null (so as to skip it)
183
184 function jmolButton(script, label, id, title) {
185   _jmolInitCheck();
186   id != undefined && id != null || (id = "jmolButton" + _jmol.buttonCount);
187   label != undefined && label != null || (label = script.substring(0, 32));
188   ++_jmol.buttonCount;
189   var scriptIndex = _jmolAddScript(script);
190   var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><input type='button' name='" + id + "' id='" + id +
191           "' value='" + label +
192           "' onclick='_jmolClick(this," + scriptIndex + _jmol.targetText +
193           ")' onmouseover='_jmolMouseOver(" + scriptIndex +
194           ");return true' onmouseout='_jmolMouseOut()' " +
195           _jmol.buttonCssText + " /></span>";
196   if (_jmol.debugAlert)
197     alert(t);
198   return _jmolDocumentWrite(t);
199 }
200
201 function jmolCheckbox(scriptWhenChecked, scriptWhenUnchecked,
202                       labelHtml, isChecked, id, title) {
203   _jmolInitCheck();
204   id != undefined && id != null || (id = "jmolCheckbox" + _jmol.checkboxCount);
205   ++_jmol.checkboxCount;
206   if (scriptWhenChecked == undefined || scriptWhenChecked == null ||
207       scriptWhenUnchecked == undefined || scriptWhenUnchecked == null) {
208     alert("jmolCheckbox requires two scripts");
209     return;
210   }
211   if (labelHtml == undefined || labelHtml == null) {
212     alert("jmolCheckbox requires a label");
213     return;
214   }
215   var indexChecked = _jmolAddScript(scriptWhenChecked);
216   var indexUnchecked = _jmolAddScript(scriptWhenUnchecked);
217   var eospan = "</span>"
218   var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><input type='checkbox' name='" + id + "' id='" + id +
219           "' onclick='_jmolCbClick(this," +
220           indexChecked + "," + indexUnchecked + _jmol.targetText +
221           ")' onmouseover='_jmolCbOver(this," + indexChecked + "," +
222           indexUnchecked +
223           ");return true' onmouseout='_jmolMouseOut()' " +
224           (isChecked ? "checked='true' " : "")+ _jmol.checkboxCssText + " />" 
225   if (labelHtml.toLowerCase().indexOf("<td>")>=0) {
226         t += eospan
227         eospan = "";
228   }
229   t += "<label for=\"" + id + "\">" + labelHtml + "</label>" +eospan;
230   if (_jmol.debugAlert)
231     alert(t);
232   return _jmolDocumentWrite(t);
233 }
234
235 function jmolStartNewRadioGroup() {
236   ++_jmol.radioGroupCount;
237 }
238
239 function jmolRadioGroup(arrayOfRadioButtons, separatorHtml, groupName, id, title) {
240   /*
241
242     array: [radio1,radio2,radio3...]
243     where radioN = ["script","label",isSelected,"id","title"]
244
245   */
246
247   _jmolInitCheck();
248   var type = typeof arrayOfRadioButtons;
249   if (type != "object" || type == null || ! arrayOfRadioButtons.length) {
250     alert("invalid arrayOfRadioButtons");
251     return;
252   }
253   separatorHtml != undefined && separatorHtml != null || (separatorHtml = "&nbsp; ");
254   var len = arrayOfRadioButtons.length;
255   jmolStartNewRadioGroup();
256   groupName || (groupName = "jmolRadioGroup" + (_jmol.radioGroupCount - 1));
257   var t = "<span id='"+(id ? id : groupName)+"'>";
258   for (var i = 0; i < len; ++i) {
259     if (i == len - 1)
260       separatorHtml = "";
261     var radio = arrayOfRadioButtons[i];
262     type = typeof radio;
263     if (type == "object") {
264       t += _jmolRadio(radio[0], radio[1], radio[2], separatorHtml, groupName, (radio.length > 3 ? radio[3]: (id ? id : groupName)+"_"+i), (radio.length > 4 ? radio[4] : 0), title);
265     } else {
266       t += _jmolRadio(radio, null, null, separatorHtml, groupName, (id ? id : groupName)+"_"+i, title);
267     }
268   }
269   t+="</span>"
270   if (_jmol.debugAlert)
271     alert(t);
272   return _jmolDocumentWrite(t);
273 }
274
275
276 function jmolRadio(script, labelHtml, isChecked, separatorHtml, groupName, id, title) {
277   _jmolInitCheck();
278   if (_jmol.radioGroupCount == 0)
279     ++_jmol.radioGroupCount;
280   var t = _jmolRadio(script, labelHtml, isChecked, separatorHtml, groupName, (id ? id : groupName + "_" + _jmol.radioCount), title ? title : 0);
281   if (_jmol.debugAlert)
282     alert(t);
283   return _jmolDocumentWrite(t);
284 }
285
286 function jmolLink(script, label, id, title) {
287   _jmolInitCheck();
288   id != undefined && id != null || (id = "jmolLink" + _jmol.linkCount);
289   label != undefined && label != null || (label = script.substring(0, 32));
290   ++_jmol.linkCount;
291   var scriptIndex = _jmolAddScript(script);
292   var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><a name='" + id + "' id='" + id + 
293           "' href='javascript:_jmolClick(this," + scriptIndex + _jmol.targetText + ");' onmouseover='_jmolMouseOver(" + scriptIndex +
294           ");return true;' onmouseout='_jmolMouseOut()' " +
295           _jmol.linkCssText + ">" + label + "</a></span>";
296   if (_jmol.debugAlert)
297     alert(t);
298   return _jmolDocumentWrite(t);
299 }
300
301 function jmolCommandInput(label, size, id, title) {
302   _jmolInitCheck();
303   id != undefined && id != null || (id = "jmolCmd" + _jmol.cmdCount);
304   label != undefined && label != null || (label = "Execute");
305   size != undefined && !isNaN(size) || (size = 60);
306   ++_jmol.cmdCount;
307   var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><input name='" + id + "' id='" + id + 
308           "' size='"+size+"' onkeypress='_jmolCommandKeyPress(event,\""+id+"\"" + _jmol.targetText + ")'><input type=button value = '"+label+"' onclick='jmolScript(document.getElementById(\""+id+"\").value" + _jmol.targetText + ")' /></span>";
309   if (_jmol.debugAlert)
310     alert(t);
311   return _jmolDocumentWrite(t);
312 }
313
314 function _jmolCommandKeyPress(e, id, target) {
315         var keycode = (window.event ? window.event.keyCode : e ? e.which : 0);
316         if (keycode == 13) {
317                 var inputBox = document.getElementById(id)
318                 _jmolScriptExecute(inputBox, inputBox.value, target)
319         }
320 }
321
322 function _jmolScriptExecute(element,script,target) {
323         if (typeof(script) == "object")
324                 script[0](element, script, target)
325         else
326                 jmolScript(script, target) 
327 }
328
329 function jmolMenu(arrayOfMenuItems, size, id, title) {
330   _jmolInitCheck();
331   id != undefined && id != null || (id = "jmolMenu" + _jmol.menuCount);
332   ++_jmol.menuCount;
333   var type = typeof arrayOfMenuItems;
334   if (type != null && type == "object" && arrayOfMenuItems.length) {
335     var len = arrayOfMenuItems.length;
336     if (typeof size != "number" || size == 1)
337       size = null;
338     else if (size < 0)
339       size = len;
340     var sizeText = size ? " size='" + size + "' " : "";
341     var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><select name='" + id + "' id='" + id +
342             "' onChange='_jmolMenuSelected(this" + _jmol.targetText + ")'" +
343             sizeText + _jmol.menuCssText + ">";
344     for (var i = 0; i < len; ++i) {
345       var menuItem = arrayOfMenuItems[i];
346       type = typeof menuItem;
347       var script, text;
348       var isSelected = undefined;
349       if (type == "object" && menuItem != null) {
350         script = menuItem[0];
351         text = menuItem[1];
352         isSelected = menuItem[2];
353       } else {
354         script = text = menuItem;
355       }
356       text != undefined && text != null || (text = script);             
357       if (script=="#optgroup") {
358         t += "<optgroup label='" + text + "'>";   
359           } else if (script=="#optgroupEnd") {
360         t += "</optgroup>";       
361           } else {              
362         var scriptIndex = _jmolAddScript(script);
363         var selectedText = isSelected ? "' selected='true'>" : "'>";
364         t += "<option value='" + scriptIndex + selectedText + text + "</option>";
365       }
366     }
367     t += "</select></span>";
368     if (_jmol.debugAlert)
369       alert(t);
370     return _jmolDocumentWrite(t);
371   }
372 }
373
374 function jmolHtml(html) {
375   return _jmolDocumentWrite(html);
376 }
377
378 function jmolBr() {
379   return _jmolDocumentWrite("<br />");
380 }
381
382 ////////////////////////////////////////////////////////////////
383 // advanced scripting functions
384 ////////////////////////////////////////////////////////////////
385
386 function jmolDebugAlert(enableAlerts) {
387   _jmol.debugAlert = (enableAlerts == undefined || enableAlerts)
388 }
389
390 function jmolAppletInline(size, inlineModel, script, nameSuffix) {
391   _jmolInitCheck();
392   return _jmolApplet(size, _jmolSterilizeInline(inlineModel),
393                      script, nameSuffix);
394 }
395
396 function jmolSetTarget(targetSuffix) {
397   _jmol.targetSuffix = targetSuffix;
398   _jmol.targetText = targetSuffix ? ",\"" + targetSuffix + "\"" : ",0";
399 }
400
401 function jmolScript(script, targetSuffix) {
402   if (script) {
403     _jmolCheckBrowser();
404     if (targetSuffix == "all") {
405       with (_jmol) {
406         for (var i = 0; i < appletSuffixes.length; ++i) {
407           var applet = _jmolGetApplet(appletSuffixes[i]);
408           if (applet) applet.script(script);
409         }
410       }
411     } else {
412       var applet=_jmolGetApplet(targetSuffix);
413       if (applet) applet.script(script);
414     }
415   }
416 }
417
418 function jmolLoadInline(model, targetSuffix) {
419   if (!model)return "ERROR: NO MODEL"
420   var applet=_jmolGetApplet(targetSuffix);
421   if (!applet)return "ERROR: NO APPLET"
422   if (typeof(model) == "string")
423     return applet.loadInlineString(model, "", false);
424   else
425     return applet.loadInlineArray(model, "", false);
426 }
427
428
429 function jmolLoadInlineScript(model, script, targetSuffix) {
430   if (!model)return "ERROR: NO MODEL"
431   var applet=_jmolGetApplet(targetSuffix);
432   if (!applet)return "ERROR: NO APPLET"
433   return applet.loadInlineString(model, script, false);
434 }
435
436
437 function jmolLoadInlineArray(ModelArray, script, targetSuffix) {
438   if (!model)return "ERROR: NO MODEL"
439   script || (script="")
440   var applet=_jmolGetApplet(targetSuffix);
441   if (!applet)return "ERROR: NO APPLET"
442   try {
443     return applet.loadInlineArray(ModelArray, script, false);
444   } catch (err) {
445     //IE 7 bug
446     return applet.loadInlineString(ModelArray.join("\n"), script, false);
447   }
448 }
449
450 function jmolAppendInlineArray(ModelArray, script, targetSuffix) {
451   if (!model)return "ERROR: NO MODEL"
452   script || (script="")
453   var applet=_jmolGetApplet(targetSuffix);
454   if (!applet)return "ERROR: NO APPLET"
455   try {
456     return applet.loadInlineArray(ModelArray, script, true);
457   } catch (err) {
458     //IE 7 bug
459     return applet.loadInlineString(ModelArray.join("\n"), script, true);
460   }
461 }
462
463 function jmolAppendInlineScript(model, script, targetSuffix) {
464   if (!model)return "ERROR: NO MODEL"
465   var applet=_jmolGetApplet(targetSuffix);
466   if (!applet)return "ERROR: NO APPLET"
467   return applet.loadInlineString(model, script, true);
468 }
469
470 function jmolCheckBrowser(action, urlOrMessage, nowOrLater) {
471   if (typeof action == "string") {
472     action = action.toLowerCase();
473     action == "alert" || action == "redirect" || action == "popup" || (action = null);
474   }
475   if (typeof action != "string")
476     alert("jmolCheckBrowser(action, urlOrMessage, nowOrLater)\n\n" +
477           "action must be 'alert', 'redirect', or 'popup'");
478   else {
479     if (typeof urlOrMessage != "string")
480       alert("jmolCheckBrowser(action, urlOrMessage, nowOrLater)\n\n" +
481             "urlOrMessage must be a string");
482     else {
483       _jmol.checkBrowserAction = action;
484       _jmol.checkBrowserUrlOrMessage = urlOrMessage;
485     }
486   }
487   if (typeof nowOrLater == "string" && nowOrLater.toLowerCase() == "now")
488     _jmolCheckBrowser();
489 }
490
491 ////////////////////////////////////////////////////////////////
492 // Cascading Style Sheet Class support
493 ////////////////////////////////////////////////////////////////
494
495 function jmolSetAppletCssClass(appletCssClass) {
496   if (_jmol.hasGetElementById) {
497     _jmol.appletCssClass = appletCssClass;
498     _jmol.appletCssText = appletCssClass ? "class='" + appletCssClass + "' " : "";
499   }
500 }
501
502 function jmolSetButtonCssClass(buttonCssClass) {
503   if (_jmol.hasGetElementById) {
504     _jmol.buttonCssClass = buttonCssClass;
505     _jmol.buttonCssText = buttonCssClass ? "class='" + buttonCssClass + "' " : "";
506   }
507 }
508
509 function jmolSetCheckboxCssClass(checkboxCssClass) {
510   if (_jmol.hasGetElementById) {
511     _jmol.checkboxCssClass = checkboxCssClass;
512     _jmol.checkboxCssText = checkboxCssClass ? "class='" + checkboxCssClass + "' " : "";
513   }
514 }
515
516 function jmolSetRadioCssClass(radioCssClass) {
517   if (_jmol.hasGetElementById) {
518     _jmol.radioCssClass = radioCssClass;
519     _jmol.radioCssText = radioCssClass ? "class='" + radioCssClass + "' " : "";
520   }
521 }
522
523 function jmolSetLinkCssClass(linkCssClass) {
524   if (_jmol.hasGetElementById) {
525     _jmol.linkCssClass = linkCssClass;
526     _jmol.linkCssText = linkCssClass ? "class='" + linkCssClass + "' " : "";
527   }
528 }
529
530 function jmolSetMenuCssClass(menuCssClass) {
531   if (_jmol.hasGetElementById) {
532     _jmol.menuCssClass = menuCssClass;
533     _jmol.menuCssText = menuCssClass ? "class='" + menuCssClass + "' " : "";
534   }
535 }
536
537 ////////////////////////////////////////////////////////////////
538 // functions for INTERNAL USE ONLY which are subject to change
539 // use at your own risk ... you have been WARNED!
540 ////////////////////////////////////////////////////////////////
541 var _jmol = {
542   currentDocument: document,
543
544   debugAlert: false,
545   
546   codebase: "",
547   modelbase: ".",
548   
549   appletCount: 0,
550   appletSuffixes: [],
551   appletWindow: null,
552   allowedJmolSize: [25, 2048, 300],   // min, max, default (pixels)
553           /*  By setting the _jmol.allowedJmolSize[] variable in the webpage 
554               before calling jmolApplet(), limits for applet size can be overriden.
555                     2048 standard for GeoWall (http://geowall.geo.lsa.umich.edu/home.html)
556           */  
557   buttonCount: 0,
558   checkboxCount: 0,
559   linkCount: 0,
560   cmdCount: 0,
561   menuCount: 0,
562   radioCount: 0,
563   radioGroupCount: 0,
564   
565   appletCssClass: null,
566   appletCssText: "",
567   buttonCssClass: null,
568   buttonCssText: "",
569   checkboxCssClass: null,
570   checkboxCssText: "",
571   java_arguments: "-Xmx512m",
572   radioCssClass: null,
573   radioCssText: "",
574   linkCssClass: null,
575   linkCssText: "",
576   menuCssClass: null,
577   menuCssText: "",
578   
579   targetSuffix: 0,
580   targetText: ",0",
581   scripts: [""],
582   params: {
583         syncId: ("" + Math.random()).substring(3),
584         progressbar: "true",
585         progresscolor: "blue",
586         boxbgcolor: "black",
587         boxfgcolor: "white",
588         boxmessage: "Downloading JmolApplet ..."
589   },
590   ua: navigator.userAgent.toLowerCase(),
591   // uaVersion: parseFloat(navigator.appVersion),  // not used
592   
593   os: "unknown",
594   browser: "unknown",
595   browserVersion: 0,
596   hasGetElementById: !!document.getElementById,
597   isJavaEnabled: navigator.javaEnabled(),
598   // isNetscape47Win: false,  // not used, N4.7 is no longer supported even for detection
599   useIEObject: false,
600   useHtml4Object: false,
601   
602   windowsClassId: "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93",
603   windowsCabUrl:
604    "http://java.sun.com/update/1.6.0/jinstall-6u22-windows-i586.cab",
605
606   isBrowserCompliant: false,
607   isJavaCompliant: false,
608   isFullyCompliant: false,
609
610   initialized: false,
611   initChecked: false,
612   
613   browserChecked: false,
614   checkBrowserAction: "alert",
615   checkBrowserUrlOrMessage: null,
616
617   archivePath: null, // JmolApplet0.jar OR JmolAppletSigned0.jar
618
619   previousOnloadHandler: null,
620
621   jmoljar: null,  
622   useNoApplet: false,
623
624   ready: {}
625 }
626
627 with (_jmol) {
628   function _jmolTestUA(candidate) {
629     var ua = _jmol.ua;
630     var index = ua.indexOf(candidate);
631     if (index < 0)
632       return false;
633     _jmol.browser = candidate;
634     _jmol.browserVersion = parseFloat(ua.substring(index+candidate.length+1));
635     return true;
636   }
637   
638   function _jmolTestOS(candidate) {
639     if (_jmol.ua.indexOf(candidate) < 0)
640       return false;
641     _jmol.os = candidate;
642     return true;
643   }
644   
645   _jmolTestUA("konqueror") ||
646   _jmolTestUA("webkit") ||
647   _jmolTestUA("omniweb") ||
648   _jmolTestUA("opera") ||
649   _jmolTestUA("webtv") ||
650   _jmolTestUA("icab") ||
651   _jmolTestUA("msie") ||
652   (_jmol.ua.indexOf("compatible") < 0 && _jmolTestUA("mozilla")); //Netscape, Mozilla, Seamonkey, Firefox and anything assimilated
653   
654   _jmolTestOS("linux") ||
655   _jmolTestOS("unix") ||
656   _jmolTestOS("mac") ||
657   _jmolTestOS("win");
658
659   /* not used:
660         isNetscape47Win = (os == "win" && browser == "mozilla" &&
661                      browserVersion >= 4.78 && browserVersion <= 4.8);
662         */
663
664   if (os == "win") {
665     isBrowserCompliant = hasGetElementById;
666   } else if (os == "mac") { // mac is the problem child :-(
667     if (browser == "mozilla" && browserVersion >= 5) {
668       // miguel 2004 11 17
669       // checking the plugins array does not work because
670       // Netscape 7.2 OS X still has Java 1.3.1 listed even though
671       // javaplugin.sf.net is installed to upgrade to 1.4.2
672       eval("try {var v = java.lang.System.getProperty('java.version');" +
673            " _jmol.isBrowserCompliant = v >= '1.4.2';" +
674            " } catch (e) { }");
675     } else if (browser == "opera" && browserVersion <= 7.54) {
676       isBrowserCompliant = false;
677     } else {
678       isBrowserCompliant = hasGetElementById &&
679         !((browser == "msie") ||
680           (browser == "webkit" && browserVersion < 125.12));
681     }
682   } else if (os == "linux" || os == "unix") {
683     if (browser == "konqueror" && browserVersion <= 3.3)
684       isBrowserCompliant = false;
685     else
686       isBrowserCompliant = hasGetElementById;
687   } else { // other OS
688     isBrowserCompliant = hasGetElementById;
689   }
690
691   // possibly more checks in the future for this
692   isJavaCompliant = isJavaEnabled;
693
694   isFullyCompliant = isBrowserCompliant && isJavaCompliant;
695
696   useIEObject = (os == "win" && browser == "msie" && browserVersion >= 5.5);
697   useHtml4Object =
698    (browser == "mozilla" && browserVersion >= 5) ||
699    (browser == "opera" && browserVersion >= 8) ||
700    (browser == "webkit" && browserVersion >= 412.2);
701  try {
702   if (top.location.search.indexOf("JMOLJAR=")>=0)
703     jmoljar = top.location.search.split("JMOLJAR=")[1].split("&")[0];
704  } catch(e) {
705   // can't access top.location
706  }
707  try {
708   useNoApplet = (top.location.search.indexOf("NOAPPLET")>=0);
709  } catch(e) {
710   // can't access top.document
711  }
712 }
713
714 function jmolSetMemoryMb(nMb) {
715   _jmol.java_arguments = "-Xmx" + Math.round(nMb) + "m"
716 }
717
718 function jmolSetParameter(name,value) {
719   _jmol.params[name] = value
720 }
721
722 function jmolSetCallback(callbackName,funcName) {
723   _jmol.params[callbackName] = funcName
724 }
725
726  try {
727 // note this is done FIRST, so it cannot override a setting done by the developer
728   if (top.location.search.indexOf("PARAMS=")>=0) {
729     var pars = unescape(top.location.search.split("PARAMS=")[1].split("&")[0]).split(";");
730     for (var i = 0; i < pars.length; i++) {
731       var p = pars[i].split(":");
732       jmolSetParameter(p[0],p[1]);
733     }
734   }
735  } catch(e) {
736   // can't access top.location
737  }
738
739 function jmolSetSyncId(n) {
740   return _jmol.params["syncId"] = n
741 }
742
743 function jmolGetSyncId() {
744   return _jmol.params["syncId"]
745 }
746
747 function jmolSetLogLevel(n) {
748   _jmol.params.logLevel = ''+n;
749 }
750
751         /*  AngelH, mar2007:
752                 By (re)setting these variables in the webpage before calling jmolApplet(), 
753                 a custom message can be provided (e.g. localized for user's language) when no Java is installed.
754         */
755 if (noJavaMsg==undefined) var noJavaMsg = 
756         "You do not have Java applets enabled in your web browser, or your browser is blocking this applet.<br />\n" +
757         "Check the warning message from your browser and/or enable Java applets in<br />\n" +
758         "your web browser preferences, or install the Java Runtime Environment from <a href='http://www.java.com'>www.java.com</a><br />";
759 if (noJavaMsg2==undefined) var noJavaMsg2 = 
760         "You do not have the<br />\n" +
761         "Java Runtime Environment<br />\n" +
762         "installed for applet support.<br />\n" +
763         "Visit <a href='http://www.java.com'>www.java.com</a>";
764 function _jmolApplet(size, inlineModel, script, nameSuffix) {
765         /*  AngelH, mar2007
766                 Fixed percent / pixel business, to avoid browser errors:
767                 put "px" where needed, avoid where not.
768
769             Bob Hanson, 1/2010
770                 Fixed inline escape changing returns to |               
771         */
772   with (_jmol) {
773     nameSuffix == undefined && (nameSuffix = appletCount);
774     appletSuffixes.push(nameSuffix);
775     ++appletCount;
776     script || (script = "select *");
777     var sz = _jmolGetAppletSize(size);
778     var widthAndHeight = " width='" + sz[0] + "' height='" + sz[1] + "' ";
779     var tHeader, tFooter;
780     codebase || jmolInitialize(".");
781     if (useIEObject || useHtml4Object) {
782       params.archive = archivePath;
783       params.mayscript = 'true';
784       params.codebase = codebase;
785       params.code = 'JmolApplet';
786       tHeader = 
787         "<object name='jmolApplet" + nameSuffix +
788         "' id='jmolApplet" + nameSuffix + "' " + appletCssText + "\n" +
789                                 widthAndHeight + "\n";
790       tFooter = "</object>";
791     }
792     if (java_arguments)
793       params.java_arguments = java_arguments;
794     if (useIEObject) { // use MSFT IE6 object tag with .cab file reference
795       tHeader += " classid='" + windowsClassId + "'\n" +
796       (windowsCabUrl ? " codebase='" + windowsCabUrl + "'\n" : "") + ">\n";
797     } else if (useHtml4Object) { // use HTML4 object tag
798       tHeader += " type='application/x-java-applet'\n>\n";
799                                 /*      " classid='java:JmolApplet'\n" +        AH removed this
800                                   Chromium Issue 62076:         Java Applets using an <object> with a classid paramater don't load.
801                                         http://code.google.com/p/chromium/issues/detail?id=62076
802                                         They say this is the correct behavior according to the spec, and there's no indication at this point 
803                                         that WebKit will be changing the handling, so eventually Safari will acquire this behavior too.
804                                         Removing the classid parameter seems to be well tolerated by all browsers (even IE!).
805                                 */
806     } else { // use applet tag
807       tHeader = 
808         "<applet name='jmolApplet" + nameSuffix +
809         "' id='jmolApplet" + nameSuffix + "' " + appletCssText + "\n" +
810                                 widthAndHeight + "\n" +
811         " code='JmolApplet'" +
812         " archive='" + archivePath + "' codebase='" + codebase + "'\n" +
813         " mayscript='true'>\n";
814       tFooter = "</applet>";
815     }
816     var visitJava;
817     if (useIEObject || useHtml4Object) {
818                 var szX = "width:" + sz[0]
819                 if ( szX.indexOf("%")==-1 ) szX+="px" 
820                 var szY = "height:" + sz[1]
821                 if ( szY.indexOf("%")==-1 ) szY+="px" 
822       visitJava =
823         "<p style='background-color:yellow; color:black; " +
824                 szX + ";" + szY + ";" +
825         // why doesn't this vertical-align work?
826         "text-align:center;vertical-align:middle;'>\n" +
827                 noJavaMsg +
828         "</p>";
829     } else {
830       visitJava =
831         "<table bgcolor='yellow'><tr>" +
832         "<td align='center' valign='middle' " + widthAndHeight + "><font color='black'>\n" +
833                 noJavaMsg2 +
834         "</font></td></tr></table>";
835     }
836     params.loadInline = (inlineModel ? inlineModel : "");
837     params.script = (script ? _jmolSterilizeScript(script) : "");
838     var t = tHeader + _jmolParams() + visitJava + tFooter;
839     jmolSetTarget(nameSuffix);
840     ready["jmolApplet" + nameSuffix] = false;
841     if (_jmol.debugAlert)
842       alert(t);
843     return _jmolDocumentWrite(t);
844   }
845 }
846
847 function _jmolParams() {
848  var t = "";
849  for (var i in _jmol.params)
850         if(_jmol.params[i]!="")
851                  t+="  <param name='"+i+"' value='"+_jmol.params[i]+"' />\n";
852  return t
853 }
854
855 function _jmolInitCheck() {
856   if (_jmol.initChecked)
857     return;
858   _jmol.initChecked = true;
859   jmolInitialize(defaultdir, defaultjar)
860 }
861
862 function _jmolCheckBrowser() {
863   with (_jmol) {
864     if (browserChecked)
865       return;
866     browserChecked = true;
867   
868     if (isFullyCompliant)
869       return true;
870
871     if (checkBrowserAction == "redirect")
872       location.href = checkBrowserUrlOrMessage;
873     else if (checkBrowserAction == "popup")
874       _jmolPopup(checkBrowserUrlOrMessage);
875     else {
876       var msg = checkBrowserUrlOrMessage;
877       if (msg == null)
878         msg = "Your web browser is not fully compatible with Jmol\n\n" +
879               "browser: " + browser +
880               "   version: " + browserVersion +
881               "   os: " + os +
882               "   isBrowserCompliant: " + isBrowserCompliant +
883               "   isJavaCompliant: " + isJavaCompliant +
884               "\n\n" + ua;
885       alert(msg);
886     }
887   }
888   return false;
889 }
890
891 function jmolSetXHTML(id) {
892         _jmol.isXHTML = true
893         _jmol.XhtmlElement = null
894         _jmol.XhtmlAppendChild = false
895         if (id){
896                 _jmol.XhtmlElement = document.getElementById(id)
897                 _jmol.XhtmlAppendChild = true
898         }
899 }
900
901 function _jmolDocumentWrite(text) {
902         if (_jmol.currentDocument) {
903                 if (_jmol.isXHTML && !_jmol.XhtmlElement) {
904                         var s = document.getElementsByTagName("script")
905                         _jmol.XhtmlElement = s.item(s.length - 1)
906                         _jmol.XhtmlAppendChild = false
907                 }
908                 if (_jmol.XhtmlElement) {
909                         _jmolDomDocumentWrite(text)
910                 } else {
911                         _jmol.currentDocument.write(text);
912                 }
913         }
914         return text;
915 }
916
917 function _jmolDomDocumentWrite(data) {
918         var pt = 0
919         var Ptr = []
920         Ptr[0] = 0
921         while (Ptr[0] < data.length) {
922                 var child = _jmolGetDomElement(data, Ptr)
923                 if (!child)break
924                 if (_jmol.XhtmlAppendChild)
925                         _jmol.XhtmlElement.appendChild(child)
926                 else
927                         _jmol.XhtmlElement.parentNode.insertBefore(child, _jmol.XhtmlElement); 
928         }
929 }
930 function _jmolGetDomElement(data, Ptr, closetag, lvel) {
931         var e = document.createElement("span")
932         e.innerHTML = data
933         Ptr[0] = data.length
934         return e
935
936 //unnecessary?
937
938         closetag || (closetag = "")
939         lvel || (lvel = 0)
940         var pt0 = Ptr[0]
941         var pt = pt0
942         while (pt < data.length && data.charAt(pt) != "<") pt++
943         if (pt != pt0) {
944                 var text = data.substring(pt0, pt)
945                 Ptr[0] = pt
946                 return document.createTextNode(text)
947         }       
948         pt0 = ++pt
949         var ch
950         while (pt < data.length && "\n\r\t >".indexOf(ch = data.charAt(pt)) < 0) pt++
951         var tagname = data.substring(pt0, pt)
952         var e = (tagname == closetag  || tagname == "/" ? "" 
953                 : document.createElementNS ? document.createElementNS('http://www.w3.org/1999/xhtml', tagname)
954                 : document.createElement(tagname));
955         if (ch == ">") {
956                 Ptr[0] = ++pt
957                 return e
958         }
959         while (pt < data.length && (ch = data.charAt(pt)) != ">") {
960                 while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) pt++
961                 pt0 = pt
962                 while (pt < data.length && "\n\r\t =/>".indexOf(ch = data.charAt(pt)) < 0) pt++
963                 var attrname = data.substring(pt0, pt).toLowerCase()
964                 if (attrname && ch != "=") 
965                         e.setAttribute(attrname, "true")
966                 while (pt < data.length && "\n\r\t ".indexOf(ch = data.charAt(pt)) >= 0) pt++
967                 if (ch == "/") {
968                         Ptr[0] = pt + 2
969                         return e
970                 } else if (ch == "=") {
971                         var quote = data.charAt(++pt)
972                         pt0 = ++pt
973                         while (pt < data.length && (ch = data.charAt(pt)) != quote) pt++
974                         var attrvalue = data.substring(pt0, pt)
975                         e.setAttribute(attrname, attrvalue)
976                         pt++
977                 }
978         }
979         Ptr[0] = ++pt
980         while (Ptr[0] < data.length) {
981                 var child = _jmolGetDomElement(data, Ptr, "/" + tagname, lvel+1)
982                 if (!child)break
983                 e.appendChild(child)
984         }
985         return e
986 }
987
988 function _jmolPopup(url) {
989   var popup = window.open(url, "JmolPopup",
990                           "left=150,top=150,height=400,width=600," +
991                           "directories=yes,location=yes,menubar=yes," +
992                           "toolbar=yes," +
993                           "resizable=yes,scrollbars=yes,status=yes");
994   if (popup.focus)
995     poup.focus();
996 }
997
998 function _jmolReadyCallback(name) {
999   if (_jmol.debugAlert)
1000     alert(name + " is ready");
1001   _jmol.ready["" + name] = true;
1002 }
1003
1004 function _jmolSterilizeScript(script) {
1005   script = script.replace(/'/g, "&#39;");
1006   if (_jmol.debugAlert)
1007     alert("script:\n" + script);
1008   return script;
1009 }
1010
1011 function _jmolSterilizeInline(model) {
1012   model = model.replace(/\r|\n|\r\n/g, (model.indexOf("|") >= 0 ? "\\/n" : "|")).replace(/'/g, "&#39;");
1013   if (_jmol.debugAlert)
1014     alert("inline model:\n" + model);
1015   return model;
1016 }
1017
1018 function _jmolRadio(script, labelHtml, isChecked, separatorHtml, groupName, id, title) {
1019   ++_jmol.radioCount;
1020   groupName != undefined && groupName != null || (groupName = "jmolRadioGroup" + (_jmol.radioGroupCount - 1));
1021   if (!script)
1022     return "";
1023   labelHtml != undefined && labelHtml != null || (labelHtml = script.substring(0, 32));
1024   separatorHtml || (separatorHtml = "")
1025   var scriptIndex = _jmolAddScript(script);
1026   var eospan = "</span>"
1027   var t = "<span id=\"span_"+id+"\""+(title ? " title=\"" + title + "\"":"")+"><input name='" 
1028         + groupName + "' id='"+id+"' type='radio' onclick='_jmolClick(this," +
1029          scriptIndex + _jmol.targetText + ");return true;' onmouseover='_jmolMouseOver(" +
1030          scriptIndex + ");return true;' onmouseout='_jmolMouseOut()' " +
1031          (isChecked ? "checked='true' " : "") + _jmol.radioCssText + " />"
1032   if (labelHtml.toLowerCase().indexOf("<td>")>=0) {
1033         t += eospan
1034         eospan = "";
1035   }
1036   t += "<label for=\"" + id + "\">" + labelHtml + "</label>" +eospan + separatorHtml;
1037
1038   return t;
1039 }
1040
1041 function _jmolFindApplet(target) {
1042   // first look for the target in the current window
1043   var applet = _jmolFindAppletInWindow(_jmol.appletWindow != null ? _jmol.appletWindow : window, target);
1044   // THEN look for the target in child frames
1045   if (applet == undefined)
1046     applet = _jmolSearchFrames(window, target);
1047   // FINALLY look for the target in sibling frames
1048   if (applet == undefined)
1049     applet = _jmolSearchFrames(top, target); // look starting in top frame
1050   return applet;
1051 }
1052
1053 function _jmolGetApplet(targetSuffix){
1054  var target = "jmolApplet" + (targetSuffix ? targetSuffix : "0");
1055  var applet = _jmolFindApplet(target);
1056  if (applet) return applet
1057  _jmol.alerted || alert("could not find applet " + target);
1058  _jmol.alerted = true;
1059  return null
1060 }
1061
1062 function _jmolSearchFrames(win, target) {
1063   var applet;
1064   var frames = win.frames;
1065   if (frames && frames.length) { // look in all the frames below this window
1066    try{
1067     for (var i = 0; i < frames.length; ++i) {
1068       applet = _jmolSearchFrames(frames[i], target);
1069       if (applet)
1070         return applet;
1071     }
1072    }catch(e) {
1073         if (_jmol.debugAlert)
1074                 alert("Jmol.js _jmolSearchFrames cannot access " + win.name + ".frame[" + i + "] consider using jmolSetAppletWindow()") 
1075    }
1076   }
1077   return applet = _jmolFindAppletInWindow(win, target)
1078 }
1079
1080 function _jmolFindAppletInWindow(win, target) {
1081     var doc = win.document;
1082                 if (doc.getElementById(target))
1083       return doc.getElementById(target);
1084     else if (doc.applets)
1085       return doc.applets[target];
1086     else
1087       return doc[target]; 
1088 }
1089
1090 function _jmolAddScript(script) {
1091   if (!script)
1092     return 0;
1093   var index = _jmol.scripts.length;
1094   _jmol.scripts[index] = script;
1095   return index;
1096 }
1097
1098 function _jmolClick(elementClicked, scriptIndex, targetSuffix) {
1099   _jmol.element = elementClicked;
1100   _jmolScriptExecute(elementClicked, _jmol.scripts[scriptIndex], targetSuffix);
1101 }
1102
1103 function _jmolMenuSelected(menuObject, targetSuffix) {
1104   var scriptIndex = menuObject.value;
1105   if (scriptIndex != undefined) {
1106     _jmolScriptExecute(menuObject, _jmol.scripts[scriptIndex], targetSuffix);
1107     return;
1108   }
1109   var len = menuObject.length;
1110   if (typeof len == "number") {
1111     for (var i = 0; i < len; ++i) {
1112       if (menuObject[i].selected) {
1113         _jmolClick(menuObject[i], menuObject[i].value, targetSuffix);
1114         return;
1115       }
1116     }
1117   }
1118   alert("?Que? menu selected bug #8734");
1119 }
1120
1121
1122 _jmol.checkboxMasters = {};
1123 _jmol.checkboxItems = {};
1124
1125 function jmolSetCheckboxGroup(chkMaster,chkBox) {
1126         var id = chkMaster;
1127         if(typeof(id)=="number")id = "jmolCheckbox" + id;
1128         chkMaster = document.getElementById(id);
1129         if (!chkMaster)alert("jmolSetCheckboxGroup: master checkbox not found: " + id);
1130         var m = _jmol.checkboxMasters[id] = {};
1131         m.chkMaster = chkMaster;
1132         m.chkGroup = {};
1133         for (var i = 1; i < arguments.length; i++){
1134                 var id = arguments[i];
1135                 if(typeof(id)=="number")id = "jmolCheckbox" + id;
1136                 checkboxItem = document.getElementById(id);
1137                 if (!checkboxItem)alert("jmolSetCheckboxGroup: group checkbox not found: " + id);
1138                 m.chkGroup[id] = checkboxItem;
1139                 _jmol.checkboxItems[id] = m;
1140         }
1141 }
1142
1143 function _jmolNotifyMaster(m){
1144         //called when a group item is checked
1145         var allOn = true;
1146         var allOff = true;
1147         for (var chkBox in m.chkGroup){
1148                 if(m.chkGroup[chkBox].checked)
1149                         allOff = false;
1150                 else
1151                         allOn = false;
1152         }
1153         if (allOn)m.chkMaster.checked = true;   
1154         if (allOff)m.chkMaster.checked = false;
1155         if ((allOn || allOff) && _jmol.checkboxItems[m.chkMaster.id])
1156                 _jmolNotifyMaster(_jmol.checkboxItems[m.chkMaster.id])
1157 }
1158
1159 function _jmolNotifyGroup(m, isOn){
1160         //called when a master item is checked
1161         for (var chkBox in m.chkGroup){
1162                 var item = m.chkGroup[chkBox]
1163                 item.checked = isOn;
1164                 if (_jmol.checkboxMasters[item.id])
1165                         _jmolNotifyGroup(_jmol.checkboxMasters[item.id], isOn)
1166         }
1167 }
1168
1169 function _jmolCbClick(ckbox, whenChecked, whenUnchecked, targetSuffix) {
1170   _jmol.control = ckbox
1171   _jmolClick(ckbox, ckbox.checked ? whenChecked : whenUnchecked, targetSuffix);
1172   if(_jmol.checkboxMasters[ckbox.id])
1173         _jmolNotifyGroup(_jmol.checkboxMasters[ckbox.id], ckbox.checked)
1174   if(_jmol.checkboxItems[ckbox.id])
1175         _jmolNotifyMaster(_jmol.checkboxItems[ckbox.id])
1176 }
1177
1178 function _jmolCbOver(ckbox, whenChecked, whenUnchecked) {
1179   window.status = _jmol.scripts[ckbox.checked ? whenUnchecked : whenChecked];
1180 }
1181
1182 function _jmolMouseOver(scriptIndex) {
1183   window.status = _jmol.scripts[scriptIndex];
1184 }
1185
1186 function _jmolMouseOut() {
1187   window.status = " ";
1188   return true;
1189 }
1190
1191 function _jmolSetCodebase(codebase) {
1192   _jmol.codebase = codebase ? codebase : ".";
1193   if (_jmol.debugAlert)
1194     alert("jmolCodebase=" + _jmol.codebase);
1195 }
1196
1197 function _jmolOnloadResetForms() {
1198   // must be evaluated ONLY once
1199   _jmol.previousOnloadHandler = window.onload;
1200   window.onload =
1201   function() {
1202     with (_jmol) {
1203       if (buttonCount+checkboxCount+menuCount+radioCount+radioGroupCount > 0) {
1204         var forms = document.forms;
1205         for (var i = forms.length; --i >= 0; )
1206           forms[i].reset();
1207       }
1208       if (previousOnloadHandler)
1209         previousOnloadHandler();
1210     }
1211   }
1212 }
1213
1214 ////////////////////////////////////
1215 /////extensions for getProperty/////
1216 ////////////////////////////////////
1217
1218
1219 function _jmolEvalJSON(s,key){
1220  s=s+""
1221  if(!s)return []
1222  if(s.charAt(0)!="{"){
1223         if(s.indexOf(" | ")>=0)s=s.replace(/\ \|\ /g, "\n")
1224         return s
1225  }
1226  var A = eval("("+s+")")
1227  if(!A)return
1228  if(key && A[key])A=A[key]
1229  return A
1230 }
1231
1232 function _jmolEnumerateObject(A,key){
1233  var sout=""
1234  if(typeof(A) == "string" && A!="null"){
1235         sout+="\n"+key+"=\""+A+"\""
1236  }else if(!isNaN(A)||A==null){
1237         sout+="\n"+key+"="+(A+""==""?"null":A)
1238  }else if(A.length){
1239     sout+=key+"=[]"
1240     for(var i=0;i<A.length;i++){
1241         sout+="\n"
1242         if(typeof(A[i]) == "object"||typeof(A[i]) == "array"){
1243                 sout+=_jmolEnumerateObject(A[i],key+"["+i+"]")
1244         }else{
1245                 sout+=key+"["+i+"]="+(typeof(A[i]) == "string" && A[i]!="null"?"\""+A[i].replace(/\"/g,"\\\"")+"\"":A[i])
1246         }
1247     }
1248  }else{
1249     if(key != ""){
1250         sout+=key+"={}"
1251         key+="."
1252     }
1253     
1254     for(var i in A){
1255         sout+="\n"
1256         if(typeof(A[i]) == "object"||typeof(A[i]) == "array"){
1257                 sout+=_jmolEnumerateObject(A[i],key+i)
1258         }else{
1259                 sout+=key+i+"="+(typeof(A[i]) == "string" && A[i]!="null"?"\""+A[i].replace(/\"/g,"\\\"")+"\"":A[i])
1260         }
1261     }
1262  } 
1263  return sout
1264 }
1265
1266
1267 function _jmolSortKey0(a,b){
1268  return (a[0]<b[0]?1:a[0]>b[0]?-1:0)
1269 }
1270
1271 function _jmolSortMessages(A){
1272  if(!A || typeof(A)!="object")return []
1273  var B = []
1274  for(var i=A.length-1;i>=0;i--)for(var j=0;j<A[i].length;j++)B[B.length]=A[i][j]
1275  if(B.length == 0) return
1276  B=B.sort(_jmolSortKey0)
1277  return B
1278 }
1279
1280 /////////additional extensions //////////
1281
1282
1283 function _jmolDomScriptLoad(URL){
1284  //open(URL) //to debug
1285  _jmol.servercall=URL
1286  var node = document.getElementById("_jmolScriptNode")
1287  if (node && _jmol.browser!="msie"){
1288     document.getElementsByTagName("HEAD")[0].removeChild(node)
1289     node=null
1290  }
1291  if (node) {
1292    node.setAttribute("src",URL)
1293  } else {
1294    node=document.createElement("script")
1295    node.setAttribute("id","_jmolScriptNode")
1296    node.setAttribute("type","text/javascript")
1297    node.setAttribute("src",URL)
1298    document.getElementsByTagName("HEAD")[0].appendChild(node)
1299  }
1300 }
1301
1302
1303 function _jmolExtractPostData(url){
1304  S=url.split("&POST:")
1305  var s=""
1306  for(var i=1;i<S.length;i++){
1307         KV=S[i].split("=")
1308         s+="&POSTKEY"+i+"="+KV[0]
1309         s+="&POSTVALUE"+i+"="+KV[1]
1310  }
1311  return "&url="+escape(S[0])+s
1312 }
1313
1314 function _jmolLoadModel(targetSuffix,remoteURL,array,isError,errorMessage){
1315  //called by server, but in client
1316  //overload this function to customize return
1317  _jmol.remoteURL=remoteURL
1318  isError && alert(errorMessage)
1319  jmolLoadInlineScript(array.join("\n"),_jmol.optionalscript,targetSuffix)
1320 }
1321
1322 //////////user property/status functions/////////
1323
1324 function jmolGetStatus(strStatus,targetSuffix){
1325  return _jmolSortMessages(jmolGetPropertyAsArray("jmolStatus",strStatus,targetSuffix))
1326 }
1327
1328 function jmolGetPropertyAsArray(sKey,sValue,targetSuffix) {
1329  return _jmolEvalJSON(jmolGetPropertyAsJSON(sKey,sValue,targetSuffix),sKey)
1330 }
1331
1332 function jmolGetPropertyAsString(sKey,sValue,targetSuffix) {
1333  var applet = _jmolGetApplet(targetSuffix);
1334  sValue == undefined && (sValue="");
1335  return (applet ? applet.getPropertyAsString(sKey,sValue) + "" : "")
1336 }
1337
1338 function jmolGetPropertyAsJSON(sKey,sValue,targetSuffix) {
1339  sValue == undefined && (sValue = "")
1340  var applet = _jmolGetApplet(targetSuffix);
1341  try {
1342   return (applet ? applet.getPropertyAsJSON(sKey,sValue) + "" : "")
1343  } catch(e) {
1344   return ""
1345  }
1346 }
1347
1348 function jmolGetPropertyAsJavaObject(sKey,sValue,targetSuffix) {
1349  sValue == undefined && (sValue = "")
1350  var applet = _jmolGetApplet(targetSuffix);
1351  return (applet ? applet.getProperty(sKey,sValue) : null)
1352 }
1353
1354
1355 function jmolDecodeJSON(s) {
1356  return _jmolEnumerateObject(_jmolEvalJSON(s),"")
1357 }
1358
1359
1360 ///////// synchronous scripting ////////
1361
1362 function jmolScriptWait(script, targetSuffix) {
1363   targetSuffix == undefined && (targetSuffix="0")
1364   var Ret=jmolScriptWaitAsArray(script, targetSuffix)
1365   var s = ""
1366   for(var i=Ret.length;--i>=0;)
1367   for(var j=0;j< Ret[i].length;j++)
1368         s+=Ret[i][j]+"\n"
1369   return s
1370 }
1371
1372 function jmolScriptWaitOutput(script, targetSuffix) {
1373   targetSuffix == undefined && (targetSuffix="0")
1374   var ret = ""
1375   try{
1376    if (script) {
1377     _jmolCheckBrowser();
1378     var applet=_jmolGetApplet(targetSuffix);
1379     if (applet) ret += applet.scriptWaitOutput(script);
1380    }
1381   }catch(e){
1382   }
1383  return ret;
1384 }
1385
1386 function jmolEvaluate(molecularMath, targetSuffix) {
1387
1388   //carries out molecular math on a model
1389
1390   targetSuffix == undefined && (targetSuffix="0")
1391   var result = "" + jmolGetPropertyAsJavaObject("evaluate", molecularMath, targetSuffix);
1392   var s = result.replace(/\-*\d+/,"")
1393   if (s == "" && !isNaN(parseInt(result)))return parseInt(result);
1394   var s = result.replace(/\-*\d*\.\d*/,"")
1395   if (s == "" && !isNaN(parseFloat(result)))return parseFloat(result);
1396   return result;
1397 }
1398
1399 function jmolScriptEcho(script, targetSuffix) {
1400   // returns a newline-separated list of all echos from a script
1401   targetSuffix == undefined && (targetSuffix="0")
1402   var Ret=jmolScriptWaitAsArray(script, targetSuffix)
1403   var s = ""
1404   for(var i=Ret.length;--i>=0;)
1405   for(var j=Ret[i].length;--j>=0;)
1406         if (Ret[i][j][1] == "scriptEcho")s+=Ret[i][j][3]+"\n"
1407   return s.replace(/ \| /g, "\n")
1408 }
1409
1410
1411 function jmolScriptMessage(script, targetSuffix) {
1412   // returns a newline-separated list of all messages from a script, ending with "script completed\n"
1413   targetSuffix == undefined && (targetSuffix="0")
1414   var Ret=jmolScriptWaitAsArray(script, targetSuffix)
1415   var s = ""
1416   for(var i=Ret.length;--i>=0;)
1417   for(var j=Ret[i].length;--j>=0;)
1418         if (Ret[i][j][1] == "scriptStatus")s+=Ret[i][j][3]+"\n"
1419   return s.replace(/ \| /g, "\n")
1420 }
1421
1422
1423 function jmolScriptWaitAsArray(script, targetSuffix) {
1424  var ret = ""
1425  try{
1426   jmolGetStatus("scriptEcho,scriptMessage,scriptStatus,scriptError",targetSuffix)
1427   if (script) {
1428     _jmolCheckBrowser();
1429     var applet=_jmolGetApplet(targetSuffix);
1430     if (applet) ret += applet.scriptWait(script);
1431     ret = _jmolEvalJSON(ret,"jmolStatus")
1432     if(typeof ret == "object")
1433         return ret
1434   }
1435  }catch(e){
1436  }
1437   return [[ret]]
1438 }
1439
1440
1441
1442 ////////////   save/restore orientation   /////////////
1443
1444 function jmolSaveOrientation(id, targetSuffix) {  
1445  targetSuffix == undefined && (targetSuffix="0")
1446  return _jmol["savedOrientation"+id] = jmolGetPropertyAsArray("orientationInfo","info",targetSuffix).moveTo
1447 }
1448
1449 function jmolRestoreOrientation(id, targetSuffix) {
1450  targetSuffix == undefined && (targetSuffix="0")
1451  var s=_jmol["savedOrientation"+id]
1452  if (!s || s == "")return
1453  s=s.replace(/1\.0/,"0")
1454  return jmolScriptWait(s,targetSuffix)
1455 }
1456
1457 function jmolRestoreOrientationDelayed(id, delay, targetSuffix) {
1458  arguments.length < 2 && (delay=1)
1459  targetSuffix == undefined && (targetSuffix="0")
1460  var s=_jmol["savedOrientation"+id]
1461  if (!s || s == "")return
1462  s=s.replace(/1\.0/,delay)
1463  return jmolScriptWait(s,targetSuffix)
1464 }
1465
1466 ////////////  add parameter /////////////
1467 /*
1468  * for adding callbacks or other parameters. Use:
1469
1470    jmolSetDocument(0)
1471    var s= jmolApplet(....)
1472    s = jmolAppletAddParam(s,"messageCallback", "myFunctionName")
1473    document.write(s)
1474    jmolSetDocument(document) // if you want to then write buttons and such normally
1475  
1476  */
1477
1478 function jmolAppletAddParam(appletCode,name,value){
1479   return (value == "" ? appletCode : appletCode.replace(/\<param/,"\n<param name='"+name+"' value='"+value+"' />\n<param"))
1480 }
1481
1482 ///////////////auto load Research Consortium for Structural Biology (RCSB) data ///////////
1483
1484 function jmolLoadAjax_STOLAF_RCSB(fileformat,pdbid,optionalscript,targetSuffix){
1485
1486  _jmol.thismodel || (_jmol.thismodel = "1crn")
1487  _jmol.serverURL || (_jmol.serverURL="http://fusion.stolaf.edu/chemistry/jmol/getajaxjs.cfm")
1488  _jmol.RCSBserver || (_jmol.RCSBserver="http://www.rcsb.org")
1489  _jmol.defaultURL_RCSB || (_jmol.defaultURL_RCSB=_jmol.RCSBserver+"/pdb/files/1CRN.CIF")
1490  fileformat || (fileformat="PDB")
1491  pdbid || (pdbid=prompt("Enter a 4-digit PDB ID:",_jmol.thismodel))
1492  if(!pdbid || pdbid.length != 4)return ""
1493  targetSuffix || (targetSuffix="0")
1494  optionalscript || (optionalscript="")
1495  var url=_jmol.defaultURL_RCSB.replace(/1CRN/g,pdbid.toUpperCase())
1496  fileformat=="CIF" || (url=url.replace(/CIF/,fileformat))
1497  _jmol.optionalscript=optionalscript
1498  _jmol.thismodel=pdbid
1499  _jmol.thistargetsuffix=targetSuffix
1500  _jmol.thisurl=url
1501  _jmol.modelArray = []
1502  url=_jmol.serverURL+"?returnfunction=_jmolLoadModel&returnArray=_jmol.modelArray&id="+targetSuffix+_jmolExtractPostData(url)
1503  _jmolDomScriptLoad(url)
1504  return url
1505 }
1506
1507
1508 ///////////////auto load NIH CACTVS data -- compound name or SMILES ///////////
1509
1510 function jmolLoadAjax_STOLAF_NIH(compoundid,optionalscript,targetSuffix){
1511  _jmol.thismodel || (_jmol.thismodel = "aspirin")
1512  _jmol.serverURL || (_jmol.serverURL="http://fusion.stolaf.edu/chemistry/jmol/getajaxjs.cfm")
1513  _jmol.defaultURL_NIH || (_jmol.defaultURL_NIH="http://cactus.nci.nih.gov/chemical/structure/FILE/file?format=sdf&get3d=True")
1514  compoundid || (compoundid=prompt("Enter a compound name or a SMILES string:",_jmol.thismodel))
1515  if(!compoundid)return ""
1516  targetSuffix || (targetSuffix="0")
1517  optionalscript || (optionalscript="")
1518  var url=_jmol.defaultURL_NIH.replace(/FILE/g,compoundid)
1519  _jmol.optionalscript=optionalscript
1520  _jmol.thismodel=compoundid
1521  _jmol.thistargetsuffix=targetSuffix
1522  _jmol.thisurl=url
1523  _jmol.modelArray = []
1524  url=_jmol.serverURL+"?returnfunction=_jmolLoadModel&returnArray=_jmol.modelArray&id="+targetSuffix+_jmolExtractPostData(url)
1525  _jmolDomScriptLoad(url)
1526  return url
1527 }
1528
1529
1530 /////////////// St. Olaf College AJAX server -- ANY URL ///////////
1531
1532 function jmolLoadAjax_STOLAF_ANY(url, userid, optionalscript,targetSuffix){
1533  _jmol.serverURL="http://fusion.stolaf.edu/chemistry/jmol/getajaxjs.cfm"
1534  _jmol.thisurlANY || (_jmol.thisurlANY = "http://www.stolaf.edu/depts/chemistry/mo/struc/data/ycp3-1.mol")
1535  url || (url=prompt("Enter any (uncompressed file) URL:", _jmol.thisurlANY))
1536  userid || (userid="0")
1537  targetSuffix || (targetSuffix="0")
1538  optionalscript || (optionalscript="")
1539  _jmol.optionalscript=optionalscript
1540  _jmol.thistargetsuffix=targetSuffix
1541  _jmol.modelArray = []
1542  _jmol.thisurl = url
1543  url=_jmol.serverURL+"?returnfunction=_jmolLoadModel&returnArray=_jmol.modelArray&id="+targetSuffix+_jmolExtractPostData(url)
1544  _jmolDomScriptLoad(url)
1545 }
1546
1547
1548 /////////////// Mineralogical Society of America (MSA) data /////////
1549
1550 function jmolLoadAjax_MSA(key,value,optionalscript,targetSuffix){
1551
1552  _jmol.thiskeyMSA || (_jmol.thiskeyMSA = "mineral")
1553  _jmol.thismodelMSA || (_jmol.thismodelMSA = "quartz")
1554  _jmol.ajaxURL_MSA || (_jmol.ajaxURL_MSA="http://rruff.geo.arizona.edu/AMS/result.php?mineral=quartz&viewing=ajaxjs")
1555  key || (key=prompt("Enter a field:", _jmol.thiskeyMSA))
1556  if(!key)return ""
1557  value || (value=prompt("Enter a "+key+":", _jmol.thismodelMSA))
1558  if(!value)return ""
1559  targetSuffix || (targetSuffix="0")
1560  optionalscript || (optionalscript="")
1561  optionalscript == 1 && (optionalscript='load "" {1 1 1}')
1562  var url=_jmol.ajaxURL_MSA.replace(/mineral/g,key).replace(/quartz/g,value)
1563  _jmol.optionalscript=optionalscript
1564  _jmol.thiskeyMSA=key
1565  _jmol.thismodelMSA=value
1566  _jmol.thistargetsuffix=targetSuffix
1567  _jmol.thisurl=url
1568  _jmol.modelArray = []
1569  loadModel=_jmolLoadModel
1570  _jmolDomScriptLoad(url)
1571  return url
1572 }
1573
1574
1575
1576 function jmolLoadAjaxJS(url, userid, optionalscript,targetSuffix){
1577  userid || (userid="0")
1578  targetSuffix || (targetSuffix="0")
1579  optionalscript || (optionalscript="")
1580  _jmol.optionalscript=optionalscript
1581  _jmol.thismodel=userid
1582  _jmol.thistargetsuffix=targetSuffix
1583  _jmol.modelArray = []
1584  _jmol.thisurl = url
1585  url+="&returnFunction=_jmolLoadModel&returnArray=_jmol.modelArray&id="+targetSuffix
1586  _jmolDomScriptLoad(url)
1587 }
1588
1589
1590 //// in case Jmol library has already been loaded:
1591
1592 }catch(e){}
1593
1594 ///////////////moving atoms //////////////
1595
1596 // HIGHLY experimental!!
1597
1598 function jmolSetAtomCoord(i,x,y,z,targetSuffix){
1599     _jmolCheckBrowser();
1600       var applet=_jmolGetApplet(targetSuffix);
1601       if (applet) applet.getProperty('jmolViewer').setAtomCoord(i,x,y,z)
1602 }
1603
1604 function jmolSetAtomCoordRelative(i,x,y,z,targetSuffix){
1605     _jmolCheckBrowser();
1606       var applet=_jmolGetApplet(targetSuffix);
1607       if (applet) applet.getProperty('jmolViewer').setAtomCoordRelative(i,x,y,z)
1608 }
1609
1610
1611 ///////////////applet fake for testing buttons/////////////
1612
1613
1614 if(_jmol.useNoApplet){
1615         jmolApplet = function(w){
1616                 var s="<table style='background-color:black' width="+w+"><tr height="+w+">"
1617                 +"<td align=center valign=center style='background-color:white'>"
1618                 +"Applet would be here"
1619                 +"<p><textarea id=fakeApplet rows=5 cols=50></textarea>"
1620                 +"</td></tr></table>"
1621                 return _jmolDocumentWrite(s)
1622         }
1623
1624         _jmolFindApplet = function(){return jmolApplet0}
1625
1626         jmolApplet0 = {
1627          script: function(script){document.getElementById("fakeApplet").value="\njmolScript:\n"+script}
1628         ,scriptWait: function(script){document.getElementById("fakeApplet").value="\njmolScriptWait:\n"+script} 
1629         ,loadInline: function(data,script){document.getElementById("fakeApplet").value="\njmolLoadInline data:\n"+data+"\n\nscript:\n"+script}
1630         }
1631 }
1632
1633
1634 ///////////////////////////////////////////
1635
1636   //  This should no longer be needed, jmolResizeApplet() is better; kept for backwards compatibility
1637   /*
1638         Resizes absolutely (pixels) or by percent of window (w or h 0.5 means 50%).
1639         targetSuffix is optional and defaults to zero (first applet in page).
1640         Both w and h are optional, but needed if you want to use targetSuffix.
1641                 h defaults to w
1642                 w defaults to 100% of window
1643         If either w or h is between 0 and 1, then it is taken as percent/100.
1644         If either w or h is greater than 1, then it is taken as a size (pixels). 
1645         */
1646 function jmolResize(w,h,targetSuffix) {
1647  _jmol.alerted = true;
1648  var percentW = (!w ? 100 : w <= 1  && w > 0 ? w * 100 : 0);
1649  var percentH = (!h ? percentW : h <= 1 && h > 0 ? h * 100 : 0);
1650  if (_jmol.browser=="msie") {
1651    var width=document.body.clientWidth;
1652    var height=document.body.clientHeight;
1653  } else {
1654    var netscapeScrollWidth=15;
1655    var width=window.innerWidth - netscapeScrollWidth;
1656    var height=window.innerHeight-netscapeScrollWidth;
1657  }
1658  var applet = _jmolGetApplet(targetSuffix);
1659  if(!applet)return;
1660  applet.style.width = (percentW ? width * percentW/100 : w)+"px";
1661  applet.style.height = (percentH ? height * percentH/100 : (h ? h : w))+"px";
1662  //title=width +  " " + height + " " + (new Date());
1663 }
1664
1665 // 13 Jun 09 -- makes jmolResize() obsolete  (kept for backwards compatibility)
1666 function jmolResizeApplet(size,targetSuffix) {
1667  // See _jmolGetAppletSize() for the formats accepted as size [same used by jmolApplet()]
1668  //  Special case: an empty value for width or height is accepted, meaning no change in that dimension.
1669  _jmol.alerted = true;
1670  var applet = _jmolGetApplet(targetSuffix);
1671  if(!applet)return;
1672  var sz = _jmolGetAppletSize(size, "px");
1673  sz[0] && (applet.style.width = sz[0]);
1674  sz[1] && (applet.style.height = sz[1]);
1675 }
1676
1677 function _jmolGetAppletSize(size, units) {
1678         /* Accepts single number or 2-value array, each one can be one of:
1679            percent (text string ending %), decimal 0 to 1 (percent/100), number, or text string (interpreted as nr.)
1680            [width, height] array of strings is returned, with units added if specified.
1681            Percent is relative to container div or element (which should have explicitly set size).
1682         */
1683   var width, height;
1684   if ( (typeof size) == "object" && size != null ) {
1685     width = size[0]; height = size[1];
1686   } else {
1687     width = height = size;
1688   }
1689   return [_jmolFixDim(width, units), _jmolFixDim(height, units)];
1690 }
1691
1692 function _jmolFixDim(x, units) {
1693   var sx = "" + x;
1694   return (sx.length == 0 ? (units ? "" : _jmol.allowedJmolSize[2])
1695         : sx.indexOf("%") == sx.length-1 ? sx 
1696         : (x = parseFloat(x)) <= 1 && x > 0 ? x * 100 + "%"
1697         : (isNaN(x = Math.floor(x)) ? _jmol.allowedJmolSize[2]
1698                 : x < _jmol.allowedJmolSize[0] ? _jmol.allowedJmolSize[0]
1699             : x > _jmol.allowedJmolSize[1] ? _jmol.allowedJmolSize[1] 
1700         : x) + (units ? units : ""));
1701 }
1702
1703
1704
1705