Package com.google.gwt.user.cellview.client

Source Code of com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident

/*
* 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.gwt.user.cellview.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.shared.impl.StringCase;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.Widget;

import java.util.HashSet;
import java.util.Set;

/**
* IE specified Impl used by cell based widgets.
*/
class CellBasedWidgetImplTrident extends CellBasedWidgetImpl {

  /**
   * The method used to dispatch non-bubbling events.
   */
  private static JavaScriptObject dispatchFocusEvent;

  /**
   * The currently focused input element, select box, or text area.
   */
  private static Element focusedInput;

  /**
   * If true, only synthesize change events when the focused input is blurred.
   */
  private static boolean focusedInputChangesOnBlurOnly;

  /**
   * The last value of the focused input element.
   */
  private static Object focusedInputValue;

  /**
   * The set of input types that can receive change events.
   */
  private static Set<String> inputTypes;

  /**
   * Dispatch an event to the cell, ensuring that the widget will catch it.
   *
   * @param widget the widget that will handle the event
   * @param target the cell element
   * @param eventBits the event bits to sink
   * @param event the event to fire, or null not to fire an event
   */
  private static void dispatchCellEvent(Widget widget, Element target,
      int eventBits, Event event) {
    // Make sure that the target is still a child of the widget. We defer the
    // firing of some events, so its possible that the DOM structure has
    // changed before we fire the event.
    if (!widget.getElement().isOrHasChild(target)) {
      return;
    }

    // Temporary listen for events from the cell. The event listener will be
    // removed in onBrowserEvent().
    DOM.setEventListener(target, widget);
    DOM.sinkEvents(target, eventBits | DOM.getEventsSunk(target));

    // Dispatch the event to the cell.
    if (event != null) {
      target.dispatchEvent(event);
    }
  }

  /**
   * Get the value of an element that has a value or checked state.
   *
   * @param elem the input element
   * @return the value of the input
   */
  private static Object getInputValue(Element elem) {
    if (isCheckbox(elem)) {
      return InputElement.as(elem).isChecked();
    }
    return getInputValueImpl(elem);
  }

  /**
   * Get the value of an element that has a value, such as an input element,
   * textarea, or select box.
   *
   * @param elem the input element
   * @return the value of the input
   */
  private static native String getInputValueImpl(Element elem) /*-{
    return elem.value;
  }-*/;

  /**
   * Used by {@link #initFocusEventSystem()} and {@link #initLoadEvents(String)}
   * to handle non bubbling events .
   *
   * @param event
   */
  @SuppressWarnings("unused")
  private static void handleNonBubblingEvent(Event event) {
    // Get the event target.
    EventTarget eventTarget = event.getEventTarget();
    if (!Element.is(eventTarget)) {
      return;
    }
    final Element target = eventTarget.cast();

    // Get the event listener.
    Element curElem = target;
    EventListener listener = DOM.getEventListener(curElem);
    while (curElem != null && listener == null) {
      curElem = curElem.getParentElement();
      listener = (curElem == null) ? null : DOM.getEventListener(curElem);
    }

    // Get the Widget from the event listener.
    if (!(listener instanceof Widget)) {
      return;
    }
    Widget widget = (Widget) listener;

    // Do not special case events that occur on the widget itself.
    if (target == widget.getElement()) {
      return;
    }

    String type = event.getType();
    if (BrowserEvents.FOCUSIN.equals(type)) {
      // If this is an input element, remember that we focused it.
      String tagName = StringCase.toLower(target.getTagName());
      if (inputTypes.contains(tagName)) {
        focusedInput = target;
        focusedInputValue = getInputValue(target);
        focusedInputChangesOnBlurOnly = !"select".equals(tagName)
            && !isCheckbox(target);
      }

      // The focus event has not fired yet, so we just need to set the
      // CellTable as the event listener and wait for it.
      dispatchCellEvent(widget, target, Event.ONFOCUS, null);
    } else if (BrowserEvents.FOCUSOUT.equals(type)) {
      // Fire a change event on the input element if the value changed.
      maybeFireChangeEvent(widget);
      focusedInput = null;

      // The blur event has already fired, so we need to synthesize one.
      Event blurEvent = Document.get().createFocusEvent().cast();
      dispatchCellEvent(widget, target, Event.ONBLUR, null);
    } else if (BrowserEvents.LOAD.equals(type) || BrowserEvents.ERROR.equals(type)) {
      DOM.dispatchEvent(event, widget.getElement(), listener);
    }
  }

