Пример #1
0
 /** Returns the first line to be generated to the output, or null if no special first line. */
 public static final String outFirstLine(Execution exec, Page page) {
   if (exec.getAttribute(FIRST_LINE_GENED) == null && !exec.isAsyncUpdate(null)) {
     exec.setAttribute(FIRST_LINE_GENED, Boolean.TRUE);
     return trimAndLF(((PageCtrl) page).getFirstLine());
   }
   return "";
 }
Пример #2
0
  /**
   * Generates and returns the ZK specific HTML tags such as stylesheet and JavaScript.
   *
   * <p>For each desktop, we have to generate a set of HTML tags to load ZK Client engine, style
   * sheets and so on. For ZUL pages, it is generated automatically by page.dsp. However, for ZHTML
   * pages, we have to generate these tags with special component such as org.zkoss.zhtml.Head, such
   * that the result HTML page is legal.
   *
   * @param exec the execution (never null)
   * @return the string holding the HTML tags, or null if already generated.
   * @param wapp the Web application. If null, exec.getDesktop().getWebApp() is used. So you have to
   *     specify it if the execution is not associated with desktop (a fake execution, such as
   *     JSP/DSP).
   * @param deviceType the device type, such as ajax. If null, exec.getDesktop().getDeviceType() is
   *     used. So you have to specify it if the execution is not associated with desktop (a fake
   *     execution).
   * @see #outHeaderZkTags
   */
  public static String outZkTags(Execution exec, WebApp wapp, String deviceType) {
    if (exec.getAttribute(ATTR_ZK_TAGS_GENERATED) != null) return null;
    exec.setAttribute(ATTR_ZK_TAGS_GENERATED, Boolean.TRUE);

    final StringBuffer sb =
        new StringBuffer(512)
            .append('\n')
            .append(outLangStyleSheets(exec, wapp, deviceType))
            .append(outLangJavaScripts(exec, wapp, deviceType));

    final Desktop desktop = exec.getDesktop();
    if (desktop != null && exec.getAttribute(ATTR_DESKTOP_JS_GENED) == null) {
      sb.append("<script class=\"z-runonce\" type=\"text/javascript\">\nzkdt('")
          .append(desktop.getId())
          .append("','")
          .append(getContextURI(exec))
          .append("','")
          .append(desktop.getUpdateURI(null))
          .append("','")
          .append(desktop.getRequestPath())
          .append("');")
          .append(outSpecialJS(desktop))
          .append("\n</script>\n");
    }

    return sb.toString();
  }
Пример #3
0
    public Set<? extends Component> getAvailableAtClient() {
      if (!isCropper()) return null;

      final Tree tree = getTree();
      final Component parent = getParent();
      final Execution exe = Executions.getCurrent();
      final String attrnm = VISIBLE_ITEM + tree.getUuid();
      Map<Treeitem, Boolean> map = cast((Map) exe.getAttribute(attrnm));
      if (map == null) {
        // Test very simple case first since getVisibleItems costly
        if (parent instanceof Treeitem) {
          for (Treeitem ti = (Treeitem) parent; ; ) {
            if (!ti.isOpen()) return Collections.emptySet();
            Component gp = ti.getParent().getParent();
            if (!(gp instanceof Treeitem)) break;
            ti = (Treeitem) gp;
          }
        }

        map = tree.getVisibleItems();
        Executions.getCurrent().setAttribute(attrnm, map);
      }
      return map.keySet();
      // yes, we return all visible items, not just direct children
      // in other words, we consider the whole tree as a single scope
      // See also bug 2814504
    }
Пример #4
0
 /** Returns the doc type, or null if not available. It is null or &lt;!DOCTYPE ...&gt;. */
 public static final String outDocType(Execution exec, Page page) {
   if (exec.getAttribute(DOCTYPE_GENED) == null && !exec.isAsyncUpdate(null)) {
     exec.setAttribute(DOCTYPE_GENED, Boolean.TRUE);
     final String docType = ((PageCtrl) page).getDocType();
     return trimAndLF(docType != null ? docType : page.getDesktop().getDevice().getDocType());
   }
   return "";
 }
