/** Add a {@link BindFunction} for a specific css selector. */ public void addBindFunctionForSelector(String cssSelector, BindFunction f) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); if (bindFunctions == null) { bindFunctions = JsObjectArray.create(); bindFunctionBySelector.put(cssSelector, bindFunctions); } bindFunctions.add(f); }
/** Return true if the element is listening for the given eventBit or eventName. */ public boolean hasHandlers(int eventBits, String eventName) { for (int i = 0, j = elementEvents.length(); i < j; i++) { BindFunction function = elementEvents.get(i); if (function.hasEventType(eventBits) || function.isTypeOf(eventName)) { return true; } } return false; }
private void resultsBuild() { selectItems = new SelectParser().parse(selectElement); if (isMultiple && choices > 0) { searchChoices.find("li." + css.searchChoice()).remove(); choices = 0; } else if (!isMultiple) { selectedItem.addClass(css.chznDefault()).find("span").text(defaultText); if (selectElement.getOptions().getLength() <= options.getDisableSearchThreshold()) { container.addClass(css.chznContainerSingleNoSearch()); } else { container.removeClass(css.chznContainerSingleNoSearch()); } } SafeHtmlBuilder content = new SafeHtmlBuilder(); for (int i = 0; i < selectItems.length(); i++) { SelectItem item = selectItems.get(i); if (item.isGroup()) { SafeHtml result = resultAddGroup((GroupItem) item); if (result != null) { content.append(result); } } else { OptionItem optionItem = (OptionItem) item; if (optionItem.isEmpty()) { continue; } SafeHtml optionHtml = resultAddOption(optionItem); if (optionHtml != null) { content.append(optionHtml); } if (optionItem.isSelected() && isMultiple) { choiceBuild(optionItem); } else if (optionItem.isSelected() && !isMultiple) { selectedItem.removeClass(css.chznDefault()).find("span").text(optionItem.getText()); if (allowSingleDeselect) { singleDeselectControlBuild(); } } } } searchFieldDisabled(); showSearchFieldDefault(); searchFieldScale(); searchResults.html(content.toSafeHtml().asString()); }
/** Dispatch an event in this element but changing the type, it's useful for special events. */ public void dispatchEvent(Event event, String eventName) { int etype = Event.getTypeInt(eventName); Object[] handlerData = $(element).data(EVENT_DATA); for (int i = 0, l = elementEvents.length(); i < l; i++) { BindFunction listener = elementEvents.get(i); String namespace = JsUtils.prop(event, "namespace"); boolean matchEV = listener != null && (listener.hasEventType(etype) || listener.isTypeOf(eventName)); boolean matchNS = matchEV && (isNullOrEmpty(namespace) || listener.nameSpace.equals(namespace)); if (matchEV && matchNS) { if (!listener.fire(event, handlerData)) { event.stopPropagation(); event.preventDefault(); } } } }
private JsNodeArray getNotPseudo( JsNodeArray previousMatch, String pseudoValue, JsNodeArray matchingElms) { if (new JsRegexp("(:\\w+[\\w\\-]*)$").test(pseudoValue)) { matchingElms = subtractArray( previousMatch, getElementsByPseudo(previousMatch, pseudoValue.substring(1), "")); } else { pseudoValue = pseudoValue.replace("^\\[#([\\w\\u00C0-\\uFFFF\\-\\_]+)\\]$", "[id=$1]"); JsObjectArray<String> notTag = new JsRegexp("^(\\w+)").exec(pseudoValue); JsObjectArray<String> notClass = new JsRegexp("^\\.([\\w\u00C0-\uFFFF\\-_]+)").exec(pseudoValue); JsObjectArray<String> notAttr = new JsRegexp("\\[(\\w+)(\\^|\\$|\\*|\\||~)?=?([\\w\\u00C0-\\uFFFF\\s\\-_\\.]+)?\\]") .exec(pseudoValue); JsRegexp notRegExp = new JsRegexp( "(^|\\s)" + (JsUtils.truth(notTag) ? notTag.get(1) : JsUtils.truth(notClass) ? notClass.get(1) : "") + "(\\s|$)", "i"); if (JsUtils.truth(notAttr)) { String notAttribute = JsUtils.truth(notAttr.get(3)) ? notAttr.get(3).replace("\\.", "\\.") : null; String notMatchingAttrVal = attrToRegExp(notAttribute, notAttr.get(2)); notRegExp = new JsRegexp(notMatchingAttrVal, "i"); } for (int v = 0, vlen = previousMatch.size(); v < vlen; v++) { Element notElm = previousMatch.getElement(v); Element addElm = null; if (JsUtils.truth(notTag) && !notRegExp.test(notElm.getNodeName())) { addElm = notElm; } else if (JsUtils.truth(notClass) && !notRegExp.test(notElm.getClassName())) { addElm = notElm; } else if (JsUtils.truth(notAttr)) { String att = getAttr(notElm, notAttr.get(1)); if (!JsUtils.truth(att) || !notRegExp.test(att)) { addElm = notElm; } } if (JsUtils.truth(addElm) && !isAdded(addElm)) { setAdded(addElm, true); matchingElms.addNode(addElm); } } } return matchingElms; }
public void bind( int eventbits, String namespace, String eventName, Object data, Function function, int times) { sink(eventbits, eventName); elementEvents.add(new BindFunction(eventbits, eventName, namespace, function, data, times)); }
public void unbind(int eventbits, String namespace, String eventName, Function f) { JsObjectArray<BindFunction> newList = JsObjectArray.createArray().cast(); for (int i = 0; i < elementEvents.length(); i++) { BindFunction listener = elementEvents.get(i); boolean matchNS = isNullOrEmpty(namespace) || listener.nameSpace.equals(namespace); boolean matchEV = eventbits <= 0 || listener.hasEventType(eventbits); boolean matchEVN = matchEV || listener.isTypeOf(eventName); boolean matchFC = f == null || listener.isEquals(f); if (matchNS && matchEV && matchEVN && matchFC) { int currentEventbits = listener.unsink(eventbits); if (currentEventbits == 0) { // the BindFunction doesn't listen anymore on any events continue; } } newList.add(listener); } elementEvents = newList; }
private void liveBitlessEvent( String eventName, String nameSpace, String cssSelector, Object data, Function... funcs) { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); if (liveBindFunction == null) { liveBindFunction = new LiveBindFunction(eventName, "live", data); sink(BITLESS, eventName); elementEvents.add(liveBindFunction); liveBindFunctionByEventName.put(eventName, liveBindFunction); } for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector( cssSelector, new BindFunction(BITLESS, eventName, nameSpace, f, data, -1)); } }
private void resultSelect(Event e) { if (resultHighlight != null) { GQuery high = resultHighlight; String highId = high.attr("id"); resultClearHighlight(); if (isMultiple) { resultDeactivate(high); } else { searchResults.find("." + css.resultSelected()).removeClass(css.resultSelected()); resultSingleSelected = high; selectedItem.removeClass(css.chznDefault()); } high.addClass(css.resultSelected()); int position = Integer.parseInt(highId.substring(highId.lastIndexOf("_") + 1)); OptionItem item = (OptionItem) selectItems.get(position); item.setSelected(true); selectElement.getOptions().getItem(item.getOptionsIndex()).setSelected(true); if (isMultiple) { choiceBuild(item); } else { selectedItem.find("span").text(item.getText()); if (allowSingleDeselect) { singleDeselectControlBuild(); } } if (!e.getMetaKey() || !isMultiple) { resultsHide(); } searchField.val(""); if (isMultiple || currentValue == null || !currentValue.equals($selectElement.val())) { String value = selectElement.getOptions().getItem(item.getOptionsIndex()).getValue(); fireEvent(new ChosenChangeEvent(value, this)); } currentValue = $selectElement.val(); searchFieldScale(); } }
private void resultDeselect(int index) { OptionItem item = (OptionItem) selectItems.get(index); item.setSelected(false); // select option in original element OptionElement option = selectElement.getOptions().getItem(item.getOptionsIndex()); option.setSelected(false); $("#" + containerId + "_o_" + index) .removeClass(css.resultSelected()) .addClass(css.activeResult()) .show(); resultClearHighlight(); winnowResults(); fireEvent(new ChosenChangeEvent(option.getValue(), false, this)); searchFieldScale(); }
private void liveBitEvent( int eventbits, String nameSpace, String cssSelector, Object data, Function... funcs) { for (int i = 0; i < 28; i++) { int event = (int) Math.pow(2, i); if ((eventbits & event) == event) { // is a LiveBindFunction already attached for this kind of event LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(event); if (liveBindFunction == null) { liveBindFunction = new LiveBindFunction(event, "live", data); sink(eventbits, null); elementEvents.add(liveBindFunction); liveBindFunctionByEventType.put(event, liveBindFunction); } for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector( cssSelector, new BindFunction(event, null, nameSpace, f, data, -1)); } } } }
/** Remove the BindFunction associated to this cssSelector. */ public void removeBindFunctionForSelector(String cssSelector, String nameSpace) { if (nameSpace == null) { bindFunctionBySelector.delete(cssSelector); } else { JsObjectArray<BindFunction> functions = bindFunctionBySelector.get(cssSelector); if (functions == null || functions.length() == 0) { return; } JsObjectArray<BindFunction> newFunctions = JsObjectArray.create(); for (int i = 0; i < functions.length(); i++) { BindFunction f = functions.get(i); if (nameSpace != null && !nameSpace.equals(f.nameSpace)) { newFunctions.add(f); } } bindFunctionBySelector.delete(cssSelector); if (newFunctions.length() > 0) { bindFunctionBySelector.put(cssSelector, newFunctions); } } }
/** * This class implements an event queue instance for one Element. The queue instance is configured * as the default event listener in GWT. * * <p>The reference to this queue is stored as a unique variable in the element's DOM. * * <p>The class takes care of calling the appropriate functions for each browser event and it also * calls sinkEvents method. */ public class EventsListener implements EventListener { /** Used for simulating mouseenter and mouseleave events. */ private static class MouseSpecialEvent extends DefaultSpecialEvent { public MouseSpecialEvent(final String type, String delegateType) { super(type, delegateType); handler = new Function() { public boolean f(Event e, Object... arg) { EventTarget eventTarget = e.getCurrentEventTarget(); Element target = eventTarget != null ? eventTarget.<Element>cast() : null; EventTarget relatedEventTarget = e.getRelatedEventTarget(); Element related = relatedEventTarget != null ? relatedEventTarget.<Element>cast() : null; if (related == null || (related != target && !GQuery.contains(target, related))) { getInstance(target).dispatchEvent(e, type); } return true; }; }; } } /** Utility class to split a list of events with or without namespaces. */ public static class EventName { public final String nameSpace; public final String eventName; public EventName(String n, String e) { nameSpace = n; eventName = e; } public static List<EventName> split(String events) { List<EventName> ret = new ArrayList<EventName>(); String[] parts = events.split("[\\s,]+"); for (String event : parts) { String[] tmp = event.split("\\.", 2); String eventName = tmp[0]; String nameSpace = tmp.length > 1 ? tmp[1] : null; ret.add(new EventName(nameSpace, eventName)); } return ret; } } /** The function used per each element event. */ private static class BindFunction { Object data; Function function; String nameSpace; int times; int type; String eventName; BindFunction( int type, String eventName, String nameSpace, Function function, Object data, int times) { this.times = times; this.eventName = eventName; this.type = type; this.function = function; this.data = data; this.nameSpace = nameSpace != null ? nameSpace : ""; } public boolean fire(Event event, Object[] eventData) { if (times != 0) { times--; Object[] arguments; eventData = eventData != null ? eventData : new Object[0]; // The argument of the function will be first the data attached to the handler then the // data attached to the event. if (data != null) { Object[] handlerData = data.getClass().isArray() ? (Object[]) data : new Object[] {data}; arguments = new Object[handlerData.length + eventData.length]; System.arraycopy(handlerData, 0, arguments, 0, handlerData.length); System.arraycopy(eventData, 0, arguments, handlerData.length, eventData.length); } else { arguments = eventData; } // FIXME(manolo): figure out when this is null, and fix or comment it. if (function != null) { return function.fe(event, arguments); } } return true; } public boolean hasEventType(int etype) { return type != BITLESS && etype != BITLESS && (type & etype) != 0; } public boolean isTypeOf(String eName) { return eventName != null && eventName.equalsIgnoreCase(eName); } /** * Remove a set of events. The bind function will not be fire anymore for those events * * @param eventBits the set of events to unsink */ public int unsink(int eventBits) { if (eventBits <= 0) { type = 0; } else { type = type & ~eventBits; } return type; } @Override public String toString() { return "bind function for event type " + (eventName != null ? eventName : "" + type); } public boolean isEquals(Function f) { assert f != null : "function f cannot be null"; return f.equals(function); } } /** {@link BindFunction} used for live() method. */ private static class LiveBindFunction extends BindFunction { JsNamedArray<JsObjectArray<BindFunction>> bindFunctionBySelector; LiveBindFunction(String eventName, String namespace, Object data) { super(BITLESS, eventName, namespace, null, data, -1); clean(); } LiveBindFunction(int type, String namespace, Object data) { super(type, null, namespace, null, data, -1); clean(); } /** Add a {@link BindFunction} for a specific css selector. */ public void addBindFunctionForSelector(String cssSelector, BindFunction f) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); if (bindFunctions == null) { bindFunctions = JsObjectArray.create(); bindFunctionBySelector.put(cssSelector, bindFunctions); } bindFunctions.add(f); } public void clean() { bindFunctionBySelector = JsNamedArray.create(); } @Override public boolean fire(Event event, Object[] eventData) { if (isEmpty()) { return true; } // first element where the event was fired Element eventTarget = getEventTarget(event); // last element where the event was dispatched on Element liveContextElement = getCurrentEventTarget(event); if (eventTarget == null || liveContextElement == null) { return true; } // Compute the live selectors which respond to this event type List<String> validSelectors = new ArrayList<String>(); for (String cssSelector : bindFunctionBySelector.keys()) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { validSelectors.add(cssSelector); break; } } } // Create a structure of elements which matches the selectors JsNamedArray<NodeList<Element>> realCurrentTargetBySelector = $(eventTarget).closest(validSelectors.toArray(new String[0]), liveContextElement); // nothing matches the selectors if (realCurrentTargetBySelector.length() == 0) { return true; } Element stopElement = null; GqEvent gqEvent = GqEvent.create(event); for (String cssSelector : realCurrentTargetBySelector.keys()) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { NodeList<Element> n = realCurrentTargetBySelector.get(cssSelector); for (int j = 0; n != null && j < n.getLength(); j++) { Element element = n.getItem(j); // When an event fired in an element stops bubbling we have to fire also all other // handlers for this element bound to this element if (stopElement == null || element.equals(stopElement)) { gqEvent.setCurrentElementTarget(element); // data eventData = $(element).data(EVENT_DATA); if (!f.fire(gqEvent, eventData)) { stopElement = element; } } } } } } // trick to reset the right currentTarget on the original event on ie gqEvent.setCurrentElementTarget(liveContextElement); return stopElement == null; } /** Remove the BindFunction associated to this cssSelector. */ public void removeBindFunctionForSelector(String cssSelector, String nameSpace) { if (nameSpace == null) { bindFunctionBySelector.delete(cssSelector); } else { JsObjectArray<BindFunction> functions = bindFunctionBySelector.get(cssSelector); if (functions == null || functions.length() == 0) { return; } JsObjectArray<BindFunction> newFunctions = JsObjectArray.create(); for (int i = 0; i < functions.length(); i++) { BindFunction f = functions.get(i); if (nameSpace != null && !nameSpace.equals(f.nameSpace)) { newFunctions.add(f); } } bindFunctionBySelector.delete(cssSelector); if (newFunctions.length() > 0) { bindFunctionBySelector.put(cssSelector, newFunctions); } } } /** * Tell if no {@link BindFunction} are linked to this object. * * @return */ public boolean isEmpty() { return bindFunctionBySelector.length() == 0; } @Override public String toString() { return "live bind function for selector " + bindFunctionBySelector.<JsCache>cast().tostring(); } /** * Return the element whose the listener fired last. It represent the context element where the * {@link LiveBindFunction} was binded. */ private Element getCurrentEventTarget(Event e) { EventTarget currentEventTarget = e.getCurrentEventTarget(); if (!Element.is(currentEventTarget)) { return null; } return Element.as(currentEventTarget); } /** Return the element that was the actual target of the element. */ private Element getEventTarget(Event e) { EventTarget eventTarget = e.getEventTarget(); if (!Element.is(eventTarget)) { return null; } return Element.as(eventTarget); } } public static final String EVENT_DATA = "___event_datas"; public static final int BITLESS = -1; public static String MOUSEENTER = "mouseenter"; public static String MOUSELEAVE = "mouseleave"; public static Map<String, SpecialEvent> special; static { // Register some special events which already exist in jQuery special = new HashMap<String, SpecialEvent>(); special.put(MOUSEENTER, new MouseSpecialEvent(MOUSEENTER, "mouseover")); special.put(MOUSELEAVE, new MouseSpecialEvent(MOUSELEAVE, "mouseout")); } public static void clean(Element e) { EventsListener ret = getGQueryEventListener(e); if (ret != null) { ret.clean(); } } public static EventsListener getInstance(Element e) { EventsListener ret = getGQueryEventListener(e); return ret != null ? ret : new EventsListener(e); } /** * We have to set the gQuery event listener to the element again when the element is a widget, * because when GWT detaches a widget it removes the event listener. */ public static void rebind(Element e) { EventsListener ret = getGQueryEventListener(e); if (ret != null) { DOM.setEventListener((com.google.gwt.user.client.Element) e, ret); } } private static native void cleanGQListeners(Element elem) /*-{ if (elem.__gwtlistener) { @com.google.gwt.user.client.DOM::setEventListener(*)(elem, elem.__gwtlistener); } elem.__gwtlistener = elem.__gqueryevent = elem.__gquery = null; }-*/; private static native EventsListener getGQueryEventListener(Element elem) /*-{ return elem.__gqueryevent; }-*/; private static native EventListener getGwtEventListener(Element elem) /*-{ return elem.__gwtlistener; }-*/; private static native void init(Element elem, EventsListener gqevent) /*-{ elem.__gwtlistener = @com.google.gwt.user.client.DOM::getEventListener(*)(elem); elem.__gqueryevent = gqevent; // Someone has reported that in IE the init can be called multiple times // causing a loop. We need some test to demonstrate this behaviour though. // Anyway we check this condition to avoid looping if (elem.__gwtlistener == gqevent) elem.__gwtlistener = null; }-*/; private static native void sinkBitlessEvent(Element elem, String name) /*-{ if (!elem.__gquery) elem.__gquery = []; if (elem.__gquery[name]) return; elem.__gquery[name] = true; var handle = function(event) { elem.__gqueryevent.@com.google.gwt.query.client.plugins.events.EventsListener::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(event); }; if (elem.addEventListener) elem.addEventListener(name, handle, true); else elem.attachEvent("on" + name, handle); }-*/; int eventBits = 0; double lastEvnt = 0; String lastType = ""; private Element element; private JsObjectArray<BindFunction> elementEvents = JsObjectArray.createArray().cast(); private JsMap<Integer, LiveBindFunction> liveBindFunctionByEventType = JsMap.create(); private JsMap<String, LiveBindFunction> liveBindFunctionByEventName = JsMap.create(); private EventsListener(Element element) { this.element = element; init(element, this); } public void bind(int eventbits, final Object data, Function... funcs) { bind(eventbits, null, data, funcs); } public void bind(int eventbits, final Object data, final Function function, int times) { bind(eventbits, null, null, data, function, times); } public void bind(int eventbits, String name, final Object data, Function... funcs) { for (Function function : funcs) { bind(eventbits, name, null, data, function, -1); } } public void bind(int eventbits, String namespace, Object data, Function function, int times) { bind(eventbits, namespace, null, data, function, times); } public void bind(String events, final Object data, Function... funcs) { if (funcs.length == 0 || funcs[0] == null) { unbind(events, null); } for (EventName ev : EventName.split(events)) { SpecialEvent hook = special.get(ev.eventName); boolean bind = hook == null || hook.setup(element) == false; for (Function function : funcs) { int b = Event.getTypeInt(ev.eventName); if (bind) { bind(b, ev.nameSpace, ev.eventName, data, function, -1); } if (hook != null) { hook.add(element, ev.eventName, ev.nameSpace, data, function); } } } } public void bind( int eventbits, String namespace, String eventName, Object data, Function function, int times) { sink(eventbits, eventName); elementEvents.add(new BindFunction(eventbits, eventName, namespace, function, data, times)); } public void die(String events, String cssSelector) { for (EventName ev : EventName.split(events)) { SpecialEvent hook = special.get(ev.eventName); boolean unbind = hook == null || hook.tearDown(element) == false; if (unbind) { die(Event.getTypeInt(ev.eventName), ev.nameSpace, ev.eventName, cssSelector); } if (hook != null) { hook.remove(element, ev.eventName, ev.nameSpace, null); } } } public void die(int eventbits, String nameSpace, String eventName, String cssSelector) { if (eventbits <= 0) { if (eventName != null && eventName.length() > 0) { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eventName, nameSpace); } else { // if eventbits == -1 and eventName is null, remove all event handlers for this selector for (String k : liveBindFunctionByEventType.keys()) { int bits = Integer.parseInt(k); LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(bits); maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, bits, null, nameSpace); } for (String k : liveBindFunctionByEventName.keys()) { int realKey = Integer.parseInt(k); LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(realKey); if (liveBindFunction != null) { String eName = liveBindFunction.eventName; maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, BITLESS, eName, nameSpace); } } } } else { LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(eventbits); maybeRemoveLiveBindFunction(liveBindFunction, cssSelector, eventbits, null, nameSpace); } } private void maybeRemoveLiveBindFunction( LiveBindFunction liveBindFunction, String cssSelector, int eventbits, String eventName, String nameSpace) { if (liveBindFunction != null) { liveBindFunction.removeBindFunctionForSelector(cssSelector, nameSpace); if (liveBindFunction.isEmpty()) { if (eventbits != BITLESS) { liveBindFunctionByEventType.remove(eventbits); } else { liveBindFunctionByEventName.remove(eventName); } } } } /** Dispatch an event in this element. */ public void dispatchEvent(Event event) { dispatchEvent(event, event.getType()); } /** Dispatch an event in this element but changing the type, it's useful for special events. */ public void dispatchEvent(Event event, String eventName) { int etype = Event.getTypeInt(eventName); Object[] handlerData = $(element).data(EVENT_DATA); for (int i = 0, l = elementEvents.length(); i < l; i++) { BindFunction listener = elementEvents.get(i); String namespace = JsUtils.prop(event, "namespace"); boolean matchEV = listener != null && (listener.hasEventType(etype) || listener.isTypeOf(eventName)); boolean matchNS = matchEV && (isNullOrEmpty(namespace) || listener.nameSpace.equals(namespace)); if (matchEV && matchNS) { if (!listener.fire(event, handlerData)) { event.stopPropagation(); event.preventDefault(); } } } } /** * Return the original gwt EventListener associated with this element, before gquery replaced it * to introduce its own event handler. */ public EventListener getOriginalEventListener() { return getGwtEventListener(element); } public void live(String events, String cssSelector, Object data, Function... funcs) { for (EventName ev : EventName.split(events)) { SpecialEvent hook = special.get(ev.eventName); boolean bind = hook == null || hook.setup(element) == false; for (Function function : funcs) { int b = Event.getTypeInt(ev.eventName); if (bind) { live(b, ev.nameSpace, ev.eventName, cssSelector, data, function); } if (hook != null) { hook.add(element, ev.eventName, ev.nameSpace, data, function); } } } } public void live( int eventbits, String nameSpace, String eventName, String cssSelector, Object data, Function... funcs) { if (eventbits != BITLESS) { liveBitEvent(eventbits, nameSpace, cssSelector, data, funcs); } else { liveBitlessEvent(eventName, nameSpace, cssSelector, data, funcs); } } private void liveBitlessEvent( String eventName, String nameSpace, String cssSelector, Object data, Function... funcs) { LiveBindFunction liveBindFunction = liveBindFunctionByEventName.get(eventName); if (liveBindFunction == null) { liveBindFunction = new LiveBindFunction(eventName, "live", data); sink(BITLESS, eventName); elementEvents.add(liveBindFunction); liveBindFunctionByEventName.put(eventName, liveBindFunction); } for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector( cssSelector, new BindFunction(BITLESS, eventName, nameSpace, f, data, -1)); } } private void liveBitEvent( int eventbits, String nameSpace, String cssSelector, Object data, Function... funcs) { for (int i = 0; i < 28; i++) { int event = (int) Math.pow(2, i); if ((eventbits & event) == event) { // is a LiveBindFunction already attached for this kind of event LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(event); if (liveBindFunction == null) { liveBindFunction = new LiveBindFunction(event, "live", data); sink(eventbits, null); elementEvents.add(liveBindFunction); liveBindFunctionByEventType.put(event, liveBindFunction); } for (Function f : funcs) { liveBindFunction.addBindFunctionForSelector( cssSelector, new BindFunction(event, null, nameSpace, f, data, -1)); } } } } public void onBrowserEvent(Event event) { double now = Duration.currentTimeMillis(); // Workaround for Issue_20 if (lastType.equals(event.getType()) && now - lastEvnt < 10 && "body".equalsIgnoreCase(element.getTagName())) { return; } lastEvnt = now; lastType = event.getType(); // Execute the original Gwt listener if (getOriginalEventListener() != null && getOriginalEventListener() != this) { getOriginalEventListener().onBrowserEvent(event); } dispatchEvent(event); } public void unbind(int eventbits) { unbind(eventbits, null, null, null); } public void unbind(int eventbits, String namespace, String eventName, Function f) { JsObjectArray<BindFunction> newList = JsObjectArray.createArray().cast(); for (int i = 0; i < elementEvents.length(); i++) { BindFunction listener = elementEvents.get(i); boolean matchNS = isNullOrEmpty(namespace) || listener.nameSpace.equals(namespace); boolean matchEV = eventbits <= 0 || listener.hasEventType(eventbits); boolean matchEVN = matchEV || listener.isTypeOf(eventName); boolean matchFC = f == null || listener.isEquals(f); if (matchNS && matchEV && matchEVN && matchFC) { int currentEventbits = listener.unsink(eventbits); if (currentEventbits == 0) { // the BindFunction doesn't listen anymore on any events continue; } } newList.add(listener); } elementEvents = newList; } /** Return true if the element is listening for the given eventBit or eventName. */ public boolean hasHandlers(int eventBits, String eventName) { for (int i = 0, j = elementEvents.length(); i < j; i++) { BindFunction function = elementEvents.get(i); if (function.hasEventType(eventBits) || function.isTypeOf(eventName)) { return true; } } return false; } private static boolean isNullOrEmpty(String s) { return s == null || s.isEmpty(); } public void unbind(String events, Function f) { for (EventName ev : EventName.split(events)) { SpecialEvent hook = special.get(ev.eventName); boolean unbind = hook == null || hook.tearDown(element) == false; if (unbind) { unbind(Event.getTypeInt(ev.eventName), ev.nameSpace, ev.eventName, f); } if (hook != null) { hook.remove(element, ev.eventName, ev.nameSpace, f); } } } public void clean() { cleanGQListeners(element); elementEvents = JsObjectArray.createArray().cast(); liveBindFunctionByEventType = JsMap.create(); eventBits = 0; } private void sink(int eventbits, String eventName) { // ensure that the gwtQuery's event listener is set as event listener of the element DOM.setEventListener((com.google.gwt.user.client.Element) element, this); if (eventbits != BITLESS) { eventBits |= eventbits; if ((eventBits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS && JsUtils.isElement(element) && element.getAttribute("tabIndex").length() == 0) { element.setAttribute("tabIndex", "0"); } DOM.sinkEvents( (com.google.gwt.user.client.Element) element, eventBits | DOM.getEventsSunk((com.google.gwt.user.client.Element) element)); } else { sinkBitlessEvent(element, eventName); } } public void cleanEventDelegation() { for (String k : liveBindFunctionByEventType.keys()) { LiveBindFunction function = liveBindFunctionByEventType.<JsCache>cast().get(k); function.clean(); } } }
@Override public boolean fire(Event event, Object[] eventData) { if (isEmpty()) { return true; } // first element where the event was fired Element eventTarget = getEventTarget(event); // last element where the event was dispatched on Element liveContextElement = getCurrentEventTarget(event); if (eventTarget == null || liveContextElement == null) { return true; } // Compute the live selectors which respond to this event type List<String> validSelectors = new ArrayList<String>(); for (String cssSelector : bindFunctionBySelector.keys()) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { validSelectors.add(cssSelector); break; } } } // Create a structure of elements which matches the selectors JsNamedArray<NodeList<Element>> realCurrentTargetBySelector = $(eventTarget).closest(validSelectors.toArray(new String[0]), liveContextElement); // nothing matches the selectors if (realCurrentTargetBySelector.length() == 0) { return true; } Element stopElement = null; GqEvent gqEvent = GqEvent.create(event); for (String cssSelector : realCurrentTargetBySelector.keys()) { JsObjectArray<BindFunction> bindFunctions = bindFunctionBySelector.get(cssSelector); for (int i = 0; bindFunctions != null && i < bindFunctions.length(); i++) { BindFunction f = bindFunctions.get(i); if (f.hasEventType(event.getTypeInt()) || f.isTypeOf(event.getType())) { NodeList<Element> n = realCurrentTargetBySelector.get(cssSelector); for (int j = 0; n != null && j < n.getLength(); j++) { Element element = n.getItem(j); // When an event fired in an element stops bubbling we have to fire also all other // handlers for this element bound to this element if (stopElement == null || element.equals(stopElement)) { gqEvent.setCurrentElementTarget(element); // data eventData = $(element).data(EVENT_DATA); if (!f.fire(gqEvent, eventData)) { stopElement = element; } } } } } } // trick to reset the right currentTarget on the original event on ie gqEvent.setCurrentElementTarget(liveContextElement); return stopElement == null; }
protected static Sequence getSequence(String expression) { int start = 0, add = 2, max = -1, modVal = -1; JsRegexp expressionRegExp = new JsRegexp( "^((odd|even)|([1-9]\\d*)|((([1-9]\\d*)?)n((\\+|\\-)(\\d+))?)|(\\-(([1-9]\\d*)?)n\\+(\\d+)))$"); JsObjectArray<String> pseudoValue = expressionRegExp.exec(expression); if (!truth(pseudoValue)) { return null; } else { if (truth(pseudoValue.get(2))) { // odd or even start = (eq(pseudoValue.get(2), "odd")) ? 1 : 2; modVal = (start == 1) ? 1 : 0; } else if (JsUtils.truth(pseudoValue.get(3))) { // single digit start = Integer.parseInt(pseudoValue.get(3), 10); add = 0; max = start; } else if (truth(pseudoValue.get(4))) { // an+b add = truth(pseudoValue.get(6)) ? Integer.parseInt(pseudoValue.get(6), 10) : 1; start = truth(pseudoValue.get(7)) ? Integer.parseInt( (pseudoValue.get(8).charAt(0) == '+' ? "" : pseudoValue.get(8)) + pseudoValue.get(9), 10) : 0; while (start < 1) { start += add; } modVal = (start > add) ? (start - add) % add : ((start == add) ? 0 : start); } else if (truth(pseudoValue.get(10))) { // -an+b add = truth(pseudoValue.get(12)) ? Integer.parseInt(pseudoValue.get(12), 10) : 1; start = max = Integer.parseInt(pseudoValue.get(13), 10); while (start > add) { start -= add; } modVal = (max > add) ? (max - add) % add : ((max == add) ? 0 : max); } } Sequence s = new Sequence(); s.start = start; s.add = add; s.max = max; s.modVal = modVal; return s; }
public NodeList<Element> select(String sel, Node ctx) { String selectors[] = sel.replace("\\s*(,)\\s*", "$1").split(","); boolean identical = false; JsNodeArray elm = JsNodeArray.create(); for (int a = 0, len = selectors.length; a < len; a++) { if (a > 0) { identical = false; for (int b = 0, bl = a; b < bl; b++) { if (JsUtils.eq(selectors[a], selectors[b])) { identical = true; break; } } if (identical) { continue; } } String currentRule = selectors[a]; JsObjectArray<String> cssSelectors = selectorSplitRegExp.match(currentRule); JsNodeArray prevElem = JsNodeArray.create(ctx); for (int i = 0, slen = cssSelectors.length(); i < slen; i++) { JsNodeArray matchingElms = JsNodeArray.create(); String rule = cssSelectors.get(i); if (i > 0 && childOrSiblingRefRegExp.test(rule)) { JsObjectArray<String> childOrSiblingRef = childOrSiblingRefRegExp.exec(rule); if (JsUtils.truth(childOrSiblingRef)) { JsObjectArray<String> nextTag = new JsRegexp("^\\w+").exec(cssSelectors.get(i + 1)); JsRegexp nextRegExp = null; String nextTagStr = null; if (JsUtils.truth(nextTag)) { nextTagStr = nextTag.get(0); nextRegExp = new JsRegexp("(^|\\s)" + nextTagStr + "(\\s|$)", "i"); } for (int j = 0, jlen = prevElem.size(); j < jlen; j++) { Node prevRef = prevElem.getNode(j); String ref = childOrSiblingRef.get(0); if (JsUtils.eq(">", ref)) { getDescendantNodes(matchingElms, nextTagStr, prevRef); } else if (JsUtils.eq("+", ref)) { getSiblingNodes(matchingElms, nextTag, nextRegExp, prevRef); } else if (JsUtils.eq("~", ref)) { getGeneralSiblingNodes(matchingElms, nextTag, nextRegExp, prevRef); } } prevElem = matchingElms; clearAdded(prevElem); rule = cssSelectors.get(++i); if (new JsRegexp("^\\w+$").test(rule)) { continue; } setSkipTag(prevElem, true); } } JsObjectArray<String> cssSelector = cssSelectorRegExp.exec(rule); SplitRule splitRule = new SplitRule( !JsUtils.truth(cssSelector.get(1)) || JsUtils.eq(cssSelector.get(3), "*") ? "*" : cssSelector.get(1), !JsUtils.eq(cssSelector.get(3), "*") ? cssSelector.get(2) : null, cssSelector.get(4), cssSelector.get(6), cssSelector.get(10)); if (JsUtils.truth(splitRule.id)) { Element domelem = Document.get().getElementById(splitRule.id.substring(1)); if (JsUtils.truth(domelem)) { matchingElms = JsNodeArray.create(domelem); } prevElem = matchingElms; } else if (JsUtils.truth(splitRule.tag) && !isSkipped(prevElem)) { if (i == 0 && matchingElms.size() == 0 && prevElem.size() == 1) { prevElem = matchingElms = JsNodeArray.create(getElementsByTagName(splitRule.tag, prevElem.getNode(0))); } else { NodeList<Element> tagCollectionMatches; for (int l = 0, ll = prevElem.size(); l < ll; l++) { tagCollectionMatches = getElementsByTagName(splitRule.tag, prevElem.getNode(l)); for (int m = 0, mlen = tagCollectionMatches.getLength(); m < mlen; m++) { Node tagMatch = tagCollectionMatches.getItem(m); if (!isAdded(tagMatch)) { setAdded(tagMatch, true); matchingElms.addNode(tagMatch); } } } prevElem = matchingElms; clearAdded(prevElem); } if (matchingElms.size() == 0) { break; } setSkipTag(prevElem, false); if (JsUtils.truth(splitRule.allClasses)) { String[] allClasses = splitRule.allClasses.replaceFirst("^\\.", "").split("\\."); JsRegexp[] regExpClassNames = new JsRegexp[allClasses.length]; for (int n = 0, nl = allClasses.length; n < nl; n++) { regExpClassNames[n] = new JsRegexp("(^|\\s)" + allClasses[n] + "(\\s|$)"); } JsNodeArray matchingClassElms = JsNodeArray.create(); for (int o = 0, olen = prevElem.size(); o < olen; o++) { Element current = prevElem.getElement(o); String elmClass = current.getClassName(); boolean addElm = false; if (JsUtils.truth(elmClass) && !isAdded(current)) { for (int p = 0, pl = regExpClassNames.length; p < pl; p++) { addElm = regExpClassNames[p].test(elmClass); if (!addElm) { break; } } if (addElm) { setAdded(current, true); matchingClassElms.addNode(current); } } } clearAdded(prevElem); prevElem = matchingElms = matchingClassElms; } if (JsUtils.truth(splitRule.allAttr)) { JsObjectArray<String> allAttr = JsRegexp.match("\\[[^\\]]+\\]", "g", splitRule.allAttr); JsRegexp[] regExpAttributes = new JsRegexp[allAttr.length()]; String[] regExpAttributesStr = new String[allAttr.length()]; JsRegexp attributeMatchRegExp = new JsRegexp("(\\w+)(\\^|\\$|\\*|\\||~)?=?[\"']?([\\w\u00C0-\uFFFF\\s\\-_\\.]+)?"); for (int q = 0, ql = allAttr.length(); q < ql; q++) { JsObjectArray<String> attributeMatch = attributeMatchRegExp.exec(allAttr.get(q)); String attributeValue = JsUtils.truth(attributeMatch.get(3)) ? attributeMatch.get(3).replaceAll("\\.", "\\.") : null; String attrVal = attrToRegExp(attributeValue, (JsUtils.or(attributeMatch.get(2), null))); regExpAttributes[q] = (JsUtils.truth(attrVal) ? new JsRegexp(attrVal) : null); regExpAttributesStr[q] = attributeMatch.get(1); } JsNodeArray matchingAttributeElms = JsNodeArray.create(); for (int r = 0, rlen = matchingElms.size(); r < rlen; r++) { Element current = matchingElms.getElement(r); boolean addElm = false; for (int s = 0, sl = regExpAttributes.length; s < sl; s++) { addElm = false; JsRegexp attributeRegexp = regExpAttributes[s]; String currentAttr = getAttr(current, regExpAttributesStr[s]); if (JsUtils.truth(currentAttr) && currentAttr.length() != 0) { if (attributeRegexp == null || attributeRegexp.test(currentAttr)) { addElm = true; } } if (!addElm) { break; } } if (addElm) { matchingAttributeElms.addNode(current); } } prevElem = matchingElms = matchingAttributeElms; } if (JsUtils.truth(splitRule.allPseudos)) { JsRegexp pseudoSplitRegExp = new JsRegexp(":(\\w[\\w\\-]*)(\\(([^\\)]+)\\))?"); JsObjectArray<String> allPseudos = JsRegexp.match("(:\\w+[\\w\\-]*)(\\([^\\)]+\\))?", "g", splitRule.allPseudos); for (int t = 0, tl = allPseudos.length(); t < tl; t++) { JsObjectArray<String> pseudo = pseudoSplitRegExp.match(allPseudos.get(t)); String pseudoClass = JsUtils.truth(pseudo.get(1)) ? pseudo.get(1).toLowerCase() : null; String pseudoValue = JsUtils.truth(pseudo.get(3)) ? pseudo.get(3) : null; matchingElms = getElementsByPseudo(matchingElms, pseudoClass, pseudoValue); clearAdded(matchingElms); } prevElem = matchingElms; } } } elm.pushAll(prevElem); } return JsUtils.unique(elm.<JsArray<Element>>cast()).cast(); }
public void clean() { cleanGQListeners(element); elementEvents = JsObjectArray.createArray().cast(); liveBindFunctionByEventType = JsMap.create(); eventBits = 0; }
private void winnowResults() { noResultClear(); int results = 0; String searchText = defaultText.equals(searchField.val()) ? "" : searchField.val().trim(); searchText = SafeHtmlUtils.htmlEscape(searchText); String regexAnchor = options.isSearchContains() ? "" : "^"; // escape reg exp special chars String escapedSearchText = regExpChars.replace(searchText, "\\$&"); String test2 = "test"; test2.substring(1); RegExp regex = RegExp.compile(regexAnchor + escapedSearchText, "i"); RegExp zregex = RegExp.compile("(" + escapedSearchText + ")", "i"); for (int i = 0; i < selectItems.length(); i++) { SelectItem item = selectItems.get(i); if (item.isDisabled() || item.isEmpty()) { continue; } if (item.isGroup()) { $('#' + item.getDomId()).css("display", "none"); } else { OptionItem option = (OptionItem) item; if (!(isMultiple && option.isSelected())) { boolean found = false; String resultId = option.getDomId(); GQuery result = $("#" + resultId); String optionContent = option.getHtml(); if (regex.test(optionContent)) { found = true; results++; } else if (optionContent.indexOf(" ") >= 0 || optionContent.indexOf("[") == 0) { String[] parts = optionContent.replaceAll("\\[|\\]", "").split(" "); for (String part : parts) { if (regex.test(part)) { found = true; results++; } } } if (found) { String text; if (searchText.length() > 0) { text = zregex.replace(optionContent, "<em>$1</em>"); } else { text = optionContent; } result.html(text); resultActivate(result); if (option.getGroupArrayIndex() != -1) { $("#" + selectItems.get(option.getGroupArrayIndex()).getDomId()) .css("display", "list-item"); } } else { if (resultHighlight != null && resultId.equals(resultHighlight.attr("id"))) { resultClearHighlight(); } resultDeactivate(result); } } } } if (results < 1 && searchText.length() > 0) { noResults(searchText); } else { winnowResultsSetHighlight(); } }