Package com.google.minijoe.html

Source Code of com.google.minijoe.html.HtmlWidget

// 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.html;

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;
import javax.microedition.lcdui.Image;

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,
* then constructs a physical EWL representation, consisting of BlockWidget,
* InputWidget, TableWidget and TextFragmentWidget.
*
* Support for input elements and CSS can be switched off using ConfigSettings
* (HTML_DISABLE_INPUT and HTML_DISABLE_CSS) to reduce the memory footprint of
* this component.
*
* @author Stefan Haustein
*/
public class HtmlWidget extends BlockWidget  {

  /**
   * If set to false, the whole document is laid out again, even if only a
   * partial layout seems necessary, for instance after an image has been
   * loaded. Disable this flag for debugging only (too see if the optimization
   * is incorrect and causes an error).
   */
  public static final boolean OPTIMIZE = !DEBUG;


  /**
   * Viewport width typically assumed by pages designed for 800 pixel displays
   */
  public static final int MEDIUM_VIEWPORT_WIDTH = 760;

  /**
   * IPhone viewport width. Typically assumed by pages designed for 1024 pixel
   * displays.
   */
  public static final int HIGH_VIEWPORT_WIDTH = 960;

  /**
   * Interleaved map of character entity reference names to their resolved
   * string values.
   *
   * TODO(haustein) improve this in KXML instead.
   */
  private static final String[] HTML_ENTITY_TABLE = {
    "auml", "ä", "ouml", "ö", "uuml", "ü", "szlig", "ß",
    "Auml", "Ä", "Ouml", "Ö", "Uuml", "Ü"
  };

  /** CSS style sheet for this document. */
  StyleSheet styleSheet = new StyleSheet(true);

  /** Maps URLs to resources (images, styles) */
  private Hashtable resources = new Hashtable();

  /** Maps URLs to information to the sources of a request. */
  private Hashtable pendingResourceRequests = new Hashtable();

  /** Request handler used by this widget to request resources. */
  protected SystemRequestHandler requestHandler;

  /** URL of the page represented by this widget. */
  private String documentUrl;

  /** Base URL for this document */
  String baseURL;

  /** Maps labels to widgets */
  Hashtable labels = new Hashtable();

  /** Map for access keys */
  Hashtable accesskeys = new Hashtable();

  /** The title of the document. Initialized to "---" to avoid null pointers. */
  String title = "---";

  /**
   * We need to keep a reference to the focused element since we may need to rebuild
   * the widgets.
   */
  Element focusedElement;

  /** True if a new style sheet is arriving; stops style processing. */
  boolean styleOutdated = false;

  /** True if the widget tree needs to be rebuilt. */
  boolean needsBuild = true;

  /** 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.
   */
  public boolean checkMediaType(String media) {
    if (media == null || media.trim().length() == 0) {
      return true;
    }
    media = media.toLowerCase();
    return media.indexOf("all") != -1 || media.indexOf("screen") != -1 ||
    (media.indexOf("handheld") != -1 && !desktopRendering);
  }

  /**
   * Creates a HTML document widget.
   *
   * @param requestHandler the object used for requesting resources (embedded images, links)
   * @param documentUrl document URL, used as base URL for resolving relative links
   * @param destopRendering enables desktop rendering for debugging purposes
   */
  public HtmlWidget(SystemRequestHandler requestHandler, String documentUrl, Hashtable elementHandlers,
      boolean desktopRendering) {
    super(null, false);
    this.requestHandler = requestHandler;
    this.desktopRendering = desktopRendering;
    this.elementHandlers = elementHandlers;

    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) {
    documentUrl = baseURL = url;
  }
 
  public void doLayout(int viewportWidth) {
    if (element == null) {
      return;
    }

    if (!layoutValid || viewportWidth != getWidth()) {
      layoutValid = false;
     
      if (desktopRendering) {
        int minWidth = getMinimumWidth(HIGH_VIEWPORT_WIDTH);
        if (minWidth > viewportWidth) {
          viewportWidth = minWidth > MEDIUM_VIEWPORT_WIDTH ?
            HIGH_VIEWPORT_WIDTH : MEDIUM_VIEWPORT_WIDTH;
        }
        setWidth(viewportWidth);
        doLayout(viewportWidth, viewportWidth, null, false);
      } else {
        int minW = getMinimumWidth(viewportWidth);
        int w = Math.max(minW, viewportWidth);
        setWidth(w);
        // TODO(haustein) We may need to make the viewport width available to calculations...
        doLayout(w, viewportWidth, null, false);
      }
    }
  }

  /**
   * Loads an HTML document from the given stream.
   *
   * @param is The stream to read the document from
   * @throws IOException Thrown if an IO exception occurs while reading from the
   *             stream.
   */
  public void load(InputStream is, String encoding) throws IOException {

    // Obtain the data, build the DOM
    Element htmlElement;

    try {
      Element dummy = new Element(this, "");
      KXmlParser parser = new KXmlParser();
      if (encoding != null && encoding.trim().length() == 0) {
        encoding = "UTF-8";
      }
      parser.setInput(is, encoding);
      parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);

      for (int i = 0; i < HTML_ENTITY_TABLE.length; i += 2) {
        parser.defineEntityReplacementText(HTML_ENTITY_TABLE[i], HTML_ENTITY_TABLE[i + 1]);
      }

      dummy.parseContent(parser);

      htmlElement = dummy.getElement("html");

      if (htmlElement == null) {
        htmlElement = new Element(this, "html");
      }
     
      element = htmlElement.getElement("body");
      if (element == null) {
        element = new Element(this, "body");
        htmlElement.addElement(element);

        for (int i = 0; i < dummy.getChildCount(); i++) {
          switch (dummy.getChildType(i)) {
            case Element.TEXT:
              element.addText(dummy.getText(i));
              break;
            case Element.ELEMENT:
              if (!dummy.getElement(i).getName().equals("html")) {
                element.addElement(dummy.getElement(i));
              }
              break;
          }
        }
      }
    } catch (XmlPullParserException e) {
      // this cannot happen since the pull parser must not throw exceptions in
      // relaxed mode
      e.printStackTrace();
      throw new IOException(e.toString());
    }

