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