com.smartgwt.mobile.client.SmartGwtMobileEntryPoint.java Source code

Java tutorial

Introduction

Here is the source code for com.smartgwt.mobile.client.SmartGwtMobileEntryPoint.java

Source

/*
 * SmartGWT Mobile
 * Copyright 2008 and beyond, Isomorphic Software, Inc.
 *
 * SmartGWT Mobile is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.  SmartGWT Mobile is also
 * available under typical commercial license terms - see
 * http://smartclient.com/license
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */

package com.smartgwt.mobile.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.MetaElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.EventListener;
import com.smartgwt.mobile.SGWTInternal;
import com.smartgwt.mobile.client.internal.theme.ios.ActivityIndicatorCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ios.AlertViewCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ios.NavigationBarCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ios.PickerCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ios.TabSetCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ios.WindowCssResourceIOS;
import com.smartgwt.mobile.client.internal.theme.ipad.WindowCssResourceIPad;
import com.smartgwt.mobile.client.internal.theme.iphone.MenuCssResourceIPhone;
import com.smartgwt.mobile.client.internal.theme.iphone.PopoverCssResourceIPhone;
import com.smartgwt.mobile.client.internal.theme.iphone.WindowCssResourceIPhone;
import com.smartgwt.mobile.client.internal.util.CSSUtil;
import com.smartgwt.mobile.client.internal.widgets.AlertView;
import com.smartgwt.mobile.client.internal.widgets.CanvasStaticImpl;
import com.smartgwt.mobile.client.internal.widgets.Picker2;
import com.smartgwt.mobile.client.internal.widgets.Popup;
import com.smartgwt.mobile.client.theme.ThemeResources;
import com.smartgwt.mobile.client.types.PageOrientation;
import com.smartgwt.mobile.client.util.Page;
import com.smartgwt.mobile.client.util.SC;
import com.smartgwt.mobile.client.util.events.OrientationChangeEvent;
import com.smartgwt.mobile.client.util.events.OrientationChangeHandler;
import com.smartgwt.mobile.client.widgets.ActivityIndicator;
import com.smartgwt.mobile.client.widgets.BaseButton;
import com.smartgwt.mobile.client.widgets.Canvas;
import com.smartgwt.mobile.client.widgets.Dialog;
import com.smartgwt.mobile.client.widgets.Header1;
import com.smartgwt.mobile.client.widgets.Popover;
import com.smartgwt.mobile.client.widgets.Progressbar;
import com.smartgwt.mobile.client.widgets.ScrollablePanel;
import com.smartgwt.mobile.client.widgets.Window;
import com.smartgwt.mobile.client.widgets.form.DynamicForm;
import com.smartgwt.mobile.client.widgets.form.fields.SliderItem;
import com.smartgwt.mobile.client.widgets.form.fields.SwitchItem;
import com.smartgwt.mobile.client.widgets.layout.NavigationBar;
import com.smartgwt.mobile.client.widgets.menu.Menu;
import com.smartgwt.mobile.client.widgets.tab.TabSet;
import com.smartgwt.mobile.client.widgets.tableview.TableView;
import com.smartgwt.mobile.client.widgets.toolbar.ToolStrip;
import com.smartgwt.mobile.internal.gwt.dom.client.DOMConstants;

/**
 * Internal SmartGWT Mobile entry point class where framework level initialization code executes
 * before the user's EntryPoint is run.
 */
public class SmartGwtMobileEntryPoint implements EntryPoint {

    private static final CanvasStaticImpl CANVAS_STATIC_IMPL = GWT.create(CanvasStaticImpl.class);
    private static boolean initialized = false;

    private static EventListener getEventListener(com.google.gwt.user.client.Element elem) {
        return DOM.getEventListener(elem);
    }

