Added:
/trunk/src/com/google/minijoe/html/js
/trunk/src/com/google/minijoe/html/js/HtmlJsFactory.java
/trunk/src/com/google/minijoe/html/js/ImageObject.java
/trunk/src/com/google/minijoe/html/js/JsWindow.java
/trunk/src/com/google/minijoe/html/js/XMLHttpRequestConstructor.java
/trunk/src/com/google/minijoe/html/js/XMLHttpRequestObject.java
/trunk/src/com/google/minijoe/html5
/trunk/src/com/google/minijoe/html5/Canvas2D.java
/trunk/src/com/google/minijoe/html5/CanvasElementHandler.java
/trunk/src/com/google/minijoe/html5/js
/trunk/src/com/google/minijoe/html5/js/CanvasObject.java
/trunk/src/com/google/minijoe/html5/js/Context2D.java
/trunk/src/com/google/minijoe/html5/js/Html5JsFactory.java
/trunk/src/com/google/minijoe/html5/js/JsWindow.java
/trunk/src/com/google/minijoe/html5/js/PolyFill.java
/trunk/src/com/google/minijoe/samples/browser/Html5Browser.java
Modified:
/trunk/.classpath
/trunk/.project
/trunk/javascript/canvasoids.js
/trunk/src/com/google/minijoe/html/Element.java
/trunk/src/com/google/minijoe/html/HtmlWidget.java
/trunk/src/com/google/minijoe/html/SystemRequestHandler.java
/trunk/src/com/google/minijoe/html/css/Style.java
/trunk/src/com/google/minijoe/samples/browser/HtmlScreen.java
/trunk/src/com/google/minijoe/samples/browser/ResourceRequester.java
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html/js/HtmlJsFactory.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,42 @@
+package com.google.minijoe.html.js;
+
+import com.google.minijoe.html5.js.JsWindow;
+import com.google.minijoe.sys.JsObject;
+import com.google.minijoe.sys.JsObjectFactory;
+
+public class HtmlJsFactory implements JsObjectFactory {
+
+ private static HtmlJsFactory factory;
+
+
+ //Html Js Native Object types
+ public static final int HTML_IMAGE_TYPE = 1;
+
+ private JsWindow environment;
+
+ /**
+ * Initialises the factory singleton, or nothing if it already exists.
+ *
+ * @param callbackContext JsObject with context to use for callbacks by
+ * any objects created by the factory
+ * @param eventLock Object used as lock for callback to synchronise on
+ */
+ public static HtmlJsFactory getFactory(JsWindow env) {
+ if (factory == null) {
+ factory = new HtmlJsFactory();
+ factory.environment = env;;
+ }
+ return factory;
+ }
+
+ public JsObject newInstance(int type) {
+ switch(type){
+ case HTML_IMAGE_TYPE:
+ //return new ImageObject(environment.getCallbackScope(),
+ // this);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html/js/ImageObject.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,82 @@
+package com.google.minijoe.html.js;
+
+import com.google.minijoe.html.BlockWidget;
+import com.google.minijoe.html.Element;
+import com.google.minijoe.html.HtmlWidget;
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+
+public class ImageObject extends JsObject {
+
+ private static final JsObject IMAGE_PROTOTYPE = new JsObject(
+ JsFunction.OBJECT_PROTOTYPE);
+
+ private JsObject callBackScope;
+
+ private Element imgElement;
+ private BlockWidget imgWidget;
+
+ private HtmlWidget root;
+
+ private static Object callbackEventLock;
+
+ public static final int ID_INIT_IMAGE = 2001;
+
+ public ImageObject(JsObject scope, Object lock, HtmlWidget root) {
+ super(IMAGE_PROTOTYPE);
+
+ this.root = root;
+ this.callBackScope = scope;
+ this.callbackEventLock = lock;
+
+ addVar("onLoad", null);
+ addVar("src", null);
+ }
+
+ public void evalNative(int index, JsArray stack, int sp, int parCount) {
+
+ switch (index) {
+ case ID_INIT_IMAGE:
+ this.imgElement = new Element(root, "img");
+ break;
+ default:
+ super.evalNative(index, stack, sp, parCount);
+ }
+ }
+
+
+ public String toString() {
+ return "[Image]";
+ }
+
+ public void setObject(String key, Object v) {
+ super.setObject(key, v);
+ if ("src".equals(key) && (v instanceof String)) {
+ this.imgElement.setAttribute("src", (String) v);
+
+ // TODO: need to make this the onLoad callback once Blockwidget
+ // has support for onload callbacks add to it!
+ this.imgWidget = new BlockWidget(this.imgElement, new boolean[] {
+ false, true });
+ }
+ }
+
+ //Callback from blockwidget notifying us that its finsihed loading the
img data
+ private void onLoadCallBack(JsFunction callBack) {
+ System.out.println("finished loading img");
+ addVar("width", new Integer(this.imgWidget.getWidth()));
+ addVar("height", new Integer(this.imgWidget.getHeight()));
+
+ JsFunction onLoadCallBack = (JsFunction) this.getObject("onLoad");
+ if (onLoadCallBack != null) {
+ synchronized (callbackEventLock) {
+ JsArray callBackStack = new JsArray();
+ callBackStack.setObject(0, this.callBackScope);
+ callBackStack.setObject(1, callBack);
+
+ callBack.eval(callBackStack, 0, 1);
+ }
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html/js/JsWindow.java Tue Jun 1 05:33:57
2010
@@ -0,0 +1,247 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.html.js;
+
+import com.google.minijoe.html.HtmlWidget;
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+import com.google.minijoe.sys.JsSystem;
+
+import java.util.Vector;
+
+import javax.microedition.lcdui.Alert;
+import javax.microedition.lcdui.AlertType;
+import javax.microedition.lcdui.Canvas;
+import javax.microedition.lcdui.Display;
+
+/**
+ * Provides a simple JavaScript environment providing a set of
+ * methods and fields described in at
+ * http://docs.google.com/a/google.com/Doc?id=cdj7wdfh_48dw4jmcfc
+ *
+ * @author Stefan Haustein
+ */
+public class JsWindow extends JsObject implements Runnable {
+
+ static final int ID_GET_ELEMENT_BY_ID = 1000;
+ static final int ID_WRITE = 1001;
+ static final int ID_SET_TIMEOUT = 1003;
+ static final int ID_SET_INTERVAL = 1004;
+
+ static int scheduleId;
+
+ static final JsObject PROTOTYPE = new JsObject(OBJECT_PROTOTYPE);
+ /**
+ * entries are Object[] containing execution time, method, interval
+ */
+ Vector schedule = new Vector();
+ static int timerId;
+ JsArray stack = new JsArray();
+ boolean stop = false;
+ Object eventLock = new Object();
+ HtmlWidget rootDocument;
+
+
+ public Object getEventLock() {
+ return eventLock;
+}
+
+/**
+ * Constructs a new environment.
+ *
+ */
+ public JsWindow(HtmlWidget rootDocument) {
+ super(PROTOTYPE);
+
+ this.rootDocument = rootDocument;
+ scopeChain = JsSystem.createGlobal();
+
+ addVar("getElementById", new JsFunction(ID_GET_ELEMENT_BY_ID, 1));
+ addVar("write", new JsFunction(ID_WRITE, 1));
+
+ addVar("setTimeout", new JsFunction(ID_SET_TIMEOUT, 2));
+ addVar("setInterval", new JsFunction(ID_SET_INTERVAL, 2));
+ addVar("document", this);
+ addVar("window", this);
+
+ if (rootDocument != null) {
+ addVar("width", new Double(rootDocument.getWidth()));
+ addVar("height", new Double(rootDocument.getHeight()));
+ addVar("innerWidth", new Double(rootDocument.getWidth()));
+ addVar("innerHeight", new Double(rootDocument.getHeight()));
+ } else {
+ System.out.println("null doc root passed to JsWIndow");
+ }
+ stack.setObject(0, this);
+ }
+
+ /**
+ * Java implementation of the provided JS methods and fields.
+ */
+ public void evalNative(int id, JsArray stack, int sp, int parCount){
+ switch(id){
+ case ID_GET_ELEMENT_BY_ID:
+ stack.setObject(sp, this);
+ break;
+
+ case ID_WRITE:
+ write(stack.getString(sp + 2));
+ break;
+
+ case ID_SET_TIMEOUT:
+ stack.setNumber(sp,
+ schedule((JsFunction) stack.getObject(sp + 2), stack.getInt(sp
+ 3), false));
+ break;
+
+ case ID_SET_INTERVAL:
+ stack.setNumber(sp,
+ schedule((JsFunction) stack.getObject(sp + 2), stack.getInt(sp
+ 3), true));
+ break;
+
+ default:
+ super.evalNative(id, stack, sp, parCount);
+ }
+ }
+
+ public HtmlWidget getRootDocument() {
+ return rootDocument;
+}
+
+private void write(String string) {
+ System.out.println("document.write: " + string);
+ }
+
+ /**
+ * Schedule a callback
+ *
+ * @param function function to be called
+ * @param dt time interval
+ * @param repeat if true, calls are rescheduled automatically
+ * @return a id that can be used to terminate an interval
+ */
+ private int schedule(JsFunction function, int dt, boolean repeat) {
+ // this should be binary search but if there should not be more than
1-3 entries anyway...
+
+ long t0 = System.currentTimeMillis() + dt;
+
+ synchronized (schedule) {
+ int i = schedule.size() - 1;
+ while (i >= 0 && ((Long) ((Object[])
schedule.elementAt(i))[1]).longValue() > t0) {
+ i--;
+ }
+
+ if (repeat) {
+ schedule.insertElementAt(new Object[]{
+ new Integer(++timerId),
+ new Long(t0),
+ function,
+ new Integer(dt)}, i + 1);
+ } else {
+ schedule.insertElementAt(new Object[]{
+ new Integer(++timerId),
+ new Long(t0),
+ function}, i + 1);
+ }
+ return timerId;
+ }
+ }
+
+ /**
+ * Runs the scheduler.
+ */
+ public void run() {
+ try {
+ while (!stop) {
+ if (schedule.size() == 0) {
+ synchronized (eventLock) {
+ eventLock.wait(20);
+ }
+ continue;
+ }
+
+ Object[] next;
+ synchronized (schedule) {
+ next = (Object[]) schedule.elementAt(0);
+ schedule.removeElementAt(0);
+ }
+ long time = ((Long) next[1]).longValue();
+ JsFunction call = (JsFunction) next[2];
+ synchronized (eventLock) {
+ eventLock.wait (Math.max(5, time - System.currentTimeMillis()));
+ // Note: stack[0] is filled with 'this' in the constructor
+ stack.setObject(1, this);
+ stack.setObject(2, call);
+ call.eval(stack, 1, 0);
+ }
+ //screen.drawTree(g, dx, dy, clipX, clipY, clipW, clipH); //FIXME:
need to get hold of g
+ if (next.length == 4) {
+ schedule(call, ((Integer) next[3]).intValue(), true);
+ }
+ }
+ } catch (Exception e) {
+ //FIXME need to handle exception better then just swallowing with err
mesg
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * The scope to use whe doing callbacks into JS functions
+ * @return
+ */
+ public JsObject getCallbackScope() {
+ return this;
+ }
+
+
+ /**
+ * Calls the given JavaScript function with the given key event.
+ * Google internal cursor key codes are converted to PC key codes.
+ *
+ * @param f the function to be called
+ * @param key the key event
+ */
+ public void keyEvent(JsFunction f, int code, int action) {
+ JsObject event = new JsObject(JsObject.OBJECT_PROTOTYPE);
+
+ switch(action){
+ case Canvas.LEFT:
+ code = 37;
+ break;
+ case Canvas.UP:
+ code = 38;
+ break;
+ case Canvas.RIGHT:
+ code = 39;
+ break;
+ case Canvas.DOWN:
+ code = 40;
+ break;
+ }
+
+ event.addVar("keyCode", new Double(code));
+ synchronized (eventLock) {
+ stack.setObject(1, this);
+ stack.setObject(2, f);
+ stack.setObject(3, event);
+ f.eval(stack, 1, 1);
+ }
+ }
+
+ public String toString(){
+ return "[object JsWindow]";
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html/js/XMLHttpRequestConstructor.java
Tue Jun 1 05:33:57 2010
@@ -0,0 +1,37 @@
+// Copyright Manichord Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*
+ * Implements as far as possible XMLHttpRequest as per:
+ * http://www.w3.org/TR/XMLHttpRequest/
+ */
+
+package com.google.minijoe.html.js;
+
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+
+public class XMLHttpRequestConstructor extends JsFunction {
+
+ private static final int XMLHTTPREQUEST_TYPE_ID = 1;
+
+ private static final JsObject XMLHTTPREQUEST_CONSTRUCTOR_PROTOTYPE
+ = new JsObject(JsFunction.FUNCTION_PROTOTYPE);
+
+ public XMLHttpRequestConstructor(HtmlJsFactory factory) {
+ super(factory, XMLHTTPREQUEST_TYPE_ID,
+ XMLHTTPREQUEST_CONSTRUCTOR_PROTOTYPE, 0, 0);
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html/js/XMLHttpRequestObject.java Tue
Jun 1 05:33:57 2010
@@ -0,0 +1,186 @@
+// Copyright Manichord Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*
+ * Implements as far as possible XMLHttpRequest as per:
+ * http://www.w3.org/TR/XMLHttpRequest/
+ */
+
+package com.google.minijoe.html.js;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.ContentConnection;
+import javax.microedition.io.HttpConnection;
+
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+
+public class XMLHttpRequestObject extends JsObject implements Runnable {
+
+ private static Object callbackEventLock;
+
+ private static final JsObject XMLHTTPREQUEST_PROTOTYPE = new JsObject(
+ JsFunction.OBJECT_PROTOTYPE);
+
+ private static final Double READY_STATE_UNSENT = new Double(0);
+ private static final Double READY_STATE_OPENED = new Double(1);
+ private static final Double READY_STATE_HEADERS_RECEIVED = new Double(2);
+ private static final Double READY_STATE_LOADING = new Double(3);
+ private static final Double READY_STATE_DONE = new Double(4);
+
+ private static final int XHR_OPEN_METHOD_ID = 1001;
+ private static final int XHR_SEND_METHOD_ID = 1002;
+ private static final int XHR_SET_REQUEST_HEADER_METHOD_ID = 1003;
+ private static final int XHR_ABORT_METHOD_ID = 1004;
+
+ private String url;
+ private String httpMethod;
+ private String username;
+ private String password;
+ private Hashtable headers = new Hashtable();
+ private StringBuffer responseText = new StringBuffer();
+ private byte[] responseBody;
+ private String requestData;
+
+ private JsObject callBackScope;
+
+ public XMLHttpRequestObject(JsObject scope, Object lock) {
+
+ super(XMLHTTPREQUEST_PROTOTYPE);
+ this.callBackScope = scope;
+ this.callbackEventLock = lock;
+
+ addNative("open", XHR_OPEN_METHOD_ID, 5);
+ addNative("send", XHR_SEND_METHOD_ID, 1);
+ addNative("setRequestHeader", XHR_SET_REQUEST_HEADER_METHOD_ID, 2);
+
+ addVar("status", null);
+ addVar("readyState", READY_STATE_UNSENT);
+ addVar("responseText", null);
+ addVar("responseXML", null); // Note: currently not supported
+ addVar("onReadyStateChange", null);
+ }
+
+ public void evalNative(int id, JsArray stack, int sp, int parCount) {
+ switch (id) {
+ case XHR_OPEN_METHOD_ID:
+ this.httpMethod = stack.getString(sp + 2);
+ this.url = stack.getString(sp + 3);
+ // if we get a 3rd param setting sync, just ignore as we always
+ // async to not block gui thread
+ this.username = stack.getString(sp + 5);
+ this.password = stack.getString(sp + 6);
+ addVar("readyState", READY_STATE_OPENED);
+ break;
+ case XHR_SEND_METHOD_ID:
+ this.requestData = stack.getString(sp + 2);
+ Thread t = new Thread(this);
+ t.start();
+ break;
+ case XHR_SET_REQUEST_HEADER_METHOD_ID:
+ this.headers.put(stack.getString(sp + 2), stack.getString(sp + 3));
+ break;
+ case XHR_ABORT_METHOD_ID:
+ // Todo:
+ break;
+ }
+ }
+
+ public void run() {
+ readHttp();
+ }
+
+ private void readHttp() {
+ HttpConnection c = null;
+ DataInputStream dis = null;
+
+ try {
+ c = (HttpConnection) Connector.open(this.url);
+
+ c.setRequestMethod(this.httpMethod);
+
+ Enumeration keys = this.headers.keys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ c.setRequestProperty((String) key, (String) headers.get(key));
+ }
+
+ addVar("readyState", READY_STATE_LOADING);
+ addVar("status", new Double(c.getResponseCode()));
+
+ int len = (int) c.getLength();
+ dis = c.openDataInputStream();
+ if (len > 0) {
+ this.responseBody = new byte[len];
+ dis.readFully(this.responseBody);
+ addVar("resBytes", this.responseBody);
+ for (int i = 0; i < this.responseBody.length; i++) {
+ // TODO: check MIME-type and handle charset correctly
+ this.responseText.append((char) this.responseBody[i]);
+ }
+ } else {
+ int ch;
+ while ((ch = dis.read()) != -1) {
+ // TODO: check MIME-type and handle charset correctly
+ this.responseText.append(ch);
+ }
+ }
+ } catch (IOException e) {
+ // swallow exception as XHR open() method cannot throw error
+ e.printStackTrace();
+ } finally {
+ if (dis != null)
+ try {
+ dis.close();
+ if (c != null) {
+ c.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ addVar("responseText", responseText.toString());
+ addVar("readyState", READY_STATE_DONE);
+ JsFunction onReadyStateChangeCallBack = (JsFunction) this
+ .getObject("onReadyStateChange");
+ if (onReadyStateChangeCallBack != null) {
+ doCallBack(onReadyStateChangeCallBack);
+ } else {
+ // System.out.println("NO callback:" + onReadyStateChangeCallBack);
+ }
+ }
+
+ private void doCallBack(JsFunction callBack) {
+ JsObject event = new JsObject(JsObject.OBJECT_PROTOTYPE);
+
+ synchronized (callbackEventLock) {
+ JsArray callBackStack = new JsArray();
+ callBackStack.setObject(0, this.callBackScope);
+ callBackStack.setObject(1, callBack);
+ callBackStack.setObject(2, event);
+
+ callBack.eval(callBackStack, 0, 1);
+ }
+ }
+
+ public String toString() {
+ return "[XMLHttpRequest]";
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/Canvas2D.java Tue Jun 1 05:33:57
2010
@@ -0,0 +1,87 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.html5;
+
+import javax.microedition.lcdui.Graphics;
+import javax.microedition.lcdui.Image;
+
+import com.google.minijoe.html.BlockWidget;
+import com.google.minijoe.html.Element;
+import com.google.minijoe.html.uibase.Widget;
+import com.google.minijoe.html5.js.CanvasObject;
+import com.google.minijoe.html5.js.Html5JsFactory;
+import com.google.minijoe.html5.js.JsWindow;
+import com.google.minijoe.sys.JsFunction;
+
+/**
+ * Simple canvas implementation for the MiniJoe HTML5 browser.
+ *
+ * @author Stefan Haustein
+ */
+public class Canvas2D extends BlockWidget {
+
+ Image canvas;
+ JsWindow env;
+ CanvasObject jsObject;
+
+ Canvas2D(Element element) {
+ super(element, false);
+ this.env = element.getHtmlWidget().globalScope;
+ //System.out.println("e data:"+element.getAttributeInt("width", 0));
+ canvas = Image.createImage(
+ element.getAttributeInt("width", 1),
+ element.getAttributeInt("height", 1)); //default 1 because 0 will
throw exception
+
+ this.jsObject = (CanvasObject)
Html5JsFactory.getFactory(env).newInstance(Html5JsFactory.HTML5_CANVAS_TYPE);
+ this.jsObject.setCanvasBuffer(canvas);
+ }
+
+ public CanvasObject getCanvasObject(){
+ return this.jsObject;
+ }
+
+ public void drawContent(Graphics g, int dx, int dy) {
+ synchronized (env.getEventLock()) {
+ g.drawImage(canvas, dx, dy, Graphics.TOP | Graphics.LEFT);
+ }
+ }
+
+ public boolean handleKeyEvent(int type, int keyCode, int action) {
+ System.out.println("handle key"+env.getObject("onkeyup"));
+ if (type == Widget.KEY_PRESSED) {
+ Object o = env.getObject("onkeydown");
+ if (o instanceof JsFunction) {
+ env.keyEvent((JsFunction) o, keyCode, action);
+ }
+ return true;
+ }
+ if (type == Widget.KEY_RELEASED) {
+ Object o = env.getObject("onkeyup");
+ if (o instanceof JsFunction) {
+ env.keyEvent((JsFunction) o, keyCode, action);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isFocusable() {
+ return true;
+ }
+
+ public String toString() {
+ return "canvas widget";
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/CanvasElementHandler.java Tue Jun
1 05:33:57 2010
@@ -0,0 +1,25 @@
+/**
+ *
+ */
+package com.google.minijoe.html5;
+
+import com.google.minijoe.html.Element;
+import com.google.minijoe.html.ElementHandler;
+import com.google.minijoe.html5.js.JsWindow;
+
+/**
+ * @author Maksim Lin
+ *
+ */
+public class CanvasElementHandler implements ElementHandler {
+
+ public CanvasElementHandler() {
+ }
+
+ public Object handle(Element element, boolean inBody) {
+ System.out.println("canvas handler made new canvas2d obj"+
+ element);
+ return new Canvas2D(element);
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/js/CanvasObject.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,65 @@
+package com.google.minijoe.html5.js;
+
+import javax.microedition.lcdui.Image;
+
+import com.google.minijoe.html.BlockWidget;
+import com.google.minijoe.html.Element;
+import com.google.minijoe.html.HtmlWidget;
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+
+public class CanvasObject extends JsObject {
+
+ private static final JsObject CANVAS_PROTOTYPE = new JsObject(
+ JsFunction.OBJECT_PROTOTYPE);
+
+ private JsObject callBackScope;
+
+ private Element canvasElement;
+ private BlockWidget canvasWidget;
+
+ private HtmlWidget root;
+
+ private Image canvasBuffer;
+
+ private static Object callbackEventLock;
+
+ public static final int ID_GET_CONTEXT = 3001;
+
+ public CanvasObject(JsObject scope, Object lock, HtmlWidget root) {
+ super(CANVAS_PROTOTYPE);
+
+ this.root = root;
+ this.callBackScope = scope;
+ this.callbackEventLock = lock;
+
+ addVar("getContext", new JsFunction(ID_GET_CONTEXT, 1));
+ }
+
+ public void setCanvasElement(Element canvasElement) {
+ this.canvasElement = canvasElement;
+ }
+
+ public void setCanvasBuffer(Image canvasBuffer) {
+ this.canvasBuffer = canvasBuffer;
+ }
+
+ public void evalNative(int index, JsArray stack, int sp, int parCount) {
+
+ switch (index) {
+ case ID_GET_CONTEXT:
+ System.out.println("get context called "+this.canvasBuffer);
+ //FIXME: check if first param == "2d" before returning 2d context
+
+ stack.setObject(sp, new Context2D(canvasBuffer));
+ break;
+ default:
+ super.evalNative(index, stack, sp, parCount);
+ }
+ }
+
+ public String toString() {
+ return "[Canvas]";
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/js/Context2D.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,693 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.html5.js;
+
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsObject;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.microedition.lcdui.Font;
+import javax.microedition.lcdui.Graphics;
+import javax.microedition.lcdui.Image;
+
+/**
+ * Graphics context: partial implementation of Javascript Context2D.
+ * Provides drawing methods.
+ *
+ * @author Stefan Haustein
+ */
+public class Context2D extends JsObject {
+
+ static final int ID_BEGIN_PATH = 100;
+ static final int ID_CLOSE_PATH = 101;
+ static final int ID_FILL = 102;
+ static final int ID_FILL_RECT = 103;
+ static final int ID_FILL_STYLE = 104;
+ static final int ID_FILL_STYLE_SET = 105;
+
+ static final int ID_LINE_TO = 106;
+ static final int ID_MOVE_TO = 107;
+ static final int ID_QUADRATIC_CURVE_TO = 108;
+
+ static final int ID_RESTORE = 109;
+ static final int ID_ROTATE = 110;
+ static final int ID_SAVE = 111;
+ static final int ID_SCALE = 112;
+ static final int ID_STROKE = 113;
+ static final int ID_STROKE_RECT = 114;
+ static final int ID_TRANSLATE = 115;
+ static final int ID_DRAW_IMAGE = 116;
+ static final int ID_TEXT_STYLE = 117;
+ static final int ID_TEXT_STYLE_SET = 118;
+ static final int ID_MEASURE_TEXT = 119;
+ static final int ID_CLEAR_RECT = 120;
+ static final int ID_ARC = 121;
+ static final int ID_BEZIER_CURVE_TO = 122;
+ static final int ID_STROKE_STYLE = 123;
+ static final int ID_STROKE_STYLE_SET = 124;
+ static final int ID_DRAW_TEXT = 125;
+
+ static final Hashtable COLORS = new Hashtable();
+
+ static final JsObject CONTEXT_PROTOTYPE = new
JsObject(JsObject.OBJECT_PROTOTYPE)
+ .addNative("beginPath", ID_BEGIN_PATH, 0)
+ .addNative("closePath", ID_CLOSE_PATH, 0)
+ .addNative("fill", ID_FILL, 0)
+ .addNative("fillStyle", ID_FILL_STYLE, -1)
+ .addNative("fillRect", ID_FILL_RECT, 4)
+ .addNative("lineTo", ID_LINE_TO, 2)
+ .addNative("moveTo", ID_MOVE_TO, 2)
+ .addNative("quadraticCurveTo", ID_QUADRATIC_CURVE_TO, 4)
+ .addNative("rotate", ID_ROTATE, 1)
+ .addNative("save", ID_SAVE, 0)
+ .addNative("stroke", ID_STROKE, 0)
+ .addNative("strokeRect", ID_STROKE_RECT, 4)
+ .addNative("scale", ID_SCALE, 2)
+ .addNative("translate", ID_TRANSLATE, 2)
+ .addNative("drawImage", ID_DRAW_IMAGE, 9)
+ .addNative("restore", ID_RESTORE, 0)
+ .addNative("textStyle", ID_TEXT_STYLE, -1)
+ .addNative("mozTextStyle", ID_TEXT_STYLE, -1)
+ .addNative("clearRect", ID_CLEAR_RECT, 4)
+ .addNative("arc", ID_ARC, 6)
+ .addNative("bezierCurveTo", ID_BEZIER_CURVE_TO, 6)
+ .addNative("strokeStyle", ID_STROKE_STYLE, -1)
+ .addNative("drawText", ID_DRAW_TEXT, 1)
+ .addNative("mozDrawText", ID_DRAW_TEXT, 1)
+ .addNative("measureText", ID_MEASURE_TEXT, 1)
+ .addNative("mozMeasureText", ID_MEASURE_TEXT, 1)
+ ;
+
+ // Value caches for curve calculations
+ static final double[] EXP2 = new double[15];
+ static final double[] EXP3 = new double[15];
+ static final double[] ZT1MT = new double[15];
+ static final double[] DT1MT2 = new double[15];
+
+ // init constants, colors
+ static{
+ COLORS.put("red", new Integer(0x0ff0000));
+ COLORS.put("green", new Integer(0x000ff00));
+ COLORS.put("blue", new Integer(0x00000ff));
+ COLORS.put("white", new Integer(0x0ffffff));
+ COLORS.put("black", new Integer(0));
+ COLORS.put("yellow", new Integer(0x0ffff00));
+ COLORS.put("grey", new Integer(0x0ff888888));
+
+ double delta = 1.0 / 16.0;
+ double pos = delta;
+ for (int i = 0; i < 15; i++){
+ double l = pos * pos;
+ EXP2[i] = l;
+ EXP3[i] = l * pos;
+ ZT1MT[i] = ((2.0 * pos * (1 - pos)));
+ DT1MT2[i] = ((3.0 * pos * (1 - pos) * (1 - pos)));
+
+// System.out.println("i: "+i+ " pos: "+ fpToString(pos)
+// + " exp2:"+fpToString(EXP2[i])
+// + " 2t(1-t) "+fpToString(ZT1MT[i])
+// + " exp3: "+fpToString(EXP3[i])
+// + " 3t(1-t)^2 "+fpToString(DT1MT2[i]));
+
+ pos += delta;
+ }
+ }
+
+ private boolean virgin;
+ private Image buffer;
+ private Graphics graphics;
+
+ private int pathPos = 0;
+ private double[] path = new double[32];
+ private boolean fill;
+
+ private int scrStartX;
+ private int scrStartY;
+
+ private int currentScrX;
+ private int currentScrY;
+ private int currentFillColor = 0x0ff000000;
+ private int currentLineColor = 0x0ff000000;
+
+ //TODO Consider changing to float and special cases for neut/simple
matrices
+ private double translateX = 0;
+ private double translateY = 0;
+ private double scaleX = 1;
+ private double scew1 = 0;
+ private double scaleY = 1;
+ private double scew2 = 0;
+
+ private Vector stack = new Vector();
+ private Font font;
+
+ public Context2D(Image graphicsBuffer){
+ super(CONTEXT_PROTOTYPE);
+
+ this.graphics = graphicsBuffer.getGraphics();
+
+ this.font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,
Font.SIZE_SMALL);
+ }
+
+ static String[] split(String s, char c) {
+ int count = 1;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == c) {
+ count++;
+ }
+ }
+ String[] parts = new String[count];
+ int lastCut = 0;
+ int n = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == c) {
+ parts[n] = s.substring(lastCut, i);
+ lastCut = i + 1;
+ }
+ }
+ parts[n] = s.substring(lastCut, s.length());
+ return parts;
+ }
+
+
+ private final int translateX(double x, double y){
+ return (int) (((x * scaleX + y * scew1)) + (translateX));
+ }
+
+ private final int translateY(double x, double y){
+ return (int) (((y * scaleY + x * scew2)) + (translateY));
+ }
+
+ public void evalNative(int id, JsArray stack, int sp, int parCount) {
+ switch (id){
+ case ID_BEGIN_PATH:
+ beginPath();
+ break;
+
+ case ID_CLOSE_PATH:
+ closePath();
+ break;
+
+ case ID_LINE_TO:
+ lineTo(stack.getNumber(sp + 2), stack.getNumber(sp + 3));
+ break;
+
+ case ID_MOVE_TO:
+ moveTo(stack.getNumber(sp + 2), stack.getNumber(sp + 3));
+ break;
+
+ case ID_FILL:
+ fill();
+ break;
+
+ case ID_FILL_RECT:
+ double x0 = stack.getNumber(sp + 2);
+ double y0 = stack.getNumber(sp + 3);
+ double x1 = x0 + stack.getNumber(sp + 4) - 1;
+ double y1 = y0 + stack.getNumber(sp + 5) - 1;
+ fillRect(x0, y0, x1, y1);
+ break;
+
+ case ID_CLEAR_RECT:
+ x0 = stack.getNumber(sp + 2);
+ y0 = stack.getNumber(sp + 3);
+ x1 = x0 + stack.getNumber(sp + 4) - 1;
+ y1 = y0 + stack.getNumber(sp + 5) - 1;
+ int sfc = currentFillColor;
+ currentFillColor = 0x0ffffffff;
+ fillRect(x0, y0, x1, y1);
+ currentFillColor = sfc;
+ break;
+
+ case ID_STROKE:
+ stroke();
+ break;
+
+ case ID_STROKE_RECT:
+ x0 = stack.getNumber(sp + 2);
+ y0 = stack.getNumber(sp + 3);
+ x1 = x0 + stack.getNumber(sp + 4) - 1;
+ y1 = y0 + stack.getNumber(sp + 5) - 1;
+ strokeRect(x0, y0, x1, y1);
+ break;
+
+ case ID_SAVE:
+ save();
+ break;
+
+ case ID_RESTORE:
+ restore();
+ break;
+
+ case ID_QUADRATIC_CURVE_TO:
+ if (pathPos + 5 >= path.length) {
+ enlargeBuf(5);
+ }
+ path[pathPos++] = ID_QUADRATIC_CURVE_TO;
+ path[pathPos++] = stack.getNumber(sp + 2);
+ path[pathPos++] = stack.getNumber(sp + 3);
+ path[pathPos++] = stack.getNumber(sp + 4);
+ path[pathPos++] = stack.getNumber(sp + 5);
+ break;
+
+ case ID_BEZIER_CURVE_TO:
+ if (pathPos + 7 >= path.length) {
+ enlargeBuf(7);
+ }
+ path[pathPos++] = ID_BEZIER_CURVE_TO;
+ path[pathPos++] = stack.getNumber(sp + 2);
+ path[pathPos++] = stack.getNumber(sp + 3);
+ path[pathPos++] = stack.getNumber(sp + 4);
+ path[pathPos++] = stack.getNumber(sp + 5);
+ path[pathPos++] = stack.getNumber(sp + 6);
+ path[pathPos++] = stack.getNumber(sp + 7);
+ break;
+
+ case ID_ROTATE:
+ double ang = -stack.getNumber(sp + 2);
+ double sin = Math.sin(ang);
+ double cos = Math.cos(ang);
+ transform(cos, -sin, sin, cos, 0, 0);
+ break;
+
+ case ID_SCALE:
+ transform(stack.getNumber(sp + 2), 0, 0, stack.getNumber(sp + 3),
0, 0);
+ break;
+
+ case ID_TRANSLATE:
+ transform(1, 0, 0, 1, stack.getNumber(sp + 2), stack.getNumber(sp
+ 3));
+ break;
+
+ case ID_FILL_STYLE_SET:
+ currentFillColor = parseColor((String) stack.getObject(sp));
+ break;
+
+ case ID_STROKE_STYLE_SET:
+ currentLineColor = parseColor((String) stack.getObject(sp));
+ break;
+
+ case ID_DRAW_IMAGE:
+ System.out.println("DrawImage Not Yet Supported");
+ break;
+
+ case ID_FILL_STYLE:
+ StringBuffer buf = new
StringBuffer(Integer.toString(currentFillColor, 16));
+ while (buf.length() < 8) {
+ buf.insert(0, '0');
+ }
+ buf.insert(0, '#');
+ stack.setObject(sp, buf.toString());
+ break;
+
+ case ID_STROKE_STYLE:
+ buf = new StringBuffer(Integer.toString(currentLineColor, 16));
+ while (buf.length() < 8) {
+ buf.insert(0, '0');
+ }
+ buf.insert(0, '#');
+ stack.setObject(sp, buf.toString());
+ break;
+
+ case ID_TEXT_STYLE:
+ break;
+
+ case ID_TEXT_STYLE_SET:
+ break;
+
+ case ID_DRAW_TEXT:
+ graphics.setColor(currentFillColor);
+ graphics.setFont(font);
+ graphics.drawString(stack.getString(sp + 2),
+ translateX(0, 0),
+ translateY(0, 0), Graphics.BASELINE | Graphics.LEFT);
+ break;
+
+ case ID_MEASURE_TEXT:
+ stack.setNumber(sp, font.stringWidth(stack.getString(sp + 2)) /
scaleX);
+ break;
+
+ case ID_ARC:
+ if (pathPos + 7 >= path.length) {
+ enlargeBuf(7);
+ }
+ path[pathPos++] = ID_ARC;
+ path[pathPos++] = stack.getNumber(sp + 2);
+ path[pathPos++] = stack.getNumber(sp + 3);
+ path[pathPos++] = stack.getNumber(sp + 4);
+ path[pathPos++] = stack.getNumber(sp + 5);
+ path[pathPos++] = stack.getNumber(sp + 6);
+ path[pathPos++] = stack.getNumber(sp + 7);
+ break;
+
+ default:
+ super.evalNative(id, stack, sp, parCount);
+ }
+ }
+
+ private final void enlargeBuf(int i) {
+ i = (path.length + i) * 3 / 2;
+
+ double[] np = new double[i];
+ System.arraycopy(path, 0, np, 0, pathPos);
+ path = np;
+ }
+
+ private final void restore() {
+ double[] state = (double[]) this.stack.elementAt(this.stack.size() -
1);
+ scaleX = state[0];
+ scew1 = state[1];
+ scew2 = state[2];
+ scaleY = state[3];
+ translateX = state[4];
+ translateY = state[5];
+ this.stack.removeElementAt(this.stack.size() - 1);
+ }
+
+ private final void save() {
+ this.stack.addElement(new double[]{scaleX, scew1, scew2, scaleY,
translateX, translateY});
+ }
+
+ private final void transform(double a2, double b2, double c2, double d2,
double e2, double f2) {
+ double tmp = b2;
+ b2 = c2;
+ c2 = tmp;
+
+ double a1 = scaleX;
+ double b1 = scew1;
+ double c1 = scew2;
+ double d1 = scaleY;
+ double e1 = translateX;
+ double f1 = translateY;
+
+ scaleX = a1 * a2 + c1 * b2;
+ scew1 = b1 * a2 + d1 * b2;
+
+ scew2 = a1 * c2 + c1 * d2;
+ scaleY = b1 * c2 + d1 * d2;
+
+ translateX = (a1 * e2 + c1 * f2) + e1;
+ translateY = (b1 * e2 + d1 * f2) + f1;
+ }
+
+ private final void drawPath(){
+ int i = 0;
+ virgin = true;
+ while (i < pathPos) {
+ int type = (int) path[i++];
+ double x = path[i++];
+ double y = path[i++];
+
+ switch(type){
+ case ID_MOVE_TO:
+ scrMoveTo(translateX(x, y), translateY(x, y));
+ break;
+ case ID_LINE_TO:
+ // sets currentScrX/Y
+ scrLineTo(translateX(x, y), translateY(x, y));
+ break;
+ case ID_BEZIER_CURVE_TO:
+ bezierCurveTo(x, y,
+ path[i],
+ path[i + 1],
+ path[i + 2],
+ path[i + 3]);
+ i += 4;
+ break;
+ case ID_QUADRATIC_CURVE_TO:
+ quadraticCurveTo(x, y,
+ path[i],
+ path[i + 1]);
+ i += 2;
+ break;
+ case ID_ARC:
+ arc(x, y,
+ path[i],
+ path[i + 1],
+ path[i + 2],
+ path[i + 3] != 0);
+ i += 4;
+ break;
+ case ID_CLOSE_PATH:
+ scrLineTo(scrStartX, scrStartY);
+ break;
+ }
+ }
+ }
+
+ private final void scrMoveTo(int i, int j) {
+ currentScrX = scrStartX = i;
+ currentScrY = scrStartY = j;
+ virgin = false;
+ if (fill) {
+ PolyFill.moveTo(i, j);
+ }
+
+ }
+
+ private final void fill() {
+ fill = true;
+ PolyFill.beginPath();
+ drawPath();
+ PolyFill.fill(graphics, currentFillColor);
+ }
+
+
+ private final void moveTo(double x, double y) {
+
+ if (pathPos + 3 >= path.length) {
+ enlargeBuf(3);
+ }
+ path[pathPos++] = ID_MOVE_TO;
+ path[pathPos++] = x;
+ path[pathPos++] = y;
+ }
+
+
+ private final void scrLineTo(int scrX, int scrY){
+
+// System.out.println("ScrLineTo("+scrX+","+scrY+")");
+
+ if (virgin) {
+ scrMoveTo(scrX, scrY);
+ } else if (fill) {
+ PolyFill.lineTo(scrX, scrY);
+ } else {
+ graphics.drawLine(currentScrX, currentScrY, scrX, scrY);
+ }
+
+ currentScrX = scrX;
+ currentScrY = scrY;
+ }
+
+ private final void lineTo(double x, double y) {
+
+ if (pathPos + 3 >= path.length) {
+ enlargeBuf(3);
+ }
+ path[pathPos++] = ID_LINE_TO;
+ path[pathPos++] = x;
+ path[pathPos++] = y;
+ }
+
+ private final void closePath(){
+ if (pathPos + 3 >= path.length) {
+ enlargeBuf(3);
+ }
+ path[pathPos] = ID_CLOSE_PATH;
+ // two ignored parameter to simplify drawing logic
+ pathPos += 3;
+ }
+
+ private final void stroke() {
+ if ((currentLineColor & 0x0ff000000) == 0) {
+ return;
+ }
+ fill = false;
+ graphics.setColor(currentLineColor & 0x0ffffff);
+ drawPath();
+ }
+
+ private final void beginPath() {
+ pathPos = 0;
+ }
+
+ private final int parseColor(String style){
+ if (style.startsWith("rgba")) {
+ int cut0 = style.indexOf('(') + 1;
+ int cut1 = style.indexOf(')', cut0);
+ String[] rgba = split(style.substring(cut0, cut1), ',');
+ return (Integer.parseInt(rgba[0].trim()) << 16) |
+ (Integer.parseInt(rgba[1].trim()) << 8) |
+ Integer.parseInt(rgba[2].trim()) |
+ (((int) (Double.parseDouble(rgba[3].trim()) * 255.0) & 0x0ff) <<
24);
+ } else if (style.startsWith("rgb")) {
+ int cut0 = style.indexOf('(') + 1;
+ int cut1 = style.indexOf(')', cut0);
+ String[] rgba = split(style.substring(cut0, cut1), ',');
+ return (Integer.parseInt(rgba[0].trim()) << 16) |
+ (Integer.parseInt(rgba[1].trim()) << 8) |
+ Integer.parseInt(rgba[2].trim()) | 0x0ff000000;
+ } else if (style.startsWith("#")) {
+ return Integer.parseInt(style.substring(1), 16) | 0x0ff000000;
+ }
+ Integer c = (Integer) COLORS.get(style);
+ return 0x0ff000000 | (c == null ? 0 : c.intValue());
+ }
+
+ private final void bezierCurveTo(double cp1x, double cp1y,
+ double cp2x, double cp2y, double x, double y){
+ int p0x = currentScrX;
+ int p0y = currentScrY;
+
+ int p1x = translateX(cp1x, cp1y);
+ int p1y = translateY(cp1x, cp1y);
+
+ int p2x = translateX(cp2x, cp2y);
+ int p2y = translateY(cp2x, cp2y);
+
+ int p3x = translateX(x, y);
+ int p3y = translateY(x, y);
+
+ int d = Math.max((p0x - p3x) * (p0x - p3x) + (p0y - p3y) * (p0y - p3y),
+ Math.max((p0x - p1x) * (p0x - p1x) + (p0y - p1y) * (p0y - p1y),
+ (p2x - p3x) * (p2x - p3x) + (p2y - p3y) * (p2y -
p3y)));
+ int step;
+ if (d > 1000) { //
+ step = d > 4000 ? 1 : 2;
+ } else {
+ step = d > 250 ? 4 : 8;
+ }
+ for (int i = step - 1; i < 15; i += step){
+ int newScrX = (int) (EXP3[14 - i] * p0x + DT1MT2[i] * p1x +
+ DT1MT2[14 - i] * p2x + EXP3[i] * p3x);
+ int newScrY = (int) (EXP3[14 - i] * p0y + DT1MT2[i] * p1y +
+ DT1MT2[14 - i] * p2y + EXP3[i] * p3y);
+
+ scrLineTo(newScrX, newScrY);
+ }
+ scrLineTo(p3x, p3y);
+ }
+
+ private final void arc(double x, double y, double radius,
+ double startAngle, double endAngle, boolean counterclockwise){
+
+ double pi2 = 2.0 * Math.PI;
+ double ang = startAngle % pi2;
+ double end = endAngle % pi2;
+ double delta = (int) (90 * (scaleX + scaleY) / radius);
+
+// System.out.println("arc start ang: "+ ang+ ";
end: "+end+ "delta: "+delta);
+
+ if (delta < Math.PI / 180.0) {
+ delta = Math.PI / 180.0;
+ }
+ if (delta > Math.PI / 10) {
+ delta = Math.PI / 10;
+ }
+
+ if (counterclockwise) {
+ delta = -delta;
+ if (end >= ang) {
+ end -= pi2;
+ }
+ } else {
+ if (end <= ang) {
+ end += pi2;
+ }
+ }
+
+// System.out.println("arc start ang: "+ ang+ "; end: "+end);
+
+ double cx;
+ double cy;
+
+ do {
+ cx = x + radius * Math.cos(ang);
+ cy = y + radius * Math.sin(ang);
+
+ scrLineTo(translateX(cx, cy), translateY(cx, cy));
+
+ ang += delta;
+ }
+ while(counterclockwise ? ang >= end : ang <= end);
+
+ cx = x + radius * Math.cos(end);
+ cy = y + radius * Math.sin(end);
+
+ scrLineTo(translateX(cx, cy), translateY(cx, cy));
+ }
+
+ private final void quadraticCurveTo(double cpx, double cpy, double x,
double y){
+
+ int p0x = currentScrX;
+ int p0y = currentScrY;
+
+ int p1x = translateX(cpx, cpy);
+ int p1y = translateY(cpx, cpy);
+
+ int p2x = translateX(x, y);
+ int p2y = translateY(x, y);
+
+ int d = Math.max(
+ (p0x - p1x) * (p0x - p1x) + (p0y - p1y) * (p0y - p1y),
+ (p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y));
+
+ int step;
+ if (d > 1000) {
+ step = d > 4000 ? 1 : 2;
+ } else {
+ step = d > 250 ? 4 : 8;
+ }
+
+ for (int i = step - 1; i < 15; i += step) {
+ int newScrX = (int) (EXP2[14 - i] * p0x + ZT1MT[i] * p1x + EXP2[i] *
p2x);
+ int newScrY = (int) (EXP2[14 - i] * p0y + ZT1MT[i] * p1y + EXP2[i] *
p2y);
+
+ scrLineTo(newScrX, newScrY);
+ }
+
+ scrLineTo(p2x, p2y);
+ }
+
+ private final void strokeRect(double x0, double y0, double x1, double
y1) {
+ beginPath();
+ moveTo(x0, y0);
+ lineTo(x1, y0);
+ lineTo(x1, y1);
+ lineTo(x0, y1);
+ lineTo(x0, y0);
+ stroke();
+ }
+
+ private final void fillRect(double x0, double y0, double x1, double y1) {
+ if (scew1 == 0 && scew2 == 0 && scaleX > 0 && scaleY > 0){
+ int sx0 = translateX(x0, y0);
+ int sy0 = translateY(x0, y0);
+ int sx1 = translateX(x1, y1);
+ int sy1 = translateY(x1, y1);
+ graphics.setColor(currentFillColor);
+ graphics.fillRect(sx0, sy0, sx1 - sx0 + 1, sy1 - sy0 + 1);
+ } else {
+ beginPath();
+ moveTo(x0, y0);
+ lineTo(x1, y0);
+ lineTo(x1, y1);
+ lineTo(x0, y1);
+ lineTo(x0, y0);
+ fill();
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/js/Html5JsFactory.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,47 @@
+package com.google.minijoe.html5.js;
+
+import com.google.minijoe.html.js.XMLHttpRequestObject;
+import com.google.minijoe.sys.JsObject;
+import com.google.minijoe.sys.JsObjectFactory;
+
+public class Html5JsFactory implements JsObjectFactory {
+
+ private static Html5JsFactory factory;
+
+ //Html5 Js Native Object types
+ public static final int HTML5_XHR_TYPE = 1;
+ public static final int HTML5_CANVAS_TYPE = 2;
+
+ private JsWindow environment;
+
+ /**
+ * Initialises the factory singleton, or nothing if it already exists.
+ *
+ * @param callbackContext JsObject with context to use for callbacks by
any
+ * objects created by the factory
+ * @param eventLock Object used as lock for callback to synchronise on
+ */
+ public static Html5JsFactory getFactory(JsWindow env) {
+ if (factory == null) {
+ factory = new Html5JsFactory();
+ factory.environment = env;;
+ }
+ return factory;
+ }
+
+ public JsObject newInstance(int type) {
+ switch(type){
+ case HTML5_XHR_TYPE:
+ return new XMLHttpRequestObject(environment,
+ environment.getEventLock());
+
+ case HTML5_CANVAS_TYPE:
+ return new CanvasObject(environment,
+ environment.getEventLock(),
+ environment.getRootDocument());
+
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/js/JsWindow.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,278 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.html5.js;
+
+import com.google.minijoe.html.Element;
+import com.google.minijoe.html.HtmlWidget;
+import com.google.minijoe.html.uibase.Widget;
+import com.google.minijoe.html5.Canvas2D;
+import com.google.minijoe.sys.JsArray;
+import com.google.minijoe.sys.JsFunction;
+import com.google.minijoe.sys.JsObject;
+import com.google.minijoe.sys.JsSystem;
+
+import java.util.Vector;
+
+import javax.microedition.lcdui.Alert;
+import javax.microedition.lcdui.AlertType;
+import javax.microedition.lcdui.Canvas;
+import javax.microedition.lcdui.Display;
+
+/**
+ * Provides a simple Browser object model "window" which acts
+ *
+ * @author Stefan Haustein
+ */
+public class JsWindow extends JsObject implements Runnable {
+
+ static final int ID_GET_ELEMENT_BY_ID = 1000;
+ static final int ID_WRITE = 1001;
+ static final int ID_SET_TIMEOUT = 1003;
+ static final int ID_SET_INTERVAL = 1004;
+ static final int ID_DEBUG = 1005; //console.debug()
+ static final int ID_WIDTH = 1006;
+ static final int ID_HEIGHT = 1007;
+
+
+ static int scheduleId;
+
+ static final JsObject PROTOTYPE = new JsObject(OBJECT_PROTOTYPE);
+ /**
+ * entries are Object[] containing execution time, method, interval
+ */
+ Vector schedule = new Vector();
+ static int timerId;
+ JsArray stack = new JsArray();
+ boolean stop = false;
+ Object eventLock = new Object();
+ HtmlWidget rootDocument;
+
+
+
+ public Object getEventLock() {
+ return eventLock;
+}
+
+/**
+ * Constructs a new environment.
+ *
+ */
+ public JsWindow(HtmlWidget rootDocument) {
+ super(PROTOTYPE);
+
+ this.rootDocument = rootDocument;
+ scopeChain = JsSystem.createGlobal();
+
+ addVar("getElementById", new JsFunction(ID_GET_ELEMENT_BY_ID, 1));
+ addVar("write", new JsFunction(ID_WRITE, 1));
+
+ addVar("setTimeout", new JsFunction(ID_SET_TIMEOUT, 2));
+ addVar("setInterval", new JsFunction(ID_SET_INTERVAL, 2));
+ addVar("document", this);
+ addVar("window", this);
+ addVar("console", this);
+
+ addVar("debug", new JsFunction(ID_DEBUG, 1)); //console.debug
+
+ if (rootDocument != null) {
+ System.out.println("doc h:"+rootDocument.getHeight());
+ addVar("width", new JsFunction(ID_WIDTH, -1));
+ addVar("height", new JsFunction(ID_HEIGHT, -1));
+ addVar("innerWidth", new JsFunction(ID_WIDTH, -1));
+ addVar("innerHeight", new JsFunction(ID_HEIGHT, -1));
+ } else {
+ System.out.println("null doc root passed to JsWindow"); //FIXME: need
proper err handling
+ }
+ stack.setObject(0, this);
+ }
+
+ /**
+ * Java implementation of the provided JS methods and fields.
+ */
+ public void evalNative(int id, JsArray stack, int sp, int parCount){
+ switch(id){
+ case ID_GET_ELEMENT_BY_ID:
+ String elemId = (String)(stack.getObject(sp + 2));
+ Element result =
rootDocument.getElement().getChildById((String)(stack.getObject(sp + 2)));
+ Widget w = this.rootDocument.getWidgetForLabel(elemId);
+
+ stack.setObject(sp, ((Canvas2D)w).getCanvasObject());
+ break;
+
+ case ID_WRITE:
+ write(stack.getString(sp + 2));
+ break;
+
+ case ID_SET_TIMEOUT:
+ stack.setNumber(sp,
+ schedule((JsFunction) stack.getObject(sp + 2), stack.getInt(sp
+ 3), false));
+ break;
+
+ case ID_SET_INTERVAL:
+ stack.setNumber(sp,
+ schedule((JsFunction) stack.getObject(sp + 2), stack.getInt(sp
+ 3), true));
+ break;
+
+ case ID_DEBUG:
+ debug(stack.getString(sp + 2));
+ break;
+
+ case ID_WIDTH:
+ stack.setNumber(sp, rootDocument.getWidth());
+ break;
+
+ case ID_HEIGHT:
+ stack.setNumber(sp, rootDocument.getHeight());
+ break;
+
+ default:
+ super.evalNative(id, stack, sp, parCount);
+ }
+ }
+
+ public HtmlWidget getRootDocument() {
+ return rootDocument;
+ }
+
+ private void write(String string) {
+ System.out.println("document.write: " + string);
+ }
+
+ private void debug(String string) {
+ System.out.println("console.debug: " + string);
+ }
+
+ /**
+ * Schedule a callback
+ *
+ * @param function function to be called
+ * @param dt time interval
+ * @param repeat if true, calls are rescheduled automatically
+ * @return a id that can be used to terminate an interval
+ */
+ private int schedule(JsFunction function, int dt, boolean repeat) {
+ // this should be binary search but if there should not be more than
1-3 entries anyway...
+
+ long t0 = System.currentTimeMillis() + dt;
+
+ synchronized (schedule) {
+ int i = schedule.size() - 1;
+ while (i >= 0 && ((Long) ((Object[])
schedule.elementAt(i))[1]).longValue() > t0) {
+ i--;
+ }
+
+ if (repeat) {
+ schedule.insertElementAt(new Object[]{
+ new Integer(++timerId),
+ new Long(t0),
+ function,
+ new Integer(dt)}, i + 1);
+ } else {
+ schedule.insertElementAt(new Object[]{
+ new Integer(++timerId),
+ new Long(t0),
+ function}, i + 1);
+ }
+ return timerId;
+ }
+ }
+
+ /**
+ * Runs the scheduler.
+ */
+ public void run() {
+ try {
+ while (!stop) {
+ if (schedule.size() == 0) {
+ synchronized (eventLock) {
+ eventLock.wait(20);
+ }
+ continue;
+ }
+
+ Object[] next;
+ synchronized (schedule) {
+ next = (Object[]) schedule.elementAt(0);
+ schedule.removeElementAt(0);
+ }
+ long time = ((Long) next[1]).longValue();
+ JsFunction call = (JsFunction) next[2];
+ synchronized (eventLock) {
+ eventLock.wait (Math.max(5, time - System.currentTimeMillis()));
+ // Note: stack[0] is filled with 'this' in the constructor
+ stack.setObject(1, this);
+ stack.setObject(2, call);
+ call.eval(stack, 1, 0);
+ }
+
+ this.rootDocument.invalidate(false);//repaint!
+ if (next.length == 4) {
+ schedule(call, ((Integer) next[3]).intValue(), true);
+ }
+ }
+ } catch (Exception e) {
+ //FIXME need to handle exception better then just swallowing with err
mesg
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * The scope to use when doing callbacks into JS functions
+ * @return
+ */
+ public JsObject getCallbackScope() {
+ return this;
+ }
+
+
+ /**
+ * Calls the given JavaScript function with the given key event.
+ * Google internal cursor key codes are converted to PC key codes.
+ *
+ * @param f the function to be called
+ * @param key the key event
+ */
+ public void keyEvent(JsFunction f, int code, int action) {
+ JsObject event = new JsObject(JsObject.OBJECT_PROTOTYPE);
+
+ switch(action){
+ case Canvas.LEFT:
+ code = 37;
+ break;
+ case Canvas.UP:
+ code = 38;
+ break;
+ case Canvas.RIGHT:
+ code = 39;
+ break;
+ case Canvas.DOWN:
+ code = 40;
+ break;
+ }
+
+ event.addVar("keyCode", new Double(code));
+ synchronized (eventLock) {
+ stack.setObject(1, this);
+ stack.setObject(2, f);
+ stack.setObject(3, event);
+ f.eval(stack, 1, 1);
+ }
+ }
+
+ public String toString() {
+ return "[object JsWindow5]";
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/html5/js/PolyFill.java Tue Jun 1
05:33:57 2010
@@ -0,0 +1,353 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.html5.js;
+
+import javax.microedition.lcdui.Graphics;
+
+/**
+ * Polygon filling implementation. Note: The methods in this class
+ * are not thread safe! Algorithm source: W.D.Fellner: Computergrafik
+ *
+ * @author Stefan Haustein
+ */
+public class PolyFill {
+
+ private PolyFill() {
+ }
+
+ public static boolean useAlpha = false;
+
+ private static boolean inFill = false;
+ private static int lastColor = 0;
+ private static boolean useFillArray;
+ private static final int INITIAL_POINTS = 32;
+ private static int[] fillArray = new int[256];
+
+ /** Last (unclosed?) segment start */
+ private static int segmentStart;
+
+ /** current position in points array */
+ private static int pos;
+
+ /** Current X position for lineTo */
+ private static int currentX;
+
+ /** Current Y position for lineTo */
+ private static int currentY;
+
+ /** x coordinates for line segments */
+ private static int[] pointsX = new int[INITIAL_POINTS];
+
+ /** y coordinates for line segments */
+ private static int[] pointsY = new int[INITIAL_POINTS];
+
+ /** next coordinate index for path */
+ private static int[] nextPos = new int[INITIAL_POINTS];
+
+ private static PolyFill reuseHead = new PolyFill();
+ private static PolyFill reusePtr;
+
+ private static PolyFill edges;
+
+ private static int bottomY;
+
+ private int yTop;
+ private int xInt;
+ private int deltaY;
+ private int deltaX;
+ private PolyFill next;
+ private PolyFill reuse;
+
+ private static PolyFill newEdge(){
+
+// return new PolyFill();
+
+ PolyFill result = reusePtr;
+ if (result.reuse == null) {
+ result.reuse = new PolyFill();
+ }
+
+ reusePtr = result.reuse;
+ return result;
+ }
+
+ /**
+ * Start a new path that will be filled. Note: There is only one global
data structure and
+ * no synchronization. Take care.
+ */
+ public static void beginPath(){
+
+ if (inFill) {
+ throw new RuntimeException("Fill Threading issue");
+ }
+
+ segmentStart = 0;
+ pos = 0;
+ }
+
+ /**
+ * Extend the current path with a line to the given coordinates.
+ */
+ public static void lineTo(int x, int y){
+
+ if (pos + 4 >= pointsX.length) {
+
+ int l = pointsX.length * 3 / 2;
+ int[] temp = new int[l];
+ System.arraycopy(pointsX, 0, temp, 0, pos);
+ pointsX = temp;
+
+ temp = new int[l];
+ System.arraycopy(pointsY, 0, temp, 0, pos);
+ pointsY = temp;
+
+ temp = new int[l];
+ System.arraycopy(nextPos, 0, temp, 0, pos);
+ nextPos = temp;
+ }
+
+ if (segmentStart == pos) {
+ pointsX[pos] = currentX;
+ pointsY[pos] = currentY;
+ nextPos[pos] = pos + 1;
+ pos++;
+ }
+
+ pointsX[pos] = currentX = x;
+ pointsY[pos] = currentY = y;
+ nextPos[pos] = pos + 1;
+
+ pos++;
+ }
+
+ /**
+ * Move to the given coordinates, possibly starting a new sub path.
+ */
+ public static void moveTo(int x, int y){
+
+ if (pos > segmentStart) {
+ nextPos[pos - 1] = segmentStart;
+ }
+
+ segmentStart = pos;
+ currentX = x;
+ currentY = y;
+ }
+
+ private static final int nextY(int k){
+
+ int compareY = pointsY[k];
+ int newY;
+ do {
+ k = nextPos[k];
+ newY = pointsY[k];
+ }
+ while(newY == compareY);
+
+ return newY;
+ }
+
+
+ private static final void insert(int p1x, int p1y, int p2x, int p2y, int
yNext){
+
+ int dx = ((p2x - p1x) << 16) / (p2y - p1y);
+ int x2 = p2x << 16;
+
+ if (p2y > p1y && p2y < yNext) {
+ p2y--;
+ x2 -= dx;
+ } else if (p2y < p1y && p2y > yNext) {
+ p2y++;
+ x2 += dx;
+ }
+
+ int dy = p2y - p1y;
+
+ int maxX;
+ int maxY;
+
+ if (dy > 0){
+ maxY = p2y;
+ maxX = x2;
+ dy++;
+ } else {
+ maxY = p1y;
+ maxX = p1x << 16;
+ dy = 1 - dy;
+ }
+
+ PolyFill newEdge = newEdge();
+ newEdge.yTop = maxY;
+ newEdge.deltaY = dy;
+ newEdge.xInt = maxX;
+ newEdge.deltaX = dx;
+
+ PolyFill edge1 = edges;
+ while (edge1.next.yTop >= maxY) {
+ edge1 = edge1.next;
+ }
+ newEdge.next = edge1.next;
+ edge1.next = newEdge;
+ }
+
+ /**
+ * Fill the path defined by beginPath(), moveTo() and lineTo().
+ */
+ public static void fill(Graphics g, int color){
+
+ int alpha = color >>> 24;
+
+ if (useAlpha ? alpha == 0 : alpha < 64) {
+ return;
+ }
+
+ inFill = true;
+
+ if (useAlpha ? alpha == 255 : alpha > 192) {
+ useFillArray = false;
+ g.setColor(color & 0x0ffffff);
+ } else {
+ useFillArray = true;
+ if (color != lastColor) {
+ lastColor = color;
+ if (useAlpha){
+ for (int i = 0; i < 255; i++) {
+ fillArray[i] = color;
+ }
+ } else {
+ for (int i = 0; i < 255; i += 2) {
+ fillArray[i] = color | 0xff000000;
+ fillArray[i + 1] = color & 0xff000000;
+ }
+ }
+ }
+ }
+
+ if (pos > segmentStart) {
+ nextPos[pos - 1] = segmentStart;
+ }
+ reusePtr = reuseHead;
+
+ // begin Edge_Sort
+ edges = newEdge();
+ PolyFill edge1 = newEdge();
+
+ edges.next = edge1;
+ edge1.next = null;
+
+ edges.yTop = Integer.MAX_VALUE;
+ edge1.yTop = Integer.MIN_VALUE;
+
+ bottomY = Integer.MAX_VALUE;
+
+ for (int k = 0; k < pos; k++) {
+ int p1x = pointsX[k];
+ int p1y = pointsY[k];
+ int next = nextPos[k];
+
+ if (pointsY[next] != p1y) {
+ insert(p1x, p1y, pointsX[next], pointsY[next], nextY(next));
+ } else {
+ drawHorizontalLine(g, p1x, p1y, pointsX[next]);
+ }
+ if (p1y < bottomY) {
+ bottomY = p1y;
+ }
+ }
+ // end Edge sort
+
+ PolyFill lActEdge = edges.next;
+
+ for (int scan = edges.next.yTop; scan >= bottomY; scan--) {
+ // addition: care about discontinuities
+ if (scan > edges.next.yTop) {
+ scan = edges.next.yTop;
+ }
+
+ // update List Ptr
+ while (lActEdge.next.yTop >= scan) {
+ lActEdge = lActEdge.next;
+ }
+
+ // sort intersections
+ PolyFill edge2 = edges.next;
+ do {
+ edge1 = edges;
+ while (edge1.next.xInt < edge2.next.xInt) {
+ edge1 = edge1.next;
+ }
+ if (edge1 != edge2){
+ PolyFill edge3 = edge2.next.next;
+ edge2.next.next = edge1.next;
+ edge1.next = edge2.next;
+ edge2.next = edge3;
+ if (edge1.next == lActEdge){
+ lActEdge = edge2;
+ }
+ } else {
+ edge2 = edge2.next;
+ }
+ }
+ while(edge2 != lActEdge);
+ // end sort intersections
+
+ // begin fill
+ edge1 = edges;
+ do {
+ edge1 = edge1.next;
+ int qx = (edge1.xInt + 32768) >> 16;
+ edge1 = edge1.next;
+ drawHorizontalLine(g, qx, scan, (edge1.xInt + 32768) >> 16);
+ } while(edge1 != lActEdge);
+ // end fill
+
+ // begin update edges
+ edge1 = edges;
+
+ PolyFill prevEdge = edge1;
+ do {
+ edge1 = prevEdge.next;
+ if (edge1.deltaY > 1) {
+ edge1.deltaY--;
+ edge1.xInt -= edge1.deltaX;
+ prevEdge = edge1;
+ } else {
+ prevEdge.next = edge1.next;
+ if (edge1 == lActEdge) {
+ lActEdge = prevEdge;
+ }
+ }
+ } while(prevEdge != lActEdge);
+ // end update edges
+ }
+
+ inFill = false;
+ }
+
+
+ private static final void drawHorizontalLine(Graphics g, int p1x, int
p1y, int p2x) {
+ if (useFillArray) {
+ if (p2x < p1x) {
+ int swap = p2x;
+ p2x = p1x;
+ p1x = swap;
+ }
+// System.out.println("x+y&1="+((p1x+p1y) & 1));
+ g.drawRGB(fillArray, (p1x + p1y) & 1, 256, p1x, p1y, p2x - p1x, 1,
true);
+ } else {
+ g.drawLine(p1x, p1y, p2x, p1y);
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/src/com/google/minijoe/samples/browser/Html5Browser.java Tue
Jun 1 05:33:57 2010
@@ -0,0 +1,141 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.minijoe.samples.browser;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import javax.microedition.lcdui.CommandListener;
+import javax.microedition.lcdui.Display;
+import javax.microedition.midlet.MIDletStateChangeException;
+
+import com.google.minijoe.html.SystemRequestHandler;
+import com.google.minijoe.html5.CanvasElementHandler;
+import com.google.minijoe.html5.js.JsWindow;
+
+/**
+ * Example application illustrating HTML5 support.
+ * Currently supported parts of
+ * HTML5: - canvas element
+ *
+ * coming soon: localStorage, video/audio element, geolocation
+ *
+ * @author Maksim Lin
+ */
+public class Html5Browser extends HtmlBrowser implements
SystemRequestHandler,
+ CommandListener, Runnable {
+
+ private Hashtable elementHandlers = new Hashtable();
+ private JsWindow jsEnv;
+
+ public Html5Browser() {
+
+ }
+
+ protected void startApp() throws MIDletStateChangeException {
+
+ display = Display.getDisplay(this);
+
+ userAgent = "MiniJoe/0.5 (like Opera Mini/2.0)" + " Platform/"
+ + getAppProperty("microedition.platform") + " Configuration/"
+ + getAppProperty("microedition.configuration") + " Profile/"
+ + getAppProperty("microEdition.profile");
+
+ StringBuffer menu = new StringBuffer(
+ "<html><head><title>Bookmarks</title></head><body><ul>");
+
+ addLink(menu,
+ "http://localhost/canvasoids.html");
+ addLink(menu,
+ "http://localhost/functionplot.html");
+ addLink(menu,
+ "http://manichord.com/test/canvasoids.html");
+
+ addLink(menu,
+ "http://minijoe.googlecode.com/svn/trunk/javascript/");
+ addLink(menu,
+ "http://minijoe.googlecode.com/svn/trunk/javascript/canvasoids.html");
+ addLink(menu,
+ "http://hsivonen.iki.fi/test/xhtml-suite/xhtml-basic.xhtml");
+ addLink(menu, "http://www.google.com/m");
+ addLink(menu, "http://mobile.google.com");
+ addLink(menu, "http://wwf.mobi/");
+ addLink(menu,
+ "http://www.bigbaer.com/css_tutorials/css.float.html.tutorial.htm");
+ addLink(menu, "http://en.m.wikipedia.org");
+ addLink(menu, "http://moblogga.mobi/");
+ addLink(menu, "http://qeep.mobi/xmps/homepage.do");
+ addLink(menu, "http://wap.cellufun.com/games.asp?g=mob&f=admobmw");
+ addLink(menu, "http://freesim.o2.co.uk/R4QAYdLT");
+
+ try {
+
+ HtmlScreen scr = showPage(null);
+
+ elementHandlers.put("canvas", new CanvasElementHandler());
+
+ scr.htmlWidget.load(new ByteArrayInputStream(menu.toString()
+ .getBytes("UTF-8")), "UTF-8");
+ scr.setStatus(null);
+ scr.repaint();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("" + e);
+ }
+ }
+
+ public void addLink(StringBuffer buf, String href) {
+ buf.append("<li><a href='").append(href).append("'>").append(href)
+ .append("</a></li>");
+ }
+
+ public void run() {
+ try {
+ while (!exit) {
+ if (requestQueue.size() == 0) {
+ synchronized (requestQueue) {
+ try {
+ requestQueue.wait(1000);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ } else {
+ ResourceRequester requester;
+ synchronized (requestQueue) {
+ requester = (ResourceRequester) requestQueue
+ .elementAt(0);
+ requestQueue.removeElementAt(0);
+ }
+ requester.run();
+ }
+ }
+ } catch (Exception exp) {
+ exp.printStackTrace();
+ }
+ finally {
+ threadCount--;
+ }
+ }
+
+ /**
+ * Overwrite this in a subclass to support additional elements.
+ */
+ public Hashtable getElementHandlers() {
+ return elementHandlers;
+ }
+}
=======================================
--- /trunk/.classpath Thu Feb 11 12:45:52 2010
+++ /trunk/.classpath Tue Jun 1 05:33:57 2010
@@ -4,5 +4,6 @@
<classpathentry kind="con"
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/me4se"/>
<classpathentry kind="lib" path="lib/kxml2-min-2.3.0.jar"/>
+ <classpathentry kind="con" path="org.elipse.mtj.JavaMEContainer/Sun
Java(TM) Wireless Toolkit 2.5.2 for CLDC/DefaultColorPhone"/>
<classpathentry kind="output" path="bin"/>
</classpath>
=======================================
--- /trunk/.project Thu Feb 11 12:45:52 2010
+++ /trunk/.project Tue Jun 1 05:33:57 2010
@@ -8,10 +8,16 @@
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.mtj.core.preverifier</name>
+ <arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.mtj.core.nature</nature>
</natures>
</projectDescription>
=======================================
--- /trunk/javascript/canvasoids.js Wed Dec 10 06:11:58 2008
+++ /trunk/javascript/canvasoids.js Tue Jun 1 05:33:57 2010
@@ -274,7 +274,7 @@
if (dx * dx + dy * dy < r.hitrange && blink == 0 && lives > 0) {
blink = 1;
- lifes -= 1;
+ lives -= 1;
ship.x = w / 2;
ship.y = h / 2;
ship.dx = 0;
@@ -301,9 +301,9 @@
ctx.fillStyle = "#00ff00";
drawString(0, 20, "" + score, "left");
drawString(w, 20, "HI " + highscore, "right");
- drawString(w, h - 5, "Ships: " + lifes, "right");
-
- if (lifes == 0) {
+ drawString(w, h - 5, "Ships: " + lives, "right");
+
+ if (lives == 0) {
drawString(w / 2, h / 2 - 10, "GAME OVER", "center");
if (blink == 0) {
drawString(w / 2, h / 2 + 10, "Press any key", "center");
@@ -319,9 +319,9 @@
document.onkeydown = function(e) {
var keycode = e.keyCode;
- if (lifes == 0) {
+ if (lives == 0) {
if (blink == 0) {
- lifes = 3;
+ lives = 3;
score = 0;
level = 0;
nextLevel();
=======================================
--- /trunk/src/com/google/minijoe/html/Element.java Fri Mar 19 10:32:21 2010
+++ /trunk/src/com/google/minijoe/html/Element.java Tue Jun 1 05:33:57 2010
@@ -14,17 +14,18 @@
package com.google.minijoe.html;
-import com.google.minijoe.html.css.Style;
-import com.google.minijoe.html.css.StyleSheet;
-
-import org.kxml2.io.KXmlParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import com.google.minijoe.html.css.Style;
+import com.google.minijoe.html.css.StyleSheet;
+
+
/**
* Stylable XML/HTML Element representation. Also used to represent an
anonymous
* parent element of the document root while parsing, in order to simplify
the
@@ -99,7 +100,7 @@
setFlags("p", FLAG_BLOCK | FLAG_PARAGRAPH);
setFlags("param", FLAG_EMPTY);
setFlags("pre", FLAG_BLOCK);
- setFlags("script", FLAG_IGNORE_CONTENT | TAG_SCRIPT);
+ setFlags("script", TAG_SCRIPT);
setFlags("style", FLAG_ADD_COMMENTS | TAG_STYLE);
setFlags("table", FLAG_BLOCK);
setFlags("title", TAG_TITLE);
@@ -233,6 +234,32 @@
public String getText(int index) {
return (String) content.elementAt(index);
}
+
+ /**
+ * Returns the first child which has a matching id attribute.
+ * Searches recursively through all children.
+ *
+ * @param id
+ * @return First child with matching id attribute
+ */
+ public Element getChildById(String id) {
+ int childCount = this.getChildCount();
+ Element currChild = null;
+ for (int i=0; i < childCount; i++) {
+ if (this.getChildType(i) == this.ELEMENT) {
+ currChild = this.getElement(i);
+ if (currChild != null &&
+ id.equals(currChild.getAttributeValue("id"))
+ ) {
+ break;
+ } else if (currChild.getChildCount() > 0) {
+ currChild.getChildById(id);
+ }
+ }
+ }
+ return currChild;
+ }
+
/**
* Returns the attribute value interpreted as a boolean.
@@ -503,6 +530,22 @@
if (href != null) {
htmlWidget.baseURL = href;
}
+ case TAG_SCRIPT:
+ String srcAttr = getAttributeValue("src");
+ if (srcAttr != null && !"".equals(srcAttr.trim())) {
+ String srcUrl = htmlWidget.getAbsoluteUrl(srcAttr);
+ if (srcAttr != null) {
+ this.htmlWidget.getResource(srcUrl,
+ SystemRequestHandler.TYPE_SCRIPT, this);
+ }
+
+ } else {
+ String jsText = getText();
+ if (jsText != null && jsText.trim() != "") {
+ this.htmlWidget.evalJS(jsText);
+ }
+ }
+ break;
}
}
@@ -777,4 +820,9 @@
public void setParent(Element parent) {
this.parent = parent;
}
-}
+
+ public HtmlWidget getHtmlWidget() {
+ return htmlWidget;
+ }
+
+}
=======================================
--- /trunk/src/com/google/minijoe/html/HtmlWidget.java Fri Mar 19 10:32:21
2010
+++ /trunk/src/com/google/minijoe/html/HtmlWidget.java Tue Jun 1 05:33:57
2010
@@ -14,12 +14,10 @@
package com.google.minijoe.html;
-import com.google.minijoe.common.Util;
-import com.google.minijoe.html.css.StyleSheet;
-import com.google.minijoe.html.uibase.Widget;
-
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+import java.util.Vector;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
@@ -28,6 +26,13 @@
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParserException;
+import com.google.minijoe.common.Util;
+import com.google.minijoe.compiler.Eval;
+import com.google.minijoe.html.css.StyleSheet;
+import com.google.minijoe.html.uibase.Widget;
+import com.google.minijoe.html5.js.JsWindow;
+import com.google.minijoe.sys.JsFunction;
+
/**
* Widget class representing a HTML document (or snippet). First builds up
a
* "logical" DOM consisting of Element objects for the (X)HTML code using
KXML,
@@ -115,12 +120,17 @@
/** Enable desktop rendering for CSS debugging purposes. */
private boolean desktopRendering;
+ public JsWindow globalScope;
+
/**
* Table mapping element names to ElementHandler instances for custom
element
* support.
*/
Hashtable elementHandlers;
+
+ private Thread jsTimerThread;
+
/**
* returns true if the media string is null or contains "all"
or "screen";
* "handheld" is accepted, too, if not in desktop rendering mode.
@@ -151,6 +161,9 @@
if (documentUrl != null) {
this.documentUrl = title = baseURL = documentUrl;
}
+ this.globalScope = new JsWindow(this);
+ this.jsTimerThread = new Thread(this.globalScope);
+ this.jsTimerThread.start();
}
public void setUrl(String url) {
@@ -254,7 +267,7 @@
* Processes links by calling the request handler.
*/
public Widget dispatchKeyEvent(int type, int keyCode, int action) {
-
+
Widget handled = super.dispatchKeyEvent(type, keyCode, action);
if (handled != null || type != KEY_PRESSED) {
return handled;
@@ -310,6 +323,10 @@
}
w.scrollIntoView();
}
+
+ public Widget getWidgetForLabel(String label) {
+ return (Widget) labels.get(label);
+ }
/**
* Converts a URL to an absolute URL, using the document base URL. If
the URL
@@ -335,9 +352,7 @@
}
synchronized (styleSheet) {
styleOutdated = false;
- Vector applyAnywhere = new Vector();
- applyAnywhere.addElement(styleSheet);
- element.apply(new Vector(), applyAnywhere);
+ element.apply(styleSheet, new Vector());
}
if (!OPTIMIZE || needsBuild) {
synchronized (this) {
@@ -355,15 +370,20 @@
*
* @param url URL of the received resource
* @param resource the received resource object
+ * @param resource type as defined in SystemRequestHandler.TYPE_XXX, use
-1 for unknown
*/
- public void addResource(String resUrl, Object resource) {
+ public void addResource(String resUrl, Object resource, int resType) {
if (resource instanceof String) {
- styleOutdated = OPTIMIZE;
- synchronized (styleSheet) {
- styleSheet.read(this, resUrl, (String) resource);
- }
- applyStyle();
+ if (resType == SystemRequestHandler.TYPE_SCRIPT) {
+ this.evalJS((String)resource);
+ } else {
+ styleOutdated = OPTIMIZE;
+ synchronized (styleSheet) {
+ styleSheet.read(this, resUrl, (String) resource);
+ }
+ applyStyle();
+ }
} else if (resource instanceof Image) {
Vector targets = (Vector) pendingResourceRequests.get(resUrl);
if (targets != null) {
@@ -445,4 +465,40 @@
public String getBaseURL() {
return baseURL;
}
-}
+
+ /**
+ * Evaluates the given js source
+ *
+ * @param jsSourceText
+ */
+ public void evalJS(String jsSourceText) {
+ try {
+ System.out.println("evaljs"+this.globalScope);
+ Eval.eval((String)jsSourceText, this.globalScope);
+ this.invalidate(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ *
+ * @param type
+ * @param evtCode either the keycode or mousebutton
+ * @return
+ */
+ public boolean callJsEventHandler(int type, int evtCode, int action) {
+ switch(type) {
+ case Widget.KEY_PRESSED:
+ Object keydown = this.globalScope.getObject("onkeydown");
+ if (keydown != null && keydown instanceof JsFunction) {
+ System.out.println("calling keydown evt func"+keydown);
+ this.globalScope.keyEvent((JsFunction)keydown, evtCode, action);
+ }
+ break;
+
+ }
+
+ return true;
+ }
+}
=======================================
--- /trunk/src/com/google/minijoe/html/SystemRequestHandler.java Thu Feb 11
12:45:52 2010
+++ /trunk/src/com/google/minijoe/html/SystemRequestHandler.java Tue Jun 1
05:33:57 2010
@@ -54,6 +54,11 @@
*/
static final int TYPE_STYLESHEET = 3;
+ /**
+ * Request for Javascript script
+ */
+ static final int TYPE_SCRIPT = 4;
+
/**
* Callback method called by the HTML document to request a resource.
*
=======================================
--- /trunk/src/com/google/minijoe/html/css/Style.java Tue Feb 16 18:23:23
2010
+++ /trunk/src/com/google/minijoe/html/css/Style.java Tue Jun 1 05:33:57
2010
@@ -984,7 +984,7 @@
int id;
if (idObj == null) {
tokenizer.debug("unrecognized property");
- id = UNRECOGNIZED_PROPERTY;
+ id = UNRECOGNIZED_PROPERTY_ID;
} else {
id = idObj.intValue();
}
@@ -1020,7 +1020,7 @@
if (v != null) {
set(id, (int) v.longValue(),
(byte) (v.longValue() >>> 32), pos);
- } else if (id != UNRECOGNIZED_PROPERTY && id !=
MULTIVALUE_FONT) {
+ } else if (id != UNRECOGNIZED_PROPERTY_ID && id !=
MULTIVALUE_FONT) {
tokenizer.debug("Unrecognized value '" + v + "' for
property " + name);
}
break;
=======================================
--- /trunk/src/com/google/minijoe/samples/browser/HtmlScreen.java Fri Feb
19 16:14:29 2010
+++ /trunk/src/com/google/minijoe/samples/browser/HtmlScreen.java Tue Jun
1 05:33:57 2010
@@ -139,11 +139,17 @@
} else if (keyCode == Widget.KEYCODE_LSK) {
browser.menu();
} else {
+ //Give any JS registered key evt handler first go at the evt
+ this.htmlWidget.callJsEventHandler(Widget.KEY_PRESSED, keyCode,
getGameAction(keyCode));
+
root.dispatchKeyEvent(Widget.KEY_PRESSED, keyCode,
getGameAction(keyCode));
}
}
protected void keyReleased(int keyCode) {
+ //Give any JS registered key evt handler first go at the evt
+ this.htmlWidget.callJsEventHandler(Widget.KEY_RELEASED, keyCode,
getGameAction(keyCode));
+
root.dispatchKeyEvent(Widget.KEY_RELEASED, keyCode,
getGameAction(keyCode));
}
=======================================
--- /trunk/src/com/google/minijoe/samples/browser/ResourceRequester.java
Mon Feb 22 16:37:29 2010
+++ /trunk/src/com/google/minijoe/samples/browser/ResourceRequester.java
Tue Jun 1 05:33:57 2010
@@ -250,7 +250,7 @@
case SystemRequestHandler.TYPE_IMAGE:
try {
Image image = Image.createImage(responseData, 0,
responseData.length);
- screen.htmlWidget.addResource(url, image);
+ screen.htmlWidget.addResource(url, image,
SystemRequestHandler.TYPE_IMAGE);
} catch (IllegalArgumentException e) {
System.err.println("Img fmt err: " + e);
}
@@ -258,11 +258,17 @@
case SystemRequestHandler.TYPE_STYLESHEET:
String styleSheet = new String(responseData, "UTF-8");
- screen.htmlWidget.addResource(url, styleSheet);
+ screen.htmlWidget.addResource(url, styleSheet,
SystemRequestHandler.TYPE_STYLESHEET);
break;
+
+ case SystemRequestHandler.TYPE_SCRIPT:
+ String javascript = new String(responseData, "UTF-8");
+ //System.out.println("adding script res..."+javascript);
+ screen.htmlWidget.addResource(url, javascript,
SystemRequestHandler.TYPE_SCRIPT);
+ break;
default:
- screen.htmlWidget.addResource(url, responseData);
+ screen.htmlWidget.addResource(url, responseData, -1);
break;
}
} catch (IOException e) {