Пример #5
0
 private void subscribe(EventListener<T> listener, EventListener<T> callback, boolean async) {
   if (async && _nAsync++ == 0) {
     final Execution exec = Executions.getCurrent();
     if (exec == null) throw new IllegalStateException("Execution required");
     ((DesktopCtrl) exec.getDesktop()).enableServerPush(true, this);
     // B65-ZK-1840 make sure the flag is true after enabling (otherwise disabling will fail)
     _serverPushEnabled = true;
   }
   _listenerInfos.add(new ListenerInfo<T>(listener, callback, async));
 }
Пример #6
0
  /**
   * Returns the content of the specified condition that will be placed inside the header element of
   * the specified page, or null if it was generated before. For HTML, the header element is the
   * HEAD element.
   *
   * <p>Notice that this method ignores the following invocations against the same page in the same
   * execution. In other words, it is safe to invoke this method multiple times.
   *
   * @param before whether to return the headers that shall be shown before ZK's CSS/JS headers. If
   *     true, only the headers that shall be shown before (such as meta) are returned. If true,
   *     only the headers that shall be shown after (such as link) are returned.
   */
  public static final String outHeaders(Execution exec, Page page, boolean before) {
    if (page == null) return "";

    String attr = "zkHeaderGened" + page.getUuid();
    if (before) attr += "Bf";
    if (exec.getAttribute(attr) != null) return null;

    exec.setAttribute(attr, Boolean.TRUE); // generated only once
    return before ? ((PageCtrl) page).getBeforeHeadTags() : ((PageCtrl) page).getAfterHeadTags();
  }
Пример #7
0
  /**
   * Generates the unavailable message in HTML tags, if any.
   *
   * @param exec the execution (never null)
   */
  public static String outUnavailable(Execution exec) {
    if (exec.getAttribute(ATTR_UNAVAILABLE_GENED) == null && !exec.isAsyncUpdate(null)) {
      exec.setAttribute(ATTR_UNAVAILABLE_GENED, Boolean.TRUE);

      final Device device = exec.getDesktop().getDevice();
      String s = device.getUnavailableMessage();
      return s != null ? "<noscript>\n" + s + "\n</noscript>" : "";
    }
    return ""; // nothing to generate
  }
Пример #8
0
  private static Map<Component, ShadowInfo> getShadowInfos(boolean autoCreate) {
    Execution exec = Executions.getCurrent();
    if (exec == null) return null;

    Map<Component, ShadowInfo> result = cast((Map) exec.getAttribute(COMPONENT_INFO));
    if (result == null && autoCreate) {
      result = new HashMap<Component, ShadowInfo>();
      exec.setAttribute(COMPONENT_INFO, result);
    }
    return result;
  }
Пример #9
0
  private boolean isEventThreadEnabled(boolean attachedRequired) {
    Desktop desktop = getDesktop();
    if (desktop == null) {
      if (attachedRequired) throw new SuspendNotAllowedException("Not attached, " + this);

      final Execution exec = Executions.getCurrent();
      if (exec == null || (desktop = exec.getDesktop()) == null)
        return true; // assume enabled (safer)
    }
    return desktop.getWebApp().getConfiguration().isEventThreadEnabled();
  }
Пример #10
0
  /**
   * Returns HTML tags to include all style sheets that are defined in all languages of the
   * specified device (never null).
   *
   * <p>In addition to style sheets defined in lang.xml and lang-addon.xml, it also include:
   *
   * <ol>
   *   <li>The style sheet specified in the theme-uri parameter.
   * </ol>
   *
   * <p>FUTURE CONSIDERATION: we might generate the inclusion on demand instead of all at once.
   *
   * @param exec the execution (never null)
   * @param wapp the Web application. If null, exec.getDesktop().getWebApp() is used. So you have to
   *     specify it if the execution is not associated with desktop (a fake execution, such as
   *     JSP/DSP).
   * @param deviceType the device type, such as ajax. If null, exec.getDesktop().getDeviceType() is
   *     used. So you have to specify it if the execution is not associated with desktop (a fake
   *     execution).
   */
  public static final String outLangStyleSheets(Execution exec, WebApp wapp, String deviceType) {
    if (exec.isAsyncUpdate(null) || exec.getAttribute(ATTR_LANG_CSS_GENED) != null)
      return ""; // nothing to generate
    exec.setAttribute(ATTR_LANG_CSS_GENED, Boolean.TRUE);

    final StringBuffer sb = new StringBuffer(512);
    for (StyleSheet ss : getStyleSheets(exec, wapp, deviceType)) append(sb, ss, exec, null);

    if (sb.length() > 0) sb.append('\n');
    return sb.toString();
  }
