package org.lobobrowser.html.renderer;
import java.awt.Cursor;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lobobrowser.html.FormInput;
import org.lobobrowser.html.HtmlRendererContext;
import org.lobobrowser.html.domimpl.ElementImpl;
import org.lobobrowser.html.domimpl.HTMLAbstractUIElement;
import org.lobobrowser.html.domimpl.HTMLButtonElementImpl;
import org.lobobrowser.html.domimpl.HTMLDocumentImpl;
import org.lobobrowser.html.domimpl.HTMLElementImpl;
import org.lobobrowser.html.domimpl.HTMLInputElementImpl;
import org.lobobrowser.html.domimpl.HTMLLinkElementImpl;
import org.lobobrowser.html.domimpl.HTMLSelectElementImpl;
import org.lobobrowser.html.domimpl.ModelNode;
import org.lobobrowser.html.domimpl.NodeImpl;
import org.lobobrowser.html.js.Event;
import org.lobobrowser.html.js.Executor;
import org.lobobrowser.html.style.RenderState;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
class HtmlController {
private static final Logger logger = Logger.getLogger(HtmlController.class.getName());
private static final HtmlController instance = new HtmlController();
static HtmlController getInstance() {
return instance;
}
/**
* @return True to propagate further and false if the event was consumed.
*/
public boolean onEnterPressed(final ModelNode node, final InputEvent event) {
if (node instanceof HTMLInputElementImpl) {
final HTMLInputElementImpl hie = (HTMLInputElementImpl) node;
if (hie.isSubmittableWithEnterKey()) {
hie.submitForm(null);
return false;
}
}
// No propagation
return false;
}
// Quick hack
private static ContextFactory getWindowFactory(final ElementImpl e) {
final HTMLDocumentImpl doc = (HTMLDocumentImpl) e.getOwnerDocument();
return doc.getWindow().getContextFactory();
}
// Quick hack
/*
private static boolean runFunction(final ElementImpl e, final Function f, final Event event) {
final HTMLDocumentImpl doc = (HTMLDocumentImpl) e.getOwnerDocument();
final Window window = doc.getWindow();
window.addJSTask(new JSRunnableTask(0, "function from HTMLController", () -> {
Executor.executeFunction(e, f, event, window.getContextFactory());
}));
return false;
}
*/
public boolean onMouseClick(final ModelNode node, final MouseEvent event, final int x, final int y) {
return onMouseClick(node, event, x, y, false);
}
/**
* @return True to propagate further and false if the event was consumed.
*/
public boolean onMouseClick(final ModelNode node, final MouseEvent event, final int x, final int y, boolean eventDispatched) {
if (logger.isLoggable(Level.INFO)) {
logger.info("onMouseClick(): node=" + node + ",class=" + node.getClass().getName());
}
// System.out.println("HtmlController.onMouseClick(): " + node + " already dispatched: " + eventDispatched);
// Get the node which is a valid Event target
/*{
NodeImpl target = (NodeImpl)node;
while(target.getParentNode() != null) {
if (target instanceof Element || target instanceof Document) { // TODO || node instanceof Window) {
break;
}
target = (NodeImpl) target.getParentNode();
}
final Event jsEvent = new Event("click", target, event, x, y);
target.dispatchEvent(jsEvent);
}*/
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Event jsEvent = new Event("click", uiElement, event, x, y);
// System.out.println("Ui element: " + uiElement.getId());
// uiElement.dispatchEvent(jsEvent);
final Function f = uiElement.getOnclick();
/* TODO: This is the original code which would return immediately if f returned false. */
if (f != null) {
// Changing argument to uiElement instead of event
if (!Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement))) {
// if (!Executor.executeFunction(uiElement, f, uiElement, getWindowFactory(uiElement))) {
return false;
}
}
/*
// Alternate JS Task version:
if (f != null) {
runFunction(uiElement, f, jsEvent);
}*/
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
if (!rcontext.onMouseClick(uiElement, event)) {
return false;
}
}
}
if (node instanceof HTMLLinkElementImpl) {
final boolean navigated = ((HTMLLinkElementImpl) node).navigate();
if (navigated) {
return false;
}
} else if (node instanceof HTMLButtonElementImpl) {
final HTMLButtonElementImpl button = (HTMLButtonElementImpl) node;
final String rawType = button.getAttribute("type");
String type;
if (rawType == null) {
type = "submit";
} else {
type = rawType.trim().toLowerCase();
}
if ("submit".equals(type)) {
FormInput[] formInputs;
final String name = button.getName();
if (name == null) {
formInputs = null;
} else {
formInputs = new FormInput[] { new FormInput(name, button.getValue()) };
}
button.submitForm(formInputs);
return false;
} else if ("reset".equals(type)) {
button.resetForm();
return false;
} else if ("button".equals(type)) {
System.out.println("Button TODO;");
} else {
// NOP for "button"!
}
}
if (!eventDispatched) {
// Get the node which is a valid Event target
if ((node instanceof Element) || (node instanceof Document)) { // TODO || node instanceof Window) {
// System.out.println("Click accepted on " + node);
final NodeImpl target = (NodeImpl) node;
final Event jsEvent = new Event("click", target, event, x, y);
target.dispatchEvent(jsEvent);
eventDispatched = true;
}
}
// } else {
// System.out.println("Bumping click to parent");
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMouseClick(parent, event, x, y, eventDispatched);
// }
// return false;
/*
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMouseClick(parent, event, x, y);*/
}
public boolean onMiddleClick(ModelNode node, MouseEvent event, int x, int y) {
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
// Needs to be done after Javascript, so the script
// is able to prevent it.
if (!rcontext.onMiddleClick(uiElement, event)) {
return false;
}
}
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMiddleClick(parent, event, x, y);
}
public boolean onContextMenu(final ModelNode node, final MouseEvent event, final int x, final int y) {
if (logger.isLoggable(Level.INFO)) {
logger.info("onContextMenu(): node=" + node + ",class=" + node.getClass().getName());
}
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOncontextmenu();
if (f != null) {
final Event jsEvent = new Event("contextmenu", uiElement, event, x, y);
if (!Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement))) {
return false;
}
}
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
// Needs to be done after Javascript, so the script
// is able to prevent it.
if (!rcontext.onContextMenu(uiElement, event)) {
return false;
}
}
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onContextMenu(parent, event, x, y);
}
public void onMouseOver(final BaseBoundableRenderable renderable, final ModelNode nodeStart, final MouseEvent event, final int x,
final int y, final ModelNode limit) {
{
ModelNode node = nodeStart;
while (node != null) {
if (node == limit) {
break;
}
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
uiElement.setMouseOver(true);
final Function f = uiElement.getOnmouseover();
if (f != null) {
final Event jsEvent = new Event("mouseover", uiElement, event, x, y);
Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement));
}
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
rcontext.onMouseOver(uiElement, event);
}
}
node = node.getParentModelNode();
}
}
setMouseOnMouseOver(renderable, nodeStart, limit);
}
private static void setMouseOnMouseOver(final BaseBoundableRenderable renderable, final ModelNode nodeStart, final ModelNode limit) {
ModelNode node = nodeStart;
while (node != null) {
if (node == limit) {
break;
}
if (node instanceof NodeImpl) {
final NodeImpl uiElement = (NodeImpl) node;
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
final RenderState rs = uiElement.getRenderState();
final Optional<Cursor> cursorOpt = rs.getCursor();
if (rcontext != null) {
if (cursorOpt.isPresent()) {
rcontext.setCursor(cursorOpt);
break;
} else {
if (node.getParentModelNode() == limit) {
if ((renderable instanceof RWord) || (renderable instanceof RBlank)) {
rcontext.setCursor(Optional.of(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)));
}
}
}
}
}
node = node.getParentModelNode();
}
}
public void onMouseOut(final ModelNode nodeStart, final MouseEvent event, final int x, final int y, final ModelNode limit) {
{
ModelNode node = nodeStart;
while (node != null) {
if (node == limit) {
break;
}
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
uiElement.setMouseOver(false);
final Function f = uiElement.getOnmouseout();
if (f != null) {
final Event jsEvent = new Event("mouseout", uiElement, event, x, y);
Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement));
}
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
rcontext.onMouseOut(uiElement, event);
}
}
node = node.getParentModelNode();
}
}
resetCursorOnMouseOut(nodeStart, limit);
}
private static void resetCursorOnMouseOut(final ModelNode nodeStart, final ModelNode limit) {
Optional<Cursor> foundCursorOpt = Optional.empty();
ModelNode node = limit;
while (node != null) {
if (node instanceof NodeImpl) {
final NodeImpl uiElement = (NodeImpl) node;
final RenderState rs = uiElement.getRenderState();
final Optional<Cursor> cursorOpt = rs.getCursor();
foundCursorOpt = cursorOpt;
if (cursorOpt.isPresent()) {
break;
}
}
node = node.getParentModelNode();
}
if (nodeStart instanceof NodeImpl) {
final NodeImpl uiElement = (NodeImpl) nodeStart;
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
// rcontext.setCursor(Optional.empty());
if (rcontext != null) {
rcontext.setCursor(foundCursorOpt);
}
}
}
/**
* @return True to propagate further, false if consumed.
*/
public boolean onDoubleClick(final ModelNode node, final MouseEvent event, final int x, final int y) {
if (logger.isLoggable(Level.INFO)) {
logger.info("onDoubleClick(): node=" + node + ",class=" + node.getClass().getName());
}
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOndblclick();
if (f != null) {
final Event jsEvent = new Event("dblclick", uiElement, event, x, y);
if (!Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement))) {
return false;
}
}
final HtmlRendererContext rcontext = uiElement.getHtmlRendererContext();
if (rcontext != null) {
if (!rcontext.onDoubleClick(uiElement, event)) {
return false;
}
}
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onDoubleClick(parent, event, x, y);
}
/**
* @return True to propagate further, false if consumed.
*/
public boolean onMouseDisarmed(final ModelNode node, final MouseEvent event) {
if (node instanceof HTMLLinkElementImpl) {
((HTMLLinkElementImpl) node).getCurrentStyle().setOverlayColor(null);
return false;
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMouseDisarmed(parent, event);
}
/**
* @return True to propagate further, false if consumed.
*/
public boolean onMouseDown(final ModelNode node, final MouseEvent event, final int x, final int y) {
boolean pass = true;
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOnmousedown();
if (f != null) {
final Event jsEvent = new Event("mousedown", uiElement, event, x, y);
pass = Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement));
}
}
if (node instanceof HTMLLinkElementImpl) {
((HTMLLinkElementImpl) node).getCurrentStyle().setOverlayColor("#9090FF80");
return false;
}
if (!pass) {
return false;
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMouseDown(parent, event, x, y);
}
/**
* @return True to propagate further, false if consumed.
*/
public boolean onMouseUp(final ModelNode node, final MouseEvent event, final int x, final int y) {
boolean pass = true;
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOnmouseup();
if (f != null) {
final Event jsEvent = new Event("mouseup", uiElement, event, x, y);
pass = Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement));
}
}
if (node instanceof HTMLLinkElementImpl) {
((HTMLLinkElementImpl) node).getCurrentStyle().setOverlayColor(null);
return false;
}
if (!pass) {
return false;
}
final ModelNode parent = node.getParentModelNode();
if (parent == null) {
return true;
}
return this.onMouseUp(parent, event, x, y);
}
/**
* @param node
* The node generating the event.
* @param x
* For images only, x coordinate of mouse click.
* @param y
* For images only, y coordinate of mouse click.
* @return True to propagate further, false if consumed.
*/
public boolean onPressed(final ModelNode node, final InputEvent event, final int x, final int y) {
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOnclick();
if (f != null) {
final Event jsEvent = new Event("click", uiElement, event, x, y);
if (!Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement))) {
return false;
}
}
}
if (node instanceof HTMLInputElementImpl) {
final HTMLInputElementImpl hie = (HTMLInputElementImpl) node;
if (hie.isSubmitInput()) {
FormInput[] formInputs;
final String name = hie.getName();
if (name == null) {
formInputs = null;
} else {
formInputs = new FormInput[] { new FormInput(name, hie.getValue()) };
}
hie.submitForm(formInputs);
} else if (hie.isImageInput()) {
final String name = hie.getName();
final String prefix = name == null ? "" : name + ".";
final FormInput[] extraFormInputs = new FormInput[] { new FormInput(prefix + "x", String.valueOf(x)),
new FormInput(prefix + "y", String.valueOf(y)) };
hie.submitForm(extraFormInputs);
} else if (hie.isResetInput()) {
hie.resetForm();
}
}
if (node instanceof HTMLElementImpl) {
final HTMLElementImpl htmlElem = (HTMLElementImpl) node;
final Event evt = new Event("click", htmlElem, event, x, y);
htmlElem.dispatchEvent(evt);
}
// No propagate
return false;
}
public boolean onChange(final ModelNode node) {
if (node instanceof HTMLSelectElementImpl) {
final HTMLSelectElementImpl uiElement = (HTMLSelectElementImpl) node;
final Function f = uiElement.getOnchange();
if (f != null) {
final Event jsEvent = new Event("change", uiElement);
if (!Executor.executeFunction(uiElement, f, jsEvent, getWindowFactory(uiElement))) {
return false;
}
}
}
// No propagate
return false;
}
public boolean onKeyUp(final ModelNode node, final KeyEvent ke) {
boolean pass = true;
if (node instanceof NodeImpl) {
final NodeImpl uiElement = (NodeImpl) node;
final Event jsEvent = new Event("keyup", uiElement, ke);
pass = uiElement.dispatchEvent(jsEvent);
System.out.println("Dispatch result: " + pass);
}
/*
if (node instanceof HTMLAbstractUIElement) {
final HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node;
final Function f = uiElement.getOnmouseup();
if (f != null) {
final Event jsEvent = new Event("keyup", uiElement, ke);
pass = Executor.executeFunction(uiElement, f, jsEvent);
}
}*/
return pass;
}
}