All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.user.cellview.client.CellBasedWidgetImplTrident Maven / Gradle / Ivy

/*
 * 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.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.Locale;
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 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 = target.getTagName().toLowerCase(Locale.ROOT);
      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 = InputElement.as(elem).getType().toLowerCase(Locale.ROOT);
    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 changeEventTriggers;

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

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

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

  @Override
  public boolean isFocusable(Element elem) {
    return focusableTypes.contains(elem.getTagName().toLowerCase(Locale.ROOT))
        || 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 = event.getType().toLowerCase(Locale.ROOT);
    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("(])", "




© 2015 - 2024 Weber Informatics LLC | Privacy Policy