    @SGWTInternal
    public static native void _init() /*-{
                                          
                                      // In JSNI, we may be passed GWT Java Object references.
                                      // These typically can not be directly manipulated -- this check will test for such objects.
                                      $wnd.SmartGWTMobile = {
                                      isNativeJavaObject : function (object) {
                                      // From observation "typeof" reports "function" for native Java objects in Firefox in development mode
                                      // and in OmniWeb, in development mode
                                      // In all other browsers, typeof reports as "object"
                                      var type = typeof object;
                                      if (type != "function" && type != "object") return false;
                                      if (@com.smartgwt.mobile.client.util.JSOHelper::isJSO(Ljava/lang/Object;)(object)) return false;
                                      return true;
                                      }
                                      };
                                          
                                      // Define isc.DataSource.create to run the static DataSource.fromConfig() method.
                                      // This allows eval() to parse JavaScript sent from the server's data source loader.
                                      // E.g.:
                                      //     isc.DataSource.create({
                                      //         ID: "someName",
                                      //         inheritsFrom: isc.DataSource.create({
                                      //             ID: "someName_inheritsFrom",
                                      //             fields:[
                                      //                 {name:"whatever", type:"text"}
                                      //             ]
                                      //         })
                                      //     });
                                      //
                                      // In compiled mode, we can't eval() in the code frame because we would have to overwrite
                                      // `window.isc', which could be an obfuscated symbol. However, the strange syntax for
                                      // eval()ing in the host frame does not work in hosted mode, which is used when running
                                      // the GWT JUnit tests. So, in hosted mode, we eval() in the code frame, but in compiled
                                      // mode, we eval() in the host frame. We need to install an `isc' global in whichever
                                      // frame is used to eval() JavaScript source.
                                      var isHosted = !@com.google.gwt.core.client.GWT::isScript()();
                                      var evalWnd = (isHosted ? window : $wnd);
                                      evalWnd.isc = {
                                      DataSource: {
                                      create : $entry(@com.smartgwt.mobile.client.data.DataSource::fromConfig(Lcom/google/gwt/core/client/JavaScriptObject;))
                                      },
                                      Date: evalWnd.Date
                                      };
                                          
                                      $wnd.Date.parseServerDate = Date.parseServerDate = $entry(function (year, month, day) {
                                      var logicalDate = @com.smartgwt.mobile.client.util.LogicalDate::new(III)(year, month, day);
                                      return logicalDate.@com.smartgwt.mobile.client.util.LogicalDate::toString()();
                                      });
                                      $wnd.Date.parseServerTime = Date.parseServerTime = $entry(function (hour, minute, second) {
                                      var logicalTime = @com.smartgwt.mobile.client.util.LogicalTime::new(III)(hour, minute, second);
                                      return logicalTime.@com.smartgwt.mobile.client.util.LogicalTime::toString()();
                                      });
                                          
                                      // Install a hook for DOM.getEventListener() to allow the GWT widget hierarchy to be
                                      // inspected.
                                      $wnd.GWT = {
                                      getEventListener : $entry(@com.smartgwt.mobile.client.SmartGwtMobileEntryPoint::getEventListener(Lcom/google/gwt/user/client/Element;))
                                      };
                                      }-*/;

    private boolean assertionsEnabled = false;