Пример #11
0
  public BindingAnnotationInfoChecker getAnnotationInfoChecker() {
    Execution exec = Executions.getCurrent();
    if (exec == null) return null;

    BindingAnnotationInfoChecker checker =
        (BindingAnnotationInfoChecker) exec.getAttribute(CHECKER_KEY);
    if (checker == null) {
      checker = createDefaultAnnotationInfoChecker();
      exec.setAttribute(CHECKER_KEY, checker);
    }
    return checker;
  }
Пример #12
0
  public BindingExecutionInfoCollector getExecutionInfoCollector() {

    Execution exec = Executions.getCurrent();
    if (exec == null) return null;

    BindingExecutionInfoCollector collector =
        (BindingExecutionInfoCollector) exec.getAttribute(COLLECTOR_KEY);
    if (collector == null) {
      collector = createBindingExecutionInfoCollector();
      exec.setAttribute(COLLECTOR_KEY, collector);
    }
    return collector;
  }
Пример #13
0
  /**
   * Outputs the HTML tags of the given component to the given writer.
   *
   * @param path the request path. If null, the servlet path is assumed.
   * @param out the output (never null).
   * @param richlet the richlet to run. If you have only one component to show and no need process
   *     it under an execution, you could use {@link #render(javax.servlet.ServletContext,
   *     javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
   *     org.zkoss.zk.ui.Component, String, java.io.Writer)} instead.
   * @since 5.0.5
   */
  public static final void render(
      ServletContext ctx,
      HttpServletRequest request,
      HttpServletResponse response,
      Richlet richlet,
      String path,
      Writer out)
      throws ServletException, IOException {
    if (path == null) path = Https.getThisServletPath(request);

    WebManager webman = WebManager.getWebManagerIfAny(ctx);
    if (webman == null) {
      final String ATTR = "org.zkoss.zkplus.embed.updateURI";
      String updateURI = Library.getProperty(ATTR);
      if (updateURI == null) updateURI = "/zkau";
      else updateURI = Utils.checkUpdateURI(updateURI, ATTR);
      webman = new WebManager(ctx, updateURI);
    }

    final Session sess = WebManager.getSession(ctx, request);
    final WebApp wapp = sess.getWebApp();
    final WebAppCtrl wappc = (WebAppCtrl) wapp;
    final Object old =
        I18Ns.setup(sess, request, response, wapp.getConfiguration().getResponseCharset());
    Execution exec = null;
    try {
      final Desktop desktop = webman.getDesktop(sess, request, response, path, true);
      if (desktop == null) // forward or redirect
      return;

      final RequestInfo ri =
          new RequestInfoImpl(wapp, sess, desktop, request, PageDefinitions.getLocator(wapp, path));
      sess.setAttribute(Attributes.GAE_FIX, new Integer(0));
      ((SessionCtrl) sess).notifyClientRequest(true);

      final UiFactory uf = wappc.getUiFactory();
      final Page page = WebManager.newPage(uf, ri, richlet, response, path);
      exec = new ExecutionImpl(ctx, request, response, desktop, page);
      exec.setAttribute(Attributes.PAGE_REDRAW_CONTROL, "page");
      exec.setAttribute(Attributes.PAGE_RENDERER, new PageRenderer(exec));

      wappc.getUiEngine().execNewPage(exec, richlet, page, out);
      // no need to set device type here, since UiEngine will do it later
    } finally {
      I18Ns.cleanup(request, old);
      if (exec != null) {
        exec.removeAttribute(Attributes.PAGE_REDRAW_CONTROL);
        exec.removeAttribute(Attributes.PAGE_RENDERER);
      }
    }
  }
