001: /*
002: * GWT-Ext Widget Library
003: * Copyright(c) 2007-2008, GWT-Ext.
004: * licensing@gwt-ext.com
005: *
006: * http://www.gwt-ext.com/license
007: */
008:
009: package com.gwtext.client.core;
010:
011: import com.google.gwt.core.client.JavaScriptObject;
012: import com.google.gwt.user.client.Element;
013: import com.gwtext.client.util.JavaScriptObjectHelper;
014:
015: /**
016: * Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
017: * <p>
018: * DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
019: * <p/>
020: * <p>
021: * All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
022: * </p>
023: * <h4>Element Selectors:</h4>
024: * <ul class="list">
025: * <li> <b>*</b> any element</li>
026: * <li> <b>E</b> an element with the tag E</li>
027: * <li> <b>E F</b> All descendent elements of E that have the tag F</li>
028: * <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
029: * <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
030: * <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
031: * </ul>
032: * <h4>Attribute Selectors:</h4>
033: * <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
034: * <ul class="list">
035: * <li> <b>E[foo]</b> has an attribute "foo"</li>
036: * <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
037: * <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
038: * <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
039: * <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
040: * <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
041: * <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
042: * </ul>
043: * <h4>Pseudo Classes:</h4>
044: * <ul class="list">
045: * <li> <b>E:first-child</b> E is the first child of its parent</li>
046: * <li> <b>E:last-child</b> E is the last child of its parent</li>
047: * <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
048: * <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
049: * <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
050: * <li> <b>E:only-child</b> E is the only child of its parent</li>
051: * <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
052: * <li> <b>E:first</b> the first E in the resultset</li>
053: * <li> <b>E:last</b> the last E in the resultset</li>
054: * <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
055: * <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
056: * <li> <b>E:even</b> shortcut for :nth-child(even)</li>
057: * <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
058: * <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
059: * <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
060: * <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
061: * <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
062: * <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
063: * </ul>
064: * <h4>CSS Value Selectors:</h4>
065: * <ul class="list">
066: * <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
067: * <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
068: * <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
069: * <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
070: * <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
071: * <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
072: * </ul><br><br>
073: */
074: public class DomQuery {
075:
076: public static class SelectorType {
077: private String type;
078:
079: private SelectorType(String type) {
080: this .type = type;
081: }
082:
083: public String getType() {
084: return type;
085: }
086: }
087:
088: public static SelectorType SELECT = new SelectorType("select");
089: public static SelectorType SIMPLE = new SelectorType("simple");
090:
091: /**
092: * Compiles a selector/xpath query into a reusable function. The returned function takes one parameter "root" (optional),
093: * which is the context node from where the query should start.
094: *
095: * @param selector the selector/xpath query
096: * @return the reusable dom query function
097: */
098: public static DomQueryFunction compile(String selector) {
099: return compile(selector, null);
100: }
101:
102: private static native JavaScriptObject doCompile(String selector,
103: String selectorType) /*-{
104: if(selectorType == null) {
105: return $wnd.Ext.DomQuery.compile(selector);
106: } else {
107: return $wnd.Ext.DomQuery.compile(selector, selectorType);
108: }
109:
110: }-*/;
111:
112: /**
113: * Compiles a selector/xpath query into a reusable function. The returned function takes one parameter "root" (optional),
114: * which is the context node from where the query should start.
115: *
116: * @param selector the selector/xpath query
117: * @param type either {@link #SELECT} (the default) or {@link #SIMPLE} for a simple selector match
118: * @return the reusable dom query function
119: */
120: public static DomQueryFunction compile(String selector,
121: SelectorType type) {
122:
123: String selectorType = type == null ? null : type.getType();
124: final JavaScriptObject fn = doCompile(selector, selectorType);
125:
126: return new DomQueryFunction() {
127: public Element[] query() {
128: JavaScriptObject els = doQuery(fn);
129: return JavaScriptObjectHelper.toElementArray(els);
130:
131: }
132:
133: private native JavaScriptObject doQuery(JavaScriptObject fn) /*-{
134: return fn();
135: }-*/;
136:
137: public Element[] query(Element root) {
138: JavaScriptObject els = doQuery(fn, root);
139: return JavaScriptObjectHelper.toElementArray(els);
140: }
141:
142: private native JavaScriptObject doQuery(
143: JavaScriptObject fn, Element root) /*-{
144: return fn(root);
145: }-*/;
146: };
147: }
148:
149: /**
150: * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child).
151: *
152: * @param els an array of elements to filter
153: * @param selector the simple selector to test
154: * @param nonMatches if true, it returns the elements that DON'T match the selector instead of the ones that match
155: * @return filtered element array
156: */
157: public static Element[] filter(Element[] els, String selector,
158: boolean nonMatches) {
159: JavaScriptObject jsElements = doFilter(JavaScriptObjectHelper
160: .convertToJavaScriptArray(els), selector, nonMatches);
161: return JavaScriptObjectHelper.toElementArray(jsElements);
162: }
163:
164: private static native JavaScriptObject doFilter(
165: JavaScriptObject els, String selector, boolean nonMatches) /*-{
166: return $wnd.Ext.DomQuery.filter(els, selector, nonMatches);
167: }-*/;
168:
169: /**
170: * Returns true if the passed element match the passed simple selector (e.g. div.some-class or span:first-child).
171: *
172: * @param id the element id
173: * @param selector the simple selector to test
174: * @return true if matched
175: */
176: public static native boolean is(String id, String selector)/*-{
177: return $wnd.Ext.DomQuery.is(id, selector);
178: }-*/;
179:
180: /**
181: * Returns true if the passed element match the passed simple selector (e.g. div.some-class or span:first-child).
182: *
183: * @param el the element
184: * @param selector the simple selector to test
185: * @return true if matched
186: */
187: public static native boolean is(Element el, String selector)/*-{
188: return $wnd.Ext.DomQuery.is(el, selector);
189: }-*/;
190:
191: /**
192: * Returns true if the passed elements match the passed simple selector (e.g. div.some-class or span:first-child).
193: *
194: * @param els the element array
195: * @param selector the simple selector to test
196: * @return true if matched
197: */
198: public static boolean is(Element[] els, String selector) {
199: return doIs(JavaScriptObjectHelper
200: .convertToJavaScriptArray(els), selector);
201: }
202:
203: private static native boolean doIs(JavaScriptObject els,
204: String selector) /*-{
205: return $wnd.Ext.DomQuery.is(els, selector);
206: }-*/;
207:
208: /**
209: * Selects a group of elements.
210: *
211: * @param selector the selector/xpath query (can be a comma separated list of selectors)
212: * @return array of selected elements
213: */
214: public static Element[] select(String selector) {
215: JavaScriptObject jsElements = doSelect(selector);
216: return JavaScriptObjectHelper.toElementArray(jsElements);
217: }
218:
219: private static native JavaScriptObject doSelect(String selector) /*-{
220: return $wnd.Ext.DomQuery.select(selector);
221: }-*/;
222:
223: /**
224: * Selects a group of elements.
225: *
226: * @param selector the selector/xpath query (can be a comma separated list of selectors)
227: * @param root the start of the query (defaults to document)
228: * @return array of selected elements
229: */
230: public static Element[] select(String selector, Element root) {
231: JavaScriptObject jsElements = doSelect(selector, root);
232: return JavaScriptObjectHelper.toElementArray(jsElements);
233: }
234:
235: private static native JavaScriptObject doSelect(String selector,
236: Element root) /*-{
237: return $wnd.Ext.DomQuery.select(selector, root);
238: }-*/;
239:
240: /**
241: * Selects a single element.
242: *
243: * @param selector the selector/xpath query
244: * @return the selected element
245: */
246: public static native Element selectNode(String selector) /*-{
247: return $wnd.Ext.DomQuery.selectNode(selector);
248: }-*/;
249:
250: /**
251: * Selects a single element.
252: *
253: * @param selector the selector/xpath query
254: * @param root the start of the query (defaults to document).
255: * @return the selected element
256: */
257: public static native Element selectNode(String selector,
258: Element root) /*-{
259: return $wnd.Ext.DomQuery.selectNode(selector, root);
260: }-*/;
261:
262: /**
263: * Selects the value of a node, parsing integers and floats.
264: *
265: * @param selector the selector/xpath query
266: * @return the node value
267: */
268: public static native float selectNumber(String selector) /*-{
269: return $wnd.Ext.DomQuery.selectNumber(selector);
270: }-*/;
271:
272: /**
273: * Selects the value of a node, parsing integers and floats.
274: *
275: * @param selector the selector/xpath query
276: * @param root the start of the query (defaults to document)
277: * @return the node value
278: */
279: public static native float selectNumber(String selector,
280: Element root) /*-{
281: return $wnd.Ext.DomQuery.selectNumber(selector, root);
282: }-*/;
283:
284: /**
285: * Selects the value of a node.
286: *
287: * @param selector the selector/xpath query
288: * @return the node value
289: */
290: public static native String selectValue(String selector) /*-{
291: return $wnd.Ext.DomQuery.selectValue(selector);
292: }-*/;
293:
294: /**
295: * Selects the value of a node
296: *
297: * @param selector the selector/xpath query
298: * @param root the start of the query (defaults to document)
299: * @return the node value
300: */
301: public static native String selectValue(String selector,
302: Element root) /*-{
303: return $wnd.Ext.DomQuery.selectValue(selector, root);
304: }-*/;
305:
306: /**
307: * Selects the value of a node, optionally replacing null with the defaultValue
308: *
309: * @param selector the selector/xpath query
310: * @param root the start of the query (defaults to document)
311: * @param defaultValue value returned if null
312: * @return the node value
313: */
314: public static native String selectValue(String selector,
315: Element root, String defaultValue) /*-{
316: return $wnd.Ext.DomQuery.selectValue(selector, root, defaultValue);
317: }-*/;
318:
319: }
|