    public void onModuleLoad() {
        // Added boolean init check flag because GWT for some reason invokes this entry point class twice in hosted mode
        // even though it appears only once in the load hierarchy. Check with GWT team.
        if (initialized)
            return;
        initialized = true;

        assert (assertionsEnabled = true) == true; // Intentional side effect.
        if (!assertionsEnabled) {
            SC.logWarn("WARNING: Assertions are not enabled. It is recommended to develop with assertions "
                    + "enabled because both GWT and SmartGWT.mobile use them to help find bugs in application code. "
                    + "To enable assertions, if using GWT 2.6.0 or later, recompile the application with the "
                    + "-checkAssertions option passed to the GWT compiler. If using GWT 2.5.1 or earlier, recompile with the "
                    + "-ea option passed to the GWT compiler.");
        }

        _init();

        // Delete all current viewport <meta> tags. We will create a new one to work around two
        // issues:
        // - In an iOS 7 UIWebView, we need to set the viewport height to fix the issue that the
        //   TabSet tabBar stays above the virtual keyboard when a text <input> has keyboard focus
        //   (http://forums.smartclient.com/showthread.php?t=29005).
        //   - On iPhone and iPad (iOS v7.0.4), we need to set the height to device-width to work
        //     around a bug. If height is set to device-height, then in landscape orientation,
        //     the <body>'s dimensions are 1024x1024, causing pickers to fail to appear.
        //     - iOS 6.1 also has this problem, but if we use height = device-width, then there
        //       are severe display issues when the orientation is changed, so we don't specify the
        //       height.
        //   - Don't want to do this unless in a UIWebView, however, because if we set the
        //     viewport height then pickers get cut off when Showcase is viewed in Mobile Safari.
        // - In an iOS 7 UIWebView, we also need a viewport height to fix a different issue, namely
        //   that if an input is tapped, but the virtual keyboard appears on top of the input, then
        //   that input element is not focused, and the ScrollablePanel fails to scroll the tapped
        //   input into view:
        //   http://stackoverflow.com/questions/19110144/ios7-issues-with-webview-focus-when-using-keyboard-html
        //   - This is not an issue on iOS 6.1.
        // - Firefox for Android does not support updating the content of an existing viewport
        //   <meta> tag. However, if a new viewport <meta> tag is added, then the viewport settings
        //   are updated: https://bugzilla.mozilla.org/show_bug.cgi?id=714737
        final NodeList<Element> metaElems = Document.get().getElementsByTagName("meta");
        for (int ri = metaElems.getLength(); ri > 0; --ri) {
            final MetaElement metaElem = metaElems.getItem(ri - 1).cast();
            if ("viewport".equals(metaElem.getName())) {
                metaElem.removeFromParent();
            }
        }
        String width = "device-width";
        String height = null;
        if (CANVAS_STATIC_IMPL.isIOSMin7_0() && CANVAS_STATIC_IMPL.isUIWebView()) {
            height = Page.getOrientation() == PageOrientation.LANDSCAPE ? "device-width" : "device-height";
            Page.addOrientationChangeHandler(new OrientationChangeHandler() {
                @Override
                public void onOrientationChange(OrientationChangeEvent event) {
                    final String newHeight = Page.getOrientation() == PageOrientation.LANDSCAPE ? "device-width"
                            : "device-height";
                    Page._updateViewport(Float.valueOf(1.0f), "device-width", newHeight, Boolean.FALSE,
                            "minimal-ui");
                }
            });
        }
        Page._updateViewport(Float.valueOf(1.0f), width, height, Boolean.FALSE, "minimal-ui");

        // Inject default styles.
        //
        // Because `CssResource' does not have support for at rules, CSS3 animations @keyframes
        // and @media rules must be added separately.
        // http://code.google.com/p/google-web-toolkit/issues/detail?id=4911
        final StringBuilder cssText = new StringBuilder();
        final String translateXStart = Canvas.isAndroid() ? "translateX(" : "translate3d(",
                translateXEnd = Canvas.isAndroid() ? ")" : ",0,0)",
                translateYStart = Canvas.isAndroid() ? "translateY(" : "translate3d(0,",
                translateYEnd = Canvas.isAndroid() ? ")" : ",0)";

        // base
        cssText.append(Canvas._CSS.getText());
        cssText.append(ThemeResources.INSTANCE.otherBaseCSS().getText());
        cssText.append("@media all and (orientation:landscape){"
                + ThemeResources.INSTANCE.baseCSSLandscape().getText() + "}");

        // activityindicator
        cssText.append(ActivityIndicator._CSS.getText());
        if (ActivityIndicator._CSS instanceof ActivityIndicatorCssResourceIOS) {
            final ActivityIndicatorCssResourceIOS CSS = (ActivityIndicatorCssResourceIOS) ActivityIndicator._CSS;
            // Android 2.3.3 Browser has a bug where attempting to animate from transform:rotate(0deg)
            // to transform:rotate(360deg) results in no animation because its angle calculations
            // are mod 360, so 0 = 360 mod 360 and it thinks that there is no change in the transform
            // from start to end. A work-around is to add a 50% keyframe of transform:rotate(180deg).
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.spinAnimationName() + "{" + "0%{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":rotate(0deg)}" + "50%{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":rotate(180deg)}" + "to{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":rotate(360deg)}" + "}"
                    + "@keyframes " + CSS.spinAnimationName() + "{" + "0%{transform:rotate(0deg)}"
                    + "50%{transform:rotate(180deg)}" + "to{transform:rotate(360deg)}" + "}");
        }

        // layout
        cssText.append(ThemeResources.INSTANCE.layoutCSS().getText());

        // panel
        cssText.append(ThemeResources.INSTANCE.panelCSS().getText());

        // headings
        cssText.append(Header1._CSS.getText());

        // buttons
        cssText.append(BaseButton._CSS.getText());

        // form
        cssText.append(DynamicForm._CSS.getText());
        // Note: GWT's CssResource does not currently support CSS3 @media queries.
        // https://code.google.com/p/google-web-toolkit/issues/detail?id=8162
        // One work-around is to add the CSS text within the media query:
        // https://code.google.com/p/google-web-toolkit/issues/detail?id=4911#c6
        cssText.append("@media all and (orientation:landscape){" + // The space after the 'and' keyword is important. Without this space, the styles are not applied on iPad in landscape orientation.
                ThemeResources.INSTANCE.formCSSLandscape().getText() + "}");

        // menu
        cssText.append(Menu._CSS.getText());
        if (Menu._CSS instanceof MenuCssResourceIPhone) {
            final MenuCssResourceIPhone CSS = (MenuCssResourceIPhone) Menu._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.menuFadeInAnimationName() + "{"
                    + "0%{opacity:0}" + "to{opacity:1}" + "}" + "@keyframes " + CSS.menuFadeInAnimationName() + "{"
                    + "0%{opacity:0}" + "to{opacity:1}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.menuFadeOutAnimationName() + "{"
                    + "0%{opacity:1}" + "50%{opacity:1}" + "to{opacity:0}" + "}" + "@keyframes "
                    + CSS.menuFadeOutAnimationName() + "{" + "0%{opacity:1}" + "50%{opacity:1}" + "to{opacity:0}"
                    + "}");
        }

        // navigationbar
        cssText.append(NavigationBar._CSS.getText());
        if (NavigationBar._CSS instanceof NavigationBarCssResourceIOS) {
            final NavigationBarCssResourceIOS CSS = (NavigationBarCssResourceIOS) NavigationBar._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarFadeInAnimationName() + "{"
                    + "0%{opacity:0}" + "to{opacity:1}" + "}" + "@keyframes " + CSS.navbarFadeInAnimationName()
                    + "{" + "0%{opacity:0}" + "to{opacity:1}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarFadeOutAnimationName() + "{"
                    + "0%{opacity:1}" + "to{opacity:0}" + "}" + "@keyframes " + CSS.navbarFadeOutAnimationName()
                    + "{" + "0%{opacity:1}" + "to{opacity:0}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarSlideInFromRightAnimationName()
                    + "{" + "0%{opacity:0;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "90px" + translateXEnd + "}" + "to{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "0"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarSlideInFromRightAnimationName() + "{"
                    + "0%{opacity:0;transform:translateX(90px)}" + "to{opacity:1;transform:translateX(0)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.navbarHeadingSlideInFromRightAnimationName() + "{" + "0%{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "60%"
                    + translateXEnd + "}" + "to{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "0"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarHeadingSlideInFromRightAnimationName()
                    + "{" + "0%{opacity:0;transform:translateX(60%)}" + "to{opacity:1;transform:translateX(0)}"
                    + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarSlideInFromLeftAnimationName()
                    + "{" + "0%{opacity:0;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "-90px" + translateXEnd + "}" + "to{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "0"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarSlideInFromLeftAnimationName() + "{"
                    + "0%{opacity:0;transform:translateX(-90px)}" + "to{opacity:1;transform:translateX(0)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.navbarHeadingSlideInFromLeftAnimationName() + "{" + "0%{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "-60%"
                    + translateXEnd + "}" + "to{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "0"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarHeadingSlideInFromLeftAnimationName()
                    + "{" + "0%{opacity:0;transform:translateX(-60%)}" + "to{opacity:1;transform:translateX(0)}"
                    + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarSlideOutRightAnimationName() + "{"
                    + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "0" + translateXEnd + "}" + "to{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "90px"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarSlideOutRightAnimationName() + "{"
                    + "0%{opacity:1;transform:translateX(0)}" + "to{opacity:0;transform:translateX(90px)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarHeadingSlideOutRightAnimationName()
                    + "{" + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "0" + translateXEnd + "}" + "to{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "60%"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarHeadingSlideOutRightAnimationName()
                    + "{" + "0%{opacity:1;transform:translateX(0)}" + "to{opacity:0;transform:translateX(60%)}"
                    + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarSlideOutLeftAnimationName() + "{"
                    + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "0" + translateXEnd + "}" + "to{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "-90px"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarSlideOutLeftAnimationName() + "{"
                    + "0%{opacity:1;transform:translateX(0)}" + "to{opacity:0;transform:translateX(-90px)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.navbarHeadingSlideOutLeftAnimationName()
                    + "{" + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateXStart + "0" + translateXEnd + "}" + "to{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateXStart + "-60%"
                    + translateXEnd + "}" + "}" + "@keyframes " + CSS.navbarHeadingSlideOutLeftAnimationName() + "{"
                    + "0%{opacity:1;transform:translateX(0)}" + "to{opacity:0;transform:translateX(-60%)}" + "}");
        }

        // popup
        cssText.append(Popup._CSS.getText());

        // picker2
        cssText.append(Picker2.CSS.getText());
        if (Picker2.CSS instanceof PickerCssResourceIOS) {
            final PickerCssResourceIOS CSS = (PickerCssResourceIOS) Picker2.CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.pickerFadeInAnimationName() + "{"
                    + "0%{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart
                    + "100%" + translateYEnd + "}" + "to{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart + "0"
                    + translateYEnd + "}" + "}" + "@keyframes " + CSS.pickerFadeInAnimationName() + "{"
                    + "0%{transform:translateY(100%)}" + "to{transform:translateY(0)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.pickerFadeOutAnimationName() + "{"
                    + "0%{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart
                    + "0" + translateYEnd + "}" + "to{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText()
                    + ":" + translateYStart + "100%" + translateYEnd + "}" + "}" + "@keyframes "
                    + CSS.pickerFadeOutAnimationName() + "{" + "0%{transform:translateY(0)}"
                    + "to{transform:translateY(100%)}" + "}");
        }

        // progressbar
        cssText.append(Progressbar._CSS.getText());

        // scrollable
        cssText.append(ScrollablePanel._CSS.getText());

        // slider
        cssText.append(SliderItem._CSS.getText());

        // switchitem
        cssText.append(SwitchItem._CSS.getText());

        // tableview
        // Don't use `TableView._CSS.ensureInjected()' because the way that CssResource.ensureInjected()
        // is implemented, the CSS rules are scheduled to be added in a timeout, which may mean
        // that the DOM elements for the first `TableView' are added to the document before
        // the `TableView' CSS styles are added. If this first `TableView' has a parent `NavStack',
        // then the browser considers the background color of the LIs to have changed when
        // the `TableView' CSS is added, meaning that the background-color transition effect
        // kicks in.
        // http://jsfiddle.net/A4QDd/
        //
        // GWT 2.5 introduces the StyleInjector.flush() function which would solve this problem,
        // but for GWT 2.4, we can simulate the same effect by always using StyleInjector.injectAtEnd()
        // (immediate = true) at the small price of more <style> elements being added to the document.
        cssText.append(TableView._CSS.getText());
        cssText.append("@media all and (orientation:landscape){"
                + ThemeResources.INSTANCE.tableViewCSSLandscape().getText() + "}");

        // tabs
        cssText.append(TabSet._CSS.getText());
        if (TabSet._CSS instanceof TabSetCssResourceIOS) {
            final TabSetCssResourceIOS CSS = (TabSetCssResourceIOS) TabSet._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.tabBarSlideOutAnimationName()
                    + "{" + "0%{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateYStart + "0" + translateYEnd + "}" + "to{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart + "100%"
                    + translateYEnd + "}" + "}" + "@keyframes " + CSS.tabBarSlideOutAnimationName() + "{"
                    + "0%{transform:translateY(0)}" + "to{transform:translateY(100%)}" + "}");
        }
        cssText.append("@media all and (orientation:landscape){"
                + ThemeResources.INSTANCE.tabsCSSLandscape().getText() + "}");

        // toolbar
        cssText.append(ToolStrip._CSS.getText());

        // window
        if (Window._CSS instanceof WindowCssResourceIOS) {
            final WindowCssResourceIOS CSS = (WindowCssResourceIOS) Window._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.modalMaskFadeInAnimationName()
                    + "{" + "0%{opacity:0}" + "to{opacity:1}" + "}" + "@keyframes "
                    + CSS.modalMaskFadeInAnimationName() + "{" + "0%{opacity:0}" + "to{opacity:1}" + "}"
                    + DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.modalMaskFadeOutAnimationName() + "{"
                    + "0%{opacity:1}" + "to{opacity:0}" + "}" + "@keyframes " + CSS.modalMaskFadeOutAnimationName()
                    + "{" + "0%{opacity:1}" + "to{opacity:0}" + "}");
        }
        // On iPad, windows look like alertviews.
        if (Window._CSS instanceof WindowCssResourceIPad) {
            final WindowCssResourceIPad CSS = (WindowCssResourceIPad) Window._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.windowBackgroundFadeInAnimationName() + "{" + "0%{opacity:0;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(1)}" + "50%{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(.95)}" + "to{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(1)}" + "}"
                    + "@keyframes " + CSS.windowBackgroundFadeInAnimationName() + "{"
                    + "0%{opacity:0;transform:scale(1)}" + "50%{opacity:1;transform:scale(.95)}"
                    + "to{transform:scale(1)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.windowBackgroundFadeOutAnimationName()
                    + "{" + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText()
                    + ":scale(1)}" + "to{opacity:0;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText()
                    + ":scale(.95)}" + "}" + "@keyframes " + CSS.windowBackgroundFadeOutAnimationName() + "{"
                    + "0%{opacity:1;transform:scale(1)}" + "to{opacity:0;transform:scale(.95)}" + "}");

            // Otherwise, windows look like action sheets.
        } else {
            assert Window._CSS instanceof WindowCssResourceIPhone;
            final WindowCssResourceIPhone CSS = (WindowCssResourceIPhone) Window._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.windowBackgroundSlideInAnimationName() + "{" + "0%{"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart + "100%"
                    + translateYEnd + "}" + "to{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateYStart + "0" + translateYEnd + "}" + "}" + "@keyframes "
                    + CSS.windowBackgroundSlideInAnimationName() + "{" + "0%{transform:translateY(100%)}"
                    + "to{transform:translateY(0)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.windowBackgroundSlideOutAnimationName()
                    + "{" + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":"
                    + translateYStart + "0" + translateYEnd + "}" + "to{opacity:1;"
                    + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":" + translateYStart + "100%"
                    + translateYEnd + "}" + "}" + "@keyframes " + CSS.windowBackgroundSlideOutAnimationName() + "{"
                    + "0%{opacity:1;transform:translateY(0)}" + "to{opacity:1;transform:translateY(100%)}" + "}");
        }
        cssText.append(Window._CSS.getText());
        cssText.append("@media all and (orientation:landscape){"
                + ThemeResources.INSTANCE.windowCSSLandscape().getText() + "}");

        // dialog
        cssText.append(Dialog._CSS.getText());

        // alertview
        if (AlertView._CSS instanceof AlertViewCssResourceIOS) {
            final AlertViewCssResourceIOS CSS = (AlertViewCssResourceIOS) AlertView._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.alertViewScreenCoverFadeInAnimationName() + "{" + "0%{opacity:0}" + "to{opacity:1}" + "}"
                    + "@keyframes " + CSS.alertViewScreenCoverFadeInAnimationName() + "{" + "0%{opacity:0}"
                    + "to{opacity:1}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " "
                    + CSS.alertViewScreenCoverFadeOutAnimationName() + "{" + "0%{opacity:1}" + "to{opacity:0}" + "}"
                    + "@keyframes " + CSS.alertViewScreenCoverFadeOutAnimationName() + "{" + "0%{opacity:1}"
                    + "to{opacity:0}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.alertViewFadeInAnimationName() + "{"
                    + "0%{opacity:0;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(1)}"
                    + "50%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(.95)}"
                    + "to{" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(1)}" + "}"
                    + "@keyframes " + CSS.alertViewFadeInAnimationName() + "{" + "0%{opacity:0;transform:scale(1)}"
                    + "50%{opacity:1;transform:scale(.95)}" + "to{transform:scale(1)}" + "}" +

                    DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.alertViewFadeOutAnimationName() + "{"
                    + "0%{opacity:1;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(1)}"
                    + "to{opacity:0;" + DOMConstants.INSTANCE.getTransformPropertyNameForCSSText() + ":scale(.95)}"
                    + "}" + "@keyframes " + CSS.alertViewFadeOutAnimationName() + "{"
                    + "0%{opacity:1;transform:scale(1)}" + "to{opacity:0;transform:scale(.95)}" + "}");
        }
        cssText.append(AlertView._CSS.getText());

        // popover
        cssText.append(Popover._CSS.getText());
        if (Popover._CSS instanceof PopoverCssResourceIPhone) {
            final PopoverCssResourceIPhone CSS = (PopoverCssResourceIPhone) Popover._CSS;
            cssText.append(DOMConstants.INSTANCE.getAtKeyframesText() + " " + CSS.popoverFadeOutAnimationName()
                    + "{" + "0%{opacity:1}" + "to{opacity:0}" + "}" + "@keyframes "
                    + CSS.popoverFadeOutAnimationName() + "{" + "0%{opacity:1}" + "to{opacity:0}" + "}");
        }

        StyleInjector.injectAtEnd(CSSUtil.fixNotSelectors(cssText.toString()), true);
    }
}