// BH 9/6/2014 5:42:32 PM  two-point gestures broken
// BH 5/8/2014 11:16:40 AM j2sPath starting with "/" fails to add idiom
// BH 1/16/2014 8:44:03 PM   Jmol.__execDelayMS = 100; // FF bug when loading into a tab that is not 
//                           immediately focused and not using jQuery for adding the applet and having  
//                           multiple applets.
// BH 12/6/2013 10:12:30 AM adding corejmoljsv.z.js
// BH 9/17/2013 10:18:40 AM  file transfer functions moved to JSmolCore 
// BH 3/5/2013 9:54:16 PM added support for a cover image: Info.coverImage, coverScript, coverTitle, deferApplet, deferUncover
// BH 1/3/2013 4:54:01 AM mouse binding should return false -- see d.bind(...), and d.bind("contextmenu") is not necessary

// JSmol.js -- Jmol pure JavaScript version
// author: Bob Hanson, hansonr@stolaf.edu	4/16/2012
// author: Takanori Nakane biochem_fan 6/12/2012

// This library requires prior inclusion of 

//  jQuery 
//	JSmoljQueryExt.js
//	JSmolCore.js
//  JSmolApplet.js
//  JSmolApi.js
//  j2sjmol.js    (Clazz and associated classes)

// these:
//
//  JSmolThree.js
//  JSmolGLmol.js
//  
//  are optional 