Пример #14
0
    // @Override
    public void render(Page page, Writer out) throws IOException {
      final Desktop desktop = _exec.getDesktop();

      out.write("<script type=\"text/javascript\">zkpb('");
      out.write(page.getUuid());
      out.write("','");
      out.write(desktop.getId());
      out.write("','");
      out.write(getContextURI());
      out.write("','");
      out.write(desktop.getUpdateURI(null));
      out.write("','");
      out.write(desktop.getRequestPath());
      out.write('\'');

      String style = page.getStyle();
      if (style != null && style.length() > 0) {
        out.write(",{style:'");
        out.write(style);
        out.write("'}");
      }

      out.write(");zkpe();</script>\n");

      for (Component root = page.getFirstRoot(); root != null; root = root.getNextSibling()) {
        // HtmlPageRenders.outStandalone(_exec, root, out);
      }
    }
Пример #15
0
 private String getContextURI() {
   if (_exec != null) {
     String s = _exec.encodeURL("/");
     int j = s.lastIndexOf('/'); // might have jsessionid=...
     return j >= 0 ? s.substring(0, j) + s.substring(j + 1) : s;
   }
   return "";
 }
Пример #16
0
  @Override
  public Collection getThemeURIs(Execution exec, List uris) {
    for (Iterator it = uris.iterator(); it.hasNext(); ) {
      if (it.next().toString().startsWith("~./eccTheme")) it.remove(); // remove the ecc theme
    }

    HttpServletRequest req = (HttpServletRequest) exec.getNativeRequest();
    Object uri = getMyThemeURI(req);
    if (uri != null) uris.add(uri);
    return uris;
  }
Пример #17
0
  /**
   * Returns a list of {@link StyleSheet} that shall be generated to the client for the specified
   * execution.
   *
   * @param exec the execution (never null)
   * @param wapp the Web application. If null, exec.getDesktop().getWebApp() is used. So you have to
   *     specify it if the execution is not associated with desktop (a fake execution, such as
   *     JSP/DSP).
   * @param deviceType the device type, such as ajax. If null, exec.getDesktop().getDeviceType() is
   *     used. So you have to specify it if the execution is not associated with desktop (a fake
   *     execution).
   */
  public static final List<StyleSheet> getStyleSheets(
      Execution exec, WebApp wapp, String deviceType) {
    if (wapp == null) wapp = exec.getDesktop().getWebApp();
    if (deviceType == null) deviceType = exec.getDesktop().getDeviceType();

    final Configuration config = wapp.getConfiguration();
    final Set<String> disabled = config.getDisabledThemeURIs();
    final List<StyleSheet> sses = new LinkedList<StyleSheet>(); // a list of StyleSheet
    for (LanguageDefinition langdef : LanguageDefinition.getByDeviceType(deviceType)) {
      for (StyleSheet ss : langdef.getStyleSheets()) {
        if (!disabled.contains(ss.getHref())) sses.add(ss);
      }
    }

    // Process configuration
    final ThemeProvider themeProvider = config.getThemeProvider();
    if (themeProvider != null) {
      final List<Object> orgss = new LinkedList<Object>();
      for (StyleSheet ss : sses) {
        final String href = ss.getHref();
        if (href != null && href.length() > 0)
          orgss.add(ss.getMedia() != null ? ss : href); // we don't support getContent
      }

      final String[] hrefs = config.getThemeURIs();
      for (int j = 0; j < hrefs.length; ++j) orgss.add(hrefs[j]);

      sses.clear();
      final Collection<?> res = themeProvider.getThemeURIs(exec, orgss);
      if (res != null) {
        for (Object re : res) {
          sses.add(
              re instanceof StyleSheet ? (StyleSheet) re : new StyleSheet((String) re, "text/css"));
        }
      }
    } else {
      final String[] hrefs = config.getThemeURIs();
      for (int j = 0; j < hrefs.length; ++j) sses.add(new StyleSheet(hrefs[j], "text/css"));
    }
    return sses;
  }