  /**
   * Check whether or not an element is a checkbox or radio button.
   *
   * @param elem the element to check
   * @return true if a checkbox, false if not
   */
  private static boolean isCheckbox(Element elem) {
    if (elem == null || !"input".equalsIgnoreCase(elem.getTagName())) {
      return false;
    }
    String inputType = StringCase.toLower(InputElement.as(elem).getType());
    return "checkbox".equals(inputType) || "radio".equals(inputType);
  }

  /**
   * Synthesize a change event on the focused input element if the value has
   * changed.
   *
   * @param widget the {@link Widget} containing the element
   */
  private static void maybeFireChangeEvent(Widget widget) {
    if (focusedInput == null) {
      return;
    }

    Object newValue = getInputValue(focusedInput);
    if (!newValue.equals(focusedInputValue)) {
      // Save the new value in case it changes again.
      focusedInputValue = newValue;

      // Fire a synthetic event to the input element.
      Element target = focusedInput;
      Event changeEvent = Document.get().createChangeEvent().cast();
      dispatchCellEvent(widget, target, Event.ONCHANGE, changeEvent);
    }
  }

  /**
   * The set of event types that can trigger a change event.
   */
  private final Set<String> changeEventTriggers;

  /**
   * If true, load events have been initialized.
   */
  private boolean loadEventsInitialized;

  public CellBasedWidgetImplTrident() {
    // Initialize the input types.
    if (inputTypes == null) {
      inputTypes = new HashSet<String>();
      inputTypes.add("select");
      inputTypes.add("input");
      inputTypes.add("textarea");
    }

    // Initialize the change event triggers.
    changeEventTriggers = new HashSet<String>();
    changeEventTriggers.add(BrowserEvents.MOUSEUP);
    changeEventTriggers.add(BrowserEvents.MOUSEWHEEL);
  }

  @Override
  public boolean isFocusable(Element elem) {
    return focusableTypes.contains(StringCase.toLower(elem.getTagName()))
        || getTabIndexIfSpecified(elem) >= 0;
  }

  @Override
  public void onBrowserEvent(final Widget widget, Event event) {
    // We need to remove the event listener from the cell now that the event
    // has fired.
    String type = StringCase.toLower(event.getType());
    if (BrowserEvents.FOCUS.equals(type) || BrowserEvents.BLUR.equals(type) || BrowserEvents.CHANGE.equals(type)) {
      EventTarget eventTarget = event.getEventTarget();
      if (Element.is(eventTarget)) {
        Element target = eventTarget.cast();
        if (target != widget.getElement()) {
          DOM.setEventListener(target, null);
        }
      }
    }

    // Update the value of the focused input box.
    if (focusedInput != null && BrowserEvents.CHANGE.equals(type)) {
      focusedInputValue = getInputValue(focusedInput);
    }

    // We might need to fire a synthetic change event on the input element.
    if (focusedInput != null && !focusedInputChangesOnBlurOnly
        && changeEventTriggers.contains(type)) {
      // Defer the change event because the change does not occur until after
      // the events specified above.
      Scheduler.get().scheduleDeferred(new ScheduledCommand() {
        public void execute() {
          maybeFireChangeEvent(widget);
        }
      });
    }
  }