;(function (Jmol) {

	Jmol._isAsync = false; // testing only
	Jmol._asyncCallbacks = {};
	
	Jmol._coreFiles = []; // required for package.js


  ///////////////////
 // This section provides an asynchronous loading sequence
//

	Jmol.__execLog = [];
	Jmol.__execStack = [];
	Jmol.__execTimer = 0;
	Jmol.__coreSet = [];
	Jmol.__coreMore = [];
	Jmol.__execDelayMS = 100; // must be > 55 ms for FF

	Jmol.showExecLog = function() { return Jmol.__execLog.join("\n") }; 

	Jmol.__addExec = function(e) {
		var s = "JSmol load " + e[0]._id + " " + e[3];
		if (self.console)console.log(s + "...")
		Jmol.__execLog.push(s);   
		Jmol.__execStack.push(e);
	}

	Jmol.__addCoreFile = function(type, path, more) {
		if (Jmol.__coreSet.join("").indexOf(type) >= 0) return;
		Jmol.__coreSet.push(type);
		Jmol.__coreSet.sort();
		var f = Jmol.__coreSet.join("");
		Jmol._coreFiles = [path + "/core/core" + (f == "jmol" ? "" : f) + ".z.js" ];
		if (more && (more = more.split(" ")))
			for (var i = 0; i < more.length; i++)
				if (Jmol.__coreMore.join("").indexOf(more[i]) < 0)
					Jmol.__coreMore.push(path + "/core/core" + more[i] + ".z.js")
		for (var i = 0; i < Jmol.__coreMore.length; i++)
			Jmol._coreFiles.push(Jmol.__coreMore[i]);			 
	}      		

  Jmol._loadZJars = function(i) {
    if (i < Jmol._coreFiles.length) {
		  Clazz._Loader.loadZJar(Jmol._coreFiles[i], Clazz._Loader.runtimeKeyClass, function() {Jmol._loadZJars(i + 1)});
		} else {
			window["java.registered"] = true;
			Jmol._nextExecution();
		}
	}
	
	    /*
ClazzLoader._loadZJars = function(i) { 
	if (Jmol._isAsync) {
		if (i < Jmol._coreFiles.length) {
		  ClazzLoader.loadZJar(Jmol._coreFiles[i], ClazzLoader.runtimeKeyClass, function() {ClazzLoader._loadZJars(i + 1)});
		} else {
			Jmol._nextExecution();
		}
		return;	
	} else {
		for (var i = 0; i < Jmol._coreFiles.length; i++)
		  ClazzLoader.loadZJar(Jmol._coreFiles[i], ClazzLoader.runtimeKeyClass);
		window["java.registered"] = true;
	}
}

ClazzLoader._loadZJars(0);

    */
    


	Jmol.__nextExecution = function(trigger) {
		delete Jmol.__execTimer;
		var es = Jmol.__execStack;
		var e;
		while (es.length > 0 && (e = es[0])[4] == "done")
			es.shift();
		if (es.length == 0)
			return;
		if (!Jmol._isAsync && !trigger) {
			setTimeout("Jmol.__nextExecution(true)",10)
			return;
		}
		e.push("done");
		var s = "JSmol exec " + e[0]._id + " " + e[3] + " " + e[2];
		if (self.System)
			System.out.println(s);
			//alert(s)
		if (self.console)console.log(s + " -- OK")
		Jmol.__execLog.push(s);
		e[1](e[0],e[2]);	
	};

	Jmol.__loadClazz = function(applet) {
		if (!Jmol.__clazzLoaded) {
			Jmol.__clazzLoaded = true;
			// create the Clazz object
			LoadClazz();
			if (applet._noMonitor)
				Clazz._LoaderProgressMonitor.showStatus = function() {}
			LoadClazz = null;

			Clazz._Loader.onGlobalLoaded = function (file) {
			 // not really.... just nothing more yet to do yet
				Clazz._LoaderProgressMonitor.showStatus("Application loaded.", true);
				if (!Jmol._debugCode || !Jmol.haveCore) {
					Jmol.haveCore = true;
					Jmol.__nextExecution();
				}
			};
		  // load package.js and j2s/core/core.z.js
			Clazz._Loader.loadPackageClasspath("java", null, true, Jmol.__nextExecution);
			return;
		}
		Jmol.__nextExecution();
	};

	Jmol.__loadClass = function(applet, javaClass) {
		Clazz._Loader.loadClass(javaClass, function() {Jmol.__nextExecution()});
	};

	Jmol._Canvas2D = function(id, Info, type, checkOnly){
		// type: Jmol or JSV
		this._uniqueId = ("" + Math.random()).substring(3);
		this._id = id;
		this._is2D = true;
		this._isJava = false;
		this._jmolType = "Jmol._Canvas2D (" + type + ")";
		switch (type) {
		case "Jmol":
			this._platform = "J.awtjs2d.Platform";
			break;
		case "JSV":
			this._isJSV = true;
			this._isLayered = true;
			this._platform = "JSV.awtjs2d.Platform";
			break;
		case "Astex":
			this._isAstex = true;
			this._platform = "astex.awtjs2d.Platform";
		}
		if (checkOnly)
			return this;
		window[id] = this;
		this._createCanvas(id, Info);
		if (!Jmol._document || this._deferApplet)
			return this;
		this._init();
		return this;
	};

	Jmol._setAppletParams = function(availableParams, params, Info, isHashtable) {
		for (var i in Info)
			if(!availableParams || availableParams.indexOf(";" + i.toLowerCase() + ";") >= 0){
				if (Info[i] == null || i == "language" && !Jmol.featureDetection.supportsLocalization())
					continue;
				if (isHashtable)
					params.put(i, (Info[i] === true ? Boolean.TRUE: Info[i] === false ? Boolean.FALSE : Info[i]))
				else
					params[i] = Info[i];
			}
	}     
	 
	Jmol._jsSetPrototype = function(proto) {
		proto._init = function() {
	 		this._setupJS();
			this._showInfo(true); 
			if (this._disableInitialConsole)
				this._showInfo(false);
		};

		proto._createCanvas = function(id, Info, glmol) {
			Jmol._setObject(this, id, Info);
			if (glmol) {
				this._GLmol = glmol;
		 		this._GLmol.applet = this;
				this._GLmol.id = this._id;
			}      
			var t = Jmol._getWrapper(this, true);
			if (this._deferApplet) {
			} else if (Jmol._document) {
				Jmol._documentWrite(t);
				this._newCanvas(false);				        
				t = "";
			} else {
				this._deferApplet = true;
				t += '<script type="text/javascript">' + id + '._cover(false)</script>';
			}
			t += Jmol._getWrapper(this, false);
			if (Info.addSelectionOptions)
				t += Jmol._getGrabberOptions(this);
			if (Jmol._debugAlert && !Jmol._document)
				alert(t);
			this._code = Jmol._documentWrite(t);
		};

		proto._newCanvas = function(doReplace) {
			if (this._is2D)
				this._createCanvas2d(doReplace);
			else
				this._GLmol.create();
		};

		proto._createCanvas2d = function(doReplace) {
			var container = Jmol.$(this, "appletdiv");
			//if (doReplace) {
				try {
				container[0].removeChild(this._canvas);
				if (this._canvas.frontLayer)
					container[0].removeChild(this._canvas.frontLayer);
				if (this._canvas.rearLayer)
					container[0].removeChild(this._canvas.rearLayer);
				if (this._canvas.imageLayer)
					container[0].removeChild(this._canvas.imageLayer);
				Jmol._jsUnsetMouse(this._mouseInterface);
				} catch (e) {}
			//}
			var w = Math.round(container.width());
			var h = Math.round(container.height());
			var canvas = document.createElement( 'canvas' );
			canvas.applet = this;
			this._canvas = canvas;
			canvas.style.width = "100%";
			canvas.style.height = "100%";
			canvas.width = w;
			canvas.height = h; // w and h used in setScreenDimension
			canvas.id = this._id + "_canvas2d";
			container.append(canvas);
			Jmol._$(canvas.id).css({"z-index":Jmol._getZ(this, "main")});
			if (this._isLayered){
				var img = document.createElement("div");
				canvas.imageLayer = img;
				img.id = this._id + "_imagelayer";
				container.append(img);
				Jmol._$(img.id).css({zIndex:Jmol._getZ(this, "image"),position:"absolute",left:"0px",top:"0px", width:"0px", height:"0px", overflow:"hidden"});
				this._mouseInterface = this._getLayer("front", container, w, h, false);
				//this._getLayer("rear", container, w, h, true);
				//Jmol._$(canvas.id).css({background:"rgb(0,0,0,0.001)", "z-index":Jmol._z.main}); 
			} else {
				this._mouseInterface = canvas;
			}
			Jmol._jsSetMouse(this._mouseInterface);
		}
    
    proto._getLayer = function(name, container, w, h, isOpaque) {
  		var c = document.createElement("canvas");
			this._canvas[name + "Layer"] = c;
			c.style.width = "100%";
			c.style.height = "100%";
			c.id = this._id + "_" + name + "Layer";
			c.width = w;
			c.height = h; // w and h used in setScreenDimension
			container.append(c);
			c.applet = this;
			Jmol._$(c.id).css({background:(isOpaque ? "rgb(0,0,0,1)" : "rgb(0,0,0,0.001)"), "z-index": Jmol._getZ(this,name),position:"absolute",left:"0px",top:"0px",overflow:"hidden"});
			return c;	
    }
    
    
		proto._setupJS = function() {
			window["j2s.lib"] = {
				base : this._j2sPath + "/",
				alias : ".",
				console : this._console,
				monitorZIndex : Jmol._getZ(this, "monitorZIndex")
			};
			var isFirst = (Jmol.__execStack.length == 0);
			if (isFirst)
				Jmol.__addExec([this, Jmol.__loadClazz, null, "loadClazz"]);
				
			if (this._isJSV) {
				Jmol.__addCoreFile("jsv", this._j2sPath, this.__Info.preloadCore);
				if (Jmol._debugCode) {
				// no min package for that
					Jmol.__addExec([this, Jmol.__loadClass, "JSV.appletjs.JSVApplet", "load JSV"]);
					if (this._isPro)
						Jmol.__addExec([this, Jmol.__loadClass, "JSV.appletjs.JSVAppletPro", "load JSV(signed)"]);
				}
			} else if (this._isAstex) {
				Jmol.__addCoreFile("astex", this._j2sPath, this.__Info.preloadCore);
				if (Jmol._debugCode) {
				// no min package for that
					Jmol.__addExec([this, Jmol.__loadClass, "astex.MoleculeViewerAppletJS", "load Astex"]);
				}
			} else {
				Jmol.__addCoreFile("jmol", this._j2sPath, this.__Info.preloadCore);
				if (!this._is2D) {
			 		Jmol.__addExec([this, Jmol.__loadClass, "J.export.JSExporter","load JSExporter"])
			//		Jmol.__addExec([this, this.__addExportHook, null, "addExportHook"])
				}			 			
				if (Jmol._debugCode)
					Jmol.__addExec([this, Jmol.__loadClass, "J.appletjs.Jmol", "load Jmol"]);
			}
			Jmol.__addExec([this, this.__startAppletJS, null, "start applet"])

			this._isSigned = true; // access all files via URL hook
			this._ready = false; 
			this._applet = null;
			this._canScript = function(script) {return true;};
			this._savedOrientations = [];
			Jmol.__execTimer && clearTimeout(Jmol.__execTimer);
			Jmol.__execTimer = setTimeout(Jmol.__nextExecution, Jmol.__execDelayMS);
		};

//		proto.__addExportHook = function(applet) {
	//		Jmol.GLmol.addExportHook(applet);
		//	Jmol.__nextExecution();
		//};
		proto.__startAppletJS = function(applet) {
			if (Jmol._version.indexOf("$Date: ") == 0)
				Jmol._version = (Jmol._version.substring(7) + " -").split(" -")[0] + " (JSmol/j2s)"
			var viewerOptions = Clazz._4Name("java.util.Hashtable").newInstance();
			Jmol._setAppletParams(applet._availableParams, viewerOptions, applet.__Info, true);
			viewerOptions.put("appletReadyCallback","Jmol._readyCallback");
			viewerOptions.put("applet", true);
			viewerOptions.put("name", applet._id);// + "_object");
			viewerOptions.put("syncId", Jmol._syncId);
			if (Jmol._isAsync)
				viewerOptions.put("async", true);
			if (applet._color) 
				viewerOptions.put("bgcolor", applet._color);
			if (!applet._is2D)  
				viewerOptions.put("script", "set multipleBondSpacing 0.35;");
			else if (applet._startupScript)
				viewerOptions.put("script", applet._startupScript)

			if (Jmol._syncedApplets.length) {
				viewerOptions.put("synccallback", "Jmol._mySyncCallback");
			}
			viewerOptions.put("signedApplet", "true");
			viewerOptions.put("platform", applet._platform);
			if (applet._is2D)
				viewerOptions.put("display",applet._id + "_canvas2d");

			// viewerOptions.put("repaintManager", "J.render");
			viewerOptions.put("documentBase", document.location.href);
			var codePath = applet._j2sPath + "/";
			if (codePath.indexOf("://") < 0) {
				var base = document.location.href.split("#")[0].split("?")[0].split("/");
				if (codePath.indexOf("/") == 0)
					base = [base[0], codePath.substring(1)];
				else
					base[base.length - 1] = codePath;
				codePath = base.join("/");
			}
			viewerOptions.put ("codePath", codePath);

			Jmol._registerApplet(applet._id, applet);
			//if (!applet._is2D)
				//applet._GLmol.applet = applet;
			try {
				applet._newApplet(viewerOptions);
			} catch (e) {
				System.out.println((Jmol._isAsync ? "normal async abort from " : "") + e);
				return;
			}
			applet._jsSetScreenDimensions();      
			Jmol.__nextExecution();
		};

		proto._restoreState = function(clazzName, state) {
			System.out.println("\n\nasynchronous restore state for " + clazzName + " " + state)
			var applet = this;
			var vwr = applet._applet && applet._applet.viewer;
			switch (state) {
			case "setOptions":
				return function(_setOptions) {applet.__startAppletJS(applet)};
			case "render":
				return function() {setTimeout(function(){vwr.refresh(2)},10)};
			default:
				switch (clazzName) {
				// debug mode only, when core.z.js has not been loaded and prior to start
				case "J.shape.Balls":
				case "J.shape.Sticks":
				case "J.shape.Frank":
					return null;
				}
				
				//if (vwr.rm.repaintPending)
					//return function() {setTimeout(function(){vwr.refresh(2)},10)};
				if (vwr && vwr.isScriptExecuting && vwr.isScriptExecuting()) {
					if (Jmol._asyncCallbacks[clazzName]) {
						System.out.println("...ignored");
						return 1;
					}
					var sc = vwr.getEvalContextAndHoldQueue(vwr.eval);
					var pc = sc.pc - 1;
					sc.asyncID = clazzName;
					Jmol._asyncCallbacks[clazzName] = function(pc) {sc.pc=pc; System.out.println("sc.asyncID="+sc.asyncID+" sc.pc = " + sc.pc);vwr.eval.resumeEval(sc)};
					vwr.eval.pc = vwr.eval.pcEnd;
					System.out.println("setting resume for pc=" + sc.pc + " " + clazzName + " to " + Jmol._asyncCallbacks[clazzName] + "//" )
					return function() {System.out.println("resuming " + clazzName + " " + Jmol._asyncCallbacks[clazzName]);Jmol._asyncCallbacks[clazzName](pc)};					
				}
				System.out.println(clazzName + "?????????????????????" + state)
				return function() {setTimeout(function(){vwr.refresh(2)},10)};
				//return null;
			}
		}
	
		proto._jsSetScreenDimensions = function() {
				if (!this._applet)return
				// strangely, if CTRL+/CTRL- are used repeatedly, then the
				// applet div can be not the same size as the canvas if there
				// is a border in place.
				var d = Jmol._getElement(this, (this._is2D ? "canvas2d" : "canvas"));
				this._applet.setScreenDimension(
				d.width, d.height);
// Math.floor(Jmol.$(this, "appletdiv").height()));

		};

		proto._show = function(tf) {
			Jmol.$setVisible(Jmol.$(this,"appletdiv"), tf);
			if (tf)
				Jmol._repaint(this, true);
		};

		proto._canScript = function(script) {return true};

		proto._delay = function(eval, sc, millis) {
		// does not take into account that scripts may be added after this and
		// need to be cached.
			this._delayID = setTimeout(function(){eval.resumeEval(sc,false)}, millis);		
		}

		proto._createDomNode = function(id, data) {
			id = this._id + "_" + id;
			var d = document.getElementById(id);
			if (d)
				document.body.removeChild(d);
			if (!data)
				return;
			if (data.indexOf("<?") == 0)
				data = data.substring(data.indexOf("<", 1));
			if (data.indexOf("/>") >= 0) {
				// no doubt there is a more efficient way to do this.
				// Firefox, at least, does not recognize "/>" in HTML blocks
				// that are added this way.
				var D = data.split("/>");
				for (var i = D.length - 1; --i >= 0;) {
					var s = D[i];
					var pt = s.lastIndexOf("<") + 1;
					var pt2 = pt;
					var len = s.length;
					var name = "";
					while (++pt2 < len) {
						if (" \t\n\r".indexOf(s.charAt(pt2))>= 0) {
							var name = s.substring(pt, pt2);
							D[i] = s + "></"+name+">";
							break;
						}	  	
					}
				}
				data = D.join('');
			}
			d = document.createElement("_xml")
			d.id = id;
			d.innerHTML = data;
			d.style.display = "none";
			document.body.appendChild(d);
			return d;
		}		

		proto.equals = function(a) { return this == a };
		proto.clone = function() { return this };
		proto.hashCode = function() { return parseInt(this._uniqueId) };  


		proto._processGesture = function(touches) {
			return this._applet.processTwoPointGesture(touches);
		}

		proto._processEvent = function(type, xym) {
			this._applet.processMouseEvent(type,xym[0],xym[1],xym[2],System.currentTimeMillis());
		}

		proto._resize = function() {
			var s = "__resizeTimeout_" + this._id;
			// only at end
			if (Jmol[s])
				clearTimeout(Jmol[s]);
			var me = this;
			Jmol[s] = setTimeout(function() {Jmol._repaint(me, true);Jmol[s]=null}, 100);
		}

		return proto;
	};

	Jmol._repaint = function(applet, asNewThread) {
		// asNewThread: true is from RepaintManager.repaintNow()
		// false is from Repaintmanager.requestRepaintAndWait()
		// called from apiPlatform Display.repaint()

		// alert("_repaint " + arguments.callee.caller.caller.exName)
		if (!applet || !applet._applet)return;

		// asNewThread = false;
		var container = Jmol.$(applet, "appletdiv");
		var w = Math.round(container.width());
		var h = Math.round(container.height());
		if (applet._is2D && (applet._canvas.width != w || applet._canvas.height != h)) {
			applet._newCanvas(true);
			applet._applet.setDisplay(applet._canvas);
		}
		applet._applet.setScreenDimension(w, h);

		if (asNewThread) {
			setTimeout(function(){ applet._applet && applet._applet.update()});
		} else {
			applet._applet.update();
		}
		// System.out.println(applet._applet.getFullName())
	}

	Jmol._getHiddenCanvas = function(applet, id, width, height, forceNew) {
		id = applet._id + "_" + id;
		var d = document.getElementById(id);
		if (d && forceNew) {
			d = null;
		}
		if (!d)
			d = document.createElement( 'canvas' );
			// for some reason both these need to be set, or maybe just d.width?
		d.width = d.style.width = width;
		d.height = d.style.height = height;
		// d.style.display = "none";
		if (d.id != id) {
			d.id = id;
		}
		return d;
	}

	Jmol._loadImage = function(platform, echoNameAndPath, bytes, fOnload, image) {
	// bytes would be from a ZIP file -- will have to reflect those back from
	// server as an image after conversion to base64
	// ah, but that's a problem, because that image would be needed to be
	// posted, but you can't post an image call.
	// so we would have to go with <image data:> which does not work in all
	// browsers. Hmm.

		var path = echoNameAndPath[1];

		if (image == null) {
			var image = new Image();
			image.onload = function() {Jmol._loadImage(platform, echoNameAndPath, null, fOnload, image)};

			if (bytes != null) {      
				bytes = J.io.Base64.getBase64(bytes).toString();      
				var filename = path.substring(url.lastIndexOf("/") + 1);                                    
				var mimetype = (filename.indexOf(".png") >= 0 ? "image/png" : filename.indexOf(".jpg") >= 0 ? "image/jpg" : "");
				 // now what?
			}
			image.src = path;
			return true; // as far as we can tell!
		}
		var width = image.width;
		var height = image.height; 
		var id = "echo_" + echoNameAndPath[0];
		var canvas = Jmol._getHiddenCanvas(platform.vwr.html5Applet, id, width, height, true);
		canvas.imageWidth = width;
		canvas.imageHeight = height;
		canvas.id = id;
		canvas.image=image;
		Jmol._setCanvasImage(canvas, width, height);
		// return a null canvas and the error in path if there is a problem
		fOnload(canvas,path);
	};

	Jmol._setCanvasImage = function(canvas, width, height) {
		canvas.buf32 = null;
		canvas.width = width;
		canvas.height = height;
		canvas.getContext("2d").drawImage(canvas.image, 0, 0, width, height);
	};

})(Jmol);
