package com.idega.presentation.remotescripting;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.idega.presentation.IWContext;
import com.idega.presentation.Layer;
import com.idega.presentation.PresentationObject;
import com.idega.presentation.PresentationObjectContainer;
import com.idega.presentation.Script;
import com.idega.presentation.ui.DropdownMenu;
import com.idega.presentation.ui.IFrame;
import com.idega.presentation.ui.InterfaceObject;
import com.idega.presentation.ui.TextInput;
import com.idega.repository.data.RefactorClassRegistry;
/**
* A class for handling remote scripting between two objects.
* @author gimmi
*/
public class RemoteScriptHandler extends PresentationObjectContainer { //implements RemoteScriptable {
private static final String PARAMETER_REMOTE_SCRIPT_HANDLING_CLASS = "prc";
private static final String PARAMETER_SOURCE_NAME = "psn";
public static final String PARAMETER_SOURCE_PARAMETER_NAME = "prmp";
private InterfaceObject source;
private PresentationObject target;
private Map parameters = new HashMap();
private Map toClear = new HashMap();
private String iframeName;
private RemoteScriptCollection remoteScriptCollection;
private boolean sourceIsTrigger = true;
/**
* Default construction should never be used unless
* class is receiving a remote call
*/
public RemoteScriptHandler() {
// Should only be used for remote calls
}
/**
* @param source The source object, that triggers the event
* @param target The target object, the one affected by the event
*/
public RemoteScriptHandler(InterfaceObject source, PresentationObject target) {
this.source = source;
this.target = target;
this.iframeName = source.getName()+"_"+target.getName();
}
public void main(IWContext iwc) throws Exception{
if (isRemoteCall(iwc)) {
handleRemoteCall(iwc);
} else {
// Adding object if they are not added already
if (this.source.getParent() == null) {
add(this.source);
}
if (this.target.getParent() == null) {
add(this.target);
}
// source MUST BE added to something before these methods are called
if (this.sourceIsTrigger) {
if (this.source instanceof TextInput) {
this.source.setOnKeyUp(getSubmitEvent(iwc));
} else {
this.source.setOnChange(getSubmitEvent(iwc));
}
}
addRemoteScriptingScripts(iwc);
}
}
private void addRemoteScriptingScripts(IWContext iwc) {
if (this.target instanceof DropdownMenu) {
addScriptForDropdown();
} else if (this.target instanceof Layer) {
addScriptForLayer();
} else {
throw new IllegalArgumentException("Unsupported target instance "+this.target.getClass().getName());
}
addCallToServer(iwc);
addBuildQueryScript();
addIFrame(iwc);
}
private void addCallToServer(IWContext iwc) {
StringBuffer buff = new StringBuffer();
buff.append("var IFrameObj; // our IFrame object").append("\n")
.append("function callToServer_"+this.iframeName+"(theFormName) {").append("\n")
.append(" if (!document.createElement) {return true};").append("\n")
.append(" var IFrameDoc;").append("\n")
.append(" if (!IFrameObj && document.createElement) {").append("\n")
.append(" // create the IFrame and assign a reference to the").append("\n")
.append(" // object to our global variable IFrameObj.").append("\n")
.append(" // this will only happen the first time") .append("\n")
.append(" // callToServer() is called").append("\n")
.append(" try {").append("\n")
.append(" var tempIFrame=document.createElement('iframe');").append("\n")
.append(" tempIFrame.setAttribute('id','"+this.iframeName+"');").append("\n")
.append(" tempIFrame.style.border='0px';").append("\n")
.append(" tempIFrame.style.width='0px';").append("\n")
.append(" tempIFrame.style.height='0px';").append("\n")
.append(" IFrameObj = document.body.appendChild(tempIFrame);").append("\n")
.append(" if (document.frames) {").append("\n")
.append(" // this is for IE5 Mac, because it will only").append("\n")
.append(" // allow access to the document object").append("\n")
.append(" // of the IFrame if we access it through").append("\n")
.append(" // the document.frames array").append("\n")
.append(" IFrameObj = document.frames['"+this.iframeName+"'];").append("\n")
.append(" }").append("\n")
.append(" } catch(exception) {").append("\n")
.append(" // This is for IE5 PC, which does not allow dynamic creation").append("\n")
.append(" // and manipulation of an iframe object. Instead, we'll fake").append("\n")
.append(" // it up by creating our own objects.").append("\n")
.append(" iframeHTML='<iframe id=\""+this.iframeName+"\" style=\"';").append("\n")
.append(" iframeHTML+='border:0px;';").append("\n")
.append(" iframeHTML+='width:0px;';").append("\n")
.append(" iframeHTML+='height:0px;';").append("\n")
.append(" iframeHTML+='\"><\\/iframe>';").append("\n")
.append(" document.body.innerHTML+=iframeHTML;").append("\n")
.append(" IFrameObj = new Object();").append("\n")
.append(" IFrameObj.document = new Object();").append("\n")
.append(" IFrameObj.document.location = new Object();").append("\n")
.append(" IFrameObj.document.location.iframe = document.getElementById('"+this.iframeName+"');").append("\n")
.append(" IFrameObj.document.location.replace = function(location) {").append("\n")
.append(" this.iframe.src = location;").append("\n")
.append(" }").append("\n")
.append(" }").append("\n")
.append(" }").append("\n")
.append(" if (navigator.userAgent.indexOf('Gecko') !=-1 && !IFrameObj.contentDocument) {").append("\n")
.append(" // we have to give NS6 a fraction of a second").append("\n")
.append(" // to recognize the new IFrame").append("\n")
.append(" setTimeout('callToServer_"+this.iframeName+"(\"'+theFormName+'\")',10);").append("\n")
.append(" return false;").append("\n")
.append(" }").append("\n")
.append(" if (IFrameObj.contentDocument) {").append("\n")
.append(" // For NS6").append("\n")
.append(" IFrameDoc = IFrameObj.contentDocument;").append("\n")
.append(" } else if (IFrameObj.contentWindow) {").append("\n")
.append(" // For IE5.5 and IE6").append("\n")
.append(" IFrameDoc = IFrameObj.contentWindow.document;").append("\n")
.append(" } else if (IFrameObj.document) {").append("\n")
.append(" // For IE5").append("\n")
.append(" IFrameDoc = IFrameObj.document;").append("\n")
.append(" } else {").append("\n")
.append(" return true;").append("\n")
.append(" }").append("\n")
.append(" IFrameDoc.location.replace('"+getRemoteUrl(iwc)+"' + buildQueryString_"+this.source.getID()+"(findObj('"+this.source.getForm().getID()+"')));").append("\n")
.append(" return false;").append("\n")
.append("}").append("\n");
if (getAssociatedScript() != null) {
getAssociatedScript().addFunction("callToServer_"+this.iframeName, buff.toString());
}
}
private void addIFrame(IWContext iwc) {
IFrame iframe = new IFrame(this.iframeName);
iframe.setID(this.iframeName);
iframe.setHeight(0);
iframe.setWidth(0);
iframe.setBorder(0);
iframe.setSrc(iwc.getIWMainApplication().getPublicObjectInstanciatorURI());
add(iframe);
}
private void addBuildQueryScript() {
StringBuffer params = new StringBuffer();
params.append("&").append(PARAMETER_SOURCE_PARAMETER_NAME).append("=").append(this.source.getName());
Set parNames = this.parameters.keySet();
Iterator iter = parNames.iterator();
while (iter.hasNext()) {
String name = (String) iter.next();
String value = (String) this.parameters.get(name);
params.append("&").append(name).append("=").append(value);
}
if (getAssociatedScript() != null) {
getAssociatedScript().addFunction("buildQueryString_"+this.source.getID()+"(theForm)", "function buildQueryString_"+this.source.getID()+"(theForm){ \n"
+" var qs = ''\n"
+" for (e=0;e<theForm.elements.length;e++) {\n"
+" if (theForm.elements[e].name != '') {\n"
+" qs+='&'\n"
+" qs+=theForm.elements[e].name+'='+theForm.elements[e].value\n"
// +" qs+=theForm.elements[e].name+'='+escape(theForm.elements[e].value)\n"
+" }\n"
+" } \n"
+" qs+='"+params.toString()+"';"
+" return qs\n"
+"}\n");
}
}
private void addScriptForDropdown() {
StringBuffer buff = new StringBuffer();
buff.append("function handleResponse_"+this.source.getID()+"(doc) {\n")
.append(" var namesEl = document.getElementById('"+this.source.getID()+"');\n")
.append(" var zipEl = document.getElementById('"+this.target.getID()+"');\n")
.append(" zipEl.options.length = 0; \n")
.append(" var dataElID = doc.getElementById('"+RemoteScriptHandler.getLayerName(this.source.getName(), "id")+"');\n")
.append(" var dataElName = doc.getElementById('"+RemoteScriptHandler.getLayerName(this.source.getName(), "name")+"');\n")
.append(" namesColl = dataElName.childNodes; \n")
.append(" idsColl = dataElID.childNodes; \n")
.append(" var numNames = namesColl.length; \n")
.append(" var str = '';\n")
.append(" var ids = '';\n")
.append(" for (var q=0; q<numNames; q++) {\n")
.append(" if (namesColl[q].nodeType!=1) continue; // it's not an element node, let's skedaddle\n")
.append(" str = namesColl[q].innerHTML;\n")
.append(" ids = idsColl[q].innerHTML;\n")
.append(" zipEl.options[zipEl.options.length] = new Option(str, ids);\n")
.append(" }\n");
buff = addClearMethods(buff);
buff.append("}\n");
getAssociatedScript().addFunction("handleResponse_"+this.source.getID(), buff.toString());
}
private void addScriptForLayer() {
StringBuffer buff = new StringBuffer();
buff.append("function handleResponse_"+this.source.getID()+"(doc) {\n")
.append(" var dataEl = doc.getElementById('"+RemoteScriptHandler.getLayerName(this.source.getName())+"');\n")
.append(" var str = '';\n")
.append(" if (dataEl != null) {\n")
.append(" namesColl = dataEl.childNodes; \n")
.append(" var numNames = namesColl.length; \n")
.append(" for (var q=0; q<numNames; q++) {\n")
.append(" if (namesColl[q].nodeType!=1) continue; // it's not an element node, let's skedaddle\n")
.append(" str+= namesColl[q].innerHTML;\n")
.append(" }\n")
.append(" } else {\n")
.append(" str = '';\n")
.append(" }\n")
.append(" var resultText = this.document.getElementById('"+this.target.getID()+"');\n")
.append(" resultText.innerHTML = str;\n");
buff = addClearMethods(buff);
buff.append("}\n");
Script s = getAssociatedScript();
if (s != null) {
s.addFunction("handleResponse_"+this.source.getID(), buff.toString());
}
}
private StringBuffer addClearMethods(StringBuffer script) {
Set keySet = this.toClear.keySet();
Iterator iter = keySet.iterator();
PresentationObject po;
String value;
while (iter.hasNext()) {
po = (InterfaceObject) iter.next();
value = (String) this.toClear.get(po);
if (po instanceof DropdownMenu) {
script.append(
" var zipEl = document.getElementById('"+po.getID()+"');\n"+
" zipEl.options.length = 0; \n" +
" zipEl.options[zipEl.options.length] = new Option('"+value+"', '-1');\n");
} else if (po instanceof Layer) {
if (value == null) {
value = "";
}
script.append(
" var resultText = this.document.getElementById('"+po.getID()+"');\n"+
" resultText.innerHTML = '"+value+"';\n");
} else {
throw new IllegalArgumentException("Unsupported target instance "+this.target.getClass().getName());
}
}
return script;
}
private void handleRemoteCall(IWContext iwc) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
String rscClassName = iwc.getParameter(PARAMETER_REMOTE_SCRIPT_HANDLING_CLASS);
RemoteScriptCollection rsc = (RemoteScriptCollection) RefactorClassRegistry.forName(rscClassName).newInstance();
this.getParentPage().setOnLoad("if (parent != self) parent.handleResponse_"+iwc.getParameter(PARAMETER_SOURCE_NAME)+"(document)");
add(rsc.getResults(iwc));
}
private String getRemoteUrl(IWContext iwc) {
// iwc.getIWMainApplication().getw
String url = iwc.getIWMainApplication().getPublicObjectInstanciatorURI(getClass());
if (url.indexOf("?") < 0) {
url += "?";
} else {
url += "&";
}
url += PARAMETER_REMOTE_SCRIPT_HANDLING_CLASS+"="+this.remoteScriptCollection.getClass().getName()+"&"+PARAMETER_SOURCE_NAME+"="+this.source.getID();
return url;
}
private boolean isRemoteCall(IWContext iwc) {
return iwc.isParameterSet(PARAMETER_REMOTE_SCRIPT_HANDLING_CLASS);
}
/**
* Method to get the name of a layer
* @param sourceName The name of the source object
* @return
*/
public static String getLayerName(String sourceName) {
return sourceName+"_div";
}
/**
* Method to get the name of a layer
* @param sourceName The name of the source object
* @param addon A string to add to the name, e.g. <code>id</code> or <code>name</code>
* @return
*/
public static String getLayerName(String sourceName, String addon) {
return sourceName+"_"+addon+"_div";
}
/**
* Method to get the event to trigger the remote script, can be used with onChange, onBlur, and so on.
* @param iwc IWContext
* @return
*/
public String getSubmitEvent(IWContext iwc) {
return "return callToServer_"+this.iframeName+"(findObj('"+this.source.getForm().getID()+"').name)";
}
/**
* Set which class handles the remote procedure
* Class must implement RemoteScripCollection class
* @param remoteScriptCollectionClass
* @throws InstantiationException
* @throws IllegalAccessException
*/
public void setRemoteScriptCollectionClass(Class remoteScriptCollectionClass) throws InstantiationException, IllegalAccessException {
this.remoteScriptCollection = (RemoteScriptCollection) remoteScriptCollectionClass.newInstance();
}
/**
* Set wether or not the source object triggers the event.
* Default value is <code>true</code>
* @param isSourceTrigger
*/
public void setIsSourceTrigger(boolean isSourceTrigger) {
this.sourceIsTrigger = isSourceTrigger;
}
/**
* Add a parameter that is submitted to the remote page
* @param name Name of the parameter
* @param value Value of the parameter
*/
public void addParameter(String name, String value) {
this.parameters.put(name, value);
}
/**
* Set if the event is supposed to clear an object
* @param po PresentationObject that is to be cleared
* @param emptyValue A value to use instead of nothing
*/
public void setToClear(PresentationObject po, String emptyValue) {
this.toClear.put(po, emptyValue);
}
}