\r
<!-- UI implementations -->\r
<replace-with\r
- class="gwtquery.plugins.commonui.client.GQuery.GQueryUiImplTrident">\r
+ class="com.google.gwt.query.client.plugins.UiPlugin.GQueryUiImplTrident">\r
<when-type-is\r
- class="gwtquery.plugins.commonui.client.GQuery.GQueryUiImpl" />\r
+ class="com.google.gwt.query.client.plugins.UiPlugin.GQueryUiImpl" />\r
<any>\r
<when-property-is name="user.agent" value="ie6" />\r
<when-property-is name="user.agent" value="ie8" />\r
</any>\r
</replace-with>\r
+ \r
+ <!-- Attribute setter implementations -->\r
+ <replace-with\r
+ class="com.google.gwt.query.client.impl.AttributeTridentImpl">\r
+ <when-type-is\r
+ class="com.google.gwt.query.client.impl.AttributeImpl" />\r
+ <when-property-is name="user.agent" value="ie6" />\r
+\r
+ </replace-with>\r
\r
<!-- IE8 needs the iframe where the js of app is loaded set to standard in order \r
to use the queryAll native selector --> \r
import com.google.gwt.dom.client.SelectElement;\r
import com.google.gwt.dom.client.Style.Display;\r
import com.google.gwt.dom.client.Style.HasCssName;\r
-import com.google.gwt.dom.client.StyleInjector.StyleInjectorImpl;\r
import com.google.gwt.dom.client.TextAreaElement;\r
import com.google.gwt.query.client.css.HasCssValue;\r
import com.google.gwt.query.client.css.TakesCssValue;\r
import com.google.gwt.query.client.css.TakesCssValue.CssSetter;\r
+import com.google.gwt.query.client.impl.AttributeImpl;\r
import com.google.gwt.query.client.impl.DocumentStyleImpl;\r
import com.google.gwt.query.client.impl.SelectorEngine;\r
import com.google.gwt.query.client.js.JsCache;\r
\r
// Sizzle POS regex : usefull in some methods\r
private static final String POS_REGEX = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)";\r
-\r
+ \r
private static JsRegexp tagNameRegex = new JsRegexp("<([\\w:]+)");\r
\r
private static final JsNamedArray<TagWrapper> wrapperMap;\r
+ \r
+ private static AttributeImpl attributeDelegate = GWT.create(AttributeImpl.class);\r
\r
static {\r
TagWrapper tableWrapper = new TagWrapper(1, "<table>", "</table>");\r
* Properties("src: 'test.jpg', alt: 'Test Image'"))\r
*/\r
public GQuery attr(Properties properties) {\r
- for (Element e : elements) {\r
- for (String name : properties.keys()) {\r
- e.setAttribute(JsUtils.hyphenize(name), properties.getStr(name));\r
- }\r
+ for (String name : properties.keys()) {\r
+ attributeDelegate.setAttribute(this, JsUtils.hyphenize(name), properties.getStr(name));\r
}\r
return this;\r
}\r
public String attr(String name) {\r
return isEmpty() ? "" : get(0).getAttribute(name);\r
}\r
-\r
+ \r
/**\r
* Set a single property to a computed value, on all matched elements.\r
*/\r
int i = 0;\r
for (Element e : elements) {\r
Object val = closure.f(e.<com.google.gwt.dom.client.Element>cast(), i++);\r
- if (val != null) {\r
- setElementAttribute(e, key, String.valueOf(val));\r
- }\r
+ attributeDelegate.setAttribute($(e), key, val);\r
}\r
return this;\r
- }\r
- \r
+ } \r
+
/**\r
* Set a single property to a value, on all matched elements.\r
*/\r
- public GQuery attr(String key, boolean value) {\r
- String val = value ? key : null;\r
- for (Element e : elements) {\r
- setElementAttribute(e, key, val);\r
- }\r
+ public GQuery attr(String key, Object value) {\r
+ assert key != null : "key cannot be null";\r
+ attributeDelegate.setAttribute(this, key, value);\r
return this;\r
}\r
\r
- /**\r
- * Set a single property to a value, on all matched elements.\r
- */\r
- public GQuery attr(String key, String value) {\r
- if (value == null) {\r
- return removeAttr(key);\r
- }\r
- for (Element e : elements) {\r
- e.setAttribute(key, value);\r
- }\r
- return this;\r
- }\r
+ \r
\r
/**\r
* Insert content before each of the matched elements. The elements must\r
* Remove the named attribute from every element in the matched set.\r
*/\r
public GQuery removeAttr(String key) {\r
- for (Element e : elements) {\r
- e.removeAttribute(key);\r
- }\r
+ attributeDelegate.removeAttribute(this, key);\r
return this;\r
}\r
\r
--- /dev/null
+/*
+ * Copyright 2011, The gwtquery team.
+ *
+ * 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.query.client.impl;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.query.client.GQuery;
+import com.google.gwt.query.client.js.JsRegexp;
+import com.google.gwt.query.client.js.JsUtils;
+
+/**
+ * Helper class for setting and getting attribute on an element.
+ *
+ */
+public class AttributeImpl {
+
+ public interface AttributeSetter {
+ public boolean isRemoval(Object value);
+
+ public void setAttribute(Element e, String name, Object value);
+ }
+
+ /**
+ * Default setter using <code>e.setAttribute()</code> method
+ *
+ */
+ protected static class DefaultSetter implements AttributeSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new DefaultSetter();
+ }
+ return INSTANCE;
+ }
+
+ protected DefaultSetter(){}
+
+ public boolean isRemoval(Object value) {
+ return value == null;
+ }
+
+ public void setAttribute(Element e, String key, Object value) {
+ e.setAttribute(key, String.valueOf(value));
+ }
+
+ }
+
+ /**
+ * value must be set on element directly
+ *
+ */
+ protected static class ValueAttrSetter extends DefaultSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ValueAttrSetter();
+ }
+ return INSTANCE;
+ }
+
+ protected ValueAttrSetter(){}
+
+ public void setAttribute(Element e, String key, Object value) {
+
+ e.setPropertyObject("value", String.valueOf(value));
+
+ super.setAttribute(e, key, value);
+ }
+
+
+ }
+
+ /**
+ * Boolean attribute :
+ * - Ensure to set a boolean value to the element's property with the same name if this last exists
+ * - Ensure to set an attribute having the value equals to the name (i.e checked="checked")
+ */
+ private static class BooleanAttrSetter extends DefaultSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new BooleanAttrSetter();
+ }
+ return INSTANCE;
+ }
+
+ protected BooleanAttrSetter(){}
+
+ public boolean isRemoval(Object value) {
+ return super.isRemoval(value) || Boolean.FALSE.equals(value);
+ }
+
+ public void setAttribute(Element e, String key, Object value) {
+
+ if (JsUtils.hasProperty(e, key)) {
+ e.setPropertyBoolean(key, true);
+ }
+
+ super.setAttribute(e, key, key.toLowerCase());
+ }
+
+ }
+
+ /**
+ * For button and inputs, the type cannot be changed once the element is attached to the dom !
+ *
+ */
+ private static class TypeAttrSetter extends DefaultSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ private static JsRegexp NOT_AUTHORIZED_NODE = new JsRegexp("^(?:button|input)$","i");
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new TypeAttrSetter();
+ }
+ return INSTANCE;
+ }
+
+ protected TypeAttrSetter(){}
+
+ public void setAttribute(Element e, String name, Object value) {
+ String tag = e.getNodeName();
+
+ if (NOT_AUTHORIZED_NODE.test(tag) && GQuery.$(e).parents("body").length() > 0){
+ //TODO maybe it's better to simply do nothing...
+ throw new RuntimeException("You cannot change type of button or input element if the element is already attached to the dom");
+ }
+ if ("input".equals(tag.toLowerCase()) && "radio".equals(value)){
+ //some browser (IE6-9) resets value property of the input when you change the type to radio button
+ InputElement ie = InputElement.as(e);
+ String keepValue = ie.getValue();
+
+ super.setAttribute(ie, "type", value);
+
+ ie.setValue(keepValue);
+
+ }else{
+ super.setAttribute(e, name, value);
+ }
+ }
+ }
+
+ private static final JsRegexp BOOLEAN_ATTR_REGEX = new JsRegexp(
+ "^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$",
+ "i");
+
+ public void removeAttribute(GQuery gQuery, String key){
+
+ for (Element e : gQuery.elements()) {
+ if (e.getNodeType() != 1) {
+ continue;
+ }
+ e.removeAttribute( key );
+
+ if (BOOLEAN_ATTR_REGEX.test(key) && JsUtils.hasProperty(e, key)){
+ e.setPropertyBoolean(key, false);
+ }
+ }
+ }
+
+ public void setAttribute(GQuery gQuery, String key, Object value) {
+
+ AttributeSetter setter = getAttributeSetter(key);
+
+ if (setter.isRemoval(value)) {
+ gQuery.removeAttr(key);
+ return;
+ }
+
+ value = fixValue(key, value);
+
+ for (Element e : gQuery.elements()) {
+ int nodeType = e.getNodeType();
+
+ //don't set attribute on text, comment and attributes nodes
+ if (nodeType == 3 || nodeType == 8 || nodeType == 2) {
+ continue;
+ }
+
+ setter.setAttribute(e, key, value);
+ }
+
+ }
+
+ protected Object fixValue(String key, Object value) {
+ return value;
+ }
+
+ protected AttributeSetter getAttributeSetter(String key) {
+
+ if ("type".equalsIgnoreCase(key)) {
+ return TypeAttrSetter.getInstance();
+
+ } else if ("value".equals("key")){
+ return ValueAttrSetter.getInstance();
+
+ } else if (BOOLEAN_ATTR_REGEX.test(key)) {
+ return BooleanAttrSetter.getInstance();
+ }
+ return DefaultSetter.getInstance();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2011, The gwtquery team.
+ *
+ * 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.query.client.impl;
+
+import com.google.gwt.dom.client.Element;
+
+/**
+ * Helper class for setting and getting attribute on an element.
+ *
+ */
+public class AttributeTridentImpl extends AttributeImpl {
+
+ /**
+ * use {@link AttrNodeSetter} for button element
+ *
+ */
+ protected static class IEValueAttrSetter extends ValueAttrSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new IEValueAttrSetter();
+ }
+ return INSTANCE;
+ }
+
+ private IEValueAttrSetter() {
+
+ }
+
+ public void setAttribute(Element e, String key, Object value) {
+
+ if ("button".equals(e.getTagName())) {
+ AttrNodeSetter.getInstance().setAttribute(e, key, value);
+ return;
+ }
+
+ super.setAttribute(e, key, value);
+ }
+
+ }
+ private static class AttrNodeSetter extends DefaultSetter {
+
+ private static AttributeSetter INSTANCE;
+
+ public static AttributeSetter getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new AttrNodeSetter();
+ }
+ return INSTANCE;
+ }
+
+ private AttrNodeSetter() {
+ }
+
+ @Override
+ public void setAttribute(Element e, String key, Object value) {
+ if (!setAttributeNode(e, key, value)) {
+ super.setAttribute(e, key, value);
+ }
+
+ }
+
+ }
+
+ private static native String getAttributeNode(Element e, String name)/*-{
+ var attrNode = e.getAttributeNode(name);
+ if (attrNode && attrNode.nodeValue !== "") {
+ return attrNode.nodeValue;
+ }
+ return null;
+ }-*/;
+
+ private static native boolean setAttributeNode(Element e, String name,
+ Object value)/*-{
+ var attrNode = e.getAttributeNode(name);
+ if (attrNode) {
+ attrNode.nodeValue = value;
+ return true;
+ }
+ return false;
+ }-*/;
+
+ @Override
+ protected Object fixValue(String key, Object value) {
+ if (("width".equalsIgnoreCase(key) || "height".equalsIgnoreCase(key))
+ && (key == null || "".equals(key))) {
+ return "auto";
+ }
+ return value;
+ }
+
+ @Override
+ protected AttributeSetter getAttributeSetter(String key) {
+ if (!"className".equals(key)
+ && ("name".equals(key) || "title".equals(key) || key.contains(":") || key.startsWith("on"))) {
+ return AttrNodeSetter.getInstance();
+ } else if ("value".equals(key)) {
+ return IEValueAttrSetter.getInstance();
+ }
+ return super.getAttributeSetter(key);
+ }
+
+}
public static native boolean truth(Object a) /*-{
return a ? true : false;
}-*/;
+
+
+ /**
+ * Check if an object have already a property with name <code>name</code> defined.
+ */
+ public static native boolean hasProperty(JavaScriptObject o, String name)/*-{
+ return name in o;
+ }-*/;
/**
* Remove duplicates from an elements array
import static com.google.gwt.query.client.GQuery.$$;
import static com.google.gwt.query.client.GQuery.document;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import junit.framework.Assert;
-
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.OptionElement;
-import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
/**
* Test class for testing gwtquery-core api.
*/
}
- public void testCheckedAttr_Issue97() {
+ public void testAttr_Issue97() {
$(e).html("<input type='checkbox' id='cb' name='cb' value='1' />");
assertNull($("#cb:checked", e).val());
+
$("#cb", e).attr("checked", "checked");
- assertEquals("1", $("#cb:checked", e).val());
+ assertEquals(1, $("#cb:checked", e).length());
+ assertEquals(true, InputElement.as($("#cb", e).get(0)).isChecked());
+ assertEquals("checked", $("#cb", e).get(0).getAttribute("checked"));
+ assertEquals(true, $("#cb", e).get(0).getPropertyBoolean("checked"));
+
$("#cb", e).removeAttr("checked");
- assertNull($("#cb:checked", e).val());
+ assertEquals(0, $("#cb:checked", e).length());
+ assertEquals(false, InputElement.as($("#cb", e).get(0)).isChecked());
+ assertEquals("", $("#cb", e).get(0).getAttribute("checked"));
+ assertEquals(false, $("#cb", e).get(0).getPropertyBoolean("checked"));
+
$("#cb", e).attr("checked", true);
- assertEquals("1", $("#cb:checked", e).val());
+ assertEquals(1, $("#cb:checked", e).length());
+ assertEquals(true, InputElement.as($("#cb", e).get(0)).isChecked());
+ assertEquals("checked", $("#cb", e).get(0).getAttribute("checked"));
+ assertEquals(true, $("#cb", e).get(0).getPropertyBoolean("checked"));
+
$("#cb", e).attr("checked", false);
- assertNull($("#cb:checked", e).val());
+ assertEquals(0, $("#cb:checked", e).length());
+ assertEquals(false, InputElement.as($("#cb", e).get(0)).isChecked());
+ assertEquals("", $("#cb", e).get(0).getAttribute("checked"));
+ assertEquals(false, $("#cb", e).get(0).getPropertyBoolean("checked"));
+
$("#cb", e).attr("checked", "");
- assertNull($("#cb:checked", e).val());
+ assertEquals(1, $("#cb:checked", e).length());
+ assertEquals(true, InputElement.as($("#cb", e).get(0)).isChecked());
+ assertEquals("checked", $("#cb", e).get(0).getAttribute("checked"));
+ assertEquals(true, $("#cb", e).get(0).getPropertyBoolean("checked"));
+
+ GQuery gq = $("<div></div>test<!-- a comment-->");
+ gq.attr("class", "test1");
+
+ assertEquals("test1", gq.get(0).getClassName());
+ assertEquals("test1", gq.attr("class"));
+ assertNull(gq.get(0).getPropertyString("class"));
+
+ gq.removeAttr("class");
+ assertEquals("", gq.get(0).getClassName());
+ assertEquals("", gq.attr("class"));
+
+ //test on value
+ $("#cb", e).attr("value", "mail");
+ assertEquals("mail", InputElement.as($("#cb", e).get(0)).getValue());
+ assertEquals("mail", $("#cb", e).get(0).getAttribute("value"));
+
+ $("#cb", e).removeAttr("value");
+ assertEquals("", InputElement.as($("#cb", e).get(0)).getValue());
+ assertEquals("", $("#cb", e).get(0).getAttribute("value"));
+
+ try{
+ $("#cb", e).attr("type", "hidden");
+ fail("Cannnot change a type of an element already attached to the dom");
+ }catch (Exception e){}
+
+ gq = $("<input type='text' value='blop'></input>");
+ gq.attr("type", "radio");
+ assertEquals("radio", InputElement.as(gq.get(0)).getType());
+ assertEquals("blop", InputElement.as(gq.get(0)).getValue());
+
+
+
+ gq.attr(Properties.create("{class:'test2', disabled:true}"));
+ InputElement ie = InputElement.as(gq.get(0));
+
+ assertEquals("test2", ie.getClassName());
+ assertEquals(true, ie.isDisabled());
+ assertEquals("disabled", ie.getAttribute("disabled"));
+
}
public void testWidthHeight() {