Пример #18
0
  private static void append(StringBuffer sb, StyleSheet ss, Execution exec, Page page) {
    String href = ss.getHref();
    String media = ss.getMedia();
    if (href != null) {
      try {
        if (exec != null) href = (String) exec.evaluate(page, href, String.class);

        if (href != null && href.length() > 0) {
          sb.append("\n<link rel=\"stylesheet\" type=\"")
              .append(ss.getType())
              .append("\" href=\"")
              .append(ServletFns.encodeURL(href));
          if (media != null) sb.append("\" media=\"").append(media);
          sb.append("\"/>");
        }
      } catch (javax.servlet.ServletException ex) {
        throw new UiException(ex);
      }
    } else {
      sb.append("\n<style");
      if (ss.getType() != null) sb.append(" type=\"").append(ss.getType()).append('"');
      sb.append(">\n").append(ss.getContent()).append("\n</style>");
    }
  }
Пример #19
0
 private static final void setRenderContext(Execution exec, RenderContext rc) {
   exec.setAttribute(ATTR_RENDER_CONTEXT, rc);
 }
Пример #20
0
 /**
  * Returns the render context, or null if not available. It is used to render the content that
  * will appear before the content generated by {@link ContentRenderer}, such as crawlable content.
  *
  * @param exec the execution. If null, {@link Executions#getCurrent} is assumed.
  */
 public static final RenderContext getRenderContext(Execution exec) {
   if (exec == null) exec = Executions.getCurrent();
   return exec != null ? (RenderContext) exec.getAttribute(ATTR_RENDER_CONTEXT) : null;
 }
Пример #21
0
 /**
  * Returns whether a component can directly generate HTML tags to the output. This flag is used by
  * components that can generate the content directly, such as {@link
  * org.zkoss.zk.ui.HtmlNativeComponent}
  *
  * @see #setDirectContent
  */
 public static boolean isDirectContent(Execution exec) {
   if (exec == null) exec = Executions.getCurrent();
   return exec != null && exec.getAttribute(ATTR_DIRECT_CONTENT) != null;
 }
Пример #22
0
 /**
  * Sets whether a component can directly generate HTML tags to the output.
  *
  * @see #isDirectContent
  */
 public static boolean setDirectContent(Execution exec, boolean direct) {
   return (direct
           ? exec.setAttribute(ATTR_DIRECT_CONTENT, Boolean.TRUE)
           : exec.removeAttribute(ATTR_DIRECT_CONTENT))
       != null;
 }
Пример #23
0
 /**
  * Returns if the ZK specific HTML tags are generated.
  *
  * @since 5.0.3
  */
 public static boolean isZkTagsGenerated(Execution exec) {
   return exec.getAttribute(ATTR_ZK_TAGS_GENERATED) != null;
 }
