Package org.omnifaces.component.script

Source Code of org.omnifaces.component.script.OnloadScript

/*
* Copyright 2012 OmniFaces.
*
* 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 org.omnifaces.component.script;

import static org.omnifaces.util.Ajax.oncomplete;
import static org.omnifaces.util.Events.subscribeToViewEvent;

import java.io.IOException;
import java.io.StringWriter;

import javax.faces.FacesException;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialViewContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ResponseWriterWrapper;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ListenerFor;
import javax.faces.event.ListenersFor;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.PostRestoreStateEvent;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

import org.omnifaces.util.Ajax;

/**
* <p>
* The <code>&lt;o:onloadScript</code> is a component that extends the standard <code>&lt;h:outputScript&gt;</code>
* which will be executed in the end of the HTML body (thus when all HTML elements are initialized in the HTML DOM tree)
* and will re-execute its script body on every ajax request. This is particularly useful if you want to re-execute a
* specific helper script to manipulate the HTML DOM tree, such as (re-)adding fancy tooltips, performing highlights,
* etcetera, also after changes in the HTML DOM tree on ajax responses.
* <p>
* You can put it anywhere in the view, it will always be relocated to the end of body.
* <pre>
* &lt;o:onloadScript&gt;alert('OnloadScript is invoked!');&lt;/o:onloadScript&gt;
* </pre>
* <p>
* The <code>&lt;o:onloadScript&gt;</code> is implicitly relocated to the end of the <code>&lt;body&gt;</code>,
* exactly like as <code>&lt;h:outputScript target="body"&gt;</code> does. So it's always executed when the entire
* <code>&lt;body&gt;</code> is finished populating and thus you don't need a <code>window.onload</code> or a
* <code>$(document).ready()</code> in there. Again, the difference with <code>&lt;h:outputScript target="body"&gt;</code>
* is that the <code>&lt;o:onloadScript&gt;</code> is also executed on every ajax request.
*
* @author Bauke Scholtz
*/
@FacesComponent(OnloadScript.COMPONENT_TYPE)
@ListenersFor({
  @ListenerFor(systemEventClass=PostAddToViewEvent.class),
  @ListenerFor(systemEventClass=PostRestoreStateEvent.class)
})
public class OnloadScript extends ScriptFamily implements SystemEventListener {

  // Public constants -----------------------------------------------------------------------------------------------

  /** The standard component type. */
  public static final String COMPONENT_TYPE = "org.omnifaces.component.script.OnloadScript";

  // Actions --------------------------------------------------------------------------------------------------------

  /**
   * Returns <code>true</code> if the given source is an instance of {@link OnloadScript} or {@link UIViewRoot}.
   */
  @Override
  public boolean isListenerForSource(Object source) {
        return source instanceof OnloadScript || source instanceof UIViewRoot;
  }

  /**
   * If the event is a {@link PostAddToViewEvent}, then relocate the component to end of body, so that we can make
   * sure that the script is executed after all HTML DOM elements are been created. Else if the event is a
   * {@link PostRestoreStateEvent} and the current request is an ajax request, then subscribe to the
   * {@link PreRenderViewEvent} event.
   */
  @Override
  public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
    FacesContext context = FacesContext.getCurrentInstance();
    UIViewRoot view = context.getViewRoot();
    boolean ajaxRequest = context.getPartialViewContext().isAjaxRequest();

    if (event instanceof PostAddToViewEvent) {
      if (!ajaxRequest || !view.getComponentResources(context, "body").contains(this)) {
        view.addComponentResource(context, this, "body");
      }
    }
    else if (event instanceof PostRestoreStateEvent) {
      if (ajaxRequest) {
        subscribeToViewEvent(PreRenderViewEvent.class, this);
      }
    }
  }

  /**
   * If the event is a {@link PreRenderViewEvent} and the current request is an ajax request and this component is
   * rendered and there are any children, then encode the children as {@link Ajax#oncomplete(String...)}.
   */
  @Override
  public void processEvent(SystemEvent event) throws AbortProcessingException {
    if (!(event instanceof PreRenderViewEvent)) {
      return;
    }

    FacesContext context = FacesContext.getCurrentInstance();

    if (!shouldEncodeOncomplete(context.getPartialViewContext())) {
      return;
    }

    pushComponentToEL(context, this);
    StringWriter buffer = new StringWriter();
    ResponseWriter originalResponseWriter = context.getResponseWriter();
    String encoding = context.getExternalContext().getRequestCharacterEncoding();
    context.getExternalContext().setResponseCharacterEncoding(encoding);
    final ResponseWriter writer = context.getRenderKit().createResponseWriter(buffer, null, encoding);
    context.setResponseWriter(new ResponseWriterWrapper() {
      @Override
      public void writeText(Object text, String property) throws IOException {
        writer.write(text.toString()); // So, don't escape HTML.
      }
      @Override
      public ResponseWriter getWrapped() {
        return writer;
      }
    });

    try {
      encodeChildren(context);
    }
    catch (IOException e) {
      throw new FacesException(e);
    }
    finally {
      if (originalResponseWriter != null) {
        context.setResponseWriter(originalResponseWriter);
      }
    }

    popComponentFromEL(context);
    String script = buffer.toString().trim();

    if (!script.isEmpty()) {
      oncomplete(script);
    }
  }

  /**
   * If the current request is not an ajax request, and this component is rendered, and there are any children, then
   * start the <code>&lt;script&gt;</code> element.
   */
  @Override
  public void encodeBegin(FacesContext context) throws IOException {
    if (!shouldEncodeHtml(context.getPartialViewContext())) {
      return;
    }

    pushComponentToEL(context, this);
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("script", this);
    writer.writeAttribute("type", "text/javascript", "type");
  }

  /**
   * If the current request is not an ajax request, and this component is rendered, and there are any children, then
   * end the <code>&lt;script&gt;</code> element.
   */
  @Override
  public void encodeEnd(FacesContext context) throws IOException {
    if (!shouldEncodeHtml(context.getPartialViewContext())) {
      return;
    }

    context.getResponseWriter().endElement("script");
    popComponentFromEL(context);
  }

  private boolean shouldEncodeHtml(PartialViewContext pvc) {
    return isRendered() && !(pvc.isAjaxRequest() && !pvc.isRenderAll());
  }

  private boolean shouldEncodeOncomplete(PartialViewContext pvc) {
    return isRendered() && pvc.isAjaxRequest() && !pvc.isRenderAll();
  }

}
TOP

Related Classes of org.omnifaces.component.script.OnloadScript

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.