  @Override
  public SafeHtml processHtml(SafeHtml html) {
    // If the widget is listening for load events, we modify the HTML to add the
    // load/error listeners.
    if (loadEventsInitialized && html != null) {
      String moduleName = GWT.getModuleName();
      String listener = "__gwt_CellBasedWidgetImplLoadListeners[\""
          + moduleName + "\"]();";

      String htmlString = html.asString();
      htmlString = htmlString.replaceAll("(<img)([\\s/>])", "<img onload='"
          + listener + "' onerror='" + listener + "'$2");

      // We assert that the resulting string is safe
      html = SafeHtmlUtils.fromTrustedString(htmlString);
    }
    return html;
  }

  @Override
  public void resetFocus(ScheduledCommand command) {
    // IE will not focus an element that was created in this event loop.
    Scheduler.get().scheduleDeferred(command);
  }

  @Override
  protected int sinkEvent(Widget widget, String typeName) {
    if (BrowserEvents.CHANGE.equals(typeName) || BrowserEvents.FOCUS.equals(typeName)
        || BrowserEvents.BLUR.equals(typeName)) {
      // Initialize the focus events.
      if (dispatchFocusEvent == null) {
        initFocusEventSystem();
      }

      // Sink the events required for focus. We use an attribute on the widget
      // to remember whether or not we've sunk the events.
      int eventsToSink = 0;
      Element elem = widget.getElement();
      String attr = "__gwtCellBasedWidgetImplDispatchingFocus";
      if (!"true".equals(elem.getAttribute(attr))) {
        elem.setAttribute(attr, "true");
        sinkFocusEvents(elem);

        // Sink the events that could trigger a change event. Change events
        // are also triggered on blur if the value changes.
        for (String trigger : changeEventTriggers) {
          eventsToSink |= Event.getTypeInt(trigger);
        }
      }
      return eventsToSink;
    } else if (BrowserEvents.LOAD.equals(typeName) || BrowserEvents.ERROR.equals(typeName)) {
      // Initialize the load listener.
      if (!loadEventsInitialized) {
        loadEventsInitialized = true;
        initLoadEvents(GWT.getModuleName());
      }
      return -1;
    } else {
      return super.sinkEvent(widget, typeName);
    }
  }

  /**
   * Get the tab index of an element if the tab index is specified.
   *
   * @param elem the Element
   * @return the tab index, or -1 if not specified
   */
  private native int getTabIndexIfSpecified(Element elem) /*-{
    var attrNode = elem.getAttributeNode('tabIndex');
    return (attrNode != null && attrNode.specified) ? elem.tabIndex : -1;
  }-*/;

  /**
   * Initialize the focus event listener.
   */
  private native void initFocusEventSystem() /*-{
    @com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident::dispatchFocusEvent = $entry(function() {
      @com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident::handleNonBubblingEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
    });
  }-*/;

  /**
   * Initialize load events. We hang a method off of $wnd so we can reference it
   * in the HTML generated for img tags.
   *
   * @param moduleName the module name of the current module
   */
  private native void initLoadEvents(String moduleName) /*-{
    // Initialize an array of listeners. Each module gets its own entry in the
    // array to prevent conflicts on pages with multiple modules.
    if (!$wnd.__gwt_CellBasedWidgetImplLoadListeners) {
      $wnd.__gwt_CellBasedWidgetImplLoadListeners = new Array();
    }

    // Add an entry for the specified module.
    $wnd.__gwt_CellBasedWidgetImplLoadListeners[moduleName] = $entry(function() {
      @com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident::handleNonBubblingEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
    });
  }-*/;

  /**
   * Sink focus events for the specified element.
   *
   * @param elem the element that will receive the events
   */
  private native void sinkFocusEvents(Element elem) /*-{
    elem.attachEvent('onfocusin',
        @com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident::dispatchFocusEvent);
    elem.attachEvent('onfocusout',
        @com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident::dispatchFocusEvent);
  }-*/;
TOP

Related Classes of com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident

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.