Пример #24
0
  /**
   * Returns the HTML content representing a page.
   *
   * @param au whether it is caused by asynchronous update
   * @param exec the execution (never null)
   */
  public static final void outPageContent(Execution exec, Page page, Writer out, boolean au)
      throws IOException {
    final Desktop desktop = page.getDesktop();
    final PageCtrl pageCtrl = (PageCtrl) page;
    final Component owner = pageCtrl.getOwner();
    boolean contained = owner == null && exec.isIncluded();
    // a standalone page (i.e., no owner), and being included by
    // non-ZK page (e.g., JSP).
    //
    // Revisit Bug 2001707: OK to use exec.isIncluded() since
    // we use PageRenderer now (rather than Servlet's include)
    // TODO: test again

    // prepare style
    String style = page.getStyle();
    if (style == null || style.length() == 0) {
      style = null;
      String wd = null, hgh = null;
      if (owner instanceof HtmlBasedComponent) {
        final HtmlBasedComponent hbc = (HtmlBasedComponent) owner;
        wd = hbc.getWidth(); // null if not set
        hgh = hbc.getHeight(); // null if not set
      }

      if (wd != null || hgh != null || contained) {
        final StringBuffer sb = new StringBuffer(32);
        HTMLs.appendStyle(sb, "width", wd != null ? wd : "100%");
        HTMLs.appendStyle(sb, "height", hgh != null ? hgh : contained ? null : "100%");
        style = sb.toString();
      }
    }

    RenderContext rc = null, old = null;
    final boolean aupg = exec.isAsyncUpdate(page); // AU this page
    final boolean includedAndPart = owner != null && !aupg;
    // this page is included and rendered with its owner
    final boolean divRequired = !au || includedAndPart;
    final boolean standalone = !au && owner == null;
    if (standalone) {
      rc =
          new RenderContext(
              out, new StringWriter(), desktop.getWebApp().getConfiguration().isCrawlable(), false);
      setRenderContext(exec, rc);
    } else if (owner != null) {
      old = getRenderContext(exec); // store
      final boolean crawlable =
          old != null && old.temp != null && desktop.getWebApp().getConfiguration().isCrawlable();
      setRenderContext(exec, crawlable ? new RenderContext(old.temp, null, true, true) : null);
    }

    // generate div first
    if (divRequired) {
      outDivTemplateBegin(out, page.getUuid());
    }
    if (standalone) { // switch out
      // don't call outDivTemplateEnd yet since rc.temp will be generated before it
      out = new StringWriter();
    } else if (divRequired) {
      outDivTemplateEnd(page, out); // close it now since no rc.temp
    }

    if (includedAndPart) {
      out = new StringWriter();
    } else if (divRequired) {
      // generate JS second
      out.write("\n<script class=\"z-runonce\" type=\"text/javascript\">\n");
    }

    exec.setAttribute(ATTR_DESKTOP_JS_GENED, Boolean.TRUE);
    final int order = ComponentRedraws.beforeRedraw(false);
    final String extra;
    try {
      if (order < 0) {
        if (aupg) out.write('[');
        else {
          out.write(outSpecialJS(desktop));
          out.write(divRequired ? "zkmx(" : "zkx(");
        }
      } else if (order > 0) // not first child
      out.write(',');
      out.write("\n[0,'"); // 0: page
      out.write(page.getUuid());
      out.write("',{");

      final StringBuffer props = new StringBuffer(128);
      final String pgid = page.getId();
      if (pgid.length() > 0) appendProp(props, "id", pgid);
      if (owner != null) {
        appendProp(props, "ow", owner.getUuid());
      } else {
        appendProp(props, "dt", desktop.getId());
        appendProp(props, "cu", getContextURI(exec));
        appendProp(props, "uu", desktop.getUpdateURI(null));
        appendProp(props, "ru", desktop.getRequestPath());
      }
      final String pageWgtCls = pageCtrl.getWidgetClass();
      if (pageWgtCls != null) appendProp(props, "wc", pageWgtCls);
      if (style != null) appendProp(props, "style", style);
      if (!isClientROD(page)) appendProp(props, "z$rod", Boolean.FALSE);
      if (contained) appendProp(props, "ct", Boolean.TRUE);
      out.write(props.toString());
      out.write("},[");

      for (Component root = page.getFirstRoot(); root != null; root = root.getNextSibling())
        ((ComponentCtrl) root).redraw(out);

      out.write("]]");
    } finally {
      extra = ComponentRedraws.afterRedraw();
    }

    if (order < 0) {
      outEndJavaScriptFunc(exec, out, extra, aupg);
    }

    if (standalone) {
      setRenderContext(exec, null);

      StringBuffer sw = ((StringWriter) out).getBuffer();
      out = rc.temp;
      if (divRequired) outDivTemplateEnd(page, out);
      // close tag after temp, but before perm (so perm won't be destroyed)
      Files.write(out, ((StringWriter) rc.perm).getBuffer()); // perm

      // B65-ZK-1836
      Files.write(
          out, new StringBuffer(sw.toString().replaceAll("</(?i)(?=script>)", "<\\\\/"))); // js
    } else if (owner != null) { // restore
      setRenderContext(exec, old);
    }

    if (includedAndPart) {
      ((Includer) owner).setRenderingResult(((StringWriter) out).toString());
    } else if (divRequired) {
      out.write("\n</script>\n");
    }
  }