    // Remove reference to dummy element that was created to simplify parsing
    htmlElement.setParent(null);

    // Apply the default style sheet and style info collected while building
    // the element three.
    applyStyle();
    invalidate(true);
  }

  /**
   * 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;
    }

    Element element = null;

    if (action == Canvas.FIRE) {
      Widget w = getFocusedWidget();
      if (w instanceof TextFragmentWidget) {
        element = ((TextFragmentWidget) w).getElement();
      } else if (w instanceof BlockWidget) {
        element = ((BlockWidget) w).getElement();
      }
    } else {
      element = (Element) accesskeys.get(new Character((char) keyCode));
    }

    if (element == null) {
      return null;
    }

    String href = element.getAttributeValue("href");
    while (href == null && element.getParent() != null) {
      element = element.getParent();
      href = element.getAttributeValue("href");
    }

    if (href == null) {
      return null;
    }

    if (href.startsWith("#")) {
      gotoLabel(href.substring(1));
    } else {
      requestHandler.requestResource(this, SystemRequestHandler.METHOD_GET,
          getAbsoluteUrl(href.trim()), SystemRequestHandler.TYPE_DOCUMENT, null);
    }
    return this;
  }

  /**
   * Scrolls to the element with the given label (&lt;a name...) and focuses it
   * (if focusable).
   */
  public void gotoLabel(String label) {
    Widget w = (Widget) labels.get(label);
    if (w == null) {
      return;
    }
    if (w.isFocusable()) {
      w.requestFocus();
    }
    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
   * is already absolute, it is returned unchanged.
   */
  public String getAbsoluteUrl(String relativeUrl) {
    return Util.getAbsoluteUrl(baseURL, relativeUrl);
  }

  /**
   * Returns the URL of this document, as set in the constructor.
   */
  public String getUrl() {
    return documentUrl;
  }

  /**
   * Applies the style sheet to the document.
   */
  void applyStyle() {
    if (element == null) {
      return;
    }
    synchronized (styleSheet) {
      styleOutdated = false;
      Vector applyAnywhere = new Vector();
      applyAnywhere.addElement(styleSheet);
      element.apply(new Vector(), applyAnywhere);
    }
    if (!OPTIMIZE || needsBuild) {
      synchronized (this) {
        needsBuild = false;
        children = new Vector();
        addChildren(element, new boolean[]{false, true});
        invalidate(true);
      }
    }
  }

  /**
   * Implementations of RequestHandler add resources via this method. Updates
   * requesters with the arrived resource.
   *
   * @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, int resType) {

    if (resource instanceof String) {
      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) {
        Image image = (Image) resource;
        pendingResourceRequests.remove(resUrl);

        for (int i = 0; i < targets.size(); i++) {
          Object t = targets.elementAt(i);
          if (t instanceof BlockWidget) {
            BlockWidget block = (BlockWidget) t;
            block.image = image;
            block.invalidate(true);
          } else if (t instanceof Image[]) {
            ((Image[]) t)[0] = image;
            invalidate(true);
          }
        }
      }
    }
    resources.put(resUrl, resource);
  }

  /**
   * Returns the loaded resource for the given URL, if available. If not
   * available, and the notify object is not null, a request for the resource
   * will be sent to the server. If the notify object is recognized by the
   * HtmlWidget (e.g. a BlockWidget), it is updated when the resource arrives.
   * Otherwise, the notify object is just treated as a flag to trigger the
   * request.
   *
   * @param url the URL of the resource; if relative, the document base URL
   *            will be used to determine the absolute URL
   * @param type the type of expected resource (
   * @param notify the object to be notified when the resource arrives.
   */
  public Object getResource(String url, int type, Object notify) {
    url = Util.getAbsoluteUrl(baseURL, url);

    Object res = resources.get(url);
    if (res == null && notify != null) {
      Vector dependencies = (Vector) pendingResourceRequests.get(url);
      if (dependencies == null) {
        dependencies = new Vector();
        pendingResourceRequests.put(url, dependencies);
      }
      dependencies.addElement(notify);
      if (dependencies.size() == 1) {
        requestHandler.requestResource(this, SystemRequestHandler.METHOD_GET, url,
            type, null);
      }
    }
    return res;
  }

  /**
   * In addition to drawing the contents by calling super, this method
   * makes sure the layout and viewport width are updated if not valid.
   */
  public void drawTree(Graphics g, int dx, int dy, int cx, int cy, int cw, int ch) {
  // Make sure the backgound covers the whole screen.
    if (boxY + boxHeight < getHeight()) {
      boxHeight = getHeight() - boxY;
    }
    if (element != null && element.getComputedStyle() != null) {
      super.drawTree(g, dx, dy, cx, cy, cw, ch);
    }
  } 

  /**
   * Returns the title of this document.
   */
  public String getTitle() {
    return title;
  }

  /**
   * Returns the base URL of this document (used to resolve relative links).
   */
  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;
  }
}
TOP

Related Classes of com.google.minijoe.html.HtmlWidget

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.