package org.lobobrowser.html.domimpl;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.lobobrowser.html.js.Executor;
import org.lobobrowser.html.js.Window;
import org.lobobrowser.js.JavaScript;
import org.lobobrowser.ua.UserAgentContext;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.w3c.dom.Document;
/**
* Implements common functionality of most elements.
*/
public class HTMLAbstractUIElement extends HTMLElementImpl {
private Function onfocus, onblur, onclick, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout, onkeypress,
onkeydown, onkeyup, oncontextmenu;
public HTMLAbstractUIElement(final String name) {
super(name);
}
public Function getOnblur() {
return this.getEventFunction(onblur, "onblur");
}
public void setOnblur(final Function onblur) {
this.onblur = onblur;
}
public Function getOnclick() {
return this.getEventFunction(onclick, "onclick");
}
public void setOnclick(final Function onclick) {
this.onclick = onclick;
}
public Function getOndblclick() {
return this.getEventFunction(ondblclick, "ondblclick");
}
public void setOndblclick(final Function ondblclick) {
this.ondblclick = ondblclick;
}
public Function getOnfocus() {
return this.getEventFunction(onfocus, "onfocus");
}
public void setOnfocus(final Function onfocus) {
this.onfocus = onfocus;
}
public Function getOnkeydown() {
return this.getEventFunction(onkeydown, "onkeydown");
}
public void setOnkeydown(final Function onkeydown) {
this.onkeydown = onkeydown;
}
public Function getOnkeypress() {
return this.getEventFunction(onkeypress, "onkeypress");
}
public void setOnkeypress(final Function onkeypress) {
this.onkeypress = onkeypress;
}
public Function getOnkeyup() {
return this.getEventFunction(onkeyup, "onkeyup");
}
public void setOnkeyup(final Function onkeyup) {
this.onkeyup = onkeyup;
}
public Function getOnmousedown() {
return this.getEventFunction(onmousedown, "onmousedown");
}
public void setOnmousedown(final Function onmousedown) {
this.onmousedown = onmousedown;
}
public Function getOnmousemove() {
return this.getEventFunction(onmousemove, "onmousemove");
}
public void setOnmousemove(final Function onmousemove) {
this.onmousemove = onmousemove;
}
public Function getOnmouseout() {
return this.getEventFunction(onmouseout, "onmouseout");
}
public void setOnmouseout(final Function onmouseout) {
this.onmouseout = onmouseout;
}
public Function getOnmouseover() {
return this.getEventFunction(onmouseover, "onmouseover");
}
public void setOnmouseover(final Function onmouseover) {
this.onmouseover = onmouseover;
}
public Function getOnmouseup() {
return this.getEventFunction(onmouseup, "onmouseup");
}
public void setOnmouseup(final Function onmouseup) {
this.onmouseup = onmouseup;
}
public Function getOncontextmenu() {
return this.getEventFunction(oncontextmenu, "oncontextmenu");
}
public void setOncontextmenu(final Function oncontextmenu) {
this.oncontextmenu = oncontextmenu;
}
public void focus() {
final UINode node = this.getUINode();
if (node != null) {
node.focus();
}
}
public void blur() {
final UINode node = this.getUINode();
if (node != null) {
node.blur();
}
}
private Map<String, Function> functionByAttribute = null;
protected Function getEventFunction(final Function varValue, final String attributeName) {
if (varValue != null) {
return varValue;
}
final String normalAttributeName = normalizeAttributeName(attributeName);
synchronized (this) {
Map<String, Function> fba = this.functionByAttribute;
Function f = fba == null ? null : fba.get(normalAttributeName);
if (f != null) {
return f;
}
final UserAgentContext uac = this.getUserAgentContext();
if (uac == null) {
throw new IllegalStateException("No user agent context.");
}
if (uac.isScriptingEnabled()) {
final String attributeValue = this.getAttribute(attributeName);
if ((attributeValue != null) && (attributeValue.length() != 0)) {
final String functionCode = "function " + normalAttributeName + "_" + System.identityHashCode(this) + "() { " + attributeValue
+ " }";
final Document doc = this.document;
if (doc == null) {
throw new IllegalStateException("Element does not belong to a document.");
}
final Window window = ((HTMLDocumentImpl) doc).getWindow();
final Context ctx = Executor.createContext(this.getDocumentURL(), uac, window.getContextFactory());
try {
final Scriptable scope = window.getWindowScope();
if (scope == null) {
throw new IllegalStateException("Scriptable (scope) instance was null");
}
final Scriptable thisScope = (Scriptable) JavaScript.getInstance().getJavascriptObject(this, scope);
try {
// TODO: Get right line number for script. //TODO: Optimize this
// in case it's called multiple times? Is that done?
f = ctx.compileFunction(thisScope, functionCode, this.getTagName() + "[" + this.getId() + "]." + attributeName, 1, null);
} catch (final EcmaError ecmaError) {
logger.log(Level.WARNING, "Javascript error at " + ecmaError.sourceName() + ":" + ecmaError.lineNumber() + ": "
+ ecmaError.getMessage(), ecmaError);
f = null;
} catch (final Exception err) {
logger.log(Level.WARNING, "Unable to evaluate Javascript code", err);
f = null;
}
} finally {
Context.exit();
}
}
if (fba == null) {
fba = new HashMap<>(1);
this.functionByAttribute = fba;
}
fba.put(normalAttributeName, f);
}
return f;
}
}
@Override
protected void handleAttributeChanged(String name, String oldValue, String newValue) {
super.handleAttributeChanged(name, oldValue, newValue);
if (name.startsWith("on")) {
synchronized (this) {
final Map<String, Function> fba = this.functionByAttribute;
if (fba != null) {
fba.remove(name);
}
}
}
}
}