Пример #25
0
  /**
   * Returns HTML tags to include all JavaScript files and codes that are required when loading a
   * ZUML page (never null).
   *
   * <p>FUTURE CONSIDERATION: we might generate the inclusion on demand instead of all at once.
   *
   * @param exec the execution (never null)
   * @param wapp the Web application. If null, exec.getDesktop().getWebApp() is used. So you have to
   *     specify it if the execution is not associated with desktop (a fake execution, such as
   *     JSP/DSP).
   * @param deviceType the device type, such as ajax. If null, exec.getDesktop().getDeviceType() is
   *     used. So you have to specify it if the execution is not associated with desktop (a fake
   *     execution).
   */
  public static final String outLangJavaScripts(Execution exec, WebApp wapp, String deviceType) {
    if (exec.isAsyncUpdate(null) || exec.getAttribute(ATTR_LANG_JS_GENED) != null)
      return ""; // nothing to generate
    exec.setAttribute(ATTR_LANG_JS_GENED, Boolean.TRUE);

    final Desktop desktop = exec.getDesktop();
    if (wapp == null) wapp = desktop.getWebApp();
    if (deviceType == null) deviceType = desktop != null ? desktop.getDeviceType() : "ajax";

    final StringBuffer sb = new StringBuffer(1536);

    final Set<JavaScript> jses = new LinkedHashSet<JavaScript>(32);
    for (LanguageDefinition langdef : LanguageDefinition.getByDeviceType(deviceType))
      jses.addAll(langdef.getJavaScripts());
    for (JavaScript js : jses) append(sb, js);

    sb.append("\n<!-- ZK ").append(wapp.getVersion());
    if (WebApps.getFeature("ee")) sb.append(" EE");
    else if (WebApps.getFeature("pe")) sb.append(" PE");
    sb.append(' ').append(wapp.getBuild());
    Object o = wapp.getAttribute("org.zkoss.zk.ui.notice");
    if (o != null) sb.append(o);
    sb.append(" -->\n");

    int tmout = 0;
    final Boolean autoTimeout = getAutomaticTimeout(desktop);
    if (autoTimeout != null
        ? autoTimeout.booleanValue()
        : wapp.getConfiguration().isAutomaticTimeout(deviceType)) {
      if (desktop != null) {
        tmout = desktop.getSession().getMaxInactiveInterval();
      } else {
        Object req = exec.getNativeRequest();
        if (req instanceof HttpServletRequest) {
          final HttpSession hsess = ((HttpServletRequest) req).getSession(false);
          if (hsess != null) {
            final Session sess = SessionsCtrl.getSession(wapp, hsess);
            if (sess != null) {
              tmout = sess.getMaxInactiveInterval();
            } else {
              // try configuration first since HttpSession's timeout is set
              // when ZK Session is created (so it is not set yet)
              // Note: no need to setMaxInactiveInternval here since it will
              // be set later or not useful at the end
              tmout = wapp.getConfiguration().getSessionMaxInactiveInterval();
              if (tmout <= 0) // system default
              tmout = hsess.getMaxInactiveInterval();
            }
          } else tmout = wapp.getConfiguration().getSessionMaxInactiveInterval();
        }
      }
      if (tmout > 0) { // unit: seconds
        int extra = tmout / 8;
        tmout += extra > 60 ? 60 : extra < 5 ? 5 : extra;
        // Add extra seconds to ensure it is really timeout
      }
    }

    final boolean
        keepDesktop =
            exec.getAttribute(Attributes.NO_CACHE) == null
                && !"page".equals(ExecutionsCtrl.getPageRedrawControl(exec)),
        groupingAllowed = isGroupingAllowed(desktop);
    final String progressboxPos =
        org.zkoss.lang.Library.getProperty("org.zkoss.zul.progressbox.position", "");
    if (tmout > 0 || keepDesktop || progressboxPos.length() > 0 || !groupingAllowed) {
      sb.append("<script class=\"z-runonce\" type=\"text/javascript\">\nzkopt({");

      if (keepDesktop) sb.append("kd:1,");
      if (!groupingAllowed) sb.append("gd:1,");
      if (tmout > 0) sb.append("to:").append(tmout).append(',');
      if (progressboxPos.length() > 0) sb.append("ppos:'").append(progressboxPos).append('\'');

      if (sb.charAt(sb.length() - 1) == ',') sb.setLength(sb.length() - 1);
      sb.append("});\n</script>");
    }

    final Device device = Devices.getDevice(deviceType);
    String s = device.getEmbedded();
    if (s != null) sb.append(s).append('\n');
    return sb.toString();
  }